12c5945caSArtem Dergachev // MoveChecker.cpp - Check use of moved-from objects. - C++ ---------------===//
22c5945caSArtem Dergachev //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
62c5945caSArtem Dergachev //
72c5945caSArtem Dergachev //===----------------------------------------------------------------------===//
82c5945caSArtem Dergachev //
92c5945caSArtem Dergachev // This defines checker which checks for potential misuses of a moved-from
102c5945caSArtem Dergachev // object. That means method calls on the object or copying it in moved-from
112c5945caSArtem Dergachev // state.
122c5945caSArtem Dergachev //
132c5945caSArtem Dergachev //===----------------------------------------------------------------------===//
142c5945caSArtem Dergachev 
1560573ae6SReid Kleckner #include "clang/AST/Attr.h"
162c5945caSArtem Dergachev #include "clang/AST/ExprCXX.h"
17748c139aSKristof Umann #include "clang/Driver/DriverDiagnostic.h"
1876a21502SKristof Umann #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
192c5945caSArtem Dergachev #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
202c5945caSArtem Dergachev #include "clang/StaticAnalyzer/Core/Checker.h"
212c5945caSArtem Dergachev #include "clang/StaticAnalyzer/Core/CheckerManager.h"
222c5945caSArtem Dergachev #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
232c5945caSArtem Dergachev #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
2411cadc3eSArtem Dergachev #include "llvm/ADT/StringSet.h"
252c5945caSArtem Dergachev 
262c5945caSArtem Dergachev using namespace clang;
272c5945caSArtem Dergachev using namespace ento;
282c5945caSArtem Dergachev 
292c5945caSArtem Dergachev namespace {
302c5945caSArtem Dergachev struct RegionState {
312c5945caSArtem Dergachev private:
322c5945caSArtem Dergachev   enum Kind { Moved, Reported } K;
RegionState__anon1dac178a0111::RegionState332c5945caSArtem Dergachev   RegionState(Kind InK) : K(InK) {}
342c5945caSArtem Dergachev 
352c5945caSArtem Dergachev public:
isReported__anon1dac178a0111::RegionState362c5945caSArtem Dergachev   bool isReported() const { return K == Reported; }
isMoved__anon1dac178a0111::RegionState372c5945caSArtem Dergachev   bool isMoved() const { return K == Moved; }
382c5945caSArtem Dergachev 
getReported__anon1dac178a0111::RegionState392c5945caSArtem Dergachev   static RegionState getReported() { return RegionState(Reported); }
getMoved__anon1dac178a0111::RegionState402c5945caSArtem Dergachev   static RegionState getMoved() { return RegionState(Moved); }
412c5945caSArtem Dergachev 
operator ==__anon1dac178a0111::RegionState422c5945caSArtem Dergachev   bool operator==(const RegionState &X) const { return K == X.K; }
Profile__anon1dac178a0111::RegionState432c5945caSArtem Dergachev   void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); }
442c5945caSArtem Dergachev };
452b500cbdSArtem Dergachev } // end of anonymous namespace
462c5945caSArtem Dergachev 
472b500cbdSArtem Dergachev namespace {
482c5945caSArtem Dergachev class MoveChecker
496c0b2ce1SArtem Dergachev     : public Checker<check::PreCall, check::PostCall,
502c5945caSArtem Dergachev                      check::DeadSymbols, check::RegionChanges> {
512c5945caSArtem Dergachev public:
522c5945caSArtem Dergachev   void checkPreCall(const CallEvent &MC, CheckerContext &C) const;
532c5945caSArtem Dergachev   void checkPostCall(const CallEvent &MC, CheckerContext &C) const;
542c5945caSArtem Dergachev   void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
552c5945caSArtem Dergachev   ProgramStateRef
562c5945caSArtem Dergachev   checkRegionChanges(ProgramStateRef State,
572c5945caSArtem Dergachev                      const InvalidatedSymbols *Invalidated,
5812f7c2baSArtem Dergachev                      ArrayRef<const MemRegion *> RequestedRegions,
5912f7c2baSArtem Dergachev                      ArrayRef<const MemRegion *> InvalidatedRegions,
602c5945caSArtem Dergachev                      const LocationContext *LCtx, const CallEvent *Call) const;
612c5945caSArtem Dergachev   void printState(raw_ostream &Out, ProgramStateRef State,
622c5945caSArtem Dergachev                   const char *NL, const char *Sep) const override;
632c5945caSArtem Dergachev 
642c5945caSArtem Dergachev private:
6569909540SArtem Dergachev   enum MisuseKind { MK_FunCall, MK_Copy, MK_Move, MK_Dereference };
66b4bde2acSArtem Dergachev   enum StdObjectKind { SK_NonStd, SK_Unsafe, SK_Safe, SK_SmartPtr };
6769909540SArtem Dergachev 
682b500cbdSArtem Dergachev   enum AggressivenessKind { // In any case, don't warn after a reset.
692b500cbdSArtem Dergachev     AK_Invalid = -1,
702b500cbdSArtem Dergachev     AK_KnownsOnly = 0,      // Warn only about known move-unsafe classes.
712b500cbdSArtem Dergachev     AK_KnownsAndLocals = 1, // Also warn about all local objects.
722b500cbdSArtem Dergachev     AK_All = 2,             // Warn on any use-after-move.
732b500cbdSArtem Dergachev     AK_NumKinds = AK_All
742b500cbdSArtem Dergachev   };
752b500cbdSArtem Dergachev 
misuseCausesCrash(MisuseKind MK)7669909540SArtem Dergachev   static bool misuseCausesCrash(MisuseKind MK) {
7769909540SArtem Dergachev     return MK == MK_Dereference;
7869909540SArtem Dergachev   }
79eb469258SArtem Dergachev 
80eb469258SArtem Dergachev   struct ObjectKind {
8169909540SArtem Dergachev     // Is this a local variable or a local rvalue reference?
82b4bde2acSArtem Dergachev     bool IsLocal;
8369909540SArtem Dergachev     // Is this an STL object? If so, of what kind?
84b4bde2acSArtem Dergachev     StdObjectKind StdKind;
8569909540SArtem Dergachev   };
8669909540SArtem Dergachev 
8769909540SArtem Dergachev   // STL smart pointers are automatically re-initialized to null when moved
8869909540SArtem Dergachev   // from. So we can't warn on many methods, but we can warn when it is
8969909540SArtem Dergachev   // dereferenced, which is UB even if the resulting lvalue never gets read.
9069909540SArtem Dergachev   const llvm::StringSet<> StdSmartPtrClasses = {
9169909540SArtem Dergachev       "shared_ptr",
9269909540SArtem Dergachev       "unique_ptr",
9369909540SArtem Dergachev       "weak_ptr",
94eb469258SArtem Dergachev   };
95eb469258SArtem Dergachev 
9611cadc3eSArtem Dergachev   // Not all of these are entirely move-safe, but they do provide *some*
9711cadc3eSArtem Dergachev   // guarantees, and it means that somebody is using them after move
9811cadc3eSArtem Dergachev   // in a valid manner.
9969909540SArtem Dergachev   // TODO: We can still try to identify *unsafe* use after move,
10069909540SArtem Dergachev   // like we did with smart pointers.
10169909540SArtem Dergachev   const llvm::StringSet<> StdSafeClasses = {
10211cadc3eSArtem Dergachev       "basic_filebuf",
10311cadc3eSArtem Dergachev       "basic_ios",
10411cadc3eSArtem Dergachev       "future",
10511cadc3eSArtem Dergachev       "optional",
10647cadd61SArtem Dergachev       "packaged_task",
10711cadc3eSArtem Dergachev       "promise",
10811cadc3eSArtem Dergachev       "shared_future",
10911cadc3eSArtem Dergachev       "shared_lock",
11011cadc3eSArtem Dergachev       "thread",
11111cadc3eSArtem Dergachev       "unique_lock",
11211cadc3eSArtem Dergachev   };
11311cadc3eSArtem Dergachev 
114b5b974e4SArtem Dergachev   // Should we bother tracking the state of the object?
shouldBeTracked(ObjectKind OK) const115b5b974e4SArtem Dergachev   bool shouldBeTracked(ObjectKind OK) const {
116b5b974e4SArtem Dergachev     // In non-aggressive mode, only warn on use-after-move of local variables
117b5b974e4SArtem Dergachev     // (or local rvalue references) and of STL objects. The former is possible
118b5b974e4SArtem Dergachev     // because local variables (or local rvalue references) are not tempting
119b5b974e4SArtem Dergachev     // their user to re-use the storage. The latter is possible because STL
120b5b974e4SArtem Dergachev     // objects are known to end up in a valid but unspecified state after the
121b5b974e4SArtem Dergachev     // move and their state-reset methods are also known, which allows us to
12269909540SArtem Dergachev     // predict precisely when use-after-move is invalid.
12369909540SArtem Dergachev     // Some STL objects are known to conform to additional contracts after move,
12469909540SArtem Dergachev     // so they are not tracked. However, smart pointers specifically are tracked
12569909540SArtem Dergachev     // because we can perform extra checking over them.
12669909540SArtem Dergachev     // In aggressive mode, warn on any use-after-move because the user has
12769909540SArtem Dergachev     // intentionally asked us to completely eliminate use-after-move
12869909540SArtem Dergachev     // in his code.
1292b500cbdSArtem Dergachev     return (Aggressiveness == AK_All) ||
1302b500cbdSArtem Dergachev            (Aggressiveness >= AK_KnownsAndLocals && OK.IsLocal) ||
1312b500cbdSArtem Dergachev            OK.StdKind == SK_Unsafe || OK.StdKind == SK_SmartPtr;
13269909540SArtem Dergachev   }
13369909540SArtem Dergachev 
13469909540SArtem Dergachev   // Some objects only suffer from some kinds of misuses, but we need to track
13569909540SArtem Dergachev   // them anyway because we cannot know in advance what misuse will we find.
shouldWarnAbout(ObjectKind OK,MisuseKind MK) const13669909540SArtem Dergachev   bool shouldWarnAbout(ObjectKind OK, MisuseKind MK) const {
13769909540SArtem Dergachev     // Additionally, only warn on smart pointers when they are dereferenced (or
13869909540SArtem Dergachev     // local or we are aggressive).
13969909540SArtem Dergachev     return shouldBeTracked(OK) &&
1402b500cbdSArtem Dergachev            ((Aggressiveness == AK_All) ||
1412b500cbdSArtem Dergachev             (Aggressiveness >= AK_KnownsAndLocals && OK.IsLocal) ||
1422b500cbdSArtem Dergachev             OK.StdKind != SK_SmartPtr || MK == MK_Dereference);
143b5b974e4SArtem Dergachev   }
144b5b974e4SArtem Dergachev 
14560eb8c11SArtem Dergachev   // Obtains ObjectKind of an object. Because class declaration cannot always
14660eb8c11SArtem Dergachev   // be easily obtained from the memory region, it is supplied separately.
14711cadc3eSArtem Dergachev   ObjectKind classifyObject(const MemRegion *MR, const CXXRecordDecl *RD) const;
148eb469258SArtem Dergachev 
14960eb8c11SArtem Dergachev   // Classifies the object and dumps a user-friendly description string to
15069909540SArtem Dergachev   // the stream.
15169909540SArtem Dergachev   void explainObject(llvm::raw_ostream &OS, const MemRegion *MR,
15269909540SArtem Dergachev                      const CXXRecordDecl *RD, MisuseKind MK) const;
15311cadc3eSArtem Dergachev 
15469909540SArtem Dergachev   bool belongsTo(const CXXRecordDecl *RD, const llvm::StringSet<> &Set) const;
15560eb8c11SArtem Dergachev 
1562c5945caSArtem Dergachev   class MovedBugVisitor : public BugReporterVisitor {
1572c5945caSArtem Dergachev   public:
MovedBugVisitor(const MoveChecker & Chk,const MemRegion * R,const CXXRecordDecl * RD,MisuseKind MK)15869909540SArtem Dergachev     MovedBugVisitor(const MoveChecker &Chk, const MemRegion *R,
15969909540SArtem Dergachev                     const CXXRecordDecl *RD, MisuseKind MK)
16069909540SArtem Dergachev         : Chk(Chk), Region(R), RD(RD), MK(MK), Found(false) {}
1612c5945caSArtem Dergachev 
Profile(llvm::FoldingSetNodeID & ID) const1622c5945caSArtem Dergachev     void Profile(llvm::FoldingSetNodeID &ID) const override {
1632c5945caSArtem Dergachev       static int X = 0;
1642c5945caSArtem Dergachev       ID.AddPointer(&X);
1652c5945caSArtem Dergachev       ID.AddPointer(Region);
16660eb8c11SArtem Dergachev       // Don't add RD because it's, in theory, uniquely determined by
16760eb8c11SArtem Dergachev       // the region. In practice though, it's not always possible to obtain
16860eb8c11SArtem Dergachev       // the declaration directly from the region, that's why we store it
16960eb8c11SArtem Dergachev       // in the first place.
1702c5945caSArtem Dergachev     }
1712c5945caSArtem Dergachev 
1726d716ef1SKristof Umann     PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
1732c5945caSArtem Dergachev                                      BugReporterContext &BRC,
1742f169e7cSArtem Dergachev                                      PathSensitiveBugReport &BR) override;
1752c5945caSArtem Dergachev 
1762c5945caSArtem Dergachev   private:
17711cadc3eSArtem Dergachev     const MoveChecker &Chk;
1782c5945caSArtem Dergachev     // The tracked region.
1792c5945caSArtem Dergachev     const MemRegion *Region;
18060eb8c11SArtem Dergachev     // The class of the tracked object.
18160eb8c11SArtem Dergachev     const CXXRecordDecl *RD;
18269909540SArtem Dergachev     // How exactly the object was misused.
18369909540SArtem Dergachev     const MisuseKind MK;
1842c5945caSArtem Dergachev     bool Found;
1852c5945caSArtem Dergachev   };
1862c5945caSArtem Dergachev 
1872b500cbdSArtem Dergachev   AggressivenessKind Aggressiveness;
188eb469258SArtem Dergachev 
189eb469258SArtem Dergachev public:
setAggressiveness(StringRef Str,CheckerManager & Mgr)190748c139aSKristof Umann   void setAggressiveness(StringRef Str, CheckerManager &Mgr) {
1912b500cbdSArtem Dergachev     Aggressiveness =
1922b500cbdSArtem Dergachev         llvm::StringSwitch<AggressivenessKind>(Str)
1932b500cbdSArtem Dergachev             .Case("KnownsOnly", AK_KnownsOnly)
1942b500cbdSArtem Dergachev             .Case("KnownsAndLocals", AK_KnownsAndLocals)
1952b500cbdSArtem Dergachev             .Case("All", AK_All)
196748c139aSKristof Umann             .Default(AK_Invalid);
197748c139aSKristof Umann 
198748c139aSKristof Umann     if (Aggressiveness == AK_Invalid)
199748c139aSKristof Umann       Mgr.reportInvalidCheckerOptionValue(this, "WarnOn",
200748c139aSKristof Umann           "either \"KnownsOnly\", \"KnownsAndLocals\" or \"All\" string value");
2012b500cbdSArtem Dergachev   };
202eb469258SArtem Dergachev 
203eb469258SArtem Dergachev private:
2043e206a59SArtem Dergachev   BugType BT{this, "Use-after-move", categories::CXXMoveSemantics};
205b5b974e4SArtem Dergachev 
206b5b974e4SArtem Dergachev   // Check if the given form of potential misuse of a given object
207b5b974e4SArtem Dergachev   // should be reported. If so, get it reported. The callback from which
208b5b974e4SArtem Dergachev   // this function was called should immediately return after the call
209b5b974e4SArtem Dergachev   // because this function adds one or two transitions.
210b5b974e4SArtem Dergachev   void modelUse(ProgramStateRef State, const MemRegion *Region,
211b5b974e4SArtem Dergachev                 const CXXRecordDecl *RD, MisuseKind MK,
212b5b974e4SArtem Dergachev                 CheckerContext &C) const;
213b5b974e4SArtem Dergachev 
214b5b974e4SArtem Dergachev   // Returns the exploded node against which the report was emitted.
215b5b974e4SArtem Dergachev   // The caller *must* add any further transitions against this node.
216eb469258SArtem Dergachev   ExplodedNode *reportBug(const MemRegion *Region, const CXXRecordDecl *RD,
2172c5945caSArtem Dergachev                           CheckerContext &C, MisuseKind MK) const;
218b5b974e4SArtem Dergachev 
2192c5945caSArtem Dergachev   bool isInMoveSafeContext(const LocationContext *LC) const;
2202c5945caSArtem Dergachev   bool isStateResetMethod(const CXXMethodDecl *MethodDec) const;
2212c5945caSArtem Dergachev   bool isMoveSafeMethod(const CXXMethodDecl *MethodDec) const;
2222c5945caSArtem Dergachev   const ExplodedNode *getMoveLocation(const ExplodedNode *N,
2232c5945caSArtem Dergachev                                       const MemRegion *Region,
2242c5945caSArtem Dergachev                                       CheckerContext &C) const;
2252c5945caSArtem Dergachev };
2262c5945caSArtem Dergachev } // end anonymous namespace
2272c5945caSArtem Dergachev 
2282c5945caSArtem Dergachev REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, RegionState)
2292c5945caSArtem Dergachev 
2308c6119a4SArtem Dergachev // Define the inter-checker API.
2318c6119a4SArtem Dergachev namespace clang {
2328c6119a4SArtem Dergachev namespace ento {
2338c6119a4SArtem Dergachev namespace move {
isMovedFrom(ProgramStateRef State,const MemRegion * Region)2348c6119a4SArtem Dergachev bool isMovedFrom(ProgramStateRef State, const MemRegion *Region) {
2358c6119a4SArtem Dergachev   const RegionState *RS = State->get<TrackedRegionMap>(Region);
2368c6119a4SArtem Dergachev   return RS && (RS->isMoved() || RS->isReported());
2378c6119a4SArtem Dergachev }
2388c6119a4SArtem Dergachev } // namespace move
2398c6119a4SArtem Dergachev } // namespace ento
2408c6119a4SArtem Dergachev } // namespace clang
2418c6119a4SArtem Dergachev 
2422c5945caSArtem Dergachev // If a region is removed all of the subregions needs to be removed too.
removeFromState(ProgramStateRef State,const MemRegion * Region)2432c5945caSArtem Dergachev static ProgramStateRef removeFromState(ProgramStateRef State,
2442c5945caSArtem Dergachev                                        const MemRegion *Region) {
2452c5945caSArtem Dergachev   if (!Region)
2462c5945caSArtem Dergachev     return State;
2472c5945caSArtem Dergachev   for (auto &E : State->get<TrackedRegionMap>()) {
2482c5945caSArtem Dergachev     if (E.first->isSubRegionOf(Region))
2492c5945caSArtem Dergachev       State = State->remove<TrackedRegionMap>(E.first);
2502c5945caSArtem Dergachev   }
2512c5945caSArtem Dergachev   return State;
2522c5945caSArtem Dergachev }
2532c5945caSArtem Dergachev 
isAnyBaseRegionReported(ProgramStateRef State,const MemRegion * Region)2542c5945caSArtem Dergachev static bool isAnyBaseRegionReported(ProgramStateRef State,
2552c5945caSArtem Dergachev                                     const MemRegion *Region) {
2562c5945caSArtem Dergachev   for (auto &E : State->get<TrackedRegionMap>()) {
2572c5945caSArtem Dergachev     if (Region->isSubRegionOf(E.first) && E.second.isReported())
2582c5945caSArtem Dergachev       return true;
2592c5945caSArtem Dergachev   }
2602c5945caSArtem Dergachev   return false;
2612c5945caSArtem Dergachev }
2622c5945caSArtem Dergachev 
unwrapRValueReferenceIndirection(const MemRegion * MR)263eb469258SArtem Dergachev static const MemRegion *unwrapRValueReferenceIndirection(const MemRegion *MR) {
264eb469258SArtem Dergachev   if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(MR)) {
265eb469258SArtem Dergachev     SymbolRef Sym = SR->getSymbol();
266eb469258SArtem Dergachev     if (Sym->getType()->isRValueReferenceType())
267eb469258SArtem Dergachev       if (const MemRegion *OriginMR = Sym->getOriginRegion())
268eb469258SArtem Dergachev         return OriginMR;
269eb469258SArtem Dergachev   }
270eb469258SArtem Dergachev   return MR;
271eb469258SArtem Dergachev }
272eb469258SArtem Dergachev 
2732f169e7cSArtem Dergachev PathDiagnosticPieceRef
VisitNode(const ExplodedNode * N,BugReporterContext & BRC,PathSensitiveBugReport & BR)2742f169e7cSArtem Dergachev MoveChecker::MovedBugVisitor::VisitNode(const ExplodedNode *N,
2752f169e7cSArtem Dergachev                                         BugReporterContext &BRC,
2762f169e7cSArtem Dergachev                                         PathSensitiveBugReport &BR) {
2772c5945caSArtem Dergachev   // We need only the last move of the reported object's region.
2782c5945caSArtem Dergachev   // The visitor walks the ExplodedGraph backwards.
2792c5945caSArtem Dergachev   if (Found)
2802c5945caSArtem Dergachev     return nullptr;
2812c5945caSArtem Dergachev   ProgramStateRef State = N->getState();
2822c5945caSArtem Dergachev   ProgramStateRef StatePrev = N->getFirstPred()->getState();
2832c5945caSArtem Dergachev   const RegionState *TrackedObject = State->get<TrackedRegionMap>(Region);
2842c5945caSArtem Dergachev   const RegionState *TrackedObjectPrev =
2852c5945caSArtem Dergachev       StatePrev->get<TrackedRegionMap>(Region);
2862c5945caSArtem Dergachev   if (!TrackedObject)
2872c5945caSArtem Dergachev     return nullptr;
2882c5945caSArtem Dergachev   if (TrackedObjectPrev && TrackedObject)
2892c5945caSArtem Dergachev     return nullptr;
2902c5945caSArtem Dergachev 
2912c5945caSArtem Dergachev   // Retrieve the associated statement.
2926b85f8e9SArtem Dergachev   const Stmt *S = N->getStmtForDiagnostics();
2932c5945caSArtem Dergachev   if (!S)
2942c5945caSArtem Dergachev     return nullptr;
2952c5945caSArtem Dergachev   Found = true;
2962c5945caSArtem Dergachev 
29760eb8c11SArtem Dergachev   SmallString<128> Str;
29860eb8c11SArtem Dergachev   llvm::raw_svector_ostream OS(Str);
29960eb8c11SArtem Dergachev 
30069909540SArtem Dergachev   ObjectKind OK = Chk.classifyObject(Region, RD);
30169909540SArtem Dergachev   switch (OK.StdKind) {
30269909540SArtem Dergachev     case SK_SmartPtr:
30369909540SArtem Dergachev       if (MK == MK_Dereference) {
30469909540SArtem Dergachev         OS << "Smart pointer";
30569909540SArtem Dergachev         Chk.explainObject(OS, Region, RD, MK);
30669909540SArtem Dergachev         OS << " is reset to null when moved from";
30769909540SArtem Dergachev         break;
30869909540SArtem Dergachev       }
30969909540SArtem Dergachev 
31069909540SArtem Dergachev       // If it's not a dereference, we don't care if it was reset to null
31169909540SArtem Dergachev       // or that it is even a smart pointer.
31269909540SArtem Dergachev       LLVM_FALLTHROUGH;
31369909540SArtem Dergachev     case SK_NonStd:
31469909540SArtem Dergachev     case SK_Safe:
31560eb8c11SArtem Dergachev       OS << "Object";
31669909540SArtem Dergachev       Chk.explainObject(OS, Region, RD, MK);
317fe5be581SArtem Dergachev       OS << " is moved";
31869909540SArtem Dergachev       break;
31969909540SArtem Dergachev     case SK_Unsafe:
32069909540SArtem Dergachev       OS << "Object";
32169909540SArtem Dergachev       Chk.explainObject(OS, Region, RD, MK);
32269909540SArtem Dergachev       OS << " is left in a valid but unspecified state after move";
32369909540SArtem Dergachev       break;
32469909540SArtem Dergachev   }
3252c5945caSArtem Dergachev 
3262c5945caSArtem Dergachev   // Generate the extra diagnostic.
3272c5945caSArtem Dergachev   PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
3282c5945caSArtem Dergachev                              N->getLocationContext());
32960eb8c11SArtem Dergachev   return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true);
3302c5945caSArtem Dergachev }
3312c5945caSArtem Dergachev 
getMoveLocation(const ExplodedNode * N,const MemRegion * Region,CheckerContext & C) const3322c5945caSArtem Dergachev const ExplodedNode *MoveChecker::getMoveLocation(const ExplodedNode *N,
3332c5945caSArtem Dergachev                                                  const MemRegion *Region,
3342c5945caSArtem Dergachev                                                  CheckerContext &C) const {
3352c5945caSArtem Dergachev   // Walk the ExplodedGraph backwards and find the first node that referred to
3362c5945caSArtem Dergachev   // the tracked region.
3372c5945caSArtem Dergachev   const ExplodedNode *MoveNode = N;
3382c5945caSArtem Dergachev 
3392c5945caSArtem Dergachev   while (N) {
3402c5945caSArtem Dergachev     ProgramStateRef State = N->getState();
3412c5945caSArtem Dergachev     if (!State->get<TrackedRegionMap>(Region))
3422c5945caSArtem Dergachev       break;
3432c5945caSArtem Dergachev     MoveNode = N;
3442c5945caSArtem Dergachev     N = N->pred_empty() ? nullptr : *(N->pred_begin());
3452c5945caSArtem Dergachev   }
3462c5945caSArtem Dergachev   return MoveNode;
3472c5945caSArtem Dergachev }
3482c5945caSArtem Dergachev 
modelUse(ProgramStateRef State,const MemRegion * Region,const CXXRecordDecl * RD,MisuseKind MK,CheckerContext & C) const349b5b974e4SArtem Dergachev void MoveChecker::modelUse(ProgramStateRef State, const MemRegion *Region,
350b5b974e4SArtem Dergachev                            const CXXRecordDecl *RD, MisuseKind MK,
351b5b974e4SArtem Dergachev                            CheckerContext &C) const {
352b5b974e4SArtem Dergachev   assert(!C.isDifferent() && "No transitions should have been made by now");
353b5b974e4SArtem Dergachev   const RegionState *RS = State->get<TrackedRegionMap>(Region);
35469909540SArtem Dergachev   ObjectKind OK = classifyObject(Region, RD);
355b5b974e4SArtem Dergachev 
35669909540SArtem Dergachev   // Just in case: if it's not a smart pointer but it does have operator *,
35769909540SArtem Dergachev   // we shouldn't call the bug a dereference.
35869909540SArtem Dergachev   if (MK == MK_Dereference && OK.StdKind != SK_SmartPtr)
35969909540SArtem Dergachev     MK = MK_FunCall;
36069909540SArtem Dergachev 
36169909540SArtem Dergachev   if (!RS || !shouldWarnAbout(OK, MK)
362b5b974e4SArtem Dergachev           || isInMoveSafeContext(C.getLocationContext())) {
363b5b974e4SArtem Dergachev     // Finalize changes made by the caller.
364b5b974e4SArtem Dergachev     C.addTransition(State);
365b5b974e4SArtem Dergachev     return;
366b5b974e4SArtem Dergachev   }
367b5b974e4SArtem Dergachev 
36869909540SArtem Dergachev   // Don't report it in case if any base region is already reported.
36969909540SArtem Dergachev   // But still generate a sink in case of UB.
37069909540SArtem Dergachev   // And still finalize changes made by the caller.
37169909540SArtem Dergachev   if (isAnyBaseRegionReported(State, Region)) {
37269909540SArtem Dergachev     if (misuseCausesCrash(MK)) {
37369909540SArtem Dergachev       C.generateSink(State, C.getPredecessor());
37469909540SArtem Dergachev     } else {
37569909540SArtem Dergachev       C.addTransition(State);
37669909540SArtem Dergachev     }
37769909540SArtem Dergachev     return;
37869909540SArtem Dergachev   }
37969909540SArtem Dergachev 
380b5b974e4SArtem Dergachev   ExplodedNode *N = reportBug(Region, RD, C, MK);
381b5b974e4SArtem Dergachev 
38269909540SArtem Dergachev   // If the program has already crashed on this path, don't bother.
38369909540SArtem Dergachev   if (N->isSink())
38469909540SArtem Dergachev     return;
38569909540SArtem Dergachev 
386b5b974e4SArtem Dergachev   State = State->set<TrackedRegionMap>(Region, RegionState::getReported());
387b5b974e4SArtem Dergachev   C.addTransition(State, N);
388b5b974e4SArtem Dergachev }
389b5b974e4SArtem Dergachev 
reportBug(const MemRegion * Region,const CXXRecordDecl * RD,CheckerContext & C,MisuseKind MK) const3902c5945caSArtem Dergachev ExplodedNode *MoveChecker::reportBug(const MemRegion *Region,
39169909540SArtem Dergachev                                      const CXXRecordDecl *RD, CheckerContext &C,
3922c5945caSArtem Dergachev                                      MisuseKind MK) const {
39369909540SArtem Dergachev   if (ExplodedNode *N = misuseCausesCrash(MK) ? C.generateErrorNode()
39469909540SArtem Dergachev                                               : C.generateNonFatalErrorNode()) {
3952c5945caSArtem Dergachev     // Uniqueing report to the same object.
3962c5945caSArtem Dergachev     PathDiagnosticLocation LocUsedForUniqueing;
3972c5945caSArtem Dergachev     const ExplodedNode *MoveNode = getMoveLocation(N, Region, C);
3982c5945caSArtem Dergachev 
3996b85f8e9SArtem Dergachev     if (const Stmt *MoveStmt = MoveNode->getStmtForDiagnostics())
4002c5945caSArtem Dergachev       LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
4012c5945caSArtem Dergachev           MoveStmt, C.getSourceManager(), MoveNode->getLocationContext());
4022c5945caSArtem Dergachev 
4032c5945caSArtem Dergachev     // Creating the error message.
40460eb8c11SArtem Dergachev     llvm::SmallString<128> Str;
40560eb8c11SArtem Dergachev     llvm::raw_svector_ostream OS(Str);
4062c5945caSArtem Dergachev     switch(MK) {
4072c5945caSArtem Dergachev       case MK_FunCall:
40860eb8c11SArtem Dergachev         OS << "Method called on moved-from object";
40969909540SArtem Dergachev         explainObject(OS, Region, RD, MK);
4102c5945caSArtem Dergachev         break;
4112c5945caSArtem Dergachev       case MK_Copy:
41260eb8c11SArtem Dergachev         OS << "Moved-from object";
41369909540SArtem Dergachev         explainObject(OS, Region, RD, MK);
41460eb8c11SArtem Dergachev         OS << " is copied";
4152c5945caSArtem Dergachev         break;
4162c5945caSArtem Dergachev       case MK_Move:
41760eb8c11SArtem Dergachev         OS << "Moved-from object";
41869909540SArtem Dergachev         explainObject(OS, Region, RD, MK);
41960eb8c11SArtem Dergachev         OS << " is moved";
4202c5945caSArtem Dergachev         break;
42169909540SArtem Dergachev       case MK_Dereference:
42269909540SArtem Dergachev         OS << "Dereference of null smart pointer";
42369909540SArtem Dergachev         explainObject(OS, Region, RD, MK);
42469909540SArtem Dergachev         break;
4252c5945caSArtem Dergachev     }
4262c5945caSArtem Dergachev 
4272f169e7cSArtem Dergachev     auto R = std::make_unique<PathSensitiveBugReport>(
4283e206a59SArtem Dergachev         BT, OS.str(), N, LocUsedForUniqueing,
4292c5945caSArtem Dergachev         MoveNode->getLocationContext()->getDecl());
4302b3d49b6SJonas Devlieghere     R->addVisitor(std::make_unique<MovedBugVisitor>(*this, Region, RD, MK));
4312c5945caSArtem Dergachev     C.emitReport(std::move(R));
4322c5945caSArtem Dergachev     return N;
4332c5945caSArtem Dergachev   }
4342c5945caSArtem Dergachev   return nullptr;
4352c5945caSArtem Dergachev }
4362c5945caSArtem Dergachev 
checkPostCall(const CallEvent & Call,CheckerContext & C) const4372c5945caSArtem Dergachev void MoveChecker::checkPostCall(const CallEvent &Call,
4382c5945caSArtem Dergachev                                 CheckerContext &C) const {
4392c5945caSArtem Dergachev   const auto *AFC = dyn_cast<AnyFunctionCall>(&Call);
4402c5945caSArtem Dergachev   if (!AFC)
4412c5945caSArtem Dergachev     return;
4422c5945caSArtem Dergachev 
4432c5945caSArtem Dergachev   ProgramStateRef State = C.getState();
4442c5945caSArtem Dergachev   const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(AFC->getDecl());
4452c5945caSArtem Dergachev   if (!MethodDecl)
4462c5945caSArtem Dergachev     return;
4472c5945caSArtem Dergachev 
4482c5945caSArtem Dergachev   // Check if an object became moved-from.
4492c5945caSArtem Dergachev   // Object can become moved from after a call to move assignment operator or
4502c5945caSArtem Dergachev   // move constructor .
451b5b974e4SArtem Dergachev   const auto *ConstructorDecl = dyn_cast<CXXConstructorDecl>(MethodDecl);
4522c5945caSArtem Dergachev   if (ConstructorDecl && !ConstructorDecl->isMoveConstructor())
4532c5945caSArtem Dergachev     return;
4542c5945caSArtem Dergachev 
4552c5945caSArtem Dergachev   if (!ConstructorDecl && !MethodDecl->isMoveAssignmentOperator())
4562c5945caSArtem Dergachev     return;
4572c5945caSArtem Dergachev 
4582c5945caSArtem Dergachev   const auto ArgRegion = AFC->getArgSVal(0).getAsRegion();
4592c5945caSArtem Dergachev   if (!ArgRegion)
4602c5945caSArtem Dergachev     return;
4612c5945caSArtem Dergachev 
4622c5945caSArtem Dergachev   // Skip moving the object to itself.
463b5b974e4SArtem Dergachev   const auto *CC = dyn_cast_or_null<CXXConstructorCall>(&Call);
4642c5945caSArtem Dergachev   if (CC && CC->getCXXThisVal().getAsRegion() == ArgRegion)
4652c5945caSArtem Dergachev     return;
466b5b974e4SArtem Dergachev 
4672c5945caSArtem Dergachev   if (const auto *IC = dyn_cast<CXXInstanceCall>(AFC))
4682c5945caSArtem Dergachev     if (IC->getCXXThisVal().getAsRegion() == ArgRegion)
4692c5945caSArtem Dergachev       return;
4702c5945caSArtem Dergachev 
4712c5945caSArtem Dergachev   const MemRegion *BaseRegion = ArgRegion->getBaseRegion();
4722c5945caSArtem Dergachev   // Skip temp objects because of their short lifetime.
4732c5945caSArtem Dergachev   if (BaseRegion->getAs<CXXTempObjectRegion>() ||
474aef5d8fdSMatheus Izvekov       AFC->getArgExpr(0)->isPRValue())
4752c5945caSArtem Dergachev     return;
4762c5945caSArtem Dergachev   // If it has already been reported do not need to modify the state.
4772c5945caSArtem Dergachev 
4782c5945caSArtem Dergachev   if (State->get<TrackedRegionMap>(ArgRegion))
4792c5945caSArtem Dergachev     return;
480b5b974e4SArtem Dergachev 
481b5b974e4SArtem Dergachev   const CXXRecordDecl *RD = MethodDecl->getParent();
482b5b974e4SArtem Dergachev   ObjectKind OK = classifyObject(ArgRegion, RD);
483b5b974e4SArtem Dergachev   if (shouldBeTracked(OK)) {
4842c5945caSArtem Dergachev     // Mark object as moved-from.
4852c5945caSArtem Dergachev     State = State->set<TrackedRegionMap>(ArgRegion, RegionState::getMoved());
4862c5945caSArtem Dergachev     C.addTransition(State);
487b5b974e4SArtem Dergachev     return;
488b5b974e4SArtem Dergachev   }
489b5b974e4SArtem Dergachev   assert(!C.isDifferent() && "Should not have made transitions on this path!");
4902c5945caSArtem Dergachev }
4912c5945caSArtem Dergachev 
isMoveSafeMethod(const CXXMethodDecl * MethodDec) const4922c5945caSArtem Dergachev bool MoveChecker::isMoveSafeMethod(const CXXMethodDecl *MethodDec) const {
4932c5945caSArtem Dergachev   // We abandon the cases where bool/void/void* conversion happens.
4942c5945caSArtem Dergachev   if (const auto *ConversionDec =
4952c5945caSArtem Dergachev           dyn_cast_or_null<CXXConversionDecl>(MethodDec)) {
4962c5945caSArtem Dergachev     const Type *Tp = ConversionDec->getConversionType().getTypePtrOrNull();
4972c5945caSArtem Dergachev     if (!Tp)
4982c5945caSArtem Dergachev       return false;
4992c5945caSArtem Dergachev     if (Tp->isBooleanType() || Tp->isVoidType() || Tp->isVoidPointerType())
5002c5945caSArtem Dergachev       return true;
5012c5945caSArtem Dergachev   }
5022c5945caSArtem Dergachev   // Function call `empty` can be skipped.
5032c5945caSArtem Dergachev   return (MethodDec && MethodDec->getDeclName().isIdentifier() &&
5042c5945caSArtem Dergachev       (MethodDec->getName().lower() == "empty" ||
5052c5945caSArtem Dergachev        MethodDec->getName().lower() == "isempty"));
5062c5945caSArtem Dergachev }
5072c5945caSArtem Dergachev 
isStateResetMethod(const CXXMethodDecl * MethodDec) const5082c5945caSArtem Dergachev bool MoveChecker::isStateResetMethod(const CXXMethodDecl *MethodDec) const {
5092c5945caSArtem Dergachev   if (!MethodDec)
5102c5945caSArtem Dergachev       return false;
5112c5945caSArtem Dergachev   if (MethodDec->hasAttr<ReinitializesAttr>())
5122c5945caSArtem Dergachev       return true;
5132c5945caSArtem Dergachev   if (MethodDec->getDeclName().isIdentifier()) {
5142c5945caSArtem Dergachev     std::string MethodName = MethodDec->getName().lower();
515f3f03662SArtem Dergachev     // TODO: Some of these methods (eg., resize) are not always resetting
516f3f03662SArtem Dergachev     // the state, so we should consider looking at the arguments.
517342a7ac8SArtem Dergachev     if (MethodName == "assign" || MethodName == "clear" ||
518342a7ac8SArtem Dergachev         MethodName == "destroy" || MethodName == "reset" ||
519342a7ac8SArtem Dergachev         MethodName == "resize" || MethodName == "shrink")
5202c5945caSArtem Dergachev       return true;
5212c5945caSArtem Dergachev   }
5222c5945caSArtem Dergachev   return false;
5232c5945caSArtem Dergachev }
5242c5945caSArtem Dergachev 
5252c5945caSArtem Dergachev // Don't report an error inside a move related operation.
5262c5945caSArtem Dergachev // We assume that the programmer knows what she does.
isInMoveSafeContext(const LocationContext * LC) const5272c5945caSArtem Dergachev bool MoveChecker::isInMoveSafeContext(const LocationContext *LC) const {
5282c5945caSArtem Dergachev   do {
5292c5945caSArtem Dergachev     const auto *CtxDec = LC->getDecl();
5302c5945caSArtem Dergachev     auto *CtorDec = dyn_cast_or_null<CXXConstructorDecl>(CtxDec);
5312c5945caSArtem Dergachev     auto *DtorDec = dyn_cast_or_null<CXXDestructorDecl>(CtxDec);
5322c5945caSArtem Dergachev     auto *MethodDec = dyn_cast_or_null<CXXMethodDecl>(CtxDec);
5332c5945caSArtem Dergachev     if (DtorDec || (CtorDec && CtorDec->isCopyOrMoveConstructor()) ||
5342c5945caSArtem Dergachev         (MethodDec && MethodDec->isOverloadedOperator() &&
5352c5945caSArtem Dergachev          MethodDec->getOverloadedOperator() == OO_Equal) ||
5362c5945caSArtem Dergachev         isStateResetMethod(MethodDec) || isMoveSafeMethod(MethodDec))
5372c5945caSArtem Dergachev       return true;
5382c5945caSArtem Dergachev   } while ((LC = LC->getParent()));
5392c5945caSArtem Dergachev   return false;
5402c5945caSArtem Dergachev }
5412c5945caSArtem Dergachev 
belongsTo(const CXXRecordDecl * RD,const llvm::StringSet<> & Set) const54269909540SArtem Dergachev bool MoveChecker::belongsTo(const CXXRecordDecl *RD,
54369909540SArtem Dergachev                             const llvm::StringSet<> &Set) const {
54411cadc3eSArtem Dergachev   const IdentifierInfo *II = RD->getIdentifier();
54569909540SArtem Dergachev   return II && Set.count(II->getName());
54611cadc3eSArtem Dergachev }
54711cadc3eSArtem Dergachev 
54811cadc3eSArtem Dergachev MoveChecker::ObjectKind
classifyObject(const MemRegion * MR,const CXXRecordDecl * RD) const54911cadc3eSArtem Dergachev MoveChecker::classifyObject(const MemRegion *MR,
55011cadc3eSArtem Dergachev                             const CXXRecordDecl *RD) const {
55111cadc3eSArtem Dergachev   // Local variables and local rvalue references are classified as "Local".
55211cadc3eSArtem Dergachev   // For the purposes of this checker, we classify move-safe STL types
55311cadc3eSArtem Dergachev   // as not-"STL" types, because that's how the checker treats them.
554eb469258SArtem Dergachev   MR = unwrapRValueReferenceIndirection(MR);
555*16be17adSBalazs Benics   bool IsLocal = isa_and_nonnull<VarRegion>(MR) &&
556*16be17adSBalazs Benics                  isa<StackSpaceRegion>(MR->getMemorySpace());
55769909540SArtem Dergachev 
55869909540SArtem Dergachev   if (!RD || !RD->getDeclContext()->isStdNamespace())
55969909540SArtem Dergachev     return { IsLocal, SK_NonStd };
56069909540SArtem Dergachev 
56169909540SArtem Dergachev   if (belongsTo(RD, StdSmartPtrClasses))
56269909540SArtem Dergachev     return { IsLocal, SK_SmartPtr };
56369909540SArtem Dergachev 
56469909540SArtem Dergachev   if (belongsTo(RD, StdSafeClasses))
56569909540SArtem Dergachev     return { IsLocal, SK_Safe };
56669909540SArtem Dergachev 
56769909540SArtem Dergachev   return { IsLocal, SK_Unsafe };
568eb469258SArtem Dergachev }
569eb469258SArtem Dergachev 
explainObject(llvm::raw_ostream & OS,const MemRegion * MR,const CXXRecordDecl * RD,MisuseKind MK) const57069909540SArtem Dergachev void MoveChecker::explainObject(llvm::raw_ostream &OS, const MemRegion *MR,
57169909540SArtem Dergachev                                 const CXXRecordDecl *RD, MisuseKind MK) const {
57260eb8c11SArtem Dergachev   // We may need a leading space every time we actually explain anything,
57360eb8c11SArtem Dergachev   // and we never know if we are to explain anything until we try.
57460eb8c11SArtem Dergachev   if (const auto DR =
57560eb8c11SArtem Dergachev           dyn_cast_or_null<DeclRegion>(unwrapRValueReferenceIndirection(MR))) {
57660eb8c11SArtem Dergachev     const auto *RegionDecl = cast<NamedDecl>(DR->getDecl());
57719701458SBruno Ricci     OS << " '" << RegionDecl->getDeclName() << "'";
57860eb8c11SArtem Dergachev   }
57969909540SArtem Dergachev 
58060eb8c11SArtem Dergachev   ObjectKind OK = classifyObject(MR, RD);
58169909540SArtem Dergachev   switch (OK.StdKind) {
58269909540SArtem Dergachev     case SK_NonStd:
58369909540SArtem Dergachev     case SK_Safe:
58469909540SArtem Dergachev       break;
58569909540SArtem Dergachev     case SK_SmartPtr:
58669909540SArtem Dergachev       if (MK != MK_Dereference)
58769909540SArtem Dergachev         break;
58869909540SArtem Dergachev 
58969909540SArtem Dergachev       // We only care about the type if it's a dereference.
59069909540SArtem Dergachev       LLVM_FALLTHROUGH;
59169909540SArtem Dergachev     case SK_Unsafe:
59260eb8c11SArtem Dergachev       OS << " of type '" << RD->getQualifiedNameAsString() << "'";
59369909540SArtem Dergachev       break;
59469909540SArtem Dergachev   };
59560eb8c11SArtem Dergachev }
59660eb8c11SArtem Dergachev 
checkPreCall(const CallEvent & Call,CheckerContext & C) const5972c5945caSArtem Dergachev void MoveChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const {
5982c5945caSArtem Dergachev   ProgramStateRef State = C.getState();
5992c5945caSArtem Dergachev 
6002c5945caSArtem Dergachev   // Remove the MemRegions from the map on which a ctor/dtor call or assignment
6012c5945caSArtem Dergachev   // happened.
6022c5945caSArtem Dergachev 
6032c5945caSArtem Dergachev   // Checking constructor calls.
6042c5945caSArtem Dergachev   if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
6052c5945caSArtem Dergachev     State = removeFromState(State, CC->getCXXThisVal().getAsRegion());
6062c5945caSArtem Dergachev     auto CtorDec = CC->getDecl();
6072c5945caSArtem Dergachev     // Check for copying a moved-from object and report the bug.
6082c5945caSArtem Dergachev     if (CtorDec && CtorDec->isCopyOrMoveConstructor()) {
6092c5945caSArtem Dergachev       const MemRegion *ArgRegion = CC->getArgSVal(0).getAsRegion();
610eb469258SArtem Dergachev       const CXXRecordDecl *RD = CtorDec->getParent();
611b5b974e4SArtem Dergachev       MisuseKind MK = CtorDec->isMoveConstructor() ? MK_Move : MK_Copy;
612b5b974e4SArtem Dergachev       modelUse(State, ArgRegion, RD, MK, C);
6132c5945caSArtem Dergachev       return;
6142c5945caSArtem Dergachev     }
615b5b974e4SArtem Dergachev   }
6162c5945caSArtem Dergachev 
6172c5945caSArtem Dergachev   const auto IC = dyn_cast<CXXInstanceCall>(&Call);
6182c5945caSArtem Dergachev   if (!IC)
6192c5945caSArtem Dergachev     return;
6206c0b2ce1SArtem Dergachev 
6216c0b2ce1SArtem Dergachev   // Calling a destructor on a moved object is fine.
6226c0b2ce1SArtem Dergachev   if (isa<CXXDestructorCall>(IC))
6236c0b2ce1SArtem Dergachev     return;
6246c0b2ce1SArtem Dergachev 
6252c5945caSArtem Dergachev   const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
6262c5945caSArtem Dergachev   if (!ThisRegion)
6272c5945caSArtem Dergachev     return;
6282c5945caSArtem Dergachev 
629b5b974e4SArtem Dergachev   // The remaining part is check only for method call on a moved-from object.
6302c5945caSArtem Dergachev   const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(IC->getDecl());
6312c5945caSArtem Dergachev   if (!MethodDecl)
6322c5945caSArtem Dergachev     return;
6336c0b2ce1SArtem Dergachev 
6342c5945caSArtem Dergachev   // We want to investigate the whole object, not only sub-object of a parent
6352c5945caSArtem Dergachev   // class in which the encountered method defined.
636b5b974e4SArtem Dergachev   ThisRegion = ThisRegion->getMostDerivedObjectRegion();
6372c5945caSArtem Dergachev 
6382c5945caSArtem Dergachev   if (isStateResetMethod(MethodDecl)) {
6392c5945caSArtem Dergachev     State = removeFromState(State, ThisRegion);
6402c5945caSArtem Dergachev     C.addTransition(State);
6412c5945caSArtem Dergachev     return;
6422c5945caSArtem Dergachev   }
6432c5945caSArtem Dergachev 
644b5b974e4SArtem Dergachev   if (isMoveSafeMethod(MethodDecl))
6452c5945caSArtem Dergachev     return;
6462c5945caSArtem Dergachev 
647b5b974e4SArtem Dergachev   // Store class declaration as well, for bug reporting purposes.
648b5b974e4SArtem Dergachev   const CXXRecordDecl *RD = MethodDecl->getParent();
6492c5945caSArtem Dergachev 
650b5b974e4SArtem Dergachev   if (MethodDecl->isOverloadedOperator()) {
651b5b974e4SArtem Dergachev     OverloadedOperatorKind OOK = MethodDecl->getOverloadedOperator();
6522c5945caSArtem Dergachev 
653b5b974e4SArtem Dergachev     if (OOK == OO_Equal) {
654b5b974e4SArtem Dergachev       // Remove the tracked object for every assignment operator, but report bug
655b5b974e4SArtem Dergachev       // only for move or copy assignment's argument.
656b5b974e4SArtem Dergachev       State = removeFromState(State, ThisRegion);
657b5b974e4SArtem Dergachev 
658b5b974e4SArtem Dergachev       if (MethodDecl->isCopyAssignmentOperator() ||
659b5b974e4SArtem Dergachev           MethodDecl->isMoveAssignmentOperator()) {
660b5b974e4SArtem Dergachev         const MemRegion *ArgRegion = IC->getArgSVal(0).getAsRegion();
661b5b974e4SArtem Dergachev         MisuseKind MK =
662b5b974e4SArtem Dergachev             MethodDecl->isMoveAssignmentOperator() ? MK_Move : MK_Copy;
663b5b974e4SArtem Dergachev         modelUse(State, ArgRegion, RD, MK, C);
664b5b974e4SArtem Dergachev         return;
665b5b974e4SArtem Dergachev       }
666b5b974e4SArtem Dergachev       C.addTransition(State);
667b5b974e4SArtem Dergachev       return;
668b5b974e4SArtem Dergachev     }
66969909540SArtem Dergachev 
67069909540SArtem Dergachev     if (OOK == OO_Star || OOK == OO_Arrow) {
67169909540SArtem Dergachev       modelUse(State, ThisRegion, RD, MK_Dereference, C);
67269909540SArtem Dergachev       return;
67369909540SArtem Dergachev     }
674b5b974e4SArtem Dergachev   }
675b5b974e4SArtem Dergachev 
676b5b974e4SArtem Dergachev   modelUse(State, ThisRegion, RD, MK_FunCall, C);
6772c5945caSArtem Dergachev }
6782c5945caSArtem Dergachev 
checkDeadSymbols(SymbolReaper & SymReaper,CheckerContext & C) const6792c5945caSArtem Dergachev void MoveChecker::checkDeadSymbols(SymbolReaper &SymReaper,
6802c5945caSArtem Dergachev                                    CheckerContext &C) const {
6812c5945caSArtem Dergachev   ProgramStateRef State = C.getState();
6822c5945caSArtem Dergachev   TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
6838dc7b982SMark de Wever   for (auto E : TrackedRegions) {
6842c5945caSArtem Dergachev     const MemRegion *Region = E.first;
6852c5945caSArtem Dergachev     bool IsRegDead = !SymReaper.isLiveRegion(Region);
6862c5945caSArtem Dergachev 
6872c5945caSArtem Dergachev     // Remove the dead regions from the region map.
6882c5945caSArtem Dergachev     if (IsRegDead) {
6892c5945caSArtem Dergachev       State = State->remove<TrackedRegionMap>(Region);
6902c5945caSArtem Dergachev     }
6912c5945caSArtem Dergachev   }
6922c5945caSArtem Dergachev   C.addTransition(State);
6932c5945caSArtem Dergachev }
6942c5945caSArtem Dergachev 
checkRegionChanges(ProgramStateRef State,const InvalidatedSymbols * Invalidated,ArrayRef<const MemRegion * > RequestedRegions,ArrayRef<const MemRegion * > InvalidatedRegions,const LocationContext * LCtx,const CallEvent * Call) const6952c5945caSArtem Dergachev ProgramStateRef MoveChecker::checkRegionChanges(
6962c5945caSArtem Dergachev     ProgramStateRef State, const InvalidatedSymbols *Invalidated,
69712f7c2baSArtem Dergachev     ArrayRef<const MemRegion *> RequestedRegions,
69812f7c2baSArtem Dergachev     ArrayRef<const MemRegion *> InvalidatedRegions,
69912f7c2baSArtem Dergachev     const LocationContext *LCtx, const CallEvent *Call) const {
70012f7c2baSArtem Dergachev   if (Call) {
70112f7c2baSArtem Dergachev     // Relax invalidation upon function calls: only invalidate parameters
70212f7c2baSArtem Dergachev     // that are passed directly via non-const pointers or non-const references
70312f7c2baSArtem Dergachev     // or rvalue references.
70412f7c2baSArtem Dergachev     // In case of an InstanceCall don't invalidate the this-region since
70512f7c2baSArtem Dergachev     // it is fully handled in checkPreCall and checkPostCall.
7062c5945caSArtem Dergachev     const MemRegion *ThisRegion = nullptr;
70712f7c2baSArtem Dergachev     if (const auto *IC = dyn_cast<CXXInstanceCall>(Call))
7082c5945caSArtem Dergachev       ThisRegion = IC->getCXXThisVal().getAsRegion();
7092c5945caSArtem Dergachev 
71012f7c2baSArtem Dergachev     // Requested ("explicit") regions are the regions passed into the call
71112f7c2baSArtem Dergachev     // directly, but not all of them end up being invalidated.
71212f7c2baSArtem Dergachev     // But when they do, they appear in the InvalidatedRegions array as well.
71312f7c2baSArtem Dergachev     for (const auto *Region : RequestedRegions) {
714e567f37dSKazu Hirata       if (ThisRegion != Region &&
715e567f37dSKazu Hirata           llvm::is_contained(InvalidatedRegions, Region))
7162c5945caSArtem Dergachev         State = removeFromState(State, Region);
7172c5945caSArtem Dergachev     }
71812f7c2baSArtem Dergachev   } else {
71912f7c2baSArtem Dergachev     // For invalidations that aren't caused by calls, assume nothing. In
72012f7c2baSArtem Dergachev     // particular, direct write into an object's field invalidates the status.
72112f7c2baSArtem Dergachev     for (const auto *Region : InvalidatedRegions)
72212f7c2baSArtem Dergachev       State = removeFromState(State, Region->getBaseRegion());
72312f7c2baSArtem Dergachev   }
7242c5945caSArtem Dergachev 
7252c5945caSArtem Dergachev   return State;
7262c5945caSArtem Dergachev }
7272c5945caSArtem Dergachev 
printState(raw_ostream & Out,ProgramStateRef State,const char * NL,const char * Sep) const7282c5945caSArtem Dergachev void MoveChecker::printState(raw_ostream &Out, ProgramStateRef State,
7292c5945caSArtem Dergachev                              const char *NL, const char *Sep) const {
7302c5945caSArtem Dergachev 
7312c5945caSArtem Dergachev   TrackedRegionMapTy RS = State->get<TrackedRegionMap>();
7322c5945caSArtem Dergachev 
7332c5945caSArtem Dergachev   if (!RS.isEmpty()) {
7342c5945caSArtem Dergachev     Out << Sep << "Moved-from objects :" << NL;
7352c5945caSArtem Dergachev     for (auto I: RS) {
7362c5945caSArtem Dergachev       I.first->dumpToStream(Out);
7372c5945caSArtem Dergachev       if (I.second.isMoved())
7382c5945caSArtem Dergachev         Out << ": moved";
7392c5945caSArtem Dergachev       else
7402c5945caSArtem Dergachev         Out << ": moved and reported";
7412c5945caSArtem Dergachev       Out << NL;
7422c5945caSArtem Dergachev     }
7432c5945caSArtem Dergachev   }
7442c5945caSArtem Dergachev }
registerMoveChecker(CheckerManager & mgr)7452c5945caSArtem Dergachev void ento::registerMoveChecker(CheckerManager &mgr) {
746eb469258SArtem Dergachev   MoveChecker *chk = mgr.registerChecker<MoveChecker>();
7472b500cbdSArtem Dergachev   chk->setAggressiveness(
74883cc1b35SKristof Umann       mgr.getAnalyzerOptions().getCheckerStringOption(chk, "WarnOn"), mgr);
7492c5945caSArtem Dergachev }
750058a7a45SKristof Umann 
shouldRegisterMoveChecker(const CheckerManager & mgr)751bda3dd0dSKirstóf Umann bool ento::shouldRegisterMoveChecker(const CheckerManager &mgr) {
752058a7a45SKristof Umann   return true;
753058a7a45SKristof Umann }
754