From 5a1348bd9d08227b2af8a94e95bf2ebb1ca1817e Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Thu, 10 Oct 2024 16:02:42 -0700 Subject: [PATCH] Fix infinite looping if swift-format is run in a directory that doesn't contain a `.swift-format` file https://github.com/swiftlang/swift-format/pull/802 this sort of infinite looping issue on Windows but introduced it on Unix platforms where `URL.deletingLastPathComponent` on `/` returns `/..`. --- Sources/SwiftFormat/API/Configuration.swift | 16 +++++++++++- .../API/ConfigurationTests.swift | 26 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/Sources/SwiftFormat/API/Configuration.swift b/Sources/SwiftFormat/API/Configuration.swift index ab8a3e952..1f84feb6f 100644 --- a/Sources/SwiftFormat/API/Configuration.swift +++ b/Sources/SwiftFormat/API/Configuration.swift @@ -328,7 +328,7 @@ public struct Configuration: Codable, Equatable { if FileManager.default.isReadableFile(atPath: candidateFile.path) { return candidateFile } - } while candidateDirectory.path != "/" + } while !candidateDirectory.isRoot return nil } @@ -370,3 +370,17 @@ public struct NoAssignmentInExpressionsConfiguration: Codable, Equatable { public init() {} } + +fileprivate extension URL { + var isRoot: Bool { + #if os(Windows) + // FIXME: We should call into Windows' native check to check if this path is a root once https://github.com/swiftlang/swift-foundation/issues/976 is fixed. + // https://github.com/swiftlang/swift-format/issues/844 + return self.pathComponents.count == 1 + #else + // On Linux, we may end up with an string for the path due to https://github.com/swiftlang/swift-foundation/issues/980 + // TODO: Remove the check for "" once https://github.com/swiftlang/swift-foundation/issues/980 is fixed. + return self.path == "/" || self.path == "" + #endif + } +} diff --git a/Tests/SwiftFormatTests/API/ConfigurationTests.swift b/Tests/SwiftFormatTests/API/ConfigurationTests.swift index 6834e9f55..e96331db9 100644 --- a/Tests/SwiftFormatTests/API/ConfigurationTests.swift +++ b/Tests/SwiftFormatTests/API/ConfigurationTests.swift @@ -17,4 +17,30 @@ final class ConfigurationTests: XCTestCase { XCTAssertEqual(defaultInitConfig, emptyJSONConfig) } + + func testMissingConfigurationFile() { + #if os(Windows) + let path = #"C:\test.swift"# + #else + let path = "/test.swift" + #endif + XCTAssertNil(Configuration.url(forConfigurationFileApplyingTo: URL(fileURLWithPath: path))) + } + + func testMissingConfigurationFileInSubdirectory() { + #if os(Windows) + let path = #"C:\whatever\test.swift"# + #else + let path = "/whatever/test.swift" + #endif + XCTAssertNil(Configuration.url(forConfigurationFileApplyingTo: URL(fileURLWithPath: path))) + } + + func testMissingConfigurationFileMountedDirectory() throws { + #if !os(Windows) + try XCTSkipIf(true, #"\\ file mounts are only a concept on Windows"#) + #endif + let path = #"\\mount\test.swift"# + XCTAssertNil(Configuration.url(forConfigurationFileApplyingTo: URL(fileURLWithPath: path))) + } }