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

Reply via email to