From acb207c93f54ee17787bfae0df3d55d7507eb59f Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Fri, 16 Dec 2016 13:16:44 -0500
Subject: [PATCH 12/26] Offer suggestions for misspelled attributes (PR
 c/70186)

gcc/ChangeLog:
	PR c/70186
	* attribs.c: Include "spellcheck.h".
	(extract_attribute_substring): Fix leading comment.  Return a bool
	signifying if truncation for underscores occurred.
	(struct edit_distance_traits<struct substring *>): New class.
	(fuzzy_lookup_scoped_attribute_spec): New function, based on
	lookup_scoped_attribute_spec.
	(decl_attributes): Move warning about ignored attributes to..
	(warn_about_unidentified_attribute): ...this new function, and
	potentially offer suggestions for misspelled attributes.

gcc/testsuite/ChangeLog:
	PR c/70186
	* c-c++-common/spellcheck-attributes.c: New test case.
	* g++.dg/cpp0x/spellcheck-attributes.C: New test case.
---
 gcc/attribs.c                                      | 115 ++++++++++++++++++---
 gcc/testsuite/c-c++-common/spellcheck-attributes.c |   5 +
 gcc/testsuite/g++.dg/cpp0x/spellcheck-attributes.C |  18 ++++
 3 files changed, 126 insertions(+), 12 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/spellcheck-attributes.c
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/spellcheck-attributes.C

diff --git a/gcc/attribs.c b/gcc/attribs.c
index 55b2127..3dc94d3 100644
--- a/gcc/attribs.c
+++ b/gcc/attribs.c
@@ -28,6 +28,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "stor-layout.h"
 #include "langhooks.h"
 #include "plugin.h"
+#include "spellcheck.h"
 
 /* Table of the tables of attributes (common, language, format, machine)
    searched.  */
@@ -97,10 +98,11 @@ static const struct attribute_spec empty_attribute_table[] =
   { NULL, 0, 0, false, false, false, NULL, false }
 };
 
-/* Return base name of the attribute.  Ie '__attr__' is turned into 'attr'.
-   To avoid need for copying, we simply return length of the string.  */
+/* Extract base name of the attribute.  Ie '__attr__' is turned into 'attr'.
+   To avoid need for copying, we simply update length of the string.
+   Return true if the string was truncated, false otherwise.  */
 
-static void
+static bool
 extract_attribute_substring (struct substring *str)
 {
   if (str->length > 4 && str->str[0] == '_' && str->str[1] == '_'
@@ -108,7 +110,9 @@ extract_attribute_substring (struct substring *str)
     {
       str->length -= 4;
       str->str += 2;
+      return true;
     }
+  return false;
 }
 
 /* Insert an array of attributes ATTRIBUTES into a namespace.  This
@@ -343,6 +347,101 @@ get_attribute_namespace (const_tree attr)
   return get_identifier ("gnu");
 }
 
+/* Specialization of edit_distance_traits for struct substring.  */
+
+template <>
+struct edit_distance_traits<struct substring *>
+{
+  static size_t get_length (struct substring *substr)
+  {
+    gcc_assert (substr);
+    return substr->length;
+  }
+
+  static const char *get_string (struct substring *substr)
+  {
+    gcc_assert (substr);
+    return substr->str;
+  }
+};
+
+/* Look for near matches for the scoped attribute with namespace NS and
+   name NAME.
+   Return the best matching attribute name, or NULL if none is found.
+   If it returns non-NULL then *UNDERSCORES is written to, with true
+   iff leading and trailing underscores were stripped from NAME
+   before the match.  */
+
+static const char *
+fuzzy_lookup_scoped_attribute_spec (const_tree ns, const_tree name,
+				    bool *underscores)
+{
+  struct substring attr;
+  scoped_attributes *attrs;
+
+  const char *ns_str = (ns != NULL_TREE) ? IDENTIFIER_POINTER (ns): NULL;
+
+  attrs = find_attribute_namespace (ns_str);
+
+  if (attrs == NULL)
+    return NULL;
+
+  attr.str = IDENTIFIER_POINTER (name);
+  attr.length = IDENTIFIER_LENGTH (name);
+  *underscores = extract_attribute_substring (&attr);
+
+  best_match <struct substring *, const char *> bm (&attr);
+
+  hash_table<attribute_hasher> *h = attrs->attribute_hash;
+  for (hash_table<attribute_hasher>::iterator iter = h->begin ();
+       iter != h->end (); ++iter)
+    bm.consider ((*iter)->name);
+  return bm.get_best_meaningful_candidate ();
+}
+
+/* Warn about attribute A being unrecognized with name NAME (an identifier),
+   within namespace NS (an identifier, or NULL_TREE).
+   Issue a hint if it appears to be misspelled.  */
+
+static void
+warn_about_unidentified_attribute (tree a, tree ns, tree name)
+{
+  bool underscores;
+  const char *hint
+    = fuzzy_lookup_scoped_attribute_spec (ns, name, &underscores);
+
+  if (ns == NULL_TREE || !cxx11_attribute_p (a))
+    if (hint)
+      if (underscores)
+	warning (OPT_Wattributes,
+		 ("%qE attribute directive ignored"
+		  "; did you mean %<__%s__%>?"),
+		 name, hint);
+      else
+	warning (OPT_Wattributes,
+		 ("%qE attribute directive ignored"
+		  "; did you mean %qs?"),
+		 name, hint);
+    else
+      warning (OPT_Wattributes, "%qE attribute directive ignored",
+	       name);
+  else
+    if (hint)
+      if (underscores)
+	warning (OPT_Wattributes,
+		 ("%<%E::%E%> scoped attribute directive"
+		  " ignored; did you mean %<%E::__%s__%>?"),
+		 ns, name, ns, hint);
+      else
+	warning (OPT_Wattributes,
+		 ("%<%E::%E%> scoped attribute directive ignored"
+		  "; did you mean %<%E::%s%>?"),
+		 ns, name, ns, hint);
+    else
+      warning (OPT_Wattributes,
+	       "%<%E::%E%> scoped attribute directive ignored",
+	       ns, name);
+}
 
 /* Process the attributes listed in ATTRIBUTES and install them in *NODE,
    which is either a DECL (including a TYPE_DECL) or a TYPE.  If a DECL,
@@ -431,15 +530,7 @@ decl_attributes (tree *node, tree attributes, int flags)
       if (spec == NULL)
 	{
 	  if (!(flags & (int) ATTR_FLAG_BUILT_IN))
-	    {
-	      if (ns == NULL_TREE || !cxx11_attribute_p (a))
-		warning (OPT_Wattributes, "%qE attribute directive ignored",
-			 name);
-	      else
-		warning (OPT_Wattributes,
-			 "%<%E::%E%> scoped attribute directive ignored",
-			 ns, name);
-	    }
+	    warn_about_unidentified_attribute (a, ns, name);
 	  continue;
 	}
       else if (list_length (args) < spec->min_length
diff --git a/gcc/testsuite/c-c++-common/spellcheck-attributes.c b/gcc/testsuite/c-c++-common/spellcheck-attributes.c
new file mode 100644
index 0000000..a0da879
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/spellcheck-attributes.c
@@ -0,0 +1,5 @@
+struct S *s1 __attribute__ ((visbility("hidden")));
+/* { dg-warning ".visbility. attribute directive ignored; did you mean .visibility." "" { target *-*-* } .-1 } */
+
+struct S *s2 __attribute__ ((__visbility__("hidden")));
+/* { dg-warning ".__visbility__. attribute directive ignored; did you mean .__visibility__." "" { target *-*-* } .-1 } */
diff --git a/gcc/testsuite/g++.dg/cpp0x/spellcheck-attributes.C b/gcc/testsuite/g++.dg/cpp0x/spellcheck-attributes.C
new file mode 100644
index 0000000..ca72608
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/spellcheck-attributes.C
@@ -0,0 +1,18 @@
+// { dg-do compile { target c++11 } }
+
+int
+test ()
+{
+  [[gnu::unused]] lab_1:
+
+  [[gnu::unuse]] lab_2:
+    /* { dg-warning ".gnu::unuse. scoped attribute directive ignored; did you mean .gnu::unused.." "" { target *-*-* } .-1 } */
+    return 0;
+
+  [[gnu::__unused__]] lab_3:
+
+  [[gnu::__unuse__]] lab_4:
+    /* { dg-warning ".gnu::__unuse__. scoped attribute directive ignored; did you mean .gnu::__unused__.." "" { target *-*-* } .-1 } */
+    return 0;
+
+}
-- 
1.8.5.3

