]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
C FE: suggest corrections for misspelled identifiers and type names
authorDavid Malcolm <dmalcolm@redhat.com>
Wed, 22 Jun 2016 15:20:41 +0000 (15:20 +0000)
committerDavid Malcolm <dmalcolm@gcc.gnu.org>
Wed, 22 Jun 2016 15:20:41 +0000 (15:20 +0000)
gcc/c-family/ChangeLog:
PR c/70339
* c-common.h (enum lookup_name_fuzzy_kind): New enum.
(lookup_name_fuzzy): New prototype.

gcc/c/ChangeLog:
PR c/70339
* c-decl.c: Include spellcheck-tree.h and gcc-rich-location.h.
(implicit_decl_warning): When issuing warnings for implicit
declarations, attempt to provide a suggestion via
lookup_name_fuzzy.
(undeclared_variable): Likewise when issuing errors.
(lookup_name_in_scope): Likewise.
(struct edit_distance_traits<cpp_hashnode *>): New struct.
(best_macro_match): New typedef.
(find_closest_macro_cpp_cb): New function.
(lookup_name_fuzzy): New function.
* c-parser.c: Include gcc-rich-location.h.
(c_token_starts_typename): Split out case CPP_KEYWORD into...
(c_keyword_starts_typename): ...this new function.
(c_parser_declaration_or_fndef): When issuing errors about
missing "struct" etc, add a fixit.  For other kinds of errors,
attempt to provide a suggestion via lookup_name_fuzzy.
(c_parser_parms_declarator): When looking ahead to detect typos in
type names, also reject CPP_KEYWORD.
(c_parser_parameter_declaration): When issuing errors about
unknown type names, attempt to provide a suggestion via
lookup_name_fuzzy.
* c-tree.h (c_keyword_starts_typename): New prototype.

gcc/ChangeLog:
PR c/70339
* diagnostic-core.h (pedwarn_at_rich_loc): New prototype.
* diagnostic.c (pedwarn_at_rich_loc): New function.
* spellcheck.h (best_match::best_match): Add a
"best_distance_so_far" optional parameter.
(best_match::set_best_so_far): New method.
(best_match::get_best_distance): New accessor.
(best_match::get_best_candidate_length): New accessor.

gcc/testsuite/ChangeLog:
PR c/70339
* c-c++-common/attributes-1.c: Update dg-prune-output to include
hint.
* gcc.dg/diagnostic-token-ranges.c (undeclared_identifier): Update
expected results due to builtin "nanl" now being suggested for
"name".
* gcc.dg/pr67580.c: Update expected messages.
* gcc.dg/spellcheck-identifiers.c: New testcase.
* gcc.dg/spellcheck-typenames.c: New testcase.

From-SVN: r237714

16 files changed:
gcc/ChangeLog
gcc/c-family/ChangeLog
gcc/c-family/c-common.h
gcc/c/ChangeLog
gcc/c/c-decl.c
gcc/c/c-parser.c
gcc/c/c-tree.h
gcc/diagnostic-core.h
gcc/diagnostic.c
gcc/spellcheck.h
gcc/testsuite/ChangeLog
gcc/testsuite/c-c++-common/attributes-1.c
gcc/testsuite/gcc.dg/diagnostic-token-ranges.c
gcc/testsuite/gcc.dg/pr67580.c
gcc/testsuite/gcc.dg/spellcheck-identifiers.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/spellcheck-typenames.c [new file with mode: 0644]

index b30caadbfb7789672d4e7ff7421e62263bb09068..0ae2b0338fbb0bcdaa7e48a09d60e4240ad6efe7 100644 (file)
@@ -1,3 +1,14 @@
+2016-06-22  David Malcolm  <dmalcolm@redhat.com>
+
+       PR c/70339
+       * diagnostic-core.h (pedwarn_at_rich_loc): New prototype.
+       * diagnostic.c (pedwarn_at_rich_loc): New function.
+       * spellcheck.h (best_match::best_match): Add a
+       "best_distance_so_far" optional parameter.
+       (best_match::set_best_so_far): New method.
+       (best_match::get_best_distance): New accessor.
+       (best_match::get_best_candidate_length): New accessor.
+
 2016-06-22  Nick Clifton  <nickc@redhat.com>
 
        * dwarf2out.c (scompare_loc_descriptor): Use SCALAR_INT_MODE_P() in
index 7d2ca462223a856472a7971cefb08d8eb5c2bb9e..cd463154da64b586d57de5df04c004721064aac3 100644 (file)
@@ -1,3 +1,9 @@
+2016-06-22  David Malcolm  <dmalcolm@redhat.com>
+
+       PR c/70339
+       * c-common.h (enum lookup_name_fuzzy_kind): New enum.
+       (lookup_name_fuzzy): New prototype.
+
 2016-06-21  John David Anglin  <danglin@gcc.gnu.org>
 
        * c-common.c (get_source_date_epoch): Use int64_t instead of long long.
index 4e6aa0051fa23a3093f9012717f79578b8bbfb41..3ad5400458efb0e9dde10519b6892254cbd5be07 100644 (file)
@@ -990,6 +990,15 @@ extern tree lookup_label (tree);
 extern tree lookup_name (tree);
 extern bool lvalue_p (const_tree);
 
+enum lookup_name_fuzzy_kind {
+  /* Names of types.  */
+  FUZZY_LOOKUP_TYPENAME,
+
+  /* Any name.  */
+  FUZZY_LOOKUP_NAME
+};
+extern tree lookup_name_fuzzy (tree, enum lookup_name_fuzzy_kind);
+
 extern bool vector_targets_convertible_p (const_tree t1, const_tree t2);
 extern bool vector_types_convertible_p (const_tree t1, const_tree t2, bool emit_lax_note);
 extern tree c_build_vec_perm_expr (location_t, tree, tree, tree, bool = true);
index 35b3de4a3659f0c075c9b0a4bbe263286c8c7abe..47d1f571f98b06ec33bcda88016cbc35074bd0a1 100644 (file)
@@ -1,3 +1,29 @@
+2016-06-22  David Malcolm  <dmalcolm@redhat.com>
+
+       PR c/70339
+       * c-decl.c: Include spellcheck-tree.h and gcc-rich-location.h.
+       (implicit_decl_warning): When issuing warnings for implicit
+       declarations, attempt to provide a suggestion via
+       lookup_name_fuzzy.
+       (undeclared_variable): Likewise when issuing errors.
+       (lookup_name_in_scope): Likewise.
+       (struct edit_distance_traits<cpp_hashnode *>): New struct.
+       (best_macro_match): New typedef.
+       (find_closest_macro_cpp_cb): New function.
+       (lookup_name_fuzzy): New function.
+       * c-parser.c: Include gcc-rich-location.h.
+       (c_token_starts_typename): Split out case CPP_KEYWORD into...
+       (c_keyword_starts_typename): ...this new function.
+       (c_parser_declaration_or_fndef): When issuing errors about
+       missing "struct" etc, add a fixit.  For other kinds of errors,
+       attempt to provide a suggestion via lookup_name_fuzzy.
+       (c_parser_parms_declarator): When looking ahead to detect typos in
+       type names, also reject CPP_KEYWORD.
+       (c_parser_parameter_declaration): When issuing errors about
+       unknown type names, attempt to provide a suggestion via
+       lookup_name_fuzzy.
+       * c-tree.h (c_keyword_starts_typename): New prototype.
+
 2016-06-20  Joseph Myers  <joseph@codesourcery.com>
 
        PR c/71601
index 5c08c5947c03dc3db7b467c83195cd5d93554a98..8b966fecc29bd71978a2314dcf7a9c3c254a2646 100644 (file)
@@ -51,6 +51,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "c-family/c-ada-spec.h"
 #include "cilk.h"
 #include "builtins.h"
+#include "spellcheck-tree.h"
+#include "gcc-rich-location.h"
 
 /* In grokdeclarator, distinguish syntactic contexts of declarators.  */
 enum decl_context
@@ -3086,13 +3088,36 @@ implicit_decl_warning (location_t loc, tree id, tree olddecl)
   if (warn_implicit_function_declaration)
     {
       bool warned;
+      tree hint = NULL_TREE;
+      if (!olddecl)
+       hint = lookup_name_fuzzy (id, FUZZY_LOOKUP_NAME);
 
       if (flag_isoc99)
-       warned = pedwarn (loc, OPT_Wimplicit_function_declaration,
-                         "implicit declaration of function %qE", id);
+       if (hint)
+         {
+           gcc_rich_location richloc (loc);
+           richloc.add_fixit_misspelled_id (loc, hint);
+           warned = pedwarn_at_rich_loc
+             (&richloc, OPT_Wimplicit_function_declaration,
+              "implicit declaration of function %qE; did you mean %qE?",
+              id, hint);
+         }
+       else
+         warned = pedwarn (loc, OPT_Wimplicit_function_declaration,
+                           "implicit declaration of function %qE", id);
       else
-       warned = warning_at (loc, OPT_Wimplicit_function_declaration,
-                            G_("implicit declaration of function %qE"), id);
+       if (hint)
+         {
+           gcc_rich_location richloc (loc);
+           richloc.add_fixit_misspelled_id (loc, hint);
+           warned = warning_at_rich_loc
+             (&richloc, OPT_Wimplicit_function_declaration,
+              G_("implicit declaration of function %qE;did you mean %qE?"),
+              id, hint);
+         }
+       else
+         warned = warning_at (loc, OPT_Wimplicit_function_declaration,
+                              G_("implicit declaration of function %qE"), id);
       if (olddecl && warned)
        locate_old_decl (olddecl);
     }
@@ -3408,13 +3433,38 @@ undeclared_variable (location_t loc, tree id)
 
   if (current_function_decl == 0)
     {
-      error_at (loc, "%qE undeclared here (not in a function)", id);
+      tree guessed_id = lookup_name_fuzzy (id, FUZZY_LOOKUP_NAME);
+      if (guessed_id)
+       {
+         gcc_rich_location richloc (loc);
+         richloc.add_fixit_misspelled_id (loc, guessed_id);
+         error_at_rich_loc (&richloc,
+                            "%qE undeclared here (not in a function);"
+                            " did you mean %qE?",
+                            id, guessed_id);
+       }
+      else
+       error_at (loc, "%qE undeclared here (not in a function)", id);
       scope = current_scope;
     }
   else
     {
       if (!objc_diagnose_private_ivar (id))
-        error_at (loc, "%qE undeclared (first use in this function)", id);
+       {
+         tree guessed_id = lookup_name_fuzzy (id, FUZZY_LOOKUP_NAME);
+         if (guessed_id)
+           {
+             gcc_rich_location richloc (loc);
+             richloc.add_fixit_misspelled_id (loc, guessed_id);
+             error_at_rich_loc
+               (&richloc,
+                "%qE undeclared (first use in this function);"
+                " did you mean %qE?",
+                id, guessed_id);
+           }
+         else
+           error_at (loc, "%qE undeclared (first use in this function)", id);
+       }
       if (!already)
        {
           inform (loc, "each undeclared identifier is reported only"
@@ -3904,6 +3954,134 @@ lookup_name_in_scope (tree name, struct c_scope *scope)
       return b->decl;
   return NULL_TREE;
 }
+
+/* Specialization of edit_distance_traits for preprocessor macros.  */
+
+template <>
+struct edit_distance_traits<cpp_hashnode *>
+{
+  static size_t get_length (cpp_hashnode *hashnode)
+  {
+    return hashnode->ident.len;
+  }
+
+  static const char *get_string (cpp_hashnode *hashnode)
+  {
+    return (const char *)hashnode->ident.str;
+  }
+};
+
+/* Specialization of best_match<> for finding the closest preprocessor
+   macro to a given identifier.  */
+
+typedef best_match<tree, cpp_hashnode *> best_macro_match;
+
+/* A callback for cpp_forall_identifiers, for use by lookup_name_fuzzy.
+   Process HASHNODE and update the best_macro_match instance pointed to be
+   USER_DATA.  */
+
+static int
+find_closest_macro_cpp_cb (cpp_reader *, cpp_hashnode *hashnode,
+                          void *user_data)
+{
+  if (hashnode->type != NT_MACRO)
+    return 1;
+
+  best_macro_match *bmm = (best_macro_match *)user_data;
+  bmm->consider (hashnode);
+
+  /* Keep iterating.  */
+  return 1;
+}
+
+/* Look for the closest match for NAME within the currently valid
+   scopes.
+
+   This finds the identifier with the lowest Levenshtein distance to
+   NAME.  If there are multiple candidates with equal minimal distance,
+   the first one found is returned.  Scopes are searched from innermost
+   outwards, and within a scope in reverse order of declaration, thus
+   benefiting candidates "near" to the current scope.
+
+   The function also looks for similar macro names to NAME, since a
+   misspelled macro name will not be expanded, and hence looks like an
+   identifier to the C frontend.
+
+   It also looks for start_typename keywords, to detect "singed" vs "signed"
+   typos.  */
+
+tree
+lookup_name_fuzzy (tree name, enum lookup_name_fuzzy_kind kind)
+{
+  gcc_assert (TREE_CODE (name) == IDENTIFIER_NODE);
+
+  best_match<tree, tree> bm (name);
+
+  /* Look within currently valid scopes.  */
+  for (c_scope *scope = current_scope; scope; scope = scope->outer)
+    for (c_binding *binding = scope->bindings; binding; binding = binding->prev)
+      {
+       if (!binding->id)
+         continue;
+       /* Don't use bindings from implicitly declared functions,
+          as they were likely misspellings themselves.  */
+       if (TREE_CODE (binding->decl) == FUNCTION_DECL)
+         if (C_DECL_IMPLICIT (binding->decl))
+           continue;
+       if (kind == FUZZY_LOOKUP_TYPENAME)
+         if (TREE_CODE (binding->decl) != TYPE_DECL)
+           continue;
+       bm.consider (binding->id);
+      }
+
+  /* Consider macros: if the user misspelled a macro name e.g. "SOME_MACRO"
+     as:
+       x = SOME_OTHER_MACRO (y);
+     then "SOME_OTHER_MACRO" will survive to the frontend and show up
+     as a misspelled identifier.
+
+     Use the best distance so far so that a candidate is only set if
+     a macro is better than anything so far.  This allows early rejection
+     (without calculating the edit distance) of macro names that must have
+     distance >= bm.get_best_distance (), and means that we only get a
+     non-NULL result for best_macro_match if it's better than any of
+     the identifiers already checked, which avoids needless creation
+     of identifiers for macro hashnodes.  */
+  best_macro_match bmm (name, bm.get_best_distance ());
+  cpp_forall_identifiers (parse_in, find_closest_macro_cpp_cb, &bmm);
+  cpp_hashnode *best_macro = bmm.get_best_meaningful_candidate ();
+  /* If a macro is the closest so far to NAME, use it, creating an
+     identifier tree node for it.  */
+  if (best_macro)
+    {
+      const char *id = (const char *)best_macro->ident.str;
+      tree macro_as_identifier
+       = get_identifier_with_length (id, best_macro->ident.len);
+      bm.set_best_so_far (macro_as_identifier,
+                         bmm.get_best_distance (),
+                         bmm.get_best_candidate_length ());
+    }
+
+  /* Try the "start_typename" keywords to detect
+     "singed" vs "signed" typos.  */
+  if (kind == FUZZY_LOOKUP_TYPENAME)
+    {
+      for (unsigned i = 0; i < num_c_common_reswords; i++)
+       {
+         const c_common_resword *resword = &c_common_reswords[i];
+         if (!c_keyword_starts_typename (resword->rid))
+           continue;
+         tree resword_identifier = ridpointers [resword->rid];
+         if (!resword_identifier)
+           continue;
+         gcc_assert (TREE_CODE (resword_identifier) == IDENTIFIER_NODE);
+         bm.consider (resword_identifier);
+       }
+    }
+
+  return bm.get_best_meaningful_candidate ();
+}
+
 \f
 /* Create the predefined scalar types of C,
    and some nodes representing standard constants (0, 1, (void *) 0).
index 78bf68e677eea4093ef9d903d221b5bb6d55a287..7f491f1dc060857e22ff489621e97d0508fa4e80 100644 (file)
@@ -58,6 +58,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "c-family/c-indentation.h"
 #include "gimple-expr.h"
 #include "context.h"
+#include "gcc-rich-location.h"
 
 /* We need to walk over decls with incomplete struct/union/enum types
    after parsing the whole translation unit.
@@ -518,6 +519,48 @@ c_parser_peek_nth_token (c_parser *parser, unsigned int n)
   return &parser->tokens[n - 1];
 }
 
+bool
+c_keyword_starts_typename (enum rid keyword)
+{
+  switch (keyword)
+    {
+    case RID_UNSIGNED:
+    case RID_LONG:
+    case RID_SHORT:
+    case RID_SIGNED:
+    case RID_COMPLEX:
+    case RID_INT:
+    case RID_CHAR:
+    case RID_FLOAT:
+    case RID_DOUBLE:
+    case RID_VOID:
+    case RID_DFLOAT32:
+    case RID_DFLOAT64:
+    case RID_DFLOAT128:
+    case RID_BOOL:
+    case RID_ENUM:
+    case RID_STRUCT:
+    case RID_UNION:
+    case RID_TYPEOF:
+    case RID_CONST:
+    case RID_ATOMIC:
+    case RID_VOLATILE:
+    case RID_RESTRICT:
+    case RID_ATTRIBUTE:
+    case RID_FRACT:
+    case RID_ACCUM:
+    case RID_SAT:
+    case RID_AUTO_TYPE:
+      return true;
+    default:
+      if (keyword >= RID_FIRST_INT_N
+         && keyword < RID_FIRST_INT_N + NUM_INT_N_ENTS
+         && int_n_enabled_p[keyword - RID_FIRST_INT_N])
+       return true;
+      return false;
+    }
+}
+
 /* Return true if TOKEN can start a type name,
    false otherwise.  */
 static bool
@@ -541,43 +584,7 @@ c_token_starts_typename (c_token *token)
          gcc_unreachable ();
        }
     case CPP_KEYWORD:
-      switch (token->keyword)
-       {
-       case RID_UNSIGNED:
-       case RID_LONG:
-       case RID_SHORT:
-       case RID_SIGNED:
-       case RID_COMPLEX:
-       case RID_INT:
-       case RID_CHAR:
-       case RID_FLOAT:
-       case RID_DOUBLE:
-       case RID_VOID:
-       case RID_DFLOAT32:
-       case RID_DFLOAT64:
-       case RID_DFLOAT128:
-       case RID_BOOL:
-       case RID_ENUM:
-       case RID_STRUCT:
-       case RID_UNION:
-       case RID_TYPEOF:
-       case RID_CONST:
-       case RID_ATOMIC:
-       case RID_VOLATILE:
-       case RID_RESTRICT:
-       case RID_ATTRIBUTE:
-       case RID_FRACT:
-       case RID_ACCUM:
-       case RID_SAT:
-       case RID_AUTO_TYPE:
-         return true;
-       default:
-         if (token->keyword >= RID_FIRST_INT_N
-             && token->keyword < RID_FIRST_INT_N + NUM_INT_N_ENTS
-             && int_n_enabled_p[token->keyword - RID_FIRST_INT_N])
-           return true;
-         return false;
-       }
+      return c_keyword_starts_typename (token->keyword);
     case CPP_LESS:
       if (c_dialect_objc ())
        return true;
@@ -1655,15 +1662,50 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
       && (!nested || !lookup_name (c_parser_peek_token (parser)->value)))
     {
       tree name = c_parser_peek_token (parser)->value;
-      error_at (here, "unknown type name %qE", name);
-      /* Give a hint to the user.  This is not C++ with its implicit
-        typedef.  */
+
+      /* Issue a warning about NAME being an unknown type name, perhaps
+        with some kind of hint.
+        If the user forgot a "struct" etc, suggest inserting
+        it.  Otherwise, attempt to look for misspellings.  */
+      gcc_rich_location richloc (here);
       if (tag_exists_p (RECORD_TYPE, name))
-       inform (here, "use %<struct%> keyword to refer to the type");
+       {
+         /* This is not C++ with its implicit typedef.  */
+         richloc.add_fixit_insert (here, "struct");
+         error_at_rich_loc (&richloc,
+                            "unknown type name %qE;"
+                            " use %<struct%> keyword to refer to the type",
+                            name);
+       }
       else if (tag_exists_p (UNION_TYPE, name))
-       inform (here, "use %<union%> keyword to refer to the type");
+       {
+         richloc.add_fixit_insert (here, "union");
+         error_at_rich_loc (&richloc,
+                            "unknown type name %qE;"
+                            " use %<union%> keyword to refer to the type",
+                            name);
+       }
       else if (tag_exists_p (ENUMERAL_TYPE, name))
-       inform (here, "use %<enum%> keyword to refer to the type");
+       {
+         richloc.add_fixit_insert (here, "enum");
+         error_at_rich_loc (&richloc,
+                            "unknown type name %qE;"
+                            " use %<enum%> keyword to refer to the type",
+                            name);
+       }
+      else
+       {
+         tree hint = lookup_name_fuzzy (name, FUZZY_LOOKUP_TYPENAME);
+         if (hint)
+           {
+             richloc.add_fixit_misspelled_id (here, hint);
+             error_at_rich_loc (&richloc,
+                                "unknown type name %qE; did you mean %qE?",
+                                name, hint);
+           }
+         else
+           error_at (here, "unknown type name %qE", name);
+       }
 
       /* Parse declspecs normally to get a correct pointer type, but avoid
          a further "fails to be a type name" error.  Refuse nested functions
@@ -3632,7 +3674,8 @@ c_parser_parms_declarator (c_parser *parser, bool id_list_ok, tree attrs)
       && c_parser_peek_2nd_token (parser)->type != CPP_NAME
       && c_parser_peek_2nd_token (parser)->type != CPP_MULT
       && c_parser_peek_2nd_token (parser)->type != CPP_OPEN_PAREN
-      && c_parser_peek_2nd_token (parser)->type != CPP_OPEN_SQUARE)
+      && c_parser_peek_2nd_token (parser)->type != CPP_OPEN_SQUARE
+      && c_parser_peek_2nd_token (parser)->type != CPP_KEYWORD)
     {
       tree list = NULL_TREE, *nextp = &list;
       while (c_parser_next_token_is (parser, CPP_NAME)
@@ -3807,7 +3850,18 @@ c_parser_parameter_declaration (c_parser *parser, tree attrs)
       c_parser_set_source_position_from_token (token);
       if (c_parser_next_tokens_start_typename (parser, cla_prefer_type))
        {
-         error_at (token->location, "unknown type name %qE", token->value);
+         tree hint = lookup_name_fuzzy (token->value, FUZZY_LOOKUP_TYPENAME);
+         if (hint)
+           {
+             gcc_assert (TREE_CODE (hint) == IDENTIFIER_NODE);
+             gcc_rich_location richloc (token->location);
+             richloc.add_fixit_misspelled_id (token->location, hint);
+             error_at_rich_loc (&richloc,
+                                "unknown type name %qE; did you mean %qE?",
+                                token->value, hint);
+           }
+         else
+           error_at (token->location, "unknown type name %qE", token->value);
          parser->error = true;
        }
       /* ??? In some Objective-C cases '...' isn't applicable so there
index 8f10a13f763795d5ed6b3837572d66334ecf534d..46be53ece2166bca7fdfaf4d756fea7e797bda1f 100644 (file)
@@ -482,6 +482,7 @@ enum c_inline_static_type {
 \f
 /* in c-parser.c */
 extern void c_parse_init (void);
+extern bool c_keyword_starts_typename (enum rid keyword);
 
 /* in c-aux-info.c */
 extern void gen_aux_info_record (tree, int, int, int);
index f7837613933c6aa50128fb123811a92f24a22cd7..51df15028d4c327dc414324a92f991f012c3d9b5 100644 (file)
@@ -76,6 +76,8 @@ extern void fatal_error (location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3)
 /* Pass one of the OPT_W* from options.h as the second parameter.  */
 extern bool pedwarn (location_t, int, const char *, ...)
      ATTRIBUTE_GCC_DIAG(3,4);
+extern bool pedwarn_at_rich_loc (rich_location *, int, const char *, ...)
+     ATTRIBUTE_GCC_DIAG(3,4);
 extern bool permerror (location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3);
 extern bool permerror_at_rich_loc (rich_location *, const char *,
                                   ...) ATTRIBUTE_GCC_DIAG(2,3);
index d39afff6622891738bfeea00d91d71bc6ca29c95..bb41011afad68cca29af356d591d293fba75224f 100644 (file)
@@ -1201,6 +1201,18 @@ pedwarn (location_t location, int opt, const char *gmsgid, ...)
   return ret;
 }
 
+/* Same as pedwarn, but using RICHLOC.  */
+
+bool
+pedwarn_at_rich_loc (rich_location *richloc, int opt, const char *gmsgid, ...)
+{
+  va_list ap;
+  va_start (ap, gmsgid);
+  bool ret = diagnostic_impl (richloc, opt, gmsgid, &ap, DK_PEDWARN);
+  va_end (ap);
+  return ret;
+}
+
 /* A "permissive" error at LOCATION: issues an error unless
    -fpermissive was given on the command line, in which case it issues
    a warning.  Use this for things that really should be errors but we
index 7379399f1fd24937c1d3017ac2beabf4a284f1d1..035f4ac360880bdbb8970bcf746d37692eaab73f 100644 (file)
@@ -69,11 +69,12 @@ class best_match
 
   /* Constructor.  */
 
-  best_match (goal_t goal)
+  best_match (GOAL_TYPE goal,
+             edit_distance_t best_distance_so_far = MAX_EDIT_DISTANCE)
   : m_goal (goal_traits::get_string (goal)),
     m_goal_len (goal_traits::get_length (goal)),
     m_best_candidate (NULL),
-    m_best_distance (MAX_EDIT_DISTANCE)
+    m_best_distance (best_distance_so_far)
   {}
 
   /* Compare the edit distance between CANDIDATE and m_goal,
@@ -118,6 +119,20 @@ class best_match
       }
   }
 
+  /* Assuming that BEST_CANDIDATE is known to be better than
+     m_best_candidate, update (without recomputing the edit distance to
+     the goal).  */
+
+  void set_best_so_far (CANDIDATE_TYPE best_candidate,
+                       edit_distance_t best_distance,
+                       size_t best_candidate_len)
+  {
+    gcc_assert (best_distance < m_best_distance);
+    m_best_candidate = best_candidate;
+    m_best_distance = best_distance;
+    m_best_candidate_len = best_candidate_len;
+  }
+
   /* Get the best candidate so far, but applying a filter to ensure
      that we return NULL if none of the candidates are close to the goal,
      to avoid offering nonsensical suggestions to the user.  */
@@ -135,6 +150,9 @@ class best_match
     return m_best_candidate;
   }
 
+  edit_distance_t get_best_distance () const { return m_best_distance; }
+  size_t get_best_candidate_length () const { return m_best_candidate_len; }
+
  private:
   const char *m_goal;
   size_t m_goal_len;
index 8cac69489d65c295d548aef29b2b25cf3482dbfa..2082fff71c9ffb316f6856c408f07336cce5149f 100644 (file)
@@ -1,3 +1,15 @@
+2016-06-22  David Malcolm  <dmalcolm@redhat.com>
+
+       PR c/70339
+       * c-c++-common/attributes-1.c: Update dg-prune-output to include
+       hint.
+       * gcc.dg/diagnostic-token-ranges.c (undeclared_identifier): Update
+       expected results due to builtin "nanl" now being suggested for
+       "name".
+       * gcc.dg/pr67580.c: Update expected messages.
+       * gcc.dg/spellcheck-identifiers.c: New testcase.
+       * gcc.dg/spellcheck-typenames.c: New testcase.
+
 2016-06-22  David Malcolm  <dmalcolm@redhat.com>
 
        * gcc.dg/plugin/diagnostic-test-show-locus-parseable-fixits.c: New
index 1657da10d5e40023250544aed9f5e1c03121292e..c348526b0d4b3cc56c7f94a53a07832f0b1510a7 100644 (file)
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-prune-output "undeclared here \\(not in a function\\)|\[^\n\r\]* was not declared in this scope" } */
+/* { dg-prune-output "undeclared here \\(not in a function\\); did you mean .carg..|\[^\n\r\]* was not declared in this scope" } */
 
 void* my_calloc(unsigned, unsigned) __attribute__((alloc_size(1,bar))); /* { dg-warning "outside range" } */
 void* my_realloc(void*, unsigned) __attribute__((alloc_size(bar))); /* { dg-warning "outside range" } */
index ac969e30d94963d70881a1d60c32c98a6ab3443c..19399490c04d3c2d88120d58966b4739947a4fd7 100644 (file)
@@ -6,11 +6,12 @@
 
 void undeclared_identifier (void)
 {
-  name; /* { dg-error "'name' undeclared" } */
+  name; /* { dg-error "'name' undeclared .first use in this function.; did you mean .nanl." } */
 /*
 { dg-begin-multiline-output "" }
    name;
    ^~~~
+   nanl
 { dg-end-multiline-output "" }
 */
 }
index 90e4b1b113fa6c05990c6e889679621a70d7d043..c2760e5da11edfb25444befd0794b49ff52c57dd 100644 (file)
@@ -8,12 +8,9 @@ enum E { A };
 void
 f (void)
 {
-  S s; /* { dg-error "unknown type name" } */
-/* { dg-message "use .struct. keyword to refer to the type" "" { target *-*-* } 11 } */
-  U u; /* { dg-error "unknown type name" } */
-/* { dg-message "use .union. keyword to refer to the type" "" { target *-*-* } 13 } */
-  E e; /* { dg-error "unknown type name" } */
-/* { dg-message "use .enum. keyword to refer to the type" "" { target *-*-* } 15 } */
+  S s; /* { dg-error "unknown type name .S.; use .struct. keyword to refer to the type" } */
+  U u; /* { dg-error "unknown type name .U.; use .union. keyword to refer to the type" } */
+  E e; /* { dg-error "unknown type name .E.; use .enum. keyword to refer to the type" } */
 }
 
 void
@@ -22,10 +19,7 @@ g (void)
   struct T { int i; };
   union V { int i; };
   enum F { J };
-  T t; /* { dg-error "unknown type name" } */
-/* { dg-message "use .struct. keyword to refer to the type" "" { target *-*-* } 25 } */
-  V v; /* { dg-error "unknown type name" } */
-/* { dg-message "use .union. keyword to refer to the type" "" { target *-*-* } 27 } */
-  F f; /* { dg-error "unknown type name" } */
-/* { dg-message "use .enum. keyword to refer to the type" "" { target *-*-* } 29 } */
+  T t; /* { dg-error "unknown type name .T.; use .struct. keyword to refer to the type" } */
+  V v; /* { dg-error "unknown type name .V.; use .union. keyword to refer to the type" } */
+  F f; /* { dg-error "unknown type name .F.; use .enum. keyword to refer to the type" } */
 }
diff --git a/gcc/testsuite/gcc.dg/spellcheck-identifiers.c b/gcc/testsuite/gcc.dg/spellcheck-identifiers.c
new file mode 100644 (file)
index 0000000..22a12d0
--- /dev/null
@@ -0,0 +1,136 @@
+/* { dg-do compile } */
+/* { dg-options "-Wimplicit-function-declaration -fdiagnostics-show-caret" } */
+
+typedef struct GtkWidget { int dummy; } GtkWidget;
+
+extern void gtk_widget_show_all (GtkWidget *w);
+
+void
+test_1 (GtkWidget *w)
+{
+  gtk_widget_showall (w); /* { dg-warning "3: implicit declaration of function .gtk_widget_showall.; did you mean .gtk_widget_show_all.?" } */
+  /* { dg-begin-multiline-output "" }
+   gtk_widget_showall (w);
+   ^~~~~~~~~~~~~~~~~~
+   gtk_widget_show_all
+   { dg-end-multiline-output "" } */
+
+  /* Ensure we don't try to suggest "gtk_widget_showall" for subsequent
+     corrections.  */
+  gtk_widget_showall_ (w); /* { dg-warning "3: implicit declaration of function .gtk_widget_showall_.; did you mean .gtk_widget_show_all.?" } */
+  /* { dg-begin-multiline-output "" }
+   gtk_widget_showall_ (w);
+   ^~~~~~~~~~~~~~~~~~~
+   gtk_widget_show_all
+   { dg-end-multiline-output "" } */
+
+  GtkWidgetShowAll (w); /* { dg-warning "3: implicit declaration of function .GtkWidgetShowAll.; did you mean .gtk_widget_show_all.?" } */
+  /* { dg-begin-multiline-output "" }
+   GtkWidgetShowAll (w);
+   ^~~~~~~~~~~~~~~~
+   gtk_widget_show_all
+   { dg-end-multiline-output "" } */
+}
+
+int
+test_2 (int param)
+{
+  return parma * parma; /* { dg-error "10: .parma. undeclared .first use in this function.; did you mean .param." } */
+  /* { dg-begin-multiline-output "" }
+   return parma * parma;
+          ^~~~~
+          param
+   { dg-end-multiline-output "" } */
+}
+
+#define MACRO(X) ((X))
+
+int
+test_3 (int i)
+{
+  return MACRAME (i); /* { dg-warning "10: implicit declaration of function .MACRAME.; did you mean .MACRO.?" } */
+  /* { dg-begin-multiline-output "" }
+   return MACRAME (i);
+          ^~~~~~~
+          MACRO
+   { dg-end-multiline-output "" } */
+}
+
+#define IDENTIFIER_POINTER(X) ((X))
+
+int
+test_4 (int node)
+{
+  return IDENTIFIER_PTR (node); /* { dg-warning "10: implicit declaration of function .IDENTIFIER_PTR.; did you mean .IDENTIFIER_POINTER.?" } */
+  /* { dg-begin-multiline-output "" }
+   return IDENTIFIER_PTR (node);
+          ^~~~~~~~~~~~~~
+          IDENTIFIER_POINTER
+   { dg-end-multiline-output "" } */
+}
+
+
+int
+test_5 (void)
+{
+  return __LINE_; /* { dg-error "10: .__LINE_. undeclared .first use in this function.; did you mean .__LINE__." } */
+  /* { dg-begin-multiline-output "" }
+   return __LINE_;
+          ^~~~~~~
+          __LINE__
+   { dg-end-multiline-output "" } */
+}
+
+#define MAX_ITEMS 100
+int array[MAX_ITEM]; /* { dg-error "11: .MAX_ITEM. undeclared here .not in a function.; did you mean .MAX_ITEMS." } */
+  /* { dg-begin-multiline-output "" }
+ int array[MAX_ITEM];
+           ^~~~~~~~
+           MAX_ITEMS
+   { dg-end-multiline-output "" } */
+
+
+enum foo {
+  FOO_FIRST,
+  FOO_SECOND
+};
+
+int
+test_6 (enum foo f)
+{
+  switch (f)
+    {
+    case FOO_FURST: /* { dg-error "10: .FOO_FURST. undeclared .first use in this function.; did you mean .FOO_FIRST." } */
+      break;
+  /* { dg-begin-multiline-output "" }
+     case FOO_FURST:
+          ^~~~~~~~~
+          FOO_FIRST
+   { dg-end-multiline-output "" } */
+
+    case FOO_SECCOND: /* { dg-error "10: .FOO_SECCOND. undeclared .first use in this function.; did you mean .FOO_SECOND." } */
+      break;
+  /* { dg-begin-multiline-output "" }
+     case FOO_SECCOND:
+          ^~~~~~~~~~~
+          FOO_SECOND
+   { dg-end-multiline-output "" } */
+
+    default:
+      break;
+    }
+}
+
+/* Verify that we offer names of builtins as suggestions.  */
+
+void
+test_7 (int i, int j)
+{
+  int buffer[100];
+  snprint (buffer, 100, "%i of %i", i, j); /* { dg-warning "3: implicit declaration of function .snprint.; did you mean .snprintf.." } */
+  /* { dg-begin-multiline-output "" }
+   snprint (buffer, 100, "%i of %i", i, j);
+   ^~~~~~~
+   snprintf
+   { dg-end-multiline-output "" } */
+}
diff --git a/gcc/testsuite/gcc.dg/spellcheck-typenames.c b/gcc/testsuite/gcc.dg/spellcheck-typenames.c
new file mode 100644 (file)
index 0000000..ae22ce3
--- /dev/null
@@ -0,0 +1,107 @@
+/* { dg-do compile } */
+/* { dg-options "-fdiagnostics-show-caret" } */
+
+void test_1 (signed char e);
+
+/* PR c/70339.  */
+void test_2 (singed char e); /* { dg-error "14: unknown type name .singed.; did you mean .signed.?" } */
+/* { dg-begin-multiline-output "" }
+ void test_2 (singed char e);
+              ^~~~~~
+              signed
+   { dg-end-multiline-output "" } */
+
+void test_3 (car e); /* { dg-error "14: unknown type name .car.; did you mean .char.?" } */
+/* { dg-begin-multiline-output "" }
+ void test_3 (car e);
+              ^~~
+              char
+   { dg-end-multiline-output "" } */
+
+/* TODO: this one could be handled better.  */
+void test_4 (signed car e); /* { dg-error "25: before .e." } */
+/* { dg-begin-multiline-output "" }
+ void test_4 (signed car e);
+                         ^
+   { dg-end-multiline-output "" } */
+
+/* Verify that we handle misspelled typedef names.  */
+
+typedef struct something {} something_t;
+
+some_thing_t test_5; /* { dg-error "1: unknown type name .some_thing_t.; did you mean .something_t.?" } */
+  /* { dg-begin-multiline-output "" }
+ some_thing_t test_5;
+ ^~~~~~~~~~~~
+ something_t
+   { dg-end-multiline-output "" } */
+
+/* TODO: we don't yet handle misspelled struct names.  */
+struct some_thing test_6; /* { dg-error "storage size of .test_6. isn't known" } */
+  /* { dg-begin-multiline-output "" }
+ struct some_thing test_6;
+                   ^~~~~~
+   { dg-end-multiline-output "" } */
+
+typedef long int64_t;
+int64 i; /* { dg-error "unknown type name 'int64'; did you mean 'int64_t'?" } */
+/* { dg-begin-multiline-output "" }
+ int64 i;
+ ^~~~~
+ int64_t
+   { dg-end-multiline-output "" } */
+
+/* Verify that gcc doesn't offer nonsensical suggestions.  */
+
+nonsensical_suggestion_t var; /* { dg-bogus "did you mean" } */
+/* { dg-error "unknown type name" "" { target { *-*-* } } 56 } */
+/* { dg-begin-multiline-output "" }
+ nonsensical_suggestion_t var;
+ ^~~~~~~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+
+/* In the following, we should suggest inserting "struct" (rather
+   than "did you mean 'float'") and provide a fixit hint.  */
+struct foo_t {
+  int i;
+};
+foo_t *foo_ptr; /* { dg-error "1: unknown type name .foo_t.; use .struct. keyword to refer to the type" } */
+/* { dg-begin-multiline-output "" }
+ foo_t *foo_ptr;
+ ^~~~~
+ struct
+   { dg-end-multiline-output "" } */
+
+
+/* Similarly for unions.  */
+union bar_t {
+  int i;
+  char j;
+};
+bar_t *bar_ptr; /* { dg-error "1: unknown type name .bar_t.; use .union. keyword to refer to the type" } */
+/* { dg-begin-multiline-output "" }
+ bar_t *bar_ptr;
+ ^~~~~
+ union
+   { dg-end-multiline-output "" } */
+
+
+/* Similarly for enums.  */
+enum baz {
+  BAZ_FIRST,
+  BAZ_SECOND
+};
+baz value; /* { dg-error "1: unknown type name .baz.; use .enum. keyword to refer to the type" } */
+/* { dg-begin-multiline-output "" }
+ baz value;
+ ^~~
+ enum
+   { dg-end-multiline-output "" } */
+
+/* TODO: it would be better to detect the "singed" vs "signed" typo here.  */
+singed char ch; /* { dg-error "8: before .char." } */
+/* { dg-begin-multiline-output "" }
+ singed char ch;
+        ^~~~
+   { dg-end-multiline-output "" } */