summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordmalcolm <dmalcolm@138bc75d-0d04-0410-961f-82ee72b054a4>2016-06-22 15:20:41 +0000
committerdmalcolm <dmalcolm@138bc75d-0d04-0410-961f-82ee72b054a4>2016-06-22 15:20:41 +0000
commit8469aece13814deddf2cd80538d33c2d0a8d60d9 (patch)
tree6249c6a772dc9140476eee29b52cfe04dbe0e29d
parent79280f4ff71a676f2c758e4f74c81ee1ae69cf8d (diff)
downloadgcc-8469aece13814deddf2cd80538d33c2d0a8d60d9.tar.gz
C FE: suggest corrections for misspelled identifiers and type names
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. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@237714 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r--gcc/ChangeLog11
-rw-r--r--gcc/c-family/ChangeLog6
-rw-r--r--gcc/c-family/c-common.h9
-rw-r--r--gcc/c/ChangeLog26
-rw-r--r--gcc/c/c-decl.c190
-rw-r--r--gcc/c/c-parser.c144
-rw-r--r--gcc/c/c-tree.h1
-rw-r--r--gcc/diagnostic-core.h2
-rw-r--r--gcc/diagnostic.c12
-rw-r--r--gcc/spellcheck.h22
-rw-r--r--gcc/testsuite/ChangeLog12
-rw-r--r--gcc/testsuite/c-c++-common/attributes-1.c2
-rw-r--r--gcc/testsuite/gcc.dg/diagnostic-token-ranges.c3
-rw-r--r--gcc/testsuite/gcc.dg/pr67580.c18
-rw-r--r--gcc/testsuite/gcc.dg/spellcheck-identifiers.c136
-rw-r--r--gcc/testsuite/gcc.dg/spellcheck-typenames.c107
16 files changed, 634 insertions, 67 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index b30caadbfb7..0ae2b0338fb 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -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
diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog
index 7d2ca462223..cd463154da6 100644
--- a/gcc/c-family/ChangeLog
+++ b/gcc/c-family/ChangeLog
@@ -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.
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 4e6aa0051fa..3ad5400458e 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -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);
diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog
index 35b3de4a365..47d1f571f98 100644
--- a/gcc/c/ChangeLog
+++ b/gcc/c/ChangeLog
@@ -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
diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index 5c08c5947c0..8b966fecc29 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -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 ();
+}
+
/* Create the predefined scalar types of C,
and some nodes representing standard constants (0, 1, (void *) 0).
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 78bf68e677e..7f491f1dc06 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -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
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index 8f10a13f763..46be53ece21 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -482,6 +482,7 @@ enum c_inline_static_type {
/* 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);
diff --git a/gcc/diagnostic-core.h b/gcc/diagnostic-core.h
index f7837613933..51df15028d4 100644
--- a/gcc/diagnostic-core.h
+++ b/gcc/diagnostic-core.h
@@ -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);
diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c
index d39afff6622..bb41011afad 100644
--- a/gcc/diagnostic.c
+++ b/gcc/diagnostic.c
@@ -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
diff --git a/gcc/spellcheck.h b/gcc/spellcheck.h
index 7379399f1fd..035f4ac3608 100644
--- a/gcc/spellcheck.h
+++ b/gcc/spellcheck.h
@@ -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;
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 8cac69489d6..2082fff71c9 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,5 +1,17 @@
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
file.
* gcc.dg/plugin/plugin.exp (plugin_test_list): Add
diff --git a/gcc/testsuite/c-c++-common/attributes-1.c b/gcc/testsuite/c-c++-common/attributes-1.c
index 1657da10d5e..c348526b0d4 100644
--- a/gcc/testsuite/c-c++-common/attributes-1.c
+++ b/gcc/testsuite/c-c++-common/attributes-1.c
@@ -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" } */
diff --git a/gcc/testsuite/gcc.dg/diagnostic-token-ranges.c b/gcc/testsuite/gcc.dg/diagnostic-token-ranges.c
index ac969e30d94..19399490c04 100644
--- a/gcc/testsuite/gcc.dg/diagnostic-token-ranges.c
+++ b/gcc/testsuite/gcc.dg/diagnostic-token-ranges.c
@@ -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 "" }
*/
}
diff --git a/gcc/testsuite/gcc.dg/pr67580.c b/gcc/testsuite/gcc.dg/pr67580.c
index 90e4b1b113f..c2760e5da11 100644
--- a/gcc/testsuite/gcc.dg/pr67580.c
+++ b/gcc/testsuite/gcc.dg/pr67580.c
@@ -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
index 00000000000..22a12d0fa1c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/spellcheck-identifiers.c
@@ -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
index 00000000000..ae22ce318ae
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/spellcheck-typenames.c
@@ -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 "" } */