This is a small patch to address https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92386 updated thanks to Andrew's feedback.
This patch implements "-Wshadow=used," which throws a warning for shadowed variables only if the shadowed variable was previously used in the same scope where it is being shadowed. This type of shadowing is particularly bad because it causes GCC to output incorrect/misleading debug information. The Bugzilla report has a minimal example and explains why a direct fix was not desirable (debug info size would blow up). I tested on x86-64-linux-gnu (Debian 12) with make -k check and saw no testsuite regressions using ./contrib/compare_tests. I also built Git and Linux with the flag: Git had 2 benign true positives while Linux had 5 benign true positives. Any comments would be much appreciated; I'm new to the codebase. Changes from v1: * Implement as part of the normal variable shadowing warnings rather than during gimplification. PR debug/92386 - gdb issue with variable-shadowing PR debug/92386 gcc/c/ChangeLog: * c-decl.cc (find_var_usage): helper to walk a tree looking for uses of a specific variable. (warn_if_shadowing): when Wshadow=used is passed, throw a warning if the variable being shadowed is used earlier in the same scope. gcc/ChangeLog: * common.opt: Added Wshadow=used option. gcc/testsuite/ChangeLog: * gcc.dg/warn-use-before-shadow.c: New test. --- gcc/c/c-decl.cc | 43 ++++++++++++++++++- gcc/common.opt | 7 +++ gcc/testsuite/gcc.dg/warn-use-before-shadow.c | 28 ++++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/gcc.dg/warn-use-before-shadow.c diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc index 8c420f22976..8a98300423b 100644 --- a/gcc/c/c-decl.cc +++ b/gcc/c/c-decl.cc @@ -3205,6 +3205,20 @@ duplicate_decls (tree newdecl, tree olddecl) } +/* Helper used by warn_if_shadowing to search for uses of a specific variable + in a tree. */ +tree find_var_usage (tree *px, int *walk_subtrees, void *data) +{ + if (*px == (tree) data) + return *px; + if (TREE_CODE (*px) == DECL_EXPR) + { + tree di = DECL_INITIAL ( DECL_EXPR_DECL (*px)); + return walk_tree (&di, find_var_usage, data, NULL); + } + return 0; +} + /* Check whether decl-node NEW_DECL shadows an existing declaration. */ static void warn_if_shadowing (tree new_decl) @@ -3214,7 +3228,8 @@ warn_if_shadowing (tree new_decl) /* Shadow warnings wanted? */ if (!(warn_shadow || warn_shadow_local - || warn_shadow_compatible_local) + || warn_shadow_compatible_local + || warn_shadow_used) /* No shadow warnings for internally generated vars. */ || DECL_IS_UNDECLARED_BUILTIN (new_decl)) return; @@ -3298,6 +3313,32 @@ warn_if_shadowing (tree new_decl) if (warned) inform (DECL_SOURCE_LOCATION (old_decl), "shadowed declaration is here"); + /* If we haven't issued any other shadowing warning for this + declaration, but -Wshadow=used was passed, issue a warning if the + now-shadowed variable was used earlier in this scope. */ + else if (warn_shadow_used && building_stmt_list_p ()) + { + for (tree_stmt_iterator tsi = tsi_start (cur_stmt_list); + !tsi_end_p (tsi); tsi_next (&tsi)) + { + tree stmt = tsi_stmt (tsi), + found = walk_tree (&stmt, find_var_usage, old_decl, NULL); + if (found) + { + if (warning_at (EXPR_LOCATION (stmt), OPT_Wshadow_used, + "variable %qD is used before being " + "shadowed", found)) + { + inform (DECL_SOURCE_LOCATION (new_decl), + "shadowing declaration is here"); + inform (DECL_SOURCE_LOCATION (old_decl), + "shadowed declaration being used " + "instead is here"); + } + break; + } + } + } break; } diff --git a/gcc/common.opt b/gcc/common.opt index e3fa0dacec4..aac3e652ef1 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -751,6 +751,13 @@ Warn when one local variable shadows another local variable or parameter of comp Wshadow-compatible-local Common Warning Undocumented Alias(Wshadow=compatible-local) +Wshadow=used +Common Var(warn_shadow_used) Warning EnabledBy(Wshadow=used) +Warn if a variable from an outer scope is used before it is shadowed in the current scope. + +Wshadow-used +Common Warning Undocumented Alias(Wshadow=used) + Wstack-protector Common Var(warn_stack_protect) Warning Warn when not issuing stack smashing protection for some reason. diff --git a/gcc/testsuite/gcc.dg/warn-use-before-shadow.c b/gcc/testsuite/gcc.dg/warn-use-before-shadow.c new file mode 100644 index 00000000000..5ebbd7ec65d --- /dev/null +++ b/gcc/testsuite/gcc.dg/warn-use-before-shadow.c @@ -0,0 +1,28 @@ +/* { dg-do compile } */ +/* { dg-options "-Wshadow=used" } */ +int A, B, C, D; + +int main(void) { + A++; /* { dg-warning "used before being shadowed" } */ + B++; + int A = 7, C = 8; + + D++; + extern int D; + D++; + + int x = 5, x2 = 6; + { + x++; /* { dg-warning "used before being shadowed" } */ + int x = 7, x2 = 7; + x--; + x2--; + } + x++; + + unsigned O; + { + unsigned J = 10 + O; /* { dg-warning "used before being shadowed" } */ + unsigned O = 5; + } +} -- 2.39.5