brintos

brintos / llvm-project-archived public Read only

0
0
Text · 27.3 KiB · f5c3407 Raw
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