diff --git a/Sources/SwiftFormat/Utilities/FileIterator.swift b/Sources/SwiftFormat/Utilities/FileIterator.swift index bd3a2c0f0..9d1a1d2cf 100644 --- a/Sources/SwiftFormat/Utilities/FileIterator.swift +++ b/Sources/SwiftFormat/Utilities/FileIterator.swift @@ -79,7 +79,7 @@ public struct FileIterator: Sequence, IteratorProtocol { else { break } - next = URL(fileURLWithPath: destination) + next = URL(fileURLWithPath: destination, relativeTo: next) fallthrough case .typeDirectory: @@ -97,12 +97,12 @@ public struct FileIterator: Sequence, IteratorProtocol { output = next } } - if let out = output, visited.contains(out.absoluteURL.standardized.path) { + if let out = output, visited.contains(out.standardizedFileURL.path) { output = nil } } if let out = output { - visited.insert(out.absoluteURL.standardized.path) + visited.insert(out.standardizedFileURL.path) } return output } @@ -135,7 +135,7 @@ public struct FileIterator: Sequence, IteratorProtocol { else { break } - path = destination + path = URL(fileURLWithPath: destination, relativeTo: item).path fallthrough case .typeRegular: diff --git a/Tests/SwiftFormatTests/Utilities/FileIteratorTests.swift b/Tests/SwiftFormatTests/Utilities/FileIteratorTests.swift index 965c90fe3..691e568c6 100644 --- a/Tests/SwiftFormatTests/Utilities/FileIteratorTests.swift +++ b/Tests/SwiftFormatTests/Utilities/FileIteratorTests.swift @@ -18,6 +18,7 @@ final class FileIteratorTests: XCTestCase { try touch("project/.hidden.swift") try touch("project/.build/generated.swift") try symlink("project/link.swift", to: "project/.hidden.swift") + try symlink("project/rellink.swift", relativeTo: ".hidden.swift") } override func tearDownWithError() throws { @@ -55,7 +56,10 @@ final class FileIteratorTests: XCTestCase { // passed to the iterator. This is meant to avoid situations where a symlink could be hidden by // shell expansion; for example, if the user writes `swift-format --no-follow-symlinks *`, if // the current directory contains a symlink, they would probably *not* expect it to be followed. - let seen = allFilesSeen(iteratingOver: [tmpURL("project/link.swift")], followSymlinks: false) + let seen = allFilesSeen( + iteratingOver: [tmpURL("project/link.swift"), tmpURL("project/rellink.swift")], + followSymlinks: false + ) XCTAssertTrue(seen.isEmpty) } } @@ -81,7 +85,7 @@ extension FileIteratorTests { } } - /// Create a symlink between files or directories in the test's temporary space. + /// Create a absolute symlink between files or directories in the test's temporary space. private func symlink(_ source: String, to target: String) throws { try FileManager.default.createSymbolicLink( at: tmpURL(source), @@ -89,6 +93,14 @@ extension FileIteratorTests { ) } + /// Create a relative symlink between files or directories in the test's temporary space. + private func symlink(_ source: String, relativeTo target: String) throws { + try FileManager.default.createSymbolicLink( + atPath: tmpURL(source).path, + withDestinationPath: target + ) + } + /// Computes the list of all files seen by using `FileIterator` to iterate over the given URLs. private func allFilesSeen(iteratingOver urls: [URL], followSymlinks: Bool) -> [String] { let iterator = FileIterator(urls: urls, followSymlinks: followSymlinks)