* [PATCH 1/2] C: convert return type of lookup_name_fuzzy from tree to const char *
@ 2016-06-30 18:27 David Malcolm
2016-06-30 18:43 ` [PATCH 2/2] C++ FE: handle misspelled identifiers and typenames David Malcolm
2016-07-13 22:09 ` [PATCH 1/2] C: convert return type of lookup_name_fuzzy from tree to const char * Jeff Law
0 siblings, 2 replies; 9+ messages in thread
From: David Malcolm @ 2016-06-30 18:27 UTC (permalink / raw)
To: gcc-patches; +Cc: David Malcolm
The followup patch implements lookup_name_fuzzy for the C++ frontend.
It's cleaner for that implementation to return a const char *, so this
patch updates the implementation in the C frontend to have the same
return type.
Successfully bootstrapped®rtested on x86_64-pc-linux-gnu
(in combination with the followup patch).
OK for trunk?
gcc/c-family/ChangeLog:
* c-common.h (lookup_name_fuzzy): Convert return type from tree to
const char *.
gcc/c/ChangeLog:
* c-decl.c (implicit_decl_warning): Update for conversion of
return type of lookup_name_fuzzy to const char *.
(undeclared_variable): Likewise.
(lookup_name_fuzzy): Convert return type from tree to
const char *.
* c-parser.c (c_parser_declaration_or_fndef): Update for
conversion of return type of lookup_name_fuzzy to const char *.
(c_parser_parameter_declaration): Likewise.
gcc/ChangeLog:
* gcc-rich-location.c
(gcc_rich_location::add_fixit_misspelled_id): New overload, taking
a const char *.
* gcc-rich-location.h
(gcc_rich_location::add_fixit_misspelled_id): Likewise.
---
gcc/c-family/c-common.h | 2 +-
gcc/c/c-decl.c | 22 +++++++++++++---------
gcc/c/c-parser.c | 10 +++++-----
gcc/gcc-rich-location.c | 13 +++++++++++++
gcc/gcc-rich-location.h | 2 ++
5 files changed, 34 insertions(+), 15 deletions(-)
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 3ad5400..44d98d1 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -997,7 +997,7 @@ enum lookup_name_fuzzy_kind {
/* Any name. */
FUZZY_LOOKUP_NAME
};
-extern tree lookup_name_fuzzy (tree, enum lookup_name_fuzzy_kind);
+extern const char *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);
diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index 8b966fe..09f7f79 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -3088,7 +3088,7 @@ implicit_decl_warning (location_t loc, tree id, tree olddecl)
if (warn_implicit_function_declaration)
{
bool warned;
- tree hint = NULL_TREE;
+ const char *hint = NULL;
if (!olddecl)
hint = lookup_name_fuzzy (id, FUZZY_LOOKUP_NAME);
@@ -3099,7 +3099,7 @@ implicit_decl_warning (location_t loc, tree id, tree olddecl)
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?",
+ "implicit declaration of function %qE; did you mean %qs?",
id, hint);
}
else
@@ -3112,7 +3112,7 @@ implicit_decl_warning (location_t loc, tree id, tree olddecl)
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?"),
+ G_("implicit declaration of function %qE;did you mean %qs?"),
id, hint);
}
else
@@ -3433,14 +3433,14 @@ undeclared_variable (location_t loc, tree id)
if (current_function_decl == 0)
{
- tree guessed_id = lookup_name_fuzzy (id, FUZZY_LOOKUP_NAME);
+ const char *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?",
+ " did you mean %qs?",
id, guessed_id);
}
else
@@ -3451,7 +3451,7 @@ undeclared_variable (location_t loc, tree id)
{
if (!objc_diagnose_private_ivar (id))
{
- tree guessed_id = lookup_name_fuzzy (id, FUZZY_LOOKUP_NAME);
+ const char *guessed_id = lookup_name_fuzzy (id, FUZZY_LOOKUP_NAME);
if (guessed_id)
{
gcc_rich_location richloc (loc);
@@ -3459,7 +3459,7 @@ undeclared_variable (location_t loc, tree id)
error_at_rich_loc
(&richloc,
"%qE undeclared (first use in this function);"
- " did you mean %qE?",
+ " did you mean %qs?",
id, guessed_id);
}
else
@@ -4010,7 +4010,7 @@ find_closest_macro_cpp_cb (cpp_reader *, cpp_hashnode *hashnode,
It also looks for start_typename keywords, to detect "singed" vs "signed"
typos. */
-tree
+const char *
lookup_name_fuzzy (tree name, enum lookup_name_fuzzy_kind kind)
{
gcc_assert (TREE_CODE (name) == IDENTIFIER_NODE);
@@ -4079,7 +4079,11 @@ lookup_name_fuzzy (tree name, enum lookup_name_fuzzy_kind kind)
}
}
- return bm.get_best_meaningful_candidate ();
+ tree best = bm.get_best_meaningful_candidate ();
+ if (best)
+ return IDENTIFIER_POINTER (best);
+ else
+ return NULL;
}
\f
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 7f491f1..0451120 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -1695,12 +1695,12 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
}
else
{
- tree hint = lookup_name_fuzzy (name, FUZZY_LOOKUP_TYPENAME);
+ const char *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?",
+ "unknown type name %qE; did you mean %qs?",
name, hint);
}
else
@@ -3850,14 +3850,14 @@ 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))
{
- tree hint = lookup_name_fuzzy (token->value, FUZZY_LOOKUP_TYPENAME);
+ const char *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?",
+ "unknown type name %qE; did you mean %qs?",
token->value, hint);
}
else
diff --git a/gcc/gcc-rich-location.c b/gcc/gcc-rich-location.c
index 15c0700..09475ff 100644
--- a/gcc/gcc-rich-location.c
+++ b/gcc/gcc-rich-location.c
@@ -74,3 +74,16 @@ gcc_rich_location::add_fixit_misspelled_id (location_t misspelled_token_loc,
= get_range_from_loc (line_table, misspelled_token_loc);
add_fixit_replace (misspelled_token_range, IDENTIFIER_POINTER (hint_id));
}
+
+/* As above, but with a const char * for the suggested replacement. */
+
+void
+gcc_rich_location::add_fixit_misspelled_id (location_t misspelled_token_loc,
+ const char *hint)
+{
+ gcc_assert (hint);
+
+ source_range misspelled_token_range
+ = get_range_from_loc (line_table, misspelled_token_loc);
+ add_fixit_replace (misspelled_token_range, hint);
+}
diff --git a/gcc/gcc-rich-location.h b/gcc/gcc-rich-location.h
index 9c8a790..aa69b2e 100644
--- a/gcc/gcc-rich-location.h
+++ b/gcc/gcc-rich-location.h
@@ -45,6 +45,8 @@ class gcc_rich_location : public rich_location
void add_fixit_misspelled_id (location_t misspelled_token_loc,
tree hint_id);
+ void add_fixit_misspelled_id (location_t misspelled_token_loc,
+ const char *hint);
};
#endif /* GCC_RICH_LOCATION_H */
--
1.8.5.3
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 2/2] C++ FE: handle misspelled identifiers and typenames
2016-06-30 18:27 [PATCH 1/2] C: convert return type of lookup_name_fuzzy from tree to const char * David Malcolm
@ 2016-06-30 18:43 ` David Malcolm
2016-07-02 12:01 ` Manuel López-Ibáñez
2016-07-13 22:12 ` Jeff Law
2016-07-13 22:09 ` [PATCH 1/2] C: convert return type of lookup_name_fuzzy from tree to const char * Jeff Law
1 sibling, 2 replies; 9+ messages in thread
From: David Malcolm @ 2016-06-30 18:43 UTC (permalink / raw)
To: gcc-patches; +Cc: David Malcolm
This is a port of the C frontend's r237714 [1] to the C++ frontend:
https://gcc.gnu.org/ml/gcc-patches/2016-06/msg01052.html
offering spelling suggestions for misspelled identifiers, macro names,
and some keywords (e.g. "singed" vs "signed" aka PR c/70339).
Unlike the C frontend, there doesn't seem to be an easy way to
distinguish between cases where we're expecting a typename vs
a variable name, so some of the logic is a little different.
Examples of suggestions can be seen in the test case.
Successfully bootstrapped®rtested on x86_64-pc-linux-gnu
(in combination with the prior patch); adds 240 PASS results to
g++.sum.
OK for trunk?
Dave
[1] aka 8469aece13814deddf2cd80538d33c2d0a8d60d9 in the git mirror
gcc/c/ChangeLog:
PR c/70339
* c-decl.c (struct edit_distance_traits<cpp_hashnode *>): Move to
spellcheck-tree.h
(best_macro_match): Likewise, converting from a typedef to a
subclass.
(find_closest_macro_cpp_cb): Move to spellcheck-tree.c.
(lookup_name_fuzzy): Update for change of best_macro_match to a
subclass with a ctor that calls cpp_forall_identifiers.
gcc/cp/ChangeLog:
PR c/70339
* 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.
(consider_binding_levels): 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): Don't
attempt to access TYPE_FIELDS within a TYPE_PACK_EXPANSION.
gcc/ChangeLog:
PR c/70339
* diagnostic-show-locus.c (diagnostic_show_locus): If this is the
same location as last time, don't skip if we have fix-it hints.
Clarify the skipping logic by converting it from one "if" clause
to repeated "if" clauses.
* spellcheck-tree.c: Include "cpplib.h".
(find_closest_macro_cpp_cb): Move here from c/c-decl.c.
(best_macro_match::best_macro_match): New constructor.
* spellcheck-tree.h (struct edit_distance_traits<cpp_hashnode *>):
Move here from c/c-decl.c.
(class best_macro_match): Move here from c/c-decl.c, converting
from a typedef to a subclass, gaining a ctor.
gcc/testsuite/ChangeLog:
PR c/70339
* g++.dg/spellcheck-identifiers.C: New test case, based on
gcc.dg/spellcheck-identifiers.c.
* g++.dg/spellcheck-typenames.C: New test case, based on
gcc.dg/spellcheck-typenames.c
---
gcc/c/c-decl.c | 42 +----
gcc/cp/name-lookup.c | 140 +++++++++++++-
gcc/cp/parser.c | 43 +++--
gcc/cp/parser.h | 1 +
gcc/cp/search.c | 4 +
gcc/diagnostic-show-locus.c | 15 +-
gcc/spellcheck-tree.c | 31 ++++
gcc/spellcheck-tree.h | 26 +++
gcc/testsuite/g++.dg/spellcheck-identifiers.C | 255 ++++++++++++++++++++++++++
gcc/testsuite/g++.dg/spellcheck-typenames.C | 84 +++++++++
10 files changed, 584 insertions(+), 57 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/spellcheck-identifiers.C
create mode 100644 gcc/testsuite/g++.dg/spellcheck-typenames.C
diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index 09f7f79..dc14485 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -3955,45 +3955,6 @@ lookup_name_in_scope (tree name, struct c_scope *scope)
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.
@@ -4047,8 +4008,7 @@ lookup_name_fuzzy (tree name, enum lookup_name_fuzzy_kind kind)
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);
+ 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, use it, creating an
identifier tree node for it. */
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index cbd5209..88a5339 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,128 @@ qualified_lookup_using_namespace (tree name, tree scope,
return result->value != error_mark_node;
}
+/* Helper function for consider_binding_levels (and, in turn,
+ lookup_name_fuzzy).
+ Traverse binding level LVL, looking for good name matches for NAME
+ (and BM).
+ Skip builtin functions unless CONSIDER_BUILTIN_FUNCTIONS is set. */
+static void
+consider_binding_level (tree name, best_match <tree, tree> &bm,
+ cp_binding_level *lvl, bool look_within_fields,
+ bool consider_builtin_functions,
+ 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 builtin functions if we're not considering them this time. */
+ if (!consider_builtin_functions)
+ if (TREE_CODE (t) == FUNCTION_DECL)
+ if (DECL_BUILT_IN (t))
+ continue;
+
+ if (DECL_NAME (t))
+ bm.consider (DECL_NAME (t));
+ }
+}
+
+/* Helper function for lookup_name_fuzzy.
+ Traverse the binding levels, looking for good name matches for BM.
+ Skip builtin functions unless CONSIDER_BUILTIN_FUNCTIONS is set. */
+static void
+consider_binding_levels (tree name, best_match <tree, tree> &bm,
+ bool consider_builtin_functions,
+ enum lookup_name_fuzzy_kind kind)
+{
+ cp_binding_level *lvl;
+ for (lvl = scope_chain->class_bindings; lvl; lvl = lvl->level_chain)
+ consider_binding_level (name, bm, lvl, true, consider_builtin_functions,
+ kind);
+
+ for (lvl = current_binding_level; lvl; lvl = lvl->level_chain)
+ consider_binding_level (name, bm, lvl, false, consider_builtin_functions,
+ kind);
+}
+
+/* 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);
+
+ /* First pass: traverse the binding levels, ignoring builtin functions
+ for now. We do this to give everything else a higher priority:
+ there are many builtin functions with short names that otherwise
+ tend to show up as unhelpful suggestions (e.g. "carg" is a poor
+ suggestion for "arg" and "char"). */
+ consider_binding_levels (name, bm, 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);
+ }
+
+ /* Try the levels again, this time considering builtin functions. */
+ consider_binding_levels (name, bm, true, kind);
+
+ /* 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 f27c479..18dc9d4 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"
\f
/* 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..4a862b7 100644
--- a/gcc/cp/search.c
+++ b/gcc/cp/search.c
@@ -1407,6 +1407,10 @@ lookup_field_fuzzy_info::fuzzy_lookup_field (tree type)
The TYPE_FIELDS of TYPENAME_TYPE is its TYPENAME_TYPE_FULLNAME. */
return;
+ /* TYPE_FIELDS is not valid for a TYPE_PACK_EXPANSION. */
+ if (TREE_CODE (type) == TYPE_PACK_EXPANSION)
+ return;
+
for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
{
if (!m_want_type_p || DECL_DECLARES_TYPE_P (field))
diff --git a/gcc/diagnostic-show-locus.c b/gcc/diagnostic-show-locus.c
index 7aab658..49f7f11 100644
--- a/gcc/diagnostic-show-locus.c
+++ b/gcc/diagnostic-show-locus.c
@@ -1280,9 +1280,18 @@ diagnostic_show_locus (diagnostic_context * context,
{
pp_newline (context->printer);
- if (!context->show_caret
- || diagnostic_location (diagnostic, 0) <= BUILTINS_LOCATION
- || diagnostic_location (diagnostic, 0) == context->last_location)
+ /* Do nothing if source-printing has been disabled. */
+ if (!context->show_caret)
+ return;
+
+ /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
+ if (diagnostic_location (diagnostic, 0) <= BUILTINS_LOCATION)
+ return;
+
+ /* Don't print the same source location twice in a row, unless we have
+ fix-it hints. */
+ if (diagnostic_location (diagnostic, 0) == context->last_location
+ && diagnostic->richloc->get_num_fixit_hints () == 0)
return;
context->last_location = diagnostic_location (diagnostic, 0);
diff --git a/gcc/spellcheck-tree.c b/gcc/spellcheck-tree.c
index 63fb1a8..ef1e689 100644
--- a/gcc/spellcheck-tree.c
+++ b/gcc/spellcheck-tree.c
@@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
+#include "cpplib.h"
#include "spellcheck-tree.h"
#include "selftest.h"
#include "stringpool.h"
@@ -65,6 +66,36 @@ find_closest_identifier (tree target, const auto_vec<tree> *candidates)
return bm.get_best_meaningful_candidate ();
}
+/* A callback for cpp_forall_identifiers, for use by best_macro_match's ctor.
+ 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;
+}
+
+/* Constructor for best_macro_match.
+ Use find_closest_macro_cpp_cb to find the closest matching macro to
+ NAME within distance < best_distance_so_far. */
+
+best_macro_match::best_macro_match (tree goal,
+ edit_distance_t best_distance_so_far,
+ cpp_reader *reader)
+ : best_match (goal, best_distance_so_far)
+{
+ cpp_forall_identifiers (reader, find_closest_macro_cpp_cb, this);
+}
+
#if CHECKING_P
namespace selftest {
diff --git a/gcc/spellcheck-tree.h b/gcc/spellcheck-tree.h
index 0d5e253..0e43d14 100644
--- a/gcc/spellcheck-tree.h
+++ b/gcc/spellcheck-tree.h
@@ -48,4 +48,30 @@ struct edit_distance_traits<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. */
+
+class best_macro_match : public best_match<tree, cpp_hashnode *>
+{
+ public:
+ best_macro_match (tree goal, edit_distance_t best_distance_so_far,
+ cpp_reader *reader);
+};
+
#endif /* GCC_SPELLCHECK_TREE_H */
diff --git a/gcc/testsuite/g++.dg/spellcheck-identifiers.C b/gcc/testsuite/g++.dg/spellcheck-identifiers.C
new file mode 100644
index 0000000..e8168be
--- /dev/null
+++ b/gcc/testsuite/g++.dg/spellcheck-identifiers.C
@@ -0,0 +1,255 @@
+/* { dg-do compile } */
+/* { dg-options "-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-error "3: 'gtk_widget_showall' was not declared in this scope" }
+ /* { dg-begin-multiline-output "" }
+ gtk_widget_showall (w);
+ ^~~~~~~~~~~~~~~~~~
+ { dg-end-multiline-output "" } */
+ // { dg-message "3: suggested alternative: 'gtk_widget_show_all'" "" { target *-*-* } 12 }
+ /* { 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-error "3: 'gtk_widget_showall_' was not declared in this scope" }
+ /* { dg-begin-multiline-output "" }
+ gtk_widget_showall_ (w);
+ ^~~~~~~~~~~~~~~~~~~
+ { dg-end-multiline-output "" } */
+ // { dg-message "3: suggested alternative: 'gtk_widget_show_all'" "" { target *-*-* } 26 }
+ /* { dg-begin-multiline-output "" }
+ gtk_widget_showall_ (w);
+ ^~~~~~~~~~~~~~~~~~~
+ gtk_widget_show_all
+ { dg-end-multiline-output "" } */
+
+ GtkWidgetShowAll (w); // { dg-error "3: 'GtkWidgetShowAll' was not declared in this scope" }
+ /* { dg-begin-multiline-output "" }
+ GtkWidgetShowAll (w);
+ ^~~~~~~~~~~~~~~~
+ { dg-end-multiline-output "" } */
+ // { dg-message "3: suggested alternative: 'gtk_widget_show_all'" "" { target *-*-* } 38 }
+ /* { 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' was not declared in this scope" }
+ /* { dg-begin-multiline-output "" }
+ return parma * parma;
+ ^~~~~
+ { dg-end-multiline-output "" } */
+ // { dg-message "10: suggested alternative: 'param'" "" { target *-*-* } 54 }
+ /* { 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-error "10: 'MACRAME' was not declared in this scope" }
+ /* { dg-begin-multiline-output "" }
+ return MACRAME (i);
+ ^~~~~~~
+ { dg-end-multiline-output "" } */
+ // { dg-message "10: suggested alternative: 'MACRO'" "" { target *-*-* } 72 }
+ /* { 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-error "10: 'IDENTIFIER_PTR' was not declared in this scope" }
+ /* { dg-begin-multiline-output "" }
+ return IDENTIFIER_PTR (node);
+ ^~~~~~~~~~~~~~
+ { dg-end-multiline-output "" } */
+ // { dg-message "10: suggested alternative: 'IDENTIFIER_POINTER'" "" { target *-*-* } 90 }
+ /* { dg-begin-multiline-output "" }
+ return IDENTIFIER_PTR (node);
+ ^~~~~~~~~~~~~~
+ IDENTIFIER_POINTER
+ { dg-end-multiline-output "" } */
+}
+
+
+int
+test_5 (void)
+{
+ return __LINE_; /* { dg-error "10: '__LINE_' was not declared in this scope" }
+ /* { dg-begin-multiline-output "" }
+ return __LINE_;
+ ^~~~~~~
+ { dg-end-multiline-output "" } */
+ // { dg-message "10: suggested alternative: '__LINE__'" "" { target *-*-* } 107 }
+ /* { dg-begin-multiline-output "" }
+ return __LINE_;
+ ^~~~~~~
+ __LINE__
+ { dg-end-multiline-output "" } */
+}
+
+#define MAX_ITEMS 100
+int array[MAX_ITEM]; // { dg-error "11: 'MAX_ITEM' was not declared in this scope" }
+ /* { dg-begin-multiline-output "" }
+ int array[MAX_ITEM];
+ ^~~~~~~~
+ { dg-end-multiline-output "" } */
+ // { dg-message "11: suggested alternative: 'MAX_ITEMS'" "" { target *-*-* } 121 }
+ /* { 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' was not declared in this scope" }
+ break;
+ /* { dg-begin-multiline-output "" }
+ case FOO_FURST:
+ ^~~~~~~~~
+ { dg-end-multiline-output "" } */
+ // { dg-message "10: suggested alternative: 'FOO_FIRST'" "" { target *-*-* } 144 }
+ /* { dg-begin-multiline-output "" }
+ case FOO_FURST:
+ ^~~~~~~~~
+ FOO_FIRST
+ { dg-end-multiline-output "" } */
+
+ case FOO_SECCOND: // { dg-error "10: 'FOO_SECCOND' was not declared in this scope" }
+ break;
+ /* { dg-begin-multiline-output "" }
+ case FOO_SECCOND:
+ ^~~~~~~~~~~
+ { dg-end-multiline-output "" } */
+ // { dg-message "10: suggested alternative: 'FOO_SECOND'" "" { target *-*-* } 157 }
+ /* { 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-error "3: 'snprint' was not declared in this scope" }
+ /* { dg-begin-multiline-output "" }
+ snprint (buffer, 100, "%i of %i", i, j);
+ ^~~~~~~
+ { dg-end-multiline-output "" } */
+ // { dg-message "3: suggested alternative: 'snprintf'" "" { target *-*-* } 181 }
+ /* { dg-begin-multiline-output "" }
+ snprint (buffer, 100, "%i of %i", i, j);
+ ^~~~~~~
+ snprintf
+ { dg-end-multiline-output "" } */
+}
+
+int
+test_8 ()
+{
+ int local = 42;
+
+ return locale; // { dg-error "10: 'locale' was not declared in this scope" }
+ /* { dg-begin-multiline-output "" }
+ return locale;
+ ^~~~~~
+ { dg-end-multiline-output "" } */
+ // { dg-message "10: suggested alternative: 'local'" "" { target *-*-* } 199 }
+ /* { dg-begin-multiline-output "" }
+ return locale;
+ ^~~~~~
+ local
+ { dg-end-multiline-output "" } */
+}
+
+class base
+{
+public:
+ int test_method_1 ();
+
+protected:
+ int m_foo;
+};
+
+class sub : public base
+{
+public:
+ int test_method_2 ();
+};
+
+int base::test_method_1 ()
+{
+ return m_food; // { dg-error "10: 'm_food' was not declared in this scope" }
+ /* { dg-begin-multiline-output "" }
+ return m_food;
+ ^~~~~~
+ { dg-end-multiline-output "" } */
+ // { dg-message "10: suggested alternative: 'm_foo'" "" { target *-*-* } 229 }
+ /* { dg-begin-multiline-output "" }
+ return m_food;
+ ^~~~~~
+ m_foo
+ { dg-end-multiline-output "" } */
+}
+
+int sub::test_method_2 ()
+{
+ return m_food; // { dg-error "10: 'm_food' was not declared in this scope" }
+ /* { dg-begin-multiline-output "" }
+ return m_food;
+ ^~~~~~
+ { dg-end-multiline-output "" } */
+ // { dg-message "10: suggested alternative: 'm_foo'" "" { target *-*-* } 244 }
+ /* { dg-begin-multiline-output "" }
+ return m_food;
+ ^~~~~~
+ m_foo
+ { dg-end-multiline-output "" } */
+}
diff --git a/gcc/testsuite/g++.dg/spellcheck-typenames.C b/gcc/testsuite/g++.dg/spellcheck-typenames.C
new file mode 100644
index 0000000..9aa5b72
--- /dev/null
+++ b/gcc/testsuite/g++.dg/spellcheck-typenames.C
@@ -0,0 +1,84 @@
+/* { 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 "21: variable or field 'test_2' declared void" }
+/* { dg-begin-multiline-output "" }
+ void test_2 (singed char e);
+ ^~~~
+ { dg-end-multiline-output "" } */
+// { dg-message "14: 'singed' was not declared in this scope" "" { target *-*-* } 7 }
+/* { dg-begin-multiline-output "" }
+ void test_2 (singed char e);
+ ^~~~~~
+ { dg-end-multiline-output "" } */
+// { dg-message "14: suggested alternative: 'signed'" "" { target *-*-* } 7 }
+/* { dg-begin-multiline-output "" }
+ void test_2 (singed char e);
+ ^~~~~~
+ signed
+ { dg-end-multiline-output "" } */
+
+void test_3 (car e); // { dg-error "14: variable or field 'test_3' declared void" }
+/* { dg-begin-multiline-output "" }
+ void test_3 (car e);
+ ^~~
+ { dg-end-multiline-output "" } */
+// { dg-message "14: 'car' was not declared in this scope" "" { target *-*-* } 24 }
+// { dg-message "14: suggested alternative: 'char'" "" { target *-*-* } 24 }
+/* { 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: expected ',' or '...' 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: 'some_thing_t' does not name a type; 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 "aggregate 'some_thing test_6' has incomplete type and cannot be defined" }
+ /* { dg-begin-multiline-output "" }
+ struct some_thing test_6;
+ ^~~~~~
+ { dg-end-multiline-output "" } */
+
+typedef long int64_t;
+int64 i; // { dg-error "1: 'int64' does not name a type; 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 "'nonsensical_suggestion_t' does not name a type" "" { target { *-*-* } } 72 } */
+/* { dg-begin-multiline-output "" }
+ nonsensical_suggestion_t var;
+ ^~~~~~~~~~~~~~~~~~~~~~~~
+ { dg-end-multiline-output "" } */
+
+singed char ch; // { dg-error "1: 'singed' does not name a type; did you mean 'signed'?" }
+/* { dg-begin-multiline-output "" }
+ singed char ch;
+ ^~~~~~
+ signed
+ { dg-end-multiline-output "" } */
--
1.8.5.3
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 2/2] C++ FE: handle misspelled identifiers and typenames
2016-06-30 18:43 ` [PATCH 2/2] C++ FE: handle misspelled identifiers and typenames David Malcolm
@ 2016-07-02 12:01 ` Manuel López-Ibáñez
2016-07-13 22:12 ` Jeff Law
1 sibling, 0 replies; 9+ messages in thread
From: Manuel López-Ibáñez @ 2016-07-02 12:01 UTC (permalink / raw)
To: David Malcolm, gcc-patches
On 30/06/16 19:53, David Malcolm wrote:
> This is a port of the C frontend's r237714 [1] to the C++ frontend:
> https://gcc.gnu.org/ml/gcc-patches/2016-06/msg01052.html
> offering spelling suggestions for misspelled identifiers, macro names,
> and some keywords (e.g. "singed" vs "signed" aka PR c/70339).
Cool!
> - 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);
It should be possible to encapsulate all this suggestion logic very deep in
diagnostics.c and replace all the above with something similar to:
const char *suggestion = NULL;
if (TREE_CODE (id) == IDENTIFIER_NODE)
suggestion = lookup_name_fuzzy (id, FUZZY_LOOKUP_TYPENAME);
error_with_suggestion(location, suggestion, "%qE does not name a type",
"%qE does not name a type; did you mean %qs?", id);
Perhaps with a bit more effort, even make the language-specific printers handle
the task of looking for the suggestion, replacing the above with something like:
error_with_suggestion(location, FUZZY_LOOKUP_TYPENAME,
"%qE does not name a type",
"%qE does not name a type; did you mean %qs?", id);
> diff --git a/gcc/diagnostic-show-locus.c b/gcc/diagnostic-show-locus.c
> index 7aab658..49f7f11 100644
> --- a/gcc/diagnostic-show-locus.c
> +++ b/gcc/diagnostic-show-locus.c
> @@ -1280,9 +1280,18 @@ diagnostic_show_locus (diagnostic_context * context,
> {
> pp_newline (context->printer);
>
> - if (!context->show_caret
> - || diagnostic_location (diagnostic, 0) <= BUILTINS_LOCATION
> - || diagnostic_location (diagnostic, 0) == context->last_location)
> + /* Do nothing if source-printing has been disabled. */
> + if (!context->show_caret)
> + return;
> +
> + /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
> + if (diagnostic_location (diagnostic, 0) <= BUILTINS_LOCATION)
> + return;
> +
> + /* Don't print the same source location twice in a row, unless we have
> + fix-it hints. */
> + if (diagnostic_location (diagnostic, 0) == context->last_location
> + && diagnostic->richloc->get_num_fixit_hints () == 0)
> return;
>
> context->last_location = diagnostic_location (diagnostic, 0);
This seems independent of the suggestion stuff and could be committed separately.
> --- a/gcc/spellcheck-tree.c
> +++ b/gcc/spellcheck-tree.c
> @@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see
> #include "coretypes.h"
> #include "tm.h"
> #include "tree.h"
> +#include "cpplib.h"
You make a language-independent file depend on cpplib.h. Wouldn't it be better
to move c-specific stuff to a new c-family/c-spellcheck.c ?
Cheers,
Manuel.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/2] C: convert return type of lookup_name_fuzzy from tree to const char *
2016-06-30 18:27 [PATCH 1/2] C: convert return type of lookup_name_fuzzy from tree to const char * David Malcolm
2016-06-30 18:43 ` [PATCH 2/2] C++ FE: handle misspelled identifiers and typenames David Malcolm
@ 2016-07-13 22:09 ` Jeff Law
1 sibling, 0 replies; 9+ messages in thread
From: Jeff Law @ 2016-07-13 22:09 UTC (permalink / raw)
To: David Malcolm, gcc-patches
On 06/30/2016 12:53 PM, David Malcolm wrote:
> The followup patch implements lookup_name_fuzzy for the C++ frontend.
> It's cleaner for that implementation to return a const char *, so this
> patch updates the implementation in the C frontend to have the same
> return type.
>
> Successfully bootstrapped®rtested on x86_64-pc-linux-gnu
> (in combination with the followup patch).
>
> OK for trunk?
>
> gcc/c-family/ChangeLog:
> * c-common.h (lookup_name_fuzzy): Convert return type from tree to
> const char *.
>
> gcc/c/ChangeLog:
> * c-decl.c (implicit_decl_warning): Update for conversion of
> return type of lookup_name_fuzzy to const char *.
> (undeclared_variable): Likewise.
> (lookup_name_fuzzy): Convert return type from tree to
> const char *.
> * c-parser.c (c_parser_declaration_or_fndef): Update for
> conversion of return type of lookup_name_fuzzy to const char *.
> (c_parser_parameter_declaration): Likewise.
>
> gcc/ChangeLog:
> * gcc-rich-location.c
> (gcc_rich_location::add_fixit_misspelled_id): New overload, taking
> a const char *.
> * gcc-rich-location.h
> (gcc_rich_location::add_fixit_misspelled_id): Likewise.
OK.
jeff
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 2/2] C++ FE: handle misspelled identifiers and typenames
2016-06-30 18:43 ` [PATCH 2/2] C++ FE: handle misspelled identifiers and typenames David Malcolm
2016-07-02 12:01 ` Manuel López-Ibáñez
@ 2016-07-13 22:12 ` Jeff Law
2016-07-14 18:31 ` David Malcolm
2016-07-20 14:19 ` [PATCH v2] " David Malcolm
1 sibling, 2 replies; 9+ messages in thread
From: Jeff Law @ 2016-07-13 22:12 UTC (permalink / raw)
To: David Malcolm, gcc-patches
On 06/30/2016 12:53 PM, David Malcolm wrote:
> This is a port of the C frontend's r237714 [1] to the C++ frontend:
> https://gcc.gnu.org/ml/gcc-patches/2016-06/msg01052.html
> offering spelling suggestions for misspelled identifiers, macro names,
> and some keywords (e.g. "singed" vs "signed" aka PR c/70339).
>
> Unlike the C frontend, there doesn't seem to be an easy way to
> distinguish between cases where we're expecting a typename vs
> a variable name, so some of the logic is a little different.
>
> Examples of suggestions can be seen in the test case.
>
> Successfully bootstrapped®rtested on x86_64-pc-linux-gnu
> (in combination with the prior patch); adds 240 PASS results to
> g++.sum.
>
> OK for trunk?
>
> Dave
>
> [1] aka 8469aece13814deddf2cd80538d33c2d0a8d60d9 in the git mirror
>
> gcc/c/ChangeLog:
> PR c/70339
> * c-decl.c (struct edit_distance_traits<cpp_hashnode *>): Move to
> spellcheck-tree.h
> (best_macro_match): Likewise, converting from a typedef to a
> subclass.
> (find_closest_macro_cpp_cb): Move to spellcheck-tree.c.
> (lookup_name_fuzzy): Update for change of best_macro_match to a
> subclass with a ctor that calls cpp_forall_identifiers.
These are fine.
>
> gcc/cp/ChangeLog:
> PR c/70339
> * 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.
> (consider_binding_levels): 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): Don't
> attempt to access TYPE_FIELDS within a TYPE_PACK_EXPANSION.
Going to let Jason on this part.
>
> gcc/ChangeLog:
> PR c/70339
> * diagnostic-show-locus.c (diagnostic_show_locus): If this is the
> same location as last time, don't skip if we have fix-it hints.
> Clarify the skipping logic by converting it from one "if" clause
> to repeated "if" clauses.
> * spellcheck-tree.c: Include "cpplib.h".
> (find_closest_macro_cpp_cb): Move here from c/c-decl.c.
> (best_macro_match::best_macro_match): New constructor.
> * spellcheck-tree.h (struct edit_distance_traits<cpp_hashnode *>):
> Move here from c/c-decl.c.
> (class best_macro_match): Move here from c/c-decl.c, converting
> from a typedef to a subclass, gaining a ctor.
OK.
>
> gcc/testsuite/ChangeLog:
> PR c/70339
> * g++.dg/spellcheck-identifiers.C: New test case, based on
> gcc.dg/spellcheck-identifiers.c.
> * g++.dg/spellcheck-typenames.C: New test case, based on
> gcc.dg/spellcheck-typenames.c
OK
jeff
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 2/2] C++ FE: handle misspelled identifiers and typenames
2016-07-13 22:12 ` Jeff Law
@ 2016-07-14 18:31 ` David Malcolm
2016-07-20 14:19 ` [PATCH v2] " David Malcolm
1 sibling, 0 replies; 9+ messages in thread
From: David Malcolm @ 2016-07-14 18:31 UTC (permalink / raw)
To: Jeff Law, gcc-patches
On Wed, 2016-07-13 at 16:12 -0600, Jeff Law wrote:
> On 06/30/2016 12:53 PM, David Malcolm wrote:
> > This is a port of the C frontend's r237714 [1] to the C++ frontend:
> > https://gcc.gnu.org/ml/gcc-patches/2016-06/msg01052.html
> > offering spelling suggestions for misspelled identifiers, macro
> > names,
> > and some keywords (e.g. "singed" vs "signed" aka PR c/70339).
> >
> > Unlike the C frontend, there doesn't seem to be an easy way to
> > distinguish between cases where we're expecting a typename vs
> > a variable name, so some of the logic is a little different.
> >
> > Examples of suggestions can be seen in the test case.
> >
> > Successfully bootstrapped®rtested on x86_64-pc-linux-gnu
> > (in combination with the prior patch); adds 240 PASS results to
> > g++.sum.
> >
> > OK for trunk?
> >
> > Dave
> >
> > [1] aka 8469aece13814deddf2cd80538d33c2d0a8d60d9 in the git mirror
> >
> > gcc/c/ChangeLog:
> > PR c/70339
> > * c-decl.c (struct edit_distance_traits<cpp_hashnode *>): Move
> > to
> > spellcheck-tree.h
> > (best_macro_match): Likewise, converting from a typedef to a
> > subclass.
> > (find_closest_macro_cpp_cb): Move to spellcheck-tree.c.
> > (lookup_name_fuzzy): Update for change of best_macro_match to a
> > subclass with a ctor that calls cpp_forall_identifiers.
> These are fine.
>
> >
> > gcc/cp/ChangeLog:
> > PR c/70339
> > * 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.
> > (consider_binding_levels): 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): Don't
> > attempt to access TYPE_FIELDS within a TYPE_PACK_EXPANSION.
> Going to let Jason on this part.
Thanks. The corresponding code in the C frontend is likely to change
due to the fix being discussed for PR c/71858, so I think I'll wait
until that other PR is fixed, and then resubmit an updated version of
this for review, incorporating equivalent fixes for C++.
> >
> > gcc/ChangeLog:
> > PR c/70339
> > * diagnostic-show-locus.c (diagnostic_show_locus): If this is
> > the
> > same location as last time, don't skip if we have fix-it hints.
> > Clarify the skipping logic by converting it from one "if"
> > clause
> > to repeated "if" clauses.
> > * spellcheck-tree.c: Include "cpplib.h".
> > (find_closest_macro_cpp_cb): Move here from c/c-decl.c.
> > (best_macro_match::best_macro_match): New constructor.
> > * spellcheck-tree.h (struct edit_distance_traits<cpp_hashnode
> > *>):
> > Move here from c/c-decl.c.
> > (class best_macro_match): Move here from c/c-decl.c, converting
> > from a typedef to a subclass, gaining a ctor.
> OK.
>
> >
> > gcc/testsuite/ChangeLog:
> > PR c/70339
> > * g++.dg/spellcheck-identifiers.C: New test case, based on
> > gcc.dg/spellcheck-identifiers.c.
> > * g++.dg/spellcheck-typenames.C: New test case, based on
> > gcc.dg/spellcheck-typenames.c
> OK
>
>
> jeff
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2] C++ FE: handle misspelled identifiers and typenames
2016-07-13 22:12 ` Jeff Law
2016-07-14 18:31 ` David Malcolm
@ 2016-07-20 14:19 ` David Malcolm
2016-07-20 14:25 ` Jakub Jelinek
2016-07-20 14:43 ` Jason Merrill
1 sibling, 2 replies; 9+ messages in thread
From: David Malcolm @ 2016-07-20 14:19 UTC (permalink / raw)
To: gcc-patches; +Cc: Jeff Law, Jason Merrill, David Malcolm
Changes in v2:
- split out the non-C++ parts already approved by Jeff (I've committed
these as r238522).
- updated to mirror the fixes for PR c/71858 Jakub made to the
corresponding C implementation in r238352, skipping anticipated decls
of builtin functions
- rewritten to more closely resemble the C FE's implementation
This is a port of the C frontend's r237714 [1] to the C++ frontend:
https://gcc.gnu.org/ml/gcc-patches/2016-06/msg01052.html
offering spelling suggestions for misspelled identifiers, macro names,
and some keywords (e.g. "singed" vs "signed" aka PR c/70339).
Examples of suggestions can be seen in the test case.
Successfully bootstrapped®rtested on x86_64-pc-linux-gnu; adds
267 PASS results to g++.sum.
OK for trunk?
[1] aka 8469aece13814deddf2cd80538d33c2d0a8d60d9 in the git mirror
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): Don't
attempt to access TYPE_FIELDS within a TYPE_PACK_EXPANSION.
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
---
gcc/cp/name-lookup.c | 116 ++++++++++-
gcc/cp/parser.c | 43 +++-
gcc/cp/parser.h | 1 +
gcc/cp/search.c | 4 +
gcc/testsuite/g++.dg/spellcheck-identifiers-2.C | 43 ++++
gcc/testsuite/g++.dg/spellcheck-identifiers.C | 255 ++++++++++++++++++++++++
gcc/testsuite/g++.dg/spellcheck-typenames.C | 84 ++++++++
7 files changed, 533 insertions(+), 13 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/spellcheck-identifiers-2.C
create mode 100644 gcc/testsuite/g++.dg/spellcheck-identifiers.C
create mode 100644 gcc/testsuite/g++.dg/spellcheck-typenames.C
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index cbd5209..561bf71 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)
+ if (DECL_BUILT_IN (t))
+ if (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 0a0f67b..7df75d6 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"
\f
/* 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..4a862b7 100644
--- a/gcc/cp/search.c
+++ b/gcc/cp/search.c
@@ -1407,6 +1407,10 @@ lookup_field_fuzzy_info::fuzzy_lookup_field (tree type)
The TYPE_FIELDS of TYPENAME_TYPE is its TYPENAME_TYPE_FULLNAME. */
return;
+ /* TYPE_FIELDS is not valid for a TYPE_PACK_EXPANSION. */
+ if (TREE_CODE (type) == TYPE_PACK_EXPANSION)
+ return;
+
for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
{
if (!m_want_type_p || DECL_DECLARES_TYPE_P (field))
diff --git a/gcc/testsuite/g++.dg/spellcheck-identifiers-2.C b/gcc/testsuite/g++.dg/spellcheck-identifiers-2.C
new file mode 100644
index 0000000..59a8ec5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/spellcheck-identifiers-2.C
@@ -0,0 +1,43 @@
+/* PR c/71858 */
+/* Make sure anticipated builtins are not considered before they are declared. */
+/* { dg-do compile } */
+/* { dg-options "-fdiagnostics-show-caret" } */
+
+int sscafn (const char *, const char *, ...);
+
+int
+test_1 (const char *p)
+{
+ int i;
+ return ssacnf (p, "%d", &i); /* { dg-error "10: .ssacnf. was not declared in this scope" } */
+ /* { dg-begin-multiline-output "" }
+ return ssacnf (p, "%d", &i);
+ ^~~~~~
+ { dg-end-multiline-output "" } */
+ // { dg-message "10: suggested alternative: 'sscafn'" "" { target *-*-* } 12 }
+ /* { dg-begin-multiline-output "" }
+ return ssacnf (p, "%d", &i);
+ ^~~~~~
+ sscafn
+ { dg-end-multiline-output "" } */
+}
+
+int scafn (const char *, ...);
+int scanf (const char *, ...);
+
+int
+test_2 (void)
+{
+ int i;
+ return sacnf ("%d", &i); /* { dg-error "10: .sacnf. was not declared in this scope" } */
+ /* { dg-begin-multiline-output "" }
+ return sacnf ("%d", &i);
+ ^~~~~
+ { dg-end-multiline-output "" } */
+ // { dg-message "10: suggested alternative: 'scanf'" "" { target *-*-* } 32 }
+ /* { dg-begin-multiline-output "" }
+ return sacnf ("%d", &i);
+ ^~~~~
+ scanf
+ { dg-end-multiline-output "" } */
+}
diff --git a/gcc/testsuite/g++.dg/spellcheck-identifiers.C b/gcc/testsuite/g++.dg/spellcheck-identifiers.C
new file mode 100644
index 0000000..0843439
--- /dev/null
+++ b/gcc/testsuite/g++.dg/spellcheck-identifiers.C
@@ -0,0 +1,255 @@
+/* { dg-do compile } */
+/* { dg-options "-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-error "3: 'gtk_widget_showall' was not declared in this scope" }
+ /* { dg-begin-multiline-output "" }
+ gtk_widget_showall (w);
+ ^~~~~~~~~~~~~~~~~~
+ { dg-end-multiline-output "" } */
+ // { dg-message "3: suggested alternative: 'gtk_widget_show_all'" "" { target *-*-* } 12 }
+ /* { 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-error "3: 'gtk_widget_showall_' was not declared in this scope" }
+ /* { dg-begin-multiline-output "" }
+ gtk_widget_showall_ (w);
+ ^~~~~~~~~~~~~~~~~~~
+ { dg-end-multiline-output "" } */
+ // { dg-message "3: suggested alternative: 'gtk_widget_show_all'" "" { target *-*-* } 26 }
+ /* { dg-begin-multiline-output "" }
+ gtk_widget_showall_ (w);
+ ^~~~~~~~~~~~~~~~~~~
+ gtk_widget_show_all
+ { dg-end-multiline-output "" } */
+
+ GtkWidgetShowAll (w); // { dg-error "3: 'GtkWidgetShowAll' was not declared in this scope" }
+ /* { dg-begin-multiline-output "" }
+ GtkWidgetShowAll (w);
+ ^~~~~~~~~~~~~~~~
+ { dg-end-multiline-output "" } */
+ // { dg-message "3: suggested alternative: 'gtk_widget_show_all'" "" { target *-*-* } 38 }
+ /* { 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' was not declared in this scope" }
+ /* { dg-begin-multiline-output "" }
+ return parma * parma;
+ ^~~~~
+ { dg-end-multiline-output "" } */
+ // { dg-message "10: suggested alternative: 'param'" "" { target *-*-* } 54 }
+ /* { 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-error "10: 'MACRAME' was not declared in this scope" }
+ /* { dg-begin-multiline-output "" }
+ return MACRAME (i);
+ ^~~~~~~
+ { dg-end-multiline-output "" } */
+ // { dg-message "10: suggested alternative: 'MACRO'" "" { target *-*-* } 72 }
+ /* { 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-error "10: 'IDENTIFIER_PTR' was not declared in this scope" }
+ /* { dg-begin-multiline-output "" }
+ return IDENTIFIER_PTR (node);
+ ^~~~~~~~~~~~~~
+ { dg-end-multiline-output "" } */
+ // { dg-message "10: suggested alternative: 'IDENTIFIER_POINTER'" "" { target *-*-* } 90 }
+ /* { dg-begin-multiline-output "" }
+ return IDENTIFIER_PTR (node);
+ ^~~~~~~~~~~~~~
+ IDENTIFIER_POINTER
+ { dg-end-multiline-output "" } */
+}
+
+
+int
+test_5 (void)
+{
+ return __LINE_; /* { dg-error "10: '__LINE_' was not declared in this scope" }
+ /* { dg-begin-multiline-output "" }
+ return __LINE_;
+ ^~~~~~~
+ { dg-end-multiline-output "" } */
+ // { dg-message "10: suggested alternative: '__LINE__'" "" { target *-*-* } 107 }
+ /* { dg-begin-multiline-output "" }
+ return __LINE_;
+ ^~~~~~~
+ __LINE__
+ { dg-end-multiline-output "" } */
+}
+
+#define MAX_ITEMS 100
+int array[MAX_ITEM]; // { dg-error "11: 'MAX_ITEM' was not declared in this scope" }
+ /* { dg-begin-multiline-output "" }
+ int array[MAX_ITEM];
+ ^~~~~~~~
+ { dg-end-multiline-output "" } */
+ // { dg-message "11: suggested alternative: 'MAX_ITEMS'" "" { target *-*-* } 121 }
+ /* { 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' was not declared in this scope" }
+ break;
+ /* { dg-begin-multiline-output "" }
+ case FOO_FURST:
+ ^~~~~~~~~
+ { dg-end-multiline-output "" } */
+ // { dg-message "10: suggested alternative: 'FOO_FIRST'" "" { target *-*-* } 144 }
+ /* { dg-begin-multiline-output "" }
+ case FOO_FURST:
+ ^~~~~~~~~
+ FOO_FIRST
+ { dg-end-multiline-output "" } */
+
+ case FOO_SECCOND: // { dg-error "10: 'FOO_SECCOND' was not declared in this scope" }
+ break;
+ /* { dg-begin-multiline-output "" }
+ case FOO_SECCOND:
+ ^~~~~~~~~~~
+ { dg-end-multiline-output "" } */
+ // { dg-message "10: suggested alternative: 'FOO_SECOND'" "" { target *-*-* } 157 }
+ /* { dg-begin-multiline-output "" }
+ case FOO_SECCOND:
+ ^~~~~~~~~~~
+ FOO_SECOND
+ { dg-end-multiline-output "" } */
+
+ default:
+ break;
+ }
+}
+
+int snprintf (char *, __SIZE_TYPE__, const char *, ...);
+
+void
+test_7 (int i, int j)
+{
+ int buffer[100];
+ snprint (buffer, 100, "%i of %i", i, j); // { dg-error "3: 'snprint' was not declared in this scope" }
+ /* { dg-begin-multiline-output "" }
+ snprint (buffer, 100, "%i of %i", i, j);
+ ^~~~~~~
+ { dg-end-multiline-output "" } */
+ // { dg-message "3: suggested alternative: 'snprintf'" "" { target *-*-* } 181 }
+ /* { dg-begin-multiline-output "" }
+ snprint (buffer, 100, "%i of %i", i, j);
+ ^~~~~~~
+ snprintf
+ { dg-end-multiline-output "" } */
+}
+
+int
+test_8 ()
+{
+ int local = 42;
+
+ return locale; // { dg-error "10: 'locale' was not declared in this scope" }
+ /* { dg-begin-multiline-output "" }
+ return locale;
+ ^~~~~~
+ { dg-end-multiline-output "" } */
+ // { dg-message "10: suggested alternative: 'local'" "" { target *-*-* } 199 }
+ /* { dg-begin-multiline-output "" }
+ return locale;
+ ^~~~~~
+ local
+ { dg-end-multiline-output "" } */
+}
+
+class base
+{
+public:
+ int test_method_1 ();
+
+protected:
+ int m_foo;
+};
+
+class sub : public base
+{
+public:
+ int test_method_2 ();
+};
+
+int base::test_method_1 ()
+{
+ return m_food; // { dg-error "10: 'm_food' was not declared in this scope" }
+ /* { dg-begin-multiline-output "" }
+ return m_food;
+ ^~~~~~
+ { dg-end-multiline-output "" } */
+ // { dg-message "10: suggested alternative: 'm_foo'" "" { target *-*-* } 229 }
+ /* { dg-begin-multiline-output "" }
+ return m_food;
+ ^~~~~~
+ m_foo
+ { dg-end-multiline-output "" } */
+}
+
+int sub::test_method_2 ()
+{
+ return m_food; // { dg-error "10: 'm_food' was not declared in this scope" }
+ /* { dg-begin-multiline-output "" }
+ return m_food;
+ ^~~~~~
+ { dg-end-multiline-output "" } */
+ // { dg-message "10: suggested alternative: 'm_foo'" "" { target *-*-* } 244 }
+ /* { dg-begin-multiline-output "" }
+ return m_food;
+ ^~~~~~
+ m_foo
+ { dg-end-multiline-output "" } */
+}
diff --git a/gcc/testsuite/g++.dg/spellcheck-typenames.C b/gcc/testsuite/g++.dg/spellcheck-typenames.C
new file mode 100644
index 0000000..9aa5b72
--- /dev/null
+++ b/gcc/testsuite/g++.dg/spellcheck-typenames.C
@@ -0,0 +1,84 @@
+/* { 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 "21: variable or field 'test_2' declared void" }
+/* { dg-begin-multiline-output "" }
+ void test_2 (singed char e);
+ ^~~~
+ { dg-end-multiline-output "" } */
+// { dg-message "14: 'singed' was not declared in this scope" "" { target *-*-* } 7 }
+/* { dg-begin-multiline-output "" }
+ void test_2 (singed char e);
+ ^~~~~~
+ { dg-end-multiline-output "" } */
+// { dg-message "14: suggested alternative: 'signed'" "" { target *-*-* } 7 }
+/* { dg-begin-multiline-output "" }
+ void test_2 (singed char e);
+ ^~~~~~
+ signed
+ { dg-end-multiline-output "" } */
+
+void test_3 (car e); // { dg-error "14: variable or field 'test_3' declared void" }
+/* { dg-begin-multiline-output "" }
+ void test_3 (car e);
+ ^~~
+ { dg-end-multiline-output "" } */
+// { dg-message "14: 'car' was not declared in this scope" "" { target *-*-* } 24 }
+// { dg-message "14: suggested alternative: 'char'" "" { target *-*-* } 24 }
+/* { 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: expected ',' or '...' 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: 'some_thing_t' does not name a type; 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 "aggregate 'some_thing test_6' has incomplete type and cannot be defined" }
+ /* { dg-begin-multiline-output "" }
+ struct some_thing test_6;
+ ^~~~~~
+ { dg-end-multiline-output "" } */
+
+typedef long int64_t;
+int64 i; // { dg-error "1: 'int64' does not name a type; 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 "'nonsensical_suggestion_t' does not name a type" "" { target { *-*-* } } 72 } */
+/* { dg-begin-multiline-output "" }
+ nonsensical_suggestion_t var;
+ ^~~~~~~~~~~~~~~~~~~~~~~~
+ { dg-end-multiline-output "" } */
+
+singed char ch; // { dg-error "1: 'singed' does not name a type; did you mean 'signed'?" }
+/* { dg-begin-multiline-output "" }
+ singed char ch;
+ ^~~~~~
+ signed
+ { dg-end-multiline-output "" } */
--
1.8.5.3
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2] C++ FE: handle misspelled identifiers and typenames
2016-07-20 14:19 ` [PATCH v2] " David Malcolm
@ 2016-07-20 14:25 ` Jakub Jelinek
2016-07-20 14:43 ` Jason Merrill
1 sibling, 0 replies; 9+ messages in thread
From: Jakub Jelinek @ 2016-07-20 14:25 UTC (permalink / raw)
To: David Malcolm; +Cc: gcc-patches, Jeff Law, Jason Merrill
On Wed, Jul 20, 2016 at 10:46:58AM -0400, David Malcolm wrote:
> + /* Skip anticipated decls of builtin functions. */
> + if (TREE_CODE (t) == FUNCTION_DECL)
> + if (DECL_BUILT_IN (t))
> + if (DECL_ANTICIPATED (t))
Just a style comment, wouldn't
if (TREE_CODE (t) == FUNCTION_DECL
&& DECL_BUILT_IN (t)
&& DECL_ANTICIPATED (t))
continue;
be better?
Jakub
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2] C++ FE: handle misspelled identifiers and typenames
2016-07-20 14:19 ` [PATCH v2] " David Malcolm
2016-07-20 14:25 ` Jakub Jelinek
@ 2016-07-20 14:43 ` Jason Merrill
1 sibling, 0 replies; 9+ messages in thread
From: Jason Merrill @ 2016-07-20 14:43 UTC (permalink / raw)
To: David Malcolm; +Cc: gcc-patches List, Jeff Law
On Wed, Jul 20, 2016 at 10:46 AM, David Malcolm <dmalcolm@redhat.com> wrote:
> @@ -1407,6 +1407,10 @@ lookup_field_fuzzy_info::fuzzy_lookup_field (tree type)
> The TYPE_FIELDS of TYPENAME_TYPE is its TYPENAME_TYPE_FULLNAME. */
> return;
>
> + /* TYPE_FIELDS is not valid for a TYPE_PACK_EXPANSION. */
> + if (TREE_CODE (type) == TYPE_PACK_EXPANSION)
> + return;
Instead of checking for various invalid codes, why don't we just check
CLASS_TYPE_P at the top, like fuzzy_lookup_fnfields?
OK with that change.
Jason
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2016-07-20 14:43 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-06-30 18:27 [PATCH 1/2] C: convert return type of lookup_name_fuzzy from tree to const char * David Malcolm
2016-06-30 18:43 ` [PATCH 2/2] C++ FE: handle misspelled identifiers and typenames David Malcolm
2016-07-02 12:01 ` Manuel López-Ibáñez
2016-07-13 22:12 ` Jeff Law
2016-07-14 18:31 ` David Malcolm
2016-07-20 14:19 ` [PATCH v2] " David Malcolm
2016-07-20 14:25 ` Jakub Jelinek
2016-07-20 14:43 ` Jason Merrill
2016-07-13 22:09 ` [PATCH 1/2] C: convert return type of lookup_name_fuzzy from tree to const char * Jeff Law
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).