All CollectingRules implement AnyCollectingRule, which is used to check
whether a linter will perform any collections and only print the
"Collecting" log message if so.
The post-collection visitor in `Configuration.visit` was iterating over
the total fileCount given, not just the files that could be collected.
This meant than when a file was skipped, the visitor would end up
referencing a linter past the end of its array, causing a crash.
In order for rules to collect arbitrary information about all files
being linted, a shared RuleStorage instance is defined in each command
and passed into the linter.
Linting now requires two "passes": once to call collect and populate the
storage (rules that are non-collecting do nothing here), and again to
call validate. The old Linter factory now creates a Prelinter, which can
collect for a file and produce a Linter that orchestrates all the
traditional validation/collection logic.
This design enforces that a file is only validated once it has been
collected; in turn, the file-visiting loop ensures that all files are
collected before the first is validated, so that the storage is fully
populated.
Use storage-backed correct method
Crash if storage for a rule is accessed prematurely
Key FileInfo by File
Rename Prelinter to Linter and Linter to CollectedLinter
Clean up rule protocols such that rule-facing storage methods are actually called
Make RuleStorage a reference type to solve mutating data races
In projects with multiple Swift targets, it's possible to have multiple
Swift files with the same name. In this case you can't differentiate
from the log, which one SwiftLint is printing about. Now in this case it
prints the relative (to your pwd) path of the file.
Fixes#2447
When SwiftLint is linting files in `visitLintableFiles` in `Configuration+CommandLine`, it:
1. Gathers all lintable files in `getFiles`. This is where the exclusion of files occurs based on the parent configuration's exclusion list.
2. These files are grouped by their specific configuration in `groupFiles`. This is where configurations for each available file are determined (and if nested configurations exist, merged). After these configurations are determined and the files are grouped accordingly, no more files are excluded from the lintable files list. Even though a file's configuration thinks it should be excluded, these files are not removed from the list of lintable files, generating the bug.
3. Finally, each file is visited by the linter.
My solution is to skip files whose merged configurations specify they should be excluded in step 2 or `groupFiles`. Therefore, they will not be visited in step 3.
* Add LintableFilesVisitor
* Move LintCommand logic into LintOrAnalyzeCommand
to prepare for the upcoming analyze command
* Add AnalyzeCommand (not fully implemented yet in SwiftLintFramework)
* Add analyzerRules configuration member
* Add AnalyzerRule protocol
* Pass compiler arguments to validate/correct
* Add requiresFileOnDisk member to RuleDescription
This will be used by AnalyzerRules because they need a file on disk
to pass in the compiler arguments to SourceKit.
* Exclusively run AnalyzerRules when the Linter has compiler arguments
* Enable testing AnalyzerRules in TestHelpers
* Add ExplicitSelfRule
* Update documentation
* Fix `analyze --autocorrect`
* Improve performance of CompilerArgumentsExtractor
* Fix lint command actually running analyze
* Move File operations in TestHelpers into a private extension
* Add analyzer column to rules command and markdown documentation
* Use a Set literal
* Make AnalyzerRule inherit from OptInRule
* Mention analyzer_rules in readme
* Mention that analyzer rules are slow
The MIT license doesn't require that all files be prepended with this
licensing or copyright information. Realm confirmed that they're ok with this
change. This will enable some companies to contribute to SwiftLint and the
date & authorship information will remain accessible via git source control.
also slightly speed up writing to the cache.
For example, on the Lyft codebase with 1,500 Swift files:
```bash
$ time swiftlint lint --quiet
swiftlint --quiet 3.53s user 0.27s system 388% cpu 0.979 total
$ rm -rf ~/Library/Caches/SwiftLint && time swiftlint lint --quiet
swiftlint --quiet 35.20s user 1.22s system 371% cpu 9.806 total
$ time swiftlint lint --quiet
swiftlint lint --quiet 0.90s user 0.13s system 218% cpu 0.472 total
$ rm -rf ~/Library/Caches/SwiftLint && time swiftlint lint --quiet
swiftlint lint --quiet 31.78s user 1.18s system 360% cpu 9.146 total
```
also remove parallelForEach from SwiftLintFramework's public interface
since calling the GCD API is almost as concise and avoids polluting the
public API.
Finally, also remove the "fast" path for parallelMap since GCD should
do the reasonable thing for us here.