[index] Add a SymbolSubKind for an ObjC unit test.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@267117 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/Index/IndexSymbol.h b/include/clang/Index/IndexSymbol.h
index 68d97d7..bd19771 100644
--- a/include/clang/Index/IndexSymbol.h
+++ b/include/clang/Index/IndexSymbol.h
@@ -63,8 +63,9 @@
Generic = 1 << 0,
TemplatePartialSpecialization = 1 << 1,
TemplateSpecialization = 1 << 2,
+ UnitTest = 1 << 3,
};
-static const unsigned SymbolSubKindBitNum = 3;
+static const unsigned SymbolSubKindBitNum = 4;
typedef unsigned SymbolSubKindSet;
/// Set of roles that are attributed to symbol occurrences.
diff --git a/lib/Index/IndexSymbol.cpp b/lib/Index/IndexSymbol.cpp
index 097aaf0..ba83d0c 100644
--- a/lib/Index/IndexSymbol.cpp
+++ b/lib/Index/IndexSymbol.cpp
@@ -16,6 +16,30 @@
using namespace clang;
using namespace clang::index;
+/// \returns true if \c D is a subclass of 'XCTestCase'.
+static bool isUnitTestCase(const ObjCInterfaceDecl *D) {
+ if (!D)
+ return false;
+ while (const ObjCInterfaceDecl *SuperD = D->getSuperClass()) {
+ if (SuperD->getName() == "XCTestCase")
+ return true;
+ D = SuperD;
+ }
+ return false;
+}
+
+/// \returns true if \c D is in a subclass of 'XCTestCase', returns void, has
+/// no parameters, and its name starts with 'test'.
+static bool isUnitTest(const ObjCMethodDecl *D) {
+ if (!D->parameters().empty())
+ return false;
+ if (!D->getReturnType()->isVoidType())
+ return false;
+ if (!D->getSelector().getNameForSlot(0).startswith("test"))
+ return false;
+ return isUnitTestCase(D->getClassInterface());
+}
+
SymbolInfo index::getSymbolInfo(const Decl *D) {
assert(D);
SymbolInfo Info;
@@ -84,10 +108,16 @@
case Decl::EnumConstant:
Info.Kind = SymbolKind::EnumConstant; break;
case Decl::ObjCInterface:
- case Decl::ObjCImplementation:
+ case Decl::ObjCImplementation: {
Info.Kind = SymbolKind::Class;
Info.Lang = SymbolLanguage::ObjC;
+ const ObjCInterfaceDecl *ClsD = dyn_cast<ObjCInterfaceDecl>(D);
+ if (!ClsD)
+ ClsD = cast<ObjCImplementationDecl>(D)->getClassInterface();
+ if (isUnitTestCase(ClsD))
+ Info.SubKinds |= (unsigned)SymbolSubKind::UnitTest;
break;
+ }
case Decl::ObjCProtocol:
Info.Kind = SymbolKind::Protocol;
Info.Lang = SymbolLanguage::ObjC;
@@ -103,6 +133,8 @@
else
Info.Kind = SymbolKind::ClassMethod;
Info.Lang = SymbolLanguage::ObjC;
+ if (isUnitTest(cast<ObjCMethodDecl>(D)))
+ Info.SubKinds |= (unsigned)SymbolSubKind::UnitTest;
break;
case Decl::ObjCProperty:
Info.Kind = SymbolKind::InstanceProperty;
@@ -314,6 +346,7 @@
APPLY_FOR_SUBKIND(Generic);
APPLY_FOR_SUBKIND(TemplatePartialSpecialization);
APPLY_FOR_SUBKIND(TemplateSpecialization);
+ APPLY_FOR_SUBKIND(UnitTest);
#undef APPLY_FOR_SUBKIND
}
@@ -329,6 +362,7 @@
case SymbolSubKind::Generic: OS << "Gen"; break;
case SymbolSubKind::TemplatePartialSpecialization: OS << "TPS"; break;
case SymbolSubKind::TemplateSpecialization: OS << "TS"; break;
+ case SymbolSubKind::UnitTest: OS << "test"; break;
}
});
}
diff --git a/test/Index/Core/index-subkinds.m b/test/Index/Core/index-subkinds.m
new file mode 100644
index 0000000..a78e123
--- /dev/null
+++ b/test/Index/Core/index-subkinds.m
@@ -0,0 +1,36 @@
+// RUN: c-index-test core -print-source-symbols -- %s -target x86_64-apple-macosx10.7 | FileCheck %s
+
+// CHECK: [[@LINE+1]]:12 | class/ObjC | XCTestCase | c:objc(cs)XCTestCase | _OBJC_CLASS_$_XCTestCase | Decl | rel: 0
+@interface XCTestCase
+@end
+
+// CHECK: [[@LINE+1]]:12 | class(test)/ObjC | MyTestCase | c:objc(cs)MyTestCase | _OBJC_CLASS_$_MyTestCase | Decl | rel: 0
+@interface MyTestCase : XCTestCase
+@end
+// CHECK: [[@LINE+1]]:17 | class(test)/ObjC | MyTestCase | c:objc(cs)MyTestCase | <no-cgname> | Def | rel: 0
+@implementation MyTestCase
+// CHECK: [[@LINE+1]]:1 | instance-method(test)/ObjC | testMe | c:objc(cs)MyTestCase(im)testMe | -[MyTestCase testMe] | Def,Dyn,RelChild | rel: 1
+-(void)testMe {}
+// CHECK: [[@LINE+1]]:1 | instance-method/ObjC | testResult | c:objc(cs)MyTestCase(im)testResult | -[MyTestCase testResult] | Def,Dyn,RelChild | rel: 1
+-(id)testResult { return 0; }
+// CHECK: [[@LINE+1]]:1 | instance-method/ObjC | testWithInt: | c:objc(cs)MyTestCase(im)testWithInt: | -[MyTestCase testWithInt:] | Def,Dyn,RelChild | rel: 1
+-(void)testWithInt:(int)i {}
+@end
+
+// CHECK: [[@LINE+1]]:12 | class(test)/ObjC | SubTestCase | c:objc(cs)SubTestCase | _OBJC_CLASS_$_SubTestCase | Decl | rel: 0
+@interface SubTestCase : MyTestCase
+@end
+// CHECK: [[@LINE+1]]:17 | class(test)/ObjC | SubTestCase | c:objc(cs)SubTestCase | <no-cgname> | Def | rel: 0
+@implementation SubTestCase
+// CHECK: [[@LINE+1]]:1 | instance-method(test)/ObjC | testIt2 | c:objc(cs)SubTestCase(im)testIt2 | -[SubTestCase testIt2] | Def,Dyn,RelChild | rel: 1
+-(void)testIt2 {}
+@end
+
+// CHECK: [[@LINE+1]]:12 | extension/ObjC | cat | c:objc(cy)MyTestCase@cat | <no-cgname> | Decl | rel: 0
+@interface MyTestCase(cat)
+@end
+// CHECK: [[@LINE+1]]:17 | extension/ObjC | MyTestCase | c:objc(cy)MyTestCase@cat | <no-cgname> | Def | rel: 0
+@implementation MyTestCase(cat)
+// CHECK: [[@LINE+1]]:1 | instance-method(test)/ObjC | testInCat | c:objc(cs)MyTestCase(im)testInCat | -[MyTestCase(cat) testInCat] | Def,Dyn,RelChild | rel: 1
+- (void)testInCat {}
+@end
diff --git a/tools/c-index-test/core_main.cpp b/tools/c-index-test/core_main.cpp
index 2823a9b..10233ec 100644
--- a/tools/c-index-test/core_main.cpp
+++ b/tools/c-index-test/core_main.cpp
@@ -170,8 +170,9 @@
static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS) {
OS << getSymbolKindString(SymInfo.Kind);
if (SymInfo.SubKinds) {
- OS << '-';
+ OS << '(';
printSymbolSubKinds(SymInfo.SubKinds, OS);
+ OS << ')';
}
OS << '/' << getSymbolLanguageString(SymInfo.Lang);
}