forked from OSchip/llvm-project
[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:
parent
a2fcee9cba
commit
2930735c1e
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue