Skip to content

Commit 910abb3

Browse files
Add an example unit test where both checkers are in action
1 parent c2bd657 commit 910abb3

File tree

1 file changed

+89
-15
lines changed

1 file changed

+89
-15
lines changed

clang/unittests/StaticAnalyzer/BlockEntranceCallbackTest.cpp

Lines changed: 89 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,17 @@
1919
#include "llvm/ADT/STLExtras.h"
2020
#include "llvm/ADT/StringExtras.h"
2121
#include "llvm/ADT/StringRef.h"
22+
#include "llvm/ADT/Twine.h"
2223
#include "llvm/Support/FormatVariadic.h"
2324
#include "llvm/Support/raw_ostream.h"
2425
#include "gtest/gtest.h"
2526

26-
#include <initializer_list>
27-
2827
using namespace clang;
2928
using namespace ento;
3029

3130
namespace {
3231

33-
class BlockEntranceCallbackTester : public Checker<check::BlockEntrance> {
32+
class BlockEntranceCallbackTester final : public Checker<check::BlockEntrance> {
3433
const BugType Bug{this, "BlockEntranceTester"};
3534

3635
public:
@@ -53,38 +52,84 @@ class BlockEntranceCallbackTester : public Checker<check::BlockEntrance> {
5352
}
5453
};
5554

56-
void registerBlockEntranceTester(CheckerManager &Mgr) {
57-
Mgr.registerChecker<BlockEntranceCallbackTester>();
55+
class BranchConditionCallbackTester final
56+
: public Checker<check::BranchCondition> {
57+
const BugType Bug{this, "BranchConditionCallbackTester"};
58+
59+
public:
60+
void checkBranchCondition(const Stmt *Condition, CheckerContext &C) const {
61+
ExplodedNode *Node = C.generateNonFatalErrorNode(C.getState());
62+
if (!Node)
63+
return;
64+
const auto *FD =
65+
cast<FunctionDecl>(C.getLocationContext()->getStackFrame()->getDecl());
66+
67+
std::string Buffer =
68+
(llvm::Twine("Within '") + FD->getIdentifier()->getName() +
69+
"': branch condition '")
70+
.str();
71+
llvm::raw_string_ostream OS(Buffer);
72+
Condition->printPretty(OS, /*Helper=*/nullptr,
73+
C.getASTContext().getPrintingPolicy());
74+
OS << "'";
75+
auto Report = std::make_unique<PathSensitiveBugReport>(Bug, Buffer, Node);
76+
C.emitReport(std::move(Report));
77+
78+
C.addTransition();
79+
}
80+
};
81+
82+
template <typename Checker> void registerChecker(CheckerManager &Mgr) {
83+
Mgr.registerChecker<Checker>();
5884
}
5985

60-
bool shouldRegisterBlockEntranceTester(const CheckerManager &) { return true; }
86+
bool shouldAlwaysRegister(const CheckerManager &) { return true; }
6187

6288
void addBlockEntranceTester(AnalysisASTConsumer &AnalysisConsumer,
6389
AnalyzerOptions &AnOpts) {
64-
AnOpts.CheckersAndPackages = {{"test.BlockEntranceTester", true}};
90+
AnOpts.CheckersAndPackages.emplace_back("test.BlockEntranceTester", true);
91+
AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {
92+
Registry.addChecker(&registerChecker<BlockEntranceCallbackTester>,
93+
&shouldAlwaysRegister, "test.BlockEntranceTester",
94+
"EmptyDescription", "EmptyDocsUri",
95+
/*IsHidden=*/false);
96+
});
97+
}
98+
99+
void addBranchConditionTester(AnalysisASTConsumer &AnalysisConsumer,
100+
AnalyzerOptions &AnOpts) {
101+
AnOpts.CheckersAndPackages.emplace_back("test.BranchConditionTester", true);
65102
AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {
66-
Registry.addChecker(
67-
&registerBlockEntranceTester, &shouldRegisterBlockEntranceTester,
68-
"test.BlockEntranceTester", "EmptyDescription", "EmptyDocsUri",
69-
/*IsHidden=*/false);
103+
Registry.addChecker(&registerChecker<BranchConditionCallbackTester>,
104+
&shouldAlwaysRegister, "test.BranchConditionTester",
105+
"EmptyDescription", "EmptyDocsUri",
106+
/*IsHidden=*/false);
70107
});
71108
}
72109

110+
[[maybe_unused]] void dumpCFGAndEgraph(AnalysisASTConsumer &AnalysisConsumer,
111+
AnalyzerOptions &AnOpts) {
112+
AnOpts.CheckersAndPackages.emplace_back("debug.DumpCFG", true);
113+
AnOpts.CheckersAndPackages.emplace_back("debug.ViewExplodedGraph", true);
114+
}
115+
73116
llvm::SmallVector<StringRef> parseEachDiag(StringRef Diags) {
74117
llvm::SmallVector<StringRef> Fragments;
75118
llvm::SplitString(Diags, Fragments, "\n");
119+
// Drop the prefix like "test.BlockEntranceTester: " from each fragment.
76120
llvm::for_each(Fragments, [](StringRef &Fragment) {
77-
Fragment.consume_front("test.BlockEntranceTester: ");
121+
Fragment = Fragment.drop_until([](char Ch) { return Ch == ' '; });
122+
Fragment.consume_front(" ");
78123
});
79124
llvm::sort(Fragments);
80125
return Fragments;
81126
}
82127

128+
template <AddCheckerFn Fn = addBlockEntranceTester, AddCheckerFn... Fns>
83129
bool runChecker(const std::string &Code, std::string &Diags) {
84130
std::string RawDiags;
85-
bool Res =
86-
runCheckerOnCode<addBlockEntranceTester>(Code, RawDiags,
87-
/*OnlyEmitWarnings=*/true);
131+
bool Res = runCheckerOnCode<Fn, Fns...>(Code, RawDiags,
132+
/*OnlyEmitWarnings=*/true);
88133
llvm::raw_string_ostream OS(Diags);
89134
llvm::interleave(parseEachDiag(RawDiags), OS, "\n");
90135
return Res;
@@ -280,4 +325,33 @@ TEST(BlockEntranceTester, Switch) {
280325
Diags);
281326
}
282327

328+
TEST(BlockEntranceTester, BlockEntranceVSBranchCondition) {
329+
constexpr auto Code = R"cpp(
330+
bool coin();
331+
int top(int x) {
332+
int v = 0;
333+
switch (x) {
334+
default: v = 30; break;
335+
}
336+
if (x == 6) {
337+
v = 40;
338+
}
339+
return v;
340+
})cpp";
341+
std::string Diags;
342+
EXPECT_TRUE((runChecker<addBlockEntranceTester, addBranchConditionTester>(
343+
Code, Diags)));
344+
EXPECT_EQ(expected({
345+
"Within 'top' B1 -> B0",
346+
"Within 'top' B2 -> B1",
347+
"Within 'top' B3 -> B1",
348+
"Within 'top' B3 -> B2",
349+
"Within 'top' B4 -> B5",
350+
"Within 'top' B5 -> B3",
351+
"Within 'top' B6 -> B4",
352+
"Within 'top': branch condition 'x == 6'",
353+
}),
354+
Diags);
355+
}
356+
283357
} // namespace

0 commit comments

Comments
 (0)