c++: Hash table iteration for namespace-member spelling suggestions

For 'no such binding' errors, we iterate over binding levels to find a
close match.  At the namespace level we were using DECL_ANTICIPATED to
skip undeclared builtins.  But (a) there are other unnameable things
there and (b) decl-anticipated is about to go away.  This changes the
namespace scanning to iterate over the hash table, and look at
non-hidden bindings.  This does mean we look at fewer strings
(hurrarh), but the order we meet them is somewhat 'random'.  Our
distance measure is not very fine grained, and a couple of testcases
change their suggestion.  I notice for the c/c++ common one, we now
match the output of the C compiler.  For the other one we think 'int'
and 'int64_t' have the same distance from 'int64', and now meet the
former first.  That's a little unfortunate.  If it's too problematic I
suppose we could sort the strings via an intermediate array before
measuring distance.

	gcc/cp/
	* name-lookup.c (consider_decl): New, broken out of ...
	(consider_binding_level): ... here.  Iterate the hash table for
	namespace bindings.
	gcc/testsuite/
	* c-c++-common/spellcheck-reserved.c: Adjust diagnostic.
	* g++.dg/spellcheck-typenames.C: Adjust diagnostic.
This commit is contained in:
Nathan Sidwell 2020-10-02 11:13:26 -07:00
parent 9340d1c97b
commit 7ee1c0413e
3 changed files with 90 additions and 48 deletions

View file

@ -6106,6 +6106,39 @@ qualified_namespace_lookup (tree scope, name_lookup *lookup)
return found;
}
static void
consider_decl (tree decl, best_match <tree, const char *> &bm,
bool consider_impl_names)
{
/* Skip compiler-generated variables (e.g. __for_begin/__for_end
within range for). */
if (TREE_CODE (decl) == VAR_DECL && DECL_ARTIFICIAL (decl))
return;
tree suggestion = DECL_NAME (decl);
if (!suggestion)
return;
/* 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;
const char *suggestion_str = IDENTIFIER_POINTER (suggestion);
/* Ignore internal names with spaces in them. */
if (strchr (suggestion_str, ' '))
return;
/* Don't suggest names that are reserved for use by the
implementation, unless NAME began with an underscore. */
if (!consider_impl_names
&& name_reserved_for_implementation_p (suggestion_str))
return;
bm.consider (suggestion_str);
}
/* Helper function for lookup_name_fuzzy.
Traverse binding level LVL, looking for good name matches for NAME
(and BM). */
@ -6129,54 +6162,63 @@ consider_binding_level (tree name, best_match <tree, const char *> &bm,
with an underscore. */
bool consider_implementation_names = (IDENTIFIER_POINTER (name)[0] == '_');
for (tree t = lvl->names; t; t = TREE_CHAIN (t))
if (lvl->kind != sk_namespace)
for (tree t = lvl->names; t; t = TREE_CHAIN (t))
{
tree d = t;
/* OVERLOADs or decls from using declaration are wrapped into
TREE_LIST. */
if (TREE_CODE (d) == TREE_LIST)
d = OVL_FIRST (TREE_VALUE (d));
/* Don't use bindings from implicitly declared functions,
as they were likely misspellings themselves. */
if (TREE_TYPE (d) == error_mark_node)
continue;
/* If we want a typename, ignore non-types. */
if (kind == FUZZY_LOOKUP_TYPENAME
&& TREE_CODE (STRIP_TEMPLATE (d)) != TYPE_DECL)
continue;
consider_decl (d, bm, consider_implementation_names);
}
else
{
tree d = t;
/* Iterate over the namespace hash table, that'll have fewer
entries than the decl list. */
tree ns = lvl->this_entity;
/* OVERLOADs or decls from using declaration are wrapped into
TREE_LIST. */
if (TREE_CODE (d) == TREE_LIST)
d = OVL_FIRST (TREE_VALUE (d));
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;
/* Don't use bindings from implicitly declared functions,
as they were likely misspellings themselves. */
if (TREE_TYPE (d) == error_mark_node)
continue;
/* Skip anticipated decls of builtin functions. */
if (TREE_CODE (d) == FUNCTION_DECL
&& fndecl_built_in_p (d)
&& DECL_ANTICIPATED (d))
continue;
/* Skip compiler-generated variables (e.g. __for_begin/__for_end
within range for). */
if (TREE_CODE (d) == VAR_DECL
&& DECL_ARTIFICIAL (d))
continue;
tree suggestion = DECL_NAME (d);
if (!suggestion)
continue;
/* 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))
continue;
const char *suggestion_str = IDENTIFIER_POINTER (suggestion);
/* Ignore internal names with spaces in them. */
if (strchr (suggestion_str, ' '))
continue;
/* Don't suggest names that are reserved for use by the
implementation, unless NAME began with an underscore. */
if (name_reserved_for_implementation_p (suggestion_str)
&& !consider_implementation_names)
continue;
bm.consider (suggestion_str);
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;
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);
}
}
}
}

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_strrchr'\\?" "" { target c++ } misspelled_reserved } */
/* { dg-error "'__builtin_strtchr' was not declared in this scope; did you mean '__builtin_strchr'\\?" "" { target c++ } misspelled_reserved } */
}
/* Similarly for a name that begins with a single underscore. */

View file

@ -54,11 +54,11 @@ struct some_thing test_6; // { dg-error "aggregate 'some_thing test_6' has incom
{ dg-end-multiline-output "" } */
typedef long int64_t;
int64 i; // { dg-error "1: 'int64' does not name a type; did you mean 'int64_t'?" }
int64 i; // { dg-error "1: 'int64' does not name a type; did you mean 'int'?" }
/* { dg-begin-multiline-output "" }
int64 i;
^~~~~
int64_t
int
{ dg-end-multiline-output "" } */
/* Verify that gcc doesn't offer nonsensical suggestions. */