r240153 - Diagnose unsafe uses of nil and __nonnull pointers.

Douglas Gregor dgregor at apple.com
Fri Jun 19 11:13:19 PDT 2015


Author: dgregor
Date: Fri Jun 19 13:13:19 2015
New Revision: 240153

URL: http://llvm.org/viewvc/llvm-project?rev=240153&view=rev
Log:
Diagnose unsafe uses of nil and __nonnull pointers.

This generalizes the checking of null arguments to also work with
values of pointer-to-function, reference-to-function, and block
pointer type, using the nullability information within the underling
function prototype to extend non-null checking, and diagnoses returns
of 'nil' within a function with a __nonnull return type.

Note that we don't warn about nil returns from Objective-C methods,
because it's common for Objective-C methods to mimic the nil-swallowing
behavior of the receiver by checking ostensibly non-null parameters
and returning nil from otherwise non-null methods in that
case.

It also diagnoses (via a separate flag) conversions from nullable to
nonnull pointers. It's a separate flag because this warning can be noisy.

Added:
    cfe/trunk/test/SemaObjC/nullability-arc.m
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticGroups.td
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/Sema/Sema.cpp
    cfe/trunk/lib/Sema/SemaChecking.cpp
    cfe/trunk/lib/Sema/SemaOverload.cpp
    cfe/trunk/test/Sema/non-null-warning.c
    cfe/trunk/test/Sema/nullability.c
    cfe/trunk/test/SemaCXX/nullability.cpp
    cfe/trunk/test/SemaObjC/nullability.m

Modified: cfe/trunk/include/clang/Basic/DiagnosticGroups.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticGroups.td?rev=240153&r1=240152&r2=240153&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticGroups.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticGroups.td Fri Jun 19 13:13:19 2015
@@ -250,6 +250,7 @@ def ModuleConflict : DiagGroup<"module-c
 def NewlineEOF : DiagGroup<"newline-eof">;
 def Nullability : DiagGroup<"nullability">;
 def NullabilityDeclSpec : DiagGroup<"nullability-declspec">;
+def NullableToNonNullConversion : DiagGroup<"nullable-to-nonnull-conversion">;
 def NullArithmetic : DiagGroup<"null-arithmetic">;
 def NullCharacter : DiagGroup<"null-character">;
 def NullDereference : DiagGroup<"null-dereference">;

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=240153&r1=240152&r2=240153&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Fri Jun 19 13:13:19 2015
@@ -7700,6 +7700,11 @@ def err_nullability_conflicting : Error<
   "'%select{__nonnull|__nullable|__null_unspecified}0' conflicts with existing "
   "specifier '%select{__nonnull|__nullable|__null_unspecified}1'">;
 
+def warn_nullability_lost : Warning<
+  "implicit conversion from nullable pointer %0 to non-nullable pointer "
+  "type %1">,
+  InGroup<NullableToNonNullConversion>, DefaultIgnore;
+
 }
 
 } // end of sema component.

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=240153&r1=240152&r2=240153&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Fri Jun 19 13:13:19 2015
@@ -8580,9 +8580,10 @@ private:
                             const FunctionProtoType *Proto,
                             SourceLocation Loc);
 
-  void checkCall(NamedDecl *FDecl, ArrayRef<const Expr *> Args,
-                 unsigned NumParams, bool IsMemberFunction, SourceLocation Loc,
-                 SourceRange Range, VariadicCallType CallType);
+  void checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto,
+                 ArrayRef<const Expr *> Args, bool IsMemberFunction, 
+                 SourceLocation Loc, SourceRange Range, 
+                 VariadicCallType CallType);
 
   bool CheckObjCString(Expr *Arg);
 

Modified: cfe/trunk/lib/Sema/Sema.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.cpp?rev=240153&r1=240152&r2=240153&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/Sema.cpp (original)
+++ cfe/trunk/lib/Sema/Sema.cpp Fri Jun 19 13:13:19 2015
@@ -356,6 +356,19 @@ ExprResult Sema::ImpCastExprToType(Expr
   assert((VK == VK_RValue || !E->isRValue()) && "can't cast rvalue to lvalue");
 #endif
 
+  // Check whether we're implicitly casting from a nullable type to a nonnull
+  // type.
+  if (auto exprNullability = E->getType()->getNullability(Context)) {
+    if (*exprNullability == NullabilityKind::Nullable) {
+      if (auto typeNullability = Ty->getNullability(Context)) {
+        if (*typeNullability == NullabilityKind::NonNull) {
+          Diag(E->getLocStart(), diag::warn_nullability_lost)
+            << E->getType() << Ty;
+        }
+      }
+    }
+  }
+
   QualType ExprTy = Context.getCanonicalType(E->getType());
   QualType TypeTy = Context.getCanonicalType(Ty);
 

Modified: cfe/trunk/lib/Sema/SemaChecking.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaChecking.cpp?rev=240153&r1=240152&r2=240153&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaChecking.cpp (original)
+++ cfe/trunk/lib/Sema/SemaChecking.cpp Fri Jun 19 13:13:19 2015
@@ -1115,6 +1115,13 @@ bool Sema::getFormatStringInfo(const For
 /// \brief Returns true if the value evaluates to null.
 static bool CheckNonNullExpr(Sema &S,
                              const Expr *Expr) {
+  // If the expression has non-null type, it doesn't evaluate to null.
+  if (auto nullability
+        = Expr->IgnoreImplicit()->getType()->getNullability(S.Context)) {
+    if (*nullability == NullabilityKind::NonNull)
+      return false;
+  }
+
   // As a special case, transparent unions initialized with zero are
   // considered null for the purposes of the nonnull attribute.
   if (const RecordType *UT = Expr->getType()->getAsUnionType()) {
@@ -1190,56 +1197,111 @@ DiagnoseCStringFormatDirectiveInCFAPI(Se
   }
 }
 
+/// Determine whether the given type has a non-null nullability annotation.
+static bool isNonNullType(ASTContext &ctx, QualType type) {
+  if (auto nullability = type->getNullability(ctx))
+    return *nullability == NullabilityKind::NonNull;
+     
+  return false;
+}
+
 static void CheckNonNullArguments(Sema &S,
                                   const NamedDecl *FDecl,
+                                  const FunctionProtoType *Proto,
                                   ArrayRef<const Expr *> Args,
                                   SourceLocation CallSiteLoc) {
+  assert((FDecl || Proto) && "Need a function declaration or prototype");
+
   // Check the attributes attached to the method/function itself.
   llvm::SmallBitVector NonNullArgs;
-  for (const auto *NonNull : FDecl->specific_attrs<NonNullAttr>()) {
-    if (!NonNull->args_size()) {
-      // Easy case: all pointer arguments are nonnull.
-      for (const auto *Arg : Args)
-        if (S.isValidPointerAttrType(Arg->getType()))
-          CheckNonNullArgument(S, Arg, CallSiteLoc);
-      return;
-    }
+  if (FDecl) {
+    // Handle the nonnull attribute on the function/method declaration itself.
+    for (const auto *NonNull : FDecl->specific_attrs<NonNullAttr>()) {
+      if (!NonNull->args_size()) {
+        // Easy case: all pointer arguments are nonnull.
+        for (const auto *Arg : Args)
+          if (S.isValidPointerAttrType(Arg->getType()))
+            CheckNonNullArgument(S, Arg, CallSiteLoc);
+        return;
+      }
 
-    for (unsigned Val : NonNull->args()) {
-      if (Val >= Args.size())
-        continue;
-      if (NonNullArgs.empty())
-        NonNullArgs.resize(Args.size());
-      NonNullArgs.set(Val);
+      for (unsigned Val : NonNull->args()) {
+        if (Val >= Args.size())
+          continue;
+        if (NonNullArgs.empty())
+          NonNullArgs.resize(Args.size());
+        NonNullArgs.set(Val);
+      }
     }
   }
 
-  // Check the attributes on the parameters.
-  ArrayRef<ParmVarDecl*> parms;
-  if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(FDecl))
-    parms = FD->parameters();
-  else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(FDecl))
-    parms = MD->parameters();
-
-  unsigned ArgIndex = 0;
-  for (ArrayRef<ParmVarDecl*>::iterator I = parms.begin(), E = parms.end();
-       I != E; ++I, ++ArgIndex) {
-    const ParmVarDecl *PVD = *I;
-    if (PVD->hasAttr<NonNullAttr>() ||
-        (ArgIndex < NonNullArgs.size() && NonNullArgs[ArgIndex]))
-      CheckNonNullArgument(S, Args[ArgIndex], CallSiteLoc);
+  if (FDecl && (isa<FunctionDecl>(FDecl) || isa<ObjCMethodDecl>(FDecl))) {
+    // Handle the nonnull attribute on the parameters of the
+    // function/method.
+    ArrayRef<ParmVarDecl*> parms;
+    if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(FDecl))
+      parms = FD->parameters();
+    else
+      parms = cast<ObjCMethodDecl>(FDecl)->parameters();
+    
+    unsigned ParamIndex = 0;
+    for (ArrayRef<ParmVarDecl*>::iterator I = parms.begin(), E = parms.end();
+         I != E; ++I, ++ParamIndex) {
+      const ParmVarDecl *PVD = *I;
+      if (PVD->hasAttr<NonNullAttr>() || 
+          isNonNullType(S.Context, PVD->getType())) {
+        if (NonNullArgs.empty())
+          NonNullArgs.resize(Args.size());
+
+        NonNullArgs.set(ParamIndex);
+      }
+    }
+  } else {
+    // If we have a non-function, non-method declaration but no
+    // function prototype, try to dig out the function prototype.
+    if (!Proto) {
+      if (const ValueDecl *VD = dyn_cast<ValueDecl>(FDecl)) {
+        QualType type = VD->getType().getNonReferenceType();
+        if (auto pointerType = type->getAs<PointerType>())
+          type = pointerType->getPointeeType();
+        else if (auto blockType = type->getAs<BlockPointerType>())
+          type = blockType->getPointeeType();
+        // FIXME: data member pointers?
+
+        // Dig out the function prototype, if there is one.
+        Proto = type->getAs<FunctionProtoType>();
+      } 
+    }
+
+    // Fill in non-null argument information from the nullability
+    // information on the parameter types (if we have them).
+    if (Proto) {
+      unsigned Index = 0;
+      for (auto paramType : Proto->getParamTypes()) {
+        if (isNonNullType(S.Context, paramType)) {
+          if (NonNullArgs.empty())
+            NonNullArgs.resize(Args.size());
+          
+          NonNullArgs.set(Index);
+        }
+        
+        ++Index;
+      }
+    }
   }
 
-  // In case this is a variadic call, check any remaining arguments.
-  for (/**/; ArgIndex < NonNullArgs.size(); ++ArgIndex)
+  // Check for non-null arguments.
+  for (unsigned ArgIndex = 0, ArgIndexEnd = NonNullArgs.size(); 
+       ArgIndex != ArgIndexEnd; ++ArgIndex) {
     if (NonNullArgs[ArgIndex])
       CheckNonNullArgument(S, Args[ArgIndex], CallSiteLoc);
+  }
 }
 
 /// Handles the checks for format strings, non-POD arguments to vararg
 /// functions, and NULL arguments passed to non-NULL parameters.
-void Sema::checkCall(NamedDecl *FDecl, ArrayRef<const Expr *> Args,
-                     unsigned NumParams, bool IsMemberFunction,
+void Sema::checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto,
+                     ArrayRef<const Expr *> Args, bool IsMemberFunction,
                      SourceLocation Loc, SourceRange Range,
                      VariadicCallType CallType) {
   // FIXME: We should check as much as we can in the template definition.
@@ -1261,6 +1323,13 @@ void Sema::checkCall(NamedDecl *FDecl, A
   // Refuse POD arguments that weren't caught by the format string
   // checks above.
   if (CallType != VariadicDoesNotApply) {
+    unsigned NumParams = Proto ? Proto->getNumParams()
+                       : FDecl && isa<FunctionDecl>(FDecl)
+                           ? cast<FunctionDecl>(FDecl)->getNumParams()
+                       : FDecl && isa<ObjCMethodDecl>(FDecl)
+                           ? cast<ObjCMethodDecl>(FDecl)->param_size()
+                       : 0;
+
     for (unsigned ArgIdx = NumParams; ArgIdx < Args.size(); ++ArgIdx) {
       // Args[ArgIdx] can be null in malformed code.
       if (const Expr *Arg = Args[ArgIdx]) {
@@ -1270,12 +1339,14 @@ void Sema::checkCall(NamedDecl *FDecl, A
     }
   }
 
-  if (FDecl) {
-    CheckNonNullArguments(*this, FDecl, Args, Loc);
+  if (FDecl || Proto) {
+    CheckNonNullArguments(*this, FDecl, Proto, Args, Loc);
 
     // Type safety checking.
-    for (const auto *I : FDecl->specific_attrs<ArgumentWithTypeTagAttr>())
-      CheckArgumentWithTypeTag(I, Args.data());
+    if (FDecl) {
+      for (const auto *I : FDecl->specific_attrs<ArgumentWithTypeTagAttr>())
+        CheckArgumentWithTypeTag(I, Args.data());
+    }
   }
 }
 
@@ -1287,8 +1358,8 @@ void Sema::CheckConstructorCall(Function
                                 SourceLocation Loc) {
   VariadicCallType CallType =
     Proto->isVariadic() ? VariadicConstructor : VariadicDoesNotApply;
-  checkCall(FDecl, Args, Proto->getNumParams(),
-            /*IsMemberFunction=*/true, Loc, SourceRange(), CallType);
+  checkCall(FDecl, Proto, Args, /*IsMemberFunction=*/true, Loc, SourceRange(), 
+            CallType);
 }
 
 /// CheckFunctionCall - Check a direct function call for various correctness
@@ -1301,7 +1372,6 @@ bool Sema::CheckFunctionCall(FunctionDec
                           IsMemberOperatorCall;
   VariadicCallType CallType = getVariadicCallType(FDecl, Proto,
                                                   TheCall->getCallee());
-  unsigned NumParams = Proto ? Proto->getNumParams() : 0;
   Expr** Args = TheCall->getArgs();
   unsigned NumArgs = TheCall->getNumArgs();
   if (IsMemberOperatorCall) {
@@ -1311,7 +1381,7 @@ bool Sema::CheckFunctionCall(FunctionDec
     ++Args;
     --NumArgs;
   }
-  checkCall(FDecl, llvm::makeArrayRef(Args, NumArgs), NumParams,
+  checkCall(FDecl, Proto, llvm::makeArrayRef(Args, NumArgs), 
             IsMemberFunction, TheCall->getRParenLoc(),
             TheCall->getCallee()->getSourceRange(), CallType);
 
@@ -1345,9 +1415,9 @@ bool Sema::CheckObjCMethodCall(ObjCMetho
   VariadicCallType CallType =
       Method->isVariadic() ? VariadicMethod : VariadicDoesNotApply;
 
-  checkCall(Method, Args, Method->param_size(),
-            /*IsMemberFunction=*/false,
-            lbrac, Method->getSourceRange(), CallType);
+  checkCall(Method, nullptr, Args,
+            /*IsMemberFunction=*/false, lbrac, Method->getSourceRange(), 
+            CallType);
 
   return false;
 }
@@ -1356,13 +1426,14 @@ bool Sema::CheckPointerCall(NamedDecl *N
                             const FunctionProtoType *Proto) {
   QualType Ty;
   if (const auto *V = dyn_cast<VarDecl>(NDecl))
-    Ty = V->getType();
+    Ty = V->getType().getNonReferenceType();
   else if (const auto *F = dyn_cast<FieldDecl>(NDecl))
-    Ty = F->getType();
+    Ty = F->getType().getNonReferenceType();
   else
     return false;
 
-  if (!Ty->isBlockPointerType() && !Ty->isFunctionPointerType())
+  if (!Ty->isBlockPointerType() && !Ty->isFunctionPointerType() &&
+      !Ty->isFunctionProtoType())
     return false;
 
   VariadicCallType CallType;
@@ -1373,11 +1444,10 @@ bool Sema::CheckPointerCall(NamedDecl *N
   } else { // Ty->isFunctionPointerType()
     CallType = VariadicFunction;
   }
-  unsigned NumParams = Proto ? Proto->getNumParams() : 0;
 
-  checkCall(NDecl, llvm::makeArrayRef(TheCall->getArgs(),
-                                      TheCall->getNumArgs()),
-            NumParams, /*IsMemberFunction=*/false, TheCall->getRParenLoc(),
+  checkCall(NDecl, Proto,
+            llvm::makeArrayRef(TheCall->getArgs(), TheCall->getNumArgs()),
+            /*IsMemberFunction=*/false, TheCall->getRParenLoc(),
             TheCall->getCallee()->getSourceRange(), CallType);
 
   return false;
@@ -1388,11 +1458,9 @@ bool Sema::CheckPointerCall(NamedDecl *N
 bool Sema::CheckOtherCall(CallExpr *TheCall, const FunctionProtoType *Proto) {
   VariadicCallType CallType = getVariadicCallType(/*FDecl=*/nullptr, Proto,
                                                   TheCall->getCallee());
-  unsigned NumParams = Proto ? Proto->getNumParams() : 0;
-
-  checkCall(/*FDecl=*/nullptr,
+  checkCall(/*FDecl=*/nullptr, Proto,
             llvm::makeArrayRef(TheCall->getArgs(), TheCall->getNumArgs()),
-            NumParams, /*IsMemberFunction=*/false, TheCall->getRParenLoc(),
+            /*IsMemberFunction=*/false, TheCall->getRParenLoc(),
             TheCall->getCallee()->getSourceRange(), CallType);
 
   return false;
@@ -5680,7 +5748,8 @@ Sema::CheckReturnValExpr(Expr *RetValExp
   CheckReturnStackAddr(*this, RetValExp, lhsType, ReturnLoc);
 
   // Check if the return value is null but should not be.
-  if (Attrs && hasSpecificAttr<ReturnsNonNullAttr>(*Attrs) &&
+  if (((Attrs && hasSpecificAttr<ReturnsNonNullAttr>(*Attrs)) ||
+       (!isObjCMethod && isNonNullType(Context, lhsType))) &&
       CheckNonNullExpr(*this, RetValExp))
     Diag(ReturnLoc, diag::warn_null_ret)
       << (isObjCMethod ? 1 : 0) << RetValExp->getSourceRange();

Modified: cfe/trunk/lib/Sema/SemaOverload.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.cpp?rev=240153&r1=240152&r2=240153&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaOverload.cpp (original)
+++ cfe/trunk/lib/Sema/SemaOverload.cpp Fri Jun 19 13:13:19 2015
@@ -11260,7 +11260,7 @@ Sema::CreateOverloadedBinOp(SourceLocati
         if (Op == OO_Equal)
           DiagnoseSelfMove(Args[0], Args[1], OpLoc);
 
-        checkCall(FnDecl, ArgsArray, 0, isa<CXXMethodDecl>(FnDecl), OpLoc,
+        checkCall(FnDecl, nullptr, ArgsArray, isa<CXXMethodDecl>(FnDecl), OpLoc, 
                   TheCall->getSourceRange(), VariadicDoesNotApply);
 
         return MaybeBindToTemporary(TheCall);

Modified: cfe/trunk/test/Sema/non-null-warning.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/non-null-warning.c?rev=240153&r1=240152&r2=240153&view=diff
==============================================================================
--- cfe/trunk/test/Sema/non-null-warning.c (original)
+++ cfe/trunk/test/Sema/non-null-warning.c Fri Jun 19 13:13:19 2015
@@ -33,3 +33,10 @@ int *foo3(int * __nonnull x) { // expect
   return 0;
 }
 
+int * ret_nonnull() {
+  return 0; // expected-warning {{null returned from function that requires a non-null return value}}
+}
+
+int main () {
+  foo(0); // expected-warning {{null passed to a callee that requires a non-null argument}}
+}

Modified: cfe/trunk/test/Sema/nullability.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/nullability.c?rev=240153&r1=240152&r2=240153&view=diff
==============================================================================
--- cfe/trunk/test/Sema/nullability.c (original)
+++ cfe/trunk/test/Sema/nullability.c Fri Jun 19 13:13:19 2015
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-nullability-declspec %s -verify
+// RUN: %clang_cc1 -fsyntax-only -fblocks -Wnullable-to-nonnull-conversion -Wno-nullability-declspec %s -verify
 
 #if __has_feature(nullability)
 #else
@@ -85,3 +85,29 @@ void printing_nullability(void) {
   int * __nullable * __nonnull iptrptr2;
   float * *fptrptr2 = iptrptr2; // expected-warning{{incompatible pointer types initializing 'float **' with an expression of type 'int * __nullable * __nonnull'}}
 }
+
+// Check passing null to a __nonnull argument.
+void accepts_nonnull_1(__nonnull int *ptr);
+void (*accepts_nonnull_2)(__nonnull int *ptr);
+void (^accepts_nonnull_3)(__nonnull int *ptr);
+
+void test_accepts_nonnull_null_pointer_literal() {
+  accepts_nonnull_1(0); // expected-warning{{null passed to a callee that requires a non-null argument}}
+  accepts_nonnull_2(0); // expected-warning{{null passed to a callee that requires a non-null argument}}
+  accepts_nonnull_3(0); // expected-warning{{null passed to a callee that requires a non-null argument}}
+}
+
+// Check returning nil from a __nonnull-returning function.
+__nonnull int *returns_int_ptr(int x) {
+  if (x) {
+    return 0; // expected-warning{{null returned from function that requires a non-null return value}}
+  }
+
+  return (__nonnull int *)0;
+}
+
+// Check nullable-to-nonnull conversions.
+void nullable_to_nonnull(__nullable int *ptr) {
+  int *a = ptr; // okay
+  __nonnull int *b = ptr; // expected-warning{{implicit conversion from nullable pointer 'int * __nullable' to non-nullable pointer type 'int * __nonnull'}}
+}

Modified: cfe/trunk/test/SemaCXX/nullability.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/nullability.cpp?rev=240153&r1=240152&r2=240153&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/nullability.cpp (original)
+++ cfe/trunk/test/SemaCXX/nullability.cpp Fri Jun 19 13:13:19 2015
@@ -40,3 +40,25 @@ struct AddNonNull2 {
   // cannot apply to that specific dependent type.
   typedef __nonnull AddNonNull<T> (*invalid4); // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'AddNonNull<T>'}}
 };
+
+// Check passing null to a __nonnull argument.
+void (*accepts_nonnull_1)(__nonnull int *ptr);
+void (*& accepts_nonnull_2)(__nonnull int *ptr) = accepts_nonnull_1;
+void (X::* accepts_nonnull_3)(__nonnull int *ptr);
+void accepts_nonnull_4(__nonnull int *ptr);
+void (&accepts_nonnull_5)(__nonnull int *ptr) = accepts_nonnull_4;
+
+void test_accepts_nonnull_null_pointer_literal(X *x) {
+  accepts_nonnull_1(0); // expected-warning{{null passed to a callee that requires a non-null argument}}
+  accepts_nonnull_2(0); // expected-warning{{null passed to a callee that requires a non-null argument}}
+  (x->*accepts_nonnull_3)(0); // expected-warning{{null passed to a callee that requires a non-null argument}}
+  accepts_nonnull_4(0); // expected-warning{{null passed to a callee that requires a non-null argument}}
+  accepts_nonnull_5(0); // expected-warning{{null passed to a callee that requires a non-null argument}}
+}
+
+template<void FP(__nonnull int*)> 
+void test_accepts_nonnull_null_pointer_literal_template() {
+  FP(0); // expected-warning{{null passed to a callee that requires a non-null argument}}
+}
+
+template void test_accepts_nonnull_null_pointer_literal_template<&accepts_nonnull_4>(); // expected-note{{instantiation of function template specialization}}

Added: cfe/trunk/test/SemaObjC/nullability-arc.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjC/nullability-arc.m?rev=240153&view=auto
==============================================================================
--- cfe/trunk/test/SemaObjC/nullability-arc.m (added)
+++ cfe/trunk/test/SemaObjC/nullability-arc.m Fri Jun 19 13:13:19 2015
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -fobjc-arc -fsyntax-only -Woverriding-method-mismatch %s -verify
+
+__attribute__((objc_root_class))
+ at interface NSFoo
+ at end
+
+// ARC qualifiers stacked with nullability.
+void accepts_arc_qualified(NSFoo * __unsafe_unretained __nonnull obj) {
+  accepts_arc_qualified(0); // expected-warning{{null passed to a callee that requires a non-null argument}}
+}

Modified: cfe/trunk/test/SemaObjC/nullability.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjC/nullability.m?rev=240153&r1=240152&r2=240153&view=diff
==============================================================================
--- cfe/trunk/test/SemaObjC/nullability.m (original)
+++ cfe/trunk/test/SemaObjC/nullability.m Fri Jun 19 13:13:19 2015
@@ -1,6 +1,9 @@
 // RUN: %clang_cc1 -fsyntax-only -fblocks -Woverriding-method-mismatch -Wno-nullability-declspec %s -verify
 
+__attribute__((objc_root_class))
 @interface NSFoo
+- (void)methodTakingIntPtr:(__nonnull int *)ptr;
+- (__nonnull int *)methodReturningIntPtr;
 @end
 
 // Nullability applies to all pointer types.
@@ -17,3 +20,14 @@ typedef __nonnull NSFoo * __nullable con
 void testBlocksPrinting(NSFoo * __nullable (^bp)(int)) {
   int *ip = bp; // expected-error{{'NSFoo * __nullable (^)(int)'}}
 }
+void test_accepts_nonnull_null_pointer_literal(NSFoo *foo) {
+  [foo methodTakingIntPtr: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
+}
+
+// Check returning nil from a __nonnull-returning method.
+ at implementation NSFoo
+- (void)methodTakingIntPtr:(__nonnull int *)ptr { }
+- (__nonnull int *)methodReturningIntPtr {
+  return 0; // no warning
+}
+ at end





More information about the cfe-commits mailing list