Index: lib/StaticAnalyzer/Checkers/MoveChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/MoveChecker.cpp +++ lib/StaticAnalyzer/Checkers/MoveChecker.cpp @@ -527,16 +527,28 @@ ArrayRef ExplicitRegions, ArrayRef Regions, const LocationContext *LCtx, const CallEvent *Call) const { - // In case of an InstanceCall don't remove the ThisRegion from the GDM since - // it is handled in checkPreCall and checkPostCall. - const MemRegion *ThisRegion = nullptr; - if (const auto *IC = dyn_cast_or_null(Call)) { - ThisRegion = IC->getCXXThisVal().getAsRegion(); - } - - for (const auto *Region : ExplicitRegions) { - if (ThisRegion != Region) - State = removeFromState(State, Region); + if (Call) { + // Relax invalidation upon function calls: only invalidate parameters + // that are passed directly via non-const pointers or non-const references + // or rvalue references. + // In case of an InstanceCall don't invalidate the this-region since + // it is fully handled in checkPreCall and checkPostCall. + const MemRegion *ThisRegion = nullptr; + if (const auto *IC = dyn_cast_or_null(Call)) + ThisRegion = IC->getCXXThisVal().getAsRegion(); + + // Explicit regions are the regions passed into the call directly, but + // not all of them end up being invalidated. The ones that do appear in + // the Regions array as well. + for (const auto *Region : ExplicitRegions) + if (ThisRegion != Region) + if (std::find(Regions.begin(), Regions.end(), Region) != Regions.end()) + State = removeFromState(State, Region); + } else { + // For invalidations that aren't caused by calls, assume nothing. In + // particular, direct write into an object's field invalidates the status. + for (const auto *Region : Regions) + State = removeFromState(State, Region->getBaseRegion()); } return State; Index: test/Analysis/use-after-move.cpp =================================================================== --- test/Analysis/use-after-move.cpp +++ test/Analysis/use-after-move.cpp @@ -116,6 +116,19 @@ bool empty() const; bool isEmpty() const; operator bool() const; + + void testUpdateField() { + A a; + A b = std::move(a); + a.i = 1; + a.foo(); // no-warning + } + void testUpdateFieldDouble() { + A a; + A b = std::move(a); + a.d = 1.0; + a.foo(); // no-warning + } }; int bignum(); @@ -594,24 +607,50 @@ foobar(a.getI(), std::move(a)); //no-warning } -void not_known(A &a); -void not_known(A *a); +void not_known_pass_by_ref(A &a); +void not_known_pass_by_const_ref(const A &a); +void not_known_pass_by_rvalue_ref(A &&a); +void not_known_pass_by_ptr(A *a); +void not_known_pass_by_const_ptr(const A *a); void regionAndPointerEscapeTest() { { A a; A b; b = std::move(a); - not_known(a); - a.foo(); //no-warning + not_known_pass_by_ref(a); + a.foo(); // no-warning + } + { + A a; + A b; + b = std::move(a); // expected-note{{Object 'a' is moved}} + not_known_pass_by_const_ref(a); + a.foo(); // expected-warning{{Method called on moved-from object 'a'}} + // expected-note@-1{{Method called on moved-from object 'a'}} + } + { + A a; + A b; + b = std::move(a); + not_known_pass_by_rvalue_ref(std::move(a)); + a.foo(); // no-warning } { A a; A b; b = std::move(a); - not_known(&a); + not_known_pass_by_ptr(&a); a.foo(); // no-warning } + { + A a; + A b; + b = std::move(a); // expected-note{{Object 'a' is moved}} + not_known_pass_by_const_ptr(&a); + a.foo(); // expected-warning{{Method called on moved-from object 'a'}} + // expected-note@-1{{Method called on moved-from object 'a'}} + } } // A declaration statement containing multiple declarations sequences the