From 3ed80b0acc638cd427a2d517cfdee51181262b03 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 21 Nov 2025 14:40:08 -0800 Subject: [PATCH] correct incorrect handling of file paths `URL.path` is not a usable rendering of the path. `URL.path` represents the URI path. If the path is meant to be consumable as a file path, the file system representation (aka FSR) must be retrieved. This improves the file path handling and makes additional tests now pass. --- .../Extensions/String+SwiftLint.swift | 2 +- .../Extensions/URL+SwiftLint.swift | 7 +++++++ .../Extensions/FileManager+SwiftLint.swift | 21 ++++++++++--------- Source/swiftlint-dev/Rules+Template.swift | 16 +++++++------- .../FileSystemAccessTests/BaselineTests.swift | 2 +- Tests/TestHelpers/TestHelpers.swift | 2 +- 6 files changed, 29 insertions(+), 21 deletions(-) create mode 100644 Source/SwiftLintCore/Extensions/URL+SwiftLint.swift diff --git a/Source/SwiftLintCore/Extensions/String+SwiftLint.swift b/Source/SwiftLintCore/Extensions/String+SwiftLint.swift index 2d6a431a43..8ece46771e 100644 --- a/Source/SwiftLintCore/Extensions/String+SwiftLint.swift +++ b/Source/SwiftLintCore/Extensions/String+SwiftLint.swift @@ -70,7 +70,7 @@ public extension String { /// /// - returns: A new `String`. func absolutePathStandardized() -> String { - bridge().absolutePathRepresentation().bridge().standardizingPath + URL(fileURLWithPath: bridge().standardizingPath.absolutePathRepresentation()).filepath } var isFile: Bool { diff --git a/Source/SwiftLintCore/Extensions/URL+SwiftLint.swift b/Source/SwiftLintCore/Extensions/URL+SwiftLint.swift new file mode 100644 index 0000000000..7e205e7ff0 --- /dev/null +++ b/Source/SwiftLintCore/Extensions/URL+SwiftLint.swift @@ -0,0 +1,7 @@ +import Foundation + +public extension URL { + var filepath: String { + self.withUnsafeFileSystemRepresentation { String(cString: $0!) } + } +} diff --git a/Source/SwiftLintFramework/Extensions/FileManager+SwiftLint.swift b/Source/SwiftLintFramework/Extensions/FileManager+SwiftLint.swift index 31280a08ed..bc6ac1748b 100644 --- a/Source/SwiftLintFramework/Extensions/FileManager+SwiftLint.swift +++ b/Source/SwiftLintFramework/Extensions/FileManager+SwiftLint.swift @@ -31,19 +31,20 @@ public protocol LintableFileManager { extension FileManager: LintableFileManager { public func filesToLint(inPath path: String, rootDirectory: String? = nil) -> [String] { - let absolutePath = path.bridge() - .absolutePathRepresentation(rootDirectory: rootDirectory ?? currentDirectoryPath).bridge() - .standardizingPath - + let root = + URL(fileURLWithPath: path.absolutePathRepresentation(rootDirectory: rootDirectory ?? currentDirectoryPath)) // if path is a file, it won't be returned in `enumerator(atPath:)` - if absolutePath.bridge().isSwiftFile(), absolutePath.isFile { - return [absolutePath] + if FileManager.default.isFile(atPath: root.path), root.pathExtension == "swift" { + return [root.standardized.filepath] } - return subpaths(atPath: absolutePath)?.parallelCompactMap { element -> String? in - guard element.bridge().isSwiftFile() else { return nil } - let absoluteElementPath = absolutePath.bridge().appendingPathComponent(element) - return absoluteElementPath.isFile ? absoluteElementPath : nil + return subpaths(atPath: root.path)?.parallelCompactMap { element -> String? in + let elementURL = URL(fileURLWithPath: element, relativeTo: root) + guard elementURL.pathExtension == "swift" else { return nil } + if FileManager.default.isFile(atPath: elementURL.path) { + return elementURL.standardized.filepath + } + return nil } ?? [] } diff --git a/Source/swiftlint-dev/Rules+Template.swift b/Source/swiftlint-dev/Rules+Template.swift index 229534ee47..91e776324c 100644 --- a/Source/swiftlint-dev/Rules+Template.swift +++ b/Source/swiftlint-dev/Rules+Template.swift @@ -49,26 +49,26 @@ extension SwiftLintDev.Rules { .appendingPathComponent("SwiftLintBuiltInRules", isDirectory: true) .appendingPathComponent("Rules", isDirectory: true) let ruleLocation = ruleDirectory.appendingPathComponent(kind.rawValue.capitalized, isDirectory: true) - guard FileManager.default.fileExists(atPath: ruleLocation.path) else { + guard FileManager.default.fileExists(atPath: ruleLocation.filepath) else { throw ValidationError("Command must be run from the root of the SwiftLint repository.") } print("Creating template(s) for new rule \"\(ruleName)\" identified by '\(ruleId)' ...") let rulePath = ruleLocation.appendingPathComponent("\(name)Rule.swift", isDirectory: false) - guard overwrite || !FileManager.default.fileExists(atPath: rulePath.path) else { + guard overwrite || !FileManager.default.fileExists(atPath: rulePath.filepath) else { throw ValidationError("Rule file already exists at \(rulePath.relativeToCurrentDirectory).") } - try ruleTemplate.write(toFile: rulePath.path, atomically: true, encoding: .utf8) + try ruleTemplate.write(toFile: rulePath.filepath, atomically: true, encoding: .utf8) print("Rule file created at \(rulePath.relativeToCurrentDirectory).") if config { let configPath = ruleDirectory .appendingPathComponent("RuleConfigurations", isDirectory: true) .appendingPathComponent("\(name)Configuration.swift", isDirectory: false) - guard overwrite || !FileManager.default.fileExists(atPath: configPath.path) else { + guard overwrite || !FileManager.default.fileExists(atPath: configPath.filepath) else { throw ValidationError( "Configuration file already exists at \(configPath.relativeToCurrentDirectory)." ) } - try configTemplate.write(toFile: configPath.path, atomically: true, encoding: .utf8) + try configTemplate.write(toFile: configPath.filepath, atomically: true, encoding: .utf8) print("Configuration file created at \(configPath.relativeToCurrentDirectory).") } if test { @@ -76,13 +76,13 @@ extension SwiftLintDev.Rules { .appendingPathComponent("Tests", isDirectory: true) .appendingPathComponent("BuiltInRulesTests", isDirectory: true) let testPath = testDirectory.appendingPathComponent("\(name)RuleTests.swift", isDirectory: false) - guard FileManager.default.fileExists(atPath: testDirectory.path) else { + guard FileManager.default.fileExists(atPath: testDirectory.filepath) else { throw ValidationError("Command must be run from the root of the SwiftLint repository.") } - guard overwrite || !FileManager.default.fileExists(atPath: testPath.path) else { + guard overwrite || !FileManager.default.fileExists(atPath: testPath.filepath) else { throw ValidationError("Test file already exists at \(testPath.relativeToCurrentDirectory).") } - try testTemplate.write(toFile: testPath.path, atomically: true, encoding: .utf8) + try testTemplate.write(toFile: testPath.filepath, atomically: true, encoding: .utf8) print("Test file created at \(testPath.relativeToCurrentDirectory).") } if !skipRegistration { diff --git a/Tests/FileSystemAccessTests/BaselineTests.swift b/Tests/FileSystemAccessTests/BaselineTests.swift index 9890287ecd..4f75bf7d88 100644 --- a/Tests/FileSystemAccessTests/BaselineTests.swift +++ b/Tests/FileSystemAccessTests/BaselineTests.swift @@ -8,7 +8,7 @@ private var temporaryDirectoryPath: String { let result = URL( fileURLWithPath: NSTemporaryDirectory(), isDirectory: true - ).path + ).filepath #if os(macOS) return "/private" + result diff --git a/Tests/TestHelpers/TestHelpers.swift b/Tests/TestHelpers/TestHelpers.swift index 07d6e107fb..cd98c312fc 100644 --- a/Tests/TestHelpers/TestHelpers.swift +++ b/Tests/TestHelpers/TestHelpers.swift @@ -39,7 +39,7 @@ private extension SwiftLintFile { public extension String { func stringByAppendingPathComponent(_ pathComponent: String) -> String { - bridge().appendingPathComponent(pathComponent) + URL(fileURLWithPath: self).appendingPathComponent(pathComponent).filepath } }