From 255aa06d40d7b151d1b26cb690e0545f834b3bea Mon Sep 17 00:00:00 2001 From: Nathan Sidwell Date: Mon, 5 Oct 2020 06:36:38 -0700 Subject: [PATCH] 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. --- gcc/cp/name-lookup.c | 116 ++++++++++++++---- .../c-c++-common/spellcheck-reserved.c | 2 +- 2 files changed, 94 insertions(+), 24 deletions(-) diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c index 190b56bf4dd..774c4473390 100644 --- a/gcc/cp/name-lookup.c +++ b/gcc/cp/name-lookup.c @@ -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 &bm, bool consider_impl_names) @@ -6110,6 +6113,65 @@ consider_decl (tree decl, best_match &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 &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 &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 &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 vec; hash_table::iterator end (DECL_NAMESPACE_BINDINGS (ns)->end ()); for (hash_table::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); } } } diff --git a/gcc/testsuite/c-c++-common/spellcheck-reserved.c b/gcc/testsuite/c-c++-common/spellcheck-reserved.c index 175ba4a2e0f..ed292f2bae0 100644 --- a/gcc/testsuite/c-c++-common/spellcheck-reserved.c +++ b/gcc/testsuite/c-c++-common/spellcheck-reserved.c @@ -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. */ -- 2.30.2