PR c++/88320 reports that the C and C++ FEs can offer bogus suggestions
for misspelled identifiers within initializers, where the thing being
initialized is suggested.  If the user follows these suggestions,
it will lead to a -Wuninitialized warning.  For example:

test.c:9:19: error: 'aresults' was not declared in this scope; did you
   mean 'aresult'?
    9 |     int aresult = aresults + 1;
      |                   ^~~~~~~~
      |                   aresult

This patch filters out any decls being initialized when considering
candidates for suggestions, fixing the issue.

For the C frontend, it makes use of the pre-existing "constructor_decl"
and "initializer_stack" globals.

For the C++ frontend, it adds a couple of fields to cp_parser.

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

OK for trunk?

gcc/c/ChangeLog:
        PR c++/88320
        * c-decl.c (lookup_name_fuzzy): Don't suggest vars that we're
        currently parsing an initializer for.
        * c-tree.h (within_initializer_for_p): New decl.
        * c-typeck.c (within_initializer_for_p): New function.

gcc/cp/ChangeLog:
        PR c++/88320
        * name-lookup.c (consider_binding_level): Don't suggest fields
        if we're currently parsing a mem-initializer-list.  Don't suggest
        vars that we're currently parsing an initializer for.
        * parser.c (cp_parser_mem_initializer_list): Update
        parser->within_mem_initializer_list on entry and exit.
        (cp_parser_init_declarator): Push the decl to
        parser->decls_being_initialized for the duration of the call to
        cp_parser_initializer.
        (within_initializer_for_p): New function.
        (within_mem_initializer_list_p): New function.
        * parser.h (struct cp_parser): Add fields
        "decls_being_initialized" and "within_mem_initializer_list";
        (within_initializer_for_p): New decl.
        (within_mem_initializer_list_p): New decl.

gcc/testsuite/ChangeLog:
        PR c++/88320
        * c-c++-common/spellcheck-in-initializer.c: New test.
        * g++.dg/spellcheck-in-initializer-2.C: New test.
---
 gcc/c/c-decl.c                                     |  7 ++++
 gcc/c/c-tree.h                                     |  1 +
 gcc/c/c-typeck.c                                   | 22 ++++++++++++
 gcc/cp/name-lookup.c                               | 11 +++++-
 gcc/cp/parser.c                                    | 42 ++++++++++++++++++++++
 gcc/cp/parser.h                                    | 12 +++++++
 .../c-c++-common/spellcheck-in-initializer.c       | 22 ++++++++++++
 gcc/testsuite/g++.dg/spellcheck-in-initializer-2.C | 26 ++++++++++++++
 8 files changed, 142 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/c-c++-common/spellcheck-in-initializer.c
 create mode 100644 gcc/testsuite/g++.dg/spellcheck-in-initializer-2.C

diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index b50f2bf..05be78c 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -4152,6 +4152,13 @@ lookup_name_fuzzy (tree name, enum 
lookup_name_fuzzy_kind kind, location_t loc)
          default:
            break;
          }
+       /* Don't suggest vars that we're in the middle of parsing an
+          initializer for, since otherwise if the user follows the
+          suggestion they'll get a -Wuninitialized warning. */
+       if (VAR_P (binding->decl)
+           && within_initializer_for_p (binding->decl))
+         continue;
+
        bm.consider (binding->id);
       }
 
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index 5ed2f48..ce1e702 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -663,6 +663,7 @@ extern void store_init_value (location_t, tree, tree, tree);
 extern void maybe_warn_string_init (location_t, tree, struct c_expr);
 extern void start_init (tree, tree, int, rich_location *);
 extern void finish_init (void);
+extern bool within_initializer_for_p (tree);
 extern void really_start_incremental_init (tree);
 extern void finish_implicit_inits (location_t, struct obstack *);
 extern void push_init_level (location_t, int, struct obstack *);
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index 8fbecfc..2573a42 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -8233,6 +8233,28 @@ finish_init (void)
   initializer_stack = p->next;
   free (p);
 }
+
+/* Return true if we're currently parsing an initializer for DECL.
+
+   This allows us to prevent offering DECL as a suggestion for an
+   unrecognized identifier - following such suggestions would lead
+   to -Wuninitialized warnings.  */
+
+bool
+within_initializer_for_p (tree decl)
+{
+  if (decl == constructor_decl)
+    return true;
+
+  for (const struct initializer_stack *p = initializer_stack;
+       p; p = p->next)
+    if (decl == p->decl)
+      return true;
+
+  return false;
+}
+
+
 
 /* Call here when we see the initializer is surrounded by braces.
    This is instead of a call to push_init_level;
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index cadf380..d7e34d5 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -6023,7 +6023,9 @@ consider_binding_level (tree name, best_match <tree, 
const char *> &bm,
                        enum lookup_name_fuzzy_kind kind)
 {
   if (look_within_fields)
-    if (lvl->this_entity && TREE_CODE (lvl->this_entity) == RECORD_TYPE)
+    if (lvl->this_entity
+       && TREE_CODE (lvl->this_entity) == RECORD_TYPE
+       && !within_mem_initializer_list_p ())
       {
        tree type = lvl->this_entity;
        bool want_type_p = (kind == FUZZY_LOOKUP_TYPENAME);
@@ -6063,6 +6065,13 @@ consider_binding_level (tree name, best_match <tree, 
const char *> &bm,
          && DECL_ARTIFICIAL (d))
        continue;
 
+      /* Don't suggest decls that we're in the middle of parsing an
+        initializer for, since otherwise if the user follows the
+        suggestion they'll get a -Wuninitialized warning. */
+      if (VAR_P (d)
+         && within_initializer_for_p (d))
+       continue;
+
       tree suggestion = DECL_NAME (d);
       if (!suggestion)
        continue;
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 5112cb4..c8d4dfe 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -14794,6 +14794,8 @@ cp_parser_mem_initializer_list (cp_parser* parser)
   tree target_ctor = error_mark_node;
   cp_token *token = cp_lexer_peek_token (parser->lexer);
 
+  ++parser->within_mem_initializer_list;
+
   /* Let the semantic analysis code know that we are starting the
      mem-initializer-list.  */
   if (!DECL_CONSTRUCTOR_P (current_function_decl))
@@ -14871,6 +14873,8 @@ cp_parser_mem_initializer_list (cp_parser* parser)
   /* Perform semantic analysis.  */
   if (DECL_CONSTRUCTOR_P (current_function_decl))
     finish_mem_initializers (mem_initializer_list);
+
+  --parser->within_mem_initializer_list;
 }
 
 /* Parse a mem-initializer.
@@ -20232,9 +20236,11 @@ cp_parser_init_declarator (cp_parser* parser,
             arguments.  So right here we only handle the latter.  */
          if (!member_p && processing_template_decl && decl != error_mark_node)
            start_lambda_scope (decl);
+         vec_safe_push (parser->decls_being_initialized, decl);
          initializer = cp_parser_initializer (parser,
                                               &is_direct_init,
                                               &is_non_constant_init);
+         parser->decls_being_initialized->pop ();
          if (!member_p && processing_template_decl && decl != error_mark_node)
            finish_lambda_scope ();
          if (initializer == error_mark_node)
@@ -41125,4 +41131,40 @@ maybe_show_extern_c_location (void)
            "%<extern \"C\"%> linkage started here");
 }
 
+/* Return true if we're currently parsing an initializer for DECL.
+
+   This allows us to prevent offering DECL as a suggestion for an
+   unrecognized identifier - following such suggestions would lead
+   to -Wuninitialized warnings.  */
+
+bool
+within_initializer_for_p (tree decl)
+{
+  if (the_parser == NULL)
+    return false;
+
+  int i;
+  tree uninitialized_decl;
+  FOR_EACH_VEC_SAFE_ELT (the_parser->decls_being_initialized, i,
+                        uninitialized_decl)
+    if (decl == uninitialized_decl)
+      return true;
+
+  return false;
+}
+
+/* Return true if we're currently parsing a mem-initializer-list.
+
+   This allows us to prevent offering fields as suggestions for
+   unrecognized identifiers - following such suggestions could lead
+   to -Wuninitialized warnings.  */
+
+bool
+within_mem_initializer_list_p ()
+{
+  if (the_parser == NULL)
+    return false;
+  return the_parser->within_mem_initializer_list;
+}
+
 #include "gt-cp-parser.h"
diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h
index 8bfa3f3..8f389e4 100644
--- a/gcc/cp/parser.h
+++ b/gcc/cp/parser.h
@@ -405,6 +405,15 @@ struct GTY(()) cp_parser {
      specification, if any, or UNKNOWN_LOCATION otherwise.  */
   location_t innermost_linkage_specification_location;
 
+  /* A stack of all variables for which we're currently parsing an initializer.
+     This allows us to prevent offering the decls as suggestions for
+     unrecognized identifiers - following such suggestions would lead to
+     -Wuninitialized warnings.  */
+  vec<tree, va_gc> *decls_being_initialized;
+
+  /* Are we within a mem-initializer-list?  This allows us to prevent
+     offering fields as suggestions for unrecognized identifiers.  */
+  int within_mem_initializer_list;
 };
 
 /* In parser.c  */
@@ -417,5 +426,8 @@ extern void cp_debug_parser (FILE *, cp_parser *);
 extern void debug (cp_parser &ref);
 extern void debug (cp_parser *ptr);
 extern bool cp_keyword_starts_decl_specifier_p (enum rid keyword);
+extern bool within_initializer_for_p (tree);
+extern bool within_mem_initializer_list_p ();
+
 
 #endif  /* GCC_CP_PARSER_H  */
diff --git a/gcc/testsuite/c-c++-common/spellcheck-in-initializer.c 
b/gcc/testsuite/c-c++-common/spellcheck-in-initializer.c
new file mode 100644
index 0000000..8af25e2
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/spellcheck-in-initializer.c
@@ -0,0 +1,22 @@
+/* PR c++/88320.  */
+
+/* Verify that we don't offer "aresult" as a suggestion
+   within its own initializer.  */
+
+int test (void)
+{
+    int vresults1 = 0;
+    int aresult = aresults + 1; /* { dg-error "did you mean 'vresults1'" } */
+
+    return aresult;
+}
+
+/* We can't easily tell if it's valid to offer member data
+   as a suggestion within an assignment expression.  */
+
+void test_2 (int the_color)
+{
+  int acolor;
+
+  acolor = color + 1; /* { dg-error "did you mean 'acolor'" } */
+}
diff --git a/gcc/testsuite/g++.dg/spellcheck-in-initializer-2.C 
b/gcc/testsuite/g++.dg/spellcheck-in-initializer-2.C
new file mode 100644
index 0000000..e0c7785
--- /dev/null
+++ b/gcc/testsuite/g++.dg/spellcheck-in-initializer-2.C
@@ -0,0 +1,26 @@
+/* PR c++/88320.  */
+
+struct test
+{
+  test (int);
+  void meth (int);
+
+  int m_color;
+};
+
+/* Verify that we don't offer a field as a suggestion
+   within a mem-initializer-list.  */
+
+test::test (int the_color)
+: m_color (color) // { dg-bogus "did you mean 'm_color'" }
+  // { dg-error "'color'" "" { target *-*-* } .-1 }
+{
+}
+
+/* We can't easily tell if it's valid to offer member data
+   as a suggestion within an assignment expression.  */
+
+void test::meth (int the_color)
+{
+  m_color = color + 1; // { dg-error "did you mean 'm_color'" }
+}
-- 
1.8.5.3

Reply via email to