[analyzer] Nullability: Suppress diagnostic on bind with cast.

Update the nullability checker to allow an explicit cast to nonnull to
suppress a warning on an assignment of nil to a nonnull:

id _Nonnull x = (id _Nonnull)nil; // no-warning

This suppression as already possible for diagnostics on returns and
function/method arguments.

rdar://problem/25381178

llvm-svn: 266219
This commit is contained in:
Devin Coughlin 2016-04-13 17:59:24 +00:00
parent 6662d6ad2a
commit 4ac12425ba
2 changed files with 70 additions and 9 deletions

View File

@ -1103,26 +1103,48 @@ void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S,
ValNullability = getNullabilityAnnotation(Sym->getType());
Nullability LocNullability = getNullabilityAnnotation(LocType);
// If the type of the RHS expression is nonnull, don't warn. This
// enables explicit suppression with a cast to nonnull.
Nullability ValueExprTypeLevelNullability = Nullability::Unspecified;
const Expr *ValueExpr = matchValueExprForBind(S);
if (ValueExpr) {
ValueExprTypeLevelNullability =
getNullabilityAnnotation(lookThroughImplicitCasts(ValueExpr)->getType());
}
bool NullAssignedToNonNull = (LocNullability == Nullability::Nonnull &&
RhsNullness == NullConstraint::IsNull);
if (Filter.CheckNullPassedToNonnull &&
RhsNullness == NullConstraint::IsNull &&
NullAssignedToNonNull &&
ValNullability != Nullability::Nonnull &&
LocNullability == Nullability::Nonnull &&
ValueExprTypeLevelNullability != Nullability::Nonnull &&
!isARCNilInitializedLocal(C, S)) {
static CheckerProgramPointTag Tag(this, "NullPassedToNonnull");
ExplodedNode *N = C.generateErrorNode(State, &Tag);
if (!N)
return;
const Stmt *ValueExpr = matchValueExprForBind(S);
if (!ValueExpr)
ValueExpr = S;
const Stmt *ValueStmt = S;
if (ValueExpr)
ValueStmt = ValueExpr;
reportBugIfInvariantHolds("Null is assigned to a pointer which is "
"expected to have non-null value",
ErrorKind::NilAssignedToNonnull, N, nullptr, C,
ValueExpr);
ValueStmt);
return;
}
// If null was returned from a non-null function, mark the nullability
// invariant as violated even if the diagnostic was suppressed.
if (NullAssignedToNonNull) {
State = State->set<InvariantViolated>(true);
C.addTransition(State);
return;
}
// Intentionally missing case: '0' is bound to a reference. It is handled by
// the DereferenceChecker.

View File

@ -174,6 +174,47 @@ void testIndirectCastNilToNonnullAndPass() {
takesNonnull(p); // expected-warning {{Null passed to a callee that requires a non-null 1st parameter}}
}
void testDirectCastNilToNonnullAndAssignToLocalInInitializer() {
Dummy * _Nonnull nonnullLocalWithAssignmentInInitializer = (Dummy * _Nonnull)0; // no-warning
(void)nonnullLocalWithAssignmentInInitializer;
// Since we've already had an invariant violation along this path,
// we shouldn't warn here.
nonnullLocalWithAssignmentInInitializer = 0;
(void)nonnullLocalWithAssignmentInInitializer;
}
void testDirectCastNilToNonnullAndAssignToLocal(Dummy * _Nonnull p) {
Dummy * _Nonnull nonnullLocalWithAssignment = p;
nonnullLocalWithAssignment = (Dummy * _Nonnull)0; // no-warning
(void)nonnullLocalWithAssignment;
// Since we've already had an invariant violation along this path,
// we shouldn't warn here.
nonnullLocalWithAssignment = 0;
(void)nonnullLocalWithAssignment;
}
void testDirectCastNilToNonnullAndAssignToParam(Dummy * _Nonnull p) {
p = (Dummy * _Nonnull)0; // no-warning
}
@interface ClassWithNonnullIvar : NSObject {
Dummy *_nonnullIvar;
}
@end
@implementation ClassWithNonnullIvar
-(void)testDirectCastNilToNonnullAndAssignToIvar {
_nonnullIvar = (Dummy * _Nonnull)0; // no-warning;
// Since we've already had an invariant violation along this path,
// we shouldn't warn here.
_nonnullIvar = 0;
}
@end
void testIndirectNilPassToNonnull() {
Dummy *p = 0;
takesNonnull(p); // expected-warning {{Null passed to a callee that requires a non-null 1st parameter}}
@ -479,9 +520,7 @@ void callMethodInSystemHeader() {
}
-(id _Nonnull)methodWithNilledOutInternal {
// The cast below should (but does not yet) suppress the warning on the
// assignment.
_nilledOutInternal = (id _Nonnull)nil; // expected-warning {{Null is assigned to a pointer which is expected to have non-null value}}
_nilledOutInternal = (id _Nonnull)nil;
return nil; // no-warning
}