Files
sourcekit-lsp/Tests/SourceKitLSPTests/ImplementationTests.swift
T
2025-10-31 14:11:11 -07:00

311 lines
7.6 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
@_spi(SourceKitLSP) import LanguageServerProtocol
import SKLogging
import SKTestSupport
import TSCBasic
import XCTest
final class ImplementationTests: SourceKitLSPTestCase {
// MARK: - Utilities
private func testImplementation(
_ markedText: String,
expectedLocations expectedLocationMarkers: [String] = ["2️⃣"],
testName: String = #function,
line: UInt = #line
) async throws {
let project = try await IndexedSingleSwiftFileTestProject(markedText, testName: testName)
let response = try await project.testClient.send(
ImplementationRequest(
textDocument: TextDocumentIdentifier(project.fileURI),
position: project.positions["1️⃣"]
)
)
let expectedLocations = expectedLocationMarkers.map {
Location(uri: project.fileURI, range: Range(project.positions[$0]))
}
XCTAssertEqual(response?.locations, expectedLocations, line: line)
}
// MARK: - Tests
func testProtocolInheritance() async throws {
try await testImplementation(
"""
protocol 1️⃣Protocol {}
struct Struct: 2️⃣Protocol {}
"""
)
}
func testProtocolStaticVar() async throws {
try await testImplementation(
"""
protocol Protocol {
static var 1️⃣staticVar: Int { get }
}
struct Struct: Protocol {
static var 2️⃣staticVar: Int { 123 }
}
"""
)
}
func testProtocolStaticFunc() async throws {
try await testImplementation(
"""
protocol Protocol {
static func 1️⃣staticFunction()
}
struct Struct: Protocol {
static func 2️⃣staticFunction() {}
}
"""
)
}
func testProtocolVar() async throws {
try await testImplementation(
"""
protocol Protocol {
var 1️⃣variable: Int { get }
}
struct Struct: Protocol {
var 2️⃣variable: Int { 123 }
}
"""
)
}
func testProtocolFunc() async throws {
try await testImplementation(
"""
protocol Protocol {
func 1️⃣function()
}
struct Struct: Protocol {
func 2️⃣function() {}
}
"""
)
}
func testClassInheritance() async throws {
try await testImplementation(
"""
class 1️⃣Class {}
class Subclass: 2️⃣Class {}
"""
)
}
func testClassClassVar() async throws {
try await testImplementation(
"""
class Class {
class var 1️⃣classVar: Int { 123 }
}
class Subclass: Class {
override class var 2️⃣classVar: Int { 123 }
}
"""
)
}
func testClassClassFunc() async throws {
try await testImplementation(
"""
class Class {
class func 1️⃣classFunction() {}
}
class Subclass: Class {
override class func 2️⃣classFunction() {}
}
"""
)
}
func testClassVar() async throws {
try await testImplementation(
"""
class Class {
var 1️⃣variable: Int { 123 }
}
class Subclass: Class {
override var 2️⃣variable: Int { 123 }
}
"""
)
}
func testClassFunc() async throws {
try await testImplementation(
"""
class Class {
func 1️⃣function() {}
}
class Subclass: Class {
override func 2️⃣function() {}
}
"""
)
}
func testClassHierarchy() async throws {
try await testImplementation(
"""
class 1️⃣MyClass {}
class SubA: 2️⃣MyClass {}
class SubASub: SubA {}
class SubB: 3️⃣MyClass {}
class SubC: 4️⃣MyClass {}
""",
expectedLocations: ["2️⃣", "3️⃣", "4️⃣"]
)
}
func testSubclassHierarchy() async throws {
try await testImplementation(
"""
class MyClass {}
class 1️⃣Subclass: MyClass {}
class SubSubClassA: 2️⃣Subclass {}
class SubSubClassB: 3️⃣Subclass {}
""",
expectedLocations: ["2️⃣", "3️⃣"]
)
}
func testProtocolHierarchy() async throws {
try await testImplementation(
"""
protocol 1️⃣MyProtocol {}
protocol OtherProtocol: 2️⃣MyProtocol {}
class ClassA: 3️⃣MyProtocol {}
class ClassB: OtherProtocol, 4️⃣MyProtocol {}
""",
expectedLocations: ["2️⃣", "3️⃣", "4️⃣"]
)
}
func testProtocolConformanceInExtension() async throws {
try await testImplementation(
"""
protocol 1️⃣MyProtocol {}
class MyClass {}
extension MyClass: 2️⃣MyProtocol {}
"""
)
}
func testStandaloneClass() async throws {
try await testImplementation(
"""
class 1️⃣MyClass {}
""",
expectedLocations: []
)
}
func testStandaloneClassFunc() async throws {
try await testImplementation(
"""
class MyClass {
func 1️⃣myFunc() {}
}
""",
expectedLocations: []
)
}
func testOverrideClassVar() async throws {
try await testImplementation(
"""
class MyClass {
var 1️⃣member: String { "puszysta" }
}
class SubclassA: MyClass {
override var 2️⃣member: String { "glazurowana" }
}
class SubclassB: MyClass {
override var 3️⃣member: String { "piaskowana" }
}
""",
expectedLocations: ["2️⃣", "3️⃣"]
)
}
func testOverrideProtocolFunc() async throws {
// TODO: We should not be reporting locations 4, 5 and 7 because they don't actually contain myFunc.
// We should, however, be reporting location 6. (https://github.com/swiftlang/sourcekit-lsp/issues/1600)
try await testImplementation(
"""
protocol MyProto {
func 1️⃣myFunc()
}
class ClassA: MyProto {
func 2️⃣myFunc() {}
}
class ClassB: MyProto {
func 3️⃣myFunc() {}
}
class 4️⃣ClassBSubA: ClassB {}
class 5️⃣ClassBSubB: ClassB {}
class RetroactiveConformanceClassWithMyFuncInClassDecl {
func 6️⃣myFunc() { }
}
extension 7️⃣RetroactiveConformanceClassWithMyFuncInClassDecl: MyProto {}
class RetroactiveConformanceClassWithMyFuncInExtension {}
extension RetroactiveConformanceClassWithMyFuncInExtension: MyProto {
func 8️⃣myFunc() { }
}
""",
expectedLocations: ["2️⃣", "3️⃣", "4️⃣", "5️⃣", "7️⃣", "8️⃣"]
)
}
func testCrossFile() async throws {
let project = try await SwiftPMTestProject(
files: [
"a.swift": """
protocol 1️⃣MyProto {}
""",
"b.swift": """
struct MyStruct: 2️⃣MyProto {}
""",
],
enableBackgroundIndexing: true
)
let (aUri, aPositions) = try project.openDocument("a.swift")
let response = try await project.testClient.send(
ImplementationRequest(
textDocument: TextDocumentIdentifier(aUri),
position: aPositions["1️⃣"]
)
)
XCTAssertEqual(
response?.locations,
[Location(uri: try project.uri(for: "b.swift"), range: Range(try project.position(of: "2️⃣", in: "b.swift")))]
)
}
}