ahatanak updated this revision to Diff 154908.
ahatanak marked 3 inline comments as done.
ahatanak added a comment.

Address review comments.


Repository:
  rC Clang

https://reviews.llvm.org/D22391

Files:
  include/clang/Basic/DiagnosticGroups.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Sema.h
  lib/Sema/SemaChecking.cpp
  lib/Sema/SemaDecl.cpp
  lib/Sema/SemaExpr.cpp
  lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
  test/Analysis/nullability-no-arc.mm
  test/Analysis/nullability.mm
  test/Analysis/nullability_nullonly.mm
  test/Sema/conditional-expr.c
  test/Sema/null_constant_to_nonnull.c
  test/SemaObjC/nullability.m

Index: test/SemaObjC/nullability.m
===================================================================
--- test/SemaObjC/nullability.m
+++ test/SemaObjC/nullability.m
@@ -38,6 +38,7 @@
 // expected-note@-1{{use nullability type specifier '_Nonnull' to affect the innermost pointer type of 'NSFoo **'}}
 - (nonnull NSFoo * _Nullable)conflictingMethod1; // expected-error{{nullability specifier '_Nullable' conflicts with existing specifier '_Nonnull'}}
 - (nonnull NSFoo * _Nonnull)redundantMethod1; // expected-warning{{duplicate nullability specifier '_Nonnull'}}
+- (void)setObject:(id _Nonnull)object atIndexedSubscript:(int)index;
 
 @property(nonnull,retain) NSFoo *property1;
 @property(nullable,assign) NSFoo ** invalidProperty1; // expected-error{{nullability keyword 'nullable' cannot be applied to multi-level pointer type 'NSFoo **'}}
@@ -65,6 +66,7 @@
   [bar setProperty1: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
   [bar setProperty2: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
   int *ptr = bar.property1; // expected-warning{{incompatible pointer types initializing 'int *' with an expression of type 'NSFoo * _Nonnull'}}
+  bar[1] = 0; // expected-warning{{null passed to a callee that requires a non-null argument}}
 }
 
 // Check returning nil from a nonnull-returning method.
@@ -84,6 +86,9 @@
   int *ip = 0;
   return ip; // expected-warning{{result type 'NSFoo * _Nonnull'}}
 }
+- (void)setObject:(id _Nonnull)object atIndexedSubscript:(int)index {
+}
+
 @end
 
 __attribute__((objc_root_class))
Index: test/Sema/null_constant_to_nonnull.c
===================================================================
--- /dev/null
+++ test/Sema/null_constant_to_nonnull.c
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -fsyntax-only -Wnullable-to-nonnull-conversion %s -verify
+
+struct S {
+  int * _Nonnull f;
+};
+
+void null_const_to_nonnull(int c, struct S *s) {
+  int * _Nonnull p0 = 0; // expected-warning{{implicitly casting a null constant to non-nullable pointer type 'int * _Nonnull'}}
+  p0 = 0; // expected-warning{{implicitly casting a null constant to non-nullable pointer type 'int * _Nonnull'}}
+  p0 = (int * _Nonnull)0; // explicit cast silences warnings
+  int * _Nonnull p1;
+  int * _Nonnull p2 = c ? p1 : 0; // expected-warning{{implicit conversion from nullable pointer 'int * _Nullable' to non-nullable pointer type 'int * _Nonnull'}}
+  p2 = c ? p1 : (int * _Nonnull)0; // explicit cast silences warnings
+  s->f = 0; // expected-warning{{implicitly casting a null constant to non-nullable pointer type 'int * _Nonnull'}}
+}
Index: test/Sema/conditional-expr.c
===================================================================
--- test/Sema/conditional-expr.c
+++ test/Sema/conditional-expr.c
@@ -17,7 +17,7 @@
   dp = ip; // expected-warning {{incompatible pointer types assigning to 'double *' from 'int *'}}
   dp = 0 ? (double *)0 : (void *)0;
   vp = 0 ? (double *)0 : (void *)0;
-  ip = 0 ? (double *)0 : (void *)0; // expected-warning {{incompatible pointer types assigning to 'int *' from 'double *'}}
+  ip = 0 ? (double *)0 : (void *)0; // expected-warning {{incompatible pointer types assigning to 'int *' from 'double * _Nullable'}}
 
   const int *cip;
   vp = (0 ? vp : cip); // expected-warning {{discards qualifiers}}
@@ -90,7 +90,7 @@
 
 int f0(int a) {
   // GCC considers this a warning.
-  return a ? f1() : nil; // expected-warning {{pointer/integer type mismatch in conditional expression ('int' and 'void *')}} expected-warning {{incompatible pointer to integer conversion returning 'void *' from a function with result type 'int'}}
+  return a ? f1() : nil; // expected-warning {{pointer/integer type mismatch in conditional expression ('int' and 'void *')}} expected-warning {{incompatible pointer to integer conversion returning 'void * _Nullable' from a function with result type 'int'}}
 }
 
 int f2(int x) {
Index: test/Analysis/nullability_nullonly.mm
===================================================================
--- test/Analysis/nullability_nullonly.mm
+++ test/Analysis/nullability_nullonly.mm
@@ -100,7 +100,7 @@
 }
 
 void testObjCARCExplicitZeroInitialization() {
-  TestObject * _Nonnull explicitlyZeroInitialized = nil; // expected-warning {{nil assigned to a pointer which is expected to have non-null value}}
+  TestObject * _Nonnull explicitlyZeroInitialized = nil; // expected-warning{{implicitly casting a null constant to non-nullable pointer type 'TestObject * _Nonnull __strong'}}
 }
 
 // Under ARC, returned expressions of ObjC objects types are are implicitly
Index: test/Analysis/nullability.mm
===================================================================
--- test/Analysis/nullability.mm
+++ test/Analysis/nullability.mm
@@ -180,7 +180,7 @@
 
   // Since we've already had an invariant violation along this path,
   // we shouldn't warn here.
-  nonnullLocalWithAssignmentInInitializer = 0;
+  nonnullLocalWithAssignmentInInitializer = 0; // expected-warning {{implicitly casting a null constant to non-nullable pointer type}}
   (void)nonnullLocalWithAssignmentInInitializer;
 
 }
@@ -192,7 +192,7 @@
 
   // Since we've already had an invariant violation along this path,
   // we shouldn't warn here.
-  nonnullLocalWithAssignment = 0;
+  nonnullLocalWithAssignment = 0; // expected-warning {{implicitly casting a null constant to non-nullable pointer type}}
   (void)nonnullLocalWithAssignment;
 }
 
Index: test/Analysis/nullability-no-arc.mm
===================================================================
--- test/Analysis/nullability-no-arc.mm
+++ test/Analysis/nullability-no-arc.mm
@@ -43,7 +43,7 @@
 }
 
 void testObjCNonARCExplicitZeroInitialization() {
-  TestObject * _Nonnull explicitlyZeroInitialized = nil; // expected-warning {{nil assigned to a pointer which is expected to have non-null value}}
+  TestObject * _Nonnull explicitlyZeroInitialized = nil; // expected-warning {{implicitly casting a null constant to non-nullable pointer type 'TestObject * _Nonnull'}}
 }
 
 @interface ClassWithInitializers : NSObject
Index: lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
===================================================================
--- lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
+++ lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
@@ -1023,30 +1023,17 @@
   // uninitialized in Sema's UninitializedValues analysis to warn when a use of
   // the zero-initialized definition will unexpectedly yield nil.
 
-  // Locals are only zero-initialized when automated reference counting
-  // is turned on.
-  if (!C.getASTContext().getLangOpts().ObjCAutoRefCount)
-    return false;
-
   auto *DS = dyn_cast<DeclStmt>(S);
   if (!DS || !DS->isSingleDecl())
     return false;
 
   auto *VD = dyn_cast<VarDecl>(DS->getSingleDecl());
   if (!VD)
     return false;
 
-  // Sema only zero-initializes locals with ObjCLifetimes.
-  if(!VD->getType().getQualifiers().hasObjCLifetime())
-    return false;
-
   const Expr *Init = VD->getInit();
   assert(Init && "ObjC local under ARC without initializer");
 
-  // Return false if the local is explicitly initialized (e.g., with '= nil').
-  if (!isa<ImplicitValueInitExpr>(Init))
-    return false;
-
   return true;
 }
 
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -7212,20 +7212,28 @@
 }
 
 /// Compute the nullability of a conditional expression.
-static QualType computeConditionalNullability(QualType ResTy, bool IsBin,
-                                              QualType LHSTy, QualType RHSTy,
+static QualType computeConditionalNullability(Sema &S, QualType ResTy,
+                                              bool IsBin, const Expr *LHSExpr,
+                                              const Expr *RHSExpr,
                                               ASTContext &Ctx) {
   if (!ResTy->isAnyPointerType())
     return ResTy;
 
-  auto GetNullability = [&Ctx](QualType Ty) {
+  auto GetNullability = [&S, &Ctx](QualType Ty, const Expr *E = nullptr) {
+    // If E evaluates to a null constant and doesn't have a non-null type,
+    // return nullable.
+    if (E && S.CheckNonNullExpr(E))
+      return NullabilityKind::Nullable;
+
     Optional<NullabilityKind> Kind = Ty->getNullability(Ctx);
     if (Kind)
       return *Kind;
     return NullabilityKind::Unspecified;
   };
 
-  auto LHSKind = GetNullability(LHSTy), RHSKind = GetNullability(RHSTy);
+  QualType LHSTy = LHSExpr->getType(), RHSTy = RHSExpr->getType();
+  auto LHSKind = GetNullability(LHSTy, LHSExpr);
+  auto RHSKind = GetNullability(RHSTy, RHSExpr);
   NullabilityKind MergedKind;
 
   // Compute nullability of a binary conditional expression.
@@ -7337,7 +7345,6 @@
     LHSExpr = CondExpr = opaqueValue;
   }
 
-  QualType LHSTy = LHSExpr->getType(), RHSTy = RHSExpr->getType();
   ExprValueKind VK = VK_RValue;
   ExprObjectKind OK = OK_Ordinary;
   ExprResult Cond = CondExpr, LHS = LHSExpr, RHS = RHSExpr;
@@ -7352,8 +7359,8 @@
 
   CheckBoolLikeConversion(Cond.get(), QuestionLoc);
 
-  result = computeConditionalNullability(result, commonExpr, LHSTy, RHSTy,
-                                         Context);
+  result = computeConditionalNullability(*this, result, commonExpr, LHSExpr,
+                                         RHSExpr, Context);
 
   if (!commonExpr)
     return new (Context)
@@ -11187,6 +11194,7 @@
     return QualType();
 
   CheckForNullPointerDereference(*this, LHSExpr);
+  checkNullConstantToNonNull(LHSExpr->getType(), RHS.get());
 
   // C99 6.5.16p3: The type of an assignment expression is the type of the
   // left operand unless the left operand has qualified type, in which case
Index: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp
+++ lib/Sema/SemaDecl.cpp
@@ -10773,6 +10773,8 @@
     Init = Result.getAs<Expr>();
   }
 
+  checkNullConstantToNonNull(DclT, Init);
+
   // Check for self-references within variable initializers.
   // Variables declared within a function/method body (except for references)
   // are handled by a dataflow analysis.
Index: lib/Sema/SemaChecking.cpp
===================================================================
--- lib/Sema/SemaChecking.cpp
+++ lib/Sema/SemaChecking.cpp
@@ -2971,10 +2971,10 @@
 /// Checks if a the given expression evaluates to null.
 ///
 /// Returns true if the value evaluates to null.
-static bool CheckNonNullExpr(Sema &S, const Expr *Expr) {
+bool Sema::CheckNonNullExpr(const Expr *Expr) const {
   // If the expression has non-null type, it doesn't evaluate to null.
   if (auto nullability
-        = Expr->IgnoreImplicit()->getType()->getNullability(S.Context)) {
+        = Expr->IgnoreImplicit()->getType()->getNullability(Context)) {
     if (*nullability == NullabilityKind::NonNull)
       return false;
   }
@@ -2992,14 +2992,14 @@
 
   bool Result;
   return (!Expr->isValueDependent() &&
-          Expr->EvaluateAsBooleanCondition(Result, S.Context) &&
+          Expr->EvaluateAsBooleanCondition(Result, Context) &&
           !Result);
 }
 
 static void CheckNonNullArgument(Sema &S,
                                  const Expr *ArgExpr,
                                  SourceLocation CallSiteLoc) {
-  if (CheckNonNullExpr(S, ArgExpr))
+  if (S.CheckNonNullExpr(ArgExpr))
     S.DiagRuntimeBehavior(CallSiteLoc, ArgExpr,
            S.PDiag(diag::warn_null_arg) << ArgExpr->getSourceRange());
 }
@@ -8772,7 +8772,7 @@
   // Check if the return value is null but should not be.
   if (((Attrs && hasSpecificAttr<ReturnsNonNullAttr>(*Attrs)) ||
        (!isObjCMethod && isNonNullType(Context, lhsType))) &&
-      CheckNonNullExpr(*this, RetValExp))
+      CheckNonNullExpr(RetValExp))
     Diag(ReturnLoc, diag::warn_null_ret)
       << (isObjCMethod ? 1 : 0) << RetValExp->getSourceRange();
 
@@ -8787,13 +8787,18 @@
       const FunctionProtoType *Proto
         = FD->getType()->castAs<FunctionProtoType>();
       if (!Proto->isNothrow(/*ResultIfDependent*/true) &&
-          CheckNonNullExpr(*this, RetValExp))
+          CheckNonNullExpr(RetValExp))
         Diag(ReturnLoc, diag::warn_operator_new_returns_null)
           << FD << getLangOpts().CPlusPlus11;
     }
   }
 }
 
+void Sema::checkNullConstantToNonNull(QualType DstTy, Expr *RHSExpr) {
+  if (isNonNullType(Context, DstTy) && CheckNonNullExpr(RHSExpr))
+    Diag(RHSExpr->getLocStart(), diag::warn_null_constant_to_nonnull) << DstTy;
+}
+
 //===--- CHECK: Floating-Point comparisons (-Wfloat-equal) ---------------===//
 
 /// Check for comparisons of floating point operands using != and ==.
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -10472,6 +10472,10 @@
 
   static bool GetFormatNSStringIdx(const FormatAttr *Format, unsigned &Idx);
 
+  /// Returns true if the expression evaluates to null and doesn't have a
+  /// non-null type.
+  bool CheckNonNullExpr(const Expr *E) const;
+
 private:
   bool CheckFormatArguments(const FormatAttr *Format,
                             ArrayRef<const Expr *> Args,
@@ -10507,6 +10511,9 @@
                           const AttrVec *Attrs = nullptr,
                           const FunctionDecl *FD = nullptr);
 
+  /// Check null constant to nonnull conversion.
+  void checkNullConstantToNonNull(QualType DstTy, Expr *RHSExpr);
+
 public:
   void CheckFloatComparison(SourceLocation Loc, Expr *LHS, Expr *RHS);
 
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -9156,6 +9156,10 @@
   "zero as null pointer constant">,
   InGroup<DiagGroup<"zero-as-null-pointer-constant">>, DefaultIgnore;
 
+def warn_null_constant_to_nonnull : Warning<
+  "implicitly casting a null constant to non-nullable pointer type %0">,
+  InGroup<NullConstantToNonnull>;
+
 def err_nullability_cs_multilevel : Error<
   "nullability keyword %0 cannot be applied to multi-level pointer type %1">;
 def note_nullability_type_specifier : Note<
Index: include/clang/Basic/DiagnosticGroups.td
===================================================================
--- include/clang/Basic/DiagnosticGroups.td
+++ include/clang/Basic/DiagnosticGroups.td
@@ -343,6 +343,7 @@
 def NullabilityDeclSpec : DiagGroup<"nullability-declspec">;
 def NullabilityInferredOnNestedType : DiagGroup<"nullability-inferred-on-nested-type">;
 def NullableToNonNullConversion : DiagGroup<"nullable-to-nonnull-conversion">;
+def NullConstantToNonnull : DiagGroup<"null-constant-to-nonnull">;
 def NullabilityCompletenessOnArrays : DiagGroup<"nullability-completeness-on-arrays">;
 def NullabilityCompleteness : DiagGroup<"nullability-completeness",
                                         [NullabilityCompletenessOnArrays]>;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to