[3/4] C++: suggest missing headers for implicit use of "std" (PR c++/85021)

Message ID 1521762258-50849-4-git-send-email-dmalcolm@redhat.com
State New
Headers show
Series
  • Improvements to #include suggestions
Related show

Commit Message

David Malcolm March 22, 2018, 11:44 p.m.
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

-- 
1.8.5.3

Comments

Jason Merrill March 29, 2018, 7:25 p.m. | #1
On Thu, Mar 22, 2018 at 7:44 PM, David Malcolm <dmalcolm@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

> +  if (!usings)

> +    return false;

> +

> +  for (unsigned ix = usings->length (); ix--;)

> +    if ((*usings)[ix] == std_node)

> +      return true;

> +

> +  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

> @@ -5397,11 +5415,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);

> @@ -5509,20 +5539,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);

> @@ -5539,6 +5562,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..95946ff

> --- /dev/null

> +++ b/gcc/testsuite/g++.dg/lookup/missing-std-include-7.C

> @@ -0,0 +1,16 @@

> +/* PR c++/85021: Verify that we suggest missing headers for common names in std::

> +   if there's a "using namespace std;" active.  */

> +

> +void test_without_using_directive ()

> +{

> +  cout << "test"; // { dg-error "'cout' was not declared in this scope" }

> +  // { dg-bogus "'<iostream>'" "" { target *-*-* } .-1 }

> +}

> +

> +using namespace std;

> +

> +void test_with_using_directive ()

> +{

> +  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

>

Patch

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);
+  if (!usings)
+    return false;
+
+  for (unsigned ix = usings->length (); ix--;)
+    if ((*usings)[ix] == std_node)
+      return true;
+
+  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
@@ -5397,11 +5415,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);
@@ -5509,20 +5539,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);
@@ -5539,6 +5562,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..95946ff
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lookup/missing-std-include-7.C
@@ -0,0 +1,16 @@ 
+/* PR c++/85021: Verify that we suggest missing headers for common names in std::
+   if there's a "using namespace std;" active.  */
+
+void test_without_using_directive ()
+{
+  cout << "test"; // { dg-error "'cout' was not declared in this scope" }
+  // { dg-bogus "'<iostream>'" "" { target *-*-* } .-1 }
+}
+
+using namespace std;
+
+void test_with_using_directive ()
+{
+  cout << "test"; // { dg-error "'cout' was not declared in this scope" }
+  // { dg-message "'std::cout' is defined in header '<iostream>'" "" { target *-*-* } .-1 }
+}