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