[analyzer] Add modeling for unique_ptr move constructor

Summary:
Add support for handling move contructor of std::unique_ptr.

Reviewers: NoQ, Szelethus, vsavchenko, xazax.hun

Reviewed By: NoQ

Subscribers: martong, cfe-commits
Tags: #clang

Differential Revision: https://reviews.llvm.org/D86373
This commit is contained in:
Nithin Vadukkumchery Rajendrakumar 2020-08-31 11:50:39 +02:00
parent db464a2753
commit 1b743a9efa
3 changed files with 98 additions and 4 deletions

View File

@ -58,6 +58,10 @@ private:
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 @@ bool SmartPtrModeling::evalCall(const CallEvent &Call,
return false;
if (const auto *CC = dyn_cast<CXXConstructorCall>(&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<TrackedRegionMap>(ThisRegion, NullVal);
@ -410,6 +417,22 @@ bool SmartPtrModeling::handleAssignOp(const CallEvent &Call,
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<TrackedRegionMap>(OtherSmartPtrRegion);
if (OtherInnerPtr) {
State = State->set<TrackedRegionMap>(ThisRegion, *OtherInnerPtr);
@ -430,7 +453,7 @@ bool SmartPtrModeling::handleAssignOp(const CallEvent &Call,
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);
}

View File

@ -144,7 +144,7 @@ void derefOnNullPtrGotMovedFromValidPtr() {
std::unique_ptr<A> 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<A> 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 @@ void derefOnAssignedZeroToNullSmartPtr() {
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<A> PToMove; // expected-note {{Default constructed smart pointer 'PToMove' is null}}
std::unique_ptr<A> 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<A> 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<A> 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<A> 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<A> 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<A> PToMove) {
std::unique_ptr<A> 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'}}
}

View File

@ -17,7 +17,8 @@ void derefAfterMove(std::unique_ptr<int> P) {
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 @@ void drefOnAssignedNullFromMethodPtrValidSmartPtr() {
P = returnRValRefOfUniquePtr();
P->foo(); // No warning.
}
void derefMoveConstructedWithValidPtr() {
std::unique_ptr<A> PToMove(new A());
std::unique_ptr<A> P(std::move(PToMove));
P->foo(); // No warning.
}
void derefMoveConstructedWithNullPtr() {
std::unique_ptr<A> PToMove;
std::unique_ptr<A> P(std::move(PToMove));
P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}}
}
void derefMoveConstructedWithUnknownPtr(std::unique_ptr<A> PToMove) {
std::unique_ptr<A> P(std::move(PToMove));
P->foo(); // No warning.
}
void derefValidPtrMovedToConstruct() {
std::unique_ptr<A> PToMove(new A());
std::unique_ptr<A> P(std::move(PToMove));
PToMove->foo(); // expected-warning {{Dereference of null smart pointer 'PToMove' [alpha.cplusplus.SmartPtr]}}
}
void derefNullPtrMovedToConstruct() {
std::unique_ptr<A> PToMove;
std::unique_ptr<A> P(std::move(PToMove));
PToMove->foo(); // expected-warning {{Dereference of null smart pointer 'PToMove' [alpha.cplusplus.SmartPtr]}}
}
void derefUnknownPtrMovedToConstruct(std::unique_ptr<A> PToMove) {
std::unique_ptr<A> P(std::move(PToMove));
PToMove->foo(); // expected-warning {{Dereference of null smart pointer 'PToMove' [alpha.cplusplus.SmartPtr]}}
}
std::unique_ptr<A> &&functionReturnsRValueRef();
void derefMoveConstructedWithRValueRefReturn() {
std::unique_ptr<A> P(functionReturnsRValueRef());
P->foo(); // No warning.
}