[Analyzer] Support note tags for smart ptr checker

Summary:
Added support for note tags for null smart_ptr reporting

Reviewers: NoQ, Szelethus, vsavchenko, xazax.hun

Reviewed By: NoQ, vsavchenko, xazax.hun

Subscribers: martong, cfe-commits
Tags: #clang

Differential Revision: https://reviews.llvm.org/D84600
This commit is contained in:
Nithin Vadukkumchery Rajendrakumar 2020-08-11 20:33:54 +02:00
parent cfdc96714b
commit 06d100a69a
6 changed files with 298 additions and 67 deletions

View File

@ -301,6 +301,26 @@ public:
IsPrunable); IsPrunable);
} }
/// A shorthand version of getNoteTag that accepts a lambda with stream for
/// note.
///
/// @param Cb Callback with 'BugReport &' and 'llvm::raw_ostream &'.
/// @param IsPrunable Whether the note is prunable. It allows BugReporter
/// to omit the note from the report if it would make the displayed
/// bug path significantly shorter.
const NoteTag *getNoteTag(
std::function<void(PathSensitiveBugReport &BR, llvm::raw_ostream &OS)> &&Cb,
bool IsPrunable = false) {
return getNoteTag(
[Cb](PathSensitiveBugReport &BR) -> std::string {
llvm::SmallString<128> Str;
llvm::raw_svector_ostream OS(Str);
Cb(BR, OS);
return std::string(OS.str());
},
IsPrunable);
}
/// Returns the word that should be used to refer to the declaration /// Returns the word that should be used to refer to the declaration
/// in the report. /// in the report.
StringRef getDeclDescription(const Decl *D); StringRef getDeclDescription(const Decl *D);

View File

@ -26,6 +26,8 @@ bool isStdSmartPtrCall(const CallEvent &Call);
/// Returns whether the smart pointer is null or not. /// Returns whether the smart pointer is null or not.
bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion); bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion);
const BugType *getNullDereferenceBugType();
} // namespace smartptr } // namespace smartptr
} // namespace ento } // namespace ento
} // namespace clang } // namespace clang

View File

@ -23,23 +23,40 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
#include "llvm/ADT/StringRef.h"
using namespace clang; using namespace clang;
using namespace ento; using namespace ento;
namespace { namespace {
static const BugType *NullDereferenceBugTypePtr;
class SmartPtrChecker : public Checker<check::PreCall> { class SmartPtrChecker : public Checker<check::PreCall> {
public:
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
BugType NullDereferenceBugType{this, "Null SmartPtr dereference", BugType NullDereferenceBugType{this, "Null SmartPtr dereference",
"C++ Smart Pointer"}; "C++ Smart Pointer"};
public:
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
private: 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 } // end of anonymous namespace
// Define the inter-checker API.
namespace clang {
namespace ento {
namespace smartptr {
const BugType *getNullDereferenceBugType() { return NullDereferenceBugTypePtr; }
} // namespace smartptr
} // namespace ento
} // namespace clang
void SmartPtrChecker::checkPreCall(const CallEvent &Call, void SmartPtrChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const { CheckerContext &C) const {
if (!smartptr::isStdSmartPtrCall(Call)) if (!smartptr::isStdSmartPtrCall(Call))
@ -55,23 +72,34 @@ void SmartPtrChecker::checkPreCall(const CallEvent &Call,
OverloadedOperatorKind OOK = OC->getOverloadedOperator(); OverloadedOperatorKind OOK = OC->getOverloadedOperator();
if (OOK == OO_Star || OOK == OO_Arrow) { if (OOK == OO_Star || OOK == OO_Arrow) {
if (smartptr::isNullSmartPtr(State, ThisRegion)) 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 { const CallEvent &Call) const {
ExplodedNode *ErrNode = C.generateErrorNode(); ExplodedNode *ErrNode = C.generateErrorNode();
if (!ErrNode) if (!ErrNode)
return; return;
llvm::SmallString<128> Str;
auto R = std::make_unique<PathSensitiveBugReport>( llvm::raw_svector_ostream OS(Str);
NullDereferenceBugType, "Dereference of null smart pointer", ErrNode); explainDereference(OS, DerefRegion, Call);
auto R = std::make_unique<PathSensitiveBugReport>(NullDereferenceBugType,
OS.str(), ErrNode);
R->markInteresting(DerefRegion);
C.emitReport(std::move(R)); 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 ";
DerefRegion->printPretty(OS);
}
void ento::registerSmartPtrChecker(CheckerManager &Mgr) { void ento::registerSmartPtrChecker(CheckerManager &Mgr) {
Mgr.registerChecker<SmartPtrChecker>(); SmartPtrChecker *Checker = Mgr.registerChecker<SmartPtrChecker>();
NullDereferenceBugTypePtr = &Checker->NullDereferenceBugType;
} }
bool ento::shouldRegisterSmartPtrChecker(const CheckerManager &mgr) { bool ento::shouldRegisterSmartPtrChecker(const CheckerManager &mgr) {

View File

@ -17,14 +17,18 @@
#include "clang/AST/DeclCXX.h" #include "clang/AST/DeclCXX.h"
#include "clang/AST/ExprCXX.h" #include "clang/AST/ExprCXX.h"
#include "clang/AST/Type.h" #include "clang/AST/Type.h"
#include "clang/Basic/LLVM.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.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/SVals.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include <string>
using namespace clang; using namespace clang;
using namespace ento; using namespace ento;
@ -49,8 +53,6 @@ public:
const LocationContext *LCtx, const CallEvent *Call) const; const LocationContext *LCtx, const CallEvent *Call) const;
private: private:
ProgramStateRef updateTrackedRegion(const CallEvent &Call, CheckerContext &C,
const MemRegion *ThisValRegion) const;
void handleReset(const CallEvent &Call, CheckerContext &C) const; void handleReset(const CallEvent &Call, CheckerContext &C) const;
void handleRelease(const CallEvent &Call, CheckerContext &C) const; void handleRelease(const CallEvent &Call, CheckerContext &C) const;
void handleSwap(const CallEvent &Call, CheckerContext &C) const; void handleSwap(const CallEvent &Call, CheckerContext &C) const;
@ -131,11 +133,11 @@ bool SmartPtrModeling::isNullAfterMoveMethod(const CallEvent &Call) const {
bool SmartPtrModeling::evalCall(const CallEvent &Call, bool SmartPtrModeling::evalCall(const CallEvent &Call,
CheckerContext &C) const { CheckerContext &C) const {
ProgramStateRef State = C.getState();
if (!smartptr::isStdSmartPtrCall(Call)) if (!smartptr::isStdSmartPtrCall(Call))
return false; return false;
if (isNullAfterMoveMethod(Call)) { if (isNullAfterMoveMethod(Call)) {
ProgramStateRef State = C.getState();
const MemRegion *ThisR = const MemRegion *ThisR =
cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion(); cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
@ -159,12 +161,46 @@ bool SmartPtrModeling::evalCall(const CallEvent &Call,
if (CC->getDecl()->isCopyOrMoveConstructor()) if (CC->getDecl()->isCopyOrMoveConstructor())
return false; return false;
const MemRegion *ThisValRegion = CC->getCXXThisVal().getAsRegion(); const MemRegion *ThisRegion = CC->getCXXThisVal().getAsRegion();
if (!ThisValRegion) if (!ThisRegion)
return false; return false;
auto State = updateTrackedRegion(Call, C, ThisValRegion); if (Call.getNumArgs() == 0) {
C.addTransition(State); auto NullVal = C.getSValBuilder().makeNull();
State = State->set<TrackedRegionMap>(ThisRegion, NullVal);
C.addTransition(
State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
llvm::raw_ostream &OS) {
if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
!BR.isInteresting(ThisRegion))
return;
OS << "Default constructed smart pointer ";
ThisRegion->printPretty(OS);
OS << " is null";
}));
} else {
const auto *TrackingExpr = Call.getArgExpr(0);
assert(TrackingExpr->getType()->isPointerType() &&
"Adding a non pointer value to TrackedRegionMap");
auto ArgVal = Call.getArgSVal(0);
State = State->set<TrackedRegionMap>(ThisRegion, ArgVal);
C.addTransition(State, C.getNoteTag([ThisRegion, TrackingExpr,
ArgVal](PathSensitiveBugReport &BR,
llvm::raw_ostream &OS) {
if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
!BR.isInteresting(ThisRegion))
return;
bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
OS << "Smart pointer ";
ThisRegion->printPretty(OS);
if (ArgVal.isZeroConstant())
OS << " is constructed using a null value";
else
OS << " is constructed";
}));
}
return true; return true;
} }
@ -207,37 +243,65 @@ ProgramStateRef SmartPtrModeling::checkRegionChanges(
void SmartPtrModeling::handleReset(const CallEvent &Call, void SmartPtrModeling::handleReset(const CallEvent &Call,
CheckerContext &C) const { CheckerContext &C) const {
ProgramStateRef State = C.getState();
const auto *IC = dyn_cast<CXXInstanceCall>(&Call); const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
if (!IC) if (!IC)
return; return;
const MemRegion *ThisValRegion = IC->getCXXThisVal().getAsRegion(); const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
if (!ThisValRegion) if (!ThisRegion)
return; return;
auto State = updateTrackedRegion(Call, C, ThisValRegion);
C.addTransition(State); assert(Call.getArgExpr(0)->getType()->isPointerType() &&
"Adding a non pointer value to TrackedRegionMap");
State = State->set<TrackedRegionMap>(ThisRegion, Call.getArgSVal(0));
const auto *TrackingExpr = Call.getArgExpr(0);
C.addTransition(
State, C.getNoteTag([ThisRegion, TrackingExpr](PathSensitiveBugReport &BR,
llvm::raw_ostream &OS) {
if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
!BR.isInteresting(ThisRegion))
return;
bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
OS << "Smart pointer ";
ThisRegion->printPretty(OS);
OS << " reset using a null value";
}));
// TODO: Make sure to ivalidate 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. // time to model all methods.
} }
void SmartPtrModeling::handleRelease(const CallEvent &Call, void SmartPtrModeling::handleRelease(const CallEvent &Call,
CheckerContext &C) const { CheckerContext &C) const {
ProgramStateRef State = C.getState();
const auto *IC = dyn_cast<CXXInstanceCall>(&Call); const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
if (!IC) if (!IC)
return; return;
const MemRegion *ThisValRegion = IC->getCXXThisVal().getAsRegion(); const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
if (!ThisValRegion) if (!ThisRegion)
return; return;
auto State = updateTrackedRegion(Call, C, ThisValRegion); const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisValRegion);
if (InnerPointVal) { if (InnerPointVal) {
State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
*InnerPointVal); *InnerPointVal);
} }
C.addTransition(State);
auto ValueToUpdate = C.getSValBuilder().makeNull();
State = State->set<TrackedRegionMap>(ThisRegion, ValueToUpdate);
C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
llvm::raw_ostream &OS) {
if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
!BR.isInteresting(ThisRegion))
return;
OS << "Smart pointer ";
ThisRegion->printPretty(OS);
OS << " is released and set to null";
}));
// TODO: Add support to enable MallocChecker to start tracking the raw // TODO: Add support to enable MallocChecker to start tracking the raw
// pointer. // pointer.
} }
@ -267,27 +331,18 @@ void SmartPtrModeling::handleSwap(const CallEvent &Call,
State = updateSwappedRegion(State, ThisRegion, ArgRegionInnerPointerVal); State = updateSwappedRegion(State, ThisRegion, ArgRegionInnerPointerVal);
State = updateSwappedRegion(State, ArgRegion, ThisRegionInnerPointerVal); State = updateSwappedRegion(State, ArgRegion, ThisRegionInnerPointerVal);
C.addTransition(State); C.addTransition(
} State, C.getNoteTag([ThisRegion, ArgRegion](PathSensitiveBugReport &BR,
llvm::raw_ostream &OS) {
ProgramStateRef if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
SmartPtrModeling::updateTrackedRegion(const CallEvent &Call, CheckerContext &C, !BR.isInteresting(ThisRegion))
const MemRegion *ThisValRegion) const { return;
// TODO: Refactor and clean up handling too many things. BR.markInteresting(ArgRegion);
ProgramStateRef State = C.getState(); OS << "Swapped null smart pointer ";
auto NumArgs = Call.getNumArgs(); ArgRegion->printPretty(OS);
OS << " with smart pointer ";
if (NumArgs == 0) { ThisRegion->printPretty(OS);
auto NullSVal = C.getSValBuilder().makeNull(); }));
State = State->set<TrackedRegionMap>(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<TrackedRegionMap>(ThisValRegion, ArgVal);
}
return State;
} }
void ento::registerSmartPtrModeling(CheckerManager &Mgr) { void ento::registerSmartPtrModeling(CheckerManager &Mgr) {

View File

@ -0,0 +1,119 @@
// RUN: %clang_analyze_cc1\
// RUN: -analyzer-checker=core,cplusplus.Move,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<A> P; // expected-note {{Default constructed smart pointer 'P' is null}}
P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}}
// expected-note@-1{{Dereference of null smart pointer 'P'}}
}
void derefAfterCtrWithNull() {
A *NullInnerPtr = nullptr; // expected-note {{'NullInnerPtr' initialized to a null pointer value}}
std::unique_ptr<A> P(NullInnerPtr); // expected-note {{Smart pointer 'P' is constructed using a null value}}
*P; // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}}
// expected-note@-1{{Dereference of null smart pointer 'P'}}
}
void derefAfterCtrWithNullVariable() {
A *NullInnerPtr = nullptr; // expected-note {{'NullInnerPtr' initialized to a null pointer value}}
std::unique_ptr<A> P(NullInnerPtr); // expected-note {{Smart pointer 'P' is constructed using a null value}}
P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}}
// expected-note@-1{{Dereference of null smart pointer 'P'}}
}
void derefAfterRelease() {
std::unique_ptr<A> P(new A());
P.release(); // expected-note {{Smart pointer 'P' is released and set to null}}
P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}}
// expected-note@-1{{Dereference of null smart pointer 'P'}}
}
void derefAfterReset() {
std::unique_ptr<A> P(new A());
P.reset(); // expected-note {{Smart pointer 'P' reset using a null value}}
P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}}
// expected-note@-1{{Dereference of null smart pointer 'P'}}
}
void derefAfterResetWithNull() {
A *NullInnerPtr = nullptr; // expected-note {{'NullInnerPtr' initialized to a null pointer value}}
std::unique_ptr<A> P(new A());
P.reset(NullInnerPtr); // expected-note {{Smart pointer 'P' reset using a null value}}
P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}}
// expected-note@-1{{Dereference of null smart pointer 'P'}}
}
// FIXME: Fix this test when support is added for tracking raw pointer
// and mark the smart pointer as interesting based on that and add tags.
void derefOnReleasedNullRawPtr() {
std::unique_ptr<A> P; // FIXME: add note "Default constructed smart pointer 'P' is null"
A *AP = P.release(); // expected-note {{'AP' initialized to a null pointer value}}
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<A> P(new A());
std::unique_ptr<A> PNull; // expected-note {{Default constructed smart pointer 'PNull' is null}}
P.swap(PNull); // expected-note {{Swapped null smart pointer 'PNull' with smart pointer 'P'}}
PNull->foo(); // No warning.
(*P).foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}}
// expected-note@-1{{Dereference of null smart pointer 'P'}}
}
// FIXME: Fix this test when "std::swap" is modeled seperately.
void derefOnStdSwappedNullPtr() {
std::unique_ptr<A> P;
std::unique_ptr<A> PNull; // expected-note {{Default constructed smart pointer 'PNull' is null}}
std::swap(P, PNull); // expected-note@Inputs/system-header-simulator-cxx.h:978 {{Swapped null smart pointer 'PNull' with smart pointer 'P'}}
// expected-note@-1 {{Calling 'swap<A>'}}
// expected-note@-2 {{Returning from 'swap<A>'}}
PNull->foo(); // expected-warning {{Dereference of null smart pointer 'PNull' [alpha.cplusplus.SmartPtr]}}
// expected-note@-1{{Dereference of null smart pointer 'PNull'}}
P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}}
// expected-note@-1{{Dereference of null smart pointer 'P'}}
}
struct StructWithSmartPtr { // expected-note {{Default constructed smart pointer 'S.P' is null}}
std::unique_ptr<A> 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 'S.P' [alpha.cplusplus.SmartPtr]}}
// expected-note@-1{{Dereference of null smart pointer 'S.P'}}
}
void noNoteTagsForNonInterestingRegion() {
std::unique_ptr<A> P; // expected-note {{Default constructed smart pointer 'P' is null}}
std::unique_ptr<A> P1; // No note.
std::unique_ptr<A> P2; // No note.
P1.release(); // No note.
P1.reset(); // No note.
P1.swap(P2); // No note.
P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}}
// expected-note@-1{{Dereference of null smart pointer 'P'}}
}
void noNoteTagsForNonMatchingBugType() {
std::unique_ptr<A> P; // No note.
std::unique_ptr<A> P1; // No note.
P1 = std::move(P); // expected-note {{Smart pointer 'P' of type 'std::unique_ptr' is reset to null when moved from}}
P->foo(); // expected-warning {{Dereference of null smart pointer 'P' of type 'std::unique_ptr' [cplusplus.Move]}}
// expected-note@-1 {{Dereference of null smart pointer 'P' of type 'std::unique_ptr'}}
}

View File

@ -12,7 +12,7 @@ void derefAfterMove(std::unique_ptr<int> P) {
std::unique_ptr<int> Q = std::move(P); std::unique_ptr<int> Q = std::move(P);
if (Q) if (Q)
clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
*Q.get() = 1; // no-warning *Q.get() = 1; // no-warning
if (P) if (P)
clang_analyzer_warnIfReached(); // no-warning clang_analyzer_warnIfReached(); // no-warning
// TODO: Report a null dereference (instead). // TODO: Report a null dereference (instead).
@ -50,37 +50,38 @@ void derefOfUnknown(std::unique_ptr<A> P) {
void derefAfterDefaultCtr() { void derefAfterDefaultCtr() {
std::unique_ptr<A> P; std::unique_ptr<A> P;
P->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}}
} }
void derefAfterCtrWithNull() { void derefAfterCtrWithNull() {
std::unique_ptr<A> P(nullptr); std::unique_ptr<A> P(nullptr);
*P; // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} *P; // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}}
} }
void derefAfterCtrWithNullReturnMethod() { void derefAfterCtrWithNullVariable() {
std::unique_ptr<A> P(return_null()); A *InnerPtr = nullptr;
P->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} std::unique_ptr<A> P(InnerPtr);
P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}}
} }
void derefAfterRelease() { void derefAfterRelease() {
std::unique_ptr<A> P(new A()); std::unique_ptr<A> P(new A());
P.release(); P.release();
clang_analyzer_numTimesReached(); // expected-warning {{1}} 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' [alpha.cplusplus.SmartPtr]}}
} }
void derefAfterReset() { void derefAfterReset() {
std::unique_ptr<A> P(new A()); std::unique_ptr<A> P(new A());
P.reset(); P.reset();
clang_analyzer_numTimesReached(); // expected-warning {{1}} 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' [alpha.cplusplus.SmartPtr]}}
} }
void derefAfterResetWithNull() { void derefAfterResetWithNull() {
std::unique_ptr<A> P(new A()); std::unique_ptr<A> P(new A());
P.reset(nullptr); 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' [alpha.cplusplus.SmartPtr]}}
} }
void derefAfterResetWithNonNull() { void derefAfterResetWithNonNull() {
@ -102,6 +103,12 @@ void derefOnReleasedNullRawPtr() {
AP->foo(); // expected-warning {{Called C++ object pointer is null [core.CallAndMessage]}} AP->foo(); // expected-warning {{Called C++ object pointer is null [core.CallAndMessage]}}
} }
void derefOnReleasedValidRawPtr() {
std::unique_ptr<A> P(new A());
A *AP = P.release();
AP->foo(); // No warning.
}
void pass_smart_ptr_by_ref(std::unique_ptr<A> &a); void pass_smart_ptr_by_ref(std::unique_ptr<A> &a);
void pass_smart_ptr_by_const_ref(const std::unique_ptr<A> &a); void pass_smart_ptr_by_const_ref(const std::unique_ptr<A> &a);
void pass_smart_ptr_by_rvalue_ref(std::unique_ptr<A> &&a); void pass_smart_ptr_by_rvalue_ref(std::unique_ptr<A> &&a);
@ -118,7 +125,7 @@ void regioninvalidationTest() {
{ {
std::unique_ptr<A> P; std::unique_ptr<A> P;
pass_smart_ptr_by_const_ref(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' [alpha.cplusplus.SmartPtr]}}
} }
{ {
std::unique_ptr<A> P; std::unique_ptr<A> P;
@ -128,7 +135,7 @@ void regioninvalidationTest() {
{ {
std::unique_ptr<A> P; std::unique_ptr<A> P;
pass_smart_ptr_by_const_rvalue_ref(std::move(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' [alpha.cplusplus.SmartPtr]}}
} }
{ {
std::unique_ptr<A> P; std::unique_ptr<A> P;
@ -138,7 +145,7 @@ void regioninvalidationTest() {
{ {
std::unique_ptr<A> P; std::unique_ptr<A> P;
pass_smart_ptr_by_const_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' [alpha.cplusplus.SmartPtr]}}
} }
} }
@ -162,7 +169,7 @@ void regioninvalidationTestWithinStruct() {
{ {
StructWithSmartPtr S; StructWithSmartPtr S;
pass_struct_with_smart_ptr_by_const_ref(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 'S.P' [alpha.cplusplus.SmartPtr]}}
} }
{ {
StructWithSmartPtr S; StructWithSmartPtr S;
@ -172,7 +179,7 @@ void regioninvalidationTestWithinStruct() {
{ {
StructWithSmartPtr S; StructWithSmartPtr S;
pass_struct_with_smart_ptr_by_const_rvalue_ref(std::move(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 'S.P' [alpha.cplusplus.SmartPtr]}}
} }
{ {
StructWithSmartPtr S; StructWithSmartPtr S;
@ -182,7 +189,7 @@ void regioninvalidationTestWithinStruct() {
{ {
StructWithSmartPtr S; StructWithSmartPtr S;
pass_struct_with_smart_ptr_by_const_ptr(&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 'S.P' [alpha.cplusplus.SmartPtr]}}
} }
} }
@ -207,24 +214,24 @@ void derefOnSwappedNullPtr() {
std::unique_ptr<A> PNull; std::unique_ptr<A> PNull;
P.swap(PNull); P.swap(PNull);
PNull->foo(); // No warning. 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' [alpha.cplusplus.SmartPtr]}}
} }
void derefOnStdSwappedNullPtr() { void derefOnStdSwappedNullPtr() {
std::unique_ptr<A> P; std::unique_ptr<A> P;
std::unique_ptr<A> PNull; std::unique_ptr<A> PNull;
std::swap(P, PNull); std::swap(P, PNull);
PNull->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} PNull->foo(); // expected-warning {{Dereference of null smart pointer 'PNull' [alpha.cplusplus.SmartPtr]}}
P->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}} P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}}
} }
void derefOnSwappedValidPtr() { void derefOnSwappedValidPtr() {
std::unique_ptr<A> P(new A()); std::unique_ptr<A> P(new A());
std::unique_ptr<A> PValid(new A()); std::unique_ptr<A> PValid(new A());
P.swap(PValid); P.swap(PValid);
(*P).foo(); // No warning. (*P).foo(); // No warning.
PValid->foo(); // No warning. PValid->foo(); // No warning.
std::swap(P, PValid); std::swap(P, PValid);
P->foo(); // No warning. P->foo(); // No warning.
PValid->foo(); // No warning. PValid->foo(); // No warning.
} }