aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/clang16/lib/StaticAnalyzer/Core/CheckerManager.cpp
diff options
context:
space:
mode:
authorthegeorg <thegeorg@yandex-team.com>2024-03-13 13:58:24 +0300
committerthegeorg <thegeorg@yandex-team.com>2024-03-13 14:11:53 +0300
commit11a895b7e15d1c5a1f52706396b82e3f9db953cb (patch)
treefabc6d883b0f946151f61ae7865cee9f529a1fdd /contrib/libs/clang16/lib/StaticAnalyzer/Core/CheckerManager.cpp
parent9685917341315774aad5733b1793b1e533a88bbb (diff)
downloadydb-11a895b7e15d1c5a1f52706396b82e3f9db953cb.tar.gz
Export clang-format16 via ydblib project
6e6be3a95868fde888d801b7590af4044049563f
Diffstat (limited to 'contrib/libs/clang16/lib/StaticAnalyzer/Core/CheckerManager.cpp')
-rw-r--r--contrib/libs/clang16/lib/StaticAnalyzer/Core/CheckerManager.cpp920
1 files changed, 920 insertions, 0 deletions
diff --git a/contrib/libs/clang16/lib/StaticAnalyzer/Core/CheckerManager.cpp b/contrib/libs/clang16/lib/StaticAnalyzer/Core/CheckerManager.cpp
new file mode 100644
index 0000000000..6fc16223ea
--- /dev/null
+++ b/contrib/libs/clang16/lib/StaticAnalyzer/Core/CheckerManager.cpp
@@ -0,0 +1,920 @@
+//===- CheckerManager.cpp - Static Analyzer Checker Manager ---------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines the Static Analyzer Checker Manager.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/AST/Stmt.h"
+#include "clang/Analysis/ProgramPoint.h"
+#include "clang/Basic/JsonSupport.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Driver/DriverDiagnostic.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FormatVariadic.h"
+#include <cassert>
+#include <optional>
+#include <vector>
+
+using namespace clang;
+using namespace ento;
+
+bool CheckerManager::hasPathSensitiveCheckers() const {
+ const auto IfAnyAreNonEmpty = [](const auto &... Callbacks) -> bool {
+ return (!Callbacks.empty() || ...);
+ };
+ return IfAnyAreNonEmpty(
+ StmtCheckers, PreObjCMessageCheckers, ObjCMessageNilCheckers,
+ PostObjCMessageCheckers, PreCallCheckers, PostCallCheckers,
+ LocationCheckers, BindCheckers, EndAnalysisCheckers,
+ BeginFunctionCheckers, EndFunctionCheckers, BranchConditionCheckers,
+ NewAllocatorCheckers, LiveSymbolsCheckers, DeadSymbolsCheckers,
+ RegionChangesCheckers, PointerEscapeCheckers, EvalAssumeCheckers,
+ EvalCallCheckers, EndOfTranslationUnitCheckers);
+}
+
+void CheckerManager::finishedCheckerRegistration() {
+#ifndef NDEBUG
+ // Make sure that for every event that has listeners, there is at least
+ // one dispatcher registered for it.
+ for (const auto &Event : Events)
+ assert(Event.second.HasDispatcher &&
+ "No dispatcher registered for an event");
+#endif
+}
+
+void CheckerManager::reportInvalidCheckerOptionValue(
+ const CheckerBase *C, StringRef OptionName,
+ StringRef ExpectedValueDesc) const {
+
+ getDiagnostics().Report(diag::err_analyzer_checker_option_invalid_input)
+ << (llvm::Twine() + C->getTagDescription() + ":" + OptionName).str()
+ << ExpectedValueDesc;
+}
+
+//===----------------------------------------------------------------------===//
+// Functions for running checkers for AST traversing..
+//===----------------------------------------------------------------------===//
+
+void CheckerManager::runCheckersOnASTDecl(const Decl *D, AnalysisManager& mgr,
+ BugReporter &BR) {
+ assert(D);
+
+ unsigned DeclKind = D->getKind();
+ CachedDeclCheckers *checkers = nullptr;
+ CachedDeclCheckersMapTy::iterator CCI = CachedDeclCheckersMap.find(DeclKind);
+ if (CCI != CachedDeclCheckersMap.end()) {
+ checkers = &(CCI->second);
+ } else {
+ // Find the checkers that should run for this Decl and cache them.
+ checkers = &CachedDeclCheckersMap[DeclKind];
+ for (const auto &info : DeclCheckers)
+ if (info.IsForDeclFn(D))
+ checkers->push_back(info.CheckFn);
+ }
+
+ assert(checkers);
+ for (const auto &checker : *checkers)
+ checker(D, mgr, BR);
+}
+
+void CheckerManager::runCheckersOnASTBody(const Decl *D, AnalysisManager& mgr,
+ BugReporter &BR) {
+ assert(D && D->hasBody());
+
+ for (const auto &BodyChecker : BodyCheckers)
+ BodyChecker(D, mgr, BR);
+}
+
+//===----------------------------------------------------------------------===//
+// Functions for running checkers for path-sensitive checking.
+//===----------------------------------------------------------------------===//
+
+template <typename CHECK_CTX>
+static void expandGraphWithCheckers(CHECK_CTX checkCtx,
+ ExplodedNodeSet &Dst,
+ const ExplodedNodeSet &Src) {
+ const NodeBuilderContext &BldrCtx = checkCtx.Eng.getBuilderContext();
+ if (Src.empty())
+ return;
+
+ typename CHECK_CTX::CheckersTy::const_iterator
+ I = checkCtx.checkers_begin(), E = checkCtx.checkers_end();
+ if (I == E) {
+ Dst.insert(Src);
+ return;
+ }
+
+ ExplodedNodeSet Tmp1, Tmp2;
+ const ExplodedNodeSet *PrevSet = &Src;
+
+ for (; I != E; ++I) {
+ ExplodedNodeSet *CurrSet = nullptr;
+ if (I+1 == E)
+ CurrSet = &Dst;
+ else {
+ CurrSet = (PrevSet == &Tmp1) ? &Tmp2 : &Tmp1;
+ CurrSet->clear();
+ }
+
+ NodeBuilder B(*PrevSet, *CurrSet, BldrCtx);
+ for (const auto &NI : *PrevSet)
+ checkCtx.runChecker(*I, B, NI);
+
+ // If all the produced transitions are sinks, stop.
+ if (CurrSet->empty())
+ return;
+
+ // Update which NodeSet is the current one.
+ PrevSet = CurrSet;
+ }
+}
+
+namespace {
+
+ struct CheckStmtContext {
+ using CheckersTy = SmallVectorImpl<CheckerManager::CheckStmtFunc>;
+
+ bool IsPreVisit;
+ const CheckersTy &Checkers;
+ const Stmt *S;
+ ExprEngine &Eng;
+ bool WasInlined;
+
+ CheckStmtContext(bool isPreVisit, const CheckersTy &checkers,
+ const Stmt *s, ExprEngine &eng, bool wasInlined = false)
+ : IsPreVisit(isPreVisit), Checkers(checkers), S(s), Eng(eng),
+ WasInlined(wasInlined) {}
+
+ CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+ CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
+
+ void runChecker(CheckerManager::CheckStmtFunc checkFn,
+ NodeBuilder &Bldr, ExplodedNode *Pred) {
+ // FIXME: Remove respondsToCallback from CheckerContext;
+ ProgramPoint::Kind K = IsPreVisit ? ProgramPoint::PreStmtKind :
+ ProgramPoint::PostStmtKind;
+ const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K,
+ Pred->getLocationContext(), checkFn.Checker);
+ CheckerContext C(Bldr, Eng, Pred, L, WasInlined);
+ checkFn(S, C);
+ }
+ };
+
+} // namespace
+
+/// Run checkers for visiting Stmts.
+void CheckerManager::runCheckersForStmt(bool isPreVisit,
+ ExplodedNodeSet &Dst,
+ const ExplodedNodeSet &Src,
+ const Stmt *S,
+ ExprEngine &Eng,
+ bool WasInlined) {
+ CheckStmtContext C(isPreVisit, getCachedStmtCheckersFor(S, isPreVisit),
+ S, Eng, WasInlined);
+ expandGraphWithCheckers(C, Dst, Src);
+}
+
+namespace {
+
+ struct CheckObjCMessageContext {
+ using CheckersTy = std::vector<CheckerManager::CheckObjCMessageFunc>;
+
+ ObjCMessageVisitKind Kind;
+ bool WasInlined;
+ const CheckersTy &Checkers;
+ const ObjCMethodCall &Msg;
+ ExprEngine &Eng;
+
+ CheckObjCMessageContext(ObjCMessageVisitKind visitKind,
+ const CheckersTy &checkers,
+ const ObjCMethodCall &msg, ExprEngine &eng,
+ bool wasInlined)
+ : Kind(visitKind), WasInlined(wasInlined), Checkers(checkers), Msg(msg),
+ Eng(eng) {}
+
+ CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+ CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
+
+ void runChecker(CheckerManager::CheckObjCMessageFunc checkFn,
+ NodeBuilder &Bldr, ExplodedNode *Pred) {
+ bool IsPreVisit;
+
+ switch (Kind) {
+ case ObjCMessageVisitKind::Pre:
+ IsPreVisit = true;
+ break;
+ case ObjCMessageVisitKind::MessageNil:
+ case ObjCMessageVisitKind::Post:
+ IsPreVisit = false;
+ break;
+ }
+
+ const ProgramPoint &L = Msg.getProgramPoint(IsPreVisit,checkFn.Checker);
+ CheckerContext C(Bldr, Eng, Pred, L, WasInlined);
+
+ checkFn(*Msg.cloneWithState<ObjCMethodCall>(Pred->getState()), C);
+ }
+ };
+
+} // namespace
+
+/// Run checkers for visiting obj-c messages.
+void CheckerManager::runCheckersForObjCMessage(ObjCMessageVisitKind visitKind,
+ ExplodedNodeSet &Dst,
+ const ExplodedNodeSet &Src,
+ const ObjCMethodCall &msg,
+ ExprEngine &Eng,
+ bool WasInlined) {
+ const auto &checkers = getObjCMessageCheckers(visitKind);
+ CheckObjCMessageContext C(visitKind, checkers, msg, Eng, WasInlined);
+ expandGraphWithCheckers(C, Dst, Src);
+}
+
+const std::vector<CheckerManager::CheckObjCMessageFunc> &
+CheckerManager::getObjCMessageCheckers(ObjCMessageVisitKind Kind) const {
+ switch (Kind) {
+ case ObjCMessageVisitKind::Pre:
+ return PreObjCMessageCheckers;
+ break;
+ case ObjCMessageVisitKind::Post:
+ return PostObjCMessageCheckers;
+ case ObjCMessageVisitKind::MessageNil:
+ return ObjCMessageNilCheckers;
+ }
+ llvm_unreachable("Unknown Kind");
+}
+
+namespace {
+
+ // FIXME: This has all the same signatures as CheckObjCMessageContext.
+ // Is there a way we can merge the two?
+ struct CheckCallContext {
+ using CheckersTy = std::vector<CheckerManager::CheckCallFunc>;
+
+ bool IsPreVisit, WasInlined;
+ const CheckersTy &Checkers;
+ const CallEvent &Call;
+ ExprEngine &Eng;
+
+ CheckCallContext(bool isPreVisit, const CheckersTy &checkers,
+ const CallEvent &call, ExprEngine &eng,
+ bool wasInlined)
+ : IsPreVisit(isPreVisit), WasInlined(wasInlined), Checkers(checkers),
+ Call(call), Eng(eng) {}
+
+ CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+ CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
+
+ void runChecker(CheckerManager::CheckCallFunc checkFn,
+ NodeBuilder &Bldr, ExplodedNode *Pred) {
+ const ProgramPoint &L = Call.getProgramPoint(IsPreVisit,checkFn.Checker);
+ CheckerContext C(Bldr, Eng, Pred, L, WasInlined);
+
+ checkFn(*Call.cloneWithState(Pred->getState()), C);
+ }
+ };
+
+} // namespace
+
+/// Run checkers for visiting an abstract call event.
+void CheckerManager::runCheckersForCallEvent(bool isPreVisit,
+ ExplodedNodeSet &Dst,
+ const ExplodedNodeSet &Src,
+ const CallEvent &Call,
+ ExprEngine &Eng,
+ bool WasInlined) {
+ CheckCallContext C(isPreVisit,
+ isPreVisit ? PreCallCheckers
+ : PostCallCheckers,
+ Call, Eng, WasInlined);
+ expandGraphWithCheckers(C, Dst, Src);
+}
+
+namespace {
+
+ struct CheckLocationContext {
+ using CheckersTy = std::vector<CheckerManager::CheckLocationFunc>;
+
+ const CheckersTy &Checkers;
+ SVal Loc;
+ bool IsLoad;
+ const Stmt *NodeEx; /* Will become a CFGStmt */
+ const Stmt *BoundEx;
+ ExprEngine &Eng;
+
+ CheckLocationContext(const CheckersTy &checkers,
+ SVal loc, bool isLoad, const Stmt *NodeEx,
+ const Stmt *BoundEx,
+ ExprEngine &eng)
+ : Checkers(checkers), Loc(loc), IsLoad(isLoad), NodeEx(NodeEx),
+ BoundEx(BoundEx), Eng(eng) {}
+
+ CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+ CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
+
+ void runChecker(CheckerManager::CheckLocationFunc checkFn,
+ NodeBuilder &Bldr, ExplodedNode *Pred) {
+ ProgramPoint::Kind K = IsLoad ? ProgramPoint::PreLoadKind :
+ ProgramPoint::PreStoreKind;
+ const ProgramPoint &L =
+ ProgramPoint::getProgramPoint(NodeEx, K,
+ Pred->getLocationContext(),
+ checkFn.Checker);
+ CheckerContext C(Bldr, Eng, Pred, L);
+ checkFn(Loc, IsLoad, BoundEx, C);
+ }
+ };
+
+} // namespace
+
+/// Run checkers for load/store of a location.
+
+void CheckerManager::runCheckersForLocation(ExplodedNodeSet &Dst,
+ const ExplodedNodeSet &Src,
+ SVal location, bool isLoad,
+ const Stmt *NodeEx,
+ const Stmt *BoundEx,
+ ExprEngine &Eng) {
+ CheckLocationContext C(LocationCheckers, location, isLoad, NodeEx,
+ BoundEx, Eng);
+ expandGraphWithCheckers(C, Dst, Src);
+}
+
+namespace {
+
+ struct CheckBindContext {
+ using CheckersTy = std::vector<CheckerManager::CheckBindFunc>;
+
+ const CheckersTy &Checkers;
+ SVal Loc;
+ SVal Val;
+ const Stmt *S;
+ ExprEngine &Eng;
+ const ProgramPoint &PP;
+
+ CheckBindContext(const CheckersTy &checkers,
+ SVal loc, SVal val, const Stmt *s, ExprEngine &eng,
+ const ProgramPoint &pp)
+ : Checkers(checkers), Loc(loc), Val(val), S(s), Eng(eng), PP(pp) {}
+
+ CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+ CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
+
+ void runChecker(CheckerManager::CheckBindFunc checkFn,
+ NodeBuilder &Bldr, ExplodedNode *Pred) {
+ const ProgramPoint &L = PP.withTag(checkFn.Checker);
+ CheckerContext C(Bldr, Eng, Pred, L);
+
+ checkFn(Loc, Val, S, C);
+ }
+ };
+
+} // namespace
+
+/// Run checkers for binding of a value to a location.
+void CheckerManager::runCheckersForBind(ExplodedNodeSet &Dst,
+ const ExplodedNodeSet &Src,
+ SVal location, SVal val,
+ const Stmt *S, ExprEngine &Eng,
+ const ProgramPoint &PP) {
+ CheckBindContext C(BindCheckers, location, val, S, Eng, PP);
+ expandGraphWithCheckers(C, Dst, Src);
+}
+
+void CheckerManager::runCheckersForEndAnalysis(ExplodedGraph &G,
+ BugReporter &BR,
+ ExprEngine &Eng) {
+ for (const auto &EndAnalysisChecker : EndAnalysisCheckers)
+ EndAnalysisChecker(G, BR, Eng);
+}
+
+namespace {
+
+struct CheckBeginFunctionContext {
+ using CheckersTy = std::vector<CheckerManager::CheckBeginFunctionFunc>;
+
+ const CheckersTy &Checkers;
+ ExprEngine &Eng;
+ const ProgramPoint &PP;
+
+ CheckBeginFunctionContext(const CheckersTy &Checkers, ExprEngine &Eng,
+ const ProgramPoint &PP)
+ : Checkers(Checkers), Eng(Eng), PP(PP) {}
+
+ CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+ CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
+
+ void runChecker(CheckerManager::CheckBeginFunctionFunc checkFn,
+ NodeBuilder &Bldr, ExplodedNode *Pred) {
+ const ProgramPoint &L = PP.withTag(checkFn.Checker);
+ CheckerContext C(Bldr, Eng, Pred, L);
+
+ checkFn(C);
+ }
+};
+
+} // namespace
+
+void CheckerManager::runCheckersForBeginFunction(ExplodedNodeSet &Dst,
+ const BlockEdge &L,
+ ExplodedNode *Pred,
+ ExprEngine &Eng) {
+ ExplodedNodeSet Src;
+ Src.insert(Pred);
+ CheckBeginFunctionContext C(BeginFunctionCheckers, Eng, L);
+ expandGraphWithCheckers(C, Dst, Src);
+}
+
+/// Run checkers for end of path.
+// Note, We do not chain the checker output (like in expandGraphWithCheckers)
+// for this callback since end of path nodes are expected to be final.
+void CheckerManager::runCheckersForEndFunction(NodeBuilderContext &BC,
+ ExplodedNodeSet &Dst,
+ ExplodedNode *Pred,
+ ExprEngine &Eng,
+ const ReturnStmt *RS) {
+ // We define the builder outside of the loop because if at least one checker
+ // creates a successor for Pred, we do not need to generate an
+ // autotransition for it.
+ NodeBuilder Bldr(Pred, Dst, BC);
+ for (const auto &checkFn : EndFunctionCheckers) {
+ const ProgramPoint &L =
+ FunctionExitPoint(RS, Pred->getLocationContext(), checkFn.Checker);
+ CheckerContext C(Bldr, Eng, Pred, L);
+ checkFn(RS, C);
+ }
+}
+
+namespace {
+
+ struct CheckBranchConditionContext {
+ using CheckersTy = std::vector<CheckerManager::CheckBranchConditionFunc>;
+
+ const CheckersTy &Checkers;
+ const Stmt *Condition;
+ ExprEngine &Eng;
+
+ CheckBranchConditionContext(const CheckersTy &checkers,
+ const Stmt *Cond, ExprEngine &eng)
+ : Checkers(checkers), Condition(Cond), Eng(eng) {}
+
+ CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+ CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
+
+ void runChecker(CheckerManager::CheckBranchConditionFunc checkFn,
+ NodeBuilder &Bldr, ExplodedNode *Pred) {
+ ProgramPoint L = PostCondition(Condition, Pred->getLocationContext(),
+ checkFn.Checker);
+ CheckerContext C(Bldr, Eng, Pred, L);
+ checkFn(Condition, C);
+ }
+ };
+
+} // namespace
+
+/// Run checkers for branch condition.
+void CheckerManager::runCheckersForBranchCondition(const Stmt *Condition,
+ ExplodedNodeSet &Dst,
+ ExplodedNode *Pred,
+ ExprEngine &Eng) {
+ ExplodedNodeSet Src;
+ Src.insert(Pred);
+ CheckBranchConditionContext C(BranchConditionCheckers, Condition, Eng);
+ expandGraphWithCheckers(C, Dst, Src);
+}
+
+namespace {
+
+ struct CheckNewAllocatorContext {
+ using CheckersTy = std::vector<CheckerManager::CheckNewAllocatorFunc>;
+
+ const CheckersTy &Checkers;
+ const CXXAllocatorCall &Call;
+ bool WasInlined;
+ ExprEngine &Eng;
+
+ CheckNewAllocatorContext(const CheckersTy &Checkers,
+ const CXXAllocatorCall &Call, bool WasInlined,
+ ExprEngine &Eng)
+ : Checkers(Checkers), Call(Call), WasInlined(WasInlined), Eng(Eng) {}
+
+ CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+ CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
+
+ void runChecker(CheckerManager::CheckNewAllocatorFunc checkFn,
+ NodeBuilder &Bldr, ExplodedNode *Pred) {
+ ProgramPoint L =
+ PostAllocatorCall(Call.getOriginExpr(), Pred->getLocationContext());
+ CheckerContext C(Bldr, Eng, Pred, L, WasInlined);
+ checkFn(cast<CXXAllocatorCall>(*Call.cloneWithState(Pred->getState())),
+ C);
+ }
+ };
+
+} // namespace
+
+void CheckerManager::runCheckersForNewAllocator(const CXXAllocatorCall &Call,
+ ExplodedNodeSet &Dst,
+ ExplodedNode *Pred,
+ ExprEngine &Eng,
+ bool WasInlined) {
+ ExplodedNodeSet Src;
+ Src.insert(Pred);
+ CheckNewAllocatorContext C(NewAllocatorCheckers, Call, WasInlined, Eng);
+ expandGraphWithCheckers(C, Dst, Src);
+}
+
+/// Run checkers for live symbols.
+void CheckerManager::runCheckersForLiveSymbols(ProgramStateRef state,
+ SymbolReaper &SymReaper) {
+ for (const auto &LiveSymbolsChecker : LiveSymbolsCheckers)
+ LiveSymbolsChecker(state, SymReaper);
+}
+
+namespace {
+
+ struct CheckDeadSymbolsContext {
+ using CheckersTy = std::vector<CheckerManager::CheckDeadSymbolsFunc>;
+
+ const CheckersTy &Checkers;
+ SymbolReaper &SR;
+ const Stmt *S;
+ ExprEngine &Eng;
+ ProgramPoint::Kind ProgarmPointKind;
+
+ CheckDeadSymbolsContext(const CheckersTy &checkers, SymbolReaper &sr,
+ const Stmt *s, ExprEngine &eng,
+ ProgramPoint::Kind K)
+ : Checkers(checkers), SR(sr), S(s), Eng(eng), ProgarmPointKind(K) {}
+
+ CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+ CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
+
+ void runChecker(CheckerManager::CheckDeadSymbolsFunc checkFn,
+ NodeBuilder &Bldr, ExplodedNode *Pred) {
+ const ProgramPoint &L = ProgramPoint::getProgramPoint(S, ProgarmPointKind,
+ Pred->getLocationContext(), checkFn.Checker);
+ CheckerContext C(Bldr, Eng, Pred, L);
+
+ // Note, do not pass the statement to the checkers without letting them
+ // differentiate if we ran remove dead bindings before or after the
+ // statement.
+ checkFn(SR, C);
+ }
+ };
+
+} // namespace
+
+/// Run checkers for dead symbols.
+void CheckerManager::runCheckersForDeadSymbols(ExplodedNodeSet &Dst,
+ const ExplodedNodeSet &Src,
+ SymbolReaper &SymReaper,
+ const Stmt *S,
+ ExprEngine &Eng,
+ ProgramPoint::Kind K) {
+ CheckDeadSymbolsContext C(DeadSymbolsCheckers, SymReaper, S, Eng, K);
+ expandGraphWithCheckers(C, Dst, Src);
+}
+
+/// Run checkers for region changes.
+ProgramStateRef
+CheckerManager::runCheckersForRegionChanges(ProgramStateRef state,
+ const InvalidatedSymbols *invalidated,
+ ArrayRef<const MemRegion *> ExplicitRegions,
+ ArrayRef<const MemRegion *> Regions,
+ const LocationContext *LCtx,
+ const CallEvent *Call) {
+ for (const auto &RegionChangesChecker : RegionChangesCheckers) {
+ // If any checker declares the state infeasible (or if it starts that way),
+ // bail out.
+ if (!state)
+ return nullptr;
+ state = RegionChangesChecker(state, invalidated, ExplicitRegions, Regions,
+ LCtx, Call);
+ }
+ return state;
+}
+
+/// Run checkers to process symbol escape event.
+ProgramStateRef
+CheckerManager::runCheckersForPointerEscape(ProgramStateRef State,
+ const InvalidatedSymbols &Escaped,
+ const CallEvent *Call,
+ PointerEscapeKind Kind,
+ RegionAndSymbolInvalidationTraits *ETraits) {
+ assert((Call != nullptr ||
+ (Kind != PSK_DirectEscapeOnCall &&
+ Kind != PSK_IndirectEscapeOnCall)) &&
+ "Call must not be NULL when escaping on call");
+ for (const auto &PointerEscapeChecker : PointerEscapeCheckers) {
+ // If any checker declares the state infeasible (or if it starts that
+ // way), bail out.
+ if (!State)
+ return nullptr;
+ State = PointerEscapeChecker(State, Escaped, Call, Kind, ETraits);
+ }
+ return State;
+}
+
+/// Run checkers for handling assumptions on symbolic values.
+ProgramStateRef
+CheckerManager::runCheckersForEvalAssume(ProgramStateRef state,
+ SVal Cond, bool Assumption) {
+ for (const auto &EvalAssumeChecker : EvalAssumeCheckers) {
+ // If any checker declares the state infeasible (or if it starts that way),
+ // bail out.
+ if (!state)
+ return nullptr;
+ state = EvalAssumeChecker(state, Cond, Assumption);
+ }
+ return state;
+}
+
+/// Run checkers for evaluating a call.
+/// Only one checker will evaluate the call.
+void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
+ const ExplodedNodeSet &Src,
+ const CallEvent &Call,
+ ExprEngine &Eng,
+ const EvalCallOptions &CallOpts) {
+ for (auto *const Pred : Src) {
+ std::optional<CheckerNameRef> evaluatorChecker;
+
+ ExplodedNodeSet checkDst;
+ NodeBuilder B(Pred, checkDst, Eng.getBuilderContext());
+
+ // Check if any of the EvalCall callbacks can evaluate the call.
+ for (const auto &EvalCallChecker : EvalCallCheckers) {
+ // TODO: Support the situation when the call doesn't correspond
+ // to any Expr.
+ ProgramPoint L = ProgramPoint::getProgramPoint(
+ Call.getOriginExpr(), ProgramPoint::PostStmtKind,
+ Pred->getLocationContext(), EvalCallChecker.Checker);
+ bool evaluated = false;
+ { // CheckerContext generates transitions(populates checkDest) on
+ // destruction, so introduce the scope to make sure it gets properly
+ // populated.
+ CheckerContext C(B, Eng, Pred, L);
+ evaluated = EvalCallChecker(Call, C);
+ }
+#ifndef NDEBUG
+ if (evaluated && evaluatorChecker) {
+ const auto toString = [](const CallEvent &Call) -> std::string {
+ std::string Buf;
+ llvm::raw_string_ostream OS(Buf);
+ Call.dump(OS);
+ OS.flush();
+ return Buf;
+ };
+ std::string AssertionMessage = llvm::formatv(
+ "The '{0}' call has been already evaluated by the {1} checker, "
+ "while the {2} checker also tried to evaluate the same call. At "
+ "most one checker supposed to evaluate a call.",
+ toString(Call), evaluatorChecker->getName(),
+ EvalCallChecker.Checker->getCheckerName());
+ llvm_unreachable(AssertionMessage.c_str());
+ }
+#endif
+ if (evaluated) {
+ evaluatorChecker = EvalCallChecker.Checker->getCheckerName();
+ Dst.insert(checkDst);
+#ifdef NDEBUG
+ break; // on release don't check that no other checker also evals.
+#endif
+ }
+ }
+
+ // If none of the checkers evaluated the call, ask ExprEngine to handle it.
+ if (!evaluatorChecker) {
+ NodeBuilder B(Pred, Dst, Eng.getBuilderContext());
+ Eng.defaultEvalCall(B, Pred, Call, CallOpts);
+ }
+ }
+}
+
+/// Run checkers for the entire Translation Unit.
+void CheckerManager::runCheckersOnEndOfTranslationUnit(
+ const TranslationUnitDecl *TU,
+ AnalysisManager &mgr,
+ BugReporter &BR) {
+ for (const auto &EndOfTranslationUnitChecker : EndOfTranslationUnitCheckers)
+ EndOfTranslationUnitChecker(TU, mgr, BR);
+}
+
+void CheckerManager::runCheckersForPrintStateJson(raw_ostream &Out,
+ ProgramStateRef State,
+ const char *NL,
+ unsigned int Space,
+ bool IsDot) const {
+ Indent(Out, Space, IsDot) << "\"checker_messages\": ";
+
+ // Create a temporary stream to see whether we have any message.
+ SmallString<1024> TempBuf;
+ llvm::raw_svector_ostream TempOut(TempBuf);
+ unsigned int InnerSpace = Space + 2;
+
+ // Create the new-line in JSON with enough space.
+ SmallString<128> NewLine;
+ llvm::raw_svector_ostream NLOut(NewLine);
+ NLOut << "\", " << NL; // Inject the ending and a new line
+ Indent(NLOut, InnerSpace, IsDot) << "\""; // then begin the next message.
+
+ ++Space;
+ bool HasMessage = false;
+
+ // Store the last CheckerTag.
+ const void *LastCT = nullptr;
+ for (const auto &CT : CheckerTags) {
+ // See whether the current checker has a message.
+ CT.second->printState(TempOut, State, /*NL=*/NewLine.c_str(), /*Sep=*/"");
+
+ if (TempBuf.empty())
+ continue;
+
+ if (!HasMessage) {
+ Out << '[' << NL;
+ HasMessage = true;
+ }
+
+ LastCT = &CT;
+ TempBuf.clear();
+ }
+
+ for (const auto &CT : CheckerTags) {
+ // See whether the current checker has a message.
+ CT.second->printState(TempOut, State, /*NL=*/NewLine.c_str(), /*Sep=*/"");
+
+ if (TempBuf.empty())
+ continue;
+
+ Indent(Out, Space, IsDot)
+ << "{ \"checker\": \"" << CT.second->getCheckerName().getName()
+ << "\", \"messages\": [" << NL;
+ Indent(Out, InnerSpace, IsDot)
+ << '\"' << TempBuf.str().trim() << '\"' << NL;
+ Indent(Out, Space, IsDot) << "]}";
+
+ if (&CT != LastCT)
+ Out << ',';
+ Out << NL;
+
+ TempBuf.clear();
+ }
+
+ // It is the last element of the 'program_state' so do not add a comma.
+ if (HasMessage)
+ Indent(Out, --Space, IsDot) << "]";
+ else
+ Out << "null";
+
+ Out << NL;
+}
+
+//===----------------------------------------------------------------------===//
+// Internal registration functions for AST traversing.
+//===----------------------------------------------------------------------===//
+
+void CheckerManager::_registerForDecl(CheckDeclFunc checkfn,
+ HandlesDeclFunc isForDeclFn) {
+ DeclCheckerInfo info = { checkfn, isForDeclFn };
+ DeclCheckers.push_back(info);
+}
+
+void CheckerManager::_registerForBody(CheckDeclFunc checkfn) {
+ BodyCheckers.push_back(checkfn);
+}
+
+//===----------------------------------------------------------------------===//
+// Internal registration functions for path-sensitive checking.
+//===----------------------------------------------------------------------===//
+
+void CheckerManager::_registerForPreStmt(CheckStmtFunc checkfn,
+ HandlesStmtFunc isForStmtFn) {
+ StmtCheckerInfo info = { checkfn, isForStmtFn, /*IsPreVisit*/true };
+ StmtCheckers.push_back(info);
+}
+
+void CheckerManager::_registerForPostStmt(CheckStmtFunc checkfn,
+ HandlesStmtFunc isForStmtFn) {
+ StmtCheckerInfo info = { checkfn, isForStmtFn, /*IsPreVisit*/false };
+ StmtCheckers.push_back(info);
+}
+
+void CheckerManager::_registerForPreObjCMessage(CheckObjCMessageFunc checkfn) {
+ PreObjCMessageCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForObjCMessageNil(CheckObjCMessageFunc checkfn) {
+ ObjCMessageNilCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForPostObjCMessage(CheckObjCMessageFunc checkfn) {
+ PostObjCMessageCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForPreCall(CheckCallFunc checkfn) {
+ PreCallCheckers.push_back(checkfn);
+}
+void CheckerManager::_registerForPostCall(CheckCallFunc checkfn) {
+ PostCallCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForLocation(CheckLocationFunc checkfn) {
+ LocationCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForBind(CheckBindFunc checkfn) {
+ BindCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForEndAnalysis(CheckEndAnalysisFunc checkfn) {
+ EndAnalysisCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForBeginFunction(CheckBeginFunctionFunc checkfn) {
+ BeginFunctionCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForEndFunction(CheckEndFunctionFunc checkfn) {
+ EndFunctionCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForBranchCondition(
+ CheckBranchConditionFunc checkfn) {
+ BranchConditionCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForNewAllocator(CheckNewAllocatorFunc checkfn) {
+ NewAllocatorCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForLiveSymbols(CheckLiveSymbolsFunc checkfn) {
+ LiveSymbolsCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForDeadSymbols(CheckDeadSymbolsFunc checkfn) {
+ DeadSymbolsCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForRegionChanges(CheckRegionChangesFunc checkfn) {
+ RegionChangesCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForPointerEscape(CheckPointerEscapeFunc checkfn){
+ PointerEscapeCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForConstPointerEscape(
+ CheckPointerEscapeFunc checkfn) {
+ PointerEscapeCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForEvalAssume(EvalAssumeFunc checkfn) {
+ EvalAssumeCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForEvalCall(EvalCallFunc checkfn) {
+ EvalCallCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForEndOfTranslationUnit(
+ CheckEndOfTranslationUnit checkfn) {
+ EndOfTranslationUnitCheckers.push_back(checkfn);
+}
+
+//===----------------------------------------------------------------------===//
+// Implementation details.
+//===----------------------------------------------------------------------===//
+
+const CheckerManager::CachedStmtCheckers &
+CheckerManager::getCachedStmtCheckersFor(const Stmt *S, bool isPreVisit) {
+ assert(S);
+
+ unsigned Key = (S->getStmtClass() << 1) | unsigned(isPreVisit);
+ CachedStmtCheckersMapTy::iterator CCI = CachedStmtCheckersMap.find(Key);
+ if (CCI != CachedStmtCheckersMap.end())
+ return CCI->second;
+
+ // Find the checkers that should run for this Stmt and cache them.
+ CachedStmtCheckers &Checkers = CachedStmtCheckersMap[Key];
+ for (const auto &Info : StmtCheckers)
+ if (Info.IsPreVisit == isPreVisit && Info.IsForStmtFn(S))
+ Checkers.push_back(Info.CheckFn);
+ return Checkers;
+}