Skip to content

Commit 6a78d37

Browse files
authored
[analyzer] Add BranchCondition callback to 'switch' (llvm#182058)
Previously the condition of a 'switch' statement did not trigger a `BranchCondition` callback. This commit resolves this old FIXME and e.g. lets the checker `core.uninitialzed.Branch` report code where the condition of a `switch` statement is undefined. This commit also contains a very small unrelated change that removes a short fragment of dead code from `processBranch`.
1 parent 63492ca commit 6a78d37

File tree

3 files changed

+123
-51
lines changed

3 files changed

+123
-51
lines changed

clang/lib/StaticAnalyzer/Core/ExprEngine.cpp

Lines changed: 61 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2872,9 +2872,6 @@ void ExprEngine::processBranch(
28722872

28732873
BranchNodeBuilder Builder(Dst, BldCtx, DstT, DstF);
28742874
for (ExplodedNode *PredN : CheckersOutSet) {
2875-
if (PredN->isSink())
2876-
continue;
2877-
28782875
ProgramStateRef PrevState = PredN->getState();
28792876

28802877
ProgramStateRef StTrue = PrevState, StFalse = PrevState;
@@ -3106,70 +3103,83 @@ void ExprEngine::processEndOfFunction(NodeBuilderContext& BC,
31063103
/// nodes by processing the 'effects' of a switch statement.
31073104
void ExprEngine::processSwitch(NodeBuilderContext &BC, const SwitchStmt *Switch,
31083105
ExplodedNode *Pred, ExplodedNodeSet &Dst) {
3106+
currBldrCtx = &BC;
31093107
const Expr *Condition = Switch->getCond();
31103108

31113109
SwitchNodeBuilder Builder(Dst, BC);
3110+
ExplodedNodeSet CheckersOutSet;
31123111

3113-
ProgramStateRef State = Pred->getState();
3114-
SVal CondV = State->getSVal(Condition, Pred->getLocationContext());
3115-
3116-
if (CondV.isUndef()) {
3117-
// FIXME: Emit warnings when the switch condition is undefined.
3118-
return;
3119-
}
3112+
getCheckerManager().runCheckersForBranchCondition(
3113+
Condition->IgnoreParens(), CheckersOutSet, Pred, *this);
31203114

3121-
std::optional<NonLoc> CondNL = CondV.getAs<NonLoc>();
3115+
for (ExplodedNode *Node : CheckersOutSet) {
3116+
ProgramStateRef State = Node->getState();
31223117

3123-
for (const CFGBlock *Block : Builder) {
3124-
// Successor may be pruned out during CFG construction.
3125-
if (!Block)
3118+
SVal CondV = State->getSVal(Condition, Node->getLocationContext());
3119+
if (CondV.isUndef()) {
3120+
// This can only happen if core.uninitialized.Branch is disabled.
31263121
continue;
3122+
}
31273123

3128-
const CaseStmt *Case = cast<CaseStmt>(Block->getLabel());
3124+
std::optional<NonLoc> CondNL = CondV.getAs<NonLoc>();
31293125

3130-
// Evaluate the LHS of the case value.
3131-
llvm::APSInt V1 = Case->getLHS()->EvaluateKnownConstInt(getContext());
3132-
assert(V1.getBitWidth() == getContext().getIntWidth(Condition->getType()));
3126+
for (const CFGBlock *Block : Builder) {
3127+
// Successor may be pruned out during CFG construction.
3128+
if (!Block)
3129+
continue;
31333130

3134-
// Get the RHS of the case, if it exists.
3135-
llvm::APSInt V2;
3136-
if (const Expr *E = Case->getRHS())
3137-
V2 = E->EvaluateKnownConstInt(getContext());
3138-
else
3139-
V2 = V1;
3131+
const CaseStmt *Case = cast<CaseStmt>(Block->getLabel());
31403132

3141-
ProgramStateRef StateMatching;
3142-
if (CondNL) {
3143-
// Split the state: this "case:" matches / does not match.
3144-
std::tie(StateMatching, State) =
3145-
State->assumeInclusiveRange(*CondNL, V1, V2);
3146-
} else {
3147-
// The switch condition is UnknownVal, so we enter each "case:" without
3148-
// any state update.
3149-
StateMatching = State;
3150-
}
3133+
// Evaluate the LHS of the case value.
3134+
llvm::APSInt V1 = Case->getLHS()->EvaluateKnownConstInt(getContext());
3135+
assert(V1.getBitWidth() ==
3136+
getContext().getIntWidth(Condition->getType()));
31513137

3152-
if (StateMatching)
3153-
Builder.generateCaseStmtNode(Block, StateMatching, Pred);
3138+
// Get the RHS of the case, if it exists.
3139+
llvm::APSInt V2;
3140+
if (const Expr *E = Case->getRHS())
3141+
V2 = E->EvaluateKnownConstInt(getContext());
3142+
else
3143+
V2 = V1;
31543144

3155-
// If _not_ entering the current case is infeasible, we are done with
3156-
// processing this branch.
3145+
ProgramStateRef StateMatching;
3146+
if (CondNL) {
3147+
// Split the state: this "case:" matches / does not match.
3148+
std::tie(StateMatching, State) =
3149+
State->assumeInclusiveRange(*CondNL, V1, V2);
3150+
} else {
3151+
// The switch condition is UnknownVal, so we enter each "case:" without
3152+
// any state update.
3153+
StateMatching = State;
3154+
}
3155+
3156+
if (StateMatching)
3157+
Builder.generateCaseStmtNode(Block, StateMatching, Node);
3158+
3159+
// If _not_ entering the current case is infeasible, then we are done
3160+
// with processing the paths through the current Node.
3161+
if (!State)
3162+
break;
3163+
}
31573164
if (!State)
3158-
return;
3159-
}
3160-
// If we have switch(enum value), the default branch is not
3161-
// feasible if all of the enum constants not covered by 'case:' statements
3162-
// are not feasible values for the switch condition.
3163-
//
3164-
// Note that this isn't as accurate as it could be. Even if there isn't
3165-
// a case for a particular enum value as long as that enum value isn't
3166-
// feasible then it shouldn't be considered for making 'default:' reachable.
3167-
if (Condition->IgnoreParenImpCasts()->getType()->isEnumeralType()) {
3168-
if (Switch->isAllEnumCasesCovered())
3169-
return;
3165+
continue;
3166+
3167+
// If we have switch(enum value), the default branch is not
3168+
// feasible if all of the enum constants not covered by 'case:' statements
3169+
// are not feasible values for the switch condition.
3170+
//
3171+
// Note that this isn't as accurate as it could be. Even if there isn't
3172+
// a case for a particular enum value as long as that enum value isn't
3173+
// feasible then it shouldn't be considered for making 'default:' reachable.
3174+
if (Condition->IgnoreParenImpCasts()->getType()->isEnumeralType()) {
3175+
if (Switch->isAllEnumCasesCovered())
3176+
continue;
3177+
}
3178+
3179+
Builder.generateDefaultCaseNode(State, Node);
31703180
}
31713181

3172-
Builder.generateDefaultCaseNode(State, Pred);
3182+
currBldrCtx = nullptr;
31733183
}
31743184

31753185
//===----------------------------------------------------------------------===//
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 -Wno-uninitialized -verify %s
2+
3+
int if_cond(void) {
4+
int foo;
5+
if (foo) //expected-warning {{Branch condition evaluates to a garbage value}}
6+
return 1;
7+
return 2;
8+
}
9+
10+
int logical_op_and_if_cond(void) {
11+
int foo, bar;
12+
if (foo && bar) //expected-warning {{Branch condition evaluates to a garbage value}}
13+
return 1;
14+
return 2;
15+
}
16+
17+
int logical_op_cond(int arg) {
18+
int foo;
19+
if (foo && arg) //expected-warning {{Branch condition evaluates to a garbage value}}
20+
return 1;
21+
return 2;
22+
}
23+
24+
int if_cond_after_logical_op(int arg) {
25+
int foo;
26+
if (arg && foo) //expected-warning {{Branch condition evaluates to a garbage value}}
27+
return 1;
28+
return 2;
29+
}
30+
31+
int ternary_cond(void) {
32+
int foo;
33+
return foo ? 1 : 2; //expected-warning {{Branch condition evaluates to a garbage value}}
34+
}
35+
36+
int while_cond(void) {
37+
int foo;
38+
while (foo) //expected-warning {{Branch condition evaluates to a garbage value}}
39+
return 1;
40+
return 2;
41+
}
42+
43+
int do_while_cond(void) {
44+
int foo, bar;
45+
do {
46+
foo = 43;
47+
} while (bar); //expected-warning {{Branch condition evaluates to a garbage value}}
48+
return foo;
49+
}
50+
51+
int switch_cond(void) {
52+
int foo;
53+
switch (foo) { //expected-warning {{Branch condition evaluates to a garbage value}}
54+
case 1:
55+
return 3;
56+
case 2:
57+
return 440;
58+
default:
59+
return 6772;
60+
}
61+
}

clang/unittests/StaticAnalyzer/BlockEntranceCallbackTest.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,7 @@ TEST(BlockEntranceTester, BlockEntranceVSBranchCondition) {
362362
"Within 'top' B5 -> B3",
363363
"Within 'top' B6 -> B4",
364364
"Within 'top': branch condition 'x == 6'",
365+
"Within 'top': branch condition 'x'",
365366
}),
366367
Diags);
367368
}

0 commit comments

Comments
 (0)