From b1b7fcffcf7a1753ccb68a59ea58fc64cc5d04e4 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Thu, 8 Feb 2018 11:06:43 -0500
Subject: [PATCH 22/42] FIXME: WIP on PR preprocessor/81419 (spellcheck on
 function-like macros)

---
 gcc/c-family/c-spellcheck.cc              | 23 +++++++++++------
 gcc/c-family/c-spellcheck.h               |  7 +++++-
 gcc/c/c-decl.c                            |  3 ++-
 gcc/cp/cp-tree.h                          | 20 ++++++++++++---
 gcc/cp/error.c                            |  4 +--
 gcc/cp/lex.c                              | 12 ++++++---
 gcc/cp/name-lookup.c                      | 21 ++++++++++++----
 gcc/cp/parser.c                           |  7 ++++--
 gcc/cp/pt.c                               |  6 +++--
 gcc/testsuite/g++.dg/spellcheck-pr81419.C | 42 +++++++++++++++++++++++++++++++
 gcc/testsuite/gcc.dg/spellcheck-pr81419.c | 42 +++++++++++++++++++++++++++++++
 11 files changed, 160 insertions(+), 27 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/spellcheck-pr81419.C
 create mode 100644 gcc/testsuite/gcc.dg/spellcheck-pr81419.c

diff --git a/gcc/c-family/c-spellcheck.cc b/gcc/c-family/c-spellcheck.cc
index 85fd278..cd36317 100644
--- a/gcc/c-family/c-spellcheck.cc
+++ b/gcc/c-family/c-spellcheck.cc
@@ -42,8 +42,8 @@ name_reserved_for_implementation_p (const char *str)
 /* Return true iff HASHNODE is a macro that should be offered as a
    suggestion for a misspelling.  */
 
-static bool
-should_suggest_as_macro_p (cpp_hashnode *hashnode)
+bool
+best_macro_match::should_suggest_as_macro_p (cpp_hashnode *hashnode)
 {
   if (!cpp_macro_p (hashnode))
     return false;
@@ -54,6 +54,10 @@ should_suggest_as_macro_p (cpp_hashnode *hashnode)
       && name_reserved_for_implementation_p ((const char *)hashnode->ident.str))
     return false;
 
+  /* FIXME: filter on function-like.  */
+  if (cpp_fun_like_macro_p (hashnode) != m_function_like)
+    return false;
+
   return true;
 }
 
@@ -65,10 +69,11 @@ static int
 find_closest_macro_cpp_cb (cpp_reader *, cpp_hashnode *hashnode,
 			   void *user_data)
 {
-  if (!should_suggest_as_macro_p (hashnode))
+  best_macro_match *bmm = (best_macro_match *)user_data;
+
+  if (!bmm->should_suggest_as_macro_p (hashnode))
     return 1;
 
-  best_macro_match *bmm = (best_macro_match *)user_data;
   bmm->consider (hashnode);
 
   /* Keep iterating.  */
@@ -77,12 +82,16 @@ find_closest_macro_cpp_cb (cpp_reader *, cpp_hashnode *hashnode,
 
 /* Constructor for best_macro_match.
    Use find_closest_macro_cpp_cb to find the closest matching macro to
-   NAME within distance < best_distance_so_far. */
+   NAME within distance < best_distance_so_far.
+   Restrict the search to function-like macros or non-function-like
+   macros based on the FUNCTION_LIKE flag.  */
 
 best_macro_match::best_macro_match (tree goal,
 				    edit_distance_t best_distance_so_far,
-				    cpp_reader *reader)
-: best_match <goal_t, candidate_t> (goal, best_distance_so_far)
+				    cpp_reader *reader,
+				    bool function_like)
+: best_match <goal_t, candidate_t> (goal, best_distance_so_far),
+  m_function_like (function_like)
 {
   cpp_forall_identifiers (reader, find_closest_macro_cpp_cb, this);
 }
diff --git a/gcc/c-family/c-spellcheck.h b/gcc/c-family/c-spellcheck.h
index 25229d5..59fe9ee 100644
--- a/gcc/c-family/c-spellcheck.h
+++ b/gcc/c-family/c-spellcheck.h
@@ -47,7 +47,12 @@ class best_macro_match : public best_match<tree, cpp_hashnode *>
 {
  public:
   best_macro_match (tree goal, edit_distance_t best_distance_so_far,
-		    cpp_reader *reader);
+		    cpp_reader *reader, bool function_like);
+
+  bool should_suggest_as_macro_p (cpp_hashnode *hashnode);
+
+ private:
+  bool m_function_like;
 };
 
 #endif  /* C_SPELLCHECK_H  */
diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index 4ea4713..cbe7aed 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -4148,7 +4148,8 @@ lookup_name_fuzzy (tree name, enum lookup_name_fuzzy_kind kind, location_t loc)
      non-NULL result for best_macro_match if it's better than any of
      the identifiers already checked, which avoids needless creation
      of identifiers for macro hashnodes.  */
-  best_macro_match bmm (name, bm.get_best_distance (), parse_in);
+  best_macro_match bmm (name, bm.get_best_distance (), parse_in,
+			kind == FUZZY_LOOKUP_FUNCTION_NAME);
   cpp_hashnode *best_macro = bmm.get_best_meaningful_candidate ();
   /* If a macro is the closest so far to NAME, use it, creating an
      identifier tree node for it.  */
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index cabfbec..68eee6a 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6542,12 +6542,25 @@ extern int diagnose_uninitialized_cst_or_ref_member (tree, bool, bool);
 extern tree build_vtbl_address                  (tree);
 extern bool maybe_reject_flexarray_init		(tree, tree);
 
+// FIXME
+enum suggestions_are_function_like
+{
+  // FIXME
+  SAFL_FUNCTION_LIKE,
+
+  // FIXME
+  SAFL_NOT_FUNCTION_LIKE,
+
+  // FIXME
+  SAFL_UNKNOWN
+};
+
 /* in lex.c */
 extern void cxx_dup_lang_specific_decl		(tree);
 extern void yyungetc				(int, int);
 
-extern tree unqualified_name_lookup_error	(tree,
-						 location_t = UNKNOWN_LOCATION);
+extern tree unqualified_name_lookup_error	(tree, location_t,
+						 enum suggestions_are_function_like);
 extern tree unqualified_fn_lookup_error		(cp_expr);
 extern tree make_conv_op_name			(tree);
 extern tree build_lang_decl			(enum tree_code, tree, tree);
@@ -7479,7 +7492,8 @@ extern tree cp_fully_fold			(tree);
 extern void clear_fold_cache			(void);
 
 /* in name-lookup.c */
-extern void suggest_alternatives_for            (location_t, tree, bool);
+extern void suggest_alternatives_for            (location_t, tree, bool,
+						 enum suggestions_are_function_like);
 extern bool suggest_alternative_in_explicit_scope (location_t, tree, tree);
 extern tree strip_using_decl                    (tree);
 
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 0b14dcc..70b9d07 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -4263,13 +4263,13 @@ qualified_name_lookup_error (tree scope, tree name,
       auto_diagnostic_group d;
       error_at (location, "%qD is not a member of %qD", name, scope);
       if (!suggest_alternative_in_explicit_scope (location, name, scope))
-	suggest_alternatives_for (location, name, false);
+	suggest_alternatives_for (location, name, false, SAFL_UNKNOWN);
     }
   else
     {
       auto_diagnostic_group d;
       error_at (location, "%<::%D%> has not been declared", name);
-      suggest_alternatives_for (location, name, true);
+      suggest_alternatives_for (location, name, true, SAFL_UNKNOWN);
     }
 }
 
diff --git a/gcc/cp/lex.c b/gcc/cp/lex.c
index 47b99c3..e3eb83d 100644
--- a/gcc/cp/lex.c
+++ b/gcc/cp/lex.c
@@ -485,10 +485,14 @@ handle_pragma_implementation (cpp_reader* /*dfile*/)
 }
 
 /* Issue an error message indicating that the lookup of NAME (an
-   IDENTIFIER_NODE) failed.  Returns the ERROR_MARK_NODE.  */
+   IDENTIFIER_NODE) failed.  Returns the ERROR_MARK_NODE.
+   FIXME: replace with SAFL
+   FUNCTION_LIKE_P is true if NAME is used in a function-like way
+   (followed by an open paren), false otherwise.  */
 
 tree
-unqualified_name_lookup_error (tree name, location_t loc)
+unqualified_name_lookup_error (tree name, location_t loc,
+			       enum suggestions_are_function_like safl)
 {
   if (loc == UNKNOWN_LOCATION)
     loc = cp_expr_loc_or_loc (name, input_location);
@@ -501,7 +505,7 @@ unqualified_name_lookup_error (tree name, location_t loc)
 	{
 	  auto_diagnostic_group d;
 	  error_at (loc, "%qD was not declared in this scope", name);
-	  suggest_alternatives_for (loc, name, true);
+	  suggest_alternatives_for (loc, name, true, safl);
 	}
       /* Prevent repeated error messages by creating a VAR_DECL with
 	 this NAME in the innermost block scope.  */
@@ -558,7 +562,7 @@ unqualified_fn_lookup_error (cp_expr name_expr)
       return name;
     }
 
-  return unqualified_name_lookup_error (name, loc);
+  return unqualified_name_lookup_error (name, loc, SAFL_FUNCTION_LIKE);
 }
 
 
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index 08632c3..acd0f84 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -5303,11 +5303,14 @@ has_using_namespace_std_directive_p ()
    lookup failed.  Search through all available namespaces and print out
    possible candidates.  If no exact matches are found, and
    SUGGEST_MISSPELLINGS is true, then also look for near-matches and
-   suggest the best near-match, if there is one.  */
+   suggest the best near-match, if there is one, using FUNCTION_LIKE_P
+   to filter the suggestions.  */
+// FIXME ^^^
 
 void
 suggest_alternatives_for (location_t location, tree name,
-			  bool suggest_misspellings)
+			  bool suggest_misspellings,
+			  enum suggestions_are_function_like safl)
 {
   vec<tree> candidates = vNULL;
   vec<tree> worklist = vNULL;
@@ -5384,8 +5387,15 @@ suggest_alternatives_for (location_t location, tree name,
   /* Otherwise, consider misspellings.  */
   if (!suggest_misspellings)
     return;
-  if (name_hint hint = lookup_name_fuzzy (name, FUZZY_LOOKUP_NAME,
-					  location))
+
+  /* If we know we're looking for a function-like name,
+     use FUZZY_LOOKUP_FUNCTION_NAME, otherwise use
+     FUZZY_LOOKUP_NAME.  */
+  enum lookup_name_fuzzy_kind kind  = (safl == SAFL_FUNCTION_LIKE
+				       ? FUZZY_LOOKUP_FUNCTION_NAME
+				       : FUZZY_LOOKUP_NAME);
+  name_hint hint = lookup_name_fuzzy (name, kind, location);
+  if (hint)
     {
       /* Show a spelling correction.  */
       gcc_rich_location richloc (location);
@@ -5938,7 +5948,8 @@ lookup_name_fuzzy (tree name, enum lookup_name_fuzzy_kind kind, location_t loc)
      distance >= bm.get_best_distance (), and means that we only get a
      non-NULL result for best_macro_match if it's better than any of
      the identifiers already checked.  */
-  best_macro_match bmm (name, bm.get_best_distance (), parse_in);
+  best_macro_match bmm (name, bm.get_best_distance (), parse_in,
+			kind == FUZZY_LOOKUP_FUNCTION_NAME);
   cpp_hashnode *best_macro = bmm.get_best_meaningful_candidate ();
   /* If a macro is the closest so far to NAME, consider it.  */
   if (best_macro)
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index c9dce48..16e398b 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -7084,7 +7084,9 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	  && cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_PAREN))
 	/* It is not a Koenig lookup function call.  */
 	postfix_expression
-	  = unqualified_name_lookup_error (postfix_expression);
+	  = unqualified_name_lookup_error (postfix_expression,
+					   UNKNOWN_LOCATION,
+					   SAFL_NOT_FUNCTION_LIKE);
 
       /* Peek at the next token.  */
       token = cp_lexer_peek_token (parser->lexer);
@@ -10423,7 +10425,8 @@ cp_parser_lambda_introducer (cp_parser* parser, tree lambda_expr)
 
 	  if (capture_init_expr == error_mark_node)
 	    {
-	      unqualified_name_lookup_error (capture_id);
+	      unqualified_name_lookup_error (capture_id, UNKNOWN_LOCATION,
+					     SAFL_NOT_FUNCTION_LIKE);
 	      continue;
 	    }
 	  else if (!VAR_P (capture_init_expr)
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 178be76..ac43f9b 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -17791,7 +17791,8 @@ tsubst_copy_and_build (tree t,
 	if (!function_p && identifier_p (decl))
 	  {
 	    if (complain & tf_error)
-	      unqualified_name_lookup_error (decl);
+	      unqualified_name_lookup_error (decl, UNKNOWN_LOCATION,
+					     SAFL_NOT_FUNCTION_LIKE);
 	    decl = error_mark_node;
 	  }
 	RETURN (decl);
@@ -18448,7 +18449,8 @@ tsubst_copy_and_build (tree t,
 	    if (identifier_p (function))
 	      {
 		if (complain & tf_error)
-		  unqualified_name_lookup_error (function);
+		  unqualified_name_lookup_error (function, UNKNOWN_LOCATION,
+						 SAFL_FUNCTION_LIKE);
 		release_tree_vector (call_args);
 		RETURN (error_mark_node);
 	      }
diff --git a/gcc/testsuite/g++.dg/spellcheck-pr81419.C b/gcc/testsuite/g++.dg/spellcheck-pr81419.C
new file mode 100644
index 0000000..37f928b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/spellcheck-pr81419.C
@@ -0,0 +1,42 @@
+/* Verify that suggested macros honor the "function-like"-ness of the
+   context in which the misspelled thing is used (PR 81419).  */
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+/* Example of a function-like macro.  */
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+/* Example of a non-function-like macro.  */
+#define SOMETHING 42
+
+/* Example of a non-function-like macro that will be used to make
+   function calls.  */
+#define CALLABLE callable
+
+int callable(unsigned int argument);
+
+/* ARRAY_SIZE is function-like, so we shouldn't offer it as a suggestion
+   for "PTR_SIZE" (PR 81419).  */
+
+int test_1 (void)
+{
+  return callable(PTR_SIZE); // { dg-error "'PTR_SIZE' was not declared" }
+  // { dg-bogus "suggested alternative: 'ARRAY_SIZE'" "" { target *-*-* } .-1 }
+}
+
+/* SOMETHING is not function-like, so we shouldn't offer it as a suggestion
+   for "SOMETHIN (42)".  */
+
+int test_2 (void)
+{
+  return SOMETHIN (42); // { dg-error "'SOMETHIN' was not declared" }
+  // { dg-bogus "suggested alternative: 'SOMETHING'" "" { target *-*-* } .-1 }
+}
+
+/* FIXME. What about this; we probably *should* suggest it:  */
+
+int test_3 (void)
+{
+  return CALLABL(42); // { dg-error "'CALLABL' was not declared" }
+  // { dg-bogus "suggested alternative: 'CALLABLE'" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/gcc.dg/spellcheck-pr81419.c b/gcc/testsuite/gcc.dg/spellcheck-pr81419.c
new file mode 100644
index 0000000..29b1b49
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/spellcheck-pr81419.c
@@ -0,0 +1,42 @@
+/* { dg-options "-Wimplicit-function-declaration" } */
+
+/* Verify that suggested macros honor the "function-like"-ness of the
+   context in which the misspelled thing is used (PR 81419).  */
+
+/* Example of a function-like macro.  */
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+/* Example of a non-function-like macro.  */
+#define SOMETHING 42
+
+/* Example of a non-function-like macro that will be used to make
+   function calls.  */
+#define CALLABLE callable
+
+int callable(unsigned int argument);
+
+/* ARRAY_SIZE is function-like, so we shouldn't offer it as a suggestion
+   for "PTR_SIZE".  */
+
+int test_1 (void)
+{
+  return callable(PTR_SIZE); /* { dg-bogus "did you mean 'ARRAY_SIZE'" } */
+  /* { dg-error "undeclared" "" { target *-*-* } .-1 } */  
+}
+
+/* SOMETHING is not function-like, so we shouldn't offer it as a suggestion
+   for "SOMETHIN (42)".  */
+
+int test_2 (void)
+{
+  return SOMETHIN (42); /* { dg-bogus "did you mean 'SOMETHING'" } */
+  /* { dg-warning "implicit declaration of function 'SOMETHIN'" "" { target *-*-* } .-1 } */
+}
+
+/* FIXME. What about this; we probably *should* suggest it:  */
+
+int test_3 (void)
+{
+  return CALLABL(42); /* { dg-bogus "did you mean 'CALLABLE'" } */
+  /* { dg-warning "implicit declaration of function 'CALLABL'" "" { target *-*-* } .-1 } */
+}
-- 
1.8.5.3

