diff --git a/clang/lib/StaticAnalyzer/Checkers/SmartPtrChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/SmartPtrChecker.cpp --- a/clang/lib/StaticAnalyzer/Checkers/SmartPtrChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/SmartPtrChecker.cpp @@ -23,6 +23,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "llvm/ADT/StringRef.h" using namespace clang; using namespace ento; @@ -36,7 +37,10 @@ void checkPreCall(const CallEvent &Call, CheckerContext &C) const; private: - void reportBug(CheckerContext &C, const CallEvent &Call) const; + void reportBug(CheckerContext &C, const MemRegion *DerefRegion, + const CallEvent &Call) const; + void explainDereference(llvm::raw_ostream &OS, const MemRegion *DerefRegion, + const CallEvent &Call) const; }; } // end of anonymous namespace @@ -55,21 +59,45 @@ OverloadedOperatorKind OOK = OC->getOverloadedOperator(); if (OOK == OO_Star || OOK == OO_Arrow) { if (smartptr::isNullSmartPtr(State, ThisRegion)) - reportBug(C, Call); + reportBug(C, ThisRegion, Call); } } -void SmartPtrChecker::reportBug(CheckerContext &C, +void SmartPtrChecker::reportBug(CheckerContext &C, const MemRegion *DerefRegion, const CallEvent &Call) const { ExplodedNode *ErrNode = C.generateErrorNode(); if (!ErrNode) return; - - auto R = std::make_unique( - NullDereferenceBugType, "Dereference of null smart pointer", ErrNode); + llvm::SmallString<128> Str; + llvm::raw_svector_ostream OS(Str); + explainDereference(OS, DerefRegion, Call); + auto R = std::make_unique(NullDereferenceBugType, + OS.str(), ErrNode); C.emitReport(std::move(R)); } +void SmartPtrChecker::explainDereference(llvm::raw_ostream &OS, + const MemRegion *DerefRegion, + const CallEvent &Call) const { + OS << "Dereference of null smart pointer"; + + // Add smart pointer variable name + if (const auto *DR = dyn_cast(DerefRegion)) { + auto SmartPtrName = DR->getDecl()->getName(); + OS << " '" << SmartPtrName << "'"; + } + + // Add smart pointer type info + const auto *MethodDecl = dyn_cast_or_null(Call.getDecl()); + if (!MethodDecl || !MethodDecl->getParent()) + return; + + const auto *RecordDecl = MethodDecl->getParent(); + if (RecordDecl && RecordDecl->getDeclName().isIdentifier()) { + OS << " of type '" << RecordDecl->getQualifiedNameAsString() << "'"; + } +} + void ento::registerSmartPtrChecker(CheckerManager &Mgr) { Mgr.registerChecker(); } 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 @@ -23,12 +23,43 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include using namespace clang; using namespace ento; +namespace { +// Helper struct for adding smart pointer note tags. +struct SmartPtrTagDetails { + enum SmartPtrKind { UniquePtr, SharedPtr, WeakPtr }; + enum SmartPtrMethodKind { Constructor, Reset, Release, Swap, Get }; + SmartPtrTagDetails(SmartPtrKind SmartPtr, SmartPtrMethodKind SmartPtrMethod, + const MemRegion *TrackingRegion, const Expr *E = nullptr, + StringRef ArgName = "") + : SmartPtr(SmartPtr), SmartPtrMethod(SmartPtrMethod), + TrackingRegion(TrackingRegion), TrackingExpr(E), + SwappedArgName(ArgName) {} + +private: + SmartPtrKind SmartPtr; + SmartPtrMethodKind SmartPtrMethod; + const MemRegion *TrackingRegion; + const Expr *TrackingExpr; + const StringRef SwappedArgName; + +public: + void explainSmartPtrAction(llvm::raw_ostream &OS) const; + void trackValidExpr(PathSensitiveBugReport &BR) const { + if (TrackingExpr) { + bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR); + } + } +}; +} // end of anonymous namespace + namespace { class SmartPtrModeling : public Checker { @@ -49,8 +80,9 @@ const LocationContext *LCtx, const CallEvent *Call) const; private: - ProgramStateRef updateTrackedRegion(const CallEvent &Call, CheckerContext &C, - const MemRegion *ThisValRegion) const; + SVal getSValForRegion(const CallEvent &Call, CheckerContext &C) const; + const NoteTag *createTag(SmartPtrTagDetails TagDetails, + CheckerContext &C) const; void handleReset(const CallEvent &Call, CheckerContext &C) const; void handleRelease(const CallEvent &Call, CheckerContext &C) const; void handleSwap(const CallEvent &Call, CheckerContext &C) const; @@ -119,6 +151,62 @@ return State; } +// Get first argument expression if exist else nullptr +static const Expr *getFirstArgExpr(const CallEvent &Call) { + if (Call.getNumArgs() > 0) { + return Call.getArgExpr(0); + } + return nullptr; +} + +void SmartPtrTagDetails::explainSmartPtrAction(llvm::raw_ostream &OS) const { + StringRef SmartPtrName, SmartPtrTypeName; + + if (const auto *DR = dyn_cast(TrackingRegion)) { + SmartPtrName = DR->getDecl()->getName(); + } + switch (SmartPtr) { + case UniquePtr: + SmartPtrTypeName = "unique_ptr"; + break; + case SharedPtr: + SmartPtrTypeName = "shared_ptr"; + break; + case WeakPtr: + SmartPtrTypeName = "weak_ptr"; + } + + switch (SmartPtrMethod) { + case Constructor: { + if (TrackingExpr) { + OS << SmartPtrTypeName << ", '" << SmartPtrName << "'" + << " constructed using a null value"; + } else { + OS << "Default constructed " << SmartPtrTypeName << ", '" << SmartPtrName + << "'" + << " is null"; + } + break; + } + case Reset: + OS << SmartPtrTypeName << ", '" << SmartPtrName << "'" + << " reset using a null value"; + break; + case Release: + OS << SmartPtrTypeName << ", '" << SmartPtrName << "'" + << " is released and set to null"; + break; + case Swap: + OS << "Swapped null " << SmartPtrTypeName << ", '" << SwappedArgName << "'" + << " with " << SmartPtrTypeName << ", '" << SmartPtrName << "'"; + break; + case Get: + OS << "Accessed inner pointer from " << SmartPtrTypeName << ", '" + << SmartPtrName << "'"; + break; + } +} + bool SmartPtrModeling::isNullAfterMoveMethod(const CallEvent &Call) const { // TODO: Update CallDescription to support anonymous calls? // TODO: Handle other methods, such as .get() or .release(). @@ -131,11 +219,11 @@ bool SmartPtrModeling::evalCall(const CallEvent &Call, CheckerContext &C) const { + ProgramStateRef State = C.getState(); if (!smartptr::isStdSmartPtrCall(Call)) return false; if (isNullAfterMoveMethod(Call)) { - ProgramStateRef State = C.getState(); const MemRegion *ThisR = cast(&Call)->getCXXThisVal().getAsRegion(); @@ -163,8 +251,15 @@ if (!ThisValRegion) return false; - auto State = updateTrackedRegion(Call, C, ThisValRegion); - C.addTransition(State); + auto ArgVal = getSValForRegion(Call, C); + State = State->set(ThisValRegion, ArgVal); + const auto *ArgExpr = getFirstArgExpr(Call); + SmartPtrTagDetails TagDetail(SmartPtrTagDetails::UniquePtr, + SmartPtrTagDetails::Constructor, ThisValRegion, + ArgExpr); + const NoteTag *CallTag = createTag(TagDetail, C); + + C.addTransition(State, CallTag); return true; } @@ -207,6 +302,7 @@ void SmartPtrModeling::handleReset(const CallEvent &Call, CheckerContext &C) const { + ProgramStateRef State = C.getState(); const auto *IC = dyn_cast(&Call); if (!IC) return; @@ -214,14 +310,22 @@ const MemRegion *ThisValRegion = IC->getCXXThisVal().getAsRegion(); if (!ThisValRegion) return; - auto State = updateTrackedRegion(Call, C, ThisValRegion); - C.addTransition(State); + + auto ArgVal = getSValForRegion(Call, C); + State = State->set(ThisValRegion, ArgVal); + const auto *ArgExpr = getFirstArgExpr(Call); + SmartPtrTagDetails TagDetail(SmartPtrTagDetails::UniquePtr, + SmartPtrTagDetails::Reset, ThisValRegion, + ArgExpr); + const NoteTag *CallTag = createTag(TagDetail, C); + C.addTransition(State, CallTag); // TODO: Make sure to ivalidate the region in the Store if we don't have // time to model all methods. } void SmartPtrModeling::handleRelease(const CallEvent &Call, CheckerContext &C) const { + ProgramStateRef State = C.getState(); const auto *IC = dyn_cast(&Call); if (!IC) return; @@ -230,14 +334,19 @@ if (!ThisValRegion) return; - auto State = updateTrackedRegion(Call, C, ThisValRegion); - const auto *InnerPointVal = State->get(ThisValRegion); if (InnerPointVal) { State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), *InnerPointVal); } - C.addTransition(State); + + auto ValueToUpdate = C.getSValBuilder().makeNull(); + State = State->set(ThisValRegion, ValueToUpdate); + + SmartPtrTagDetails TagDetail(SmartPtrTagDetails::UniquePtr, + SmartPtrTagDetails::Release, ThisValRegion); + const NoteTag *CallTag = createTag(TagDetail, C); + C.addTransition(State, CallTag); // TODO: Add support to enable MallocChecker to start tracking the raw // pointer. } @@ -267,27 +376,42 @@ State = updateSwappedRegion(State, ThisRegion, ArgRegionInnerPointerVal); State = updateSwappedRegion(State, ArgRegion, ThisRegionInnerPointerVal); - C.addTransition(State); + StringRef SwappedRegionName = ""; + if (const auto *DR = dyn_cast(ArgRegion)) + SwappedRegionName = DR->getDecl()->getName(); + SmartPtrTagDetails TagDetail(SmartPtrTagDetails::UniquePtr, + SmartPtrTagDetails::Swap, ThisRegion, nullptr, + SwappedRegionName); + const NoteTag *CallTag = createTag(TagDetail, C); + C.addTransition(State, CallTag); } -ProgramStateRef -SmartPtrModeling::updateTrackedRegion(const CallEvent &Call, CheckerContext &C, - const MemRegion *ThisValRegion) const { - // TODO: Refactor and clean up handling too many things. +// Gets the SVal to track for a smart pointer memory region +SVal SmartPtrModeling::getSValForRegion(const CallEvent &Call, + CheckerContext &C) const { ProgramStateRef State = C.getState(); auto NumArgs = Call.getNumArgs(); - if (NumArgs == 0) { - auto NullSVal = C.getSValBuilder().makeNull(); - State = State->set(ThisValRegion, NullSVal); - } else if (NumArgs == 1) { - auto ArgVal = Call.getArgSVal(0); - assert(Call.getArgExpr(0)->getType()->isPointerType() && - "Adding a non pointer value to TrackedRegionMap"); - State = State->set(ThisValRegion, ArgVal); - } + if (NumArgs == 0) + return C.getSValBuilder().makeNull(); - return State; + auto ArgVal = Call.getArgSVal(0); + assert(Call.getArgExpr(0)->getType()->isPointerType() && + "Adding a non pointer value to TrackedRegionMap"); + return ArgVal; +} + +const NoteTag *SmartPtrModeling::createTag(SmartPtrTagDetails TagDetails, + CheckerContext &C) const { + return C.getNoteTag( + [TagDetails](PathSensitiveBugReport &BR) -> std::string { + SmallString<128> Msg; + llvm::raw_svector_ostream Out(Msg); + TagDetails.trackValidExpr(BR); + TagDetails.explainSmartPtrAction(Out); + return std::string(Out.str()); + }, + false); } void ento::registerSmartPtrModeling(CheckerManager &Mgr) { diff --git a/clang/test/Analysis/smart-ptr-text-output.cpp b/clang/test/Analysis/smart-ptr-text-output.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/smart-ptr-text-output.cpp @@ -0,0 +1,94 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.cplusplus.SmartPtr\ +// RUN: -analyzer-config cplusplus.SmartPtrModeling:ModelSmartPtrDereference=true\ +// RUN: -analyzer-output=text -std=c++11 %s -verify=expected + +#include "Inputs/system-header-simulator-cxx.h" + +class A { +public: + A(){}; + void foo(); +}; + +A *return_null() { + return nullptr; +} + +void derefAfterDefaultCtr() { + std::unique_ptr P; // expected-note {{Default constructed unique_ptr, 'P' is null}} + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' of type 'std::unique_ptr' [alpha.cplusplus.SmartPtr]}} + // expected-note@-1{{Dereference of null smart pointer 'P' of type 'std::unique_ptr'}} +} + +void derefAfterCtrWithNull() { + A *NullInnerPtr = nullptr; // expected-note {{'NullInnerPtr' initialized to a null pointer value}} + std::unique_ptr P(NullInnerPtr); // expected-note {{unique_ptr, 'P' constructed using a null value}} + *P; // expected-warning {{Dereference of null smart pointer 'P' of type 'std::unique_ptr' [alpha.cplusplus.SmartPtr]}} + // expected-note@-1{{Dereference of null smart pointer 'P' of type 'std::unique_ptr'}} +} + +void derefAfterCtrWithNullVariable() { + A *NullInnerPtr = nullptr; // expected-note {{'NullInnerPtr' initialized to a null pointer value}} + std::unique_ptr P(NullInnerPtr); // expected-note {{unique_ptr, 'P' constructed using a null value}} + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' of type 'std::unique_ptr' [alpha.cplusplus.SmartPtr]}} + // expected-note@-1{{Dereference of null smart pointer 'P' of type 'std::unique_ptr'}} +} + +void derefAfterRelease() { + std::unique_ptr P(new A()); + P.release(); // expected-note {{unique_ptr, 'P' is released and set to null}} + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' of type 'std::unique_ptr' [alpha.cplusplus.SmartPtr]}} + // expected-note@-1{{Dereference of null smart pointer 'P' of type 'std::unique_ptr'}} +} + +void derefAfterReset() { + std::unique_ptr P(new A()); + P.reset(); // expected-note {{unique_ptr, 'P' reset using a null value}} + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' of type 'std::unique_ptr' [alpha.cplusplus.SmartPtr]}} + // expected-note@-1{{Dereference of null smart pointer 'P' of type 'std::unique_ptr'}} +} + +void derefAfterResetWithNull() { + A *NullInnerPtr = nullptr; // expected-note {{'NullInnerPtr' initialized to a null pointer value}} + std::unique_ptr P(new A()); + P.reset(NullInnerPtr); // expected-note {{unique_ptr, 'P' reset using a null value}} + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' of type 'std::unique_ptr' [alpha.cplusplus.SmartPtr]}} + // expected-note@-1{{Dereference of null smart pointer 'P' of type 'std::unique_ptr'}} +} + +void derefOnReleasedNullRawPtr() { + std::unique_ptr P; // expected-note {{Default constructed unique_ptr, 'P' is null}} + A *AP = P.release(); // expected-note {{'AP' initialized to a null pointer value}} + // expected-note@-1 {{unique_ptr, 'P' is released and set to null}} + AP->foo(); // expected-warning {{Called C++ object pointer is null [core.CallAndMessage]}} + // expected-note@-1{{Called C++ object pointer is null}} +} + +void derefOnSwappedNullPtr() { + std::unique_ptr P(new A()); + std::unique_ptr PNull; // expected-note {{Default constructed unique_ptr, 'PNull' is null}} + P.swap(PNull); // expected-note {{Swapped null unique_ptr, 'PNull' with unique_ptr, 'P'}} + PNull->foo(); // No warning. + (*P).foo(); // expected-warning {{Dereference of null smart pointer 'P' of type 'std::unique_ptr' [alpha.cplusplus.SmartPtr]}} + // expected-note@-1{{Dereference of null smart pointer 'P' of type 'std::unique_ptr'}} +} + +// TODO: Enabale this test when "std::swap" is modeled seperately. +void derefOnStdSwappedNullPtr() { + std::unique_ptr P; + std::unique_ptr PNull; + // std::swap(P, PNull); + // PNull->foo(); + // P->foo(); +} + +struct StructWithSmartPtr { // expected-note {{Default constructed unique_ptr, 'P' is null}} + std::unique_ptr P; +}; + +void derefAfterDefaultCtrInsideStruct() { + StructWithSmartPtr S; // expected-note {{Calling implicit default constructor for 'StructWithSmartPtr'}} + // expected-note@-1 {{Returning from default constructor for 'StructWithSmartPtr'}} + S.P->foo(); // expected-warning {{Dereference of null smart pointer 'P' of type 'std::unique_ptr' [alpha.cplusplus.SmartPtr]}} + // expected-note@-1{{Dereference of null smart pointer 'P' of type 'std::unique_ptr'}} +} 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 @@ -12,7 +12,7 @@ std::unique_ptr Q = std::move(P); if (Q) clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} - *Q.get() = 1; // no-warning + *Q.get() = 1; // no-warning if (P) clang_analyzer_warnIfReached(); // no-warning // TODO: Report a null dereference (instead). @@ -50,37 +50,38 @@ void derefAfterDefaultCtr() { std::unique_ptr P; - P->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' of type 'std::unique_ptr' [alpha.cplusplus.SmartPtr]}} } void derefAfterCtrWithNull() { std::unique_ptr P(nullptr); - *P; // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} + *P; // expected-warning {{Dereference of null smart pointer 'P' of type 'std::unique_ptr' [alpha.cplusplus.SmartPtr]}} } -void derefAfterCtrWithNullReturnMethod() { - std::unique_ptr P(return_null()); - P->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} +void derefAfterCtrWithNullVariable() { + A *InnerPtr = nullptr; + std::unique_ptr P(InnerPtr); + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' of type 'std::unique_ptr' [alpha.cplusplus.SmartPtr]}} } void derefAfterRelease() { 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 'P' of type 'std::unique_ptr' [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 'P' of type 'std::unique_ptr' [alpha.cplusplus.SmartPtr]}} } void derefAfterResetWithNull() { std::unique_ptr P(new A()); P.reset(nullptr); - P->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' of type 'std::unique_ptr' [alpha.cplusplus.SmartPtr]}} } void derefAfterResetWithNonNull() { @@ -102,6 +103,12 @@ AP->foo(); // expected-warning {{Called C++ object pointer is null [core.CallAndMessage]}} } +void derefOnReleasedValidRawPtr() { + std::unique_ptr P(new A()); + A *AP = P.release(); + AP->foo(); // No warning. +} + void pass_smart_ptr_by_ref(std::unique_ptr &a); void pass_smart_ptr_by_const_ref(const std::unique_ptr &a); void pass_smart_ptr_by_rvalue_ref(std::unique_ptr &&a); @@ -118,7 +125,7 @@ { std::unique_ptr P; pass_smart_ptr_by_const_ref(P); - P->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' of type 'std::unique_ptr' [alpha.cplusplus.SmartPtr]}} } { std::unique_ptr P; @@ -128,7 +135,7 @@ { std::unique_ptr P; pass_smart_ptr_by_const_rvalue_ref(std::move(P)); - P->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' of type 'std::unique_ptr' [alpha.cplusplus.SmartPtr]}} } { std::unique_ptr P; @@ -138,7 +145,7 @@ { std::unique_ptr P; pass_smart_ptr_by_const_ptr(&P); - P->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' of type 'std::unique_ptr' [alpha.cplusplus.SmartPtr]}} } } @@ -162,7 +169,7 @@ { StructWithSmartPtr S; pass_struct_with_smart_ptr_by_const_ref(S); - S.P->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} + S.P->foo(); // expected-warning {{Dereference of null smart pointer 'P' of type 'std::unique_ptr' [alpha.cplusplus.SmartPtr]}} } { StructWithSmartPtr S; @@ -172,7 +179,7 @@ { StructWithSmartPtr S; pass_struct_with_smart_ptr_by_const_rvalue_ref(std::move(S)); - S.P->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} + S.P->foo(); // expected-warning {{Dereference of null smart pointer 'P' of type 'std::unique_ptr' [alpha.cplusplus.SmartPtr]}} } { StructWithSmartPtr S; @@ -182,7 +189,7 @@ { StructWithSmartPtr S; pass_struct_with_smart_ptr_by_const_ptr(&S); - S.P->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} + S.P->foo(); // expected-warning {{Dereference of null smart pointer 'P' of type 'std::unique_ptr' [alpha.cplusplus.SmartPtr]}} } } @@ -207,24 +214,24 @@ std::unique_ptr PNull; P.swap(PNull); PNull->foo(); // No warning. - (*P).foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} + (*P).foo(); // expected-warning {{Dereference of null smart pointer 'P' of type 'std::unique_ptr' [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]}} + PNull->foo(); // expected-warning {{Dereference of null smart pointer 'PNull' of type 'std::unique_ptr' [alpha.cplusplus.SmartPtr]}} + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' of type 'std::unique_ptr' [alpha.cplusplus.SmartPtr]}} } void derefOnSwappedValidPtr() { std::unique_ptr P(new A()); std::unique_ptr PValid(new A()); P.swap(PValid); - (*P).foo(); // No warning. + (*P).foo(); // No warning. PValid->foo(); // No warning. std::swap(P, PValid); - P->foo(); // No warning. + P->foo(); // No warning. PValid->foo(); // No warning. }