From 55798c84dfc79cb1c42e89f1065cd32da17a06aa Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Wed, 14 Sep 2016 13:48:12 -0400
Subject: [PATCH 12/44] FIXME: c++: WIP on hints for missing std:: headers

---
 gcc/cp/cp-tree.h                                  |   2 +-
 gcc/cp/error.c                                    |   4 +-
 gcc/cp/lex.c                                      |   2 +-
 gcc/cp/name-lookup.c                              | 114 +++++++++++++++++++++-
 gcc/testsuite/g++.dg/lookup/missing-std-include.C |  23 +++++
 5 files changed, 138 insertions(+), 7 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/lookup/missing-std-include.C

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 5bcb98b..01961ef 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6844,7 +6844,7 @@ 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, tree);
 extern tree strip_using_decl                    (tree);
 
 /* in constraint.cc */
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 55125ff..c5f5844 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -3938,11 +3938,11 @@ 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);
+      suggest_alternatives_for (location, name, scope);
     }
   else
     {
       error_at (location, "%<::%D%> has not been declared", name);
-      suggest_alternatives_for (location, name);
+      suggest_alternatives_for (location, name, scope);
     }
 }
diff --git a/gcc/cp/lex.c b/gcc/cp/lex.c
index 43827e5..768fb23 100644
--- a/gcc/cp/lex.c
+++ b/gcc/cp/lex.c
@@ -458,7 +458,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, NULL_TREE);
 	}
       /* 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 022ab6a..6ac24d8 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -4390,12 +4390,118 @@ remove_hidden_names (tree fns)
   return fns;
 }
 
+/* FIXME.  */
+
+static const char *
+get_std_name_hint (const char *name)
+{
+  struct std_name_hint
+  {
+    const char *name;
+    const char *header;
+  };
+  static const std_name_hint hints[] = {
+    /* <array>.  */
+    {"array", "array"}, // C++11
+    /* <deque>.  */
+    {"deque", "deque"},
+    /* <forward_list>.  */
+    {"forward_list", "forward_list"},  // C++11
+    /* <fstream>.  */
+    {"basic_filebuf", "fstream"},
+    {"basic_ifstream", "fstream"},
+    {"basic_ofstream", "fstream"},
+    {"basic_fstream", "fstream"},
+    /* <iostream>.  */
+    {"cin", "iostream"},
+    {"cout", "iostream"},
+    {"cerr", "iostream"},
+    {"clog", "iostream"},
+    {"wcin", "iostream"},
+    {"wcout", "iostream"},
+    {"wclog", "iostream"},
+    /* <list>.  */
+    {"list", "list"},
+    /* <map>.  */
+    {"map", "map"},
+    {"multimap", "map"},
+    /* <queue>.  */
+    {"queue", "queue"},
+    {"priority_queue", "queue"},
+    /* <ostream>.  */
+    {"ostream", "ostream"},
+    {"wostream", "ostream"},
+    {"ends", "ostream"},
+    {"flush", "ostream"},
+    {"endl", "ostream"},
+    /* <set>.  */
+    {"set", "set"},
+    {"multiset", "set"},
+    /* <sstream>.  */
+    {"basic_stringbuf", "sstream"},
+    {"basic_istringstream", "sstream"},
+    {"basic_ostringstream", "sstream"},
+    {"basic_stringstream", "sstream"},
+    /* <stack>.  */
+    {"stack", "stack"},
+    /* <string>.  */
+    {"string", "string"},
+    {"wstring", "string"},
+    {"u16string", "string"},
+    {"u32string", "string"},
+    /* <unordered_map>.  */
+    {"unordered_map", "unordered_map"}, // C++11
+    {"unordered_multimap", "unordered_map"}, // C++11
+    /* <unordered_set>.  */
+    {"unordered_set", "unordered_set"}, // C++11
+    {"unordered_multiset", "unordered_set"}, // C++11
+    /* <vector>.  */
+    {"vector", "vector"},
+  };
+  const size_t num_hints = sizeof (hints) / sizeof (hints[0]);
+  for (size_t i = 0; i < num_hints; i++)
+    {
+      if (0 == strcmp (name, hints[i].name))
+	return hints[i].header;
+    }
+  return NULL;
+}
+
+/* Subroutine of suggest_alternatives_for, for use when we have no
+   suggestions to offer.
+   If SCOPE is the "std" namespace, then suggest pertinent header
+   files for NAME.  */
+
+static void
+maybe_suggest_missing_header (location_t location, tree name, tree scope)
+{
+  if (scope == NULL_TREE)
+    return;
+  if (TREE_CODE (scope) != NAMESPACE_DECL)
+    return;
+  /* We only offer suggestions for the "std" namespace.  */
+  if (scope != std_node)
+    return;
+  gcc_assert (TREE_CODE (name) == IDENTIFIER_NODE);
+
+  const char *name_str = IDENTIFIER_POINTER (name);
+  const char *header_hint = get_std_name_hint (name_str);
+  if (header_hint)
+    inform (location,
+	    "%<std::%s%> is defined in header %<<%s>%>;"
+	    " did you forget to %<#include <%s>%>?",
+	    name_str, header_hint, header_hint);
+}
+
 /* 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 SCOPE is non-NULL, it is a namespace explicitly provided by the
+   user, which can be used for suggesting missing header files if all
+   else fails.  */
 
 void
-suggest_alternatives_for (location_t location, tree name)
+suggest_alternatives_for (location_t location, tree name, tree scope)
 {
   vec<tree> candidates = vNULL;
   vec<tree> namespaces_to_search = vNULL;
@@ -4439,7 +4545,7 @@ suggest_alternatives_for (location_t location, tree name)
   namespaces_to_search.release ();
 
   /* Nothing useful to report for NAME.  Report on likely misspellings,
-     or do nothing.  */
+     report on missing headers, or do nothing.  */
   if (candidates.is_empty ())
     {
       const char *fuzzy_name = lookup_name_fuzzy (name, FUZZY_LOOKUP_NAME);
@@ -4450,6 +4556,8 @@ suggest_alternatives_for (location_t location, tree name)
 	  inform_at_rich_loc (&richloc, "suggested alternative: %qs",
 			      fuzzy_name);
 	}
+      else
+	maybe_suggest_missing_header (location, name, scope);
       return;
     }
 
diff --git a/gcc/testsuite/g++.dg/lookup/missing-std-include.C b/gcc/testsuite/g++.dg/lookup/missing-std-include.C
new file mode 100644
index 0000000..032431f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lookup/missing-std-include.C
@@ -0,0 +1,23 @@
+//#include <string>
+//#include <iostream>
+//#include <array>
+//#include <vector>
+
+void test (void)
+{
+  std::string s ("hello world");
+
+  std::cout << 10; // { dg-error ".cout. is not a member of .std." }
+  // { dg-message ".std::cout. is defined in header .<iostream>.; did you forget to .#include <iostream>.?" "" { target *-*-* } 9 }
+
+  int i;
+  std::cin >> i; // { dg-error ".cin. is not a member of .std." }
+  // { dg-message ".std::cin. is defined in header .<iostream>.; did you forget to .#include <iostream>.?" "" { target *-*-* } 13 }
+
+  //std::array a;
+
+  std::vector<int> v; // { dg-error ".vector. is not a member of .std." }
+
+  std::list<int> lst;
+  std::test t;
+}
-- 
1.8.5.3

