r246105 - [Static Analyzer] Checks to catch nullability related issues.

Gabor Horvath via cfe-commits cfe-commits at lists.llvm.org
Wed Aug 26 16:17:43 PDT 2015


Author: xazax
Date: Wed Aug 26 18:17:43 2015
New Revision: 246105

URL: http://llvm.org/viewvc/llvm-project?rev=246105&view=rev
Log:
[Static Analyzer] Checks to catch nullability related issues.

Differential Revision: http://reviews.llvm.org/D11468

Added:
    cfe/trunk/docs/analyzer/nullability.rst
    cfe/trunk/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
    cfe/trunk/test/Analysis/nullability.mm
Modified:
    cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt
    cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td

Added: cfe/trunk/docs/analyzer/nullability.rst
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/analyzer/nullability.rst?rev=246105&view=auto
==============================================================================
--- cfe/trunk/docs/analyzer/nullability.rst (added)
+++ cfe/trunk/docs/analyzer/nullability.rst Wed Aug 26 18:17:43 2015
@@ -0,0 +1,92 @@
+============
+Nullability Checks
+============
+
+This document is a high level description of the nullablility checks.
+These checks intended to use the annotations that is described in this
+RFC: http://lists.cs.uiuc.edu/pipermail/cfe-dev/2015-March/041798.html.
+
+Let's consider the following 2 categories:
+
+1) nullable
+============
+
+If a pointer 'p' has a nullable annotation and no explicit null check or assert, we should warn in the following cases:
+- 'p' gets implicitly converted into nonnull pointer, for example, we are passing it to a function that takes a nonnull parameter.
+- 'p' gets dereferenced
+
+Taking a branch on nullable pointers are the same like taking branch on null unspecified pointers.
+
+Explicit cast from nullable to nonnul::
+
+    __nullable id foo;
+    id bar = foo;
+    takesNonNull((_nonnull) bar); <— should not warn here (backward compatibility hack)
+    anotherTakesNonNull(bar); <— would be great to warn here, but not necessary(*)
+
+Because bar corresponds to the same symbol all the time it is not easy to implement the checker that way the cast only suppress the first call but not the second. For this reason in the first implementation after a contradictory cast happens, I will treat bar as nullable unspecified, this way all of the warnings will be suppressed. Treating the symbol as nullable unspecified also has an advantage that in case the takesNonNull function body is being inlined, the will be no warning, when the symbol is dereferenced. In case I have time after the initial version I might spend additional time to try to find a more sophisticated solution, in which we would produce the second warning (*).
+ 
+2) nonnull
+============
+
+- Dereferencing a nonnull, or sending message to it is ok.
+- Converting nonnull to nullable is Ok.
+- When there is an explicit cast from nonnull to nullable I will trust the cast (it is probable there for a reason, because this cast does not suppress any warnings or errors).
+- But what should we do about null checks?::
+
+    __nonnull id takesNonnull(__nonnull id x) {
+        if (x == nil) {
+            // Defensive backward compatible code:
+            ....
+            return nil; <- Should the analyzer cover this piece of code? Should we require the cast (__nonnull)nil?
+        }
+        ....
+    }
+
+There are these directions:
+- We can either take the branch; this way the branch is analyzed
+	- Should we not warn about any nullability issues in that branch? Probably not, it is ok to break the nullability postconditions when the nullability preconditions are violated.
+- We can assume that these pointers are not null and we lose coverage with the analyzer. (This can be implemented either in constraint solver or in the checker itself.)
+
+Other Issues to keep in mind/take care of:
+Messaging:
+- Sending a message to a nullable pointer
+	- Even though the method might return a nonnull pointer, when it was sent to a nullable pointer the return type will be nullable.
+	- The result is nullable unless the receiver is known to be non null.
+- Sending a message to a unspecified or nonnull pointer
+	- If the pointer is not assumed to be nil, we should be optimistic and use the nullability implied by the method.
+        - This will not happen automatically, since the AST will have null unspecified in this case.
+
+Inlining
+============
+
+A symbol may need to be treated differently inside an inlined body. For example, consider these conversions from nonnull to nullable in presence of inlining::
+
+    id obj = getNonnull();
+    takesNullable(obj);
+    takesNonnull(obj);
+    
+    void takesNullable(nullable id obj) {
+       obj->ivar // we should assume obj is nullable and warn here
+    }
+           
+With no special treatment, when the takesNullable is inlined the analyzer will not warn when the obj symbol is dereferenced. One solution for this is to reanalyze takesNullable as a top level function to get possible violations. The alternative method, deducing nullability information from the arguments after inlining is not robust enough (for example there might be more parameters with different nullability, but in the given path the two parameters might end up being the same symbol or there can be nested functions that take different view of the nullability of the same symbol). So the symbol will remain nonnull to avoid false positives but the functions that takes nullable parameters will be analyzed separately as well without inlining.
+
+Annotations on multi level pointers
+============
+
+Tracking multiple levels of annotations for pointers pointing to pointers would make the checker more complicated, because this way a vector of nullability qualifiers would be needed to be tracked for each symbol. This is not a big caveat, since once the top level pointer is dereferenced, the symvol for the inner pointer will have the nullability information. The lack of multi level annotation tracking only observable, when multiple levels of pointers are passed to a function which has a parameter with multiple levels of annotations. So for now the checker support the top level nullability qualifiers only.::
+
+    int * __nonnull * __nullable p;
+    int ** q = p;
+    takesStarNullableStarNullable(q);
+
+Implementation notes
+============
+
+What to track?
+- The checker would track memory regions, and to each relevant region a qualifier information would be attached which is either nullable, nonnull or null unspecified (or contradicted to suppress warnings for a specific region).
+- On a branch, where a nullable pointer is known to be non null, the checker treat it as a same way as a pointer annotated as nonnull.
+- When there is an explicit cast from a null unspecified to either nonnull or nullable I will trust the cast.
+- Unannotated pointers are treated the same way as pointers annotated with nullability unspecified qualifier, unless the region is wrapped in ASSUME_NONNULL macros.
+- We might want to implement a callback for entry points to top level functions, where the pointer nullability assumptions would be made.

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt?rev=246105&r1=246104&r2=246105&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt Wed Aug 26 18:17:43 2015
@@ -49,6 +49,7 @@ add_clang_library(clangStaticAnalyzerChe
   NSErrorChecker.cpp
   NoReturnFunctionChecker.cpp
   NonNullParamChecker.cpp
+  NullabilityChecker.cpp
   ObjCAtSyncChecker.cpp
   ObjCContainersASTChecker.cpp
   ObjCContainersChecker.cpp

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td?rev=246105&r1=246104&r2=246105&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td Wed Aug 26 18:17:43 2015
@@ -19,6 +19,7 @@ def Core : Package<"core">;
 def CoreBuiltin : Package<"builtin">, InPackage<Core>;
 def CoreUninitialized  : Package<"uninitialized">, InPackage<Core>;
 def CoreAlpha : Package<"core">, InPackage<Alpha>, Hidden;
+def Nullability : Package<"nullability">, InPackage<CoreAlpha>, Hidden;
 
 def Cplusplus : Package<"cplusplus">;
 def CplusplusAlpha : Package<"cplusplus">, InPackage<Alpha>, Hidden;
@@ -130,6 +131,30 @@ def TestAfterDivZeroChecker : Checker<"T
 
 } // end "alpha.core"
 
+let ParentPackage = Nullability in {
+
+def NullPassedToNonnullChecker : Checker<"NullPassedToNonnull">,
+  HelpText<"Warns when a null pointer is passed to a pointer which has a _Nonnull type.">,
+  DescFile<"NullabilityChecker.cpp">;
+
+def NullReturnedFromNonnullChecker : Checker<"NullReturnedFromNonnull">,
+  HelpText<"Warns when a null pointer is returned from a function that has _Nonnull return type.">,
+  DescFile<"NullabilityChecker.cpp">;
+
+def NullableDereferencedChecker : Checker<"NullableDereferenced">,
+  HelpText<"Warns when a nullable pointer is dereferenced.">,
+  DescFile<"NullabilityChecker.cpp">;
+
+def NullablePassedToNonnullChecker : Checker<"NullablePassedToNonnull">,
+  HelpText<"Warns when a nullable pointer is passed to a pointer which has a _Nonnull type.">,
+  DescFile<"NullabilityChecker.cpp">;
+
+def NullableReturnedFromNonnullChecker : Checker<"NullablePassedToNonnull">,
+  HelpText<"Warns when a nullable pointer is returned from a function that has _Nonnull return type.">,
+  DescFile<"NullabilityChecker.cpp">;
+
+} // end "alpha.core.nullability"
+
 //===----------------------------------------------------------------------===//
 // Evaluate "builtin" functions.
 //===----------------------------------------------------------------------===//

Added: cfe/trunk/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp?rev=246105&view=auto
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp (added)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp Wed Aug 26 18:17:43 2015
@@ -0,0 +1,838 @@
+//== Nullabilityhecker.cpp - Nullability checker ----------------*- C++ -*--==//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This checker tries to find nullability violations. There are several kinds of
+// possible violations:
+// * Null pointer is passed to a pointer which has a _Nonnull type.
+// * Null pointer is returned from a function which has a _Nonnull return type.
+// * Nullable pointer is passed to a pointer which has a _Nonnull type.
+// * Nullable pointer is returned from a function which has a _Nonnull return
+//   type.
+// * Nullable pointer is dereferenced.
+//
+// This checker propagates the nullability information of the pointers and looks
+// for the patterns that are described above. Explicit casts are trusted and are
+// considered a way to suppress false positives for this checker. The other way
+// to suppress warnings would be to add asserts or guarding if statements to the
+// code. In addition to the nullability propagation this checker also uses some
+// heuristics to suppress potential false positives.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangSACheckers.h"
+#include "llvm/Support/Path.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+// Do not reorder! The getMostNullable method relies on the order.
+// Optimization: Most pointers expected to be unspecified. When a symbol has an
+// unspecified or nonnull type non of the rules would indicate any problem for
+// that symbol. For this reason only nullable and contradicted nullability are
+// stored for a symbol. When a symbol is already contradicted, it can not be
+// casted back to nullable.
+enum class Nullability : char {
+  Contradicted, // Tracked nullability is contradicted by an explicit cast. Do
+                // not report any nullability related issue for this symbol.
+                // This nullability is propagated agressively to avoid false
+                // positive results. See the comment on getMostNullable method.
+  Nullable,
+  Unspecified,
+  Nonnull
+};
+
+/// Returns the most nullable nullability. This is used for message expressions
+/// like [reciever method], where the nullability of this expression is either
+/// the nullability of the receiver or the nullability of the return type of the
+/// method, depending on which is more nullable. Contradicted is considered to
+/// be the most nullable, to avoid false positive results.
+static Nullability getMostNullable(Nullability Lhs, Nullability Rhs) {
+  return static_cast<Nullability>(
+      std::min(static_cast<char>(Lhs), static_cast<char>(Rhs)));
+}
+
+static const char *getNullabilityString(Nullability Nullab) {
+  switch (Nullab) {
+  case Nullability::Contradicted:
+    return "contradicted";
+  case Nullability::Nullable:
+    return "nullable";
+  case Nullability::Unspecified:
+    return "unspecified";
+  case Nullability::Nonnull:
+    return "nonnull";
+  }
+  assert(false);
+  return "";
+}
+
+// These enums are used as an index to ErrorMessages array.
+enum class ErrorKind : int {
+  NilAssignedToNonnull,
+  NilPassedToNonnull,
+  NilReturnedToNonnull,
+  NullableAssignedToNonnull,
+  NullableReturnedToNonnull,
+  NullableDereferenced,
+  NullablePassedToNonnull
+};
+
+const char *ErrorMessages[] = {"Null pointer is assigned to a pointer which "
+                               "has _Nonnull type",
+                               "Null pointer is passed to a parameter which is "
+                               "marked as _Nonnull",
+                               "Null pointer is returned from a function that "
+                               "has _Nonnull return type",
+                               "Nullable pointer is assigned to a pointer "
+                               "which has _Nonnull type",
+                               "Nullable pointer is returned from a function "
+                               "that has _Nonnull return type",
+                               "Nullable pointer is dereferenced",
+                               "Nullable pointer is passed to a parameter "
+                               "which is marked as _Nonnull"};
+
+class NullabilityChecker
+    : public Checker<check::Bind, check::PreCall, check::PreStmt<ReturnStmt>,
+                     check::PostCall, check::PostStmt<ExplicitCastExpr>,
+                     check::PostObjCMessage, check::DeadSymbols,
+                     check::Event<ImplicitNullDerefEvent>> {
+  mutable std::unique_ptr<BugType> BT;
+
+public:
+  void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const;
+  void checkPostStmt(const ExplicitCastExpr *CE, CheckerContext &C) const;
+  void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
+  void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
+  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+  void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
+  void checkEvent(ImplicitNullDerefEvent Event) const;
+
+  void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
+                  const char *Sep) const override;
+
+  struct NullabilityChecksFilter {
+    DefaultBool CheckNullPassedToNonnull;
+    DefaultBool CheckNullReturnedFromNonnull;
+    DefaultBool CheckNullableDereferenced;
+    DefaultBool CheckNullablePassedToNonnull;
+    DefaultBool CheckNullableReturnedFromNonnull;
+
+    CheckName CheckNameNullPassedToNonnull;
+    CheckName CheckNameNullReturnedFromNonnull;
+    CheckName CheckNameNullableDereferenced;
+    CheckName CheckNameNullablePassedToNonnull;
+    CheckName CheckNameNullableReturnedFromNonnull;
+  };
+
+  NullabilityChecksFilter Filter;
+
+private:
+  class NullabilityBugVisitor
+      : public BugReporterVisitorImpl<NullabilityBugVisitor> {
+  public:
+    NullabilityBugVisitor(const MemRegion *M) : Region(M) {}
+
+    void Profile(llvm::FoldingSetNodeID &ID) const override {
+      static int X = 0;
+      ID.AddPointer(&X);
+      ID.AddPointer(Region);
+    }
+
+    PathDiagnosticPiece *VisitNode(const ExplodedNode *N,
+                                   const ExplodedNode *PrevN,
+                                   BugReporterContext &BRC,
+                                   BugReport &BR) override;
+
+  private:
+    // The tracked region.
+    const MemRegion *Region;
+  };
+
+  void reportBug(ErrorKind Error, ExplodedNode *N, const MemRegion *Region,
+                 BugReporter &BR, const Stmt *ValueExpr = nullptr) const {
+    if (!BT)
+      BT.reset(new BugType(this, "Nullability", "Memory error"));
+    const char *Msg = ErrorMessages[static_cast<int>(Error)];
+    assert(Msg);
+    std::unique_ptr<BugReport> R(new BugReport(*BT, Msg, N));
+    if (Region) {
+      R->markInteresting(Region);
+      R->addVisitor(llvm::make_unique<NullabilityBugVisitor>(Region));
+    }
+    if (ValueExpr) {
+      R->addRange(ValueExpr->getSourceRange());
+      if (Error == ErrorKind::NilAssignedToNonnull ||
+          Error == ErrorKind::NilPassedToNonnull ||
+          Error == ErrorKind::NilReturnedToNonnull)
+        bugreporter::trackNullOrUndefValue(N, ValueExpr, *R);
+    }
+    BR.emitReport(std::move(R));
+  }
+};
+
+class NullabilityState {
+public:
+  NullabilityState(Nullability Nullab, const Stmt *Source = nullptr)
+      : Nullab(Nullab), Source(Source) {}
+
+  const Stmt *getNullabilitySource() const { return Source; }
+
+  Nullability getValue() const { return Nullab; }
+
+  void Profile(llvm::FoldingSetNodeID &ID) const {
+    ID.AddInteger(static_cast<char>(Nullab));
+    ID.AddPointer(Source);
+  }
+
+  void print(raw_ostream &Out) const {
+    Out << getNullabilityString(Nullab) << "\n";
+  }
+
+private:
+  Nullability Nullab;
+  // 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.
+  const Stmt *Source;
+};
+
+bool operator==(NullabilityState Lhs, NullabilityState Rhs) {
+  return Lhs.getValue() == Rhs.getValue() &&
+         Lhs.getNullabilitySource() == Rhs.getNullabilitySource();
+}
+
+} // end anonymous namespace
+
+REGISTER_MAP_WITH_PROGRAMSTATE(NullabilityMap, const MemRegion *,
+                               NullabilityState)
+
+enum class NullConstraint { IsNull, IsNotNull, Unknown };
+
+static NullConstraint getNullConstraint(DefinedOrUnknownSVal Val,
+                                        ProgramStateRef State) {
+  ConditionTruthVal Nullness = State->isNull(Val);
+  if (Nullness.isConstrainedFalse())
+    return NullConstraint::IsNotNull;
+  if (Nullness.isConstrainedTrue())
+    return NullConstraint::IsNull;
+  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) {
+  auto RegionSVal = Val.getAs<loc::MemRegionVal>();
+  if (!RegionSVal)
+    return nullptr;
+
+  const MemRegion *Region = RegionSVal->getRegion();
+
+  if (CheckSuperRegion) {
+    if (auto FieldReg = Region->getAs<FieldRegion>())
+      return dyn_cast<SymbolicRegion>(FieldReg->getSuperRegion());
+    else if (auto ElementReg = Region->getAs<ElementRegion>())
+      return dyn_cast<SymbolicRegion>(ElementReg->getSuperRegion());
+  }
+
+  return dyn_cast<SymbolicRegion>(Region);
+}
+
+PathDiagnosticPiece *NullabilityChecker::NullabilityBugVisitor::VisitNode(
+    const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC,
+    BugReport &BR) {
+  ProgramStateRef state = N->getState();
+  ProgramStateRef statePrev = PrevN->getState();
+
+  const NullabilityState *TrackedNullab = state->get<NullabilityMap>(Region);
+  const NullabilityState *TrackedNullabPrev =
+      statePrev->get<NullabilityMap>(Region);
+  if (!TrackedNullab)
+    return nullptr;
+
+  if (TrackedNullabPrev &&
+      TrackedNullabPrev->getValue() == TrackedNullab->getValue())
+    return nullptr;
+
+  // Retrieve the associated statement.
+  const Stmt *S = TrackedNullab->getNullabilitySource();
+  if (!S) {
+    ProgramPoint ProgLoc = N->getLocation();
+    if (Optional<StmtPoint> SP = ProgLoc.getAs<StmtPoint>()) {
+      S = SP->getStmt();
+    }
+  }
+
+  if (!S)
+    return nullptr;
+
+  std::string InfoText =
+      (llvm::Twine("Nullability '") +
+       getNullabilityString(TrackedNullab->getValue()) + "' is infered")
+          .str();
+
+  // Generate the extra diagnostic.
+  PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
+                             N->getLocationContext());
+  return new PathDiagnosticEventPiece(Pos, InfoText, true, nullptr);
+}
+
+static Nullability getNullabilityAnnotation(QualType Type) {
+  const auto *AttrType = Type->getAs<AttributedType>();
+  if (!AttrType)
+    return Nullability::Unspecified;
+  if (AttrType->getAttrKind() == AttributedType::attr_nullable)
+    return Nullability::Nullable;
+  else if (AttrType->getAttrKind() == AttributedType::attr_nonnull)
+    return Nullability::Nonnull;
+  return Nullability::Unspecified;
+}
+
+/// Cleaning up the program state.
+void NullabilityChecker::checkDeadSymbols(SymbolReaper &SR,
+                                          CheckerContext &C) const {
+  ProgramStateRef State = C.getState();
+  NullabilityMapTy Nullabilities = State->get<NullabilityMap>();
+  for (NullabilityMapTy::iterator I = Nullabilities.begin(),
+                                  E = Nullabilities.end();
+       I != E; ++I) {
+    if (!SR.isLiveRegion(I->first)) {
+      State = State->remove<NullabilityMap>(I->first);
+    }
+  }
+}
+
+/// This callback triggers when a pointer is dereferenced and the analyzer does
+/// not know anything about the value of that pointer. When that pointer is
+/// nullable, this code emits a warning.
+void NullabilityChecker::checkEvent(ImplicitNullDerefEvent Event) const {
+  const MemRegion *Region =
+      getTrackRegion(Event.Location, /*CheckSuperregion=*/true);
+  if (!Region)
+    return;
+
+  ProgramStateRef State = Event.SinkNode->getState();
+  const NullabilityState *TrackedNullability =
+      State->get<NullabilityMap>(Region);
+
+  if (!TrackedNullability)
+    return;
+
+  if (Filter.CheckNullableDereferenced &&
+      TrackedNullability->getValue() == Nullability::Nullable) {
+    BugReporter &BR = *Event.BR;
+    reportBug(ErrorKind::NullableDereferenced, Event.SinkNode, Region, BR);
+  }
+}
+
+/// This method check when nullable pointer or null value is returned from a
+/// function that has nonnull return type.
+///
+/// TODO: when nullability preconditons are violated, it is ok to violate the
+/// nullability postconditons (i.e.: when one of the nonnull parameters are null
+/// this check should not report any nullability related issue).
+void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
+                                      CheckerContext &C) const {
+  auto RetExpr = S->getRetValue();
+  if (!RetExpr)
+    return;
+
+  if (!RetExpr->getType()->isAnyPointerType())
+    return;
+
+  ProgramStateRef State = C.getState();
+  auto RetSVal =
+      State->getSVal(S, C.getLocationContext()).getAs<DefinedOrUnknownSVal>();
+  if (!RetSVal)
+    return;
+
+  AnalysisDeclContext *DeclCtxt =
+      C.getLocationContext()->getAnalysisDeclContext();
+  const FunctionType *FuncType = DeclCtxt->getDecl()->getFunctionType();
+  if (!FuncType)
+    return;
+
+  NullConstraint Nullness = getNullConstraint(*RetSVal, State);
+
+  Nullability StaticNullability =
+      getNullabilityAnnotation(FuncType->getReturnType());
+
+  if (Filter.CheckNullReturnedFromNonnull &&
+      Nullness == NullConstraint::IsNull &&
+      StaticNullability == Nullability::Nonnull) {
+    static CheckerProgramPointTag Tag(this, "NullReturnedFromNonnull");
+    ExplodedNode *N = C.addTransition(State, C.getPredecessor(), &Tag);
+    reportBug(ErrorKind::NilReturnedToNonnull, N, nullptr, C.getBugReporter(),
+              S);
+    return;
+  }
+
+  const MemRegion *Region = getTrackRegion(*RetSVal);
+  if (!Region)
+    return;
+
+  const NullabilityState *TrackedNullability =
+      State->get<NullabilityMap>(Region);
+  if (TrackedNullability) {
+    Nullability TrackedNullabValue = TrackedNullability->getValue();
+    if (Filter.CheckNullableReturnedFromNonnull &&
+        Nullness != NullConstraint::IsNotNull &&
+        TrackedNullabValue == Nullability::Nullable &&
+        StaticNullability == Nullability::Nonnull) {
+      static CheckerProgramPointTag Tag(this, "NullableReturnedFromNonnull");
+      ExplodedNode *N = C.addTransition(State, C.getPredecessor(), &Tag);
+      reportBug(ErrorKind::NullableReturnedToNonnull, N, Region,
+                C.getBugReporter());
+    }
+    return;
+  }
+  if (StaticNullability == Nullability::Nullable) {
+    State = State->set<NullabilityMap>(Region,
+                                       NullabilityState(StaticNullability, S));
+    C.addTransition(State);
+  }
+}
+
+/// This callback warns when a nullable pointer or a null value is passed to a
+/// function that expects its argument to be nonnull.
+void NullabilityChecker::checkPreCall(const CallEvent &Call,
+                                      CheckerContext &C) const {
+  if (!Call.getDecl())
+    return;
+
+  ProgramStateRef State = C.getState();
+  ProgramStateRef OrigState = State;
+
+  unsigned Idx = 0;
+  for (const ParmVarDecl *Param : Call.parameters()) {
+    if (Param->isParameterPack())
+      break;
+
+    const Expr *ArgExpr = nullptr;
+    if (Idx < Call.getNumArgs())
+      ArgExpr = Call.getArgExpr(Idx);
+    auto ArgSVal = Call.getArgSVal(Idx++).getAs<DefinedOrUnknownSVal>();
+    if (!ArgSVal)
+      continue;
+
+    if (!Param->getType()->isAnyPointerType() &&
+        !Param->getType()->isReferenceType())
+      continue;
+
+    NullConstraint Nullness = getNullConstraint(*ArgSVal, State);
+
+    Nullability ParamNullability = getNullabilityAnnotation(Param->getType());
+    Nullability ArgStaticNullability =
+        getNullabilityAnnotation(ArgExpr->getType());
+
+    if (Filter.CheckNullPassedToNonnull && Nullness == NullConstraint::IsNull &&
+        ArgStaticNullability != Nullability::Nonnull &&
+        ParamNullability == Nullability::Nonnull) {
+      static CheckerProgramPointTag Tag(this, "NullPassedToNonnull");
+      ExplodedNode *N = C.generateSink(State, C.getPredecessor(), &Tag);
+      reportBug(ErrorKind::NilPassedToNonnull, N, nullptr, C.getBugReporter(),
+                ArgExpr);
+      return;
+    }
+
+    const MemRegion *Region = getTrackRegion(*ArgSVal);
+    if (!Region)
+      continue;
+
+    const NullabilityState *TrackedNullability =
+        State->get<NullabilityMap>(Region);
+
+    if (TrackedNullability) {
+      if (Nullness == NullConstraint::IsNotNull ||
+          TrackedNullability->getValue() != Nullability::Nullable)
+        continue;
+
+      if (Filter.CheckNullablePassedToNonnull &&
+          ParamNullability == Nullability::Nonnull) {
+        static CheckerProgramPointTag Tag(this, "NullablePassedToNonnull");
+        ExplodedNode *N = C.generateSink(State, C.getPredecessor(), &Tag);
+        reportBug(ErrorKind::NullablePassedToNonnull, N, Region,
+                  C.getBugReporter(), ArgExpr);
+        return;
+      }
+      if (Filter.CheckNullableDereferenced &&
+          Param->getType()->isReferenceType()) {
+        static CheckerProgramPointTag Tag(this, "NullableDereferenced");
+        ExplodedNode *N = C.generateSink(State, C.getPredecessor(), &Tag);
+        reportBug(ErrorKind::NullableDereferenced, N, Region,
+                  C.getBugReporter(), ArgExpr);
+        return;
+      }
+      continue;
+    }
+    // No tracked nullability yet.
+    if (ArgStaticNullability != Nullability::Nullable)
+      continue;
+    State = State->set<NullabilityMap>(
+        Region, NullabilityState(ArgStaticNullability, ArgExpr));
+  }
+  if (State != OrigState)
+    C.addTransition(State);
+}
+
+/// Suppress the nullability warnings for some functions.
+void NullabilityChecker::checkPostCall(const CallEvent &Call,
+                                       CheckerContext &C) const {
+  auto Decl = Call.getDecl();
+  if (!Decl)
+    return;
+  // ObjC Messages handles in a different callback.
+  if (Call.getKind() == CE_ObjCMessage)
+    return;
+  const FunctionType *FuncType = Decl->getFunctionType();
+  if (!FuncType)
+    return;
+  QualType ReturnType = FuncType->getReturnType();
+  if (!ReturnType->isAnyPointerType())
+    return;
+  const MemRegion *Region = getTrackRegion(Call.getReturnValue());
+  if (!Region)
+    return;
+  ProgramStateRef State = C.getState();
+
+  // CG headers are misannotated. Do not warn for symbols that are the results
+  // of CG calls.
+  const SourceManager &SM = C.getSourceManager();
+  StringRef FilePath = SM.getFilename(SM.getSpellingLoc(Decl->getLocStart()));
+  if (llvm::sys::path::filename(FilePath).startswith("CG")) {
+    State = State->set<NullabilityMap>(Region, Nullability::Contradicted);
+    C.addTransition(State);
+    return;
+  }
+
+  const NullabilityState *TrackedNullability =
+      State->get<NullabilityMap>(Region);
+
+  if (!TrackedNullability &&
+      getNullabilityAnnotation(ReturnType) == Nullability::Nullable) {
+    State = State->set<NullabilityMap>(Region, Nullability::Nullable);
+    C.addTransition(State);
+  }
+}
+
+static Nullability getReceiverNullability(const ObjCMethodCall &M,
+                                          ProgramStateRef State) {
+  Nullability RetNullability = Nullability::Unspecified;
+  if (M.isReceiverSelfOrSuper()) {
+    // For super and super class receivers we assume that the receiver is
+    // nonnull.
+    RetNullability = Nullability::Nonnull;
+  } else {
+    // Otherwise look up nullability in the state.
+    SVal Receiver = M.getReceiverSVal();
+    auto ValueRegionSVal = Receiver.getAs<loc::MemRegionVal>();
+    if (ValueRegionSVal) {
+      const MemRegion *SelfRegion = ValueRegionSVal->getRegion();
+      assert(SelfRegion);
+
+      const NullabilityState *TrackedSelfNullability =
+          State->get<NullabilityMap>(SelfRegion);
+      if (TrackedSelfNullability) {
+        RetNullability = TrackedSelfNullability->getValue();
+      }
+    }
+    if (auto DefOrUnknown = Receiver.getAs<DefinedOrUnknownSVal>()) {
+      // If the receiver is constrained to be nonnull, assume that it is nonnull
+      // regardless of its type.
+      NullConstraint Nullness = getNullConstraint(*DefOrUnknown, State);
+      if (Nullness == NullConstraint::IsNotNull)
+        RetNullability = Nullability::Nonnull;
+    }
+  }
+  return RetNullability;
+}
+
+/// Calculate the nullability of the result of a message expr based on the
+/// nullability of the receiver, the nullability of the return value, and the
+/// constraints.
+void NullabilityChecker::checkPostObjCMessage(const ObjCMethodCall &M,
+                                              CheckerContext &C) const {
+  auto Decl = M.getDecl();
+  if (!Decl)
+    return;
+  QualType RetType = Decl->getReturnType();
+  if (!RetType->isAnyPointerType())
+    return;
+
+  const MemRegion *ReturnRegion = getTrackRegion(M.getReturnValue());
+  if (!ReturnRegion)
+    return;
+
+  ProgramStateRef State = C.getState();
+  auto Interface = Decl->getClassInterface();
+  auto Name = Interface ? Interface->getName() : "";
+  // In order to reduce the noise in the diagnostics generated by this checker,
+  // some framework and programming style based heuristics are used. These
+  // heuristics are for Cocoa APIs which have NS prefix.
+  if (Name.startswith("NS")) {
+    // Developers rely on dynamic invariants such as an item should be available
+    // in a collection, or a collection is not empty often. Those invariants can
+    // not be inferred by any static analysis tool. To not to bother the users
+    // with too many false positives, every item retrieval function should be
+    // ignored for collections. The instance methods of dictionaries in Cocoa
+    // are either item retrieval related or not interesting nullability wise.
+    // Using this fact, to keep the code easier to read just ignore the return
+    // value of every instance method of dictionaries.
+    if (M.isInstanceMessage() && Name.find("Dictionary") != StringRef::npos) {
+      State =
+          State->set<NullabilityMap>(ReturnRegion, Nullability::Contradicted);
+      C.addTransition(State);
+      return;
+    }
+    // For similar reasons ignore some methods of Cocoa arrays.
+    StringRef FirstSelectorSlot = M.getSelector().getNameForSlot(0);
+    if (Name.find("Array") != StringRef::npos &&
+        (FirstSelectorSlot == "firstObject" ||
+         FirstSelectorSlot == "lastObject")) {
+      State =
+          State->set<NullabilityMap>(ReturnRegion, Nullability::Contradicted);
+      C.addTransition(State);
+      return;
+    }
+
+    // Encoding related methods of string should not fail when lossless
+    // encodings are used. Using lossless encodings is so frequent that ignoring
+    // this class of methods reduced the emitted diagnostics by about 30% on
+    // some projects (and all of that was false positives).
+    if (Name.find("String") != StringRef::npos) {
+      for (auto Param : M.parameters()) {
+        if (Param->getName() == "encoding") {
+          State = State->set<NullabilityMap>(ReturnRegion,
+                                             Nullability::Contradicted);
+          C.addTransition(State);
+          return;
+        }
+      }
+    }
+  }
+
+  const ObjCMessageExpr *Message = M.getOriginExpr();
+  Nullability SelfNullability = getReceiverNullability(M, State);
+
+  const NullabilityState *NullabilityOfReturn =
+      State->get<NullabilityMap>(ReturnRegion);
+
+  if (NullabilityOfReturn) {
+    // When we have a nullability tracked for the return value, the nullability
+    // of the expression will be the most nullable of the receiver and the
+    // return value.
+    Nullability RetValTracked = NullabilityOfReturn->getValue();
+    Nullability ComputedNullab =
+        getMostNullable(RetValTracked, SelfNullability);
+    if (ComputedNullab != RetValTracked &&
+        ComputedNullab != Nullability::Unspecified) {
+      const Stmt *NullabilitySource =
+          ComputedNullab == RetValTracked
+              ? NullabilityOfReturn->getNullabilitySource()
+              : Message->getInstanceReceiver();
+      State = State->set<NullabilityMap>(
+          ReturnRegion, NullabilityState(ComputedNullab, NullabilitySource));
+      C.addTransition(State);
+    }
+    return;
+  }
+
+  // No tracked information. Use static type information for return value.
+  Nullability RetNullability = getNullabilityAnnotation(RetType);
+
+  // Properties might be computed. For this reason the static analyzer creates a
+  // new symbol each time an unknown property  is read. To avoid false pozitives
+  // do not treat unknown properties as nullable, even when they explicitly
+  // marked nullable.
+  if (M.getMessageKind() == OCM_PropertyAccess && !C.wasInlined)
+    RetNullability = Nullability::Nonnull;
+
+  Nullability ComputedNullab = getMostNullable(RetNullability, SelfNullability);
+  if (ComputedNullab == Nullability::Nullable) {
+    const Stmt *NullabilitySource = ComputedNullab == RetNullability
+                                        ? Message
+                                        : Message->getInstanceReceiver();
+    State = State->set<NullabilityMap>(
+        ReturnRegion, NullabilityState(ComputedNullab, NullabilitySource));
+    C.addTransition(State);
+  }
+}
+
+/// Explicit casts are trusted. If there is a disagreement in the nullability
+/// annotations in the destination and the source or '0' is casted to nonnull
+/// track the value as having contraditory nullability. This will allow users to
+/// suppress warnings.
+void NullabilityChecker::checkPostStmt(const ExplicitCastExpr *CE,
+                                       CheckerContext &C) const {
+  QualType OriginType = CE->getSubExpr()->getType();
+  QualType DestType = CE->getType();
+  if (!OriginType->isAnyPointerType())
+    return;
+  if (!DestType->isAnyPointerType())
+    return;
+
+  Nullability DestNullability = getNullabilityAnnotation(DestType);
+
+  // No explicit nullability in the destination type, so this cast does not
+  // change the nullability.
+  if (DestNullability == Nullability::Unspecified)
+    return;
+
+  ProgramStateRef State = C.getState();
+  auto RegionSVal =
+      State->getSVal(CE, C.getLocationContext()).getAs<DefinedOrUnknownSVal>();
+  const MemRegion *Region = getTrackRegion(*RegionSVal);
+  if (!Region)
+    return;
+
+  // When 0 is converted to nonnull mark it as contradicted.
+  if (DestNullability == Nullability::Nonnull) {
+    NullConstraint Nullness = getNullConstraint(*RegionSVal, State);
+    if (Nullness == NullConstraint::IsNull) {
+      State = State->set<NullabilityMap>(Region, Nullability::Contradicted);
+      C.addTransition(State);
+      return;
+    }
+  }
+
+  const NullabilityState *TrackedNullability =
+      State->get<NullabilityMap>(Region);
+
+  if (!TrackedNullability) {
+    if (DestNullability != Nullability::Nullable)
+      return;
+    State = State->set<NullabilityMap>(Region,
+                                       NullabilityState(DestNullability, CE));
+    C.addTransition(State);
+    return;
+  }
+
+  if (TrackedNullability->getValue() != DestNullability &&
+      TrackedNullability->getValue() != Nullability::Contradicted) {
+    State = State->set<NullabilityMap>(Region, Nullability::Contradicted);
+    C.addTransition(State);
+  }
+}
+
+/// 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,
+                                   CheckerContext &C) const {
+  const TypedValueRegion *TVR =
+      dyn_cast_or_null<TypedValueRegion>(L.getAsRegion());
+  if (!TVR)
+    return;
+
+  QualType LocType = TVR->getValueType();
+  if (!LocType->isAnyPointerType())
+    return;
+
+  auto ValDefOrUnknown = V.getAs<DefinedOrUnknownSVal>();
+  if (!ValDefOrUnknown)
+    return;
+
+  ProgramStateRef State = C.getState();
+  NullConstraint RhsNullness = getNullConstraint(*ValDefOrUnknown, State);
+
+  Nullability ValNullability = Nullability::Unspecified;
+  if (SymbolRef Sym = ValDefOrUnknown->getAsSymbol())
+    ValNullability = getNullabilityAnnotation(Sym->getType());
+
+  Nullability LocNullability = getNullabilityAnnotation(LocType);
+  if (Filter.CheckNullPassedToNonnull &&
+      RhsNullness == NullConstraint::IsNull &&
+      ValNullability != Nullability::Nonnull &&
+      LocNullability == Nullability::Nonnull) {
+    static CheckerProgramPointTag Tag(this, "NullPassedToNonnull");
+    ExplodedNode *N = C.addTransition(State, C.getPredecessor(), &Tag);
+    reportBug(ErrorKind::NilAssignedToNonnull, N, nullptr, C.getBugReporter(),
+              S);
+    return;
+  }
+  // Intentionally missing case: '0' is bound to a reference. It is handled by
+  // the DereferenceChecker.
+
+  const MemRegion *ValueRegion = getTrackRegion(*ValDefOrUnknown);
+  if (!ValueRegion)
+    return;
+
+  const NullabilityState *TrackedNullability =
+      State->get<NullabilityMap>(ValueRegion);
+
+  if (TrackedNullability) {
+    if (RhsNullness == NullConstraint::IsNotNull ||
+        TrackedNullability->getValue() != Nullability::Nullable)
+      return;
+    if (Filter.CheckNullablePassedToNonnull &&
+        LocNullability == Nullability::Nonnull) {
+      static CheckerProgramPointTag Tag(this, "NullablePassedToNonnull");
+      ExplodedNode *N = C.addTransition(State, C.getPredecessor(), &Tag);
+      reportBug(ErrorKind::NullableAssignedToNonnull, N, ValueRegion,
+                C.getBugReporter());
+    }
+    return;
+  }
+
+  const auto *BinOp = dyn_cast<BinaryOperator>(S);
+
+  if (ValNullability == Nullability::Nullable) {
+    // Trust the static information of the value more than the static
+    // information on the location.
+    const Stmt *NullabilitySource = BinOp ? BinOp->getRHS() : S;
+    State = State->set<NullabilityMap>(
+        ValueRegion, NullabilityState(ValNullability, NullabilitySource));
+    C.addTransition(State);
+    return;
+  }
+
+  if (LocNullability == Nullability::Nullable) {
+    const Stmt *NullabilitySource = BinOp ? BinOp->getLHS() : S;
+    State = State->set<NullabilityMap>(
+        ValueRegion, NullabilityState(LocNullability, NullabilitySource));
+    C.addTransition(State);
+  }
+}
+
+void NullabilityChecker::printState(raw_ostream &Out, ProgramStateRef State,
+                                    const char *NL, const char *Sep) const {
+
+  NullabilityMapTy B = State->get<NullabilityMap>();
+
+  if (B.isEmpty())
+    return;
+
+  Out << Sep << NL;
+
+  for (NullabilityMapTy::iterator I = B.begin(), E = B.end(); I != E; ++I) {
+    Out << I->first << " : ";
+    I->second.print(Out);
+    Out << NL;
+  }
+}
+
+#define REGISTER_CHECKER(name)                                                 \
+  void ento::register##name##Checker(CheckerManager &mgr) {                    \
+    NullabilityChecker *checker = mgr.registerChecker<NullabilityChecker>();   \
+    checker->Filter.Check##name = true;                                        \
+    checker->Filter.CheckName##name = mgr.getCurrentCheckName();               \
+  }
+
+REGISTER_CHECKER(NullPassedToNonnull)
+REGISTER_CHECKER(NullReturnedFromNonnull)
+REGISTER_CHECKER(NullableDereferenced)
+REGISTER_CHECKER(NullablePassedToNonnull)
+REGISTER_CHECKER(NullableReturnedFromNonnull)

Added: cfe/trunk/test/Analysis/nullability.mm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/nullability.mm?rev=246105&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/nullability.mm (added)
+++ cfe/trunk/test/Analysis/nullability.mm Wed Aug 26 18:17:43 2015
@@ -0,0 +1,181 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.core.nullability -verify %s
+
+#define nil 0
+#define BOOL int
+
+ at protocol NSObject
++ (id)alloc;
+- (id)init;
+ at end
+
+ at protocol NSCopying
+ at end
+
+__attribute__((objc_root_class))
+ at interface
+NSObject<NSObject>
+ at end
+
+ at interface NSString : NSObject<NSCopying>
+- (BOOL)isEqualToString : (NSString *_Nonnull)aString;
+- (NSString *)stringByAppendingString:(NSString *_Nonnull)aString;
+ at end
+
+ at interface TestObject : NSObject
+- (int *_Nonnull)returnsNonnull;
+- (int *_Nullable)returnsNullable;
+- (int *)returnsUnspecified;
+- (void)takesNonnull:(int *_Nonnull)p;
+- (void)takesNullable:(int *_Nullable)p;
+- (void)takesUnspecified:(int *)p;
+ at property(readonly, strong) NSString *stuff;
+ at end
+
+TestObject * getUnspecifiedTestObject();
+TestObject *_Nonnull getNonnullTestObject();
+TestObject *_Nullable getNullableTestObject();
+
+int getRandom();
+
+typedef struct Dummy { int val; } Dummy;
+
+void takesNullable(Dummy *_Nullable);
+void takesNonnull(Dummy *_Nonnull);
+void takesUnspecified(Dummy *);
+
+Dummy *_Nullable returnsNullable();
+Dummy *_Nonnull returnsNonnull();
+Dummy *returnsUnspecified();
+int *_Nullable returnsNullableInt();
+
+template <typename T> T *eraseNullab(T *p) { return p; }
+
+void testBasicRules() {
+  Dummy *p = returnsNullable();
+  int *ptr = returnsNullableInt();
+  // Make every dereference a different path to avoid sinks after errors.
+  switch (getRandom()) {
+  case 0: {
+    Dummy &r = *p; // expected-warning {{}}
+  } break;
+  case 1: {
+    int b = p->val; // expected-warning {{}}
+  } break;
+  case 2: {
+    int stuff = *ptr; // expected-warning {{}}
+  } break;
+  case 3:
+    takesNonnull(p); // expected-warning {{}}
+    break;
+  case 4: {
+    Dummy d;
+    takesNullable(&d);
+    Dummy dd(d);
+    break;
+  }
+  // Here the copy constructor is called, so a reference is initialized with the
+  // value of p. No ImplicitNullDereference event will be dispatched for this
+  // case. A followup patch is expected to fix this in NonNullParamChecker.
+  default: { Dummy d = *p; } break; // No warning.
+  }
+  if (p) {
+    takesNonnull(p);
+    if (getRandom()) {
+      Dummy &r = *p;
+    } else {
+      int b = p->val;
+    }
+  }
+  Dummy *q = 0;
+  if (getRandom()) {
+    takesNullable(q);
+    takesNonnull(q); // expected-warning {{}}
+  }
+  Dummy a;
+  Dummy *_Nonnull nonnull = &a;
+  nonnull = q; // expected-warning {{}}
+  q = &a;
+  takesNullable(q);
+  takesNonnull(q);
+}
+
+void testMultiParamChecking(Dummy *_Nonnull a, Dummy *_Nullable b,
+                            Dummy *_Nonnull c);
+
+void testArgumentTracking(Dummy *_Nonnull nonnull, Dummy *_Nullable nullable) {
+  Dummy *p = nullable;
+  Dummy *q = nonnull;
+  switch(getRandom()) {
+  case 1: nonnull = p; break; // expected-warning {{}}
+  case 2: p = 0; break;
+  case 3: q = p; break;
+  case 4: testMultiParamChecking(nonnull, nullable, nonnull); break;
+  case 5: testMultiParamChecking(nonnull, nonnull, nonnull); break;
+  case 6: testMultiParamChecking(nonnull, nullable, nullable); break; // expected-warning {{}}
+  case 7: testMultiParamChecking(nullable, nullable, nonnull); // expected-warning {{}}
+  case 8: testMultiParamChecking(nullable, nullable, nullable); // expected-warning {{}}
+  case 9: testMultiParamChecking((Dummy *_Nonnull)0, nullable, nonnull); break;
+  }
+}
+
+Dummy *_Nonnull testNullableReturn(Dummy *_Nullable a) {
+  Dummy *p = a;
+  return p; // expected-warning {{}}
+}
+
+Dummy *_Nonnull testNullReturn() {
+  Dummy *p = 0;
+  return p; // expected-warning {{}}
+}
+
+void testObjCMessageResultNullability() {
+  // The expected result: the most nullable of self and method return type.
+  TestObject *o = getUnspecifiedTestObject();
+  int *shouldBeNullable = [eraseNullab(getNullableTestObject()) returnsNonnull];
+  switch (getRandom()) {
+  case 0:
+    // The core analyzer assumes that the receiver is non-null after a message
+    // send. This is to avoid some false positives, and increase performance
+    // but it also reduces the coverage and makes this checker unable to reason
+    // about the nullness of the receiver. 
+    [o takesNonnull:shouldBeNullable]; // No warning expected.
+    break;
+  case 1:
+    shouldBeNullable =
+        [eraseNullab(getNullableTestObject()) returnsUnspecified];
+    [o takesNonnull:shouldBeNullable]; // No warning expected.
+    break;
+  case 3:
+    shouldBeNullable = [eraseNullab(getNullableTestObject()) returnsNullable];
+    [o takesNonnull:shouldBeNullable]; // expected-warning {{}}
+    break;
+  case 4:
+    shouldBeNullable = [eraseNullab(getNonnullTestObject()) returnsNullable];
+    [o takesNonnull:shouldBeNullable]; // expected-warning {{}}
+    break;
+  case 5:
+    shouldBeNullable =
+        [eraseNullab(getUnspecifiedTestObject()) returnsNullable];
+    [o takesNonnull:shouldBeNullable]; // expected-warning {{}}
+    break;
+  case 6:
+    shouldBeNullable = [eraseNullab(getNullableTestObject()) returnsNullable];
+    [o takesNonnull:shouldBeNullable]; // expected-warning {{}}
+    break;
+  case 7: {
+    int *shouldBeNonnull = [eraseNullab(getNonnullTestObject()) returnsNonnull];
+    [o takesNonnull:shouldBeNonnull];
+  } break;
+  }
+}
+
+void testCast() {
+  Dummy *p = (Dummy * _Nonnull)returnsNullable();
+  takesNonnull(p);
+}
+
+void testInvalidPropagation() {
+  Dummy *p = returnsUnspecified();
+  takesNullable(p);
+  takesNonnull(p);
+}




More information about the cfe-commits mailing list