rigortype 0.3.0 → 0.3.2
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/README.md +10 -10
- data/data/builtins/ruby_core/array.yml +416 -392
- data/data/builtins/ruby_core/file.yml +42 -42
- data/data/builtins/ruby_core/hash.yml +302 -302
- data/data/builtins/ruby_core/io.yml +191 -191
- data/data/builtins/ruby_core/numeric.yml +321 -366
- data/data/builtins/ruby_core/proc.yml +124 -124
- data/data/builtins/ruby_core/range.yml +21 -21
- data/data/builtins/ruby_core/rational.yml +39 -39
- data/data/builtins/ruby_core/re.yml +65 -65
- data/data/builtins/ruby_core/set.yml +106 -106
- data/data/builtins/ruby_core/struct.yml +14 -14
- data/data/core_overlay/pathname.rbs +5 -0
- data/data/core_overlay/resolv.rbs +31 -0
- data/data/core_overlay/string_scanner.rbs +11 -5
- data/data/gem_overlay/activesupport/core_ext.rbs +10 -6
- data/data/vendored_gem_sigs/bundler/bundler.rbs +39 -27
- data/data/vendored_gem_sigs/cgi/cgi_extras.rbs +6 -0
- data/data/vendored_gem_sigs/prism/prism_supplement.rbs +7 -0
- data/data/vendored_gem_sigs/racc/racc.rbs +67 -0
- data/data/vendored_gem_sigs/rubygems/rubygems_extras.rbs +20 -9
- data/docs/handbook/01-getting-started.md +22 -34
- data/docs/handbook/06-classes.md +1 -1
- data/docs/handbook/07-rbs-and-extended.md +76 -101
- data/docs/handbook/08-understanding-errors.md +114 -247
- data/docs/handbook/09-plugins.md +54 -144
- data/docs/handbook/README.md +5 -3
- data/docs/handbook/appendix-liskov.md +4 -2
- data/docs/handbook/appendix-phpstan.md +14 -7
- data/docs/handbook/appendix-steep.md +4 -2
- data/docs/handbook/appendix-type-theory.md +3 -1
- data/docs/manual/02-cli-reference.md +103 -11
- data/docs/manual/03-configuration.md +16 -1
- data/docs/manual/04-diagnostics.md +36 -4
- data/docs/manual/06-baseline.md +35 -1
- data/docs/manual/08-skills.md +6 -1
- data/docs/manual/09-editor-integration.md +3 -2
- data/docs/manual/12-caching.md +17 -5
- data/docs/manual/15-type-protection-coverage.md +122 -1
- data/docs/manual/plugins/rigor-actioncable.md +32 -0
- data/docs/manual/plugins/rigor-devise.md +4 -2
- data/docs/manual/plugins/rigor-rbs-inline.md +22 -0
- data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -0
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +21 -2
- data/lib/rigor/analysis/check_rules.rb +79 -19
- data/lib/rigor/analysis/incremental_session.rb +156 -11
- data/lib/rigor/analysis/plugin_fact_fingerprint.rb +14 -0
- data/lib/rigor/analysis/run_cache_key.rb +27 -1
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +201 -0
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +28 -10
- data/lib/rigor/analysis/runner/pool_coordinator.rb +7 -10
- data/lib/rigor/analysis/runner/project_pre_passes.rb +3 -1
- data/lib/rigor/analysis/runner.rb +80 -8
- data/lib/rigor/bleeding_edge.rb +132 -7
- data/lib/rigor/cache/descriptor.rb +6 -1
- data/lib/rigor/cache/engine_source.rb +162 -0
- data/lib/rigor/cache/incremental_snapshot.rb +87 -18
- data/lib/rigor/cache/rbs_cache_producer.rb +11 -1
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +38 -0
- data/lib/rigor/cache/store.rb +99 -24
- data/lib/rigor/cli/check_command.rb +61 -34
- data/lib/rigor/cli/check_invocation.rb +84 -0
- data/lib/rigor/cli/coverage_command.rb +15 -2
- data/lib/rigor/cli/coverage_mutation.rb +242 -11
- data/lib/rigor/cli/doctor_command.rb +6 -8
- data/lib/rigor/cli/fused_protection_renderer.rb +10 -0
- data/lib/rigor/cli/fused_protection_report.rb +12 -3
- data/lib/rigor/cli/mutation_fork_scan.rb +64 -0
- data/lib/rigor/cli/mutation_protection_renderer.rb +12 -0
- data/lib/rigor/cli/mutation_protection_report.rb +26 -5
- data/lib/rigor/cli/protection_renderer.rb +13 -0
- data/lib/rigor/cli/protection_report.rb +11 -3
- data/lib/rigor/cli/show_bleedingedge_command.rb +17 -4
- data/lib/rigor/cli/sig_gen_command.rb +5 -4
- data/lib/rigor/cli/skill_command.rb +21 -1
- data/lib/rigor/cli/skill_deep_probe.rb +172 -0
- data/lib/rigor/cli/skill_describe.rb +75 -9
- data/lib/rigor/configuration.rb +45 -6
- data/lib/rigor/environment/bundle_sig_discovery.rb +2 -1
- data/lib/rigor/environment/default_libraries.rb +5 -4
- data/lib/rigor/environment/rbs_coverage_report.rb +1 -1
- data/lib/rigor/environment/rbs_loader.rb +434 -75
- data/lib/rigor/environment.rb +38 -14
- data/lib/rigor/inference/expression_typer.rb +15 -0
- data/lib/rigor/inference/fork_map.rb +9 -0
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +50 -2
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/member_shape_projection.rb +35 -1
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +6 -1
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +16 -0
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +64 -5
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +137 -9
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +3 -2
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +124 -1
- data/lib/rigor/inference/mutation_widening.rb +30 -25
- data/lib/rigor/inference/narrowing.rb +3 -1
- data/lib/rigor/inference/optimistic_origin.rb +58 -0
- data/lib/rigor/inference/protection_scanner.rb +18 -2
- data/lib/rigor/inference/receiver_alias.rb +57 -0
- data/lib/rigor/inference/scope_indexer.rb +78 -27
- data/lib/rigor/inference/statement_evaluator.rb +40 -2
- data/lib/rigor/language_server/buffer_resolution.rb +6 -3
- data/lib/rigor/language_server/buffer_table.rb +68 -6
- data/lib/rigor/language_server/diagnostic_publisher.rb +207 -4
- data/lib/rigor/language_server/incremental_sync.rb +159 -0
- data/lib/rigor/language_server/project_context.rb +45 -0
- data/lib/rigor/language_server/publish_batcher.rb +82 -0
- data/lib/rigor/language_server/server.rb +38 -9
- data/lib/rigor/language_server.rb +2 -0
- data/lib/rigor/plugin/base.rb +29 -2
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +9 -3
- data/lib/rigor/protection/closure_kill_oracle.rb +171 -0
- data/lib/rigor/protection/dependency_closure.rb +59 -0
- data/lib/rigor/protection/diagnostic_oracle.rb +12 -8
- data/lib/rigor/protection/discovery_seed.rb +116 -0
- data/lib/rigor/protection/kill_signature.rb +31 -0
- data/lib/rigor/protection/mutation_cache.rb +355 -0
- data/lib/rigor/protection/mutation_scanner.rb +55 -11
- data/lib/rigor/protection/mutator.rb +26 -4
- data/lib/rigor/runtime/jit.rb +63 -1
- data/lib/rigor/scope/discovery_index.rb +9 -0
- data/lib/rigor/scope.rb +52 -8
- data/lib/rigor/sig_gen/generator.rb +130 -31
- data/lib/rigor/sig_gen/layout_index.rb +6 -0
- data/lib/rigor/sig_gen/meta_class_shape.rb +84 -0
- data/lib/rigor/sig_gen/renderer.rb +20 -1
- data/lib/rigor/sig_gen/write_result.rb +6 -4
- data/lib/rigor/sig_gen/writer.rb +195 -47
- data/lib/rigor/sig_gen.rb +1 -0
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +51 -1
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +6 -6
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/result_shape.rb +146 -0
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +177 -12
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +74 -7
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +257 -0
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/params_shape.rb +69 -0
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +87 -11
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +34 -4
- data/sig/rigor/cache.rbs +6 -0
- data/sig/rigor/inference/void_origin.rbs +21 -0
- data/sig/rigor/plugin/base.rbs +4 -3
- data/sig/rigor/scope.rbs +8 -0
- metadata +23 -3
data/docs/handbook/09-plugins.md
CHANGED
|
@@ -9,9 +9,10 @@ It does **not** teach plugin *authoring*. That lives in
|
|
|
9
9
|
[`examples/`](../../examples/README.md) — six tutorial
|
|
10
10
|
walkthroughs, each spotlighting one extension surface.
|
|
11
11
|
Ready-to-install gems for real frameworks live in
|
|
12
|
-
[`plugins/`](../../plugins/README.md)
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
[`plugins/`](../../plugins/README.md), and activating one is
|
|
13
|
+
[manual — Using plugins](../manual/07-plugins.md). Read on to
|
|
14
|
+
decide whether you need a plugin; go to `examples/` once you
|
|
15
|
+
want to write one.
|
|
15
16
|
|
|
16
17
|
## When you reach for a plugin
|
|
17
18
|
|
|
@@ -51,151 +52,60 @@ compares the six worked examples on architectural axes
|
|
|
51
52
|
engine-collaboration via `Scope#type_of`, cross-plugin facts,
|
|
52
53
|
return-type contributions, …) and recommends a reading order.
|
|
53
54
|
|
|
54
|
-
##
|
|
55
|
+
## Two authoring paths
|
|
55
56
|
|
|
56
57
|
> Still here? Most readers should jump to
|
|
57
58
|
> [Should you write one?](#should-you-write-one) first — the
|
|
58
59
|
> answer is usually "no, RBS and `RBS::Extended` get you
|
|
59
|
-
> there."
|
|
60
|
-
|
|
61
|
-
The
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
which
|
|
107
|
-
|
|
108
|
-
## Macro / DSL expansion substrate (ADR-16)
|
|
109
|
-
|
|
110
|
-
A second authoring path was added on top of the hand-rolled
|
|
111
|
-
walker contract above: the **macro expansion substrate**
|
|
112
|
-
(ADR-16). For metaprogramming-heavy DSLs — Rails-style
|
|
113
|
-
`has_one_attached`, dry-struct's `attribute`, Devise's
|
|
114
|
-
`devise :strategy`, Sinatra's `get '/foo' do ... end` — the
|
|
115
|
-
substrate lets a plugin author **declare** the call shape
|
|
116
|
-
instead of walking the AST by hand. The plugin's body becomes
|
|
117
|
-
a single manifest entry; the substrate handles literal-symbol
|
|
118
|
-
extraction, name interpolation, registry lookup, and per-method
|
|
119
|
-
synthesis.
|
|
120
|
-
|
|
121
|
-
Four tier shapes are recognised. The
|
|
122
|
-
[per-library survey](../notes/20260515-macro-expansion-library-survey.md)
|
|
123
|
-
identifies which libraries fit each tier and which fall
|
|
124
|
-
outside the substrate's scope.
|
|
125
|
-
|
|
126
|
-
| Tier | Shape | Manifest declaration | Worked example |
|
|
127
|
-
| --- | --- | --- | --- |
|
|
128
|
-
| **A — block-as-method** | DSL call's block runs as an instance method on the receiver class (`Sinatra::Base#generate_method`) | `block_as_methods: [Macro::BlockAsMethod.new(receiver_constraint:, method_names:)]` | [`rigor-sinatra`](../../plugins/rigor-sinatra/) |
|
|
129
|
-
| **B — trait-inlining registry** | Class-level call enumerates symbols → bundled registry maps each to a module → substrate explodes the module's RBS methods onto the calling class | `trait_registries: [Macro::TraitRegistry.new(receiver_constraint:, method_name:, modules_by_symbol:, always_included:)]` | [`rigor-devise`](../../plugins/rigor-devise/) |
|
|
130
|
-
| **C — heredoc template** | Class-level call interpolates a literal symbol into a method-name template; substrate emits synthetic readers | `heredoc_templates: [Macro::HeredocTemplate.new(receiver_constraint:, method_name:, symbol_arg_position:, emit:)]` | [`rigor-dry-struct`](../../plugins/rigor-dry-struct/) |
|
|
131
|
-
|
|
132
|
-
The three Tier-A/B/C plugins above are each ~60–110 LoC of
|
|
133
|
-
**purely declarative** Ruby — no walker, no
|
|
134
|
-
`diagnostics_for_file`, no plugin-side state. The substrate's
|
|
135
|
-
pre-pass + dispatcher integration do the work.
|
|
136
|
-
|
|
137
|
-
### Concern re-targeting
|
|
138
|
-
|
|
139
|
-
`ActiveSupport::Concern.included do ... end` is a *deferred
|
|
140
|
-
class_eval*: any DSL calls inside the block fire on whoever
|
|
141
|
-
includes the concern, not on the concern module itself. The
|
|
142
|
-
substrate's scanner handles this re-targeting automatically.
|
|
143
|
-
For source like:
|
|
144
|
-
|
|
145
|
-
```ruby
|
|
146
|
-
module Auditable
|
|
147
|
-
extend ActiveSupport::Concern
|
|
148
|
-
included do
|
|
149
|
-
attribute :audited_at, Types::Time
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
class Address < Dry::Struct
|
|
154
|
-
include Auditable
|
|
155
|
-
attribute :city, Types::String
|
|
156
|
-
end
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
`Address` gets BOTH `city` (direct) AND `audited_at` (re-targeted
|
|
160
|
-
from `Auditable`) as synthetic readers. The same pattern works
|
|
161
|
-
for Tier B traits (Devise modules included via Concerns).
|
|
162
|
-
|
|
163
|
-
### Floor / ceiling
|
|
164
|
-
|
|
165
|
-
Per ADR-16 § WD13, the **floor** is that synthetic methods emit
|
|
166
|
-
by NAME so cross-file dispatch resolves (no more
|
|
167
|
-
`call.undefined-method`). The common cases also recover precise
|
|
168
|
-
return types: **Tier B** redispatches on the origin module's
|
|
169
|
-
authored RBS (a Devise `valid_password?` resolves to `bool`,
|
|
170
|
-
not `Dynamic[T]`), and **Tier C** resolves a plain class-name
|
|
171
|
-
return to its `Nominal`. What still degrades to `Dynamic[T]` is
|
|
172
|
-
the parameterised / utility-type-shaped Tier C return
|
|
173
|
-
(`Array[String]`, `Pick<T, K>`); routing those through the
|
|
174
|
-
[ADR-13](../adr/13-typenode-resolver-plugin.md) resolver chain
|
|
175
|
-
is the **ceiling**, demand-driven. The substrate never
|
|
176
|
-
*fabricates* precision per ADR-5 robustness.
|
|
177
|
-
|
|
178
|
-
### Choosing between the substrate and a hand-rolled walker
|
|
179
|
-
|
|
180
|
-
| If the DSL is… | Use the substrate | Use a hand-rolled walker |
|
|
181
|
-
| --- | --- | --- |
|
|
182
|
-
| `class-level call with literal symbol args + framework class_eval'd heredoc` | ✓ Tier C | — |
|
|
183
|
-
| `class-level call with literal symbol args + registry-driven module include` | ✓ Tier B | — |
|
|
184
|
-
| `class-level call with do…end block running as an instance method` | ✓ Tier A | — |
|
|
185
|
-
| `external Ruby files instance_eval'd under a declared self` | ✓ Tier D (contract only as of v0.1.x) | — |
|
|
186
|
-
| `domain DSL whose return type depends on argument shape` | — | `dynamic_return` ([`rigor-lisp-eval`](../../examples/rigor-lisp-eval/)) |
|
|
187
|
-
| `cross-file validation (collect declarations, then validate uses)` | — | Two-pass walker ([`rigor-statesman`](../../plugins/rigor-statesman/)) |
|
|
188
|
-
| `parsing an external project file (routes, schema, locale)` | — | `IoBoundary` + cache producer ([`rigor-routes`](../../examples/rigor-routes/)) |
|
|
189
|
-
| `schema-graph recorder (GraphQL-Ruby-style)` | — | Schema-resolution pass (no plugin authored yet) |
|
|
190
|
-
|
|
191
|
-
The substrate and the hand-rolled walker contract coexist —
|
|
192
|
-
a plugin can mix `manifest`-declared substrate entries with a
|
|
193
|
-
`diagnostics_for_file` walker. The
|
|
194
|
-
[`skills/rigor-plugin-author/SKILL.md`](../../skills/rigor-plugin-author/SKILL.md)
|
|
195
|
-
SKILL captures the decision flow in detail; the survey at
|
|
196
|
-
[`docs/notes/20260515-macro-expansion-library-survey.md`](../notes/20260515-macro-expansion-library-survey.md)
|
|
197
|
-
records which Ruby libraries the substrate covers and which
|
|
198
|
-
fall outside.
|
|
60
|
+
> there." What follows is for when it is "yes."
|
|
61
|
+
|
|
62
|
+
The decision that shapes everything else is *which* of the two
|
|
63
|
+
authoring paths your DSL falls into.
|
|
64
|
+
|
|
65
|
+
**Declare it.** If the DSL is a class-level call with literal
|
|
66
|
+
symbol arguments — a Rails-style `has_one_attached`, a
|
|
67
|
+
dry-struct `attribute`, a Devise `devise :strategy`, a Sinatra
|
|
68
|
+
`get "/foo" do … end` — the **macro-expansion substrate**
|
|
69
|
+
([ADR-16](../adr/16-macro-expansion.md)) already knows that
|
|
70
|
+
shape. You write a manifest entry describing the call, and the
|
|
71
|
+
substrate does the literal-symbol extraction, the name
|
|
72
|
+
interpolation and the per-method synthesis. The three bundled
|
|
73
|
+
plugins on this path are 60–110 lines of declarative Ruby with
|
|
74
|
+
no AST walking at all. The substrate also understands
|
|
75
|
+
`ActiveSupport::Concern`'s deferred `included do … end` block,
|
|
76
|
+
so a DSL call written inside a concern lands on the class that
|
|
77
|
+
includes it rather than on the concern.
|
|
78
|
+
|
|
79
|
+
**Walk it.** If the type depends on something the shape of the
|
|
80
|
+
call cannot tell you — argument *values* (`Lisp.eval` above), a
|
|
81
|
+
declaration made elsewhere in the project, or the contents of
|
|
82
|
+
an external file such as a route table or a schema dump — you
|
|
83
|
+
write a walker instead, and the plugin contract gives you the
|
|
84
|
+
hooks for it: a per-file emission pass, per-call-site
|
|
85
|
+
return-type and flow-narrowing contributions, sandboxed file
|
|
86
|
+
and HTTPS reads under a trust policy, cached producers for
|
|
87
|
+
expensive parses, and a cross-plugin fact store so one plugin's
|
|
88
|
+
parse feeds another plugin's checks.
|
|
89
|
+
|
|
90
|
+
The two paths coexist — one plugin can declare substrate
|
|
91
|
+
entries *and* walk files — and where you go next depends on
|
|
92
|
+
which of them you need:
|
|
93
|
+
|
|
94
|
+
- [`examples/README.md`](../../examples/README.md) — the six
|
|
95
|
+
walkthroughs, each spotlighting one contract surface, with a
|
|
96
|
+
map of which example demonstrates which one.
|
|
97
|
+
- [`docs/internal-spec/plugin.md`](../internal-spec/plugin.md)
|
|
98
|
+
— the binding plugin contract: manifest, hooks, services,
|
|
99
|
+
registry, load order. Its siblings
|
|
100
|
+
[`plugin-trust.md`](../internal-spec/plugin-trust.md) and
|
|
101
|
+
[`plugin-cache-producers.md`](../internal-spec/plugin-cache-producers.md)
|
|
102
|
+
cover the I/O and caching surfaces.
|
|
103
|
+
- [`docs/internal-spec/macro-substrate.md`](../internal-spec/macro-substrate.md)
|
|
104
|
+
— the substrate's tiers, the manifest field each one
|
|
105
|
+
declares, and how much return-type precision each recovers.
|
|
106
|
+
- [The macro-expansion library survey](../notes/20260515-macro-expansion-library-survey.md)
|
|
107
|
+
— which real Ruby libraries fit which tier, and which fall
|
|
108
|
+
outside the substrate entirely.
|
|
199
109
|
|
|
200
110
|
## Should you write one?
|
|
201
111
|
|
data/docs/handbook/README.md
CHANGED
|
@@ -56,9 +56,11 @@ up the flag, key, or command that *acts* on it.
|
|
|
56
56
|
how to nudge it through `.rbs` files and `%a{rigor:v1:…}`
|
|
57
57
|
directives.
|
|
58
58
|
8. [**Understanding errors**](08-understanding-errors.md) —
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
reading a diagnostic: what each rule family claims, why one
|
|
60
|
+
fires when you did not expect it (and stays silent when you
|
|
61
|
+
did), and which layer to reach for when you want it quieter.
|
|
62
|
+
The catalogue itself is
|
|
63
|
+
[manual — Diagnostics](../manual/04-diagnostics.md).
|
|
62
64
|
9. [**Plugins**](09-plugins.md) — when to author one,
|
|
63
65
|
pointer to the [examples/](../../examples/README.md)
|
|
64
66
|
landing page.
|
|
@@ -571,8 +571,10 @@ model"](appendix-type-theory.md#what-rigor-does-not-model) records.
|
|
|
571
571
|
authoring the declared contracts `def.return-type-mismatch` checks
|
|
572
572
|
against.
|
|
573
573
|
- [Chapter 8 — Understanding errors](08-understanding-errors.md) for
|
|
574
|
-
the `def
|
|
575
|
-
|
|
574
|
+
what the `def.*` family claims, and
|
|
575
|
+
[manual — Diagnostics](../manual/04-diagnostics.md) for the
|
|
576
|
+
`def.return-type-mismatch` / `call.argument-type-mismatch`
|
|
577
|
+
entries and the severity profiles.
|
|
576
578
|
|
|
577
579
|
If you want to compare against another *tool* rather than the
|
|
578
580
|
*principle*, the sibling appendices cover
|
|
@@ -72,8 +72,10 @@ in another tab, almost every advanced refinement transfers.
|
|
|
72
72
|
## The `@phpstan-assert` family
|
|
73
73
|
|
|
74
74
|
PHPStan's assertion-narrowing PHPDoc tags map directly onto
|
|
75
|
-
Rigor's `RBS::Extended` directive grammar.
|
|
76
|
-
|
|
75
|
+
Rigor's `RBS::Extended` directive grammar. This is the mapping;
|
|
76
|
+
[Chapter 7](07-rbs-and-extended.md) works the Rigor side
|
|
77
|
+
through examples, and the directive reference itself is
|
|
78
|
+
[manual — RBS::Extended annotations](../manual/16-rbs-extended-annotations.md):
|
|
77
79
|
|
|
78
80
|
| PHPStan PHPDoc | Rigor RBS::Extended | Effect |
|
|
79
81
|
| --- | --- | --- |
|
|
@@ -149,7 +151,8 @@ Rigor has two baseline mechanisms: a **managed** baseline
|
|
|
149
151
|
the next `rigor check` via the `baseline:` config key — the
|
|
150
152
|
closest match to PHPStan's `--baseline`), and a **lightweight**
|
|
151
153
|
ad-hoc snapshot (`rigor diff` over a `--format=json` dump).
|
|
152
|
-
|
|
154
|
+
[Manual — Baselines](../manual/06-baseline.md) walks through
|
|
155
|
+
both.
|
|
153
156
|
|
|
154
157
|
The `includes:` semantics also match PHPStan's: declaration
|
|
155
158
|
order, later overrides earlier, the current file's keys win
|
|
@@ -322,11 +325,15 @@ You probably do not need to read the rest of this appendix
|
|
|
322
325
|
section sequentially. Three useful pointers:
|
|
323
326
|
|
|
324
327
|
- [Chapter 7 — RBS and `RBS::Extended`](07-rbs-and-extended.md)
|
|
325
|
-
|
|
326
|
-
table
|
|
328
|
+
works the directive grammar through examples; the reference
|
|
329
|
+
table is
|
|
330
|
+
[manual — RBS::Extended annotations](../manual/16-rbs-extended-annotations.md).
|
|
327
331
|
- [Chapter 8 — Understanding errors](08-understanding-errors.md)
|
|
328
|
-
|
|
329
|
-
|
|
332
|
+
explains how to read a diagnostic and which knob to reach
|
|
333
|
+
for; the catalogue, severity profiles and baselines — every
|
|
334
|
+
PHPStan onboarding analogue — are
|
|
335
|
+
[manual — Diagnostics](../manual/04-diagnostics.md) and
|
|
336
|
+
[manual — Baselines](../manual/06-baseline.md).
|
|
330
337
|
- [Chapter 9 — Plugins](09-plugins.md) for the
|
|
331
338
|
Type-Specifying / Dynamic-Return analogues.
|
|
332
339
|
|
|
@@ -321,8 +321,10 @@ section sequentially. Three useful pointers:
|
|
|
321
321
|
if you want to see how the directive grammar layers on top
|
|
322
322
|
of the RBS you already write.
|
|
323
323
|
- [Chapter 8 — Understanding errors](08-understanding-errors.md)
|
|
324
|
-
for
|
|
325
|
-
|
|
324
|
+
for how to read a diagnostic, and
|
|
325
|
+
[manual — Diagnostics](../manual/04-diagnostics.md) for the
|
|
326
|
+
rule catalogue and severity profiles — the analogue to
|
|
327
|
+
Steep's diagnostic config.
|
|
326
328
|
- [`docs/notes/20260503-steep-cross-check-triage.md`](../notes/20260503-steep-cross-check-triage.md)
|
|
327
329
|
for a worked side-by-side run of Steep and Rigor on the
|
|
328
330
|
same project (the project itself).
|
|
@@ -1651,7 +1651,9 @@ practical companion:
|
|
|
1651
1651
|
for the directive grammar that lets you teach Rigor about a
|
|
1652
1652
|
custom predicate.
|
|
1653
1653
|
- [Chapter 8 — Understanding errors](08-understanding-errors.md)
|
|
1654
|
-
for
|
|
1654
|
+
for how a diagnostic reads, and
|
|
1655
|
+
[manual — Diagnostics](../manual/04-diagnostics.md) for the
|
|
1656
|
+
rule catalogue (the user-visible end of the trinary
|
|
1655
1657
|
certainty).
|
|
1656
1658
|
|
|
1657
1659
|
If you want to compare against another *tool* rather than the
|
|
@@ -48,11 +48,38 @@ the `paths:` list from the configuration file.
|
|
|
48
48
|
| `--treat-all-as-inline-rbs` | Force-load `rigor-rbs-inline` with `require_magic_comment: false`, so every analysed file is treated as inline-RBS without the `# rbs_inline: enabled` comment (ADR-32). |
|
|
49
49
|
| `--bleeding-edge[=ids]` | Adopt the bleeding-edge overlay for this run, overriding the configured [`bleeding_edge:`](03-configuration.md) selection (ADR-50 § WD2). Bare adopts every queued feature; `--bleeding-edge=a,b` adopts only the named feature ids. Inspect it with [`rigor show-bleedingedge`](#rigor-show-bleedingedge). |
|
|
50
50
|
| `--no-bleeding-edge` | Ignore any configured `bleeding_edge:` selection for this run (adopt none). |
|
|
51
|
-
| `--tmp-file=PATH --instead-of=PATH` | Editor mode: analyse `PATH` using the buffer in `--tmp-file`. Both required together. |
|
|
51
|
+
| `--tmp-file=PATH --instead-of=PATH` | Editor mode: analyse `PATH` using the buffer in `--tmp-file`. Both required together. Alone, only the buffer's own file produces diagnostics; add `--incremental` for whole-project scope (see below). |
|
|
52
52
|
|
|
53
53
|
Exit `0` when no error-severity diagnostics remain, `1` when
|
|
54
54
|
any are reported, `64` on a usage error.
|
|
55
55
|
|
|
56
|
+
### Editor mode scope
|
|
57
|
+
|
|
58
|
+
`--tmp-file` / `--instead-of` on their own analyse **only** the
|
|
59
|
+
buffer's file: fast, and blind to what the unsaved edit does to the
|
|
60
|
+
rest of the project.
|
|
61
|
+
|
|
62
|
+
Adding `--incremental` analyses the **whole project with the buffer
|
|
63
|
+
substituted** — the edited file and the files that depend on it are
|
|
64
|
+
re-analysed, everything else is served from the incremental snapshot.
|
|
65
|
+
An unsaved change to a method's return type therefore surfaces in its
|
|
66
|
+
callers, not just in the file being edited.
|
|
67
|
+
|
|
68
|
+
Two things to know:
|
|
69
|
+
|
|
70
|
+
- It needs a snapshot to reuse. Run `rigor check --incremental` once
|
|
71
|
+
(it is also what a normal terminal check should use) and every later
|
|
72
|
+
editor invocation gets whole-project scope. Without one, Rigor says
|
|
73
|
+
so on stderr and analyses the buffer alone.
|
|
74
|
+
- An editor-mode run never writes the snapshot — the buffer's bytes
|
|
75
|
+
exist only in your editor, and recording them would make the next
|
|
76
|
+
ordinary run believe it had already analysed a file in a state it
|
|
77
|
+
was never in.
|
|
78
|
+
|
|
79
|
+
`--verify-incremental` refuses a buffer: it compares against a full
|
|
80
|
+
analysis of the files on disk, which a buffer contradicts by
|
|
81
|
+
construction.
|
|
82
|
+
|
|
56
83
|
## `rigor init`
|
|
57
84
|
|
|
58
85
|
Write a starter configuration file.
|
|
@@ -333,7 +360,10 @@ the effectiveness ratio and `--format=json` carries `mode`,
|
|
|
333
360
|
`killed`, `survived`, `effectiveness_ratio`, per-file rows, and
|
|
334
361
|
`add_a_type_here`. It is the truth tier behind the static
|
|
335
362
|
`--protection` proxy, at the cost of many analyses — an opt-in CI
|
|
336
|
-
deep-dive, not an interactive check.
|
|
363
|
+
deep-dive, not an interactive check. `--workers=N` fork-parallelizes
|
|
364
|
+
this tier too (the whole-project pre-pass is paid once on the parent
|
|
365
|
+
and the per-file measurement is spread across workers), with the
|
|
366
|
+
same precedence chain and the same byte-identical output.
|
|
337
367
|
|
|
338
368
|
```sh
|
|
339
369
|
rigor coverage --protection --mutation [paths]
|
|
@@ -352,7 +382,10 @@ cost is proportional to the protection hole. `--format=json`
|
|
|
352
382
|
carries `mode` (`protection-fused`), `type_killed`,
|
|
353
383
|
`test_killed`, `unprotected`, `protected_ratio`, per-file rows,
|
|
354
384
|
and `add_protection_here`; `--threshold` gates on the fused
|
|
355
|
-
ratio.
|
|
385
|
+
ratio. This tier is always sequential — the suite hook shells out
|
|
386
|
+
to your test runner, and concurrent runs would race over one
|
|
387
|
+
working tree — so `--workers` does not apply and an explicit one
|
|
388
|
+
is reported as ignored on stderr.
|
|
356
389
|
|
|
357
390
|
`--test-command=CMD` is the runner hook (default
|
|
358
391
|
`bundle exec rake`). The suite must pass on clean code first, or
|
|
@@ -378,6 +411,18 @@ to a deterministic sample of `N` mutations per file, bounding the
|
|
|
378
411
|
cost on large files. Per-file ratios then become estimates, noted
|
|
379
412
|
on stderr so `--format=json` stdout stays clean.
|
|
380
413
|
|
|
414
|
+
A re-run is served from a per-file measurement cache while
|
|
415
|
+
nothing that could change a file's number has moved: the file
|
|
416
|
+
itself, any file it was recorded as reading from, the resolved
|
|
417
|
+
configuration, `sig/`, the gem set, the engine version, `--limit`
|
|
418
|
+
/ `--seed`, and the adopted bleeding-edge features. It reads the
|
|
419
|
+
cross-file edges a `rigor check --incremental` run records, so
|
|
420
|
+
warm one once per project; without a usable snapshot every file
|
|
421
|
+
is measured, never silently served. `--no-cache` measures
|
|
422
|
+
everything from scratch. A one-line stderr report says which of
|
|
423
|
+
those happened — see
|
|
424
|
+
[Type-protection coverage](15-type-protection-coverage.md).
|
|
425
|
+
|
|
381
426
|
```sh
|
|
382
427
|
rigor coverage --protection --mutation --with-tests \
|
|
383
428
|
--test-command "bundle exec rspec" --include-dynamic [paths]
|
|
@@ -479,6 +524,7 @@ so a skill can never be shadowed by a verb.
|
|
|
479
524
|
|
|
480
525
|
```sh
|
|
481
526
|
rigor skill [<name>] [--full <name>] [--path <name>] [--list] [--describe]
|
|
527
|
+
rigor skill describe [--deep]
|
|
482
528
|
```
|
|
483
529
|
|
|
484
530
|
| Form | Purpose |
|
|
@@ -488,6 +534,33 @@ rigor skill [<name>] [--full <name>] [--path <name>] [--list] [--describe]
|
|
|
488
534
|
| `--full <name>` | Print the `SKILL.md` body **followed by every `references/*.md` inline** — the complete, version-current procedure in one call. This is what a skill's "First: load the version-current copy" directive points at, so a copy vendored into a project (e.g. via `npx skills add`) re-fetches its current steps from the installed gem instead of following a frozen copy. |
|
|
489
535
|
| `--path <name>` | Print the single-line absolute `SKILL.md` path, suitable as input to a file-reading tool. |
|
|
490
536
|
| `--describe` | Probe the project's state (config / baseline / `sig/` / CI — presence only, never runs `rigor check`) and recommend the next skill to run. Also spelled `describe`; surfaced top-level as [`rigor describe`](#rigor-describe) below. |
|
|
537
|
+
| `describe --deep` | The same report, except it **runs `rigor check` first** and routes the headline recommendation on the result. Opt-in, because it costs a full analysis and writes `.rigor/cache` — the un-flagged form stays presence-only and side-effect-free. |
|
|
538
|
+
|
|
539
|
+
### `describe --deep`
|
|
540
|
+
|
|
541
|
+
By default the recommendation comes from a presence-only probe, so it can
|
|
542
|
+
tell that a project *has* a config but not whether the analysis is
|
|
543
|
+
healthy. `--deep` runs the check for you and lets the result pick the
|
|
544
|
+
headline, using the same routing the `## For the agent` section already
|
|
545
|
+
teaches:
|
|
546
|
+
|
|
547
|
+
| Deep check result | Headline becomes |
|
|
548
|
+
| --- | --- |
|
|
549
|
+
| RBS environment built to 0 classes, or a `configuration-error` diagnostic | `rigor-doctor` — the analysis is hollow until the setup is fixed. |
|
|
550
|
+
| Call sites resolving to the project's own definitions (reopened core / gem classes), as at least a third of the errors | `rigor-monkeypatch-resolve` — list them in `pre_eval:` and they clear wholesale. |
|
|
551
|
+
| Any remaining error diagnostics | `rigor-baseline-reduce`. Proven monkey-patch sites below that share are still reported here, so the finding survives even when the headline stays on the larger problem. |
|
|
552
|
+
| Clean, or the project has no config yet | Unchanged — the presence-only recommendation stands. |
|
|
553
|
+
|
|
554
|
+
If the check **cannot run at all** (no config, an unloadable plugin, a
|
|
555
|
+
malformed config) `--deep` does not fail and does not pretend the project
|
|
556
|
+
is clean: it reports what went wrong, falls back to the presence-only
|
|
557
|
+
recommendation, and points you at `rigor doctor`. It deliberately does
|
|
558
|
+
*not* route on weaker signals — "framework calls typing as `Dynamic`" is
|
|
559
|
+
still a judgement call left to you and your agent.
|
|
560
|
+
|
|
561
|
+
```sh
|
|
562
|
+
rigor skill describe --deep # also: rigor describe --deep
|
|
563
|
+
```
|
|
491
564
|
|
|
492
565
|
The verb spellings `rigor skill list` / `print <name>` / `path <name>`
|
|
493
566
|
were **removed in v0.3.0** — the positional slot is a skill name, so
|
|
@@ -511,6 +584,10 @@ It reports a presence-only project-state probe (does a `.rigor.yml`,
|
|
|
511
584
|
recommended next skill. It is read-only and side-effect-free — it never
|
|
512
585
|
runs `rigor check`. Identical output to `rigor skill describe`.
|
|
513
586
|
|
|
587
|
+
`rigor describe --deep` forwards to
|
|
588
|
+
[`rigor skill describe --deep`](#rigor-skill), which opts into running the
|
|
589
|
+
check first — slow, and it writes the cache.
|
|
590
|
+
|
|
514
591
|
## `rigor docs`
|
|
515
592
|
|
|
516
593
|
Print the documentation bundled inside the `rigortype` gem
|
|
@@ -546,7 +623,7 @@ pages from the installed gem with no HTTP request.
|
|
|
546
623
|
## `rigor show-bleedingedge`
|
|
547
624
|
|
|
548
625
|
Print the **bleeding-edge overlay** — the Rigor-maintained set of
|
|
549
|
-
the next major's queued
|
|
626
|
+
the next major's queued changes ([ADR-50](../adr/50-release-engineering-and-stability-strategy.md)
|
|
550
627
|
§ WD2) — and report which of them the project's
|
|
551
628
|
[`bleeding_edge:`](03-configuration.md) configuration adopts. Read-only:
|
|
552
629
|
it loads `.rigor.yml` to resolve the active selection but runs no
|
|
@@ -561,16 +638,31 @@ rigor show-bleedingedge [--config PATH] [--format text|json]
|
|
|
561
638
|
| `--config PATH` | Use this `.rigor.yml` instead of auto-discovery. |
|
|
562
639
|
| `--format text\|json` | Output format. Default `text`. |
|
|
563
640
|
|
|
564
|
-
Each queued feature appears with its stable id,
|
|
565
|
-
and whether your configuration adopts it.
|
|
566
|
-
|
|
567
|
-
|
|
641
|
+
Each queued feature appears with its stable id, its **kind** — `severity`
|
|
642
|
+
or `behaviour` — and whether your configuration adopts it. A `severity`
|
|
643
|
+
feature also prints the rule → severity diff it imposes; a `behaviour`
|
|
644
|
+
feature changes a measurement, an algorithm, or a default without moving
|
|
645
|
+
any rule's severity, so it has no such diff and its summary is the whole
|
|
646
|
+
description. See [`docs/compatibility.md`](../compatibility.md) for how
|
|
647
|
+
bleeding-edge fits the stability model.
|
|
568
648
|
|
|
569
649
|
Queued today:
|
|
570
650
|
|
|
571
|
-
| Feature id | What it changes |
|
|
572
|
-
| --- | --- |
|
|
573
|
-
| `reject-unparseable-signatures` | An unparseable `.rbs` under `signature_paths:` **fails the run** (`rbs.coverage.quarantined-signature` → `error`) instead of being skipped with a warning. |
|
|
651
|
+
| Feature id | Kind | What it changes |
|
|
652
|
+
| --- | --- | --- |
|
|
653
|
+
| `reject-unparseable-signatures` | severity | An unparseable `.rbs` under `signature_paths:` **fails the run** (`rbs.coverage.quarantined-signature` → `error`) instead of being skipped with a warning. |
|
|
654
|
+
| `use-of-void-value` | severity | Using a value recovered from an author-declared `-> void` return in value context is reported as `static.value-use.void` (`warning`). |
|
|
655
|
+
| `discovery-seeded-mutation-sites` | behaviour | [`rigor coverage --protection --mutation`](15-type-protection-coverage.md) measures against the same cross-file project discovery Tier 1 already uses — both when picking the sites and when deciding whether a breakage was caught — so a call on a project class declared in a *sibling* file is measured instead of dropped, and a breakage there can actually be caught. **Adds sites to the denominator, so the reported effectiveness ratio moves** — check it against any `--threshold` you pin in CI before adopting. |
|
|
656
|
+
| `dependent-closure-kill-oracle` | behaviour | [`rigor coverage --protection --mutation`](15-type-protection-coverage.md) counts a breakage as caught when the diagnostic appears anywhere in the mutated file **or the files that depend on it**, instead of in the mutated file alone — so changing what a method returns counts as caught when the error lands in its callers. Can only **add** kills, so the ratio moves up or not at all; it costs about a third more wall time per mutant, and a ratio measured under it is not comparable with one measured without it. |
|
|
657
|
+
|
|
658
|
+
Once a feature **graduates** — it becomes the default at a major
|
|
659
|
+
([ADR-50](../adr/50-release-engineering-and-stability-strategy.md) § WD7)
|
|
660
|
+
— it leaves the queued list and appears under `Graduated`, an
|
|
661
|
+
acknowledgement that naming it in `bleeding_edge:` no longer does
|
|
662
|
+
anything: the behaviour is on for everyone. The section is absent while
|
|
663
|
+
nothing has graduated. In `--format json` the same information is the
|
|
664
|
+
`graduated` array, alongside `overlay` (every queued feature, each with
|
|
665
|
+
its `kind`), `active`, and `unknown_selected`.
|
|
574
666
|
|
|
575
667
|
## `rigor doctor`
|
|
576
668
|
|
|
@@ -116,7 +116,22 @@ statically and may resolve at run time. The same findings appear in the
|
|
|
116
116
|
| `severity_profile` | String | `"balanced"` | `lenient`, `balanced`, or `strict` — see [Diagnostics](04-diagnostics.md). |
|
|
117
117
|
| `severity_overrides` | Hash | `{}` | Per-rule / per-family severity, e.g. `{ call: warning, flow.always-truthy-condition: off }`. |
|
|
118
118
|
| `baseline` | String / `false` | `nil` | Path to a `.rigor-baseline.yml`, or `false` to disable an inherited one. See [Baselines](06-baseline.md). |
|
|
119
|
-
| `bleeding_edge` | Boolean / Array / Hash | `false` | Adopt the next major's queued
|
|
119
|
+
| `bleeding_edge` | Boolean / Array / Hash | `false` | Adopt the next major's queued changes early ([ADR-50](../adr/50-release-engineering-and-stability-strategy.md) § WD2). `false` adopts none; `true` adopts the whole overlay; a list of feature ids adopts only those; `{ all: true, except: [ids] }` adopts all but the named. Orthogonal to `severity_profile`. Override it for a single run with [`rigor check --bleeding-edge[=ids]`](02-cli-reference.md#rigor-check) / `--no-bleeding-edge`. Inspect with [`rigor show-bleedingedge`](02-cli-reference.md#rigor-show-bleedingedge). |
|
|
120
|
+
|
|
121
|
+
A queued feature is one of two kinds, and `bleeding_edge:` selects both the
|
|
122
|
+
same way:
|
|
123
|
+
|
|
124
|
+
- A **severity** feature promotes one or more rules — a discipline Rigor
|
|
125
|
+
already reports quietly becomes an error or a warning. Its diff is a list
|
|
126
|
+
of rule ids, printed by `rigor show-bleedingedge`.
|
|
127
|
+
- A **behaviour** feature changes a measurement, an algorithm, or a
|
|
128
|
+
default, and moves no rule's severity. There is no rule-id diff to read,
|
|
129
|
+
so its summary in `rigor show-bleedingedge` is the whole description of
|
|
130
|
+
what adopting it does.
|
|
131
|
+
|
|
132
|
+
An id you name that this version of Rigor does not know is ignored rather
|
|
133
|
+
than rejected, so a shared `.rigor.yml` can name a feature that only some
|
|
134
|
+
of the versions in use have queued.
|
|
120
135
|
|
|
121
136
|
### Dependency RBS discovery
|
|
122
137
|
|
|
@@ -97,7 +97,21 @@ re-stamps it for the run. Three profiles, set with the
|
|
|
97
97
|
| --- | --- |
|
|
98
98
|
| `lenient` | Only proven diagnostics are errors; uncertain ones drop to `warning` / `info`. For incremental adoption on legacy code. |
|
|
99
99
|
| `balanced` *(default)* | Most rules `error`; `dump.type` `info`; uncertain rules `warning`. |
|
|
100
|
-
| `strict` | Nearly every rule is an `error
|
|
100
|
+
| `strict` | Nearly every rule is an `error`. The exceptions: `call.self-undefined-method` and `static.value-use.void` stay `off` (both opt-in only), `flow.unreachable-clause` stays `warning` pending its false-positive gate, and the three `suppression.*` rules stay `warning` — a stale suppression comment is worth telling you about, but it is not a reason to fail a build. CI-friendly. |
|
|
101
|
+
|
|
102
|
+
Under `balanced`, the rules that do **not** emit as `error`
|
|
103
|
+
are:
|
|
104
|
+
|
|
105
|
+
| Severity | Rules |
|
|
106
|
+
| --- | --- |
|
|
107
|
+
| `warning` | `call.unresolved-toplevel`, `def.ivar-write-mismatch`, `def.return-type-mismatch`, `def.override-visibility-reduced`, `def.override-return-widened`, `def.override-param-narrowed`, `flow.unreachable-branch`, `flow.always-truthy-condition`, `flow.dead-assignment`, `flow.duplicate-hash-key`, `flow.return-in-ensure`, `flow.shadowed-rescue-clause`, `suppression.unknown-rule`, `suppression.empty`, `suppression.unknown-marker` |
|
|
108
|
+
| `info` | `flow.unreachable-clause`, `dump.type` |
|
|
109
|
+
| `off` | `call.self-undefined-method`, `static.value-use.void` |
|
|
110
|
+
|
|
111
|
+
Everything else emits as `error`. For one rule under all three
|
|
112
|
+
profiles, `rigor explain <rule>` prints `Authored severity:` and
|
|
113
|
+
`Severity by profile:` — that output is generated from the rule
|
|
114
|
+
catalogue itself, so it is the per-rule source of truth.
|
|
101
115
|
|
|
102
116
|
For finer control, `severity_overrides:` maps a rule ID or a
|
|
103
117
|
family to one of `error`, `warning`, `info`, or `off`:
|
|
@@ -109,7 +123,15 @@ severity_overrides:
|
|
|
109
123
|
call: warning
|
|
110
124
|
```
|
|
111
125
|
|
|
112
|
-
A rule-specific override beats a family override.
|
|
126
|
+
A rule-specific override beats a family override. `off` drops
|
|
127
|
+
the diagnostic from the result entirely, which makes
|
|
128
|
+
`severity_overrides:` the lighter-touch sibling of `disable:`
|
|
129
|
+
below — both silence a rule; the override reads as "this one
|
|
130
|
+
rule, at this severity" alongside the rest of the profile.
|
|
131
|
+
|
|
132
|
+
YAML reserves the bareword `off` as a boolean. If an override
|
|
133
|
+
that names it seems not to apply, quote it — `"off"` — and the
|
|
134
|
+
same for `on`.
|
|
113
135
|
|
|
114
136
|
## Machine-readable output (`--format json`)
|
|
115
137
|
|
|
@@ -198,7 +220,10 @@ config.merge(extra) # rigor:disable call.undefined-method
|
|
|
198
220
|
```
|
|
199
221
|
|
|
200
222
|
It accepts qualified IDs, family wildcards (`call`), a
|
|
201
|
-
comma- or space-separated list, or `all`.
|
|
223
|
+
comma- or space-separated list, or `all`. The comment must sit
|
|
224
|
+
on the line the diagnostic points at; there is no
|
|
225
|
+
`disable-block` form, so an expression spread over several
|
|
226
|
+
lines needs the comment on each line that fires.
|
|
202
227
|
|
|
203
228
|
A marker that cannot work is flagged rather than silently
|
|
204
229
|
ignored: a token that names no known rule (a typo like
|
|
@@ -217,7 +242,14 @@ suppressible like any other rule.
|
|
|
217
242
|
|
|
218
243
|
**In-source, whole file.** `# rigor:disable-file <rules>`
|
|
219
244
|
anywhere in a file suppresses those rules for every line;
|
|
220
|
-
`# rigor:disable-file all` silences the file.
|
|
245
|
+
`# rigor:disable-file all` silences the file. Convention is to
|
|
246
|
+
put it near the top — typically on a generated file, a fixture,
|
|
247
|
+
or a vendored snippet — but every comment in the file is
|
|
248
|
+
scanned, so any placement works.
|
|
249
|
+
|
|
250
|
+
The three layers **compose**: a file-scope marker does not
|
|
251
|
+
cancel a line-scope one, and a project-wide `disable:` still
|
|
252
|
+
applies to a file that carries neither.
|
|
221
253
|
|
|
222
254
|
**Project-wide.** The `disable:` config key turns rules off
|
|
223
255
|
across the whole run:
|