In the C++ FE, after emitting various errors about unrecognized names, the parser can call suggest_alternatives_for and/or suggest_alternative_in_explicit_scope. These can issue zero or more suggestions for the unrecognized name, or various other "note" diagnostics suggesting how to fix the problem.
For example, currently g++ emits: t.cc:12:3: error: 'gtk_widget_showall' was not declared in this scope 12 | gtk_widget_showall (w); | ^~~~~~~~~~~~~~~~~~ t.cc:12:3: note: suggested alternative: 'gtk_widget_show_all' 12 | gtk_widget_showall (w); | ^~~~~~~~~~~~~~~~~~ | gtk_widget_show_all This patch consolidates the common case when there is a single candidate, so that the error can issue a fix-it hint directly. This simplifies the above to: t.cc:12:3: error: 'gtk_widget_showall' was not declared in this scope; did you mean 'gtk_widget_show_all'? 12 | gtk_widget_showall (w); | ^~~~~~~~~~~~~~~~~~ | gtk_widget_show_all omitting the second "note" diagnostic. Doing so requires changing the above "suggest_" functions so that rather than being called after "error" and emitting a note directly, they are called before the "error", and return a name_hint, which can contain a suggestion and/or a deferred diagnostic. The "single candidate" case is handled via a suggestion, and the "multiple candidates" case via a new subclass of deferred_diagnostic. There was some complication due to the fact that we don't always have enough location information to issue a fix-it hint. Specifically, for the case in qualified_name_lookup_error, the location is that of the name, but the location of the qualifier prefix isn't reliably available. For some hints, e.g. spell-corrections, the replacement is of the name, and for others, e.g. parent namespaces, it's for the qualified name. The patch addresses this by splitting this case out into a new "suggest_alternatives_in_other_namespaces" function, for which fix-it hints aren't issued. Another complication is that of emitting a note when --param cxx-max-namespaces-for-diagnostic-help is reached. The patch emulates the existing behavior by emitting the note from a deferred_diagnostic. This potentially needs to co-exist with another deferred_diagnostic, so it works as a decorator around any other such deferred_diagnostic. Doing so requires slightly extending class name_hint. On adding test coverage for the various cases, I discovered that after emitting a "FOO is not a namespace-name" error, we also emit a "expected namespace-name before" error. The patch removes this second error for the case where it's redundant, simplifying this case from e.g.: spellcheck-ns.C:10:24: error: 'inner_ms' is not a namespace-name 10 | using namespace outer::inner_ms; | ^~~~~~~~ spellcheck-ns.C:10:24: note: suggested alternative: 'inner_ns' 10 | using namespace outer::inner_ms; | ^~~~~~~~ | inner_ns spellcheck-ns.C:10:32: error: expected namespace-name before ';' token 10 | using namespace outer::inner_ms; | ^ to: spellcheck-ns.C:10:24: error: 'inner_ms' is not a namespace-name; did you mean 'inner_ns'? 10 | using namespace outer::inner_ms; | ^~~~~~~~ | inner_ns Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu (on top of https://gcc.gnu.org/ml/gcc-patches/2018-09/msg01272.html "v2: C++: suggestions for misspelled private members (PR c++/84993)") OK for trunk? gcc/c-family/ChangeLog: * name-hint.h (class name_hint): Add copy constructor and copy assignment operator. (name_hint::operator bool): Also return true if m_deferred is non-NULL. (name_hint::take_deferred): New member function. gcc/c/ChangeLog: * c-decl.c (implicit_decl_warning): Update "is there a suggestion" logic for change to name_hint::operator bool. (undeclared_variable): Likewise. * c-parser.c (c_parser_declaration_or_fndef): Likewise. (c_parser_parameter_declaration): Likewise. gcc/cp/ChangeLog: * cp-name-hint.h: New file. * cp-tree.h (expr_to_string): New decl. (suggest_alternatives_for): Move to cp-name-hint.h, changing return type from bool to name_hint. (suggest_alternative_in_explicit_scope): Likewise. * error.c: Define INCLUDE_UNIQUE_PTR. Include "cp-name-hint.h". (expr_to_string): Make non-static. (qualified_name_lookup_error): For the non-"::" case, take responsibity for issuing any suggestion from suggest_alternative_in_explicit_scope, as it changes from returning a bool to returning a name_hint. Replace fallback call to suggest_alternatives_for to a call to suggest_alternatives_in_other_namespaces, capturing the fact that we don't have enough location information to issue a fix-it hint for this case. Update the error to support emitting a fix-it hint where appropriate. For the "::" case, take responsibility for issuing any suggestion from suggest_alternatives_for, supporting emitting a fix-it hint. * lex.c: Define INCLUDE_UNIQUE_PTR. Include "gcc-rich-location.h" and "cp-name-hint.h". (unqualified_name_lookup_error): Take responsibility for issuing any suggestion from suggest_alternatives_for, supporting emitting a fix-it hint. * name-lookup.c (class namespace_limit_reached): New subclass of deferred_diagnostic. (class show_candidate_location): Likewise. (class suggest_alternatives): Likewise. (class namespace_hints): New class. (suggest_alternatives_for): Convert return type from bool to name_hint, replacing all direct diagnostic emission by setting suggestions on the return value, or creating deferred diagnostics. Specifically, split out initial traversal of namespaces into namespace_hints' ctor, and maybe_decorate_with_limit, and move the rest of the implementation to namespace_hints::convert_candidates_to_name_hint and suggest_alternatives_for_1. (namespace_hints::namespace_hints): New ctor, adapted from suggest_alternatives_for's initial namespace traversal, storing location and name, and converting locals "candidates", "limited" and "limit" into members. (namespace_hints::convert_candidates_to_name_hint): New member function. (namespace_hints::maybe_decorate_with_limit): New member function. (suggest_alternatives_for_1): New function, based on second half of old implementation of suggest_alternatives_for, converting from immediate emission of suggestions to using name_hint. (suggest_alternatives_in_other_namespaces): New function. (maybe_suggest_missing_std_header): Convert from immediate emission of suggestions to using name_hint, moving emission implementation to... (class missing_std_header): New subclass of deferred_diagnostic. (maybe_suggest_missing_header): Convert return type from bool to name_hint. (suggest_alternative_in_explicit_scope): Convert from immediate emission of suggestions to using name_hint. * parser.c: Replace include of "c-family/name-hint.h" with "cp-name-hint.h". (cp_parser_diagnose_invalid_type_name): Update "is there a suggestion" logic for change to name_hint::operator bool. Take responsibility for emitting fix-it hints from suggest_alternative_in_explicit_scope. (cp_parser_namespace_name): Take responsibility for emitting fix-it hints from suggest_alternative_in_explicit_scope. Don't emit the "expected namespace-name" error if we've already emitted an "is not a namespace-name" error. gcc/testsuite/ChangeLog: * c-c++-common/spellcheck-reserved.c: Update expected output for C++ for merger of "did you mean" suggestions into the error message. * g++.dg/ext/builtin3.C: Update expected output for merger of "did you mean" suggestion into the error. * g++.dg/lookup/error1.C: Likewise. * g++.dg/lookup/pr77549.C: Likewise. * g++.dg/lookup/pr80913.C: Likewise. * g++.dg/lookup/suggestions1.C: Likewise. * g++.dg/lookup/suggestions2.C: New test. * g++.dg/overload/koenig1.C: Update expected output as above. * g++.dg/spellcheck-identifiers-2.C: Likewise. * g++.dg/spellcheck-identifiers.C: Likewise. * g++.dg/spellcheck-ns.C: New test. * g++.dg/spellcheck-pr77829.C: Update expected output as above. * g++.dg/spellcheck-pr78656.C: Likewise. * g++.dg/spellcheck-pr79298.C: Likewise, adding -fdiagnostics-show-caret to options. * g++.dg/spellcheck-pr80177.C: Likewise. * g++.dg/spellcheck-single-vs-multiple.C: New test. * g++.dg/spellcheck-typenames.C: Update expected output as above. * g++.dg/template/static10.C: Likewise. * g++.old-deja/g++.mike/ns5.C: Likewise. * g++.old-deja/g++.mike/ns7.C: Likewise. * g++.old-deja/g++.ns/koenig5.C: Likewise. * g++.old-deja/g++.other/lineno5.C: Likewise. libstdc++-v3/ChangeLog: * testsuite/17_intro/using_namespace_std_exp_neg.cc: Remove "expected namespace-name before" error. * testsuite/17_intro/using_namespace_std_tr1_neg.cc: Likewise. --- gcc/c-family/name-hint.h | 27 +- gcc/c/c-decl.c | 24 +- gcc/c/c-parser.c | 12 +- gcc/cp/cp-name-hint.h | 37 ++ gcc/cp/cp-tree.h | 3 +- gcc/cp/error.c | 43 ++- gcc/cp/lex.c | 17 +- gcc/cp/name-lookup.c | 407 ++++++++++++++++----- gcc/cp/parser.c | 84 ++++- gcc/testsuite/c-c++-common/spellcheck-reserved.c | 9 +- gcc/testsuite/g++.dg/ext/builtin3.C | 3 +- gcc/testsuite/g++.dg/lookup/error1.C | 3 +- gcc/testsuite/g++.dg/lookup/pr77549.C | 15 +- gcc/testsuite/g++.dg/lookup/pr80913.C | 3 +- gcc/testsuite/g++.dg/lookup/suggestions1.C | 8 +- gcc/testsuite/g++.dg/lookup/suggestions2.C | 128 +++++++ gcc/testsuite/g++.dg/overload/koenig1.C | 3 +- gcc/testsuite/g++.dg/spellcheck-identifiers-2.C | 14 +- gcc/testsuite/g++.dg/spellcheck-identifiers.C | 98 +---- gcc/testsuite/g++.dg/spellcheck-ns.C | 22 ++ gcc/testsuite/g++.dg/spellcheck-pr77829.C | 51 +-- gcc/testsuite/g++.dg/spellcheck-pr78656.C | 14 +- gcc/testsuite/g++.dg/spellcheck-pr79298.C | 13 +- gcc/testsuite/g++.dg/spellcheck-pr80177.C | 9 +- .../g++.dg/spellcheck-single-vs-multiple.C | 79 ++++ gcc/testsuite/g++.dg/spellcheck-typenames.C | 10 +- gcc/testsuite/g++.dg/template/static10.C | 4 +- gcc/testsuite/g++.old-deja/g++.mike/ns5.C | 3 +- gcc/testsuite/g++.old-deja/g++.mike/ns7.C | 3 +- gcc/testsuite/g++.old-deja/g++.ns/koenig5.C | 3 +- gcc/testsuite/g++.old-deja/g++.other/lineno5.C | 3 +- .../17_intro/using_namespace_std_exp_neg.cc | 2 - .../17_intro/using_namespace_std_tr1_neg.cc | 2 - 33 files changed, 806 insertions(+), 350 deletions(-) create mode 100644 gcc/cp/cp-name-hint.h create mode 100644 gcc/testsuite/g++.dg/lookup/suggestions2.C create mode 100644 gcc/testsuite/g++.dg/spellcheck-ns.C create mode 100644 gcc/testsuite/g++.dg/spellcheck-single-vs-multiple.C diff --git a/gcc/c-family/name-hint.h b/gcc/c-family/name-hint.h index ef0e4a3..f1788e1 100644 --- a/gcc/c-family/name-hint.h +++ b/gcc/c-family/name-hint.h @@ -98,8 +98,33 @@ public: { } + /* Emulation of a "move" constructor, but really a copy + constructor. */ + + name_hint (const name_hint &other) + : m_suggestion (other.m_suggestion), + m_deferred (const_cast<name_hint &> (other).take_deferred ()) + { + } + + /* Emulation of "move" assigment, but really copy assignment. */ + + name_hint& operator= (const name_hint &other) + { + m_suggestion = other.m_suggestion; + m_deferred = const_cast<name_hint &> (other).take_deferred (); + return *this; + } + const char *suggestion () const { return m_suggestion; } - operator bool () const { return m_suggestion != NULL; } + + /* Does this name_hint have a suggestion or a deferred diagnostic? */ + operator bool () const { return (m_suggestion != NULL + || m_deferred != NULL); } + + /* Take ownership of this name_hint's deferred_diagnostic, for use + in chaining up deferred diagnostics. */ + gnu::unique_ptr<deferred_diagnostic> take_deferred () { return move (m_deferred); } /* Call this on a name_hint if the corresponding warning was not emitted, in which case we should also not emit the deferred_diagnostic. */ diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c index fdcfbde..cc9dc67 100644 --- a/gcc/c/c-decl.c +++ b/gcc/c/c-decl.c @@ -3152,27 +3152,27 @@ implicit_decl_warning (location_t loc, tree id, tree olddecl) if (flag_isoc99) { - if (hint) + if (const char *suggestion = hint.suggestion ()) { gcc_rich_location richloc (loc); - richloc.add_fixit_replace (hint.suggestion ()); + richloc.add_fixit_replace (suggestion); warned = pedwarn (&richloc, OPT_Wimplicit_function_declaration, "implicit declaration of function %qE;" " did you mean %qs?", - id, hint.suggestion ()); + id, suggestion); } else warned = pedwarn (loc, OPT_Wimplicit_function_declaration, "implicit declaration of function %qE", id); } - else if (hint) + else if (const char *suggestion = hint.suggestion ()) { gcc_rich_location richloc (loc); - richloc.add_fixit_replace (hint.suggestion ()); + richloc.add_fixit_replace (suggestion); warned = warning_at (&richloc, OPT_Wimplicit_function_declaration, G_("implicit declaration of function %qE; did you mean %qs?"), - id, hint.suggestion ()); + id, suggestion); } else warned = warning_at (loc, OPT_Wimplicit_function_declaration, @@ -3515,14 +3515,14 @@ undeclared_variable (location_t loc, tree id) if (current_function_decl == NULL_TREE) { name_hint guessed_id = lookup_name_fuzzy (id, FUZZY_LOOKUP_NAME, loc); - if (guessed_id) + if (const char *suggestion = guessed_id.suggestion ()) { gcc_rich_location richloc (loc); - richloc.add_fixit_replace (guessed_id.suggestion ()); + richloc.add_fixit_replace (suggestion); error_at (&richloc, "%qE undeclared here (not in a function);" " did you mean %qs?", - id, guessed_id.suggestion ()); + id, suggestion); } else error_at (loc, "%qE undeclared here (not in a function)", id); @@ -3533,14 +3533,14 @@ undeclared_variable (location_t loc, tree id) if (!objc_diagnose_private_ivar (id)) { name_hint guessed_id = lookup_name_fuzzy (id, FUZZY_LOOKUP_NAME, loc); - if (guessed_id) + if (const char *suggestion = guessed_id.suggestion ()) { gcc_rich_location richloc (loc); - richloc.add_fixit_replace (guessed_id.suggestion ()); + richloc.add_fixit_replace (suggestion); error_at (&richloc, "%qE undeclared (first use in this function);" " did you mean %qs?", - id, guessed_id.suggestion ()); + id, suggestion); } else error_at (loc, "%qE undeclared (first use in this function)", id); diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c index 1766a25..3afdda6 100644 --- a/gcc/c/c-parser.c +++ b/gcc/c/c-parser.c @@ -1817,12 +1817,12 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, auto_diagnostic_group d; name_hint hint = lookup_name_fuzzy (name, FUZZY_LOOKUP_TYPENAME, here); - if (hint) + if (const char *suggestion = hint.suggestion ()) { - richloc.add_fixit_replace (hint.suggestion ()); + richloc.add_fixit_replace (suggestion); error_at (&richloc, "unknown type name %qE; did you mean %qs?", - name, hint.suggestion ()); + name, suggestion); } else error_at (here, "unknown type name %qE", name); @@ -4054,13 +4054,13 @@ c_parser_parameter_declaration (c_parser *parser, tree attrs) name_hint hint = lookup_name_fuzzy (token->value, FUZZY_LOOKUP_TYPENAME, token->location); - if (hint) + if (const char *suggestion = hint.suggestion ()) { gcc_rich_location richloc (token->location); - richloc.add_fixit_replace (hint.suggestion ()); + richloc.add_fixit_replace (suggestion); error_at (&richloc, "unknown type name %qE; did you mean %qs?", - token->value, hint.suggestion ()); + token->value, suggestion); } else error_at (token->location, "unknown type name %qE", token->value); diff --git a/gcc/cp/cp-name-hint.h b/gcc/cp/cp-name-hint.h new file mode 100644 index 0000000..5d1cdc3 --- /dev/null +++ b/gcc/cp/cp-name-hint.h @@ -0,0 +1,37 @@ +/* Declarations for working with name_hint instances in the C++ frontend. + Copyright (C) 2018 Free Software Foundation, Inc. + Contributed by David Malcolm <dmalc...@redhat.com> + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#ifndef GCC_CP_NAME_HINT_H +#define GCC_CP_NAME_HINT_H + +/* class name_hint is declared in c-family/name-hint.h, but due + to issues described in that header, we have to jump through some + #define hoops to be able to include it. + + This header (cp/cp-name-hint.h) exists to limit the C++ frontend's + exposure to the issue. */ + +#include "c-family/name-hint.h" + +extern name_hint suggest_alternatives_for (location_t, tree, bool); +extern name_hint suggest_alternatives_in_other_namespaces (location_t, tree); +extern name_hint suggest_alternative_in_explicit_scope (location_t, tree, tree); + +#endif /* GCC_CP_NAME_HINT_H */ diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 6c12c5f..9fac9aa 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6443,6 +6443,7 @@ extern const char *decl_as_string (tree, int); extern const char *decl_as_string_translate (tree, int); extern const char *decl_as_dwarf_string (tree, int); extern const char *expr_as_string (tree, int); +extern const char *expr_to_string (tree); extern const char *lang_decl_name (tree, int, bool); extern const char *lang_decl_dwarf_name (tree, int, bool); extern const char *language_to_string (enum languages); @@ -7476,8 +7477,6 @@ extern tree cp_fully_fold (tree); extern void clear_fold_cache (void); /* in name-lookup.c */ -extern void suggest_alternatives_for (location_t, tree, bool); -extern bool suggest_alternative_in_explicit_scope (location_t, tree, tree); extern tree strip_using_decl (tree); /* Tell the binding oracle what kind of binding we are looking for. */ diff --git a/gcc/cp/error.c b/gcc/cp/error.c index 601f6d2..0f70360 100644 --- a/gcc/cp/error.c +++ b/gcc/cp/error.c @@ -18,6 +18,8 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #include "config.h" +/* For use with name_hint. */ +#define INCLUDE_UNIQUE_PTR #include "system.h" #include "coretypes.h" #include "cp-tree.h" @@ -32,6 +34,7 @@ along with GCC; see the file COPYING3. If not see #include "ubsan.h" #include "internal-fn.h" #include "gcc-rich-location.h" +#include "cp-name-hint.h" #define pp_separate_with_comma(PP) pp_cxx_separate_with (PP, ',') #define pp_separate_with_semicolon(PP) pp_cxx_separate_with (PP, ';') @@ -54,7 +57,6 @@ static const char *args_to_string (tree, int); static const char *code_to_string (enum tree_code); static const char *cv_to_string (tree, int); static const char *decl_to_string (tree, int); -static const char *expr_to_string (tree); static const char *fndecl_to_string (tree, int); static const char *op_to_string (bool, enum tree_code); static const char *parm_to_string (int); @@ -3059,7 +3061,7 @@ decl_to_string (tree decl, int verbose) return pp_ggc_formatted_text (cxx_pp); } -static const char * +const char * expr_to_string (tree decl) { reinit_cxx_pp (); @@ -4271,15 +4273,42 @@ qualified_name_lookup_error (tree scope, tree name, else if (scope != global_namespace) { auto_diagnostic_group d; - error_at (location, "%qD is not a member of %qD", name, scope); - if (!suggest_alternative_in_explicit_scope (location, name, scope)) - suggest_alternatives_for (location, name, false); + bool emit_fixit = true; + name_hint hint + = suggest_alternative_in_explicit_scope (location, name, scope); + if (!hint) + { + hint = suggest_alternatives_in_other_namespaces (location, name); + /* "location" is just the location of the name, not of the explicit + scope, and it's not easy to get at the latter, so we can't issue + fix-it hints for the suggestion. */ + emit_fixit = false; + } + if (const char *suggestion = hint.suggestion ()) + { + gcc_rich_location richloc (location); + if (emit_fixit) + richloc.add_fixit_replace (suggestion); + error_at (&richloc, "%qD is not a member of %qD; did you mean %qs?", + name, scope, suggestion); + } + else + error_at (location, "%qD is not a member of %qD", name, scope); } else { auto_diagnostic_group d; - error_at (location, "%<::%D%> has not been declared", name); - suggest_alternatives_for (location, name, true); + name_hint hint = suggest_alternatives_for (location, name, true); + if (const char *suggestion = hint.suggestion ()) + { + gcc_rich_location richloc (location); + richloc.add_fixit_replace (suggestion); + error_at (&richloc, + "%<::%D%> has not been declared; did you mean %qs?", + name, suggestion); + } + else + error_at (location, "%<::%D%> has not been declared", name); } } diff --git a/gcc/cp/lex.c b/gcc/cp/lex.c index 47b99c3..410dfd1 100644 --- a/gcc/cp/lex.c +++ b/gcc/cp/lex.c @@ -22,12 +22,16 @@ along with GCC; see the file COPYING3. If not see /* This file is the lexical analyzer for GNU C++. */ #include "config.h" +/* For use with name_hint. */ +#define INCLUDE_UNIQUE_PTR #include "system.h" #include "coretypes.h" #include "cp-tree.h" #include "stringpool.h" #include "c-family/c-pragma.h" #include "c-family/c-objc.h" +#include "gcc-rich-location.h" +#include "cp-name-hint.h" static int interface_strcmp (const char *); static void init_cp_pragma (void); @@ -500,8 +504,17 @@ unqualified_name_lookup_error (tree name, location_t loc) if (!objc_diagnose_private_ivar (name)) { auto_diagnostic_group d; - error_at (loc, "%qD was not declared in this scope", name); - suggest_alternatives_for (loc, name, true); + name_hint hint = suggest_alternatives_for (loc, name, true); + if (const char *suggestion = hint.suggestion ()) + { + gcc_rich_location richloc (loc); + richloc.add_fixit_replace (suggestion); + error_at (&richloc, + "%qD was not declared in this scope; did you mean %qs?", + name, suggestion); + } + else + error_at (loc, "%qD was not declared in this scope", name); } /* Prevent repeated error messages by creating a VAR_DECL with this NAME in the innermost block scope. */ diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c index c56bfe5..ac18996 100644 --- a/gcc/cp/name-lookup.c +++ b/gcc/cp/name-lookup.c @@ -41,7 +41,10 @@ static cxx_binding *cxx_binding_make (tree value, tree type); static cp_binding_level *innermost_nonclass_level (void); static void set_identifier_type_value_with_scope (tree id, tree decl, cp_binding_level *b); -static bool maybe_suggest_missing_std_header (location_t location, tree name); +static name_hint maybe_suggest_missing_std_header (location_t location, + tree name); +static name_hint suggest_alternatives_for_1 (location_t location, tree name, + bool suggest_misspellings); /* Create an overload suitable for recording an artificial TYPE_DECL and another decl. We use this machanism to implement the struct @@ -5318,20 +5321,132 @@ has_using_namespace_std_directive_p () return false; } -/* Suggest alternatives for NAME, an IDENTIFIER_NODE for which name - lookup failed. Search through all available namespaces and print out - possible candidates. If no exact matches are found, and - SUGGEST_MISSPELLINGS is true, then also look for near-matches and - suggest the best near-match, if there is one. */ +/* Subclass of deferred_diagnostic, for issuing a note when + --param cxx-max-namespaces-for-diagnostic-help is reached. -void -suggest_alternatives_for (location_t location, tree name, - bool suggest_misspellings) + The note should be issued after the error, but before any other + deferred diagnostics. This is handled by decorating a wrapped + deferred_diagnostic, and emitting a note before that wrapped note is + deleted. */ + +class namespace_limit_reached : public deferred_diagnostic +{ + public: + namespace_limit_reached (location_t loc, unsigned limit, tree name, + gnu::unique_ptr<deferred_diagnostic> wrapped) + : deferred_diagnostic (loc), + m_limit (limit), m_name (name), + m_wrapped (move (wrapped)) + { + } + + ~namespace_limit_reached () + { + /* Unconditionally warn that the search was truncated. */ + inform (get_location (), + "maximum limit of %d namespaces searched for %qE", + m_limit, m_name); + /* m_wrapped will be implicitly deleted after this, emitting any followup + diagnostic after the above note. */ + } + + private: + unsigned m_limit; + tree m_name; + gnu::unique_ptr<deferred_diagnostic> m_wrapped; +}; + +/* Subclass of deferred_diagnostic, for use when issuing a single suggestion. + Emit a note showing the location of the declaration of the suggestion. */ + +class show_candidate_location : public deferred_diagnostic +{ + public: + show_candidate_location (location_t loc, tree candidate) + : deferred_diagnostic (loc), + m_candidate (candidate) + { + } + + ~show_candidate_location () + { + inform (location_of (m_candidate), "%qE declared here", m_candidate); + } + + private: + tree m_candidate; +}; + +/* Subclass of deferred_diagnostic, for use when there are multiple candidates + to be suggested by suggest_alternatives_for. + + Emit a series of notes showing the various suggestions. */ + +class suggest_alternatives : public deferred_diagnostic { - vec<tree> candidates = vNULL; - vec<tree> worklist = vNULL; - unsigned limit = PARAM_VALUE (CXX_MAX_NAMESPACES_FOR_DIAGNOSTIC_HELP); - bool limited = false; + public: + suggest_alternatives (location_t loc, vec<tree> candidates) + : deferred_diagnostic (loc), + m_candidates (candidates) + { + } + + ~suggest_alternatives () + { + if (m_candidates.length ()) + { + inform_n (get_location (), m_candidates.length (), + "suggested alternative:", + "suggested alternatives:"); + for (unsigned ix = 0; ix != m_candidates.length (); ix++) + { + tree val = m_candidates[ix]; + + inform (location_of (val), " %qE", val); + } + } + m_candidates.release (); + } + + private: + vec<tree> m_candidates; +}; + +/* A class for encapsulating the result of a search across + multiple namespaces for an unrecognized name seen at a + given source location. */ + +class namespace_hints +{ + public: + namespace_hints (location_t loc, tree name); + + name_hint convert_candidates_to_name_hint (); + name_hint maybe_decorate_with_limit (name_hint); + + private: + location_t m_loc; + tree m_name; + vec<tree> m_candidates; + + /* Value of "--param cxx-max-namespaces-for-diagnostic-help". */ + unsigned m_limit; + + /* Was the limit reached? */ + bool m_limited; +}; + +/* Constructor for namespace_hints. Search namespaces, looking for a match + for unrecognized NAME seen at LOC. */ + +namespace_hints::namespace_hints (location_t loc, tree name) +: m_loc(loc), m_name (name) +{ + auto_vec<tree> worklist; + + m_candidates = vNULL; + m_limited = false; + m_limit = PARAM_VALUE (CXX_MAX_NAMESPACES_FOR_DIAGNOSTIC_HELP); /* Breadth-first search of namespaces. Up to limit namespaces searched (limit zero == unlimited). */ @@ -5342,14 +5457,14 @@ suggest_alternatives_for (location_t location, tree name, name_lookup lookup (name); if (lookup.search_qualified (ns, false)) - candidates.safe_push (lookup.value); + m_candidates.safe_push (lookup.value); - if (!limited) + if (!m_limited) { /* Look for child namespaces. We have to do this indirectly because they are chained in reverse order, which is confusing to the user. */ - vec<tree> children = vNULL; + auto_vec<tree> children; for (tree decl = NAMESPACE_LEVEL (ns)->names; decl; decl = TREE_CHAIN (decl)) @@ -5358,60 +5473,141 @@ suggest_alternatives_for (location_t location, tree name, && !DECL_NAMESPACE_INLINE_P (decl)) children.safe_push (decl); - while (!limited && !children.is_empty ()) + while (!m_limited && !children.is_empty ()) { - if (worklist.length () == limit) - { - /* Unconditionally warn that the search was truncated. */ - inform (location, - "maximum limit of %d namespaces searched for %qE", - limit, name); - limited = true; - } + if (worklist.length () == m_limit) + m_limited = true; else worklist.safe_push (children.pop ()); } - children.release (); } } - worklist.release (); +} - if (candidates.length ()) - { - inform_n (location, candidates.length (), - "suggested alternative:", - "suggested alternatives:"); - for (unsigned ix = 0; ix != candidates.length (); ix++) - { - tree val = candidates[ix]; +/* Drop ownership of m_candidates, using it to generate a name_hint at m_loc + for m_name, an IDENTIFIER_NODE for which name lookup failed. - inform (location_of (val), " %qE", val); - } - candidates.release (); - return; + If m_candidates is non-empty, use it to generate a suggestion and/or + a deferred diagnostic that lists the possible candidate(s). +*/ + +name_hint +namespace_hints::convert_candidates_to_name_hint () +{ + /* How many candidates do we have? */ + + /* If we have just one candidate, issue a name_hint with it as a suggestion + (so that consumers are able to suggest it within the error message and emit + it as a fix-it hint), and with a note showing the candidate's location. */ + if (m_candidates.length () == 1) + { + tree candidate = m_candidates[0]; + /* Clean up CANDIDATES. */ + m_candidates.release (); + return name_hint (expr_to_string (candidate), + new show_candidate_location (m_loc, candidate)); } + else if (m_candidates.length () > 1) + /* If we have more than one candidate, issue a name_hint without a single + "suggestion", but with a deferred diagnostic that lists the + various candidates. This takes ownership of m_candidates. */ + return name_hint (NULL, new suggest_alternatives (m_loc, m_candidates)); + /* Otherwise, m_candidates ought to be empty, so no cleanup is necessary. */ + gcc_assert (m_candidates.length () == 0); + gcc_assert (m_candidates == vNULL); + + return name_hint (); +} + +/* If --param cxx-max-namespaces-for-diagnostic-help was reached, + then we want to emit a note about after the error, but before + any other deferred diagnostics. + + Handle this by figuring out what hint is needed, then optionally + decorating HINT with a namespace_limit_reached wrapper. */ + +name_hint +namespace_hints::maybe_decorate_with_limit (name_hint hint) +{ + if (m_limited) + return name_hint (hint.suggestion (), + new namespace_limit_reached (m_loc, m_limit, + m_name, + hint.take_deferred ())); + else + return hint; +} + +/* Generate a name_hint at LOCATION for NAME, an IDENTIFIER_NODE for which + name lookup failed. + + Search through all available namespaces and generate a suggestion and/or + a deferred diagnostic that lists possible candidate(s). + + If no exact matches are found, and SUGGEST_MISSPELLINGS is true, then also + look for near-matches and suggest the best near-match, if there is one. + + If nothing is found, then an empty name_hint is returned. */ + +name_hint +suggest_alternatives_for (location_t location, tree name, + bool suggest_misspellings) +{ + /* First, search for exact matches in other namespaces. */ + namespace_hints ns_hints (location, name); + name_hint result = ns_hints.convert_candidates_to_name_hint (); + + /* Otherwise, try other approaches. */ + if (!result) + result = suggest_alternatives_for_1 (location, name, suggest_misspellings); + + return ns_hints.maybe_decorate_with_limit (result); +} + +/* The second half of suggest_alternatives_for, for when no exact matches + were found in other namespaces. */ + +static name_hint +suggest_alternatives_for_1 (location_t location, tree name, + bool suggest_misspellings) +{ /* No candidates were found in the available namespaces. */ /* If there's a "using namespace std;" active, and this is one of the most common "std::" names, then it's probably a missing #include. */ if (has_using_namespace_std_directive_p ()) - if (maybe_suggest_missing_std_header (location, name)) - return; + { + name_hint hint = maybe_suggest_missing_std_header (location, name); + if (hint) + return hint; + } /* Otherwise, consider misspellings. */ if (!suggest_misspellings) - return; - if (name_hint hint = lookup_name_fuzzy (name, FUZZY_LOOKUP_NAME, - location)) - { - /* Show a spelling correction. */ - gcc_rich_location richloc (location); + return name_hint (); - richloc.add_fixit_replace (hint.suggestion ()); - inform (&richloc, "suggested alternative: %qs", hint.suggestion ()); - } + return lookup_name_fuzzy (name, FUZZY_LOOKUP_NAME, location); +} + +/* Generate a name_hint at LOCATION for NAME, an IDENTIFIER_NODE for which + name lookup failed. + + Search through all available namespaces and generate a suggestion and/or + a deferred diagnostic that lists possible candidate(s). + + This is similiar to suggest_alternatives_for, but doesn't fallback to + the other approaches used by that function. */ + +name_hint +suggest_alternatives_in_other_namespaces (location_t location, tree name) +{ + namespace_hints ns_hints (location, name); + + name_hint result = ns_hints.convert_candidates_to_name_hint (); + + return ns_hints.maybe_decorate_with_limit (result); } /* A well-known name within the C++ standard library, returned by @@ -5622,11 +5818,51 @@ get_cxx_dialect_name (enum cxx_dialect dialect) } } -/* Suggest pertinent header files for NAME at LOCATION, for common - names within the "std" namespace. - Return true iff a suggestion was offered. */ +/* Subclass of deferred_diagnostic for use for names in the "std" namespace + that weren't recognized, but for which we know which header it ought to be + in. -static bool + Emit a note either suggesting the header to be included, or noting that + the current dialect is too early for the given name. */ + +class missing_std_header : public deferred_diagnostic +{ + public: + missing_std_header (location_t loc, + const char *name_str, + const std_name_hint *header_hint) + : deferred_diagnostic (loc), + m_name_str (name_str), + m_header_hint (header_hint) + {} + ~missing_std_header () + { + gcc_rich_location richloc (get_location ()); + if (cxx_dialect >= m_header_hint->min_dialect) + { + const char *header = m_header_hint->header; + maybe_add_include_fixit (&richloc, header, true); + inform (&richloc, + "%<std::%s%> is defined in header %qs;" + " did you forget to %<#include %s%>?", + m_name_str, header, header); + } + else + inform (&richloc, + "%<std::%s%> is only available from %s onwards", + m_name_str, get_cxx_dialect_name (m_header_hint->min_dialect)); + } + +private: + const char *m_name_str; + const std_name_hint *m_header_hint; +}; + +/* Attempt to generate a name_hint that suggests pertinent header files + for NAME at LOCATION, for common names within the "std" namespace, + or an empty name_hint if this isn't applicable. */ + +static name_hint maybe_suggest_missing_std_header (location_t location, tree name) { gcc_assert (TREE_CODE (name) == IDENTIFIER_NODE); @@ -5634,62 +5870,49 @@ maybe_suggest_missing_std_header (location_t location, tree name) const char *name_str = IDENTIFIER_POINTER (name); const std_name_hint *header_hint = get_std_name_hint (name_str); if (!header_hint) - return false; + return name_hint (); - gcc_rich_location richloc (location); - if (cxx_dialect >= header_hint->min_dialect) - { - const char *header = header_hint->header; - maybe_add_include_fixit (&richloc, header, true); - inform (&richloc, - "%<std::%s%> is defined in header %qs;" - " did you forget to %<#include %s%>?", - name_str, header, header); - } - else - { - inform (&richloc, - "%<std::%s%> is only available from %s onwards", - name_str, get_cxx_dialect_name (header_hint->min_dialect)); - } - return true; + return name_hint (NULL, new missing_std_header (location, name_str, + header_hint)); } -/* If SCOPE is the "std" namespace, then suggest pertinent header - files for NAME at LOCATION. - Return true iff a suggestion was offered. */ +/* Attempt to generate a name_hint that suggests a missing header file + for NAME within SCOPE at LOCATION, or an empty name_hint if this isn't + applicable. */ -static bool +static name_hint maybe_suggest_missing_header (location_t location, tree name, tree scope) { if (scope == NULL_TREE) - return false; + return name_hint (); if (TREE_CODE (scope) != NAMESPACE_DECL) - return false; + return name_hint (); /* We only offer suggestions for the "std" namespace. */ if (scope != std_node) - return false; + return name_hint (); return maybe_suggest_missing_std_header (location, name); } -/* Look for alternatives for NAME, an IDENTIFIER_NODE for which name - lookup failed within the explicitly provided SCOPE. Suggest the - the best meaningful candidates (if any) as a fix-it hint. - Return true iff a suggestion was provided. */ +/* Generate a name_hint at LOCATION for NAME, an IDENTIFIER_NODE for which name + lookup failed within the explicitly provided SCOPE. -bool + Suggest the the best meaningful candidates (if any), otherwise + an empty name_hint is returned. */ + +name_hint suggest_alternative_in_explicit_scope (location_t location, tree name, tree scope) { /* Something went very wrong; don't suggest anything. */ if (name == error_mark_node) - return false; + return name_hint (); /* Resolve any namespace aliases. */ scope = ORIGINAL_NAMESPACE (scope); - if (maybe_suggest_missing_header (location, name, scope)) - return true; + name_hint hint = maybe_suggest_missing_header (location, name, scope); + if (hint) + return hint; cp_binding_level *level = NAMESPACE_LEVEL (scope); @@ -5699,15 +5922,9 @@ suggest_alternative_in_explicit_scope (location_t location, tree name, /* See if we have a good suggesion for the user. */ const char *fuzzy_name = bm.get_best_meaningful_candidate (); if (fuzzy_name) - { - gcc_rich_location richloc (location); - richloc.add_fixit_replace (fuzzy_name); - inform (&richloc, "suggested alternative: %qs", - fuzzy_name); - return true; - } + return name_hint (fuzzy_name, NULL); - return false; + return name_hint (); } /* Look up NAME (an IDENTIFIER_NODE) in SCOPE (either a NAMESPACE_DECL diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index f5e4fa4..28c9edc 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -43,7 +43,7 @@ along with GCC; see the file COPYING3. If not see #include "context.h" #include "gcc-rich-location.h" #include "tree-iterator.h" -#include "c-family/name-hint.h" +#include "cp-name-hint.h" /* The lexer. */ @@ -3301,13 +3301,13 @@ cp_parser_diagnose_invalid_type_name (cp_parser *parser, tree id, name_hint hint; if (TREE_CODE (id) == IDENTIFIER_NODE) hint = lookup_name_fuzzy (id, FUZZY_LOOKUP_TYPENAME, location); - if (hint) + if (const char *suggestion = hint.suggestion ()) { gcc_rich_location richloc (location); - richloc.add_fixit_replace (hint.suggestion ()); + richloc.add_fixit_replace (suggestion); error_at (&richloc, "%qE does not name a type; did you mean %qs?", - id, hint.suggestion ()); + id, suggestion); } else error_at (location, "%qE does not name a type", id); @@ -3373,23 +3373,54 @@ cp_parser_diagnose_invalid_type_name (cp_parser *parser, tree id, if (TREE_CODE (parser->scope) == NAMESPACE_DECL) { auto_diagnostic_group d; + name_hint hint; + if (!DECL_P (decl) + && decl == error_mark_node) + hint = suggest_alternative_in_explicit_scope (location, id, + parser->scope); + const char *suggestion = hint.suggestion (); + gcc_rich_location richloc (location_of (id)); + if (suggestion) + richloc.add_fixit_replace (suggestion); if (cp_lexer_next_token_is (parser->lexer, CPP_LESS)) - error_at (location_of (id), - "%qE in namespace %qE does not name a template type", - id, parser->scope); + { + if (suggestion) + error_at (&richloc, + "%qE in namespace %qE does not name a template" + " type; did you mean %qs?", + id, parser->scope, suggestion); + else + error_at (&richloc, + "%qE in namespace %qE does not name a template type", + id, parser->scope); + } else if (TREE_CODE (id) == TEMPLATE_ID_EXPR) - error_at (location_of (id), - "%qE in namespace %qE does not name a template type", - TREE_OPERAND (id, 0), parser->scope); + { + if (suggestion) + error_at (&richloc, + "%qE in namespace %qE does not name a template" + " type; did you mean %qs?", + TREE_OPERAND (id, 0), parser->scope, suggestion); + else + error_at (&richloc, + "%qE in namespace %qE does not name a template" + " type", + TREE_OPERAND (id, 0), parser->scope); + } else - error_at (location_of (id), - "%qE in namespace %qE does not name a type", - id, parser->scope); + { + if (suggestion) + error_at (&richloc, + "%qE in namespace %qE does not name a type" + "; did you mean %qs?", + id, parser->scope, suggestion); + else + error_at (&richloc, + "%qE in namespace %qE does not name a type", + id, parser->scope); + } if (DECL_P (decl)) inform (DECL_SOURCE_LOCATION (decl), "%qD declared here", decl); - else if (decl == error_mark_node) - suggest_alternative_in_explicit_scope (location, id, - parser->scope); } else if (CLASS_TYPE_P (parser->scope) && constructor_name_p (id, parser->scope)) @@ -18630,13 +18661,26 @@ cp_parser_namespace_name (cp_parser* parser) if (!cp_parser_uncommitted_to_tentative_parse_p (parser)) { auto_diagnostic_group d; - error_at (token->location, "%qD is not a namespace-name", identifier); + name_hint hint; if (namespace_decl == error_mark_node && parser->scope && TREE_CODE (parser->scope) == NAMESPACE_DECL) - suggest_alternative_in_explicit_scope (token->location, identifier, - parser->scope); + hint = suggest_alternative_in_explicit_scope (token->location, + identifier, + parser->scope); + if (const char *suggestion = hint.suggestion ()) + { + gcc_rich_location richloc (token->location); + richloc.add_fixit_replace (suggestion); + error_at (&richloc, + "%qD is not a namespace-name; did you mean %qs?", + identifier, suggestion); + } + else + error_at (token->location, "%qD is not a namespace-name", + identifier); } - cp_parser_error (parser, "expected namespace-name"); + else + cp_parser_error (parser, "expected namespace-name"); namespace_decl = error_mark_node; } diff --git a/gcc/testsuite/c-c++-common/spellcheck-reserved.c b/gcc/testsuite/c-c++-common/spellcheck-reserved.c index 79b6532..ed292f2 100644 --- a/gcc/testsuite/c-c++-common/spellcheck-reserved.c +++ b/gcc/testsuite/c-c++-common/spellcheck-reserved.c @@ -30,8 +30,7 @@ void test (const char *buf, char ch) { __builtin_strtchr (buf, ch); /* { dg-line misspelled_reserved } */ /* { dg-warning "did you mean '__builtin_strchr'" "" { target c } misspelled_reserved } */ - /* { dg-error "not declared" "" { target c++ } misspelled_reserved } */ - /* { dg-message "'__builtin_strrchr'" "" { target c++ } misspelled_reserved } */ + /* { dg-error "'__builtin_strtchr' was not declared in this scope; did you mean '__builtin_strrchr'\\?" "" { target c++ } misspelled_reserved } */ } /* Similarly for a name that begins with a single underscore. */ @@ -40,8 +39,7 @@ void test_2 (const char *buf, char ch) { _builtin_strchr (buf, ch); /* { dg-line misspelled_one_underscore } */ /* { dg-warning "did you mean '__builtin_strchr'" "" { target c } misspelled_one_underscore } */ - /* { dg-error "not declared" "" { target c++ } misspelled_one_underscore } */ - /* { dg-message "'__builtin_strchr'" "" { target c++ } misspelled_one_underscore } */ + /* { dg-error "'_builtin_strchr' was not declared in this scope; did you mean '__builtin_strchr'\\?" "" { target c++ } misspelled_one_underscore } */ } /* Verify that we can correct "__FILE_" to "__FILE__". */ @@ -50,6 +48,5 @@ const char * test_3 (void) { return __FILE_; /* { dg-line misspelled__FILE_ } */ /* { dg-error "did you mean '__FILE__'" "" { target c } misspelled__FILE_ } */ - /* { dg-error "not declared" "" { target c++ } misspelled__FILE_ } */ - /* { dg-message "'__FILE__'" "" { target c++ } misspelled__FILE_ } */ + /* { dg-error "'__FILE_' was not declared in this scope; did you mean '__FILE__'\\?" "" { target c++ } misspelled__FILE_ } */ } diff --git a/gcc/testsuite/g++.dg/ext/builtin3.C b/gcc/testsuite/g++.dg/ext/builtin3.C index 6becaa0..31d2ac6 100644 --- a/gcc/testsuite/g++.dg/ext/builtin3.C +++ b/gcc/testsuite/g++.dg/ext/builtin3.C @@ -9,6 +9,5 @@ extern "C" int printf(char*, ...); // { dg-message "std::printf" } } void foo() { - printf("abc"); // { dg-error "3:'printf' was not declared" } - // { dg-message "suggested alternative" "suggested alternative" { target *-*-* } .-1 } + printf("abc"); // { dg-error "3:'printf' was not declared in this scope; did you mean 'std::printf'\\?" } } diff --git a/gcc/testsuite/g++.dg/lookup/error1.C b/gcc/testsuite/g++.dg/lookup/error1.C index d2741fb..1f267e7 100644 --- a/gcc/testsuite/g++.dg/lookup/error1.C +++ b/gcc/testsuite/g++.dg/lookup/error1.C @@ -3,8 +3,7 @@ // { dg-do compile } namespace N { int i; } // { dg-message "N::i" } -void foo() { i; } // { dg-error "not declared" } - // { dg-message "suggested alternative" "suggested alternative" { target *-*-* } .-1 } +void foo() { i; } // { dg-error "'i' was not declared in this scope; did you mean 'N::i'\\?" } using namespace N; void bar() { i; } diff --git a/gcc/testsuite/g++.dg/lookup/pr77549.C b/gcc/testsuite/g++.dg/lookup/pr77549.C index b4b8d0e..af7c630 100644 --- a/gcc/testsuite/g++.dg/lookup/pr77549.C +++ b/gcc/testsuite/g++.dg/lookup/pr77549.C @@ -22,8 +22,8 @@ void f2 () { using N::bar; - baz++; // { dg-error "'baz' was not declared in this scope" } -} // { dg-message "note: suggested alternative: 'bar'" "" { target *-*-* } .-1 } + baz++; // { dg-error "'baz' was not declared in this scope; did you mean 'bar'\\?" } +} int bar () @@ -44,8 +44,8 @@ void f3 () { using M::bar; - baz (); // { dg-error "'baz' was not declared in this scope" } -} // { dg-message "note: suggested alternative: 'bar'" "" { target *-*-* } .-1 } + baz (); // { dg-error "'baz' was not declared in this scope; did you mean 'bar'\\?" } +} namespace O { @@ -70,7 +70,6 @@ f4 () { using O::foo; using P::bar; - fooo (); // { dg-error "'fooo' was not declared in this scope" } - // { dg-message "note: suggested alternative: 'foo'" "" { target *-*-* } .-1 } - baz (); // { dg-error "'baz' was not declared in this scope" } -} // { dg-message "note: suggested alternative: 'bar'" "" { target *-*-* } .-1 } + fooo (); // { dg-error "'fooo' was not declared in this scope; did you mean 'foo'\\?" } + baz (); // { dg-error "'baz' was not declared in this scope; did you mean 'bar'\\?" } +} diff --git a/gcc/testsuite/g++.dg/lookup/pr80913.C b/gcc/testsuite/g++.dg/lookup/pr80913.C index a7866bc..028e61a 100644 --- a/gcc/testsuite/g++.dg/lookup/pr80913.C +++ b/gcc/testsuite/g++.dg/lookup/pr80913.C @@ -6,6 +6,5 @@ struct meminfo {}; void frob () { - meminf (); // { dg-error "not declared" } - // { dg-message "suggested alternative" "" { target *-*-* } .-1 } + meminf (); // { dg-error "'meminf' was not declared in this scope; did you mean 'meminfo'\\?" } } diff --git a/gcc/testsuite/g++.dg/lookup/suggestions1.C b/gcc/testsuite/g++.dg/lookup/suggestions1.C index da98d11c..47126a3 100644 --- a/gcc/testsuite/g++.dg/lookup/suggestions1.C +++ b/gcc/testsuite/g++.dg/lookup/suggestions1.C @@ -1,8 +1,6 @@ // { dg-do compile } -namespace N { namespace M { int foo; } } // { dg-message "N::M::foo" } -int f (void) { return N::foo; } // { dg-error "not a member" } -// { dg-message "suggested alternative" "missing namespace" { target *-*-* } .-1 } +namespace N { namespace M { int foo; } } // { dg-message "'N::M::foo' declared here" } +int f (void) { return N::foo; } // { dg-error "'foo' is not a member of 'N'; did you mean 'N::M::foo'\\?" } -int g (void) { return ::foo; } // { dg-error "not been declared" } -// { dg-message "suggested alternative" "omitted namespace" { target *-*-* } .-1 } +int g (void) { return ::foo; } // { dg-error "'::foo' has not been declared; did you mean 'N::M::foo'\\?" } diff --git a/gcc/testsuite/g++.dg/lookup/suggestions2.C b/gcc/testsuite/g++.dg/lookup/suggestions2.C new file mode 100644 index 0000000..900439f --- /dev/null +++ b/gcc/testsuite/g++.dg/lookup/suggestions2.C @@ -0,0 +1,128 @@ +/* Suggestions involving namespaces. + + The long variable names in this test case are close enough that we offer + spellchecking suggestions for them in the given namespace, with fix-it + hints. + + The short variable names don't get spellchecking suggestions; instead + we offer suggestions about other namespaces. However, as we don't + reliably have location information about the namespace part of the name, + we shouldn't offer fix-it hints for such cases. */ + +// { dg-do compile } +// { dg-options "-fdiagnostics-show-caret" } + +namespace outer_ns { + int var_in_outer_ns; // { dg-line decl_of_var_in_outer_ns } + int o; // { dg-line decl_of_o } + + namespace inner_ns_a { + int var_in_inner_ns_a; + int a; // { dg-line decl_of_a } + } + namespace inner_ns_b { + int var_in_inner_ns_b; + int b; // { dg-line decl_of_b } + } +} + +/* This one should get spell-corrected within the same namespace, + with a fix-it hint. */ + +int test_1_long (void) { + return outer_ns::var_in_inner_ns_a; // { dg-error "did you mean 'var_in_outer_ns'" } + /* { dg-begin-multiline-output "" } + return outer_ns::var_in_inner_ns_a; + ^~~~~~~~~~~~~~~~~ + var_in_outer_ns + { dg-end-multiline-output "" } */ +} + +/* This one should get a namespace suggestion (child namespace), + with no fix-it hint. */ + +int test_1_short (void) { + return outer_ns::a; // { dg-error "did you mean 'outer_ns::inner_ns_a::a'" } + /* { dg-begin-multiline-output "" } + return outer_ns::a; + ^ + { dg-end-multiline-output "" } */ + // { dg-message "declared here" "" { target *-*-*} decl_of_a } + /* { dg-begin-multiline-output "" } + int a; + ^ + { dg-end-multiline-output "" } */ +} + +/* This one should get spell-corrected within the same namespace, + with a fix-it hint. */ + +int test_2_long (void) { + return outer_ns::inner_ns_a::var_in_outer_ns; // { dg-error "did you mean 'var_in_inner_ns_a'" } + /* { dg-begin-multiline-output "" } + return outer_ns::inner_ns_a::var_in_outer_ns; + ^~~~~~~~~~~~~~~ + var_in_inner_ns_a + { dg-end-multiline-output "" } */ +} + +/* This one should get a namespace suggestion (parent namespace), + with no fix-it hint. */ + +int test_2_short (void) { + return outer_ns::inner_ns_a::o; // { dg-error "did you mean 'outer_ns::o'" } + /* { dg-begin-multiline-output "" } + return outer_ns::inner_ns_a::o; + ^ + { dg-end-multiline-output "" } */ + // { dg-message "declared here" "" { target *-*-*} decl_of_o } + /* { dg-begin-multiline-output "" } + int o; + ^ + { dg-end-multiline-output "" } */ +} + +/* This one should get spell-corrected within the same namespace, + with a fix-it hint. */ + +int test_3_long (void) { + return outer_ns::inner_ns_a::var_in_inner_ns_b; // { dg-error "did you mean 'var_in_inner_ns_a'" } + /* { dg-begin-multiline-output "" } + return outer_ns::inner_ns_a::var_in_inner_ns_b; + ^~~~~~~~~~~~~~~~~ + var_in_inner_ns_a + { dg-end-multiline-output "" } */ +} + +/* This one should get a namespace suggestion (sibling namespace), + with no fix-it hint. */ + +int test_3_short (void) { + return outer_ns::inner_ns_a::b; // { dg-error "did you mean 'outer_ns::inner_ns_b::b'" } + /* { dg-begin-multiline-output "" } + return outer_ns::inner_ns_a::b; + ^ + { dg-end-multiline-output "" } */ + // { dg-message "declared here" "" { target *-*-*} decl_of_b } + /* { dg-begin-multiline-output "" } + int b; + ^ + { dg-end-multiline-output "" } */ +} + +/* This one should get a namespace suggestion, from the global ns to a child ns. + It should get a fix-it hint. */ + +int test_4_long (void) { + return ::var_in_outer_ns; // { dg-error "did you mean 'outer_ns::var_in_outer_ns'" } + /* { dg-begin-multiline-output "" } + return ::var_in_outer_ns; + ^~~~~~~~~~~~~~~ + outer_ns::var_in_outer_ns + { dg-end-multiline-output "" } */ + // { dg-message "declared here" "" { target *-*-*} decl_of_var_in_outer_ns } + /* { dg-begin-multiline-output "" } + int var_in_outer_ns; + ^~~~~~~~~~~~~~~ + { dg-end-multiline-output "" } */ +} diff --git a/gcc/testsuite/g++.dg/overload/koenig1.C b/gcc/testsuite/g++.dg/overload/koenig1.C index 3c1c293..5508061 100644 --- a/gcc/testsuite/g++.dg/overload/koenig1.C +++ b/gcc/testsuite/g++.dg/overload/koenig1.C @@ -13,7 +13,6 @@ void g () { B *bp; N::A *ap; - f (bp); // { dg-error "3:'f' was not declared" } - // { dg-message "suggested alternative" "suggested alternative" { target *-*-* } .-1 } + f (bp); // { dg-error "3:'f' was not declared in this scope; did you mean 'N::f'" } f (ap); } diff --git a/gcc/testsuite/g++.dg/spellcheck-identifiers-2.C b/gcc/testsuite/g++.dg/spellcheck-identifiers-2.C index 59a8ec5..67ae52b 100644 --- a/gcc/testsuite/g++.dg/spellcheck-identifiers-2.C +++ b/gcc/testsuite/g++.dg/spellcheck-identifiers-2.C @@ -9,12 +9,7 @@ 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 } + return ssacnf (p, "%d", &i); /* { dg-error "10: .ssacnf. was not declared in this scope; did you mean 'sscafn'\\?" } */ /* { dg-begin-multiline-output "" } return ssacnf (p, "%d", &i); ^~~~~~ @@ -29,12 +24,7 @@ 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 } + return sacnf ("%d", &i); /* { dg-error "10: .sacnf. was not declared in this scope; did you mean 'scanf'\\?" } */ /* { dg-begin-multiline-output "" } return sacnf ("%d", &i); ^~~~~ diff --git a/gcc/testsuite/g++.dg/spellcheck-identifiers.C b/gcc/testsuite/g++.dg/spellcheck-identifiers.C index e4a606e..a9521af 100644 --- a/gcc/testsuite/g++.dg/spellcheck-identifiers.C +++ b/gcc/testsuite/g++.dg/spellcheck-identifiers.C @@ -9,12 +9,7 @@ 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 } + gtk_widget_showall (w); // { dg-error "3: 'gtk_widget_showall' was not declared in this scope; did you mean 'gtk_widget_show_all'\\?" } /* { dg-begin-multiline-output "" } gtk_widget_showall (w); ^~~~~~~~~~~~~~~~~~ @@ -23,24 +18,14 @@ test_1 (GtkWidget *w) /* 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 } + gtk_widget_showall_ (w); // { dg-error "3: 'gtk_widget_showall_' was not declared in this scope; did you mean 'gtk_widget_show_all'\\?" } /* { dg-begin-multiline-output "" } gtk_widget_showall_ (w); ^~~~~~~~~~~~~~~~~~~ gtk_widget_show_all { dg-end-multiline-output "" } */ - GtkWidgetShowAll (w); // { dg-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 } + GtkWidgetShowAll (w); // { dg-error "3: 'GtkWidgetShowAll' was not declared in this scope; did you mean 'gtk_widget_show_all'\\?" } /* { dg-begin-multiline-output "" } GtkWidgetShowAll (w); ^~~~~~~~~~~~~~~~ @@ -51,12 +36,7 @@ test_1 (GtkWidget *w) 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 } + return parma * parma; // { dg-error "10: 'parma' was not declared in this scope; did you mean 'param'\\?" } /* { dg-begin-multiline-output "" } return parma * parma; ^~~~~ @@ -69,12 +49,7 @@ test_2 (int param) 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 } + return MACRAME (i); // { dg-error "10: 'MACRAME' was not declared in this scope; did you mean 'MACRO'\\?" } /* { dg-begin-multiline-output "" } return MACRAME (i); ^~~~~~~ @@ -87,12 +62,7 @@ test_3 (int i) 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 } + return IDENTIFIER_PTR (node); // { dg-error "10: 'IDENTIFIER_PTR' was not declared in this scope; did you mean 'IDENTIFIER_POINTER'\\?" } /* { dg-begin-multiline-output "" } return IDENTIFIER_PTR (node); ^~~~~~~~~~~~~~ @@ -104,12 +74,7 @@ test_4 (int node) 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 } + return __LINE_; /* { dg-error "10: '__LINE_' was not declared in this scope; did you mean '__LINE__'\\?" } /* { dg-begin-multiline-output "" } return __LINE_; ^~~~~~~ @@ -118,12 +83,7 @@ test_5 (void) } #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 } +int array[MAX_ITEM]; // { dg-error "11: 'MAX_ITEM' was not declared in this scope; did you mean 'MAX_ITEMS'\\?" } /* { dg-begin-multiline-output "" } int array[MAX_ITEM]; ^~~~~~~~ @@ -141,29 +101,19 @@ test_6 (enum foo f) { switch (f) { - case FOO_FURST: // { dg-error "10: 'FOO_FURST' was not declared in this scope" } + case FOO_FURST: // { dg-error "10: 'FOO_FURST' was not declared in this scope; did you mean 'FOO_FIRST'\\?" } 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" } + case FOO_SECCOND: // { dg-error "10: 'FOO_SECCOND' was not declared in this scope; did you mean 'FOO_SECOND'\\?" } 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 "" } */ @@ -178,12 +128,7 @@ 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 } + snprint (buffer, 100, "%i of %i", i, j); // { dg-error "3: 'snprint' was not declared in this scope; did you mean 'snprintf'\\?" } /* { dg-begin-multiline-output "" } snprint (buffer, 100, "%i of %i", i, j); ^~~~~~~ @@ -196,12 +141,7 @@ 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 } + return locale; // { dg-error "10: 'locale' was not declared in this scope; did you mean 'local'\\?" } /* { dg-begin-multiline-output "" } return locale; ^~~~~~ @@ -226,12 +166,7 @@ public: 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 } + return m_food; // { dg-error "10: 'm_food' was not declared in this scope; did you mean 'm_foo'\\?" } /* { dg-begin-multiline-output "" } return m_food; ^~~~~~ @@ -241,12 +176,7 @@ int base::test_method_1 () 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 } + return m_food; // { dg-error "10: 'm_food' was not declared in this scope; did you mean 'm_foo'\\?" } /* { dg-begin-multiline-output "" } return m_food; ^~~~~~ diff --git a/gcc/testsuite/g++.dg/spellcheck-ns.C b/gcc/testsuite/g++.dg/spellcheck-ns.C new file mode 100644 index 0000000..4f7452a --- /dev/null +++ b/gcc/testsuite/g++.dg/spellcheck-ns.C @@ -0,0 +1,22 @@ +// { dg-options "-fdiagnostics-show-caret" } + +namespace outer { + namespace inner_ns { + } + typedef int some_typedef; +} + +using namespace outer; +using namespace outer::inner_ms; // { dg-error "'inner_ms' is not a namespace-name; did you mean 'inner_ns'" } +/* { dg-begin-multiline-output "" } + using namespace outer::inner_ms; + ^~~~~~~~ + inner_ns + { dg-end-multiline-output "" } */ + +outer::some_typedfe var; // { dg-error "'some_typedfe' in namespace 'outer' does not name a type; did you mean 'some_typedef'" } +/* { dg-begin-multiline-output "" } + outer::some_typedfe var; + ^~~~~~~~~~~~ + some_typedef + { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/g++.dg/spellcheck-pr77829.C b/gcc/testsuite/g++.dg/spellcheck-pr77829.C index 2f75779..1707134 100644 --- a/gcc/testsuite/g++.dg/spellcheck-pr77829.C +++ b/gcc/testsuite/g++.dg/spellcheck-pr77829.C @@ -18,12 +18,7 @@ namespace detail { void fn_1_explicit () { - detail::some_type i; // { dg-error ".some_type. is not a member of .detail." } - // { dg-message "suggested alternative: .some_typedef." "" { target *-*-* } .-1 } - /* { dg-begin-multiline-output "" } - detail::some_type i; - ^~~~~~~~~ - { dg-end-multiline-output "" } */ + detail::some_type i; // { dg-error ".some_type. is not a member of .detail.; did you mean 'some_typedef'\\?" } /* { dg-begin-multiline-output "" } detail::some_type i; ^~~~~~~~~ @@ -35,12 +30,7 @@ namespace detail { void fn_1_implicit () { - some_type i; // { dg-error ".some_type. was not declared in this scope" } - // { dg-message "suggested alternative: .some_typedef." "" { target *-*-* } .-1 } - /* { dg-begin-multiline-output "" } - some_type i; - ^~~~~~~~~ - { dg-end-multiline-output "" } */ + some_type i; // { dg-error ".some_type. was not declared in this scope; did you mean 'some_typedef'\\?" } /* { dg-begin-multiline-output "" } some_type i; ^~~~~~~~~ @@ -54,12 +44,7 @@ void fn_1_implicit () /* Tests of lookup of a function. */ void fn_2_explicit (int i) { - detail::foo(i); // { dg-error ".foo. is not a member of .detail." } - // { dg-message "suggested alternative: ._foo." "" { target *-*-* } .-1 } - /* { dg-begin-multiline-output "" } - detail::foo(i); - ^~~ - { dg-end-multiline-output "" } */ + detail::foo(i); // { dg-error ".foo. is not a member of .detail.; did you mean '_foo'\\?" } /* { dg-begin-multiline-output "" } detail::foo(i); ^~~ @@ -70,12 +55,7 @@ void fn_2_explicit (int i) { namespace detail { void fn_2_implicit (int i) { - foo(i); // { dg-error ".foo. was not declared in this scope" } - // { dg-message "suggested alternative: ._foo." "" { target *-*-* } .-1 } - /* { dg-begin-multiline-output "" } - foo(i); - ^~~ - { dg-end-multiline-output "" } */ + foo(i); // { dg-error ".foo. was not declared in this scope; did you mean '_foo'\\?" } /* { dg-begin-multiline-output "" } foo(i); ^~~ @@ -89,13 +69,7 @@ void fn_2_implicit (int i) { /* Examples using a template. */ void fn_3_explicit (int i) { - detail::something_els(i); // { dg-error ".something_els. is not a member of .detail." } - // { dg-message "suggested alternative: .something_else." "" { target *-*-* } .-1 } - /* { dg-begin-multiline-output "" } - detail::something_els(i); - ^~~~~~~~~~~~~ - { dg-end-multiline-output "" } */ - + detail::something_els(i); // { dg-error ".something_els. is not a member of .detail.; did you mean 'something_else'\\?" } /* { dg-begin-multiline-output "" } detail::something_els(i); ^~~~~~~~~~~~~ @@ -106,13 +80,7 @@ void fn_3_explicit (int i) { namespace detail { void fn_3_implicit (int i) { - something_els(i); // { dg-error ".something_els. was not declared in this scope" } - // { dg-message "suggested alternative: .something_else." "" { target *-*-* } .-1 } - /* { dg-begin-multiline-output "" } - something_els(i); - ^~~~~~~~~~~~~ - { dg-end-multiline-output "" } */ - + something_els(i); // { dg-error ".something_els. was not declared in this scope; did you mean 'something_else'\\?" } /* { dg-begin-multiline-output "" } something_els(i); ^~~~~~~~~~~~~ @@ -153,12 +121,7 @@ typedef int another_typedef; void fn_5 () { - ::another_type i; // { dg-error ".::another_type. has not been declared" } - // { dg-message "suggested alternative: .another_typedef." "" { target *-*-* } .-1 } - /* { dg-begin-multiline-output "" } - ::another_type i; - ^~~~~~~~~~~~ - { dg-end-multiline-output "" } */ + ::another_type i; // { dg-error ".::another_type. has not been declared; did you mean 'another_typedef'\\?" } /* { dg-begin-multiline-output "" } ::another_type i; ^~~~~~~~~~~~ diff --git a/gcc/testsuite/g++.dg/spellcheck-pr78656.C b/gcc/testsuite/g++.dg/spellcheck-pr78656.C index ded4bb6..ead4e08 100644 --- a/gcc/testsuite/g++.dg/spellcheck-pr78656.C +++ b/gcc/testsuite/g++.dg/spellcheck-pr78656.C @@ -4,12 +4,7 @@ void* allocate(std::size_t n) { - return std::allocate<char>().allocate(n); // { dg-error ".allocate. is not a member of .std." } - // { dg-message "suggested alternative: .allocator." "" { target *-*-* } .-1 } - /* { dg-begin-multiline-output "" } - return std::allocate<char>().allocate(n); - ^~~~~~~~ - { dg-end-multiline-output "" } */ + return std::allocate<char>().allocate(n); // { dg-error ".allocate. is not a member of .std.; did you mean 'allocator'\\?" } /* { dg-begin-multiline-output "" } return std::allocate<char>().allocate(n); ^~~~~~~~ @@ -22,12 +17,7 @@ void* allocate(std::size_t n) void* test_2(std::size_t n) { - return std::alocator<char>().allocate(n); // { dg-error ".alocator. is not a member of .std." } - // { dg-message "suggested alternative: .allocator." "" { target *-*-* } .-1 } - /* { dg-begin-multiline-output "" } - return std::alocator<char>().allocate(n); - ^~~~~~~~ - { dg-end-multiline-output "" } */ + return std::alocator<char>().allocate(n); // { dg-error ".alocator. is not a member of .std.; did you mean 'allocator'\\?" } /* { dg-begin-multiline-output "" } return std::alocator<char>().allocate(n); ^~~~~~~~ diff --git a/gcc/testsuite/g++.dg/spellcheck-pr79298.C b/gcc/testsuite/g++.dg/spellcheck-pr79298.C index 4d7bbf9..7016ee5 100644 --- a/gcc/testsuite/g++.dg/spellcheck-pr79298.C +++ b/gcc/testsuite/g++.dg/spellcheck-pr79298.C @@ -1,5 +1,6 @@ // Ensure that we can offer suggestions for misspellings via a // namespace alias. +// { dg-options "-fdiagnostics-show-caret" } namespace N { int x; int color; } namespace M = N; @@ -8,10 +9,18 @@ namespace O = M; int foo () { return M::y; // { dg-error ".y. is not a member of .M." } + /* { dg-begin-multiline-output "" } + return M::y; + ^ + { dg-end-multiline-output "" } */ } int bar () { - return O::colour; // { dg-error ".colour. is not a member of .O." } - // { dg-message "suggested alternative: .color." "" { target *-*-* } .-1 } + return O::colour; // { dg-error ".colour. is not a member of .O.; did you mean 'color'\\?" } + /* { dg-begin-multiline-output "" } + return O::colour; + ^~~~~~ + color + { dg-end-multiline-output "" } */ } diff --git a/gcc/testsuite/g++.dg/spellcheck-pr80177.C b/gcc/testsuite/g++.dg/spellcheck-pr80177.C index 2ff24e8..7367887 100644 --- a/gcc/testsuite/g++.dg/spellcheck-pr80177.C +++ b/gcc/testsuite/g++.dg/spellcheck-pr80177.C @@ -1,7 +1,12 @@ // { dg-do compile { target c++11 } } +// { dg-options "-fdiagnostics-show-caret" } void pr80177 () { - static_assertion (1 == 0, "1 == 0"); // { dg-error "3: 'static_assertion' was not declared in this scope" } - // { dg-message "3: suggested alternative: 'static_assert'" "" { target *-*-* } .-1 } + static_assertion (1 == 0, "1 == 0"); // { dg-error "3: 'static_assertion' was not declared in this scope; did you mean 'static_assert'\\?" } + /* { dg-begin-multiline-output "" } + static_assertion (1 == 0, "1 == 0"); + ^~~~~~~~~~~~~~~~ + static_assert + { dg-end-multiline-output "" } */ } diff --git a/gcc/testsuite/g++.dg/spellcheck-single-vs-multiple.C b/gcc/testsuite/g++.dg/spellcheck-single-vs-multiple.C new file mode 100644 index 0000000..7d9b87a1 --- /dev/null +++ b/gcc/testsuite/g++.dg/spellcheck-single-vs-multiple.C @@ -0,0 +1,79 @@ +/* Example of namespace suggestions, covering the special-case handling + of where there's one suggestion, vs multiple suggestions. */ + +/* { dg-options "-fdiagnostics-show-caret" } */ + +/* Missing a namespace, where there's one candidate. + Verify that we issue a fix-it hint. */ + +namespace ns1 +{ + void foo_1 (); // { dg-line foo_1_decl } +} + +void test_1 () +{ + foo_1 (); // { dg-error "'foo_1' was not declared in this scope; did you mean 'ns1::foo_1'\\?" } + /* { dg-begin-multiline-output "" } + foo_1 (); + ^~~~~ + ns1::foo_1 + { dg-end-multiline-output "" } */ + // { dg-message "'ns1::foo_1' declared here" "" { target *-*-*} foo_1_decl } + /* { dg-begin-multiline-output "" } + void foo_1 (); + ^~~~~ + { dg-end-multiline-output "" } */ +} + +/* Missing a namespace, where there are multiple candidates. + We don't issue a fix-it hint. */ + +namespace ns2_a +{ + char foo_2 (); // { dg-line ns2_a_foo_2_decl } +} + +namespace ns2_b +{ + int foo_2 (); // { dg-line ns2_b_foo_2_decl } +} + +void test_2 () +{ + foo_2 (); // { dg-line foo_2_usage } + // { dg-error "'foo_2' was not declared in this scope" "" { target *-*-*} foo_2_usage } + /* { dg-begin-multiline-output "" } + foo_2 (); + ^~~~~ + { dg-end-multiline-output "" } */ + // { dg-message "suggested alternatives:" "" { target *-*-*} foo_2_usage } + // { dg-message " 'ns2_a::foo_2'" "" { target *-*-*} ns2_a_foo_2_decl } + /* { dg-begin-multiline-output "" } + char foo_2 (); + ^~~~~ + { dg-end-multiline-output "" } */ + // { dg-message " 'ns2_b::foo_2'" "" { target *-*-*} ns2_b_foo_2_decl } + /* { dg-begin-multiline-output "" } + int foo_2 (); + ^~~~~ + { dg-end-multiline-output "" } */ +} + +/* Misspelling within an explicit namespace. + Verify that we issue a fix-it hint. */ + +namespace ns3 +{ + void foo_3 (); +} + +void test_3 () +{ + ns3::goo_3 (); // { dg-error "'goo_3' is not a member of 'ns3'; did you mean 'foo_3'\\?" } + /* { dg-begin-multiline-output "" } + ns3::goo_3 (); + ^~~~~ + foo_3 + { dg-end-multiline-output "" } */ +} diff --git a/gcc/testsuite/g++.dg/spellcheck-typenames.C b/gcc/testsuite/g++.dg/spellcheck-typenames.C index 01bcf78..25d3f1d 100644 --- a/gcc/testsuite/g++.dg/spellcheck-typenames.C +++ b/gcc/testsuite/g++.dg/spellcheck-typenames.C @@ -9,12 +9,7 @@ void test_2 (singed char e); // { dg-error "21: variable or field 'test_2' decla 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-message "14: 'singed' was not declared in this scope; did you mean 'signed'\\?" "" { target *-*-* } 7 } /* { dg-begin-multiline-output "" } void test_2 (singed char e); ^~~~~~ @@ -26,8 +21,7 @@ void test_3 (car e); // { dg-error "14: variable or field 'test_3' declared void 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-message "14: 'car' was not declared in this scope; did you mean 'char'\\?" "" { target *-*-* } 19 } /* { dg-begin-multiline-output "" } void test_3 (car e); ^~~ diff --git a/gcc/testsuite/g++.dg/template/static10.C b/gcc/testsuite/g++.dg/template/static10.C index 5740ac4..36fed38 100644 --- a/gcc/testsuite/g++.dg/template/static10.C +++ b/gcc/testsuite/g++.dg/template/static10.C @@ -19,6 +19,6 @@ namespace __gnu_debug_def namespace std { template<> void - vector<int, allocator<int> >::swap(vector<int, allocator<int> >&) { } // { dg-error "" } - // { dg-message "suggested alternative" "suggested alternative" { target *-*-* } .-1 } + vector<int, allocator<int> >::swap(vector<int, allocator<int> >&) { } // { dg-error "did you mean 'std::allocator'" } + // { dg-error "" "" { target *-*-*} .-1 } } diff --git a/gcc/testsuite/g++.old-deja/g++.mike/ns5.C b/gcc/testsuite/g++.old-deja/g++.mike/ns5.C index 3d317bf..832b5e8 100644 --- a/gcc/testsuite/g++.old-deja/g++.mike/ns5.C +++ b/gcc/testsuite/g++.old-deja/g++.mike/ns5.C @@ -3,5 +3,4 @@ namespace A { int i = 1; // { dg-message "A::i" } } -int j = i; // { dg-error "" } - // { dg-message "suggested alternative" "suggested alternative" { target *-*-* } .-1 } +int j = i; // { dg-error "'i' was not declared in this scope; did you mean 'A::i'" } diff --git a/gcc/testsuite/g++.old-deja/g++.mike/ns7.C b/gcc/testsuite/g++.old-deja/g++.mike/ns7.C index 14a38b6..6f9e6d2 100644 --- a/gcc/testsuite/g++.old-deja/g++.mike/ns7.C +++ b/gcc/testsuite/g++.old-deja/g++.mike/ns7.C @@ -5,6 +5,5 @@ namespace A { } namespace B { - int j = i; // { dg-error "" } - // { dg-message "suggested alternative" "suggested alternative" { target *-*-* } .-1 } + int j = i; // { dg-error "'i' was not declared in this scope; did you mean 'A::i'" } } diff --git a/gcc/testsuite/g++.old-deja/g++.ns/koenig5.C b/gcc/testsuite/g++.old-deja/g++.ns/koenig5.C index 2246f8a..4461d13 100644 --- a/gcc/testsuite/g++.old-deja/g++.ns/koenig5.C +++ b/gcc/testsuite/g++.old-deja/g++.ns/koenig5.C @@ -14,6 +14,5 @@ void g() foo(new X); // ok -- DR 218 says that we find the global // foo variable first, and therefore do not // perform argument-dependent lookup. - bar(new X); // { dg-error "3:'bar' was not declared" } - // { dg-message "suggested alternative" "suggested alternative" { target *-*-* } .-1 } + bar(new X); // { dg-error "3:'bar' was not declared in this scope; did you mean 'A::bar'" } } diff --git a/gcc/testsuite/g++.old-deja/g++.other/lineno5.C b/gcc/testsuite/g++.old-deja/g++.other/lineno5.C index 63dc0b4..1865f11 100644 --- a/gcc/testsuite/g++.old-deja/g++.other/lineno5.C +++ b/gcc/testsuite/g++.old-deja/g++.other/lineno5.C @@ -15,6 +15,5 @@ namespace tmp { class A { public: - int kaka(tmp::B = b); // { dg-error "" } no b in scope - // { dg-message "suggested alternative" "suggested alternative" { target *-*-* } .-1 } + int kaka(tmp::B = b); // { dg-error "'b' was not declared in this scope; did you mean 'tmp::b'" } }; diff --git a/libstdc++-v3/testsuite/17_intro/using_namespace_std_exp_neg.cc b/libstdc++-v3/testsuite/17_intro/using_namespace_std_exp_neg.cc index 5821f2f..2bf8f12 100644 --- a/libstdc++-v3/testsuite/17_intro/using_namespace_std_exp_neg.cc +++ b/libstdc++-v3/testsuite/17_intro/using_namespace_std_exp_neg.cc @@ -61,5 +61,3 @@ namespace gnu { using namespace std::experimental; // { dg-error "is not a namespace-name" } } - -// { dg-error "expected namespace-name before" "" { target *-*-* } 62 } diff --git a/libstdc++-v3/testsuite/17_intro/using_namespace_std_tr1_neg.cc b/libstdc++-v3/testsuite/17_intro/using_namespace_std_tr1_neg.cc index aad4894..8a1527c 100644 --- a/libstdc++-v3/testsuite/17_intro/using_namespace_std_tr1_neg.cc +++ b/libstdc++-v3/testsuite/17_intro/using_namespace_std_tr1_neg.cc @@ -64,5 +64,3 @@ namespace gnu { using namespace std::tr1; // { dg-error "is not a namespace-name" } } - -// { dg-error "expected namespace-name before" "" { target *-*-* } 65 } -- 1.8.5.3