mirror of
https://github.com/nicklockwood/SwiftFormat.git
synced 2026-05-17 10:30:35 +00:00
fix race condition when parsing config files (#2521)
This commit is contained in:
committed by
Cal Stephens
parent
8b998f030d
commit
3e5bdf2cf3
@@ -346,10 +346,22 @@ func gatherOptions(_ options: inout Options, for inputURL: URL, with logger: Log
|
||||
private var configCache = [URL: [[String: String]]]()
|
||||
private let configQueue = DispatchQueue(label: "swiftformat.config", qos: .userInteractive)
|
||||
private func processDirectory(_ inputURL: URL, with options: inout Options, logger: Logger?) throws {
|
||||
if let args = configQueue.sync(execute: { configCache[inputURL] }) {
|
||||
try options.addArguments(args, in: inputURL.path)
|
||||
return
|
||||
let inputURL = inputURL.standardizedFileURL
|
||||
let args = try configQueue.sync { () throws -> [[String: String]] in
|
||||
if let args = configCache[inputURL] {
|
||||
return args
|
||||
}
|
||||
|
||||
let args = try parseConfigArguments(in: inputURL, options: options, logger: logger)
|
||||
configCache[inputURL] = args
|
||||
return args
|
||||
}
|
||||
|
||||
assert(options.formatOptions != nil)
|
||||
try options.addArguments(args, in: inputURL.path)
|
||||
}
|
||||
|
||||
private func parseConfigArguments(in inputURL: URL, options: Options, logger: Logger?) throws -> [[String: String]] {
|
||||
var args = [[String: String]]()
|
||||
let manager = FileManager.default
|
||||
let configFile = inputURL.appendingPathComponent(swiftFormatConfigurationFile)
|
||||
@@ -394,11 +406,7 @@ private func processDirectory(_ inputURL: URL, with options: inout Options, logg
|
||||
}
|
||||
}
|
||||
}
|
||||
configQueue.async {
|
||||
configCache[inputURL] = args
|
||||
}
|
||||
assert(options.formatOptions != nil)
|
||||
try options.addArguments(args, in: inputURL.standardizedFileURL.path)
|
||||
return args
|
||||
}
|
||||
|
||||
/// Line and column offset in source
|
||||
|
||||
@@ -646,6 +646,39 @@ final class CommandLineTests: XCTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
func testConfigFileIsReadOnceWhenGatheringOptionsConcurrently() throws {
|
||||
try withTmpFiles([
|
||||
".swiftformat": """
|
||||
--rules indent
|
||||
""",
|
||||
"File.swift": """
|
||||
let value = 0
|
||||
""",
|
||||
]) { url in
|
||||
guard url.pathExtension == "swift" else { return }
|
||||
|
||||
let configFile = url.deletingLastPathComponent().appendingPathComponent(".swiftformat")
|
||||
var logMessages = [String]()
|
||||
var errors = [Error]()
|
||||
let logQueue = DispatchQueue(label: "swiftformat.test.config-log")
|
||||
|
||||
DispatchQueue.concurrentPerform(iterations: 50) { _ in
|
||||
var options = Options.default
|
||||
do {
|
||||
try gatherOptions(&options, for: url, with: { message in
|
||||
logQueue.sync { logMessages.append(message) }
|
||||
})
|
||||
} catch {
|
||||
logQueue.sync { errors.append(error) }
|
||||
}
|
||||
}
|
||||
|
||||
let messages = logMessages.filter { $0 == "Reading config file at \(configFile.path)" }
|
||||
XCTAssertEqual(messages.count, 1, "\(messages)")
|
||||
XCTAssertTrue(errors.isEmpty, "\(errors)")
|
||||
}
|
||||
}
|
||||
|
||||
func testLintCommandOutputsOrganizeDeclarationOrderingViolations() {
|
||||
var output: [String] = []
|
||||
CLI.print = { message, _ in
|
||||
|
||||
Reference in New Issue
Block a user