Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@

### Enhancements

* None.
* Add a target-aware analyzer execution mode with bounded isolated worker
processes, whole-target collecting rules, opt-in `unused_import` batches of
at most 32 requested files, deterministic reporting, and versioned execution
evidence.
[Derek Pearson](https://github.com/dpearson2699)
[#3020](https://github.com/realm/SwiftLint/issues/3020)

### Bug Fixes

Expand Down
51 changes: 51 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -936,6 +936,57 @@ This can be obtained by

Analyzer rules tend to be considerably slower than lint rules.

Analyzer work can also run in bounded, isolated SwiftLint processes. A build
system integration supplies a versioned target plan with `--target-plan`, sets
the global process limit with `--jobs`, and chooses where SwiftLint writes the
run record with `--execution-evidence`. The target plan describes each target's
complete source inventory, compilation database, configured analyzer rules,
and any requested-source batches.

For example, a plan that divides `unused_import` work into two jobs has this
shape:

```json
{
"schemaIdentity": "swiftlint-analyzer-target-plan",
"schemaVersion": 1,
"compilerLogSha256": "0000000000000000000000000000000000000000000000000000000000000000",
"workingDirectory": "/path/to/package",
"rules": ["unused_import"],
"targets": [{
"targetId": "application",
"moduleName": "FixtureApp",
"sourceRoot": "Sources",
"compileCommandsPath": "application.compile-commands.json",
"compileCommandsSha256": "1111111111111111111111111111111111111111111111111111111111111111",
"sourceFiles": ["Sources/App.swift", "Sources/Model.swift"],
"sourceFilesSha256": "2222222222222222222222222222222222222222222222222222222222222222",
"rulePlans": [{
"rule": "unused_import",
"mode": "batches",
"batches": [
{"batchIndex": 0, "requestedPaths": ["Sources/App.swift"]},
{"batchIndex": 1, "requestedPaths": ["Sources/Model.swift"]}
]
}]
}]
}
```

Replace the example digests with SHA-256 values for the compiler log and
compilation database. `sourceFilesSha256` is the SHA-256 digest of the sorted
`sourceFiles` array encoded as compact JSON with a trailing newline. SwiftLint
resolves `workingDirectory` from the invocation directory, source paths from
`workingDirectory`, and compilation database paths from the target-plan file.
Every target and rule must appear in deterministic order.

Collecting rules always receive their complete target source inventory. Other
rules may use batches only when they explicitly declare that capability;
`unused_import` initially permits batches of at most 32 requested files. Every
worker still receives the complete target compilation database. SwiftLint
cancels the remaining workers after a failure and merges successful results in
target-plan order so reporter output remains deterministic.

## Using Multiple Configuration Files

SwiftLint offers a variety of ways to include multiple configuration files.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import SourceKittenFramework
private let moduleToLog = ProcessInfo.processInfo.environment["SWIFTLINT_LOG_MODULE_USAGE"]

@DisabledWithoutSourceKit
struct UnusedImportRule: CorrectableRule, AnalyzerRule {
struct UnusedImportRule: CorrectableRule, AnalyzerRule, AnalyzerBatchingRule {
var configuration = UnusedImportConfiguration()

let analyzerBatchSize = 32

static let description = RuleDescription(
identifier: "unused_import",
name: "Unused Import",
Expand Down
10 changes: 10 additions & 0 deletions Source/SwiftLintCore/Protocols/Rule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,16 @@ public protocol Rule: Sendable {
func notifyRuleDisabledOnce()
}

/// Opt-in capability for analyzer rules that can safely run against deterministic subsets of a target's sources.
///
/// Absence of this capability means the rule must receive the entire target. The worker coordinator uses this
/// package-only protocol to reject unsafe target-plan batches.
package protocol AnalyzerBatchingRule: Rule {
/// The largest requested-source batch that preserves the rule's analyzer semantics when the worker also receives
/// the complete target compilation database.
var analyzerBatchSize: Int { get }
}

public extension Rule {
var shouldLintEmptyFiles: Bool {
false
Expand Down
Loading