]> granicus.if.org Git - clang/commitdiff
Implement the 'null_resettable' attribute for Objective-C properties.
authorDouglas Gregor <dgregor@apple.com>
Fri, 19 Jun 2015 18:14:46 +0000 (18:14 +0000)
committerDouglas Gregor <dgregor@apple.com>
Fri, 19 Jun 2015 18:14:46 +0000 (18:14 +0000)
'null_resettable' properties are those whose getters return nonnull
but whose setters take nil, to "reset" the property to some
default. Implements rdar://problem/19051334.

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

include/clang/AST/DeclObjC.h
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Sema/DeclSpec.h
include/clang/Sema/Sema.h
lib/AST/DeclPrinter.cpp
lib/Parse/ParseObjc.cpp
lib/Sema/SemaDeclObjC.cpp
lib/Sema/SemaObjCProperty.cpp
test/SemaObjC/nullability.m

index 86f1af1122a2eab905a2dc2ce31c55b6ff51ffff..c3fb57770245f2247683f31f2442eb70e3f8475b 100644 (file)
@@ -2206,13 +2206,14 @@ public:
     OBJC_PR_unsafe_unretained = 0x800,
     /// Indicates that the nullability of the type was spelled with a
     /// property attribute rather than a type qualifier.
-    OBJC_PR_nullability = 0x1000
+    OBJC_PR_nullability = 0x1000,
+    OBJC_PR_null_resettable = 0x2000
     // Adding a property should change NumPropertyAttrsBits
   };
 
   enum {
     /// \brief Number of bits fitting all the property attributes.
-    NumPropertyAttrsBits = 13
+    NumPropertyAttrsBits = 14
   };
 
   enum SetterKind { Assign, Retain, Copy, Weak };
index cc1a80ec63b3a336243a33b0b962ff364957f1a7..c86342b659fa3f1f4972e755dcea9206b03ae40a 100644 (file)
@@ -7714,6 +7714,10 @@ def note_nullability_type_specifier : Note<
   "'%select{__nonnull|__nullable|__null_unspecified}0' to affect the innermost "
   "pointer type of %1">;
 
+def warn_null_resettable_setter : Warning<
+  "synthesized setter %0 for null_resettable property %1 does not handle nil">,
+  InGroup<Nullability>;
+
 }
 
 } // end of sema component.
index 9fe0cc7952fb36d79b6399001ac5b21ddacadcf2..74332016a963d8f800294af82228dbc194842ef9 100644 (file)
@@ -805,7 +805,8 @@ public:
     DQ_PR_weak =   0x200,
     DQ_PR_strong = 0x400,
     DQ_PR_unsafe_unretained = 0x800,
-    DQ_PR_nullability = 0x1000
+    DQ_PR_nullability = 0x1000,
+    DQ_PR_null_resettable = 0x2000
   };
 
   ObjCDeclSpec()
@@ -865,7 +866,7 @@ private:
   ObjCDeclQualifier objcDeclQualifier : 7;
 
   // NOTE: VC++ treats enums as signed, avoid using ObjCPropertyAttributeKind
-  unsigned PropertyAttributes : 13;
+  unsigned PropertyAttributes : 14;
 
   unsigned Nullability : 2;
 
index 11933ec929dafd77e5dde1d9bbdbbcb2516b48d3..981f027239fb79e7d206fc1199efacc965443b9c 100644 (file)
@@ -2918,6 +2918,9 @@ public:
                                        ObjCContainerDecl *CDecl,
                                        bool SynthesizeProperties);
 
+  /// Diagnose any null-resettable synthesized setters.
+  void diagnoseNullResettableSynthesizedSetters(ObjCImplDecl *impDecl);
+
   /// DefaultSynthesizeProperties - This routine default synthesizes all
   /// properties which must be synthesized in the class's \@implementation.
   void DefaultSynthesizeProperties (Scope *S, ObjCImplDecl* IMPDecl,
index 609cc08545ac802cbc47dde70066502620434eb1..d6c03169d9cb1725ef4f574efbad2599c30c88a2 100644 (file)
@@ -1213,8 +1213,14 @@ void DeclPrinter::VisitObjCPropertyDecl(ObjCPropertyDecl *PDecl) {
     if (PDecl->getPropertyAttributes() &
         ObjCPropertyDecl::OBJC_PR_nullability) {
       if (auto nullability = stripOuterNullability(T)) {
-        Out << (first ? ' ' : ',') 
-            << getNullabilitySpelling(*nullability).substr(2);
+        if (*nullability == NullabilityKind::Unspecified &&
+            (PDecl->getPropertyAttributes() &
+               ObjCPropertyDecl::OBJC_PR_null_resettable)) {
+          Out << (first ? ' ' : ',') << "null_resettable";
+        } else {
+          Out << (first ? ' ' : ',')
+              << getNullabilitySpelling(*nullability).substr(2);
+        }
         first = false;
       }
     }
index e0f716e82f24f797eb18f8a615038efbbb70b89e..83236602df75667d35da85134924d35a71260c76 100644 (file)
@@ -611,6 +611,7 @@ static void diagnoseRedundantPropertyNullability(Parser &P,
 ///     nonnull
 ///     nullable
 ///     null_unspecified
+///     null_resettable
 ///
 void Parser::ParseObjCPropertyAttribute(ObjCDeclSpec &DS) {
   assert(Tok.getKind() == tok::l_paren);
@@ -717,6 +718,16 @@ void Parser::ParseObjCPropertyAttribute(ObjCDeclSpec &DS) {
                                              Tok.getLocation());
       DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_nullability);
       DS.setNullability(Tok.getLocation(), NullabilityKind::Unspecified);
+    } else if (II->isStr("null_resettable")) {
+      if (DS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nullability)
+        diagnoseRedundantPropertyNullability(*this, DS,
+                                             NullabilityKind::Unspecified,
+                                             Tok.getLocation());
+      DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_nullability);
+      DS.setNullability(Tok.getLocation(), NullabilityKind::Unspecified);
+
+      // Also set the null_resettable bit.
+      DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_null_resettable);
     } else {
       Diag(AttrName, diag::err_objc_expected_property_attr) << II;
       SkipUntil(tok::r_paren, StopAtSemi);
index 78c76abd4c714d60f350070cd5c108a3be1ff1c2..62ba7d5633c01ef28c78fecb9d67530881909965 100644 (file)
@@ -2024,6 +2024,9 @@ void Sema::ImplMethodsVsClassMethods(Scope *S, ObjCImplDecl* IMPDecl,
     DiagnoseUnimplementedProperties(S, IMPDecl, CDecl, SynthesizeProperties);
   }
 
+  // Diagnose null-resettable synthesized setters.
+  diagnoseNullResettableSynthesizedSetters(IMPDecl);
+
   SelectorSet ClsMap;
   for (const auto *I : IMPDecl->class_methods())
     ClsMap.insert(I->getSelector());
index f62af8eb81d544e3b1c5eaae3f0f6e56dcaed8b0..d8e7f64836f171d3d1bd70b774363609c861a70a 100644 (file)
@@ -361,6 +361,9 @@ Sema::HandlePropertyInClassExtension(Scope *S,
     PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_atomic);
   if (Attributes & ObjCDeclSpec::DQ_PR_nullability)
     PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_nullability);
+  if (Attributes & ObjCDeclSpec::DQ_PR_null_resettable)
+    PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_null_resettable);
+
   // Set setter/getter selector name. Needed later.
   PDecl->setGetterName(GetterSel);
   PDecl->setSetterName(SetterSel);
@@ -646,6 +649,9 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S,
   if (Attributes & ObjCDeclSpec::DQ_PR_nullability)
     PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_nullability);
 
+  if (Attributes & ObjCDeclSpec::DQ_PR_null_resettable)
+    PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_null_resettable);
+
   return PDecl;
 }
 
@@ -1760,6 +1766,33 @@ void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl,
   }
 }
 
+void Sema::diagnoseNullResettableSynthesizedSetters(ObjCImplDecl *impDecl) {
+  for (const auto *propertyImpl : impDecl->property_impls()) {
+    const auto *property = propertyImpl->getPropertyDecl();
+
+    // Warn about null_resettable properties with synthesized setters,
+    // because the setter won't properly handle nil.
+    if (propertyImpl->getPropertyImplementation()
+          == ObjCPropertyImplDecl::Synthesize &&
+        (property->getPropertyAttributes() &
+         ObjCPropertyDecl::OBJC_PR_null_resettable) &&
+        property->getGetterMethodDecl() &&
+        property->getSetterMethodDecl()) {
+      auto *getterMethod = property->getGetterMethodDecl();
+      auto *setterMethod = property->getSetterMethodDecl();
+      if (!impDecl->getInstanceMethod(setterMethod->getSelector()) &&
+          !impDecl->getInstanceMethod(getterMethod->getSelector())) {
+        SourceLocation loc = propertyImpl->getLocation();
+        if (loc.isInvalid())
+          loc = impDecl->getLocStart();
+
+        Diag(loc, diag::warn_null_resettable_setter)
+          << setterMethod->getSelector() << property->getDeclName();
+      }
+    }
+  }
+}
+
 void
 Sema::AtomicPropertySetterGetterRules (ObjCImplDecl* IMPDecl,
                                        ObjCContainerDecl* IDecl) {
@@ -1995,9 +2028,21 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property,
       redeclaredProperty->getLocation() :
       property->getLocation();
 
+    // If the property is null_resettable, the getter returns nonnull.
+    QualType resultTy = property->getType();
+    if (property->getPropertyAttributes() &
+        ObjCPropertyDecl::OBJC_PR_null_resettable) {
+      QualType modifiedTy = resultTy;
+      if (auto nullability = AttributedType::stripOuterNullability(modifiedTy)){
+        if (*nullability == NullabilityKind::Unspecified)
+          resultTy = Context.getAttributedType(AttributedType::attr_nonnull,
+                                               modifiedTy, modifiedTy);
+      }
+    }
+
     GetterMethod = ObjCMethodDecl::Create(Context, Loc, Loc,
                              property->getGetterName(),
-                             property->getType(), nullptr, CD,
+                             resultTy, nullptr, CD,
                              /*isInstance=*/true, /*isVariadic=*/false,
                              /*isPropertyAccessor=*/true,
                              /*isImplicitlyDeclared=*/true, /*isDefined=*/false,
@@ -2058,12 +2103,25 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property,
                                 ObjCMethodDecl::Optional :
                                 ObjCMethodDecl::Required);
 
+      // If the property is null_resettable, the setter accepts a
+      // nullable value.
+      QualType paramTy = property->getType().getUnqualifiedType();
+      if (property->getPropertyAttributes() &
+          ObjCPropertyDecl::OBJC_PR_null_resettable) {
+        QualType modifiedTy = paramTy;
+        if (auto nullability = AttributedType::stripOuterNullability(modifiedTy)){
+          if (*nullability == NullabilityKind::Unspecified)
+            paramTy = Context.getAttributedType(AttributedType::attr_nullable,
+                                                modifiedTy, modifiedTy);
+        }
+      }
+
       // Invent the arguments for the setter. We don't bother making a
       // nice name for the argument.
       ParmVarDecl *Argument = ParmVarDecl::Create(Context, SetterMethod,
                                                   Loc, Loc,
                                                   property->getIdentifier(),
-                                    property->getType().getUnqualifiedType(),
+                                                  paramTy,
                                                   /*TInfo=*/nullptr,
                                                   SC_None,
                                                   nullptr);
index b852584758cee9e8e0d8ad574e3f9d23ea771dd3..ca875b56a289a7f7a2646eac81716e700bd992ae 100644 (file)
@@ -164,6 +164,45 @@ void test_instancetype(InitializableClass * __nonnull ic, id __nonnull object) {
   ip = [InitializableClass returnInstanceOfMe]; // expected-warning{{incompatible pointer types assigning to 'int *' from 'InitializableClass * __nullable'}}
   ip = [object returnMe]; // expected-warning{{incompatible pointer types assigning to 'int *' from 'id __nullable'}}
 }
+
+// Check null_resettable getters/setters.
+__attribute__((objc_root_class))
+@interface NSResettable
+@property(null_resettable,retain) NSResettable *resettable1; // expected-note{{passing argument to parameter 'resettable1' here}}
+@property(null_resettable,retain,nonatomic) NSResettable *resettable2;
+@property(null_resettable,retain,nonatomic) NSResettable *resettable3;
+@property(null_resettable,retain,nonatomic) NSResettable *resettable4;
+@property(null_resettable,retain,nonatomic) NSResettable *resettable5;
+@property(null_resettable,retain,nonatomic) NSResettable *resettable6;
+@end
+
+void test_null_resettable(NSResettable *r, int *ip) {
+  [r setResettable1:ip]; // expected-warning{{incompatible pointer types sending 'int *' to parameter of type 'NSResettable * __nullable'}}
+  r.resettable1 = ip; // expected-warning{{incompatible pointer types assigning to 'NSResettable * __nullable' from 'int *'}}
+}
+
+@implementation NSResettable // expected-warning{{synthesized setter 'setResettable4:' for null_resettable property 'resettable4' does not handle nil}}
+- (NSResettable *)resettable1 {
+  int *ip = 0;
+  return ip; // expected-warning{{result type 'NSResettable * __nonnull'}}
+}
+
+- (void)setResettable1:(NSResettable *)param {
+}
+
+@synthesize resettable2; // no warning; not synthesized
+@synthesize resettable3; // expected-warning{{synthesized setter 'setResettable3:' for null_resettable property 'resettable3' does not handle nil}}
+
+- (void)setResettable2:(NSResettable *)param {
+}
+
+@dynamic resettable5;
+
+- (NSResettable *)resettable6 {
+  return 0; // no warning
+}
+@end
+
 // rdar://problem/19814852
 @interface MultiProp
 @property (nullable, copy) id a, b, c;