Home | History | Annotate | Line # | Download | only in Checkers
      1 // MoveChecker.cpp - Check use of moved-from objects. - 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 defines checker which checks for potential misuses of a moved-from
     10 // object. That means method calls on the object or copying it in moved-from
     11 // state.
     12 //
     13 //===----------------------------------------------------------------------===//
     14 
     15 #include "clang/AST/Attr.h"
     16 #include "clang/AST/ExprCXX.h"
     17 #include "clang/Driver/DriverDiagnostic.h"
     18 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
     19 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
     20 #include "clang/StaticAnalyzer/Core/Checker.h"
     21 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
     22 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
     23 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
     24 #include "llvm/ADT/StringSet.h"
     25 
     26 using namespace clang;
     27 using namespace ento;
     28 
     29 namespace {
     30 struct RegionState {
     31 private:
     32   enum Kind { Moved, Reported } K;
     33   RegionState(Kind InK) : K(InK) {}
     34 
     35 public:
     36   bool isReported() const { return K == Reported; }
     37   bool isMoved() const { return K == Moved; }
     38 
     39   static RegionState getReported() { return RegionState(Reported); }
     40   static RegionState getMoved() { return RegionState(Moved); }
     41 
     42   bool operator==(const RegionState &X) const { return K == X.K; }
     43   void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); }
     44 };
     45 } // end of anonymous namespace
     46 
     47 namespace {
     48 class MoveChecker
     49     : public Checker<check::PreCall, check::PostCall,
     50                      check::DeadSymbols, check::RegionChanges> {
     51 public:
     52   void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
     53   void checkPreCall(const CallEvent &MC, CheckerContext &C) const;
     54   void checkPostCall(const CallEvent &MC, CheckerContext &C) const;
     55   void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
     56   ProgramStateRef
     57   checkRegionChanges(ProgramStateRef State,
     58                      const InvalidatedSymbols *Invalidated,
     59                      ArrayRef<const MemRegion *> RequestedRegions,
     60                      ArrayRef<const MemRegion *> InvalidatedRegions,
     61                      const LocationContext *LCtx, const CallEvent *Call) const;
     62   void printState(raw_ostream &Out, ProgramStateRef State,
     63                   const char *NL, const char *Sep) const override;
     64 
     65 private:
     66   enum MisuseKind { MK_FunCall, MK_Copy, MK_Move, MK_Dereference };
     67   enum StdObjectKind { SK_NonStd, SK_Unsafe, SK_Safe, SK_SmartPtr };
     68 
     69   enum AggressivenessKind { // In any case, don't warn after a reset.
     70     AK_Invalid = -1,
     71     AK_KnownsOnly = 0,      // Warn only about known move-unsafe classes.
     72     AK_KnownsAndLocals = 1, // Also warn about all local objects.
     73     AK_All = 2,             // Warn on any use-after-move.
     74     AK_NumKinds = AK_All
     75   };
     76 
     77   static bool misuseCausesCrash(MisuseKind MK) {
     78     return MK == MK_Dereference;
     79   }
     80 
     81   struct ObjectKind {
     82     // Is this a local variable or a local rvalue reference?
     83     bool IsLocal;
     84     // Is this an STL object? If so, of what kind?
     85     StdObjectKind StdKind;
     86   };
     87 
     88   // STL smart pointers are automatically re-initialized to null when moved
     89   // from. So we can't warn on many methods, but we can warn when it is
     90   // dereferenced, which is UB even if the resulting lvalue never gets read.
     91   const llvm::StringSet<> StdSmartPtrClasses = {
     92       "shared_ptr",
     93       "unique_ptr",
     94       "weak_ptr",
     95   };
     96 
     97   // Not all of these are entirely move-safe, but they do provide *some*
     98   // guarantees, and it means that somebody is using them after move
     99   // in a valid manner.
    100   // TODO: We can still try to identify *unsafe* use after move,
    101   // like we did with smart pointers.
    102   const llvm::StringSet<> StdSafeClasses = {
    103       "basic_filebuf",
    104       "basic_ios",
    105       "future",
    106       "optional",
    107       "packaged_task",
    108       "promise",
    109       "shared_future",
    110       "shared_lock",
    111       "thread",
    112       "unique_lock",
    113   };
    114 
    115   // Should we bother tracking the state of the object?
    116   bool shouldBeTracked(ObjectKind OK) const {
    117     // In non-aggressive mode, only warn on use-after-move of local variables
    118     // (or local rvalue references) and of STL objects. The former is possible
    119     // because local variables (or local rvalue references) are not tempting
    120     // their user to re-use the storage. The latter is possible because STL
    121     // objects are known to end up in a valid but unspecified state after the
    122     // move and their state-reset methods are also known, which allows us to
    123     // predict precisely when use-after-move is invalid.
    124     // Some STL objects are known to conform to additional contracts after move,
    125     // so they are not tracked. However, smart pointers specifically are tracked
    126     // because we can perform extra checking over them.
    127     // In aggressive mode, warn on any use-after-move because the user has
    128     // intentionally asked us to completely eliminate use-after-move
    129     // in his code.
    130     return (Aggressiveness == AK_All) ||
    131            (Aggressiveness >= AK_KnownsAndLocals && OK.IsLocal) ||
    132            OK.StdKind == SK_Unsafe || OK.StdKind == SK_SmartPtr;
    133   }
    134 
    135   // Some objects only suffer from some kinds of misuses, but we need to track
    136   // them anyway because we cannot know in advance what misuse will we find.
    137   bool shouldWarnAbout(ObjectKind OK, MisuseKind MK) const {
    138     // Additionally, only warn on smart pointers when they are dereferenced (or
    139     // local or we are aggressive).
    140     return shouldBeTracked(OK) &&
    141            ((Aggressiveness == AK_All) ||
    142             (Aggressiveness >= AK_KnownsAndLocals && OK.IsLocal) ||
    143             OK.StdKind != SK_SmartPtr || MK == MK_Dereference);
    144   }
    145 
    146   // Obtains ObjectKind of an object. Because class declaration cannot always
    147   // be easily obtained from the memory region, it is supplied separately.
    148   ObjectKind classifyObject(const MemRegion *MR, const CXXRecordDecl *RD) const;
    149 
    150   // Classifies the object and dumps a user-friendly description string to
    151   // the stream.
    152   void explainObject(llvm::raw_ostream &OS, const MemRegion *MR,
    153                      const CXXRecordDecl *RD, MisuseKind MK) const;
    154 
    155   bool belongsTo(const CXXRecordDecl *RD, const llvm::StringSet<> &Set) const;
    156 
    157   class MovedBugVisitor : public BugReporterVisitor {
    158   public:
    159     MovedBugVisitor(const MoveChecker &Chk, const MemRegion *R,
    160                     const CXXRecordDecl *RD, MisuseKind MK)
    161         : Chk(Chk), Region(R), RD(RD), MK(MK), Found(false) {}
    162 
    163     void Profile(llvm::FoldingSetNodeID &ID) const override {
    164       static int X = 0;
    165       ID.AddPointer(&X);
    166       ID.AddPointer(Region);
    167       // Don't add RD because it's, in theory, uniquely determined by
    168       // the region. In practice though, it's not always possible to obtain
    169       // the declaration directly from the region, that's why we store it
    170       // in the first place.
    171     }
    172 
    173     PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
    174                                      BugReporterContext &BRC,
    175                                      PathSensitiveBugReport &BR) override;
    176 
    177   private:
    178     const MoveChecker &Chk;
    179     // The tracked region.
    180     const MemRegion *Region;
    181     // The class of the tracked object.
    182     const CXXRecordDecl *RD;
    183     // How exactly the object was misused.
    184     const MisuseKind MK;
    185     bool Found;
    186   };
    187 
    188   AggressivenessKind Aggressiveness;
    189 
    190 public:
    191   void setAggressiveness(StringRef Str, CheckerManager &Mgr) {
    192     Aggressiveness =
    193         llvm::StringSwitch<AggressivenessKind>(Str)
    194             .Case("KnownsOnly", AK_KnownsOnly)
    195             .Case("KnownsAndLocals", AK_KnownsAndLocals)
    196             .Case("All", AK_All)
    197             .Default(AK_Invalid);
    198 
    199     if (Aggressiveness == AK_Invalid)
    200       Mgr.reportInvalidCheckerOptionValue(this, "WarnOn",
    201           "either \"KnownsOnly\", \"KnownsAndLocals\" or \"All\" string value");
    202   };
    203 
    204 private:
    205   BugType BT{this, "Use-after-move", categories::CXXMoveSemantics};
    206 
    207   // Check if the given form of potential misuse of a given object
    208   // should be reported. If so, get it reported. The callback from which
    209   // this function was called should immediately return after the call
    210   // because this function adds one or two transitions.
    211   void modelUse(ProgramStateRef State, const MemRegion *Region,
    212                 const CXXRecordDecl *RD, MisuseKind MK,
    213                 CheckerContext &C) const;
    214 
    215   // Returns the exploded node against which the report was emitted.
    216   // The caller *must* add any further transitions against this node.
    217   ExplodedNode *reportBug(const MemRegion *Region, const CXXRecordDecl *RD,
    218                           CheckerContext &C, MisuseKind MK) const;
    219 
    220   bool isInMoveSafeContext(const LocationContext *LC) const;
    221   bool isStateResetMethod(const CXXMethodDecl *MethodDec) const;
    222   bool isMoveSafeMethod(const CXXMethodDecl *MethodDec) const;
    223   const ExplodedNode *getMoveLocation(const ExplodedNode *N,
    224                                       const MemRegion *Region,
    225                                       CheckerContext &C) const;
    226 };
    227 } // end anonymous namespace
    228 
    229 REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, RegionState)
    230 
    231 // Define the inter-checker API.
    232 namespace clang {
    233 namespace ento {
    234 namespace move {
    235 bool isMovedFrom(ProgramStateRef State, const MemRegion *Region) {
    236   const RegionState *RS = State->get<TrackedRegionMap>(Region);
    237   return RS && (RS->isMoved() || RS->isReported());
    238 }
    239 } // namespace move
    240 } // namespace ento
    241 } // namespace clang
    242 
    243 // If a region is removed all of the subregions needs to be removed too.
    244 static ProgramStateRef removeFromState(ProgramStateRef State,
    245                                        const MemRegion *Region) {
    246   if (!Region)
    247     return State;
    248   for (auto &E : State->get<TrackedRegionMap>()) {
    249     if (E.first->isSubRegionOf(Region))
    250       State = State->remove<TrackedRegionMap>(E.first);
    251   }
    252   return State;
    253 }
    254 
    255 static bool isAnyBaseRegionReported(ProgramStateRef State,
    256                                     const MemRegion *Region) {
    257   for (auto &E : State->get<TrackedRegionMap>()) {
    258     if (Region->isSubRegionOf(E.first) && E.second.isReported())
    259       return true;
    260   }
    261   return false;
    262 }
    263 
    264 static const MemRegion *unwrapRValueReferenceIndirection(const MemRegion *MR) {
    265   if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(MR)) {
    266     SymbolRef Sym = SR->getSymbol();
    267     if (Sym->getType()->isRValueReferenceType())
    268       if (const MemRegion *OriginMR = Sym->getOriginRegion())
    269         return OriginMR;
    270   }
    271   return MR;
    272 }
    273 
    274 PathDiagnosticPieceRef
    275 MoveChecker::MovedBugVisitor::VisitNode(const ExplodedNode *N,
    276                                         BugReporterContext &BRC,
    277                                         PathSensitiveBugReport &BR) {
    278   // We need only the last move of the reported object's region.
    279   // The visitor walks the ExplodedGraph backwards.
    280   if (Found)
    281     return nullptr;
    282   ProgramStateRef State = N->getState();
    283   ProgramStateRef StatePrev = N->getFirstPred()->getState();
    284   const RegionState *TrackedObject = State->get<TrackedRegionMap>(Region);
    285   const RegionState *TrackedObjectPrev =
    286       StatePrev->get<TrackedRegionMap>(Region);
    287   if (!TrackedObject)
    288     return nullptr;
    289   if (TrackedObjectPrev && TrackedObject)
    290     return nullptr;
    291 
    292   // Retrieve the associated statement.
    293   const Stmt *S = N->getStmtForDiagnostics();
    294   if (!S)
    295     return nullptr;
    296   Found = true;
    297 
    298   SmallString<128> Str;
    299   llvm::raw_svector_ostream OS(Str);
    300 
    301   ObjectKind OK = Chk.classifyObject(Region, RD);
    302   switch (OK.StdKind) {
    303     case SK_SmartPtr:
    304       if (MK == MK_Dereference) {
    305         OS << "Smart pointer";
    306         Chk.explainObject(OS, Region, RD, MK);
    307         OS << " is reset to null when moved from";
    308         break;
    309       }
    310 
    311       // If it's not a dereference, we don't care if it was reset to null
    312       // or that it is even a smart pointer.
    313       LLVM_FALLTHROUGH;
    314     case SK_NonStd:
    315     case SK_Safe:
    316       OS << "Object";
    317       Chk.explainObject(OS, Region, RD, MK);
    318       OS << " is moved";
    319       break;
    320     case SK_Unsafe:
    321       OS << "Object";
    322       Chk.explainObject(OS, Region, RD, MK);
    323       OS << " is left in a valid but unspecified state after move";
    324       break;
    325   }
    326 
    327   // Generate the extra diagnostic.
    328   PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
    329                              N->getLocationContext());
    330   return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true);
    331 }
    332 
    333 const ExplodedNode *MoveChecker::getMoveLocation(const ExplodedNode *N,
    334                                                  const MemRegion *Region,
    335                                                  CheckerContext &C) const {
    336   // Walk the ExplodedGraph backwards and find the first node that referred to
    337   // the tracked region.
    338   const ExplodedNode *MoveNode = N;
    339 
    340   while (N) {
    341     ProgramStateRef State = N->getState();
    342     if (!State->get<TrackedRegionMap>(Region))
    343       break;
    344     MoveNode = N;
    345     N = N->pred_empty() ? nullptr : *(N->pred_begin());
    346   }
    347   return MoveNode;
    348 }
    349 
    350 void MoveChecker::modelUse(ProgramStateRef State, const MemRegion *Region,
    351                            const CXXRecordDecl *RD, MisuseKind MK,
    352                            CheckerContext &C) const {
    353   assert(!C.isDifferent() && "No transitions should have been made by now");
    354   const RegionState *RS = State->get<TrackedRegionMap>(Region);
    355   ObjectKind OK = classifyObject(Region, RD);
    356 
    357   // Just in case: if it's not a smart pointer but it does have operator *,
    358   // we shouldn't call the bug a dereference.
    359   if (MK == MK_Dereference && OK.StdKind != SK_SmartPtr)
    360     MK = MK_FunCall;
    361 
    362   if (!RS || !shouldWarnAbout(OK, MK)
    363           || isInMoveSafeContext(C.getLocationContext())) {
    364     // Finalize changes made by the caller.
    365     C.addTransition(State);
    366     return;
    367   }
    368 
    369   // Don't report it in case if any base region is already reported.
    370   // But still generate a sink in case of UB.
    371   // And still finalize changes made by the caller.
    372   if (isAnyBaseRegionReported(State, Region)) {
    373     if (misuseCausesCrash(MK)) {
    374       C.generateSink(State, C.getPredecessor());
    375     } else {
    376       C.addTransition(State);
    377     }
    378     return;
    379   }
    380 
    381   ExplodedNode *N = reportBug(Region, RD, C, MK);
    382 
    383   // If the program has already crashed on this path, don't bother.
    384   if (N->isSink())
    385     return;
    386 
    387   State = State->set<TrackedRegionMap>(Region, RegionState::getReported());
    388   C.addTransition(State, N);
    389 }
    390 
    391 ExplodedNode *MoveChecker::reportBug(const MemRegion *Region,
    392                                      const CXXRecordDecl *RD, CheckerContext &C,
    393                                      MisuseKind MK) const {
    394   if (ExplodedNode *N = misuseCausesCrash(MK) ? C.generateErrorNode()
    395                                               : C.generateNonFatalErrorNode()) {
    396     // Uniqueing report to the same object.
    397     PathDiagnosticLocation LocUsedForUniqueing;
    398     const ExplodedNode *MoveNode = getMoveLocation(N, Region, C);
    399 
    400     if (const Stmt *MoveStmt = MoveNode->getStmtForDiagnostics())
    401       LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
    402           MoveStmt, C.getSourceManager(), MoveNode->getLocationContext());
    403 
    404     // Creating the error message.
    405     llvm::SmallString<128> Str;
    406     llvm::raw_svector_ostream OS(Str);
    407     switch(MK) {
    408       case MK_FunCall:
    409         OS << "Method called on moved-from object";
    410         explainObject(OS, Region, RD, MK);
    411         break;
    412       case MK_Copy:
    413         OS << "Moved-from object";
    414         explainObject(OS, Region, RD, MK);
    415         OS << " is copied";
    416         break;
    417       case MK_Move:
    418         OS << "Moved-from object";
    419         explainObject(OS, Region, RD, MK);
    420         OS << " is moved";
    421         break;
    422       case MK_Dereference:
    423         OS << "Dereference of null smart pointer";
    424         explainObject(OS, Region, RD, MK);
    425         break;
    426     }
    427 
    428     auto R = std::make_unique<PathSensitiveBugReport>(
    429         BT, OS.str(), N, LocUsedForUniqueing,
    430         MoveNode->getLocationContext()->getDecl());
    431     R->addVisitor(std::make_unique<MovedBugVisitor>(*this, Region, RD, MK));
    432     C.emitReport(std::move(R));
    433     return N;
    434   }
    435   return nullptr;
    436 }
    437 
    438 void MoveChecker::checkPostCall(const CallEvent &Call,
    439                                 CheckerContext &C) const {
    440   const auto *AFC = dyn_cast<AnyFunctionCall>(&Call);
    441   if (!AFC)
    442     return;
    443 
    444   ProgramStateRef State = C.getState();
    445   const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(AFC->getDecl());
    446   if (!MethodDecl)
    447     return;
    448 
    449   // Check if an object became moved-from.
    450   // Object can become moved from after a call to move assignment operator or
    451   // move constructor .
    452   const auto *ConstructorDecl = dyn_cast<CXXConstructorDecl>(MethodDecl);
    453   if (ConstructorDecl && !ConstructorDecl->isMoveConstructor())
    454     return;
    455 
    456   if (!ConstructorDecl && !MethodDecl->isMoveAssignmentOperator())
    457     return;
    458 
    459   const auto ArgRegion = AFC->getArgSVal(0).getAsRegion();
    460   if (!ArgRegion)
    461     return;
    462 
    463   // Skip moving the object to itself.
    464   const auto *CC = dyn_cast_or_null<CXXConstructorCall>(&Call);
    465   if (CC && CC->getCXXThisVal().getAsRegion() == ArgRegion)
    466     return;
    467 
    468   if (const auto *IC = dyn_cast<CXXInstanceCall>(AFC))
    469     if (IC->getCXXThisVal().getAsRegion() == ArgRegion)
    470       return;
    471 
    472   const MemRegion *BaseRegion = ArgRegion->getBaseRegion();
    473   // Skip temp objects because of their short lifetime.
    474   if (BaseRegion->getAs<CXXTempObjectRegion>() ||
    475       AFC->getArgExpr(0)->isRValue())
    476     return;
    477   // If it has already been reported do not need to modify the state.
    478 
    479   if (State->get<TrackedRegionMap>(ArgRegion))
    480     return;
    481 
    482   const CXXRecordDecl *RD = MethodDecl->getParent();
    483   ObjectKind OK = classifyObject(ArgRegion, RD);
    484   if (shouldBeTracked(OK)) {
    485     // Mark object as moved-from.
    486     State = State->set<TrackedRegionMap>(ArgRegion, RegionState::getMoved());
    487     C.addTransition(State);
    488     return;
    489   }
    490   assert(!C.isDifferent() && "Should not have made transitions on this path!");
    491 }
    492 
    493 bool MoveChecker::isMoveSafeMethod(const CXXMethodDecl *MethodDec) const {
    494   // We abandon the cases where bool/void/void* conversion happens.
    495   if (const auto *ConversionDec =
    496           dyn_cast_or_null<CXXConversionDecl>(MethodDec)) {
    497     const Type *Tp = ConversionDec->getConversionType().getTypePtrOrNull();
    498     if (!Tp)
    499       return false;
    500     if (Tp->isBooleanType() || Tp->isVoidType() || Tp->isVoidPointerType())
    501       return true;
    502   }
    503   // Function call `empty` can be skipped.
    504   return (MethodDec && MethodDec->getDeclName().isIdentifier() &&
    505       (MethodDec->getName().lower() == "empty" ||
    506        MethodDec->getName().lower() == "isempty"));
    507 }
    508 
    509 bool MoveChecker::isStateResetMethod(const CXXMethodDecl *MethodDec) const {
    510   if (!MethodDec)
    511       return false;
    512   if (MethodDec->hasAttr<ReinitializesAttr>())
    513       return true;
    514   if (MethodDec->getDeclName().isIdentifier()) {
    515     std::string MethodName = MethodDec->getName().lower();
    516     // TODO: Some of these methods (eg., resize) are not always resetting
    517     // the state, so we should consider looking at the arguments.
    518     if (MethodName == "assign" || MethodName == "clear" ||
    519         MethodName == "destroy" || MethodName == "reset" ||
    520         MethodName == "resize" || MethodName == "shrink")
    521       return true;
    522   }
    523   return false;
    524 }
    525 
    526 // Don't report an error inside a move related operation.
    527 // We assume that the programmer knows what she does.
    528 bool MoveChecker::isInMoveSafeContext(const LocationContext *LC) const {
    529   do {
    530     const auto *CtxDec = LC->getDecl();
    531     auto *CtorDec = dyn_cast_or_null<CXXConstructorDecl>(CtxDec);
    532     auto *DtorDec = dyn_cast_or_null<CXXDestructorDecl>(CtxDec);
    533     auto *MethodDec = dyn_cast_or_null<CXXMethodDecl>(CtxDec);
    534     if (DtorDec || (CtorDec && CtorDec->isCopyOrMoveConstructor()) ||
    535         (MethodDec && MethodDec->isOverloadedOperator() &&
    536          MethodDec->getOverloadedOperator() == OO_Equal) ||
    537         isStateResetMethod(MethodDec) || isMoveSafeMethod(MethodDec))
    538       return true;
    539   } while ((LC = LC->getParent()));
    540   return false;
    541 }
    542 
    543 bool MoveChecker::belongsTo(const CXXRecordDecl *RD,
    544                             const llvm::StringSet<> &Set) const {
    545   const IdentifierInfo *II = RD->getIdentifier();
    546   return II && Set.count(II->getName());
    547 }
    548 
    549 MoveChecker::ObjectKind
    550 MoveChecker::classifyObject(const MemRegion *MR,
    551                             const CXXRecordDecl *RD) const {
    552   // Local variables and local rvalue references are classified as "Local".
    553   // For the purposes of this checker, we classify move-safe STL types
    554   // as not-"STL" types, because that's how the checker treats them.
    555   MR = unwrapRValueReferenceIndirection(MR);
    556   bool IsLocal =
    557       MR && isa<VarRegion>(MR) && isa<StackSpaceRegion>(MR->getMemorySpace());
    558 
    559   if (!RD || !RD->getDeclContext()->isStdNamespace())
    560     return { IsLocal, SK_NonStd };
    561 
    562   if (belongsTo(RD, StdSmartPtrClasses))
    563     return { IsLocal, SK_SmartPtr };
    564 
    565   if (belongsTo(RD, StdSafeClasses))
    566     return { IsLocal, SK_Safe };
    567 
    568   return { IsLocal, SK_Unsafe };
    569 }
    570 
    571 void MoveChecker::explainObject(llvm::raw_ostream &OS, const MemRegion *MR,
    572                                 const CXXRecordDecl *RD, MisuseKind MK) const {
    573   // We may need a leading space every time we actually explain anything,
    574   // and we never know if we are to explain anything until we try.
    575   if (const auto DR =
    576           dyn_cast_or_null<DeclRegion>(unwrapRValueReferenceIndirection(MR))) {
    577     const auto *RegionDecl = cast<NamedDecl>(DR->getDecl());
    578     OS << " '" << RegionDecl->getDeclName() << "'";
    579   }
    580 
    581   ObjectKind OK = classifyObject(MR, RD);
    582   switch (OK.StdKind) {
    583     case SK_NonStd:
    584     case SK_Safe:
    585       break;
    586     case SK_SmartPtr:
    587       if (MK != MK_Dereference)
    588         break;
    589 
    590       // We only care about the type if it's a dereference.
    591       LLVM_FALLTHROUGH;
    592     case SK_Unsafe:
    593       OS << " of type '" << RD->getQualifiedNameAsString() << "'";
    594       break;
    595   };
    596 }
    597 
    598 void MoveChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const {
    599   ProgramStateRef State = C.getState();
    600 
    601   // Remove the MemRegions from the map on which a ctor/dtor call or assignment
    602   // happened.
    603 
    604   // Checking constructor calls.
    605   if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
    606     State = removeFromState(State, CC->getCXXThisVal().getAsRegion());
    607     auto CtorDec = CC->getDecl();
    608     // Check for copying a moved-from object and report the bug.
    609     if (CtorDec && CtorDec->isCopyOrMoveConstructor()) {
    610       const MemRegion *ArgRegion = CC->getArgSVal(0).getAsRegion();
    611       const CXXRecordDecl *RD = CtorDec->getParent();
    612       MisuseKind MK = CtorDec->isMoveConstructor() ? MK_Move : MK_Copy;
    613       modelUse(State, ArgRegion, RD, MK, C);
    614       return;
    615     }
    616   }
    617 
    618   const auto IC = dyn_cast<CXXInstanceCall>(&Call);
    619   if (!IC)
    620     return;
    621 
    622   // Calling a destructor on a moved object is fine.
    623   if (isa<CXXDestructorCall>(IC))
    624     return;
    625 
    626   const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
    627   if (!ThisRegion)
    628     return;
    629 
    630   // The remaining part is check only for method call on a moved-from object.
    631   const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(IC->getDecl());
    632   if (!MethodDecl)
    633     return;
    634 
    635   // We want to investigate the whole object, not only sub-object of a parent
    636   // class in which the encountered method defined.
    637   ThisRegion = ThisRegion->getMostDerivedObjectRegion();
    638 
    639   if (isStateResetMethod(MethodDecl)) {
    640     State = removeFromState(State, ThisRegion);
    641     C.addTransition(State);
    642     return;
    643   }
    644 
    645   if (isMoveSafeMethod(MethodDecl))
    646     return;
    647 
    648   // Store class declaration as well, for bug reporting purposes.
    649   const CXXRecordDecl *RD = MethodDecl->getParent();
    650 
    651   if (MethodDecl->isOverloadedOperator()) {
    652     OverloadedOperatorKind OOK = MethodDecl->getOverloadedOperator();
    653 
    654     if (OOK == OO_Equal) {
    655       // Remove the tracked object for every assignment operator, but report bug
    656       // only for move or copy assignment's argument.
    657       State = removeFromState(State, ThisRegion);
    658 
    659       if (MethodDecl->isCopyAssignmentOperator() ||
    660           MethodDecl->isMoveAssignmentOperator()) {
    661         const MemRegion *ArgRegion = IC->getArgSVal(0).getAsRegion();
    662         MisuseKind MK =
    663             MethodDecl->isMoveAssignmentOperator() ? MK_Move : MK_Copy;
    664         modelUse(State, ArgRegion, RD, MK, C);
    665         return;
    666       }
    667       C.addTransition(State);
    668       return;
    669     }
    670 
    671     if (OOK == OO_Star || OOK == OO_Arrow) {
    672       modelUse(State, ThisRegion, RD, MK_Dereference, C);
    673       return;
    674     }
    675   }
    676 
    677   modelUse(State, ThisRegion, RD, MK_FunCall, C);
    678 }
    679 
    680 void MoveChecker::checkDeadSymbols(SymbolReaper &SymReaper,
    681                                    CheckerContext &C) const {
    682   ProgramStateRef State = C.getState();
    683   TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
    684   for (auto E : TrackedRegions) {
    685     const MemRegion *Region = E.first;
    686     bool IsRegDead = !SymReaper.isLiveRegion(Region);
    687 
    688     // Remove the dead regions from the region map.
    689     if (IsRegDead) {
    690       State = State->remove<TrackedRegionMap>(Region);
    691     }
    692   }
    693   C.addTransition(State);
    694 }
    695 
    696 ProgramStateRef MoveChecker::checkRegionChanges(
    697     ProgramStateRef State, const InvalidatedSymbols *Invalidated,
    698     ArrayRef<const MemRegion *> RequestedRegions,
    699     ArrayRef<const MemRegion *> InvalidatedRegions,
    700     const LocationContext *LCtx, const CallEvent *Call) const {
    701   if (Call) {
    702     // Relax invalidation upon function calls: only invalidate parameters
    703     // that are passed directly via non-const pointers or non-const references
    704     // or rvalue references.
    705     // In case of an InstanceCall don't invalidate the this-region since
    706     // it is fully handled in checkPreCall and checkPostCall.
    707     const MemRegion *ThisRegion = nullptr;
    708     if (const auto *IC = dyn_cast<CXXInstanceCall>(Call))
    709       ThisRegion = IC->getCXXThisVal().getAsRegion();
    710 
    711     // Requested ("explicit") regions are the regions passed into the call
    712     // directly, but not all of them end up being invalidated.
    713     // But when they do, they appear in the InvalidatedRegions array as well.
    714     for (const auto *Region : RequestedRegions) {
    715       if (ThisRegion != Region) {
    716         if (llvm::find(InvalidatedRegions, Region) !=
    717             std::end(InvalidatedRegions)) {
    718           State = removeFromState(State, Region);
    719         }
    720       }
    721     }
    722   } else {
    723     // For invalidations that aren't caused by calls, assume nothing. In
    724     // particular, direct write into an object's field invalidates the status.
    725     for (const auto *Region : InvalidatedRegions)
    726       State = removeFromState(State, Region->getBaseRegion());
    727   }
    728 
    729   return State;
    730 }
    731 
    732 void MoveChecker::printState(raw_ostream &Out, ProgramStateRef State,
    733                              const char *NL, const char *Sep) const {
    734 
    735   TrackedRegionMapTy RS = State->get<TrackedRegionMap>();
    736 
    737   if (!RS.isEmpty()) {
    738     Out << Sep << "Moved-from objects :" << NL;
    739     for (auto I: RS) {
    740       I.first->dumpToStream(Out);
    741       if (I.second.isMoved())
    742         Out << ": moved";
    743       else
    744         Out << ": moved and reported";
    745       Out << NL;
    746     }
    747   }
    748 }
    749 void ento::registerMoveChecker(CheckerManager &mgr) {
    750   MoveChecker *chk = mgr.registerChecker<MoveChecker>();
    751   chk->setAggressiveness(
    752       mgr.getAnalyzerOptions().getCheckerStringOption(chk, "WarnOn"), mgr);
    753 }
    754 
    755 bool ento::shouldRegisterMoveChecker(const CheckerManager &mgr) {
    756   return true;
    757 }
    758