diff --git a/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp index 137dedbf4ce6..89b1358a2a5d 100644 --- a/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -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(S)) { + if (BinOp->getOpcode() == BO_Assign) + return BinOp->getRHS(); + } + + // For `int x = e` the value expression is the initializer. + if (auto *DS = dyn_cast(S)) { + if (DS->isSingleDecl()) { + auto *VD = dyn_cast(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 /// pointer or null symbol is assigned to a pointer with a nonnull type. 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); if (!N) return; + + const Stmt *ValueExpr = matchValueExprForBind(S); + if (!ValueExpr) + ValueExpr = S; + reportBugIfPreconditionHolds(ErrorKind::NilAssignedToNonnull, N, nullptr, C, - S); + ValueExpr); return; } // Intentionally missing case: '0' is bound to a reference. It is handled by diff --git a/clang/test/Analysis/nullability.mm b/clang/test/Analysis/nullability.mm index 14cfb67e986a..2a96431980f2 100644 --- a/clang/test/Analysis/nullability.mm +++ b/clang/test/Analysis/nullability.mm @@ -238,6 +238,19 @@ Dummy *_Nonnull testDefensiveInlineChecks(Dummy * p) { case 3: inlinedUnspecified(p); break; } 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; }