To support this, we first must use an async entrypoint to the CLI, which
we do by changing the lint and analyze commands to conform to
`AsyncParsableCommand`.
The in order to map over the closures with suspension points, we pull in
CollectionConcurrencyKit as a new dependency.
This change does not touch SwiftLintFramework, only the CLI target.
* Integrate OS Signposts to help profile SwiftLint performance
* Split LintOrAnalyzeCommand.lintOrAnalyze(_:) into two functions
Using a builder reference type to share common mutable state.
We currently try to derive the root path by using the path passed in. However, this doesn't allow us to actually get the root, just the containing directory of the path passed in. We also don't currently have a heuristic to use for deriving the root path for multiple paths passed in.
The fix (which fixes#3383) is to remove our root path calculation and just use the current working directory as the root instead.
This was previously attempted in #3342, but produced a bug in the case where `--config` is used to specify a config from outside of the source tree. The `--config` argument wasn't always being used as an override, and was being merged with the config in the source tree. This has now been addressed and reverts the revert done in #3362.
Fixes#3341
As we discussed here https://github.com/realm/SwiftLint/pull/3325 sometimes current excluding algorithm maybe slower than excluding paths by absolute prefix. So I added option for such cases.
Based on what I've checked it works faster for next scenarios:
- the number of input files is relatively small (e.g. when using `use-script-input-files`) and excluded directories contain relatively big number of lintable files
- the number of excluded directories relatively small (e.g. Pods + ThirdParty) and globs not used
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.