diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index fcd3887ec7a09..8144d16772892 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -1206,6 +1206,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..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); @@ -591,6 +588,10 @@ class ExprEngine { ExplodedNode *Pred, ExplodedNodeSet &Dst); + void ConstructInitList(const Expr *Source, ArrayRef 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/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index c77ef26da568d..d87484470f8b5 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. @@ -2315,11 +2314,22 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, break; } - case Stmt::InitListExprClass: + case Stmt::InitListExprClass: { + const InitListExpr *E = cast(S); Bldr.takeNodes(Pred); - VisitInitListExpr(cast(S), Pred, Dst); + ConstructInitList(E, E->inits(), E->isTransparent(), Pred, Dst); Bldr.addNodes(Dst); break; + } + + case Expr::CXXParenListInitExprClass: { + const CXXParenListInitExpr *E = cast(S); + Bldr.takeNodes(Pred); + ConstructInitList(E, E->getInitExprs(), /*IsTransparent*/ false, Pred, + Dst); + Bldr.addNodes(Dst); + break; + } case Stmt::MemberExprClass: Bldr.takeNodes(Pred); @@ -4114,3 +4124,33 @@ void *ProgramStateTrait::GDMIndex() { } void ExprEngine::anchor() { } + +void ExprEngine::ConstructInitList(const Expr *E, ArrayRef Args, + bool IsTransparent, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + assert((isa(E))); + + const LocationContext *LC = Pred->getLocationContext(); + + StmtNodeBuilder B(Pred, Dst, *currBldrCtx); + ProgramStateRef S = Pred->getState(); + QualType T = E->getType().getCanonicalType(); + + bool IsCompound = T->isArrayType() || T->isRecordType() || + T->isAnyComplexType() || T->isVectorType(); + + if (Args.size() > 1 || (E->isPRValue() && IsCompound && !IsTransparent)) { + llvm::ImmutableList 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 fa8e669b6bb2f..f1a25a750dd0d 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -771,54 +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) { - 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 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(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)); -} - void ExprEngine::VisitGuardedExpr(const Expr *Ex, const Expr *L, const Expr *R, diff --git a/clang/test/Analysis/div-zero-cxx20.cpp b/clang/test/Analysis/div-zero-cxx20.cpp new file mode 100644 index 0000000000000..00ea96e796777 --- /dev/null +++ b/clang/test/Analysis/div-zero-cxx20.cpp @@ -0,0 +1,61 @@ +// 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) {} +}; + +struct F { + int x; +}; + +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}} +} + +int t6() { + F f(32); + return 1 / (f.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..51ea25e828a18 100644 --- a/clang/test/Analysis/div-zero.cpp +++ b/clang/test/Analysis/div-zero.cpp @@ -11,3 +11,63 @@ 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} {} +}; + +struct F { + int x; +}; + +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}} +} + +int t6() { + F f{32}; + return 1 / (f.x - 32); // expected-warning {{Division by zero}} +} +}