Skip to content

Make the Diagnose module build in Swift 6 mode #1293

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Sources/Diagnose/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
add_library(Diagnose STATIC
CommandConfiguration+Sendable.swift
CommandLineArgumentsReducer.swift
DiagnoseCommand.swift
MergeSwiftFiles.swift
Expand All @@ -11,9 +12,10 @@ add_library(Diagnose STATIC
ReproducerBundle.swift
RequestInfo.swift
SourceKitD+RunWithYaml.swift
SourcekitdRequestCommand.swift
SourceKitDRequestExecutor.swift
SourceReducer.swift
SourcekitdRequestCommand.swift
StderrStreamConcurrencySafe.swift
SwiftFrontendCrashScraper.swift
Toolchain+SwiftFrontend.swift)

Expand Down
17 changes: 17 additions & 0 deletions Sources/Diagnose/CommandConfiguration+Sendable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import ArgumentParser

// If `CommandConfiguration` is not sendable, commands can't have static `configuration` properties.
// Needed until we update Swift CI to swift-argument-parser 1.3.1, which has this conformance (rdar://128042447).
extension CommandConfiguration: @unchecked @retroactive Sendable {}
3 changes: 3 additions & 0 deletions Sources/Diagnose/CommandLineArgumentsReducer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import LSPLogging
// MARK: - Entry point

extension RequestInfo {
@MainActor
func reduceCommandLineArguments(
using executor: SourceKitRequestExecutor,
progressUpdate: (_ progress: Double, _ message: String) -> Void
Expand Down Expand Up @@ -49,6 +50,7 @@ fileprivate class CommandLineArgumentReducer {
self.progressUpdate = progressUpdate
}

@MainActor
func run(initialRequestInfo: RequestInfo) async throws -> RequestInfo {
var requestInfo = initialRequestInfo
requestInfo = try await reduce(initialRequestInfo: requestInfo, simultaneousRemove: 10)
Expand Down Expand Up @@ -113,6 +115,7 @@ fileprivate class CommandLineArgumentReducer {
return requestInfo
}

@MainActor
private func tryRemoving(
_ argumentsToRemove: ClosedRange<Int>,
from requestInfo: RequestInfo
Expand Down
21 changes: 17 additions & 4 deletions Sources/Diagnose/DiagnoseCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ import SKCore

import struct TSCBasic.AbsolutePath
import class TSCBasic.Process
import var TSCBasic.stderrStream
import class TSCUtility.PercentProgressAnimation

/// When diagnosis is started, a progress bar displayed on the terminal that shows how far the diagnose command has
/// progressed.
/// Can't be a member of `DiagnoseCommand` because then `DiagnoseCommand` is no longer codable, which it needs to be
/// to be a `AsyncParsableCommand`.
@MainActor
private var progressBar: PercentProgressAnimation? = nil

/// A component of the diagnostic bundle that's collected in independent stages.
Expand All @@ -35,7 +35,7 @@ fileprivate enum BundleComponent: String, CaseIterable, ExpressibleByArgument {
}

public struct DiagnoseCommand: AsyncParsableCommand {
public static var configuration: CommandConfiguration = CommandConfiguration(
public static let configuration: CommandConfiguration = CommandConfiguration(
commandName: "diagnose",
abstract: "Creates a bundle containing information that help diagnose issues with sourcekit-lsp"
)
Expand Down Expand Up @@ -72,6 +72,7 @@ public struct DiagnoseCommand: AsyncParsableCommand {
}
}

@MainActor
var toolchain: Toolchain? {
get async throws {
if let toolchainOverride {
Expand All @@ -96,6 +97,7 @@ public struct DiagnoseCommand: AsyncParsableCommand {

public init() {}

@MainActor
private func addSourcekitdCrashReproducer(toBundle bundlePath: URL) async throws {
reportProgress(.reproducingSourcekitdCrash(progress: 0), message: "Trying to reduce recent sourcekitd crashes")
for (name, requestInfo) in try requestInfos() {
Expand All @@ -121,6 +123,7 @@ public struct DiagnoseCommand: AsyncParsableCommand {
}
}

@MainActor
private func addSwiftFrontendCrashReproducer(toBundle bundlePath: URL) async throws {
reportProgress(
.reproducingSwiftFrontendCrash(progress: 0),
Expand Down Expand Up @@ -182,14 +185,16 @@ public struct DiagnoseCommand: AsyncParsableCommand {
}

/// Execute body and if it throws, log the error.
private func orPrintError(_ body: () async throws -> Void) async {
@MainActor
private func orPrintError(_ body: @MainActor () async throws -> Void) async {
do {
try await body()
} catch {
print(error)
}
}

@MainActor
private func addOsLog(toBundle bundlePath: URL) async throws {
#if os(macOS)
reportProgress(.collectingLogMessages(progress: 0), message: "Collecting log messages")
Expand Down Expand Up @@ -227,6 +232,7 @@ public struct DiagnoseCommand: AsyncParsableCommand {
#endif
}

@MainActor
private func addCrashLogs(toBundle bundlePath: URL) throws {
#if os(macOS)
reportProgress(.collectingCrashReports, message: "Collecting crash reports")
Expand All @@ -252,6 +258,7 @@ public struct DiagnoseCommand: AsyncParsableCommand {
#endif
}

@MainActor
private func addSwiftVersion(toBundle bundlePath: URL) async throws {
let outputFileUrl = bundlePath.appendingPathComponent("swift-versions.txt")
FileManager.default.createFile(atPath: outputFileUrl.path, contents: nil)
Expand Down Expand Up @@ -283,10 +290,12 @@ public struct DiagnoseCommand: AsyncParsableCommand {
}
}

@MainActor
private func reportProgress(_ state: DiagnoseProgressState, message: String) {
progressBar?.update(step: Int(state.progress * 100), total: 100, text: message)
}

@MainActor
public func run() async throws {
print(
"""
Expand All @@ -303,7 +312,10 @@ public struct DiagnoseCommand: AsyncParsableCommand {
"""
)

progressBar = PercentProgressAnimation(stream: stderrStream, header: "Diagnosing sourcekit-lsp issues")
progressBar = PercentProgressAnimation(
stream: stderrStreamConcurrencySafe,
header: "Diagnosing sourcekit-lsp issues"
)

let dateFormatter = ISO8601DateFormatter()
dateFormatter.timeZone = NSTimeZone.local
Expand Down Expand Up @@ -342,6 +354,7 @@ public struct DiagnoseCommand: AsyncParsableCommand {

}

@MainActor
private func reduce(
requestInfo: RequestInfo,
toolchain: Toolchain?,
Expand Down
1 change: 1 addition & 0 deletions Sources/Diagnose/MergeSwiftFiles.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ extension RequestInfo {
/// Check if the issue reproduces when merging all `.swift` input files into a single file.
///
/// Returns `nil` if the issue didn't reproduce with all `.swift` files merged.
@MainActor
func mergeSwiftFiles(
using executor: SourceKitRequestExecutor,
progressUpdate: (_ progress: Double, _ message: String) -> Void
Expand Down
4 changes: 3 additions & 1 deletion Sources/Diagnose/ReduceCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import var TSCBasic.stderrStream
import class TSCUtility.PercentProgressAnimation

public struct ReduceCommand: AsyncParsableCommand {
public static var configuration: CommandConfiguration = CommandConfiguration(
public static let configuration: CommandConfiguration = CommandConfiguration(
commandName: "reduce",
abstract: "Reduce a single sourcekitd crash",
shouldDisplay: false
Expand Down Expand Up @@ -56,6 +56,7 @@ public struct ReduceCommand: AsyncParsableCommand {
private var nsPredicate: NSPredicate? { nil }
#endif

@MainActor
var toolchain: Toolchain? {
get async throws {
if let toolchainOverride {
Expand All @@ -68,6 +69,7 @@ public struct ReduceCommand: AsyncParsableCommand {

public init() {}

@MainActor
public func run() async throws {
guard let sourcekitd = try await toolchain?.sourcekitd else {
throw ReductionError("Unable to find sourcekitd.framework")
Expand Down
9 changes: 7 additions & 2 deletions Sources/Diagnose/ReduceFrontendCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import var TSCBasic.stderrStream
import class TSCUtility.PercentProgressAnimation

public struct ReduceFrontendCommand: AsyncParsableCommand {
public static var configuration: CommandConfiguration = CommandConfiguration(
public static let configuration: CommandConfiguration = CommandConfiguration(
commandName: "reduce-frontend",
abstract: "Reduce a single swift-frontend crash",
shouldDisplay: false
Expand Down Expand Up @@ -64,6 +64,7 @@ public struct ReduceFrontendCommand: AsyncParsableCommand {
)
var frontendArgs: [String]

@MainActor
var toolchain: Toolchain? {
get async throws {
if let toolchainOverride {
Expand All @@ -76,6 +77,7 @@ public struct ReduceFrontendCommand: AsyncParsableCommand {

public init() {}

@MainActor
public func run() async throws {
guard let sourcekitd = try await toolchain?.sourcekitd else {
throw ReductionError("Unable to find sourcekitd.framework")
Expand All @@ -84,7 +86,10 @@ public struct ReduceFrontendCommand: AsyncParsableCommand {
throw ReductionError("Unable to find swift-frontend")
}

let progressBar = PercentProgressAnimation(stream: stderrStream, header: "Reducing swift-frontend crash")
let progressBar = PercentProgressAnimation(
stream: stderrStream,
header: "Reducing swift-frontend crash"
)

let executor = OutOfProcessSourceKitRequestExecutor(
sourcekitd: sourcekitd.asURL,
Expand Down
1 change: 1 addition & 0 deletions Sources/Diagnose/ReduceSourceKitDRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

extension RequestInfo {
/// Reduce the input file of this request and the command line arguments.
@MainActor
func reduce(
using executor: SourceKitRequestExecutor,
progressUpdate: (_ progress: Double, _ message: String) -> Void
Expand Down
1 change: 1 addition & 0 deletions Sources/Diagnose/ReduceSwiftFrontend.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//

@MainActor
@_spi(Testing)
public func reduceFrontendIssue(
frontendArgs: [String],
Expand Down
4 changes: 2 additions & 2 deletions Sources/Diagnose/RequestInfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import RegexBuilder

/// All the information necessary to replay a sourcektid request.
@_spi(Testing)
public struct RequestInfo {
public struct RequestInfo: Sendable {
/// The JSON request object. Contains the following dynamic placeholders:
/// - `$OFFSET`: To be replaced by `offset` before running the request
/// - `$FILE`: Will be replaced with a path to the file that contains the reduced source code.
Expand Down Expand Up @@ -51,7 +51,7 @@ public struct RequestInfo {
}

/// A fake value that is used to indicate that we are reducing a `swift-frontend` issue instead of a sourcekitd issue.
static var fakeRequestTemplateForFrontendIssues = """
static let fakeRequestTemplateForFrontendIssues = """
{
key.request: sourcekit-lsp-fake-request-for-frontend-crash
key.compilerargs: [
Expand Down
7 changes: 4 additions & 3 deletions Sources/Diagnose/SourceKitDRequestExecutor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import struct TSCBasic.ProcessResult

/// The different states in which a sourcekitd request can finish.
@_spi(Testing)
public enum SourceKitDRequestResult {
public enum SourceKitDRequestResult: Sendable {
/// The request succeeded.
case success(response: String)

Expand All @@ -46,11 +46,12 @@ fileprivate extension String {
/// An executor that can run a sourcekitd request and indicate whether the request reprodes a specified issue.
@_spi(Testing)
public protocol SourceKitRequestExecutor {
func runSourceKitD(request: RequestInfo) async throws -> SourceKitDRequestResult
func runSwiftFrontend(request: RequestInfo) async throws -> SourceKitDRequestResult
@MainActor func runSourceKitD(request: RequestInfo) async throws -> SourceKitDRequestResult
@MainActor func runSwiftFrontend(request: RequestInfo) async throws -> SourceKitDRequestResult
}

extension SourceKitRequestExecutor {
@MainActor
func run(request: RequestInfo) async throws -> SourceKitDRequestResult {
if request.requestTemplate == RequestInfo.fakeRequestTemplateForFrontendIssues {
return try await runSwiftFrontend(request: request)
Expand Down
6 changes: 6 additions & 0 deletions Sources/Diagnose/SourceReducer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import SwiftSyntax

extension RequestInfo {
@_spi(Testing)
@MainActor
public func reduceInputFile(
using executor: SourceKitRequestExecutor,
progressUpdate: (_ progress: Double, _ message: String) -> Void
Expand Down Expand Up @@ -61,6 +62,7 @@ fileprivate enum ReductionStepResult {
}

/// Reduces an input source file while continuing to reproduce the crash
@MainActor
fileprivate class SourceReducer {
/// The executor that is used to run a sourcekitd request and check whether it
/// still crashes.
Expand All @@ -84,6 +86,7 @@ fileprivate class SourceReducer {
}

/// Reduce the file contents in `initialRequest` to a smaller file that still reproduces a crash.
@MainActor
func run(initialRequestInfo: RequestInfo) async throws -> RequestInfo {
var requestInfo = initialRequestInfo
self.initialImportCount = Parser.parse(source: requestInfo.fileContents).numberOfImports
Expand Down Expand Up @@ -242,6 +245,7 @@ fileprivate class SourceReducer {
///
/// If the request still crashes after applying the edits computed by `reduce`, return the reduced request info.
/// Otherwise, return `nil`
@MainActor
private func runReductionStep(
requestInfo: RequestInfo,
reportProgress: Bool = true,
Expand Down Expand Up @@ -608,6 +612,7 @@ fileprivate class FirstImportFinder: SyntaxAnyVisitor {
/// the file that imports the module. If `areFallbackArgs` is set, we have synthesized fallback arguments that only
/// contain a target and SDK. This is useful when reducing a swift-frontend crash because sourcekitd requires driver
/// arguments but the swift-frontend crash has frontend args.
@MainActor
fileprivate func getSwiftInterface(
_ moduleName: String,
executor: SourceKitRequestExecutor,
Expand Down Expand Up @@ -680,6 +685,7 @@ fileprivate func getSwiftInterface(
return try JSONDecoder().decode(String.self, from: sanitizedData)
}

@MainActor
fileprivate func inlineFirstImport(
in tree: SourceFileSyntax,
executor: SourceKitRequestExecutor,
Expand Down
2 changes: 1 addition & 1 deletion Sources/Diagnose/SourcekitdRequestCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import SourceKitD
import struct TSCBasic.AbsolutePath

public struct SourceKitdRequestCommand: AsyncParsableCommand {
public static var configuration = CommandConfiguration(
public static let configuration = CommandConfiguration(
commandName: "run-sourcekitd-request",
abstract: "Run a sourcekitd request and print its result",
shouldDisplay: false
Expand Down
24 changes: 24 additions & 0 deletions Sources/Diagnose/StderrStreamConcurrencySafe.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import TSCLibc

import class TSCBasic.LocalFileOutputByteStream
import class TSCBasic.ThreadSafeOutputByteStream

// A version of `stderrStream` from `TSCBasic` that is a `let` and can thus be used from Swift 6.
let stderrStreamConcurrencySafe: ThreadSafeOutputByteStream = try! ThreadSafeOutputByteStream(
LocalFileOutputByteStream(
filePointer: TSCLibc.stderr,
closeOnDeinit: false
)
)
Loading