Objective-C properties: merge attributes when redeclaring 'readonly' as 'readwrite' in an extension.

r251874 stopped back-patching the AST when an Objective-C 'readonly'
property is redeclared in a class extension as 'readwrite'. However,
it did not properly handle merging of Objective-C property attributes
(e.g., getter name, ownership, atomicity) to the redeclaration,
leading to bad metadata. Merge (and check!) those property attributes
so we get the right metadata and reasonable ASTs. Fixes
rdar://problem/23823989.

llvm-svn: 255309
This commit is contained in:
Douglas Gregor 2015-12-10 23:02:09 +00:00
parent f5d34b7b9d
commit 9dd25b7696
8 changed files with 190 additions and 112 deletions

View File

@ -2509,17 +2509,14 @@ public:
void setPropertyAttributes(PropertyAttributeKind PRVal) {
PropertyAttributes |= PRVal;
}
void overwritePropertyAttributes(unsigned PRVal) {
PropertyAttributes = PRVal;
}
PropertyAttributeKind getPropertyAttributesAsWritten() const {
return PropertyAttributeKind(PropertyAttributesAsWritten);
}
bool hasWrittenStorageAttribute() const {
return PropertyAttributesAsWritten & (OBJC_PR_assign | OBJC_PR_copy |
OBJC_PR_unsafe_unretained | OBJC_PR_retain | OBJC_PR_strong |
OBJC_PR_weak);
}
void setPropertyAttributesAsWritten(PropertyAttributeKind PRVal) {
PropertyAttributesAsWritten = PRVal;
}

View File

@ -930,6 +930,9 @@ def warn_missing_explicit_synthesis : Warning <
def warn_property_getter_owning_mismatch : Warning<
"property declared as returning non-retained objects"
"; getter returning retained objects">;
def warn_property_redecl_getter_mismatch : Warning<
"getter name mismatch between property redeclaration (%1) and its original "
"declaration (%0)">, InGroup<PropertyAttr>;
def error_property_setter_ambiguous_use : Error<
"synthesized properties %0 and %1 both claim setter %2 -"
" use of this setter will cause unexpected behavior">;

View File

@ -3057,11 +3057,9 @@ public:
FieldDeclarator &FD,
Selector GetterSel,
Selector SetterSel,
const bool isAssign,
const bool isReadWrite,
const unsigned Attributes,
unsigned &Attributes,
const unsigned AttributesAsWritten,
bool *isOverridingProperty,
QualType T,
TypeSourceInfo *TSI,
tok::ObjCKeywordKind MethodImplKind);
@ -3075,7 +3073,6 @@ public:
FieldDeclarator &FD,
Selector GetterSel,
Selector SetterSel,
const bool isAssign,
const bool isReadWrite,
const unsigned Attributes,
const unsigned AttributesAsWritten,
@ -7333,7 +7330,6 @@ public:
SourceLocation LParenLoc,
FieldDeclarator &FD, ObjCDeclSpec &ODS,
Selector GetterSel, Selector SetterSel,
bool *OverridingProperty,
tok::ObjCKeywordKind MethodImplKind,
DeclContext *lexicalDC = nullptr);

View File

@ -633,7 +633,6 @@ ObjCTypeParamList *Parser::parseObjCTypeParamList() {
void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey,
Decl *CDecl) {
SmallVector<Decl *, 32> allMethods;
SmallVector<Decl *, 16> allProperties;
SmallVector<DeclGroupPtrTy, 8> allTUVariables;
tok::ObjCKeywordKind MethodImplKind = tok::objc_not_keyword;
@ -789,12 +788,9 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey,
SetterSel = SelectorTable::constructSetterSelector(
PP.getIdentifierTable(), PP.getSelectorTable(),
FD.D.getIdentifier());
bool isOverridingProperty = false;
Decl *Property = Actions.ActOnProperty(
getCurScope(), AtLoc, LParenLoc, FD, OCDS, GetterSel, SetterSel,
&isOverridingProperty, MethodImplKind);
if (!isOverridingProperty)
allProperties.push_back(Property);
MethodImplKind);
FD.complete(Property);
};

View File

@ -153,13 +153,26 @@ static unsigned deducePropertyOwnershipFromType(Sema &S, QualType T) {
return 0;
}
static const unsigned OwnershipMask =
(ObjCPropertyDecl::OBJC_PR_assign |
ObjCPropertyDecl::OBJC_PR_retain |
ObjCPropertyDecl::OBJC_PR_copy |
ObjCPropertyDecl::OBJC_PR_weak |
ObjCPropertyDecl::OBJC_PR_strong |
ObjCPropertyDecl::OBJC_PR_unsafe_unretained);
static unsigned getOwnershipRule(unsigned attr) {
return attr & (ObjCPropertyDecl::OBJC_PR_assign |
ObjCPropertyDecl::OBJC_PR_retain |
ObjCPropertyDecl::OBJC_PR_copy |
ObjCPropertyDecl::OBJC_PR_weak |
ObjCPropertyDecl::OBJC_PR_strong |
ObjCPropertyDecl::OBJC_PR_unsafe_unretained);
unsigned result = attr & OwnershipMask;
// From an ownership perspective, assign and unsafe_unretained are
// identical; make sure one also implies the other.
if (result & (ObjCPropertyDecl::OBJC_PR_assign |
ObjCPropertyDecl::OBJC_PR_unsafe_unretained)) {
result |= ObjCPropertyDecl::OBJC_PR_assign |
ObjCPropertyDecl::OBJC_PR_unsafe_unretained;
}
return result;
}
Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc,
@ -168,7 +181,6 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc,
ObjCDeclSpec &ODS,
Selector GetterSel,
Selector SetterSel,
bool *isOverridingProperty,
tok::ObjCKeywordKind MethodImplKind,
DeclContext *lexicalDC) {
unsigned Attributes = ODS.getPropertyAttributes();
@ -182,19 +194,6 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc,
// default is readwrite!
!(Attributes & ObjCDeclSpec::DQ_PR_readonly));
// Property defaults to 'assign' if it is readwrite, unless this is ARC
// and the type is retainable.
bool isAssign;
if (Attributes & (ObjCDeclSpec::DQ_PR_assign |
ObjCDeclSpec::DQ_PR_unsafe_unretained)) {
isAssign = true;
} else if (getOwnershipRule(Attributes) || !isReadWrite) {
isAssign = false;
} else {
isAssign = (!getLangOpts().ObjCAutoRefCount ||
!T->isObjCRetainableType());
}
// Proceed with constructing the ObjCPropertyDecls.
ObjCContainerDecl *ClassDecl = cast<ObjCContainerDecl>(CurContext);
ObjCPropertyDecl *Res = nullptr;
@ -202,11 +201,10 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc,
if (CDecl->IsClassExtension()) {
Res = HandlePropertyInClassExtension(S, AtLoc, LParenLoc,
FD, GetterSel, SetterSel,
isAssign, isReadWrite,
isReadWrite,
Attributes,
ODS.getPropertyAttributes(),
isOverridingProperty, T, TSI,
MethodImplKind);
T, TSI, MethodImplKind);
if (!Res)
return nullptr;
}
@ -214,7 +212,7 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc,
if (!Res) {
Res = CreatePropertyDecl(S, ClassDecl, AtLoc, LParenLoc, FD,
GetterSel, SetterSel, isAssign, isReadWrite,
GetterSel, SetterSel, isReadWrite,
Attributes, ODS.getPropertyAttributes(),
T, TSI, MethodImplKind);
if (lexicalDC)
@ -342,12 +340,15 @@ static bool LocPropertyAttribute( ASTContext &Context, const char *attrName,
/// Check for a mismatch in the atomicity of the given properties.
static void checkAtomicPropertyMismatch(Sema &S,
ObjCPropertyDecl *OldProperty,
ObjCPropertyDecl *NewProperty) {
ObjCPropertyDecl *NewProperty,
bool PropagateAtomicity) {
// If the atomicity of both matches, we're done.
bool OldIsAtomic =
(OldProperty->getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nonatomic) == 0;
(OldProperty->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_nonatomic)
== 0;
bool NewIsAtomic =
(NewProperty->getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nonatomic) == 0;
(NewProperty->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_nonatomic)
== 0;
if (OldIsAtomic == NewIsAtomic) return;
// Determine whether the given property is readonly and implicitly
@ -355,18 +356,36 @@ static void checkAtomicPropertyMismatch(Sema &S,
auto isImplicitlyReadonlyAtomic = [](ObjCPropertyDecl *Property) -> bool {
// Is it readonly?
auto Attrs = Property->getPropertyAttributes();
if ((Attrs & ObjCDeclSpec::DQ_PR_readonly) == 0) return false;
if ((Attrs & ObjCPropertyDecl::OBJC_PR_readonly) == 0) return false;
// Is it nonatomic?
if (Attrs & ObjCDeclSpec::DQ_PR_nonatomic) return false;
if (Attrs & ObjCPropertyDecl::OBJC_PR_nonatomic) return false;
// Was 'atomic' specified directly?
if (Property->getPropertyAttributesAsWritten() & ObjCDeclSpec::DQ_PR_atomic)
if (Property->getPropertyAttributesAsWritten() &
ObjCPropertyDecl::OBJC_PR_atomic)
return false;
return true;
};
// If we're allowed to propagate atomicity, and the new property did
// not specify atomicity at all, propagate.
const unsigned AtomicityMask =
(ObjCPropertyDecl::OBJC_PR_atomic | ObjCPropertyDecl::OBJC_PR_nonatomic);
if (PropagateAtomicity &&
((NewProperty->getPropertyAttributesAsWritten() & AtomicityMask) == 0)) {
unsigned Attrs = NewProperty->getPropertyAttributes();
Attrs = Attrs & ~AtomicityMask;
if (OldIsAtomic)
Attrs |= ObjCPropertyDecl::OBJC_PR_atomic;
else
Attrs |= ObjCPropertyDecl::OBJC_PR_nonatomic;
NewProperty->overwritePropertyAttributes(Attrs);
return;
}
// One of the properties is atomic; if it's a readonly property, and
// 'atomic' wasn't explicitly specified, we're okay.
if ((OldIsAtomic && isImplicitlyReadonlyAtomic(OldProperty)) ||
@ -393,11 +412,9 @@ Sema::HandlePropertyInClassExtension(Scope *S,
SourceLocation LParenLoc,
FieldDeclarator &FD,
Selector GetterSel, Selector SetterSel,
const bool isAssign,
const bool isReadWrite,
const unsigned Attributes,
unsigned &Attributes,
const unsigned AttributesAsWritten,
bool *isOverridingProperty,
QualType T,
TypeSourceInfo *TSI,
tok::ObjCKeywordKind MethodImplKind) {
@ -411,7 +428,6 @@ Sema::HandlePropertyInClassExtension(Scope *S,
// already declared.
if (!CCPrimary) {
Diag(CDecl->getLocation(), diag::err_continuation_class);
*isOverridingProperty = true;
return nullptr;
}
@ -427,11 +443,73 @@ Sema::HandlePropertyInClassExtension(Scope *S,
return nullptr;
}
// Check for consistency with the previous declaration, if there is one.
if (PIDecl) {
// A readonly property declared in the primary class can be refined
// by adding a readwrite property within an extension.
// Anything else is an error.
if (!(PIDecl->isReadOnly() && isReadWrite)) {
// 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
// declaration to be readonly.
unsigned diag =
(Attributes & ObjCDeclSpec::DQ_PR_readwrite) &&
(PIDecl->getPropertyAttributesAsWritten() &
ObjCPropertyDecl::OBJC_PR_readwrite)
? diag::err_use_continuation_class_redeclaration_readwrite
: diag::err_use_continuation_class;
Diag(AtLoc, diag)
<< CCPrimary->getDeclName();
Diag(PIDecl->getLocation(), diag::note_property_declare);
return nullptr;
}
// Check for consistency of getters.
if (PIDecl->getGetterName() != GetterSel) {
// If the getter was written explicitly, complain.
if (AttributesAsWritten & ObjCDeclSpec::DQ_PR_getter) {
Diag(AtLoc, diag::warn_property_redecl_getter_mismatch)
<< PIDecl->getGetterName() << GetterSel;
Diag(PIDecl->getLocation(), diag::note_property_declare);
}
// Always adopt the getter from the original declaration.
GetterSel = PIDecl->getGetterName();
Attributes |= ObjCDeclSpec::DQ_PR_getter;
}
// Check consistency of ownership.
unsigned ExistingOwnership
= getOwnershipRule(PIDecl->getPropertyAttributes());
unsigned NewOwnership = getOwnershipRule(Attributes);
if (ExistingOwnership && NewOwnership != ExistingOwnership) {
// If the ownership was written explicitly, complain.
if (getOwnershipRule(AttributesAsWritten)) {
Diag(AtLoc, diag::warn_property_attr_mismatch);
Diag(PIDecl->getLocation(), diag::note_property_declare);
}
// Take the ownership from the original property.
Attributes = (Attributes & ~OwnershipMask) | ExistingOwnership;
}
// If the redeclaration is 'weak' but the original property is not,
if ((Attributes & ObjCPropertyDecl::OBJC_PR_weak) &&
!(PIDecl->getPropertyAttributesAsWritten()
& ObjCPropertyDecl::OBJC_PR_weak) &&
PIDecl->getType()->getAs<ObjCObjectPointerType>() &&
PIDecl->getType().getObjCLifetime() == Qualifiers::OCL_None) {
Diag(AtLoc, diag::warn_property_implicitly_mismatched);
Diag(PIDecl->getLocation(), diag::note_property_declare);
}
}
// Create a new ObjCPropertyDecl with the DeclContext being
// the class extension.
ObjCPropertyDecl *PDecl = CreatePropertyDecl(S, CDecl, AtLoc, LParenLoc,
FD, GetterSel, SetterSel,
isAssign, isReadWrite,
isReadWrite,
Attributes, AttributesAsWritten,
T, TSI, MethodImplKind, DC);
@ -464,57 +542,10 @@ Sema::HandlePropertyInClassExtension(Scope *S,
return nullptr;
}
}
// A readonly property declared in the primary class can be refined
// by adding a readwrite property within an extension.
// Anything else is an error.
unsigned PIkind = PIDecl->getPropertyAttributesAsWritten();
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
// declaration to be readonly.
unsigned diag =
(Attributes & ObjCDeclSpec::DQ_PR_readwrite) &&
(PIkind & ObjCPropertyDecl::OBJC_PR_readwrite)
? diag::err_use_continuation_class_redeclaration_readwrite
: diag::err_use_continuation_class;
Diag(AtLoc, diag)
<< CCPrimary->getDeclName();
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.
checkAtomicPropertyMismatch(*this, PIDecl, PDecl);
*isOverridingProperty = true;
checkAtomicPropertyMismatch(*this, PIDecl, PDecl, true);
// Make sure getter/setter are appropriately synthesized.
ProcessPropertyDecl(PDecl);
@ -528,7 +559,6 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S,
FieldDeclarator &FD,
Selector GetterSel,
Selector SetterSel,
const bool isAssign,
const bool isReadWrite,
const unsigned Attributes,
const unsigned AttributesAsWritten,
@ -538,10 +568,23 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S,
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
// Property defaults to 'assign' if it is readwrite, unless this is ARC
// and the type is retainable.
bool isAssign;
if (Attributes & (ObjCDeclSpec::DQ_PR_assign |
ObjCDeclSpec::DQ_PR_unsafe_unretained)) {
isAssign = true;
} else if (getOwnershipRule(Attributes) || !isReadWrite) {
isAssign = false;
} else {
isAssign = (!getLangOpts().ObjCAutoRefCount ||
!T->isObjCRetainableType());
}
// Issue a warning if property is 'assign' as default and its
// object, which is gc'able conforms to NSCopying protocol
if (getLangOpts().getGC() != LangOptions::NonGC &&
isAssign && !(Attributes & ObjCDeclSpec::DQ_PR_assign))
isAssign && !(Attributes & ObjCDeclSpec::DQ_PR_assign)) {
if (const ObjCObjectPointerType *ObjPtrTy =
T->getAs<ObjCObjectPointerType>()) {
ObjCInterfaceDecl *IDecl = ObjPtrTy->getObjectType()->getInterface();
@ -551,6 +594,7 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S,
if (IDecl->ClassImplementsProtocol(PNSCopying, true))
Diag(AtLoc, diag::warn_implements_nscopying) << PropertyId;
}
}
if (T->isObjCObjectType()) {
SourceLocation StarLoc = TInfo->getTypeLoc().getLocEnd();
@ -802,6 +846,31 @@ DiagnosePropertyMismatchDeclInProtocols(Sema &S, SourceLocation AtLoc,
S.Diag(AtLoc, diag::note_property_synthesize);
}
/// Determine whether any storage attributes were written on the property.
static bool hasWrittenStorageAttribute(ObjCPropertyDecl *Prop) {
if (Prop->getPropertyAttributesAsWritten() & OwnershipMask) return true;
// If this is a readwrite property in a class extension that refines
// a readonly property in the original class definition, check it as
// well.
// If it's a readonly property, we're not interested.
if (Prop->isReadOnly()) return false;
// Is it declared in an extension?
auto Category = dyn_cast<ObjCCategoryDecl>(Prop->getDeclContext());
if (!Category || !Category->IsClassExtension()) return false;
// Find the corresponding property in the primary class definition.
auto OrigClass = Category->getClassInterface();
for (auto Found : OrigClass->lookup(Prop->getDeclName())) {
if (ObjCPropertyDecl *OrigProp = dyn_cast<ObjCPropertyDecl>(Found))
return OrigProp->getPropertyAttributesAsWritten() & OwnershipMask;
}
return false;
}
/// ActOnPropertyImplDecl - This routine performs semantic checks and
/// builds the AST node for a property implementation declaration; declared
/// as \@synthesize or \@dynamic.
@ -1029,7 +1098,7 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S,
// It's an error if we have to do this and the user didn't
// explicitly write an ownership attribute on the property.
if (!property->hasWrittenStorageAttribute() &&
if (!hasWrittenStorageAttribute(property) &&
!(kind & ObjCPropertyDecl::OBJC_PR_strong)) {
Diag(PropertyDiagLoc,
diag::err_arc_objc_property_default_assign_on_object);
@ -1364,7 +1433,7 @@ Sema::DiagnosePropertyMismatch(ObjCPropertyDecl *Property,
// Check for nonatomic; note that nonatomic is effectively
// meaningless for readonly properties, so don't diagnose if the
// atomic property is 'readonly'.
checkAtomicPropertyMismatch(*this, SuperProperty, Property);
checkAtomicPropertyMismatch(*this, SuperProperty, Property, false);
if (Property->getSetterName() != SuperProperty->getSetterName()) {
Diag(Property->getLocation(), diag::warn_property_attribute)
<< Property->getDeclName() << "setter" << inheritedName;

View File

@ -1,9 +1,10 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm %s -o - | FileCheck %s
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-weak -fobjc-runtime-has-weak -emit-llvm %s -o - | FileCheck %s
// Checks metadata for properties in a few cases.
// Property from a class extension:
__attribute__((objc_root_class))
@interface Foo
@end
@ -20,12 +21,17 @@
// CHECK: @"\01l_OBJC_$_PROP_LIST_Foo" = private global { i32, i32, [1 x %struct._prop_t] }
// Readonly property in interface made readwrite in a category:
__attribute__((objc_root_class))
@interface FooRO
@property (readonly) int evolvingprop;
@property (nonatomic,readonly,getter=isBooleanProp) int booleanProp;
@property (nonatomic,readonly,weak) Foo *weakProp;
@end
@interface FooRO ()
@property int evolvingprop;
@property int booleanProp;
@property Foo *weakProp;
@end
@implementation FooRO
@ -34,6 +40,8 @@
// Metadata for _evolvingprop should be present, and PROP_LIST for FooRO should
// still have only one entry, and the one entry should point to the version of
// the property with a getter and setter.
// CHECK: [[getter:@OBJC_PROP_NAME_ATTR[^ ]+]] = private global [13 x i8] c"evolvingprop\00"
// CHECK: [[setter:@OBJC_PROP_NAME_ATTR[^ ]+]] = private global [18 x i8] c"Ti,V_evolvingprop\00",
// CHECK: @"\01l_OBJC_$_PROP_LIST_FooRO" = private global { i32, i32, [1 x %struct._prop_t] }{{.*}}[[getter]]{{.*}}[[setter]]
// CHECK: [[evolvinggetter:@OBJC_PROP_NAME_ATTR[^ ]+]] = private global [13 x i8] c"evolvingprop\00"
// CHECK: [[evolvingsetter:@OBJC_PROP_NAME_ATTR[^ ]+]] = private global [18 x i8] c"Ti,V_evolvingprop\00",
// CHECK: [[booleanmetadata:@OBJC_PROP_NAME_ATTR[^ ]+]] = private global [34 x i8] c"Ti,N,GisBooleanProp,V_booleanProp\00"
// CHECK: [[weakmetadata:@OBJC_PROP_NAME_ATTR[^ ]+]] = private global [23 x i8] c"T@\22Foo\22,W,N,V_weakProp\00"
// CHECK: @"\01l_OBJC_$_PROP_LIST_FooRO" = private global { i32, i32, [3 x %struct._prop_t] }{{.*}}[[evolvinggetter]]{{.*}}[[evolvingsetter]]{{.*}}[[booleanmetadata]]

View File

@ -31,3 +31,12 @@ typedef signed char BOOL;
@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'}}
@end
__attribute__((objc_root_class))
@interface A
@property (nonatomic, readonly, getter=isAvailable) int available; // expected-note{{property declared here}}
@end
@interface A ()
@property (nonatomic, assign, getter=wasAvailable) int available; // expected-warning{{getter name mismatch between property redeclaration ('wasAvailable') and its original declaration ('isAvailable')}}
@end

View File

@ -22,15 +22,15 @@
@end
@interface AtomicInheritanceSub2 : AtomicInheritanceSuper2
@property (nonatomic, readwrite, retain) A *property; // FIXME: should be okay
@property (nonatomic, readwrite, retain) A *property;
@end
@interface ReadonlyAtomic
@property (readonly, nonatomic) A *property; // expected-note{{property declared here}}
@property (readonly, nonatomic) A *property;
@end
@interface ReadonlyAtomic ()
@property (readwrite) A *property; // expected-warning{{'atomic' attribute on property 'property' does not match the property inherited from 'ReadonlyAtomic'}}
@property (readwrite) A *property;
@end
// Readonly, atomic public redeclaration of property in subclass.