From f4cbf6aa7b936e16a5487efe7837d63ea24d7c9f Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Fri, 7 Apr 2017 13:16:26 -0400
Subject: [PATCH 04/10] FIXME: WIP for macro use-before-def hint (PR c++/72786)

---
 gcc/c-family/c-common.h                          | 37 +++++++++++++++++-
 gcc/c/c-decl.c                                   | 21 +++++-----
 gcc/c/c-parser.c                                 | 14 +++----
 gcc/cp/name-lookup.c                             | 49 +++++++++++++++++++++---
 gcc/cp/parser.c                                  | 44 ++++++++++++++++-----
 gcc/spellcheck.h                                 |  6 +++
 gcc/testsuite/g++.dg/spellcheck-macro-ordering.C |  6 +++
 libcpp/include/cpplib.h                          |  1 +
 libcpp/macro.c                                   |  8 ++++
 9 files changed, 152 insertions(+), 34 deletions(-)

diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index b933342..8d0db93 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1009,7 +1009,42 @@ enum lookup_name_fuzzy_kind {
   /* Any name.  */
   FUZZY_LOOKUP_NAME
 };
-extern const char *lookup_name_fuzzy (tree, enum lookup_name_fuzzy_kind);
+
+/* FIXME.  */
+
+class deferred_diagnostic
+{
+ public:
+  virtual ~deferred_diagnostic () {}
+  virtual void emit () = 0;
+};
+
+/* FIXME.  */
+
+class name_hint
+{
+public:
+  name_hint (const char *suggestion, deferred_diagnostic *deferred)
+  : m_suggestion (suggestion), m_deferred (deferred)
+  {}
+
+  ~name_hint ()
+  {
+    if (m_deferred)
+      m_deferred->emit ();
+    delete m_deferred;
+  }
+
+  const char *suggestion () const { return m_suggestion; }
+  operator bool () const { return m_suggestion != NULL; }
+
+private:
+  const char *m_suggestion;
+  // FIXME: should this be a unique_ptr; where was that gdb patch?
+  deferred_diagnostic *m_deferred;
+};
+
+extern name_hint lookup_name_fuzzy (tree, enum lookup_name_fuzzy_kind);
 
 extern bool vector_targets_convertible_p (const_tree t1, const_tree t2);
 extern bool vector_types_convertible_p (const_tree t1, const_tree t2, bool emit_lax_note);
diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index 64a1107..72f6754 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -3129,9 +3129,10 @@ implicit_decl_warning (location_t loc, tree id, tree olddecl)
     return;
 
   bool warned;
+  // FIXME: convert to name_hint once we have copy ctor/assign
   const char *hint = NULL;
   if (!olddecl)
-    hint = lookup_name_fuzzy (id, FUZZY_LOOKUP_FUNCTION_NAME);
+    hint = lookup_name_fuzzy (id, FUZZY_LOOKUP_FUNCTION_NAME).suggestion ();
 
   if (flag_isoc99)
     {
@@ -3475,15 +3476,15 @@ undeclared_variable (location_t loc, tree id)
 
   if (current_function_decl == 0)
     {
-      const char *guessed_id = lookup_name_fuzzy (id, FUZZY_LOOKUP_NAME);
+      name_hint guessed_id = lookup_name_fuzzy (id, FUZZY_LOOKUP_NAME);
       if (guessed_id)
 	{
 	  gcc_rich_location richloc (loc);
-	  richloc.add_fixit_replace (guessed_id);
+	  richloc.add_fixit_replace (guessed_id.suggestion ());
 	  error_at_rich_loc (&richloc,
 			     "%qE undeclared here (not in a function);"
 			     " did you mean %qs?",
-			     id, guessed_id);
+			     id, guessed_id.suggestion ());
 	}
       else
 	error_at (loc, "%qE undeclared here (not in a function)", id);
@@ -3493,16 +3494,16 @@ undeclared_variable (location_t loc, tree id)
     {
       if (!objc_diagnose_private_ivar (id))
 	{
-	  const char *guessed_id = lookup_name_fuzzy (id, FUZZY_LOOKUP_NAME);
+	  name_hint guessed_id = lookup_name_fuzzy (id, FUZZY_LOOKUP_NAME);
 	  if (guessed_id)
 	    {
 	      gcc_rich_location richloc (loc);
-	      richloc.add_fixit_replace (guessed_id);
+	      richloc.add_fixit_replace (guessed_id.suggestion ());
 	      error_at_rich_loc
 		(&richloc,
 		 "%qE undeclared (first use in this function);"
 		 " did you mean %qs?",
-		 id, guessed_id);
+		 id, guessed_id.suggestion ());
 	    }
 	  else
 	    error_at (loc, "%qE undeclared (first use in this function)", id);
@@ -4013,7 +4014,7 @@ lookup_name_in_scope (tree name, struct c_scope *scope)
    It also looks for start_typename keywords, to detect "singed" vs "signed"
    typos.  */
 
-const char *
+name_hint
 lookup_name_fuzzy (tree name, enum lookup_name_fuzzy_kind kind)
 {
   gcc_assert (TREE_CODE (name) == IDENTIFIER_NODE);
@@ -4104,9 +4105,9 @@ lookup_name_fuzzy (tree name, enum lookup_name_fuzzy_kind kind)
 
   tree best = bm.get_best_meaningful_candidate ();
   if (best)
-    return IDENTIFIER_POINTER (best);
+    return name_hint (IDENTIFIER_POINTER (best), NULL);
   else
-    return NULL;
+    return name_hint (NULL, NULL);
 }
 
 
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 9398652..e1edb2a 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -1623,13 +1623,13 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
 	}
       else
 	{
-	  const char *hint = lookup_name_fuzzy (name, FUZZY_LOOKUP_TYPENAME);
+	  name_hint hint = lookup_name_fuzzy (name, FUZZY_LOOKUP_TYPENAME);
 	  if (hint)
 	    {
-	      richloc.add_fixit_replace (hint);
+	      richloc.add_fixit_replace (hint.suggestion ());
 	      error_at_rich_loc (&richloc,
 				 "unknown type name %qE; did you mean %qs?",
-				 name, hint);
+				 name, hint.suggestion ());
 	    }
 	  else
 	    error_at (here, "unknown type name %qE", name);
@@ -3848,15 +3848,15 @@ c_parser_parameter_declaration (c_parser *parser, tree attrs)
       c_parser_set_source_position_from_token (token);
       if (c_parser_next_tokens_start_typename (parser, cla_prefer_type))
 	{
-	  const char *hint = lookup_name_fuzzy (token->value,
-						FUZZY_LOOKUP_TYPENAME);
+	  name_hint hint = lookup_name_fuzzy (token->value,
+					      FUZZY_LOOKUP_TYPENAME);
 	  if (hint)
 	    {
 	      gcc_rich_location richloc (token->location);
-	      richloc.add_fixit_replace (hint);
+	      richloc.add_fixit_replace (hint.suggestion ());
 	      error_at_rich_loc (&richloc,
 				 "unknown type name %qE; did you mean %qs?",
-				 token->value, hint);
+				 token->value, hint.suggestion ());
 	    }
 	  else
 	    error_at (token->location, "unknown type name %qE", token->value);
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index 0c5df93..ca185ce 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -4515,13 +4515,13 @@ suggest_alternatives_for (location_t location, tree name,
     {
       if (suggest_misspellings)
 	{
-	  const char *fuzzy_name = lookup_name_fuzzy (name, FUZZY_LOOKUP_NAME);
-	  if (fuzzy_name)
+	  name_hint hint = lookup_name_fuzzy (name, FUZZY_LOOKUP_NAME);
+	  if (hint)
 	    {
 	      gcc_rich_location richloc (location);
-	      richloc.add_fixit_replace (fuzzy_name);
+	      richloc.add_fixit_replace (hint.suggestion ());
 	      inform_at_rich_loc (&richloc, "suggested alternative: %qs",
-				  fuzzy_name);
+				  hint.suggestion ());
 	    }
 	}
       return;
@@ -4951,11 +4951,41 @@ consider_binding_level (tree name, best_match <tree, const char *> &bm,
     }
 }
 
+/* FIXME.  */
+
+class macro_use_before_def : public deferred_diagnostic
+{
+ public:
+  macro_use_before_def (location_t loc, cpp_hashnode *macro)
+  : m_loc (loc), m_macro (macro)
+  {
+    gcc_assert (macro);
+  }
+
+  void emit () OVERRIDE FINAL
+  {
+    source_location def_loc = cpp_macro_definition_location (m_macro);
+    if (def_loc != UNKNOWN_LOCATION)
+      {
+	/* FIXME: do we want to impose an ordering constraint on the
+	   locations?  What if the macro was undeffed?  */
+	inform (m_loc, "the macro %qs had not yet been defined",
+		(const char *)m_macro->ident.str);
+	inform (def_loc, "it was later defined here");
+      }
+  }
+
+ private:
+  location_t m_loc;
+  cpp_hashnode *m_macro;
+};
+
+
 /* Search for near-matches for NAME within the current bindings, and within
    macro names, returning the best match as a const char *, or NULL if
    no reasonable match is found.  */
 
-const char *
+name_hint
 lookup_name_fuzzy (tree name, enum lookup_name_fuzzy_kind kind)
 {
   gcc_assert (TREE_CODE (name) == IDENTIFIER_NODE);
@@ -4986,6 +5016,13 @@ lookup_name_fuzzy (tree name, enum lookup_name_fuzzy_kind kind)
   /* If a macro is the closest so far to NAME, consider it.  */
   if (best_macro)
     bm.consider ((const char *)best_macro->ident.str);
+  else if (bmm.get_best_distance () == 0)
+    {
+      cpp_hashnode *macro = bmm.get_best_candidate_meaning_be_damned ();
+      if (macro)
+	return name_hint (NULL,
+			  new macro_use_before_def (input_location, macro));
+    }
 
   /* Try the "starts_decl_specifier_p" keywords to detect
      "singed" vs "signed" typos.  */
@@ -5010,7 +5047,7 @@ lookup_name_fuzzy (tree name, enum lookup_name_fuzzy_kind kind)
       bm.consider (IDENTIFIER_POINTER (resword_identifier));
     }
 
-  return bm.get_best_meaningful_candidate ();
+  return name_hint (bm.get_best_meaningful_candidate (), NULL);
 }
 
 /* Subroutine of outer_binding.
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 4714bc6..0de4ab9 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -3173,19 +3173,43 @@ cp_parser_diagnose_invalid_type_name (cp_parser *parser, tree id,
   else if (!parser->scope)
     {
       /* Issue an error message.  */
-      const char *suggestion = NULL;
+      /* FIXME: if name_hint properly supported copy ctor (unique_ptr?),
+         then we could do:  */
+#if 0
+      name_hint hint;
       if (TREE_CODE (id) == IDENTIFIER_NODE)
-        suggestion = lookup_name_fuzzy (id, FUZZY_LOOKUP_TYPENAME);
+        hint = lookup_name_fuzzy (id, FUZZY_LOOKUP_TYPENAME);
       if (suggestion)
-	{
-	  gcc_rich_location richloc (location);
-	  richloc.add_fixit_replace (suggestion);
-	  error_at_rich_loc (&richloc,
-			     "%qE does not name a type; did you mean %qs?",
-			     id, suggestion);
-	}
+        {
+          gcc_rich_location richloc (location);
+          richloc.add_fixit_replace (hint.suggestion ());
+          error_at_rich_loc (&richloc,
+                             "%qE does not name a type; did you mean %qs?",
+                             id, hint.suggestion ());
+        }
       else
-	error_at (location, "%qE does not name a type", id);
+        error_at (location, "%qE does not name a type", id);
+#else
+      // FIXME: for now, duplicate the logic, to avoid having
+      // to implement copy-ctor for name_hint
+      if (TREE_CODE (id) == IDENTIFIER_NODE)
+        {
+          name_hint hint = lookup_name_fuzzy (id, FUZZY_LOOKUP_TYPENAME);
+          if (hint)
+            {
+              gcc_rich_location richloc (location);
+              richloc.add_fixit_replace (hint.suggestion ());
+              error_at_rich_loc (&richloc,
+                                 "%qE does not name a type; did you mean %qs?",
+                                 id, hint.suggestion ());
+            }
+          else
+            error_at (location, "%qE does not name a type", id);
+        }
+      else
+        error_at (location, "%qE does not name a type", id);
+#endif
+
       /* If we're in a template class, it's possible that the user was
 	 referring to a type from a base class.  For example:
 
diff --git a/gcc/spellcheck.h b/gcc/spellcheck.h
index 2edc695..192ab6a 100644
--- a/gcc/spellcheck.h
+++ b/gcc/spellcheck.h
@@ -178,6 +178,12 @@ class best_match
     return m_best_candidate;
   }
 
+  // FIXME: need non-offensive name
+  candidate_t get_best_candidate_meaning_be_damned () const
+  {
+    return m_best_candidate;
+  }
+
   edit_distance_t get_best_distance () const { return m_best_distance; }
   size_t get_best_candidate_length () const { return m_best_candidate_len; }
 
diff --git a/gcc/testsuite/g++.dg/spellcheck-macro-ordering.C b/gcc/testsuite/g++.dg/spellcheck-macro-ordering.C
index 3b888c6..ecc11ee 100644
--- a/gcc/testsuite/g++.dg/spellcheck-macro-ordering.C
+++ b/gcc/testsuite/g++.dg/spellcheck-macro-ordering.C
@@ -9,7 +9,13 @@ class DocTargetDriver {
   // { dg-bogus "did you mean" "" { target *-*-* } .-3 }
   // { dg-error "expected .;. at end of member declaration" "" { target *-*-* } .-4 }
   // { dg-error ".OVERRIDE. does not name a type" "" { target *-*-* } .-5 }
+  // { dg-message "the macro 'OVERRIDE' had not yet been defined" "" { target *-*-* } .-6 }
 };
 
 #define OVERRIDE override
+// { dg-message "it was later defined here" "" { target *-*-* } .-1 }
 
+
+
+/* FIXME: add an examples on an undef (maybe as a different testcase, to allow
+   to dg-bogus.  */
diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
index b843992..cf2fdc6 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -876,6 +876,7 @@ extern const cpp_token *cpp_get_token_with_location (cpp_reader *,
 extern bool cpp_fun_like_macro_p (cpp_hashnode *);
 extern const unsigned char *cpp_macro_definition (cpp_reader *,
 						  cpp_hashnode *);
+extern source_location cpp_macro_definition_location (cpp_hashnode *);
 extern void _cpp_backup_tokens (cpp_reader *, unsigned int);
 extern const cpp_token *cpp_peek_token (cpp_reader *, int);
 
diff --git a/libcpp/macro.c b/libcpp/macro.c
index de18c22..acb1fa5 100644
--- a/libcpp/macro.c
+++ b/libcpp/macro.c
@@ -3472,3 +3472,11 @@ cpp_macro_definition (cpp_reader *pfile, cpp_hashnode *node)
   *buffer = '\0';
   return pfile->macro_buffer;
 }
+
+/* FIXME.  */
+
+source_location
+cpp_macro_definition_location (cpp_hashnode *node)
+{
+  return node->value.macro->line;
+}
-- 
1.8.5.3

