c++: Make spell corrections consistent
authorNathan Sidwell <nathan@acm.org>
Mon, 5 Oct 2020 13:36:38 +0000 (06:36 -0700)
committerNathan Sidwell <nathan@acm.org>
Mon, 5 Oct 2020 13:39:10 +0000 (06:39 -0700)
My change to namespace-scope spell corrections ignored the issue that
different targets might have different builtins, and therefore perturb
iteration order.  This fixes it by using an intermediate array of
identifier, which we sort before considering.

gcc/cp/
* name-lookup.c (maybe_add_fuzzy_decl): New.
(maybe_add_fuzzy_binding): New.
(consider_binding_level): Use intermediate sortable vector for
namespace bindings.
gcc/testsuite/
* c-c++-common/spellcheck-reserved.c: Restore diagnostic.

gcc/cp/name-lookup.c
gcc/testsuite/c-c++-common/spellcheck-reserved.c

index 190b56bf4dd041e9f270e6fd31c592af4ebfa6b8..774c4473390b29d35dc44438912b621dbe6abdcf 100644 (file)
@@ -6077,6 +6077,9 @@ qualified_namespace_lookup (tree scope, name_lookup *lookup)
   return found;
 }
 
+/* If DECL is suitably visible to the user, consider its name for
+   spelling correction.  */
+
 static void
 consider_decl (tree decl,  best_match <tree, const char *> &bm,
               bool consider_impl_names)
@@ -6110,6 +6113,65 @@ consider_decl (tree decl,  best_match <tree, const char *> &bm,
   bm.consider (suggestion_str);
 }
 
+/* If DECL is suitably visible to the user, add its name to VEC and
+   return true.  Otherwise return false.  */
+
+static bool
+maybe_add_fuzzy_decl (auto_vec<tree> &vec, tree decl)
+{
+  /* Skip compiler-generated variables (e.g. __for_begin/__for_end
+     within range for).  */
+  if (TREE_CODE (decl) == VAR_DECL && DECL_ARTIFICIAL (decl))
+    return false;
+
+  tree suggestion = DECL_NAME (decl);
+  if (!suggestion)
+    return false;
+
+  /* Don't suggest names that are for anonymous aggregate types, as
+     they are an implementation detail generated by the compiler.  */
+  if (IDENTIFIER_ANON_P (suggestion))
+    return false;
+
+  vec.safe_push (suggestion);
+
+  return true;
+}
+
+/* Examing the namespace binding BINDING, and add at most one instance
+   of the name, if it contains a visible entity of interest.  */
+
+void
+maybe_add_fuzzy_binding (auto_vec<tree> &vec, tree binding,
+                             lookup_name_fuzzy_kind kind)
+{
+  tree value = NULL_TREE;
+
+  if (STAT_HACK_P (binding))
+    {
+      if (!STAT_TYPE_HIDDEN_P (binding)
+         && STAT_TYPE (binding))
+       {
+         if (maybe_add_fuzzy_decl (vec, STAT_TYPE (binding)))
+           return;
+       }
+      else if (!STAT_DECL_HIDDEN_P (binding))
+       value = STAT_DECL (binding);
+    }
+  else
+    value = binding;
+
+  value = ovl_skip_hidden (value);
+  if (value)
+    {
+      value = OVL_FIRST (value);
+      if (kind != FUZZY_LOOKUP_TYPENAME
+         || TREE_CODE (STRIP_TEMPLATE (value)) == TYPE_DECL)
+       if (maybe_add_fuzzy_decl (vec, value))
+         return;
+    }
+}
+
 /* Helper function for lookup_name_fuzzy.
    Traverse binding level LVL, looking for good name matches for NAME
    (and BM).  */
@@ -6157,38 +6219,46 @@ consider_binding_level (tree name, best_match <tree, const char *> &bm,
       }
   else
     {
-      /* Iterate over the namespace hash table, that'll have fewer
-        entries than the decl list.  */
+      /* We need to iterate over the namespace hash table, in order to
+         not mention hidden entities.  But hash table iteration is
+         (essentially) unpredictable, our correction-distance measure
+         is very granular, and we pick the first of equal distances.
+         Hence, we need to call the distance-measurer in a predictable
+         order.  So, iterate over the namespace hash, inserting
+         visible names into a vector.  Then sort the vector.  Then
+         determine spelling distance.  */
+      
       tree ns = lvl->this_entity;
+      auto_vec<tree> vec;
 
       hash_table<named_decl_hash>::iterator end
        (DECL_NAMESPACE_BINDINGS (ns)->end ());
       for (hash_table<named_decl_hash>::iterator iter
             (DECL_NAMESPACE_BINDINGS (ns)->begin ()); iter != end; ++iter)
+       maybe_add_fuzzy_binding (vec, *iter, kind);
+
+      vec.qsort ([] (const void *a_, const void *b_)
+                {
+                  return strcmp (IDENTIFIER_POINTER (*(const tree *)a_),
+                                 IDENTIFIER_POINTER (*(const tree *)b_));
+                });
+
+      /* Examine longest to shortest.  */
+      for (unsigned ix = vec.length (); ix--;)
        {
-         tree binding = *iter;
-         tree value = NULL_TREE;
+         const char *str = IDENTIFIER_POINTER (vec[ix]);
 
-         if (STAT_HACK_P (binding))
-           {
-             if (!STAT_TYPE_HIDDEN_P (binding)
-                 && STAT_TYPE (binding))
-               consider_decl (STAT_TYPE (binding), bm,
-                              consider_implementation_names);
-             else if (!STAT_DECL_HIDDEN_P (binding))
-               value = STAT_DECL (binding);
-           }
-         else
-           value = binding;
+         /* Ignore internal names with spaces in them.  */
+         if (strchr (str, ' '))
+           continue;
          
-         value = ovl_skip_hidden (value);
-         if (value)
-           {
-             value = OVL_FIRST (value);
-             if (!(kind == FUZZY_LOOKUP_TYPENAME
-                   && TREE_CODE (STRIP_TEMPLATE (value)) != TYPE_DECL))
-               consider_decl (value, bm, consider_implementation_names);
-           }
+         /* Don't suggest names that are reserved for use by the
+            implementation, unless NAME began with an underscore.  */
+         if (!consider_implementation_names
+             && name_reserved_for_implementation_p (str))
+           continue;
+
+         bm.consider (str);
        }
     }
 }
index 175ba4a2e0fedb9b8118cc1555c6767e7e9a6cde..ed292f2bae0f3ef6a9f2cd092605199a1e880907 100644 (file)
@@ -30,7 +30,7 @@ void test (const char *buf, char ch)
 {
   __builtin_strtchr (buf, ch); /* { dg-line misspelled_reserved } */
   /* { dg-warning "did you mean '__builtin_strchr'" "" { target c } misspelled_reserved } */
-  /* { dg-error "'__builtin_strtchr' was not declared in this scope; did you mean '__builtin_strchr'\\?" "" { target c++ } misspelled_reserved } */
+  /* { dg-error "'__builtin_strtchr' was not declared in this scope; did you mean '__builtin_strrchr'\\?" "" { target c++ } misspelled_reserved } */
 }
 
 /* Similarly for a name that begins with a single underscore.  */