ahatanak created this revision.
ahatanak added a reviewer: doug.gregor.
ahatanak added a subscriber: cfe-commits.

This patch makes clang issue a warning when a null constant is used in a 
context where a non null expression is expected. For example:

```
int * _Nonnull p0 = 0; // warning expected here
int * _Nonnull p1;
int * _Nonnull p2 = c ? p1 : 0; // warning expected here
```

A new function Sema::diagnoseNullPtrToNonnullCast is defined, which checks 
whether a null pointer constant is being cast to a _Nonnull pointer type, and 
called before ImplicitCastExprs are created.

rdar://problem/24724255
rdar://problem/22074116



https://reviews.llvm.org/D22391

Files:
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Sema.h
  lib/Sema/Sema.cpp
  lib/Sema/SemaExpr.cpp
  test/Sema/nullability.c
  test/SemaCXX/nullability.cpp

Index: test/SemaCXX/nullability.cpp
===================================================================
--- test/SemaCXX/nullability.cpp
+++ test/SemaCXX/nullability.cpp
@@ -54,16 +54,16 @@
 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}}
+  accepts_nonnull_1(0); // expected-warning{{null passed to a callee that requires a non-null argument}} expected-warning{{implicitly casting a null constant to non-nullable pointer type 'int * _Nonnull'}}
+  accepts_nonnull_2(0); // expected-warning{{null passed to a callee that requires a non-null argument}} expected-warning{{implicitly casting a null constant to non-nullable pointer type 'int * _Nonnull'}}
+  (x->*accepts_nonnull_3)(0); // expected-warning{{null passed to a callee that requires a non-null argument}} expected-warning{{implicitly casting a null constant to non-nullable pointer type 'int * _Nonnull'}}
+  accepts_nonnull_4(0); // expected-warning{{null passed to a callee that requires a non-null argument}} expected-warning{{implicitly casting a null constant to non-nullable pointer type 'int * _Nonnull'}}
+  accepts_nonnull_5(0); // expected-warning{{null passed to a callee that requires a non-null argument}} expected-warning{{implicitly casting a null constant to non-nullable pointer type 'int * _Nonnull'}}
 }
 
 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}}
+  FP(0); // expected-warning{{null passed to a callee that requires a non-null argument}} expected-warning{{implicitly casting a null constant to non-nullable pointer type 'int * _Nonnull'}}
 }
 
 template void test_accepts_nonnull_null_pointer_literal_template<&accepts_nonnull_4>(); // expected-note{{instantiation of function template specialization}}
Index: test/Sema/nullability.c
===================================================================
--- test/Sema/nullability.c
+++ test/Sema/nullability.c
@@ -106,15 +106,15 @@
 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}}
+  accepts_nonnull_1(0); // expected-warning{{null passed to a callee that requires a non-null argument}} expected-warning{{implicitly casting a null constant to non-nullable pointer type 'int * _Nonnull}}
+  accepts_nonnull_2(0); // expected-warning{{null passed to a callee that requires a non-null argument}} expected-warning{{implicitly casting a null constant to non-nullable pointer type 'int * _Nonnull}}
+  accepts_nonnull_3(0); // expected-warning{{null passed to a callee that requires a non-null argument}} expected-warning{{implicitly casting a null constant to non-nullable pointer type 'int * _Nonnull}}
 }
 
 // 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 0; // expected-warning{{null returned from function that requires a non-null return value}} expected-warning{{implicitly casting a null constant to non-nullable pointer type 'int * _Nonnull}}
   }
 
   return (_Nonnull int *)0;
@@ -128,3 +128,9 @@
 
   accepts_nonnull_1(ptr); // expected-warning{{implicit conversion from nullable pointer 'int * _Nullable' to non-nullable pointer type 'int * _Nonnull'}}
 }
+
+void null_const_to_nonnull(int c) {
+  int * _Nonnull p0 = 0; // expected-warning{{implicitly casting a null constant to non-nullable pointer type 'int * _Nonnull'}}
+  int * _Nonnull p1;
+  int * _Nonnull p2 = c ? p1 : 0; // expected-warning{{implicitly casting a null constant to non-nullable pointer type 'int * _Nonnull'}}
+}
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -709,6 +709,7 @@
       E->getType().getObjCLifetime() == Qualifiers::OCL_Weak)
     Cleanup.setExprNeedsCleanups(true);
 
+  diagnoseNullPtrToNonnullCast(T, E, E->getExprLoc());
   ExprResult Res = ImplicitCastExpr::Create(Context, T, CK_LValueToRValue, E,
                                             nullptr, VK_RValue);
 
Index: lib/Sema/Sema.cpp
===================================================================
--- lib/Sema/Sema.cpp
+++ lib/Sema/Sema.cpp
@@ -362,6 +362,17 @@
   Diag(Loc, diag::warn_nullability_lost) << SrcType << DstType;
 }
 
+void Sema::diagnoseNullPtrToNonnullCast(QualType DstType, Expr *E,
+                                        SourceLocation Loc) {
+  if (!DstType->isPointerType() || CurContext->isDependentContext())
+    return;
+
+  if (Optional<NullabilityKind> Kind = DstType->getNullability(Context))
+    if (*Kind == NullabilityKind::NonNull &&
+        E->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNotNull))
+      Diag(Loc, diag::warn_null_const_to_nonnull) << DstType;
+}
+
 /// ImpCastExprToType - If Expr is not of type 'Type', insert an implicit cast.
 /// If there is already an implicit cast, merge into the existing one.
 /// The result is of the given category.
@@ -401,6 +412,7 @@
     }
   }
 
+  diagnoseNullPtrToNonnullCast(Ty, E, E->getExprLoc());
   return ImplicitCastExpr::Create(Context, Ty, Kind, E, BasePath, VK);
 }
 
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -3601,6 +3601,11 @@
   void diagnoseNullableToNonnullConversion(QualType DstType, QualType SrcType,
                                            SourceLocation Loc);
 
+  /// Warn if we're implicitly casting from a null pointer constant to a
+  /// _Nonnull pointer type.
+  void diagnoseNullPtrToNonnullCast(QualType DstType, Expr *E,
+                                    SourceLocation Loc);
+
   ParsingDeclState PushParsingDeclaration(sema::DelayedDiagnosticPool &pool) {
     return DelayedDiagnostics.push(pool);
   }
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -8482,6 +8482,10 @@
   "type %1">,
   InGroup<NullableToNonNullConversion>, DefaultIgnore;
 
+def warn_null_const_to_nonnull : Warning<
+  "implicitly casting a null constant to non-nullable pointer type %0">,
+  InGroup<NullableToNonNullConversion>, DefaultIgnore;
+
 def err_nullability_cs_multilevel : Error<
   "nullability keyword %0 cannot be applied to multi-level pointer type %1">;
 def note_nullability_type_specifier : Note<
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to