sonicop 26.8.108 → 26.8.110

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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CONFORMANCE.md +55 -15
  3. data/Cargo.lock +1 -1
  4. data/Cargo.toml +1 -1
  5. data/NOTICE +152 -6
  6. data/README.ja.md +39 -7
  7. data/README.md +36 -8
  8. data/lib/sonicop/version.rb +1 -1
  9. data/src/config/loader.rs +11 -1
  10. data/src/engine.rs +173 -14
  11. data/src/nul_bytes.rs +130 -16
  12. data/src/rules/layout/block_alignment.rs +5 -0
  13. data/src/rules/layout/empty_comment.rs +5 -1
  14. data/src/rules/layout/empty_line_after_guard_clause.rs +33 -13
  15. data/src/rules/layout/leading_comment_space.rs +6 -4
  16. data/src/rules/layout/trailing_whitespace.rs +19 -1
  17. data/src/rules/lint/ambiguity.rs +6 -1
  18. data/src/rules/lint/duplicate_hash_key.rs +11 -8
  19. data/src/rules/lint/literal_as_condition.rs +20 -20
  20. data/src/rules/lint/literal_in_interpolation.rs +10 -4
  21. data/src/rules/lint/node_equality.rs +106 -0
  22. data/src/rules/lint/regexp_tree.rs +94 -6
  23. data/src/rules/lint/safe_navigation_chain.rs +91 -33
  24. data/src/rules/lint/syntax.rs +97 -0
  25. data/src/rules/send_node.rs +55 -0
  26. data/src/rules/style/collection_compact.rs +14 -8
  27. data/src/rules/style/conditional_assignment.rs +23 -22
  28. data/src/rules/style/double_negation.rs +18 -22
  29. data/src/rules/style/each_with_object.rs +5 -1
  30. data/src/rules/style/explicit_block_argument.rs +53 -6
  31. data/src/rules/style/if_with_boolean_literal_branches.rs +7 -11
  32. data/src/rules/style/invertible_unless_condition.rs +20 -13
  33. data/src/rules/style/map_into_array.rs +10 -2
  34. data/src/rules/style/multiline_ternary_operator.rs +12 -0
  35. data/src/rules/style/negated_if.rs +10 -22
  36. data/src/rules/style/negated_if_else_condition.rs +10 -22
  37. data/src/rules/style/negated_unless.rs +3 -6
  38. data/src/rules/style/negated_while.rs +13 -21
  39. data/src/rules/style/partition_instead_of_double_select.rs +3 -10
  40. data/src/rules/style/redundant_condition.rs +71 -21
  41. data/src/rules/style/redundant_parentheses.rs +40 -4
  42. data/src/rules/style/redundant_return.rs +49 -39
  43. data/src/rules/style/single_argument_dig.rs +20 -10
  44. data/src/rules/style/tally_method.rs +10 -0
  45. data/src/rules/support.rs +53 -8
  46. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bbee17a19e72bf5b5dad03303c42be9905e5dfeef68faf8f1155b5f9910f3fac
4
- data.tar.gz: d756a00e368f91dcf8c3ca4d2e0e9df85c0315638d1aaa0d653e6cfbb4d68648
3
+ metadata.gz: a00a7d0dc817fde927c8f9a5eb24db975f85aa2f4dda36ae1f7de548aa6557f6
4
+ data.tar.gz: 2c09e86e186f680c8f31cf5fd0e979fb974bc23ad173fa14e4f100f98dc95ce3
5
5
  SHA512:
6
- metadata.gz: cdbfc78cb9c5f22948032627bd4dbcb6f28f9b044c69d2ee139cb16b598e928113e64d3d0267505d58160b000a9a66193456cd6943fb1f95b86f8c7d1699061d
7
- data.tar.gz: 6d7cf795e8e02782a81c4f16a9e2805cbd93dc88daf25d175944aefeb3b1a06af405b64c1c720129bf8945a8eee7873882968d5649efa43670d0029094e991ae
6
+ metadata.gz: 786c6eb994b2d5020f9f035623ec0f54ab95ad3f0359f747efd0bf97fd1bd4cca00d21b7a652b483861aef2af80dabb18dea4185bf9320a214307c4ea8780a8a
7
+ data.tar.gz: 8c0b93e14e6e7a56f497cc55aad95ce730c681515f9e2f8d67efcfe3c8ae44d98dc8f5ab1bf9c25b60f6de0791b0ed077bc3df0b4a679d3c07416a731426c66d
data/CONFORMANCE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # RuboCop conformance
2
2
 
3
- Snapshot date: 2026-08-14
3
+ Snapshot date: per row — see the `Measured` column of *Results*
4
4
  Reference: RuboCop 1.89.0 on Ruby 4.0.6
5
5
  Configuration: RuboCop 1.89.0 built-in defaults (`--force-default-config`) on both sides
6
6
 
@@ -30,7 +30,11 @@ sequence in UTF-8` on it — and `test/ruby/test_file_exhaustive.rb` holds an of
30
30
  not valid UTF-8, which stops the JSON formatter from emitting a document. The comparison for that
31
31
  corpus therefore covers 7,464 of its 7,466 files.
32
32
 
33
- The **target file lists match exactly** on all five — not just the counts but the paths. That is
33
+ The **target file lists match exactly** on all five — not just the counts but the paths, compared as
34
+ sets: rubocop/rubocop (1,765), rails/rails (3,551), mastodon/mastodon (3,290), Homebrew/brew (2,179)
35
+ and ruby/ruby (7,466), with nothing on either side of any of the five. Counts alone would not settle
36
+ it — a set that loses one file and gains another keeps its count — so the check compares the sorted
37
+ lists and reports what only one side holds. That is
34
38
  worth stating separately because file discovery is where a port silently diverges first: RuboCop
35
39
  never reads `.gitignore`, applies a shebang test only to extensionless files, descends through
36
40
  directory symlinks, and treats a hidden path by its *first* component rather than by any dot in it.
@@ -51,19 +55,47 @@ sonicop --force-default-config --format json
51
55
 
52
56
  ## Results
53
57
 
54
- | Corpus | Excess | Missing | Field differences |
55
- |---|---:|---:|---|
56
- | rubocop/rubocop | 0 | 0 | none |
57
- | rails/rails | 0 | 0 | none |
58
- | mastodon/mastodon | 0 | 0 | none |
59
- | Homebrew/brew | 263 | 997 | none |
60
- | ruby/ruby | 142 | 585 | 5 |
61
-
62
- Three of the five agree offense for offense RuboCop's own tree, Rails and Mastodon — with no
63
- excess, no shortfall and no metadata differences anywhere.
64
-
65
- Homebrew's remaining difference is entirely `Lint/Syntax`; every other cop matches exactly. Much of
66
- ruby/ruby's is too, with 117 of the 585 missing and 44 of the 142 excess being `Lint/Syntax` itself,
58
+ The two kinds of difference are counted separately because they mean different things. A
59
+ `Lint/Syntax` difference says the two disagree about whether a file parses, and everything else in
60
+ that file follows from it; a difference in any other cop says the port reads the same tree and
61
+ draws a different conclusion. Only the second is a defect in a cop.
62
+
63
+ | Corpus | Excess | Missing | of which `Lint/Syntax` | Other cops | Field differences | Measured |
64
+ |---|---:|---:|---|---:|---|---|
65
+ | rubocop/rubocop | 0 | 0 | — | 0 | correctable ×1 | 2026-08-17 |
66
+ | rails/rails | 0 | 0 || 0 | none | 2026-08-17 |
67
+ | mastodon/mastodon | 0 | 0 | — | 0 | none | 2026-08-17 |
68
+ | Homebrew/brew | 263 | 997 | **all of them** | **0** | none | 2026-08-17 |
69
+ | ruby/ruby | 142 | 585 | 117 missing, 44 excess | 92 | 5 | 2026-08-16 |
70
+
71
+ The last column is per row on purpose. A single date at the top of the file would say the five were
72
+ measured together, and they were not: the first four are re-measured on every release, ruby/ruby is
73
+ not — it is the one corpus whose reference cannot be produced in a single run (see above), so it is
74
+ re-measured deliberately rather than routinely. Its row is the older of the two and should be read
75
+ as such.
76
+
77
+ **No cop other than `Lint/Syntax` differs on four of the five corpora**, Homebrew included: across
78
+ its 2,179 files and 49,920 offenses the two agree on every position, message, severity and
79
+ correctability that is not a syntax diagnostic.
80
+
81
+ Homebrew's 1,260 differences — 997 missing and 263 excess — share one cause, and it is not a parser
82
+ bug on either side. Homebrew is a Ruby 4.0
83
+ codebase — `Library/Homebrew/.ruby-version` says `4.0.6` — but that file sits below the directory the
84
+ run starts from, and `TargetRubyVersion` is only inferred from the working directory's ancestors. Both
85
+ tools therefore fall back to the default of 2.7 (RuboCop says so itself: `Using Ruby 2.7 parser`) and
86
+ both call `dry_run:,` — a hash value omission, valid since 3.1 — a syntax error. **Run the same corpus
87
+ 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, and the first diagnostic in each file lands at the same
89
+ position. Only the second and later diagnostics diverge, because the two parsers recover from the
90
+ error differently — which is what puts offenses on both sides of the ledger rather than only on
91
+ RuboCop's. Those are not chased; see *Known divergences*.
92
+
93
+ What is checked here is that every one of the 1,260 is a `Lint/Syntax` offense, in both directions.
94
+ What is not checked is which recovery produced which diagnostic: the 263 Sonicop reports and RuboCop
95
+ does not have not been read one by one. They are accounted for as a class, not individually.
96
+
97
+ Much of ruby/ruby's difference is the same shape,
98
+ with 117 of the 585 missing and 44 of the 142 excess being `Lint/Syntax` itself,
67
99
  and the rest follows from it: a file the two disagree about is inspected by one tool and skipped by
68
100
  the other, so every offense in it lands on one side of the ledger. Counting that through, 635 of the
69
101
  727 differences sit in files one tool or the other calls a syntax error, and they are spread thinly
@@ -271,6 +303,14 @@ current list of what these corpora never exercise; the main ones are non-default
271
303
  values, extension plugins, and Windows line endings. Cops that never fire contribute nothing to a
272
304
  match count, and their silence is indistinguishable from agreement.
273
305
 
306
+ That limit is not hypothetical, and one instance is now measured. `Style/HashSyntax` defaults
307
+ `EnforcedShorthandSyntax` to `either`, under which neither tool reports anything; the port
308
+ implements none of the other four values, so half the cop is missing. Both the corpus runs and a
309
+ sweep of RuboCop's own specs report agreement for it, because both run at the default. A cop can be
310
+ half absent and still match everywhere the default reaches. Where a cop's behaviour is selected by
311
+ configuration, this document covers the default branch only — the others are neither measured nor
312
+ claimed.
313
+
274
314
  The 215 cops RuboCop ships switched off are a limit of a different kind. They are implemented, but a
275
315
  default run never reaches them, so the corpus numbers above say nothing about them. What stands
276
316
  behind those is a separate measurement: the same corpora linted with all 609 cops switched on, on
data/Cargo.lock CHANGED
@@ -742,7 +742,7 @@ checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
742
742
 
743
743
  [[package]]
744
744
  name = "sonicop"
745
- version = "26.8.108"
745
+ version = "26.8.110"
746
746
  dependencies = [
747
747
  "anyhow",
748
748
  "assert_cmd",
data/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sonicop"
3
- version = "26.8.108"
3
+ version = "26.8.110"
4
4
  edition = "2024"
5
5
  rust-version = "1.85"
6
6
  description = "A fast, native RuboCop-compatible Ruby linter and formatter"
data/NOTICE CHANGED
@@ -1,7 +1,153 @@
1
- Sonicop includes and adapts configuration data from RuboCop 1.89.0.
2
- RuboCop is Copyright (c) 2012-26 Bozhidar Batsov and contributors,
3
- licensed under the MIT License. See licenses/RUBOCOP.txt.
1
+ NOTICE
4
2
 
5
- Sonicop links to owayo/tree-sitter-ruby, derived from tree-sitter-ruby.
6
- tree-sitter-ruby is Copyright (c) 2016 Rob Rix and contributors,
7
- licensed under the MIT License. See licenses/TREE_SITTER_RUBY.txt.
3
+ sonicop is distributed under the MIT License (see LICENSE). It also carries
4
+ material generated from, or copied verbatim out of, the projects listed below.
5
+ Each is redistributed under its own licence, reproduced in full at the end of
6
+ this file.
7
+
8
+ This file is an inventory as much as an attribution: it records **what is taken,
9
+ from where, and how to regenerate it**. Keeping it current is what makes the
10
+ licence position checkable rather than remembered.
11
+
12
+ **Adding anything derived from another project means adding it here in the
13
+ same change.** This file was written only after a routine attempt to list what
14
+ we vendor turned up a corpus nobody had recorded -- the inventory found it,
15
+ not the attribution. An entry that is added late is an entry that was, for a
16
+ while, undocumented in a public repository.
17
+
18
+ Known to be coming: tests/fixtures/upstream_spec_cases.jsonl, which will hold
19
+ input text from RuboCop's specs. It is deliberately **not** listed yet -- this
20
+ file describes what is here now, not what is planned.
21
+
22
+ ================================================================================
23
+ WHAT IS TAKEN
24
+ ================================================================================
25
+
26
+ RuboCop -- https://github.com/rubocop/rubocop -- MIT
27
+ Copyright (c) 2012-26 Bozhidar Batsov
28
+
29
+ config/default.yml
30
+ The upstream configuration, taken verbatim at v1.89.0. It is the sole
31
+ source of truth for which cops exist and what their defaults are.
32
+ Regenerate: scripts/sync_default_yml.sh 1.89.0
33
+ Full text also kept at licenses/RUBOCOP.txt
34
+
35
+ Ruby on Rails -- https://github.com/rails/rails -- MIT
36
+ Copyright (c) David Heinemeier Hansson
37
+
38
+ Ruby -- https://github.com/ruby/ruby -- 2-clause BSDL
39
+ Copyright (C) 1993-2013 Yukihiro Matsumoto
40
+
41
+ Ruby is dual-licensed. Its COPYING offers a choice:
42
+
43
+ "You can redistribute it and/or modify it under either the terms of the
44
+ 2-clause BSDL (see the file BSDL), or the conditions below"
45
+
46
+ **sonicop takes the 2-clause BSDL**, reproduced below. This is written down
47
+ because the alternative -- saying nothing -- leaves the next reader to work
48
+ out why the longer Ruby License is absent.
49
+
50
+ Homebrew -- https://github.com/Homebrew/brew -- BSD 2-Clause
51
+ Copyright (c) 2009-present, Homebrew contributors
52
+
53
+ Rails, Ruby and Homebrew contribute to one file, together with RuboCop:
54
+
55
+ tests/fixtures/regexp_trees.jsonl
56
+ Every distinct regular-expression literal appearing in those projects'
57
+ Ruby sources, paired with the tree the `regexp_parser` gem builds for it.
58
+ It is the differential check for this port's own regexp scanner.
59
+ Corpora, commit hashes and the regeneration command are recorded in
60
+ tests/fixtures/regexp_trees.PROVENANCE.
61
+
62
+ Note: mastodon is **not** among the corpora. It is AGPL-3.0, and this
63
+ repository is MIT; the two are not obviously compatible, so it is left
64
+ out rather than reasoned about. Only permissively licensed corpora are
65
+ used, and dropping mastodon cost 218 of 10,966 patterns.
66
+
67
+ tree-sitter-ruby -- https://github.com/owayo/tree-sitter-ruby -- MIT
68
+ Copyright (c) 2016 Rob Rix and contributors
69
+
70
+ Linked as a dependency (see Cargo.toml). sonicop uses a fork; the grammar
71
+ itself is derived from the original. Full text: licenses/TREE_SITTER_RUBY.txt
72
+
73
+ ================================================================================
74
+ MIT License -- applies to RuboCop and to Ruby on Rails
75
+ ================================================================================
76
+
77
+ Copyright (c) 2012-26 Bozhidar Batsov
78
+ Copyright (c) David Heinemeier Hansson
79
+
80
+ Permission is hereby granted, free of charge, to any person obtaining
81
+ a copy of this software and associated documentation files (the
82
+ "Software"), to deal in the Software without restriction, including
83
+ without limitation the rights to use, copy, modify, merge, publish,
84
+ distribute, sublicense, and/or sell copies of the Software, and to
85
+ permit persons to whom the Software is furnished to do so, subject to
86
+ the following conditions:
87
+
88
+ The above copyright notice and this permission notice shall be
89
+ included in all copies or substantial portions of the Software.
90
+
91
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
92
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
93
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
94
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
95
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
96
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
97
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
98
+ ================================================================================
99
+ 2-clause BSDL -- applies to Ruby
100
+ ================================================================================
101
+
102
+ Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved.
103
+
104
+ Redistribution and use in source and binary forms, with or without
105
+ modification, are permitted provided that the following conditions
106
+ are met:
107
+ 1. Redistributions of source code must retain the above copyright
108
+ notice, this list of conditions and the following disclaimer.
109
+ 2. Redistributions in binary form must reproduce the above copyright
110
+ notice, this list of conditions and the following disclaimer in the
111
+ documentation and/or other materials provided with the distribution.
112
+
113
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
114
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
115
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
116
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
117
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
118
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
119
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
120
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
121
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
122
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
123
+ SUCH DAMAGE.
124
+
125
+ ================================================================================
126
+ BSD 2-Clause License -- applies to Homebrew
127
+ ================================================================================
128
+
129
+ BSD 2-Clause License
130
+
131
+ Copyright (c) 2009-present, Homebrew contributors
132
+ All rights reserved.
133
+
134
+ Redistribution and use in source and binary forms, with or without
135
+ modification, are permitted provided that the following conditions are met:
136
+
137
+ * Redistributions of source code must retain the above copyright notice, this
138
+ list of conditions and the following disclaimer.
139
+
140
+ * Redistributions in binary form must reproduce the above copyright notice,
141
+ this list of conditions and the following disclaimer in the documentation
142
+ and/or other materials provided with the distribution.
143
+
144
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
145
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
146
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
147
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
148
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
149
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
150
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
151
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
152
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
153
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.ja.md CHANGED
@@ -41,6 +41,10 @@ sonicop --show-cops
41
41
  ```
42
42
 
43
43
  **RuboCop 1.89 の全 609 Cop を実装しています。** 本家のレジストリと名前まで一致しており、
44
+ ただしこれは**レジストリについての主張**であって、すべての設定についての主張ではありません。
45
+ `Style/HashSyntax` は存在し既定の届く範囲では一致しますが、`EnforcedShorthandSyntax` の
46
+ 既定以外の 4 つの値はいずれも未実装です。**Cop は半分欠けたまま、既定の届く範囲では
47
+ 一致し続けられます** — [CONFORMANCE.md](CONFORMANCE.md) の *Limits* を参照してください。
44
48
  `Enabled: pending` の 159 個と `Enabled: false` の 56 個も含みます。この 215 個は本家でも
45
49
  既定の実行では走らないので、`--only` で名指しするか設定で有効にしてください。本家にも
46
50
  存在しない Cop 名だけをエラーにし、必要なら `--ignore-unrecognized-cops` で続行できます。
@@ -103,8 +107,26 @@ Style/StringLiterals:
103
107
  ```
104
108
 
105
109
  既存コマンドとの互換性を保つため、server/LSP/MCP、plugin、cache 系の引数も受理します。
106
- 未対応の機能を要求する引数は互換 no-op として stderr に明示します。現在、サーバートランスポート、
107
- Ruby プラグイン実行、キャッシュ再利用、カスタム Cop、実装済み以外の Cop は実行しません。
110
+ サーバートランスポート、Ruby プラグイン実行、キャッシュ再利用、カスタム Cop、実装済み以外の
111
+ Cop は実行しません。
112
+
113
+ そのうち大半は、その旨を出力します。`--server` / `--no-server` / `--lsp` / `--mcp` /
114
+ `--plugin` は stderr に 1 行の注記を出します。cache 系は何も出しませんが、
115
+ 無言でよいのは片方だけです。
116
+
117
+ - `--cache=false` はキャッシュを使わないことを求めるもので、sonicop はそれを満たしているため、
118
+ 無言が正しい応答です。
119
+ - `--cache=true` はキャッシュ再利用を求めるものですが、sonicop はこれを提供しておらず、
120
+ それでも無言です。
121
+
122
+ Cop の設定値も後者と同じ挙動です。sonicop が実装していない設定値 — たとえば
123
+ `Style/HashSyntax` の `EnforcedShorthandSyntax` — は**警告なしに無視されます**。
124
+ 名前を綴り間違えた設定値も同様に無視されます。つまり
125
+ **offense が 0 件であることは、その設定が効いた証拠にはなりません**。
126
+ 無視された設定値と、違反の無いファイルが、同じ出力になるためです。
127
+
128
+ Cop の*名前*は検査されます。設定ファイルに未知の Cop 名があれば、実行はエラーで止まります。
129
+ 素通りするのは、既知の Cop の中の設定値です。
108
130
 
109
131
  ### 適合性
110
132
 
@@ -114,8 +136,12 @@ Ruby プラグイン実行、キャッシュ再利用、カスタム Cop、実
114
136
 
115
137
  5 つのうち 3 つが**完全一致**です。RuboCop 自身のツリー(5,766 件)、Rails(167,760 件)、
116
138
  Mastodon(15,286 件)で、過剰も不足もメタデータ差もありません。対象ファイル一覧は 5 つすべてで
117
- 一致します。残る差分は `Lint/Syntax` に集中しており、本家の LALR パーサが構文エラーから回復して
118
- 出す追加診断を tree-sitter では再現できないことによるものです。autocorrect RuboCop 自身のツリーと
139
+ **件数だけでなくパスまで**一致します(集合として比較。どの側にも余りはありません)。
140
+ 残る差分は `Lint/Syntax` に集中しています。その大半は、本家の LALR パーサが
141
+ 構文エラーから回復して出す追加診断を tree-sitter では再現できないことによるもので、
142
+ **これが説明するのは不足の向きだけ**です。**過剰の向きは別の問題で、未調査です** —
143
+ Homebrew の 263 件はすべて `Lint/Syntax` であることまでは確認していますが、
144
+ 移植版だけが構文エラーと呼ぶその中身は調べていません。autocorrect は RuboCop 自身のツリーと
119
145
  Mastodon でバイト単位に一致します。この 2 つは死守ラインとして扱い、バイト一致が崩れた場合は
120
146
  既知差分ではなく退行として直します。
121
147
 
@@ -126,10 +152,15 @@ Mastodon でバイト単位に一致します。この 2 つは死守ライン
126
152
 
127
153
  適合性検証に使う 5 コーパスすべてで測定しました。両者とも同梱の既定設定
128
154
  (`--force-default-config`)で走らせているためプロジェクト側の `.rubocop.yml` は読まず、
129
- どのコーパスでも**対象ファイル一覧は完全に一致**しています。
155
+ どのコーパスでも**対象ファイル数は一致**しています。この計測に必要なのはそこまでで、
156
+ どちらの側も少なく検査してはいない、と言えます。パス単位の一致はより強い主張で、
157
+ 上の*適合性*の節で 5 つすべてについて示していますが、それは固定したリビジョンでの測定であって
158
+ この速度計測の run そのものではありません。
130
159
 
131
160
  両者とも既定の全 Cop で走らせています。**同じ 394 Cop** が名前まで一致しているため、
132
161
  どちらも絞る必要がなく、素の実行がそのまま対等な比較になります。
162
+ (394 は 609 から `Enabled: pending` の 159 個と `Enabled: false` の 56 個を除いた残りで、
163
+ 既定の実行はどちらの群にも届きません。)
133
164
  各値は暖機後 2 回の最速値です。
134
165
 
135
166
  | コーパス | ファイル | RuboCop 並列 | Sonicop 並列 | RuboCop 単一 | Sonicop 単一 |
@@ -147,8 +178,9 @@ Mastodon でバイト単位に一致します。この 2 つは死守ライン
147
178
  エンジンを測っていますが、並列はエンジンに加えて「その実行でスケジューリングがそのツリーに
148
179
  どれだけ噛み合ったか」を測っています。
149
180
 
150
- 仕事を省いて速いわけではありません。この同じ 394 Cop について、RuboCop 自身のツリーと
151
- Mastodon では**すべての offense が一致**し、autocorrect もバイト単位で一致します。
181
+ 仕事を省いて速いわけではありません。この同じ 394 Cop について、RuboCop 自身のツリー・
182
+ Rails・Mastodon 3 つで**すべての offense が一致**します(計 188,812 件、どちらの側にも
183
+ 残りません)。autocorrect は前者と後者でバイト単位に一致します。
152
184
 
153
185
  再現時に注意が必要な点が 2 つあります。RuboCop は **`--cache false` と併用すると
154
186
  `--parallel` を黙って無効化します**。そのためここでの並列実行はキャッシュを有効にしたうえで
data/README.md CHANGED
@@ -41,6 +41,10 @@ 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).
44
48
  That includes the 159 shipped as `Enabled: pending` and the 56 shipped as `Enabled: false`, which a
45
49
  default run does not reach on either side — name them with `--only` or switch them on in a
46
50
  configuration, exactly as with RuboCop. Unknown cop names still fail validation unless
@@ -111,9 +115,24 @@ Style/StringLiterals:
111
115
  ```
112
116
 
113
117
  The CLI accepts RuboCop's server/LSP/MCP, plugin, and cache flags to keep existing command lines
114
- parse-compatible. These flags are reported as compatibility no-ops when they request unsupported
115
- functionality. Sonicop does not currently provide server transports, Ruby plugin execution, cache
116
- reuse, custom Ruby cops, or cops outside the implemented set.
118
+ parse-compatible. Sonicop does not provide server transports, Ruby plugin execution, cache reuse,
119
+ custom Ruby cops, or cops outside the implemented set.
120
+
121
+ Most of those flags say so. `--server`, `--no-server`, `--lsp`, `--mcp`, and `--plugin` each print
122
+ a one-line notice on stderr. The cache flags print nothing, and only one of them is silent for a
123
+ good reason:
124
+
125
+ - `--cache=false` asks for no caching, which sonicop already satisfies, so silence is the correct
126
+ answer.
127
+ - `--cache=true` asks for cache reuse, which sonicop does not provide, and it is silent anyway.
128
+
129
+ Cop settings behave like the second case. A setting sonicop does not implement — for example
130
+ `EnforcedShorthandSyntax` under `Style/HashSyntax` — is ignored without any warning, and so is a
131
+ setting whose name is simply misspelled. **A run that reports no offenses is therefore not evidence
132
+ that a setting took effect**, because an ignored setting and a clean file produce the same output.
133
+
134
+ Cop *names* are checked: an unrecognised cop in a configuration file stops the run with an error.
135
+ It is the settings inside a recognised cop that pass unvalidated.
117
136
 
118
137
  ### Conformance
119
138
 
@@ -124,8 +143,12 @@ column, length, message, severity and correctability.
124
143
 
125
144
  Three of the five match **exactly**: RuboCop's own tree (5,766 offenses), Rails (167,760) and
126
145
  Mastodon (15,286), with no excess, no shortfall and no metadata differences. The target file lists
127
- match exactly on all five. What remains is concentrated in `Lint/Syntax`, where RuboCop's LALR
128
- parser recovers from an error and emits diagnostics a tree-sitter parse cannot reconstruct.
146
+ match exactly on all five — paths, not just counts, compared as sets. What remains is concentrated in
147
+ `Lint/Syntax`. Most of it is RuboCop's
148
+ LALR parser recovering from an error and emitting diagnostics a tree-sitter parse cannot
149
+ reconstruct — that direction accounts for the shortfall. **The excess is a separate question and has
150
+ not been investigated**; on Homebrew all 263 of it is `Lint/Syntax` too, but what Sonicop calls a
151
+ syntax error there and RuboCop does not is unexamined.
129
152
  Autocorrect is byte-identical on RuboCop's own tree and on Mastodon, the two corpora held as a hard
130
153
  line: a change that breaks byte equality there is a regression, not a new known divergence.
131
154
 
@@ -136,10 +159,14 @@ measured at, and the two ways a measurement of this kind can mislead you.
136
159
 
137
160
  Measured over all five conformance corpora. Both tools were given their own bundled default
138
161
  configuration (`--force-default-config`), so neither reads the project's `.rubocop.yml`, and on every
139
- corpus the two resolve **the same file list**.
162
+ corpus the two resolve **the same number of files** — which is what these timings need, since it
163
+ means neither side is inspecting less. Path-by-path equality is a stronger claim, established under
164
+ *Conformance* above for all five, but on the pinned corpus revisions rather than on these timing runs.
140
165
 
141
166
  Both tools run their full default set — **the same 394 cops**, matched name for name — so neither
142
- side is restricted and the comparison is like-for-like as it stands. Times are the fastest of two
167
+ side is restricted and the comparison is like-for-like as it stands. (394 is what is left of the 609
168
+ once the 159 RuboCop ships as `Enabled: pending` and the 56 it ships as `Enabled: false` are set
169
+ aside; a default run reaches neither group on either side.) Times are the fastest of two
143
170
  warmed runs.
144
171
 
145
172
  | Corpus | Files | RuboCop parallel | Sonicop parallel | RuboCop single | Sonicop single |
@@ -158,7 +185,8 @@ else the machine was doing. Single-process measures the engines; parallel measur
158
185
  how well each one's scheduling happens to fit that tree on that run.
159
186
 
160
187
  The speed is not bought by skipping work: over those same 394 cops the two agree on **every offense**
161
- on RuboCop's own tree and on Mastodon, and autocorrect there is byte-identical.
188
+ on RuboCop's own tree, on Rails and on Mastodon — 188,812 offenses with nothing on either side of the
189
+ ledger — and autocorrect is byte-identical on the first and the last.
162
190
 
163
191
  Two details matter for reproducing this. RuboCop **silently turns `--parallel` off when combined
164
192
  with `--cache false`**, so its parallel runs here use a cache directory that is deleted before each
@@ -2,5 +2,5 @@
2
2
 
3
3
  # Generated from Cargo.toml by `rake version:sync`. Do not edit by hand.
4
4
  module Sonicop
5
- VERSION = '26.8.108'
5
+ VERSION = '26.8.110'
6
6
  end
data/src/config/loader.rs CHANGED
@@ -11,7 +11,17 @@ pub(super) fn find_config(start: &Path) -> Option<PathBuf> {
11
11
  if candidate.is_file() {
12
12
  return fs::canonicalize(candidate).ok();
13
13
  }
14
- if project_root.as_deref().is_none_or(|root| directory == root) {
14
+ // `break if dir == stop_dir || dir == FileFinder.root_level`
15
+ //
16
+ // **`stop_dir` が無いことは「止まらない」であって「止まる」ではない。**
17
+ // 上流の `find_project_dotfile` は `find_file_upwards(DOTFILE, target_dir, project_root)`
18
+ // で、`project_root` が nil のときは何とも一致しないので**ファイルシステムの根まで
19
+ // 昇る**。ここを `is_none_or` で書いていたため、Gemfile の無い木では最初の 1 段で
20
+ // 止まり、**`.rubocop.yml` がリポジトリ直下・コードが `lib/` という標準の配置で
21
+ // 設定が 1 つも効かなかった。**
22
+ //
23
+ // `ancestors()` は根で終わるので、`root_level` の側は自然に満たされる。
24
+ if project_root.as_deref() == Some(directory) {
15
25
  break;
16
26
  }
17
27
  }