Index: clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp @@ -208,13 +208,14 @@ // this function was called should immediately return after the call // because this function adds one or two transitions. void modelUse(ProgramStateRef State, const MemRegion *Region, - const CXXRecordDecl *RD, MisuseKind MK, + const Expr *ArgExpr, const CXXRecordDecl *RD, MisuseKind MK, CheckerContext &C) const; // Returns the exploded node against which the report was emitted. // The caller *must* add any further transitions against this node. - ExplodedNode *reportBug(const MemRegion *Region, const CXXRecordDecl *RD, - CheckerContext &C, MisuseKind MK) const; + ExplodedNode *reportBug(const MemRegion *Region, const Expr *ArgExpr, + const CXXRecordDecl *RD, CheckerContext &C, + MisuseKind MK) const; bool isInMoveSafeContext(const LocationContext *LC) const; bool isStateResetMethod(const CXXMethodDecl *MethodDec) const; @@ -347,7 +348,7 @@ } void MoveChecker::modelUse(ProgramStateRef State, const MemRegion *Region, - const CXXRecordDecl *RD, MisuseKind MK, + const Expr* ArgExpr, const CXXRecordDecl *RD, MisuseKind MK, CheckerContext &C) const { assert(!C.isDifferent() && "No transitions should have been made by now"); const RegionState *RS = State->get(Region); @@ -377,7 +378,7 @@ return; } - ExplodedNode *N = reportBug(Region, RD, C, MK); + ExplodedNode *N = reportBug(Region, ArgExpr, RD, C, MK); // If the program has already crashed on this path, don't bother. if (N->isSink()) @@ -387,7 +388,7 @@ C.addTransition(State, N); } -ExplodedNode *MoveChecker::reportBug(const MemRegion *Region, +ExplodedNode *MoveChecker::reportBug(const MemRegion *Region, const Expr *Arg, const CXXRecordDecl *RD, CheckerContext &C, MisuseKind MK) const { if (ExplodedNode *N = misuseCausesCrash(MK) ? C.generateErrorNode() @@ -428,6 +429,18 @@ BT, OS.str(), N, LocUsedForUniqueing, MoveNode->getLocationContext()->getDecl()); R->addVisitor(std::make_unique(*this, Region, RD, MK)); + if (Arg) { + Arg = Arg->IgnoreParenImpCasts(); + if (auto ArgDeclRefExpr = dyn_cast(Arg)) { + if (auto VD = dyn_cast(ArgDeclRefExpr->getDecl())) { + auto State = N->getState(); + auto RawArgSval = State->getLValue(VD, N->getLocationContext()); + if (RawArgSval.getAsRegion() != Region) + bugreporter::trackExpressionValue(N, Arg, *R); + } + } else + bugreporter::trackExpressionValue(N, Arg, *R); + } C.emitReport(std::move(R)); return N; } @@ -609,7 +622,7 @@ const MemRegion *ArgRegion = CC->getArgSVal(0).getAsRegion(); const CXXRecordDecl *RD = CtorDec->getParent(); MisuseKind MK = CtorDec->isMoveConstructor() ? MK_Move : MK_Copy; - modelUse(State, ArgRegion, RD, MK, C); + modelUse(State, ArgRegion, CC->getArgExpr(0), RD, MK, C); return; } } @@ -660,7 +673,7 @@ const MemRegion *ArgRegion = IC->getArgSVal(0).getAsRegion(); MisuseKind MK = MethodDecl->isMoveAssignmentOperator() ? MK_Move : MK_Copy; - modelUse(State, ArgRegion, RD, MK, C); + modelUse(State, ArgRegion, IC->getArgExpr(0), RD, MK, C); return; } C.addTransition(State); @@ -668,12 +681,12 @@ } if (OOK == OO_Star || OOK == OO_Arrow) { - modelUse(State, ThisRegion, RD, MK_Dereference, C); + modelUse(State, ThisRegion, IC->getCXXThisExpr(), RD, MK_Dereference, C); return; } } - modelUse(State, ThisRegion, RD, MK_FunCall, C); + modelUse(State, ThisRegion, IC->getCXXThisExpr(), RD, MK_FunCall, C); } void MoveChecker::checkDeadSymbols(SymbolReaper &SymReaper, Index: clang/test/Analysis/use-after-move.cpp =================================================================== --- clang/test/Analysis/use-after-move.cpp +++ clang/test/Analysis/use-after-move.cpp @@ -59,7 +59,7 @@ class B { public: - B() = default; + B() = default; // aggressive-note 2 {{Returning without writing to '*this'}} B(const B &) = default; B(B &&) = default; B& operator=(const B &q) = default; @@ -75,7 +75,8 @@ public: B b; - A(int ii = 42, double dd = 1.0) : d(dd), i(ii), b(B()) {} + A(int ii = 42, double dd = 1.0) : d(dd), i(ii), b(B()) {} // aggressive-note 2{{Calling defaulted default constructor for 'B'}} + // aggressive-note@-1 2{{Returning from default constructor for 'B'}} void moveconstruct(A &&other) { std::swap(b, other.b); std::swap(d, other.d); @@ -177,7 +178,7 @@ // peaceful-note@-1 {{Moved-from object 'a' is copied}} } { - A a; + A a; // peaceful-note {{'a' initialized here}} A b = std::move(a); // peaceful-note {{Object 'a' is moved}} b = std::move(a); // peaceful-warning {{Moved-from object 'a' is moved}} // peaceful-note@-1 {{Moved-from object 'a' is moved}} @@ -200,7 +201,7 @@ // peaceful-note@-1 {{Moved-from object 'a' is copied}} } { - A a; + A a; // peaceful-note {{'a' initialized here}} A b; b = std::move(a); // peaceful-note {{Object 'a' is moved}} A c(std::move(a)); // peaceful-warning {{Moved-from object 'a' is moved}} @@ -366,7 +367,7 @@ } } { - A a; + A a; // peaceful-note {{'a' initialized here}} for (int i = 0; i < bignum(); i++) { // peaceful-note {{Loop condition is true. Entering loop body}} // peaceful-note@-1 {{Loop condition is true. Entering loop body}} constCopyOrMoveCall(std::move(a)); // peaceful-note {{Object 'a' is moved}} @@ -518,12 +519,12 @@ void PtrAndArrayTest() { A *Ptr = new A(1, 1.5); - A Arr[10]; + A Arr[10]; // aggressive-note{{'Arr' initialized here}} Arr[2] = std::move(*Ptr); // aggressive-note{{Object is moved}} (*Ptr).foo(); // aggressive-warning{{Method called on moved-from object}} // aggressive-note@-1{{Method called on moved-from object}} - Ptr = &Arr[1]; + Ptr = &Arr[1]; // aggressive-note {{Value assigned to 'Ptr'}} Arr[3] = std::move(Arr[1]); // aggressive-note {{Object is moved}} Ptr->foo(); // aggressive-warning {{Method called on moved-from object}} // aggressive-note@-1 {{Method called on moved-from object}} @@ -619,10 +620,10 @@ } void interFunTest2() { - A a; + A a; // peaceful-note {{'a' initialized here}} A b; b = std::move(a); // peaceful-note {{Object 'a' is moved}} - interFunTest1(a); // peaceful-note {{Calling 'interFunTest1'}} + interFunTest1(a); // peaceful-note {{Calling 'interFunTest1'}} peaceful-note {{Passing value via 1st parameter 'a'}} } void foobar(A a, int i); @@ -788,13 +789,15 @@ void subRegionMoveTest() { { - A a; + A a; // aggressive-note {{Calling default constructor for 'A'}} + // aggressive-note@-1 {{Returning from default constructor for 'A'}} B b = std::move(a.b); // aggressive-note {{Object 'b' is moved}} a.b.foo(); // aggressive-warning {{Method called on moved-from object 'b'}} // aggressive-note@-1 {{Method called on moved-from object 'b'}} } { - A a; + A a; // aggressive-note {{Calling default constructor for 'A'}} + // aggressive-note@-1 {{Returning from default constructor for 'A'}} A a1 = std::move(a); // aggressive-note {{Calling move constructor for 'A'}} // aggressive-note@-1 {{Returning from move constructor for 'A'}} a.b.foo(); // aggressive-warning{{Method called on moved-from object 'b'}} @@ -1012,3 +1015,11 @@ // aggressive-note@-2 {{Moved-from object 'Task' is moved}} } }; + +void notes_for_references() { + A a; // peaceful-note {{'a' initialized here}} + A& ref = a; // peaceful-note {{'ref' initialized here}} + A b = std::move(a); // peaceful-note {{Object 'a' is moved}} + ref.foo(); // peaceful-warning {{Method called on moved-from object 'a'}} + // peaceful-note@-1 {{Method called on moved-from object 'a'}} +}