diff --git a/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp --- a/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp @@ -58,6 +58,10 @@ void handleSwap(const CallEvent &Call, CheckerContext &C) const; void handleGet(const CallEvent &Call, CheckerContext &C) const; bool handleAssignOp(const CallEvent &Call, CheckerContext &C) const; + bool handleMoveCtr(const CallEvent &Call, CheckerContext &C, + const MemRegion *ThisRegion) const; + bool updateMovedSmartPointers(CheckerContext &C, const MemRegion *ThisRegion, + const MemRegion *OtherSmartPtrRegion) const; using SmartPtrMethodHandlerFn = void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const; @@ -160,13 +164,16 @@ return false; if (const auto *CC = dyn_cast(&Call)) { - if (CC->getDecl()->isCopyOrMoveConstructor()) + if (CC->getDecl()->isCopyConstructor()) return false; const MemRegion *ThisRegion = CC->getCXXThisVal().getAsRegion(); if (!ThisRegion) return false; + if (CC->getDecl()->isMoveConstructor()) + return handleMoveCtr(Call, C, ThisRegion); + if (Call.getNumArgs() == 0) { auto NullVal = C.getSValBuilder().makeNull(); State = State->set(ThisRegion, NullVal); @@ -410,6 +417,22 @@ return true; } + return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion); +} + +bool SmartPtrModeling::handleMoveCtr(const CallEvent &Call, CheckerContext &C, + const MemRegion *ThisRegion) const { + const auto *OtherSmartPtrRegion = Call.getArgSVal(0).getAsRegion(); + if (!OtherSmartPtrRegion) + return false; + + return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion); +} + +bool SmartPtrModeling::updateMovedSmartPointers( + CheckerContext &C, const MemRegion *ThisRegion, + const MemRegion *OtherSmartPtrRegion) const { + ProgramStateRef State = C.getState(); const auto *OtherInnerPtr = State->get(OtherSmartPtrRegion); if (OtherInnerPtr) { State = State->set(ThisRegion, *OtherInnerPtr); @@ -430,7 +453,7 @@ ThisRegion->printPretty(OS); } if (BR.isInteresting(ThisRegion) && IsArgValNull) { - OS << "Null pointer value move-assigned to "; + OS << "A null pointer value is moved to "; ThisRegion->printPretty(OS); BR.markInteresting(OtherSmartPtrRegion); } diff --git a/clang/test/Analysis/smart-ptr-text-output.cpp b/clang/test/Analysis/smart-ptr-text-output.cpp --- a/clang/test/Analysis/smart-ptr-text-output.cpp +++ b/clang/test/Analysis/smart-ptr-text-output.cpp @@ -144,7 +144,7 @@ std::unique_ptr P(new A()); // expected-note {{Smart pointer 'P' is constructed}} // FIXME: above note should go away once we fix marking region not interested. std::unique_ptr PToMove; // expected-note {{Default constructed smart pointer 'PToMove' is null}} - P = std::move(PToMove); // expected-note {{Null pointer value move-assigned to 'P'}} + P = std::move(PToMove); // expected-note {{A null pointer value is moved to 'P'}} P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}} // expected-note@-1 {{Dereference of null smart pointer 'P'}} } @@ -170,3 +170,32 @@ P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}} // expected-note@-1 {{Dereference of null smart pointer 'P'}} } + +void derefMoveConstructedWithNullPtr() { + std::unique_ptr PToMove; // expected-note {{Default constructed smart pointer 'PToMove' is null}} + std::unique_ptr P(std::move(PToMove)); // expected-note {{A null pointer value is moved to 'P'}} + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}} + // expected-note@-1{{Dereference of null smart pointer 'P'}} +} + +void derefValidPtrMovedToConstruct() { + std::unique_ptr PToMove(new A()); // expected-note {{Smart pointer 'PToMove' is constructed}} + // FIXME: above note should go away once we fix marking region not interested. + std::unique_ptr P(std::move(PToMove)); // expected-note {{Smart pointer 'PToMove' is null after being moved to 'P'}} + PToMove->foo(); // expected-warning {{Dereference of null smart pointer 'PToMove' [alpha.cplusplus.SmartPtr]}} + // expected-note@-1{{Dereference of null smart pointer 'PToMove'}} +} + +void derefNullPtrMovedToConstruct() { + std::unique_ptr PToMove; // expected-note {{Default constructed smart pointer 'PToMove' is null}} + // FIXME: above note should go away once we fix marking region not interested. + std::unique_ptr P(std::move(PToMove)); // expected-note {{Smart pointer 'PToMove' is null after being moved to 'P'}} + PToMove->foo(); // expected-warning {{Dereference of null smart pointer 'PToMove' [alpha.cplusplus.SmartPtr]}} + // expected-note@-1{{Dereference of null smart pointer 'PToMove'}} +} + +void derefUnknownPtrMovedToConstruct(std::unique_ptr PToMove) { + std::unique_ptr P(std::move(PToMove)); // expected-note {{Smart pointer 'PToMove' is null after; previous value moved to 'P'}} + PToMove->foo(); // expected-warning {{Dereference of null smart pointer 'PToMove' [alpha.cplusplus.SmartPtr]}} + // expected-note@-1{{Dereference of null smart pointer 'PToMove'}} +} diff --git a/clang/test/Analysis/smart-ptr.cpp b/clang/test/Analysis/smart-ptr.cpp --- a/clang/test/Analysis/smart-ptr.cpp +++ b/clang/test/Analysis/smart-ptr.cpp @@ -17,7 +17,8 @@ if (P) clang_analyzer_warnIfReached(); // no-warning // TODO: Report a null dereference (instead). - *P.get() = 1; // expected-warning {{Method called on moved-from object 'P'}} + *P.get() = 1; // expected-warning {{Method called on moved-from object 'P' [cplusplus.Move]}} + // expected-warning@-1 {{Dereference of null pointer [core.NullDereference]}} } // Don't crash when attempting to model a call with unknown callee. @@ -333,3 +334,44 @@ P = returnRValRefOfUniquePtr(); P->foo(); // No warning. } + +void derefMoveConstructedWithValidPtr() { + std::unique_ptr PToMove(new A()); + std::unique_ptr P(std::move(PToMove)); + P->foo(); // No warning. +} + +void derefMoveConstructedWithNullPtr() { + std::unique_ptr PToMove; + std::unique_ptr P(std::move(PToMove)); + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}} +} + +void derefMoveConstructedWithUnknownPtr(std::unique_ptr PToMove) { + std::unique_ptr P(std::move(PToMove)); + P->foo(); // No warning. +} + +void derefValidPtrMovedToConstruct() { + std::unique_ptr PToMove(new A()); + std::unique_ptr P(std::move(PToMove)); + PToMove->foo(); // expected-warning {{Dereference of null smart pointer 'PToMove' [alpha.cplusplus.SmartPtr]}} +} + +void derefNullPtrMovedToConstruct() { + std::unique_ptr PToMove; + std::unique_ptr P(std::move(PToMove)); + PToMove->foo(); // expected-warning {{Dereference of null smart pointer 'PToMove' [alpha.cplusplus.SmartPtr]}} +} + +void derefUnknownPtrMovedToConstruct(std::unique_ptr PToMove) { + std::unique_ptr P(std::move(PToMove)); + PToMove->foo(); // expected-warning {{Dereference of null smart pointer 'PToMove' [alpha.cplusplus.SmartPtr]}} +} + +std::unique_ptr &&functionReturnsRValueRef(); + +void derefMoveConstructedWithRValueRefReturn() { + std::unique_ptr P(functionReturnsRValueRef()); + P->foo(); // No warning. +}