From ba8d2d684e74a20bef03828c21c991d222c7e9e5 Mon Sep 17 00:00:00 2001 From: Fariborz Jahanian Date: Sat, 22 Nov 2008 20:25:50 +0000 Subject: [PATCH] Support for implicit property assignment. Error assigning to 'implicit' property with no 'setter'. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@59878 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Expr.h | 3 +- include/clang/AST/ExprObjC.h | 6 +-- include/clang/Basic/DiagnosticKinds.def | 3 ++ lib/AST/Expr.cpp | 6 +++ lib/AST/StmtSerialization.cpp | 2 +- lib/Sema/SemaExpr.cpp | 44 +++++++++++++++++-- .../SemaObjC/property-error-readonly-assign.m | 10 ++++- 7 files changed, 64 insertions(+), 10 deletions(-) diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index e7aab276a4..387ff7111b 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -104,7 +104,8 @@ public: MLV_ConstQualified, MLV_ArrayType, MLV_NotBlockQualified, - MLV_ReadonlyProperty + MLV_ReadonlyProperty, + MLV_NoSetterProperty }; isModifiableLvalueResult isModifiableLvalue(ASTContext &Ctx) const; diff --git a/include/clang/AST/ExprObjC.h b/include/clang/AST/ExprObjC.h index 9cb9af5249..25a3b5fcec 100644 --- a/include/clang/AST/ExprObjC.h +++ b/include/clang/AST/ExprObjC.h @@ -251,17 +251,15 @@ private: public: ObjCKVCRefExpr(ObjCMethodDecl *getter, QualType t, + ObjCMethodDecl *setter, SourceLocation l, Expr *base) - : Expr(ObjCKVCRefExprClass, t), Setter(0), + : Expr(ObjCKVCRefExprClass, t), Setter(setter), Getter(getter), Loc(l), Base(base) { } ObjCMethodDecl *getGetterMethod() const { return Getter; } - void setSetterMethod(ObjCMethodDecl *setter) { - Setter = setter; - } ObjCMethodDecl *getSetterMethod() const { return Setter; } diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index 9e55eb37af..48cdb1f85a 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -543,6 +543,9 @@ DIAG(error_property_ivar_type, ERROR, "type of property '%0' does not match type of ivar '%1'") DIAG(error_readonly_property_assignment, ERROR, "assigning to property with 'readonly' attribute not allowed") +DIAG(error_nosetter_property_assignment, ERROR, + "setter method is needed to assign to object using property" + " assignment syntax") DIAG(warn_readonly_property, WARNING, "attribute 'readonly' of property '%0' restricts attribute " "'readwrite' of property inherited from '%1'") diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 6a15ea68a5..0ff345a1a8 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -557,6 +557,12 @@ Expr::isModifiableLvalueResult Expr::isModifiableLvalue(ASTContext &Ctx) const { return MLV_ReadonlyProperty; } } + // Assigning to an 'implicit' property? + if (getStmtClass() == ObjCKVCRefExprClass) { + const ObjCKVCRefExpr* KVCExpr = cast(this); + if (KVCExpr->getSetterMethod() == 0) + return MLV_NoSetterProperty; + } return MLV_Valid; } diff --git a/lib/AST/StmtSerialization.cpp b/lib/AST/StmtSerialization.cpp index 2d6f3c7e8a..8bf7039685 100644 --- a/lib/AST/StmtSerialization.cpp +++ b/lib/AST/StmtSerialization.cpp @@ -1173,7 +1173,7 @@ ObjCKVCRefExpr* ObjCKVCRefExpr::CreateImpl(Deserializer& D, ASTContext& C) { SourceLocation Loc = SourceLocation::ReadVal(D); QualType T = QualType::ReadVal(D); - ObjCKVCRefExpr* dr = new ObjCKVCRefExpr(NULL,T,Loc,0); + ObjCKVCRefExpr* dr = new ObjCKVCRefExpr(NULL,T,NULL,Loc,0); D.ReadPtr(dr->Setter,false); D.ReadPtr(dr->Getter,false); return dr; diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 8474548740..ef3ead4fa2 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -1090,6 +1090,20 @@ CheckExtVectorComponent(QualType baseType, SourceLocation OpLoc, return VT; // should never get here (a typedef type should always be found). } +/// constructSetterName - Return the setter name for the given +/// identifier, i.e. "set" + Name where the initial character of Name +/// has been capitalized. +// FIXME: Merge with same routine in Parser. But where should this +// live? +static IdentifierInfo *constructSetterName(IdentifierTable &Idents, + const IdentifierInfo *Name) { + llvm::SmallString<100> SelectorName; + SelectorName = "set"; + SelectorName.append(Name->getName(), Name->getName()+Name->getLength()); + SelectorName[3] = toupper(SelectorName[3]); + return &Idents.get(&SelectorName[0], &SelectorName[SelectorName.size()]); +} + Action::ExprResult Sema:: ActOnMemberReferenceExpr(ExprTy *Base, SourceLocation OpLoc, tok::TokenKind OpKind, SourceLocation MemberLoc, @@ -1198,9 +1212,30 @@ ActOnMemberReferenceExpr(ExprTy *Base, SourceLocation OpLoc, } if (Getter) { // If we found a getter then this may be a valid dot-reference, we - // will look for the matching setter, if it is needed. But we don't - // know this yet. - return new ObjCKVCRefExpr(Getter, Getter->getResultType(), + // will look for the matching setter, in case it is needed. + IdentifierInfo *SetterName = constructSetterName(PP.getIdentifierTable(), + &Member); + Selector SetterSel = PP.getSelectorTable().getUnarySelector(SetterName); + ObjCMethodDecl *Setter = IFace->lookupInstanceMethod(SetterSel); + if (!Setter) { + // If this reference is in an @implementation, also check for 'private' + // methods. + if (ObjCMethodDecl *CurMeth = getCurMethodDecl()) + if (ObjCInterfaceDecl *ClassDecl = CurMeth->getClassInterface()) + if (ObjCImplementationDecl *ImpDecl = + ObjCImplementations[ClassDecl->getIdentifier()]) + Setter = ImpDecl->getInstanceMethod(SetterSel); + } + // Look through local category implementations associated with the class. + if (!Setter) { + for (unsigned i = 0; i < ObjCCategoryImpls.size() && !Setter; i++) { + if (ObjCCategoryImpls[i]->getClassInterface() == IFace) + Setter = ObjCCategoryImpls[i]->getInstanceMethod(SetterSel); + } + } + + // FIXME: we must check that the setter has property type. + return new ObjCKVCRefExpr(Getter, Getter->getResultType(), Setter, MemberLoc, BaseExpr); } } @@ -2522,6 +2557,9 @@ static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) { case Expr::MLV_ReadonlyProperty: Diag = diag::error_readonly_property_assignment; break; + case Expr::MLV_NoSetterProperty: + Diag = diag::error_nosetter_property_assignment; + break; } if (NeedType) diff --git a/test/SemaObjC/property-error-readonly-assign.m b/test/SemaObjC/property-error-readonly-assign.m index 0a49ee4809..673dc25aa8 100644 --- a/test/SemaObjC/property-error-readonly-assign.m +++ b/test/SemaObjC/property-error-readonly-assign.m @@ -6,8 +6,16 @@ @property int ok; @end -void f0(A *a) { +@interface B + -(void) setOk:(int)arg; + -(int) x; + -(int) ok; +@end + +void f0(A *a, B* b) { a.x = 10; // expected-error {{assigning to property with 'readonly' attribute not allowed}} a.ok = 20; + b.x = 10; // expected-error {{setter method is needed to assign to object using property assignment syntax}} + b.ok = 20; } -- 2.40.0