Skip to content

Commit

Permalink
[Static Analyzer] Make NonNullParamChecker emit implicit null derefer…
Browse files Browse the repository at this point in the history
…ence events.

Differential Revision: http://reviews.llvm.org/D11433

llvm-svn: 246182
  • Loading branch information
Xazax-hun committed Aug 27, 2015
1 parent 8251f97 commit 8d3ad6b
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 25 deletions.
4 changes: 4 additions & 0 deletions clang/include/clang/StaticAnalyzer/Core/Checker.h
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,10 @@ struct ImplicitNullDerefEvent {
bool IsLoad;
ExplodedNode *SinkNode;
BugReporter *BR;
// When true, the dereference is in the source code directly. When false, the
// dereference might happen later (for example pointer passed to a parameter
// that is marked with nonnull attribute.)
bool IsDirectDereference;
};

/// \brief A helper class which wraps a boolean value set to false by default.
Expand Down
8 changes: 5 additions & 3 deletions clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,8 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
// null or not-null. Record the error node as an "implicit" null
// dereference.
if (ExplodedNode *N = C.generateSink(nullState)) {
ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() };
ImplicitNullDerefEvent event = {l, isLoad, N, &C.getBugReporter(),
/*IsDirectDereference=*/false};
dispatchEvent(event);
}
}
Expand Down Expand Up @@ -257,8 +258,9 @@ void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
// At this point the value could be either null or non-null.
// Record this as an "implicit" null dereference.
if (ExplodedNode *N = C.generateSink(StNull)) {
ImplicitNullDerefEvent event = { V, /*isLoad=*/true, N,
&C.getBugReporter() };
ImplicitNullDerefEvent event = {V, /*isLoad=*/true, N,
&C.getBugReporter(),
/*IsDirectDereference=*/false};
dispatchEvent(event);
}
}
Expand Down
42 changes: 25 additions & 17 deletions clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ using namespace ento;

namespace {
class NonNullParamChecker
: public Checker< check::PreCall > {
: public Checker< check::PreCall, EventDispatcher<ImplicitNullDerefEvent> > {
mutable std::unique_ptr<BugType> BTAttrNonNull;
mutable std::unique_ptr<BugType> BTNullRefArg;

Expand Down Expand Up @@ -139,26 +139,34 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call,
ProgramStateRef stateNotNull, stateNull;
std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);

if (stateNull && !stateNotNull) {
// Generate an error node. Check for a null node in case
// we cache out.
if (ExplodedNode *errorNode = C.generateSink(stateNull)) {
if (stateNull) {
if (!stateNotNull) {
// Generate an error node. Check for a null node in case
// we cache out.
if (ExplodedNode *errorNode = C.generateSink(stateNull)) {

std::unique_ptr<BugReport> R;
if (haveAttrNonNull)
R = genReportNullAttrNonNull(errorNode, ArgE);
else if (haveRefTypeParam)
R = genReportReferenceToNullPointer(errorNode, ArgE);
std::unique_ptr<BugReport> R;
if (haveAttrNonNull)
R = genReportNullAttrNonNull(errorNode, ArgE);
else if (haveRefTypeParam)
R = genReportReferenceToNullPointer(errorNode, ArgE);

// Highlight the range of the argument that was null.
R->addRange(Call.getArgSourceRange(idx));
// Highlight the range of the argument that was null.
R->addRange(Call.getArgSourceRange(idx));

// Emit the bug report.
C.emitReport(std::move(R));
}
// Emit the bug report.
C.emitReport(std::move(R));
}

// Always return. Either we cached out or we just emitted an error.
return;
// Always return. Either we cached out or we just emitted an error.
return;
}
if (ExplodedNode *N = C.generateSink(stateNull)) {
ImplicitNullDerefEvent event = {
V, false, N, &C.getBugReporter(),
/*IsDirectDereference=*/haveRefTypeParam};
dispatchEvent(event);
}
}

// If a pointer value passed the check we should assume that it is
Expand Down
5 changes: 4 additions & 1 deletion clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,10 @@ void NullabilityChecker::checkEvent(ImplicitNullDerefEvent Event) const {
if (Filter.CheckNullableDereferenced &&
TrackedNullability->getValue() == Nullability::Nullable) {
BugReporter &BR = *Event.BR;
reportBug(ErrorKind::NullableDereferenced, Event.SinkNode, Region, BR);
if (Event.IsDirectDereference)
reportBug(ErrorKind::NullableDereferenced, Event.SinkNode, Region, BR);
else
reportBug(ErrorKind::NullablePassedToNonnull, Event.SinkNode, Region, BR);
}
}

Expand Down
8 changes: 4 additions & 4 deletions clang/test/Analysis/nullability.mm
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ - (void)takesUnspecified:(int *)p;

template <typename T> T *eraseNullab(T *p) { return p; }

void takesAttrNonnull(Dummy *p) __attribute((nonnull(1)));

void testBasicRules() {
Dummy *p = returnsNullable();
int *ptr = returnsNullableInt();
Expand All @@ -73,10 +75,8 @@ void testBasicRules() {
Dummy dd(d);
break;
}
// Here the copy constructor is called, so a reference is initialized with the
// value of p. No ImplicitNullDereference event will be dispatched for this
// case. A followup patch is expected to fix this in NonNullParamChecker.
default: { Dummy d = *p; } break; // No warning.
case 5: takesAttrNonnull(p); break; // expected-warning {{Nullable pointer is passed to}}
default: { Dummy d = *p; } break; // expected-warning {{Nullable pointer is dereferenced}}
}
if (p) {
takesNonnull(p);
Expand Down

0 comments on commit 8d3ad6b

Please sign in to comment.