Skip to content

Commit 73202fc

Browse files
committed
Merge pull request #2518 from modocache/sourcekit-is-test-candidate-accessibility
[SR-710][Index] isTestCandidate access for Linux
2 parents 6a6c6ca + 22fb093 commit 73202fc

File tree

5 files changed

+306
-19
lines changed

5 files changed

+306
-19
lines changed

lib/Index/Index.cpp

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -797,25 +797,42 @@ static bool isTestCandidate(ValueDecl *D) {
797797
if (!D->hasName())
798798
return false;
799799

800-
// A 'test candidate' is a class instance method that returns void, has no
801-
// parameters and starts with 'test'.
802-
// FIXME: Also test if it is ObjC exportable ?
803-
if (auto FD = dyn_cast<FuncDecl>(D)) {
804-
if (FD->isStatic())
805-
return false;
806-
if (!D->getDeclContext()->isTypeContext())
807-
return false;
808-
auto NTD = getNominalParent(D);
809-
if (!NTD)
810-
return false;
811-
Type RetTy = FD->getResultType();
812-
if (FD->getParameterLists().size() != 2)
813-
return false;
814-
auto paramList = FD->getParameterList(1);
815-
if (RetTy && RetTy->isVoid() && isa<ClassDecl>(NTD) &&
816-
paramList->size() == 0 && FD->getName().str().startswith("test"))
817-
return true;
818-
}
800+
// A 'test candidate' is:
801+
// 1. An instance method...
802+
auto FD = dyn_cast<FuncDecl>(D);
803+
if (!FD)
804+
return false;
805+
if (!D->isInstanceMember())
806+
return false;
807+
808+
// 2. ...on a class or extension (not a struct)...
809+
auto NTD = getNominalParent(D);
810+
if (!NTD)
811+
return false;
812+
if (!isa<ClassDecl>(NTD))
813+
return false;
814+
815+
// 3. ...that returns void...
816+
Type RetTy = FD->getResultType();
817+
if (RetTy && !RetTy->isVoid())
818+
return false;
819+
820+
// 4. ...takes no parameters...
821+
if (FD->getParameterLists().size() != 2)
822+
return false;
823+
if (FD->getParameterList(1)->size() != 0)
824+
return false;
825+
826+
// 5. ...is of at least 'internal' accessibility (unless we can use
827+
// Objective-C reflection)...
828+
#if SWIFT_OBJC_INTEROP
829+
if (D->getFormalAccess() < Accessibility::Internal)
830+
return false;
831+
#endif
832+
833+
// 6. ...and starts with "test".
834+
if (FD->getName().str().startswith("test"))
835+
return true;
819836

820837
return false;
821838
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// RUN: %sourcekitd-test -req=index %s -- -serialize-diagnostics -serialize-diagnostics-path %t.dia %s | %sed_clean > %t.response
2+
// RUN: diff -u %s.response %t.response
3+
4+
// This test verifies that, when Objective-C interop is disabled, private
5+
// methods are *not* surfaced as "test candidates".
6+
// FIXME: Ideally this test would run on any OS, provided Objective-C interop
7+
// was disabled.
8+
// REQUIRES: OS=linux-gnu
9+
10+
func test_takesNoParams_andReturnsVoid_butIsNotAnInstanceMethod() {}
11+
12+
struct MyStruct {
13+
func test_startsWithTest_takesNoParams_returnsVoid_butIsDefinedOnAStruct() {}
14+
}
15+
16+
private class MyPrivateClass() {
17+
func test_startsWithTest_takesNoParams_returnsVoid_butIsPrivate() {}
18+
}
19+
20+
public class MyClass {
21+
func doesNotStartWithTest() {}
22+
func test_startsWithTest_butTakesAParam(param: Int) {}
23+
func test_startsWithTest_andTakesNoParams_butReturnsNonVoid() -> Int {}
24+
private func test_startsWithTest_takesNoParams_andReturnsVoid_butIsPrivate() {}
25+
func test_startsWithTest_takesNoParams_returnsVoid() {}
26+
func test_startsWithTest_takesNoParams_returnsVoid_andThrows() throws {}
27+
}
28+
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
{
2+
key.hash: <hash>,
3+
key.dependencies: [
4+
{
5+
key.kind: source.lang.swift.import.module.swift,
6+
key.name: "Swift",
7+
key.filepath: Swift.swiftmodule,
8+
key.hash: <hash>,
9+
key.is_system: 1
10+
}
11+
],
12+
key.entities: [
13+
{
14+
key.kind: source.lang.swift.decl.function.free,
15+
key.name: "test_takesNoParams_andReturnsVoid_butIsNotAnInstanceMethod()",
16+
key.usr: "s:F23index_is_test_candidate58test_takesNoParams_andReturnsVoid_butIsNotAnInstanceMethodFT_T_",
17+
key.line: 4,
18+
key.column: 6
19+
},
20+
{
21+
key.kind: source.lang.swift.decl.class,
22+
key.name: "MyPrivateClass",
23+
key.usr: "s:C23index_is_test_candidateP33_E06F4E7BC5F577AB6E2EC6D3ECA1C8B914MyPrivateClass",
24+
key.line: 6,
25+
key.column: 15
26+
},
27+
{
28+
key.kind: source.lang.swift.decl.class,
29+
key.name: "MyClass",
30+
key.usr: "s:C23index_is_test_candidate7MyClass",
31+
key.line: 10,
32+
key.column: 14,
33+
key.entities: [
34+
{
35+
key.kind: source.lang.swift.decl.function.method.instance,
36+
key.name: "doesNotStartWithTest()",
37+
key.usr: "s:FC23index_is_test_candidate7MyClass20doesNotStartWithTestFT_T_",
38+
key.line: 11,
39+
key.column: 8
40+
},
41+
{
42+
key.kind: source.lang.swift.decl.function.method.instance,
43+
key.name: "test_startsWithTest_butTakesAParam(param:)",
44+
key.usr: "s:FC23index_is_test_candidate7MyClass34test_startsWithTest_butTakesAParamFT5paramSi_T_",
45+
key.line: 12,
46+
key.column: 8,
47+
key.entities: [
48+
{
49+
key.kind: source.lang.swift.ref.struct,
50+
key.name: "Int",
51+
key.usr: "s:Si",
52+
key.line: 12,
53+
key.column: 50
54+
}
55+
]
56+
},
57+
{
58+
key.kind: source.lang.swift.decl.function.method.instance,
59+
key.name: "test_startsWithTest_andTakesNoParams_butReturnsNonVoid()",
60+
key.usr: "s:FC23index_is_test_candidate7MyClass54test_startsWithTest_andTakesNoParams_butReturnsNonVoidFT_Si",
61+
key.line: 13,
62+
key.column: 8,
63+
key.entities: [
64+
{
65+
key.kind: source.lang.swift.ref.struct,
66+
key.name: "Int",
67+
key.usr: "s:Si",
68+
key.line: 13,
69+
key.column: 68
70+
}
71+
]
72+
},
73+
{
74+
key.kind: source.lang.swift.decl.function.method.instance,
75+
key.name: "test_startsWithTest_takesNoParams_andReturnsVoid_butIsPrivate()",
76+
key.usr: "s:FC23index_is_test_candidate7MyClassP33_E06F4E7BC5F577AB6E2EC6D3ECA1C8B961test_startsWithTest_takesNoParams_andReturnsVoid_butIsPrivateFT_T_",
77+
key.line: 14,
78+
key.column: 16,
79+
},
80+
{
81+
key.kind: source.lang.swift.decl.function.method.instance,
82+
key.name: "test_startsWithTest_takesNoParams_returnsVoid()",
83+
key.usr: "s:FC23index_is_test_candidate7MyClass45test_startsWithTest_takesNoParams_returnsVoidFT_T_",
84+
key.line: 15,
85+
key.column: 8,
86+
key.is_test_candidate: 1
87+
},
88+
{
89+
key.kind: source.lang.swift.decl.function.method.instance,
90+
key.name: "test_startsWithTest_takesNoParams_returnsVoid_andThrows()",
91+
key.usr: "s:FC23index_is_test_candidate7MyClass55test_startsWithTest_takesNoParams_returnsVoid_andThrowsFzT_T_",
92+
key.line: 16,
93+
key.column: 8,
94+
key.is_test_candidate: 1
95+
}
96+
]
97+
}
98+
]
99+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: %sourcekitd-test -req=index %s -- -serialize-diagnostics -serialize-diagnostics-path %t.dia %s | %sed_clean > %t.response
2+
// RUN: diff -u %s.response %t.response
3+
4+
// This test verifies that, when Objective-C interop is enabled, all "test
5+
// candidate" methods are surfaced regardless of visibility. (On Linux, only
6+
// internal or public methods are considered "test candidates".)
7+
// REQUIRES: objc_interop
8+
9+
func test_takesNoParams_andReturnsVoid_butIsNotAnInstanceMethod() {}
10+
11+
struct MyStruct {
12+
func test_startsWithTest_takesNoParams_returnsVoid_butIsDefinedOnAStruct() {}
13+
}
14+
15+
private class MyPrivateClass() {
16+
func test_startsWithTest_takesNoParams_returnsVoid_andIsPrivate() {}
17+
}
18+
19+
public class MyClass {
20+
func doesNotStartWithTest() {}
21+
func test_startsWithTest_butTakesAParam(param: Int) {}
22+
func test_startsWithTest_andTakesNoParams_butReturnsNonVoid() -> Int {}
23+
private func test_startsWithTest_takesNoParams_returnsVoid_andIsPrivate() {}
24+
func test_startsWithTest_takesNoParams_returnsVoid() {}
25+
func test_startsWithTest_takesNoParams_returnsVoid_andThrows() throws {}
26+
}
27+
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
{
2+
key.hash: <hash>,
3+
key.dependencies: [
4+
{
5+
key.kind: source.lang.swift.import.module.swift,
6+
key.name: "Swift",
7+
key.filepath: Swift.swiftmodule,
8+
key.hash: <hash>,
9+
key.is_system: 1
10+
}
11+
],
12+
key.entities: [
13+
{
14+
key.kind: source.lang.swift.decl.function.free,
15+
key.name: "test_takesNoParams_andReturnsVoid_butIsNotAnInstanceMethod()",
16+
key.usr: "s:F28index_is_test_candidate_objc58test_takesNoParams_andReturnsVoid_butIsNotAnInstanceMethodFT_T_",
17+
key.line: 9,
18+
key.column: 6
19+
},
20+
{
21+
key.kind: source.lang.swift.decl.struct,
22+
key.name: "MyStruct",
23+
key.usr: "s:V28index_is_test_candidate_objc8MyStruct",
24+
key.line: 11,
25+
key.column: 8,
26+
key.entities: [
27+
{
28+
key.kind: source.lang.swift.decl.function.method.instance,
29+
key.name: "test_startsWithTest_takesNoParams_returnsVoid_butIsDefinedOnAStruct()",
30+
key.usr: "s:FV28index_is_test_candidate_objc8MyStruct67test_startsWithTest_takesNoParams_returnsVoid_butIsDefinedOnAStructFT_T_",
31+
key.line: 12,
32+
key.column: 8
33+
}
34+
]
35+
},
36+
{
37+
key.kind: source.lang.swift.decl.class,
38+
key.name: "MyPrivateClass",
39+
key.usr: "s:C28index_is_test_candidate_objcP33_32FED72643814BE1A523406CD2E729AA14MyPrivateClass",
40+
key.line: 15,
41+
key.column: 15
42+
},
43+
{
44+
key.kind: source.lang.swift.decl.class,
45+
key.name: "MyClass",
46+
key.usr: "s:C28index_is_test_candidate_objc7MyClass",
47+
key.line: 19,
48+
key.column: 14,
49+
key.entities: [
50+
{
51+
key.kind: source.lang.swift.decl.function.method.instance,
52+
key.name: "doesNotStartWithTest()",
53+
key.usr: "s:FC28index_is_test_candidate_objc7MyClass20doesNotStartWithTestFT_T_",
54+
key.line: 20,
55+
key.column: 8
56+
},
57+
{
58+
key.kind: source.lang.swift.decl.function.method.instance,
59+
key.name: "test_startsWithTest_butTakesAParam(param:)",
60+
key.usr: "s:FC28index_is_test_candidate_objc7MyClass34test_startsWithTest_butTakesAParamFT5paramSi_T_",
61+
key.line: 21,
62+
key.column: 8,
63+
key.entities: [
64+
{
65+
key.kind: source.lang.swift.ref.struct,
66+
key.name: "Int",
67+
key.usr: "s:Si",
68+
key.line: 21,
69+
key.column: 50
70+
}
71+
]
72+
},
73+
{
74+
key.kind: source.lang.swift.decl.function.method.instance,
75+
key.name: "test_startsWithTest_andTakesNoParams_butReturnsNonVoid()",
76+
key.usr: "s:FC28index_is_test_candidate_objc7MyClass54test_startsWithTest_andTakesNoParams_butReturnsNonVoidFT_Si",
77+
key.line: 22,
78+
key.column: 8,
79+
key.entities: [
80+
{
81+
key.kind: source.lang.swift.ref.struct,
82+
key.name: "Int",
83+
key.usr: "s:Si",
84+
key.line: 22,
85+
key.column: 68
86+
}
87+
]
88+
},
89+
{
90+
key.kind: source.lang.swift.decl.function.method.instance,
91+
key.name: "test_startsWithTest_takesNoParams_returnsVoid_andIsPrivate()",
92+
key.usr: "s:FC28index_is_test_candidate_objc7MyClassP33_32FED72643814BE1A523406CD2E729AA58test_startsWithTest_takesNoParams_returnsVoid_andIsPrivateFT_T_",
93+
key.line: 23,
94+
key.column: 16,
95+
key.is_test_candidate: 1
96+
},
97+
{
98+
key.kind: source.lang.swift.decl.function.method.instance,
99+
key.name: "test_startsWithTest_takesNoParams_returnsVoid()",
100+
key.usr: "s:FC28index_is_test_candidate_objc7MyClass45test_startsWithTest_takesNoParams_returnsVoidFT_T_",
101+
key.line: 24,
102+
key.column: 8,
103+
key.is_test_candidate: 1
104+
},
105+
{
106+
key.kind: source.lang.swift.decl.function.method.instance,
107+
key.name: "test_startsWithTest_takesNoParams_returnsVoid_andThrows()",
108+
key.usr: "s:FC28index_is_test_candidate_objc7MyClass55test_startsWithTest_takesNoParams_returnsVoid_andThrowsFzT_T_",
109+
key.line: 25,
110+
key.column: 8,
111+
key.is_test_candidate: 1
112+
}
113+
]
114+
}
115+
]
116+
}

0 commit comments

Comments
 (0)