//===--- SemaObjCProperty.cpp - Semantic Analysis for ObjC @property ------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file implements semantic analysis for Objective C @property and // @synthesize declarations. // //===----------------------------------------------------------------------===// #include "Sema.h" #include "SemaInit.h" #include "clang/AST/ExprObjC.h" using namespace clang; //===----------------------------------------------------------------------===// // Grammar actions. //===----------------------------------------------------------------------===// Sema::DeclPtrTy Sema::ActOnProperty(Scope *S, SourceLocation AtLoc, FieldDeclarator &FD, ObjCDeclSpec &ODS, Selector GetterSel, Selector SetterSel, DeclPtrTy ClassCategory, bool *isOverridingProperty, tok::ObjCKeywordKind MethodImplKind) { unsigned Attributes = ODS.getPropertyAttributes(); bool isReadWrite = ((Attributes & ObjCDeclSpec::DQ_PR_readwrite) || // default is readwrite! !(Attributes & ObjCDeclSpec::DQ_PR_readonly)); // property is defaulted to 'assign' if it is readwrite and is // not retain or copy bool isAssign = ((Attributes & ObjCDeclSpec::DQ_PR_assign) || (isReadWrite && !(Attributes & ObjCDeclSpec::DQ_PR_retain) && !(Attributes & ObjCDeclSpec::DQ_PR_copy))); QualType T = GetTypeForDeclarator(FD.D, S); if (T->isReferenceType()) { Diag(AtLoc, diag::error_reference_property); return DeclPtrTy(); } // Proceed with constructing the ObjCPropertDecls. ObjCContainerDecl *ClassDecl = cast(ClassCategory.getAs()); if (ObjCCategoryDecl *CDecl = dyn_cast(ClassDecl)) if (CDecl->IsClassExtension()) return HandlePropertyInClassExtension(S, CDecl, AtLoc, FD, GetterSel, SetterSel, isAssign, isReadWrite, Attributes, isOverridingProperty, T, MethodImplKind); DeclPtrTy Res = DeclPtrTy::make(CreatePropertyDecl(S, ClassDecl, AtLoc, FD, GetterSel, SetterSel, isAssign, isReadWrite, Attributes, T, MethodImplKind)); // Validate the attributes on the @property. CheckObjCPropertyAttributes(Res, AtLoc, Attributes); return Res; } Sema::DeclPtrTy Sema::HandlePropertyInClassExtension(Scope *S, ObjCCategoryDecl *CDecl, SourceLocation AtLoc, FieldDeclarator &FD, Selector GetterSel, Selector SetterSel, const bool isAssign, const bool isReadWrite, const unsigned Attributes, bool *isOverridingProperty, QualType T, tok::ObjCKeywordKind MethodImplKind) { // Diagnose if this property is already in continuation class. DeclContext *DC = cast(CDecl); IdentifierInfo *PropertyId = FD.D.getIdentifier(); if (ObjCPropertyDecl *prevDecl = ObjCPropertyDecl::findPropertyDecl(DC, PropertyId)) { Diag(AtLoc, diag::err_duplicate_property); Diag(prevDecl->getLocation(), diag::note_property_declare); return DeclPtrTy(); } // Create a new ObjCPropertyDecl with the DeclContext being // the class extension. ObjCPropertyDecl *PDecl = ObjCPropertyDecl::Create(Context, DC, FD.D.getIdentifierLoc(), PropertyId, AtLoc, T); if (Attributes & ObjCDeclSpec::DQ_PR_readonly) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_readonly); if (Attributes & ObjCDeclSpec::DQ_PR_readwrite) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_readwrite); DC->addDecl(PDecl); // We need to look in the @interface to see if the @property was // already declared. ObjCInterfaceDecl *CCPrimary = CDecl->getClassInterface(); if (!CCPrimary) { Diag(CDecl->getLocation(), diag::err_continuation_class); *isOverridingProperty = true; return DeclPtrTy(); } // Find the property in continuation class's primary class only. ObjCPropertyDecl *PIDecl = CCPrimary->FindPropertyVisibleInPrimaryClass(PropertyId); if (!PIDecl) { // No matching property found in the primary class. Just fall thru // and add property to continuation class's primary class. ObjCPropertyDecl *PDecl = CreatePropertyDecl(S, CCPrimary, AtLoc, FD, GetterSel, SetterSel, isAssign, isReadWrite, Attributes, T, 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(PDecl, CCPrimary); return DeclPtrTy::make(PDecl); } // The property 'PIDecl's readonly attribute will be over-ridden // with continuation class's readwrite property attribute! unsigned PIkind = PIDecl->getPropertyAttributes(); if (isReadWrite && (PIkind & ObjCPropertyDecl::OBJC_PR_readonly)) { unsigned retainCopyNonatomic = (ObjCPropertyDecl::OBJC_PR_retain | ObjCPropertyDecl::OBJC_PR_copy | ObjCPropertyDecl::OBJC_PR_nonatomic); if ((Attributes & retainCopyNonatomic) != (PIkind & retainCopyNonatomic)) { Diag(AtLoc, diag::warn_property_attr_mismatch); Diag(PIDecl->getLocation(), diag::note_property_declare); } DeclContext *DC = cast(CCPrimary); if (!ObjCPropertyDecl::findPropertyDecl(DC, PIDecl->getDeclName().getAsIdentifierInfo())) { // 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); DeclPtrTy ProtocolPtrTy = ActOnProperty(S, AtLoc, FD, ProtocolPropertyODS, PIDecl->getGetterName(), PIDecl->getSetterName(), DeclPtrTy::make(CCPrimary), isOverridingProperty, MethodImplKind); PIDecl = cast(ProtocolPtrTy.getAs()); } PIDecl->makeitReadWriteAttribute(); if (Attributes & ObjCDeclSpec::DQ_PR_retain) PIDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_retain); if (Attributes & ObjCDeclSpec::DQ_PR_copy) PIDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_copy); PIDecl->setSetterName(SetterSel); } else { Diag(AtLoc, diag::err_use_continuation_class) << CCPrimary->getDeclName(); 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); return DeclPtrTy(); } ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S, ObjCContainerDecl *CDecl, SourceLocation AtLoc, FieldDeclarator &FD, Selector GetterSel, Selector SetterSel, const bool isAssign, const bool isReadWrite, const unsigned Attributes, QualType T, tok::ObjCKeywordKind MethodImplKind, DeclContext *lexicalDC){ IdentifierInfo *PropertyId = FD.D.getIdentifier(); // Issue a warning if property is 'assign' as default and its object, which is // gc'able conforms to NSCopying protocol if (getLangOptions().getGCMode() != LangOptions::NonGC && isAssign && !(Attributes & ObjCDeclSpec::DQ_PR_assign)) if (const ObjCObjectPointerType *ObjPtrTy = T->getAs()) { ObjCInterfaceDecl *IDecl = ObjPtrTy->getObjectType()->getInterface(); if (IDecl) if (ObjCProtocolDecl* PNSCopying = LookupProtocol(&Context.Idents.get("NSCopying"), AtLoc)) if (IDecl->ClassImplementsProtocol(PNSCopying, true)) Diag(AtLoc, diag::warn_implements_nscopying) << PropertyId; } if (T->isObjCObjectType()) Diag(FD.D.getIdentifierLoc(), diag::err_statically_allocated_object); DeclContext *DC = cast(CDecl); ObjCPropertyDecl *PDecl = ObjCPropertyDecl::Create(Context, DC, FD.D.getIdentifierLoc(), PropertyId, AtLoc, T); if (ObjCPropertyDecl *prevDecl = ObjCPropertyDecl::findPropertyDecl(DC, PropertyId)) { Diag(PDecl->getLocation(), diag::err_duplicate_property); Diag(prevDecl->getLocation(), diag::note_property_declare); PDecl->setInvalidDecl(); } else { DC->addDecl(PDecl); if (lexicalDC) PDecl->setLexicalDeclContext(lexicalDC); } if (T->isArrayType() || T->isFunctionType()) { Diag(AtLoc, diag::err_property_type) << T; PDecl->setInvalidDecl(); } ProcessDeclAttributes(S, PDecl, FD.D); // Regardless of setter/getter attribute, we save the default getter/setter // selector names in anticipation of declaration of setter/getter methods. PDecl->setGetterName(GetterSel); PDecl->setSetterName(SetterSel); if (Attributes & ObjCDeclSpec::DQ_PR_readonly) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_readonly); if (Attributes & ObjCDeclSpec::DQ_PR_getter) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_getter); if (Attributes & ObjCDeclSpec::DQ_PR_setter) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_setter); if (isReadWrite) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_readwrite); if (Attributes & ObjCDeclSpec::DQ_PR_retain) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_retain); if (Attributes & ObjCDeclSpec::DQ_PR_copy) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_copy); if (isAssign) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_assign); if (Attributes & ObjCDeclSpec::DQ_PR_nonatomic) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_nonatomic); if (MethodImplKind == tok::objc_required) PDecl->setPropertyImplementation(ObjCPropertyDecl::Required); else if (MethodImplKind == tok::objc_optional) PDecl->setPropertyImplementation(ObjCPropertyDecl::Optional); return PDecl; } /// ActOnPropertyImplDecl - This routine performs semantic checks and /// builds the AST node for a property implementation declaration; declared /// as @synthesize or @dynamic. /// Sema::DeclPtrTy Sema::ActOnPropertyImplDecl(Scope *S, SourceLocation AtLoc, SourceLocation PropertyLoc, bool Synthesize, DeclPtrTy ClassCatImpDecl, IdentifierInfo *PropertyId, IdentifierInfo *PropertyIvar) { ObjCContainerDecl *ClassImpDecl = cast_or_null(ClassCatImpDecl.getAs()); // Make sure we have a context for the property implementation declaration. if (!ClassImpDecl) { Diag(AtLoc, diag::error_missing_property_context); return DeclPtrTy(); } ObjCPropertyDecl *property = 0; ObjCInterfaceDecl* IDecl = 0; // Find the class or category class where this property must have // a declaration. ObjCImplementationDecl *IC = 0; ObjCCategoryImplDecl* CatImplClass = 0; if ((IC = dyn_cast(ClassImpDecl))) { IDecl = IC->getClassInterface(); // We always synthesize an interface for an implementation // without an interface decl. So, IDecl is always non-zero. assert(IDecl && "ActOnPropertyImplDecl - @implementation without @interface"); // Look for this property declaration in the @implementation's @interface property = IDecl->FindPropertyDeclaration(PropertyId); if (!property) { Diag(PropertyLoc, diag::error_bad_property_decl) << IDecl->getDeclName(); return DeclPtrTy(); } if (const ObjCCategoryDecl *CD = dyn_cast(property->getDeclContext())) { if (!CD->IsClassExtension()) { Diag(PropertyLoc, diag::error_category_property) << CD->getDeclName(); Diag(property->getLocation(), diag::note_property_declare); return DeclPtrTy(); } } } else if ((CatImplClass = dyn_cast(ClassImpDecl))) { if (Synthesize) { Diag(AtLoc, diag::error_synthesize_category_decl); return DeclPtrTy(); } IDecl = CatImplClass->getClassInterface(); if (!IDecl) { Diag(AtLoc, diag::error_missing_property_interface); return DeclPtrTy(); } ObjCCategoryDecl *Category = IDecl->FindCategoryDeclaration(CatImplClass->getIdentifier()); // If category for this implementation not found, it is an error which // has already been reported eralier. if (!Category) return DeclPtrTy(); // Look for this property declaration in @implementation's category property = Category->FindPropertyDeclaration(PropertyId); if (!property) { Diag(PropertyLoc, diag::error_bad_category_property_decl) << Category->getDeclName(); return DeclPtrTy(); } } else { Diag(AtLoc, diag::error_bad_property_context); return DeclPtrTy(); } ObjCIvarDecl *Ivar = 0; // Check that we have a valid, previously declared ivar for @synthesize if (Synthesize) { // @synthesize if (!PropertyIvar) PropertyIvar = PropertyId; QualType PropType = Context.getCanonicalType(property->getType()); // Check that this is a previously declared 'ivar' in 'IDecl' interface ObjCInterfaceDecl *ClassDeclared; Ivar = IDecl->lookupInstanceVariable(PropertyIvar, ClassDeclared); if (!Ivar) { Ivar = ObjCIvarDecl::Create(Context, ClassImpDecl, PropertyLoc, PropertyIvar, PropType, /*Dinfo=*/0, ObjCIvarDecl::Protected, (Expr *)0); ClassImpDecl->addDecl(Ivar); IDecl->makeDeclVisibleInContext(Ivar, false); property->setPropertyIvarDecl(Ivar); if (!getLangOptions().ObjCNonFragileABI) Diag(PropertyLoc, diag::error_missing_property_ivar_decl) << PropertyId; // Note! I deliberately want it to fall thru so, we have a // a property implementation and to avoid future warnings. } else if (getLangOptions().ObjCNonFragileABI && ClassDeclared != IDecl) { Diag(PropertyLoc, diag::error_ivar_in_superclass_use) << property->getDeclName() << Ivar->getDeclName() << ClassDeclared->getDeclName(); Diag(Ivar->getLocation(), diag::note_previous_access_declaration) << Ivar << Ivar->getNameAsCString(); // Note! I deliberately want it to fall thru so more errors are caught. } QualType IvarType = Context.getCanonicalType(Ivar->getType()); // Check that type of property and its ivar are type compatible. if (PropType != IvarType) { bool compat = false; if (isa(PropType) && isa(IvarType)) compat = Context.canAssignObjCInterfaces( PropType->getAs(), IvarType->getAs()); else compat = (CheckAssignmentConstraints(PropType, IvarType) == Compatible); if (!compat) { Diag(PropertyLoc, diag::error_property_ivar_type) << property->getDeclName() << PropType << Ivar->getDeclName() << IvarType; Diag(Ivar->getLocation(), diag::note_ivar_decl); // Note! I deliberately want it to fall thru so, we have a // a property implementation and to avoid future warnings. } // FIXME! Rules for properties are somewhat different that those // for assignments. Use a new routine to consolidate all cases; // specifically for property redeclarations as well as for ivars. QualType lhsType =Context.getCanonicalType(PropType).getUnqualifiedType(); QualType rhsType =Context.getCanonicalType(IvarType).getUnqualifiedType(); if (lhsType != rhsType && lhsType->isArithmeticType()) { Diag(PropertyLoc, diag::error_property_ivar_type) << property->getDeclName() << PropType << Ivar->getDeclName() << IvarType; Diag(Ivar->getLocation(), diag::note_ivar_decl); // Fall thru - see previous comment } // __weak is explicit. So it works on Canonical type. if (PropType.isObjCGCWeak() && !IvarType.isObjCGCWeak() && getLangOptions().getGCMode() != LangOptions::NonGC) { Diag(PropertyLoc, diag::error_weak_property) << property->getDeclName() << Ivar->getDeclName(); // Fall thru - see previous comment } if ((property->getType()->isObjCObjectPointerType() || PropType.isObjCGCStrong()) && IvarType.isObjCGCWeak() && getLangOptions().getGCMode() != LangOptions::NonGC) { Diag(PropertyLoc, diag::error_strong_property) << property->getDeclName() << Ivar->getDeclName(); // Fall thru - see previous comment } } } else if (PropertyIvar) // @dynamic Diag(PropertyLoc, diag::error_dynamic_property_ivar_decl); assert (property && "ActOnPropertyImplDecl - property declaration missing"); ObjCPropertyImplDecl *PIDecl = ObjCPropertyImplDecl::Create(Context, CurContext, AtLoc, PropertyLoc, property, (Synthesize ? ObjCPropertyImplDecl::Synthesize : ObjCPropertyImplDecl::Dynamic), Ivar); if (ObjCMethodDecl *getterMethod = property->getGetterMethodDecl()) { getterMethod->createImplicitParams(Context, IDecl); if (getLangOptions().CPlusPlus && Synthesize) { // For Objective-C++, need to synthesize the AST for the IVAR object to be // returned by the getter as it must conform to C++'s copy-return rules. // FIXME. Eventually we want to do this for Objective-C as well. ImplicitParamDecl *SelfDecl = getterMethod->getSelfDecl(); DeclRefExpr *SelfExpr = new (Context) DeclRefExpr(SelfDecl,SelfDecl->getType(), SourceLocation()); Expr *IvarRefExpr = new (Context) ObjCIvarRefExpr(Ivar, Ivar->getType(), AtLoc, SelfExpr, true, true); OwningExprResult Res = PerformCopyInitialization(InitializedEntity::InitializeResult( SourceLocation(), getterMethod->getResultType(), /*NRVO=*/false), SourceLocation(), Owned(IvarRefExpr)); if (!Res.isInvalid()) { Expr *ResExpr = Res.takeAs(); if (ResExpr) ResExpr = MaybeCreateCXXExprWithTemporaries(ResExpr); PIDecl->setGetterCXXConstructor(ResExpr); } } } if (ObjCMethodDecl *setterMethod = property->getSetterMethodDecl()) { setterMethod->createImplicitParams(Context, IDecl); if (getLangOptions().CPlusPlus && Synthesize) { // FIXME. Eventually we want to do this for Objective-C as well. ImplicitParamDecl *SelfDecl = setterMethod->getSelfDecl(); DeclRefExpr *SelfExpr = new (Context) DeclRefExpr(SelfDecl,SelfDecl->getType(), SourceLocation()); Expr *lhs = new (Context) ObjCIvarRefExpr(Ivar, Ivar->getType(), AtLoc, SelfExpr, true, true); ObjCMethodDecl::param_iterator P = setterMethod->param_begin(); ParmVarDecl *Param = (*P); Expr *rhs = new (Context) DeclRefExpr(Param,Param->getType(), SourceLocation()); OwningExprResult Res = BuildBinOp(S, SourceLocation(), BinaryOperator::Assign, lhs, rhs); PIDecl->setSetterCXXAssignment(Res.takeAs()); } } if (IC) { if (Synthesize) if (ObjCPropertyImplDecl *PPIDecl = IC->FindPropertyImplIvarDecl(PropertyIvar)) { Diag(PropertyLoc, diag::error_duplicate_ivar_use) << PropertyId << PPIDecl->getPropertyDecl()->getIdentifier() << PropertyIvar; Diag(PPIDecl->getLocation(), diag::note_previous_use); } if (ObjCPropertyImplDecl *PPIDecl = IC->FindPropertyImplDecl(PropertyId)) { Diag(PropertyLoc, diag::error_property_implemented) << PropertyId; Diag(PPIDecl->getLocation(), diag::note_previous_declaration); return DeclPtrTy(); } IC->addPropertyImplementation(PIDecl); } else { if (Synthesize) if (ObjCPropertyImplDecl *PPIDecl = CatImplClass->FindPropertyImplIvarDecl(PropertyIvar)) { Diag(PropertyLoc, diag::error_duplicate_ivar_use) << PropertyId << PPIDecl->getPropertyDecl()->getIdentifier() << PropertyIvar; Diag(PPIDecl->getLocation(), diag::note_previous_use); } if (ObjCPropertyImplDecl *PPIDecl = CatImplClass->FindPropertyImplDecl(PropertyId)) { Diag(PropertyLoc, diag::error_property_implemented) << PropertyId; Diag(PPIDecl->getLocation(), diag::note_previous_declaration); return DeclPtrTy(); } CatImplClass->addPropertyImplementation(PIDecl); } return DeclPtrTy::make(PIDecl); } //===----------------------------------------------------------------------===// // Helper methods. //===----------------------------------------------------------------------===// /// DiagnosePropertyMismatch - Compares two properties for their /// attributes and types and warns on a variety of inconsistencies. /// void Sema::DiagnosePropertyMismatch(ObjCPropertyDecl *Property, ObjCPropertyDecl *SuperProperty, const IdentifierInfo *inheritedName) { ObjCPropertyDecl::PropertyAttributeKind CAttr = Property->getPropertyAttributes(); ObjCPropertyDecl::PropertyAttributeKind SAttr = SuperProperty->getPropertyAttributes(); if ((CAttr & ObjCPropertyDecl::OBJC_PR_readonly) && (SAttr & ObjCPropertyDecl::OBJC_PR_readwrite)) Diag(Property->getLocation(), diag::warn_readonly_property) << Property->getDeclName() << inheritedName; if ((CAttr & ObjCPropertyDecl::OBJC_PR_copy) != (SAttr & ObjCPropertyDecl::OBJC_PR_copy)) Diag(Property->getLocation(), diag::warn_property_attribute) << Property->getDeclName() << "copy" << inheritedName; else if ((CAttr & ObjCPropertyDecl::OBJC_PR_retain) != (SAttr & ObjCPropertyDecl::OBJC_PR_retain)) Diag(Property->getLocation(), diag::warn_property_attribute) << Property->getDeclName() << "retain" << inheritedName; if ((CAttr & ObjCPropertyDecl::OBJC_PR_nonatomic) != (SAttr & ObjCPropertyDecl::OBJC_PR_nonatomic)) Diag(Property->getLocation(), diag::warn_property_attribute) << Property->getDeclName() << "atomic" << inheritedName; if (Property->getSetterName() != SuperProperty->getSetterName()) Diag(Property->getLocation(), diag::warn_property_attribute) << Property->getDeclName() << "setter" << inheritedName; if (Property->getGetterName() != SuperProperty->getGetterName()) Diag(Property->getLocation(), diag::warn_property_attribute) << Property->getDeclName() << "getter" << inheritedName; QualType LHSType = Context.getCanonicalType(SuperProperty->getType()); QualType RHSType = Context.getCanonicalType(Property->getType()); if (!Context.typesAreCompatible(LHSType, RHSType)) { // FIXME: Incorporate this test with typesAreCompatible. if (LHSType->isObjCQualifiedIdType() && RHSType->isObjCQualifiedIdType()) if (Context.ObjCQualifiedIdTypesAreCompatible(LHSType, RHSType, false)) return; Diag(Property->getLocation(), diag::warn_property_types_are_incompatible) << Property->getType() << SuperProperty->getType() << inheritedName; } } bool Sema::DiagnosePropertyAccessorMismatch(ObjCPropertyDecl *property, ObjCMethodDecl *GetterMethod, SourceLocation Loc) { if (GetterMethod && GetterMethod->getResultType() != property->getType()) { AssignConvertType result = Incompatible; if (property->getType()->isObjCObjectPointerType()) result = CheckAssignmentConstraints(GetterMethod->getResultType(), property->getType()); if (result != Compatible) { Diag(Loc, diag::warn_accessor_property_type_mismatch) << property->getDeclName() << GetterMethod->getSelector(); Diag(GetterMethod->getLocation(), diag::note_declared_at); return true; } } return false; } /// ComparePropertiesInBaseAndSuper - This routine compares property /// declarations in base and its super class, if any, and issues /// diagnostics in a variety of inconsistant situations. /// void Sema::ComparePropertiesInBaseAndSuper(ObjCInterfaceDecl *IDecl) { ObjCInterfaceDecl *SDecl = IDecl->getSuperClass(); if (!SDecl) return; // FIXME: O(N^2) for (ObjCInterfaceDecl::prop_iterator S = SDecl->prop_begin(), E = SDecl->prop_end(); S != E; ++S) { ObjCPropertyDecl *SuperPDecl = (*S); // Does property in super class has declaration in current class? for (ObjCInterfaceDecl::prop_iterator I = IDecl->prop_begin(), E = IDecl->prop_end(); I != E; ++I) { ObjCPropertyDecl *PDecl = (*I); if (SuperPDecl->getIdentifier() == PDecl->getIdentifier()) DiagnosePropertyMismatch(PDecl, SuperPDecl, SDecl->getIdentifier()); } } } /// MatchOneProtocolPropertiesInClass - This routine goes thru the list /// of properties declared in a protocol and compares their attribute against /// the same property declared in the class or category. void Sema::MatchOneProtocolPropertiesInClass(Decl *CDecl, ObjCProtocolDecl *PDecl) { ObjCInterfaceDecl *IDecl = dyn_cast_or_null(CDecl); if (!IDecl) { // Category ObjCCategoryDecl *CatDecl = static_cast(CDecl); assert (CatDecl && "MatchOneProtocolPropertiesInClass"); if (!CatDecl->IsClassExtension()) for (ObjCProtocolDecl::prop_iterator P = PDecl->prop_begin(), E = PDecl->prop_end(); P != E; ++P) { ObjCPropertyDecl *Pr = (*P); ObjCCategoryDecl::prop_iterator CP, CE; // Is this property already in category's list of properties? for (CP = CatDecl->prop_begin(), CE = CatDecl->prop_end(); CP!=CE; ++CP) if ((*CP)->getIdentifier() == Pr->getIdentifier()) break; if (CP != CE) // Property protocol already exist in class. Diagnose any mismatch. DiagnosePropertyMismatch((*CP), Pr, PDecl->getIdentifier()); } return; } for (ObjCProtocolDecl::prop_iterator P = PDecl->prop_begin(), E = PDecl->prop_end(); P != E; ++P) { ObjCPropertyDecl *Pr = (*P); ObjCInterfaceDecl::prop_iterator CP, CE; // Is this property already in class's list of properties? for (CP = IDecl->prop_begin(), CE = IDecl->prop_end(); CP != CE; ++CP) if ((*CP)->getIdentifier() == Pr->getIdentifier()) break; if (CP != CE) // Property protocol already exist in class. Diagnose any mismatch. DiagnosePropertyMismatch((*CP), Pr, PDecl->getIdentifier()); } } /// CompareProperties - This routine compares properties /// declared in 'ClassOrProtocol' objects (which can be a class or an /// inherited protocol with the list of properties for class/category 'CDecl' /// void Sema::CompareProperties(Decl *CDecl, DeclPtrTy ClassOrProtocol) { Decl *ClassDecl = ClassOrProtocol.getAs(); ObjCInterfaceDecl *IDecl = dyn_cast_or_null(CDecl); if (!IDecl) { // Category ObjCCategoryDecl *CatDecl = static_cast(CDecl); assert (CatDecl && "CompareProperties"); if (ObjCCategoryDecl *MDecl = dyn_cast(ClassDecl)) { for (ObjCCategoryDecl::protocol_iterator P = MDecl->protocol_begin(), E = MDecl->protocol_end(); P != E; ++P) // Match properties of category with those of protocol (*P) MatchOneProtocolPropertiesInClass(CatDecl, *P); // Go thru the list of protocols for this category and recursively match // their properties with those in the category. for (ObjCCategoryDecl::protocol_iterator P = CatDecl->protocol_begin(), E = CatDecl->protocol_end(); P != E; ++P) CompareProperties(CatDecl, DeclPtrTy::make(*P)); } else { ObjCProtocolDecl *MD = cast(ClassDecl); for (ObjCProtocolDecl::protocol_iterator P = MD->protocol_begin(), E = MD->protocol_end(); P != E; ++P) MatchOneProtocolPropertiesInClass(CatDecl, *P); } return; } if (ObjCInterfaceDecl *MDecl = dyn_cast(ClassDecl)) { for (ObjCInterfaceDecl::protocol_iterator P = MDecl->protocol_begin(), E = MDecl->protocol_end(); P != E; ++P) // Match properties of class IDecl with those of protocol (*P). MatchOneProtocolPropertiesInClass(IDecl, *P); // Go thru the list of protocols for this class and recursively match // their properties with those declared in the class. for (ObjCInterfaceDecl::protocol_iterator P = IDecl->protocol_begin(), E = IDecl->protocol_end(); P != E; ++P) CompareProperties(IDecl, DeclPtrTy::make(*P)); } else { ObjCProtocolDecl *MD = cast(ClassDecl); for (ObjCProtocolDecl::protocol_iterator P = MD->protocol_begin(), E = MD->protocol_end(); P != E; ++P) MatchOneProtocolPropertiesInClass(IDecl, *P); } } /// isPropertyReadonly - Return true if property is readonly, by searching /// for the property in the class and in its categories and implementations /// bool Sema::isPropertyReadonly(ObjCPropertyDecl *PDecl, ObjCInterfaceDecl *IDecl) { // by far the most common case. if (!PDecl->isReadOnly()) return false; // Even if property is ready only, if interface has a user defined setter, // it is not considered read only. if (IDecl->getInstanceMethod(PDecl->getSetterName())) return false; // Main class has the property as 'readonly'. Must search // through the category list to see if the property's // attribute has been over-ridden to 'readwrite'. for (ObjCCategoryDecl *Category = IDecl->getCategoryList(); Category; Category = Category->getNextClassCategory()) { // Even if property is ready only, if a category has a user defined setter, // it is not considered read only. if (Category->getInstanceMethod(PDecl->getSetterName())) return false; ObjCPropertyDecl *P = Category->FindPropertyDeclaration(PDecl->getIdentifier()); if (P && !P->isReadOnly()) return false; } // Also, check for definition of a setter method in the implementation if // all else failed. if (ObjCMethodDecl *OMD = dyn_cast(CurContext)) { if (ObjCImplementationDecl *IMD = dyn_cast(OMD->getDeclContext())) { if (IMD->getInstanceMethod(PDecl->getSetterName())) return false; } else if (ObjCCategoryImplDecl *CIMD = dyn_cast(OMD->getDeclContext())) { if (CIMD->getInstanceMethod(PDecl->getSetterName())) return false; } } // Lastly, look through the implementation (if one is in scope). if (ObjCImplementationDecl *ImpDecl = IDecl->getImplementation()) if (ImpDecl->getInstanceMethod(PDecl->getSetterName())) return false; // If all fails, look at the super class. if (ObjCInterfaceDecl *SIDecl = IDecl->getSuperClass()) return isPropertyReadonly(PDecl, SIDecl); return true; } /// CollectImmediateProperties - This routine collects all properties in /// the class and its conforming protocols; but not those it its super class. void Sema::CollectImmediateProperties(ObjCContainerDecl *CDecl, llvm::DenseMap& PropMap) { if (ObjCInterfaceDecl *IDecl = dyn_cast(CDecl)) { for (ObjCContainerDecl::prop_iterator P = IDecl->prop_begin(), E = IDecl->prop_end(); P != E; ++P) { ObjCPropertyDecl *Prop = (*P); PropMap[Prop->getIdentifier()] = Prop; } // scan through class's protocols. for (ObjCInterfaceDecl::protocol_iterator PI = IDecl->protocol_begin(), E = IDecl->protocol_end(); PI != E; ++PI) // Exclude property for protocols which conform to class's super-class, // as super-class has to implement the property. if (!ProtocolConformsToSuperClass(IDecl, (*PI))) CollectImmediateProperties((*PI), PropMap); } if (ObjCCategoryDecl *CATDecl = dyn_cast(CDecl)) { if (!CATDecl->IsClassExtension()) for (ObjCContainerDecl::prop_iterator P = CATDecl->prop_begin(), E = CATDecl->prop_end(); P != E; ++P) { ObjCPropertyDecl *Prop = (*P); PropMap[Prop->getIdentifier()] = Prop; } // scan through class's protocols. for (ObjCInterfaceDecl::protocol_iterator PI = CATDecl->protocol_begin(), E = CATDecl->protocol_end(); PI != E; ++PI) CollectImmediateProperties((*PI), PropMap); } else if (ObjCProtocolDecl *PDecl = dyn_cast(CDecl)) { for (ObjCProtocolDecl::prop_iterator P = PDecl->prop_begin(), E = PDecl->prop_end(); P != E; ++P) { ObjCPropertyDecl *Prop = (*P); ObjCPropertyDecl *&PropEntry = PropMap[Prop->getIdentifier()]; if (!PropEntry) PropEntry = Prop; } // scan through protocol's protocols. for (ObjCProtocolDecl::protocol_iterator PI = PDecl->protocol_begin(), E = PDecl->protocol_end(); PI != E; ++PI) CollectImmediateProperties((*PI), PropMap); } } /// CollectClassPropertyImplementations - This routine collects list of /// properties to be implemented in the class. This includes, class's /// and its conforming protocols' properties. static void CollectClassPropertyImplementations(ObjCContainerDecl *CDecl, llvm::DenseMap& PropMap) { if (ObjCInterfaceDecl *IDecl = dyn_cast(CDecl)) { for (ObjCContainerDecl::prop_iterator P = IDecl->prop_begin(), E = IDecl->prop_end(); P != E; ++P) { ObjCPropertyDecl *Prop = (*P); PropMap[Prop->getIdentifier()] = Prop; } for (ObjCInterfaceDecl::protocol_iterator PI = IDecl->protocol_begin(), E = IDecl->protocol_end(); PI != E; ++PI) CollectClassPropertyImplementations((*PI), PropMap); } else if (ObjCProtocolDecl *PDecl = dyn_cast(CDecl)) { for (ObjCProtocolDecl::prop_iterator P = PDecl->prop_begin(), E = PDecl->prop_end(); P != E; ++P) { ObjCPropertyDecl *Prop = (*P); PropMap[Prop->getIdentifier()] = Prop; } // scan through protocol's protocols. for (ObjCProtocolDecl::protocol_iterator PI = PDecl->protocol_begin(), E = PDecl->protocol_end(); PI != E; ++PI) CollectClassPropertyImplementations((*PI), PropMap); } } /// CollectSuperClassPropertyImplementations - This routine collects list of /// properties to be implemented in super class(s) and also coming from their /// conforming protocols. static void CollectSuperClassPropertyImplementations(ObjCInterfaceDecl *CDecl, llvm::DenseMap& PropMap) { if (ObjCInterfaceDecl *SDecl = CDecl->getSuperClass()) { while (SDecl) { CollectClassPropertyImplementations(SDecl, PropMap); SDecl = SDecl->getSuperClass(); } } } /// ProtocolConformsToSuperClass - Returns true if class's given protocol /// conforms to one of its super class's protocols. bool Sema::ProtocolConformsToSuperClass(const ObjCInterfaceDecl *IDecl, const ObjCProtocolDecl *PDecl) { if (const ObjCInterfaceDecl *CDecl = IDecl->getSuperClass()) { for (ObjCInterfaceDecl::protocol_iterator PI = CDecl->protocol_begin(), E = CDecl->protocol_end(); PI != E; ++PI) { if (ProtocolConformsToProtocol((*PI), PDecl)) return true; return ProtocolConformsToSuperClass(CDecl, PDecl); } } return false; } bool Sema::ProtocolConformsToProtocol(const ObjCProtocolDecl *NestedProtocol, const ObjCProtocolDecl *PDecl) { if (PDecl->getIdentifier() == NestedProtocol->getIdentifier()) return true; // scan through protocol's protocols. for (ObjCProtocolDecl::protocol_iterator PI = PDecl->protocol_begin(), E = PDecl->protocol_end(); PI != E; ++PI) if (ProtocolConformsToProtocol(NestedProtocol, (*PI))) return true; return false; } /// LookupPropertyDecl - Looks up a property in the current class and all /// its protocols. ObjCPropertyDecl *Sema::LookupPropertyDecl(const ObjCContainerDecl *CDecl, IdentifierInfo *II) { if (const ObjCInterfaceDecl *IDecl = dyn_cast(CDecl)) { for (ObjCContainerDecl::prop_iterator P = IDecl->prop_begin(), E = IDecl->prop_end(); P != E; ++P) { ObjCPropertyDecl *Prop = (*P); if (Prop->getIdentifier() == II) return Prop; } // scan through class's protocols. for (ObjCInterfaceDecl::protocol_iterator PI = IDecl->protocol_begin(), E = IDecl->protocol_end(); PI != E; ++PI) { ObjCPropertyDecl *Prop = LookupPropertyDecl((*PI), II); if (Prop) return Prop; } } else if (const ObjCProtocolDecl *PDecl = dyn_cast(CDecl)) { for (ObjCProtocolDecl::prop_iterator P = PDecl->prop_begin(), E = PDecl->prop_end(); P != E; ++P) { ObjCPropertyDecl *Prop = (*P); if (Prop->getIdentifier() == II) return Prop; } // scan through protocol's protocols. for (ObjCProtocolDecl::protocol_iterator PI = PDecl->protocol_begin(), E = PDecl->protocol_end(); PI != E; ++PI) { ObjCPropertyDecl *Prop = LookupPropertyDecl((*PI), II); if (Prop) return Prop; } } return 0; } /// DefaultSynthesizeProperties - This routine default synthesizes all /// properties which must be synthesized in class's @implementation. void Sema::DefaultSynthesizeProperties (Scope *S, ObjCImplDecl* IMPDecl, ObjCInterfaceDecl *IDecl) { llvm::DenseMap PropMap; CollectClassPropertyImplementations(IDecl, PropMap); if (PropMap.empty()) return; llvm::DenseMap SuperPropMap; CollectSuperClassPropertyImplementations(IDecl, SuperPropMap); for (llvm::DenseMap::iterator P = PropMap.begin(), E = PropMap.end(); P != E; ++P) { ObjCPropertyDecl *Prop = P->second; // If property to be implemented in the super class, ignore. if (SuperPropMap[Prop->getIdentifier()]) continue; // Is there a matching propery synthesize/dynamic? if (Prop->isInvalidDecl() || Prop->getPropertyImplementation() == ObjCPropertyDecl::Optional || IMPDecl->FindPropertyImplIvarDecl(Prop->getIdentifier())) continue; ActOnPropertyImplDecl(S, IMPDecl->getLocation(), IMPDecl->getLocation(), true, DeclPtrTy::make(IMPDecl), Prop->getIdentifier(), Prop->getIdentifier()); } } void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl, ObjCContainerDecl *CDecl, const llvm::DenseSet& InsMap) { llvm::DenseMap PropMap; CollectImmediateProperties(CDecl, PropMap); if (PropMap.empty()) return; llvm::DenseSet PropImplMap; for (ObjCImplDecl::propimpl_iterator I = IMPDecl->propimpl_begin(), EI = IMPDecl->propimpl_end(); I != EI; ++I) PropImplMap.insert((*I)->getPropertyDecl()); for (llvm::DenseMap::iterator P = PropMap.begin(), E = PropMap.end(); P != E; ++P) { ObjCPropertyDecl *Prop = P->second; // Is there a matching propery synthesize/dynamic? if (Prop->isInvalidDecl() || Prop->getPropertyImplementation() == ObjCPropertyDecl::Optional || PropImplMap.count(Prop)) continue; if (!InsMap.count(Prop->getGetterName())) { Diag(Prop->getLocation(), isa(CDecl) ? diag::warn_setter_getter_impl_required_in_category : diag::warn_setter_getter_impl_required) << Prop->getDeclName() << Prop->getGetterName(); Diag(IMPDecl->getLocation(), diag::note_property_impl_required); } if (!Prop->isReadOnly() && !InsMap.count(Prop->getSetterName())) { Diag(Prop->getLocation(), isa(CDecl) ? diag::warn_setter_getter_impl_required_in_category : diag::warn_setter_getter_impl_required) << Prop->getDeclName() << Prop->getSetterName(); Diag(IMPDecl->getLocation(), diag::note_property_impl_required); } } } void Sema::AtomicPropertySetterGetterRules (ObjCImplDecl* IMPDecl, ObjCContainerDecl* IDecl) { // Rules apply in non-GC mode only if (getLangOptions().getGCMode() != LangOptions::NonGC) return; for (ObjCContainerDecl::prop_iterator I = IDecl->prop_begin(), E = IDecl->prop_end(); I != E; ++I) { ObjCPropertyDecl *Property = (*I); unsigned Attributes = Property->getPropertyAttributes(); // We only care about readwrite atomic property. if ((Attributes & ObjCPropertyDecl::OBJC_PR_nonatomic) || !(Attributes & ObjCPropertyDecl::OBJC_PR_readwrite)) continue; if (const ObjCPropertyImplDecl *PIDecl = IMPDecl->FindPropertyImplDecl(Property->getIdentifier())) { if (PIDecl->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) continue; ObjCMethodDecl *GetterMethod = IMPDecl->getInstanceMethod(Property->getGetterName()); ObjCMethodDecl *SetterMethod = IMPDecl->getInstanceMethod(Property->getSetterName()); if ((GetterMethod && !SetterMethod) || (!GetterMethod && SetterMethod)) { SourceLocation MethodLoc = (GetterMethod ? GetterMethod->getLocation() : SetterMethod->getLocation()); Diag(MethodLoc, diag::warn_atomic_property_rule) << Property->getIdentifier(); Diag(Property->getLocation(), diag::note_property_declare); } } } } /// ProcessPropertyDecl - Make sure that any user-defined setter/getter methods /// have the property type and issue diagnostics if they don't. /// Also synthesize a getter/setter method if none exist (and update the /// appropriate lookup tables. FIXME: Should reconsider if adding synthesized /// methods is the "right" thing to do. void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property, ObjCContainerDecl *CD) { ObjCMethodDecl *GetterMethod, *SetterMethod; GetterMethod = CD->getInstanceMethod(property->getGetterName()); SetterMethod = CD->getInstanceMethod(property->getSetterName()); DiagnosePropertyAccessorMismatch(property, GetterMethod, property->getLocation()); if (SetterMethod) { ObjCPropertyDecl::PropertyAttributeKind CAttr = property->getPropertyAttributes(); if ((!(CAttr & ObjCPropertyDecl::OBJC_PR_readonly)) && Context.getCanonicalType(SetterMethod->getResultType()) != Context.VoidTy) Diag(SetterMethod->getLocation(), diag::err_setter_type_void); if (SetterMethod->param_size() != 1 || ((*SetterMethod->param_begin())->getType() != property->getType())) { Diag(property->getLocation(), diag::warn_accessor_property_type_mismatch) << property->getDeclName() << SetterMethod->getSelector(); Diag(SetterMethod->getLocation(), diag::note_declared_at); } } // Synthesize getter/setter methods if none exist. // Find the default getter and if one not found, add one. // FIXME: The synthesized property we set here is misleading. We almost always // synthesize these methods unless the user explicitly provided prototypes // (which is odd, but allowed). Sema should be typechecking that the // declarations jive in that situation (which it is not currently). if (!GetterMethod) { // 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. GetterMethod = ObjCMethodDecl::Create(Context, property->getLocation(), property->getLocation(), property->getGetterName(), property->getType(), 0, CD, true, false, true, (property->getPropertyImplementation() == ObjCPropertyDecl::Optional) ? ObjCMethodDecl::Optional : ObjCMethodDecl::Required); CD->addDecl(GetterMethod); // FIXME: Eventually this shouldn't be needed, as the lexical context // and the real context should be the same. if (DeclContext *lexicalDC = property->getLexicalDeclContext()) GetterMethod->setLexicalDeclContext(lexicalDC); } else // A user declared getter will be synthesize when @synthesize of // the property with the same name is seen in the @implementation GetterMethod->setSynthesized(true); property->setGetterMethodDecl(GetterMethod); // Skip setter if property is read-only. if (!property->isReadOnly()) { // Find the default setter and if one not found, add one. if (!SetterMethod) { // 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. SetterMethod = ObjCMethodDecl::Create(Context, property->getLocation(), property->getLocation(), property->getSetterName(), Context.VoidTy, 0, CD, true, false, true, (property->getPropertyImplementation() == ObjCPropertyDecl::Optional) ? ObjCMethodDecl::Optional : ObjCMethodDecl::Required); // Invent the arguments for the setter. We don't bother making a // nice name for the argument. ParmVarDecl *Argument = ParmVarDecl::Create(Context, SetterMethod, property->getLocation(), property->getIdentifier(), property->getType(), /*TInfo=*/0, VarDecl::None, VarDecl::None, 0); SetterMethod->setMethodParams(Context, &Argument, 1, 1); CD->addDecl(SetterMethod); // FIXME: Eventually this shouldn't be needed, as the lexical context // and the real context should be the same. if (DeclContext *lexicalDC = property->getLexicalDeclContext()) SetterMethod->setLexicalDeclContext(lexicalDC); } else // A user declared setter will be synthesize when @synthesize of // the property with the same name is seen in the @implementation SetterMethod->setSynthesized(true); property->setSetterMethodDecl(SetterMethod); } // Add any synthesized methods to the global pool. This allows us to // handle the following, which is supported by GCC (and part of the design). // // @interface Foo // @property double bar; // @end // // void thisIsUnfortunate() { // id foo; // double bar = [foo bar]; // } // if (GetterMethod) AddInstanceMethodToGlobalPool(GetterMethod); if (SetterMethod) AddInstanceMethodToGlobalPool(SetterMethod); } void Sema::CheckObjCPropertyAttributes(DeclPtrTy PropertyPtrTy, SourceLocation Loc, unsigned &Attributes) { // FIXME: Improve the reported location. Decl *PDecl = PropertyPtrTy.getAs(); if (!PDecl) return; ObjCPropertyDecl *PropertyDecl = cast(PDecl); QualType PropertyTy = PropertyDecl->getType(); // readonly and readwrite/assign/retain/copy conflict. if ((Attributes & ObjCDeclSpec::DQ_PR_readonly) && (Attributes & (ObjCDeclSpec::DQ_PR_readwrite | ObjCDeclSpec::DQ_PR_assign | ObjCDeclSpec::DQ_PR_copy | ObjCDeclSpec::DQ_PR_retain))) { const char * which = (Attributes & ObjCDeclSpec::DQ_PR_readwrite) ? "readwrite" : (Attributes & ObjCDeclSpec::DQ_PR_assign) ? "assign" : (Attributes & ObjCDeclSpec::DQ_PR_copy) ? "copy" : "retain"; Diag(Loc, (Attributes & (ObjCDeclSpec::DQ_PR_readwrite)) ? diag::err_objc_property_attr_mutually_exclusive : diag::warn_objc_property_attr_mutually_exclusive) << "readonly" << which; } // Check for copy or retain on non-object types. if ((Attributes & (ObjCDeclSpec::DQ_PR_copy | ObjCDeclSpec::DQ_PR_retain)) && !PropertyTy->isObjCObjectPointerType() && !PropertyTy->isBlockPointerType() && !Context.isObjCNSObjectType(PropertyTy) && !PropertyDecl->getAttr()) { Diag(Loc, diag::err_objc_property_requires_object) << (Attributes & ObjCDeclSpec::DQ_PR_copy ? "copy" : "retain"); Attributes &= ~(ObjCDeclSpec::DQ_PR_copy | ObjCDeclSpec::DQ_PR_retain); } // Check for more than one of { assign, copy, retain }. if (Attributes & ObjCDeclSpec::DQ_PR_assign) { if (Attributes & ObjCDeclSpec::DQ_PR_copy) { Diag(Loc, diag::err_objc_property_attr_mutually_exclusive) << "assign" << "copy"; Attributes &= ~ObjCDeclSpec::DQ_PR_copy; } if (Attributes & ObjCDeclSpec::DQ_PR_retain) { Diag(Loc, diag::err_objc_property_attr_mutually_exclusive) << "assign" << "retain"; Attributes &= ~ObjCDeclSpec::DQ_PR_retain; } } else if (Attributes & ObjCDeclSpec::DQ_PR_copy) { if (Attributes & ObjCDeclSpec::DQ_PR_retain) { Diag(Loc, diag::err_objc_property_attr_mutually_exclusive) << "copy" << "retain"; Attributes &= ~ObjCDeclSpec::DQ_PR_retain; } } // Warn if user supplied no assignment attribute, property is // readwrite, and this is an object type. if (!(Attributes & (ObjCDeclSpec::DQ_PR_assign | ObjCDeclSpec::DQ_PR_copy | ObjCDeclSpec::DQ_PR_retain)) && !(Attributes & ObjCDeclSpec::DQ_PR_readonly) && PropertyTy->isObjCObjectPointerType()) { // Skip this warning in gc-only mode. if (getLangOptions().getGCMode() != LangOptions::GCOnly) Diag(Loc, diag::warn_objc_property_no_assignment_attribute); // If non-gc code warn that this is likely inappropriate. if (getLangOptions().getGCMode() == LangOptions::NonGC) Diag(Loc, diag::warn_objc_property_default_assign_on_object); // FIXME: Implement warning dependent on NSCopying being // implemented. See also: // // (please trim this list while you are at it). } if (!(Attributes & ObjCDeclSpec::DQ_PR_copy) && getLangOptions().getGCMode() == LangOptions::GCOnly && PropertyTy->isBlockPointerType()) Diag(Loc, diag::warn_objc_property_copy_missing_on_block); }