[clang] [analyzer] Ignoring `T v=v; ` idiom for uninitialized variable checker and dead store checker (PR #187530)
Ella Ma via cfe-commits
cfe-commits at lists.llvm.org
Fri Jun 12 05:18:48 PDT 2026
https://github.com/Snape3058 updated https://github.com/llvm/llvm-project/pull/187530
>From 1d87171dc76bab8fd78e60deb9b239874c5fd83b Mon Sep 17 00:00:00 2001
From: Ella Ma <alansnape3058 at gmail.com>
Date: Thu, 19 Mar 2026 14:15:11 +0100
Subject: [PATCH 1/5] wip-2026-03-19_16:24:09
---
clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp | 14 +++++++++
.../Analysis/issue-173210-self-assign-init.c | 29 +++++++++++++++++++
2 files changed, 43 insertions(+)
create mode 100644 clang/test/Analysis/issue-173210-self-assign-init.c
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index 67beed5dbb6fb..214f266e21e4c 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -624,6 +624,20 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
return;
}
+ // Bypass a nop initialization that assign to itself at variable declaration.
+ // I.e., int x = x;
+ // This is an idiom in C code and GCC will not generate any assemblies for
+ // this self initialization, even under -O0, but Clang will.
+ // Since the frontend will warn in C++ code, and it is ill-formed for C++
+ // reference types, the bypass is effected to C code only.
+ if (getContext().getLangOpts().getCLangStd())
+ if (const Expr *EI = VD->getInit())
+ if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(EI->IgnoreImpCasts()))
+ if (VD == DR->getDecl()) {
+ 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..c9e5e2be58875
--- /dev/null
+++ b/clang/test/Analysis/issue-173210-self-assign-init.c
@@ -0,0 +1,29 @@
+// RUN: %clang_analyze_cc1 %s \
+// RUN: -analyzer-checker=core,debug.ExprInspection \
+// RUN: -verify
+
+// Self assignment initialization in C code will be treated as nop.
+// We will not report the VarDecl, but the following DeclRefExpr if it has not
+// yet been initialized then.
+
+void clang_analyzer_warnIfReached();
+
+struct S { int x; };
+union U { int x; };
+
+void nowarn() {
+ int x = x; // no-warning
+ int *p = p; // no-warning
+ struct S s = s; // no-warning
+ union U u = u; // no-warning
+ // Ensure the analysis is not terminated sliently.
+ clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+}
+
+int warn() {
+ int x = x;
+ return x; // expected-warning{{Undefined or garbage value returned to caller}}
+}
+
+// NOTE: The self assignment of reference type is tested with stack-addr-ps.cpp.
+// I.e., `int& i = i;` in function f5
>From 90734881a4366bfcc80e05e1407caadbcec3056a Mon Sep 17 00:00:00 2001
From: Ella Ma <alansnape3058 at gmail.com>
Date: Fri, 15 May 2026 14:40:46 +0200
Subject: [PATCH 2/5] wip-2026-05-15_14:40:41
---
clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp | 24 +++++++++++-------
.../Analysis/issue-173210-self-assign-init.c | 25 +++++++++++++------
2 files changed, 32 insertions(+), 17 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index 214f266e21e4c..c003ad015bb1b 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -626,15 +626,21 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
// Bypass a nop initialization that assign to itself at variable declaration.
// I.e., int x = x;
- // This is an idiom in C code and GCC will not generate any assemblies for
- // this self initialization, even under -O0, but Clang will.
- // Since the frontend will warn in C++ code, and it is ill-formed for C++
- // reference types, the bypass is effected to C code only.
- if (getContext().getLangOpts().getCLangStd())
- if (const Expr *EI = VD->getInit())
- if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(EI->IgnoreImpCasts()))
- if (VD == DR->getDecl()) {
- Dst.insert(Pred);
+ // This is an idiom in C code, and GCC will not generate any assemblies for
+ // this self initialization, even under -O0, although Clang will.
+ // We therefore ignore all types for C code.
+ // For C++ code, Sema will not report for fundamental types and pointers.
+ // We hence also ignore them as in C, but leave the uninitialized variable
+ // report of references to the checker. For record types, as their AST
+ // structures are different in C++, they will not hit the filter here and
+ // will be checked by the checker.
+ if (const Expr *EI = VD->getInit())
+ if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(EI->IgnoreImpCasts()))
+ if (VD == DR->getDecl())
+ if (getContext().getLangOpts().getCLangStd() ||
+ (getContext().getLangOpts().getCPlusPlusLangStd() &&
+ !VD->getType()->isReferenceType())) {
+ Dst.Add(Pred);
return;
}
diff --git a/clang/test/Analysis/issue-173210-self-assign-init.c b/clang/test/Analysis/issue-173210-self-assign-init.c
index c9e5e2be58875..bf943554f18a9 100644
--- a/clang/test/Analysis/issue-173210-self-assign-init.c
+++ b/clang/test/Analysis/issue-173210-self-assign-init.c
@@ -1,6 +1,9 @@
-// RUN: %clang_analyze_cc1 %s \
+// RUN: %clang_analyze_cc1 -xc %s \
// RUN: -analyzer-checker=core,debug.ExprInspection \
// RUN: -verify
+// RUN: %clang_analyze_cc1 -xc++ %s \
+// RUN: -analyzer-checker=core,debug.ExprInspection \
+// RUN: -verify -w
// Self assignment initialization in C code will be treated as nop.
// We will not report the VarDecl, but the following DeclRefExpr if it has not
@@ -12,18 +15,24 @@ struct S { int x; };
union U { int x; };
void nowarn() {
- int x = x; // no-warning
- int *p = p; // no-warning
- struct S s = s; // no-warning
- union U u = u; // no-warning
+ int x = x; // no-warnings for C/C++
+ int *p = p; // no-warnings for C/C++
+ struct S s = s; // no-warning for C, but C++ will not report
+ union U u = u; // no-warning for C, but C++ will not report
// Ensure the analysis is not terminated sliently.
clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
}
int warn() {
- int x = x;
+ int x = x; // no-warnings for C/C++
return x; // expected-warning{{Undefined or garbage value returned to caller}}
}
-// NOTE: The self assignment of reference type is tested with stack-addr-ps.cpp.
-// I.e., `int& i = i;` in function f5
+// NOTE: The self assignment of reference type is also tested in stack-addr-ps.cpp.
+// E.g., `int& i = i;` in function f5
+// We only keep a simple regression confirmation here.
+#ifdef __cplusplus
+void warnref() {
+ int &x = x; // expected-warning{{Assigned value is uninitialized}}
+}
+#endif // __cplusplus
>From 832c8ab35525b2690db00269ad3b231ab1486087 Mon Sep 17 00:00:00 2001
From: Ella Ma <alansnape3058 at gmail.com>
Date: Fri, 15 May 2026 15:41:52 +0200
Subject: [PATCH 3/5] Add or insert? I am really confused :(
---
clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index c003ad015bb1b..e418423e2cccb 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -640,7 +640,7 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
if (getContext().getLangOpts().getCLangStd() ||
(getContext().getLangOpts().getCPlusPlusLangStd() &&
!VD->getType()->isReferenceType())) {
- Dst.Add(Pred);
+ Dst.insert(Pred);
return;
}
>From b233e4b9a00ed5791e338bd45bfdb3fbc3744071 Mon Sep 17 00:00:00 2001
From: Ella Ma <alansnape3058 at gmail.com>
Date: Wed, 27 May 2026 19:30:20 +0200
Subject: [PATCH 4/5] add the fix for deadcode.DeadStores, reusing the test
case
---
clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp | 6 ++++++
clang/test/Analysis/issue-173210-self-assign-init.c | 4 ++--
2 files changed, 8 insertions(+), 2 deletions(-)
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/test/Analysis/issue-173210-self-assign-init.c b/clang/test/Analysis/issue-173210-self-assign-init.c
index bf943554f18a9..50e47d3e1238b 100644
--- a/clang/test/Analysis/issue-173210-self-assign-init.c
+++ b/clang/test/Analysis/issue-173210-self-assign-init.c
@@ -1,8 +1,8 @@
// RUN: %clang_analyze_cc1 -xc %s \
-// RUN: -analyzer-checker=core,debug.ExprInspection \
+// RUN: -analyzer-checker=core,debug.ExprInspection,deadcode.DeadStores \
// RUN: -verify
// RUN: %clang_analyze_cc1 -xc++ %s \
-// RUN: -analyzer-checker=core,debug.ExprInspection \
+// RUN: -analyzer-checker=core,debug.ExprInspection,deadcode.DeadStores \
// RUN: -verify -w
// Self assignment initialization in C code will be treated as nop.
>From 19da6560c9bd16324e08a08affbe0ffefe9f08a6 Mon Sep 17 00:00:00 2001
From: Ella Ma <alansnape3058 at gmail.com>
Date: Fri, 12 Jun 2026 13:55:15 +0200
Subject: [PATCH 5/5] address the reviews
---
clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp | 23 ++++----
.../Analysis/issue-173210-self-assign-init.c | 53 ++++++++++++++-----
2 files changed, 49 insertions(+), 27 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index 8cd13b3655c09..c8a78c434acfe 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -620,25 +620,20 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
return;
}
- // Bypass a nop initialization that assign to itself at variable declaration.
- // I.e., int x = x;
- // This is an idiom in C code, and GCC will not generate any assemblies for
- // this self initialization, even under -O0, although Clang will.
- // We therefore ignore all types for C code.
- // For C++ code, Sema will not report for fundamental types and pointers.
- // We hence also ignore them as in C, but leave the uninitialized variable
- // report of references to the checker. For record types, as their AST
- // structures are different in C++, they will not hit the filter here and
- // will be checked by the checker.
- if (const Expr *EI = VD->getInit())
+ // 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()) {
if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(EI->IgnoreImpCasts()))
if (VD == DR->getDecl())
- if (getContext().getLangOpts().getCLangStd() ||
- (getContext().getLangOpts().getCPlusPlusLangStd() &&
- !VD->getType()->isReferenceType())) {
+ if (!VD->getType()->isReferenceType()) {
Dst.insert(Pred);
return;
}
+ }
// FIXME: all pre/post visits should eventually be handled by ::Visit().
ExplodedNodeSet dstPreVisit;
diff --git a/clang/test/Analysis/issue-173210-self-assign-init.c b/clang/test/Analysis/issue-173210-self-assign-init.c
index 50e47d3e1238b..c53b9afdd0f30 100644
--- a/clang/test/Analysis/issue-173210-self-assign-init.c
+++ b/clang/test/Analysis/issue-173210-self-assign-init.c
@@ -6,30 +6,57 @@
// RUN: -verify -w
// Self assignment initialization in C code will be treated as nop.
-// We will not report the VarDecl, but the following DeclRefExpr if it has not
-// yet been initialized then.
+// 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 };
-void nowarn() {
- int x = x; // no-warnings for C/C++
- int *p = p; // no-warnings for C/C++
- struct S s = s; // no-warning for C, but C++ will not report
- union U u = u; // no-warning for C, but C++ will not report
- // Ensure the analysis is not terminated sliently.
- clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
-}
+// No need to test VarDecl of multiple variables, as they will be split into
+// single ones when constructing the CFG.
-int warn() {
- int x = x; // no-warnings for C/C++
+int warnvar() {
+ 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 *warnptr() {
+ int *p = p; // Same as warnvar.
+ return p; // expected-warning{{Undefined or garbage value returned to caller}}
+}
+
+enum T warnenum() {
+ enum T t = t; // Same as warnvar.
+ return t; // expected-warning{{Undefined or garbage value returned to caller}}
+}
+
+int warnstruct() {
+ struct S s = s; // 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.
+ return s.x; // expected-warning{{Undefined or garbage value returned to caller}}
+}
+
+#ifndef __cplusplus
+int warnunion() {
+ union U u = u; // 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.
+ return u.x; // expected-warning{{Undefined or garbage value returned to caller}}
+}
+#endif // not __cplusplus
+
// NOTE: The self assignment of reference type is also tested in stack-addr-ps.cpp.
-// E.g., `int& i = i;` in function f5
+// I.e., `int& i = i;` in function f5
// We only keep a simple regression confirmation here.
#ifdef __cplusplus
void warnref() {
More information about the cfe-commits
mailing list