From f8304323228537c613df04b8467be64b296b4447 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Tue, 2 Jul 2024 13:41:34 -0400 Subject: [PATCH] Fix incorrect ConfiguredTargets for Package.swift w/ multiple workspaces In a project with multiple folders each containing a Package.swift, the `bestWorkspace` found in `workspaceForDocument(uri:)` was always the first one encountered. `fileHandlingCapability(for:)` checks if there are configured targets for the Package.swift and if there are, the workspace is chosen since the Package.swift is determined to be part of the workspace. However the check in `configuredTargets(for:)` always returned a target for any `Package.swift`, even if that `Package.swift` was not part of the workspace associated with the BuildSystem. Ultimately this manifested as code completion not working in all but one of the Package.swift files in a multi workspace project. Some work was done in #1210 to address swiftlang/vscode-swift#768, which is where this issue originated from. However while verifying swiftlang/vscode-swift#768 I found that #1210 didn't fully address code completion of `Package.swift` files in multi workspace projects. --- .../SwiftPMBuildSystem.swift | 8 ++-- Tests/SourceKitLSPTests/WorkspaceTests.swift | 43 +++++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/Sources/SKSwiftPMWorkspace/SwiftPMBuildSystem.swift b/Sources/SKSwiftPMWorkspace/SwiftPMBuildSystem.swift index bd9df1e0d..2690a39e9 100644 --- a/Sources/SKSwiftPMWorkspace/SwiftPMBuildSystem.swift +++ b/Sources/SKSwiftPMWorkspace/SwiftPMBuildSystem.swift @@ -400,7 +400,7 @@ extension SwiftPMBuildSystem { ) self.fileToTargets = [DocumentURI: [SwiftBuildTarget]]( - modulesGraph.allTargets.flatMap { target in + modulesGraph.allModules.flatMap { target in return target.sources.paths.compactMap { (filePath) -> (key: DocumentURI, value: [SwiftBuildTarget])? in guard let buildTarget = buildDescription.getBuildTarget(for: target, in: modulesGraph) else { return nil @@ -412,7 +412,7 @@ extension SwiftPMBuildSystem { ) self.sourceDirToTargets = [DocumentURI: [SwiftBuildTarget]]( - modulesGraph.allTargets.compactMap { (target) -> (DocumentURI, [SwiftBuildTarget])? in + modulesGraph.allModules.compactMap { (target) -> (DocumentURI, [SwiftBuildTarget])? in guard let buildTarget = buildDescription.getBuildTarget(for: target, in: modulesGraph) else { return nil } @@ -545,7 +545,9 @@ extension SwiftPMBuildSystem: SKCore.BuildSystem { return targets.map(ConfiguredTarget.init) } - if path.basename == "Package.swift" { + if path.basename == "Package.swift" + && projectRoot == (try? TSCBasic.resolveSymlinks(TSCBasic.AbsolutePath(path.parentDirectory))) + { // We use an empty target name to represent the package manifest since an empty target name is not valid for any // user-defined target. return [ConfiguredTarget.forPackageManifest] diff --git a/Tests/SourceKitLSPTests/WorkspaceTests.swift b/Tests/SourceKitLSPTests/WorkspaceTests.swift index 5542c44f1..6a81b2338 100644 --- a/Tests/SourceKitLSPTests/WorkspaceTests.swift +++ b/Tests/SourceKitLSPTests/WorkspaceTests.swift @@ -191,6 +191,49 @@ final class WorkspaceTests: XCTestCase { XCTAssertEqual(diags, .full(RelatedFullDocumentDiagnosticReport(items: []))) } + func testCorrectWorkspaceForPackageSwiftInMultiSwiftPMWorkspaceSetup() async throws { + let project = try await MultiFileTestProject( + files: [ + // PackageA + "PackageA/Sources/MyLibrary/libA.swift": "", + "PackageA/Package.swift": SwiftPMTestProject.defaultPackageManifest, + + // PackageB + "PackageB/Sources/MyLibrary/libB.swift": "", + "PackageB/Package.swift": SwiftPMTestProject.defaultPackageManifest, + ], + workspaces: { scratchDir in + return [ + WorkspaceFolder(uri: DocumentURI(scratchDir)), + WorkspaceFolder(uri: DocumentURI(scratchDir.appendingPathComponent("PackageA"))), + WorkspaceFolder(uri: DocumentURI(scratchDir.appendingPathComponent("PackageB"))), + ] + } + ) + + let pkgA = DocumentURI( + project.scratchDirectory + .appendingPathComponent("PackageA") + .appendingPathComponent("Package.swift") + ) + + let pkgB = DocumentURI( + project.scratchDirectory + .appendingPathComponent("PackageB") + .appendingPathComponent("Package.swift") + ) + + assertEqual( + await project.testClient.server.workspaceForDocument(uri: pkgA)?.rootUri, + DocumentURI(project.scratchDirectory.appendingPathComponent("PackageA")) + ) + + assertEqual( + await project.testClient.server.workspaceForDocument(uri: pkgB)?.rootUri, + DocumentURI(project.scratchDirectory.appendingPathComponent("PackageB")) + ) + } + func testSwiftPMPackageInSubfolder() async throws { let packageManifest = """ // swift-tools-version: 5.7