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