Skip to content

Commit 16beed8

Browse files
committed
sanitize rules: remove 24 overlapping/obsolete rules, split performance_anti_patterns into 5 focused rules, extract prefer_weak_let and async_stream_safety (wvc-7oa)
1 parent 4904625 commit 16beed8

136 files changed

Lines changed: 5389 additions & 10211 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
# 04e-hge
3+
title: '`redundant_sendable`: detect redundant conformance in public extension context'
4+
status: completed
5+
type: bug
6+
priority: normal
7+
created_at: 2026-04-11T17:53:01Z
8+
updated_at: 2026-04-11T18:19:01Z
9+
sync:
10+
github:
11+
issue_number: "177"
12+
synced_at: "2026-04-11T18:44:01Z"
13+
---
14+
15+
The `redundant_sendable` rule currently only checks types decorated with `@MainActor` or configured global actors. It does not detect redundant `Sendable` conformance on types defined inside public extensions that inherit isolation.
16+
17+
Upstream reference: nicklockwood/SwiftFormat 0.60.1 fixed `redundantSendable` incorrectly removing `Sendable` conformance on types in public extensions.
18+
19+
20+
## Summary of Changes
21+
22+
Updated `isIsolatedToActor()` in `RedundantSendableRule` to walk ancestor declarations. Types inside a `@MainActor` extension or nested inside a `@MainActor` parent type now correctly inherit that isolation, making their explicit `Sendable` conformance flaggable as redundant.
23+
24+
**Changed file:** `Sources/SwiftiomaticKit/Rules/Redundancy/Visibility/RedundantSendableRule.swift`
25+
26+
- Extracted attribute check into `AttributeListSyntax.isActorIsolated(actors:)` helper
27+
- `isIsolatedToActor()` now walks the parent syntax chain checking all `DeclGroupSyntax` ancestors (extensions, structs, classes, enums) for global actor attributes
28+
- Added non-triggering examples: types in plain extensions and public extensions (no isolation)
29+
- Added triggering examples: type in `@MainActor extension`, nested type in `@MainActor struct`
30+
- Added correction pairs for both new triggering cases
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
# 15g-v3q
3+
title: Clean up test infrastructure from Swift review
4+
status: completed
5+
type: task
6+
priority: normal
7+
created_at: 2026-04-11T17:50:48Z
8+
updated_at: 2026-04-11T17:55:05Z
9+
sync:
10+
github:
11+
issue_number: "179"
12+
synced_at: "2026-04-11T18:44:01Z"
13+
---
14+
15+
Fix all 6 findings from /swift review of Tests/SwiftiomaticTests/:
16+
17+
- [x] 1. Delete dead `String+StaticString.swift` (unsafe, unused)
18+
- [x] 2. Replace `checkError` with `#expect(throws:)` (22 call sites)
19+
- [x] 3. Remove dead XCTest `file:/line:` params from `LinterCacheTests`
20+
- [x] 4. Delete dead `lineDiff` function from `RuleTestHelpers`
21+
- [x] 5. Consolidate 3 duplicate rule-registration lazy vars
22+
- [x] 6. Skipped: `macOSSDKPath()` is sync; converting to `Subprocess` (async) would cascade through the test infra
23+
24+
25+
## Summary of Changes
26+
27+
All actionable findings fixed. Item 6 skipped — `macOSSDKPath()` is called synchronously and converting to async `Subprocess` would cascade through the entire test infrastructure for minimal benefit.
28+
29+
Build: passed. Tests: 463 passed, 0 failed.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
# 345-ocg
3+
title: Config file selected via file importer not loaded into app
4+
status: completed
5+
type: bug
6+
priority: normal
7+
created_at: 2026-04-11T17:41:06Z
8+
updated_at: 2026-04-11T17:54:52Z
9+
sync:
10+
github:
11+
issue_number: "184"
12+
synced_at: "2026-04-11T18:44:01Z"
13+
---
14+
15+
## Description
16+
17+
The file importer dialog opens and allows selecting a .swiftiomatic.yaml config file, but after selection the app still shows "No configuration file selected" — the chosen file is not actually loaded/applied.
18+
19+
## Steps to Reproduce
20+
21+
1. Open the Swiftiomatic app
22+
2. Go to Configuration File section
23+
3. Click "Choose..."
24+
4. Select a .swiftiomatic.yaml file
25+
5. Observe: Path still shows "No configuration file selected"
26+
27+
## TODO
28+
29+
- [x] Trace the file importer flow in the SwiftUI app
30+
- [x] Identify where the selected URL is (or isn't) being stored/applied
31+
- [x] Create a failing test if possible — not practical (sandboxed app entitlement + file importer UI)
32+
- [x] Fix the issue
33+
- [x] Verify the fix
34+
35+
## Summary of Changes
36+
37+
Removed security-scoped bookmark approach entirely. Config is now read once from the selected file, then persisted as a YAML string in App Group UserDefaults (matching SwiftFormat's pattern). Added `toYAMLString()` and `fromYAMLString()` to `Configuration` for serialization. Removed `com.apple.security.files.bookmarks.app-scope` entitlement.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
# 9qw-72x
3+
title: 'Improve rule summaries: fill empty static let summary fields'
4+
status: ready
5+
type: task
6+
priority: normal
7+
created_at: 2026-04-11T18:22:20Z
8+
updated_at: 2026-04-11T18:22:20Z
9+
sync:
10+
github:
11+
issue_number: "181"
12+
synced_at: "2026-04-11T18:44:01Z"
13+
---
14+
15+
6+ rules have `static let summary = ""`. Fill them in with concise, useful descriptions.
16+
17+
Identified rules with empty summaries:
18+
- `static_over_final_class`
19+
- `non_overridable_class_declaration`
20+
- `opening_brace`
21+
- `contrasted_opening_brace`
22+
- `number_separator`
23+
- `unhandled_throwing_task`
24+
- (scan for others)
25+
26+
## Tasks
27+
28+
- [ ] Find all rules with empty summary fields
29+
- [ ] Write concise summaries for each
30+
- [ ] Verify build passes
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
# j8s-d2r
3+
title: Modernize macOSSDKPath() from Process to Subprocess
4+
status: completed
5+
type: task
6+
priority: low
7+
created_at: 2026-04-11T17:56:25Z
8+
updated_at: 2026-04-11T18:18:15Z
9+
sync:
10+
github:
11+
issue_number: "186"
12+
synced_at: "2026-04-11T18:44:01Z"
13+
---
14+
15+
In `Tests/SwiftiomaticTests/Support/LintTestHelpers.swift:56-67`, `macOSSDKPath()` uses the legacy `Process` + `Pipe` + `waitUntilExit()` pattern to get the macOS SDK path via `xcrun --show-sdk-path`.
16+
17+
Modernizing to `Subprocess` (swift-subprocess) requires making the function `async`, which cascades through:
18+
- `makeCompilerArguments()` (called on `SwiftSource`)
19+
- All callers in `violations()`, `assertCorrection()`, `testCorrection()`, etc.
20+
21+
### Resolution
22+
23+
Option 1+2 hybrid: made `macOSSDKPath()` async with `Subprocess.run()` and cached the result with `Mutex<String?>`. All callers were already async so cascade was minimal (just `makeCompilerArguments()`).
24+
25+
Moved SDK path resolution to its own file (`SDKPath.swift`) to avoid `Subprocess.Configuration` / `SwiftiomaticKit.Configuration` name clash (separate issue `xsm-oy1` filed for the underlying enum-shadows-module problem).
26+
27+
### Original Options
28+
1. **Make the chain async** — most correct, but touches many test helpers
29+
2. **Cache the SDK path in a lazy var** — call `Subprocess` once at test setup, store the result; `macOSSDKPath()` becomes a simple property read. Minimal cascade.
30+
3. **Leave as-is**`Process` works fine for a one-shot test helper
31+
32+
Option 2 is likely the best tradeoff.
33+
34+
35+
## Summary of Changes
36+
37+
- **New file:** `Tests/SwiftiomaticTests/Support/SDKPath.swift` — async `macOSSDKPath()` using `Subprocess.run()` with `Mutex` cache
38+
- **Modified:** `Tests/SwiftiomaticTests/Support/LintTestHelpers.swift` — removed old `Process`-based `macOSSDKPath()`, made `makeCompilerArguments()` async, added `await` at 4 call sites
39+
- **Modified:** `Package.swift` — added `swift-subprocess` 0.4.x dependency (test target only)
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
---
2+
# kl9-aud
3+
title: Add `dump-config` CLI subcommand to show resolved configuration
4+
status: completed
5+
type: feature
6+
priority: normal
7+
created_at: 2026-04-11T17:53:01Z
8+
updated_at: 2026-04-11T18:00:36Z
9+
sync:
10+
github:
11+
issue_number: "178"
12+
synced_at: "2026-04-11T18:44:01Z"
13+
---
14+
15+
Add a CLI subcommand that dumps the effective/resolved configuration for a given path, similar to swift-format's `dump-effective-configuration`.
16+
17+
This would show the merged result of nested `.swiftiomatic.yaml` files, including which rules are enabled, their severity, and any per-rule options.
18+
19+
Useful for debugging why a rule fires or doesn't fire in a specific directory.
20+
21+
Upstream reference: swiftlang/swift-format `dump-effective-configuration` subcommand (602.0.0)
22+
23+
24+
## Summary of Changes
25+
26+
- **`dump-config` CLI subcommand**: New subcommand that resolves and displays the effective configuration for any given file or directory path, after merging nested `.swiftiomatic.yaml` files.
27+
- `--format text|json|yaml` output modes
28+
- `--show-chain` flag to display which config files were merged (leaf → root)
29+
- `--config` option for explicit config path (bypasses chain resolution)
30+
- **`Configuration.toFullDictionary()`** and **`toFullYAMLString()`**: New public methods that serialize ALL configuration values (including defaults), unlike `toYAMLString()` which only writes non-default values.
31+
- **`ConfigurationResolver.configChain(for:)`**: New public method exposing the config file chain for a given path.
32+
- **7 new tests** in `ConfigurationResolverTests`: cover `configChain`, `toFullDictionary`, and `toFullYAMLString`.
33+
34+
### Files changed
35+
- `Sources/SwiftiomaticCLI/SwiftiomaticCLI.swift` — added `DumpConfig` command + `DumpConfigFormat` enum
36+
- `Sources/SwiftiomaticKit/Configuration/Configuration.swift` — added `toFullDictionary()`, `toFullYAMLString()`
37+
- `Sources/SwiftiomaticKit/Configuration/ConfigurationResolver.swift` — added public `configChain(for:)`
38+
- `Tests/SwiftiomaticTests/Configuration/ConfigurationResolverTests.swift` — 7 new tests
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
# lv4-z40
3+
title: '`SortImportsRule`: group `@_implementationOnly` imports separately'
4+
status: ready
5+
type: feature
6+
priority: normal
7+
created_at: 2026-04-11T17:53:01Z
8+
updated_at: 2026-04-11T17:53:01Z
9+
sync:
10+
github:
11+
issue_number: "182"
12+
synced_at: "2026-04-11T18:44:01Z"
13+
---
14+
15+
The `SortImportsRule` currently treats all imports as a single alphabetically-sorted group. It should support grouping imports by attribute, placing `@_implementationOnly` imports in a separate group (typically after regular imports).
16+
17+
Upstream reference: swiftlang/swift-format 602.0.0 added separate grouping for `@_implementationOnly` imports in `OrderedImports`.
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
---
2+
# nk3-b8j
3+
title: Review upstream SwiftLint, SwiftFormat, and swift-format releases for porting opportunities
4+
status: completed
5+
type: task
6+
priority: normal
7+
created_at: 2026-04-11T17:37:51Z
8+
updated_at: 2026-04-11T18:10:04Z
9+
sync:
10+
github:
11+
issue_number: "185"
12+
synced_at: "2026-04-11T18:44:01Z"
13+
---
14+
15+
Review the latest releases of the three cited linting/formatting tools for features, rules, and fixes worth porting to Swiftiomatic.
16+
17+
## nicklockwood/SwiftFormat — `0.60.1` (Mar 7, 2026)
18+
19+
- [x] Fixed `redundantSendable` removing `Sendable` conformance on types in public extensions — **we have `redundant_sendable` but it only checks `@MainActor` types, not public extension context; gap noted**
20+
- [x] Fixed `redundantSendable` leaving extra space when removing `:` — N/A, our rule is lint-only (not correctable)
21+
- [x] **`redundant_property` fixed** — now skips variables with explicit type annotations (`binding.typeAnnotation == nil` guard added); test added
22+
- [x] Updated `enumNamespaces` to preserve Swift Testing suites as structs — **already handled**: our `enum_namespaces` skips all types with attributes (`guard attributes.isEmpty`), so `@Suite` structs are safe
23+
24+
## realm/SwiftLint — `0.63.2` (Jan 26, 2026)
25+
26+
### New rules to consider porting
27+
28+
- [x] `unneeded_throws_rethrows`**already implemented** as `unneeded_throws_rethrows` rule
29+
- [x] `unneeded_escaping`**already implemented** with taint analysis for escape tracking
30+
- [x] `multiline_call_arguments`**already implemented** with `max_number_of_single_line_parameters` and `allows_single_line` options
31+
32+
### Notable changes
33+
34+
- [x] `redundant_self`**already implemented** with `only_in_closures` (default true) and `keep_in_initializers` options
35+
- [x] `vertical_whitespace_between_cases`**already has** `separation` config (`always`/`never`)
36+
- [x] `line_length`**already correct**: `FunctionLineVisitor` collects from signature start to `signature.endPositionBeforeTrailingTrivia`, excluding bodies
37+
- [x] `large_tuple`**already has** `ignore_regex` option for `Regex<(...)>` generic arguments
38+
- [x] `--disable-sourcekit` — N/A, our design is inverted: SourceKit is **opt-in** via `--sourcekit` flag (disabled by default)
39+
40+
## swiftlang/swift-format — `602.0.0` (Sep 16, 2025)
41+
42+
### Notable changes
43+
44+
- [ ] `dump-effective-configuration` subcommand — **we don't have this**; CLI has `analyze`, `format`, `list-rules`, `generate-docs`, `migrate` but no config introspection
45+
- [x] `UseLetInEveryBoundCaseVariable`**no equivalent rule**; low priority, niche pattern
46+
- [ ] **File-level ignore directive** — our `sm:disable` supports `:previous`/`:this`/`:next` scopes only; bare `sm:disable RULE` disables until `sm:enable`; no explicit file-level scope like `sm:disable:file`
47+
- [x] **Linter warnings by default****already matches**: our `SeverityOption` defaults to `.warning`
48+
- [x] Severity in configuration — **we kept severity** (per-rule configurable); noted divergence from swift-format's removal
49+
- [ ] `@_implementationOnly` import grouping — **our `SortImportsRule` treats all imports as one alphabetical group**; no attribute-based grouping
50+
- [x] Comments in config files — **already supported**: YAML natively supports comments
51+
- [x] `unsafe` expression support — **handled**: `Swift62ModernizationRule` detects unsafe buffer pointers and suggests `Span`/`RawSpan` migration
52+
- [x] `InlineArray` type sugar — **handled**: `Swift62ModernizationRule` suggests `InlineArray<N, T>` for homogeneous tuples (SE-0453)
53+
- [x] Unified whitespace handling — **already our design**: single rule set with `.lint`/`.format` scopes
54+
55+
56+
## Summary
57+
58+
### Already implemented (16/22)
59+
All SwiftLint 0.63.x rules and config options already exist. swift-format design choices (warnings default, unified whitespace, YAML comments) already match.
60+
61+
### Fixed (1/22)
62+
- `redundant_property` now preserves variables with explicit type annotations
63+
64+
### Future work (5/22)
65+
- `dump-effective-configuration` CLI subcommand
66+
- File-level `sm:disable:file` scope
67+
- `@_implementationOnly` import grouping in `SortImportsRule`
68+
- `redundant_sendable` public extension context awareness
69+
- `UseLetInEveryBoundCaseVariable` (low priority)
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
# rb0-c9s
3+
title: Support file-level `sm:disable:file` scope
4+
status: completed
5+
type: feature
6+
priority: normal
7+
created_at: 2026-04-11T17:53:01Z
8+
updated_at: 2026-04-11T18:16:35Z
9+
sync:
10+
github:
11+
issue_number: "180"
12+
synced_at: "2026-04-11T18:44:01Z"
13+
---
14+
15+
Add a file-level scope to the `sm:disable` directive system.
16+
17+
Currently supported scopes: `:previous`, `:this`, `:next`, and bare (disable until `sm:enable`).
18+
19+
A file-level scope like `// sm:disable:file rule_id` at the top of a file would disable a rule for the entire file without needing a matching `sm:enable`.
20+
21+
Upstream reference: swiftlang/swift-format 602.0.0 added file-level ignore directives for specific rules.
22+
23+
## Plan
24+
25+
- [x] Add `case file` to `Command.Modifier` enum
26+
- [x] Handle `.file` in `expand()` — single disable from line 0 to EOF
27+
- [x] Add parsing tests for `sm:disable:file` / `sm:enable:file`
28+
- [x] Add expansion tests
29+
- [x] Add region tests
30+
- [x] Add integration tests (violation suppression)
31+
- [x] Add superfluous disable command tests
32+
33+
34+
## Summary of Changes
35+
36+
Added `sm:disable:file` / `sm:enable:file` directive scope. The `:file` modifier expands to a single command at line 0, creating a region that covers the entire file regardless of where the directive appears in the source. No parser changes were needed — the existing `Modifier(rawValue:)` initializer picks up the new `file` case automatically.
37+
38+
### Files changed
39+
- `Sources/SwiftiomaticKit/Models/Command.swift` — added `case file` to `Modifier` enum, added `.file` case to `expand()`
40+
- `Tests/SwiftiomaticTests/Configuration/CommandTests.swift` — parsing, expansion, and superfluous disable tests
41+
- `Tests/SwiftiomaticTests/Rules/Infrastructure/RegionTests.swift` — region building tests
42+
- `Tests/SwiftiomaticTests/Rules/Infrastructure/DisableAllTests.swift` — integration tests for violation suppression

0 commit comments

Comments
 (0)