sonicop 26.8.111-aarch64-linux → 26.8.112-aarch64-linux
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CONFORMANCE.md +64 -31
- data/NOTICE +15 -3
- data/README.ja.md +112 -37
- data/README.md +107 -27
- data/lib/sonicop/runner.rb +7 -1
- data/lib/sonicop/version.rb +1 -1
- data/libexec/sonicop +0 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f1cab35205ef1b9545aa27aae10b033f94a6ef4285ad74f0f8439176570517ea
|
|
4
|
+
data.tar.gz: 8fa47739e28ec78bcd4ad740ffa2afdb4069a6fc673c9b11ef56f6a9ee6802fc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ee256fff60b527f0baf546b06a61ee57c0f12f3e4bdc25d109972a2376d8f723f66918de47b2c7c26305347c39be451c09422d37cb4d251e7abb4e9bce1ff7fa
|
|
7
|
+
data.tar.gz: 783be95e866e2ec1eeef816e1bd42500b01ff3d0b200073246d2c5e128fb80bd6722920c18d2591d2eb86eb0deb7928962baa9a3edc7d1c83f47352b59a9cde4
|
data/CONFORMANCE.md
CHANGED
|
@@ -55,17 +55,18 @@ sonicop --force-default-config --format json
|
|
|
55
55
|
|
|
56
56
|
## Results
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
`Lint/Syntax`
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
Three kinds of difference are counted separately because they mean different things. The set of
|
|
59
|
+
files with a fatal `Lint/Syntax` offense says whether the parsers disagree about accepting a file.
|
|
60
|
+
`Lint/Syntax` positions inside a file both already rejected measure diagnostic recovery, not file
|
|
61
|
+
acceptance. A difference in any other cop says the port reads the same tree and draws a different
|
|
62
|
+
conclusion. Only the last category is a defect in a cop.
|
|
62
63
|
|
|
63
64
|
| Corpus | Excess | Missing | of which `Lint/Syntax` | Other cops | Field differences | Measured |
|
|
64
65
|
|---|---:|---:|---|---:|---|---|
|
|
65
66
|
| rubocop/rubocop | 0 | 0 | — | 0 | correctable ×1 | 2026-08-17 |
|
|
66
67
|
| rails/rails | 0 | 0 | — | 0 | none | 2026-08-17 |
|
|
67
68
|
| mastodon/mastodon | 0 | 0 | — | 0 | none | 2026-08-17 |
|
|
68
|
-
| Homebrew/brew | 263 | 997 | **all of them** | **0** | none | 2026-08-
|
|
69
|
+
| Homebrew/brew | 263 | 997 | **all of them** | **0** | none | 2026-08-18 |
|
|
69
70
|
| ruby/ruby | 142 | 585 | 117 missing, 44 excess | 92 | 5 | 2026-08-16 |
|
|
70
71
|
|
|
71
72
|
The last column is per row on purpose. A single date at the top of the file would say the five were
|
|
@@ -85,14 +86,19 @@ run starts from, and `TargetRubyVersion` is only inferred from the working direc
|
|
|
85
86
|
tools therefore fall back to the default of 2.7 (RuboCop says so itself: `Using Ruby 2.7 parser`) and
|
|
86
87
|
both call `dry_run:,` — a hash value omission, valid since 3.1 — a syntax error. **Run the same corpus
|
|
87
88
|
at 3.1 and both report zero `Lint/Syntax`.** What is left is not disagreement about which files parse:
|
|
88
|
-
the file sets are identical, 569 on each side,
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
89
|
+
the file sets are identical, 569 on each side, with no file unique to either parser. The two parsers
|
|
90
|
+
recover differently after encountering invalid syntax, which puts diagnostic positions on both sides
|
|
91
|
+
of the ledger rather than only on RuboCop's.
|
|
92
|
+
|
|
93
|
+
All 263 Sonicop-only positions were classified mechanically, not inferred from their cop name. They
|
|
94
|
+
occur in 135 of those shared syntax-error files, and every one follows a `Lint/Syntax` position that
|
|
95
|
+
both tools reported in the same file. Their messages are 113 end-of-input (`$end`), 86 right
|
|
96
|
+
parentheses (`tRPAREN`), 53 commas (`tCOMMA`), seven `end` keywords (`kEND`), two equals signs
|
|
97
|
+
(`tEQL`), one `when` (`kWHEN`) and one identifier (`tIDENTIFIER`). Thus the former “excess” is not a
|
|
98
|
+
set of valid files rejected only by Sonicop; it is the other direction of the same diagnostic-recovery
|
|
99
|
+
divergence as the 997 RuboCop-only positions. Suppressing every diagnostic after Sonicop's first one
|
|
100
|
+
would make the excess zero, but would also discard 1,998 positions that already match and increase the
|
|
101
|
+
missing side from 997 to 2,995. That is not a conformance fix. See *Known divergences*.
|
|
96
102
|
|
|
97
103
|
Much of ruby/ruby's difference is the same shape,
|
|
98
104
|
with 117 of the 585 missing and 44 of the 142 excess being `Lint/Syntax` itself,
|
|
@@ -181,10 +187,10 @@ but no longer reproduces fails the test, and so does one that appears without be
|
|
|
181
187
|
### Error recovery after a syntax error
|
|
182
188
|
|
|
183
189
|
RuboCop parses with `parser`, an LALR parser that recovers from an error and keeps going, emitting
|
|
184
|
-
further diagnostics from the recovered state. Sonicop parses with tree-sitter
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
`cannot assign to a keyword`, and repeated `unexpected token` inside one multi-line hash.
|
|
190
|
+
further diagnostics from the recovered state. Sonicop parses with tree-sitter and reaches different
|
|
191
|
+
recovery states. On Homebrew this produces 997 RuboCop-only and 263 Sonicop-only `Lint/Syntax`
|
|
192
|
+
positions: examples on the RuboCop side include `class definition in method body`, `dynamic constant
|
|
193
|
+
assignment`, `cannot assign to a keyword`, and repeated `unexpected token` inside one multi-line hash.
|
|
188
194
|
|
|
189
195
|
What the divergence does not change is **which** files are held to be unparseable, and that is what
|
|
190
196
|
decides whether a file is inspected at all: RuboCop runs no cop other than `Lint/Syntax` on a file
|
|
@@ -198,9 +204,10 @@ programs, one of which is English prose that happens to parse as Ruby — where
|
|
|
198
204
|
error and `parser` does not. Those five hold 465 of that corpus's 773 differences.
|
|
199
205
|
|
|
200
206
|
Within Homebrew's 569 files the agreement is partial, as recovery cannot be reproduced: 499 report the
|
|
201
|
-
same first diagnostic (position and message), and 222 report an identical list end to end.
|
|
202
|
-
|
|
203
|
-
|
|
207
|
+
same first diagnostic (position and message), and 222 report an identical list end to end. In all 569,
|
|
208
|
+
Sonicop's earliest source-position diagnostic is also present in RuboCop's output. The 277 files in
|
|
209
|
+
between are the divergence in its purest form — the two agree on at least one error position and part
|
|
210
|
+
company as recovery proceeds. The 70
|
|
204
211
|
files whose first diagnostic differs follow one shape — an endless method definition (`def to_s =
|
|
205
212
|
to_str`, valid from Ruby 3.0, rejected by the default `TargetRubyVersion: 2.7`) leaves `parser`'s
|
|
206
213
|
method context open, so the enclosing `class` emits `class definition in method body` when it is
|
|
@@ -299,17 +306,43 @@ defect, so the difference stands as it is.
|
|
|
299
306
|
## Limits
|
|
300
307
|
|
|
301
308
|
A clean run is a property of the corpora, not a general claim. `known_divergences.yml` carries the
|
|
302
|
-
current list of what these corpora never exercise; the main ones are
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
309
|
+
current list of what these corpora never exercise; the main ones are extension plugins and Windows
|
|
310
|
+
line endings. Cops that never fire contribute nothing to a match count, and their silence is
|
|
311
|
+
indistinguishable from agreement.
|
|
312
|
+
|
|
313
|
+
Non-default configuration values used to be the largest of those gaps, and are now measured. Every
|
|
314
|
+
one of the 111 cops carrying an `Enforced*` setting was switched to a non-default value at once —
|
|
315
|
+
`Layout/SpaceInsideParens` to `space`, `Style/HashSyntax` to `hash_rockets` with
|
|
316
|
+
`EnforcedShorthandSyntax: always`, and so on — and rubocop/rubocop re-run against RuboCop under the
|
|
317
|
+
same file. Of 622,317 reference offenses, **99.99% match**, and 84 of the 96 cops that fired match
|
|
318
|
+
exactly.
|
|
319
|
+
|
|
320
|
+
| Cop | Offenses differing | |
|
|
321
|
+
|---|---:|---|
|
|
322
|
+
| `Layout/HashAlignment` | 35 | **RuboCop's own bug**, see below |
|
|
323
|
+
| `Style/BlockDelimiters` | 17 | |
|
|
324
|
+
| `Style/ConditionalAssignment` | 12 | |
|
|
325
|
+
| `Style/MixinGrouping` | 8 | |
|
|
326
|
+
| `Layout/SpaceInsideStringInterpolation` | 6 | |
|
|
327
|
+
| `Style/NumericPredicate` | 5 | |
|
|
328
|
+
| `Style/PercentQLiterals` | 4 | |
|
|
329
|
+
| `Naming/VariableName` | 3 | |
|
|
330
|
+
| `Lint/SymbolConversion` | 3 | |
|
|
331
|
+
| `Layout/SpaceInsideParens` | 1 | |
|
|
332
|
+
|
|
333
|
+
`Layout/HashAlignment`'s 35 are not a defect here. Under `EnforcedHashRocketStyle: separator`
|
|
334
|
+
RuboCop raises `Parser::ClobberingError` on `spec/rubocop/config_loader_spec.rb` — three times, at
|
|
335
|
+
`1111:35`, `1130:38` and `1217:35` — and reports nothing for the hashes it crashed on. Sonicop
|
|
336
|
+
inspects them and reports. Every one of the 35 sits in that file between lines 1114 and 1234.
|
|
337
|
+
Reproduce with:
|
|
338
|
+
|
|
339
|
+
```bash
|
|
340
|
+
rubocop -c <variants.yml> --only Layout/HashAlignment -d spec/rubocop/config_loader_spec.rb
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
One value per cop is not the whole configuration space — a cop with four supported styles is
|
|
344
|
+
measured at two of them — so this is a floor, not a ceiling. It does settle the shape of the old
|
|
345
|
+
limit: a cop is no longer allowed to be half absent and still count as matching.
|
|
313
346
|
|
|
314
347
|
The 215 cops RuboCop ships switched off are a limit of a different kind. They are implemented, but a
|
|
315
348
|
default run never reaches them, so the corpus numbers above say nothing about them. What stands
|
data/NOTICE
CHANGED
|
@@ -15,9 +15,9 @@ licence position checkable rather than remembered.
|
|
|
15
15
|
not the attribution. An entry that is added late is an entry that was, for a
|
|
16
16
|
while, undocumented in a public repository.
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
That inventory is now complete for what is here. The entry that used to be
|
|
19
|
+
listed as "coming" -- the spec-derived fixtures -- has landed and is recorded
|
|
20
|
+
below with the rest.
|
|
21
21
|
|
|
22
22
|
================================================================================
|
|
23
23
|
WHAT IS TAKEN
|
|
@@ -32,6 +32,18 @@ RuboCop -- https://github.com/rubocop/rubocop -- MIT
|
|
|
32
32
|
Regenerate: scripts/sync_default_yml.sh 1.89.0
|
|
33
33
|
Full text also kept at licenses/RUBOCOP.txt
|
|
34
34
|
|
|
35
|
+
tests/fixtures/upstream_spec_cases.jsonl
|
|
36
|
+
tests/fixtures/upstream_spec_divergences.jsonl
|
|
37
|
+
Input text taken out of RuboCop's own cop specs, each paired with what
|
|
38
|
+
RuboCop 1.89.0 actually reports for it. The spec text is upstream's; the
|
|
39
|
+
expectation beside it is not -- upstream was run once and its output
|
|
40
|
+
recorded, because upstream does not always behave the way its own specs
|
|
41
|
+
say. The second file holds the cases whose `-A` output does not parse as
|
|
42
|
+
Ruby, kept apart so that nothing demands this port reproduce them.
|
|
43
|
+
Regenerate: make spec-fixtures (runs upstream once per cop; needs the
|
|
44
|
+
rubocop gem, which the test run itself does not)
|
|
45
|
+
Read by tests/spec_fixtures.rs
|
|
46
|
+
|
|
35
47
|
Ruby on Rails -- https://github.com/rails/rails -- MIT
|
|
36
48
|
Copyright (c) David Heinemeier Hansson
|
|
37
49
|
|
data/README.ja.md
CHANGED
|
@@ -32,22 +32,80 @@ CI へ最小限の変更で導入できます。
|
|
|
32
32
|
|
|
33
33
|
## 主な機能
|
|
34
34
|
|
|
35
|
-
Bundler、Gemspec、Layout、Lint、Metrics、Migration、Naming、Security、Style
|
|
36
|
-
|
|
35
|
+
Bundler、Gemspec、Layout、Lint、Metrics、Migration、Naming、Security、Style の各デパートメントの
|
|
36
|
+
Cop を実装しています。一覧はバイナリ自身が正本です。
|
|
37
37
|
|
|
38
38
|
```bash
|
|
39
39
|
# 認識済み Cop と実装状況の一覧
|
|
40
40
|
sonicop --show-cops
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
**RuboCop 1.89 の全 609 Cop を実装しています。**
|
|
44
|
-
ただしこれは**レジストリについての主張**であって、すべての設定についての主張ではありません。
|
|
45
|
-
`Style/HashSyntax` は存在し既定の届く範囲では一致しますが、`EnforcedShorthandSyntax` の
|
|
46
|
-
既定以外の 4 つの値はいずれも未実装です。**Cop は半分欠けたまま、既定の届く範囲では
|
|
47
|
-
一致し続けられます** — [CONFORMANCE.md](CONFORMANCE.md) の *Limits* を参照してください。
|
|
43
|
+
**RuboCop 1.89 の全 609 Cop を実装しています。** 本家のレジストリと名前まで一致しています。
|
|
48
44
|
`Enabled: pending` の 159 個と `Enabled: false` の 56 個も含みます。この 215 個は本家でも
|
|
49
|
-
既定の実行では走らないので、`--only`
|
|
50
|
-
|
|
45
|
+
既定の実行では走らないので、`--only` で名指しするか設定で有効にしてください。本家に存在しない
|
|
46
|
+
Cop 名を書いた場合だけエラーで止まります(`--ignore-unrecognized-cops` で続行できます)。
|
|
47
|
+
|
|
48
|
+
### Cop 別の一致状況
|
|
49
|
+
|
|
50
|
+
全 609 Cop を両者で有効にし、本家の spec が供給する 37,491 ケースを、それぞれの spec が
|
|
51
|
+
指定した `TargetRubyVersion` で走らせて比較した結果です。**完全一致**とは、その Cop の offense が
|
|
52
|
+
位置・メッセージ・重大度・修正可否まで 1 件残らず一致し、どちらにも余りがないことを指します。
|
|
53
|
+
|
|
54
|
+
<!-- conformance:start -->
|
|
55
|
+
| デパートメント | Cop 数 | 検証済み | 完全一致 | 相違 |
|
|
56
|
+
|---|---:|---:|---:|---:|
|
|
57
|
+
| Bundler | 7 | 7 | **7 ✓** | 0 |
|
|
58
|
+
| Gemspec | 10 | 10 | **10 ✓** | 0 |
|
|
59
|
+
| Layout | 100 | 100 | 90 | 10 |
|
|
60
|
+
| Lint | 157 | 157 | 147 | 10 |
|
|
61
|
+
| Metrics | 10 | 10 | **10 ✓** | 0 |
|
|
62
|
+
| Migration | 1 | 1 | **1 ✓** | 0 |
|
|
63
|
+
| Naming | 19 | 19 | **19 ✓** | 0 |
|
|
64
|
+
| Security | 7 | 7 | **7 ✓** | 0 |
|
|
65
|
+
| Style | 298 | 298 | 288 | 10 |
|
|
66
|
+
| **合計** | **609** | **609** | **579** | **30** |
|
|
67
|
+
<!-- conformance:end -->
|
|
68
|
+
|
|
69
|
+
**先に読むべきは「検証済み」の列です。** ここで一度も発火しなかった Cop は、沈黙が一致と
|
|
70
|
+
見分けられないため、訊かれないまま一致に数えられてしまいます。**609 個すべてがここで発火します**。
|
|
71
|
+
それが「完全一致」の列を意味あるものにしています。
|
|
72
|
+
|
|
73
|
+
そのうち 3 個は専用の run を要しました。`Lint/DeprecatedReference`・`Lint/NameTypo`・
|
|
74
|
+
`Lint/UnusedPrivateMethod` は `rubydex` のプロジェクトインデックスが無ければ何も報告せず、
|
|
75
|
+
gem の導入と `AllCops/UseProjectIndex` の有効化が要ります。`Lint/DeprecatedReference` は
|
|
76
|
+
さらに条件があり、`@deprecated` を持つメソッドの定義クラスを**継承したクラスの中**から
|
|
77
|
+
呼ぶ必要があります。本家の spec は「インデックス無しでは offense を出さない」という
|
|
78
|
+
`expect_no_offenses` で始まるので「到達不能」と読みたくなりますが、そうではありません。
|
|
79
|
+
|
|
80
|
+
「完全一致」を 609 にし、「相違」をゼロにすることが現在の目標です。実コードを足しても
|
|
81
|
+
届きません。RuboCop が既定で無効にしている 56 Cop と、pending として出荷している Cop の多くは、
|
|
82
|
+
ツリーがどれだけ大きくても素の実行では発火しないためです。そこに届くのは本家の spec が
|
|
83
|
+
供給する入力の方で、`tests/fixtures/upstream_spec_capture.jsonl` に記録したケースは
|
|
84
|
+
**609 Cop すべてに到達します**(実測)。走らせ方には間違えやすい点が 2 つあり、
|
|
85
|
+
**どちらも失敗せずに表を縮めます**。
|
|
86
|
+
|
|
87
|
+
- **`TargetRubyVersion` は入力の一部であって、全体の設定ではありません。** 2.7 に固定すると
|
|
88
|
+
`Style/ArrayIntersect`・`Naming/BlockForwarding`・`Style/ItBlockParameter` ほか 11 個が
|
|
89
|
+
そもそも発火できません。各ケースはその spec が指定した版で走らせます。
|
|
90
|
+
- **ファイル名そのものを見る Cop があります。** `Bundler/*` は `Gemfile`、`Gemspec/*` は
|
|
91
|
+
`.gemspec`、`Naming/FileName` は名前自体を読みます。全ケースを `.rb` で書き出すと、
|
|
92
|
+
この 17 Cop が何にも一致しませんでした。
|
|
93
|
+
|
|
94
|
+
これとは別に、2026-08-29 に本家 Cop spec から直接抽出できた 11,506 ケースを oracle で
|
|
95
|
+
照合しました。本家が入力を読めなかった 226 ケースとクラッシュした 1 ケースを除き、測定できた
|
|
96
|
+
範囲では Sonicop の**検出差分・訂正差分ともにゼロ**でした。この結果は上の表には加えていません。
|
|
97
|
+
直接抽出できるケースが無い Cop が 51 個あり、ディレクティブ系 3 Cop は `--only` では測定不能な
|
|
98
|
+
ため、この掃引だけで全 609 Cop の完全一致を証明できないからです。また、この直接掃引は各例の
|
|
99
|
+
`TargetRubyVersion` をすべて再現せず、中立的な既定条件で実行しています。そのため、今回変更した
|
|
100
|
+
Ruby 3.4 依存の 4 Cop は 3.4 で別途比較し、メッセージと位置が本家に完全一致することを確認しました。
|
|
101
|
+
|
|
102
|
+
設定値については別に測っています。既定値でしか一致しない Cop は半分しか実装していないのと
|
|
103
|
+
同じだからです。`Enforced*` 系の設定を持つ 111 Cop すべてを**既定以外の値**に倒して同じコーパスを
|
|
104
|
+
流すと、**622,317 件の offense のうち 99.995% が一致**し、発火した 96 Cop のうち 85 個が完全一致です。
|
|
105
|
+
残るのは 10 Cop(いずれも 17 件以下)と、本家がクラッシュして sonicop が正常に検出する 1 Cop です。
|
|
106
|
+
内訳は [CONFORMANCE.md](CONFORMANCE.md) にあります。
|
|
107
|
+
|
|
108
|
+
どちらの表も `scripts/conformance_table.rb` で再現できます。
|
|
51
109
|
|
|
52
110
|
## インストール
|
|
53
111
|
|
|
@@ -70,7 +128,7 @@ cargo install --git https://github.com/owayo/sonicop
|
|
|
70
128
|
# 現在のプロジェクトを検査
|
|
71
129
|
sonicop
|
|
72
130
|
|
|
73
|
-
# Cop
|
|
131
|
+
# Cop/デパートメントを指定
|
|
74
132
|
sonicop --only Layout,Style/StringLiterals app spec
|
|
75
133
|
|
|
76
134
|
# 安全な自動修正/全自動修正
|
|
@@ -106,24 +164,30 @@ Style/StringLiterals:
|
|
|
106
164
|
EnforcedStyle: single_quotes
|
|
107
165
|
```
|
|
108
166
|
|
|
109
|
-
既存コマンドとの互換性を保つため、server/LSP/MCP、plugin
|
|
110
|
-
サーバートランスポート、Ruby
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
`
|
|
124
|
-
|
|
167
|
+
既存コマンドとの互換性を保つため、server/LSP/MCP、plugin 系の引数も受理します。
|
|
168
|
+
サーバートランスポート、Ruby プラグイン実行、カスタム Cop、実装済み以外の Cop は実行しません。
|
|
169
|
+
これらはその旨を出力します。`--server` / `--no-server` / `--lsp` / `--mcp` / `--plugin` は
|
|
170
|
+
stderr に 1 行の注記を出します。
|
|
171
|
+
|
|
172
|
+
cache 系の引数は、受理するだけでなく実際に効きます。sonicop は独自の結果キャッシュを持ち、
|
|
173
|
+
検査時からサイズ・更新時刻・パーミッションのいずれも動いていないファイルには、
|
|
174
|
+
保存済みのレポートをそのまま返します。
|
|
175
|
+
|
|
176
|
+
- キャッシュは既定で有効です。`--cache false` で無効化できます。設定ファイルの
|
|
177
|
+
`AllCops/MaxFilesInCache: 0` でも同じです。
|
|
178
|
+
- 置き場所は `--cache-root DIR` で指定します。省略時は `$XDG_CACHE_HOME/sonicop`、
|
|
179
|
+
macOS では `~/Library/Caches/sonicop`、それ以外は `~/.cache/sonicop` です。
|
|
180
|
+
`--cache-root` は `--cache false` とは併用できません。
|
|
181
|
+
- 保持するレポート数の上限は `AllCops/MaxFilesInCache` で、既定は本家と同じ 20,000 件です。
|
|
182
|
+
- autocorrect 実行、`--stdin`、`--profile`、`--memory` では読み書きしません。
|
|
183
|
+
- 本家のキャッシュとは共有しません。形式が別物であり、書いたときとまったく同じ
|
|
184
|
+
ビルドの sonicop にしかエントリを返さないためです。
|
|
185
|
+
|
|
186
|
+
無言なのは Cop の設定値のほうです。sonicop が実装していない設定値は**警告なしに無視されます**。
|
|
187
|
+
名前を綴り間違えた設定値も同様です。つまり
|
|
125
188
|
**offense が 0 件であることは、その設定が効いた証拠にはなりません**。
|
|
126
189
|
無視された設定値と、違反の無いファイルが、同じ出力になるためです。
|
|
190
|
+
どの設定値まで検証済みかは上の *Cop 別の一致状況* を参照してください。
|
|
127
191
|
|
|
128
192
|
Cop の*名前*は検査されます。設定ファイルに未知の Cop 名があれば、実行はエラーで止まります。
|
|
129
193
|
素通りするのは、既知の Cop の中の設定値です。
|
|
@@ -139,11 +203,13 @@ Mastodon(15,286 件)で、過剰も不足もメタデータ差もありま
|
|
|
139
203
|
**件数だけでなくパスまで**一致します(集合として比較。どの側にも余りはありません)。
|
|
140
204
|
残る差分は `Lint/Syntax` に集中しています。その大半は、本家の LALR パーサが
|
|
141
205
|
構文エラーから回復して出す追加診断を tree-sitter では再現できないことによるもので、
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
206
|
+
診断位置の差は不足と過剰の両方に出ます。Homebrew の不足 997 件・過剰 263 件はすべて
|
|
207
|
+
`Lint/Syntax` ですが、**構文エラーと判定したファイル集合は 569 対 569 で完全一致**し、
|
|
208
|
+
移植版だけが拒否したファイルは 0 件です。過剰 263 件は共有エラーファイル 135 件にあり、
|
|
209
|
+
すべて同じファイル内の共通診断より後ろにあるため、別の受理判定バグではなく回復後の診断位置差です。
|
|
210
|
+
Homebrew を問題の構文をサポートする Ruby 3.1 として測ると、両者とも `Lint/Syntax` は 0 件になります。
|
|
211
|
+
autocorrect は RuboCop 自身のツリーと Mastodon でバイト単位に一致します。この 2 つは死守ラインと
|
|
212
|
+
して扱い、バイト一致が崩れた場合は既知差分ではなく退行として直します。
|
|
147
213
|
|
|
148
214
|
コマンド、この数値を測ったコーパスのコミット、この種の計測が誤った結論を導く 2 つの罠は
|
|
149
215
|
[CONFORMANCE.md](CONFORMANCE.md) にまとめています。
|
|
@@ -182,19 +248,22 @@ Mastodon でバイト単位に一致します。この 2 つは死守ライン
|
|
|
182
248
|
Rails・Mastodon の 3 つで**すべての offense が一致**します(計 188,812 件、どちらの側にも
|
|
183
249
|
残りません)。autocorrect は前者と後者でバイト単位に一致します。
|
|
184
250
|
|
|
185
|
-
再現時に注意が必要な点が
|
|
251
|
+
再現時に注意が必要な点が 3 つあります。RuboCop は **`--cache false` と併用すると
|
|
186
252
|
`--parallel` を黙って無効化します**。そのためここでの並列実行はキャッシュを有効にしたうえで
|
|
187
253
|
実行ごとにキャッシュディレクトリを消しており、`--cache false --parallel` で計測すると
|
|
188
254
|
単一プロセスを測ることになり差が過大に出ます。また RuboCop の既定は単一プロセス、
|
|
189
|
-
Sonicop は `--no-parallel`
|
|
255
|
+
Sonicop は `--no-parallel` を渡さない限り並列です。そして**両方ともキャッシュを空にする**
|
|
256
|
+
必要があります。Sonicop も既定でキャッシュするため、同じツリーを 2 回目に流すと自分の
|
|
257
|
+
キャッシュが答えてしまい、エンジンについては何も測れません。どちらにも使い捨ての
|
|
258
|
+
キャッシュディレクトリを渡してください。
|
|
190
259
|
|
|
191
260
|
```bash
|
|
192
261
|
# RuboCop(並列・キャッシュは毎回空・既定の全 394 Cop)
|
|
193
262
|
rubocop --force-default-config --cache true --cache-root "$(mktemp -d)" \
|
|
194
263
|
--no-color --parallel -f quiet
|
|
195
264
|
|
|
196
|
-
# Sonicop
|
|
197
|
-
sonicop --force-default-config --format quiet
|
|
265
|
+
# Sonicop(キャッシュは毎回空)
|
|
266
|
+
sonicop --force-default-config --cache-root "$(mktemp -d)" --format quiet
|
|
198
267
|
```
|
|
199
268
|
|
|
200
269
|
測定機は Apple M2(8 コア)、Ruby 4.0.6(YJIT 利用可)、RubyGems 導入の RuboCop 1.89.0。
|
|
@@ -220,8 +289,8 @@ make gem # source gem
|
|
|
220
289
|
|
|
221
290
|
### Cop の追加
|
|
222
291
|
|
|
223
|
-
Cop は `src/rules
|
|
224
|
-
1
|
|
292
|
+
Cop は `src/rules/<デパートメント>/<cop>.rs` の 1 ファイルで、公開するのは
|
|
293
|
+
`check(context, offenses)` の 1 関数だけです。登録はデパートメントの `mod.rs` に 1 行を足します。
|
|
225
294
|
|
|
226
295
|
```rust
|
|
227
296
|
department_rules! {
|
|
@@ -245,6 +314,12 @@ Cop ごとの全走査はファイル規模ではなく Cop 数に比例して
|
|
|
245
314
|
`scripts/sync_default_yml.sh <rubocop-version>` で行い、由来のバージョンがファイル先頭に
|
|
246
315
|
記録されます。
|
|
247
316
|
|
|
317
|
+
`src/display_width_table.rs` も生成物で、コミットします。RuboCop は表示桁を
|
|
318
|
+
`unicode-display_width` gem で数えるため、この表は手書きせず gem から生成しています。
|
|
319
|
+
手書きの例外表は実際にずれており、NFD 分解された日本語でキャレットの本数が合わなくなっていました。
|
|
320
|
+
再生成は `ruby scripts/dump_display_width.rb > src/display_width_table.rs` で行い、
|
|
321
|
+
gem と Unicode のバージョンがファイル先頭に記録されます。
|
|
322
|
+
|
|
248
323
|
依存更新には `depup --install` を使います。Ruby grammar は再現可能性のため `Cargo.toml` で
|
|
249
324
|
fork のコミットを固定しています。
|
|
250
325
|
|
data/README.md
CHANGED
|
@@ -41,15 +41,76 @@ sonicop --show-cops
|
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
**All 609 RuboCop 1.89 cops are implemented**, matched name for name against the upstream registry.
|
|
44
|
-
That is a claim about the registry, not about every configuration: `Style/HashSyntax` is present and
|
|
45
|
-
matches everywhere its default reaches, yet none of the four non-default values of its
|
|
46
|
-
`EnforcedShorthandSyntax` are implemented. **A cop can be half absent and still match everywhere the
|
|
47
|
-
default reaches** — see *Limits* in [CONFORMANCE.md](CONFORMANCE.md).
|
|
48
44
|
That includes the 159 shipped as `Enabled: pending` and the 56 shipped as `Enabled: false`, which a
|
|
49
45
|
default run does not reach on either side — name them with `--only` or switch them on in a
|
|
50
46
|
configuration, exactly as with RuboCop. Unknown cop names still fail validation unless
|
|
51
47
|
`--ignore-unrecognized-cops` is supplied.
|
|
52
48
|
|
|
49
|
+
### Cop conformance
|
|
50
|
+
|
|
51
|
+
All 609 cops switched on, on both sides, over the 37,491 cases RuboCop's own specs supply, each
|
|
52
|
+
run at the `TargetRubyVersion` its spec asked for. A cop counts as an **exact match** only when its
|
|
53
|
+
offenses agree completely: every position, message, severity and correctable flag, with nothing
|
|
54
|
+
extra on either side.
|
|
55
|
+
|
|
56
|
+
<!-- conformance:start -->
|
|
57
|
+
| Department | Cops | Exercised | Exact match | Diverging |
|
|
58
|
+
|---|---:|---:|---:|---:|
|
|
59
|
+
| Bundler | 7 | 7 | **7 ✓** | 0 |
|
|
60
|
+
| Gemspec | 10 | 10 | **10 ✓** | 0 |
|
|
61
|
+
| Layout | 100 | 100 | 90 | 10 |
|
|
62
|
+
| Lint | 157 | 157 | 147 | 10 |
|
|
63
|
+
| Metrics | 10 | 10 | **10 ✓** | 0 |
|
|
64
|
+
| Migration | 1 | 1 | **1 ✓** | 0 |
|
|
65
|
+
| Naming | 19 | 19 | **19 ✓** | 0 |
|
|
66
|
+
| Security | 7 | 7 | **7 ✓** | 0 |
|
|
67
|
+
| Style | 298 | 298 | 288 | 10 |
|
|
68
|
+
| **Total** | **609** | **609** | **579** | **30** |
|
|
69
|
+
<!-- conformance:end -->
|
|
70
|
+
|
|
71
|
+
**Read the *Exercised* column first.** A cop nothing here made fire contributes neither way — its
|
|
72
|
+
silence is indistinguishable from agreement, so it would be counted as agreement without ever
|
|
73
|
+
being asked. **Every one of the 609 fires here**, which is what makes the *Exact match* column
|
|
74
|
+
mean what it says.
|
|
75
|
+
|
|
76
|
+
Three of them took a run of their own. `Lint/DeprecatedReference`, `Lint/NameTypo` and
|
|
77
|
+
`Lint/UnusedPrivateMethod` report nothing without a `rubydex` project index, which needs the gem
|
|
78
|
+
installed and `AllCops/UseProjectIndex` switched on — and `Lint/DeprecatedReference` needs more
|
|
79
|
+
than that: the call has to sit inside a class inheriting the one whose method carries the
|
|
80
|
+
`@deprecated` tag. Upstream's own specs open with an `expect_no_offenses` saying the cop is silent
|
|
81
|
+
without the index, which is easy to read as "unreachable"; it is not.
|
|
82
|
+
|
|
83
|
+
Getting *Exact match* to 609 while reducing *Diverging* to zero is the current goal. More real Ruby
|
|
84
|
+
does not get there: the 56 cops RuboCop ships disabled, and much of what it ships as pending, never
|
|
85
|
+
fire in a plain run however large the tree. What does reach every one of them is the input its own
|
|
86
|
+
specs supply — the cases recorded in `tests/fixtures/upstream_spec_capture.jsonl` touch **609 of 609
|
|
87
|
+
cops**, measured. Two things about running them are easy to get wrong, and both silently shrink the
|
|
88
|
+
table rather than failing:
|
|
89
|
+
|
|
90
|
+
- **`TargetRubyVersion` is part of the input, not a global.** Pinning everything at 2.7 leaves
|
|
91
|
+
`Style/ArrayIntersect`, `Naming/BlockForwarding`, `Style/ItBlockParameter` and eleven others
|
|
92
|
+
unable to fire at all. Each case is run at the version its spec asked for.
|
|
93
|
+
- **The filename is what several cops inspect.** `Bundler/*` needs a `Gemfile`, `Gemspec/*` a
|
|
94
|
+
`.gemspec`, and `Naming/FileName` reads the name itself. Writing every case as `.rb` had all 17
|
|
95
|
+
of those cops matching nothing.
|
|
96
|
+
|
|
97
|
+
A separate direct oracle sweep on 2026-08-29 examined 11,506 cases extractable from the upstream
|
|
98
|
+
cop specs. RuboCop could not read 226 of those inputs and crashed on one; among the measurable
|
|
99
|
+
cases, Sonicop had **zero detection differences and zero correction differences**. This result is
|
|
100
|
+
not folded into the table above: 51 cops had no directly extractable case, and three directive cops
|
|
101
|
+
cannot be measured under `--only`, so the sweep does not prove that all 609 cops are exact. The
|
|
102
|
+
direct sweep also uses neutral/default conditions rather than preserving every example's
|
|
103
|
+
`TargetRubyVersion`. The four Ruby 3.4-sensitive cops changed in this pass were therefore compared
|
|
104
|
+
separately at 3.4, where their messages and locations matched RuboCop exactly.
|
|
105
|
+
|
|
106
|
+
Configuration is measured separately, because a cop that only matches at its default value is half
|
|
107
|
+
a cop. Every one of the 111 cops carrying an `Enforced*` setting was switched to a **non-default**
|
|
108
|
+
value at once and the corpus re-run: **99.995% of 622,317 offenses match**, with 85 of the 96 cops
|
|
109
|
+
that fired matching exactly. The residue is 10 cops of at most 17 offenses each, plus one where
|
|
110
|
+
RuboCop crashes and sonicop does not; the list is in [CONFORMANCE.md](CONFORMANCE.md).
|
|
111
|
+
|
|
112
|
+
Reproduce either table with `scripts/conformance_table.rb`.
|
|
113
|
+
|
|
53
114
|
## Installation
|
|
54
115
|
|
|
55
116
|
```bash
|
|
@@ -114,22 +175,29 @@ Style/StringLiterals:
|
|
|
114
175
|
EnforcedStyle: single_quotes
|
|
115
176
|
```
|
|
116
177
|
|
|
117
|
-
The CLI accepts RuboCop's server/LSP/MCP
|
|
118
|
-
parse-compatible. Sonicop does not provide server transports, Ruby plugin execution,
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
178
|
+
The CLI accepts RuboCop's server/LSP/MCP and plugin flags to keep existing command lines
|
|
179
|
+
parse-compatible. Sonicop does not provide server transports, Ruby plugin execution, custom Ruby
|
|
180
|
+
cops, or cops outside the implemented set. Each of those flags says so: `--server`, `--no-server`,
|
|
181
|
+
`--lsp`, `--mcp`, and `--plugin` print a one-line notice on stderr.
|
|
182
|
+
|
|
183
|
+
The cache flags are honoured rather than merely parsed. Sonicop keeps a result cache of its own and
|
|
184
|
+
serves a stored report for a file whose size, modification time and permission bits have not moved
|
|
185
|
+
since it was inspected.
|
|
186
|
+
|
|
187
|
+
- Caching is on by default. `--cache false` turns it off, as does `AllCops/MaxFilesInCache: 0` in a
|
|
188
|
+
configuration file.
|
|
189
|
+
- `--cache-root DIR` chooses where it lives. Without it the root is `$XDG_CACHE_HOME/sonicop`, or
|
|
190
|
+
`~/Library/Caches/sonicop` on macOS, or `~/.cache/sonicop`. `--cache-root` cannot be combined
|
|
191
|
+
with `--cache false`.
|
|
192
|
+
- `AllCops/MaxFilesInCache` bounds how many reports are kept, defaulting to RuboCop's 20,000.
|
|
193
|
+
- Autocorrect runs, `--stdin`, `--profile` and `--memory` neither read nor write it.
|
|
194
|
+
- It is not shared with RuboCop's cache: the formats are unrelated, and an entry is only served back
|
|
195
|
+
to a build of Sonicop identical to the one that wrote it.
|
|
196
|
+
|
|
197
|
+
Cop settings are the silent case. A setting sonicop does not implement is ignored without
|
|
198
|
+
any warning, and so is a setting whose name is simply misspelled. **A run that reports no offenses
|
|
199
|
+
is therefore not evidence that a setting took effect**, because an ignored setting and a clean file
|
|
200
|
+
produce the same output. *Cop conformance* above says which values have been measured.
|
|
133
201
|
|
|
134
202
|
Cop *names* are checked: an unrecognised cop in a configuration file stops the run with an error.
|
|
135
203
|
It is the settings inside a recognised cop that pass unvalidated.
|
|
@@ -146,9 +214,12 @@ Mastodon (15,286), with no excess, no shortfall and no metadata differences. The
|
|
|
146
214
|
match exactly on all five — paths, not just counts, compared as sets. What remains is concentrated in
|
|
147
215
|
`Lint/Syntax`. Most of it is RuboCop's
|
|
148
216
|
LALR parser recovering from an error and emitting diagnostics a tree-sitter parse cannot
|
|
149
|
-
reconstruct
|
|
150
|
-
|
|
151
|
-
|
|
217
|
+
reconstruct, and the resulting position differences go in both directions. On Homebrew all 997
|
|
218
|
+
missing and 263 excess positions are `Lint/Syntax`, but the **sets of files rejected as syntax
|
|
219
|
+
errors are exactly the same: 569 versus 569**, with no file rejected only by Sonicop. The 263 excess
|
|
220
|
+
positions occur in 135 shared syntax-error files and every one follows a diagnostic at a position
|
|
221
|
+
shared by both tools, so they are recovery-position differences rather than a separate acceptance
|
|
222
|
+
bug. At Ruby 3.1, which supports the syntax used there, both tools report zero `Lint/Syntax` offenses.
|
|
152
223
|
Autocorrect is byte-identical on RuboCop's own tree and on Mastodon, the two corpora held as a hard
|
|
153
224
|
line: a change that breaks byte equality there is a regression, not a new known divergence.
|
|
154
225
|
|
|
@@ -188,19 +259,21 @@ The speed is not bought by skipping work: over those same 394 cops the two agree
|
|
|
188
259
|
on RuboCop's own tree, on Rails and on Mastodon — 188,812 offenses with nothing on either side of the
|
|
189
260
|
ledger — and autocorrect is byte-identical on the first and the last.
|
|
190
261
|
|
|
191
|
-
|
|
262
|
+
Three details matter for reproducing this. RuboCop **silently turns `--parallel` off when combined
|
|
192
263
|
with `--cache false`**, so its parallel runs here use a cache directory that is deleted before each
|
|
193
264
|
run rather than disabled; timing it with `--cache false --parallel` measures a single process and
|
|
194
265
|
overstates the difference. RuboCop's default is a single process, while Sonicop is parallel unless
|
|
195
|
-
`--no-parallel` is passed.
|
|
266
|
+
`--no-parallel` is passed. And both sides need a **cold** cache: Sonicop caches by default too, so a
|
|
267
|
+
second run over the same tree answers from its own cache and measures nothing about the engine.
|
|
268
|
+
Give each tool a throwaway cache root.
|
|
196
269
|
|
|
197
270
|
```bash
|
|
198
271
|
# RuboCop, parallel, cold cache, its full default set of 394 cops
|
|
199
272
|
rubocop --force-default-config --cache true --cache-root "$(mktemp -d)" \
|
|
200
273
|
--no-color --parallel -f quiet
|
|
201
274
|
|
|
202
|
-
# Sonicop
|
|
203
|
-
sonicop --force-default-config --format quiet
|
|
275
|
+
# Sonicop, cold cache
|
|
276
|
+
sonicop --force-default-config --cache-root "$(mktemp -d)" --format quiet
|
|
204
277
|
```
|
|
205
278
|
|
|
206
279
|
Machine: Apple M2 (8 cores), Ruby 4.0.6 with YJIT available, RubyGems-installed RuboCop 1.89.0.
|
|
@@ -254,6 +327,13 @@ fails when the two disagree.
|
|
|
254
327
|
`scripts/sync_default_yml.sh <rubocop-version>`, which records the source version in the file
|
|
255
328
|
header.
|
|
256
329
|
|
|
330
|
+
`src/display_width_table.rs` is generated and committed too. RuboCop measures display columns with
|
|
331
|
+
the `unicode-display_width` gem, so the table is taken from the gem rather than restated by hand —
|
|
332
|
+
an exception table written out by hand had already drifted far enough to draw the wrong number of
|
|
333
|
+
carets under decomposed Japanese. Regenerate it with
|
|
334
|
+
`ruby scripts/dump_display_width.rb > src/display_width_table.rs`, which records the gem and Unicode
|
|
335
|
+
versions in the file header.
|
|
336
|
+
|
|
257
337
|
Dependencies are updated with `depup --install`. The Ruby grammar dependency is pinned to an exact
|
|
258
338
|
fork commit in `Cargo.toml` for reproducible builds.
|
|
259
339
|
|
data/lib/sonicop/runner.rb
CHANGED
|
@@ -15,7 +15,13 @@ module Sonicop
|
|
|
15
15
|
MESSAGE
|
|
16
16
|
|
|
17
17
|
begin
|
|
18
|
-
exec(binary, *arguments)
|
|
18
|
+
# `[binary, binary]` = exec 形式の強制。`exec(binary, *arguments)` は arguments が空、
|
|
19
|
+
# つまり「カレントディレクトリを見る素の `sonicop`」という最も普通の呼び出しのとき
|
|
20
|
+
# `exec(String)` = commandline 形式に落ちる。この形式は空白で単語分割し、メタ文字が
|
|
21
|
+
# あればまるごと /bin/sh に渡すため、binary のパス次第で ENOENT や exit 127 になる。
|
|
22
|
+
# パスの出所は SONICOP_BINARY と gem の install 先で、どちらもメタ文字を含まない保証がない。
|
|
23
|
+
# argv0 は multi-arg 形式が既定で入れるものと同じ値を明示している。
|
|
24
|
+
exec([binary, binary], *arguments)
|
|
19
25
|
rescue SystemCallError => error
|
|
20
26
|
# 実行できない典型は「platform gem は入ったが実行環境と ABI が違う」ケース
|
|
21
27
|
# (musl 環境に glibc ビルドが入るなど)。素の errno だけでは原因が読めない。
|
data/lib/sonicop/version.rb
CHANGED
data/libexec/sonicop
CHANGED
|
Binary file
|