10b57cec5SDimitry Andric // MoveChecker.cpp - Check use of moved-from objects. - C++ ---------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // This defines checker which checks for potential misuses of a moved-from 100b57cec5SDimitry Andric // object. That means method calls on the object or copying it in moved-from 110b57cec5SDimitry Andric // state. 120b57cec5SDimitry Andric // 130b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 140b57cec5SDimitry Andric 15480093f4SDimitry Andric #include "clang/AST/Attr.h" 160b57cec5SDimitry Andric #include "clang/AST/ExprCXX.h" 170b57cec5SDimitry Andric #include "clang/Driver/DriverDiagnostic.h" 180b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 190b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 200b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h" 210b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h" 220b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 230b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 240b57cec5SDimitry Andric #include "llvm/ADT/StringSet.h" 250b57cec5SDimitry Andric 260b57cec5SDimitry Andric using namespace clang; 270b57cec5SDimitry Andric using namespace ento; 280b57cec5SDimitry Andric 290b57cec5SDimitry Andric namespace { 300b57cec5SDimitry Andric struct RegionState { 310b57cec5SDimitry Andric private: 320b57cec5SDimitry Andric enum Kind { Moved, Reported } K; 330b57cec5SDimitry Andric RegionState(Kind InK) : K(InK) {} 340b57cec5SDimitry Andric 350b57cec5SDimitry Andric public: 360b57cec5SDimitry Andric bool isReported() const { return K == Reported; } 370b57cec5SDimitry Andric bool isMoved() const { return K == Moved; } 380b57cec5SDimitry Andric 390b57cec5SDimitry Andric static RegionState getReported() { return RegionState(Reported); } 400b57cec5SDimitry Andric static RegionState getMoved() { return RegionState(Moved); } 410b57cec5SDimitry Andric 420b57cec5SDimitry Andric bool operator==(const RegionState &X) const { return K == X.K; } 430b57cec5SDimitry Andric void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); } 440b57cec5SDimitry Andric }; 450b57cec5SDimitry Andric } // end of anonymous namespace 460b57cec5SDimitry Andric 470b57cec5SDimitry Andric namespace { 480b57cec5SDimitry Andric class MoveChecker 490b57cec5SDimitry Andric : public Checker<check::PreCall, check::PostCall, 500b57cec5SDimitry Andric check::DeadSymbols, check::RegionChanges> { 510b57cec5SDimitry Andric public: 520b57cec5SDimitry Andric void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const; 530b57cec5SDimitry Andric void checkPreCall(const CallEvent &MC, CheckerContext &C) const; 540b57cec5SDimitry Andric void checkPostCall(const CallEvent &MC, CheckerContext &C) const; 550b57cec5SDimitry Andric void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; 560b57cec5SDimitry Andric ProgramStateRef 570b57cec5SDimitry Andric checkRegionChanges(ProgramStateRef State, 580b57cec5SDimitry Andric const InvalidatedSymbols *Invalidated, 590b57cec5SDimitry Andric ArrayRef<const MemRegion *> RequestedRegions, 600b57cec5SDimitry Andric ArrayRef<const MemRegion *> InvalidatedRegions, 610b57cec5SDimitry Andric const LocationContext *LCtx, const CallEvent *Call) const; 620b57cec5SDimitry Andric void printState(raw_ostream &Out, ProgramStateRef State, 630b57cec5SDimitry Andric const char *NL, const char *Sep) const override; 640b57cec5SDimitry Andric 650b57cec5SDimitry Andric private: 660b57cec5SDimitry Andric enum MisuseKind { MK_FunCall, MK_Copy, MK_Move, MK_Dereference }; 670b57cec5SDimitry Andric enum StdObjectKind { SK_NonStd, SK_Unsafe, SK_Safe, SK_SmartPtr }; 680b57cec5SDimitry Andric 690b57cec5SDimitry Andric enum AggressivenessKind { // In any case, don't warn after a reset. 700b57cec5SDimitry Andric AK_Invalid = -1, 710b57cec5SDimitry Andric AK_KnownsOnly = 0, // Warn only about known move-unsafe classes. 720b57cec5SDimitry Andric AK_KnownsAndLocals = 1, // Also warn about all local objects. 730b57cec5SDimitry Andric AK_All = 2, // Warn on any use-after-move. 740b57cec5SDimitry Andric AK_NumKinds = AK_All 750b57cec5SDimitry Andric }; 760b57cec5SDimitry Andric 770b57cec5SDimitry Andric static bool misuseCausesCrash(MisuseKind MK) { 780b57cec5SDimitry Andric return MK == MK_Dereference; 790b57cec5SDimitry Andric } 800b57cec5SDimitry Andric 810b57cec5SDimitry Andric struct ObjectKind { 820b57cec5SDimitry Andric // Is this a local variable or a local rvalue reference? 830b57cec5SDimitry Andric bool IsLocal; 840b57cec5SDimitry Andric // Is this an STL object? If so, of what kind? 850b57cec5SDimitry Andric StdObjectKind StdKind; 860b57cec5SDimitry Andric }; 870b57cec5SDimitry Andric 880b57cec5SDimitry Andric // STL smart pointers are automatically re-initialized to null when moved 890b57cec5SDimitry Andric // from. So we can't warn on many methods, but we can warn when it is 900b57cec5SDimitry Andric // dereferenced, which is UB even if the resulting lvalue never gets read. 910b57cec5SDimitry Andric const llvm::StringSet<> StdSmartPtrClasses = { 920b57cec5SDimitry Andric "shared_ptr", 930b57cec5SDimitry Andric "unique_ptr", 940b57cec5SDimitry Andric "weak_ptr", 950b57cec5SDimitry Andric }; 960b57cec5SDimitry Andric 970b57cec5SDimitry Andric // Not all of these are entirely move-safe, but they do provide *some* 980b57cec5SDimitry Andric // guarantees, and it means that somebody is using them after move 990b57cec5SDimitry Andric // in a valid manner. 1000b57cec5SDimitry Andric // TODO: We can still try to identify *unsafe* use after move, 1010b57cec5SDimitry Andric // like we did with smart pointers. 1020b57cec5SDimitry Andric const llvm::StringSet<> StdSafeClasses = { 1030b57cec5SDimitry Andric "basic_filebuf", 1040b57cec5SDimitry Andric "basic_ios", 1050b57cec5SDimitry Andric "future", 1060b57cec5SDimitry Andric "optional", 107e8d8bef9SDimitry Andric "packaged_task", 1080b57cec5SDimitry Andric "promise", 1090b57cec5SDimitry Andric "shared_future", 1100b57cec5SDimitry Andric "shared_lock", 1110b57cec5SDimitry Andric "thread", 1120b57cec5SDimitry Andric "unique_lock", 1130b57cec5SDimitry Andric }; 1140b57cec5SDimitry Andric 1150b57cec5SDimitry Andric // Should we bother tracking the state of the object? 1160b57cec5SDimitry Andric bool shouldBeTracked(ObjectKind OK) const { 1170b57cec5SDimitry Andric // In non-aggressive mode, only warn on use-after-move of local variables 1180b57cec5SDimitry Andric // (or local rvalue references) and of STL objects. The former is possible 1190b57cec5SDimitry Andric // because local variables (or local rvalue references) are not tempting 1200b57cec5SDimitry Andric // their user to re-use the storage. The latter is possible because STL 1210b57cec5SDimitry Andric // objects are known to end up in a valid but unspecified state after the 1220b57cec5SDimitry Andric // move and their state-reset methods are also known, which allows us to 1230b57cec5SDimitry Andric // predict precisely when use-after-move is invalid. 1240b57cec5SDimitry Andric // Some STL objects are known to conform to additional contracts after move, 1250b57cec5SDimitry Andric // so they are not tracked. However, smart pointers specifically are tracked 1260b57cec5SDimitry Andric // because we can perform extra checking over them. 1270b57cec5SDimitry Andric // In aggressive mode, warn on any use-after-move because the user has 1280b57cec5SDimitry Andric // intentionally asked us to completely eliminate use-after-move 1290b57cec5SDimitry Andric // in his code. 1300b57cec5SDimitry Andric return (Aggressiveness == AK_All) || 1310b57cec5SDimitry Andric (Aggressiveness >= AK_KnownsAndLocals && OK.IsLocal) || 1320b57cec5SDimitry Andric OK.StdKind == SK_Unsafe || OK.StdKind == SK_SmartPtr; 1330b57cec5SDimitry Andric } 1340b57cec5SDimitry Andric 1350b57cec5SDimitry Andric // Some objects only suffer from some kinds of misuses, but we need to track 1360b57cec5SDimitry Andric // them anyway because we cannot know in advance what misuse will we find. 1370b57cec5SDimitry Andric bool shouldWarnAbout(ObjectKind OK, MisuseKind MK) const { 1380b57cec5SDimitry Andric // Additionally, only warn on smart pointers when they are dereferenced (or 1390b57cec5SDimitry Andric // local or we are aggressive). 1400b57cec5SDimitry Andric return shouldBeTracked(OK) && 1410b57cec5SDimitry Andric ((Aggressiveness == AK_All) || 1420b57cec5SDimitry Andric (Aggressiveness >= AK_KnownsAndLocals && OK.IsLocal) || 1430b57cec5SDimitry Andric OK.StdKind != SK_SmartPtr || MK == MK_Dereference); 1440b57cec5SDimitry Andric } 1450b57cec5SDimitry Andric 1460b57cec5SDimitry Andric // Obtains ObjectKind of an object. Because class declaration cannot always 1470b57cec5SDimitry Andric // be easily obtained from the memory region, it is supplied separately. 1480b57cec5SDimitry Andric ObjectKind classifyObject(const MemRegion *MR, const CXXRecordDecl *RD) const; 1490b57cec5SDimitry Andric 1500b57cec5SDimitry Andric // Classifies the object and dumps a user-friendly description string to 1510b57cec5SDimitry Andric // the stream. 1520b57cec5SDimitry Andric void explainObject(llvm::raw_ostream &OS, const MemRegion *MR, 1530b57cec5SDimitry Andric const CXXRecordDecl *RD, MisuseKind MK) const; 1540b57cec5SDimitry Andric 1550b57cec5SDimitry Andric bool belongsTo(const CXXRecordDecl *RD, const llvm::StringSet<> &Set) const; 1560b57cec5SDimitry Andric 1570b57cec5SDimitry Andric class MovedBugVisitor : public BugReporterVisitor { 1580b57cec5SDimitry Andric public: 1590b57cec5SDimitry Andric MovedBugVisitor(const MoveChecker &Chk, const MemRegion *R, 1600b57cec5SDimitry Andric const CXXRecordDecl *RD, MisuseKind MK) 1610b57cec5SDimitry Andric : Chk(Chk), Region(R), RD(RD), MK(MK), Found(false) {} 1620b57cec5SDimitry Andric 1630b57cec5SDimitry Andric void Profile(llvm::FoldingSetNodeID &ID) const override { 1640b57cec5SDimitry Andric static int X = 0; 1650b57cec5SDimitry Andric ID.AddPointer(&X); 1660b57cec5SDimitry Andric ID.AddPointer(Region); 1670b57cec5SDimitry Andric // Don't add RD because it's, in theory, uniquely determined by 1680b57cec5SDimitry Andric // the region. In practice though, it's not always possible to obtain 1690b57cec5SDimitry Andric // the declaration directly from the region, that's why we store it 1700b57cec5SDimitry Andric // in the first place. 1710b57cec5SDimitry Andric } 1720b57cec5SDimitry Andric 173a7dea167SDimitry Andric PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, 1740b57cec5SDimitry Andric BugReporterContext &BRC, 175a7dea167SDimitry Andric PathSensitiveBugReport &BR) override; 1760b57cec5SDimitry Andric 1770b57cec5SDimitry Andric private: 1780b57cec5SDimitry Andric const MoveChecker &Chk; 1790b57cec5SDimitry Andric // The tracked region. 1800b57cec5SDimitry Andric const MemRegion *Region; 1810b57cec5SDimitry Andric // The class of the tracked object. 1820b57cec5SDimitry Andric const CXXRecordDecl *RD; 1830b57cec5SDimitry Andric // How exactly the object was misused. 1840b57cec5SDimitry Andric const MisuseKind MK; 1850b57cec5SDimitry Andric bool Found; 1860b57cec5SDimitry Andric }; 1870b57cec5SDimitry Andric 1880b57cec5SDimitry Andric AggressivenessKind Aggressiveness; 1890b57cec5SDimitry Andric 1900b57cec5SDimitry Andric public: 1910b57cec5SDimitry Andric void setAggressiveness(StringRef Str, CheckerManager &Mgr) { 1920b57cec5SDimitry Andric Aggressiveness = 1930b57cec5SDimitry Andric llvm::StringSwitch<AggressivenessKind>(Str) 1940b57cec5SDimitry Andric .Case("KnownsOnly", AK_KnownsOnly) 1950b57cec5SDimitry Andric .Case("KnownsAndLocals", AK_KnownsAndLocals) 1960b57cec5SDimitry Andric .Case("All", AK_All) 1970b57cec5SDimitry Andric .Default(AK_Invalid); 1980b57cec5SDimitry Andric 1990b57cec5SDimitry Andric if (Aggressiveness == AK_Invalid) 2000b57cec5SDimitry Andric Mgr.reportInvalidCheckerOptionValue(this, "WarnOn", 2010b57cec5SDimitry Andric "either \"KnownsOnly\", \"KnownsAndLocals\" or \"All\" string value"); 2020b57cec5SDimitry Andric }; 2030b57cec5SDimitry Andric 2040b57cec5SDimitry Andric private: 205*fe6060f1SDimitry Andric BugType BT{this, "Use-after-move", categories::CXXMoveSemantics}; 2060b57cec5SDimitry Andric 2070b57cec5SDimitry Andric // Check if the given form of potential misuse of a given object 2080b57cec5SDimitry Andric // should be reported. If so, get it reported. The callback from which 2090b57cec5SDimitry Andric // this function was called should immediately return after the call 2100b57cec5SDimitry Andric // because this function adds one or two transitions. 2110b57cec5SDimitry Andric void modelUse(ProgramStateRef State, const MemRegion *Region, 2120b57cec5SDimitry Andric const CXXRecordDecl *RD, MisuseKind MK, 2130b57cec5SDimitry Andric CheckerContext &C) const; 2140b57cec5SDimitry Andric 2150b57cec5SDimitry Andric // Returns the exploded node against which the report was emitted. 2160b57cec5SDimitry Andric // The caller *must* add any further transitions against this node. 2170b57cec5SDimitry Andric ExplodedNode *reportBug(const MemRegion *Region, const CXXRecordDecl *RD, 2180b57cec5SDimitry Andric CheckerContext &C, MisuseKind MK) const; 2190b57cec5SDimitry Andric 2200b57cec5SDimitry Andric bool isInMoveSafeContext(const LocationContext *LC) const; 2210b57cec5SDimitry Andric bool isStateResetMethod(const CXXMethodDecl *MethodDec) const; 2220b57cec5SDimitry Andric bool isMoveSafeMethod(const CXXMethodDecl *MethodDec) const; 2230b57cec5SDimitry Andric const ExplodedNode *getMoveLocation(const ExplodedNode *N, 2240b57cec5SDimitry Andric const MemRegion *Region, 2250b57cec5SDimitry Andric CheckerContext &C) const; 2260b57cec5SDimitry Andric }; 2270b57cec5SDimitry Andric } // end anonymous namespace 2280b57cec5SDimitry Andric 2290b57cec5SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, RegionState) 2300b57cec5SDimitry Andric 2310b57cec5SDimitry Andric // Define the inter-checker API. 2320b57cec5SDimitry Andric namespace clang { 2330b57cec5SDimitry Andric namespace ento { 2340b57cec5SDimitry Andric namespace move { 2350b57cec5SDimitry Andric bool isMovedFrom(ProgramStateRef State, const MemRegion *Region) { 2360b57cec5SDimitry Andric const RegionState *RS = State->get<TrackedRegionMap>(Region); 2370b57cec5SDimitry Andric return RS && (RS->isMoved() || RS->isReported()); 2380b57cec5SDimitry Andric } 2390b57cec5SDimitry Andric } // namespace move 2400b57cec5SDimitry Andric } // namespace ento 2410b57cec5SDimitry Andric } // namespace clang 2420b57cec5SDimitry Andric 2430b57cec5SDimitry Andric // If a region is removed all of the subregions needs to be removed too. 2440b57cec5SDimitry Andric static ProgramStateRef removeFromState(ProgramStateRef State, 2450b57cec5SDimitry Andric const MemRegion *Region) { 2460b57cec5SDimitry Andric if (!Region) 2470b57cec5SDimitry Andric return State; 2480b57cec5SDimitry Andric for (auto &E : State->get<TrackedRegionMap>()) { 2490b57cec5SDimitry Andric if (E.first->isSubRegionOf(Region)) 2500b57cec5SDimitry Andric State = State->remove<TrackedRegionMap>(E.first); 2510b57cec5SDimitry Andric } 2520b57cec5SDimitry Andric return State; 2530b57cec5SDimitry Andric } 2540b57cec5SDimitry Andric 2550b57cec5SDimitry Andric static bool isAnyBaseRegionReported(ProgramStateRef State, 2560b57cec5SDimitry Andric const MemRegion *Region) { 2570b57cec5SDimitry Andric for (auto &E : State->get<TrackedRegionMap>()) { 2580b57cec5SDimitry Andric if (Region->isSubRegionOf(E.first) && E.second.isReported()) 2590b57cec5SDimitry Andric return true; 2600b57cec5SDimitry Andric } 2610b57cec5SDimitry Andric return false; 2620b57cec5SDimitry Andric } 2630b57cec5SDimitry Andric 2640b57cec5SDimitry Andric static const MemRegion *unwrapRValueReferenceIndirection(const MemRegion *MR) { 2650b57cec5SDimitry Andric if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(MR)) { 2660b57cec5SDimitry Andric SymbolRef Sym = SR->getSymbol(); 2670b57cec5SDimitry Andric if (Sym->getType()->isRValueReferenceType()) 2680b57cec5SDimitry Andric if (const MemRegion *OriginMR = Sym->getOriginRegion()) 2690b57cec5SDimitry Andric return OriginMR; 2700b57cec5SDimitry Andric } 2710b57cec5SDimitry Andric return MR; 2720b57cec5SDimitry Andric } 2730b57cec5SDimitry Andric 274a7dea167SDimitry Andric PathDiagnosticPieceRef 2750b57cec5SDimitry Andric MoveChecker::MovedBugVisitor::VisitNode(const ExplodedNode *N, 276a7dea167SDimitry Andric BugReporterContext &BRC, 277a7dea167SDimitry Andric PathSensitiveBugReport &BR) { 2780b57cec5SDimitry Andric // We need only the last move of the reported object's region. 2790b57cec5SDimitry Andric // The visitor walks the ExplodedGraph backwards. 2800b57cec5SDimitry Andric if (Found) 2810b57cec5SDimitry Andric return nullptr; 2820b57cec5SDimitry Andric ProgramStateRef State = N->getState(); 2830b57cec5SDimitry Andric ProgramStateRef StatePrev = N->getFirstPred()->getState(); 2840b57cec5SDimitry Andric const RegionState *TrackedObject = State->get<TrackedRegionMap>(Region); 2850b57cec5SDimitry Andric const RegionState *TrackedObjectPrev = 2860b57cec5SDimitry Andric StatePrev->get<TrackedRegionMap>(Region); 2870b57cec5SDimitry Andric if (!TrackedObject) 2880b57cec5SDimitry Andric return nullptr; 2890b57cec5SDimitry Andric if (TrackedObjectPrev && TrackedObject) 2900b57cec5SDimitry Andric return nullptr; 2910b57cec5SDimitry Andric 2920b57cec5SDimitry Andric // Retrieve the associated statement. 293a7dea167SDimitry Andric const Stmt *S = N->getStmtForDiagnostics(); 2940b57cec5SDimitry Andric if (!S) 2950b57cec5SDimitry Andric return nullptr; 2960b57cec5SDimitry Andric Found = true; 2970b57cec5SDimitry Andric 2980b57cec5SDimitry Andric SmallString<128> Str; 2990b57cec5SDimitry Andric llvm::raw_svector_ostream OS(Str); 3000b57cec5SDimitry Andric 3010b57cec5SDimitry Andric ObjectKind OK = Chk.classifyObject(Region, RD); 3020b57cec5SDimitry Andric switch (OK.StdKind) { 3030b57cec5SDimitry Andric case SK_SmartPtr: 3040b57cec5SDimitry Andric if (MK == MK_Dereference) { 3050b57cec5SDimitry Andric OS << "Smart pointer"; 3060b57cec5SDimitry Andric Chk.explainObject(OS, Region, RD, MK); 3070b57cec5SDimitry Andric OS << " is reset to null when moved from"; 3080b57cec5SDimitry Andric break; 3090b57cec5SDimitry Andric } 3100b57cec5SDimitry Andric 3110b57cec5SDimitry Andric // If it's not a dereference, we don't care if it was reset to null 3120b57cec5SDimitry Andric // or that it is even a smart pointer. 3130b57cec5SDimitry Andric LLVM_FALLTHROUGH; 3140b57cec5SDimitry Andric case SK_NonStd: 3150b57cec5SDimitry Andric case SK_Safe: 3160b57cec5SDimitry Andric OS << "Object"; 3170b57cec5SDimitry Andric Chk.explainObject(OS, Region, RD, MK); 3180b57cec5SDimitry Andric OS << " is moved"; 3190b57cec5SDimitry Andric break; 3200b57cec5SDimitry Andric case SK_Unsafe: 3210b57cec5SDimitry Andric OS << "Object"; 3220b57cec5SDimitry Andric Chk.explainObject(OS, Region, RD, MK); 3230b57cec5SDimitry Andric OS << " is left in a valid but unspecified state after move"; 3240b57cec5SDimitry Andric break; 3250b57cec5SDimitry Andric } 3260b57cec5SDimitry Andric 3270b57cec5SDimitry Andric // Generate the extra diagnostic. 3280b57cec5SDimitry Andric PathDiagnosticLocation Pos(S, BRC.getSourceManager(), 3290b57cec5SDimitry Andric N->getLocationContext()); 3300b57cec5SDimitry Andric return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true); 3310b57cec5SDimitry Andric } 3320b57cec5SDimitry Andric 3330b57cec5SDimitry Andric const ExplodedNode *MoveChecker::getMoveLocation(const ExplodedNode *N, 3340b57cec5SDimitry Andric const MemRegion *Region, 3350b57cec5SDimitry Andric CheckerContext &C) const { 3360b57cec5SDimitry Andric // Walk the ExplodedGraph backwards and find the first node that referred to 3370b57cec5SDimitry Andric // the tracked region. 3380b57cec5SDimitry Andric const ExplodedNode *MoveNode = N; 3390b57cec5SDimitry Andric 3400b57cec5SDimitry Andric while (N) { 3410b57cec5SDimitry Andric ProgramStateRef State = N->getState(); 3420b57cec5SDimitry Andric if (!State->get<TrackedRegionMap>(Region)) 3430b57cec5SDimitry Andric break; 3440b57cec5SDimitry Andric MoveNode = N; 3450b57cec5SDimitry Andric N = N->pred_empty() ? nullptr : *(N->pred_begin()); 3460b57cec5SDimitry Andric } 3470b57cec5SDimitry Andric return MoveNode; 3480b57cec5SDimitry Andric } 3490b57cec5SDimitry Andric 3500b57cec5SDimitry Andric void MoveChecker::modelUse(ProgramStateRef State, const MemRegion *Region, 3510b57cec5SDimitry Andric const CXXRecordDecl *RD, MisuseKind MK, 3520b57cec5SDimitry Andric CheckerContext &C) const { 3530b57cec5SDimitry Andric assert(!C.isDifferent() && "No transitions should have been made by now"); 3540b57cec5SDimitry Andric const RegionState *RS = State->get<TrackedRegionMap>(Region); 3550b57cec5SDimitry Andric ObjectKind OK = classifyObject(Region, RD); 3560b57cec5SDimitry Andric 3570b57cec5SDimitry Andric // Just in case: if it's not a smart pointer but it does have operator *, 3580b57cec5SDimitry Andric // we shouldn't call the bug a dereference. 3590b57cec5SDimitry Andric if (MK == MK_Dereference && OK.StdKind != SK_SmartPtr) 3600b57cec5SDimitry Andric MK = MK_FunCall; 3610b57cec5SDimitry Andric 3620b57cec5SDimitry Andric if (!RS || !shouldWarnAbout(OK, MK) 3630b57cec5SDimitry Andric || isInMoveSafeContext(C.getLocationContext())) { 3640b57cec5SDimitry Andric // Finalize changes made by the caller. 3650b57cec5SDimitry Andric C.addTransition(State); 3660b57cec5SDimitry Andric return; 3670b57cec5SDimitry Andric } 3680b57cec5SDimitry Andric 3690b57cec5SDimitry Andric // Don't report it in case if any base region is already reported. 3700b57cec5SDimitry Andric // But still generate a sink in case of UB. 3710b57cec5SDimitry Andric // And still finalize changes made by the caller. 3720b57cec5SDimitry Andric if (isAnyBaseRegionReported(State, Region)) { 3730b57cec5SDimitry Andric if (misuseCausesCrash(MK)) { 3740b57cec5SDimitry Andric C.generateSink(State, C.getPredecessor()); 3750b57cec5SDimitry Andric } else { 3760b57cec5SDimitry Andric C.addTransition(State); 3770b57cec5SDimitry Andric } 3780b57cec5SDimitry Andric return; 3790b57cec5SDimitry Andric } 3800b57cec5SDimitry Andric 3810b57cec5SDimitry Andric ExplodedNode *N = reportBug(Region, RD, C, MK); 3820b57cec5SDimitry Andric 3830b57cec5SDimitry Andric // If the program has already crashed on this path, don't bother. 3840b57cec5SDimitry Andric if (N->isSink()) 3850b57cec5SDimitry Andric return; 3860b57cec5SDimitry Andric 3870b57cec5SDimitry Andric State = State->set<TrackedRegionMap>(Region, RegionState::getReported()); 3880b57cec5SDimitry Andric C.addTransition(State, N); 3890b57cec5SDimitry Andric } 3900b57cec5SDimitry Andric 3910b57cec5SDimitry Andric ExplodedNode *MoveChecker::reportBug(const MemRegion *Region, 3920b57cec5SDimitry Andric const CXXRecordDecl *RD, CheckerContext &C, 3930b57cec5SDimitry Andric MisuseKind MK) const { 3940b57cec5SDimitry Andric if (ExplodedNode *N = misuseCausesCrash(MK) ? C.generateErrorNode() 3950b57cec5SDimitry Andric : C.generateNonFatalErrorNode()) { 3960b57cec5SDimitry Andric // Uniqueing report to the same object. 3970b57cec5SDimitry Andric PathDiagnosticLocation LocUsedForUniqueing; 3980b57cec5SDimitry Andric const ExplodedNode *MoveNode = getMoveLocation(N, Region, C); 3990b57cec5SDimitry Andric 400a7dea167SDimitry Andric if (const Stmt *MoveStmt = MoveNode->getStmtForDiagnostics()) 4010b57cec5SDimitry Andric LocUsedForUniqueing = PathDiagnosticLocation::createBegin( 4020b57cec5SDimitry Andric MoveStmt, C.getSourceManager(), MoveNode->getLocationContext()); 4030b57cec5SDimitry Andric 4040b57cec5SDimitry Andric // Creating the error message. 4050b57cec5SDimitry Andric llvm::SmallString<128> Str; 4060b57cec5SDimitry Andric llvm::raw_svector_ostream OS(Str); 4070b57cec5SDimitry Andric switch(MK) { 4080b57cec5SDimitry Andric case MK_FunCall: 4090b57cec5SDimitry Andric OS << "Method called on moved-from object"; 4100b57cec5SDimitry Andric explainObject(OS, Region, RD, MK); 4110b57cec5SDimitry Andric break; 4120b57cec5SDimitry Andric case MK_Copy: 4130b57cec5SDimitry Andric OS << "Moved-from object"; 4140b57cec5SDimitry Andric explainObject(OS, Region, RD, MK); 4150b57cec5SDimitry Andric OS << " is copied"; 4160b57cec5SDimitry Andric break; 4170b57cec5SDimitry Andric case MK_Move: 4180b57cec5SDimitry Andric OS << "Moved-from object"; 4190b57cec5SDimitry Andric explainObject(OS, Region, RD, MK); 4200b57cec5SDimitry Andric OS << " is moved"; 4210b57cec5SDimitry Andric break; 4220b57cec5SDimitry Andric case MK_Dereference: 4230b57cec5SDimitry Andric OS << "Dereference of null smart pointer"; 4240b57cec5SDimitry Andric explainObject(OS, Region, RD, MK); 4250b57cec5SDimitry Andric break; 4260b57cec5SDimitry Andric } 4270b57cec5SDimitry Andric 428a7dea167SDimitry Andric auto R = std::make_unique<PathSensitiveBugReport>( 429*fe6060f1SDimitry Andric BT, OS.str(), N, LocUsedForUniqueing, 4300b57cec5SDimitry Andric MoveNode->getLocationContext()->getDecl()); 431a7dea167SDimitry Andric R->addVisitor(std::make_unique<MovedBugVisitor>(*this, Region, RD, MK)); 4320b57cec5SDimitry Andric C.emitReport(std::move(R)); 4330b57cec5SDimitry Andric return N; 4340b57cec5SDimitry Andric } 4350b57cec5SDimitry Andric return nullptr; 4360b57cec5SDimitry Andric } 4370b57cec5SDimitry Andric 4380b57cec5SDimitry Andric void MoveChecker::checkPostCall(const CallEvent &Call, 4390b57cec5SDimitry Andric CheckerContext &C) const { 4400b57cec5SDimitry Andric const auto *AFC = dyn_cast<AnyFunctionCall>(&Call); 4410b57cec5SDimitry Andric if (!AFC) 4420b57cec5SDimitry Andric return; 4430b57cec5SDimitry Andric 4440b57cec5SDimitry Andric ProgramStateRef State = C.getState(); 4450b57cec5SDimitry Andric const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(AFC->getDecl()); 4460b57cec5SDimitry Andric if (!MethodDecl) 4470b57cec5SDimitry Andric return; 4480b57cec5SDimitry Andric 4490b57cec5SDimitry Andric // Check if an object became moved-from. 4500b57cec5SDimitry Andric // Object can become moved from after a call to move assignment operator or 4510b57cec5SDimitry Andric // move constructor . 4520b57cec5SDimitry Andric const auto *ConstructorDecl = dyn_cast<CXXConstructorDecl>(MethodDecl); 4530b57cec5SDimitry Andric if (ConstructorDecl && !ConstructorDecl->isMoveConstructor()) 4540b57cec5SDimitry Andric return; 4550b57cec5SDimitry Andric 4560b57cec5SDimitry Andric if (!ConstructorDecl && !MethodDecl->isMoveAssignmentOperator()) 4570b57cec5SDimitry Andric return; 4580b57cec5SDimitry Andric 4590b57cec5SDimitry Andric const auto ArgRegion = AFC->getArgSVal(0).getAsRegion(); 4600b57cec5SDimitry Andric if (!ArgRegion) 4610b57cec5SDimitry Andric return; 4620b57cec5SDimitry Andric 4630b57cec5SDimitry Andric // Skip moving the object to itself. 4640b57cec5SDimitry Andric const auto *CC = dyn_cast_or_null<CXXConstructorCall>(&Call); 4650b57cec5SDimitry Andric if (CC && CC->getCXXThisVal().getAsRegion() == ArgRegion) 4660b57cec5SDimitry Andric return; 4670b57cec5SDimitry Andric 4680b57cec5SDimitry Andric if (const auto *IC = dyn_cast<CXXInstanceCall>(AFC)) 4690b57cec5SDimitry Andric if (IC->getCXXThisVal().getAsRegion() == ArgRegion) 4700b57cec5SDimitry Andric return; 4710b57cec5SDimitry Andric 4720b57cec5SDimitry Andric const MemRegion *BaseRegion = ArgRegion->getBaseRegion(); 4730b57cec5SDimitry Andric // Skip temp objects because of their short lifetime. 4740b57cec5SDimitry Andric if (BaseRegion->getAs<CXXTempObjectRegion>() || 475*fe6060f1SDimitry Andric AFC->getArgExpr(0)->isPRValue()) 4760b57cec5SDimitry Andric return; 4770b57cec5SDimitry Andric // If it has already been reported do not need to modify the state. 4780b57cec5SDimitry Andric 4790b57cec5SDimitry Andric if (State->get<TrackedRegionMap>(ArgRegion)) 4800b57cec5SDimitry Andric return; 4810b57cec5SDimitry Andric 4820b57cec5SDimitry Andric const CXXRecordDecl *RD = MethodDecl->getParent(); 4830b57cec5SDimitry Andric ObjectKind OK = classifyObject(ArgRegion, RD); 4840b57cec5SDimitry Andric if (shouldBeTracked(OK)) { 4850b57cec5SDimitry Andric // Mark object as moved-from. 4860b57cec5SDimitry Andric State = State->set<TrackedRegionMap>(ArgRegion, RegionState::getMoved()); 4870b57cec5SDimitry Andric C.addTransition(State); 4880b57cec5SDimitry Andric return; 4890b57cec5SDimitry Andric } 4900b57cec5SDimitry Andric assert(!C.isDifferent() && "Should not have made transitions on this path!"); 4910b57cec5SDimitry Andric } 4920b57cec5SDimitry Andric 4930b57cec5SDimitry Andric bool MoveChecker::isMoveSafeMethod(const CXXMethodDecl *MethodDec) const { 4940b57cec5SDimitry Andric // We abandon the cases where bool/void/void* conversion happens. 4950b57cec5SDimitry Andric if (const auto *ConversionDec = 4960b57cec5SDimitry Andric dyn_cast_or_null<CXXConversionDecl>(MethodDec)) { 4970b57cec5SDimitry Andric const Type *Tp = ConversionDec->getConversionType().getTypePtrOrNull(); 4980b57cec5SDimitry Andric if (!Tp) 4990b57cec5SDimitry Andric return false; 5000b57cec5SDimitry Andric if (Tp->isBooleanType() || Tp->isVoidType() || Tp->isVoidPointerType()) 5010b57cec5SDimitry Andric return true; 5020b57cec5SDimitry Andric } 5030b57cec5SDimitry Andric // Function call `empty` can be skipped. 5040b57cec5SDimitry Andric return (MethodDec && MethodDec->getDeclName().isIdentifier() && 5050b57cec5SDimitry Andric (MethodDec->getName().lower() == "empty" || 5060b57cec5SDimitry Andric MethodDec->getName().lower() == "isempty")); 5070b57cec5SDimitry Andric } 5080b57cec5SDimitry Andric 5090b57cec5SDimitry Andric bool MoveChecker::isStateResetMethod(const CXXMethodDecl *MethodDec) const { 5100b57cec5SDimitry Andric if (!MethodDec) 5110b57cec5SDimitry Andric return false; 5120b57cec5SDimitry Andric if (MethodDec->hasAttr<ReinitializesAttr>()) 5130b57cec5SDimitry Andric return true; 5140b57cec5SDimitry Andric if (MethodDec->getDeclName().isIdentifier()) { 5150b57cec5SDimitry Andric std::string MethodName = MethodDec->getName().lower(); 5160b57cec5SDimitry Andric // TODO: Some of these methods (eg., resize) are not always resetting 5170b57cec5SDimitry Andric // the state, so we should consider looking at the arguments. 5180b57cec5SDimitry Andric if (MethodName == "assign" || MethodName == "clear" || 5190b57cec5SDimitry Andric MethodName == "destroy" || MethodName == "reset" || 5200b57cec5SDimitry Andric MethodName == "resize" || MethodName == "shrink") 5210b57cec5SDimitry Andric return true; 5220b57cec5SDimitry Andric } 5230b57cec5SDimitry Andric return false; 5240b57cec5SDimitry Andric } 5250b57cec5SDimitry Andric 5260b57cec5SDimitry Andric // Don't report an error inside a move related operation. 5270b57cec5SDimitry Andric // We assume that the programmer knows what she does. 5280b57cec5SDimitry Andric bool MoveChecker::isInMoveSafeContext(const LocationContext *LC) const { 5290b57cec5SDimitry Andric do { 5300b57cec5SDimitry Andric const auto *CtxDec = LC->getDecl(); 5310b57cec5SDimitry Andric auto *CtorDec = dyn_cast_or_null<CXXConstructorDecl>(CtxDec); 5320b57cec5SDimitry Andric auto *DtorDec = dyn_cast_or_null<CXXDestructorDecl>(CtxDec); 5330b57cec5SDimitry Andric auto *MethodDec = dyn_cast_or_null<CXXMethodDecl>(CtxDec); 5340b57cec5SDimitry Andric if (DtorDec || (CtorDec && CtorDec->isCopyOrMoveConstructor()) || 5350b57cec5SDimitry Andric (MethodDec && MethodDec->isOverloadedOperator() && 5360b57cec5SDimitry Andric MethodDec->getOverloadedOperator() == OO_Equal) || 5370b57cec5SDimitry Andric isStateResetMethod(MethodDec) || isMoveSafeMethod(MethodDec)) 5380b57cec5SDimitry Andric return true; 5390b57cec5SDimitry Andric } while ((LC = LC->getParent())); 5400b57cec5SDimitry Andric return false; 5410b57cec5SDimitry Andric } 5420b57cec5SDimitry Andric 5430b57cec5SDimitry Andric bool MoveChecker::belongsTo(const CXXRecordDecl *RD, 5440b57cec5SDimitry Andric const llvm::StringSet<> &Set) const { 5450b57cec5SDimitry Andric const IdentifierInfo *II = RD->getIdentifier(); 5460b57cec5SDimitry Andric return II && Set.count(II->getName()); 5470b57cec5SDimitry Andric } 5480b57cec5SDimitry Andric 5490b57cec5SDimitry Andric MoveChecker::ObjectKind 5500b57cec5SDimitry Andric MoveChecker::classifyObject(const MemRegion *MR, 5510b57cec5SDimitry Andric const CXXRecordDecl *RD) const { 5520b57cec5SDimitry Andric // Local variables and local rvalue references are classified as "Local". 5530b57cec5SDimitry Andric // For the purposes of this checker, we classify move-safe STL types 5540b57cec5SDimitry Andric // as not-"STL" types, because that's how the checker treats them. 5550b57cec5SDimitry Andric MR = unwrapRValueReferenceIndirection(MR); 5560b57cec5SDimitry Andric bool IsLocal = 5570b57cec5SDimitry Andric MR && isa<VarRegion>(MR) && isa<StackSpaceRegion>(MR->getMemorySpace()); 5580b57cec5SDimitry Andric 5590b57cec5SDimitry Andric if (!RD || !RD->getDeclContext()->isStdNamespace()) 5600b57cec5SDimitry Andric return { IsLocal, SK_NonStd }; 5610b57cec5SDimitry Andric 5620b57cec5SDimitry Andric if (belongsTo(RD, StdSmartPtrClasses)) 5630b57cec5SDimitry Andric return { IsLocal, SK_SmartPtr }; 5640b57cec5SDimitry Andric 5650b57cec5SDimitry Andric if (belongsTo(RD, StdSafeClasses)) 5660b57cec5SDimitry Andric return { IsLocal, SK_Safe }; 5670b57cec5SDimitry Andric 5680b57cec5SDimitry Andric return { IsLocal, SK_Unsafe }; 5690b57cec5SDimitry Andric } 5700b57cec5SDimitry Andric 5710b57cec5SDimitry Andric void MoveChecker::explainObject(llvm::raw_ostream &OS, const MemRegion *MR, 5720b57cec5SDimitry Andric const CXXRecordDecl *RD, MisuseKind MK) const { 5730b57cec5SDimitry Andric // We may need a leading space every time we actually explain anything, 5740b57cec5SDimitry Andric // and we never know if we are to explain anything until we try. 5750b57cec5SDimitry Andric if (const auto DR = 5760b57cec5SDimitry Andric dyn_cast_or_null<DeclRegion>(unwrapRValueReferenceIndirection(MR))) { 5770b57cec5SDimitry Andric const auto *RegionDecl = cast<NamedDecl>(DR->getDecl()); 578e8d8bef9SDimitry Andric OS << " '" << RegionDecl->getDeclName() << "'"; 5790b57cec5SDimitry Andric } 5800b57cec5SDimitry Andric 5810b57cec5SDimitry Andric ObjectKind OK = classifyObject(MR, RD); 5820b57cec5SDimitry Andric switch (OK.StdKind) { 5830b57cec5SDimitry Andric case SK_NonStd: 5840b57cec5SDimitry Andric case SK_Safe: 5850b57cec5SDimitry Andric break; 5860b57cec5SDimitry Andric case SK_SmartPtr: 5870b57cec5SDimitry Andric if (MK != MK_Dereference) 5880b57cec5SDimitry Andric break; 5890b57cec5SDimitry Andric 5900b57cec5SDimitry Andric // We only care about the type if it's a dereference. 5910b57cec5SDimitry Andric LLVM_FALLTHROUGH; 5920b57cec5SDimitry Andric case SK_Unsafe: 5930b57cec5SDimitry Andric OS << " of type '" << RD->getQualifiedNameAsString() << "'"; 5940b57cec5SDimitry Andric break; 5950b57cec5SDimitry Andric }; 5960b57cec5SDimitry Andric } 5970b57cec5SDimitry Andric 5980b57cec5SDimitry Andric void MoveChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { 5990b57cec5SDimitry Andric ProgramStateRef State = C.getState(); 6000b57cec5SDimitry Andric 6010b57cec5SDimitry Andric // Remove the MemRegions from the map on which a ctor/dtor call or assignment 6020b57cec5SDimitry Andric // happened. 6030b57cec5SDimitry Andric 6040b57cec5SDimitry Andric // Checking constructor calls. 6050b57cec5SDimitry Andric if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) { 6060b57cec5SDimitry Andric State = removeFromState(State, CC->getCXXThisVal().getAsRegion()); 6070b57cec5SDimitry Andric auto CtorDec = CC->getDecl(); 6080b57cec5SDimitry Andric // Check for copying a moved-from object and report the bug. 6090b57cec5SDimitry Andric if (CtorDec && CtorDec->isCopyOrMoveConstructor()) { 6100b57cec5SDimitry Andric const MemRegion *ArgRegion = CC->getArgSVal(0).getAsRegion(); 6110b57cec5SDimitry Andric const CXXRecordDecl *RD = CtorDec->getParent(); 6120b57cec5SDimitry Andric MisuseKind MK = CtorDec->isMoveConstructor() ? MK_Move : MK_Copy; 6130b57cec5SDimitry Andric modelUse(State, ArgRegion, RD, MK, C); 6140b57cec5SDimitry Andric return; 6150b57cec5SDimitry Andric } 6160b57cec5SDimitry Andric } 6170b57cec5SDimitry Andric 6180b57cec5SDimitry Andric const auto IC = dyn_cast<CXXInstanceCall>(&Call); 6190b57cec5SDimitry Andric if (!IC) 6200b57cec5SDimitry Andric return; 6210b57cec5SDimitry Andric 6220b57cec5SDimitry Andric // Calling a destructor on a moved object is fine. 6230b57cec5SDimitry Andric if (isa<CXXDestructorCall>(IC)) 6240b57cec5SDimitry Andric return; 6250b57cec5SDimitry Andric 6260b57cec5SDimitry Andric const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); 6270b57cec5SDimitry Andric if (!ThisRegion) 6280b57cec5SDimitry Andric return; 6290b57cec5SDimitry Andric 6300b57cec5SDimitry Andric // The remaining part is check only for method call on a moved-from object. 6310b57cec5SDimitry Andric const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(IC->getDecl()); 6320b57cec5SDimitry Andric if (!MethodDecl) 6330b57cec5SDimitry Andric return; 6340b57cec5SDimitry Andric 6350b57cec5SDimitry Andric // We want to investigate the whole object, not only sub-object of a parent 6360b57cec5SDimitry Andric // class in which the encountered method defined. 6370b57cec5SDimitry Andric ThisRegion = ThisRegion->getMostDerivedObjectRegion(); 6380b57cec5SDimitry Andric 6390b57cec5SDimitry Andric if (isStateResetMethod(MethodDecl)) { 6400b57cec5SDimitry Andric State = removeFromState(State, ThisRegion); 6410b57cec5SDimitry Andric C.addTransition(State); 6420b57cec5SDimitry Andric return; 6430b57cec5SDimitry Andric } 6440b57cec5SDimitry Andric 6450b57cec5SDimitry Andric if (isMoveSafeMethod(MethodDecl)) 6460b57cec5SDimitry Andric return; 6470b57cec5SDimitry Andric 6480b57cec5SDimitry Andric // Store class declaration as well, for bug reporting purposes. 6490b57cec5SDimitry Andric const CXXRecordDecl *RD = MethodDecl->getParent(); 6500b57cec5SDimitry Andric 6510b57cec5SDimitry Andric if (MethodDecl->isOverloadedOperator()) { 6520b57cec5SDimitry Andric OverloadedOperatorKind OOK = MethodDecl->getOverloadedOperator(); 6530b57cec5SDimitry Andric 6540b57cec5SDimitry Andric if (OOK == OO_Equal) { 6550b57cec5SDimitry Andric // Remove the tracked object for every assignment operator, but report bug 6560b57cec5SDimitry Andric // only for move or copy assignment's argument. 6570b57cec5SDimitry Andric State = removeFromState(State, ThisRegion); 6580b57cec5SDimitry Andric 6590b57cec5SDimitry Andric if (MethodDecl->isCopyAssignmentOperator() || 6600b57cec5SDimitry Andric MethodDecl->isMoveAssignmentOperator()) { 6610b57cec5SDimitry Andric const MemRegion *ArgRegion = IC->getArgSVal(0).getAsRegion(); 6620b57cec5SDimitry Andric MisuseKind MK = 6630b57cec5SDimitry Andric MethodDecl->isMoveAssignmentOperator() ? MK_Move : MK_Copy; 6640b57cec5SDimitry Andric modelUse(State, ArgRegion, RD, MK, C); 6650b57cec5SDimitry Andric return; 6660b57cec5SDimitry Andric } 6670b57cec5SDimitry Andric C.addTransition(State); 6680b57cec5SDimitry Andric return; 6690b57cec5SDimitry Andric } 6700b57cec5SDimitry Andric 6710b57cec5SDimitry Andric if (OOK == OO_Star || OOK == OO_Arrow) { 6720b57cec5SDimitry Andric modelUse(State, ThisRegion, RD, MK_Dereference, C); 6730b57cec5SDimitry Andric return; 6740b57cec5SDimitry Andric } 6750b57cec5SDimitry Andric } 6760b57cec5SDimitry Andric 6770b57cec5SDimitry Andric modelUse(State, ThisRegion, RD, MK_FunCall, C); 6780b57cec5SDimitry Andric } 6790b57cec5SDimitry Andric 6800b57cec5SDimitry Andric void MoveChecker::checkDeadSymbols(SymbolReaper &SymReaper, 6810b57cec5SDimitry Andric CheckerContext &C) const { 6820b57cec5SDimitry Andric ProgramStateRef State = C.getState(); 6830b57cec5SDimitry Andric TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>(); 684480093f4SDimitry Andric for (auto E : TrackedRegions) { 6850b57cec5SDimitry Andric const MemRegion *Region = E.first; 6860b57cec5SDimitry Andric bool IsRegDead = !SymReaper.isLiveRegion(Region); 6870b57cec5SDimitry Andric 6880b57cec5SDimitry Andric // Remove the dead regions from the region map. 6890b57cec5SDimitry Andric if (IsRegDead) { 6900b57cec5SDimitry Andric State = State->remove<TrackedRegionMap>(Region); 6910b57cec5SDimitry Andric } 6920b57cec5SDimitry Andric } 6930b57cec5SDimitry Andric C.addTransition(State); 6940b57cec5SDimitry Andric } 6950b57cec5SDimitry Andric 6960b57cec5SDimitry Andric ProgramStateRef MoveChecker::checkRegionChanges( 6970b57cec5SDimitry Andric ProgramStateRef State, const InvalidatedSymbols *Invalidated, 6980b57cec5SDimitry Andric ArrayRef<const MemRegion *> RequestedRegions, 6990b57cec5SDimitry Andric ArrayRef<const MemRegion *> InvalidatedRegions, 7000b57cec5SDimitry Andric const LocationContext *LCtx, const CallEvent *Call) const { 7010b57cec5SDimitry Andric if (Call) { 7020b57cec5SDimitry Andric // Relax invalidation upon function calls: only invalidate parameters 7030b57cec5SDimitry Andric // that are passed directly via non-const pointers or non-const references 7040b57cec5SDimitry Andric // or rvalue references. 7050b57cec5SDimitry Andric // In case of an InstanceCall don't invalidate the this-region since 7060b57cec5SDimitry Andric // it is fully handled in checkPreCall and checkPostCall. 7070b57cec5SDimitry Andric const MemRegion *ThisRegion = nullptr; 7080b57cec5SDimitry Andric if (const auto *IC = dyn_cast<CXXInstanceCall>(Call)) 7090b57cec5SDimitry Andric ThisRegion = IC->getCXXThisVal().getAsRegion(); 7100b57cec5SDimitry Andric 7110b57cec5SDimitry Andric // Requested ("explicit") regions are the regions passed into the call 7120b57cec5SDimitry Andric // directly, but not all of them end up being invalidated. 7130b57cec5SDimitry Andric // But when they do, they appear in the InvalidatedRegions array as well. 7140b57cec5SDimitry Andric for (const auto *Region : RequestedRegions) { 7150b57cec5SDimitry Andric if (ThisRegion != Region) { 7160b57cec5SDimitry Andric if (llvm::find(InvalidatedRegions, Region) != 7170b57cec5SDimitry Andric std::end(InvalidatedRegions)) { 7180b57cec5SDimitry Andric State = removeFromState(State, Region); 7190b57cec5SDimitry Andric } 7200b57cec5SDimitry Andric } 7210b57cec5SDimitry Andric } 7220b57cec5SDimitry Andric } else { 7230b57cec5SDimitry Andric // For invalidations that aren't caused by calls, assume nothing. In 7240b57cec5SDimitry Andric // particular, direct write into an object's field invalidates the status. 7250b57cec5SDimitry Andric for (const auto *Region : InvalidatedRegions) 7260b57cec5SDimitry Andric State = removeFromState(State, Region->getBaseRegion()); 7270b57cec5SDimitry Andric } 7280b57cec5SDimitry Andric 7290b57cec5SDimitry Andric return State; 7300b57cec5SDimitry Andric } 7310b57cec5SDimitry Andric 7320b57cec5SDimitry Andric void MoveChecker::printState(raw_ostream &Out, ProgramStateRef State, 7330b57cec5SDimitry Andric const char *NL, const char *Sep) const { 7340b57cec5SDimitry Andric 7350b57cec5SDimitry Andric TrackedRegionMapTy RS = State->get<TrackedRegionMap>(); 7360b57cec5SDimitry Andric 7370b57cec5SDimitry Andric if (!RS.isEmpty()) { 7380b57cec5SDimitry Andric Out << Sep << "Moved-from objects :" << NL; 7390b57cec5SDimitry Andric for (auto I: RS) { 7400b57cec5SDimitry Andric I.first->dumpToStream(Out); 7410b57cec5SDimitry Andric if (I.second.isMoved()) 7420b57cec5SDimitry Andric Out << ": moved"; 7430b57cec5SDimitry Andric else 7440b57cec5SDimitry Andric Out << ": moved and reported"; 7450b57cec5SDimitry Andric Out << NL; 7460b57cec5SDimitry Andric } 7470b57cec5SDimitry Andric } 7480b57cec5SDimitry Andric } 7490b57cec5SDimitry Andric void ento::registerMoveChecker(CheckerManager &mgr) { 7500b57cec5SDimitry Andric MoveChecker *chk = mgr.registerChecker<MoveChecker>(); 7510b57cec5SDimitry Andric chk->setAggressiveness( 7520b57cec5SDimitry Andric mgr.getAnalyzerOptions().getCheckerStringOption(chk, "WarnOn"), mgr); 7530b57cec5SDimitry Andric } 7540b57cec5SDimitry Andric 7555ffd83dbSDimitry Andric bool ento::shouldRegisterMoveChecker(const CheckerManager &mgr) { 7560b57cec5SDimitry Andric return true; 7570b57cec5SDimitry Andric } 758