diff options
author | David Malcolm <dmalcolm@redhat.com> | 2016-06-22 15:20:41 +0000 |
---|---|---|
committer | David Malcolm <dmalcolm@gcc.gnu.org> | 2016-06-22 15:20:41 +0000 |
commit | 1a4f11c88ae761d4c618e540e07e4e32e85850d1 (patch) | |
tree | 6249c6a772dc9140476eee29b52cfe04dbe0e29d /gcc/c/c-parser.c | |
parent | 6f99ef82f1457d2f71121853ef2f006d0800bd19 (diff) | |
download | gcc-1a4f11c88ae761d4c618e540e07e4e32e85850d1.zip gcc-1a4f11c88ae761d4c618e540e07e4e32e85850d1.tar.gz gcc-1a4f11c88ae761d4c618e540e07e4e32e85850d1.tar.bz2 |
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.
From-SVN: r237714
Diffstat (limited to 'gcc/c/c-parser.c')
-rw-r--r-- | gcc/c/c-parser.c | 144 |
1 files changed, 99 insertions, 45 deletions
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c index 78bf68e..7f491f1 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 |