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 <[email protected]> Date: Thu, 19 Mar 2026 14:15:11 +0100 Subject: [PATCH 1/6] 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 <[email protected]> Date: Fri, 15 May 2026 14:40:46 +0200 Subject: [PATCH 2/6] 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 <[email protected]> Date: Fri, 15 May 2026 15:41:52 +0200 Subject: [PATCH 3/6] 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 <[email protected]> Date: Wed, 27 May 2026 19:30:20 +0200 Subject: [PATCH 4/6] 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 <[email protected]> Date: Fri, 12 Jun 2026 13:55:15 +0200 Subject: [PATCH 5/6] 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() { >From 5b3b53e2bcb83f7fdebf7e183b7528743f6f9ad8 Mon Sep 17 00:00:00 2001 From: Ella Ma <[email protected]> Date: Fri, 19 Jun 2026 15:33:50 +0200 Subject: [PATCH 6/6] apply suggestions 2026-06-19 --- .../Analysis/issue-173210-self-assign-init.c | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/clang/test/Analysis/issue-173210-self-assign-init.c b/clang/test/Analysis/issue-173210-self-assign-init.c index c53b9afdd0f30..daf4936d56756 100644 --- a/clang/test/Analysis/issue-173210-self-assign-init.c +++ b/clang/test/Analysis/issue-173210-self-assign-init.c @@ -1,9 +1,9 @@ -// RUN: %clang_analyze_cc1 -xc %s \ -// RUN: -analyzer-checker=core,debug.ExprInspection,deadcode.DeadStores \ -// RUN: -verify -// RUN: %clang_analyze_cc1 -xc++ %s \ -// RUN: -analyzer-checker=core,debug.ExprInspection,deadcode.DeadStores \ -// RUN: -verify -w +// RUN: %clang_analyze_cc1 %s -verify -xc \ +// RUN: -analyzer-checker=core,debug.ExprInspection,deadcode.DeadStores + +// Use -w to suppress the C++ -Wuninitialized warnings on struct and reference. +// RUN: %clang_analyze_cc1 %s -verify -xc++ -w \ +// 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 @@ -37,20 +37,20 @@ enum T warnenum() { } 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. + // 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 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. + // 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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
