forked from OSchip/llvm-project
[analyzer] Pass value expression for inlined defensive checks when binding null to nonnull.
The nullability checker was not suppressing false positives resulting from inlined defensive checks when null was bound to a nonnull variable because it was passing the entire bind statement rather than the value expression to trackNullOrUndefValue(). This commit changes that checker to synactically match on the bind statement to extract the value expression so it can be passed to trackNullOrUndefValue(). rdar://problem/23575439 llvm-svn: 254007
This commit is contained in:
parent
860e29e0c3
commit
c1986638a7
|
@ -862,6 +862,30 @@ void NullabilityChecker::checkPostStmt(const ExplicitCastExpr *CE,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// For a given statement performing a bind, attempt to syntactically
|
||||||
|
/// match the expression resulting in the bound value.
|
||||||
|
static const Expr * matchValueExprForBind(const Stmt *S) {
|
||||||
|
// For `x = e` the value expression is the right-hand side.
|
||||||
|
if (auto *BinOp = dyn_cast<BinaryOperator>(S)) {
|
||||||
|
if (BinOp->getOpcode() == BO_Assign)
|
||||||
|
return BinOp->getRHS();
|
||||||
|
}
|
||||||
|
|
||||||
|
// For `int x = e` the value expression is the initializer.
|
||||||
|
if (auto *DS = dyn_cast<DeclStmt>(S)) {
|
||||||
|
if (DS->isSingleDecl()) {
|
||||||
|
auto *VD = dyn_cast<VarDecl>(DS->getSingleDecl());
|
||||||
|
if (!VD)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (const Expr *Init = VD->getInit())
|
||||||
|
return Init;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
/// Propagate the nullability information through binds and warn when nullable
|
/// Propagate the nullability information through binds and warn when nullable
|
||||||
/// pointer or null symbol is assigned to a pointer with a nonnull type.
|
/// pointer or null symbol is assigned to a pointer with a nonnull type.
|
||||||
void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S,
|
void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S,
|
||||||
|
@ -898,8 +922,13 @@ void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S,
|
||||||
ExplodedNode *N = C.generateErrorNode(State, &Tag);
|
ExplodedNode *N = C.generateErrorNode(State, &Tag);
|
||||||
if (!N)
|
if (!N)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
const Stmt *ValueExpr = matchValueExprForBind(S);
|
||||||
|
if (!ValueExpr)
|
||||||
|
ValueExpr = S;
|
||||||
|
|
||||||
reportBugIfPreconditionHolds(ErrorKind::NilAssignedToNonnull, N, nullptr, C,
|
reportBugIfPreconditionHolds(ErrorKind::NilAssignedToNonnull, N, nullptr, C,
|
||||||
S);
|
ValueExpr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Intentionally missing case: '0' is bound to a reference. It is handled by
|
// Intentionally missing case: '0' is bound to a reference. It is handled by
|
||||||
|
|
|
@ -238,6 +238,19 @@ Dummy *_Nonnull testDefensiveInlineChecks(Dummy * p) {
|
||||||
case 3: inlinedUnspecified(p); break;
|
case 3: inlinedUnspecified(p); break;
|
||||||
}
|
}
|
||||||
if (getRandom())
|
if (getRandom())
|
||||||
takesNonnull(p);
|
takesNonnull(p); // no-warning
|
||||||
|
|
||||||
|
if (getRandom()) {
|
||||||
|
Dummy *_Nonnull varWithInitializer = p; // no-warning
|
||||||
|
|
||||||
|
Dummy *_Nonnull var1WithInitializer = p, // no-warning
|
||||||
|
*_Nonnull var2WithInitializer = p; // no-warning
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getRandom()) {
|
||||||
|
Dummy *_Nonnull varWithoutInitializer;
|
||||||
|
varWithoutInitializer = p; // no-warning
|
||||||
|
}
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue