lint command paths argument is unexpectedly case sensitive when applying exclude rules
swiftlint lint [
If my code is held in /home/a/b/c and I pass in the argument /home/a/b/C then SwiftLint scans the directory /home/a/b/c/.
That's cool, and expected. it's a Mac, it's not case sensitive.
BUT. If my config.yml file contains an exclusion rule:
excluded: # paths to ignore during linting.
- build
😀 then the exclusion works when the invocation looks like this:
swiftlint lint --config config.yml /home/a/b/C
😡 but the exclusion is not honored in this case:
swiftlint lint --config config.yml /home/a/b/C
and then my /home/a/b/c/build directory gets scanned, with horrible errors as a result.
Reproduce:
Death-Star:myCode apple$ pwd
/tmp/myCode
Death-Star:myCode apple$ find . -print -exec cat '{}' ';'
.
cat: .: Is a directory
./lint.yml
excluded: # paths to ignore during linting.
- badCode
./1.swift
if a = 3 {
print("hi")
}
./badCode
cat: ./badCode: Is a directory
./badCode/bad.swift
if a = 3 {
print("hi")
}
Death-Star:myCode apple$ swiftlint --config lint.yml /tmp/myCode/
Linting Swift files at paths /tmp/myCode/
Linting '1.swift' (1/1)
Done linting! Found 0 violations, 0 serious in 1 file.
Death-Star:myCode apple$ swiftlint --config lint.yml /tmp/MYCODE/
Linting Swift files at paths /tmp/MYCODE/
Linting '1.swift' (1/2)
Linting 'bad.swift' (2/2)
Done linting! Found 0 violations, 0 serious in 2 files.
Death-Star:myCode apple$
0.49.1
This issue is caused by the fact that the lintable results and excluded paths are computed slightly different ways which makes otherwise equal paths not match the case-sensitive comparison used in the linter:
the lintable paths are computed from the supplied path (rootDirectory)
the excluded paths are computed relative to the CWD
When the set of excluded paths are subtracted from the lintable paths in the function filterExcludedPaths, you get something like this:
result.minusSet(Set(excludedPaths))
but result contains "/tmp/MYCODE/badCode/bad.swift" and excludedPaths contains "/tmp/MyCode/badCode/bad.swift", so the path doesn't match and isn't excluded.
But why are these different?
The function absolutePathRepresentation(rootDirectory treats relative and absolute paths differently:
public func absolutePathRepresentation(rootDirectory: String = FileManager.default.currentDirectoryPath) -> String {
if isAbsolutePath { return bridge() }
#if os(Linux)
return NSURL(fileURLWithPath: NSURL.fileURL(withPathComponents: [rootDirectory, bridge()])!.path).standardizingPath!.path
#else
return NSString.path(withComponents: [rootDirectory, bridge()]).bridge().standardizingPath
#endif
}
for example, the following two scenarios:
mkdir -p /tmp/MyCode/badCode
touch /tmp/MyCode/badCode.swift
mkdir -p /tmp/Something/Other
let's find the absolute representation of an absolute directory:
(lldb) p "/tmp/MYCODE/BADCODE".bridge().absolutePathRepresentation(rootDirectory: "/tmp/Something/Other")
(String) $R95 = "/tmp/MYCODE/BADCODE"
but when asking the same thing of a relative directory:
p "../../MYCODE/BADCODE".bridge().absolutePathRepresentation(rootDirectory: "/tmp/Something/Other")
(String) $R94 = "/tmp/MyCode/badCode"
It turns out that even removing the check isAbsolutePath from the above function doesn't help.