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

Reply via email to