Skip to content

Commit 1c49cf1

Browse files
authored
Merge pull request swiftlang#2192 from hamishknight/tic-tac-toe-two-6.2
2 parents d8d5661 + 263341d commit 1c49cf1

File tree

2 files changed

+151
-11
lines changed

2 files changed

+151
-11
lines changed

Sources/SourceKitLSP/Swift/SwiftTestingScanner.swift

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,9 @@ final class SyntacticSwiftTestingTestScanner: SyntaxVisitor {
237237
return .skipChildren
238238
}
239239

240+
let displayName = attributeData?.displayName ?? typeNames.last!
241+
let typeNames = typeNames.map { backtickIfNeeded($0).name }
242+
240243
let memberScanner = SyntacticSwiftTestingTestScanner(
241244
snapshot: snapshot,
242245
allTestsDisabled: attributeData?.isDisabled ?? false,
@@ -256,7 +259,7 @@ final class SyntacticSwiftTestingTestScanner: SyntaxVisitor {
256259
let testItem = AnnotatedTestItem(
257260
testItem: TestItem(
258261
id: (parentTypeNames + typeNames).joined(separator: "/"),
259-
label: attributeData?.displayName ?? typeNames.last!,
262+
label: displayName,
260263
disabled: (attributeData?.isDisabled ?? false) || allTestsDisabled,
261264
style: TestStyle.swiftTesting,
262265
location: Location(uri: snapshot.uri, range: range),
@@ -305,15 +308,13 @@ final class SyntacticSwiftTestingTestScanner: SyntaxVisitor {
305308
return visitTypeOrExtensionDecl(node, typeNames: [identifier.name])
306309
}
307310

308-
/// If the given identifier requires backticks to be a valid decl identifier,
311+
/// If the given name requires backticks to be a valid decl identifier,
309312
/// applies backticks and returns `true` along with the new name. Otherwise
310-
/// returns `false` with the original name.
311-
func backtickIfNeeded(_ ident: Identifier) -> (backticked: Bool, name: String) {
312-
let name = ident.name
313-
if name == "``" {
314-
// Special case: `` stays as ``. Any other backticked name will have
315-
// been stripped by Identifier.
316-
return (true, name)
313+
/// returns `false` with the name.
314+
func backtickIfNeeded(_ name: String) -> (backticked: Bool, name: String) {
315+
var name = name
316+
if name.first == "`" && name.last == "`" {
317+
name = String(name.dropFirst().dropLast())
317318
}
318319
let needsBackticks = !name.isValidSwiftIdentifier(for: .variableName)
319320
return (needsBackticks, needsBackticks ? "`\(name)`" : name)
@@ -335,16 +336,30 @@ final class SyntacticSwiftTestingTestScanner: SyntaxVisitor {
335336
let parameters = node.signature.parameterClause.parameters.map { param in
336337
let result =
337338
if let identifier = param.firstName.identifier {
338-
backtickIfNeeded(identifier).name
339+
backtickIfNeeded(identifier.name).name
339340
} else {
340341
// Something like `_`, leave as-is.
341342
param.firstName.text
342343
}
343344
return "\(result):"
344345
}.joined()
345346

346-
let (hasBackticks, baseName) = backtickIfNeeded(identifier)
347+
let (hasBackticks, baseName) = backtickIfNeeded(identifier.name)
347348
let fullName = "\(baseName)(\(parameters))"
349+
350+
// If we have a display name provided by the attribute, use it, otherwise
351+
// we can infer the display name from a raw identifier if we have one.
352+
//
353+
// A raw identifier is considered an alternative way of spelling the display
354+
// name, so e.g these have the same display name:
355+
//
356+
// ```
357+
// @Test("foo bar") func foo() {}
358+
// @Test func `foo bar`() {}
359+
// ```
360+
//
361+
// as such it shouldn't include any parameters. If we just have a regular
362+
// name then we use the full name as the display name.
348363
let displayName = attributeData.displayName ?? (hasBackticks ? identifier.name : fullName)
349364

350365
let range = snapshot.absolutePositionRange(

Tests/SourceKitLSPTests/DocumentTestDiscoveryTests.swift

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,131 @@ final class DocumentTestDiscoveryTests: XCTestCase {
783783
)
784784
}
785785

786+
func testSwiftTestingTestWithNestedRawIdentifiers() async throws {
787+
let testClient = try await TestSourceKitLSPClient()
788+
let uri = DocumentURI(for: .swift)
789+
790+
let positions = testClient.openDocument(
791+
"""
792+
import Testing
793+
794+
1️⃣struct `A B` {
795+
2️⃣struct `C` {
796+
3️⃣struct `D.E` {
797+
4️⃣@Test
798+
func `foo bar`() {
799+
#expect(1 == 2)
800+
}5️⃣
801+
}6️⃣
802+
}7️⃣
803+
}8️⃣
804+
""",
805+
uri: uri
806+
)
807+
808+
let tests = try await testClient.send(DocumentTestsRequest(textDocument: TextDocumentIdentifier(uri)))
809+
XCTAssertEqual(
810+
tests,
811+
[
812+
TestItem(
813+
id: "`A B`",
814+
label: "A B",
815+
style: TestStyle.swiftTesting,
816+
location: Location(uri: uri, range: positions["1️⃣"]..<positions["8️⃣"]),
817+
children: [
818+
TestItem(
819+
id: "`A B`/C",
820+
label: "C",
821+
style: TestStyle.swiftTesting,
822+
location: Location(uri: uri, range: positions["2️⃣"]..<positions["7️⃣"]),
823+
children: [
824+
TestItem(
825+
id: "`A B`/C/`D.E`",
826+
label: "D.E",
827+
style: TestStyle.swiftTesting,
828+
location: Location(uri: uri, range: positions["3️⃣"]..<positions["6️⃣"]),
829+
children: [
830+
TestItem(
831+
id: "`A B`/C/`D.E`/`foo bar`()",
832+
label: "foo bar",
833+
style: TestStyle.swiftTesting,
834+
location: Location(uri: uri, range: positions["4️⃣"]..<positions["5️⃣"])
835+
)
836+
]
837+
)
838+
]
839+
)
840+
]
841+
)
842+
]
843+
)
844+
}
845+
846+
func testSwiftTestingTestWithNestedRawIdentifiersExtension() async throws {
847+
let testClient = try await TestSourceKitLSPClient()
848+
let uri = DocumentURI(for: .swift)
849+
850+
let positions = testClient.openDocument(
851+
"""
852+
import Testing
853+
854+
struct `A.B` {
855+
struct `C` {
856+
struct `D E` {}
857+
}
858+
}
859+
1️⃣extension `A.B`.`C`.`D E` {
860+
2️⃣@Test
861+
func `foo bar`() {
862+
#expect(1 == 2)
863+
}3️⃣
864+
}4️⃣
865+
5️⃣extension `A.B` {
866+
6️⃣@Test
867+
func `bar baz`() {
868+
#expect(1 == 2)
869+
}7️⃣
870+
}8️⃣
871+
""",
872+
uri: uri
873+
)
874+
875+
let tests = try await testClient.send(DocumentTestsRequest(textDocument: TextDocumentIdentifier(uri)))
876+
XCTAssertEqual(
877+
tests,
878+
[
879+
TestItem(
880+
id: "`A.B`/C/`D E`",
881+
label: "D E",
882+
style: TestStyle.swiftTesting,
883+
location: Location(uri: uri, range: positions["1️⃣"]..<positions["4️⃣"]),
884+
children: [
885+
TestItem(
886+
id: "`A.B`/C/`D E`/`foo bar`()",
887+
label: "foo bar",
888+
style: TestStyle.swiftTesting,
889+
location: Location(uri: uri, range: positions["2️⃣"]..<positions["3️⃣"])
890+
)
891+
]
892+
),
893+
TestItem(
894+
id: "`A.B`",
895+
label: "A.B",
896+
style: TestStyle.swiftTesting,
897+
location: Location(uri: uri, range: positions["5️⃣"]..<positions["8️⃣"]),
898+
children: [
899+
TestItem(
900+
id: "`A.B`/`bar baz`()",
901+
label: "bar baz",
902+
style: TestStyle.swiftTesting,
903+
location: Location(uri: uri, range: positions["6️⃣"]..<positions["7️⃣"])
904+
)
905+
]
906+
),
907+
]
908+
)
909+
}
910+
786911
func testSwiftTestingTestWithSlashRawIdentifiers() async throws {
787912
let testClient = try await TestSourceKitLSPClient()
788913
let uri = DocumentURI(for: .swift)

0 commit comments

Comments
 (0)