aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang-c/Index.h13
-rw-r--r--test/Index/cursor-dynamic-call.mm59
-rw-r--r--tools/c-index-test/c-index-test.c3
-rw-r--r--tools/libclang/CXCursor.cpp27
-rw-r--r--tools/libclang/libclang.exports1
5 files changed, 102 insertions, 1 deletions
diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h
index b34918fb6f..33a92e4653 100644
--- a/include/clang-c/Index.h
+++ b/include/clang-c/Index.h
@@ -3144,6 +3144,19 @@ CINDEX_LINKAGE CXCursor clang_getCanonicalCursor(CXCursor);
CINDEX_LINKAGE int clang_Cursor_getObjCSelectorIndex(CXCursor);
/**
+ * \brief Given a cursor pointing to a C++ method call or an ObjC message,
+ * returns non-zero if the method/message is "dynamic", meaning:
+ *
+ * For a C++ method: the call is virtual.
+ * For an ObjC message: the receiver is an object instance, not 'super' or a
+ * specific class.
+ *
+ * If the method/message is "static" or the cursor does not point to a
+ * method/message, it will return zero.
+ */
+CINDEX_LINKAGE int clang_Cursor_isDynamicCall(CXCursor C);
+
+/**
* \brief Given a cursor that represents a declaration, return the associated
* comment's source range. The range may include multiple consecutive comments
* with whitespace in between.
diff --git a/test/Index/cursor-dynamic-call.mm b/test/Index/cursor-dynamic-call.mm
new file mode 100644
index 0000000000..f9d6a8716d
--- /dev/null
+++ b/test/Index/cursor-dynamic-call.mm
@@ -0,0 +1,59 @@
+
+struct SB {
+ virtual void meth();
+};
+
+struct SS : public SB {
+ void submeth() {
+ this->meth();
+ SB::meth();
+ }
+};
+
+@interface IB
+-(void)meth;
++(void)ClsMeth;
+@end
+
+@interface IS : IB
+-(void)submeth;
++(void)ClsMeth;
+@end
+
+@implementation IS
+-(void)submeth {
+ [self meth];
+ [super meth];
+}
++(void)ClsMeth {
+ [super ClsMeth];
+}
+@end
+
+void foo(SS *ss, IS* is, Class cls) {
+ ss->meth();
+ [is meth];
+ [IB ClsMeth];
+ [cls ClsMeth];
+}
+
+// RUN: c-index-test -cursor-at=%s:8:11 \
+// RUN: -cursor-at=%s:9:11 \
+// RUN: -cursor-at=%s:25:11 \
+// RUN: -cursor-at=%s:26:11 \
+// RUN: -cursor-at=%s:29:11 \
+// RUN: -cursor-at=%s:34:9 \
+// RUN: -cursor-at=%s:35:9 \
+// RUN: -cursor-at=%s:36:9 \
+// RUN: -cursor-at=%s:37:9 \
+// RUN: %s | FileCheck %s
+
+// CHECK: 8:11 MemberRefExpr=meth:3:16 {{.*}} Dynamic-call
+// CHECK-NOT: 9:9 {{.*}} Dynamic-call
+// CHECK: 25:3 ObjCMessageExpr=meth:14:8 {{.*}} Dynamic-call
+// CHECK-NOT: 26:3 {{.*}} Dynamic-call
+// CHECK-NOT: 29:3 {{.*}} Dynamic-call
+// CHECK: 34:7 MemberRefExpr=meth:3:16 {{.*}} Dynamic-call
+// CHECK: 35:3 ObjCMessageExpr=meth:14:8 {{.*}} Dynamic-call
+// CHECK-NOT: 36:3 {{.*}} Dynamic-call
+// CHECK: 37:3 ObjCMessageExpr=ClsMeth:15:8 {{.*}} Dynamic-call
diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c
index 43229fdc13..79a3c573b9 100644
--- a/tools/c-index-test/c-index-test.c
+++ b/tools/c-index-test/c-index-test.c
@@ -1614,6 +1614,9 @@ static int inspect_cursor_at(int argc, const char **argv) {
clang_disposeString(Spelling);
if (clang_Cursor_getObjCSelectorIndex(Cursor) != -1)
printf(" Selector index=%d",clang_Cursor_getObjCSelectorIndex(Cursor));
+ if (clang_Cursor_isDynamicCall(Cursor))
+ printf(" Dynamic-call");
+
if (completionString != NULL) {
printf("\nCompletion string: ");
print_completion_string(completionString, stdout);
diff --git a/tools/libclang/CXCursor.cpp b/tools/libclang/CXCursor.cpp
index ae7d806c7f..2757af434c 100644
--- a/tools/libclang/CXCursor.cpp
+++ b/tools/libclang/CXCursor.cpp
@@ -1319,5 +1319,30 @@ void clang_disposeOverriddenCursors(CXCursor *overridden) {
pool.AvailableCursors.push_back(Vec);
}
-
+
+int clang_Cursor_isDynamicCall(CXCursor C) {
+ const Expr *E = 0;
+ if (clang_isExpression(C.kind))
+ E = getCursorExpr(C);
+ if (!E)
+ return 0;
+
+ if (const ObjCMessageExpr *MsgE = dyn_cast<ObjCMessageExpr>(E))
+ return MsgE->getReceiverKind() == ObjCMessageExpr::Instance;
+
+ const MemberExpr *ME = 0;
+ if (isa<MemberExpr>(E))
+ ME = cast<MemberExpr>(E);
+ else if (const CallExpr *CE = dyn_cast<CallExpr>(E))
+ ME = dyn_cast_or_null<MemberExpr>(CE->getCallee());
+
+ if (ME) {
+ if (const CXXMethodDecl *
+ MD = dyn_cast_or_null<CXXMethodDecl>(ME->getMemberDecl()))
+ return MD->isVirtual() && !ME->hasQualifier();
+ }
+
+ return 0;
+}
+
} // end: extern "C"
diff --git a/tools/libclang/libclang.exports b/tools/libclang/libclang.exports
index 1272a02a12..3775ce1476 100644
--- a/tools/libclang/libclang.exports
+++ b/tools/libclang/libclang.exports
@@ -12,6 +12,7 @@ clang_Cursor_getNumArguments
clang_Cursor_getObjCSelectorIndex
clang_Cursor_getSpellingNameRange
clang_Cursor_getTranslationUnit
+clang_Cursor_isDynamicCall
clang_Cursor_isNull
clang_IndexAction_create
clang_IndexAction_dispose