Skip to content

Commit cda28e2

Browse files
authored
[analyzer] Support parenthesized list initialization (CXXParenListInitExpr) (#148988)
This patch addresses the lack of support for parenthesized initialization in the Clang Static Analyzer's `ExprEngine`. Previously, initializations such as `V v(1, 2);` were not modeled properly, which could lead to false negatives in analyses like `DivideZero`. ```cpp struct A { int x; A(int v) : x(v) {} }; int t() { A a(42); return 1 / (a.x - 42); // expected-warning {{Division by zero}} } ``` Fixes #148875
1 parent 1e7446f commit cda28e2

File tree

6 files changed

+170
-54
lines changed

6 files changed

+170
-54
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,6 +1207,8 @@ Static Analyzer
12071207
---------------
12081208
- Fixed a crash when C++20 parenthesized initializer lists are used. This issue
12091209
was causing a crash in clang-tidy. (#GH136041)
1210+
- The Clang Static Analyzer now handles parenthesized initialization.
1211+
(#GH148875)
12101212

12111213
New features
12121214
^^^^^^^^^^^^

clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -499,9 +499,6 @@ class ExprEngine {
499499
void VisitGuardedExpr(const Expr *Ex, const Expr *L, const Expr *R,
500500
ExplodedNode *Pred, ExplodedNodeSet &Dst);
501501

502-
void VisitInitListExpr(const InitListExpr *E, ExplodedNode *Pred,
503-
ExplodedNodeSet &Dst);
504-
505502
/// VisitAttributedStmt - Transfer function logic for AttributedStmt.
506503
void VisitAttributedStmt(const AttributedStmt *A, ExplodedNode *Pred,
507504
ExplodedNodeSet &Dst);
@@ -591,6 +588,10 @@ class ExprEngine {
591588
ExplodedNode *Pred,
592589
ExplodedNodeSet &Dst);
593590

591+
void ConstructInitList(const Expr *Source, ArrayRef<Expr *> Args,
592+
bool IsTransparent, ExplodedNode *Pred,
593+
ExplodedNodeSet &Dst);
594+
594595
/// evalEagerlyAssumeBifurcation - Given the nodes in 'Src', eagerly assume
595596
/// concrete boolean values for 'Ex', storing the resulting nodes in 'Dst'.
596597
void evalEagerlyAssumeBifurcation(ExplodedNodeSet &Dst, ExplodedNodeSet &Src,

clang/lib/StaticAnalyzer/Core/ExprEngine.cpp

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1941,7 +1941,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
19411941
case Stmt::ConceptSpecializationExprClass:
19421942
case Stmt::CXXRewrittenBinaryOperatorClass:
19431943
case Stmt::RequiresExprClass:
1944-
case Expr::CXXParenListInitExprClass:
19451944
case Stmt::EmbedExprClass:
19461945
// Fall through.
19471946

@@ -2315,11 +2314,22 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
23152314
break;
23162315
}
23172316

2318-
case Stmt::InitListExprClass:
2317+
case Stmt::InitListExprClass: {
2318+
const InitListExpr *E = cast<InitListExpr>(S);
23192319
Bldr.takeNodes(Pred);
2320-
VisitInitListExpr(cast<InitListExpr>(S), Pred, Dst);
2320+
ConstructInitList(E, E->inits(), E->isTransparent(), Pred, Dst);
23212321
Bldr.addNodes(Dst);
23222322
break;
2323+
}
2324+
2325+
case Expr::CXXParenListInitExprClass: {
2326+
const CXXParenListInitExpr *E = cast<CXXParenListInitExpr>(S);
2327+
Bldr.takeNodes(Pred);
2328+
ConstructInitList(E, E->getInitExprs(), /*IsTransparent*/ false, Pred,
2329+
Dst);
2330+
Bldr.addNodes(Dst);
2331+
break;
2332+
}
23232333

23242334
case Stmt::MemberExprClass:
23252335
Bldr.takeNodes(Pred);
@@ -4114,3 +4124,33 @@ void *ProgramStateTrait<ReplayWithoutInlining>::GDMIndex() {
41144124
}
41154125

41164126
void ExprEngine::anchor() { }
4127+
4128+
void ExprEngine::ConstructInitList(const Expr *E, ArrayRef<Expr *> Args,
4129+
bool IsTransparent, ExplodedNode *Pred,
4130+
ExplodedNodeSet &Dst) {
4131+
assert((isa<InitListExpr, CXXParenListInitExpr>(E)));
4132+
4133+
const LocationContext *LC = Pred->getLocationContext();
4134+
4135+
StmtNodeBuilder B(Pred, Dst, *currBldrCtx);
4136+
ProgramStateRef S = Pred->getState();
4137+
QualType T = E->getType().getCanonicalType();
4138+
4139+
bool IsCompound = T->isArrayType() || T->isRecordType() ||
4140+
T->isAnyComplexType() || T->isVectorType();
4141+
4142+
if (Args.size() > 1 || (E->isPRValue() && IsCompound && !IsTransparent)) {
4143+
llvm::ImmutableList<SVal> ArgList = getBasicVals().getEmptySValList();
4144+
for (Expr *E : llvm::reverse(Args))
4145+
ArgList = getBasicVals().prependSVal(S->getSVal(E, LC), ArgList);
4146+
4147+
B.generateNode(E, Pred,
4148+
S->BindExpr(E, LC, svalBuilder.makeCompoundVal(T, ArgList)));
4149+
} else {
4150+
B.generateNode(E, Pred,
4151+
S->BindExpr(E, LC,
4152+
Args.size() == 0
4153+
? getSValBuilder().makeZeroVal(T)
4154+
: S->getSVal(Args.front(), LC)));
4155+
}
4156+
}

clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -771,54 +771,6 @@ void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred,
771771
Bldr.generateNode(B, Pred, state->BindExpr(B, Pred->getLocationContext(), X));
772772
}
773773

774-
void ExprEngine::VisitInitListExpr(const InitListExpr *IE,
775-
ExplodedNode *Pred,
776-
ExplodedNodeSet &Dst) {
777-
StmtNodeBuilder B(Pred, Dst, *currBldrCtx);
778-
779-
ProgramStateRef state = Pred->getState();
780-
const LocationContext *LCtx = Pred->getLocationContext();
781-
QualType T = getContext().getCanonicalType(IE->getType());
782-
unsigned NumInitElements = IE->getNumInits();
783-
784-
if (!IE->isGLValue() && !IE->isTransparent() &&
785-
(T->isArrayType() || T->isRecordType() || T->isVectorType() ||
786-
T->isAnyComplexType())) {
787-
llvm::ImmutableList<SVal> vals = getBasicVals().getEmptySValList();
788-
789-
// Handle base case where the initializer has no elements.
790-
// e.g: static int* myArray[] = {};
791-
if (NumInitElements == 0) {
792-
SVal V = svalBuilder.makeCompoundVal(T, vals);
793-
B.generateNode(IE, Pred, state->BindExpr(IE, LCtx, V));
794-
return;
795-
}
796-
797-
for (const Stmt *S : llvm::reverse(*IE)) {
798-
SVal V = state->getSVal(cast<Expr>(S), LCtx);
799-
vals = getBasicVals().prependSVal(V, vals);
800-
}
801-
802-
B.generateNode(IE, Pred,
803-
state->BindExpr(IE, LCtx,
804-
svalBuilder.makeCompoundVal(T, vals)));
805-
return;
806-
}
807-
808-
// Handle scalars: int{5} and int{} and GLvalues.
809-
// Note, if the InitListExpr is a GLvalue, it means that there is an address
810-
// representing it, so it must have a single init element.
811-
assert(NumInitElements <= 1);
812-
813-
SVal V;
814-
if (NumInitElements == 0)
815-
V = getSValBuilder().makeZeroVal(T);
816-
else
817-
V = state->getSVal(IE->getInit(0), LCtx);
818-
819-
B.generateNode(IE, Pred, state->BindExpr(IE, LCtx, V));
820-
}
821-
822774
void ExprEngine::VisitGuardedExpr(const Expr *Ex,
823775
const Expr *L,
824776
const Expr *R,
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// RUN: %clang_analyze_cc1 -analyzer-checker=core.DivideZero -std=c++20 -verify %s
2+
3+
namespace GH148875 {
4+
struct A {
5+
int x;
6+
A(int v) : x(v) {}
7+
};
8+
9+
struct B {
10+
int x;
11+
B() : x(0) {}
12+
};
13+
14+
struct C {
15+
int x, y;
16+
C(int a, int b) : x(a), y(b) {}
17+
};
18+
19+
struct D {
20+
int x;
21+
};
22+
23+
struct E {
24+
D d;
25+
E(int a) : d(a) {}
26+
};
27+
28+
struct F {
29+
int x;
30+
};
31+
32+
int t1() {
33+
A a{42};
34+
return 1 / (a.x - 42); // expected-warning {{Division by zero}}
35+
}
36+
37+
int t2() {
38+
B b{};
39+
return 1 / b.x; // expected-warning {{Division by zero}}
40+
}
41+
42+
int t3() {
43+
C c1{1, -1};
44+
return 1 / (c1.x + c1.y); // expected-warning {{Division by zero}}
45+
}
46+
47+
int t4() {
48+
C c2{0, 0};
49+
return 1 / (c2.x + c2.y); // expected-warning {{Division by zero}}
50+
}
51+
52+
int t5() {
53+
E e{32};
54+
return 1 / (e.d.x - 32); // expected-warning {{Division by zero}}
55+
}
56+
57+
int t6() {
58+
F f(32);
59+
return 1 / (f.x - 32); // expected-warning {{Division by zero}}
60+
}
61+
} // namespace GH148875

clang/test/Analysis/div-zero.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,63 @@ int fooPR10616 (int qX ) {
1111
return (a % (qX-1)); // expected-warning {{Division by zero}}
1212

1313
}
14+
15+
namespace GH148875 {
16+
struct A {
17+
int x;
18+
A(int v) : x(v) {}
19+
};
20+
21+
struct B {
22+
int x;
23+
B() : x(0) {}
24+
};
25+
26+
struct C {
27+
int x, y;
28+
C(int a, int b) : x(a), y(b) {}
29+
};
30+
31+
struct D {
32+
int x;
33+
};
34+
35+
struct E {
36+
D d;
37+
E(int a) : d{a} {}
38+
};
39+
40+
struct F {
41+
int x;
42+
};
43+
44+
int t1() {
45+
A a{42};
46+
return 1 / (a.x - 42); // expected-warning {{Division by zero}}
47+
}
48+
49+
int t2() {
50+
B b{};
51+
return 1 / b.x; // expected-warning {{Division by zero}}
52+
}
53+
54+
int t3() {
55+
C c1{1, -1};
56+
return 1 / (c1.x + c1.y); // expected-warning {{Division by zero}}
57+
}
58+
59+
int t4() {
60+
C c2{0, 0};
61+
return 1 / (c2.x + c2.y); // expected-warning {{Division by zero}}
62+
}
63+
64+
int t5() {
65+
E e{32};
66+
return 1 / (e.d.x - 32); // expected-warning {{Division by zero}}
67+
}
68+
69+
int t6() {
70+
F f{32};
71+
return 1 / (f.x - 32); // expected-warning {{Division by zero}}
72+
}
73+
}

0 commit comments

Comments
 (0)