Skip to content

Commit 43881fc

Browse files
committed
Address typo-kind review comments from #1037
1 parent d784537 commit 43881fc

File tree

7 files changed

+102
-78
lines changed

7 files changed

+102
-78
lines changed

Sources/Diagnose/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
add_library(Diagnose STATIC
22
CommandLineArgumentsReducer.swift
33
DiagnoseCommand.swift
4-
FileReducer.swift
54
FixItApplier.swift
65
OSLogScraper.swift
76
ReductionError.swift
87
ReproducerBundle.swift
98
RequestInfo.swift
109
SourceKitDRequestExecutor.swift
11-
SourcekitdRequestCommand.swift)
10+
SourcekitdRequestCommand.swift
11+
SourceReducer.swift)
1212

1313
set_target_properties(Diagnose PROPERTIES
1414
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY})

Sources/Diagnose/CommandLineArgumentsReducer.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,19 @@
1212

1313
import Foundation
1414

15+
// MARK: - Entry point
16+
17+
extension RequestInfo {
18+
func reduceCommandLineArguments(using executor: SourceKitRequestExecutor) async throws -> RequestInfo {
19+
let reducer = CommandLineArgumentReducer(sourcekitdExecutor: executor)
20+
return try await reducer.run(initialRequestInfo: self)
21+
}
22+
}
23+
24+
// MARK: - FileProducer
25+
1526
/// Reduces the compiler arguments needed to reproduce a sourcekitd crash.
16-
class CommandLineArgumentReducer {
27+
fileprivate class CommandLineArgumentReducer {
1728
/// The executor that is used to run a sourcekitd request and check whether it
1829
/// still crashes.
1930
private let sourcekitdExecutor: SourceKitRequestExecutor

Sources/Diagnose/DiagnoseCommand.swift

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ public struct DiagnoseCommand: AsyncParsableCommand {
2626
@Option(
2727
name: .customLong("request-file"),
2828
help:
29-
"Path to a sourcekitd request. If not specified, the command will look for crashed sourcekitd requests and have been logged to OSLog"
29+
"Path to a sourcekitd request. If not specified, the command will look for crashed sourcekitd requests that have been logged to OSLog"
3030
)
3131
var sourcekitdRequestPath: String?
3232

3333
@Option(
3434
name: .customLong("os-log-history"),
35-
help: "If now request file is passed, how many minutes of OS Log history should be scraped for a crash."
35+
help: "If no request file is passed, how many minutes of OS Log history should be scraped for a crash."
3636
)
3737
var osLogScrapeDuration: Int = 60
3838

@@ -88,45 +88,53 @@ public struct DiagnoseCommand: AsyncParsableCommand {
8888
throw ReductionError("Unable to find sourcekitd.framework")
8989
}
9090

91+
var reproducerBundle: URL?
9192
for (name, requestInfo) in try requestInfos() {
9293
print("-- Diagnosing \(name)")
9394
do {
94-
var requestInfo = requestInfo
95-
var nspredicate: NSPredicate? = nil
96-
#if canImport(Darwin)
97-
if let predicate {
98-
nspredicate = NSPredicate(format: predicate)
99-
}
100-
#endif
101-
let executor = SourceKitRequestExecutor(
102-
sourcekitd: URL(fileURLWithPath: sourcekitd),
103-
reproducerPredicate: nspredicate
104-
)
105-
let fileReducer = FileReducer(sourcekitdExecutor: executor)
106-
requestInfo = try await fileReducer.run(initialRequestInfo: requestInfo)
107-
108-
let commandLineReducer = CommandLineArgumentReducer(sourcekitdExecutor: executor)
109-
requestInfo = try await commandLineReducer.run(initialRequestInfo: requestInfo)
110-
111-
let reproducerBundle = try makeReproducerBundle(for: requestInfo)
112-
113-
print("----------------------------------------")
114-
print(
115-
"Reduced SourceKit crash and created a bundle that contains information to reproduce the issue at the following path."
116-
)
117-
print("Please file an issue at https://github.com/apple/sourcekit-lsp/issues/new and attach this bundle")
118-
print()
119-
print(reproducerBundle.path)
120-
121-
// We have found a reproducer. Stop. Looking further probably won't help because other crashes are likely the same cause.
122-
return
95+
reproducerBundle = try await reduce(requestInfo: requestInfo, sourcekitd: sourcekitd)
96+
// If reduce didn't throw, we have found a reproducer. Stop.
97+
// Looking further probably won't help because other crashes are likely the same cause.
98+
break
12399
} catch {
124100
// Reducing this request failed. Continue reducing the next one, maybe that one succeeds.
125101
print(error)
126102
}
127103
}
128104

129-
print("No reducible crashes found")
130-
throw ExitCode(1)
105+
guard let reproducerBundle else {
106+
print("No reducible crashes found")
107+
throw ExitCode(1)
108+
}
109+
print(
110+
"""
111+
----------------------------------------
112+
Reduced SourceKit issue and created a bundle that contains a reduced sourcekitd request exhibiting the issue
113+
and all the files referenced from the request.
114+
The information in this bundle should be sufficient to reproduce the issue.
115+
116+
Please file an issue at https://github.com/apple/sourcekit-lsp/issues/new and attach the bundle located at
117+
\(reproducerBundle.path)
118+
"""
119+
)
120+
121+
}
122+
123+
private func reduce(requestInfo: RequestInfo, sourcekitd: String) async throws -> URL {
124+
var requestInfo = requestInfo
125+
var nspredicate: NSPredicate? = nil
126+
#if canImport(Darwin)
127+
if let predicate {
128+
nspredicate = NSPredicate(format: predicate)
129+
}
130+
#endif
131+
let executor = SourceKitRequestExecutor(
132+
sourcekitd: URL(fileURLWithPath: sourcekitd),
133+
reproducerPredicate: nspredicate
134+
)
135+
requestInfo = try await requestInfo.reduceInputFile(using: executor)
136+
requestInfo = try await requestInfo.reduceCommandLineArguments(using: executor)
137+
138+
return try makeReproducerBundle(for: requestInfo)
131139
}
132140
}

Sources/Diagnose/RequestInfo.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@ struct RequestInfo {
1818
/// The JSON request object. Contains the following dynamic placeholders:
1919
/// - `$OFFSET`: To be replaced by `offset` before running the request
2020
/// - `$FILE`: Will be replaced with a path to the file that contains the reduced source code.
21-
/// - `$COMPILERARGS`: Will be replaced by the compiler arguments of the request
21+
/// - `$COMPILER_ARGS`: Will be replaced by the compiler arguments of the request
2222
var requestTemplate: String
2323

2424
/// The offset at which the sourcekitd request should be run. Replaces the
2525
/// `$OFFSET` placeholder in the request template.
2626
var offset: Int
2727

28-
/// The compiler arguments of the request. Replaces the `$COMPILERARGS`placeholder in the request template.
28+
/// The compiler arguments of the request. Replaces the `$COMPILER_ARGS`placeholder in the request template.
2929
var compilerArgs: [String]
3030

3131
/// The contents of the file that the sourcekitd request operates on.
@@ -42,7 +42,7 @@ struct RequestInfo {
4242
return
4343
requestTemplate
4444
.replacingOccurrences(of: "$OFFSET", with: String(offset))
45-
.replacingOccurrences(of: "$COMPILERARGS", with: compilerArgs)
45+
.replacingOccurrences(of: "$COMPILER_ARGS", with: compilerArgs)
4646
.replacingOccurrences(of: "$FILE", with: file.path)
4747

4848
}
@@ -106,7 +106,7 @@ private func extractCompilerArguments(
106106
else {
107107
return (requestTemplate, [])
108108
}
109-
let template = lines[...compilerArgsStartIndex] + ["$COMPILERARGS"] + lines[compilerArgsEndIndex...]
109+
let template = lines[...compilerArgsStartIndex] + ["$COMPILER_ARGS"] + lines[compilerArgsEndIndex...]
110110
let compilerArgsJson = "[" + lines[(compilerArgsStartIndex + 1)..<compilerArgsEndIndex].joined(separator: "\n") + "]"
111111
let compilerArgs = try JSONDecoder().decode([String].self, from: compilerArgsJson)
112112
return (template.joined(separator: "\n"), compilerArgs)

Sources/Diagnose/SourceKitDRequestExecutor.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import SourceKitD
1616
import struct TSCBasic.AbsolutePath
1717
import class TSCBasic.Process
1818

19-
/// The different states in which a sourcektid request can finish.
19+
/// The different states in which a sourcekitd request can finish.
2020
enum SourceKitDRequestResult {
2121
/// The request succeeded.
2222
case success(response: String)

Sources/Diagnose/FileReducer.swift renamed to Sources/Diagnose/SourceReducer.swift

Lines changed: 38 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,19 @@ import SourceKitD
1515
import SwiftParser
1616
import SwiftSyntax
1717

18+
// MARK: - Entry point
19+
20+
extension RequestInfo {
21+
func reduceInputFile(using executor: SourceKitRequestExecutor) async throws -> RequestInfo {
22+
let reducer = SourceReducer(sourcekitdExecutor: executor)
23+
return try await reducer.run(initialRequestInfo: self)
24+
}
25+
}
26+
27+
// MARK: - SourceReducer
28+
1829
/// Reduces an input source file while continuing to reproduce the crash
19-
class FileReducer {
30+
fileprivate class SourceReducer {
2031
/// The executor that is used to run a sourcekitd request and check whether it
2132
/// still crashes.
2233
private let sourcekitdExecutor: SourceKitRequestExecutor
@@ -32,7 +43,7 @@ class FileReducer {
3243
/// Reduce the file contents in `initialRequest` to a smaller file that still reproduces a crash.
3344
func run(initialRequestInfo: RequestInfo) async throws -> RequestInfo {
3445
var requestInfo = initialRequestInfo
35-
try await validateRequestInfoCrashes(requestInfo: requestInfo)
46+
try await validateRequestInfoReproucesIssue(requestInfo: requestInfo)
3647

3748
requestInfo = try await fatalErrorFunctionBodies(requestInfo)
3849
requestInfo = try await removeMembersAndCodeBlockItemsBodies(requestInfo)
@@ -51,12 +62,12 @@ class FileReducer {
5162
return requestInfo
5263
}
5364

54-
// MARK: - Reduction steps
65+
// MARK: Reduction steps
5566

56-
private func validateRequestInfoCrashes(requestInfo: RequestInfo) async throws {
67+
private func validateRequestInfoReproucesIssue(requestInfo: RequestInfo) async throws {
5768
let initialReproducer = try await runReductionStep(requestInfo: requestInfo) { tree in [] }
5869
if initialReproducer == nil {
59-
throw ReductionError("Initial request info did not crash")
70+
throw ReductionError("Initial request info did not reproduce the issue")
6071
}
6172
}
6273

@@ -110,7 +121,7 @@ class FileReducer {
110121
}
111122
}
112123

113-
// MARK: - Primitives to run reduction steps
124+
// MARK: Primitives to run reduction steps
114125

115126
func logSuccessfulReduction(_ requestInfo: RequestInfo) {
116127
print("Reduced source file to \(requestInfo.fileContents.utf8.count) bytes")
@@ -191,15 +202,15 @@ class FileReducer {
191202

192203
// MARK: - Reduce functions
193204

194-
/// See `FileReducer.runReductionStep`
195-
protocol StatefulReducer {
205+
/// See `SourceReducer.runReductionStep`
206+
fileprivate protocol StatefulReducer {
196207
func reduce(tree: SourceFileSyntax) -> [SourceEdit]
197208
}
198209

199210
// MARK: Replace function bodies
200211

201212
/// Tries replacing one function body by `fatalError()` at a time.
202-
class ReplaceFunctionBodiesByFatalError: StatefulReducer {
213+
fileprivate class ReplaceFunctionBodiesByFatalError: StatefulReducer {
203214
/// The function bodies that should not be replaced by `fatalError()`.
204215
///
205216
/// When we tried replacing a function body by `fatalError`, it gets added to this list.
@@ -240,24 +251,23 @@ class ReplaceFunctionBodiesByFatalError: StatefulReducer {
240251
}
241252
if keepFunctionBodies.contains(node.statements.description.trimmingCharacters(in: .whitespacesAndNewlines)) {
242253
return .visitChildren
243-
} else {
244-
keepFunctionBodies.append(node.statements.description.trimmingCharacters(in: .whitespacesAndNewlines))
245-
edits.append(
246-
SourceEdit(
247-
range: node.statements.position..<node.statements.endPosition,
248-
replacement: "\(node.statements.leadingTrivia)fatalError()"
249-
)
250-
)
251-
return .skipChildren
252254
}
255+
keepFunctionBodies.append(node.statements.description.trimmingCharacters(in: .whitespacesAndNewlines))
256+
edits.append(
257+
SourceEdit(
258+
range: node.statements.position..<node.statements.endPosition,
259+
replacement: "\(node.statements.leadingTrivia)fatalError()"
260+
)
261+
)
262+
return .skipChildren
253263
}
254264
}
255265
}
256266

257267
// MARK: Remove members and code block items
258268

259269
/// Tries removing `MemberBlockItemSyntax` and `CodeBlockItemSyntax` one at a time.
260-
class RemoveMembersAndCodeBlockItems: StatefulReducer {
270+
fileprivate class RemoveMembersAndCodeBlockItems: StatefulReducer {
261271
/// The code block items / members that shouldn't be removed.
262272
///
263273
/// See `ReplaceFunctionBodiesByFatalError.keepFunctionBodies`.
@@ -323,7 +333,7 @@ class RemoveMembersAndCodeBlockItems: StatefulReducer {
323333
}
324334

325335
/// Removes all comments from the source file.
326-
func removeComments(from tree: SourceFileSyntax) -> [SourceEdit] {
336+
fileprivate func removeComments(from tree: SourceFileSyntax) -> [SourceEdit] {
327337
class CommentRemover: SyntaxVisitor {
328338
var edits: [SourceEdit] = []
329339

@@ -366,7 +376,7 @@ fileprivate extension TriviaPiece {
366376

367377
// MARK: Inline first include
368378

369-
class FirstImportFinder: SyntaxAnyVisitor {
379+
fileprivate class FirstImportFinder: SyntaxAnyVisitor {
370380
var firstImport: ImportDeclSyntax?
371381

372382
override func visitAny(_ node: Syntax) -> SyntaxVisitorContinueKind {
@@ -391,24 +401,18 @@ class FirstImportFinder: SyntaxAnyVisitor {
391401
}
392402
}
393403

394-
private func getSwiftInterface(_ moduleName: String, executor: SourceKitRequestExecutor, compilerArgs: [String])
395-
async throws -> String
396-
{
397-
// FIXME: Use the sourcekitd specified on the command line once rdar://121676425 is fixed
398-
let sourcekitdPath =
399-
"/Applications/Geode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/sourcekitdInProc.framework/sourcekitdInProc"
400-
let executor = SourceKitRequestExecutor(
401-
sourcekitd: URL(fileURLWithPath: sourcekitdPath),
402-
reproducerPredicate: nil
403-
)
404-
404+
fileprivate func getSwiftInterface(
405+
_ moduleName: String,
406+
executor: SourceKitRequestExecutor,
407+
compilerArgs: [String]
408+
) async throws -> String {
405409
// We use `RequestInfo` and its template to add the compiler arguments to the request.
406410
let requestTemplate = """
407411
{
408412
key.request: source.request.editor.open.interface,
409413
key.name: "fake",
410414
key.compilerargs: [
411-
$COMPILERARGS
415+
$COMPILER_ARGS
412416
],
413417
key.modulename: "\(moduleName)"
414418
}
@@ -448,7 +452,7 @@ private func getSwiftInterface(_ moduleName: String, executor: SourceKitRequestE
448452
return try JSONDecoder().decode(String.self, from: sanitizedData)
449453
}
450454

451-
func inlineFirstImport(
455+
fileprivate func inlineFirstImport(
452456
in tree: SourceFileSyntax,
453457
executor: SourceKitRequestExecutor,
454458
compilerArgs: [String]

Sources/sourcekit-lsp/SourceKitLSP.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -267,8 +267,9 @@ struct SourceKitLSP: AsyncParsableCommand {
267267
}
268268
)
269269

270-
// Park the main function by sleeping for a year.
271-
// All request handling is done on other threads.
272-
try await Task.sleep(for: .seconds(60 * 60 * 24 * 365))
270+
// Park the main function.
271+
// All request handling is done on other threads and sourcekit-lsp exits by calling `_Exit` when it receives a
272+
// shutdown notification.
273+
let _: Void = await withCheckedContinuation { _ in }
273274
}
274275
}

0 commit comments

Comments
 (0)