c++: Make spell corrections consistent

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.
This commit is contained in:
Nathan Sidwell 2020-10-05 06:36:38 -07:00
parent bf490f0636
commit 255aa06d40
2 changed files with 95 additions and 25 deletions

View 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)
{
tree binding = *iter;
tree value = NULL_TREE;
maybe_add_fuzzy_binding (vec, *iter, kind);
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;
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--;)
{
const char *str = IDENTIFIER_POINTER (vec[ix]);
/* 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);
}
}
}

View 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. */