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, as gcc 9 now 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.

The root cause is that dump_scope doesn't print anything when called for
CONST_DECL in a namespace: the scope is an ENUMERAL_TYPE, rather than
a namespace.

This patch tweaks dump_scope to detect ENUMERAL_TYPE, and to use the
enclosing namespace, 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 };
      |          ^~~~~~~~~~~

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.

OK for trunk?

gcc/cp/ChangeLog:
        PR c++/88121
        * error.c (dump_scope): Handle ENUMERAL_TYPE by using the
        CP_TYPE_CONTEXT of the type.

gcc/testsuite/ChangeLog:
        PR c++/88121
        * g++.dg/lookup/suggestions-scoped-enums.C: New test.
        * g++.dg/lookup/suggestions-unscoped-enums.C: New test.
---
 gcc/cp/error.c                                     |  6 ++
 .../g++.dg/lookup/suggestions-scoped-enums.C       | 13 ++++
 .../g++.dg/lookup/suggestions-unscoped-enums.C     | 91 ++++++++++++++++++++++
 3 files changed, 110 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/lookup/suggestions-scoped-enums.C
 create mode 100644 gcc/testsuite/g++.dg/lookup/suggestions-unscoped-enums.C

diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 72b42bd..6fee62d 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -182,6 +182,12 @@ dump_scope (cxx_pretty_printer *pp, tree scope, int flags)
   if (scope == NULL_TREE)
     return;
 
+  /* Enum values will be CONST_DECL with an ENUMERAL_TYPE as their
+     "scope".  Use CP_TYPE_CONTEXT of the ENUMERAL_TYPE, so as to
+     print the enclosing namespace.  */
+  if (TREE_CODE (scope) == ENUMERAL_TYPE)
+    scope = CP_TYPE_CONTEXT (scope);
+
   if (TREE_CODE (scope) == NAMESPACE_DECL)
     {
       if (scope != global_namespace)
diff --git a/gcc/testsuite/g++.dg/lookup/suggestions-scoped-enums.C 
b/gcc/testsuite/g++.dg/lookup/suggestions-scoped-enums.C
new file mode 100644
index 0000000..2bf3ed6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lookup/suggestions-scoped-enums.C
@@ -0,0 +1,13 @@
+// { dg-do compile { target c++11 } }
+// { dg-options "-fdiagnostics-show-caret" }
+
+enum class vegetable { CARROT, TURNIP };
+
+void misspelled_value_in_scoped_enum ()
+{
+  vegetable::TURNUP; // { dg-error "'TURNUP' is not a member of 'vegetable'" }
+  /* { dg-begin-multiline-output "" }
+   vegetable::TURNUP;
+              ^~~~~~
+     { dg-end-multiline-output "" } */
+}
diff --git a/gcc/testsuite/g++.dg/lookup/suggestions-unscoped-enums.C 
b/gcc/testsuite/g++.dg/lookup/suggestions-unscoped-enums.C
new file mode 100644
index 0000000..bc610d0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lookup/suggestions-unscoped-enums.C
@@ -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_2
+{
+  enum enum_in_outer_ns_2 { 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_2::NIGHT'" }
+  /* { dg-begin-multiline-output "" }
+   outer_ns_a::NIGHT;
+               ^~~~~
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+   enum enum_in_outer_ns_2 { NIGHT, DAY };
+                             ^~~~~
+     { dg-end-multiline-output "" } */
+}
-- 
1.8.5.3

Reply via email to