//===----------------------------------------------------------------------===// // // 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")))] ) } }