From f778e503a5409814425be12cdd9721b3e7b0fed5 Mon Sep 17 00:00:00 2001 From: dmalcolm Date: Fri, 20 Jan 2017 14:36:46 +0000 Subject: [PATCH] C++: fix fix-it hints for misspellings within explicit namespaces gcc/cp/ChangeLog: PR c++/77829 PR c++/78656 * cp-tree.h (suggest_alternatives_for): Add bool param. (suggest_alternative_in_explicit_scope): New decl. * error.c (qualified_name_lookup_error): When SCOPE is a namespace that isn't the global one, call new function suggest_alternative_in_explicit_scope, only calling suggest_alternatives_for if it fails, and disabling near match searches fort that case. When SCOPE is the global namespace, pass true for new param to suggest_alternatives_for to allow for fuzzy name lookups. * lex.c (unqualified_name_lookup_error): Pass true for new param to suggest_alternatives_for. * name-lookup.c (consider_binding_level): Add forward decl. (suggest_alternatives_for): Add "suggest_misspellings" param, using it to conditionalize the fuzzy name-lookup code. (suggest_alternative_in_explicit_scope): New function. * parser.c (cp_parser_primary_expression): When calling finish_id_expression, pass location of id_expression rather than that of id_expr_token. (cp_parser_id_expression): Convert local "unqualified_id" from tree to cp_expr to avoid implicitly dropping location information. gcc/testsuite/ChangeLog: PR c++/77829 PR c++/78656 * g++.dg/spellcheck-pr77829.C: New test case. * g++.dg/spellcheck-pr78656.C: New test case. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@244715 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/cp/ChangeLog | 25 +++++ gcc/cp/cp-tree.h | 3 +- gcc/cp/error.c | 5 +- gcc/cp/lex.c | 2 +- gcc/cp/name-lookup.c | 55 ++++++++-- gcc/cp/parser.c | 4 +- gcc/testsuite/ChangeLog | 7 ++ gcc/testsuite/g++.dg/spellcheck-pr77829.C | 167 ++++++++++++++++++++++++++++++ gcc/testsuite/g++.dg/spellcheck-pr78656.C | 39 +++++++ 9 files changed, 293 insertions(+), 14 deletions(-) create mode 100644 gcc/testsuite/g++.dg/spellcheck-pr77829.C create mode 100644 gcc/testsuite/g++.dg/spellcheck-pr78656.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 6554cb7cc75..9b71c90f742 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,28 @@ +2017-01-20 David Malcolm + + PR c++/77829 + PR c++/78656 + * cp-tree.h (suggest_alternatives_for): Add bool param. + (suggest_alternative_in_explicit_scope): New decl. + * error.c (qualified_name_lookup_error): When SCOPE is a namespace + that isn't the global one, call new function + suggest_alternative_in_explicit_scope, only calling + suggest_alternatives_for if it fails, and disabling near match + searches fort that case. When SCOPE is the global namespace, + pass true for new param to suggest_alternatives_for to allow for + fuzzy name lookups. + * lex.c (unqualified_name_lookup_error): Pass true for new param + to suggest_alternatives_for. + * name-lookup.c (consider_binding_level): Add forward decl. + (suggest_alternatives_for): Add "suggest_misspellings" param, + using it to conditionalize the fuzzy name-lookup code. + (suggest_alternative_in_explicit_scope): New function. + * parser.c (cp_parser_primary_expression): When calling + finish_id_expression, pass location of id_expression rather + than that of id_expr_token. + (cp_parser_id_expression): Convert local "unqualified_id" from + tree to cp_expr to avoid implicitly dropping location information. + 2017-01-20 Marek Polacek PR c/64279 diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index f7c7a359a48..b4c4dfa2f6b 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6937,7 +6937,8 @@ extern tree cp_fully_fold (tree); extern void clear_fold_cache (void); /* in name-lookup.c */ -extern void suggest_alternatives_for (location_t, tree); +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); /* in constraint.cc */ diff --git a/gcc/cp/error.c b/gcc/cp/error.c index 72044a9013c..4f4c11df570 100644 --- a/gcc/cp/error.c +++ b/gcc/cp/error.c @@ -3777,11 +3777,12 @@ qualified_name_lookup_error (tree scope, tree name, else if (scope != global_namespace) { error_at (location, "%qD is not a member of %qD", name, scope); - suggest_alternatives_for (location, name); + if (!suggest_alternative_in_explicit_scope (location, name, scope)) + suggest_alternatives_for (location, name, false); } else { error_at (location, "%<::%D%> has not been declared", name); - suggest_alternatives_for (location, name); + suggest_alternatives_for (location, name, true); } } diff --git a/gcc/cp/lex.c b/gcc/cp/lex.c index 797dd9656e7..60a70e9b325 100644 --- a/gcc/cp/lex.c +++ b/gcc/cp/lex.c @@ -441,7 +441,7 @@ unqualified_name_lookup_error (tree name, location_t loc) if (!objc_diagnose_private_ivar (name)) { error_at (loc, "%qD was not declared in this scope", name); - suggest_alternatives_for (loc, name); + suggest_alternatives_for (loc, name, true); } /* 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 3c7559f33db..4004640a60c 100644 --- a/gcc/cp/name-lookup.c +++ b/gcc/cp/name-lookup.c @@ -48,6 +48,10 @@ static bool lookup_using_namespace (tree, struct scope_binding *, tree, tree, int); static bool qualified_lookup_using_namespace (tree, tree, struct scope_binding *, int); +static void consider_binding_level (tree name, best_match &bm, + cp_binding_level *lvl, + bool look_within_fields, + enum lookup_name_fuzzy_kind kind); static tree lookup_type_current_level (tree); static tree push_using_directive (tree); static tree lookup_extern_c_fun_in_all_ns (tree); @@ -4431,10 +4435,13 @@ remove_hidden_names (tree fns) /* Suggest alternatives for NAME, an IDENTIFIER_NODE for which name lookup failed. Search through all available namespaces and print out - possible candidates. */ + 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. */ void -suggest_alternatives_for (location_t location, tree name) +suggest_alternatives_for (location_t location, tree name, + bool suggest_misspellings) { vec candidates = vNULL; vec namespaces_to_search = vNULL; @@ -4481,13 +4488,16 @@ suggest_alternatives_for (location_t location, tree name) or do nothing. */ if (candidates.is_empty ()) { - const char *fuzzy_name = lookup_name_fuzzy (name, FUZZY_LOOKUP_NAME); - if (fuzzy_name) + if (suggest_misspellings) { - gcc_rich_location richloc (location); - richloc.add_fixit_replace (fuzzy_name); - inform_at_rich_loc (&richloc, "suggested alternative: %qs", - fuzzy_name); + const char *fuzzy_name = lookup_name_fuzzy (name, FUZZY_LOOKUP_NAME); + if (fuzzy_name) + { + gcc_rich_location richloc (location); + richloc.add_fixit_replace (fuzzy_name); + inform_at_rich_loc (&richloc, "suggested alternative: %qs", + fuzzy_name); + } } return; } @@ -4502,6 +4512,35 @@ suggest_alternatives_for (location_t location, tree name) candidates.release (); } +/* 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. */ + +bool +suggest_alternative_in_explicit_scope (location_t location, tree name, + tree scope) +{ + cp_binding_level *level = NAMESPACE_LEVEL (scope); + + best_match bm (name); + consider_binding_level (name, bm, level, false, FUZZY_LOOKUP_NAME); + + /* See if we have a good suggesion for the user. */ + tree best_id = bm.get_best_meaningful_candidate (); + if (best_id) + { + const char *fuzzy_name = IDENTIFIER_POINTER (best_id); + gcc_rich_location richloc (location); + richloc.add_fixit_replace (fuzzy_name); + inform_at_rich_loc (&richloc, "suggested alternative: %qs", + fuzzy_name); + return true; + } + + return false; +} + /* Unscoped lookup of a global: iterate over current namespaces, considering using-directives. */ diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 29dcfea283f..4ab0b6974d6 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -5332,7 +5332,7 @@ cp_parser_primary_expression (cp_parser *parser, template_p, done, address_p, template_arg_p, &error_msg, - id_expr_token->location)); + id_expression.get_location ())); if (error_msg) cp_parser_error (parser, error_msg); decl.set_location (id_expr_token->location); @@ -5425,7 +5425,7 @@ cp_parser_id_expression (cp_parser *parser, tree saved_scope; tree saved_object_scope; tree saved_qualifying_scope; - tree unqualified_id; + cp_expr unqualified_id; bool is_template; /* See if the next token is the `template' keyword. */ diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 201395d9e55..fde93280263 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2017-01-20 David Malcolm + + PR c++/77829 + PR c++/78656 + * g++.dg/spellcheck-pr77829.C: New test case. + * g++.dg/spellcheck-pr78656.C: New test case. + 2017-01-20 Marek Polacek PR c/64279 diff --git a/gcc/testsuite/g++.dg/spellcheck-pr77829.C b/gcc/testsuite/g++.dg/spellcheck-pr77829.C new file mode 100644 index 00000000000..2f75779a1b4 --- /dev/null +++ b/gcc/testsuite/g++.dg/spellcheck-pr77829.C @@ -0,0 +1,167 @@ +// { dg-options "-fdiagnostics-show-caret" } + +/* Various tests of name lookup within a namespace, both within an explicitly + given namespace, or implicitly. */ + +namespace detail { + /* Various things to look for. */ + + typedef int some_typedef; + + int _foo(int i) { return i; } + + template + T something_else (T i) { return i; } +} + +/* Tests of lookup of a typedef. */ + +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 "" } */ + /* { dg-begin-multiline-output "" } + detail::some_type i; + ^~~~~~~~~ + some_typedef + { dg-end-multiline-output "" } */ +} + +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 "" } */ + /* { dg-begin-multiline-output "" } + some_type i; + ^~~~~~~~~ + some_typedef + { dg-end-multiline-output "" } */ +} + +} // namespace detail + + +/* 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 "" } */ + /* { dg-begin-multiline-output "" } + detail::foo(i); + ^~~ + _foo + { dg-end-multiline-output "" } */ +} + +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 "" } */ + /* { dg-begin-multiline-output "" } + foo(i); + ^~~ + _foo + { dg-end-multiline-output "" } */ +} + +} // namespace detail + + +/* 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 "" } */ + + /* { dg-begin-multiline-output "" } + detail::something_els(i); + ^~~~~~~~~~~~~ + something_else + { dg-end-multiline-output "" } */ +} + +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 "" } */ + + /* { dg-begin-multiline-output "" } + something_els(i); + ^~~~~~~~~~~~~ + something_else + { dg-end-multiline-output "" } */ +} + +} // namespace detail + + +/* Tests of lookup for which no hint is available. */ + +void fn_4_explicit (int i) { + detail::not_recognized(i); // { dg-error ".not_recognized. is not a member of .detail." } + /* { dg-begin-multiline-output "" } + detail::not_recognized(i); + ^~~~~~~~~~~~~~ + { dg-end-multiline-output "" } */ +} + +namespace detail { + +void fn_4_implicit (int i) +{ + not_recognized(i); // { dg-error ".not_recognized. was not declared in this scope" } + /* { dg-begin-multiline-output "" } + not_recognized(i); + ^~~~~~~~~~~~~~ + { dg-end-multiline-output "" } */ +} + +} // namespace detail + + +/* Test for failed lookup explicitly within global namespace. */ + +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 "" } */ + /* { dg-begin-multiline-output "" } + ::another_type i; + ^~~~~~~~~~~~ + another_typedef + { dg-end-multiline-output "" } */ +} diff --git a/gcc/testsuite/g++.dg/spellcheck-pr78656.C b/gcc/testsuite/g++.dg/spellcheck-pr78656.C new file mode 100644 index 00000000000..ded4bb6be85 --- /dev/null +++ b/gcc/testsuite/g++.dg/spellcheck-pr78656.C @@ -0,0 +1,39 @@ +// { dg-options "-fdiagnostics-show-caret" } + +#include + +void* allocate(std::size_t n) +{ + return std::allocate().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().allocate(n); + ^~~~~~~~ + { dg-end-multiline-output "" } */ + /* { dg-begin-multiline-output "" } + return std::allocate().allocate(n); + ^~~~~~~~ + allocator + { dg-end-multiline-output "" } */ + + // Various errors follow that we don't care about; suppress them: + // { dg-excess-errors "7: " } +} + +void* test_2(std::size_t n) +{ + return std::alocator().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().allocate(n); + ^~~~~~~~ + { dg-end-multiline-output "" } */ + /* { dg-begin-multiline-output "" } + return std::alocator().allocate(n); + ^~~~~~~~ + allocator + { dg-end-multiline-output "" } */ + + // Various errors follow that we don't care about; suppress them: + // { dg-excess-errors "25: " } +} -- 2.11.4.GIT