Skip to content

Commit 786f807

Browse files
committed
Update the compiler arguments used for background AST builds
This fixes two issues: 1. The SwiftPM build system was setup without passing through whether it should prepare or not. This meant that we lost eg. the argument to allow compiler errors when building the AST (even though it was set when building the modules) 2. The compiler argument adjustment to remove harmful and unnecessary flags only applied to indexing arguments, not those passed to the AST builds Resolves rdar://141508656. (cherry picked from commit ab12429)
1 parent 89f7613 commit 786f807

13 files changed

+325
-240
lines changed

Contributor Documentation/Implementing a BSP server.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ If the build system loads the entire build graph during initialization, it may i
2929

3030
## Supporting background indexing
3131

32-
To support background indexing, the build system must set `data.prepareProvider: true` in the `build/initialize` response and implement the `buildTarget/prepare` method.
32+
To support background indexing, the build system must set `data.prepareProvider: true` in the `build/initialize` response and implement the `buildTarget/prepare` method. The compiler options used to prepare a target should match those sent for `textDocument/sourceKitOptions` in order to avoid mismatches when loading modules.
3333

3434
## Optional methods
3535

Sources/BuildSystemIntegration/BuildSystemManager.swift

Lines changed: 164 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
314314
}
315315
}
316316

317-
private var cachedSourceKitOptions = RequestCache<TextDocumentSourceKitOptionsRequest>()
317+
private var cachedAdjustedSourceKitOptions = RequestCache<TextDocumentSourceKitOptionsRequest>()
318318

319319
private var cachedBuildTargets = Cache<WorkspaceBuildTargetsRequest, [BuildTargetIdentifier: BuildTargetInfo]>()
320320

@@ -518,7 +518,7 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
518518
} else {
519519
nil
520520
}
521-
self.cachedSourceKitOptions.clear(isolation: self) { cacheKey in
521+
self.cachedAdjustedSourceKitOptions.clear(isolation: self) { cacheKey in
522522
guard let updatedTargets else {
523523
// All targets might have changed
524524
return true
@@ -747,13 +747,22 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
747747
target: target,
748748
language: language
749749
)
750-
751-
let response = try await cachedSourceKitOptions.get(request, isolation: self) { request in
752-
try await buildSystemAdapter.send(request)
750+
let response = try await cachedAdjustedSourceKitOptions.get(request, isolation: self) { request in
751+
let options = try await buildSystemAdapter.send(request)
752+
switch language.semanticKind {
753+
case .swift:
754+
return options?.adjustArgsForSemanticSwiftFunctionality(fileToIndex: document)
755+
case .clang:
756+
return options?.adjustingArgsForSemanticClangFunctionality()
757+
default:
758+
return options
759+
}
753760
}
761+
754762
guard let response else {
755763
return nil
756764
}
765+
757766
return FileBuildSettings(
758767
compilerArguments: response.compilerArguments,
759768
workingDirectory: response.workingDirectory,
@@ -1228,3 +1237,153 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
12281237
private func isDescendant(_ selfPathComponents: [String], of otherPathComponents: [String]) -> Bool {
12291238
return selfPathComponents.dropLast().starts(with: otherPathComponents)
12301239
}
1240+
1241+
fileprivate extension TextDocumentSourceKitOptionsResponse {
1242+
/// Adjust compiler arguments that were created for building to compiler arguments that should be used for indexing
1243+
/// or background AST builds.
1244+
///
1245+
/// This removes compiler arguments that produce output files and adds arguments to eg. allow errors and index the
1246+
/// file.
1247+
func adjustArgsForSemanticSwiftFunctionality(fileToIndex: DocumentURI) -> TextDocumentSourceKitOptionsResponse {
1248+
let optionsToRemove: [CompilerCommandLineOption] = [
1249+
.flag("c", [.singleDash]),
1250+
.flag("disable-cmo", [.singleDash]),
1251+
.flag("emit-dependencies", [.singleDash]),
1252+
.flag("emit-module-interface", [.singleDash]),
1253+
.flag("emit-module", [.singleDash]),
1254+
.flag("emit-objc-header", [.singleDash]),
1255+
.flag("incremental", [.singleDash]),
1256+
.flag("no-color-diagnostics", [.singleDash]),
1257+
.flag("parseable-output", [.singleDash]),
1258+
.flag("save-temps", [.singleDash]),
1259+
.flag("serialize-diagnostics", [.singleDash]),
1260+
.flag("use-frontend-parseable-output", [.singleDash]),
1261+
.flag("validate-clang-modules-once", [.singleDash]),
1262+
.flag("whole-module-optimization", [.singleDash]),
1263+
.flag("experimental-skip-all-function-bodies", frontendName: "Xfrontend", [.singleDash]),
1264+
.flag("experimental-skip-non-inlinable-function-bodies", frontendName: "Xfrontend", [.singleDash]),
1265+
.flag("experimental-skip-non-exportable-decls", frontendName: "Xfrontend", [.singleDash]),
1266+
.flag("experimental-lazy-typecheck", frontendName: "Xfrontend", [.singleDash]),
1267+
1268+
.option("clang-build-session-file", [.singleDash], [.separatedBySpace]),
1269+
.option("emit-module-interface-path", [.singleDash], [.separatedBySpace]),
1270+
.option("emit-module-path", [.singleDash], [.separatedBySpace]),
1271+
.option("emit-objc-header-path", [.singleDash], [.separatedBySpace]),
1272+
.option("emit-package-module-interface-path", [.singleDash], [.separatedBySpace]),
1273+
.option("emit-private-module-interface-path", [.singleDash], [.separatedBySpace]),
1274+
.option("num-threads", [.singleDash], [.separatedBySpace]),
1275+
// Technically, `-o` and the output file don't need to be separated by a space. Eg. `swiftc -oa file.swift` is
1276+
// valid and will write to an output file named `a`.
1277+
// We can't support that because the only way to know that `-output-file-map` is a different flag and not an option
1278+
// to write to an output file named `utput-file-map` is to know all compiler arguments of `swiftc`, which we don't.
1279+
.option("o", [.singleDash], [.separatedBySpace]),
1280+
.option("output-file-map", [.singleDash], [.separatedBySpace, .separatedByEqualSign]),
1281+
]
1282+
1283+
var result: [String] = []
1284+
result.reserveCapacity(compilerArguments.count)
1285+
var iterator = compilerArguments.makeIterator()
1286+
while let argument = iterator.next() {
1287+
switch optionsToRemove.firstMatch(for: argument) {
1288+
case .removeOption:
1289+
continue
1290+
case .removeOptionAndNextArgument:
1291+
_ = iterator.next()
1292+
continue
1293+
case .removeOptionAndPreviousArgument(let name):
1294+
if let previousArg = result.last, previousArg.hasSuffix("-\(name)") {
1295+
_ = result.popLast()
1296+
}
1297+
continue
1298+
case nil:
1299+
break
1300+
}
1301+
result.append(argument)
1302+
}
1303+
1304+
result += [
1305+
// Avoid emitting the ABI descriptor, we don't need it
1306+
"-Xfrontend", "-empty-abi-descriptor",
1307+
]
1308+
1309+
result += supplementalClangIndexingArgs.flatMap { ["-Xcc", $0] }
1310+
1311+
return TextDocumentSourceKitOptionsResponse(compilerArguments: result, workingDirectory: workingDirectory)
1312+
}
1313+
1314+
/// Adjust compiler arguments that were created for building to compiler arguments that should be used for indexing
1315+
/// or background AST builds.
1316+
///
1317+
/// This removes compiler arguments that produce output files and adds arguments to eg. typecheck only.
1318+
func adjustingArgsForSemanticClangFunctionality() -> TextDocumentSourceKitOptionsResponse {
1319+
let optionsToRemove: [CompilerCommandLineOption] = [
1320+
// Disable writing of a depfile
1321+
.flag("M", [.singleDash]),
1322+
.flag("MD", [.singleDash]),
1323+
.flag("MMD", [.singleDash]),
1324+
.flag("MG", [.singleDash]),
1325+
.flag("MM", [.singleDash]),
1326+
.flag("MV", [.singleDash]),
1327+
// Don't create phony targets
1328+
.flag("MP", [.singleDash]),
1329+
// Don't write out compilation databases
1330+
.flag("MJ", [.singleDash]),
1331+
// Don't compile
1332+
.flag("c", [.singleDash]),
1333+
1334+
.flag("fmodules-validate-once-per-build-session", [.singleDash]),
1335+
1336+
// Disable writing of a depfile
1337+
.option("MT", [.singleDash], [.noSpace, .separatedBySpace]),
1338+
.option("MF", [.singleDash], [.noSpace, .separatedBySpace]),
1339+
.option("MQ", [.singleDash], [.noSpace, .separatedBySpace]),
1340+
1341+
// Don't write serialized diagnostic files
1342+
.option("serialize-diagnostics", [.singleDash, .doubleDash], [.separatedBySpace]),
1343+
1344+
.option("fbuild-session-file", [.singleDash], [.separatedByEqualSign]),
1345+
]
1346+
1347+
var result: [String] = []
1348+
result.reserveCapacity(compilerArguments.count)
1349+
var iterator = compilerArguments.makeIterator()
1350+
while let argument = iterator.next() {
1351+
switch optionsToRemove.firstMatch(for: argument) {
1352+
case .removeOption:
1353+
continue
1354+
case .removeOptionAndNextArgument:
1355+
_ = iterator.next()
1356+
continue
1357+
case .removeOptionAndPreviousArgument(let name):
1358+
if let previousArg = result.last, previousArg.hasSuffix("-\(name)") {
1359+
_ = result.popLast()
1360+
}
1361+
continue
1362+
case nil:
1363+
break
1364+
}
1365+
result.append(argument)
1366+
}
1367+
result += supplementalClangIndexingArgs
1368+
result.append(
1369+
"-fsyntax-only"
1370+
)
1371+
return TextDocumentSourceKitOptionsResponse(compilerArguments: result, workingDirectory: workingDirectory)
1372+
}
1373+
}
1374+
1375+
fileprivate let supplementalClangIndexingArgs: [String] = [
1376+
// Retain extra information for indexing
1377+
"-fretain-comments-from-system-headers",
1378+
// Pick up macro definitions during indexing
1379+
"-Xclang", "-detailed-preprocessing-record",
1380+
1381+
// libclang uses 'raw' module-format. Match it so we can reuse the module cache and PCHs that libclang uses.
1382+
"-Xclang", "-fmodule-format=raw",
1383+
1384+
// Be less strict - we want to continue and typecheck/index as much as possible
1385+
"-Xclang", "-fallow-pch-with-compiler-errors",
1386+
"-Xclang", "-fallow-pcm-with-compiler-errors",
1387+
"-Wno-non-modular-include-in-framework-module",
1388+
"-Wno-incomplete-umbrella",
1389+
]

Sources/BuildSystemIntegration/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ add_library(BuildSystemIntegration STATIC
1010
BuiltInBuildSystemAdapter.swift
1111
CompilationDatabase.swift
1212
CompilationDatabaseBuildSystem.swift
13+
CompilerCommandLineOption.swift
1314
DetermineBuildSystem.swift
1415
ExternalBuildSystemAdapter.swift
1516
FallbackBuildSettings.swift

Sources/SemanticIndex/CompilerCommandLineOption.swift renamed to Sources/BuildSystemIntegration/CompilerCommandLineOption.swift

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,18 @@
1212

1313
package struct CompilerCommandLineOption {
1414
/// Return value of `matches(argument:)`.
15-
package enum Match {
15+
package enum Match: Equatable {
1616
/// The `CompilerCommandLineOption` matched the command line argument. The next element in the command line is a
1717
/// separate argument and should not be removed.
1818
case removeOption
1919

2020
/// The `CompilerCommandLineOption` matched the command line argument. The next element in the command line is an
2121
/// argument to this option and should be removed as well.
2222
case removeOptionAndNextArgument
23+
24+
/// The `CompilerCommandLineOption` matched the command line argument. The previous element in the command line is
25+
/// a prefix to this argument and should be removed if it matches `name`.
26+
case removeOptionAndPreviousArgument(name: String)
2327
}
2428

2529
package enum DashSpelling {
@@ -39,16 +43,28 @@ package struct CompilerCommandLineOption {
3943
/// The name of the option, without any preceeding `-` or `--`.
4044
private let name: String
4145

46+
/// The name of the argument that prefixes this flag, without any preceeding `-` or `--` (eg. `Xfrontend`/`Xclang`).
47+
private let frontendName: String?
48+
4249
/// Whether the option can be spelled with one or two dashes.
4350
private let dashSpellings: [DashSpelling]
4451

4552
/// The ways that arguments can specified after the option. Empty if the option is a flag that doesn't take any
4653
/// argument.
4754
private let argumentStyles: [ArgumentStyles]
4855

49-
package static func flag(_ name: String, _ dashSpellings: [DashSpelling]) -> CompilerCommandLineOption {
56+
package static func flag(
57+
_ name: String,
58+
frontendName: String? = nil,
59+
_ dashSpellings: [DashSpelling]
60+
) -> CompilerCommandLineOption {
5061
precondition(!dashSpellings.isEmpty)
51-
return CompilerCommandLineOption(name: name, dashSpellings: dashSpellings, argumentStyles: [])
62+
return CompilerCommandLineOption(
63+
name: name,
64+
frontendName: frontendName,
65+
dashSpellings: dashSpellings,
66+
argumentStyles: []
67+
)
5268
}
5369

5470
package static func option(
@@ -58,10 +74,29 @@ package struct CompilerCommandLineOption {
5874
) -> CompilerCommandLineOption {
5975
precondition(!dashSpellings.isEmpty)
6076
precondition(!argumentStyles.isEmpty)
61-
return CompilerCommandLineOption(name: name, dashSpellings: dashSpellings, argumentStyles: argumentStyles)
77+
return CompilerCommandLineOption(
78+
name: name,
79+
frontendName: nil,
80+
dashSpellings: dashSpellings,
81+
argumentStyles: argumentStyles
82+
)
6283
}
6384

6485
package func matches(argument: String) -> Match? {
86+
let match = matchesIgnoringFrontend(argument: argument)
87+
guard let match, let frontendName else {
88+
return match
89+
}
90+
91+
switch match {
92+
case .removeOption:
93+
return .removeOptionAndPreviousArgument(name: frontendName)
94+
default:
95+
return match
96+
}
97+
}
98+
99+
private func matchesIgnoringFrontend(argument: String) -> Match? {
65100
let argumentName: Substring
66101
if argument.hasPrefix("--") {
67102
if dashSpellings.contains(.doubleDash) {

Sources/BuildSystemIntegration/SwiftPMBuildSystem.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,8 @@ package actor SwiftPMBuildSystem: BuiltInBuildSystem {
309309
configuration: buildConfiguration,
310310
toolchain: destinationSwiftPMToolchain,
311311
triple: destinationSDK.targetTriple,
312-
flags: buildFlags
312+
flags: buildFlags,
313+
prepareForIndexing: options.backgroundPreparationModeOrDefault.toSwiftPMPreparation
313314
)
314315

315316
packageLoadingQueue.async {
@@ -735,4 +736,17 @@ fileprivate extension URL {
735736
}
736737
}
737738

739+
fileprivate extension SourceKitLSPOptions.BackgroundPreparationMode {
740+
var toSwiftPMPreparation: BuildParameters.PrepareForIndexingMode {
741+
switch self {
742+
case .build:
743+
return .off
744+
case .noLazy:
745+
return .noLazy
746+
case .enabled:
747+
return .on
748+
}
749+
}
750+
}
751+
738752
#endif

Sources/LanguageServerProtocolExtensions/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ add_library(LanguageServerProtocolExtensions STATIC
33
Connection+Send.swift
44
DocumentURI+CustomLogStringConvertible.swift
55
DocumentURI+symlinkTarget.swift
6-
Language+InferredFromFileExtension.swift
6+
Language+Inference.swift
77
LocalConnection.swift
88
QueueBasedMessageHandler.swift
99
RequestAndReply.swift

Sources/LanguageServerProtocolExtensions/Language+InferredFromFileExtension.swift renamed to Sources/LanguageServerProtocolExtensions/Language+Inference.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,22 @@ import LanguageServerProtocol
1919
#endif
2020

2121
extension Language {
22+
package enum SemanticKind {
23+
case clang
24+
case swift
25+
}
26+
27+
package var semanticKind: SemanticKind? {
28+
switch self {
29+
case .swift:
30+
return .swift
31+
case .c, .cpp, .objective_c, .objective_cpp:
32+
return .clang
33+
default:
34+
return nil
35+
}
36+
}
37+
2238
package init?(inferredFromFileExtension uri: DocumentURI) {
2339
// URL.pathExtension is only set for file URLs but we want to also infer a file extension for non-file URLs like
2440
// untitled:file.cpp

Sources/SemanticIndex/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11

22
add_library(SemanticIndex STATIC
33
CheckedIndex.swift
4-
CompilerCommandLineOption.swift
54
IndexTaskDescription.swift
65
IndexTestHooks.swift
76
PreparationTaskDescription.swift

0 commit comments

Comments
 (0)