diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 46330a400f..5a04843054 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -1614,6 +1614,11 @@ public: const ObjCMethodDecl *Redecl) { ObjCMethodRedecls[MD] = Redecl; } + + /// \brief Returns the objc interface that \arg ND belongs to if it is a + /// objc method/property/ivar etc. that is part of an interface, + /// otherwise returns null. + ObjCInterfaceDecl *getObjContainingInterface(NamedDecl *ND) const; /// \brief Set the copy inialization expression of a block var decl. void setBlockVarCopyInits(VarDecl*VD, Expr* Init); diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index b695a5b709..896a80a90f 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -1261,6 +1261,17 @@ void ASTContext::setObjCImplementation(ObjCCategoryDecl *CatD, ObjCImpls[CatD] = ImplD; } +ObjCInterfaceDecl *ASTContext::getObjContainingInterface(NamedDecl *ND) const { + if (ObjCInterfaceDecl *ID = dyn_cast(ND->getDeclContext())) + return ID; + if (ObjCCategoryDecl *CD = dyn_cast(ND->getDeclContext())) + return CD->getClassInterface(); + if (ObjCImplDecl *IMD = dyn_cast(ND->getDeclContext())) + return IMD->getClassInterface(); + + return 0; +} + /// \brief Get the copy initialization expression of VarDecl,or NULL if /// none exists. Expr *ASTContext::getBlockVarCopyInits(const VarDecl*VD) { diff --git a/test/Index/usrs.m b/test/Index/usrs.m index a76394b746..e7a926f3f3 100644 --- a/test/Index/usrs.m +++ b/test/Index/usrs.m @@ -80,6 +80,14 @@ int test_multi_declaration(void) { - (void)method; @end +@interface CWithExt2 +@end +@interface CWithExt2 () { + id var_ext; +} +@property (assign) id pro_ext; +@end + // CHECK: usrs.m c:usrs.m@67@F@my_helper Extent=[3:1 - 3:60] // CHECK: usrs.m c:usrs.m@95@F@my_helper@x Extent=[3:29 - 3:34] // CHECK: usrs.m c:usrs.m@102@F@my_helper@y Extent=[3:36 - 3:41] @@ -118,9 +126,9 @@ int test_multi_declaration(void) { // CHECK: usrs.m c:usrs.m@551@F@local_func@x Extent=[49:23 - 49:28] // CHECK: usrs.m c:objc(cs)CWithExt Extent=[51:1 - 53:5] // CHECK: usrs.m c:objc(cs)CWithExt(im)meth1 Extent=[52:1 - 52:14] -// CHECK: usrs.m c:objc(cs)CWithExt Extent=[54:1 - 56:5] +// CHECK: usrs.m c:objc(ext)CWithExt@usrs.m@612 Extent=[54:1 - 56:5] // CHECK: usrs.m c:objc(cs)CWithExt(im)meth2 Extent=[55:1 - 55:14] -// CHECK: usrs.m c:objc(cs)CWithExt Extent=[57:1 - 59:5] +// CHECK: usrs.m c:objc(ext)CWithExt@usrs.m@654 Extent=[57:1 - 59:5] // CHECK: usrs.m c:objc(cs)CWithExt(im)meth3 Extent=[58:1 - 58:14] // CHECK: usrs.m c:objc(cy)CWithExt@Bar Extent=[60:1 - 62:5] // CHECK: usrs.m c:objc(cs)CWithExt(im)meth4 Extent=[61:1 - 61:14] @@ -137,6 +145,12 @@ int test_multi_declaration(void) { // CHECK: usrs.m c:usrs.m@980@F@test_multi_declaration@baz Extent=[74:25 - 74:32] // CHECK: usrs.m c:objc(pl)P1 Extent=[79:1 - 81:5] // CHECK: usrs.m c:objc(pl)P1(im)method Extent=[80:1 - 80:16] +// CHECK: usrs.m c:objc(cs)CWithExt2 Extent=[83:1 - 84:5] +// CHECK: usrs.m c:objc(ext)CWithExt2@usrs.m@1111 Extent=[85:1 - 89:5] +// CHECK: usrs.m c:objc(cs)CWithExt2@var_ext Extent=[86:3 - 86:13] +// CHECK: usrs.m c:objc(cs)CWithExt2(py)pro_ext Extent=[88:1 - 88:30] +// CHECK: usrs.m c:objc(cs)CWithExt2(im)pro_ext Extent=[88:23 - 88:30] +// CHECK: usrs.m c:objc(cs)CWithExt2(im)setPro_ext: Extent=[88:23 - 88:30] // RUN: c-index-test -test-load-source all %s | FileCheck -check-prefix=CHECK-source %s // CHECK-source: usrs.m:3:19: FunctionDecl=my_helper:3:19 (Definition) Extent=[3:1 - 3:60] diff --git a/tools/libclang/CIndexUSRs.cpp b/tools/libclang/CIndexUSRs.cpp index 11b124c562..32c4db6abe 100644 --- a/tools/libclang/CIndexUSRs.cpp +++ b/tools/libclang/CIndexUSRs.cpp @@ -169,7 +169,12 @@ void USRGenerator::VisitDeclContext(DeclContext *DC) { } void USRGenerator::VisitFieldDecl(FieldDecl *D) { - VisitDeclContext(D->getDeclContext()); + // The USR for an ivar declared in a class extension is based on the + // ObjCInterfaceDecl, not the ObjCCategoryDecl. + if (ObjCInterfaceDecl *ID = Context->getObjContainingInterface(D)) + Visit(ID); + else + VisitDeclContext(D->getDeclContext()); Out << (isa(D) ? "@" : "@FI@"); if (EmitDeclName(D)) { // Bit fields can be anonymous. @@ -336,9 +341,11 @@ void USRGenerator::VisitObjCContainerDecl(ObjCContainerDecl *D) { IgnoreResults = true; return; } + // Specially handle class extensions, which are anonymous categories. + // We want to mangle in the location to uniquely distinguish them. if (CD->IsClassExtension()) { - // An extension semantically continues the interface of the class. - GenObjCClass(ID->getName()); + Out << "objc(ext)" << ID->getName() << '@'; + GenLoc(CD); } else GenObjCCategory(ID->getName(), CD->getName()); @@ -366,7 +373,12 @@ void USRGenerator::VisitObjCContainerDecl(ObjCContainerDecl *D) { } void USRGenerator::VisitObjCPropertyDecl(ObjCPropertyDecl *D) { - Visit(cast(D->getDeclContext())); + // The USR for a property declared in a class extension or category is based + // on the ObjCInterfaceDecl, not the ObjCCategoryDecl. + if (ObjCInterfaceDecl *ID = Context->getObjContainingInterface(D)) + Visit(ID); + else + Visit(cast(D->getDeclContext())); GenObjCProperty(D->getName()); }