Index: lib/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- lib/StaticAnalyzer/Checkers/Checkers.td +++ 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: lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp +++ lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -136,6 +136,7 @@ }; NullabilityChecksFilter Filter; + DefaultBool NeedTracking; private: class NullabilityBugVisitor @@ -188,6 +189,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 { @@ -210,10 +216,13 @@ private: Nullability Nullab; - // Source is the expression which determined the nullability. For example in a - // message like [nullable nonnull_returning] has nullable nullability, because + // Source is the expression which determined the nullability. For example in + // a + // message like [nullable nonnull_returning] has nullable nullability, + // because // the receiver is nullable. Here the receiver will be the source of the - // nullability. This is useful information when the diagnostics are generated. + // nullability. This is useful information when the diagnostics are + // generated. const Stmt *Source; }; @@ -229,8 +238,10 @@ // If the nullability precondition of a function is violated, we should not // report nullability related issues on that path. For this reason once a -// precondition is not met on a path, this checker will be esentially turned off -// for the rest of the analysis. We do not want to generate a sink node however, +// precondition is not met on a path, this checker will be esentially turned +// off +// for the rest of the analysis. We do not want to generate a sink node +// however, // so this checker would not lead to reduced coverage. REGISTER_TRAIT_WITH_PROGRAMSTATE(PreconditionViolated, bool) @@ -246,10 +257,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 +953,16 @@ } } -#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) +REGISTER_CHECKER(NullPassedToNonnull, false) +REGISTER_CHECKER(NullReturnedFromNonnull, false) +REGISTER_CHECKER(NullableDereferenced, true) +REGISTER_CHECKER(NullablePassedToNonnull, true) +REGISTER_CHECKER(NullableReturnedFromNonnull, true) Index: test/Analysis/nullability.mm =================================================================== --- test/Analysis/nullability.mm +++ 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: test/Analysis/nullability_nullonly.mm =================================================================== --- /dev/null +++ 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; +}