From: Steve Naroff Date: Mon, 9 Mar 2009 21:12:44 +0000 (+0000) Subject: Implement property '.' notation on Factory/Class objects. Parser changes aren't very... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=61f72cbd037e58f12cfe90cd442373f44092f030;p=clang Implement property '.' notation on Factory/Class objects. Parser changes aren't very pretty:-( This fixes Implement class setter/getter for properties. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@66465 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/ExprObjC.h b/include/clang/AST/ExprObjC.h index f74e2ab2f9..d312d539a8 100644 --- a/include/clang/AST/ExprObjC.h +++ b/include/clang/AST/ExprObjC.h @@ -205,7 +205,6 @@ private: ObjCPropertyDecl *AsProperty; SourceLocation IdLoc; Stmt *Base; - public: ObjCPropertyRefExpr(ObjCPropertyDecl *PD, QualType t, SourceLocation l, Expr *base) @@ -249,7 +248,10 @@ private: ObjCMethodDecl *Setter; ObjCMethodDecl *Getter; SourceLocation Loc; + // FIXME: Swizzle these into a single pointer. Stmt *Base; + ObjCInterfaceDecl *ClassProp; + SourceLocation ClassLoc; public: ObjCKVCRefExpr(ObjCMethodDecl *getter, @@ -257,7 +259,14 @@ public: ObjCMethodDecl *setter, SourceLocation l, Expr *base) : Expr(ObjCKVCRefExprClass, t), Setter(setter), - Getter(getter), Loc(l), Base(base) { + Getter(getter), Loc(l), Base(base), ClassProp(0), ClassLoc(SourceLocation()) { + } + ObjCKVCRefExpr(ObjCMethodDecl *getter, + QualType t, + ObjCMethodDecl *setter, + SourceLocation l, ObjCInterfaceDecl *C, SourceLocation CL) + : Expr(ObjCKVCRefExprClass, t), Setter(setter), + Getter(getter), Loc(l), Base(0), ClassProp(C), ClassLoc(CL) { } ObjCMethodDecl *getGetterMethod() const { @@ -267,8 +276,10 @@ public: return Setter; } - virtual SourceRange getSourceRange() const { - return SourceRange(getBase()->getLocStart(), Loc); + virtual SourceRange getSourceRange() const { + if (Base) + return SourceRange(getBase()->getLocStart(), Loc); + return SourceRange(ClassLoc, Loc); } const Expr *getBase() const { return cast(Base); } Expr *getBase() { return cast(Base); } diff --git a/include/clang/Basic/IdentifierTable.h b/include/clang/Basic/IdentifierTable.h index 32818a9137..7630811f21 100644 --- a/include/clang/Basic/IdentifierTable.h +++ b/include/clang/Basic/IdentifierTable.h @@ -428,7 +428,19 @@ public: Selector getNullarySelector(IdentifierInfo *ID) { return Selector(ID, 0); } - + + /// constructSetterName - Return the setter name for the given + /// identifier, i.e. "set" + Name where the initial character of Name + /// has been capitalized. + 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()]); + } + // Emit - Emit a SelectorTable to bitcode. void Emit(llvm::Serializer& S) const; diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index 87faf4cf9e..906e50e439 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -1362,6 +1362,14 @@ public: return 0; } + virtual OwningExprResult ActOnClassPropertyRefExpr( + IdentifierInfo &receiverName, + IdentifierInfo &propertyName, + SourceLocation &receiverNameLoc, + SourceLocation &propertyNameLoc) { + return ExprEmpty(); + } + // ActOnClassMessage - used for both unary and keyword messages. // ArgExprs is optional - if it is present, the number of expressions // is obtained from NumArgs. diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index bbe074a8fb..5bf9783cfb 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -1437,6 +1437,10 @@ bool Parser::isDeclarationSpecifier() { default: return false; case tok::identifier: // foo::bar + // Unfortunate hack to support "Class.factoryMethod" notation. + if (getLang().ObjC1 && NextToken().is(tok::period)) + return false; + // Annotate typenames and C++ scope specifiers. If we get one, just // recurse to handle whatever we get. if (TryAnnotateTypeOrScopeToken()) diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 331f3181cd..000cb9d606 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -569,6 +569,28 @@ Parser::OwningExprResult Parser::ParseCastExpression(bool isUnaryExpression, return ParseCastExpression(isUnaryExpression, isAddressOfOperand); } + // Support 'Class.property' notation. + // We don't use isTokObjCMessageIdentifierReceiver(), since it allows + // 'super' (which is inappropriate here). + if (getLang().ObjC1 && + Actions.getTypeName(*Tok.getIdentifierInfo(), + Tok.getLocation(), CurScope) && + NextToken().is(tok::period)) { + IdentifierInfo &ReceiverName = *Tok.getIdentifierInfo(); + SourceLocation IdentLoc = ConsumeToken(); + SourceLocation DotLoc = ConsumeToken(); + + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); + return ExprError(); + } + IdentifierInfo &PropertyName = *Tok.getIdentifierInfo(); + SourceLocation PropertyLoc = ConsumeToken(); + + Res = Actions.ActOnClassPropertyRefExpr(ReceiverName, PropertyName, + IdentLoc, PropertyLoc); + return move(Res); + } // Consume the identifier so that we can see if it is followed by a '('. // Function designators are allowed to be undeclared (C99 6.5.1p2), so we // need to know whether or not this identifier is a function designator or diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index e9bb3d7c31..8a3672d29c 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -199,18 +199,6 @@ Parser::DeclTy *Parser::ParseObjCAtInterfaceDeclaration( return ClsType; } -/// constructSetterName - Return the setter name for the given -/// identifier, i.e. "set" + Name where the initial character of Name -/// has been capitalized. -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()]); -} - /// objc-interface-decl-list: /// empty /// objc-interface-decl-list objc-property-decl [OBJC2] @@ -340,8 +328,9 @@ void Parser::ParseObjCInterfaceDeclList(DeclTy *interfaceDecl, PP.getSelectorTable().getNullarySelector(SelName); IdentifierInfo *SetterName = OCDS.getSetterName(); if (!SetterName) - SetterName = constructSetterName(PP.getIdentifierTable(), - FD.D.getIdentifier()); + SetterName = + SelectorTable::constructSetterName(PP.getIdentifierTable(), + FD.D.getIdentifier()); Selector SetterSel = PP.getSelectorTable().getUnarySelector(SetterName); bool isOverridingProperty = false; diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 10a3a9defd..4ac8ffc0fb 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -1791,6 +1791,12 @@ public: ObjCMethodDecl *LookupPrivateInstanceMethod(Selector Sel, ObjCInterfaceDecl *ClassDecl); + virtual OwningExprResult ActOnClassPropertyRefExpr( + IdentifierInfo &receiverName, + IdentifierInfo &propertyName, + SourceLocation &receiverNameLoc, + SourceLocation &propertyNameLoc); + // ActOnClassMessage - used for both unary and keyword messages. // ArgExprs is optional - if it is present, the number of expressions // is obtained from NumArgs. diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index d96b5e56d4..f37c0d5bad 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -1657,20 +1657,6 @@ CheckExtVectorComponent(QualType baseType, SourceLocation OpLoc, } -/// 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::OwningExprResult Sema::ActOnMemberReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc, tok::TokenKind OpKind, SourceLocation MemberLoc, @@ -1900,8 +1886,9 @@ Sema::ActOnMemberReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc, // If we found a getter then this may be a valid dot-reference, we // will look for the matching setter, in case it is needed. - IdentifierInfo *SetterName = constructSetterName(PP.getIdentifierTable(), - &Member); + IdentifierInfo *SetterName = + SelectorTable::constructSetterName(PP.getIdentifierTable(), &Member); + Selector SetterSel = PP.getSelectorTable().getUnarySelector(SetterName); ObjCMethodDecl *Setter = IFace->lookupInstanceMethod(SetterSel); if (!Setter) { diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index 6b40c89a8c..512a72f9b4 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/lib/Sema/SemaExprObjC.cpp @@ -16,6 +16,8 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/ExprObjC.h" #include "llvm/ADT/SmallString.h" +#include "clang/Lex/Preprocessor.h" + using namespace clang; Sema::ExprResult Sema::ParseObjCStringLiteral(SourceLocation *AtLocs, @@ -267,6 +269,78 @@ ObjCMethodDecl *Sema::LookupPrivateInstanceMethod(Selector Sel, return Method; } +Action::OwningExprResult Sema::ActOnClassPropertyRefExpr( + IdentifierInfo &receiverName, + IdentifierInfo &propertyName, + SourceLocation &receiverNameLoc, + SourceLocation &propertyNameLoc) { + + ObjCInterfaceDecl *IFace = getObjCInterfaceDecl(&receiverName); + + // Search for a declared property first. + + Selector Sel = PP.getSelectorTable().getNullarySelector(&propertyName); + ObjCMethodDecl *Getter = IFace->lookupClassMethod(Sel); + + // If this reference is in an @implementation, check for 'private' methods. + if (!Getter) + if (ObjCMethodDecl *CurMeth = getCurMethodDecl()) + if (ObjCInterfaceDecl *ClassDecl = CurMeth->getClassInterface()) + if (ObjCImplementationDecl *ImpDecl = + ObjCImplementations[ClassDecl->getIdentifier()]) + Getter = ImpDecl->getClassMethod(Sel); + + if (Getter) { + // FIXME: refactor/share with ActOnMemberReference(). + // Check if we can reference this property. + if (DiagnoseUseOfDecl(Getter, propertyNameLoc)) + return ExprError(); + } + + // Look for the matching setter, in case it is needed. + IdentifierInfo *SetterName = + SelectorTable::constructSetterName(PP.getIdentifierTable(), &propertyName); + + Selector SetterSel = PP.getSelectorTable().getUnarySelector(SetterName); + ObjCMethodDecl *Setter = IFace->lookupClassMethod(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->getClassMethod(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]->getClassMethod(SetterSel); + } + } + + if (Setter && DiagnoseUseOfDecl(Setter, propertyNameLoc)) + return ExprError(); + + if (Getter || Setter) { + QualType PType; + + if (Getter) + PType = Getter->getResultType(); + else { + for (ObjCMethodDecl::param_iterator PI = Setter->param_begin(), + E = Setter->param_end(); PI != E; ++PI) + PType = (*PI)->getType(); + } + return Owned(new (Context) ObjCKVCRefExpr(Getter, PType, Setter, + propertyNameLoc, IFace, receiverNameLoc)); + } + return ExprError(Diag(propertyNameLoc, diag::err_property_not_found) + << &propertyName << Context.getObjCInterfaceType(IFace)); +} + + // ActOnClassMessage - used for both unary and keyword messages. // ArgExprs is optional - if it is present, the number of expressions // is obtained from Sel.getNumArgs(). diff --git a/test/Parser/check-objc2-syntax-1.m b/test/Parser/check-objc2-syntax-1.m index b0b0e8a35e..967fa7583c 100644 --- a/test/Parser/check-objc2-syntax-1.m +++ b/test/Parser/check-objc2-syntax-1.m @@ -5,6 +5,6 @@ @end int main (void) { - return Subclass.magicNumber; // expected-error {{unexpected interface name 'Subclass': expected expression}} + return Subclass.magicNumber; } diff --git a/test/SemaObjC/newproperty-class-method-1.m b/test/SemaObjC/newproperty-class-method-1.m new file mode 100644 index 0000000000..177cb3e8fc --- /dev/null +++ b/test/SemaObjC/newproperty-class-method-1.m @@ -0,0 +1,63 @@ +// RUN: clang %s -verify -fsyntax-only + +@interface Subclass ++ (int)magicNumber; ++ (void)setMagicNumber:(int)value; ++ (void)setFakeSetterNumber:(int)value; +@end + +@implementation Subclass +int _magicNumber = 0; ++ (int)magicNumber { + return _magicNumber; +} + ++ (void)setMagicNumber:(int)value { + _magicNumber = value; +} + ++ (void)setFakeSetterNumber:(int)value { + _magicNumber = value; +} + ++ (void) classMeth +{ +#if 0 +// FIXME: implement. + self.magicNumber = 10; + if (self.magicNumber != 10) + abort (); +#endif +} +@end + +int main (void) { + + int a; + Subclass.magicNumber = 2 /*[Subclass setMagicNumber:2]*/; + if (Subclass.magicNumber != 0) + abort (); + if (Subclass.magicNumber != 2) + abort (); + Subclass.magicNumber += 3; + if (Subclass.magicNumber != 5) + abort (); + Subclass.magicNumber -= 5; + if (Subclass.magicNumber != 0) + abort (); + /* We only have a setter in the following case. */ + Subclass.fakeSetterNumber = 123; + + /* We read it using the other getter. */ + if (Subclass.magicNumber != 123) + abort (); + Subclass.fakeSetterNumber = Subclass.magicNumber; + if (Subclass.magicNumber != 123) + abort (); + + Subclass.fakeSetterNumberX = 123; // expected-error{{property 'fakeSetterNumberX' not found on object of type 'Subclass'}} + + /* Test class methods using the new syntax. */ + [Subclass classMeth]; + return 0; +}