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 @@ -87,6 +87,17 @@ } // namespace ento } // namespace clang +static ProgramStateRef updateSwappedRegion(ProgramStateRef State, + const MemRegion *Region, + const SVal *RegionInnerPointerVal) { + if (RegionInnerPointerVal) { + State = State->set(Region, *RegionInnerPointerVal); + } else { + State = State->remove(Region); + } + return State; +} + bool SmartPtrModeling::isNullAfterMoveMethod(const CallEvent &Call) const { // TODO: Update CallDescription to support anonymous calls? // TODO: Handle other methods, such as .get() or .release(). @@ -169,7 +180,7 @@ return; auto State = updateTrackedRegion(Call, C, ThisValRegion); C.addTransition(State); - // TODO: Make sure to ivalidate the the region in the Store if we don't have + // TODO: Make sure to ivalidate the region in the Store if we don't have // time to model all methods. } @@ -197,7 +208,30 @@ void SmartPtrModeling::handleSwap(const CallEvent &Call, CheckerContext &C) const { - // TODO: Add support to handle swap method. + // To model unique_ptr::swap() method. + const auto *IC = dyn_cast(&Call); + if (!IC) + return; + + const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); + if (!ThisRegion) + return; + + const auto *ArgRegion = Call.getArgSVal(0).getAsRegion(); + if (!ArgRegion) + return; + + auto State = C.getState(); + const auto *ThisRegionInnerPointerVal = + State->get(ThisRegion); + const auto *ArgRegionInnerPointerVal = + State->get(ArgRegion); + + // Swap the tracked region values. + State = updateSwappedRegion(State, ThisRegion, ArgRegionInnerPointerVal); + State = updateSwappedRegion(State, ArgRegion, ThisRegionInnerPointerVal); + + C.addTransition(State); } ProgramStateRef diff --git a/clang/test/Analysis/Inputs/system-header-simulator-cxx.h b/clang/test/Analysis/Inputs/system-header-simulator-cxx.h --- a/clang/test/Analysis/Inputs/system-header-simulator-cxx.h +++ b/clang/test/Analysis/Inputs/system-header-simulator-cxx.h @@ -960,7 +960,13 @@ T *operator->() const; operator bool() const; }; -} + + // TODO :: Once the deleter parameter is added update with additional template parameter. + template + void swap(unique_ptr &x, unique_ptr &y) noexcept { + x.swap(y); + } + } // namespace std #endif #ifdef TEST_INLINABLE_ALLOCATORS 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 @@ -67,14 +67,14 @@ std::unique_ptr P(new A()); P.release(); clang_analyzer_numTimesReached(); // expected-warning {{1}} - P->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} + P->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} } void derefAfterReset() { std::unique_ptr P(new A()); P.reset(); clang_analyzer_numTimesReached(); // expected-warning {{1}} - P->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} + P->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} } void derefAfterResetWithNull() { @@ -101,3 +101,30 @@ A *AP = P.release(); AP->foo(); // expected-warning {{Called C++ object pointer is null [core.CallAndMessage]}} } + +void derefOnSwappedNullPtr() { + std::unique_ptr P(new A()); + std::unique_ptr PNull; + P.swap(PNull); + PNull->foo(); // No warning. + (*P).foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} +} + +void derefOnStdSwappedNullPtr() { + std::unique_ptr P; + std::unique_ptr PNull; + std::swap(P, PNull); + PNull->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} + P->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} +} + +void derefOnSwappedValidPtr() { + std::unique_ptr P(new A()); + std::unique_ptr PValid(new A()); + P.swap(PValid); + (*P).foo(); // No warning. + PValid->foo(); // No warning. + std::swap(P, PValid); + P->foo(); // No warning. + PValid->foo(); // No warning. +}