From 1a4f11c88ae761d4c618e540e07e4e32e85850d1 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Wed, 22 Jun 2016 15:20:41 +0000 Subject: [PATCH] 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): 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 --- gcc/ChangeLog | 11 + gcc/c-family/ChangeLog | 6 + gcc/c-family/c-common.h | 9 + gcc/c/ChangeLog | 26 +++ gcc/c/c-decl.c | 190 +++++++++++++++++- gcc/c/c-parser.c | 144 ++++++++----- gcc/c/c-tree.h | 1 + gcc/diagnostic-core.h | 2 + gcc/diagnostic.c | 12 ++ gcc/spellcheck.h | 22 +- gcc/testsuite/ChangeLog | 12 ++ gcc/testsuite/c-c++-common/attributes-1.c | 2 +- .../gcc.dg/diagnostic-token-ranges.c | 3 +- gcc/testsuite/gcc.dg/pr67580.c | 18 +- gcc/testsuite/gcc.dg/spellcheck-identifiers.c | 136 +++++++++++++ gcc/testsuite/gcc.dg/spellcheck-typenames.c | 107 ++++++++++ 16 files changed, 634 insertions(+), 67 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/spellcheck-identifiers.c create mode 100644 gcc/testsuite/gcc.dg/spellcheck-typenames.c 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 + + 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 * 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 + + PR c/70339 + * c-common.h (enum lookup_name_fuzzy_kind): New enum. + (lookup_name_fuzzy): New prototype. + 2016-06-21 John David Anglin * 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 + + 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): 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 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 +{ + 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 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 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 % 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 % keyword to refer to the type", + name); + } else if (tag_exists_p (UNION_TYPE, name)) - inform (here, "use % keyword to refer to the type"); + { + richloc.add_fixit_insert (here, "union"); + error_at_rich_loc (&richloc, + "unknown type name %qE;" + " use % keyword to refer to the type", + name); + } else if (tag_exists_p (ENUMERAL_TYPE, name)) - inform (here, "use % keyword to refer to the type"); + { + richloc.add_fixit_insert (here, "enum"); + error_at_rich_loc (&richloc, + "unknown type name %qE;" + " use % 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,3 +1,15 @@ +2016-06-22 David Malcolm + + 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 * gcc.dg/plugin/diagnostic-test-show-locus-parseable-fixits.c: New 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 "" } */ -- 2.30.2