On Thu, 2018-03-29 at 15:25 -0400, Jason Merrill wrote: > On Thu, Mar 22, 2018 at 7:44 PM, David Malcolm <dmalc...@redhat.com> > wrote: > > We provide fix-it hints for the most common "std" names when an > > explicit > > "std::" prefix is present, however we don't yet provide fix-it > > hints for > > this implicit case: > > > > using namespace std; > > void f() { cout << "test"; } > > > > for which we emit: > > > > t.cc: In function 'void f()': > > t.cc:2:13: error: 'cout' was not declared in this scope > > void f() { cout << "test"; } > > ^~~~ > > > > This patch detects if a "using namespace std;" directive is present > > in the current namespace, and if so, offers a suggestion for > > unrecognized names that are in our list of common "std" names: > > > > t.cc: In function 'void f()': > > t.cc:2:13: error: 'cout' was not declared in this scope > > void f() { cout << "test"; } > > ^~~~ > > t.cc:2:13: note: 'std::cout' is defined in header '<iostream>'; > > did you forget to '#include <iostream>'? > > +#include <iostream> > > using namespace std; > > void f() { cout << "test"; } > > ^~~~ > > > > Is there a better way to test for the using directive? > > > > Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu. > > > > OK for trunk? > > > > gcc/cp/ChangeLog: > > PR c++/85021 > > * name-lookup.c (has_using_namespace_std_directive_p): New > > function. > > (suggest_alternatives_for): Simplify if/else logic using > > early > > returns. If no candidates were found, and there's a > > "using namespace std;" directive, call > > maybe_suggest_missing_std_header. > > (maybe_suggest_missing_header): Split later part of the > > function > > into.. > > (maybe_suggest_missing_std_header): New. > > > > gcc/testsuite/ChangeLog: > > PR c++/85021 > > * g++.dg/lookup/missing-std-include-7.C: New test. > > --- > > gcc/cp/name-lookup.c | 68 > > +++++++++++++++++----- > > .../g++.dg/lookup/missing-std-include-7.C | 16 +++++ > > 2 files changed, 70 insertions(+), 14 deletions(-) > > create mode 100644 gcc/testsuite/g++.dg/lookup/missing-std- > > include-7.C > > > > diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c > > index 061729a..4eb980e 100644 > > --- a/gcc/cp/name-lookup.c > > +++ b/gcc/cp/name-lookup.c > > @@ -41,6 +41,7 @@ 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); > > > > /* Create an overload suitable for recording an artificial > > TYPE_DECL > > and another decl. We use this machanism to implement the > > struct > > @@ -5327,6 +5328,23 @@ qualify_lookup (tree val, int flags) > > return true; > > } > > > > +/* Is there a "using namespace std;" directive within the current > > + namespace? */ > > + > > +static bool > > +has_using_namespace_std_directive_p () > > +{ > > + vec<tree, va_gc> *usings = DECL_NAMESPACE_USING > > (current_namespace); > > Checking in just the current namespace won't find a "using namespace > std" in an inner or outer scope; I think you want to add something to > name-lookup.c that iterates over the current enclosing scopes like > name_lookup::search_unqualified. Nathan can probably be more > specific. > > Jason
Thanks. Here's an updated version of the patch. Rather than just search in DECL_NAMESPACE_USING (current_namespace), this version mimics name_lookup::search_unqualified by searching for local using-directives in the current_binding_level until it reaches a sk_namespace, and then searching in current_namespace and its ancestors. (and builds out the test coverage accordingly) Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu; adds 57 PASS results to g++.sum. OK for trunk? gcc/cp/ChangeLog: PR c++/85021 * name-lookup.c (using_directives_contain_std_p): New function. (has_using_namespace_std_directive_p): New function. (suggest_alternatives_for): Simplify if/else logic using early returns. If no candidates were found, and there's a "using namespace std;" directive, call maybe_suggest_missing_std_header. (maybe_suggest_missing_header): Split later part of the function into.. (maybe_suggest_missing_std_header): New. gcc/testsuite/ChangeLog: PR c++/85021 * g++.dg/lookup/missing-std-include-7.C: New test. --- gcc/cp/name-lookup.c | 93 ++++++++++++++++--- .../g++.dg/lookup/missing-std-include-7.C | 100 +++++++++++++++++++++ 2 files changed, 179 insertions(+), 14 deletions(-) create mode 100644 gcc/testsuite/g++.dg/lookup/missing-std-include-7.C diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c index 9b5db3d..a9f3094 100644 --- a/gcc/cp/name-lookup.c +++ b/gcc/cp/name-lookup.c @@ -41,6 +41,7 @@ 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); /* Create an overload suitable for recording an artificial TYPE_DECL and another decl. We use this machanism to implement the struct @@ -5330,6 +5331,48 @@ qualify_lookup (tree val, int flags) return true; } +/* Is there a "using namespace std;" directive within USINGS? */ + +static bool +using_directives_contain_std_p (vec<tree, va_gc> *usings) +{ + if (!usings) + return false; + + for (unsigned ix = usings->length (); ix--;) + if ((*usings)[ix] == std_node) + return true; + + return false; +} + +/* Is there a "using namespace std;" directive within the current + namespace (or its ancestors)? + Compare with name_lookup::search_unqualified. */ + +static bool +has_using_namespace_std_directive_p () +{ + /* Look at local using-directives. */ + for (cp_binding_level *level = current_binding_level; + level->kind != sk_namespace; + level = level->level_chain) + if (using_directives_contain_std_p (level->using_directives)) + return true; + + /* Look at this namespace and its ancestors. */ + for (tree scope = current_namespace; scope; scope = CP_DECL_CONTEXT (scope)) + { + if (using_directives_contain_std_p (DECL_NAMESPACE_USING (scope))) + return true; + + if (scope == global_namespace) + break; + } + + 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 @@ -5400,11 +5443,23 @@ suggest_alternatives_for (location_t location, tree name, inform (location_of (val), " %qE", val); } candidates.release (); + return; } - else if (!suggest_misspellings) - ; - else if (name_hint hint = lookup_name_fuzzy (name, FUZZY_LOOKUP_NAME, - location)) + + /* 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; + + /* 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); @@ -5512,20 +5567,13 @@ get_std_name_hint (const char *name) return NULL; } -/* If SCOPE is the "std" namespace, then suggest pertinent header - files for NAME at LOCATION. +/* Suggest pertinent header files for NAME at LOCATION, for common + names within the "std" namespace. Return true iff a suggestion was offered. */ static bool -maybe_suggest_missing_header (location_t location, tree name, tree scope) +maybe_suggest_missing_std_header (location_t location, tree name) { - if (scope == NULL_TREE) - return false; - if (TREE_CODE (scope) != NAMESPACE_DECL) - return false; - /* We only offer suggestions for the "std" namespace. */ - if (scope != std_node) - return false; gcc_assert (TREE_CODE (name) == IDENTIFIER_NODE); const char *name_str = IDENTIFIER_POINTER (name); @@ -5542,6 +5590,23 @@ maybe_suggest_missing_header (location_t location, tree name, tree scope) return true; } +/* If SCOPE is the "std" namespace, then suggest pertinent header + files for NAME at LOCATION. + Return true iff a suggestion was offered. */ + +static bool +maybe_suggest_missing_header (location_t location, tree name, tree scope) +{ + if (scope == NULL_TREE) + return false; + if (TREE_CODE (scope) != NAMESPACE_DECL) + return false; + /* We only offer suggestions for the "std" namespace. */ + if (scope != std_node) + return false; + 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. diff --git a/gcc/testsuite/g++.dg/lookup/missing-std-include-7.C b/gcc/testsuite/g++.dg/lookup/missing-std-include-7.C new file mode 100644 index 0000000..4c87c8c --- /dev/null +++ b/gcc/testsuite/g++.dg/lookup/missing-std-include-7.C @@ -0,0 +1,100 @@ +/* PR c++/85021: Verify that we suggest missing headers for common names in std:: + if there's a "using namespace std;" active. */ + +/* No using-directive. */ + +void test_1 () +{ + cout << "test"; // { dg-error "'cout' was not declared in this scope" } + // { dg-bogus "'<iostream>'" "" { target *-*-* } .-1 } +} + +/* Local using-directive. */ + +void test_2 () +{ + using namespace std; + cout << "test"; // { dg-error "'cout' was not declared in this scope" } + // { dg-message "'std::cout' is defined in header '<iostream>'" "" { target *-*-* } .-1 } +} + +/* Local using-directive, but not of "std". */ + +namespace not_std {} +void test_3 () +{ + using namespace not_std; + cout << "test"; // { dg-error "'cout' was not declared in this scope" } + // { dg-bogus "'<iostream>'" "" { target *-*-* } .-1 } +} + +/* Local using-directive in wrong block. */ + +void test_4 () +{ + { + using namespace std; + } + cout << "test"; // { dg-error "'cout' was not declared in this scope" } + // { dg-bogus "'<iostream>'" "" { target *-*-* } .-1 } +} + +/* Local using-directive used from nested block. */ + +void test_5 () +{ + using namespace std; + + for (int i = 0; i < 10; i++) + { + cout << "test"; // { dg-error "'cout' was not declared in this scope" } + // { dg-message "'std::cout' is defined in header '<iostream>'" "" { target *-*-* } .-1 } + } +} + +namespace ns_1 { + +namespace ns_2 { + +using namespace std; + +/* using-directive within the same namespace. */ + +void test_6 () +{ + cout << "test"; // { dg-error "'cout' was not declared in this scope" } + // { dg-message "'std::cout' is defined in header '<iostream>'" "" { target *-*-* } .-1 } +} + +namespace ns_3 { + +/* Locate the using-directive within ns_2, the parent namespace. */ + +void test_7 () +{ + cout << "test"; // { dg-error "'cout' was not declared in this scope" } + // { dg-message "'std::cout' is defined in header '<iostream>'" "" { target *-*-* } .-1 } +} + +} // namespace ns_3 +} // namespace ns_2 + +/* Back in ns_1, should not locate the using-directive. */ + +void test_8 () +{ + cout << "test"; // { dg-error "'cout' was not declared in this scope" } + // { dg-bogus "'<iostream>'" "" { target *-*-* } .-1 } +} + +} // namespace ns_1 + +/* using-directive in global namespace. */ +using namespace std; + +void test_9 () +{ + cout << "test"; // { dg-error "'cout' was not declared in this scope" } + // { dg-message "'std::cout' is defined in header '<iostream>'" "" { target *-*-* } .-1 } +} + -- 1.8.5.3