aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp
diff options
context:
space:
mode:
authorDavid Malcolm <dmalcolm@redhat.com>2016-07-20 18:42:11 +0000
committerDavid Malcolm <dmalcolm@gcc.gnu.org>2016-07-20 18:42:11 +0000
commit52ed68f71a750260fd926479e41e3f7ad7cd9aa2 (patch)
treeb49c604effb64ec60ec0f92309badfd3781fc6c4 /gcc/cp
parent1397e163014843fa6803b3959adfc0011d75bc6a (diff)
downloadgcc-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/ChangeLog22
-rw-r--r--gcc/cp/name-lookup.c116
-rw-r--r--gcc/cp/parser.c43
-rw-r--r--gcc/cp/parser.h1
-rw-r--r--gcc/cp/search.c8
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))