Skip to content

Commit 5ed678a

Browse files
[CFG] Add a BuildOption to consider default branch of switch on covered enumerations (#161345)
By default, the `default:` branch (or the successor if there is no `default` and cases return) of a switch on fully covered enumerations is considered as "Unreachable". It is a sane assumption in most cases, but not always. That commit allows to change such behavior when needed.
1 parent ed12dc5 commit 5ed678a

File tree

3 files changed

+159
-2
lines changed

3 files changed

+159
-2
lines changed

clang/include/clang/Analysis/CFG.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1251,6 +1251,7 @@ class CFG {
12511251
bool MarkElidedCXXConstructors = false;
12521252
bool AddVirtualBaseBranches = false;
12531253
bool OmitImplicitValueInitializers = false;
1254+
bool AssumeReachableDefaultInSwitchStatements = false;
12541255

12551256
BuildOptions() = default;
12561257

clang/lib/Analysis/CFG.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4516,10 +4516,13 @@ CFGBlock *CFGBuilder::VisitSwitchStmt(SwitchStmt *Terminator) {
45164516
//
45174517
// Note: We add a successor to a switch that is considered covered yet has no
45184518
// case statements if the enumeration has no enumerators.
4519+
// We also consider this successor reachable if
4520+
// BuildOpts.SwitchReqDefaultCoveredEnum is true.
45194521
bool SwitchAlwaysHasSuccessor = false;
45204522
SwitchAlwaysHasSuccessor |= switchExclusivelyCovered;
4521-
SwitchAlwaysHasSuccessor |= Terminator->isAllEnumCasesCovered() &&
4522-
Terminator->getSwitchCaseList();
4523+
SwitchAlwaysHasSuccessor |=
4524+
!BuildOpts.AssumeReachableDefaultInSwitchStatements &&
4525+
Terminator->isAllEnumCasesCovered() && Terminator->getSwitchCaseList();
45234526
addSuccessor(SwitchTerminatedBlock, DefaultCaseBlock,
45244527
!SwitchAlwaysHasSuccessor);
45254528

clang/unittests/Analysis/CFGTest.cpp

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,159 @@ TEST(CFG, DependantBaseAddImplicitDtors) {
9393
.getStatus());
9494
}
9595

96+
TEST(CFG, SwitchCoveredEnumNoDefault) {
97+
const char *Code = R"(
98+
enum class E {E1, E2};
99+
int f(E e) {
100+
switch(e) {
101+
case E::E1:
102+
return 1;
103+
case E::E2:
104+
return 2;
105+
}
106+
return 0;
107+
}
108+
)";
109+
CFG::BuildOptions Options;
110+
Options.AssumeReachableDefaultInSwitchStatements = true;
111+
BuildResult B = BuildCFG(Code, Options);
112+
ASSERT_EQ(BuildResult::BuiltCFG, B.getStatus());
113+
114+
// [B5 (ENTRY)]
115+
// Succs (1): B2
116+
//
117+
// [B1]
118+
// 1: 0
119+
// 2: return [B1.1];
120+
// Preds (1): B2
121+
// Succs (1): B0
122+
//
123+
// [B2]
124+
// 1: e (ImplicitCastExpr, LValueToRValue, E)
125+
// T: switch [B2.1]
126+
// Preds (1): B5
127+
// Succs (3): B3 B4 B1
128+
//
129+
// [B3]
130+
// case E::E2:
131+
// 1: 2
132+
// 2: return [B3.1];
133+
// Preds (1): B2
134+
// Succs (1): B0
135+
//
136+
// [B4]
137+
// case E::E1:
138+
// 1: 1
139+
// 2: return [B4.1];
140+
// Preds (1): B2
141+
// Succs (1): B0
142+
//
143+
// [B0 (EXIT)]
144+
// Preds (3): B1 B3 B4
145+
146+
auto *CFG = B.getCFG();
147+
const auto &Entry = CFG->getEntry();
148+
ASSERT_EQ(1u, Entry.succ_size());
149+
// First successor of Entry is the switch
150+
CFGBlock *SwitchBlock = *Entry.succ_begin();
151+
ASSERT_EQ(3u, SwitchBlock->succ_size());
152+
// Last successor of the switch is after the switch
153+
auto NoCaseSucc = SwitchBlock->succ_rbegin();
154+
EXPECT_TRUE(NoCaseSucc->isReachable());
155+
156+
// Checking that the same node is Unreachable without this setting
157+
Options.AssumeReachableDefaultInSwitchStatements = false;
158+
B = BuildCFG(Code, Options);
159+
ASSERT_EQ(BuildResult::BuiltCFG, B.getStatus());
160+
161+
const auto &Entry2 = B.getCFG()->getEntry();
162+
ASSERT_EQ(1u, Entry2.succ_size());
163+
CFGBlock *SwitchBlock2 = *Entry2.succ_begin();
164+
ASSERT_EQ(3u, SwitchBlock2->succ_size());
165+
auto NoCaseSucc2 = SwitchBlock2->succ_rbegin();
166+
EXPECT_FALSE(NoCaseSucc2->isReachable());
167+
}
168+
169+
TEST(CFG, SwitchCoveredEnumWithDefault) {
170+
const char *Code = R"(
171+
enum class E {E1, E2};
172+
int f(E e) {
173+
switch(e) {
174+
case E::E1:
175+
return 1;
176+
case E::E2:
177+
return 2;
178+
default:
179+
return 0;
180+
}
181+
return -1;
182+
}
183+
)";
184+
CFG::BuildOptions Options;
185+
Options.AssumeReachableDefaultInSwitchStatements = true;
186+
BuildResult B = BuildCFG(Code, Options);
187+
ASSERT_EQ(BuildResult::BuiltCFG, B.getStatus());
188+
189+
// [B6 (ENTRY)]
190+
// Succs (1): B2
191+
//
192+
// [B1]
193+
// 1: -1
194+
// 2: return [B1.1];
195+
// Succs (1): B0
196+
//
197+
// [B2]
198+
// 1: e (ImplicitCastExpr, LValueToRValue, E)
199+
// T: switch [B2.1]
200+
// Preds (1): B6
201+
// Succs (3): B4 B5 B3
202+
//
203+
// [B3]
204+
// default:
205+
// 1: 0
206+
// 2: return [B3.1];
207+
// Preds (1): B2
208+
// Succs (1): B0
209+
//
210+
// [B4]
211+
// case E::E2:
212+
// 1: 2
213+
// 2: return [B4.1];
214+
// Preds (1): B2
215+
// Succs (1): B0
216+
//
217+
// [B5]
218+
// case E::E1:
219+
// 1: 1
220+
// 2: return [B5.1];
221+
// Preds (1): B2
222+
// Succs (1): B0
223+
//
224+
// [B0 (EXIT)]
225+
// Preds (4): B1 B3 B4 B5
226+
227+
const auto &Entry = B.getCFG()->getEntry();
228+
ASSERT_EQ(1u, Entry.succ_size());
229+
// First successor of Entry is the switch
230+
CFGBlock *SwitchBlock = *Entry.succ_begin();
231+
ASSERT_EQ(3u, SwitchBlock->succ_size());
232+
// Last successor of the switch is the default branch
233+
auto defaultBlock = SwitchBlock->succ_rbegin();
234+
EXPECT_TRUE(defaultBlock->isReachable());
235+
236+
// Checking that the same node is Unreachable without this setting
237+
Options.AssumeReachableDefaultInSwitchStatements = false;
238+
B = BuildCFG(Code, Options);
239+
ASSERT_EQ(BuildResult::BuiltCFG, B.getStatus());
240+
241+
const auto &Entry2 = B.getCFG()->getEntry();
242+
ASSERT_EQ(1u, Entry2.succ_size());
243+
CFGBlock *SwitchBlock2 = *Entry2.succ_begin();
244+
ASSERT_EQ(3u, SwitchBlock2->succ_size());
245+
auto defaultBlock2 = SwitchBlock2->succ_rbegin();
246+
EXPECT_FALSE(defaultBlock2->isReachable());
247+
}
248+
96249
TEST(CFG, IsLinear) {
97250
auto expectLinear = [](bool IsLinear, const char *Code) {
98251
BuildResult B = BuildCFG(Code);

0 commit comments

Comments
 (0)