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" /* 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