[Static Analyzer] Moving nullability checkers to a top level package.

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

llvm-svn: 247590
This commit is contained in:
Gabor Horvath 2015-09-14 18:31:34 +00:00
parent a2fcee9cba
commit 2930735c1e
4 changed files with 117 additions and 13 deletions

View File

@ -19,7 +19,7 @@ def Core : Package<"core">;
def CoreBuiltin : Package<"builtin">, InPackage<Core>;
def CoreUninitialized : Package<"uninitialized">, InPackage<Core>;
def CoreAlpha : Package<"core">, InPackage<Alpha>, Hidden;
def Nullability : Package<"nullability">, InPackage<CoreAlpha>, Hidden;
def Nullability : Package<"nullability">, InPackage<Alpha>, Hidden;
def Cplusplus : Package<"cplusplus">;
def CplusplusAlpha : Package<"cplusplus">, InPackage<Alpha>, Hidden;
@ -153,7 +153,7 @@ def NullableReturnedFromNonnullChecker : Checker<"NullablePassedToNonnull">,
HelpText<"Warns when a nullable pointer is returned from a function that has _Nonnull return type.">,
DescFile<"NullabilityChecker.cpp">;
} // end "alpha.core.nullability"
} // end "alpha.nullability"
//===----------------------------------------------------------------------===//
// Evaluate "builtin" functions.

View File

@ -136,6 +136,11 @@ public:
};
NullabilityChecksFilter Filter;
// When set to false no nullability information will be tracked in
// NullabilityMap. It is possible to catch errors like passing a null pointer
// to a callee that expects nonnull argument without the information that is
// stroed in the NullabilityMap. This is an optimization.
DefaultBool NeedTracking;
private:
class NullabilityBugVisitor
@ -188,6 +193,11 @@ private:
}
BR.emitReport(std::move(R));
}
/// If an SVal wraps a region that should be tracked, it will return a pointer
/// to the wrapped region. Otherwise it will return a nullptr.
const SymbolicRegion *getTrackRegion(SVal Val,
bool CheckSuperRegion = false) const;
};
class NullabilityState {
@ -246,10 +256,11 @@ static NullConstraint getNullConstraint(DefinedOrUnknownSVal Val,
return NullConstraint::Unknown;
}
// If an SVal wraps a region that should be tracked, it will return a pointer
// to the wrapped region. Otherwise it will return a nullptr.
static const SymbolicRegion *getTrackRegion(SVal Val,
bool CheckSuperRegion = false) {
const SymbolicRegion *
NullabilityChecker::getTrackRegion(SVal Val, bool CheckSuperRegion) const {
if (!NeedTracking)
return nullptr;
auto RegionSVal = Val.getAs<loc::MemRegionVal>();
if (!RegionSVal)
return nullptr;
@ -941,15 +952,21 @@ void NullabilityChecker::printState(raw_ostream &Out, ProgramStateRef State,
}
}
#define REGISTER_CHECKER(name) \
#define REGISTER_CHECKER(name, trackingRequired) \
void ento::register##name##Checker(CheckerManager &mgr) { \
NullabilityChecker *checker = mgr.registerChecker<NullabilityChecker>(); \
checker->Filter.Check##name = true; \
checker->Filter.CheckName##name = mgr.getCurrentCheckName(); \
checker->NeedTracking = checker->NeedTracking || trackingRequired; \
}
REGISTER_CHECKER(NullPassedToNonnull)
REGISTER_CHECKER(NullReturnedFromNonnull)
REGISTER_CHECKER(NullableDereferenced)
REGISTER_CHECKER(NullablePassedToNonnull)
REGISTER_CHECKER(NullableReturnedFromNonnull)
// The checks are likely to be turned on by default and it is possible to do
// them without tracking any nullability related information. As an optimization
// no nullability information will be tracked when only these two checks are
// enables.
REGISTER_CHECKER(NullPassedToNonnull, false)
REGISTER_CHECKER(NullReturnedFromNonnull, false)
REGISTER_CHECKER(NullableDereferenced, true)
REGISTER_CHECKER(NullablePassedToNonnull, true)
REGISTER_CHECKER(NullableReturnedFromNonnull, true)

View File

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.core.nullability -verify %s
// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.nullability -verify %s
#define nil 0
#define BOOL int

View File

@ -0,0 +1,87 @@
// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.nullability.NullPassedToNonnull,alpha.nullability.NullReturnedFromNonnull -verify %s
int getRandom();
typedef struct Dummy { int val; } Dummy;
void takesNullable(Dummy *_Nullable);
void takesNonnull(Dummy *_Nonnull);
Dummy *_Nullable returnsNullable();
void testBasicRules() {
// The tracking of nullable values is turned off.
Dummy *p = returnsNullable();
takesNonnull(p); // no warning
Dummy *q = 0;
if (getRandom()) {
takesNullable(q);
takesNonnull(q); // expected-warning {{}}
}
}
Dummy *_Nonnull testNullReturn() {
Dummy *p = 0;
return p; // expected-warning {{}}
}
void onlyReportFirstPreconditionViolationOnPath() {
Dummy *p = 0;
takesNonnull(p); // expected-warning {{}}
takesNonnull(p); // No warning.
// Passing null to nonnull is a sink. Stop the analysis.
int i = 0;
i = 5 / i; // no warning
(void)i;
}
Dummy *_Nonnull doNotWarnWhenPreconditionIsViolatedInTopFunc(
Dummy *_Nonnull p) {
if (!p) {
Dummy *ret =
0; // avoid compiler warning (which is not generated by the analyzer)
if (getRandom())
return ret; // no warning
else
return p; // no warning
} else {
return p;
}
}
Dummy *_Nonnull doNotWarnWhenPreconditionIsViolated(Dummy *_Nonnull p) {
if (!p) {
Dummy *ret =
0; // avoid compiler warning (which is not generated by the analyzer)
if (getRandom())
return ret; // no warning
else
return p; // no warning
} else {
return p;
}
}
void testPreconditionViolationInInlinedFunction(Dummy *p) {
doNotWarnWhenPreconditionIsViolated(p);
}
void inlinedNullable(Dummy *_Nullable p) {
if (p) return;
}
void inlinedNonnull(Dummy *_Nonnull p) {
if (p) return;
}
void inlinedUnspecified(Dummy *p) {
if (p) return;
}
Dummy *_Nonnull testDefensiveInlineChecks(Dummy * p) {
switch (getRandom()) {
case 1: inlinedNullable(p); break;
case 2: inlinedNonnull(p); break;
case 3: inlinedUnspecified(p); break;
}
if (getRandom())
takesNonnull(p);
return p;
}