10b57cec5SDimitry Andric // SmartPtrModeling.cpp - Model behavior of C++ smart pointers - C++ ------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file defines a checker that models various aspects of
100b57cec5SDimitry Andric // C++ smart pointer behavior.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric
140b57cec5SDimitry Andric #include "Move.h"
155ffd83dbSDimitry Andric #include "SmartPtr.h"
160b57cec5SDimitry Andric
175ffd83dbSDimitry Andric #include "clang/AST/DeclCXX.h"
18e8d8bef9SDimitry Andric #include "clang/AST/DeclarationName.h"
190b57cec5SDimitry Andric #include "clang/AST/ExprCXX.h"
205ffd83dbSDimitry Andric #include "clang/AST/Type.h"
21e8d8bef9SDimitry Andric #include "clang/Basic/LLVM.h"
220b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
230b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
240b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
250b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
26349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
270b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
280b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
29fe6060f1SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
30e8d8bef9SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
315ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
325ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
33e8d8bef9SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
3406c3fb27SDimitry Andric #include "llvm/ADT/STLExtras.h"
35fe6060f1SDimitry Andric #include "llvm/Support/ErrorHandling.h"
36bdd1243dSDimitry Andric #include <optional>
37e8d8bef9SDimitry Andric #include <string>
380b57cec5SDimitry Andric
390b57cec5SDimitry Andric using namespace clang;
400b57cec5SDimitry Andric using namespace ento;
410b57cec5SDimitry Andric
420b57cec5SDimitry Andric namespace {
43fe6060f1SDimitry Andric
44e8d8bef9SDimitry Andric class SmartPtrModeling
45e8d8bef9SDimitry Andric : public Checker<eval::Call, check::DeadSymbols, check::RegionChanges,
46e8d8bef9SDimitry Andric check::LiveSymbols> {
475ffd83dbSDimitry Andric
48e8d8bef9SDimitry Andric bool isBoolConversionMethod(const CallEvent &Call) const;
490b57cec5SDimitry Andric
500b57cec5SDimitry Andric public:
515ffd83dbSDimitry Andric // Whether the checker should model for null dereferences of smart pointers.
5281ad6265SDimitry Andric bool ModelSmartPtrDereference = false;
530b57cec5SDimitry Andric bool evalCall(const CallEvent &Call, CheckerContext &C) const;
545ffd83dbSDimitry Andric void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
55e8d8bef9SDimitry Andric ProgramStateRef
56e8d8bef9SDimitry Andric checkRegionChanges(ProgramStateRef State,
57e8d8bef9SDimitry Andric const InvalidatedSymbols *Invalidated,
58e8d8bef9SDimitry Andric ArrayRef<const MemRegion *> ExplicitRegions,
59e8d8bef9SDimitry Andric ArrayRef<const MemRegion *> Regions,
60e8d8bef9SDimitry Andric const LocationContext *LCtx, const CallEvent *Call) const;
61e8d8bef9SDimitry Andric void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
62e8d8bef9SDimitry Andric const char *Sep) const override;
63e8d8bef9SDimitry Andric void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
645ffd83dbSDimitry Andric
655ffd83dbSDimitry Andric private:
665ffd83dbSDimitry Andric void handleReset(const CallEvent &Call, CheckerContext &C) const;
675ffd83dbSDimitry Andric void handleRelease(const CallEvent &Call, CheckerContext &C) const;
68fe6060f1SDimitry Andric void handleSwapMethod(const CallEvent &Call, CheckerContext &C) const;
69e8d8bef9SDimitry Andric void handleGet(const CallEvent &Call, CheckerContext &C) const;
70e8d8bef9SDimitry Andric bool handleAssignOp(const CallEvent &Call, CheckerContext &C) const;
71e8d8bef9SDimitry Andric bool handleMoveCtr(const CallEvent &Call, CheckerContext &C,
72e8d8bef9SDimitry Andric const MemRegion *ThisRegion) const;
73e8d8bef9SDimitry Andric bool updateMovedSmartPointers(CheckerContext &C, const MemRegion *ThisRegion,
7481ad6265SDimitry Andric const MemRegion *OtherSmartPtrRegion,
7581ad6265SDimitry Andric const CallEvent &Call) const;
76e8d8bef9SDimitry Andric void handleBoolConversion(const CallEvent &Call, CheckerContext &C) const;
77fe6060f1SDimitry Andric bool handleComparisionOp(const CallEvent &Call, CheckerContext &C) const;
78fe6060f1SDimitry Andric bool handleOstreamOperator(const CallEvent &Call, CheckerContext &C) const;
79fe6060f1SDimitry Andric bool handleSwap(ProgramStateRef State, SVal First, SVal Second,
80fe6060f1SDimitry Andric CheckerContext &C) const;
81fe6060f1SDimitry Andric std::pair<SVal, ProgramStateRef>
82fe6060f1SDimitry Andric retrieveOrConjureInnerPtrVal(ProgramStateRef State,
83fe6060f1SDimitry Andric const MemRegion *ThisRegion, const Expr *E,
84fe6060f1SDimitry Andric QualType Type, CheckerContext &C) const;
855ffd83dbSDimitry Andric
865ffd83dbSDimitry Andric using SmartPtrMethodHandlerFn =
875ffd83dbSDimitry Andric void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const;
885ffd83dbSDimitry Andric CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{
89*0fca6ea1SDimitry Andric {{CDM::CXXMethod, {"reset"}}, &SmartPtrModeling::handleReset},
90*0fca6ea1SDimitry Andric {{CDM::CXXMethod, {"release"}}, &SmartPtrModeling::handleRelease},
91*0fca6ea1SDimitry Andric {{CDM::CXXMethod, {"swap"}, 1}, &SmartPtrModeling::handleSwapMethod},
92*0fca6ea1SDimitry Andric {{CDM::CXXMethod, {"get"}}, &SmartPtrModeling::handleGet}};
93*0fca6ea1SDimitry Andric const CallDescription StdSwapCall{CDM::SimpleFunc, {"std", "swap"}, 2};
94*0fca6ea1SDimitry Andric const CallDescriptionSet MakeUniqueVariants{
95*0fca6ea1SDimitry Andric {CDM::SimpleFunc, {"std", "make_unique"}},
96*0fca6ea1SDimitry Andric {CDM::SimpleFunc, {"std", "make_unique_for_overwrite"}}};
970b57cec5SDimitry Andric };
980b57cec5SDimitry Andric } // end of anonymous namespace
990b57cec5SDimitry Andric
REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap,const MemRegion *,SVal)1005ffd83dbSDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, SVal)
1015ffd83dbSDimitry Andric
102fe6060f1SDimitry Andric // Checks if RD has name in Names and is in std namespace
103fe6060f1SDimitry Andric static bool hasStdClassWithName(const CXXRecordDecl *RD,
104fe6060f1SDimitry Andric ArrayRef<llvm::StringLiteral> Names) {
105fe6060f1SDimitry Andric if (!RD || !RD->getDeclContext()->isStdNamespace())
106fe6060f1SDimitry Andric return false;
107349cc55cSDimitry Andric if (RD->getDeclName().isIdentifier())
108349cc55cSDimitry Andric return llvm::is_contained(Names, RD->getName());
109fe6060f1SDimitry Andric return false;
110fe6060f1SDimitry Andric }
111fe6060f1SDimitry Andric
112fe6060f1SDimitry Andric constexpr llvm::StringLiteral STD_PTR_NAMES[] = {"shared_ptr", "unique_ptr",
113fe6060f1SDimitry Andric "weak_ptr"};
114fe6060f1SDimitry Andric
isStdSmartPtr(const CXXRecordDecl * RD)115fe6060f1SDimitry Andric static bool isStdSmartPtr(const CXXRecordDecl *RD) {
116fe6060f1SDimitry Andric return hasStdClassWithName(RD, STD_PTR_NAMES);
117fe6060f1SDimitry Andric }
118fe6060f1SDimitry Andric
isStdSmartPtr(const Expr * E)119fe6060f1SDimitry Andric static bool isStdSmartPtr(const Expr *E) {
120fe6060f1SDimitry Andric return isStdSmartPtr(E->getType()->getAsCXXRecordDecl());
121fe6060f1SDimitry Andric }
122fe6060f1SDimitry Andric
1235ffd83dbSDimitry Andric // Define the inter-checker API.
1245ffd83dbSDimitry Andric namespace clang {
1255ffd83dbSDimitry Andric namespace ento {
1265ffd83dbSDimitry Andric namespace smartptr {
isStdSmartPtrCall(const CallEvent & Call)1275ffd83dbSDimitry Andric bool isStdSmartPtrCall(const CallEvent &Call) {
1285ffd83dbSDimitry Andric const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
1295ffd83dbSDimitry Andric if (!MethodDecl || !MethodDecl->getParent())
1305ffd83dbSDimitry Andric return false;
131fe6060f1SDimitry Andric return isStdSmartPtr(MethodDecl->getParent());
132fe6060f1SDimitry Andric }
1335ffd83dbSDimitry Andric
isStdSmartPtr(const CXXRecordDecl * RD)134fe6060f1SDimitry Andric bool isStdSmartPtr(const CXXRecordDecl *RD) {
135fe6060f1SDimitry Andric if (!RD || !RD->getDeclContext()->isStdNamespace())
1365ffd83dbSDimitry Andric return false;
1375ffd83dbSDimitry Andric
138fe6060f1SDimitry Andric if (RD->getDeclName().isIdentifier()) {
139fe6060f1SDimitry Andric StringRef Name = RD->getName();
1405ffd83dbSDimitry Andric return Name == "shared_ptr" || Name == "unique_ptr" || Name == "weak_ptr";
1415ffd83dbSDimitry Andric }
1425ffd83dbSDimitry Andric return false;
1435ffd83dbSDimitry Andric }
1445ffd83dbSDimitry Andric
isStdSmartPtr(const Expr * E)145fe6060f1SDimitry Andric bool isStdSmartPtr(const Expr *E) {
146fe6060f1SDimitry Andric return isStdSmartPtr(E->getType()->getAsCXXRecordDecl());
147fe6060f1SDimitry Andric }
148fe6060f1SDimitry Andric
isNullSmartPtr(const ProgramStateRef State,const MemRegion * ThisRegion)1495ffd83dbSDimitry Andric bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) {
1505ffd83dbSDimitry Andric const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
151e8d8bef9SDimitry Andric return InnerPointVal &&
152e8d8bef9SDimitry Andric !State->assume(InnerPointVal->castAs<DefinedOrUnknownSVal>(), true);
1535ffd83dbSDimitry Andric }
1545ffd83dbSDimitry Andric } // namespace smartptr
1555ffd83dbSDimitry Andric } // namespace ento
1565ffd83dbSDimitry Andric } // namespace clang
1575ffd83dbSDimitry Andric
158e8d8bef9SDimitry Andric // If a region is removed all of the subregions need to be removed too.
159e8d8bef9SDimitry Andric static TrackedRegionMapTy
removeTrackedSubregions(TrackedRegionMapTy RegionMap,TrackedRegionMapTy::Factory & RegionMapFactory,const MemRegion * Region)160e8d8bef9SDimitry Andric removeTrackedSubregions(TrackedRegionMapTy RegionMap,
161e8d8bef9SDimitry Andric TrackedRegionMapTy::Factory &RegionMapFactory,
162e8d8bef9SDimitry Andric const MemRegion *Region) {
163e8d8bef9SDimitry Andric if (!Region)
164e8d8bef9SDimitry Andric return RegionMap;
165e8d8bef9SDimitry Andric for (const auto &E : RegionMap) {
166e8d8bef9SDimitry Andric if (E.first->isSubRegionOf(Region))
167e8d8bef9SDimitry Andric RegionMap = RegionMapFactory.remove(RegionMap, E.first);
168e8d8bef9SDimitry Andric }
169e8d8bef9SDimitry Andric return RegionMap;
170e8d8bef9SDimitry Andric }
171e8d8bef9SDimitry Andric
updateSwappedRegion(ProgramStateRef State,const MemRegion * Region,const SVal * RegionInnerPointerVal)172e8d8bef9SDimitry Andric static ProgramStateRef updateSwappedRegion(ProgramStateRef State,
173e8d8bef9SDimitry Andric const MemRegion *Region,
174e8d8bef9SDimitry Andric const SVal *RegionInnerPointerVal) {
175e8d8bef9SDimitry Andric if (RegionInnerPointerVal) {
176e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(Region, *RegionInnerPointerVal);
177e8d8bef9SDimitry Andric } else {
178e8d8bef9SDimitry Andric State = State->remove<TrackedRegionMap>(Region);
179e8d8bef9SDimitry Andric }
180e8d8bef9SDimitry Andric return State;
181e8d8bef9SDimitry Andric }
182e8d8bef9SDimitry Andric
getInnerPointerType(CheckerContext C,const CXXRecordDecl * RD)183fe6060f1SDimitry Andric static QualType getInnerPointerType(CheckerContext C, const CXXRecordDecl *RD) {
184fe6060f1SDimitry Andric if (!RD || !RD->isInStdNamespace())
185fe6060f1SDimitry Andric return {};
186fe6060f1SDimitry Andric
187fe6060f1SDimitry Andric const auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RD);
188fe6060f1SDimitry Andric if (!TSD)
189fe6060f1SDimitry Andric return {};
190fe6060f1SDimitry Andric
191fe6060f1SDimitry Andric auto TemplateArgs = TSD->getTemplateArgs().asArray();
192fe6060f1SDimitry Andric if (TemplateArgs.empty())
193fe6060f1SDimitry Andric return {};
194fe6060f1SDimitry Andric auto InnerValueType = TemplateArgs[0].getAsType();
195fe6060f1SDimitry Andric return C.getASTContext().getPointerType(InnerValueType.getCanonicalType());
196fe6060f1SDimitry Andric }
197fe6060f1SDimitry Andric
198fe6060f1SDimitry Andric // This is for use with standalone-functions like std::make_unique,
199fe6060f1SDimitry Andric // std::make_unique_for_overwrite, etc. It reads the template parameter and
200fe6060f1SDimitry Andric // returns the pointer type corresponding to it,
getPointerTypeFromTemplateArg(const CallEvent & Call,CheckerContext & C)201fe6060f1SDimitry Andric static QualType getPointerTypeFromTemplateArg(const CallEvent &Call,
202fe6060f1SDimitry Andric CheckerContext &C) {
203fe6060f1SDimitry Andric const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
2045f757f3fSDimitry Andric if (!FD || !FD->getPrimaryTemplate())
205fe6060f1SDimitry Andric return {};
206fe6060f1SDimitry Andric const auto &TemplateArgs = FD->getTemplateSpecializationArgs()->asArray();
207fe6060f1SDimitry Andric if (TemplateArgs.size() == 0)
208fe6060f1SDimitry Andric return {};
209fe6060f1SDimitry Andric auto ValueType = TemplateArgs[0].getAsType();
210fe6060f1SDimitry Andric return C.getASTContext().getPointerType(ValueType.getCanonicalType());
211fe6060f1SDimitry Andric }
212fe6060f1SDimitry Andric
213e8d8bef9SDimitry Andric // Helper method to get the inner pointer type of specialized smart pointer
214e8d8bef9SDimitry Andric // Returns empty type if not found valid inner pointer type.
getInnerPointerType(const CallEvent & Call,CheckerContext & C)215e8d8bef9SDimitry Andric static QualType getInnerPointerType(const CallEvent &Call, CheckerContext &C) {
216e8d8bef9SDimitry Andric const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
217e8d8bef9SDimitry Andric if (!MethodDecl || !MethodDecl->getParent())
218e8d8bef9SDimitry Andric return {};
219e8d8bef9SDimitry Andric
220e8d8bef9SDimitry Andric const auto *RecordDecl = MethodDecl->getParent();
221fe6060f1SDimitry Andric return getInnerPointerType(C, RecordDecl);
222e8d8bef9SDimitry Andric }
223e8d8bef9SDimitry Andric
224e8d8bef9SDimitry Andric // Helper method to pretty print region and avoid extra spacing.
checkAndPrettyPrintRegion(llvm::raw_ostream & OS,const MemRegion * Region)225e8d8bef9SDimitry Andric static void checkAndPrettyPrintRegion(llvm::raw_ostream &OS,
226e8d8bef9SDimitry Andric const MemRegion *Region) {
227e8d8bef9SDimitry Andric if (Region->canPrintPretty()) {
228e8d8bef9SDimitry Andric OS << " ";
229e8d8bef9SDimitry Andric Region->printPretty(OS);
230e8d8bef9SDimitry Andric }
231e8d8bef9SDimitry Andric }
232e8d8bef9SDimitry Andric
isBoolConversionMethod(const CallEvent & Call) const233e8d8bef9SDimitry Andric bool SmartPtrModeling::isBoolConversionMethod(const CallEvent &Call) const {
2340b57cec5SDimitry Andric // TODO: Update CallDescription to support anonymous calls?
2350b57cec5SDimitry Andric // TODO: Handle other methods, such as .get() or .release().
2360b57cec5SDimitry Andric // But once we do, we'd need a visitor to explain null dereferences
2370b57cec5SDimitry Andric // that are found via such modeling.
2380b57cec5SDimitry Andric const auto *CD = dyn_cast_or_null<CXXConversionDecl>(Call.getDecl());
2390b57cec5SDimitry Andric return CD && CD->getConversionType()->isBooleanType();
2400b57cec5SDimitry Andric }
2410b57cec5SDimitry Andric
242fe6060f1SDimitry Andric constexpr llvm::StringLiteral BASIC_OSTREAM_NAMES[] = {"basic_ostream"};
243fe6060f1SDimitry Andric
isStdBasicOstream(const Expr * E)244fe6060f1SDimitry Andric bool isStdBasicOstream(const Expr *E) {
245fe6060f1SDimitry Andric const auto *RD = E->getType()->getAsCXXRecordDecl();
246fe6060f1SDimitry Andric return hasStdClassWithName(RD, BASIC_OSTREAM_NAMES);
247fe6060f1SDimitry Andric }
248fe6060f1SDimitry Andric
isStdFunctionCall(const CallEvent & Call)249fe6060f1SDimitry Andric static bool isStdFunctionCall(const CallEvent &Call) {
250fe6060f1SDimitry Andric return Call.getDecl() && Call.getDecl()->getDeclContext()->isStdNamespace();
251fe6060f1SDimitry Andric }
252fe6060f1SDimitry Andric
isStdOstreamOperatorCall(const CallEvent & Call)253fe6060f1SDimitry Andric bool isStdOstreamOperatorCall(const CallEvent &Call) {
254fe6060f1SDimitry Andric if (Call.getNumArgs() != 2 || !isStdFunctionCall(Call))
255fe6060f1SDimitry Andric return false;
256fe6060f1SDimitry Andric const auto *FC = dyn_cast<SimpleFunctionCall>(&Call);
257fe6060f1SDimitry Andric if (!FC)
258fe6060f1SDimitry Andric return false;
259fe6060f1SDimitry Andric const FunctionDecl *FD = FC->getDecl();
260fe6060f1SDimitry Andric if (!FD->isOverloadedOperator())
261fe6060f1SDimitry Andric return false;
262fe6060f1SDimitry Andric const OverloadedOperatorKind OOK = FD->getOverloadedOperator();
263fe6060f1SDimitry Andric if (OOK != clang::OO_LessLess)
264fe6060f1SDimitry Andric return false;
265fe6060f1SDimitry Andric return isStdSmartPtr(Call.getArgExpr(1)) &&
266fe6060f1SDimitry Andric isStdBasicOstream(Call.getArgExpr(0));
267fe6060f1SDimitry Andric }
268fe6060f1SDimitry Andric
isPotentiallyComparisionOpCall(const CallEvent & Call)269fe6060f1SDimitry Andric static bool isPotentiallyComparisionOpCall(const CallEvent &Call) {
270fe6060f1SDimitry Andric if (Call.getNumArgs() != 2 || !isStdFunctionCall(Call))
271fe6060f1SDimitry Andric return false;
272fe6060f1SDimitry Andric return smartptr::isStdSmartPtr(Call.getArgExpr(0)) ||
273fe6060f1SDimitry Andric smartptr::isStdSmartPtr(Call.getArgExpr(1));
274fe6060f1SDimitry Andric }
275fe6060f1SDimitry Andric
evalCall(const CallEvent & Call,CheckerContext & C) const2760b57cec5SDimitry Andric bool SmartPtrModeling::evalCall(const CallEvent &Call,
2770b57cec5SDimitry Andric CheckerContext &C) const {
278fe6060f1SDimitry Andric
279e8d8bef9SDimitry Andric ProgramStateRef State = C.getState();
280fe6060f1SDimitry Andric
281fe6060f1SDimitry Andric // If any one of the arg is a unique_ptr, then
282fe6060f1SDimitry Andric // we can try this function
283fe6060f1SDimitry Andric if (ModelSmartPtrDereference && isPotentiallyComparisionOpCall(Call))
284fe6060f1SDimitry Andric if (handleComparisionOp(Call, C))
285fe6060f1SDimitry Andric return true;
286fe6060f1SDimitry Andric
287fe6060f1SDimitry Andric if (ModelSmartPtrDereference && isStdOstreamOperatorCall(Call))
288fe6060f1SDimitry Andric return handleOstreamOperator(Call, C);
289fe6060f1SDimitry Andric
290349cc55cSDimitry Andric if (StdSwapCall.matches(Call)) {
291fe6060f1SDimitry Andric // Check the first arg, if it is of std::unique_ptr type.
292fe6060f1SDimitry Andric assert(Call.getNumArgs() == 2 && "std::swap should have two arguments");
293fe6060f1SDimitry Andric const Expr *FirstArg = Call.getArgExpr(0);
294fe6060f1SDimitry Andric if (!smartptr::isStdSmartPtr(FirstArg->getType()->getAsCXXRecordDecl()))
295fe6060f1SDimitry Andric return false;
296fe6060f1SDimitry Andric return handleSwap(State, Call.getArgSVal(0), Call.getArgSVal(1), C);
297fe6060f1SDimitry Andric }
298fe6060f1SDimitry Andric
299*0fca6ea1SDimitry Andric if (MakeUniqueVariants.contains(Call)) {
300fe6060f1SDimitry Andric if (!ModelSmartPtrDereference)
301fe6060f1SDimitry Andric return false;
302fe6060f1SDimitry Andric
303bdd1243dSDimitry Andric const std::optional<SVal> ThisRegionOpt =
304bdd1243dSDimitry Andric Call.getReturnValueUnderConstruction();
305fe6060f1SDimitry Andric if (!ThisRegionOpt)
306fe6060f1SDimitry Andric return false;
307fe6060f1SDimitry Andric
308fe6060f1SDimitry Andric const auto PtrVal = C.getSValBuilder().getConjuredHeapSymbolVal(
309fe6060f1SDimitry Andric Call.getOriginExpr(), C.getLocationContext(),
310fe6060f1SDimitry Andric getPointerTypeFromTemplateArg(Call, C), C.blockCount());
311fe6060f1SDimitry Andric
312fe6060f1SDimitry Andric const MemRegion *ThisRegion = ThisRegionOpt->getAsRegion();
313fe6060f1SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, PtrVal);
314fe6060f1SDimitry Andric State = State->assume(PtrVal, true);
315fe6060f1SDimitry Andric
316fe6060f1SDimitry Andric // TODO: ExprEngine should do this for us.
317fe6060f1SDimitry Andric // For a bit more context:
318fe6060f1SDimitry Andric // 1) Why do we need this? Since we are modelling a "function"
319fe6060f1SDimitry Andric // that returns a constructed object we need to store this information in
320fe6060f1SDimitry Andric // the program state.
321fe6060f1SDimitry Andric //
322fe6060f1SDimitry Andric // 2) Why does this work?
323fe6060f1SDimitry Andric // `updateObjectsUnderConstruction` does exactly as it sounds.
324fe6060f1SDimitry Andric //
325fe6060f1SDimitry Andric // 3) How should it look like when moved to the Engine?
326fe6060f1SDimitry Andric // It would be nice if we can just
327fe6060f1SDimitry Andric // pretend we don't need to know about this - ie, completely automatic work.
328fe6060f1SDimitry Andric // However, realistically speaking, I think we would need to "signal" the
329fe6060f1SDimitry Andric // ExprEngine evalCall handler that we are constructing an object with this
330fe6060f1SDimitry Andric // function call (constructors obviously construct, hence can be
331fe6060f1SDimitry Andric // automatically deduced).
332fe6060f1SDimitry Andric auto &Engine = State->getStateManager().getOwningEngine();
333fe6060f1SDimitry Andric State = Engine.updateObjectsUnderConstruction(
334fe6060f1SDimitry Andric *ThisRegionOpt, nullptr, State, C.getLocationContext(),
335fe6060f1SDimitry Andric Call.getConstructionContext(), {});
336fe6060f1SDimitry Andric
337fe6060f1SDimitry Andric // We don't leave a note here since it is guaranteed the
338fe6060f1SDimitry Andric // unique_ptr from this call is non-null (hence is safe to de-reference).
339fe6060f1SDimitry Andric C.addTransition(State);
340fe6060f1SDimitry Andric return true;
341fe6060f1SDimitry Andric }
342fe6060f1SDimitry Andric
3435ffd83dbSDimitry Andric if (!smartptr::isStdSmartPtrCall(Call))
3440b57cec5SDimitry Andric return false;
3450b57cec5SDimitry Andric
346e8d8bef9SDimitry Andric if (isBoolConversionMethod(Call)) {
3470b57cec5SDimitry Andric const MemRegion *ThisR =
3480b57cec5SDimitry Andric cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
3490b57cec5SDimitry Andric
350e8d8bef9SDimitry Andric if (ModelSmartPtrDereference) {
351e8d8bef9SDimitry Andric // The check for the region is moved is duplicated in handleBoolOperation
352e8d8bef9SDimitry Andric // method.
353e8d8bef9SDimitry Andric // FIXME: Once we model std::move for smart pointers clean up this and use
354e8d8bef9SDimitry Andric // that modeling.
355e8d8bef9SDimitry Andric handleBoolConversion(Call, C);
356e8d8bef9SDimitry Andric return true;
357e8d8bef9SDimitry Andric } else {
3580b57cec5SDimitry Andric if (!move::isMovedFrom(State, ThisR)) {
359e8d8bef9SDimitry Andric // TODO: Model this case as well. At least, avoid invalidation of
360e8d8bef9SDimitry Andric // globals.
3610b57cec5SDimitry Andric return false;
3620b57cec5SDimitry Andric }
3630b57cec5SDimitry Andric
3640b57cec5SDimitry Andric // TODO: Add a note to bug reports describing this decision.
365e8d8bef9SDimitry Andric C.addTransition(State->BindExpr(
366e8d8bef9SDimitry Andric Call.getOriginExpr(), C.getLocationContext(),
3670b57cec5SDimitry Andric C.getSValBuilder().makeZeroVal(Call.getResultType())));
368e8d8bef9SDimitry Andric
3690b57cec5SDimitry Andric return true;
3700b57cec5SDimitry Andric }
371e8d8bef9SDimitry Andric }
3720b57cec5SDimitry Andric
3735ffd83dbSDimitry Andric if (!ModelSmartPtrDereference)
3745ffd83dbSDimitry Andric return false;
3755ffd83dbSDimitry Andric
3765ffd83dbSDimitry Andric if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
377e8d8bef9SDimitry Andric if (CC->getDecl()->isCopyConstructor())
3785ffd83dbSDimitry Andric return false;
3795ffd83dbSDimitry Andric
380e8d8bef9SDimitry Andric const MemRegion *ThisRegion = CC->getCXXThisVal().getAsRegion();
381e8d8bef9SDimitry Andric if (!ThisRegion)
3825ffd83dbSDimitry Andric return false;
3835ffd83dbSDimitry Andric
38481ad6265SDimitry Andric QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType();
38581ad6265SDimitry Andric
386e8d8bef9SDimitry Andric if (CC->getDecl()->isMoveConstructor())
387e8d8bef9SDimitry Andric return handleMoveCtr(Call, C, ThisRegion);
388e8d8bef9SDimitry Andric
389e8d8bef9SDimitry Andric if (Call.getNumArgs() == 0) {
39081ad6265SDimitry Andric auto NullVal = C.getSValBuilder().makeNullWithType(ThisType);
391e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, NullVal);
392e8d8bef9SDimitry Andric
393e8d8bef9SDimitry Andric C.addTransition(
394e8d8bef9SDimitry Andric State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
395e8d8bef9SDimitry Andric llvm::raw_ostream &OS) {
396e8d8bef9SDimitry Andric if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
397e8d8bef9SDimitry Andric !BR.isInteresting(ThisRegion))
398e8d8bef9SDimitry Andric return;
399e8d8bef9SDimitry Andric OS << "Default constructed smart pointer";
400e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion);
401e8d8bef9SDimitry Andric OS << " is null";
402e8d8bef9SDimitry Andric }));
403e8d8bef9SDimitry Andric } else {
404e8d8bef9SDimitry Andric const auto *TrackingExpr = Call.getArgExpr(0);
405e8d8bef9SDimitry Andric assert(TrackingExpr->getType()->isPointerType() &&
406e8d8bef9SDimitry Andric "Adding a non pointer value to TrackedRegionMap");
407e8d8bef9SDimitry Andric auto ArgVal = Call.getArgSVal(0);
408e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, ArgVal);
409e8d8bef9SDimitry Andric
410e8d8bef9SDimitry Andric C.addTransition(State, C.getNoteTag([ThisRegion, TrackingExpr,
411e8d8bef9SDimitry Andric ArgVal](PathSensitiveBugReport &BR,
412e8d8bef9SDimitry Andric llvm::raw_ostream &OS) {
413e8d8bef9SDimitry Andric if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
414e8d8bef9SDimitry Andric !BR.isInteresting(ThisRegion))
415e8d8bef9SDimitry Andric return;
416e8d8bef9SDimitry Andric bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
417e8d8bef9SDimitry Andric OS << "Smart pointer";
418e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion);
419e8d8bef9SDimitry Andric if (ArgVal.isZeroConstant())
420e8d8bef9SDimitry Andric OS << " is constructed using a null value";
421e8d8bef9SDimitry Andric else
422e8d8bef9SDimitry Andric OS << " is constructed";
423e8d8bef9SDimitry Andric }));
424e8d8bef9SDimitry Andric }
4255ffd83dbSDimitry Andric return true;
4260b57cec5SDimitry Andric }
4270b57cec5SDimitry Andric
428e8d8bef9SDimitry Andric if (handleAssignOp(Call, C))
429e8d8bef9SDimitry Andric return true;
430e8d8bef9SDimitry Andric
4315ffd83dbSDimitry Andric const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call);
4325ffd83dbSDimitry Andric if (!Handler)
4335ffd83dbSDimitry Andric return false;
4345ffd83dbSDimitry Andric (this->**Handler)(Call, C);
4355ffd83dbSDimitry Andric
4365ffd83dbSDimitry Andric return C.isDifferent();
4375ffd83dbSDimitry Andric }
4385ffd83dbSDimitry Andric
retrieveOrConjureInnerPtrVal(ProgramStateRef State,const MemRegion * ThisRegion,const Expr * E,QualType Type,CheckerContext & C) const439fe6060f1SDimitry Andric std::pair<SVal, ProgramStateRef> SmartPtrModeling::retrieveOrConjureInnerPtrVal(
440fe6060f1SDimitry Andric ProgramStateRef State, const MemRegion *ThisRegion, const Expr *E,
441fe6060f1SDimitry Andric QualType Type, CheckerContext &C) const {
442fe6060f1SDimitry Andric const auto *Ptr = State->get<TrackedRegionMap>(ThisRegion);
443fe6060f1SDimitry Andric if (Ptr)
444fe6060f1SDimitry Andric return {*Ptr, State};
445fe6060f1SDimitry Andric auto Val = C.getSValBuilder().conjureSymbolVal(E, C.getLocationContext(),
446fe6060f1SDimitry Andric Type, C.blockCount());
447fe6060f1SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, Val);
448fe6060f1SDimitry Andric return {Val, State};
449fe6060f1SDimitry Andric }
450fe6060f1SDimitry Andric
handleComparisionOp(const CallEvent & Call,CheckerContext & C) const451fe6060f1SDimitry Andric bool SmartPtrModeling::handleComparisionOp(const CallEvent &Call,
452fe6060f1SDimitry Andric CheckerContext &C) const {
453fe6060f1SDimitry Andric const auto *FC = dyn_cast<SimpleFunctionCall>(&Call);
454fe6060f1SDimitry Andric if (!FC)
455fe6060f1SDimitry Andric return false;
456fe6060f1SDimitry Andric const FunctionDecl *FD = FC->getDecl();
457fe6060f1SDimitry Andric if (!FD->isOverloadedOperator())
458fe6060f1SDimitry Andric return false;
459fe6060f1SDimitry Andric const OverloadedOperatorKind OOK = FD->getOverloadedOperator();
460fe6060f1SDimitry Andric if (!(OOK == OO_EqualEqual || OOK == OO_ExclaimEqual || OOK == OO_Less ||
461fe6060f1SDimitry Andric OOK == OO_LessEqual || OOK == OO_Greater || OOK == OO_GreaterEqual ||
462fe6060f1SDimitry Andric OOK == OO_Spaceship))
463fe6060f1SDimitry Andric return false;
464fe6060f1SDimitry Andric
465fe6060f1SDimitry Andric // There are some special cases about which we can infer about
466fe6060f1SDimitry Andric // the resulting answer.
467fe6060f1SDimitry Andric // For reference, there is a discussion at https://reviews.llvm.org/D104616.
468fe6060f1SDimitry Andric // Also, the cppreference page is good to look at
469fe6060f1SDimitry Andric // https://en.cppreference.com/w/cpp/memory/unique_ptr/operator_cmp.
470fe6060f1SDimitry Andric
471fe6060f1SDimitry Andric auto makeSValFor = [&C, this](ProgramStateRef State, const Expr *E,
472fe6060f1SDimitry Andric SVal S) -> std::pair<SVal, ProgramStateRef> {
473fe6060f1SDimitry Andric if (S.isZeroConstant()) {
474fe6060f1SDimitry Andric return {S, State};
475fe6060f1SDimitry Andric }
476fe6060f1SDimitry Andric const MemRegion *Reg = S.getAsRegion();
477fe6060f1SDimitry Andric assert(Reg &&
478fe6060f1SDimitry Andric "this pointer of std::unique_ptr should be obtainable as MemRegion");
479fe6060f1SDimitry Andric QualType Type = getInnerPointerType(C, E->getType()->getAsCXXRecordDecl());
480fe6060f1SDimitry Andric return retrieveOrConjureInnerPtrVal(State, Reg, E, Type, C);
481fe6060f1SDimitry Andric };
482fe6060f1SDimitry Andric
483fe6060f1SDimitry Andric SVal First = Call.getArgSVal(0);
484fe6060f1SDimitry Andric SVal Second = Call.getArgSVal(1);
485fe6060f1SDimitry Andric const auto *FirstExpr = Call.getArgExpr(0);
486fe6060f1SDimitry Andric const auto *SecondExpr = Call.getArgExpr(1);
487fe6060f1SDimitry Andric
488fe6060f1SDimitry Andric const auto *ResultExpr = Call.getOriginExpr();
489fe6060f1SDimitry Andric const auto *LCtx = C.getLocationContext();
490fe6060f1SDimitry Andric auto &Bldr = C.getSValBuilder();
491fe6060f1SDimitry Andric ProgramStateRef State = C.getState();
492fe6060f1SDimitry Andric
493fe6060f1SDimitry Andric SVal FirstPtrVal, SecondPtrVal;
494fe6060f1SDimitry Andric std::tie(FirstPtrVal, State) = makeSValFor(State, FirstExpr, First);
495fe6060f1SDimitry Andric std::tie(SecondPtrVal, State) = makeSValFor(State, SecondExpr, Second);
496fe6060f1SDimitry Andric BinaryOperatorKind BOK =
497fe6060f1SDimitry Andric operationKindFromOverloadedOperator(OOK, true).GetBinaryOpUnsafe();
498fe6060f1SDimitry Andric auto RetVal = Bldr.evalBinOp(State, BOK, FirstPtrVal, SecondPtrVal,
499fe6060f1SDimitry Andric Call.getResultType());
500fe6060f1SDimitry Andric
501fe6060f1SDimitry Andric if (OOK != OO_Spaceship) {
502fe6060f1SDimitry Andric ProgramStateRef TrueState, FalseState;
503fe6060f1SDimitry Andric std::tie(TrueState, FalseState) =
504fe6060f1SDimitry Andric State->assume(*RetVal.getAs<DefinedOrUnknownSVal>());
505fe6060f1SDimitry Andric if (TrueState)
506fe6060f1SDimitry Andric C.addTransition(
507fe6060f1SDimitry Andric TrueState->BindExpr(ResultExpr, LCtx, Bldr.makeTruthVal(true)));
508fe6060f1SDimitry Andric if (FalseState)
509fe6060f1SDimitry Andric C.addTransition(
510fe6060f1SDimitry Andric FalseState->BindExpr(ResultExpr, LCtx, Bldr.makeTruthVal(false)));
511fe6060f1SDimitry Andric } else {
512fe6060f1SDimitry Andric C.addTransition(State->BindExpr(ResultExpr, LCtx, RetVal));
513fe6060f1SDimitry Andric }
514fe6060f1SDimitry Andric return true;
515fe6060f1SDimitry Andric }
516fe6060f1SDimitry Andric
handleOstreamOperator(const CallEvent & Call,CheckerContext & C) const517fe6060f1SDimitry Andric bool SmartPtrModeling::handleOstreamOperator(const CallEvent &Call,
518fe6060f1SDimitry Andric CheckerContext &C) const {
519fe6060f1SDimitry Andric // operator<< does not modify the smart pointer.
520fe6060f1SDimitry Andric // And we don't really have much of modelling of basic_ostream.
521fe6060f1SDimitry Andric // So, we are better off:
522fe6060f1SDimitry Andric // 1) Invalidating the mem-region of the ostream object at hand.
523fe6060f1SDimitry Andric // 2) Setting the SVal of the basic_ostream as the return value.
524fe6060f1SDimitry Andric // Not very satisfying, but it gets the job done, and is better
525fe6060f1SDimitry Andric // than the default handling. :)
526fe6060f1SDimitry Andric
527fe6060f1SDimitry Andric ProgramStateRef State = C.getState();
528fe6060f1SDimitry Andric const auto StreamVal = Call.getArgSVal(0);
529fe6060f1SDimitry Andric const MemRegion *StreamThisRegion = StreamVal.getAsRegion();
530fe6060f1SDimitry Andric if (!StreamThisRegion)
531fe6060f1SDimitry Andric return false;
532fe6060f1SDimitry Andric State =
533fe6060f1SDimitry Andric State->invalidateRegions({StreamThisRegion}, Call.getOriginExpr(),
534fe6060f1SDimitry Andric C.blockCount(), C.getLocationContext(), false);
535fe6060f1SDimitry Andric State =
536fe6060f1SDimitry Andric State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), StreamVal);
537fe6060f1SDimitry Andric C.addTransition(State);
538fe6060f1SDimitry Andric return true;
539fe6060f1SDimitry Andric }
540fe6060f1SDimitry Andric
checkDeadSymbols(SymbolReaper & SymReaper,CheckerContext & C) const5415ffd83dbSDimitry Andric void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper,
5425ffd83dbSDimitry Andric CheckerContext &C) const {
5435ffd83dbSDimitry Andric ProgramStateRef State = C.getState();
5445ffd83dbSDimitry Andric // Clean up dead regions from the region map.
5455ffd83dbSDimitry Andric TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
5465ffd83dbSDimitry Andric for (auto E : TrackedRegions) {
5475ffd83dbSDimitry Andric const MemRegion *Region = E.first;
5485ffd83dbSDimitry Andric bool IsRegDead = !SymReaper.isLiveRegion(Region);
5495ffd83dbSDimitry Andric
5505ffd83dbSDimitry Andric if (IsRegDead)
5515ffd83dbSDimitry Andric State = State->remove<TrackedRegionMap>(Region);
5525ffd83dbSDimitry Andric }
5535ffd83dbSDimitry Andric C.addTransition(State);
5545ffd83dbSDimitry Andric }
5555ffd83dbSDimitry Andric
printState(raw_ostream & Out,ProgramStateRef State,const char * NL,const char * Sep) const556e8d8bef9SDimitry Andric void SmartPtrModeling::printState(raw_ostream &Out, ProgramStateRef State,
557e8d8bef9SDimitry Andric const char *NL, const char *Sep) const {
558e8d8bef9SDimitry Andric TrackedRegionMapTy RS = State->get<TrackedRegionMap>();
559e8d8bef9SDimitry Andric
560e8d8bef9SDimitry Andric if (!RS.isEmpty()) {
561e8d8bef9SDimitry Andric Out << Sep << "Smart ptr regions :" << NL;
562e8d8bef9SDimitry Andric for (auto I : RS) {
563e8d8bef9SDimitry Andric I.first->dumpToStream(Out);
564e8d8bef9SDimitry Andric if (smartptr::isNullSmartPtr(State, I.first))
565e8d8bef9SDimitry Andric Out << ": Null";
566e8d8bef9SDimitry Andric else
567e8d8bef9SDimitry Andric Out << ": Non Null";
568e8d8bef9SDimitry Andric Out << NL;
569e8d8bef9SDimitry Andric }
570e8d8bef9SDimitry Andric }
571e8d8bef9SDimitry Andric }
572e8d8bef9SDimitry Andric
checkRegionChanges(ProgramStateRef State,const InvalidatedSymbols * Invalidated,ArrayRef<const MemRegion * > ExplicitRegions,ArrayRef<const MemRegion * > Regions,const LocationContext * LCtx,const CallEvent * Call) const573e8d8bef9SDimitry Andric ProgramStateRef SmartPtrModeling::checkRegionChanges(
574e8d8bef9SDimitry Andric ProgramStateRef State, const InvalidatedSymbols *Invalidated,
575e8d8bef9SDimitry Andric ArrayRef<const MemRegion *> ExplicitRegions,
576e8d8bef9SDimitry Andric ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
577e8d8bef9SDimitry Andric const CallEvent *Call) const {
578e8d8bef9SDimitry Andric TrackedRegionMapTy RegionMap = State->get<TrackedRegionMap>();
579e8d8bef9SDimitry Andric TrackedRegionMapTy::Factory &RegionMapFactory =
580e8d8bef9SDimitry Andric State->get_context<TrackedRegionMap>();
581e8d8bef9SDimitry Andric for (const auto *Region : Regions)
582e8d8bef9SDimitry Andric RegionMap = removeTrackedSubregions(RegionMap, RegionMapFactory,
583e8d8bef9SDimitry Andric Region->getBaseRegion());
584e8d8bef9SDimitry Andric return State->set<TrackedRegionMap>(RegionMap);
585e8d8bef9SDimitry Andric }
586e8d8bef9SDimitry Andric
checkLiveSymbols(ProgramStateRef State,SymbolReaper & SR) const587e8d8bef9SDimitry Andric void SmartPtrModeling::checkLiveSymbols(ProgramStateRef State,
588e8d8bef9SDimitry Andric SymbolReaper &SR) const {
589e8d8bef9SDimitry Andric // Marking tracked symbols alive
590e8d8bef9SDimitry Andric TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
59106c3fb27SDimitry Andric for (SVal Val : llvm::make_second_range(TrackedRegions)) {
59206c3fb27SDimitry Andric for (SymbolRef Sym : Val.symbols()) {
59306c3fb27SDimitry Andric SR.markLive(Sym);
594e8d8bef9SDimitry Andric }
595e8d8bef9SDimitry Andric }
596e8d8bef9SDimitry Andric }
597e8d8bef9SDimitry Andric
handleReset(const CallEvent & Call,CheckerContext & C) const5985ffd83dbSDimitry Andric void SmartPtrModeling::handleReset(const CallEvent &Call,
5995ffd83dbSDimitry Andric CheckerContext &C) const {
600e8d8bef9SDimitry Andric ProgramStateRef State = C.getState();
6015ffd83dbSDimitry Andric const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
6025ffd83dbSDimitry Andric if (!IC)
6035ffd83dbSDimitry Andric return;
6045ffd83dbSDimitry Andric
605e8d8bef9SDimitry Andric const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
606e8d8bef9SDimitry Andric if (!ThisRegion)
6075ffd83dbSDimitry Andric return;
608e8d8bef9SDimitry Andric
609e8d8bef9SDimitry Andric assert(Call.getArgExpr(0)->getType()->isPointerType() &&
610e8d8bef9SDimitry Andric "Adding a non pointer value to TrackedRegionMap");
611e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, Call.getArgSVal(0));
612e8d8bef9SDimitry Andric const auto *TrackingExpr = Call.getArgExpr(0);
613e8d8bef9SDimitry Andric C.addTransition(
614e8d8bef9SDimitry Andric State, C.getNoteTag([ThisRegion, TrackingExpr](PathSensitiveBugReport &BR,
615e8d8bef9SDimitry Andric llvm::raw_ostream &OS) {
616e8d8bef9SDimitry Andric if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
617e8d8bef9SDimitry Andric !BR.isInteresting(ThisRegion))
618e8d8bef9SDimitry Andric return;
619e8d8bef9SDimitry Andric bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
620e8d8bef9SDimitry Andric OS << "Smart pointer";
621e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion);
622e8d8bef9SDimitry Andric OS << " reset using a null value";
623e8d8bef9SDimitry Andric }));
624e8d8bef9SDimitry Andric // TODO: Make sure to ivalidate the region in the Store if we don't have
6255ffd83dbSDimitry Andric // time to model all methods.
6265ffd83dbSDimitry Andric }
6275ffd83dbSDimitry Andric
handleRelease(const CallEvent & Call,CheckerContext & C) const6285ffd83dbSDimitry Andric void SmartPtrModeling::handleRelease(const CallEvent &Call,
6295ffd83dbSDimitry Andric CheckerContext &C) const {
630e8d8bef9SDimitry Andric ProgramStateRef State = C.getState();
6315ffd83dbSDimitry Andric const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
6325ffd83dbSDimitry Andric if (!IC)
6335ffd83dbSDimitry Andric return;
6345ffd83dbSDimitry Andric
635e8d8bef9SDimitry Andric const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
636e8d8bef9SDimitry Andric if (!ThisRegion)
6375ffd83dbSDimitry Andric return;
6385ffd83dbSDimitry Andric
639e8d8bef9SDimitry Andric const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
6405ffd83dbSDimitry Andric
6415ffd83dbSDimitry Andric if (InnerPointVal) {
6425ffd83dbSDimitry Andric State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
6435ffd83dbSDimitry Andric *InnerPointVal);
6445ffd83dbSDimitry Andric }
645e8d8bef9SDimitry Andric
64681ad6265SDimitry Andric QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType();
64781ad6265SDimitry Andric auto ValueToUpdate = C.getSValBuilder().makeNullWithType(ThisType);
648e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, ValueToUpdate);
649e8d8bef9SDimitry Andric
650e8d8bef9SDimitry Andric C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
651e8d8bef9SDimitry Andric llvm::raw_ostream &OS) {
652e8d8bef9SDimitry Andric if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
653e8d8bef9SDimitry Andric !BR.isInteresting(ThisRegion))
654e8d8bef9SDimitry Andric return;
655e8d8bef9SDimitry Andric
656e8d8bef9SDimitry Andric OS << "Smart pointer";
657e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion);
658e8d8bef9SDimitry Andric OS << " is released and set to null";
659e8d8bef9SDimitry Andric }));
6605ffd83dbSDimitry Andric // TODO: Add support to enable MallocChecker to start tracking the raw
6615ffd83dbSDimitry Andric // pointer.
6625ffd83dbSDimitry Andric }
6635ffd83dbSDimitry Andric
handleSwapMethod(const CallEvent & Call,CheckerContext & C) const664fe6060f1SDimitry Andric void SmartPtrModeling::handleSwapMethod(const CallEvent &Call,
6655ffd83dbSDimitry Andric CheckerContext &C) const {
666e8d8bef9SDimitry Andric // To model unique_ptr::swap() method.
667e8d8bef9SDimitry Andric const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
668e8d8bef9SDimitry Andric if (!IC)
669e8d8bef9SDimitry Andric return;
670e8d8bef9SDimitry Andric
671e8d8bef9SDimitry Andric auto State = C.getState();
672fe6060f1SDimitry Andric handleSwap(State, IC->getCXXThisVal(), Call.getArgSVal(0), C);
673fe6060f1SDimitry Andric }
674e8d8bef9SDimitry Andric
handleSwap(ProgramStateRef State,SVal First,SVal Second,CheckerContext & C) const675fe6060f1SDimitry Andric bool SmartPtrModeling::handleSwap(ProgramStateRef State, SVal First,
676fe6060f1SDimitry Andric SVal Second, CheckerContext &C) const {
677fe6060f1SDimitry Andric const MemRegion *FirstThisRegion = First.getAsRegion();
678fe6060f1SDimitry Andric if (!FirstThisRegion)
679fe6060f1SDimitry Andric return false;
680fe6060f1SDimitry Andric const MemRegion *SecondThisRegion = Second.getAsRegion();
681fe6060f1SDimitry Andric if (!SecondThisRegion)
682fe6060f1SDimitry Andric return false;
683e8d8bef9SDimitry Andric
684fe6060f1SDimitry Andric const auto *FirstInnerPtrVal = State->get<TrackedRegionMap>(FirstThisRegion);
685fe6060f1SDimitry Andric const auto *SecondInnerPtrVal =
686fe6060f1SDimitry Andric State->get<TrackedRegionMap>(SecondThisRegion);
687fe6060f1SDimitry Andric
688fe6060f1SDimitry Andric State = updateSwappedRegion(State, FirstThisRegion, SecondInnerPtrVal);
689fe6060f1SDimitry Andric State = updateSwappedRegion(State, SecondThisRegion, FirstInnerPtrVal);
690fe6060f1SDimitry Andric
691fe6060f1SDimitry Andric C.addTransition(State, C.getNoteTag([FirstThisRegion, SecondThisRegion](
692fe6060f1SDimitry Andric PathSensitiveBugReport &BR,
693e8d8bef9SDimitry Andric llvm::raw_ostream &OS) {
694fe6060f1SDimitry Andric if (&BR.getBugType() != smartptr::getNullDereferenceBugType())
695e8d8bef9SDimitry Andric return;
696fe6060f1SDimitry Andric if (BR.isInteresting(FirstThisRegion) &&
697fe6060f1SDimitry Andric !BR.isInteresting(SecondThisRegion)) {
698fe6060f1SDimitry Andric BR.markInteresting(SecondThisRegion);
699fe6060f1SDimitry Andric BR.markNotInteresting(FirstThisRegion);
700fe6060f1SDimitry Andric }
701fe6060f1SDimitry Andric if (BR.isInteresting(SecondThisRegion) &&
702fe6060f1SDimitry Andric !BR.isInteresting(FirstThisRegion)) {
703fe6060f1SDimitry Andric BR.markInteresting(FirstThisRegion);
704fe6060f1SDimitry Andric BR.markNotInteresting(SecondThisRegion);
705fe6060f1SDimitry Andric }
706fe6060f1SDimitry Andric // TODO: We need to emit some note here probably!!
707e8d8bef9SDimitry Andric }));
708fe6060f1SDimitry Andric
709fe6060f1SDimitry Andric return true;
7105ffd83dbSDimitry Andric }
7115ffd83dbSDimitry Andric
handleGet(const CallEvent & Call,CheckerContext & C) const712e8d8bef9SDimitry Andric void SmartPtrModeling::handleGet(const CallEvent &Call,
713e8d8bef9SDimitry Andric CheckerContext &C) const {
7145ffd83dbSDimitry Andric ProgramStateRef State = C.getState();
715e8d8bef9SDimitry Andric const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
716e8d8bef9SDimitry Andric if (!IC)
717e8d8bef9SDimitry Andric return;
7185ffd83dbSDimitry Andric
719e8d8bef9SDimitry Andric const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
720e8d8bef9SDimitry Andric if (!ThisRegion)
721e8d8bef9SDimitry Andric return;
722e8d8bef9SDimitry Andric
723e8d8bef9SDimitry Andric SVal InnerPointerVal;
724fe6060f1SDimitry Andric std::tie(InnerPointerVal, State) = retrieveOrConjureInnerPtrVal(
725fe6060f1SDimitry Andric State, ThisRegion, Call.getOriginExpr(), Call.getResultType(), C);
726e8d8bef9SDimitry Andric State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
727e8d8bef9SDimitry Andric InnerPointerVal);
728e8d8bef9SDimitry Andric // TODO: Add NoteTag, for how the raw pointer got using 'get' method.
729e8d8bef9SDimitry Andric C.addTransition(State);
730e8d8bef9SDimitry Andric }
731e8d8bef9SDimitry Andric
handleAssignOp(const CallEvent & Call,CheckerContext & C) const732e8d8bef9SDimitry Andric bool SmartPtrModeling::handleAssignOp(const CallEvent &Call,
733e8d8bef9SDimitry Andric CheckerContext &C) const {
734e8d8bef9SDimitry Andric ProgramStateRef State = C.getState();
735e8d8bef9SDimitry Andric const auto *OC = dyn_cast<CXXMemberOperatorCall>(&Call);
736e8d8bef9SDimitry Andric if (!OC)
737e8d8bef9SDimitry Andric return false;
738e8d8bef9SDimitry Andric OverloadedOperatorKind OOK = OC->getOverloadedOperator();
739e8d8bef9SDimitry Andric if (OOK != OO_Equal)
740e8d8bef9SDimitry Andric return false;
741e8d8bef9SDimitry Andric const MemRegion *ThisRegion = OC->getCXXThisVal().getAsRegion();
742e8d8bef9SDimitry Andric if (!ThisRegion)
743e8d8bef9SDimitry Andric return false;
744e8d8bef9SDimitry Andric
74581ad6265SDimitry Andric QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType();
74681ad6265SDimitry Andric
747e8d8bef9SDimitry Andric const MemRegion *OtherSmartPtrRegion = OC->getArgSVal(0).getAsRegion();
748e8d8bef9SDimitry Andric // In case of 'nullptr' or '0' assigned
749e8d8bef9SDimitry Andric if (!OtherSmartPtrRegion) {
750e8d8bef9SDimitry Andric bool AssignedNull = Call.getArgSVal(0).isZeroConstant();
751e8d8bef9SDimitry Andric if (!AssignedNull)
752e8d8bef9SDimitry Andric return false;
75381ad6265SDimitry Andric auto NullVal = C.getSValBuilder().makeNullWithType(ThisType);
754e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, NullVal);
755e8d8bef9SDimitry Andric C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
756e8d8bef9SDimitry Andric llvm::raw_ostream &OS) {
757e8d8bef9SDimitry Andric if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
758e8d8bef9SDimitry Andric !BR.isInteresting(ThisRegion))
759e8d8bef9SDimitry Andric return;
760e8d8bef9SDimitry Andric OS << "Smart pointer";
761e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion);
762e8d8bef9SDimitry Andric OS << " is assigned to null";
763e8d8bef9SDimitry Andric }));
764e8d8bef9SDimitry Andric return true;
765e8d8bef9SDimitry Andric }
766e8d8bef9SDimitry Andric
76781ad6265SDimitry Andric return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion, Call);
768e8d8bef9SDimitry Andric }
769e8d8bef9SDimitry Andric
handleMoveCtr(const CallEvent & Call,CheckerContext & C,const MemRegion * ThisRegion) const770e8d8bef9SDimitry Andric bool SmartPtrModeling::handleMoveCtr(const CallEvent &Call, CheckerContext &C,
771e8d8bef9SDimitry Andric const MemRegion *ThisRegion) const {
772e8d8bef9SDimitry Andric const auto *OtherSmartPtrRegion = Call.getArgSVal(0).getAsRegion();
773e8d8bef9SDimitry Andric if (!OtherSmartPtrRegion)
774e8d8bef9SDimitry Andric return false;
775e8d8bef9SDimitry Andric
77681ad6265SDimitry Andric return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion, Call);
777e8d8bef9SDimitry Andric }
778e8d8bef9SDimitry Andric
updateMovedSmartPointers(CheckerContext & C,const MemRegion * ThisRegion,const MemRegion * OtherSmartPtrRegion,const CallEvent & Call) const779e8d8bef9SDimitry Andric bool SmartPtrModeling::updateMovedSmartPointers(
780e8d8bef9SDimitry Andric CheckerContext &C, const MemRegion *ThisRegion,
78181ad6265SDimitry Andric const MemRegion *OtherSmartPtrRegion, const CallEvent &Call) const {
782e8d8bef9SDimitry Andric ProgramStateRef State = C.getState();
78381ad6265SDimitry Andric QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType();
784e8d8bef9SDimitry Andric const auto *OtherInnerPtr = State->get<TrackedRegionMap>(OtherSmartPtrRegion);
785e8d8bef9SDimitry Andric if (OtherInnerPtr) {
786e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, *OtherInnerPtr);
78781ad6265SDimitry Andric
78881ad6265SDimitry Andric auto NullVal = C.getSValBuilder().makeNullWithType(ThisType);
789e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
790e8d8bef9SDimitry Andric bool IsArgValNull = OtherInnerPtr->isZeroConstant();
791e8d8bef9SDimitry Andric
792e8d8bef9SDimitry Andric C.addTransition(
793e8d8bef9SDimitry Andric State,
794e8d8bef9SDimitry Andric C.getNoteTag([ThisRegion, OtherSmartPtrRegion, IsArgValNull](
795e8d8bef9SDimitry Andric PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
796e8d8bef9SDimitry Andric if (&BR.getBugType() != smartptr::getNullDereferenceBugType())
797e8d8bef9SDimitry Andric return;
798e8d8bef9SDimitry Andric if (BR.isInteresting(OtherSmartPtrRegion)) {
799e8d8bef9SDimitry Andric OS << "Smart pointer";
800e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion);
801e8d8bef9SDimitry Andric OS << " is null after being moved to";
802e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion);
803e8d8bef9SDimitry Andric }
804e8d8bef9SDimitry Andric if (BR.isInteresting(ThisRegion) && IsArgValNull) {
805e8d8bef9SDimitry Andric OS << "A null pointer value is moved to";
806e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion);
807e8d8bef9SDimitry Andric BR.markInteresting(OtherSmartPtrRegion);
808e8d8bef9SDimitry Andric }
809e8d8bef9SDimitry Andric }));
810e8d8bef9SDimitry Andric return true;
811e8d8bef9SDimitry Andric } else {
812e8d8bef9SDimitry Andric // In case we dont know anything about value we are moving from
813e8d8bef9SDimitry Andric // remove the entry from map for which smart pointer got moved to.
81481ad6265SDimitry Andric // For unique_ptr<A>, Ty will be 'A*'.
81581ad6265SDimitry Andric auto NullVal = C.getSValBuilder().makeNullWithType(ThisType);
816e8d8bef9SDimitry Andric State = State->remove<TrackedRegionMap>(ThisRegion);
817e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
818e8d8bef9SDimitry Andric C.addTransition(State, C.getNoteTag([OtherSmartPtrRegion,
819e8d8bef9SDimitry Andric ThisRegion](PathSensitiveBugReport &BR,
820e8d8bef9SDimitry Andric llvm::raw_ostream &OS) {
821e8d8bef9SDimitry Andric if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
822e8d8bef9SDimitry Andric !BR.isInteresting(OtherSmartPtrRegion))
823e8d8bef9SDimitry Andric return;
824e8d8bef9SDimitry Andric OS << "Smart pointer";
825e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion);
826e8d8bef9SDimitry Andric OS << " is null after; previous value moved to";
827e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion);
828e8d8bef9SDimitry Andric }));
829e8d8bef9SDimitry Andric return true;
830e8d8bef9SDimitry Andric }
831e8d8bef9SDimitry Andric return false;
832e8d8bef9SDimitry Andric }
833e8d8bef9SDimitry Andric
handleBoolConversion(const CallEvent & Call,CheckerContext & C) const834e8d8bef9SDimitry Andric void SmartPtrModeling::handleBoolConversion(const CallEvent &Call,
835e8d8bef9SDimitry Andric CheckerContext &C) const {
836e8d8bef9SDimitry Andric // To model unique_ptr::operator bool
837e8d8bef9SDimitry Andric ProgramStateRef State = C.getState();
838e8d8bef9SDimitry Andric const Expr *CallExpr = Call.getOriginExpr();
839e8d8bef9SDimitry Andric const MemRegion *ThisRegion =
840e8d8bef9SDimitry Andric cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
841e8d8bef9SDimitry Andric
84281ad6265SDimitry Andric QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType();
84381ad6265SDimitry Andric
844e8d8bef9SDimitry Andric SVal InnerPointerVal;
845e8d8bef9SDimitry Andric if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) {
846e8d8bef9SDimitry Andric InnerPointerVal = *InnerValPtr;
847e8d8bef9SDimitry Andric } else {
848e8d8bef9SDimitry Andric // In case of inner pointer SVal is not available we create
849e8d8bef9SDimitry Andric // conjureSymbolVal for inner pointer value.
850e8d8bef9SDimitry Andric auto InnerPointerType = getInnerPointerType(Call, C);
851e8d8bef9SDimitry Andric if (InnerPointerType.isNull())
852e8d8bef9SDimitry Andric return;
853e8d8bef9SDimitry Andric
854e8d8bef9SDimitry Andric const LocationContext *LC = C.getLocationContext();
855e8d8bef9SDimitry Andric InnerPointerVal = C.getSValBuilder().conjureSymbolVal(
856e8d8bef9SDimitry Andric CallExpr, LC, InnerPointerType, C.blockCount());
857e8d8bef9SDimitry Andric State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal);
858e8d8bef9SDimitry Andric }
859e8d8bef9SDimitry Andric
860e8d8bef9SDimitry Andric if (State->isNull(InnerPointerVal).isConstrainedTrue()) {
861e8d8bef9SDimitry Andric State = State->BindExpr(CallExpr, C.getLocationContext(),
862e8d8bef9SDimitry Andric C.getSValBuilder().makeTruthVal(false));
863e8d8bef9SDimitry Andric
864e8d8bef9SDimitry Andric C.addTransition(State);
865e8d8bef9SDimitry Andric return;
866e8d8bef9SDimitry Andric } else if (State->isNonNull(InnerPointerVal).isConstrainedTrue()) {
867e8d8bef9SDimitry Andric State = State->BindExpr(CallExpr, C.getLocationContext(),
868e8d8bef9SDimitry Andric C.getSValBuilder().makeTruthVal(true));
869e8d8bef9SDimitry Andric
870e8d8bef9SDimitry Andric C.addTransition(State);
871e8d8bef9SDimitry Andric return;
872e8d8bef9SDimitry Andric } else if (move::isMovedFrom(State, ThisRegion)) {
873e8d8bef9SDimitry Andric C.addTransition(
874e8d8bef9SDimitry Andric State->BindExpr(CallExpr, C.getLocationContext(),
875e8d8bef9SDimitry Andric C.getSValBuilder().makeZeroVal(Call.getResultType())));
876e8d8bef9SDimitry Andric return;
877e8d8bef9SDimitry Andric } else {
878e8d8bef9SDimitry Andric ProgramStateRef NotNullState, NullState;
879e8d8bef9SDimitry Andric std::tie(NotNullState, NullState) =
880e8d8bef9SDimitry Andric State->assume(InnerPointerVal.castAs<DefinedOrUnknownSVal>());
881e8d8bef9SDimitry Andric
88281ad6265SDimitry Andric auto NullVal = C.getSValBuilder().makeNullWithType(ThisType);
883e8d8bef9SDimitry Andric // Explicitly tracking the region as null.
884e8d8bef9SDimitry Andric NullState = NullState->set<TrackedRegionMap>(ThisRegion, NullVal);
885e8d8bef9SDimitry Andric
886e8d8bef9SDimitry Andric NullState = NullState->BindExpr(CallExpr, C.getLocationContext(),
887e8d8bef9SDimitry Andric C.getSValBuilder().makeTruthVal(false));
888e8d8bef9SDimitry Andric C.addTransition(NullState, C.getNoteTag(
889e8d8bef9SDimitry Andric [ThisRegion](PathSensitiveBugReport &BR,
890e8d8bef9SDimitry Andric llvm::raw_ostream &OS) {
891e8d8bef9SDimitry Andric OS << "Assuming smart pointer";
892e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion);
893e8d8bef9SDimitry Andric OS << " is null";
894e8d8bef9SDimitry Andric },
895e8d8bef9SDimitry Andric /*IsPrunable=*/true));
896e8d8bef9SDimitry Andric NotNullState =
897e8d8bef9SDimitry Andric NotNullState->BindExpr(CallExpr, C.getLocationContext(),
898e8d8bef9SDimitry Andric C.getSValBuilder().makeTruthVal(true));
899e8d8bef9SDimitry Andric C.addTransition(
900e8d8bef9SDimitry Andric NotNullState,
901e8d8bef9SDimitry Andric C.getNoteTag(
902e8d8bef9SDimitry Andric [ThisRegion](PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
903e8d8bef9SDimitry Andric OS << "Assuming smart pointer";
904e8d8bef9SDimitry Andric checkAndPrettyPrintRegion(OS, ThisRegion);
905e8d8bef9SDimitry Andric OS << " is non-null";
906e8d8bef9SDimitry Andric },
907e8d8bef9SDimitry Andric /*IsPrunable=*/true));
908e8d8bef9SDimitry Andric return;
909e8d8bef9SDimitry Andric }
9105ffd83dbSDimitry Andric }
9115ffd83dbSDimitry Andric
registerSmartPtrModeling(CheckerManager & Mgr)9125ffd83dbSDimitry Andric void ento::registerSmartPtrModeling(CheckerManager &Mgr) {
9135ffd83dbSDimitry Andric auto *Checker = Mgr.registerChecker<SmartPtrModeling>();
9145ffd83dbSDimitry Andric Checker->ModelSmartPtrDereference =
9155ffd83dbSDimitry Andric Mgr.getAnalyzerOptions().getCheckerBooleanOption(
9165ffd83dbSDimitry Andric Checker, "ModelSmartPtrDereference");
9175ffd83dbSDimitry Andric }
9185ffd83dbSDimitry Andric
shouldRegisterSmartPtrModeling(const CheckerManager & mgr)9195ffd83dbSDimitry Andric bool ento::shouldRegisterSmartPtrModeling(const CheckerManager &mgr) {
9205ffd83dbSDimitry Andric const LangOptions &LO = mgr.getLangOpts();
9210b57cec5SDimitry Andric return LO.CPlusPlus;
9220b57cec5SDimitry Andric }
923