Patch to supprt case of readonly property being

assigned to when it has user declared setter method
defined in the class implementation (but no declaration in
the class itself).

llvm-svn: 62098
This commit is contained in:
Fariborz Jahanian 2009-01-12 19:55:42 +00:00
parent df5598444a
commit 8e1555c7c3
7 changed files with 109 additions and 43 deletions

View File

@ -405,7 +405,6 @@ public:
ObjCCategoryDecl *FindCategoryDeclaration(IdentifierInfo *CategoryId) const;
ObjCIvarDecl *FindIvarDeclaration(IdentifierInfo *IvarId) const;
bool isPropertyReadonly(ObjCPropertyDecl *PropertyDecl) const;
typedef ObjCList<ObjCProtocolDecl>::iterator protocol_iterator;
protocol_iterator protocol_begin() const {return ReferencedProtocols.begin();}

View File

@ -251,34 +251,6 @@ void ObjCMethodDecl::setMethodParams(ParmVarDecl **NewParamInfo,
}
}
/// isPropertyReadonly - Return true if property is readonly, by searching
/// for the property in the class and in its categories.
///
bool ObjCInterfaceDecl::isPropertyReadonly(ObjCPropertyDecl *PDecl) const
{
// Even if property is ready only, if interface has a user defined setter,
// it is not considered read only.
if (!PDecl->isReadOnly() || 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 = 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;
}
return true;
}
/// FindCategoryDeclaration - Finds category declaration in the list of
/// categories for this class and returns it. Name of the category is passed
/// in 'CategoryId'. If category not found, return 0;

View File

@ -604,19 +604,7 @@ Expr::isModifiableLvalueResult Expr::isModifiableLvalue(ASTContext &Ctx) const {
if (!BDR->isByRef() && isa<VarDecl>(BDR->getDecl()))
return MLV_NotBlockQualified;
}
// Assigning to a readonly property?
if (getStmtClass() == ObjCPropertyRefExprClass) {
const ObjCPropertyRefExpr* PropExpr = cast<ObjCPropertyRefExpr>(this);
if (ObjCPropertyDecl *PDecl = PropExpr->getProperty()) {
QualType BaseType = PropExpr->getBase()->getType();
if (const PointerType *PTy = BaseType->getAsPointerType())
if (const ObjCInterfaceType *IFTy =
PTy->getPointeeType()->getAsObjCInterfaceType())
if (ObjCInterfaceDecl *IFace = IFTy->getDecl())
if (IFace->isPropertyReadonly(PDecl))
return MLV_ReadonlyProperty;
}
}
// Assigning to an 'implicit' property?
else if (getStmtClass() == ObjCKVCRefExprClass) {
const ObjCKVCRefExpr* KVCExpr = cast<ObjCKVCRefExpr>(this);

View File

@ -538,6 +538,9 @@ public:
ObjCMethodDecl *IntfMethod);
NamespaceDecl *GetStdNamespace();
bool isPropertyReadonly(ObjCPropertyDecl *PropertyDecl,
ObjCInterfaceDecl *IDecl) const;
/// CheckProtocolMethodDefs - This routine checks unimplemented
/// methods declared in protocol, and those referenced by it.

View File

@ -699,6 +699,51 @@ void Sema::WarnConflictingTypedMethods(ObjCMethodDecl *ImpMethodDecl,
}
}
/// 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) const {
// 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<ObjCMethodDecl>(CurContext)) {
if (ObjCImplementationDecl *IMD =
dyn_cast<ObjCImplementationDecl>(OMD->getDeclContext())) {
if (IMD->getInstanceMethod(PDecl->getSetterName()))
return false;
}
else if (ObjCCategoryImplDecl *CIMD =
dyn_cast<ObjCCategoryImplDecl>(OMD->getDeclContext())) {
if (CIMD->getInstanceMethod(PDecl->getSetterName()))
return false;
}
}
return true;
}
/// FIXME: Type hierarchies in Objective-C can be deep. We could most
/// likely improve the efficiency of selector lookups and type
/// checking by associating with each protocol / interface / category

View File

@ -2920,10 +2920,33 @@ inline QualType Sema::CheckLogicalOperands( // C99 6.5.[13,14]
return InvalidOperands(Loc, lex, rex);
}
/// IsReadonlyProperty - Verify that otherwise a valid l-value expression
/// is a read-only property; return true if so. A readonly property expression
/// depends on various declarations and thus must be treated specially.
///
static bool IsReadonlyProperty(Expr *E, Sema &S)
{
if (E->getStmtClass() == Expr::ObjCPropertyRefExprClass) {
const ObjCPropertyRefExpr* PropExpr = cast<ObjCPropertyRefExpr>(E);
if (ObjCPropertyDecl *PDecl = PropExpr->getProperty()) {
QualType BaseType = PropExpr->getBase()->getType();
if (const PointerType *PTy = BaseType->getAsPointerType())
if (const ObjCInterfaceType *IFTy =
PTy->getPointeeType()->getAsObjCInterfaceType())
if (ObjCInterfaceDecl *IFace = IFTy->getDecl())
if (S.isPropertyReadonly(PDecl, IFace))
return true;
}
}
return false;
}
/// CheckForModifiableLvalue - Verify that E is a modifiable lvalue. If not,
/// emit an error and return true. If so, return false.
static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) {
Expr::isModifiableLvalueResult IsLV = E->isModifiableLvalue(S.Context);
Expr::isModifiableLvalueResult IsLV = E->isModifiableLvalue(S.Context);
if (IsLV == Expr::MLV_Valid && IsReadonlyProperty(E, S))
IsLV = Expr::MLV_ReadonlyProperty;
if (IsLV == Expr::MLV_Valid)
return false;

View File

@ -23,3 +23,39 @@
self.z = 2; // expected-error {{assigning to property with 'readonly' attribute not allowed}}
}
@end
// Test when property is 'readonly' but it has a setter in
// its implementation only.
@interface I1 {
}
@property(readonly) int identifier;
@end
@implementation I1
@dynamic identifier;
- (void)setIdentifier:(int)ident {}
- (id)initWithIdentifier:(int)Arg {
self.identifier = 0;
}
@end
// Also in a category implementation
@interface I1(CAT)
@property(readonly) int rprop;
@end
@implementation I1(CAT)
@dynamic rprop;
- (void)setRprop:(int)ident {}
- (id)initWithIdentifier:(int)Arg {
self.rprop = 0;
}
@end