SwiftLint icon indicating copy to clipboard operation
SwiftLint copied to clipboard

lint command paths argument is unexpectedly case sensitive when applying exclude rules

Open alexbbrown opened this issue 3 years ago • 2 comments

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$ 

alexbbrown avatar Oct 18 '22 22:10 alexbbrown

0.49.1

alexbbrown avatar Oct 18 '22 22:10 alexbbrown

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.

alexbbrown avatar Oct 22 '22 01:10 alexbbrown