19
19
#include " llvm/ADT/STLExtras.h"
20
20
#include " llvm/ADT/StringExtras.h"
21
21
#include " llvm/ADT/StringRef.h"
22
+ #include " llvm/ADT/Twine.h"
22
23
#include " llvm/Support/FormatVariadic.h"
23
24
#include " llvm/Support/raw_ostream.h"
24
25
#include " gtest/gtest.h"
25
26
26
- #include < initializer_list>
27
-
28
27
using namespace clang ;
29
28
using namespace ento ;
30
29
31
30
namespace {
32
31
33
- class BlockEntranceCallbackTester : public Checker <check::BlockEntrance> {
32
+ class BlockEntranceCallbackTester final : public Checker<check::BlockEntrance> {
34
33
const BugType Bug{this , " BlockEntranceTester" };
35
34
36
35
public:
@@ -53,38 +52,84 @@ class BlockEntranceCallbackTester : public Checker<check::BlockEntrance> {
53
52
}
54
53
};
55
54
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>();
58
84
}
59
85
60
- bool shouldRegisterBlockEntranceTester (const CheckerManager &) { return true ; }
86
+ bool shouldAlwaysRegister (const CheckerManager &) { return true ; }
61
87
62
88
void addBlockEntranceTester (AnalysisASTConsumer &AnalysisConsumer,
63
89
AnalyzerOptions &AnOpts) {
64
- AnOpts.CheckersAndPackages = {{" test.BlockEntranceTester" , true }};
90
+ AnOpts.CheckersAndPackages .emplace_back (" test.BlockEntranceTester" , true );
91
+ AnalysisConsumer.AddCheckerRegistrationFn ([](CheckerRegistry &Registry) {
92
+ Registry.addChecker (®isterChecker<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 );
65
102
AnalysisConsumer.AddCheckerRegistrationFn ([](CheckerRegistry &Registry) {
66
- Registry.addChecker (
67
- ®isterBlockEntranceTester, &shouldRegisterBlockEntranceTester ,
68
- " test.BlockEntranceTester " , " EmptyDescription" , " EmptyDocsUri" ,
69
- /* IsHidden=*/ false );
103
+ Registry.addChecker (®isterChecker<BranchConditionCallbackTester>,
104
+ &shouldAlwaysRegister, " test.BranchConditionTester " ,
105
+ " EmptyDescription" , " EmptyDocsUri" ,
106
+ /* IsHidden=*/ false );
70
107
});
71
108
}
72
109
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
+
73
116
llvm::SmallVector<StringRef> parseEachDiag (StringRef Diags) {
74
117
llvm::SmallVector<StringRef> Fragments;
75
118
llvm::SplitString (Diags, Fragments, " \n " );
119
+ // Drop the prefix like "test.BlockEntranceTester: " from each fragment.
76
120
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 (" " );
78
123
});
79
124
llvm::sort (Fragments);
80
125
return Fragments;
81
126
}
82
127
128
+ template <AddCheckerFn Fn = addBlockEntranceTester, AddCheckerFn... Fns>
83
129
bool runChecker (const std::string &Code, std::string &Diags) {
84
130
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 );
88
133
llvm::raw_string_ostream OS (Diags);
89
134
llvm::interleave (parseEachDiag (RawDiags), OS, " \n " );
90
135
return Res;
@@ -280,4 +325,33 @@ TEST(BlockEntranceTester, Switch) {
280
325
Diags);
281
326
}
282
327
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
+
283
357
} // namespace
0 commit comments