Probably not entirely fool-proof when using statement expressions in initializers, but should be good enough.
Bootstrapped and regression tested on x86_64. c: Diagnose declarations that are used only in their own initializer [PR115027] Track the declaration that is currently being initialized and do not mark it as read when it is used in its own initializer. This then allows it to be diagnosed as set-but-unused when it is not used elsewhere. PR c/115027 gcc/c/ * c-tree.h (in_decl_init): Declare variable. * c-parser.cc (c_parser_initializer): Record decl being initialized. * c-typeck.cc (in_decl_init): Defintie variable. (mark_exp_read): Ignore decl currently being initialized. gcc/testsuite/ * gcc.dg/pr115027.c: New test. diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc index 8c4e697a4e1..46060665115 100644 --- a/gcc/c/c-parser.cc +++ b/gcc/c/c-parser.cc @@ -6126,11 +6126,14 @@ c_parser_type_name (c_parser *parser, bool alignas_ok) static struct c_expr c_parser_initializer (c_parser *parser, tree decl) { + struct c_expr ret; + tree save = in_decl_init; + in_decl_init = decl; + if (c_parser_next_token_is (parser, CPP_OPEN_BRACE)) - return c_parser_braced_init (parser, NULL_TREE, false, NULL, decl); + ret = c_parser_braced_init (parser, NULL_TREE, false, NULL, decl); else { - struct c_expr ret; location_t loc = c_parser_peek_token (parser)->location; ret = c_parser_expr_no_commas (parser, NULL); if (decl != error_mark_node && C_DECL_VARIABLE_SIZE (decl)) @@ -6154,8 +6157,9 @@ c_parser_initializer (c_parser *parser, tree decl) || C_DECL_DECLARED_CONSTEXPR (COMPOUND_LITERAL_EXPR_DECL (ret.value)))) ret = convert_lvalue_to_rvalue (loc, ret, true, true, true); - return ret; } + in_decl_init = save; + return ret; } /* The location of the last comma within the current initializer list, diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h index 15da875a029..8013963b06d 100644 --- a/gcc/c/c-tree.h +++ b/gcc/c/c-tree.h @@ -740,6 +740,8 @@ extern int in_typeof; extern bool c_in_omp_for; extern bool c_omp_array_section_p; +extern tree in_decl_init; + extern tree c_last_sizeof_arg; extern location_t c_last_sizeof_loc; diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc index 455dc374b48..34279dc1d1a 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -73,6 +73,9 @@ int in_sizeof; /* The level of nesting inside "typeof". */ int in_typeof; +/* When inside an initializer, this is set to the decl being initialized. */ +tree in_decl_init; + /* True when parsing OpenMP loop expressions. */ bool c_in_omp_for; @@ -2047,7 +2050,8 @@ mark_exp_read (tree exp) { case VAR_DECL: case PARM_DECL: - DECL_READ_P (exp) = 1; + if (exp != in_decl_init) + DECL_READ_P (exp) = 1; break; case ARRAY_REF: case COMPONENT_REF: diff --git a/gcc/testsuite/gcc.dg/pr115027.c b/gcc/testsuite/gcc.dg/pr115027.c new file mode 100644 index 00000000000..ac2699f8392 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr115027.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-Wunused-but-set-variable" } */ + +void f(void) +{ + struct foo { void *p; }; + struct foo g = { &g }; /* { dg-warning "set but not used" } */ +}