forked from OSchip/llvm-project
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:
parent
70cc9875d8
commit
9527bbfc08
|
@ -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); }
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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().
|
||||
|
|
|
@ -5,6 +5,6 @@
|
|||
@end
|
||||
|
||||
int main (void) {
|
||||
return Subclass.magicNumber; // expected-error {{unexpected interface name 'Subclass': expected expression}}
|
||||
return Subclass.magicNumber;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue