Skip to content

Commit 23780ca

Browse files
authored
Merge pull request #2061 from ahoppen/normalized-path
Do not apply file path normalization in `mainFiles(containing:)` to the file itself
2 parents 1f1e440 + e198b4b commit 23780ca

File tree

6 files changed

+100
-10
lines changed

6 files changed

+100
-10
lines changed

Sources/BuildSystemIntegration/BuildSystemManager.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1312,6 +1312,19 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
13121312
#if canImport(Darwin)
13131313
if let buildableSourceFiles = try? await self.buildableSourceFiles() {
13141314
return mainFiles.map { mainFile in
1315+
if mainFile == uri {
1316+
// Do not apply the standardized file normalization to the source file itself. Otherwise we would get the
1317+
// following behavior:
1318+
// - We have a build system that uses standardized file paths and index a file as /tmp/test.c
1319+
// - We are asking for the main files of /private/tmp/test.c
1320+
// - Since indexstore-db uses realpath for everything, we find the unit for /tmp/test.c as a unit containg
1321+
// /private/tmp/test.c, which has /private/tmp/test.c as the main file.
1322+
// - If we applied the path normalization, we would normalize /private/tmp/test.c to /tmp/test.c, thus
1323+
// reporting that /tmp/test.c is a main file containing /private/tmp/test.c,
1324+
// But that doesn't make sense (it would, in fact cause us to treat /private/tmp/test.c as a header file that
1325+
// we should index using /tmp/test.c as a main file.
1326+
return mainFile
1327+
}
13151328
if buildableSourceFiles.contains(mainFile) {
13161329
return mainFile
13171330
}

Sources/SKTestSupport/CustomBuildServerTestProject.swift

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ package import LanguageServerProtocol
1717
import LanguageServerProtocolExtensions
1818
import SKLogging
1919
package import SKOptions
20-
import SourceKitLSP
20+
package import SourceKitLSP
2121
import SwiftExtensions
2222
import ToolchainRegistry
2323
import XCTest
@@ -245,21 +245,24 @@ package final class CustomBuildServerTestProject<BuildServer: CustomBuildServer>
245245
files: [RelativeFileLocation: String],
246246
buildServer buildServerType: BuildServer.Type,
247247
options: SourceKitLSPOptions? = nil,
248+
hooks: Hooks = Hooks(),
248249
enableBackgroundIndexing: Bool = false,
250+
testScratchDir: URL? = nil,
249251
testName: String = #function
250252
) async throws {
251-
let hooks: Hooks = Hooks(
252-
buildSystemHooks: BuildSystemHooks(injectBuildServer: { [buildServerBox] projectRoot, connectionToSourceKitLSP in
253-
let buildServer = BuildServer(projectRoot: projectRoot, connectionToSourceKitLSP: connectionToSourceKitLSP)
254-
buildServerBox.value = buildServer
255-
return LocalConnection(receiverName: "TestBuildSystem", handler: buildServer)
256-
})
257-
)
253+
var hooks = hooks
254+
XCTAssertNil(hooks.buildSystemHooks.injectBuildServer)
255+
hooks.buildSystemHooks.injectBuildServer = { [buildServerBox] projectRoot, connectionToSourceKitLSP in
256+
let buildServer = BuildServer(projectRoot: projectRoot, connectionToSourceKitLSP: connectionToSourceKitLSP)
257+
buildServerBox.value = buildServer
258+
return LocalConnection(receiverName: "TestBuildSystem", handler: buildServer)
259+
}
258260
try await super.init(
259261
files: files,
260262
options: options,
261263
hooks: hooks,
262264
enableBackgroundIndexing: enableBackgroundIndexing,
265+
testScratchDir: testScratchDir,
263266
testName: testName
264267
)
265268
}

Sources/SKTestSupport/MultiFileTestProject.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,10 +139,11 @@ package class MultiFileTestProject {
139139
enableBackgroundIndexing: Bool = false,
140140
usePullDiagnostics: Bool = true,
141141
preInitialization: ((TestSourceKitLSPClient) -> Void)? = nil,
142+
testScratchDir overrideTestScratchDir: URL? = nil,
142143
cleanUp: (@Sendable () -> Void)? = nil,
143144
testName: String = #function
144145
) async throws {
145-
scratchDirectory = try testScratchDir(testName: testName)
146+
scratchDirectory = try overrideTestScratchDir ?? testScratchDir(testName: testName)
146147
self.fileData = try Self.writeFilesToDisk(files: files, scratchDirectory: scratchDirectory)
147148

148149
self.testClient = try await TestSourceKitLSPClient(

Sources/SemanticIndex/SemanticIndexManager.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,7 @@ package final actor SemanticIndexManager {
481481
// if we request the same header to be indexed twice, we'll pick the same unit file the second time around,
482482
// realize that its timestamp is later than the modification date of the header and we don't need to re-index.
483483
let mainFile = await buildSystemManager.mainFiles(containing: uri)
484+
.filter { sourceFiles.contains($0) }
484485
.sorted(by: { $0.stringValue < $1.stringValue }).first
485486
guard let mainFile else {
486487
logger.log("Not indexing \(uri) because its main file could not be inferred")

Tests/SourceKitLSPTests/BackgroundIndexingTests.swift

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2210,6 +2210,77 @@ final class BackgroundIndexingTests: XCTestCase {
22102210
}
22112211
)
22122212
}
2213+
2214+
func testBuildSystemUsesStandardizedFileUrlsInsteadOfRealpath() async throws {
2215+
try SkipUnless.platformIsDarwin("The realpath vs standardized path difference only exists on macOS")
2216+
2217+
final class BuildSystem: CustomBuildServer {
2218+
let inProgressRequestsTracker = CustomBuildServerInProgressRequestTracker()
2219+
private let projectRoot: URL
2220+
private var testFileURL: URL { projectRoot.appendingPathComponent("test.c").standardized }
2221+
2222+
required init(projectRoot: URL, connectionToSourceKitLSP: any LanguageServerProtocol.Connection) {
2223+
self.projectRoot = projectRoot
2224+
}
2225+
2226+
func initializeBuildRequest(_ request: InitializeBuildRequest) async throws -> InitializeBuildResponse {
2227+
return initializationResponse(
2228+
initializeData: SourceKitInitializeBuildResponseData(
2229+
indexDatabasePath: try projectRoot.appendingPathComponent("index-db").filePath,
2230+
indexStorePath: try projectRoot.appendingPathComponent("index-store").filePath,
2231+
prepareProvider: true,
2232+
sourceKitOptionsProvider: true
2233+
)
2234+
)
2235+
}
2236+
2237+
func buildTargetSourcesRequest(_ request: BuildTargetSourcesRequest) async throws -> BuildTargetSourcesResponse {
2238+
return BuildTargetSourcesResponse(items: [
2239+
SourcesItem(target: .dummy, sources: [SourceItem(uri: URI(testFileURL), kind: .file, generated: false)])
2240+
])
2241+
}
2242+
2243+
func textDocumentSourceKitOptionsRequest(
2244+
_ request: TextDocumentSourceKitOptionsRequest
2245+
) async throws -> TextDocumentSourceKitOptionsResponse? {
2246+
return TextDocumentSourceKitOptionsResponse(compilerArguments: [request.textDocument.uri.pseudoPath])
2247+
}
2248+
}
2249+
2250+
let scratchDirectory = URL(fileURLWithPath: "/tmp")
2251+
.appendingPathComponent("sourcekitlsp-test")
2252+
.appendingPathComponent(testScratchName())
2253+
let indexedFiles = ThreadSafeBox<[DocumentURI]>(initialValue: [])
2254+
let project = try await CustomBuildServerTestProject(
2255+
files: [
2256+
"test.c": "void x() {}"
2257+
],
2258+
buildServer: BuildSystem.self,
2259+
hooks: Hooks(
2260+
indexHooks: IndexHooks(
2261+
updateIndexStoreTaskDidStart: { task in
2262+
indexedFiles.withLock { indexedFiles in
2263+
indexedFiles += task.filesToIndex.map(\.file.sourceFile)
2264+
}
2265+
}
2266+
)
2267+
),
2268+
enableBackgroundIndexing: true,
2269+
testScratchDir: scratchDirectory
2270+
)
2271+
try await project.testClient.send(PollIndexRequest())
2272+
2273+
// Ensure that changing `/private/tmp/.../test.c` only causes `/tmp/.../test.c` to be indexed, not
2274+
// `/private/tmp/.../test.c`.
2275+
indexedFiles.value = []
2276+
let testFileURL = try XCTUnwrap(project.uri(for: "test.c").fileURL?.realpath)
2277+
try await "void y() {}".writeWithRetry(to: testFileURL)
2278+
project.testClient.send(
2279+
DidChangeWatchedFilesNotification(changes: [FileEvent(uri: DocumentURI(testFileURL), type: .changed)])
2280+
)
2281+
try await project.testClient.send(PollIndexRequest())
2282+
XCTAssertEqual(indexedFiles.value, [try project.uri(for: "test.c")])
2283+
}
22132284
}
22142285

22152286
extension HoverResponseContents {

Tests/SourceKitLSPTests/WorkspaceTests.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1303,7 +1303,8 @@ final class WorkspaceTests: XCTestCase {
13031303
try SkipUnless.platformIsDarwin("The realpath vs standardized path difference only exists on macOS")
13041304

13051305
// Explicitly create a directory at /tmp (which is a standardized path but whose realpath is /private/tmp)
1306-
let scratchDirectory = URL(fileURLWithPath: "/tmp").appendingPathComponent("sourcekitlsp-test-\(UUID())")
1306+
let scratchDirectory = URL(fileURLWithPath: "/tmp")
1307+
.appendingPathComponent(testScratchName())
13071308
try FileManager.default.createDirectory(at: scratchDirectory, withIntermediateDirectories: true)
13081309

13091310
defer {

0 commit comments

Comments
 (0)