Skip to content

Commit 104f5d1

Browse files
[analyzer] Introduce the check::BlockEntrance checker callback (#140924)
Tranersing the CFG blocks of a function is a fundamental operation. Many C++ constructs can create splits in the control-flow, such as `if`, `for`, and similar control structures or ternary expressions, gnu conditionals, gotos, switches and possibly more. Checkers should be able to get notifications about entering or leaving a CFG block of interest. Note that in the ExplodedGraph there is always a BlockEntrance ProgramPoint right after the BlockEdge ProgramPoint. I considered naming this callback check::BlockEdge, but then that may leave the observer of the graph puzzled to see BlockEdge points followed more BlockEdge nodes describing the same CFG transition. This confusion could also apply to Bug Report Visitors too. Because of this, I decided to hook BlockEntrance ProgramPoints instead. The same confusion applies here, but I find this still a better place TBH. There would only appear only one BlockEntrance ProgramPoint in the graph if no checkers modify the state or emit a bug report. Otherwise they modify some GDM (aka. State) thus create a new ExplodedNode with the same BlockEntrance ProgramPoint in the graph. CPP-6484
1 parent 80da58d commit 104f5d1

File tree

10 files changed

+526
-16
lines changed

10 files changed

+526
-16
lines changed

clang/include/clang/StaticAnalyzer/Core/Checker.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,22 @@ class Bind {
221221
}
222222
};
223223

224+
class BlockEntrance {
225+
template <typename CHECKER>
226+
static void _checkBlockEntrance(void *Checker,
227+
const clang::BlockEntrance &Entrance,
228+
CheckerContext &C) {
229+
((const CHECKER *)Checker)->checkBlockEntrance(Entrance, C);
230+
}
231+
232+
public:
233+
template <typename CHECKER>
234+
static void _register(CHECKER *checker, CheckerManager &mgr) {
235+
mgr._registerForBlockEntrance(CheckerManager::CheckBlockEntranceFunc(
236+
checker, _checkBlockEntrance<CHECKER>));
237+
}
238+
};
239+
224240
class EndAnalysis {
225241
template <typename CHECKER>
226242
static void _checkEndAnalysis(void *checker, ExplodedGraph &G,
@@ -536,6 +552,8 @@ class CheckerBase : public CheckerFrontend, public CheckerBackend {
536552
template <typename... CHECKs>
537553
class Checker : public CheckerBase, public CHECKs... {
538554
public:
555+
using BlockEntrance = clang::BlockEntrance;
556+
539557
template <typename CHECKER>
540558
static void _register(CHECKER *Chk, CheckerManager &Mgr) {
541559
(CHECKs::_register(Chk, Mgr), ...);
@@ -565,6 +583,8 @@ class Checker : public CheckerBase, public CHECKs... {
565583
template <typename... CHECKs>
566584
class CheckerFamily : public CheckerBackend, public CHECKs... {
567585
public:
586+
using BlockEntrance = clang::BlockEntrance;
587+
568588
template <typename CHECKER>
569589
static void _register(CHECKER *Chk, CheckerManager &Mgr) {
570590
(CHECKs::_register(Chk, Mgr), ...);

clang/include/clang/StaticAnalyzer/Core/CheckerManager.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,12 @@ class CheckerManager {
344344
const Stmt *S, ExprEngine &Eng,
345345
const ProgramPoint &PP);
346346

347+
/// Run checkers after taking a control flow edge.
348+
void runCheckersForBlockEntrance(ExplodedNodeSet &Dst,
349+
const ExplodedNodeSet &Src,
350+
const BlockEntrance &Entrance,
351+
ExprEngine &Eng) const;
352+
347353
/// Run checkers for end of analysis.
348354
void runCheckersForEndAnalysis(ExplodedGraph &G, BugReporter &BR,
349355
ExprEngine &Eng);
@@ -496,6 +502,9 @@ class CheckerManager {
496502
using CheckBindFunc =
497503
CheckerFn<void(SVal location, SVal val, const Stmt *S, CheckerContext &)>;
498504

505+
using CheckBlockEntranceFunc =
506+
CheckerFn<void(const BlockEntrance &, CheckerContext &)>;
507+
499508
using CheckEndAnalysisFunc =
500509
CheckerFn<void (ExplodedGraph &, BugReporter &, ExprEngine &)>;
501510

@@ -557,6 +566,8 @@ class CheckerManager {
557566

558567
void _registerForBind(CheckBindFunc checkfn);
559568

569+
void _registerForBlockEntrance(CheckBlockEntranceFunc checkfn);
570+
560571
void _registerForEndAnalysis(CheckEndAnalysisFunc checkfn);
561572

562573
void _registerForBeginFunction(CheckBeginFunctionFunc checkfn);
@@ -663,6 +674,8 @@ class CheckerManager {
663674

664675
std::vector<CheckBindFunc> BindCheckers;
665676

677+
std::vector<CheckBlockEntranceFunc> BlockEntranceCheckers;
678+
666679
std::vector<CheckEndAnalysisFunc> EndAnalysisCheckers;
667680

668681
std::vector<CheckBeginFunctionFunc> BeginFunctionCheckers;

clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,10 @@ class ExprEngine {
321321
NodeBuilderWithSinks &nodeBuilder,
322322
ExplodedNode *Pred);
323323

324+
void runCheckersForBlockEntrance(const NodeBuilderContext &BldCtx,
325+
const BlockEntrance &Entrance,
326+
ExplodedNode *Pred, ExplodedNodeSet &Dst);
327+
324328
/// ProcessBranch - Called by CoreEngine. Used to generate successor nodes by
325329
/// processing the 'effects' of a branch condition. If the branch condition
326330
/// is a loop condition, IterationsCompletedInLoop is the number of completed

clang/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class CheckerDocumentation
3939
check::ASTDecl<FunctionDecl>,
4040
check::BeginFunction,
4141
check::Bind,
42+
check::BlockEntrance,
4243
check::BranchCondition,
4344
check::ConstPointerEscape,
4445
check::DeadSymbols,
@@ -128,7 +129,20 @@ class CheckerDocumentation
128129
/// check::PostCall
129130
void checkPostCall(const CallEvent &Call, CheckerContext &C) const {}
130131

131-
/// Pre-visit of the condition statement of a branch (such as IfStmt).
132+
/// Pre-visit of the condition statement of a branch.
133+
/// For example:
134+
/// - logical operators (&&, ||)
135+
/// - if, do, while, for, ranged-for statements
136+
/// - ternary operators (?:), gnu conditionals, gnu choose expressions
137+
/// Interestingly, switch statements don't seem to trigger BranchCondition.
138+
///
139+
/// check::BlockEntrance is a similar callback, which is strictly more
140+
/// generic. Prefer check::BranchCondition to check::BlockEntrance if
141+
/// pre-visiting conditional statements is enough for the checker.
142+
/// Note that check::BlockEntrance is also invoked for leaving basic blocks
143+
/// while entering the next.
144+
///
145+
/// check::BranchCondition
132146
void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const {}
133147

134148
/// Post-visit the C++ operator new's allocation call.
@@ -165,6 +179,29 @@ class CheckerDocumentation
165179
/// check::Bind
166180
void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &) const {}
167181

182+
/// Called after a CFG edge is taken within a function.
183+
///
184+
/// This callback can be used to obtain information about potential branching
185+
/// points or any other constructs that involve traversing a CFG edge.
186+
///
187+
/// check::BranchCondition is a similar callback, which is only invoked for
188+
/// pre-visiting the condition statement of a branch. Prefer that callback if
189+
/// possible.
190+
///
191+
/// \remark There is no CFG edge from the caller to a callee, consequently
192+
/// this callback is not invoked for "inlining" a function call.
193+
/// \remark Once a function call is inlined, we will start from the imaginary
194+
/// "entry" basic block of that CFG. This callback will be invoked for
195+
/// entering the real first basic block of the "inlined" function body from
196+
/// that "entry" basic block.
197+
/// \remark This callback is also invoked for entering the imaginary "exit"
198+
/// basic block of the CFG when returning from a function.
199+
///
200+
/// \param E The ProgramPoint that describes the transition.
201+
///
202+
/// check::BlockEntrance
203+
void checkBlockEntrance(const BlockEntrance &E, CheckerContext &) const {}
204+
168205
/// Called whenever a symbol becomes dead.
169206
///
170207
/// This callback should be used by the checkers to aggressively clean

clang/lib/StaticAnalyzer/Core/CheckerManager.cpp

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,11 @@ bool CheckerManager::hasPathSensitiveCheckers() const {
4141
return IfAnyAreNonEmpty(
4242
StmtCheckers, PreObjCMessageCheckers, ObjCMessageNilCheckers,
4343
PostObjCMessageCheckers, PreCallCheckers, PostCallCheckers,
44-
LocationCheckers, BindCheckers, EndAnalysisCheckers,
45-
BeginFunctionCheckers, EndFunctionCheckers, BranchConditionCheckers,
46-
NewAllocatorCheckers, LiveSymbolsCheckers, DeadSymbolsCheckers,
47-
RegionChangesCheckers, PointerEscapeCheckers, EvalAssumeCheckers,
48-
EvalCallCheckers, EndOfTranslationUnitCheckers);
44+
LocationCheckers, BindCheckers, BlockEntranceCheckers,
45+
EndAnalysisCheckers, BeginFunctionCheckers, EndFunctionCheckers,
46+
BranchConditionCheckers, NewAllocatorCheckers, LiveSymbolsCheckers,
47+
DeadSymbolsCheckers, RegionChangesCheckers, PointerEscapeCheckers,
48+
EvalAssumeCheckers, EvalCallCheckers, EndOfTranslationUnitCheckers);
4949
}
5050

5151
void CheckerManager::reportInvalidCheckerOptionValue(
@@ -418,6 +418,42 @@ void CheckerManager::runCheckersForBind(ExplodedNodeSet &Dst,
418418
expandGraphWithCheckers(C, Dst, Src);
419419
}
420420

421+
namespace {
422+
struct CheckBlockEntranceContext {
423+
using CheckBlockEntranceFunc = CheckerManager::CheckBlockEntranceFunc;
424+
using CheckersTy = std::vector<CheckBlockEntranceFunc>;
425+
426+
const CheckersTy &Checkers;
427+
const BlockEntrance &Entrance;
428+
ExprEngine &Eng;
429+
430+
CheckBlockEntranceContext(const CheckersTy &Checkers,
431+
const BlockEntrance &Entrance, ExprEngine &Eng)
432+
: Checkers(Checkers), Entrance(Entrance), Eng(Eng) {}
433+
434+
auto checkers_begin() const { return Checkers.begin(); }
435+
auto checkers_end() const { return Checkers.end(); }
436+
437+
void runChecker(CheckBlockEntranceFunc CheckFn, NodeBuilder &Bldr,
438+
ExplodedNode *Pred) {
439+
llvm::TimeTraceScope TimeScope(
440+
checkerScopeName("BlockEntrance", CheckFn.Checker));
441+
CheckerContext C(Bldr, Eng, Pred, Entrance.withTag(CheckFn.Checker));
442+
CheckFn(Entrance, C);
443+
}
444+
};
445+
446+
} // namespace
447+
448+
void CheckerManager::runCheckersForBlockEntrance(ExplodedNodeSet &Dst,
449+
const ExplodedNodeSet &Src,
450+
const BlockEntrance &Entrance,
451+
ExprEngine &Eng) const {
452+
CheckBlockEntranceContext C(BlockEntranceCheckers, Entrance, Eng);
453+
llvm::TimeTraceScope TimeScope{"CheckerManager::runCheckersForBlockEntrance"};
454+
expandGraphWithCheckers(C, Dst, Src);
455+
}
456+
421457
void CheckerManager::runCheckersForEndAnalysis(ExplodedGraph &G,
422458
BugReporter &BR,
423459
ExprEngine &Eng) {
@@ -875,6 +911,10 @@ void CheckerManager::_registerForBind(CheckBindFunc checkfn) {
875911
BindCheckers.push_back(checkfn);
876912
}
877913

914+
void CheckerManager::_registerForBlockEntrance(CheckBlockEntranceFunc checkfn) {
915+
BlockEntranceCheckers.push_back(checkfn);
916+
}
917+
878918
void CheckerManager::_registerForEndAnalysis(CheckEndAnalysisFunc checkfn) {
879919
EndAnalysisCheckers.push_back(checkfn);
880920
}

clang/lib/StaticAnalyzer/Core/CoreEngine.cpp

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -304,26 +304,37 @@ void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) {
304304
}
305305
}
306306

307+
ExplodedNodeSet CheckerNodes;
308+
BlockEntrance BE(L.getSrc(), L.getDst(), Pred->getLocationContext());
309+
ExprEng.runCheckersForBlockEntrance(BuilderCtx, BE, Pred, CheckerNodes);
310+
307311
// Process the final state transition.
308-
ExprEng.processEndOfFunction(BuilderCtx, Pred, RS);
312+
for (ExplodedNode *P : CheckerNodes) {
313+
ExprEng.processEndOfFunction(BuilderCtx, P, RS);
314+
}
309315

310316
// This path is done. Don't enqueue any more nodes.
311317
return;
312318
}
313319

314320
// Call into the ExprEngine to process entering the CFGBlock.
315-
ExplodedNodeSet dstNodes;
316321
BlockEntrance BE(L.getSrc(), L.getDst(), Pred->getLocationContext());
317-
NodeBuilderWithSinks nodeBuilder(Pred, dstNodes, BuilderCtx, BE);
318-
ExprEng.processCFGBlockEntrance(L, nodeBuilder, Pred);
322+
ExplodedNodeSet DstNodes;
323+
NodeBuilderWithSinks NodeBuilder(Pred, DstNodes, BuilderCtx, BE);
324+
ExprEng.processCFGBlockEntrance(L, NodeBuilder, Pred);
319325

320326
// Auto-generate a node.
321-
if (!nodeBuilder.hasGeneratedNodes()) {
322-
nodeBuilder.generateNode(Pred->State, Pred);
327+
if (!NodeBuilder.hasGeneratedNodes()) {
328+
NodeBuilder.generateNode(Pred->State, Pred);
329+
}
330+
331+
ExplodedNodeSet CheckerNodes;
332+
for (auto *N : DstNodes) {
333+
ExprEng.runCheckersForBlockEntrance(BuilderCtx, BE, N, CheckerNodes);
323334
}
324335

325336
// Enqueue nodes onto the worklist.
326-
enqueue(dstNodes);
337+
enqueue(CheckerNodes);
327338
}
328339

329340
void CoreEngine::HandleBlockEntrance(const BlockEntrance &L,

clang/lib/StaticAnalyzer/Core/ExprEngine.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2617,6 +2617,19 @@ void ExprEngine::processCFGBlockEntrance(const BlockEdge &L,
26172617
}
26182618
}
26192619

2620+
void ExprEngine::runCheckersForBlockEntrance(const NodeBuilderContext &BldCtx,
2621+
const BlockEntrance &Entrance,
2622+
ExplodedNode *Pred,
2623+
ExplodedNodeSet &Dst) {
2624+
llvm::PrettyStackTraceFormat CrashInfo(
2625+
"Processing block entrance B%d -> B%d",
2626+
Entrance.getPreviousBlock()->getBlockID(),
2627+
Entrance.getBlock()->getBlockID());
2628+
currBldrCtx = &BldCtx;
2629+
getCheckerManager().runCheckersForBlockEntrance(Dst, Pred, Entrance, *this);
2630+
currBldrCtx = nullptr;
2631+
}
2632+
26202633
//===----------------------------------------------------------------------===//
26212634
// Branch processing.
26222635
//===----------------------------------------------------------------------===//

clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -711,9 +711,10 @@ void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred,
711711
}
712712

713713
ExplodedNode *N = Pred;
714-
while (!N->getLocation().getAs<BlockEntrance>()) {
714+
while (!N->getLocation().getAs<BlockEdge>()) {
715715
ProgramPoint P = N->getLocation();
716-
assert(P.getAs<PreStmt>()|| P.getAs<PreStmtPurgeDeadSymbols>());
716+
assert(P.getAs<PreStmt>() || P.getAs<PreStmtPurgeDeadSymbols>() ||
717+
P.getAs<BlockEntrance>());
717718
(void) P;
718719
if (N->pred_size() != 1) {
719720
// We failed to track back where we came from.
@@ -729,7 +730,6 @@ void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred,
729730
return;
730731
}
731732

732-
N = *N->pred_begin();
733733
BlockEdge BE = N->getLocation().castAs<BlockEdge>();
734734
SVal X;
735735

0 commit comments

Comments
 (0)