Commit cb731872 by David Malcolm Committed by David Malcolm

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

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"; }
               ^~~~

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.

From-SVN: r259179
parent a82f886a
2018-04-06 David Malcolm <dmalcolm@redhat.com>
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.
2018-04-06 Jason Merrill <jason@redhat.com> 2018-04-06 Jason Merrill <jason@redhat.com>
PR c++/85242 - ICE with class definition in template parm. PR c++/85242 - ICE with class definition in template parm.
......
...@@ -41,6 +41,7 @@ static cxx_binding *cxx_binding_make (tree value, tree type); ...@@ -41,6 +41,7 @@ static cxx_binding *cxx_binding_make (tree value, tree type);
static cp_binding_level *innermost_nonclass_level (void); static cp_binding_level *innermost_nonclass_level (void);
static void set_identifier_type_value_with_scope (tree id, tree decl, static void set_identifier_type_value_with_scope (tree id, tree decl,
cp_binding_level *b); 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 /* Create an overload suitable for recording an artificial TYPE_DECL
and another decl. We use this machanism to implement the struct and another decl. We use this machanism to implement the struct
...@@ -5330,6 +5331,48 @@ qualify_lookup (tree val, int flags) ...@@ -5330,6 +5331,48 @@ qualify_lookup (tree val, int flags)
return true; 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 /* Suggest alternatives for NAME, an IDENTIFIER_NODE for which name
lookup failed. Search through all available namespaces and print out lookup failed. Search through all available namespaces and print out
possible candidates. If no exact matches are found, and possible candidates. If no exact matches are found, and
...@@ -5400,11 +5443,23 @@ suggest_alternatives_for (location_t location, tree name, ...@@ -5400,11 +5443,23 @@ suggest_alternatives_for (location_t location, tree name,
inform (location_of (val), " %qE", val); inform (location_of (val), " %qE", val);
} }
candidates.release (); candidates.release ();
return;
} }
else if (!suggest_misspellings)
; /* No candidates were found in the available namespaces. */
else if (name_hint hint = lookup_name_fuzzy (name, FUZZY_LOOKUP_NAME,
location)) /* 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. */ /* Show a spelling correction. */
gcc_rich_location richloc (location); gcc_rich_location richloc (location);
...@@ -5512,20 +5567,13 @@ get_std_name_hint (const char *name) ...@@ -5512,20 +5567,13 @@ get_std_name_hint (const char *name)
return NULL; return NULL;
} }
/* If SCOPE is the "std" namespace, then suggest pertinent header /* Suggest pertinent header files for NAME at LOCATION, for common
files for NAME at LOCATION. names within the "std" namespace.
Return true iff a suggestion was offered. */ Return true iff a suggestion was offered. */
static bool 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); gcc_assert (TREE_CODE (name) == IDENTIFIER_NODE);
const char *name_str = IDENTIFIER_POINTER (name); const char *name_str = IDENTIFIER_POINTER (name);
...@@ -5542,6 +5590,23 @@ maybe_suggest_missing_header (location_t location, tree name, tree scope) ...@@ -5542,6 +5590,23 @@ maybe_suggest_missing_header (location_t location, tree name, tree scope)
return true; 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 /* Look for alternatives for NAME, an IDENTIFIER_NODE for which name
lookup failed within the explicitly provided SCOPE. Suggest the lookup failed within the explicitly provided SCOPE. Suggest the
the best meaningful candidates (if any) as a fix-it hint. the best meaningful candidates (if any) as a fix-it hint.
......
2018-04-06 David Malcolm <dmalcolm@redhat.com>
PR c++/85021
* g++.dg/lookup/missing-std-include-7.C: New test.
2018-04-06 Tamar Christina <tamar.christina@arm.com> 2018-04-06 Tamar Christina <tamar.christina@arm.com>
* gcc.dg/struct-simple.c: Revert r254862. * gcc.dg/struct-simple.c: Revert r254862.
......
/* 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 }
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment