Implement property '.' notation on Factory/Class objects. Parser changes aren't very pretty:-(

This fixes <rdar://problem/6496506> Implement class setter/getter for properties.

llvm-svn: 66465
This commit is contained in:
Steve Naroff 2009-03-09 21:12:44 +00:00
parent 70cc9875d8
commit 9527bbfc08
11 changed files with 212 additions and 36 deletions

View File

@ -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<Expr>(Base); }
Expr *getBase() { return cast<Expr>(Base); }

View File

@ -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;

View File

@ -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.

View File

@ -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())

View File

@ -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

View File

@ -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;

View File

@ -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.

View File

@ -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) {

View File

@ -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().

View File

@ -5,6 +5,6 @@
@end
int main (void) {
return Subclass.magicNumber; // expected-error {{unexpected interface name 'Subclass': expected expression}}
return Subclass.magicNumber;
}

View File

@ -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;
}