Skip to content

Commit 1b743a9

Browse files
committed
[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
1 parent db464a2 commit 1b743a9

File tree

3 files changed

+98
-4
lines changed

3 files changed

+98
-4
lines changed

clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ class SmartPtrModeling
5858
void handleSwap(const CallEvent &Call, CheckerContext &C) const;
5959
void handleGet(const CallEvent &Call, CheckerContext &C) const;
6060
bool handleAssignOp(const CallEvent &Call, CheckerContext &C) const;
61+
bool handleMoveCtr(const CallEvent &Call, CheckerContext &C,
62+
const MemRegion *ThisRegion) const;
63+
bool updateMovedSmartPointers(CheckerContext &C, const MemRegion *ThisRegion,
64+
const MemRegion *OtherSmartPtrRegion) const;
6165

6266
using SmartPtrMethodHandlerFn =
6367
void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const;
@@ -160,13 +164,16 @@ bool SmartPtrModeling::evalCall(const CallEvent &Call,
160164
return false;
161165

162166
if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
163-
if (CC->getDecl()->isCopyOrMoveConstructor())
167+
if (CC->getDecl()->isCopyConstructor())
164168
return false;
165169

166170
const MemRegion *ThisRegion = CC->getCXXThisVal().getAsRegion();
167171
if (!ThisRegion)
168172
return false;
169173

174+
if (CC->getDecl()->isMoveConstructor())
175+
return handleMoveCtr(Call, C, ThisRegion);
176+
170177
if (Call.getNumArgs() == 0) {
171178
auto NullVal = C.getSValBuilder().makeNull();
172179
State = State->set<TrackedRegionMap>(ThisRegion, NullVal);
@@ -410,6 +417,22 @@ bool SmartPtrModeling::handleAssignOp(const CallEvent &Call,
410417
return true;
411418
}
412419

420+
return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion);
421+
}
422+
423+
bool SmartPtrModeling::handleMoveCtr(const CallEvent &Call, CheckerContext &C,
424+
const MemRegion *ThisRegion) const {
425+
const auto *OtherSmartPtrRegion = Call.getArgSVal(0).getAsRegion();
426+
if (!OtherSmartPtrRegion)
427+
return false;
428+
429+
return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion);
430+
}
431+
432+
bool SmartPtrModeling::updateMovedSmartPointers(
433+
CheckerContext &C, const MemRegion *ThisRegion,
434+
const MemRegion *OtherSmartPtrRegion) const {
435+
ProgramStateRef State = C.getState();
413436
const auto *OtherInnerPtr = State->get<TrackedRegionMap>(OtherSmartPtrRegion);
414437
if (OtherInnerPtr) {
415438
State = State->set<TrackedRegionMap>(ThisRegion, *OtherInnerPtr);
@@ -430,7 +453,7 @@ bool SmartPtrModeling::handleAssignOp(const CallEvent &Call,
430453
ThisRegion->printPretty(OS);
431454
}
432455
if (BR.isInteresting(ThisRegion) && IsArgValNull) {
433-
OS << "Null pointer value move-assigned to ";
456+
OS << "A null pointer value is moved to ";
434457
ThisRegion->printPretty(OS);
435458
BR.markInteresting(OtherSmartPtrRegion);
436459
}

clang/test/Analysis/smart-ptr-text-output.cpp

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ void derefOnNullPtrGotMovedFromValidPtr() {
144144
std::unique_ptr<A> P(new A()); // expected-note {{Smart pointer 'P' is constructed}}
145145
// FIXME: above note should go away once we fix marking region not interested.
146146
std::unique_ptr<A> PToMove; // expected-note {{Default constructed smart pointer 'PToMove' is null}}
147-
P = std::move(PToMove); // expected-note {{Null pointer value move-assigned to 'P'}}
147+
P = std::move(PToMove); // expected-note {{A null pointer value is moved to 'P'}}
148148
P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}}
149149
// expected-note@-1 {{Dereference of null smart pointer 'P'}}
150150
}
@@ -170,3 +170,32 @@ void derefOnAssignedZeroToNullSmartPtr() {
170170
P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}}
171171
// expected-note@-1 {{Dereference of null smart pointer 'P'}}
172172
}
173+
174+
void derefMoveConstructedWithNullPtr() {
175+
std::unique_ptr<A> PToMove; // expected-note {{Default constructed smart pointer 'PToMove' is null}}
176+
std::unique_ptr<A> P(std::move(PToMove)); // expected-note {{A null pointer value is moved to 'P'}}
177+
P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}}
178+
// expected-note@-1{{Dereference of null smart pointer 'P'}}
179+
}
180+
181+
void derefValidPtrMovedToConstruct() {
182+
std::unique_ptr<A> PToMove(new A()); // expected-note {{Smart pointer 'PToMove' is constructed}}
183+
// FIXME: above note should go away once we fix marking region not interested.
184+
std::unique_ptr<A> P(std::move(PToMove)); // expected-note {{Smart pointer 'PToMove' is null after being moved to 'P'}}
185+
PToMove->foo(); // expected-warning {{Dereference of null smart pointer 'PToMove' [alpha.cplusplus.SmartPtr]}}
186+
// expected-note@-1{{Dereference of null smart pointer 'PToMove'}}
187+
}
188+
189+
void derefNullPtrMovedToConstruct() {
190+
std::unique_ptr<A> PToMove; // expected-note {{Default constructed smart pointer 'PToMove' is null}}
191+
// FIXME: above note should go away once we fix marking region not interested.
192+
std::unique_ptr<A> P(std::move(PToMove)); // expected-note {{Smart pointer 'PToMove' is null after being moved to 'P'}}
193+
PToMove->foo(); // expected-warning {{Dereference of null smart pointer 'PToMove' [alpha.cplusplus.SmartPtr]}}
194+
// expected-note@-1{{Dereference of null smart pointer 'PToMove'}}
195+
}
196+
197+
void derefUnknownPtrMovedToConstruct(std::unique_ptr<A> PToMove) {
198+
std::unique_ptr<A> P(std::move(PToMove)); // expected-note {{Smart pointer 'PToMove' is null after; previous value moved to 'P'}}
199+
PToMove->foo(); // expected-warning {{Dereference of null smart pointer 'PToMove' [alpha.cplusplus.SmartPtr]}}
200+
// expected-note@-1{{Dereference of null smart pointer 'PToMove'}}
201+
}

clang/test/Analysis/smart-ptr.cpp

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ void derefAfterMove(std::unique_ptr<int> P) {
1717
if (P)
1818
clang_analyzer_warnIfReached(); // no-warning
1919
// TODO: Report a null dereference (instead).
20-
*P.get() = 1; // expected-warning {{Method called on moved-from object 'P'}}
20+
*P.get() = 1; // expected-warning {{Method called on moved-from object 'P' [cplusplus.Move]}}
21+
// expected-warning@-1 {{Dereference of null pointer [core.NullDereference]}}
2122
}
2223

2324
// Don't crash when attempting to model a call with unknown callee.
@@ -333,3 +334,44 @@ void drefOnAssignedNullFromMethodPtrValidSmartPtr() {
333334
P = returnRValRefOfUniquePtr();
334335
P->foo(); // No warning.
335336
}
337+
338+
void derefMoveConstructedWithValidPtr() {
339+
std::unique_ptr<A> PToMove(new A());
340+
std::unique_ptr<A> P(std::move(PToMove));
341+
P->foo(); // No warning.
342+
}
343+
344+
void derefMoveConstructedWithNullPtr() {
345+
std::unique_ptr<A> PToMove;
346+
std::unique_ptr<A> P(std::move(PToMove));
347+
P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}}
348+
}
349+
350+
void derefMoveConstructedWithUnknownPtr(std::unique_ptr<A> PToMove) {
351+
std::unique_ptr<A> P(std::move(PToMove));
352+
P->foo(); // No warning.
353+
}
354+
355+
void derefValidPtrMovedToConstruct() {
356+
std::unique_ptr<A> PToMove(new A());
357+
std::unique_ptr<A> P(std::move(PToMove));
358+
PToMove->foo(); // expected-warning {{Dereference of null smart pointer 'PToMove' [alpha.cplusplus.SmartPtr]}}
359+
}
360+
361+
void derefNullPtrMovedToConstruct() {
362+
std::unique_ptr<A> PToMove;
363+
std::unique_ptr<A> P(std::move(PToMove));
364+
PToMove->foo(); // expected-warning {{Dereference of null smart pointer 'PToMove' [alpha.cplusplus.SmartPtr]}}
365+
}
366+
367+
void derefUnknownPtrMovedToConstruct(std::unique_ptr<A> PToMove) {
368+
std::unique_ptr<A> P(std::move(PToMove));
369+
PToMove->foo(); // expected-warning {{Dereference of null smart pointer 'PToMove' [alpha.cplusplus.SmartPtr]}}
370+
}
371+
372+
std::unique_ptr<A> &&functionReturnsRValueRef();
373+
374+
void derefMoveConstructedWithRValueRefReturn() {
375+
std::unique_ptr<A> P(functionReturnsRValueRef());
376+
P->foo(); // No warning.
377+
}

0 commit comments

Comments
 (0)