diff options
author | David Malcolm <dmalcolm@redhat.com> | 2016-07-20 18:42:11 +0000 |
---|---|---|
committer | David Malcolm <dmalcolm@gcc.gnu.org> | 2016-07-20 18:42:11 +0000 |
commit | 52ed68f71a750260fd926479e41e3f7ad7cd9aa2 (patch) | |
tree | b49c604effb64ec60ec0f92309badfd3781fc6c4 /gcc/cp | |
parent | 1397e163014843fa6803b3959adfc0011d75bc6a (diff) | |
download | gcc-52ed68f71a750260fd926479e41e3f7ad7cd9aa2.zip gcc-52ed68f71a750260fd926479e41e3f7ad7cd9aa2.tar.gz gcc-52ed68f71a750260fd926479e41e3f7ad7cd9aa2.tar.bz2 |
C++ FE: handle misspelled identifiers and typenames
gcc/cp/ChangeLog:
PR c/70339
PR c/71858
* name-lookup.c: Include gcc-rich-location.h, spellcheck-tree.h,
and parser.h.
(suggest_alternatives_for): If no candidates are found, try
lookup_name_fuzzy and report if if finds a suggestion.
(consider_binding_level): New function.
(lookup_name_fuzzy) New function.
* parser.c: Include gcc-rich-location.h.
(cp_lexer_next_token_is_decl_specifier_keyword): Move most of
logic into...
(cp_keyword_starts_decl_specifier_p): ...this new function.
(cp_parser_diagnose_invalid_type_name): When issuing
"does not name a type" errors, attempt to make a suggestion using
lookup_name_fuzzy.
* parser.h (cp_keyword_starts_decl_specifier_p): New prototype.
* search.c (lookup_field_fuzzy_info::fuzzy_lookup_field): Reject
types that are not CLASS_TYPE_P, rather than rejecting individual
tree codes.
gcc/testsuite/ChangeLog:
PR c/70339
PR c/71858
* g++.dg/spellcheck-identifiers.C: New test case, based on
gcc.dg/spellcheck-identifiers.c.
* g++.dg/spellcheck-identifiers-2.C: New test case, based on
gcc.dg/spellcheck-identifiers-2.c.
* g++.dg/spellcheck-typenames.C: New test case, based on
gcc.dg/spellcheck-typenames.c
From-SVN: r238538
Diffstat (limited to 'gcc/cp')
-rw-r--r-- | gcc/cp/ChangeLog | 22 | ||||
-rw-r--r-- | gcc/cp/name-lookup.c | 116 | ||||
-rw-r--r-- | gcc/cp/parser.c | 43 | ||||
-rw-r--r-- | gcc/cp/parser.h | 1 | ||||
-rw-r--r-- | gcc/cp/search.c | 8 |
5 files changed, 170 insertions, 20 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index cc5df6e..ab0446b 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,25 @@ +2016-07-20 David Malcolm <dmalcolm@redhat.com> + + PR c/70339 + PR c/71858 + * name-lookup.c: Include gcc-rich-location.h, spellcheck-tree.h, + and parser.h. + (suggest_alternatives_for): If no candidates are found, try + lookup_name_fuzzy and report if if finds a suggestion. + (consider_binding_level): New function. + (lookup_name_fuzzy) New function. + * parser.c: Include gcc-rich-location.h. + (cp_lexer_next_token_is_decl_specifier_keyword): Move most of + logic into... + (cp_keyword_starts_decl_specifier_p): ...this new function. + (cp_parser_diagnose_invalid_type_name): When issuing + "does not name a type" errors, attempt to make a suggestion using + lookup_name_fuzzy. + * parser.h (cp_keyword_starts_decl_specifier_p): New prototype. + * search.c (lookup_field_fuzzy_info::fuzzy_lookup_field): Reject + types that are not CLASS_TYPE_P, rather than rejecting individual + tree codes. + 2016-07-20 Jakub Jelinek <jakub@redhat.com> PR c++/71909 diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c index cbd5209..7c3942a52 100644 --- a/gcc/cp/name-lookup.c +++ b/gcc/cp/name-lookup.c @@ -29,6 +29,9 @@ along with GCC; see the file COPYING3. If not see #include "debug.h" #include "c-family/c-pragma.h" #include "params.h" +#include "gcc-rich-location.h" +#include "spellcheck-tree.h" +#include "parser.h" /* The bindings for a particular name in a particular scope. */ @@ -4435,9 +4438,20 @@ suggest_alternatives_for (location_t location, tree name) namespaces_to_search.release (); - /* Nothing useful to report. */ + /* Nothing useful to report for NAME. Report on likely misspellings, + or do nothing. */ if (candidates.is_empty ()) - return; + { + const char *fuzzy_name = lookup_name_fuzzy (name, FUZZY_LOOKUP_NAME); + if (fuzzy_name) + { + gcc_rich_location richloc (location); + richloc.add_fixit_misspelled_id (location, fuzzy_name); + inform_at_rich_loc (&richloc, "suggested alternative: %qs", + fuzzy_name); + } + return; + } inform_n (location, candidates.length (), "suggested alternative:", @@ -4672,6 +4686,104 @@ qualified_lookup_using_namespace (tree name, tree scope, return result->value != error_mark_node; } +/* Helper function for lookup_name_fuzzy. + Traverse binding level LVL, looking for good name matches for NAME + (and BM). */ +static void +consider_binding_level (tree name, best_match <tree, tree> &bm, + cp_binding_level *lvl, bool look_within_fields, + enum lookup_name_fuzzy_kind kind) +{ + if (look_within_fields) + if (lvl->this_entity && TREE_CODE (lvl->this_entity) == RECORD_TYPE) + { + tree type = lvl->this_entity; + bool want_type_p = (kind == FUZZY_LOOKUP_TYPENAME); + tree best_matching_field + = lookup_member_fuzzy (type, name, want_type_p); + if (best_matching_field) + bm.consider (best_matching_field); + } + + for (tree t = lvl->names; t; t = TREE_CHAIN (t)) + { + /* Don't use bindings from implicitly declared functions, + as they were likely misspellings themselves. */ + if (TREE_TYPE (t) == error_mark_node) + continue; + + /* Skip anticipated decls of builtin functions. */ + if (TREE_CODE (t) == FUNCTION_DECL + && DECL_BUILT_IN (t) + && DECL_ANTICIPATED (t)) + continue; + + if (DECL_NAME (t)) + bm.consider (DECL_NAME (t)); + } +} + +/* Search for near-matches for NAME within the current bindings, and within + macro names, returning the best match as a const char *, or NULL if + no reasonable match is found. */ + +const char * +lookup_name_fuzzy (tree name, enum lookup_name_fuzzy_kind kind) +{ + gcc_assert (TREE_CODE (name) == IDENTIFIER_NODE); + + best_match <tree, tree> bm (name); + + cp_binding_level *lvl; + for (lvl = scope_chain->class_bindings; lvl; lvl = lvl->level_chain) + consider_binding_level (name, bm, lvl, true, kind); + + for (lvl = current_binding_level; lvl; lvl = lvl->level_chain) + consider_binding_level (name, bm, lvl, false, kind); + + /* 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. */ + best_macro_match bmm (name, bm.get_best_distance (), parse_in); + cpp_hashnode *best_macro = bmm.get_best_meaningful_candidate (); + /* If a macro is the closest so far to NAME, suggest it. */ + if (best_macro) + return (const char *)best_macro->ident.str; + + /* Try the "starts_decl_specifier_p" keywords to detect + "singed" vs "signed" typos. */ + for (unsigned i = 0; i < num_c_common_reswords; i++) + { + const c_common_resword *resword = &c_common_reswords[i]; + + if (!cp_keyword_starts_decl_specifier_p (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); + } + + /* See if we have a good suggesion for the user. */ + tree best_id = bm.get_best_meaningful_candidate (); + if (best_id) + return IDENTIFIER_POINTER (best_id); + + /* No meaningful suggestion available. */ + return NULL; +} + /* Subroutine of outer_binding. Returns TRUE if BINDING is a binding to a template parameter of diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 84dad48..8fceaed 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -40,6 +40,7 @@ along with GCC; see the file COPYING3. If not see #include "c-family/c-indentation.h" #include "context.h" #include "cp-cilkplus.h" +#include "gcc-rich-location.h" /* The lexer. */ @@ -937,15 +938,12 @@ cp_lexer_next_token_is_not_keyword (cp_lexer* lexer, enum rid keyword) return cp_lexer_peek_token (lexer)->keyword != keyword; } -/* Return true if the next token is a keyword for a decl-specifier. */ +/* Return true if KEYWORD can start a decl-specifier. */ -static bool -cp_lexer_next_token_is_decl_specifier_keyword (cp_lexer *lexer) +bool +cp_keyword_starts_decl_specifier_p (enum rid keyword) { - cp_token *token; - - token = cp_lexer_peek_token (lexer); - switch (token->keyword) + switch (keyword) { /* auto specifier: storage-class-specifier in C++, simple-type-specifier in C++0x. */ @@ -985,14 +983,25 @@ cp_lexer_next_token_is_decl_specifier_keyword (cp_lexer *lexer) 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]) + 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 the next token is a keyword for a decl-specifier. */ + +static bool +cp_lexer_next_token_is_decl_specifier_keyword (cp_lexer *lexer) +{ + cp_token *token; + + token = cp_lexer_peek_token (lexer); + return cp_keyword_starts_decl_specifier_p (token->keyword); +} + /* Returns TRUE iff the token T begins a decltype type. */ static bool @@ -3154,7 +3163,19 @@ cp_parser_diagnose_invalid_type_name (cp_parser *parser, tree id, else if (!parser->scope) { /* Issue an error message. */ - error_at (location, "%qE does not name a type", id); + const char *suggestion = NULL; + if (TREE_CODE (id) == IDENTIFIER_NODE) + suggestion = lookup_name_fuzzy (id, FUZZY_LOOKUP_TYPENAME); + if (suggestion) + { + gcc_rich_location richloc (location); + richloc.add_fixit_misspelled_id (location, suggestion); + error_at_rich_loc (&richloc, + "%qE does not name a type; did you mean %qs?", + id, suggestion); + } + else + error_at (location, "%qE does not name a type", id); /* If we're in a template class, it's possible that the user was referring to a type from a base class. For example: diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h index ccbace9..2923378 100644 --- a/gcc/cp/parser.h +++ b/gcc/cp/parser.h @@ -420,5 +420,6 @@ extern void debug (vec<cp_token, va_gc> *ptr); extern void cp_debug_parser (FILE *, cp_parser *); extern void debug (cp_parser &ref); extern void debug (cp_parser *ptr); +extern bool cp_keyword_starts_decl_specifier_p (enum rid keyword); #endif /* GCC_CP_PARSER_H */ diff --git a/gcc/cp/search.c b/gcc/cp/search.c index 990c3fe..8b5f329 100644 --- a/gcc/cp/search.c +++ b/gcc/cp/search.c @@ -1398,13 +1398,7 @@ lookup_field_fuzzy_info::fuzzy_lookup_fnfields (tree type) void lookup_field_fuzzy_info::fuzzy_lookup_field (tree type) { - if (TREE_CODE (type) == TEMPLATE_TYPE_PARM - || TREE_CODE (type) == BOUND_TEMPLATE_TEMPLATE_PARM - || TREE_CODE (type) == TYPENAME_TYPE) - /* The TYPE_FIELDS of a TEMPLATE_TYPE_PARM and - BOUND_TEMPLATE_TEMPLATE_PARM are not fields at all; - instead TYPE_FIELDS is the TEMPLATE_PARM_INDEX. - The TYPE_FIELDS of TYPENAME_TYPE is its TYPENAME_TYPE_FULLNAME. */ + if (!CLASS_TYPE_P (type)) return; for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) |