[index/AST] Add references for ObjC getter=/setter= property attributes and related property getter/setter role fixes

This enhances the AST to keep track of locations of the names in those ObjC property attributes, and reports them for indexing.

Patch by Nathan Hawes!
https://reviews.llvm.org/D30907

llvm-svn: 297972
This commit is contained in:
Argyrios Kyrtzidis 2017-03-16 18:25:40 +00:00
parent 4377314a98
commit 194b28ebb1
11 changed files with 148 additions and 42 deletions

View File

@ -743,6 +743,8 @@ private:
Selector GetterName; // getter name of NULL if no getter
Selector SetterName; // setter name of NULL if no setter
SourceLocation GetterNameLoc; // location of the getter attribute's value
SourceLocation SetterNameLoc; // location of the setter attribute's value
ObjCMethodDecl *GetterMethodDecl; // Declaration of getter instance method
ObjCMethodDecl *SetterMethodDecl; // Declaration of setter instance method
@ -855,10 +857,18 @@ public:
}
Selector getGetterName() const { return GetterName; }
void setGetterName(Selector Sel) { GetterName = Sel; }
SourceLocation getGetterNameLoc() const { return GetterNameLoc; }
void setGetterName(Selector Sel, SourceLocation Loc) {
GetterName = Sel;
GetterNameLoc = Loc;
}
Selector getSetterName() const { return SetterName; }
void setSetterName(Selector Sel) { SetterName = Sel; }
SourceLocation getSetterNameLoc() const { return SetterNameLoc; }
void setSetterName(Selector Sel, SourceLocation Loc) {
SetterName = Sel;
SetterNameLoc = Loc;
}
ObjCMethodDecl *getGetterMethodDecl() const { return GetterMethodDecl; }
void setGetterMethodDecl(ObjCMethodDecl *gDecl) { GetterMethodDecl = gDecl; }

View File

@ -861,11 +861,19 @@ public:
const IdentifierInfo *getGetterName() const { return GetterName; }
IdentifierInfo *getGetterName() { return GetterName; }
void setGetterName(IdentifierInfo *name) { GetterName = name; }
SourceLocation getGetterNameLoc() const { return GetterNameLoc; }
void setGetterName(IdentifierInfo *name, SourceLocation loc) {
GetterName = name;
GetterNameLoc = loc;
}
const IdentifierInfo *getSetterName() const { return SetterName; }
IdentifierInfo *getSetterName() { return SetterName; }
void setSetterName(IdentifierInfo *name) { SetterName = name; }
SourceLocation getSetterNameLoc() const { return SetterNameLoc; }
void setSetterName(IdentifierInfo *name, SourceLocation loc) {
SetterName = name;
SetterNameLoc = loc;
}
private:
// FIXME: These two are unrelated and mutually exclusive. So perhaps
@ -882,6 +890,9 @@ private:
IdentifierInfo *GetterName; // getter name or NULL if no getter
IdentifierInfo *SetterName; // setter name or NULL if no setter
SourceLocation GetterNameLoc; // location of the getter attribute's value
SourceLocation SetterNameLoc; // location of the setter attribute's value
};
/// \brief Represents a C++ unqualified-id that has been parsed.

View File

@ -3266,7 +3266,9 @@ public:
SourceLocation LParenLoc,
FieldDeclarator &FD,
Selector GetterSel,
SourceLocation GetterNameLoc,
Selector SetterSel,
SourceLocation SetterNameLoc,
const bool isReadWrite,
unsigned &Attributes,
const unsigned AttributesAsWritten,
@ -3282,7 +3284,9 @@ public:
SourceLocation LParenLoc,
FieldDeclarator &FD,
Selector GetterSel,
SourceLocation GetterNameLoc,
Selector SetterSel,
SourceLocation SetterNameLoc,
const bool isReadWrite,
const unsigned Attributes,
const unsigned AttributesAsWritten,

View File

@ -4580,8 +4580,10 @@ Decl *ASTNodeImporter::VisitObjCPropertyDecl(ObjCPropertyDecl *D) {
ToProperty->setPropertyAttributes(D->getPropertyAttributes());
ToProperty->setPropertyAttributesAsWritten(
D->getPropertyAttributesAsWritten());
ToProperty->setGetterName(Importer.Import(D->getGetterName()));
ToProperty->setSetterName(Importer.Import(D->getSetterName()));
ToProperty->setGetterName(Importer.Import(D->getGetterName()),
Importer.Import(D->getGetterNameLoc()));
ToProperty->setSetterName(Importer.Import(D->getSetterName()),
Importer.Import(D->getSetterNameLoc()));
ToProperty->setGetterMethodDecl(
cast_or_null<ObjCMethodDecl>(Importer.Import(D->getGetterMethodDecl())));
ToProperty->setSetterMethodDecl(

View File

@ -22,6 +22,10 @@ class BodyIndexer : public RecursiveASTVisitor<BodyIndexer> {
SmallVector<Stmt*, 16> StmtStack;
typedef RecursiveASTVisitor<BodyIndexer> base;
Stmt *getParentStmt() const {
return StmtStack.size() < 2 ? nullptr : StmtStack.end()[-2];
}
public:
BodyIndexer(IndexingContext &indexCtx,
const NamedDecl *Parent, const DeclContext *DC)
@ -178,7 +182,8 @@ public:
SymbolRoleSet Roles{};
SmallVector<SymbolRelation, 2> Relations;
addCallRole(Roles, Relations);
if (E->isImplicit())
Stmt *Containing = getParentStmt();
if (E->isImplicit() || (Containing && isa<PseudoObjectExpr>(Containing)))
Roles |= (unsigned)SymbolRole::Implicit;
if (isDynamic(E)) {
@ -194,9 +199,12 @@ public:
}
bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *E) {
if (E->isExplicitProperty())
if (E->isExplicitProperty()) {
SmallVector<SymbolRelation, 2> Relations;
SymbolRoleSet Roles = getRolesForRef(E, Relations);
return IndexCtx.handleReference(E->getExplicitProperty(), E->getLocation(),
Parent, ParentDC, SymbolRoleSet(), {}, E);
Parent, ParentDC, Roles, Relations, E);
}
// No need to do a handleReference for the objc method, because there will
// be a message expr as part of PseudoObjectExpr.

View File

@ -98,9 +98,28 @@ public:
if (MethodLoc.isInvalid())
MethodLoc = D->getLocation();
SourceLocation AttrLoc;
// check for (getter=/setter=)
if (AssociatedProp) {
bool isGetter = !D->param_size();
AttrLoc = isGetter ?
AssociatedProp->getGetterNameLoc():
AssociatedProp->getSetterNameLoc();
}
SymbolRoleSet Roles = (SymbolRoleSet)SymbolRole::Dynamic;
if (D->isImplicit())
Roles |= (SymbolRoleSet)SymbolRole::Implicit;
if (D->isImplicit()) {
if (AttrLoc.isValid()) {
MethodLoc = AttrLoc;
} else {
Roles |= (SymbolRoleSet)SymbolRole::Implicit;
}
} else if (AttrLoc.isValid()) {
IndexCtx.handleReference(D, AttrLoc, cast<NamedDecl>(D->getDeclContext()),
D->getDeclContext(), 0);
}
if (!IndexCtx.handleDecl(D, MethodLoc, Roles, Relations))
return false;
IndexCtx.indexTypeSourceInfo(D->getReturnTypeSourceInfo(), D);

View File

@ -929,7 +929,7 @@ void Parser::ParseObjCPropertyAttribute(ObjCDeclSpec &DS) {
if (IsSetter) {
DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_setter);
DS.setSetterName(SelIdent);
DS.setSetterName(SelIdent, SelLoc);
if (ExpectAndConsume(tok::colon,
diag::err_expected_colon_after_setter_name)) {
@ -938,7 +938,7 @@ void Parser::ParseObjCPropertyAttribute(ObjCDeclSpec &DS) {
}
} else {
DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_getter);
DS.setGetterName(SelIdent);
DS.setGetterName(SelIdent, SelLoc);
}
} else if (II->isStr("nonnull")) {
if (DS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nullability)

View File

@ -200,9 +200,10 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc,
if (ObjCCategoryDecl *CDecl = dyn_cast<ObjCCategoryDecl>(ClassDecl)) {
if (CDecl->IsClassExtension()) {
Res = HandlePropertyInClassExtension(S, AtLoc, LParenLoc,
FD, GetterSel, SetterSel,
isReadWrite,
Attributes,
FD,
GetterSel, ODS.getGetterNameLoc(),
SetterSel, ODS.getSetterNameLoc(),
isReadWrite, Attributes,
ODS.getPropertyAttributes(),
T, TSI, MethodImplKind);
if (!Res)
@ -212,9 +213,10 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc,
if (!Res) {
Res = CreatePropertyDecl(S, ClassDecl, AtLoc, LParenLoc, FD,
GetterSel, SetterSel, isReadWrite,
Attributes, ODS.getPropertyAttributes(),
T, TSI, MethodImplKind);
GetterSel, ODS.getGetterNameLoc(), SetterSel,
ODS.getSetterNameLoc(), isReadWrite, Attributes,
ODS.getPropertyAttributes(), T, TSI,
MethodImplKind);
if (lexicalDC)
Res->setLexicalDeclContext(lexicalDC);
}
@ -412,7 +414,10 @@ Sema::HandlePropertyInClassExtension(Scope *S,
SourceLocation AtLoc,
SourceLocation LParenLoc,
FieldDeclarator &FD,
Selector GetterSel, Selector SetterSel,
Selector GetterSel,
SourceLocation GetterNameLoc,
Selector SetterSel,
SourceLocation SetterNameLoc,
const bool isReadWrite,
unsigned &Attributes,
const unsigned AttributesAsWritten,
@ -512,7 +517,8 @@ Sema::HandlePropertyInClassExtension(Scope *S,
// Create a new ObjCPropertyDecl with the DeclContext being
// the class extension.
ObjCPropertyDecl *PDecl = CreatePropertyDecl(S, CDecl, AtLoc, LParenLoc,
FD, GetterSel, SetterSel,
FD, GetterSel, GetterNameLoc,
SetterSel, SetterNameLoc,
isReadWrite,
Attributes, AttributesAsWritten,
T, TSI, MethodImplKind, DC);
@ -562,7 +568,9 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S,
SourceLocation LParenLoc,
FieldDeclarator &FD,
Selector GetterSel,
SourceLocation GetterNameLoc,
Selector SetterSel,
SourceLocation SetterNameLoc,
const bool isReadWrite,
const unsigned Attributes,
const unsigned AttributesAsWritten,
@ -640,8 +648,8 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S,
// Regardless of setter/getter attribute, we save the default getter/setter
// selector names in anticipation of declaration of setter/getter methods.
PDecl->setGetterName(GetterSel);
PDecl->setSetterName(SetterSel);
PDecl->setGetterName(GetterSel, GetterNameLoc);
PDecl->setSetterName(SetterSel, SetterNameLoc);
PDecl->setPropertyAttributesAsWritten(
makePropertyAttributesAsWritten(AttributesAsWritten));

View File

@ -1124,8 +1124,10 @@ void ASTDeclReader::VisitObjCPropertyDecl(ObjCPropertyDecl *D) {
(ObjCPropertyDecl::PropertyAttributeKind)Record.readInt());
D->setPropertyImplementation(
(ObjCPropertyDecl::PropertyControl)Record.readInt());
D->setGetterName(Record.readDeclarationName().getObjCSelector());
D->setSetterName(Record.readDeclarationName().getObjCSelector());
D->setGetterName(Record.readDeclarationName().getObjCSelector(),
ReadSourceLocation());
D->setSetterName(Record.readDeclarationName().getObjCSelector(),
ReadSourceLocation());
D->setGetterMethodDecl(ReadDeclAs<ObjCMethodDecl>());
D->setSetterMethodDecl(ReadDeclAs<ObjCMethodDecl>());
D->setPropertyIvarDecl(ReadDeclAs<ObjCIvarDecl>());

View File

@ -799,7 +799,9 @@ void ASTDeclWriter::VisitObjCPropertyDecl(ObjCPropertyDecl *D) {
// FIXME: stable encoding
Record.push_back((unsigned)D->getPropertyImplementation());
Record.AddDeclarationName(D->getGetterName());
Record.AddSourceLocation(D->getGetterNameLoc());
Record.AddDeclarationName(D->getSetterName());
Record.AddSourceLocation(D->getSetterNameLoc());
Record.AddDeclRef(D->getGetterMethodDecl());
Record.AddDeclRef(D->getSetterMethodDecl());
Record.AddDeclRef(D->getPropertyIvarDecl());

View File

@ -121,39 +121,79 @@ extern int setjmp(jmp_buf);
@end
@interface I2
@property (readwrite) id prop;
// CHECK: [[@LINE-1]]:12 | class/ObjC | I2 | [[I2_USR:.*]] | {{.*}} | Decl | rel: 0
// CHECK: [[@LINE+4]]:63 | instance-property(IB,IBColl)/ObjC | buttons | c:objc(cs)I2(py)buttons | <no-cgname> | Decl,RelChild | rel: 1
// CHECK-NEXT: RelChild | I2 | c:objc(cs)I2
@property (readwrite) id prop;
// CHECK: [[@LINE-1]]:26 | instance-method/acc-get/ObjC | prop | [[I2_prop_getter_USR:.*]] | -[I2 prop] | Decl,Dyn,Impl,RelChild,RelAcc | rel: 2
// CHECK: [[@LINE-2]]:26 | instance-method/acc-set/ObjC | setProp: | [[I2_prop_setter_USR:.*]] | -[I2 setProp:] | Decl,Dyn,Impl,RelChild,RelAcc | rel: 2
// CHECK: [[@LINE-3]]:26 | instance-property/ObjC | prop | [[I2_prop_USR:.*]] | <no-cgname> | Decl,RelChild | rel: 1
@property (readwrite, getter=customGet, setter=customSet:) id unrelated;
// CHECK: [[@LINE-1]]:30 | instance-method/acc-get/ObjC | customGet | {{.*}} | -[I2 customGet] | Decl,Dyn,RelChild,RelAcc | rel: 2
// CHECK: [[@LINE-2]]:48 | instance-method/acc-set/ObjC | customSet: | {{.*}} | -[I2 customSet:] | Decl,Dyn,RelChild,RelAcc | rel: 2
// CHECK: [[@LINE-3]]:63 | instance-property/ObjC | unrelated | {{.*}} | <no-cgname> | Decl,RelChild | rel: 1
-(id)declaredGet;
@property (readwrite, getter=declaredGet) id otherProp;
// CHECK: [[@LINE-1]]:30 | instance-method/acc-get/ObjC | declaredGet | {{.*}} | -[I2 declaredGet] | Ref,RelCont | rel: 1
// CHECK: [[@LINE-3]]:6 | instance-method/acc-get/ObjC | declaredGet | {{.*}} | -[I2 declaredGet] | Decl,Dyn,RelChild,RelAcc | rel: 2
// CHECK: [[@LINE-3]]:46 | instance-method/acc-set/ObjC | setOtherProp: | {{.*}} | -[I2 setOtherProp:] | Decl,Dyn,Impl,RelChild,RelAcc | rel: 2
// CHECK: [[@LINE+4]]:63 | instance-property(IB,IBColl)/ObjC | buttons | [[buttons_USR:.*]] | <no-cgname> | Decl,RelChild | rel: 1
// CHECK-NEXT: RelChild | I2 | [[I2_USR]]
// CHECK: [[@LINE+2]]:50 | class/ObjC | I1 | c:objc(cs)I1 | _OBJC_CLASS_$_I1 | Ref,RelCont,RelIBType | rel: 1
// CHECK-NEXT: RelCont,RelIBType | buttons | c:objc(cs)I2(py)buttons
// CHECK-NEXT: RelCont,RelIBType | buttons | [[buttons_USR]]
@property (nonatomic, strong) IBOutletCollection(I1) NSArray *buttons;
@end
@implementation I2
// CHECK: [[@LINE+9]]:13 | instance-property/ObjC | prop | c:objc(cs)I2(py)prop | <no-cgname> | Def,RelChild,RelAcc | rel: 2
// CHECK-NEXT: RelChild | I2 | c:objc(cs)I2
// CHECK: [[@LINE+9]]:13 | instance-property/ObjC | prop | [[I2_prop_USR:.*]] | <no-cgname> | Def,RelChild,RelAcc | rel: 2
// CHECK-NEXT: RelChild | I2 | [[I2_USR]]
// CHECK-NEXT: RelAcc | _prop | c:objc(cs)I2@_prop
// CHECK: [[@LINE+6]]:13 | instance-method/acc-get/ObjC | prop | c:objc(cs)I2(im)prop | -[I2 prop] | Def,Impl,RelChild | rel: 1
// CHECK-NEXT: RelChild | I2 | c:objc(cs)I2
// CHECK: [[@LINE+4]]:13 | instance-method/acc-set/ObjC | setProp: | c:objc(cs)I2(im)setProp: | -[I2 setProp:] | Def,Impl,RelChild | rel: 1
// CHECK-NEXT: RelChild | I2 | c:objc(cs)I2
// CHECK: [[@LINE+6]]:13 | instance-method/acc-get/ObjC | prop | [[I2_prop_getter_USR]] | -[I2 prop] | Def,Impl,RelChild | rel: 1
// CHECK-NEXT: RelChild | I2 | [[I2_USR]]
// CHECK: [[@LINE+4]]:13 | instance-method/acc-set/ObjC | setProp: | [[I2_prop_setter_USR]] | -[I2 setProp:] | Def,Impl,RelChild | rel: 1
// CHECK-NEXT: RelChild | I2 | [[I2_USR]]
// CHECK: [[@LINE+2]]:20 | field/ObjC | _prop | c:objc(cs)I2@_prop | <no-cgname> | Def,RelChild | rel: 1
// CHECK-NEXT: RelChild | I2 | c:objc(cs)I2
// CHECK-NEXT: RelChild | I2 | [[I2_USR]]
@synthesize prop = _prop;
// CHECK: [[@LINE+11]]:12 | instance-method(IB)/ObjC | doAction:foo: | c:objc(cs)I2(im)doAction:foo: | -[I2 doAction:foo:] | Def,Dyn,RelChild | rel: 1
// CHECK-NEXT: RelChild | I2 | c:objc(cs)I2
// CHECK: [[@LINE+11]]:12 | instance-method(IB)/ObjC | doAction:foo: | [[doAction_USR:.*]] | -[I2 doAction:foo:] | Def,Dyn,RelChild | rel: 1
// CHECK-NEXT: RelChild | I2 | [[I2_USR]]
// CHECK: [[@LINE+9]]:22 | class/ObjC | I1 | c:objc(cs)I1 | _OBJC_CLASS_$_I1 | Ref,RelCont,RelIBType | rel: 1
// CHECK-NEXT: RelCont,RelIBType | doAction:foo: | c:objc(cs)I2(im)doAction:foo:
// CHECK-NEXT: RelCont,RelIBType | doAction:foo: | [[doAction_USR]]
// CHECK-NOT: [[@LINE+7]]:27 | param
// LOCAL: [[@LINE+6]]:27 | param(local)/C | sender | c:{{.*}} | _sender | Def,RelChild | rel: 1
// LOCAL-NEXT: RelChild | doAction:foo: | c:objc(cs)I2(im)doAction:foo:
// LOCAL-NEXT: RelChild | doAction:foo: | [[doAction_USR:.*]]
// CHECK: [[@LINE+4]]:39 | class/ObjC | I1 | c:objc(cs)I1 | _OBJC_CLASS_$_I1 | Ref,RelCont | rel: 1
// CHECK-NOT: [[@LINE+3]]:44 | param
// LOCAL: [[@LINE+2]]:44 | param(local)/C | bar | c:{{.*}} | _bar | Def,RelChild | rel: 1
// LOCAL-NEXT: RelChild | doAction:foo: | c:objc(cs)I2(im)doAction:foo:
-(IBAction)doAction:(I1 *)sender foo:(I1 *)bar {}
// LOCAL-NEXT: RelChild | doAction:foo: | [[doAction_USR]]
-(IBAction)doAction:(I1 *)sender foo:(I1 *)bar {
[self prop];
// CHECK: [[@LINE-1]]:9 | instance-method/acc-get/ObjC | prop | [[I2_prop_getter_USR]] | -[I2 prop] | Ref,Call,Dyn,RelRec,RelCall,RelCont | rel: 2
// CHECK-NEXT: RelCall,RelCont | doAction:foo: | [[doAction_USR]]
// CHECK-NEXT: RelRec | I2 | [[I2_USR]]
[self setProp: bar];
// CHECK: [[@LINE-1]]:9 | instance-method/acc-set/ObjC | setProp: | [[I2_prop_setter_USR]] | -[I2 setProp:] | Ref,Call,Dyn,RelRec,RelCall,RelCont | rel: 2
// CHECK-NEXT: RelCall,RelCont | doAction:foo: | [[doAction_USR]]
// CHECK-NEXT: RelRec | I2 | [[I2_USR]]
self.prop;
// CHECK: [[@LINE-1]]:8 | instance-property/ObjC | prop | [[I2_prop_USR]] | <no-cgname> | Ref,RelCont | rel: 1
// CHECK-NEXT: RelCont | doAction:foo: | [[doAction_USR]]
// CHECK: [[@LINE-3]]:8 | instance-method/acc-get/ObjC | prop | [[I2_prop_getter_USR]] | -[I2 prop] | Ref,Call,Dyn,Impl,RelRec,RelCall,RelCont | rel: 2
// CHECK-NEXT: RelCall,RelCont | doAction:foo: | [[doAction_USR]]
// CHECK-NEXT: RelRec | I2 | [[I2_USR]]
self.prop = self.prop;
// CHECK: [[@LINE-1]]:8 | instance-property/ObjC | prop | [[I2_prop_USR]] | <no-cgname> | Ref,Writ,RelCont | rel: 1
// CHECK-NEXT: RelCont | doAction:foo: | [[doAction_USR]]
// CHECK:[[@LINE-3]]:8 | instance-method/acc-set/ObjC | setProp: | [[I2_prop_setter_USR]] | -[I2 setProp:] | Ref,Call,Dyn,Impl,RelRec,RelCall,RelCont | rel: 2
// CHECK-NEXT: RelCall,RelCont | doAction:foo: | [[doAction_USR]]
// CHECK-NEXT: RelRec | I2 | [[I2_USR]]
}
@end
@interface I3