]> granicus.if.org Git - clang/commitdiff
[analyzer] Pass value expression for inlined defensive checks when binding null to...
authorDevin Coughlin <dcoughlin@apple.com>
Tue, 24 Nov 2015 19:15:11 +0000 (19:15 +0000)
committerDevin Coughlin <dcoughlin@apple.com>
Tue, 24 Nov 2015 19:15:11 +0000 (19:15 +0000)
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

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@254007 91177308-0d34-0410-b5e6-96231b3b80d8

lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
test/Analysis/nullability.mm

index 137dedbf4ce68ba1e6849dcdc6f0c2905c335389..89b1358a2a5d13fac0a95168b2968a4174507cd6 100644 (file)
@@ -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
 /// 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
index 14cfb67e986a80a148ed56871adaeca5a5d978bc..2a96431980f242b4bd6bc0f3dd123c3afc5f8597 100644 (file)
@@ -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;
 }