xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp (revision d563d7a7313cf47dcb24c6370a035bd803965b4e)
1 // SmartPtrModeling.cpp - Model behavior of C++ smart pointers - C++ ------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file defines a checker that models various aspects of
10 // C++ smart pointer behavior.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "Move.h"
15 #include "SmartPtr.h"
16 
17 #include "clang/AST/DeclCXX.h"
18 #include "clang/AST/DeclarationName.h"
19 #include "clang/AST/ExprCXX.h"
20 #include "clang/AST/Type.h"
21 #include "clang/Basic/LLVM.h"
22 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
23 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
24 #include "clang/StaticAnalyzer/Core/Checker.h"
25 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
26 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
27 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
28 #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
29 #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
30 #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
31 #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
32 #include <string>
33 
34 using namespace clang;
35 using namespace ento;
36 
37 namespace {
38 class SmartPtrModeling
39     : public Checker<eval::Call, check::DeadSymbols, check::RegionChanges,
40                      check::LiveSymbols> {
41 
42   bool isBoolConversionMethod(const CallEvent &Call) const;
43 
44 public:
45   // Whether the checker should model for null dereferences of smart pointers.
46   DefaultBool ModelSmartPtrDereference;
47   bool evalCall(const CallEvent &Call, CheckerContext &C) const;
48   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
49   void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
50   ProgramStateRef
51   checkRegionChanges(ProgramStateRef State,
52                      const InvalidatedSymbols *Invalidated,
53                      ArrayRef<const MemRegion *> ExplicitRegions,
54                      ArrayRef<const MemRegion *> Regions,
55                      const LocationContext *LCtx, const CallEvent *Call) const;
56   void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
57                   const char *Sep) const override;
58   void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
59 
60 private:
61   void handleReset(const CallEvent &Call, CheckerContext &C) const;
62   void handleRelease(const CallEvent &Call, CheckerContext &C) const;
63   void handleSwap(const CallEvent &Call, CheckerContext &C) const;
64   void handleGet(const CallEvent &Call, CheckerContext &C) const;
65   bool handleAssignOp(const CallEvent &Call, CheckerContext &C) const;
66   bool handleMoveCtr(const CallEvent &Call, CheckerContext &C,
67                      const MemRegion *ThisRegion) const;
68   bool updateMovedSmartPointers(CheckerContext &C, const MemRegion *ThisRegion,
69                                 const MemRegion *OtherSmartPtrRegion) const;
70   void handleBoolConversion(const CallEvent &Call, CheckerContext &C) const;
71 
72   using SmartPtrMethodHandlerFn =
73       void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const;
74   CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{
75       {{"reset"}, &SmartPtrModeling::handleReset},
76       {{"release"}, &SmartPtrModeling::handleRelease},
77       {{"swap", 1}, &SmartPtrModeling::handleSwap},
78       {{"get"}, &SmartPtrModeling::handleGet}};
79 };
80 } // end of anonymous namespace
81 
82 REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, SVal)
83 
84 // Define the inter-checker API.
85 namespace clang {
86 namespace ento {
87 namespace smartptr {
88 bool isStdSmartPtrCall(const CallEvent &Call) {
89   const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
90   if (!MethodDecl || !MethodDecl->getParent())
91     return false;
92 
93   const auto *RecordDecl = MethodDecl->getParent();
94   if (!RecordDecl || !RecordDecl->getDeclContext()->isStdNamespace())
95     return false;
96 
97   if (RecordDecl->getDeclName().isIdentifier()) {
98     StringRef Name = RecordDecl->getName();
99     return Name == "shared_ptr" || Name == "unique_ptr" || Name == "weak_ptr";
100   }
101   return false;
102 }
103 
104 bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) {
105   const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
106   return InnerPointVal && InnerPointVal->isZeroConstant();
107 }
108 } // namespace smartptr
109 } // namespace ento
110 } // namespace clang
111 
112 // If a region is removed all of the subregions need to be removed too.
113 static TrackedRegionMapTy
114 removeTrackedSubregions(TrackedRegionMapTy RegionMap,
115                         TrackedRegionMapTy::Factory &RegionMapFactory,
116                         const MemRegion *Region) {
117   if (!Region)
118     return RegionMap;
119   for (const auto &E : RegionMap) {
120     if (E.first->isSubRegionOf(Region))
121       RegionMap = RegionMapFactory.remove(RegionMap, E.first);
122   }
123   return RegionMap;
124 }
125 
126 static ProgramStateRef updateSwappedRegion(ProgramStateRef State,
127                                            const MemRegion *Region,
128                                            const SVal *RegionInnerPointerVal) {
129   if (RegionInnerPointerVal) {
130     State = State->set<TrackedRegionMap>(Region, *RegionInnerPointerVal);
131   } else {
132     State = State->remove<TrackedRegionMap>(Region);
133   }
134   return State;
135 }
136 
137 // Helper method to get the inner pointer type of specialized smart pointer
138 // Returns empty type if not found valid inner pointer type.
139 static QualType getInnerPointerType(const CallEvent &Call, CheckerContext &C) {
140   const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
141   if (!MethodDecl || !MethodDecl->getParent())
142     return {};
143 
144   const auto *RecordDecl = MethodDecl->getParent();
145   if (!RecordDecl || !RecordDecl->isInStdNamespace())
146     return {};
147 
148   const auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl);
149   if (!TSD)
150     return {};
151 
152   auto TemplateArgs = TSD->getTemplateArgs().asArray();
153   if (TemplateArgs.size() == 0)
154     return {};
155   auto InnerValueType = TemplateArgs[0].getAsType();
156   return C.getASTContext().getPointerType(InnerValueType.getCanonicalType());
157 }
158 
159 // Helper method to pretty print region and avoid extra spacing.
160 static void checkAndPrettyPrintRegion(llvm::raw_ostream &OS,
161                                       const MemRegion *Region) {
162   if (Region->canPrintPretty()) {
163     OS << " ";
164     Region->printPretty(OS);
165   }
166 }
167 
168 bool SmartPtrModeling::isBoolConversionMethod(const CallEvent &Call) const {
169   // TODO: Update CallDescription to support anonymous calls?
170   // TODO: Handle other methods, such as .get() or .release().
171   // But once we do, we'd need a visitor to explain null dereferences
172   // that are found via such modeling.
173   const auto *CD = dyn_cast_or_null<CXXConversionDecl>(Call.getDecl());
174   return CD && CD->getConversionType()->isBooleanType();
175 }
176 
177 bool SmartPtrModeling::evalCall(const CallEvent &Call,
178                                 CheckerContext &C) const {
179   ProgramStateRef State = C.getState();
180   if (!smartptr::isStdSmartPtrCall(Call))
181     return false;
182 
183   if (isBoolConversionMethod(Call)) {
184     const MemRegion *ThisR =
185         cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
186 
187     if (ModelSmartPtrDereference) {
188       // The check for the region is moved is duplicated in handleBoolOperation
189       // method.
190       // FIXME: Once we model std::move for smart pointers clean up this and use
191       // that modeling.
192       handleBoolConversion(Call, C);
193       return true;
194     } else {
195       if (!move::isMovedFrom(State, ThisR)) {
196         // TODO: Model this case as well. At least, avoid invalidation of
197         // globals.
198         return false;
199       }
200 
201       // TODO: Add a note to bug reports describing this decision.
202       C.addTransition(State->BindExpr(
203           Call.getOriginExpr(), C.getLocationContext(),
204           C.getSValBuilder().makeZeroVal(Call.getResultType())));
205 
206       return true;
207     }
208   }
209 
210   if (!ModelSmartPtrDereference)
211     return false;
212 
213   if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
214     if (CC->getDecl()->isCopyConstructor())
215       return false;
216 
217     const MemRegion *ThisRegion = CC->getCXXThisVal().getAsRegion();
218     if (!ThisRegion)
219       return false;
220 
221     if (CC->getDecl()->isMoveConstructor())
222       return handleMoveCtr(Call, C, ThisRegion);
223 
224     if (Call.getNumArgs() == 0) {
225       auto NullVal = C.getSValBuilder().makeNull();
226       State = State->set<TrackedRegionMap>(ThisRegion, NullVal);
227 
228       C.addTransition(
229           State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
230                                            llvm::raw_ostream &OS) {
231             if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
232                 !BR.isInteresting(ThisRegion))
233               return;
234             OS << "Default constructed smart pointer";
235             checkAndPrettyPrintRegion(OS, ThisRegion);
236             OS << " is null";
237           }));
238     } else {
239       const auto *TrackingExpr = Call.getArgExpr(0);
240       assert(TrackingExpr->getType()->isPointerType() &&
241              "Adding a non pointer value to TrackedRegionMap");
242       auto ArgVal = Call.getArgSVal(0);
243       State = State->set<TrackedRegionMap>(ThisRegion, ArgVal);
244 
245       C.addTransition(State, C.getNoteTag([ThisRegion, TrackingExpr,
246                                            ArgVal](PathSensitiveBugReport &BR,
247                                                    llvm::raw_ostream &OS) {
248         if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
249             !BR.isInteresting(ThisRegion))
250           return;
251         bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
252         OS << "Smart pointer";
253         checkAndPrettyPrintRegion(OS, ThisRegion);
254         if (ArgVal.isZeroConstant())
255           OS << " is constructed using a null value";
256         else
257           OS << " is constructed";
258       }));
259     }
260     return true;
261   }
262 
263   if (handleAssignOp(Call, C))
264     return true;
265 
266   const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call);
267   if (!Handler)
268     return false;
269   (this->**Handler)(Call, C);
270 
271   return C.isDifferent();
272 }
273 
274 void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper,
275                                         CheckerContext &C) const {
276   ProgramStateRef State = C.getState();
277   // Clean up dead regions from the region map.
278   TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
279   for (auto E : TrackedRegions) {
280     const MemRegion *Region = E.first;
281     bool IsRegDead = !SymReaper.isLiveRegion(Region);
282 
283     if (IsRegDead)
284       State = State->remove<TrackedRegionMap>(Region);
285   }
286   C.addTransition(State);
287 }
288 
289 void SmartPtrModeling::printState(raw_ostream &Out, ProgramStateRef State,
290                                   const char *NL, const char *Sep) const {
291   TrackedRegionMapTy RS = State->get<TrackedRegionMap>();
292 
293   if (!RS.isEmpty()) {
294     Out << Sep << "Smart ptr regions :" << NL;
295     for (auto I : RS) {
296       I.first->dumpToStream(Out);
297       if (smartptr::isNullSmartPtr(State, I.first))
298         Out << ": Null";
299       else
300         Out << ": Non Null";
301       Out << NL;
302     }
303   }
304 }
305 
306 ProgramStateRef SmartPtrModeling::checkRegionChanges(
307     ProgramStateRef State, const InvalidatedSymbols *Invalidated,
308     ArrayRef<const MemRegion *> ExplicitRegions,
309     ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
310     const CallEvent *Call) const {
311   TrackedRegionMapTy RegionMap = State->get<TrackedRegionMap>();
312   TrackedRegionMapTy::Factory &RegionMapFactory =
313       State->get_context<TrackedRegionMap>();
314   for (const auto *Region : Regions)
315     RegionMap = removeTrackedSubregions(RegionMap, RegionMapFactory,
316                                         Region->getBaseRegion());
317   return State->set<TrackedRegionMap>(RegionMap);
318 }
319 
320 void SmartPtrModeling::checkLiveSymbols(ProgramStateRef State,
321                                         SymbolReaper &SR) const {
322   // Marking tracked symbols alive
323   TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
324   for (auto I = TrackedRegions.begin(), E = TrackedRegions.end(); I != E; ++I) {
325     SVal Val = I->second;
326     for (auto si = Val.symbol_begin(), se = Val.symbol_end(); si != se; ++si) {
327       SR.markLive(*si);
328     }
329   }
330 }
331 
332 void SmartPtrModeling::handleReset(const CallEvent &Call,
333                                    CheckerContext &C) const {
334   ProgramStateRef State = C.getState();
335   const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
336   if (!IC)
337     return;
338 
339   const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
340   if (!ThisRegion)
341     return;
342 
343   assert(Call.getArgExpr(0)->getType()->isPointerType() &&
344          "Adding a non pointer value to TrackedRegionMap");
345   State = State->set<TrackedRegionMap>(ThisRegion, Call.getArgSVal(0));
346   const auto *TrackingExpr = Call.getArgExpr(0);
347   C.addTransition(
348       State, C.getNoteTag([ThisRegion, TrackingExpr](PathSensitiveBugReport &BR,
349                                                      llvm::raw_ostream &OS) {
350         if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
351             !BR.isInteresting(ThisRegion))
352           return;
353         bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
354         OS << "Smart pointer";
355         checkAndPrettyPrintRegion(OS, ThisRegion);
356         OS << " reset using a null value";
357       }));
358   // TODO: Make sure to ivalidate the region in the Store if we don't have
359   // time to model all methods.
360 }
361 
362 void SmartPtrModeling::handleRelease(const CallEvent &Call,
363                                      CheckerContext &C) const {
364   ProgramStateRef State = C.getState();
365   const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
366   if (!IC)
367     return;
368 
369   const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
370   if (!ThisRegion)
371     return;
372 
373   const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
374 
375   if (InnerPointVal) {
376     State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
377                             *InnerPointVal);
378   }
379 
380   auto ValueToUpdate = C.getSValBuilder().makeNull();
381   State = State->set<TrackedRegionMap>(ThisRegion, ValueToUpdate);
382 
383   C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
384                                                    llvm::raw_ostream &OS) {
385     if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
386         !BR.isInteresting(ThisRegion))
387       return;
388 
389     OS << "Smart pointer";
390     checkAndPrettyPrintRegion(OS, ThisRegion);
391     OS << " is released and set to null";
392   }));
393   // TODO: Add support to enable MallocChecker to start tracking the raw
394   // pointer.
395 }
396 
397 void SmartPtrModeling::handleSwap(const CallEvent &Call,
398                                   CheckerContext &C) const {
399   // To model unique_ptr::swap() method.
400   const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
401   if (!IC)
402     return;
403 
404   const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
405   if (!ThisRegion)
406     return;
407 
408   const auto *ArgRegion = Call.getArgSVal(0).getAsRegion();
409   if (!ArgRegion)
410     return;
411 
412   auto State = C.getState();
413   const auto *ThisRegionInnerPointerVal =
414       State->get<TrackedRegionMap>(ThisRegion);
415   const auto *ArgRegionInnerPointerVal =
416       State->get<TrackedRegionMap>(ArgRegion);
417 
418   // Swap the tracked region values.
419   State = updateSwappedRegion(State, ThisRegion, ArgRegionInnerPointerVal);
420   State = updateSwappedRegion(State, ArgRegion, ThisRegionInnerPointerVal);
421 
422   C.addTransition(
423       State, C.getNoteTag([ThisRegion, ArgRegion](PathSensitiveBugReport &BR,
424                                                   llvm::raw_ostream &OS) {
425         if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
426             !BR.isInteresting(ThisRegion))
427           return;
428         BR.markInteresting(ArgRegion);
429         OS << "Swapped null smart pointer";
430         checkAndPrettyPrintRegion(OS, ArgRegion);
431         OS << " with smart pointer";
432         checkAndPrettyPrintRegion(OS, ThisRegion);
433       }));
434 }
435 
436 void SmartPtrModeling::handleGet(const CallEvent &Call,
437                                  CheckerContext &C) const {
438   ProgramStateRef State = C.getState();
439   const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
440   if (!IC)
441     return;
442 
443   const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
444   if (!ThisRegion)
445     return;
446 
447   SVal InnerPointerVal;
448   if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) {
449     InnerPointerVal = *InnerValPtr;
450   } else {
451     const auto *CallExpr = Call.getOriginExpr();
452     InnerPointerVal = C.getSValBuilder().conjureSymbolVal(
453         CallExpr, C.getLocationContext(), Call.getResultType(), C.blockCount());
454     State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal);
455   }
456 
457   State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
458                           InnerPointerVal);
459   // TODO: Add NoteTag, for how the raw pointer got using 'get' method.
460   C.addTransition(State);
461 }
462 
463 bool SmartPtrModeling::handleAssignOp(const CallEvent &Call,
464                                       CheckerContext &C) const {
465   ProgramStateRef State = C.getState();
466   const auto *OC = dyn_cast<CXXMemberOperatorCall>(&Call);
467   if (!OC)
468     return false;
469   OverloadedOperatorKind OOK = OC->getOverloadedOperator();
470   if (OOK != OO_Equal)
471     return false;
472   const MemRegion *ThisRegion = OC->getCXXThisVal().getAsRegion();
473   if (!ThisRegion)
474     return false;
475 
476   const MemRegion *OtherSmartPtrRegion = OC->getArgSVal(0).getAsRegion();
477   // In case of 'nullptr' or '0' assigned
478   if (!OtherSmartPtrRegion) {
479     bool AssignedNull = Call.getArgSVal(0).isZeroConstant();
480     if (!AssignedNull)
481       return false;
482     auto NullVal = C.getSValBuilder().makeNull();
483     State = State->set<TrackedRegionMap>(ThisRegion, NullVal);
484     C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
485                                                      llvm::raw_ostream &OS) {
486       if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
487           !BR.isInteresting(ThisRegion))
488         return;
489       OS << "Smart pointer";
490       checkAndPrettyPrintRegion(OS, ThisRegion);
491       OS << " is assigned to null";
492     }));
493     return true;
494   }
495 
496   return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion);
497 }
498 
499 bool SmartPtrModeling::handleMoveCtr(const CallEvent &Call, CheckerContext &C,
500                                      const MemRegion *ThisRegion) const {
501   const auto *OtherSmartPtrRegion = Call.getArgSVal(0).getAsRegion();
502   if (!OtherSmartPtrRegion)
503     return false;
504 
505   return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion);
506 }
507 
508 bool SmartPtrModeling::updateMovedSmartPointers(
509     CheckerContext &C, const MemRegion *ThisRegion,
510     const MemRegion *OtherSmartPtrRegion) const {
511   ProgramStateRef State = C.getState();
512   const auto *OtherInnerPtr = State->get<TrackedRegionMap>(OtherSmartPtrRegion);
513   if (OtherInnerPtr) {
514     State = State->set<TrackedRegionMap>(ThisRegion, *OtherInnerPtr);
515     auto NullVal = C.getSValBuilder().makeNull();
516     State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
517     bool IsArgValNull = OtherInnerPtr->isZeroConstant();
518 
519     C.addTransition(
520         State,
521         C.getNoteTag([ThisRegion, OtherSmartPtrRegion, IsArgValNull](
522                          PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
523           if (&BR.getBugType() != smartptr::getNullDereferenceBugType())
524             return;
525           if (BR.isInteresting(OtherSmartPtrRegion)) {
526             OS << "Smart pointer";
527             checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion);
528             OS << " is null after being moved to";
529             checkAndPrettyPrintRegion(OS, ThisRegion);
530           }
531           if (BR.isInteresting(ThisRegion) && IsArgValNull) {
532             OS << "A null pointer value is moved to";
533             checkAndPrettyPrintRegion(OS, ThisRegion);
534             BR.markInteresting(OtherSmartPtrRegion);
535           }
536         }));
537     return true;
538   } else {
539     // In case we dont know anything about value we are moving from
540     // remove the entry from map for which smart pointer got moved to.
541     auto NullVal = C.getSValBuilder().makeNull();
542     State = State->remove<TrackedRegionMap>(ThisRegion);
543     State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
544     C.addTransition(State, C.getNoteTag([OtherSmartPtrRegion,
545                                          ThisRegion](PathSensitiveBugReport &BR,
546                                                      llvm::raw_ostream &OS) {
547       if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
548           !BR.isInteresting(OtherSmartPtrRegion))
549         return;
550       OS << "Smart pointer";
551       checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion);
552       OS << " is null after; previous value moved to";
553       checkAndPrettyPrintRegion(OS, ThisRegion);
554     }));
555     return true;
556   }
557   return false;
558 }
559 
560 void SmartPtrModeling::handleBoolConversion(const CallEvent &Call,
561                                             CheckerContext &C) const {
562   // To model unique_ptr::operator bool
563   ProgramStateRef State = C.getState();
564   const Expr *CallExpr = Call.getOriginExpr();
565   const MemRegion *ThisRegion =
566       cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
567 
568   SVal InnerPointerVal;
569   if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) {
570     InnerPointerVal = *InnerValPtr;
571   } else {
572     // In case of inner pointer SVal is not available we create
573     // conjureSymbolVal for inner pointer value.
574     auto InnerPointerType = getInnerPointerType(Call, C);
575     if (InnerPointerType.isNull())
576       return;
577 
578     const LocationContext *LC = C.getLocationContext();
579     InnerPointerVal = C.getSValBuilder().conjureSymbolVal(
580         CallExpr, LC, InnerPointerType, C.blockCount());
581     State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal);
582   }
583 
584   if (State->isNull(InnerPointerVal).isConstrainedTrue()) {
585     State = State->BindExpr(CallExpr, C.getLocationContext(),
586                             C.getSValBuilder().makeTruthVal(false));
587 
588     C.addTransition(State);
589     return;
590   } else if (State->isNonNull(InnerPointerVal).isConstrainedTrue()) {
591     State = State->BindExpr(CallExpr, C.getLocationContext(),
592                             C.getSValBuilder().makeTruthVal(true));
593 
594     C.addTransition(State);
595     return;
596   } else if (move::isMovedFrom(State, ThisRegion)) {
597     C.addTransition(
598         State->BindExpr(CallExpr, C.getLocationContext(),
599                         C.getSValBuilder().makeZeroVal(Call.getResultType())));
600     return;
601   } else {
602     ProgramStateRef NotNullState, NullState;
603     std::tie(NotNullState, NullState) =
604         State->assume(InnerPointerVal.castAs<DefinedOrUnknownSVal>());
605 
606     auto NullVal = C.getSValBuilder().makeNull();
607     // Explicitly tracking the region as null.
608     NullState = NullState->set<TrackedRegionMap>(ThisRegion, NullVal);
609 
610     NullState = NullState->BindExpr(CallExpr, C.getLocationContext(),
611                                     C.getSValBuilder().makeTruthVal(false));
612     C.addTransition(NullState, C.getNoteTag(
613                                    [ThisRegion](PathSensitiveBugReport &BR,
614                                                 llvm::raw_ostream &OS) {
615                                      OS << "Assuming smart pointer";
616                                      checkAndPrettyPrintRegion(OS, ThisRegion);
617                                      OS << " is null";
618                                    },
619                                    /*IsPrunable=*/true));
620     NotNullState =
621         NotNullState->BindExpr(CallExpr, C.getLocationContext(),
622                                C.getSValBuilder().makeTruthVal(true));
623     C.addTransition(
624         NotNullState,
625         C.getNoteTag(
626             [ThisRegion](PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
627               OS << "Assuming smart pointer";
628               checkAndPrettyPrintRegion(OS, ThisRegion);
629               OS << " is non-null";
630             },
631             /*IsPrunable=*/true));
632     return;
633   }
634 }
635 
636 void ento::registerSmartPtrModeling(CheckerManager &Mgr) {
637   auto *Checker = Mgr.registerChecker<SmartPtrModeling>();
638   Checker->ModelSmartPtrDereference =
639       Mgr.getAnalyzerOptions().getCheckerBooleanOption(
640           Checker, "ModelSmartPtrDereference");
641 }
642 
643 bool ento::shouldRegisterSmartPtrModeling(const CheckerManager &mgr) {
644   const LangOptions &LO = mgr.getLangOpts();
645   return LO.CPlusPlus;
646 }
647