diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h index ab7674a794e4f7f347778b0ccafba0920b7a17be..f78df52d83eaa066ac13d04ec7f0e09b974b06e8 100644 --- a/include/clang-c/Index.h +++ b/include/clang-c/Index.h @@ -1417,7 +1417,59 @@ CINDEX_LINKAGE CXCursor clang_getCursorSemanticParent(CXCursor cursor); * and the lexical context of the second \c C::f is the translation unit. */ CINDEX_LINKAGE CXCursor clang_getCursorLexicalParent(CXCursor cursor); - + +/** + * \brief Determine the set of methods that are overridden by the given + * method. + * + * In both Objective-C and C++, a method (aka virtual member function, + * in C++) can override a virtual method in a base class. For + * Objective-C, a method is said to override any method in the class's + * interface (if we're coming from an implementation), its protocols, + * or its categories, that has the same selector and is of the same + * kind (class or instance). If no such method exists, the search + * continues to the class's superclass, its protocols, and its + * categories, and so on. + * + * For C++, a virtual member function overrides any virtual member + * function with the same signature that occurs in its base + * classes. With multiple inheritance, a virtual member function can + * override several virtual member functions coming from different + * base classes. + * + * In all cases, this function determines the immediate overridden + * method, rather than all of the overridden methods. For example, if + * a method is originally declared in a class A, then overridden in B + * (which in inherits from A) and also in C (which inherited from B), + * then the only overridden method returned from this function when + * invoked on C's method will be B's method. The client may then + * invoke this function again, given the previously-found overridden + * methods, to map out the complete method-override set. + * + * \param cursor A cursor representing an Objective-C or C++ + * method. This routine will compute the set of methods that this + * method overrides. + * + * \param overridden A pointer whose pointee will be replaced with a + * pointer to an array of cursors, representing the set of overridden + * methods. If there are no overridden methods, the pointee will be + * set to NULL. The pointee must be freed via a call to + * \c clang_disposeOverriddenCursors(). + * + * \param num_overridden A pointer to the number of overridden + * functions, will be set to the number of overridden functions in the + * array pointed to by \p overridden. + */ +CINDEX_LINKAGE void clang_getOverriddenCursors(CXCursor cursor, + CXCursor **overridden, + unsigned *num_overridden); + +/** + * \brief Free the set of overridden cursors returned by \c + * clang_getOverriddenCursors(). + */ +CINDEX_LINKAGE void clang_disposeOverriddenCursors(CXCursor *overridden); + /** * @} */ diff --git a/test/Index/local-symbols.m b/test/Index/local-symbols.m index b9f4fe2aeabc0afcb1d2a75232e6d2c65231f3e9..af3b601465a802f087067f0b3cc2d79c186e5c94 100644 --- a/test/Index/local-symbols.m +++ b/test/Index/local-symbols.m @@ -32,7 +32,7 @@ // CHECK: local-symbols.m:9:1: ObjCInstanceMethodDecl=bar:9:1 Extent=[9:1 - 9:12] // CHECK: local-symbols.m:9:4: TypeRef=id:0:0 Extent=[9:4 - 9:6] // CHECK: local-symbols.m:12:1: ObjCImplementationDecl=Foo:12:1 (Definition) Extent=[12:1 - 16:2] -// CHECK: local-symbols.m:13:1: ObjCInstanceMethodDecl=bar:13:1 (Definition) Extent=[13:1 - 15:2] +// CHECK: local-symbols.m:13:1: ObjCInstanceMethodDecl=bar:13:1 (Definition) [Overrides @9:1] Extent=[13:1 - 15:2] // CHECK: local-symbols.m:13:4: TypeRef=id:0:0 Extent=[13:4 - 13:6] // CHECK: local-symbols.m:14:10: UnexposedExpr= Extent=[14:10 - 14:11] // CHECK: local-symbols.m:14:10: UnexposedExpr= Extent=[14:10 - 14:11] diff --git a/test/Index/overrides.cpp b/test/Index/overrides.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3dee607e43183bbb7bfb4baccea79e8b57cda53f --- /dev/null +++ b/test/Index/overrides.cpp @@ -0,0 +1,20 @@ +struct A { + virtual void f(int); +}; + +struct B { + virtual void f(int); + virtual void g(); +}; + +struct C : B, A { + virtual void g(); +}; + +struct D : C { + virtual void f(int); +}; + +// RUN: c-index-test -test-load-source local %s | FileCheck %s +// CHECK: overrides.cpp:11:16: CXXMethod=g:11:16 [Overrides @7:16] Extent=[11:16 - 11:19] +// CHECK: overrides.cpp:15:16: CXXMethod=f:15:16 [Overrides @2:16, @6:16] Extent=[15:16 - 15:22] diff --git a/test/Index/overrides.m b/test/Index/overrides.m new file mode 100644 index 0000000000000000000000000000000000000000..2197aaaa3672fd374115035a7ed5c3dd2f03c66b --- /dev/null +++ b/test/Index/overrides.m @@ -0,0 +1,35 @@ + +@protocol P1 +- (void)protoMethod; +- (void)protoMethodWithParam:(int)param; +@end + +@protocol P3 +- (void)protoMethod; +@end + +@protocol P2 <P1> +- (void)protoMethod; +@end + +@interface A +- (void)method; ++ (void)methodWithParam:(int)param; +@end + +@interface B : A <P2, P3> +- (void)method; +- (void)protoMethod; +@end + +@implementation B +- (void)method { } ++ (void)methodWithParam:(int)param { } +@end + +// RUN: c-index-test -test-load-source local %s | FileCheck %s +// CHECK: overrides.m:12:1: ObjCInstanceMethodDecl=protoMethod:12:1 [Overrides @3:1] Extent=[12:1 - 12:21] +// CHECK: overrides.m:21:1: ObjCInstanceMethodDecl=method:21:1 [Overrides @16:1] Extent=[21:1 - 21:16] +// CHECK: overrides.m:22:1: ObjCInstanceMethodDecl=protoMethod:22:1 [Overrides @12:1, @8:1] Extent=[22:1 - 22:21] +// CHECK: overrides.m:26:1: ObjCInstanceMethodDecl=method:26:1 (Definition) [Overrides @21:1] Extent=[26:1 - 26:19] +// CHECK: overrides.m:27:1: ObjCClassMethodDecl=methodWithParam::27:1 (Definition) [Overrides @17:1] Extent=[27:1 - 27:39] diff --git a/test/Index/properties-class-extensions.m b/test/Index/properties-class-extensions.m index dfeff8205ee63537a163227a3185db96284a9f2b..7fe2c41e58423cb7f8fb8710d6945695971da018 100644 --- a/test/Index/properties-class-extensions.m +++ b/test/Index/properties-class-extensions.m @@ -80,7 +80,7 @@ // CHECK: properties-class-extensions.m:30:12: ObjCClassRef=Rdar8467189_Foo:28:12 Extent=[30:12 - 30:27] // CHECK: properties-class-extensions.m:31:40: ObjCPropertyDecl=Rdar8467189_Bar:31:40 Extent=[31:40 - 31:55] // CHECK: properties-class-extensions.m:31:23: ObjCClassRef=Rdar8467189_Bar:24:8 Extent=[31:23 - 31:38] -// CHECK: properties-class-extensions.m:31:40: ObjCInstanceMethodDecl=Rdar8467189_Bar:31:40 Extent=[31:40 - 31:55] +// CHECK: properties-class-extensions.m:31:40: ObjCInstanceMethodDecl=Rdar8467189_Bar:31:40 [Overrides @26:39] Extent=[31:40 - 31:55] // CHECK: properties-class-extensions.m:31:40: ObjCInstanceMethodDecl=setRdar8467189_Bar::31:40 Extent=[31:40 - 31:55] // CHECK: properties-class-extensions.m:31:40: ParmDecl=Rdar8467189_Bar:31:40 (Definition) Extent=[31:40 - 31:55] // CHECK: properties-class-extensions.m:35:12: ObjCInterfaceDecl=Qux:35:12 Extent=[35:1 - 36:5] diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c index 874c73282c8a772dcaefd0f3eb16985ef2b56012..f83162295d4dfbf1bc36973df5b4f7afef3fd621 100644 --- a/tools/c-index-test/c-index-test.c +++ b/tools/c-index-test/c-index-test.c @@ -167,7 +167,9 @@ static void PrintCursor(CXCursor Cursor) { CXCursor Referenced; unsigned line, column; CXCursor SpecializationOf; - + CXCursor *overridden; + unsigned num_overridden; + ks = clang_getCursorKindSpelling(Cursor.kind); string = clang_getCursorSpelling(Cursor); printf("%s=%s", clang_getCString(ks), @@ -251,6 +253,21 @@ static void PrintCursor(CXCursor Cursor) { clang_getCString(Name), line, column); clang_disposeString(Name); } + + clang_getOverriddenCursors(Cursor, &overridden, &num_overridden); + if (num_overridden) { + unsigned I; + printf(" [Overrides "); + for (I = 0; I != num_overridden; ++I) { + CXSourceLocation Loc = clang_getCursorLocation(overridden[I]); + clang_getInstantiationLocation(Loc, 0, &line, &column, 0); + if (I) + printf(", "); + printf("@%d:%d", line, column); + } + printf("]"); + clang_disposeOverriddenCursors(overridden); + } } } diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index f41906209d1dfee070eca9dfc5821793c0a14785..a291f47c0198b493d0b9be44d7663dc2df0a967c 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -4065,6 +4065,117 @@ CXCursor clang_getCursorLexicalParent(CXCursor cursor) { return clang_getNullCursor(); } +static void CollectOverriddenMethods(DeclContext *Ctx, + ObjCMethodDecl *Method, + llvm::SmallVectorImpl<ObjCMethodDecl *> &Methods) { + if (!Ctx) + return; + + // If we have a class or category implementation, jump straight to the + // interface. + if (ObjCImplDecl *Impl = dyn_cast<ObjCImplDecl>(Ctx)) + return CollectOverriddenMethods(Impl->getClassInterface(), Method, Methods); + + ObjCContainerDecl *Container = dyn_cast<ObjCContainerDecl>(Ctx); + if (!Container) + return; + + // Check whether we have a matching method at this level. + if (ObjCMethodDecl *Overridden = Container->getMethod(Method->getSelector(), + Method->isInstanceMethod())) + if (Method != Overridden) { + // We found an override at this level; there is no need to look + // into other protocols or categories. + Methods.push_back(Overridden); + return; + } + + if (ObjCProtocolDecl *Protocol = dyn_cast<ObjCProtocolDecl>(Container)) { + for (ObjCProtocolDecl::protocol_iterator P = Protocol->protocol_begin(), + PEnd = Protocol->protocol_end(); + P != PEnd; ++P) + CollectOverriddenMethods(*P, Method, Methods); + } + + if (ObjCCategoryDecl *Category = dyn_cast<ObjCCategoryDecl>(Container)) { + for (ObjCCategoryDecl::protocol_iterator P = Category->protocol_begin(), + PEnd = Category->protocol_end(); + P != PEnd; ++P) + CollectOverriddenMethods(*P, Method, Methods); + } + + if (ObjCInterfaceDecl *Interface = dyn_cast<ObjCInterfaceDecl>(Container)) { + for (ObjCInterfaceDecl::protocol_iterator P = Interface->protocol_begin(), + PEnd = Interface->protocol_end(); + P != PEnd; ++P) + CollectOverriddenMethods(*P, Method, Methods); + + for (ObjCCategoryDecl *Category = Interface->getCategoryList(); + Category; Category = Category->getNextClassCategory()) + CollectOverriddenMethods(Category, Method, Methods); + + // We only look into the superclass if we haven't found anything yet. + if (Methods.empty()) + if (ObjCInterfaceDecl *Super = Interface->getSuperClass()) + return CollectOverriddenMethods(Super, Method, Methods); + } +} + +void clang_getOverriddenCursors(CXCursor cursor, + CXCursor **overridden, + unsigned *num_overridden) { + if (overridden) + *overridden = 0; + if (num_overridden) + *num_overridden = 0; + if (!overridden || !num_overridden) + return; + + if (!clang_isDeclaration(cursor.kind)) + return; + + Decl *D = getCursorDecl(cursor); + if (!D) + return; + + // Handle C++ member functions. + ASTUnit *CXXUnit = getCursorASTUnit(cursor); + if (CXXMethodDecl *CXXMethod = dyn_cast<CXXMethodDecl>(D)) { + *num_overridden = CXXMethod->size_overridden_methods(); + if (!*num_overridden) + return; + + *overridden = new CXCursor [*num_overridden]; + unsigned I = 0; + for (CXXMethodDecl::method_iterator + M = CXXMethod->begin_overridden_methods(), + MEnd = CXXMethod->end_overridden_methods(); + M != MEnd; (void)++M, ++I) + (*overridden)[I] = MakeCXCursor(const_cast<CXXMethodDecl*>(*M), CXXUnit); + return; + } + + ObjCMethodDecl *Method = dyn_cast<ObjCMethodDecl>(D); + if (!Method) + return; + + // Handle Objective-C methods. + llvm::SmallVector<ObjCMethodDecl *, 4> Methods; + CollectOverriddenMethods(Method->getDeclContext(), Method, Methods); + + if (Methods.empty()) + return; + + *num_overridden = Methods.size(); + *overridden = new CXCursor [Methods.size()]; + for (unsigned I = 0, N = Methods.size(); I != N; ++I) + (*overridden)[I] = MakeCXCursor(Methods[I], CXXUnit); +} + +void clang_disposeOverriddenCursors(CXCursor *overridden) { + delete [] overridden; +} + } // end: extern "C" diff --git a/tools/libclang/libclang.darwin.exports b/tools/libclang/libclang.darwin.exports index da1a6eefc66425e0245f42e502a4cfc81eda8b51..d2e246e7e8dd94e05d7cfda9ff15de58bca835c6 100644 --- a/tools/libclang/libclang.darwin.exports +++ b/tools/libclang/libclang.darwin.exports @@ -21,6 +21,7 @@ _clang_defaultSaveOptions _clang_disposeCodeCompleteResults _clang_disposeDiagnostic _clang_disposeIndex +_clang_disposeOverriddenCursors _clang_disposeString _clang_disposeTokens _clang_disposeTranslationUnit @@ -77,6 +78,7 @@ _clang_getNumCompletionChunks _clang_getNumDiagnostics _clang_getNumOverloadedDecls _clang_getOverloadedDecl +_clang_getOverriddenCursors _clang_getPointeeType _clang_getRange _clang_getRangeEnd diff --git a/tools/libclang/libclang.exports b/tools/libclang/libclang.exports index 511c85d26f03986bd6c7ffa48ccbf2171634d17a..d8ae1e47b39cdb73f0eab2209c862606a10ac7e1 100644 --- a/tools/libclang/libclang.exports +++ b/tools/libclang/libclang.exports @@ -21,6 +21,7 @@ clang_defaultSaveOptions clang_disposeCodeCompleteResults clang_disposeDiagnostic clang_disposeIndex +clang_disposeOverriddenCursors clang_disposeString clang_disposeTokens clang_disposeTranslationUnit @@ -77,6 +78,7 @@ clang_getNumCompletionChunks clang_getNumDiagnostics clang_getNumOverloadedDecls clang_getOverloadedDecl +clang_getOverriddenCursors clang_getPointeeType clang_getRange clang_getRangeEnd