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
