forked from OSchip/llvm-project
[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:
parent
cfdc96714b
commit
06d100a69a
|
@ -301,6 +301,26 @@ public:
|
|||
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
|
||||
/// in the report.
|
||||
StringRef getDeclDescription(const Decl *D);
|
||||
|
|
|
@ -26,6 +26,8 @@ bool isStdSmartPtrCall(const CallEvent &Call);
|
|||
/// Returns whether the smart pointer is null or not.
|
||||
bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion);
|
||||
|
||||
const BugType *getNullDereferenceBugType();
|
||||
|
||||
} // namespace smartptr
|
||||
} // namespace ento
|
||||
} // namespace clang
|
||||
|
|
|
@ -23,23 +23,40 @@
|
|||
#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;
|
||||
|
||||
namespace {
|
||||
|
||||
static const BugType *NullDereferenceBugTypePtr;
|
||||
|
||||
class SmartPtrChecker : public Checker<check::PreCall> {
|
||||
public:
|
||||
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
|
||||
BugType NullDereferenceBugType{this, "Null SmartPtr dereference",
|
||||
"C++ Smart Pointer"};
|
||||
|
||||
public:
|
||||
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
|
||||
|
||||
// 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,
|
||||
CheckerContext &C) const {
|
||||
if (!smartptr::isStdSmartPtrCall(Call))
|
||||
|
@ -55,23 +72,34 @@ void SmartPtrChecker::checkPreCall(const CallEvent &Call,
|
|||
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<PathSensitiveBugReport>(
|
||||
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<PathSensitiveBugReport>(NullDereferenceBugType,
|
||||
OS.str(), ErrNode);
|
||||
R->markInteresting(DerefRegion);
|
||||
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) {
|
||||
Mgr.registerChecker<SmartPtrChecker>();
|
||||
SmartPtrChecker *Checker = Mgr.registerChecker<SmartPtrChecker>();
|
||||
NullDereferenceBugTypePtr = &Checker->NullDereferenceBugType;
|
||||
}
|
||||
|
||||
bool ento::shouldRegisterSmartPtrChecker(const CheckerManager &mgr) {
|
||||
|
|
|
@ -17,14 +17,18 @@
|
|||
#include "clang/AST/DeclCXX.h"
|
||||
#include "clang/AST/ExprCXX.h"
|
||||
#include "clang/AST/Type.h"
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
|
||||
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
|
||||
#include "clang/StaticAnalyzer/Core/Checker.h"
|
||||
#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 "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
|
||||
#include <string>
|
||||
|
||||
using namespace clang;
|
||||
using namespace ento;
|
||||
|
@ -49,8 +53,6 @@ public:
|
|||
const LocationContext *LCtx, const CallEvent *Call) const;
|
||||
|
||||
private:
|
||||
ProgramStateRef updateTrackedRegion(const CallEvent &Call, CheckerContext &C,
|
||||
const MemRegion *ThisValRegion) 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;
|
||||
|
@ -131,11 +133,11 @@ bool SmartPtrModeling::isNullAfterMoveMethod(const CallEvent &Call) const {
|
|||
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<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
|
||||
|
||||
|
@ -159,12 +161,46 @@ bool SmartPtrModeling::evalCall(const CallEvent &Call,
|
|||
if (CC->getDecl()->isCopyOrMoveConstructor())
|
||||
return false;
|
||||
|
||||
const MemRegion *ThisValRegion = CC->getCXXThisVal().getAsRegion();
|
||||
if (!ThisValRegion)
|
||||
const MemRegion *ThisRegion = CC->getCXXThisVal().getAsRegion();
|
||||
if (!ThisRegion)
|
||||
return false;
|
||||
|
||||
auto State = updateTrackedRegion(Call, C, ThisValRegion);
|
||||
C.addTransition(State);
|
||||
if (Call.getNumArgs() == 0) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -207,37 +243,65 @@ ProgramStateRef SmartPtrModeling::checkRegionChanges(
|
|||
|
||||
void SmartPtrModeling::handleReset(const CallEvent &Call,
|
||||
CheckerContext &C) const {
|
||||
ProgramStateRef State = C.getState();
|
||||
const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
|
||||
if (!IC)
|
||||
return;
|
||||
|
||||
const MemRegion *ThisValRegion = IC->getCXXThisVal().getAsRegion();
|
||||
if (!ThisValRegion)
|
||||
const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
|
||||
if (!ThisRegion)
|
||||
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
|
||||
// time to model all methods.
|
||||
}
|
||||
|
||||
void SmartPtrModeling::handleRelease(const CallEvent &Call,
|
||||
CheckerContext &C) const {
|
||||
ProgramStateRef State = C.getState();
|
||||
const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
|
||||
if (!IC)
|
||||
return;
|
||||
|
||||
const MemRegion *ThisValRegion = IC->getCXXThisVal().getAsRegion();
|
||||
if (!ThisValRegion)
|
||||
const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
|
||||
if (!ThisRegion)
|
||||
return;
|
||||
|
||||
auto State = updateTrackedRegion(Call, C, ThisValRegion);
|
||||
const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
|
||||
|
||||
const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisValRegion);
|
||||
if (InnerPointVal) {
|
||||
State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
|
||||
*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
|
||||
// pointer.
|
||||
}
|
||||
|
@ -267,27 +331,18 @@ void SmartPtrModeling::handleSwap(const CallEvent &Call,
|
|||
State = updateSwappedRegion(State, ThisRegion, ArgRegionInnerPointerVal);
|
||||
State = updateSwappedRegion(State, ArgRegion, ThisRegionInnerPointerVal);
|
||||
|
||||
C.addTransition(State);
|
||||
}
|
||||
|
||||
ProgramStateRef
|
||||
SmartPtrModeling::updateTrackedRegion(const CallEvent &Call, CheckerContext &C,
|
||||
const MemRegion *ThisValRegion) const {
|
||||
// TODO: Refactor and clean up handling too many things.
|
||||
ProgramStateRef State = C.getState();
|
||||
auto NumArgs = Call.getNumArgs();
|
||||
|
||||
if (NumArgs == 0) {
|
||||
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;
|
||||
C.addTransition(
|
||||
State, C.getNoteTag([ThisRegion, ArgRegion](PathSensitiveBugReport &BR,
|
||||
llvm::raw_ostream &OS) {
|
||||
if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
|
||||
!BR.isInteresting(ThisRegion))
|
||||
return;
|
||||
BR.markInteresting(ArgRegion);
|
||||
OS << "Swapped null smart pointer ";
|
||||
ArgRegion->printPretty(OS);
|
||||
OS << " with smart pointer ";
|
||||
ThisRegion->printPretty(OS);
|
||||
}));
|
||||
}
|
||||
|
||||
void ento::registerSmartPtrModeling(CheckerManager &Mgr) {
|
||||
|
|
|
@ -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'}}
|
||||
}
|
|
@ -12,7 +12,7 @@ void derefAfterMove(std::unique_ptr<int> P) {
|
|||
std::unique_ptr<int> 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 derefOfUnknown(std::unique_ptr<A> P) {
|
|||
|
||||
void derefAfterDefaultCtr() {
|
||||
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() {
|
||||
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() {
|
||||
std::unique_ptr<A> P(return_null());
|
||||
P->foo(); // expected-warning {{Dereference of null smart pointer [alpha.cplusplus.SmartPtr]}}
|
||||
void derefAfterCtrWithNullVariable() {
|
||||
A *InnerPtr = nullptr;
|
||||
std::unique_ptr<A> P(InnerPtr);
|
||||
P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}}
|
||||
}
|
||||
|
||||
void derefAfterRelease() {
|
||||
std::unique_ptr<A> 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' [alpha.cplusplus.SmartPtr]}}
|
||||
}
|
||||
|
||||
void derefAfterReset() {
|
||||
std::unique_ptr<A> 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' [alpha.cplusplus.SmartPtr]}}
|
||||
}
|
||||
|
||||
void derefAfterResetWithNull() {
|
||||
std::unique_ptr<A> 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' [alpha.cplusplus.SmartPtr]}}
|
||||
}
|
||||
|
||||
void derefAfterResetWithNonNull() {
|
||||
|
@ -102,6 +103,12 @@ void derefOnReleasedNullRawPtr() {
|
|||
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_const_ref(const 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;
|
||||
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;
|
||||
|
@ -128,7 +135,7 @@ void regioninvalidationTest() {
|
|||
{
|
||||
std::unique_ptr<A> 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;
|
||||
|
@ -138,7 +145,7 @@ void regioninvalidationTest() {
|
|||
{
|
||||
std::unique_ptr<A> 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;
|
||||
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;
|
||||
|
@ -172,7 +179,7 @@ void regioninvalidationTestWithinStruct() {
|
|||
{
|
||||
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 'S.P' [alpha.cplusplus.SmartPtr]}}
|
||||
}
|
||||
{
|
||||
StructWithSmartPtr S;
|
||||
|
@ -182,7 +189,7 @@ void regioninvalidationTestWithinStruct() {
|
|||
{
|
||||
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 'S.P' [alpha.cplusplus.SmartPtr]}}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -207,24 +214,24 @@ void derefOnSwappedNullPtr() {
|
|||
std::unique_ptr<A> 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' [alpha.cplusplus.SmartPtr]}}
|
||||
}
|
||||
|
||||
void derefOnStdSwappedNullPtr() {
|
||||
std::unique_ptr<A> P;
|
||||
std::unique_ptr<A> 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' [alpha.cplusplus.SmartPtr]}}
|
||||
P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}}
|
||||
}
|
||||
|
||||
void derefOnSwappedValidPtr() {
|
||||
std::unique_ptr<A> P(new A());
|
||||
std::unique_ptr<A> 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.
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue