mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-04-09 17:11:20 +08:00
C++: namespaces and scopes for enum values (PR c++/88121)
Consider this test case: namespace json { enum { JSON_OBJECT }; } void test () { JSON_OBJECT; } which erroneously accesses an enum value in another namespace without qualifying the access. GCC 6 through 8 issue a suggestion that doesn't mention the namespace: <source>: In function 'void test()': <source>:8:3: error: 'JSON_OBJECT' was not declared in this scope JSON_OBJECT; ^~~~~~~~~~~ <source>:8:3: note: suggested alternative: <source>:3:10: note: 'JSON_OBJECT' enum { JSON_OBJECT }; ^~~~~~~~~~~ which is suboptimal. I made the problem worse with r265610, which consolidates the single suggestion into the error, and emits: <source>: In function 'void test()': <source>:8:3: error: 'JSON_OBJECT' was not declared in this scope; did you mean 'JSON_OBJECT'? 8 | JSON_OBJECT; | ^~~~~~~~~~~ | JSON_OBJECT <source>:3:10: note: 'JSON_OBJECT' declared here 3 | enum { JSON_OBJECT }; | ^~~~~~~~~~~ where the message: 'JSON_OBJECT' was not declared in this scope; did you mean 'JSON_OBJECT'? is nonsensical. This patch tweaks dump_scope to detect unscoped enums, and to use the enclosing namespace for them, so that the CONST_DECL is dumped as "json::JSON_OBJECT". This changes the output for the above so that it refers to the namespace, fixing the issue: <source>:8:3: error: 'JSON_OBJECT' was not declared in this scope; did you mean 'json::JSON_OBJECT'? 9 | JSON_OBJECT; | ^~~~~~~~~~~ | json::JSON_OBJECT <source>3:10: note: 'json::JSON_OBJECT' declared here 3 | enum { JSON_OBJECT }; | ^~~~~~~~~~~ The patch also fixes scope-printing for values within scoped enums. To exercise this, the patch extends the scanner for namespaces for exact matches for a name, so that we also scan inside scoped enums, to cover the case where someone doesn't supply the scope. Hence with the patch given e.g.: enum class vegetable { CARROT, TURNIP }; we're able to offer e.g.: suggestions-scoped-enums.C:50:3: error: 'CARROT' was not declared in this scope; did you mean 'vegetable::CARROT'? 50 | CARROT; | ^~~~~~ | vegetable::CARROT and this exercises the code path above. The patch updates dump_scope for scoped enums so that we print the scope when printing the value ("vegetable::CARROT"), rather than just the name of the value ("CARROT"). Finally, the patch adds spell-corrections within a scoped enum, giving e.g.: suggestions-scoped-enums.C:18:14: error: 'TURNUP' is not a member of 'vegetable'; did you mean 'TURNIP'? 18 | vegetable::TURNUP; | ^~~~~~ | TURNIP gcc/cp/ChangeLog: PR c++/88121 * cp-name-hint.h (suggest_alternative_in_scoped_enum): New decl. * error.c (dump_scope): Ensure that we print any scope for values of unscoped enums. Print the scope of values of scoped enums. (qualified_name_lookup_error): Offer suggestions for failures within scoped enums by calling suggest_alternative_in_scoped_enum. * name-lookup.c (class namespace_hints): Update comment to mention scoped enums. (namespace_hints::namespace_hints): Call maybe_add_candidate_for_scoped_enum. (namespace_hints::maybe_add_candidate_for_scoped_enum): New member (suggest_alternatives_for): Update comment to mention scoped enums. (suggest_alternative_in_scoped_enum): New function. gcc/testsuite/ChangeLog: PR c++/88121 * g++.dg/lookup/suggestions-scoped-enums.C: New test. * g++.dg/lookup/suggestions-unscoped-enums.C: New test. From-SVN: r266644
This commit is contained in:
parent
171954d816
commit
3ded6ffdfd
gcc
@ -1,3 +1,20 @@
|
||||
2018-11-29 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR c++/88121
|
||||
* cp-name-hint.h (suggest_alternative_in_scoped_enum): New decl.
|
||||
* error.c (dump_scope): Ensure that we print any scope for values
|
||||
of unscoped enums. Print the scope of values of scoped enums.
|
||||
(qualified_name_lookup_error): Offer suggestions for failures
|
||||
within scoped enums by calling suggest_alternative_in_scoped_enum.
|
||||
* name-lookup.c (class namespace_hints): Update comment to mention
|
||||
scoped enums.
|
||||
(namespace_hints::namespace_hints): Call
|
||||
maybe_add_candidate_for_scoped_enum.
|
||||
(namespace_hints::maybe_add_candidate_for_scoped_enum): New member
|
||||
(suggest_alternatives_for): Update comment to mention scoped
|
||||
enums.
|
||||
(suggest_alternative_in_scoped_enum): New function.
|
||||
|
||||
2018-11-28 Marek Polacek <polacek@redhat.com>
|
||||
|
||||
Implement P1094R2, Nested inline namespaces.
|
||||
|
@ -33,5 +33,6 @@ along with GCC; see the file COPYING3. If not see
|
||||
extern name_hint suggest_alternatives_for (location_t, tree, bool);
|
||||
extern name_hint suggest_alternatives_in_other_namespaces (location_t, tree);
|
||||
extern name_hint suggest_alternative_in_explicit_scope (location_t, tree, tree);
|
||||
extern name_hint suggest_alternative_in_scoped_enum (tree, tree);
|
||||
|
||||
#endif /* GCC_CP_NAME_HINT_H */
|
||||
|
@ -182,6 +182,12 @@ dump_scope (cxx_pretty_printer *pp, tree scope, int flags)
|
||||
if (scope == NULL_TREE)
|
||||
return;
|
||||
|
||||
/* Enum values within an unscoped enum will be CONST_DECL with an
|
||||
ENUMERAL_TYPE as their "scope". Use CP_TYPE_CONTEXT of the
|
||||
ENUMERAL_TYPE, so as to print any enclosing namespace. */
|
||||
if (UNSCOPED_ENUM_P (scope))
|
||||
scope = CP_TYPE_CONTEXT (scope);
|
||||
|
||||
if (TREE_CODE (scope) == NAMESPACE_DECL)
|
||||
{
|
||||
if (scope != global_namespace)
|
||||
@ -190,7 +196,8 @@ dump_scope (cxx_pretty_printer *pp, tree scope, int flags)
|
||||
pp_cxx_colon_colon (pp);
|
||||
}
|
||||
}
|
||||
else if (AGGREGATE_TYPE_P (scope))
|
||||
else if (AGGREGATE_TYPE_P (scope)
|
||||
|| SCOPED_ENUM_P (scope))
|
||||
{
|
||||
dump_type (pp, scope, f);
|
||||
pp_cxx_colon_colon (pp);
|
||||
@ -4261,7 +4268,21 @@ qualified_name_lookup_error (tree scope, tree name,
|
||||
print_candidates (decl);
|
||||
}
|
||||
else
|
||||
error_at (location, "%qD is not a member of %qT", name, scope);
|
||||
{
|
||||
name_hint hint;
|
||||
if (SCOPED_ENUM_P (scope))
|
||||
hint = suggest_alternative_in_scoped_enum (name, scope);
|
||||
if (const char *suggestion = hint.suggestion ())
|
||||
{
|
||||
gcc_rich_location richloc (location);
|
||||
richloc.add_fixit_replace (suggestion);
|
||||
error_at (&richloc,
|
||||
"%qD is not a member of %qT; did you mean %qs?",
|
||||
name, scope, suggestion);
|
||||
}
|
||||
else
|
||||
error_at (location, "%qD is not a member of %qT", name, scope);
|
||||
}
|
||||
}
|
||||
else if (scope != global_namespace)
|
||||
{
|
||||
|
@ -5396,8 +5396,8 @@ class suggest_alternatives : public deferred_diagnostic
|
||||
};
|
||||
|
||||
/* A class for encapsulating the result of a search across
|
||||
multiple namespaces for an unrecognized name seen at a
|
||||
given source location. */
|
||||
multiple namespaces (and scoped enums within them) for an
|
||||
unrecognized name seen at a given source location. */
|
||||
|
||||
class namespace_hints
|
||||
{
|
||||
@ -5408,6 +5408,8 @@ class namespace_hints
|
||||
name_hint maybe_decorate_with_limit (name_hint);
|
||||
|
||||
private:
|
||||
void maybe_add_candidate_for_scoped_enum (tree scoped_enum, tree name);
|
||||
|
||||
location_t m_loc;
|
||||
tree m_name;
|
||||
vec<tree> m_candidates;
|
||||
@ -5419,8 +5421,8 @@ class namespace_hints
|
||||
bool m_limited;
|
||||
};
|
||||
|
||||
/* Constructor for namespace_hints. Search namespaces, looking for a match
|
||||
for unrecognized NAME seen at LOC. */
|
||||
/* Constructor for namespace_hints. Search namespaces and scoped enums,
|
||||
looking for an exact match for unrecognized NAME seen at LOC. */
|
||||
|
||||
namespace_hints::namespace_hints (location_t loc, tree name)
|
||||
: m_loc(loc), m_name (name)
|
||||
@ -5451,10 +5453,22 @@ namespace_hints::namespace_hints (location_t loc, tree name)
|
||||
|
||||
for (tree decl = NAMESPACE_LEVEL (ns)->names;
|
||||
decl; decl = TREE_CHAIN (decl))
|
||||
if (TREE_CODE (decl) == NAMESPACE_DECL
|
||||
&& !DECL_NAMESPACE_ALIAS (decl)
|
||||
&& !DECL_NAMESPACE_INLINE_P (decl))
|
||||
children.safe_push (decl);
|
||||
{
|
||||
if (TREE_CODE (decl) == NAMESPACE_DECL
|
||||
&& !DECL_NAMESPACE_ALIAS (decl)
|
||||
&& !DECL_NAMESPACE_INLINE_P (decl))
|
||||
children.safe_push (decl);
|
||||
|
||||
/* Look for exact matches for NAME within scoped enums.
|
||||
These aren't added to the worklist, and so don't count
|
||||
against the search limit. */
|
||||
if (TREE_CODE (decl) == TYPE_DECL)
|
||||
{
|
||||
tree type = TREE_TYPE (decl);
|
||||
if (SCOPED_ENUM_P (type))
|
||||
maybe_add_candidate_for_scoped_enum (type, name);
|
||||
}
|
||||
}
|
||||
|
||||
while (!m_limited && !children.is_empty ())
|
||||
{
|
||||
@ -5522,11 +5536,32 @@ namespace_hints::maybe_decorate_with_limit (name_hint hint)
|
||||
return hint;
|
||||
}
|
||||
|
||||
/* Look inside SCOPED_ENUM for exact matches for NAME.
|
||||
If one is found, add its CONST_DECL to m_candidates. */
|
||||
|
||||
void
|
||||
namespace_hints::maybe_add_candidate_for_scoped_enum (tree scoped_enum,
|
||||
tree name)
|
||||
{
|
||||
gcc_assert (SCOPED_ENUM_P (scoped_enum));
|
||||
|
||||
for (tree iter = TYPE_VALUES (scoped_enum); iter; iter = TREE_CHAIN (iter))
|
||||
{
|
||||
tree id = TREE_PURPOSE (iter);
|
||||
if (id == name)
|
||||
{
|
||||
m_candidates.safe_push (TREE_VALUE (iter));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Generate a name_hint at LOCATION for NAME, an IDENTIFIER_NODE for which
|
||||
name lookup failed.
|
||||
|
||||
Search through all available namespaces and generate a suggestion and/or
|
||||
a deferred diagnostic that lists possible candidate(s).
|
||||
Search through all available namespaces and any scoped enums within them
|
||||
and generate a suggestion and/or a deferred diagnostic that lists possible
|
||||
candidate(s).
|
||||
|
||||
If no exact matches are found, and SUGGEST_MISSPELLINGS is true, then also
|
||||
look for near-matches and suggest the best near-match, if there is one.
|
||||
@ -5910,6 +5945,23 @@ suggest_alternative_in_explicit_scope (location_t location, tree name,
|
||||
return name_hint ();
|
||||
}
|
||||
|
||||
/* Given NAME, look within SCOPED_ENUM for possible spell-correction
|
||||
candidates. */
|
||||
|
||||
name_hint
|
||||
suggest_alternative_in_scoped_enum (tree name, tree scoped_enum)
|
||||
{
|
||||
gcc_assert (SCOPED_ENUM_P (scoped_enum));
|
||||
|
||||
best_match <tree, const char *> bm (name);
|
||||
for (tree iter = TYPE_VALUES (scoped_enum); iter; iter = TREE_CHAIN (iter))
|
||||
{
|
||||
tree id = TREE_PURPOSE (iter);
|
||||
bm.consider (IDENTIFIER_POINTER (id));
|
||||
}
|
||||
return name_hint (bm.get_best_meaningful_candidate (), NULL);
|
||||
}
|
||||
|
||||
/* Look up NAME (an IDENTIFIER_NODE) in SCOPE (either a NAMESPACE_DECL
|
||||
or a class TYPE).
|
||||
|
||||
|
@ -1,3 +1,9 @@
|
||||
2018-11-29 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR c++/88121
|
||||
* g++.dg/lookup/suggestions-scoped-enums.C: New test.
|
||||
* g++.dg/lookup/suggestions-unscoped-enums.C: New test.
|
||||
|
||||
2018-11-29 Peter Bergner <bergner@linux.ibm.com>
|
||||
|
||||
PR target/87496
|
||||
|
110
gcc/testsuite/g++.dg/lookup/suggestions-scoped-enums.C
Normal file
110
gcc/testsuite/g++.dg/lookup/suggestions-scoped-enums.C
Normal file
@ -0,0 +1,110 @@
|
||||
// { dg-do compile { target c++11 } }
|
||||
// { dg-options "-fdiagnostics-show-caret" }
|
||||
|
||||
/* Verify that we offer suggestions for misspelled values
|
||||
in scoped enums, and for values from scoped enums that
|
||||
are missing their scope.
|
||||
Verify that we emit fix-it hints for those cases for
|
||||
which we have adequate location information. */
|
||||
|
||||
enum class vegetable { CARROT, TURNIP }; // { dg-line decl_of_vegetable }
|
||||
namespace pasta
|
||||
{
|
||||
enum class shape { LASAGNA, LINGUINE, SPAGHETTI, TAGLIATELLE }; // { dg-line decl_of_shape }
|
||||
}
|
||||
|
||||
void misspelled_value_in_scoped_enum ()
|
||||
{
|
||||
vegetable::TURNUP; // { dg-error "'TURNUP' is not a member of 'vegetable'; did you mean 'TURNIP'" }
|
||||
/* { dg-begin-multiline-output "" }
|
||||
vegetable::TURNUP;
|
||||
^~~~~~
|
||||
TURNIP
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
void misspelled_value_using_explicit_ns ()
|
||||
{
|
||||
pasta::shape::SPOOGHETTI; // { dg-error "'SPOOGHETTI' is not a member of 'pasta::shape'; did you mean 'SPAGHETTI'" }
|
||||
/* { dg-begin-multiline-output "" }
|
||||
pasta::shape::SPOOGHETTI;
|
||||
^~~~~~~~~~
|
||||
SPAGHETTI
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
namespace pasta {
|
||||
void misspelled_value_using_implicit_ns ()
|
||||
{
|
||||
shape::SPOOGHETTI; // { dg-error "'SPOOGHETTI' is not a member of 'pasta::shape'; did you mean 'SPAGHETTI'" }
|
||||
/* { dg-begin-multiline-output "" }
|
||||
shape::SPOOGHETTI;
|
||||
^~~~~~~~~~
|
||||
SPAGHETTI
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
} // namespace pasta
|
||||
|
||||
void unqualified_enum_in_global_ns ()
|
||||
{
|
||||
CARROT; // { dg-error "'CARROT' was not declared in this scope; did you mean 'vegetable::CARROT'" }
|
||||
/* { dg-begin-multiline-output "" }
|
||||
CARROT;
|
||||
^~~~~~
|
||||
vegetable::CARROT
|
||||
{ dg-end-multiline-output "" } */
|
||||
// { dg-message "'vegetable::CARROT' declared here" "" { target *-*-* } decl_of_vegetable }
|
||||
/* { dg-begin-multiline-output "" }
|
||||
enum class vegetable { CARROT, TURNIP };
|
||||
^~~~~~
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
void unqualified_enum_in_ns ()
|
||||
{
|
||||
LASAGNA; // { dg-error "'LASAGNA' was not declared in this scope; did you mean 'pasta::shape::LASAGNA'" }
|
||||
/* { dg-begin-multiline-output "" }
|
||||
LASAGNA;
|
||||
^~~~~~~
|
||||
pasta::shape::LASAGNA
|
||||
{ dg-end-multiline-output "" } */
|
||||
// { dg-message "'pasta::shape::LASAGNA' declared here" "" { target *-*-* } decl_of_shape }
|
||||
/* { dg-begin-multiline-output "" }
|
||||
enum class shape { LASAGNA, LINGUINE, SPAGHETTI, TAGLIATELLE };
|
||||
^~~~~~~
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
/* We can't guarantee location information here, so don't expect a
|
||||
fix-it hint. */
|
||||
|
||||
void unqualified_enum_in_explicit_ns ()
|
||||
{
|
||||
pasta::LINGUINE; // { dg-error "'LINGUINE' is not a member of 'pasta'; did you mean 'pasta::shape::LINGUINE'" }
|
||||
/* { dg-begin-multiline-output "" }
|
||||
pasta::LINGUINE;
|
||||
^~~~~~~~
|
||||
{ dg-end-multiline-output "" } */
|
||||
// { dg-message "'pasta::shape::LINGUINE' declared here" "" { target *-*-* } decl_of_shape }
|
||||
/* { dg-begin-multiline-output "" }
|
||||
enum class shape { LASAGNA, LINGUINE, SPAGHETTI, TAGLIATELLE };
|
||||
^~~~~~~~
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
namespace pasta {
|
||||
void unqualified_enum_in_implicit_ns ()
|
||||
{
|
||||
TAGLIATELLE; // { dg-error "'TAGLIATELLE' was not declared in this scope; did you mean 'pasta::shape::TAGLIATELLE'" }
|
||||
/* { dg-begin-multiline-output "" }
|
||||
TAGLIATELLE;
|
||||
^~~~~~~~~~~
|
||||
pasta::shape::TAGLIATELLE
|
||||
{ dg-end-multiline-output "" } */
|
||||
// { dg-message "'pasta::shape::TAGLIATELLE' declared here" "" { target *-*-* } decl_of_shape }
|
||||
/* { dg-begin-multiline-output "" }
|
||||
enum class shape { LASAGNA, LINGUINE, SPAGHETTI, TAGLIATELLE };
|
||||
^~~~~~~~~~~
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
}
|
91
gcc/testsuite/g++.dg/lookup/suggestions-unscoped-enums.C
Normal file
91
gcc/testsuite/g++.dg/lookup/suggestions-unscoped-enums.C
Normal file
@ -0,0 +1,91 @@
|
||||
// { dg-options "-fdiagnostics-show-caret" }
|
||||
|
||||
enum { LASAGNA, SPAGHETTI };
|
||||
namespace outer_ns_a
|
||||
{
|
||||
enum enum_in_outer_ns_a { STRAWBERRY, BANANA };
|
||||
namespace inner_ns
|
||||
{
|
||||
enum enum_in_inner_ns { ELEPHANT, LION };
|
||||
}
|
||||
}
|
||||
namespace outer_ns_b
|
||||
{
|
||||
enum enum_in_outer_ns_b { NIGHT, DAY };
|
||||
}
|
||||
|
||||
void misspelled_enum_in_global_ns ()
|
||||
{
|
||||
SPOOGHETTI; // { dg-error "'SPOOGHETTI' was not declared in this scope; did you mean 'SPAGHETTI'" }
|
||||
/* { dg-begin-multiline-output "" }
|
||||
SPOOGHETTI;
|
||||
^~~~~~~~~~
|
||||
SPAGHETTI
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
void unqualified_enum_in_outer_ns ()
|
||||
{
|
||||
BANANA; // { dg-error "'BANANA' was not declared in this scope; did you mean 'outer_ns_a::BANANA'" }
|
||||
/* { dg-begin-multiline-output "" }
|
||||
BANANA;
|
||||
^~~~~~
|
||||
outer_ns_a::BANANA
|
||||
{ dg-end-multiline-output "" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
enum enum_in_outer_ns_a { STRAWBERRY, BANANA };
|
||||
^~~~~~
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
namespace outer_ns_a
|
||||
{
|
||||
void misspelled_unqualified_enum_in_outer_ns () {
|
||||
BANANAS; // { dg-error "'BANANAS' was not declared in this scope; did you mean 'BANANA'" }
|
||||
/* { dg-begin-multiline-output "" }
|
||||
BANANAS;
|
||||
^~~~~~~
|
||||
BANANA
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
};
|
||||
|
||||
void unqualified_enum_in_inner_ns ()
|
||||
{
|
||||
ELEPHANT; // { dg-error "'ELEPHANT' was not declared in this scope; did you mean 'outer_ns_a::inner_ns::ELEPHANT'" }
|
||||
/* { dg-begin-multiline-output "" }
|
||||
ELEPHANT;
|
||||
^~~~~~~~
|
||||
outer_ns_a::inner_ns::ELEPHANT
|
||||
{ dg-end-multiline-output "" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
enum enum_in_inner_ns { ELEPHANT, LION };
|
||||
^~~~~~~~
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
void partially_qualified_enum_in_inner_ns ()
|
||||
{
|
||||
outer_ns_a::ELEPHANT; // { dg-error "'ELEPHANT' is not a member of 'outer_ns_a'; did you mean 'outer_ns_a::inner_ns::ELEPHANT'" }
|
||||
/* { dg-begin-multiline-output "" }
|
||||
outer_ns_a::ELEPHANT;
|
||||
^~~~~~~~
|
||||
{ dg-end-multiline-output "" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
enum enum_in_inner_ns { ELEPHANT, LION };
|
||||
^~~~~~~~
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
void wrongly_qualified_enum ()
|
||||
{
|
||||
outer_ns_a::NIGHT; // { dg-error "'NIGHT' is not a member of 'outer_ns_a'; did you mean 'outer_ns_b::NIGHT'" }
|
||||
/* { dg-begin-multiline-output "" }
|
||||
outer_ns_a::NIGHT;
|
||||
^~~~~
|
||||
{ dg-end-multiline-output "" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
enum enum_in_outer_ns_b { NIGHT, DAY };
|
||||
^~~~~
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user