diff options
Diffstat (limited to 'contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp')
-rw-r--r-- | contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp | 647 |
1 files changed, 647 insertions, 0 deletions
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp new file mode 100644 index 000000000000..6ee7bd9252b3 --- /dev/null +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp @@ -0,0 +1,647 @@ +// SmartPtrModeling.cpp - Model behavior of C++ smart pointers - C++ ------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines a checker that models various aspects of +// C++ smart pointer behavior. +// +//===----------------------------------------------------------------------===// + +#include "Move.h" +#include "SmartPtr.h" + +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclarationName.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/Type.h" +#include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include <string> + +using namespace clang; +using namespace ento; + +namespace { +class SmartPtrModeling + : public Checker<eval::Call, check::DeadSymbols, check::RegionChanges, + check::LiveSymbols> { + + bool isBoolConversionMethod(const CallEvent &Call) const; + +public: + // Whether the checker should model for null dereferences of smart pointers. + DefaultBool ModelSmartPtrDereference; + bool evalCall(const CallEvent &Call, CheckerContext &C) const; + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; + ProgramStateRef + checkRegionChanges(ProgramStateRef State, + const InvalidatedSymbols *Invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, + const LocationContext *LCtx, const CallEvent *Call) const; + void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, + const char *Sep) const override; + void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const; + +private: + void handleReset(const CallEvent &Call, CheckerContext &C) const; + void handleRelease(const CallEvent &Call, CheckerContext &C) const; + void handleSwap(const CallEvent &Call, CheckerContext &C) const; + void handleGet(const CallEvent &Call, CheckerContext &C) const; + bool handleAssignOp(const CallEvent &Call, CheckerContext &C) const; + bool handleMoveCtr(const CallEvent &Call, CheckerContext &C, + const MemRegion *ThisRegion) const; + bool updateMovedSmartPointers(CheckerContext &C, const MemRegion *ThisRegion, + const MemRegion *OtherSmartPtrRegion) const; + void handleBoolConversion(const CallEvent &Call, CheckerContext &C) const; + + using SmartPtrMethodHandlerFn = + void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const; + CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{ + {{"reset"}, &SmartPtrModeling::handleReset}, + {{"release"}, &SmartPtrModeling::handleRelease}, + {{"swap", 1}, &SmartPtrModeling::handleSwap}, + {{"get"}, &SmartPtrModeling::handleGet}}; +}; +} // end of anonymous namespace + +REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, SVal) + +// Define the inter-checker API. +namespace clang { +namespace ento { +namespace smartptr { +bool isStdSmartPtrCall(const CallEvent &Call) { + const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl()); + if (!MethodDecl || !MethodDecl->getParent()) + return false; + + const auto *RecordDecl = MethodDecl->getParent(); + if (!RecordDecl || !RecordDecl->getDeclContext()->isStdNamespace()) + return false; + + if (RecordDecl->getDeclName().isIdentifier()) { + StringRef Name = RecordDecl->getName(); + return Name == "shared_ptr" || Name == "unique_ptr" || Name == "weak_ptr"; + } + return false; +} + +bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) { + const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion); + return InnerPointVal && + !State->assume(InnerPointVal->castAs<DefinedOrUnknownSVal>(), true); +} +} // namespace smartptr +} // namespace ento +} // namespace clang + +// If a region is removed all of the subregions need to be removed too. +static TrackedRegionMapTy +removeTrackedSubregions(TrackedRegionMapTy RegionMap, + TrackedRegionMapTy::Factory &RegionMapFactory, + const MemRegion *Region) { + if (!Region) + return RegionMap; + for (const auto &E : RegionMap) { + if (E.first->isSubRegionOf(Region)) + RegionMap = RegionMapFactory.remove(RegionMap, E.first); + } + return RegionMap; +} + +static ProgramStateRef updateSwappedRegion(ProgramStateRef State, + const MemRegion *Region, + const SVal *RegionInnerPointerVal) { + if (RegionInnerPointerVal) { + State = State->set<TrackedRegionMap>(Region, *RegionInnerPointerVal); + } else { + State = State->remove<TrackedRegionMap>(Region); + } + return State; +} + +// Helper method to get the inner pointer type of specialized smart pointer +// Returns empty type if not found valid inner pointer type. +static QualType getInnerPointerType(const CallEvent &Call, CheckerContext &C) { + const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl()); + if (!MethodDecl || !MethodDecl->getParent()) + return {}; + + const auto *RecordDecl = MethodDecl->getParent(); + if (!RecordDecl || !RecordDecl->isInStdNamespace()) + return {}; + + const auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl); + if (!TSD) + return {}; + + auto TemplateArgs = TSD->getTemplateArgs().asArray(); + if (TemplateArgs.size() == 0) + return {}; + auto InnerValueType = TemplateArgs[0].getAsType(); + return C.getASTContext().getPointerType(InnerValueType.getCanonicalType()); +} + +// Helper method to pretty print region and avoid extra spacing. +static void checkAndPrettyPrintRegion(llvm::raw_ostream &OS, + const MemRegion *Region) { + if (Region->canPrintPretty()) { + OS << " "; + Region->printPretty(OS); + } +} + +bool SmartPtrModeling::isBoolConversionMethod(const CallEvent &Call) const { + // TODO: Update CallDescription to support anonymous calls? + // TODO: Handle other methods, such as .get() or .release(). + // But once we do, we'd need a visitor to explain null dereferences + // that are found via such modeling. + const auto *CD = dyn_cast_or_null<CXXConversionDecl>(Call.getDecl()); + return CD && CD->getConversionType()->isBooleanType(); +} + +bool SmartPtrModeling::evalCall(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + if (!smartptr::isStdSmartPtrCall(Call)) + return false; + + if (isBoolConversionMethod(Call)) { + const MemRegion *ThisR = + cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion(); + + if (ModelSmartPtrDereference) { + // The check for the region is moved is duplicated in handleBoolOperation + // method. + // FIXME: Once we model std::move for smart pointers clean up this and use + // that modeling. + handleBoolConversion(Call, C); + return true; + } else { + if (!move::isMovedFrom(State, ThisR)) { + // TODO: Model this case as well. At least, avoid invalidation of + // globals. + return false; + } + + // TODO: Add a note to bug reports describing this decision. + C.addTransition(State->BindExpr( + Call.getOriginExpr(), C.getLocationContext(), + C.getSValBuilder().makeZeroVal(Call.getResultType()))); + + return true; + } + } + + if (!ModelSmartPtrDereference) + return false; + + if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) { + if (CC->getDecl()->isCopyConstructor()) + return false; + + const MemRegion *ThisRegion = CC->getCXXThisVal().getAsRegion(); + if (!ThisRegion) + return false; + + if (CC->getDecl()->isMoveConstructor()) + return handleMoveCtr(Call, C, ThisRegion); + + if (Call.getNumArgs() == 0) { + auto NullVal = C.getSValBuilder().makeNull(); + State = State->set<TrackedRegionMap>(ThisRegion, NullVal); + + C.addTransition( + State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR, + llvm::raw_ostream &OS) { + if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || + !BR.isInteresting(ThisRegion)) + return; + OS << "Default constructed smart pointer"; + checkAndPrettyPrintRegion(OS, ThisRegion); + OS << " is null"; + })); + } else { + const auto *TrackingExpr = Call.getArgExpr(0); + assert(TrackingExpr->getType()->isPointerType() && + "Adding a non pointer value to TrackedRegionMap"); + auto ArgVal = Call.getArgSVal(0); + State = State->set<TrackedRegionMap>(ThisRegion, ArgVal); + + C.addTransition(State, C.getNoteTag([ThisRegion, TrackingExpr, + ArgVal](PathSensitiveBugReport &BR, + llvm::raw_ostream &OS) { + if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || + !BR.isInteresting(ThisRegion)) + return; + bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR); + OS << "Smart pointer"; + checkAndPrettyPrintRegion(OS, ThisRegion); + if (ArgVal.isZeroConstant()) + OS << " is constructed using a null value"; + else + OS << " is constructed"; + })); + } + return true; + } + + if (handleAssignOp(Call, C)) + return true; + + const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call); + if (!Handler) + return false; + (this->**Handler)(Call, C); + + return C.isDifferent(); +} + +void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + // Clean up dead regions from the region map. + TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>(); + for (auto E : TrackedRegions) { + const MemRegion *Region = E.first; + bool IsRegDead = !SymReaper.isLiveRegion(Region); + + if (IsRegDead) + State = State->remove<TrackedRegionMap>(Region); + } + C.addTransition(State); +} + +void SmartPtrModeling::printState(raw_ostream &Out, ProgramStateRef State, + const char *NL, const char *Sep) const { + TrackedRegionMapTy RS = State->get<TrackedRegionMap>(); + + if (!RS.isEmpty()) { + Out << Sep << "Smart ptr regions :" << NL; + for (auto I : RS) { + I.first->dumpToStream(Out); + if (smartptr::isNullSmartPtr(State, I.first)) + Out << ": Null"; + else + Out << ": Non Null"; + Out << NL; + } + } +} + +ProgramStateRef SmartPtrModeling::checkRegionChanges( + ProgramStateRef State, const InvalidatedSymbols *Invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx, + const CallEvent *Call) const { + TrackedRegionMapTy RegionMap = State->get<TrackedRegionMap>(); + TrackedRegionMapTy::Factory &RegionMapFactory = + State->get_context<TrackedRegionMap>(); + for (const auto *Region : Regions) + RegionMap = removeTrackedSubregions(RegionMap, RegionMapFactory, + Region->getBaseRegion()); + return State->set<TrackedRegionMap>(RegionMap); +} + +void SmartPtrModeling::checkLiveSymbols(ProgramStateRef State, + SymbolReaper &SR) const { + // Marking tracked symbols alive + TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>(); + for (auto I = TrackedRegions.begin(), E = TrackedRegions.end(); I != E; ++I) { + SVal Val = I->second; + for (auto si = Val.symbol_begin(), se = Val.symbol_end(); si != se; ++si) { + SR.markLive(*si); + } + } +} + +void SmartPtrModeling::handleReset(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + const auto *IC = dyn_cast<CXXInstanceCall>(&Call); + if (!IC) + return; + + const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); + if (!ThisRegion) + return; + + assert(Call.getArgExpr(0)->getType()->isPointerType() && + "Adding a non pointer value to TrackedRegionMap"); + State = State->set<TrackedRegionMap>(ThisRegion, Call.getArgSVal(0)); + const auto *TrackingExpr = Call.getArgExpr(0); + C.addTransition( + State, C.getNoteTag([ThisRegion, TrackingExpr](PathSensitiveBugReport &BR, + llvm::raw_ostream &OS) { + if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || + !BR.isInteresting(ThisRegion)) + return; + bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR); + OS << "Smart pointer"; + checkAndPrettyPrintRegion(OS, ThisRegion); + OS << " reset using a null value"; + })); + // TODO: Make sure to ivalidate the region in the Store if we don't have + // time to model all methods. +} + +void SmartPtrModeling::handleRelease(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + const auto *IC = dyn_cast<CXXInstanceCall>(&Call); + if (!IC) + return; + + const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); + if (!ThisRegion) + return; + + const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion); + + if (InnerPointVal) { + State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), + *InnerPointVal); + } + + auto ValueToUpdate = C.getSValBuilder().makeNull(); + State = State->set<TrackedRegionMap>(ThisRegion, ValueToUpdate); + + C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR, + llvm::raw_ostream &OS) { + if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || + !BR.isInteresting(ThisRegion)) + return; + + OS << "Smart pointer"; + checkAndPrettyPrintRegion(OS, ThisRegion); + OS << " is released and set to null"; + })); + // TODO: Add support to enable MallocChecker to start tracking the raw + // pointer. +} + +void SmartPtrModeling::handleSwap(const CallEvent &Call, + CheckerContext &C) const { + // To model unique_ptr::swap() method. + const auto *IC = dyn_cast<CXXInstanceCall>(&Call); + if (!IC) + return; + + const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); + if (!ThisRegion) + return; + + const auto *ArgRegion = Call.getArgSVal(0).getAsRegion(); + if (!ArgRegion) + return; + + auto State = C.getState(); + const auto *ThisRegionInnerPointerVal = + State->get<TrackedRegionMap>(ThisRegion); + const auto *ArgRegionInnerPointerVal = + State->get<TrackedRegionMap>(ArgRegion); + + // Swap the tracked region values. + State = updateSwappedRegion(State, ThisRegion, ArgRegionInnerPointerVal); + State = updateSwappedRegion(State, ArgRegion, ThisRegionInnerPointerVal); + + C.addTransition( + State, C.getNoteTag([ThisRegion, ArgRegion](PathSensitiveBugReport &BR, + llvm::raw_ostream &OS) { + if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || + !BR.isInteresting(ThisRegion)) + return; + BR.markInteresting(ArgRegion); + OS << "Swapped null smart pointer"; + checkAndPrettyPrintRegion(OS, ArgRegion); + OS << " with smart pointer"; + checkAndPrettyPrintRegion(OS, ThisRegion); + })); +} + +void SmartPtrModeling::handleGet(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + const auto *IC = dyn_cast<CXXInstanceCall>(&Call); + if (!IC) + return; + + const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); + if (!ThisRegion) + return; + + SVal InnerPointerVal; + if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) { + InnerPointerVal = *InnerValPtr; + } else { + const auto *CallExpr = Call.getOriginExpr(); + InnerPointerVal = C.getSValBuilder().conjureSymbolVal( + CallExpr, C.getLocationContext(), Call.getResultType(), C.blockCount()); + State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal); + } + + State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), + InnerPointerVal); + // TODO: Add NoteTag, for how the raw pointer got using 'get' method. + C.addTransition(State); +} + +bool SmartPtrModeling::handleAssignOp(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + const auto *OC = dyn_cast<CXXMemberOperatorCall>(&Call); + if (!OC) + return false; + OverloadedOperatorKind OOK = OC->getOverloadedOperator(); + if (OOK != OO_Equal) + return false; + const MemRegion *ThisRegion = OC->getCXXThisVal().getAsRegion(); + if (!ThisRegion) + return false; + + const MemRegion *OtherSmartPtrRegion = OC->getArgSVal(0).getAsRegion(); + // In case of 'nullptr' or '0' assigned + if (!OtherSmartPtrRegion) { + bool AssignedNull = Call.getArgSVal(0).isZeroConstant(); + if (!AssignedNull) + return false; + auto NullVal = C.getSValBuilder().makeNull(); + State = State->set<TrackedRegionMap>(ThisRegion, NullVal); + C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR, + llvm::raw_ostream &OS) { + if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || + !BR.isInteresting(ThisRegion)) + return; + OS << "Smart pointer"; + checkAndPrettyPrintRegion(OS, ThisRegion); + OS << " is assigned to null"; + })); + return true; + } + + return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion); +} + +bool SmartPtrModeling::handleMoveCtr(const CallEvent &Call, CheckerContext &C, + const MemRegion *ThisRegion) const { + const auto *OtherSmartPtrRegion = Call.getArgSVal(0).getAsRegion(); + if (!OtherSmartPtrRegion) + return false; + + return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion); +} + +bool SmartPtrModeling::updateMovedSmartPointers( + CheckerContext &C, const MemRegion *ThisRegion, + const MemRegion *OtherSmartPtrRegion) const { + ProgramStateRef State = C.getState(); + const auto *OtherInnerPtr = State->get<TrackedRegionMap>(OtherSmartPtrRegion); + if (OtherInnerPtr) { + State = State->set<TrackedRegionMap>(ThisRegion, *OtherInnerPtr); + auto NullVal = C.getSValBuilder().makeNull(); + State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal); + bool IsArgValNull = OtherInnerPtr->isZeroConstant(); + + C.addTransition( + State, + C.getNoteTag([ThisRegion, OtherSmartPtrRegion, IsArgValNull]( + PathSensitiveBugReport &BR, llvm::raw_ostream &OS) { + if (&BR.getBugType() != smartptr::getNullDereferenceBugType()) + return; + if (BR.isInteresting(OtherSmartPtrRegion)) { + OS << "Smart pointer"; + checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion); + OS << " is null after being moved to"; + checkAndPrettyPrintRegion(OS, ThisRegion); + } + if (BR.isInteresting(ThisRegion) && IsArgValNull) { + OS << "A null pointer value is moved to"; + checkAndPrettyPrintRegion(OS, ThisRegion); + BR.markInteresting(OtherSmartPtrRegion); + } + })); + return true; + } else { + // In case we dont know anything about value we are moving from + // remove the entry from map for which smart pointer got moved to. + auto NullVal = C.getSValBuilder().makeNull(); + State = State->remove<TrackedRegionMap>(ThisRegion); + State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal); + C.addTransition(State, C.getNoteTag([OtherSmartPtrRegion, + ThisRegion](PathSensitiveBugReport &BR, + llvm::raw_ostream &OS) { + if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || + !BR.isInteresting(OtherSmartPtrRegion)) + return; + OS << "Smart pointer"; + checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion); + OS << " is null after; previous value moved to"; + checkAndPrettyPrintRegion(OS, ThisRegion); + })); + return true; + } + return false; +} + +void SmartPtrModeling::handleBoolConversion(const CallEvent &Call, + CheckerContext &C) const { + // To model unique_ptr::operator bool + ProgramStateRef State = C.getState(); + const Expr *CallExpr = Call.getOriginExpr(); + const MemRegion *ThisRegion = + cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion(); + + SVal InnerPointerVal; + if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) { + InnerPointerVal = *InnerValPtr; + } else { + // In case of inner pointer SVal is not available we create + // conjureSymbolVal for inner pointer value. + auto InnerPointerType = getInnerPointerType(Call, C); + if (InnerPointerType.isNull()) + return; + + const LocationContext *LC = C.getLocationContext(); + InnerPointerVal = C.getSValBuilder().conjureSymbolVal( + CallExpr, LC, InnerPointerType, C.blockCount()); + State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal); + } + + if (State->isNull(InnerPointerVal).isConstrainedTrue()) { + State = State->BindExpr(CallExpr, C.getLocationContext(), + C.getSValBuilder().makeTruthVal(false)); + + C.addTransition(State); + return; + } else if (State->isNonNull(InnerPointerVal).isConstrainedTrue()) { + State = State->BindExpr(CallExpr, C.getLocationContext(), + C.getSValBuilder().makeTruthVal(true)); + + C.addTransition(State); + return; + } else if (move::isMovedFrom(State, ThisRegion)) { + C.addTransition( + State->BindExpr(CallExpr, C.getLocationContext(), + C.getSValBuilder().makeZeroVal(Call.getResultType()))); + return; + } else { + ProgramStateRef NotNullState, NullState; + std::tie(NotNullState, NullState) = + State->assume(InnerPointerVal.castAs<DefinedOrUnknownSVal>()); + + auto NullVal = C.getSValBuilder().makeNull(); + // Explicitly tracking the region as null. + NullState = NullState->set<TrackedRegionMap>(ThisRegion, NullVal); + + NullState = NullState->BindExpr(CallExpr, C.getLocationContext(), + C.getSValBuilder().makeTruthVal(false)); + C.addTransition(NullState, C.getNoteTag( + [ThisRegion](PathSensitiveBugReport &BR, + llvm::raw_ostream &OS) { + OS << "Assuming smart pointer"; + checkAndPrettyPrintRegion(OS, ThisRegion); + OS << " is null"; + }, + /*IsPrunable=*/true)); + NotNullState = + NotNullState->BindExpr(CallExpr, C.getLocationContext(), + C.getSValBuilder().makeTruthVal(true)); + C.addTransition( + NotNullState, + C.getNoteTag( + [ThisRegion](PathSensitiveBugReport &BR, llvm::raw_ostream &OS) { + OS << "Assuming smart pointer"; + checkAndPrettyPrintRegion(OS, ThisRegion); + OS << " is non-null"; + }, + /*IsPrunable=*/true)); + return; + } +} + +void ento::registerSmartPtrModeling(CheckerManager &Mgr) { + auto *Checker = Mgr.registerChecker<SmartPtrModeling>(); + Checker->ModelSmartPtrDereference = + Mgr.getAnalyzerOptions().getCheckerBooleanOption( + Checker, "ModelSmartPtrDereference"); +} + +bool ento::shouldRegisterSmartPtrModeling(const CheckerManager &mgr) { + const LangOptions &LO = mgr.getLangOpts(); + return LO.CPlusPlus; +} |