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 190b56b..774c447 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 175ba4a..ed292f2 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.  */