https://github.com/a-tarasyuk updated https://github.com/llvm/llvm-project/pull/148988
>From 5db59d5b7f9b834762f86aa69040314e8faeb649 Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk <oleksandr.taras...@outlook.com> Date: Wed, 16 Jul 2025 02:09:37 +0300 Subject: [PATCH 1/7] [Analyzer] support parenthesized list initialization --- clang/docs/ReleaseNotes.rst | 2 + .../Core/PathSensitive/ExprEngine.h | 3 ++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 7 ++- .../lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 31 ++++++++++++ clang/test/Analysis/div-zero.cpp | 50 ++++++++++++++++--- 5 files changed, 86 insertions(+), 7 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 1eb3e369a302e..06a41700081a9 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -1199,6 +1199,8 @@ Static Analyzer --------------- - Fixed a crash when C++20 parenthesized initializer lists are used. This issue was causing a crash in clang-tidy. (#GH136041) +- The Clang Static Analyzer now handles parenthesized initialization. + (#GH148875) New features ^^^^^^^^^^^^ diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index 6370586e218ef..79d86aef8a0c6 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -586,6 +586,9 @@ class ExprEngine { void VisitCXXDeleteExpr(const CXXDeleteExpr *CDE, ExplodedNode *Pred, ExplodedNodeSet &Dst); + void VisitCXXParenListInitExpr(const CXXParenListInitExpr *PLIE, + ExplodedNode *Pred, ExplodedNodeSet &Dst); + /// Create a C++ temporary object for an rvalue. void CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME, ExplodedNode *Pred, diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index c77ef26da568d..8f0cdd46045d0 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1941,7 +1941,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::ConceptSpecializationExprClass: case Stmt::CXXRewrittenBinaryOperatorClass: case Stmt::RequiresExprClass: - case Expr::CXXParenListInitExprClass: case Stmt::EmbedExprClass: // Fall through. @@ -2321,6 +2320,12 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, Bldr.addNodes(Dst); break; + case Expr::CXXParenListInitExprClass: + Bldr.takeNodes(Pred); + VisitCXXParenListInitExpr(cast<CXXParenListInitExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); + break; + case Stmt::MemberExprClass: Bldr.takeNodes(Pred); VisitMemberExpr(cast<MemberExpr>(S), Pred, Dst); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 85353848aa124..059a435bd3e9e 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -1233,3 +1233,34 @@ void ExprEngine::VisitAttributedStmt(const AttributedStmt *A, getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, A, *this); } + +void ExprEngine::VisitCXXParenListInitExpr(const CXXParenListInitExpr *E, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); + + ProgramStateRef S = Pred->getState(); + QualType T = getContext().getCanonicalType(E->getType()); + + const LocationContext *LCtx = Pred->getLocationContext(); + + SmallVector<SVal, 4> ArgVals; + for (Expr *Arg : E->getInitExprs()) + ArgVals.push_back(S->getSVal(Arg, LCtx)); + + if (!E->isGLValue() && (T->isRecordType() || T->isArrayType())) { + llvm::ImmutableList<SVal> ArgList = getBasicVals().getEmptySValList(); + + for (const SVal &V : llvm::reverse(ArgVals)) + ArgList = getBasicVals().prependSVal(V, ArgList); + + Bldr.generateNode( + E, Pred, S->BindExpr(E, LCtx, svalBuilder.makeCompoundVal(T, ArgList))); + } else { + Bldr.generateNode(E, Pred, + S->BindExpr(E, LCtx, + ArgVals.empty() + ? getSValBuilder().makeZeroVal(T) + : ArgVals.front())); + } +} diff --git a/clang/test/Analysis/div-zero.cpp b/clang/test/Analysis/div-zero.cpp index 063450d8883b0..2a44ad132d4a5 100644 --- a/clang/test/Analysis/div-zero.cpp +++ b/clang/test/Analysis/div-zero.cpp @@ -1,13 +1,51 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core.DivideZero -verify %s +// RUN: %clang_analyze_cc1 -analyzer-checker=core.DivideZero -std=c++20 -verify %s -int fooPR10616 (int qX ) { +namespace GH10616 { +int foo(int qX) { int a, c, d; - d = (qX-1); - while ( d != 0 ) { - d = c - (c/d) * d; + d = (qX - 1); + while (d != 0) { + d = c - (c / d) * d; } - return (a % (qX-1)); // expected-warning {{Division by zero}} + return (a % (qX - 1)); // expected-warning {{Division by zero}} +} +} // namespace GH10616 + +namespace GH148875 { +struct A { + int x; + A(int v) : x(v) {} +}; + +struct B { + int x; + B() : x(0) {} +}; + +struct C { + int x, y; + C(int a, int b) : x(a), y(b) {} +}; + +int t1() { + A a(42); + return 1 / (a.x - 42); // expected-warning {{Division by zero}} +} + +int t2() { + B b; + return 1 / b.x; // expected-warning {{Division by zero}} +} + +int t3() { + C c1(1, -1); + return 1 / (c1.x + c1.y); // expected-warning {{Division by zero}} +} +int t4() { + C c2(0, 0); + return 1 / (c2.x + c2.y); // expected-warning {{Division by zero}} } +} // namespace GH148875 >From a2a7346a4770e5a857b2e6234124170f71019695 Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk <oleksandr.taras...@outlook.com> Date: Wed, 16 Jul 2025 16:44:25 +0300 Subject: [PATCH 2/7] add additinal tests --- clang/test/Analysis/div-zero.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/clang/test/Analysis/div-zero.cpp b/clang/test/Analysis/div-zero.cpp index 2a44ad132d4a5..cdfac210e174e 100644 --- a/clang/test/Analysis/div-zero.cpp +++ b/clang/test/Analysis/div-zero.cpp @@ -29,6 +29,15 @@ struct C { C(int a, int b) : x(a), y(b) {} }; +struct D { + int x; +}; + +struct E { + D d; + E(int a) : d(a) {} +}; + int t1() { A a(42); return 1 / (a.x - 42); // expected-warning {{Division by zero}} @@ -48,4 +57,9 @@ int t4() { C c2(0, 0); return 1 / (c2.x + c2.y); // expected-warning {{Division by zero}} } + +int t5() { + E e = 32; + return 1 / (e.d.x - 32); // expected-warning {{Division by zero}} +} } // namespace GH148875 >From 8bb56ceae99e43b62b6635b8c8143e530313efb4 Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk <oleksandr.taras...@outlook.com> Date: Wed, 16 Jul 2025 16:46:06 +0300 Subject: [PATCH 3/7] remove redundant vector --- .../lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 059a435bd3e9e..fde3939a07e29 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -1237,30 +1237,27 @@ void ExprEngine::VisitAttributedStmt(const AttributedStmt *A, void ExprEngine::VisitCXXParenListInitExpr(const CXXParenListInitExpr *E, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); + const LocationContext *LC = Pred->getLocationContext(); + StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); ProgramStateRef S = Pred->getState(); - QualType T = getContext().getCanonicalType(E->getType()); - const LocationContext *LCtx = Pred->getLocationContext(); + QualType T = E->getType().getCanonicalType(); + ArrayRef<Expr *> Inits = E->getInitExprs(); - SmallVector<SVal, 4> ArgVals; - for (Expr *Arg : E->getInitExprs()) - ArgVals.push_back(S->getSVal(Arg, LCtx)); - - if (!E->isGLValue() && (T->isRecordType() || T->isArrayType())) { + if (Inits.size() > 1 || + (E->isPRValue() && (T->isRecordType() || T->isArrayType()))) { llvm::ImmutableList<SVal> ArgList = getBasicVals().getEmptySValList(); - - for (const SVal &V : llvm::reverse(ArgVals)) - ArgList = getBasicVals().prependSVal(V, ArgList); + for (Expr *E : llvm::reverse(Inits)) + ArgList = getBasicVals().prependSVal(S->getSVal(E, LC), ArgList); Bldr.generateNode( - E, Pred, S->BindExpr(E, LCtx, svalBuilder.makeCompoundVal(T, ArgList))); + E, Pred, S->BindExpr(E, LC, svalBuilder.makeCompoundVal(T, ArgList))); } else { Bldr.generateNode(E, Pred, - S->BindExpr(E, LCtx, - ArgVals.empty() + S->BindExpr(E, LC, + Inits.size() == 0 ? getSValBuilder().makeZeroVal(T) - : ArgVals.front())); + : S->getSVal(Inits.front(), LC))); } } >From 6d3dd39d4c68f1b0cfbb6f51b4cc2c6acd4d7318 Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk <oleksandr.taras...@outlook.com> Date: Wed, 16 Jul 2025 18:20:04 +0300 Subject: [PATCH 4/7] revert test --- clang/test/Analysis/div-zero.cpp | 64 +++----------------------------- 1 file changed, 6 insertions(+), 58 deletions(-) diff --git a/clang/test/Analysis/div-zero.cpp b/clang/test/Analysis/div-zero.cpp index cdfac210e174e..063450d8883b0 100644 --- a/clang/test/Analysis/div-zero.cpp +++ b/clang/test/Analysis/div-zero.cpp @@ -1,65 +1,13 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core.DivideZero -std=c++20 -verify %s +// RUN: %clang_analyze_cc1 -analyzer-checker=core.DivideZero -verify %s -namespace GH10616 { -int foo(int qX) { +int fooPR10616 (int qX ) { int a, c, d; - d = (qX - 1); - while (d != 0) { - d = c - (c / d) * d; + d = (qX-1); + while ( d != 0 ) { + d = c - (c/d) * d; } - return (a % (qX - 1)); // expected-warning {{Division by zero}} -} -} // namespace GH10616 - -namespace GH148875 { -struct A { - int x; - A(int v) : x(v) {} -}; - -struct B { - int x; - B() : x(0) {} -}; - -struct C { - int x, y; - C(int a, int b) : x(a), y(b) {} -}; - -struct D { - int x; -}; - -struct E { - D d; - E(int a) : d(a) {} -}; - -int t1() { - A a(42); - return 1 / (a.x - 42); // expected-warning {{Division by zero}} -} - -int t2() { - B b; - return 1 / b.x; // expected-warning {{Division by zero}} -} - -int t3() { - C c1(1, -1); - return 1 / (c1.x + c1.y); // expected-warning {{Division by zero}} -} - -int t4() { - C c2(0, 0); - return 1 / (c2.x + c2.y); // expected-warning {{Division by zero}} -} + return (a % (qX-1)); // expected-warning {{Division by zero}} -int t5() { - E e = 32; - return 1 / (e.d.x - 32); // expected-warning {{Division by zero}} } -} // namespace GH148875 >From 64de252d3d87e87a9c1aa05b5f22fe51b099fcfc Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk <oleksandr.taras...@outlook.com> Date: Wed, 16 Jul 2025 18:20:20 +0300 Subject: [PATCH 5/7] add test relaetd to c++20 only --- clang/test/Analysis/div-zero-cxx20.cpp | 52 ++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 clang/test/Analysis/div-zero-cxx20.cpp diff --git a/clang/test/Analysis/div-zero-cxx20.cpp b/clang/test/Analysis/div-zero-cxx20.cpp new file mode 100644 index 0000000000000..efc54a275e874 --- /dev/null +++ b/clang/test/Analysis/div-zero-cxx20.cpp @@ -0,0 +1,52 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core.DivideZero -std=c++20 -verify %s + +namespace GH148875 { +struct A { + int x; + A(int v) : x(v) {} +}; + +struct B { + int x; + B() : x(0) {} +}; + +struct C { + int x, y; + C(int a, int b) : x(a), y(b) {} +}; + +struct D { + int x; +}; + +struct E { + D d; + E(int a) : d(a) {} +}; + +int t1() { + A a(42); + return 1 / (a.x - 42); // expected-warning {{Division by zero}} +} + +int t2() { + B b; + return 1 / b.x; // expected-warning {{Division by zero}} +} + +int t3() { + C c1(1, -1); + return 1 / (c1.x + c1.y); // expected-warning {{Division by zero}} +} + +int t4() { + C c2(0, 0); + return 1 / (c2.x + c2.y); // expected-warning {{Division by zero}} +} + +int t5() { + E e = 32; + return 1 / (e.d.x - 32); // expected-warning {{Division by zero}} +} +} // namespace GH148875 >From db6f5d6a90b7fe1ed49983d181bc0fae4daa5297 Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk <oleksandr.taras...@outlook.com> Date: Thu, 17 Jul 2025 17:38:15 +0300 Subject: [PATCH 6/7] share initializer list modeling between VisitInitListExpr and VisitCXXParenListInitExpr --- .../Core/PathSensitive/ExprEngine.h | 4 + clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp | 77 ++++++++----------- .../lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 25 +----- clang/test/Analysis/div-zero-cxx20.cpp | 10 +-- clang/test/Analysis/div-zero.cpp | 51 ++++++++++++ 5 files changed, 96 insertions(+), 71 deletions(-) diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index 79d86aef8a0c6..2810e10e0bec8 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -594,6 +594,10 @@ class ExprEngine { ExplodedNode *Pred, ExplodedNodeSet &Dst); + void CreateInitializationList(const Expr *Source, ArrayRef<Expr *> Args, + bool IsTransparent, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + /// evalEagerlyAssumeBifurcation - Given the nodes in 'Src', eagerly assume /// concrete boolean values for 'Ex', storing the resulting nodes in 'Dst'. void evalEagerlyAssumeBifurcation(ExplodedNodeSet &Dst, ExplodedNodeSet &Src, diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp index fa8e669b6bb2f..997d8f0186765 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -774,49 +774,7 @@ void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred, void ExprEngine::VisitInitListExpr(const InitListExpr *IE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - StmtNodeBuilder B(Pred, Dst, *currBldrCtx); - - ProgramStateRef state = Pred->getState(); - const LocationContext *LCtx = Pred->getLocationContext(); - QualType T = getContext().getCanonicalType(IE->getType()); - unsigned NumInitElements = IE->getNumInits(); - - if (!IE->isGLValue() && !IE->isTransparent() && - (T->isArrayType() || T->isRecordType() || T->isVectorType() || - T->isAnyComplexType())) { - llvm::ImmutableList<SVal> vals = getBasicVals().getEmptySValList(); - - // Handle base case where the initializer has no elements. - // e.g: static int* myArray[] = {}; - if (NumInitElements == 0) { - SVal V = svalBuilder.makeCompoundVal(T, vals); - B.generateNode(IE, Pred, state->BindExpr(IE, LCtx, V)); - return; - } - - for (const Stmt *S : llvm::reverse(*IE)) { - SVal V = state->getSVal(cast<Expr>(S), LCtx); - vals = getBasicVals().prependSVal(V, vals); - } - - B.generateNode(IE, Pred, - state->BindExpr(IE, LCtx, - svalBuilder.makeCompoundVal(T, vals))); - return; - } - - // Handle scalars: int{5} and int{} and GLvalues. - // Note, if the InitListExpr is a GLvalue, it means that there is an address - // representing it, so it must have a single init element. - assert(NumInitElements <= 1); - - SVal V; - if (NumInitElements == 0) - V = getSValBuilder().makeZeroVal(T); - else - V = state->getSVal(IE->getInit(0), LCtx); - - B.generateNode(IE, Pred, state->BindExpr(IE, LCtx, V)); + CreateInitializationList(IE, IE->inits(), IE->isTransparent(), Pred, Dst); } void ExprEngine::VisitGuardedExpr(const Expr *Ex, @@ -1197,3 +1155,36 @@ void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U, } Dst.insert(Dst2); } + +void ExprEngine::CreateInitializationList(const Expr *E, ArrayRef<Expr *> Args, + bool IsTransparent, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + assert((isa<InitListExpr>(E) || isa<CXXParenListInitExpr>(E)) && + "Expected InitListExpr or CXXParenListInitExpr"); + + const LocationContext *LC = Pred->getLocationContext(); + + StmtNodeBuilder B(Pred, Dst, *currBldrCtx); + ProgramStateRef S = Pred->getState(); + QualType T = E->getType().getCanonicalType(); + + bool IsCompound = + E->isPRValue() && (T->isArrayType() || T->isRecordType() || + T->isAnyComplexType() || T->isVectorType()); + + if (Args.size() > 1 || (IsCompound && !IsTransparent)) { + llvm::ImmutableList<SVal> ArgList = getBasicVals().getEmptySValList(); + for (Expr *E : llvm::reverse(Args)) + ArgList = getBasicVals().prependSVal(S->getSVal(E, LC), ArgList); + + B.generateNode(E, Pred, + S->BindExpr(E, LC, svalBuilder.makeCompoundVal(T, ArgList))); + } else { + B.generateNode(E, Pred, + S->BindExpr(E, LC, + Args.size() == 0 + ? getSValBuilder().makeZeroVal(T) + : S->getSVal(Args.front(), LC))); + } +} diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index fde3939a07e29..08b334ec1f13b 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -1237,27 +1237,6 @@ void ExprEngine::VisitAttributedStmt(const AttributedStmt *A, void ExprEngine::VisitCXXParenListInitExpr(const CXXParenListInitExpr *E, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - const LocationContext *LC = Pred->getLocationContext(); - - StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); - ProgramStateRef S = Pred->getState(); - - QualType T = E->getType().getCanonicalType(); - ArrayRef<Expr *> Inits = E->getInitExprs(); - - if (Inits.size() > 1 || - (E->isPRValue() && (T->isRecordType() || T->isArrayType()))) { - llvm::ImmutableList<SVal> ArgList = getBasicVals().getEmptySValList(); - for (Expr *E : llvm::reverse(Inits)) - ArgList = getBasicVals().prependSVal(S->getSVal(E, LC), ArgList); - - Bldr.generateNode( - E, Pred, S->BindExpr(E, LC, svalBuilder.makeCompoundVal(T, ArgList))); - } else { - Bldr.generateNode(E, Pred, - S->BindExpr(E, LC, - Inits.size() == 0 - ? getSValBuilder().makeZeroVal(T) - : S->getSVal(Inits.front(), LC))); - } + CreateInitializationList(E, E->getInitExprs(), /*IsTransparent*/ false, Pred, + Dst); } diff --git a/clang/test/Analysis/div-zero-cxx20.cpp b/clang/test/Analysis/div-zero-cxx20.cpp index efc54a275e874..adc75344970d2 100644 --- a/clang/test/Analysis/div-zero-cxx20.cpp +++ b/clang/test/Analysis/div-zero-cxx20.cpp @@ -26,27 +26,27 @@ struct E { }; int t1() { - A a(42); + A a{42}; return 1 / (a.x - 42); // expected-warning {{Division by zero}} } int t2() { - B b; + B b{}; return 1 / b.x; // expected-warning {{Division by zero}} } int t3() { - C c1(1, -1); + C c1{1, -1}; return 1 / (c1.x + c1.y); // expected-warning {{Division by zero}} } int t4() { - C c2(0, 0); + C c2{0, 0}; return 1 / (c2.x + c2.y); // expected-warning {{Division by zero}} } int t5() { - E e = 32; + E e{32}; return 1 / (e.d.x - 32); // expected-warning {{Division by zero}} } } // namespace GH148875 diff --git a/clang/test/Analysis/div-zero.cpp b/clang/test/Analysis/div-zero.cpp index 063450d8883b0..498ad159afab0 100644 --- a/clang/test/Analysis/div-zero.cpp +++ b/clang/test/Analysis/div-zero.cpp @@ -11,3 +11,54 @@ int fooPR10616 (int qX ) { return (a % (qX-1)); // expected-warning {{Division by zero}} } + +namespace GH148875 { + struct A { + int x; + A(int v) : x(v) {} +}; + +struct B { + int x; + B() : x(0) {} +}; + +struct C { + int x, y; + C(int a, int b) : x(a), y(b) {} +}; + +struct D { + int x; +}; + +struct E { + D d; + E(int a) : d{a} {} +}; + +int t1() { + A a{42}; + return 1 / (a.x - 42); // expected-warning {{Division by zero}} +} + +int t2() { + B b{}; + return 1 / b.x; // expected-warning {{Division by zero}} +} + +int t3() { + C c1{1, -1}; + return 1 / (c1.x + c1.y); // expected-warning {{Division by zero}} +} + +int t4() { + C c2{0, 0}; + return 1 / (c2.x + c2.y); // expected-warning {{Division by zero}} +} + +int t5() { + E e{32}; + return 1 / (e.d.x - 32); // expected-warning {{Division by zero}} +} +} >From ab541b09b39b64a702a7fb5a0de4f8988c34e7ed Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk <oleksandr.taras...@outlook.com> Date: Thu, 17 Jul 2025 20:13:35 +0300 Subject: [PATCH 7/7] refactor visitors to use common helper --- .../Core/PathSensitive/ExprEngine.h | 12 ++--- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 45 +++++++++++++++++-- clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp | 39 ---------------- .../lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 7 --- 4 files changed, 44 insertions(+), 59 deletions(-) diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index 2810e10e0bec8..fbb34340a5c67 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -499,9 +499,6 @@ class ExprEngine { void VisitGuardedExpr(const Expr *Ex, const Expr *L, const Expr *R, ExplodedNode *Pred, ExplodedNodeSet &Dst); - void VisitInitListExpr(const InitListExpr *E, ExplodedNode *Pred, - ExplodedNodeSet &Dst); - /// VisitAttributedStmt - Transfer function logic for AttributedStmt. void VisitAttributedStmt(const AttributedStmt *A, ExplodedNode *Pred, ExplodedNodeSet &Dst); @@ -586,17 +583,14 @@ class ExprEngine { void VisitCXXDeleteExpr(const CXXDeleteExpr *CDE, ExplodedNode *Pred, ExplodedNodeSet &Dst); - void VisitCXXParenListInitExpr(const CXXParenListInitExpr *PLIE, - ExplodedNode *Pred, ExplodedNodeSet &Dst); - /// Create a C++ temporary object for an rvalue. void CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME, ExplodedNode *Pred, ExplodedNodeSet &Dst); - void CreateInitializationList(const Expr *Source, ArrayRef<Expr *> Args, - bool IsTransparent, ExplodedNode *Pred, - ExplodedNodeSet &Dst); + void ConstructInitList(const Expr *Source, ArrayRef<Expr *> Args, + bool IsTransparent, ExplodedNode *Pred, + ExplodedNodeSet &Dst); /// evalEagerlyAssumeBifurcation - Given the nodes in 'Src', eagerly assume /// concrete boolean values for 'Ex', storing the resulting nodes in 'Dst'. diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 8f0cdd46045d0..bd00d47f161ff 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -2314,17 +2314,22 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, break; } - case Stmt::InitListExprClass: + case Stmt::InitListExprClass: { + const InitListExpr *E = cast<InitListExpr>(S); Bldr.takeNodes(Pred); - VisitInitListExpr(cast<InitListExpr>(S), Pred, Dst); + ConstructInitList(E, E->inits(), E->isTransparent(), Pred, Dst); Bldr.addNodes(Dst); break; + } - case Expr::CXXParenListInitExprClass: + case Expr::CXXParenListInitExprClass: { + const CXXParenListInitExpr *E = cast<CXXParenListInitExpr>(S); Bldr.takeNodes(Pred); - VisitCXXParenListInitExpr(cast<CXXParenListInitExpr>(S), Pred, Dst); + ConstructInitList(E, E->getInitExprs(), /*IsTransparent*/ false, Pred, + Dst); Bldr.addNodes(Dst); break; + } case Stmt::MemberExprClass: Bldr.takeNodes(Pred); @@ -4119,3 +4124,35 @@ void *ProgramStateTrait<ReplayWithoutInlining>::GDMIndex() { } void ExprEngine::anchor() { } + +void ExprEngine::ConstructInitList(const Expr *E, ArrayRef<Expr *> Args, + bool IsTransparent, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + assert((isa<InitListExpr>(E) || isa<CXXParenListInitExpr>(E)) && + "Expected InitListExpr or CXXParenListInitExpr"); + + const LocationContext *LC = Pred->getLocationContext(); + + StmtNodeBuilder B(Pred, Dst, *currBldrCtx); + ProgramStateRef S = Pred->getState(); + QualType T = E->getType().getCanonicalType(); + + bool IsCompound = + E->isPRValue() && (T->isArrayType() || T->isRecordType() || + T->isAnyComplexType() || T->isVectorType()); + + if (Args.size() > 1 || (IsCompound && !IsTransparent)) { + llvm::ImmutableList<SVal> ArgList = getBasicVals().getEmptySValList(); + for (Expr *E : llvm::reverse(Args)) + ArgList = getBasicVals().prependSVal(S->getSVal(E, LC), ArgList); + + B.generateNode(E, Pred, + S->BindExpr(E, LC, svalBuilder.makeCompoundVal(T, ArgList))); + } else { + B.generateNode(E, Pred, + S->BindExpr(E, LC, + Args.size() == 0 + ? getSValBuilder().makeZeroVal(T) + : S->getSVal(Args.front(), LC))); + } +} diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp index 997d8f0186765..f1a25a750dd0d 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -771,12 +771,6 @@ void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred, Bldr.generateNode(B, Pred, state->BindExpr(B, Pred->getLocationContext(), X)); } -void ExprEngine::VisitInitListExpr(const InitListExpr *IE, - ExplodedNode *Pred, - ExplodedNodeSet &Dst) { - CreateInitializationList(IE, IE->inits(), IE->isTransparent(), Pred, Dst); -} - void ExprEngine::VisitGuardedExpr(const Expr *Ex, const Expr *L, const Expr *R, @@ -1155,36 +1149,3 @@ void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U, } Dst.insert(Dst2); } - -void ExprEngine::CreateInitializationList(const Expr *E, ArrayRef<Expr *> Args, - bool IsTransparent, - ExplodedNode *Pred, - ExplodedNodeSet &Dst) { - assert((isa<InitListExpr>(E) || isa<CXXParenListInitExpr>(E)) && - "Expected InitListExpr or CXXParenListInitExpr"); - - const LocationContext *LC = Pred->getLocationContext(); - - StmtNodeBuilder B(Pred, Dst, *currBldrCtx); - ProgramStateRef S = Pred->getState(); - QualType T = E->getType().getCanonicalType(); - - bool IsCompound = - E->isPRValue() && (T->isArrayType() || T->isRecordType() || - T->isAnyComplexType() || T->isVectorType()); - - if (Args.size() > 1 || (IsCompound && !IsTransparent)) { - llvm::ImmutableList<SVal> ArgList = getBasicVals().getEmptySValList(); - for (Expr *E : llvm::reverse(Args)) - ArgList = getBasicVals().prependSVal(S->getSVal(E, LC), ArgList); - - B.generateNode(E, Pred, - S->BindExpr(E, LC, svalBuilder.makeCompoundVal(T, ArgList))); - } else { - B.generateNode(E, Pred, - S->BindExpr(E, LC, - Args.size() == 0 - ? getSValBuilder().makeZeroVal(T) - : S->getSVal(Args.front(), LC))); - } -} diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 08b334ec1f13b..85353848aa124 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -1233,10 +1233,3 @@ void ExprEngine::VisitAttributedStmt(const AttributedStmt *A, getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, A, *this); } - -void ExprEngine::VisitCXXParenListInitExpr(const CXXParenListInitExpr *E, - ExplodedNode *Pred, - ExplodedNodeSet &Dst) { - CreateInitializationList(E, E->getInitExprs(), /*IsTransparent*/ false, Pred, - Dst); -} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits