From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 81762 invoked by alias); 5 Dec 2018 20:41:21 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Received: (qmail 81741 invoked by uid 89); 5 Dec 2018 20:41:19 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-26.9 required=5.0 tests=BAYES_00,GIT_PATCH_0,GIT_PATCH_1,GIT_PATCH_2,GIT_PATCH_3,SPF_HELO_PASS autolearn=ham version=3.3.2 spammy=offering, fes, gty, GTY X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Wed, 05 Dec 2018 20:41:15 +0000 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id C89C8315485A for ; Wed, 5 Dec 2018 20:41:14 +0000 (UTC) Received: from c64.redhat.com (ovpn-112-11.phx2.redhat.com [10.3.112.11]) by smtp.corp.redhat.com (Postfix) with ESMTP id B4BDA5C5E0; Wed, 5 Dec 2018 20:41:13 +0000 (UTC) From: David Malcolm To: gcc-patches@gcc.gnu.org Cc: David Malcolm Subject: [PATCH] C/C++: don't suggest decls that are being initialized (PR c++/88320) Date: Wed, 05 Dec 2018 20:41:00 -0000 Message-Id: <1544045340-57379-1-git-send-email-dmalcolm@redhat.com> X-IsSubscribed: yes X-SW-Source: 2018-12/txt/msg00309.txt.bz2 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 &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 &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) "% 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 *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