Author: Ella Ma
Date: 2026-07-15T11:25:01+02:00
New Revision: 4aa1590ad66a1bff9b3c74b4a2c7366473015b4c

URL: 
https://github.com/llvm/llvm-project/commit/4aa1590ad66a1bff9b3c74b4a2c7366473015b4c
DIFF: 
https://github.com/llvm/llvm-project/commit/4aa1590ad66a1bff9b3c74b4a2c7366473015b4c.diff

LOG: [analyzer] Ignoring `T v=v;` idiom for uninitialized variable checker and 
dead store checker (#187530)

Closing #173210

The self-assignment initialization `Type var = var;` is an idiom in C
code. The analyzer is expected to suppress the uninitialized assignment
reports and dead store reports for this idiom. Variables that are really
uninitialized will be reported until they are actually used. Since GCC
will not generate assembly code for such usage, even if under -O0
optimization (but Clang will), this patch resolves this problem by
ignoring such self-assigned `DeclStmt`s in the `ExprEngine`, as well as
in the `DeadStoreObs` of the dead store checker.

This ignorance will be applied to C variables and non-reference C++
variables. For record types in C++, since the constructors will always
be invoked, they will not be affected by this change.

Added: 
    clang/test/Analysis/issue-173210-self-assign-init.c

Modified: 
    clang/docs/ReleaseNotes.md
    clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
    clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 760c973eb6a73..e61455acc12a4 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -203,6 +203,7 @@ latest release, please see the [Clang Web 
Site](https://clang.llvm.org) or the
 %   - New checkers and features
 %   - Improvements
 %   - Moved checkers
+%   - Diagnostic changes
 
 #### Improvements
 
@@ -211,6 +212,10 @@ latest release, please see the [Clang Web 
Site](https://clang.llvm.org) or the
 
 #### Moved checkers
 
+#### Diagnostic changes
+
+- For self-assignments during initialization (`T v = v;`), 
`core.uninitialized.Assign` will not report them as uninitialized accesses 
(except C++ reference types), and the checks will be delayed until the first 
accesses of these variables; `deadcode.DeadStores` will not report them as dead 
stores. (#GH187530)
+
 (release-notes-sanitizers)=
 
 ### Sanitizers

diff  --git a/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
index 48e358c1e6242..0d693a3e3ff3d 100644
--- a/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
@@ -431,6 +431,12 @@ class DeadStoreObs : public LiveVariables::Observer {
                   //  bug.
                   if (isa<ParmVarDecl>(VD) && VD->getType()->isScalarType())
                     return;
+                  // Special case: check for self-initializations.
+                  //
+                  //  e.g. int x = x;
+                  //
+                  if (VD == V)
+                    return;
                 }
 
               PathDiagnosticLocation Loc =

diff  --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp 
b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index 33c31fe10782a..8eb1b42600a81 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -590,6 +590,27 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, 
ExplodedNode *Pred,
     return;
   }
 
+  // Self-assignment initialization in variable declaration,
+  // i.e., `int x = x;`,
+  // is a C idiom to suppress warnings of unused variables.
+  // This filter will not match variables of C++ record types, but will match
+  // C++ references. Allow references continuing here to make the undefined
+  // value checker report self-assignments of C++ references.
+  if (const Expr *EI = VD->getInit()) {
+    // Ignore InitListExpr if exists.
+    if (const auto *IL = dyn_cast<InitListExpr>(EI);
+        IL && IL->getNumInits() == 1)
+      EI = IL->getInit(0);
+
+    // Ignore parentheses and implict casts.
+    if (const auto *DR = dyn_cast<DeclRefExpr>(EI->IgnoreParenImpCasts())) {
+      if (VD == DR->getDecl() && !VD->getType()->isReferenceType()) {
+        Dst.insert(Pred);
+        return;
+      }
+    }
+  }
+
   // FIXME: all pre/post visits should eventually be handled by ::Visit().
   ExplodedNodeSet dstPreVisit;
   getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, DS, *this);

diff  --git a/clang/test/Analysis/issue-173210-self-assign-init.c 
b/clang/test/Analysis/issue-173210-self-assign-init.c
new file mode 100644
index 0000000000000..57f5c42bf69ab
--- /dev/null
+++ b/clang/test/Analysis/issue-173210-self-assign-init.c
@@ -0,0 +1,111 @@
+// RUN: %clang_analyze_cc1 %s -verify -xc \
+// RUN:   -analyzer-checker=core,debug.ExprInspection,deadcode.DeadStores
+
+// Suppress the C++ -Wuninitialized warnings on struct and reference.
+// RUN: %clang_analyze_cc1 %s -verify -xc++ -Wno-uninitialized \
+// RUN:   -analyzer-checker=core,debug.ExprInspection,deadcode.DeadStores
+
+// Self assignment initialization in C code will be treated as nop.
+// We will report the VarDecl only if it was left uninitialized by the time of
+// a subsequent DeclRefExpr.
+
+// NOTE: No warnings from the deadcode.DeadStores checker.
+
+void clang_analyzer_warnIfReached();
+
+struct S { int x; };
+union U { int x; };
+enum T { TT };
+
+// No need to test VarDecl of multiple variables, as they will be split into
+// single ones when constructing the CFG.
+
+int check_var() {
+  int x = x; // no-warnings for C/C++, binding is skipped via the
+             // self-assignment filter.
+  return x; // expected-warning{{Undefined or garbage value returned to 
caller}}
+}
+
+int *check_ptr() {
+  int *p = p; // Same as warnvar.
+  return p; // expected-warning{{Undefined or garbage value returned to 
caller}}
+}
+
+enum T check_enum() {
+  enum T t = t; // Same as warnvar.
+  return t; // expected-warning{{Undefined or garbage value returned to 
caller}}
+}
+
+int check_struct() {
+  // no-warnings for C/C++:
+  // In C, same as warnvar.
+  // In C++, binding is handled in the ctor call and 's.x' is bound to an 
Undefined.
+  struct S s = s; // no-warnings
+  return s.x; // expected-warning{{Undefined or garbage value returned to 
caller}}
+}
+
+#ifndef __cplusplus
+int check_union() {
+  // no-warnings for C/C++:
+  // In C, same as warnvar.
+  // In C++, binding is handled in the ctor call and 'u' is bound to a
+  // lazyCompoundVal, which will not trigger an undefined usage warning.
+  union U u = u; // no-warnings
+  return u.x; // expected-warning{{Undefined or garbage value returned to 
caller}}
+}
+#endif // not __cplusplus
+
+#ifdef __cplusplus
+
+// NOTE: The self assignment of reference type is also tested in 
stack-addr-ps.cpp.
+// I.e., `int& i = i;` in function f5
+// We only keep a simple regression confirmation here.
+void check_ref() {
+  int &x = x; // expected-warning{{Assigned value is uninitialized}}
+}
+
+// Confirmation for default member initializer.
+struct struct_self_assign {
+  int x = x; // no-warnings
+};
+int check_struct_self_assign() {
+  struct_self_assign s; // no-warnings
+  return s.x; // FIXME: there should be a warning.
+}
+
+// Confirmation for constructor initialization list.
+struct struct_init_list {
+  int x;
+  struct_init_list()
+    : x(x) // expected-warning{{Assigned value is uninitialized}}
+  {}
+};
+void check_struct_init_list() {
+  // Trigger the ctor call.
+  struct_init_list s;
+}
+
+// The below two have the same AST structure.
+int check_copy_list_initialization() {
+  int x = {x}; // no-warnings
+  return x; // expected-warning{{Undefined or garbage value returned to 
caller}}
+};
+int check_direct_list_initialization() {
+  int x {x}; // no-warnings
+  return x; // expected-warning{{Undefined or garbage value returned to 
caller}}
+};
+
+// Having the same AST structure as `int x = x;`, rather than `int x = (x);`.
+int check_direct_initialization() {
+  int x (x); // no-warnings
+  return x; // expected-warning{{Undefined or garbage value returned to 
caller}}
+}
+
+#endif // __cplusplus
+
+// Ignore parentheses for the initialization-with-a-macro cases, such as
+// #define VAR_INIT(x) (x) and int x = VAR_INIT(x);
+int check_paren_init() {
+  int x = (x); // no-warnings
+  return x; // expected-warning{{Undefined or garbage value returned to 
caller}}
+}


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to