From: Argyrios Kyrtzidis Date: Thu, 16 Mar 2017 18:25:40 +0000 (+0000) Subject: [index/AST] Add references for ObjC getter=/setter= property attributes and related... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=10eccfa5a05824de7afe89808068c8eb2091f378;p=clang [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 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@297972 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/DeclObjC.h b/include/clang/AST/DeclObjC.h index a445042aec..642aa803bf 100644 --- a/include/clang/AST/DeclObjC.h +++ b/include/clang/AST/DeclObjC.h @@ -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; } diff --git a/include/clang/Sema/DeclSpec.h b/include/clang/Sema/DeclSpec.h index 82f04a584b..df5e105036 100644 --- a/include/clang/Sema/DeclSpec.h +++ b/include/clang/Sema/DeclSpec.h @@ -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. diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 6995740635..c773392dd9 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -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, diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 2a7768a424..ed405b5c90 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -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(Importer.Import(D->getGetterMethodDecl()))); ToProperty->setSetterMethodDecl( diff --git a/lib/Index/IndexBody.cpp b/lib/Index/IndexBody.cpp index 7681de7559..7f09290de4 100644 --- a/lib/Index/IndexBody.cpp +++ b/lib/Index/IndexBody.cpp @@ -22,6 +22,10 @@ class BodyIndexer : public RecursiveASTVisitor { SmallVector StmtStack; typedef RecursiveASTVisitor 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 Relations; addCallRole(Roles, Relations); - if (E->isImplicit()) + Stmt *Containing = getParentStmt(); + if (E->isImplicit() || (Containing && isa(Containing))) Roles |= (unsigned)SymbolRole::Implicit; if (isDynamic(E)) { @@ -194,9 +199,12 @@ public: } bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *E) { - if (E->isExplicitProperty()) + if (E->isExplicitProperty()) { + SmallVector 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. diff --git a/lib/Index/IndexDecl.cpp b/lib/Index/IndexDecl.cpp index 4c5a87b29b..581e3a2740 100644 --- a/lib/Index/IndexDecl.cpp +++ b/lib/Index/IndexDecl.cpp @@ -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(D->getDeclContext()), + D->getDeclContext(), 0); + } + if (!IndexCtx.handleDecl(D, MethodLoc, Roles, Relations)) return false; IndexCtx.indexTypeSourceInfo(D->getReturnTypeSourceInfo(), D); diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index 81761bf8d2..9f6f91c9fc 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -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) diff --git a/lib/Sema/SemaObjCProperty.cpp b/lib/Sema/SemaObjCProperty.cpp index 3481b82679..b950615210 100644 --- a/lib/Sema/SemaObjCProperty.cpp +++ b/lib/Sema/SemaObjCProperty.cpp @@ -200,9 +200,10 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc, if (ObjCCategoryDecl *CDecl = dyn_cast(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)); diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index d33a475927..a0ca321bc7 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -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()); D->setSetterMethodDecl(ReadDeclAs()); D->setPropertyIvarDecl(ReadDeclAs()); diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index 190d3975b7..41b9a97c78 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -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()); diff --git a/test/Index/Core/index-source.m b/test/Index/Core/index-source.m index 2675edd026..32b38f41cc 100644 --- a/test/Index/Core/index-source.m +++ b/test/Index/Core/index-source.m @@ -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 | | 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:.*]] | | 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 | {{.*}} | | 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:.*]] | | 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 | | Def,RelChild,RelAcc | rel: 2 -// CHECK-NEXT: RelChild | I2 | c:objc(cs)I2 +// CHECK: [[@LINE+9]]:13 | instance-property/ObjC | prop | [[I2_prop_USR:.*]] | | 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 | | 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]] | | 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]] | | 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