From 707b8e586051a1aed6fb22d9bf57efb79df2fd51 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 c0a317a16..fea4575ba 100644 --- a/Sources/SKSwiftPMWorkspace/SwiftPMBuildSystem.swift +++ b/Sources/SKSwiftPMWorkspace/SwiftPMBuildSystem.swift @@ -399,7 +399,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 @@ -411,7 +411,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 } @@ -544,7 +544,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 984c51161..8841fe571 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