Index: cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td +++ cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td @@ -19,7 +19,7 @@ def CoreBuiltin : Package<"builtin">, InPackage; def CoreUninitialized : Package<"uninitialized">, InPackage; def CoreAlpha : Package<"core">, InPackage, Hidden; -def Nullability : Package<"nullability">, InPackage, Hidden; +def Nullability : Package<"nullability">, InPackage, Hidden; def Cplusplus : Package<"cplusplus">; def CplusplusAlpha : Package<"cplusplus">, InPackage, Hidden; @@ -153,7 +153,7 @@ 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. Index: cfe/trunk/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -136,6 +136,11 @@ }; 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 @@ } 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 @@ 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(); if (!RegionSVal) return nullptr; @@ -941,15 +952,21 @@ } } -#define REGISTER_CHECKER(name) \ +#define REGISTER_CHECKER(name, trackingRequired) \ void ento::register##name##Checker(CheckerManager &mgr) { \ NullabilityChecker *checker = mgr.registerChecker(); \ 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) Index: cfe/trunk/test/Analysis/nullability.mm =================================================================== --- cfe/trunk/test/Analysis/nullability.mm +++ cfe/trunk/test/Analysis/nullability.mm @@ -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 Index: cfe/trunk/test/Analysis/nullability_nullonly.mm =================================================================== --- cfe/trunk/test/Analysis/nullability_nullonly.mm +++ cfe/trunk/test/Analysis/nullability_nullonly.mm @@ -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; +}