Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/include/clang/Analysis/CFG.h
Original file line number Diff line number Diff line change
Expand Up @@ -1251,6 +1251,7 @@ class CFG {
bool MarkElidedCXXConstructors = false;
bool AddVirtualBaseBranches = false;
bool OmitImplicitValueInitializers = false;
bool AssumeReachableDefaultInSwitchStatements = false;

BuildOptions() = default;

Expand Down
7 changes: 5 additions & 2 deletions clang/lib/Analysis/CFG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4516,10 +4516,13 @@ CFGBlock *CFGBuilder::VisitSwitchStmt(SwitchStmt *Terminator) {
//
// Note: We add a successor to a switch that is considered covered yet has no
// case statements if the enumeration has no enumerators.
// We also consider this successor reachable if
// BuildOpts.SwitchReqDefaultCoveredEnum is true.
bool SwitchAlwaysHasSuccessor = false;
SwitchAlwaysHasSuccessor |= switchExclusivelyCovered;
SwitchAlwaysHasSuccessor |= Terminator->isAllEnumCasesCovered() &&
Terminator->getSwitchCaseList();
SwitchAlwaysHasSuccessor |=
!BuildOpts.AssumeReachableDefaultInSwitchStatements &&
Terminator->isAllEnumCasesCovered() && Terminator->getSwitchCaseList();
addSuccessor(SwitchTerminatedBlock, DefaultCaseBlock,
!SwitchAlwaysHasSuccessor);

Expand Down
153 changes: 153 additions & 0 deletions clang/unittests/Analysis/CFGTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,159 @@ TEST(CFG, DependantBaseAddImplicitDtors) {
.getStatus());
}

TEST(CFG, SwitchCoveredEnumNoDefault) {
const char *Code = R"(
enum class E {E1, E2};
int f(E e) {
switch(e) {
case E::E1:
return 1;
case E::E2:
return 2;
}
return 0;
}
)";
CFG::BuildOptions Options;
Options.AssumeReachableDefaultInSwitchStatements = true;
BuildResult B = BuildCFG(Code, Options);
EXPECT_EQ(BuildResult::BuiltCFG, B.getStatus());

// [B5 (ENTRY)]
// Succs (1): B2
//
// [B1]
// 1: 0
// 2: return [B1.1];
// Preds (1): B2
// Succs (1): B0
//
// [B2]
// 1: e (ImplicitCastExpr, LValueToRValue, E)
// T: switch [B2.1]
// Preds (1): B5
// Succs (3): B3 B4 B1
//
// [B3]
// case E::E2:
// 1: 2
// 2: return [B3.1];
// Preds (1): B2
// Succs (1): B0
//
// [B4]
// case E::E1:
// 1: 1
// 2: return [B4.1];
// Preds (1): B2
// Succs (1): B0
//
// [B0 (EXIT)]
// Preds (3): B1 B3 B4

auto *CFG = B.getCFG();
const auto &Entry = CFG->getEntry();
ASSERT_EQ(1u, Entry.succ_size());
// First successor of Entry is the switch
CFGBlock *SwitchBlock = *Entry.succ_begin();
ASSERT_EQ(3u, SwitchBlock->succ_size());
// Last successor of the switch is after the switch
auto NoCaseSucc = SwitchBlock->succ_rbegin();
EXPECT_TRUE(NoCaseSucc->isReachable());

// Checking that the same node is Unreachable without this setting
Options.AssumeReachableDefaultInSwitchStatements = false;
B = BuildCFG(Code, Options);
EXPECT_EQ(BuildResult::BuiltCFG, B.getStatus());

const auto &Entry2 = B.getCFG()->getEntry();
ASSERT_EQ(1u, Entry2.succ_size());
CFGBlock *SwitchBlock2 = *Entry2.succ_begin();
ASSERT_EQ(3u, SwitchBlock2->succ_size());
auto NoCaseSucc2 = SwitchBlock2->succ_rbegin();
EXPECT_FALSE(NoCaseSucc2->isReachable());
}

TEST(CFG, SwitchCoveredEnumWithDefault) {
const char *Code = R"(
enum class E {E1, E2};
int f(E e) {
switch(e) {
case E::E1:
return 1;
case E::E2:
return 2;
default:
return 0;
}
return -1;
}
)";
CFG::BuildOptions Options;
Options.AssumeReachableDefaultInSwitchStatements = true;
BuildResult B = BuildCFG(Code, Options);
EXPECT_EQ(BuildResult::BuiltCFG, B.getStatus());

// [B6 (ENTRY)]
// Succs (1): B2
//
// [B1]
// 1: -1
// 2: return [B1.1];
// Succs (1): B0
//
// [B2]
// 1: e (ImplicitCastExpr, LValueToRValue, E)
// T: switch [B2.1]
// Preds (1): B6
// Succs (3): B4 B5 B3
//
// [B3]
// default:
// 1: 0
// 2: return [B3.1];
// Preds (1): B2
// Succs (1): B0
//
// [B4]
// case E::E2:
// 1: 2
// 2: return [B4.1];
// Preds (1): B2
// Succs (1): B0
//
// [B5]
// case E::E1:
// 1: 1
// 2: return [B5.1];
// Preds (1): B2
// Succs (1): B0
//
// [B0 (EXIT)]
// Preds (4): B1 B3 B4 B5

const auto &Entry = B.getCFG()->getEntry();
ASSERT_EQ(1u, Entry.succ_size());
// First successor of Entry is the switch
CFGBlock *SwitchBlock = *Entry.succ_begin();
ASSERT_EQ(3u, SwitchBlock->succ_size());
// Last successor of the switch is the default branch
auto defaultBlock = SwitchBlock->succ_rbegin();
EXPECT_TRUE(defaultBlock->isReachable());

// Checking that the same node is Unreachable without this setting
Options.AssumeReachableDefaultInSwitchStatements = false;
B = BuildCFG(Code, Options);
EXPECT_EQ(BuildResult::BuiltCFG, B.getStatus());

const auto &Entry2 = B.getCFG()->getEntry();
ASSERT_EQ(1u, Entry2.succ_size());
CFGBlock *SwitchBlock2 = *Entry2.succ_begin();
ASSERT_EQ(3u, SwitchBlock2->succ_size());
auto defaultBlock2 = SwitchBlock2->succ_rbegin();
EXPECT_FALSE(defaultBlock2->isReachable());
}

TEST(CFG, IsLinear) {
auto expectLinear = [](bool IsLinear, const char *Code) {
BuildResult B = BuildCFG(Code);
Expand Down
Loading