diff options
-rw-r--r-- | include/clang/AST/ASTMutationListener.h | 12 | ||||
-rw-r--r-- | include/clang/AST/DeclObjC.h | 5 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 3 | ||||
-rw-r--r-- | include/clang/Sema/Sema.h | 2 | ||||
-rw-r--r-- | include/clang/Serialization/ASTWriter.h | 3 | ||||
-rw-r--r-- | lib/ARCMigrate/TransProperties.cpp | 26 | ||||
-rw-r--r-- | lib/AST/DeclObjC.cpp | 76 | ||||
-rw-r--r-- | lib/CodeGen/CGDebugInfo.cpp | 7 | ||||
-rw-r--r-- | lib/Frontend/MultiplexConsumer.cpp | 10 | ||||
-rw-r--r-- | lib/Sema/SemaDeclObjC.cpp | 14 | ||||
-rw-r--r-- | lib/Sema/SemaExprObjC.cpp | 3 | ||||
-rw-r--r-- | lib/Sema/SemaObjCProperty.cpp | 312 | ||||
-rw-r--r-- | lib/Serialization/ASTWriter.cpp | 15 | ||||
-rw-r--r-- | test/FixIt/atomic-property.m | 6 | ||||
-rw-r--r-- | test/Index/complete-kvc.m | 2 | ||||
-rw-r--r-- | test/Modules/ModuleDebugInfo.m | 2 | ||||
-rw-r--r-- | test/PCH/chain-categories.m | 13 | ||||
-rw-r--r-- | test/SemaObjC/atomoic-property-synnthesis-rules.m | 24 | ||||
-rw-r--r-- | test/SemaObjC/property-3.m | 2 | ||||
-rw-r--r-- | test/SemaObjC/property-in-class-extension-1.m | 6 | ||||
-rw-r--r-- | test/SemaObjCXX/property-invalid-type.mm | 4 |
21 files changed, 268 insertions, 279 deletions
diff --git a/include/clang/AST/ASTMutationListener.h b/include/clang/AST/ASTMutationListener.h index f4026e9526..3ff392de11 100644 --- a/include/clang/AST/ASTMutationListener.h +++ b/include/clang/AST/ASTMutationListener.h @@ -92,18 +92,6 @@ public: virtual void AddedObjCCategoryToInterface(const ObjCCategoryDecl *CatD, const ObjCInterfaceDecl *IFD) {} - /// \brief A objc class extension redeclared or introduced a property. - /// - /// \param Prop the property in the class extension - /// - /// \param OrigProp the property from the original interface that was declared - /// or null if the property was introduced. - /// - /// \param ClassExt the class extension. - virtual void AddedObjCPropertyInClassExtension(const ObjCPropertyDecl *Prop, - const ObjCPropertyDecl *OrigProp, - const ObjCCategoryDecl *ClassExt) {} - /// \brief A declaration is marked used which was not previously marked used. /// /// \param D the declaration marked used diff --git a/include/clang/AST/DeclObjC.h b/include/clang/AST/DeclObjC.h index 96563ee688..833683fae2 100644 --- a/include/clang/AST/DeclObjC.h +++ b/include/clang/AST/DeclObjC.h @@ -2524,11 +2524,6 @@ public: PropertyAttributesAsWritten = PRVal; } - void makeitReadWriteAttribute() { - PropertyAttributes &= ~OBJC_PR_readonly; - PropertyAttributes |= OBJC_PR_readwrite; - } - // Helper methods for accessing attributes. /// isReadOnly - Return true iff the property has a setter. diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 97910798f1..542ccbef76 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -884,7 +884,8 @@ def err_property_type : Error<"property cannot have array or function type %0">; def error_missing_property_context : Error< "missing context for property implementation declaration">; def error_bad_property_decl : Error< - "property implementation must have its declaration in interface %0">; + "property implementation must have its declaration in interface %0 or one of " + "its extensions">; def error_category_property : Error< "property declared in category %0 cannot be implemented in " "class implementation">; diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 89964ff3a4..c97d2cd790 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -3047,7 +3047,7 @@ public: /// warning) when atomic property has one but not the other user-declared /// setter or getter. void AtomicPropertySetterGetterRules(ObjCImplDecl* IMPDecl, - ObjCContainerDecl* IDecl); + ObjCInterfaceDecl* IDecl); void DiagnoseOwningPropertyGetterSynthesis(const ObjCImplementationDecl *D); diff --git a/include/clang/Serialization/ASTWriter.h b/include/clang/Serialization/ASTWriter.h index 700f44cd61..b00b7d2d06 100644 --- a/include/clang/Serialization/ASTWriter.h +++ b/include/clang/Serialization/ASTWriter.h @@ -876,9 +876,6 @@ public: void FunctionDefinitionInstantiated(const FunctionDecl *D) override; void AddedObjCCategoryToInterface(const ObjCCategoryDecl *CatD, const ObjCInterfaceDecl *IFD) override; - void AddedObjCPropertyInClassExtension(const ObjCPropertyDecl *Prop, - const ObjCPropertyDecl *OrigProp, - const ObjCCategoryDecl *ClassExt) override; void DeclarationMarkedUsed(const Decl *D) override; void DeclarationMarkedOpenMPThreadPrivate(const Decl *D) override; void RedefinedHiddenDefinition(const NamedDecl *D, Module *M) override; diff --git a/lib/ARCMigrate/TransProperties.cpp b/lib/ARCMigrate/TransProperties.cpp index ab128844b4..8667bc2a37 100644 --- a/lib/ARCMigrate/TransProperties.cpp +++ b/lib/ARCMigrate/TransProperties.cpp @@ -96,6 +96,10 @@ public: collectProperties(iface, AtProps); + // Look through extensions. + for (auto *Ext : iface->visible_extensions()) + collectProperties(Ext, AtProps); + typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl> prop_impl_iterator; for (prop_impl_iterator @@ -137,19 +141,6 @@ public: Transaction Trans(Pass.TA); rewriteProperty(props, atLoc); } - - AtPropDeclsTy AtExtProps; - // Look through extensions. - for (auto *Ext : iface->visible_extensions()) - collectProperties(Ext, AtExtProps, &AtProps); - - for (AtPropDeclsTy::iterator - I = AtExtProps.begin(), E = AtExtProps.end(); I != E; ++I) { - SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first); - PropsTy &props = I->second; - Transaction Trans(Pass.TA); - doActionForExtensionProp(props, atLoc); - } } private: @@ -177,15 +168,6 @@ private: } } - void doActionForExtensionProp(PropsTy &props, SourceLocation atLoc) { - llvm::DenseMap<IdentifierInfo *, PropActionKind>::iterator I; - I = ActionOnProp.find(props[0].PropD->getIdentifier()); - if (I == ActionOnProp.end()) - return; - - doPropAction(I->second, props, atLoc, false); - } - void rewriteProperty(PropsTy &props, SourceLocation atLoc) { ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props); diff --git a/lib/AST/DeclObjC.cpp b/lib/AST/DeclObjC.cpp index 280c412ae8..b5dc9e122c 100644 --- a/lib/AST/DeclObjC.cpp +++ b/lib/AST/DeclObjC.cpp @@ -161,6 +161,15 @@ ObjCPropertyDecl::findPropertyDecl(const DeclContext *DC, return nullptr; } + // If context is class, then lookup property in its extensions. + // This comes before property is looked up in primary class. + if (auto *IDecl = dyn_cast<ObjCInterfaceDecl>(DC)) { + for (const auto *Ext : IDecl->known_extensions()) + if (ObjCPropertyDecl *PD = ObjCPropertyDecl::findPropertyDecl(Ext, + propertyID)) + return PD; + } + DeclContext::lookup_result R = DC->lookup(propertyID); for (DeclContext::lookup_iterator I = R.begin(), E = R.end(); I != E; ++I) @@ -190,6 +199,15 @@ ObjCPropertyDecl *ObjCContainerDecl::FindPropertyDeclaration( if (Def->isHidden()) return nullptr; } + + // Search the extensions of a class first; they override what's in + // the class itself. + if (const auto *ClassDecl = dyn_cast<ObjCInterfaceDecl>(this)) { + for (const auto *Ext : ClassDecl->visible_extensions()) { + if (auto *P = Ext->FindPropertyDeclaration(PropertyId)) + return P; + } + } if (ObjCPropertyDecl *PD = ObjCPropertyDecl::findPropertyDecl(cast<DeclContext>(this), PropertyId)) @@ -207,7 +225,7 @@ ObjCPropertyDecl *ObjCContainerDecl::FindPropertyDeclaration( } case Decl::ObjCInterface: { const ObjCInterfaceDecl *OID = cast<ObjCInterfaceDecl>(this); - // Look through categories (but not extensions). + // Look through categories (but not extensions; they were handled above). for (const auto *Cat : OID->visible_categories()) { if (!Cat->IsClassExtension()) if (ObjCPropertyDecl *P = Cat->FindPropertyDeclaration(PropertyId)) @@ -327,6 +345,13 @@ void ObjCInterfaceDecl::collectPropertiesToImplement(PropertyMap &PM, PM[Prop->getIdentifier()] = Prop; PO.push_back(Prop); } + for (const auto *Ext : known_extensions()) { + const ObjCCategoryDecl *ClassExt = Ext; + for (auto *Prop : ClassExt->properties()) { + PM[Prop->getIdentifier()] = Prop; + PO.push_back(Prop); + } + } for (const auto *PI : all_referenced_protocols()) PI->collectPropertiesToImplement(PM, PO); // Note, the properties declared only in class extensions are still copied @@ -1182,18 +1207,47 @@ ObjCMethodDecl::findPropertyDecl(bool CheckOverrides) const { if (isPropertyAccessor()) { const ObjCContainerDecl *Container = cast<ObjCContainerDecl>(getParent()); - // If container is class extension, find its primary class. - if (const ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(Container)) - if (CatDecl->IsClassExtension()) - Container = CatDecl->getClassInterface(); - bool IsGetter = (NumArgs == 0); - for (const auto *I : Container->properties()) { - Selector NextSel = IsGetter ? I->getGetterName() - : I->getSetterName(); - if (NextSel == Sel) - return I; + /// Local function that attempts to find a matching property within the + /// given Objective-C container. + auto findMatchingProperty = + [&](const ObjCContainerDecl *Container) -> const ObjCPropertyDecl * { + + for (const auto *I : Container->properties()) { + Selector NextSel = IsGetter ? I->getGetterName() + : I->getSetterName(); + if (NextSel == Sel) + return I; + } + + return nullptr; + }; + + // Look in the container we were given. + if (const auto *Found = findMatchingProperty(Container)) + return Found; + + // If we're in a category or extension, look in the main class. + const ObjCInterfaceDecl *ClassDecl = nullptr; + if (const auto *Category = dyn_cast<ObjCCategoryDecl>(Container)) { + ClassDecl = Category->getClassInterface(); + if (const auto *Found = findMatchingProperty(ClassDecl)) + return Found; + } else { + // Determine whether the container is a class. + ClassDecl = dyn_cast<ObjCInterfaceDecl>(Container); + } + + // If we have a class, check its visible extensions. + if (ClassDecl) { + for (const auto *Ext : ClassDecl->visible_extensions()) { + if (Ext == Container) + continue; + + if (const auto *Found = findMatchingProperty(Ext)) + return Found; + } } llvm_unreachable("Marked as a property accessor but no property found!"); diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index 96649c6706..7efd1ecba9 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -216,6 +216,13 @@ StringRef CGDebugInfo::getObjCMethodName(const ObjCMethodDecl *OMD) { } else if (const ObjCInterfaceDecl *OID = dyn_cast<const ObjCInterfaceDecl>(DC)) { OS << OID->getName(); + } else if (const ObjCCategoryDecl *OC = dyn_cast<ObjCCategoryDecl>(DC)) { + if (OC->IsClassExtension()) { + OS << OC->getClassInterface()->getName(); + } else { + OS << ((const NamedDecl *)OC)->getIdentifier()->getNameStart() << '(' + << OC->getIdentifier()->getNameStart() << ')'; + } } else if (const ObjCCategoryImplDecl *OCD = dyn_cast<const ObjCCategoryImplDecl>(DC)) { OS << ((const NamedDecl *)OCD)->getIdentifier()->getNameStart() << '(' diff --git a/lib/Frontend/MultiplexConsumer.cpp b/lib/Frontend/MultiplexConsumer.cpp index 91ee100f63..12c85240bd 100644 --- a/lib/Frontend/MultiplexConsumer.cpp +++ b/lib/Frontend/MultiplexConsumer.cpp @@ -122,9 +122,6 @@ public: void AddedObjCCategoryToInterface(const ObjCCategoryDecl *CatD, const ObjCInterfaceDecl *IFD) override; void FunctionDefinitionInstantiated(const FunctionDecl *D) override; - void AddedObjCPropertyInClassExtension(const ObjCPropertyDecl *Prop, - const ObjCPropertyDecl *OrigProp, - const ObjCCategoryDecl *ClassExt) override; void DeclarationMarkedUsed(const Decl *D) override; void DeclarationMarkedOpenMPThreadPrivate(const Decl *D) override; void RedefinedHiddenDefinition(const NamedDecl *D, Module *M) override; @@ -207,13 +204,6 @@ void MultiplexASTMutationListener::FunctionDefinitionInstantiated( for (auto &Listener : Listeners) Listener->FunctionDefinitionInstantiated(D); } -void MultiplexASTMutationListener::AddedObjCPropertyInClassExtension( - const ObjCPropertyDecl *Prop, - const ObjCPropertyDecl *OrigProp, - const ObjCCategoryDecl *ClassExt) { - for (size_t i = 0, e = Listeners.size(); i != e; ++i) - Listeners[i]->AddedObjCPropertyInClassExtension(Prop, OrigProp, ClassExt); -} void MultiplexASTMutationListener::DeclarationMarkedUsed(const Decl *D) { for (size_t i = 0, e = Listeners.size(); i != e; ++i) Listeners[i]->DeclarationMarkedUsed(D); diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index 90c829e740..2622e5ed83 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -2844,6 +2844,20 @@ void Sema::ImplMethodsVsClassMethods(Scope *S, ObjCImplDecl* IMPDecl, for (const auto *I : IMPDecl->instance_methods()) InsMap.insert(I->getSelector()); + // Add the selectors for getters/setters of @dynamic properties. + for (const auto *PImpl : IMPDecl->property_impls()) { + // We only care about @dynamic implementations. + if (PImpl->getPropertyImplementation() != ObjCPropertyImplDecl::Dynamic) + continue; + + const auto *P = PImpl->getPropertyDecl(); + if (!P) continue; + + InsMap.insert(P->getGetterName()); + if (!P->getSetterName().isNull()) + InsMap.insert(P->getSetterName()); + } + // Check and see if properties declared in the interface have either 1) // an implementation or 2) there is a @synthesize/@dynamic implementation // of the property in the @implementation. diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index 227b322adb..3257741429 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/lib/Sema/SemaExprObjC.cpp @@ -1779,8 +1779,7 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, diag::err_property_not_found_forward_class, MemberName, BaseRange)) return ExprError(); - - // Search for a declared property first. + if (ObjCPropertyDecl *PD = IFace->FindPropertyDeclaration(Member)) { // Check whether we can reference this property. if (DiagnoseUseOfDecl(PD, MemberLoc)) diff --git a/lib/Sema/SemaObjCProperty.cpp b/lib/Sema/SemaObjCProperty.cpp index bd99143d4c..1d5d8b9489 100644 --- a/lib/Sema/SemaObjCProperty.cpp +++ b/lib/Sema/SemaObjCProperty.cpp @@ -262,8 +262,12 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc, } } } else if (ObjCCategoryDecl *Cat = dyn_cast<ObjCCategoryDecl>(ClassDecl)) { - for (auto *P : Cat->protocols()) - CheckPropertyAgainstProtocol(*this, Res, P, KnownProtos); + // We don't check if class extension. Because properties in class extension + // are meant to override some of the attributes and checking has already done + // when property in class extension is constructed. + if (!Cat->IsClassExtension()) + for (auto *P : Cat->protocols()) + CheckPropertyAgainstProtocol(*this, Res, P, KnownProtos); } else { ObjCProtocolDecl *Proto = cast<ObjCProtocolDecl>(ClassDecl); for (auto *P : Proto->protocols()) @@ -355,46 +359,6 @@ Sema::HandlePropertyInClassExtension(Scope *S, IdentifierInfo *PropertyId = FD.D.getIdentifier(); ObjCInterfaceDecl *CCPrimary = CDecl->getClassInterface(); - if (CCPrimary) { - // Check for duplicate declaration of this property in current and - // other class extensions. - for (const auto *Ext : CCPrimary->known_extensions()) { - if (ObjCPropertyDecl *prevDecl - = ObjCPropertyDecl::findPropertyDecl(Ext, PropertyId)) { - Diag(AtLoc, diag::err_duplicate_property); - Diag(prevDecl->getLocation(), diag::note_property_declare); - return nullptr; - } - } - } - - // Create a new ObjCPropertyDecl with the DeclContext being - // the class extension. - // FIXME. We should really be using CreatePropertyDecl for this. - ObjCPropertyDecl *PDecl = - ObjCPropertyDecl::Create(Context, DC, FD.D.getIdentifierLoc(), - PropertyId, AtLoc, LParenLoc, T, TSI); - PDecl->setPropertyAttributesAsWritten( - makePropertyAttributesAsWritten(AttributesAsWritten)); - if (Attributes & ObjCDeclSpec::DQ_PR_readonly) - PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_readonly); - if (Attributes & ObjCDeclSpec::DQ_PR_readwrite) - PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_readwrite); - if (Attributes & ObjCDeclSpec::DQ_PR_nonatomic) - PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_nonatomic); - if (Attributes & ObjCDeclSpec::DQ_PR_atomic) - 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); - ProcessDeclAttributes(S, PDecl, FD.D); - DC->addDecl(PDecl); - // We need to look in the @interface to see if the @property was // already declared. if (!CCPrimary) { @@ -403,32 +367,35 @@ Sema::HandlePropertyInClassExtension(Scope *S, return nullptr; } - // Find the property in continuation class's primary class only. + // Find the property in the extended class's primary class or + // extensions. ObjCPropertyDecl *PIDecl = CCPrimary->FindPropertyVisibleInPrimaryClass(PropertyId); + // If we found a property in an extension, complain. + if (PIDecl && isa<ObjCCategoryDecl>(PIDecl->getDeclContext())) { + Diag(AtLoc, diag::err_duplicate_property); + Diag(PIDecl->getLocation(), diag::note_property_declare); + return nullptr; + } + + // Create a new ObjCPropertyDecl with the DeclContext being + // the class extension. + ObjCPropertyDecl *PDecl = CreatePropertyDecl(S, CDecl, AtLoc, LParenLoc, + FD, GetterSel, SetterSel, + isAssign, isReadWrite, + Attributes, AttributesAsWritten, + T, TSI, MethodImplKind, DC); + + // If there was no declaration of a property with the same name in + // the primary class, we're done. if (!PIDecl) { - // No matching property found in the primary class. Just fall thru - // and add property to continuation class's primary class. - ObjCPropertyDecl *PrimaryPDecl = - CreatePropertyDecl(S, CCPrimary, AtLoc, LParenLoc, - FD, GetterSel, SetterSel, isAssign, isReadWrite, - Attributes,AttributesAsWritten, T, TSI, MethodImplKind, - DC); - - // A case of continuation class adding a new property in the class. This - // is not what it was meant for. However, gcc supports it and so should we. - // Make sure setter/getters are declared here. - ProcessPropertyDecl(PrimaryPDecl, CCPrimary, + ProcessPropertyDecl(PDecl, CDecl, /* redeclaredProperty = */ nullptr, /* lexicalDC = */ CDecl); - PDecl->setGetterMethodDecl(PrimaryPDecl->getGetterMethodDecl()); - PDecl->setSetterMethodDecl(PrimaryPDecl->getSetterMethodDecl()); - if (ASTMutationListener *L = Context.getASTMutationListener()) - L->AddedObjCPropertyInClassExtension(PrimaryPDecl, /*OrigProp=*/nullptr, - CDecl); - return PrimaryPDecl; + return PDecl; } + if (!Context.hasSameType(PIDecl->getType(), PDecl->getType())) { bool IncompatibleObjC = false; QualType ConvertedType; @@ -451,82 +418,12 @@ Sema::HandlePropertyInClassExtension(Scope *S, return nullptr; } } - - // The property 'PIDecl's readonly attribute will be over-ridden - // with continuation class's readwrite property attribute! + + // A readonly property declared in the primary class can be refined + // by adding a rewrite property within an extension. + // Anything else is an error. unsigned PIkind = PIDecl->getPropertyAttributesAsWritten(); - if (isReadWrite && (PIkind & ObjCPropertyDecl::OBJC_PR_readonly)) { - PIkind &= ~ObjCPropertyDecl::OBJC_PR_readonly; - PIkind |= ObjCPropertyDecl::OBJC_PR_readwrite; - PIkind |= deducePropertyOwnershipFromType(*this, PIDecl->getType()); - unsigned ClassExtensionMemoryModel = getOwnershipRule(Attributes); - unsigned PrimaryClassMemoryModel = getOwnershipRule(PIkind); - if (PrimaryClassMemoryModel && ClassExtensionMemoryModel && - (PrimaryClassMemoryModel != ClassExtensionMemoryModel)) { - Diag(AtLoc, diag::warn_property_attr_mismatch); - Diag(PIDecl->getLocation(), diag::note_property_declare); - } - else if (getLangOpts().ObjCAutoRefCount) { - QualType PrimaryPropertyQT = - Context.getCanonicalType(PIDecl->getType()).getUnqualifiedType(); - if (isa<ObjCObjectPointerType>(PrimaryPropertyQT)) { - bool PropertyIsWeak = ((PIkind & ObjCPropertyDecl::OBJC_PR_weak) != 0); - Qualifiers::ObjCLifetime PrimaryPropertyLifeTime = - PrimaryPropertyQT.getObjCLifetime(); - if (PrimaryPropertyLifeTime == Qualifiers::OCL_None && - (Attributes & ObjCDeclSpec::DQ_PR_weak) && - !PropertyIsWeak) { - Diag(AtLoc, diag::warn_property_implicitly_mismatched); - Diag(PIDecl->getLocation(), diag::note_property_declare); - } - } - } - - DeclContext *DC = cast<DeclContext>(CCPrimary); - if (!ObjCPropertyDecl::findPropertyDecl(DC, - PIDecl->getDeclName().getAsIdentifierInfo())) { - // In mrr mode, 'readwrite' property must have an explicit - // memory attribute. If none specified, select the default (assign). - if (!getLangOpts().ObjCAutoRefCount) { - if (!(PIkind & (ObjCDeclSpec::DQ_PR_assign | - ObjCDeclSpec::DQ_PR_retain | - ObjCDeclSpec::DQ_PR_strong | - ObjCDeclSpec::DQ_PR_copy | - ObjCDeclSpec::DQ_PR_unsafe_unretained | - ObjCDeclSpec::DQ_PR_weak))) - PIkind |= ObjCPropertyDecl::OBJC_PR_assign; - } - - // Protocol is not in the primary class. Must build one for it. - ObjCDeclSpec ProtocolPropertyODS; - // FIXME. Assuming that ObjCDeclSpec::ObjCPropertyAttributeKind - // and ObjCPropertyDecl::PropertyAttributeKind have identical - // values. Should consolidate both into one enum type. - ProtocolPropertyODS. - setPropertyAttributes((ObjCDeclSpec::ObjCPropertyAttributeKind) - PIkind); - // Must re-establish the context from class extension to primary - // class context. - ContextRAII SavedContext(*this, CCPrimary); - - Decl *ProtocolPtrTy = - ActOnProperty(S, AtLoc, LParenLoc, FD, ProtocolPropertyODS, - PIDecl->getGetterName(), - PIDecl->getSetterName(), - isOverridingProperty, - MethodImplKind, - /* lexicalDC = */ CDecl); - PIDecl = cast<ObjCPropertyDecl>(ProtocolPtrTy); - } - PIDecl->makeitReadWriteAttribute(); - if (Attributes & ObjCDeclSpec::DQ_PR_retain) - PIDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_retain); - if (Attributes & ObjCDeclSpec::DQ_PR_strong) - PIDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_strong); - if (Attributes & ObjCDeclSpec::DQ_PR_copy) - PIDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_copy); - PIDecl->setSetterName(SetterSel); - } else { + if (!(isReadWrite && (PIkind & ObjCPropertyDecl::OBJC_PR_readonly))) { // Tailor the diagnostics for the common case where a readwrite // property is declared both in the @interface and the continuation. // This is a common error where the user often intended the original @@ -541,13 +438,53 @@ Sema::HandlePropertyInClassExtension(Scope *S, Diag(PIDecl->getLocation(), diag::note_property_declare); return nullptr; } + + PIkind &= ~ObjCPropertyDecl::OBJC_PR_readonly; + PIkind |= ObjCPropertyDecl::OBJC_PR_readwrite; + PIkind |= deducePropertyOwnershipFromType(*this, PIDecl->getType()); + unsigned ClassExtensionMemoryModel = getOwnershipRule(Attributes); + unsigned PrimaryClassMemoryModel = getOwnershipRule(PIkind); + if (PrimaryClassMemoryModel && ClassExtensionMemoryModel && + (PrimaryClassMemoryModel != ClassExtensionMemoryModel)) { + Diag(AtLoc, diag::warn_property_attr_mismatch); + Diag(PIDecl->getLocation(), diag::note_property_declare); + } else if (getLangOpts().ObjCAutoRefCount) { + QualType PrimaryPropertyQT = + Context.getCanonicalType(PIDecl->getType()).getUnqualifiedType(); + if (isa<ObjCObjectPointerType>(PrimaryPropertyQT)) { + bool PropertyIsWeak = ((PIkind & ObjCPropertyDecl::OBJC_PR_weak) != 0); + Qualifiers::ObjCLifetime PrimaryPropertyLifeTime = + PrimaryPropertyQT.getObjCLifetime(); + if (PrimaryPropertyLifeTime == Qualifiers::OCL_None && + (Attributes & ObjCDeclSpec::DQ_PR_weak) && + !PropertyIsWeak) { + Diag(AtLoc, diag::warn_property_implicitly_mismatched); + Diag(PIDecl->getLocation(), diag::note_property_declare); + } + } + } + + // Check that atomicity of property in class extension matches the previous + // declaration. + unsigned PDeclAtomicity = + PDecl->getPropertyAttributes() & (ObjCDeclSpec::DQ_PR_atomic | ObjCDeclSpec::DQ_PR_nonatomic); + unsigned PIDeclAtomicity = + PIDecl->getPropertyAttributes() & (ObjCDeclSpec::DQ_PR_atomic | ObjCDeclSpec::DQ_PR_nonatomic); + if (PDeclAtomicity != PIDeclAtomicity) { + bool PDeclAtomic = (!PDeclAtomicity || PDeclAtomicity & ObjCDeclSpec::DQ_PR_atomic); + bool PIDeclAtomic = (!PIDeclAtomicity || PIDeclAtomicity & ObjCDeclSpec::DQ_PR_atomic); + if (PDeclAtomic != PIDeclAtomic) { + Diag(PDecl->getLocation(), diag::warn_property_attribute) + << PDecl->getDeclName() << "atomic" + << cast<ObjCContainerDecl>(PIDecl->getDeclContext())->getName(); + Diag(PIDecl->getLocation(), diag::note_property_declare); + } + } + *isOverridingProperty = true; - // Make sure setter decl is synthesized, and added to primary class's list. - ProcessPropertyDecl(PIDecl, CCPrimary, PDecl, CDecl); - PDecl->setGetterMethodDecl(PIDecl->getGetterMethodDecl()); - PDecl->setSetterMethodDecl(PIDecl->getSetterMethodDecl()); - if (ASTMutationListener *L = Context.getASTMutationListener()) - L->AddedObjCPropertyInClassExtension(PDecl, PIDecl, CDecl); + + // Make sure setter decl is synthesized, and added to continuation class's list. + ProcessPropertyDecl(PDecl, CDecl, PIDecl, CDecl); return PDecl; } @@ -1480,6 +1417,11 @@ static void CollectImmediateProperties(ObjCContainerDecl *CDecl, if (ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl)) { for (auto *Prop : IDecl->properties()) PropMap[Prop->getIdentifier()] = Prop; + + // Collect the properties from visible extensions. + for (auto *Ext : IDecl->visible_extensions()) + CollectImmediateProperties(Ext, PropMap, SuperPropMap, IncludeProtocols); + if (IncludeProtocols) { // Scan through class's protocols. for (auto *PI : IDecl->all_referenced_protocols()) @@ -1487,9 +1429,8 @@ static void CollectImmediateProperties(ObjCContainerDecl *CDecl, } } if (ObjCCategoryDecl *CATDecl = dyn_cast<ObjCCategoryDecl>(CDecl)) { - if (!CATDecl->IsClassExtension()) - for (auto *Prop : CATDecl->properties()) - PropMap[Prop->getIdentifier()] = Prop; + for (auto *Prop : CATDecl->properties()) + PropMap[Prop->getIdentifier()] = Prop; if (IncludeProtocols) { // Scan through class's protocols. for (auto *PI : CATDecl->protocols()) @@ -1549,6 +1490,14 @@ Sema::IvarBacksCurrentMethodAccessor(ObjCInterfaceDecl *IFace, (Property->getPropertyIvarDecl() == IV)) return true; } + // Also look up property declaration in class extension whose one of its + // accessors is implemented by this method. + for (const auto *Ext : IFace->known_extensions()) + for (const auto *Property : Ext->properties()) + if ((Property->getGetterName() == IMD->getSelector() || + Property->getSetterName() == IMD->getSelector()) && + (Property->getPropertyIvarDecl() == IV)) + return true; return false; } @@ -1833,11 +1782,20 @@ void Sema::diagnoseNullResettableSynthesizedSetters(const ObjCImplDecl *impDecl) void Sema::AtomicPropertySetterGetterRules (ObjCImplDecl* IMPDecl, - ObjCContainerDecl* IDecl) { + ObjCInterfaceDecl* IDecl) { // Rules apply in non-GC mode only if (getLangOpts().getGC() != LangOptions::NonGC) return; - for (const auto *Property : IDecl->properties()) { + ObjCContainerDecl::PropertyMap PM; + for (auto *Prop : IDecl->properties()) + PM[Prop->getIdentifier()] = Prop; + for (const auto *Ext : IDecl->known_extensions()) + for (auto *Prop : Ext->properties()) + PM[Prop->getIdentifier()] = Prop; + + for (ObjCContainerDecl::PropertyMap::iterator I = PM.begin(), E = PM.end(); + I != E; ++I) { + const ObjCPropertyDecl *Property = I->second; ObjCMethodDecl *GetterMethod = nullptr; ObjCMethodDecl *SetterMethod = nullptr; bool LookedUpGetterSetter = false; @@ -1884,30 +1842,23 @@ Sema::AtomicPropertySetterGetterRules (ObjCImplDecl* IMPDecl, << Property->getIdentifier() << (GetterMethod != nullptr) << (SetterMethod != nullptr); // fixit stuff. - if (!AttributesAsWritten) { - if (Property->getLParenLoc().isValid()) { - // @property () ... case. - SourceRange PropSourceRange(Property->getAtLoc(), - Property->getLParenLoc()); - Diag(Property->getLocation(), diag::note_atomic_property_fixup_suggest) << - FixItHint::CreateReplacement(PropSourceRange, "@property (nonatomic"); - } - else { - //@property id etc. - SourceLocation endLoc = - Property->getTypeSourceInfo()->getTypeLoc().getBeginLoc(); - endLoc = endLoc.getLocWithOffset(-1); - SourceRange PropSourceRange(Property->getAtLoc(), endLoc); - Diag(Property->getLocation(), diag::note_atomic_property_fixup_suggest) << - FixItHint::CreateReplacement(PropSourceRange, "@property (nonatomic) "); - } - } - else if (!(AttributesAsWritten & ObjCPropertyDecl::OBJC_PR_atomic)) { + if (Property->getLParenLoc().isValid() && + !(AttributesAsWritten & ObjCPropertyDecl::OBJC_PR_atomic)) { // @property () ... case. - SourceLocation endLoc = Property->getLParenLoc(); - SourceRange PropSourceRange(Property->getAtLoc(), endLoc); - Diag(Property->getLocation(), diag::note_atomic_property_fixup_suggest) << - FixItHint::CreateReplacement(PropSourceRange, "@property (nonatomic, "); + SourceLocation AfterLParen = + getLocForEndOfToken(Property->getLParenLoc()); + StringRef NonatomicStr = AttributesAsWritten? "nonatomic, " + : "nonatomic"; + Diag(Property->getLocation(), + diag::note_atomic_property_fixup_suggest) + << FixItHint::CreateInsertion(AfterLParen, NonatomicStr); + } else if (Property->getLParenLoc().isInvalid()) { + //@property id etc. + SourceLocation startLoc = + Property->getTypeSourceInfo()->getTypeLoc().getBeginLoc(); + Diag(Property->getLocation(), + diag::note_atomic_property_fixup_suggest) + << FixItHint::CreateInsertion(startLoc, "(nonatomic) "); } else Diag(MethodLoc, diag::note_atomic_property_fixup_suggest); @@ -2035,7 +1986,20 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property, return; GetterMethod = CD->getInstanceMethod(property->getGetterName()); + // if setter or getter is not found in class extension, it might be + // in the primary class. + if (!GetterMethod) + if (const ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CD)) + if (CatDecl->IsClassExtension()) + GetterMethod = CatDecl->getClassInterface()-> + getInstanceMethod(property->getGetterName()); + SetterMethod = CD->getInstanceMethod(property->getSetterName()); + if (!SetterMethod) + if (const ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CD)) + if (CatDecl->IsClassExtension()) + SetterMethod = CatDecl->getClassInterface()-> + getInstanceMethod(property->getSetterName()); DiagnosePropertyAccessorMismatch(property, GetterMethod, property->getLocation()); @@ -2068,9 +2032,7 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property, // No instance method of same name as property getter name was found. // Declare a getter method and add it to the list of methods // for this class. - SourceLocation Loc = redeclaredProperty ? - redeclaredProperty->getLocation() : - property->getLocation(); + SourceLocation Loc = property->getLocation(); // If the property is null_resettable, the getter returns nonnull. QualType resultTy = property->getType(); @@ -2130,9 +2092,7 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property, // No instance method of same name as property setter name was found. // Declare a setter method and add it to the list of methods // for this class. - SourceLocation Loc = redeclaredProperty ? - redeclaredProperty->getLocation() : - property->getLocation(); + SourceLocation Loc = property->getLocation(); SetterMethod = ObjCMethodDecl::Create(Context, Loc, Loc, diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 331fa38590..4399fdcb2f 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -5756,21 +5756,6 @@ void ASTWriter::AddedObjCCategoryToInterface(const ObjCCategoryDecl *CatD, const_cast<ObjCInterfaceDecl *>(IFD->getDefinition())); } - -void ASTWriter::AddedObjCPropertyInClassExtension(const ObjCPropertyDecl *Prop, - const ObjCPropertyDecl *OrigProp, - const ObjCCategoryDecl *ClassExt) { - const ObjCInterfaceDecl *D = ClassExt->getClassInterface(); - if (!D) - return; - - assert(!WritingAST && "Already writing the AST!"); - if (!D->isFromASTFile()) - return; // Declaration not imported from PCH. - - RewriteDecl(D); -} - void ASTWriter::DeclarationMarkedUsed(const Decl *D) { assert(!WritingAST && "Already writing the AST!"); if (!D->isFromASTFile()) diff --git a/test/FixIt/atomic-property.m b/test/FixIt/atomic-property.m index 9ede7f1e76..84dd820e1c 100644 --- a/test/FixIt/atomic-property.m +++ b/test/FixIt/atomic-property.m @@ -23,7 +23,7 @@ - (id) atomic_prop1 { return 0; } @end -// CHECK: {4:1-4:10}:"@property (nonatomic) " -// CHECK: {9:1-9:12}:"@property (nonatomic" -// CHECK: {13:1-13:12}:"@property (nonatomic, " +// CHECK-DAG: {4:11-4:11}:"(nonatomic) " +// CHECK-DAG: {9:12-9:12}:"nonatomic" +// CHECK-DAG: {13:12-13:12}:"nonatomic, " diff --git a/test/Index/complete-kvc.m b/test/Index/complete-kvc.m index 62d98a9b20..336d41d759 100644 --- a/test/Index/complete-kvc.m +++ b/test/Index/complete-kvc.m @@ -101,5 +101,5 @@ typedef signed char BOOL; // RUN: c-index-test -code-completion-at=%s:52:8 %s | FileCheck -check-prefix=CHECK-CC3 %s // CHECK-CC3: ObjCInstanceMethodDecl:{TypedText countOfIntProperty} (55) -// CHECK-CC3-NEXT: ObjCInstanceMethodDecl:{TypedText intProperty} (40) +// CHECK-CC3-NEXT: ObjCInstanceMethodDecl:{TypedText intProperty} (42) // CHECK-CC3-NEXT: ObjCInstanceMethodDecl:{TypedText isIntProperty} (40) diff --git a/test/Modules/ModuleDebugInfo.m b/test/Modules/ModuleDebugInfo.m index b7dfb22b71..0974f38cc2 100644 --- a/test/Modules/ModuleDebugInfo.m +++ b/test/Modules/ModuleDebugInfo.m @@ -33,7 +33,7 @@ // CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "InnerEnum" // CHECK: !DISubprogram(name: "+[ObjCClass classMethod]" // CHECK: !DISubprogram(name: "-[ObjCClass instanceMethodWithInt:]" -// CHECK: !DISubprogram(name: "-[ categoryMethod]" +// CHECK: !DISubprogram(name: "-[Category(Category) categoryMethod]" // MODULE-CHECK: !DICompositeType(tag: DW_TAG_enumeration_type, // MODULE-CHECK-SAME: scope: ![[MODULE:[0-9]+]], diff --git a/test/PCH/chain-categories.m b/test/PCH/chain-categories.m index 7836e09d88..dc57387fd5 100644 --- a/test/PCH/chain-categories.m +++ b/test/PCH/chain-categories.m @@ -16,6 +16,10 @@ - (void)finalize; @end +@interface NSObject (Properties) +@property (readonly,nonatomic) int intProp; +@end + //===----------------------------------------------------------------------===// #elif !defined(HEADER2) #define HEADER2 @@ -34,6 +38,12 @@ -(void)extMeth; @end +@interface NSObject () +@property (readwrite,nonatomic) int intProp; +@end + +@class NSObject; + //===----------------------------------------------------------------------===// #else //===----------------------------------------------------------------------===// @@ -47,6 +57,9 @@ void test(NSObject *o) { [o extMeth]; + + // Make sure the property is treated as read-write. + o.intProp = 17; } //===----------------------------------------------------------------------===// diff --git a/test/SemaObjC/atomoic-property-synnthesis-rules.m b/test/SemaObjC/atomoic-property-synnthesis-rules.m index b681558220..c7fac7b618 100644 --- a/test/SemaObjC/atomoic-property-synnthesis-rules.m +++ b/test/SemaObjC/atomoic-property-synnthesis-rules.m @@ -129,10 +129,8 @@ // read-only in class, read-write in class extension - might warn @property(readonly) int GetSet_ReadWriteInExt; -@property(readonly) int Get_ReadWriteInExt; // expected-note {{property declared here}} \ - // expected-note {{setter and getter must both be synthesized}} -@property(readonly) int Set_ReadWriteInExt; // expected-note {{property declared here}} \ - // expected-note {{setter and getter must both be synthesized}} +@property(readonly) int Get_ReadWriteInExt; +@property(readonly) int Set_ReadWriteInExt; @property(readonly) int None_ReadWriteInExt; @property(nonatomic,readonly) int GetSet_Nonatomic_ReadWriteInExt; @property(nonatomic,readonly) int Get_Nonatomic_ReadWriteInExt; @@ -162,10 +160,8 @@ @property(nonatomic,readonly) int None_Nonatomic_ReadOnly_LateSynthesize; @property(readonly) int GetSet_ReadWriteInExt_LateSynthesize; -@property(readonly) int Get_ReadWriteInExt_LateSynthesize; // expected-note {{property declared here}} \ - // expected-note {{setter and getter must both be synthesized}} -@property(readonly) int Set_ReadWriteInExt_LateSynthesize; // expected-note {{property declared here}} \ - // expected-note {{setter and getter must both be synthesized}} +@property(readonly) int Get_ReadWriteInExt_LateSynthesize; +@property(readonly) int Set_ReadWriteInExt_LateSynthesize; @property(readonly) int None_ReadWriteInExt_LateSynthesize; @property(nonatomic,readonly) int GetSet_Nonatomic_ReadWriteInExt_LateSynthesize; @property(nonatomic,readonly) int Get_Nonatomic_ReadWriteInExt_LateSynthesize; @@ -207,8 +203,10 @@ @interface Foo () @property(readwrite) int GetSet_ReadWriteInExt; -@property(readwrite) int Get_ReadWriteInExt; -@property(readwrite) int Set_ReadWriteInExt; +@property(readwrite) int Get_ReadWriteInExt; // expected-note {{property declared here}} \ + // expected-note {{setter and getter must both be synthesized}} +@property(readwrite) int Set_ReadWriteInExt; // expected-note {{property declared here}} \ + // expected-note {{setter and getter must both be synthesized}} @property(readwrite) int None_ReadWriteInExt; @property(nonatomic,readwrite) int GetSet_Nonatomic_ReadWriteInExt; @property(nonatomic,readwrite) int Get_Nonatomic_ReadWriteInExt; @@ -216,8 +214,10 @@ @property(nonatomic,readwrite) int None_Nonatomic_ReadWriteInExt; @property(readwrite) int GetSet_ReadWriteInExt_LateSynthesize; -@property(readwrite) int Get_ReadWriteInExt_LateSynthesize; -@property(readwrite) int Set_ReadWriteInExt_LateSynthesize; +@property(readwrite) int Get_ReadWriteInExt_LateSynthesize; // expected-note {{property declared here}} \ + // expected-note {{setter and getter must both be synthesized}} +@property(readwrite) int Set_ReadWriteInExt_LateSynthesize; // expected-note {{property declared here}} \ + // expected-note {{setter and getter must both be synthesized}} @property(readwrite) int None_ReadWriteInExt_LateSynthesize; @property(nonatomic,readwrite) int GetSet_Nonatomic_ReadWriteInExt_LateSynthesize; @property(nonatomic,readwrite) int Get_Nonatomic_ReadWriteInExt_LateSynthesize; diff --git a/test/SemaObjC/property-3.m b/test/SemaObjC/property-3.m index 3f82bcc3b7..7c0ba579ee 100644 --- a/test/SemaObjC/property-3.m +++ b/test/SemaObjC/property-3.m @@ -29,5 +29,5 @@ typedef signed char BOOL; @interface EKCalendar () <EKProtocolMutableCalendar> @property (nonatomic, assign) BOOL allowReminders; -@property (nonatomic, assign) BOOL allowNonatomicProperty; // expected-warning {{'atomic' attribute on property 'allowNonatomicProperty' does not match the property inherited from 'EKProtocolCalendar'}} +@property (nonatomic, assign) BOOL allowNonatomicProperty; // expected-warning {{'atomic' attribute on property 'allowNonatomicProperty' does not match the property inherited from EKProtocolCalendar}} @end diff --git a/test/SemaObjC/property-in-class-extension-1.m b/test/SemaObjC/property-in-class-extension-1.m index 6e9d476c18..8f5907b54d 100644 --- a/test/SemaObjC/property-in-class-extension-1.m +++ b/test/SemaObjC/property-in-class-extension-1.m @@ -10,7 +10,7 @@ @property (nonatomic, copy, readonly) NSString* matchingMemoryModel; -@property (nonatomic, retain, readonly) NSString* addingNoNewMemoryModel; +@property (atomic, retain, readonly) NSString* addingNoNewMemoryModel; @property (readonly) NSString* none; @property (readonly) NSString* none1; @@ -50,10 +50,14 @@ // rdar://12214070 @interface radar12214070 @property (nonatomic, atomic, readonly) float propertyName; // expected-error {{property attributes 'atomic' and 'nonatomic' are mutually exclusive}} + +@property (nonatomic, readonly) float propertyName2; // expected-note {{property declared here}} @end @interface radar12214070 () @property (atomic, nonatomic, readonly, readwrite) float propertyName; // expected-error {{property attributes 'readonly' and 'readwrite' are mutually exclusive}} \ // expected-error {{property attributes 'atomic' and 'nonatomic' are mutually exclusive}} + +@property (atomic, readwrite) float propertyName2; // expected-warning {{'atomic' attribute on property 'propertyName2' does not match the property inherited from radar12214070}} @end diff --git a/test/SemaObjCXX/property-invalid-type.mm b/test/SemaObjCXX/property-invalid-type.mm index 5b8a848df4..648235894e 100644 --- a/test/SemaObjCXX/property-invalid-type.mm +++ b/test/SemaObjCXX/property-invalid-type.mm @@ -13,11 +13,11 @@ @synthesize response; - (void) foo :(A*) a // expected-error {{expected a type}} { - self.response = a; + self.response = a; // expected-error{{assigning to 'int *' from incompatible type 'id'}} } @end void foo(I *i) { - i.helper; + i.helper; // expected-warning{{property access result unused - getters should not be used for side effects}} } |