Skip to content

Commit 25548d5

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.
1 parent 883a17c commit 25548d5

13 files changed

+308
-229
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
@@ -325,7 +325,7 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
325325
}
326326
}
327327

328-
private var cachedSourceKitOptions = RequestCache<TextDocumentSourceKitOptionsRequest>()
328+
private var cachedAdjustedSourceKitOptions = RequestCache<TextDocumentSourceKitOptionsRequest>()
329329

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

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

Sources/BuildSystemIntegration/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ add_library(BuildSystemIntegration STATIC
99
BuiltInBuildSystem.swift
1010
BuiltInBuildSystemAdapter.swift
1111
CompilationDatabase.swift
12+
CompilerCommandLineOption.swift
1213
DetermineBuildSystem.swift
1314
ExternalBuildSystemAdapter.swift
1415
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
@@ -287,7 +287,8 @@ package actor SwiftPMBuildSystem: BuiltInBuildSystem {
287287
configuration: buildConfiguration,
288288
toolchain: destinationSwiftPMToolchain,
289289
triple: destinationSDK.targetTriple,
290-
flags: buildFlags
290+
flags: buildFlags,
291+
prepareForIndexing: options.backgroundPreparationModeOrDefault.toSwiftPMPreparation
291292
)
292293

293294
packageLoadingQueue.async {
@@ -709,4 +710,17 @@ fileprivate extension URL {
709710
}
710711
}
711712

713+
fileprivate extension SourceKitLSPOptions.BackgroundPreparationMode {
714+
var toSwiftPMPreparation: BuildParameters.PrepareForIndexingMode {
715+
switch self {
716+
case .build:
717+
return .off
718+
case .noLazy:
719+
return .noLazy
720+
case .enabled:
721+
return .on
722+
}
723+
}
724+
}
725+
712726
#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
IndexHooks.swift
76
PreparationTaskDescription.swift

0 commit comments

Comments
 (0)