rigortype 0.4.4 → 0.4.5
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 +5 -5
- data/docs/handbook/11-sig-gen.md +90 -17
- data/docs/manual/02-cli-reference.md +39 -9
- data/docs/manual/03-configuration.md +10 -8
- data/docs/manual/04-diagnostics.md +16 -5
- data/docs/manual/05-inspecting-types.md +2 -2
- data/docs/manual/06-baseline.md +1 -1
- data/docs/manual/07-plugins.md +2 -1
- data/docs/manual/08-skills.md +7 -0
- data/docs/manual/10-mcp-server.md +8 -1
- data/docs/manual/18-removing-dead-code.md +18 -7
- data/docs/manual/plugins/rigor-rspec.md +101 -1
- data/lib/rigor/analysis/check_rules/clause_coverage.rb +134 -0
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +111 -7
- data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +190 -0
- data/lib/rigor/analysis/check_rules/rule_ids.rb +3 -1
- data/lib/rigor/analysis/check_rules/source_arity.rb +32 -3
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +13 -4
- data/lib/rigor/analysis/check_rules.rb +228 -15
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +2 -1
- data/lib/rigor/analysis/incremental.rb +49 -0
- data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +36 -18
- data/lib/rigor/analysis/incremental_session.rb +166 -24
- data/lib/rigor/analysis/path_expansion.rb +3 -1
- data/lib/rigor/analysis/reachability/graph.rb +90 -13
- data/lib/rigor/analysis/reachability/scan.rb +279 -39
- data/lib/rigor/analysis/rule_catalog.rb +17 -4
- data/lib/rigor/analysis/runner/pool_coordinator.rb +69 -16
- data/lib/rigor/analysis/runner.rb +69 -65
- data/lib/rigor/analysis/severity_stamp.rb +11 -0
- data/lib/rigor/analysis/worker_session.rb +12 -1
- data/lib/rigor/cache/descriptor.rb +40 -7
- data/lib/rigor/cache/engine_source.rb +3 -2
- data/lib/rigor/cache/incremental_snapshot.rb +26 -8
- data/lib/rigor/cache/rbs_descriptor.rb +2 -1
- data/lib/rigor/cache/store.rb +4 -3
- data/lib/rigor/cli/command.rb +3 -1
- data/lib/rigor/cli/docs_command.rb +3 -2
- data/lib/rigor/cli/doctor_command.rb +1 -1
- data/lib/rigor/cli/explain_command.rb +3 -2
- data/lib/rigor/cli/plugin_command.rb +2 -1
- data/lib/rigor/cli/plugins_command.rb +2 -1
- data/lib/rigor/cli/sig_gen_command.rb +32 -11
- data/lib/rigor/cli/skill_command.rb +2 -1
- data/lib/rigor/cli/skill_describe.rb +3 -2
- data/lib/rigor/cli/unused_command.rb +10 -7
- data/lib/rigor/cli.rb +3 -2
- data/lib/rigor/config_audit.rb +60 -15
- data/lib/rigor/configuration/severity_profile.rb +20 -2
- data/lib/rigor/configuration.rb +29 -3
- data/lib/rigor/effects/signature_sources.rb +3 -1
- data/lib/rigor/environment/bundle_sig_discovery.rb +3 -2
- data/lib/rigor/environment/gated_signature_guard.rb +2 -1
- data/lib/rigor/environment/gated_signature_plan.rb +2 -1
- data/lib/rigor/environment/installed_gem_set.rb +2 -1
- data/lib/rigor/environment/missing_gem_constant_index.rb +4 -3
- data/lib/rigor/environment/rbs_loader.rb +21 -1
- data/lib/rigor/inference/block_call_timing.rb +7 -1
- data/lib/rigor/inference/builtins/method_catalog.rb +2 -1
- data/lib/rigor/inference/captured_locals.rb +44 -13
- data/lib/rigor/inference/clause_widening.rb +90 -0
- data/lib/rigor/inference/definer_resolution.rb +87 -20
- data/lib/rigor/inference/expression_typer.rb +35 -5
- data/lib/rigor/inference/intervening_ivar_reset.rb +303 -0
- data/lib/rigor/inference/macro_block_self_type.rb +20 -6
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +170 -35
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +50 -2
- data/lib/rigor/inference/method_dispatcher.rb +67 -3
- data/lib/rigor/inference/module_function_state.rb +137 -42
- data/lib/rigor/inference/narrowing.rb +54 -10
- data/lib/rigor/inference/parameter_inference_collector.rb +4 -26
- data/lib/rigor/inference/project_patched_methods.rb +168 -4
- data/lib/rigor/inference/project_patched_mixins.rb +162 -0
- data/lib/rigor/inference/project_patched_scanner.rb +51 -12
- data/lib/rigor/inference/project_patched_visibility.rb +214 -0
- data/lib/rigor/inference/project_seed_tables.rb +120 -0
- data/lib/rigor/inference/refined_dispatch/constructed_class.rb +113 -0
- data/lib/rigor/inference/refined_dispatch/own_method.rb +41 -12
- data/lib/rigor/inference/reflective_ivar_write.rb +6 -6
- data/lib/rigor/inference/return_barrier.rb +16 -0
- data/lib/rigor/inference/scope_indexer.rb +675 -105
- data/lib/rigor/inference/self_call_ivar_writes.rb +11 -6
- data/lib/rigor/inference/singleton_external_ahead.rb +64 -0
- data/lib/rigor/inference/singleton_hook_decline.rb +7 -0
- data/lib/rigor/inference/statement_evaluator.rb +446 -139
- data/lib/rigor/inference/unthreaded_rebinds.rb +3 -1
- data/lib/rigor/lens/contract_reader.rb +45 -2
- data/lib/rigor/lens/document.rb +2 -1
- data/lib/rigor/lens/file_reader.rb +12 -2
- data/lib/rigor/lens/members.rb +16 -2
- data/lib/rigor/lens/run.rb +34 -7
- data/lib/rigor/mcp/server.rb +2 -2
- data/lib/rigor/plugin/base.rb +3 -2
- data/lib/rigor/plugin/io_boundary.rb +42 -6
- data/lib/rigor/plugin/isolation.rb +4 -3
- data/lib/rigor/protection/discovery_seed.rb +9 -36
- data/lib/rigor/protection/mutation_cache.rb +8 -2
- data/lib/rigor/rooted_glob.rb +27 -0
- data/lib/rigor/scope/discovery_index.rb +5 -0
- data/lib/rigor/scope/resolution_chain/relevance.rb +14 -0
- data/lib/rigor/scope/resolution_chain.rb +40 -5
- data/lib/rigor/scope.rb +45 -2
- data/lib/rigor/sig_gen/alias_index.rb +2 -1
- data/lib/rigor/sig_gen/classification.rb +4 -1
- data/lib/rigor/sig_gen/generator.rb +167 -22
- data/lib/rigor/sig_gen/layout_index.rb +5 -2
- data/lib/rigor/sig_gen/observation_collector.rb +137 -11
- data/lib/rigor/sig_gen/renderer.rb +13 -155
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +11 -0
- data/lib/rigor/sig_gen/write_reporter.rb +203 -0
- data/lib/rigor/sig_gen/write_result.rb +3 -1
- data/lib/rigor/sig_gen/writer.rb +5 -3
- data/lib/rigor/signature_path_audit.rb +4 -2
- data/lib/rigor/type/constant.rb +10 -1
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +1 -1
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/render_locals.rb +6 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +1 -1
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_discoverer.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +4 -3
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +1 -1
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +1 -1
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +1 -1
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_discoverer.rb +1 -1
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +1 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +1 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +38 -9
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/target_detector.rb +1 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +1 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +1 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +7 -2
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +5 -5
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +18 -17
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +1 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +1 -1
- data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +4 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +2 -2
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +2 -2
- data/sig/rigor/plugin/io_boundary.rbs +5 -2
- data/sig/rigor/rooted_glob.rbs +7 -0
- data/sig/rigor/scope.rbs +4 -3
- data/skills/rigor-doctor/references/01-checks.md +23 -11
- data/skills/rigor-type-oracle/references/01-oracle-commands.md +2 -1
- metadata +13 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4da9b991a97cd586806b416facb6dbf6c3a3b938c7c9433e330bc44c0757fdaf
|
|
4
|
+
data.tar.gz: 1167e8d91ccbc8740ac4094f67db82937bd3b374d2488a43e514f7922c7a98b1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: db2047e49879c399812704702d4064304bf4ac9be8fe25132cc65ac441e2962f7b8c45cf59371727dffaf52caab9accbfb7c8ea0abe1976f264a7a4506e46e6f
|
|
7
|
+
data.tar.gz: e426da15aeb714ab906df710946ae3b2c96b35509632b9c35efba289d303da519f58e3cd362a8b47c4ba8a9f883e54e657edeb3e09c1934b4cd542f119440678
|
data/README.md
CHANGED
|
@@ -106,7 +106,7 @@ https://raw.githubusercontent.com/rigortype/rigor/refs/heads/master/docs/install
|
|
|
106
106
|
```
|
|
107
107
|
|
|
108
108
|
Ready-made prompts — 日本語, 简体中文, 繁體中文, 한국어, Español,
|
|
109
|
-
Português, Français, Deutsch, Italiano, Tiếng Việt, ภาษาไทย,
|
|
109
|
+
Português (Brasil), Português, Français, Deutsch, Italiano, Tiếng Việt, ภาษาไทย,
|
|
110
110
|
Bahasa Indonesia, Polski, Українська, Русский, Română, Türkçe,
|
|
111
111
|
العربية, فارسی — are in
|
|
112
112
|
the [installation guide](https://rigor.typedduck.fail/manual/01-installation/#set-up-in-your-language).
|
|
@@ -232,7 +232,7 @@ rigor docs --list # list every bundled page
|
|
|
232
232
|
|
|
233
233
|
## Status
|
|
234
234
|
|
|
235
|
-
Current release: **`v0.4.
|
|
235
|
+
Current release: **`v0.4.5`** (2026-10-19) — on the
|
|
236
236
|
`0.4.x` line, continuing the evaluation era opened by `v0.2.0`, the first
|
|
237
237
|
publicly-announced (general / evaluation) release. The line publishes
|
|
238
238
|
an enumerated [compatibility surface](docs/compatibility.md) as a
|
|
@@ -256,9 +256,9 @@ Mozilla Public License Version 2.0. See [`LICENSE`](LICENSE).
|
|
|
256
256
|
|
|
257
257
|
## Sponsors
|
|
258
258
|
|
|
259
|
-
Rigor is built and maintained by one person, who has
|
|
260
|
-
|
|
261
|
-
|
|
259
|
+
Rigor is built and maintained by one person, who has put nearly every
|
|
260
|
+
spare hour of personal time into it since development began in April
|
|
261
|
+
2026. Sustainable development depends on sustainable funding.
|
|
262
262
|
|
|
263
263
|
**The current monthly budget is well short of the $200 minimum needed
|
|
264
264
|
to keep this pace going; $600 or more would let me work on it
|
data/docs/handbook/11-sig-gen.md
CHANGED
|
@@ -165,6 +165,11 @@ The `sig.skipped.*` reasons are:
|
|
|
165
165
|
RBS for it, so `--write` and `--check` are unaffected. Its
|
|
166
166
|
JSON row has no `class` key. `def self.x`, a `def` inside a
|
|
167
167
|
block body and one inside `class << obj` are not reported.
|
|
168
|
+
- `sig.skipped.uncertain-module-function` — a bare `public`,
|
|
169
|
+
`private` or `protected` that may not run (under an `if`, in
|
|
170
|
+
a block) sits between a bare `module_function` and the
|
|
171
|
+
method, so it is either a module function or an instance
|
|
172
|
+
method only. sig-gen writes neither.
|
|
168
173
|
- `sig.skipped.unrenderable-rbs` — the signature Rigor
|
|
169
174
|
rendered for this method does not parse as RBS. This one
|
|
170
175
|
is a **bug in Rigor**, not a property of your code: every
|
|
@@ -527,7 +532,13 @@ A declared `untyped` that you wrote in `sig/` is treated
|
|
|
527
532
|
the same way for a value literal: an Integer, String or
|
|
528
533
|
Symbol the body proves is widened to its class (`-> 1`
|
|
529
534
|
becomes `-> Integer`, `-> ("S" | nil)` becomes
|
|
530
|
-
`-> (String | nil)`),
|
|
535
|
+
`-> (String | nil)`), and `true` or `false` is widened to
|
|
536
|
+
`bool` (written `(false | true)`), so a subclass that
|
|
537
|
+
overrides the method with the other one is not reported as
|
|
538
|
+
widening the return. A body that returns only `nil` keeps
|
|
539
|
+
the `untyped`: it is the shape of a template method a
|
|
540
|
+
subclass fills in, and `-> nil` would make every override
|
|
541
|
+
that returns a value a widening. `nil` inside a union and a
|
|
531
542
|
computed `Float` are proposed as they are. An `untyped` that
|
|
532
543
|
only the inline reader synthesizes for a `def` you did not
|
|
533
544
|
annotate keeps the literal, as does a method no `.rbs`
|
|
@@ -590,9 +601,16 @@ list is the current state):
|
|
|
590
601
|
`attr_writer` / `attr_accessor` ivar reads untyped.
|
|
591
602
|
The accessor, and any other method returning that
|
|
592
603
|
ivar, is skipped even beside a concrete write such as
|
|
593
|
-
`@logger = Logger.new
|
|
594
|
-
|
|
595
|
-
from.
|
|
604
|
+
`@logger = Logger.new`. `--params=observed` types an
|
|
605
|
+
`attr_reader` from the constructor parameter its ivar
|
|
606
|
+
is assigned from (`Box.new(0)` gives
|
|
607
|
+
`def count: () -> 0`) only when that assignment is the
|
|
608
|
+
ivar's only write in the class's body in that file: an
|
|
609
|
+
attr writer, a hand-written setter, or a write in any
|
|
610
|
+
other instance method leaves both reader and writer
|
|
611
|
+
skipped. A writer declared in another file, in a
|
|
612
|
+
superclass or module, or through `class_eval` from
|
|
613
|
+
outside is not seen. The
|
|
596
614
|
generator emits the long-form `def name: () -> T`
|
|
597
615
|
spelling so the writer's merge path applies unchanged;
|
|
598
616
|
existing short-form `attr_reader name: T` declarations
|
|
@@ -616,9 +634,28 @@ two are wired today, one is reserved.
|
|
|
616
634
|
| Policy | Behaviour |
|
|
617
635
|
| --- | --- |
|
|
618
636
|
| `untyped` (default) | Every parameter is spelled `untyped`. No inference-derived parameter contract is imposed on future callers. The user retains complete authorship over parameter typing. |
|
|
619
|
-
| `observed` | Collect argument types from every call site under `--observe=PATH...` (defaults to the configured `test_paths:`, or whichever of `spec/` and `test/` exist), union per parameter position, erase to RBS, emit the union. With no test root to observe, or a declared root that does not exist, sig-gen says so on stderr. |
|
|
637
|
+
| `observed` | Collect argument types from every call site under `--observe=PATH...` (defaults to the configured `test_paths:`, or whichever of `spec/` and `test/` exist), union per parameter position, erase to RBS, emit the union. With no test root to observe, or a declared root that does not exist or is not read, sig-gen says so on stderr, and `--format=json` carries the same fact in a top-level `observation` key (below). |
|
|
620
638
|
| `observed-strict` | Reserved. Will additionally widen to capability roles (`_ToStr`, `_ToS`, …) once the role catalog ships. Currently rejected with a usage error. |
|
|
621
639
|
|
|
640
|
+
Under `--params=observed`, the `--print` / `--diff` JSON payload gains
|
|
641
|
+
a top-level `observation` key next to `candidates`, so a consumer that
|
|
642
|
+
never reads stderr (the MCP `rigor_sig_gen` tool included) can tell
|
|
643
|
+
"no test root was observed" from "the tests pass no typed arguments":
|
|
644
|
+
|
|
645
|
+
```json
|
|
646
|
+
{ "candidates": [ … ],
|
|
647
|
+
"observation": { "paths": ["/app/spec"], "missing": ["/app/tests"] } }
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
`paths` are the roots the collector reads (a directory or a `.rb`
|
|
651
|
+
file); `missing` are the other declared roots: ones that do not exist,
|
|
652
|
+
or exist but are not read (`README.md`). An identical repeat is listed once. `paths: []` with `missing: []` means
|
|
653
|
+
there was no test root at all. Paths are spelled as the run resolved
|
|
654
|
+
them: `--observe=PATH` as typed, `test_paths:` as absolute paths, resolved against the
|
|
655
|
+
config file's directory, auto-detected `spec/` / `test/` absolute. The key is
|
|
656
|
+
absent under `--params=untyped`, and absent from the `--write` and
|
|
657
|
+
`--check` JSON payloads, which have their own shapes.
|
|
658
|
+
|
|
622
659
|
The default deliberately favours `untyped` because of
|
|
623
660
|
[ADR-5](../adr/5-robustness-principle.md)'s clause 2: a
|
|
624
661
|
method's parameter contract should be the **most permissive**
|
|
@@ -704,7 +741,7 @@ output it got before.
|
|
|
704
741
|
## RSpec-aware observations
|
|
705
742
|
|
|
706
743
|
When the observed test roots hold an RSpec suite, the
|
|
707
|
-
generator recognises
|
|
744
|
+
generator recognises four RSpec-shaped binding patterns
|
|
708
745
|
and uses them to type receivers that would otherwise
|
|
709
746
|
degrade to `Dynamic[top]`:
|
|
710
747
|
|
|
@@ -712,10 +749,12 @@ degrade to `Dynamic[top]`:
|
|
|
712
749
|
RSpec.describe Calc do
|
|
713
750
|
subject { Calc.new } # binds :subject → Nominal[Calc]
|
|
714
751
|
let(:other) { Calc.new } # binds :other → Nominal[Calc]
|
|
752
|
+
before { @calc = Calc.new } # binds @calc → Nominal[Calc]
|
|
715
753
|
|
|
716
754
|
it "..." do
|
|
717
755
|
subject.greet("Alice") # observed: Calc#greet receives String
|
|
718
756
|
other.greet("Bob") # observed: same
|
|
757
|
+
@calc.greet("Carol") # observed: same
|
|
719
758
|
described_class.new.add(1, 2) # observed: Calc#add receives Integer, Integer
|
|
720
759
|
end
|
|
721
760
|
end
|
|
@@ -724,17 +763,36 @@ end
|
|
|
724
763
|
The recogniser handles `RSpec.describe Foo`, bare
|
|
725
764
|
`describe Foo` (no `RSpec.` receiver), `subject { … }`,
|
|
726
765
|
`subject(:name) { … }`, `let(:name) { … }`, `let!(:name)`,
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
766
|
+
an ivar assigned by a top-level statement of a `before`
|
|
767
|
+
block, and `described_class.new(...)`. Same-name `subject` /
|
|
768
|
+
`let` bindings across nested scopes are last-wins; the
|
|
769
|
+
recogniser does not re-implement RSpec's full scope rules —
|
|
770
|
+
the typical one-spec-file shape is the target. A `before`
|
|
771
|
+
ivar is scoped: it types reads only inside the `describe` /
|
|
772
|
+
`context` block that holds the `before`, through nested
|
|
773
|
+
`describe` / `context` / `it` / hook blocks but not through
|
|
774
|
+
any other block (`Class.new { … }`, `instance_eval { … }`,
|
|
775
|
+
`each { … }`), and a read that two
|
|
776
|
+
enclosing `before`s assign different classes is not
|
|
777
|
+
observed.
|
|
731
778
|
|
|
732
779
|
A Minitest suite under `test/` is observed too, with no
|
|
733
780
|
recogniser of its own: a call whose receiver types to one
|
|
734
|
-
class counts. A receiver assigned in `setup`
|
|
735
|
-
inside a `test_*` method
|
|
736
|
-
a
|
|
737
|
-
|
|
781
|
+
class counts. A receiver assigned in `setup` reads as
|
|
782
|
+
`Foo | nil` inside a `test_*` method, since `setup` is not
|
|
783
|
+
`initialize`; a receiver of that shape — one class or class
|
|
784
|
+
object plus `nil` — is observed as that class when `nil`
|
|
785
|
+
does not answer the method, because such a call cannot
|
|
786
|
+
have been made on `nil`. A call of a method `nil` does
|
|
787
|
+
answer (`==`, `respond_to?`, `is_a?`) is not observed, as
|
|
788
|
+
it may have run on `nil`; with `&.` it is, since `&.` skips
|
|
789
|
+
`nil`. Which methods `nil` answers comes from the loaded
|
|
790
|
+
RBS for `NilClass`, `Object` and `Kernel`, so a method a
|
|
791
|
+
library adds to `nil` (ActiveSupport's `try`, `as_json`)
|
|
792
|
+
counts only when that library's RBS is loaded.
|
|
793
|
+
`@foo&.bar(1)` counts the same way. A receiver
|
|
794
|
+
that could be one of two classes (`Foo | Bar | nil`), or
|
|
795
|
+
`untyped | nil`, is still not observed.
|
|
738
796
|
|
|
739
797
|
The recogniser is part of the generator itself; you do not
|
|
740
798
|
need to install `rigor-rspec` to benefit from it. If you
|
|
@@ -765,16 +823,31 @@ by side without coordination.
|
|
|
765
823
|
`def f`, a plain proposal over a `def self?.f`, or a
|
|
766
824
|
method declared in a later opening is skipped. A
|
|
767
825
|
`def f: ... | ...` overload is never replaced and never
|
|
768
|
-
counts as the file that declares `f`. An `alias`
|
|
769
|
-
`
|
|
826
|
+
counts as the file that declares `f`. An `alias new old`
|
|
827
|
+
(or `alias self.new self.old`) declares `new` under its own
|
|
828
|
+
kind and is never replaced, so a proposal for that name is
|
|
829
|
+
skipped.
|
|
770
830
|
- **Will not** touch files outside the configured signature
|
|
771
831
|
tree.
|
|
832
|
+
- **Will** name every proposal it skipped, one
|
|
833
|
+
` skipped Class#method (reason): <proposed RBS>` line under
|
|
834
|
+
its target (`Class.method` for a singleton), with the same
|
|
835
|
+
rows under `skipped` in `--format=json`. The reason is
|
|
836
|
+
`user-authored` (the target declares the member and the
|
|
837
|
+
writer does not replace it) or `outside-sig-root` (the
|
|
838
|
+
member routes to a file in a signature path other than the
|
|
839
|
+
first, which `--write` does not modify, whether the member
|
|
840
|
+
is declared there or is new to a class that lives there).
|
|
841
|
+
A target left unchanged by skips alone prints
|
|
842
|
+
`unchanged <file>`. The exit status stays `0`: a skip is
|
|
843
|
+
not a refusal, and `--check` still reports `sig/` as up to
|
|
844
|
+
date.
|
|
772
845
|
- **Will not** replace an existing method declaration
|
|
773
846
|
unless `--overwrite` is set AND the candidate is a
|
|
774
847
|
`tighter-return`, and then only its return changes: the
|
|
775
848
|
rest of the declaration is kept. Without `--overwrite`,
|
|
776
849
|
existing declarations are user-authored and the new method
|
|
777
|
-
is
|
|
850
|
+
is skipped.
|
|
778
851
|
- **Will not** change an existing method declaration that
|
|
779
852
|
disagrees with the method's inline declaration unless
|
|
780
853
|
`--overwrite` is set; it reports the method as `REFUSED`
|
|
@@ -224,7 +224,7 @@ rigor type-scan PATH...
|
|
|
224
224
|
`--show-recognized` includes fully-covered classes,
|
|
225
225
|
`--threshold=RATIO` makes the command exit non-zero when the
|
|
226
226
|
unrecognized-node ratio exceeds `RATIO`, and `--format=text|json`
|
|
227
|
-
selects the output format.
|
|
227
|
+
selects the output format. `--config=PATH` overrides config discovery.
|
|
228
228
|
|
|
229
229
|
## `rigor effects`
|
|
230
230
|
|
|
@@ -354,6 +354,8 @@ accept `--config=PATH`, `--format=text|json` and `--full`;
|
|
|
354
354
|
configured one — `--baseline <(git show
|
|
355
355
|
origin/main:.rigor-effects.yml)` in a bot),
|
|
356
356
|
`--strict-tolerated` and `--no-tolerated-effects`.
|
|
357
|
+
`explain` additionally accepts `--symbol=KEY` to explain one
|
|
358
|
+
specific unit instead of the changed ones.
|
|
357
359
|
|
|
358
360
|
The file records two tables. `methods:` holds each method's
|
|
359
361
|
**direct** summary — what its own body does, block literals
|
|
@@ -476,12 +478,13 @@ Print the catalogue entry for a diagnostic rule, or list every
|
|
|
476
478
|
rule when called with no argument.
|
|
477
479
|
|
|
478
480
|
```sh
|
|
479
|
-
rigor explain [rule]
|
|
481
|
+
rigor explain [options] [rule]
|
|
480
482
|
```
|
|
481
483
|
|
|
482
484
|
`rule` is a rule ID (`call.undefined-method`), a legacy alias,
|
|
483
|
-
or a family wildcard (`call`, `flow`, `def`, `assert`, `dump`
|
|
484
|
-
|
|
485
|
+
or a family wildcard (`call`, `flow`, `def`, `assert`, `dump`, `global`,
|
|
486
|
+
`suppression`, `static`, `effect`).
|
|
487
|
+
`--format=text|json` selects the output format. Exit `64` for an unknown rule.
|
|
485
488
|
|
|
486
489
|
It also answers the `sig.skipped.*` identifiers `rigor sig-gen`
|
|
487
490
|
prints when it declines to write a signature — `rigor explain
|
|
@@ -495,11 +498,13 @@ Compare the current diagnostics against a saved baseline JSON
|
|
|
495
498
|
and report only what is new.
|
|
496
499
|
|
|
497
500
|
```sh
|
|
498
|
-
rigor diff <baseline.json> [paths...]
|
|
501
|
+
rigor diff [options] <baseline.json> [paths...]
|
|
499
502
|
```
|
|
500
503
|
|
|
501
504
|
`--current=PATH` compares against a saved diagnostics JSON
|
|
502
|
-
instead of running a fresh check.
|
|
505
|
+
instead of running a fresh check. `--format=text|json` selects the
|
|
506
|
+
output format (default `text`), and `--config=PATH` passes a custom
|
|
507
|
+
configuration to the implicit check. Exit `1` when new
|
|
503
508
|
diagnostics appear.
|
|
504
509
|
|
|
505
510
|
## `rigor sig-gen`
|
|
@@ -533,6 +538,10 @@ refuse a file, so a tighter return `--write` declines without
|
|
|
533
538
|
`--overwrite` does not fail it; `--check --overwrite` counts one.
|
|
534
539
|
See [handbook chapter 11](../handbook/11-sig-gen.md#keeping-sig-current-in-ci).
|
|
535
540
|
|
|
541
|
+
`--write` names each proposal it skipped (a member `sig/` already declares, an `alias` included, or one that
|
|
542
|
+
routes to a signature path it does not write to, because it is declared there or its class lives there) on its own `skipped Class#method (reason)` line; the exit status is
|
|
543
|
+
unchanged. See [handbook chapter 11](../handbook/11-sig-gen.md#safety-what---write-will-and-will-not-do).
|
|
544
|
+
|
|
536
545
|
A method declared inline with `# @rbs` / `#:` is written as that
|
|
537
546
|
declaration, not as what its body infers; a parameter-only
|
|
538
547
|
annotation keeps its parameters and takes the return from the body,
|
|
@@ -616,7 +625,8 @@ rigor baseline <generate|regenerate|dump|drift|prune> [options]
|
|
|
616
625
|
|
|
617
626
|
`generate` and `regenerate` accept `--output=PATH` and
|
|
618
627
|
`--match-mode=rule|message`; `dump`, `drift`, and `prune` accept
|
|
619
|
-
`--baseline=PATH` to read a non-default baseline file.
|
|
628
|
+
`--baseline=PATH` to read a non-default baseline file. `dump`
|
|
629
|
+
also accepts `--format=text|json` for machine-readable output.
|
|
620
630
|
|
|
621
631
|
## `rigor triage`
|
|
622
632
|
|
|
@@ -1163,8 +1173,17 @@ Runs a scoped analysis and audits:
|
|
|
1163
1173
|
- **Plugin load errors** — whether every configured plugin loaded.
|
|
1164
1174
|
- **Baseline drift** — whether the current diagnostics have drifted
|
|
1165
1175
|
from the saved baseline.
|
|
1166
|
-
- **
|
|
1167
|
-
|
|
1176
|
+
- **Plugin gap** — whether the project's direct dependencies (the
|
|
1177
|
+
`DEPENDENCIES` section of `Gemfile.lock`, plus the Rails components
|
|
1178
|
+
a direct `rails` stands for) include gems a bundled plugin models —
|
|
1179
|
+
Rails, Devise, Pundit, Sidekiq, ... — while `plugins:` does not list
|
|
1180
|
+
that plugin ([ADR-96](../adr/96-plugin-target-gems.md)). An entry
|
|
1181
|
+
with `enabled: false` counts as listed. Each missing plugin is a
|
|
1182
|
+
warning, since leaving one out can be deliberate; it is a failure
|
|
1183
|
+
only when `plugins:` lists none of the bundled plugins that model
|
|
1184
|
+
gems (those declaring `target_gems:`).
|
|
1185
|
+
Skipped when no `.rigor.yml` or `.rigor.dist.yml` is discovered in
|
|
1186
|
+
the current directory.
|
|
1168
1187
|
- **Gemfile install** — whether Rigor itself resolves as one of the
|
|
1169
1188
|
project's dependencies, which
|
|
1170
1189
|
[Installing Rigor](01-installation.md) tells you not to do. Only a
|
|
@@ -1177,6 +1196,16 @@ Runs a scoped analysis and audits:
|
|
|
1177
1196
|
versioned together, so a copy from another installation can run
|
|
1178
1197
|
the engine with a mismatched plugin. A warning, not a failure;
|
|
1179
1198
|
third-party plugins from your own bundle are never flagged.
|
|
1199
|
+
- **Bundle layout** — whether Rigor can locate the bundle a
|
|
1200
|
+
`Gemfile.lock` in the current directory was installed into; when it
|
|
1201
|
+
cannot (typically gems in the active Ruby's default gem home), no
|
|
1202
|
+
gem-shipped `sig/` is discovered. Rigor finds a bundle only where the
|
|
1203
|
+
project records it. A `bundler.bundle_path:` in `.rigor.yml` is used
|
|
1204
|
+
alone and must exist. Without one — and unless
|
|
1205
|
+
`bundler.auto_detect: false` — Rigor reads `.bundle/config`'s
|
|
1206
|
+
`BUNDLE_PATH`, then an in-tree `vendor/bundle`, then the global
|
|
1207
|
+
Bundler config. A warning, not a failure: the default layout is
|
|
1208
|
+
correct, and most gems ship no `sig/`.
|
|
1180
1209
|
|
|
1181
1210
|
Text output prints `[PASS]`, `[FAIL]`, or `[WARN]` per check plus a
|
|
1182
1211
|
routed hint (e.g. "Run `rigor baseline regenerate`"). JSON output
|
|
@@ -1237,6 +1266,7 @@ diagnostics about Rigor's own inference cutoffs and memory — see
|
|
|
1237
1266
|
| `0` | Success — no diagnostic at or above the exit threshold (`error` by default; `rigor check --fail-on=SEVERITY` lowers it to `warning` or `info`). |
|
|
1238
1267
|
| `1` | Diagnostics found at or above the threshold, or a per-command failure (parse error, missing file, new diagnostics on `diff`, effect drift on `effects check`). |
|
|
1239
1268
|
| `64` | Usage error — unknown command, bad flag, malformed argument, or a value in `.rigor.yml` the loader cannot proceed on. |
|
|
1269
|
+
| `70` | Internal fatal error — `SystemStackError` or `NoMemoryError` inside the analysis; Rigor could not finish the run, so its report on disk is empty rather than clean. |
|
|
1240
1270
|
|
|
1241
1271
|
`rigor triage` is the exception: it is advisory and always
|
|
1242
1272
|
exits `0`.
|
|
@@ -128,18 +128,20 @@ These are warnings, not errors — partial or optional bundles and
|
|
|
128
128
|
forward-looking config are valid setups. The audit only fires on explicit,
|
|
129
129
|
working-setup-safe signals: an unset default (auto-detected `<root>/sig`,
|
|
130
130
|
auto-detected bundle) is never warned about, and a
|
|
131
|
-
`
|
|
132
|
-
`
|
|
133
|
-
|
|
134
|
-
closely, because what
|
|
131
|
+
`rbs_extended.…`, `rbs.…` or `dynamic.…` key in `severity_overrides:` or
|
|
132
|
+
`disable:` is left alone, since the engine emits those ids outside the
|
|
133
|
+
catalogue. `disable:` and `severity_overrides:` are both checked
|
|
134
|
+
closely, because what they can reach is known: a plugin's rule counts
|
|
135
135
|
only in its qualified `plugin.<id>.<rule>` form, so a bare plugin rule
|
|
136
136
|
(`rspec.let-binding`) is warned about. A qualified token is checked
|
|
137
137
|
against `plugins:` only when every entry's plugin ID is known without
|
|
138
138
|
loading it — an explicit `id:`, or a bundled plugin's gem name minus
|
|
139
139
|
`rigor-`; one third-party gem listed without `id:` turns that check
|
|
140
|
-
off. A token under `rbs_extended.…`, `rbs.…` or `dynamic.…`
|
|
141
|
-
alone, other than the reserved run-level rows (`rbs.coverage.…`,
|
|
142
|
-
`dynamic.dependency-source.…`), which are flagged
|
|
140
|
+
off. A `disable:` token under `rbs_extended.…`, `rbs.…` or `dynamic.…`
|
|
141
|
+
is left alone, other than the reserved run-level rows (`rbs.coverage.…`,
|
|
142
|
+
`dynamic.dependency-source.…`), which are flagged; in `severity_overrides:`
|
|
143
|
+
those rows are the intended keys and are never flagged, while a bare plugin
|
|
144
|
+
rule, `all`, or a `plugin.`-qualified key for an unconfigured plugin is. The same findings appear in the
|
|
143
145
|
`--format=json` payload under `config_warnings` (each tagged with a
|
|
144
146
|
`kind`), so CI can assert on them.
|
|
145
147
|
|
|
@@ -149,7 +151,7 @@ alone, other than the reserved run-level rows (`rbs.coverage.…`,
|
|
|
149
151
|
| --- | --- | --- | --- |
|
|
150
152
|
| `disable` | Array | `[]` | Rule IDs or families to suppress project-wide; `all` suppresses every built-in check rule. A plugin's diagnostics are named by qualified ID (`plugin.<id>.<rule>`, or `plugin.<id>` for the whole plugin); a bare ID or `all` does not reach them. The run-level `.rigor.yml` rows (`plugin_loader.*`, `dynamic.dependency-source.*`, `rbs.coverage.*`, `plugin_trust.*`, `pool-degraded`) are not reached by any spelling; use `severity_overrides:` for those. See [Diagnostics](04-diagnostics.md#suppressing-a-diagnostic). |
|
|
151
153
|
| `severity_profile` | String | `"balanced"` | `lenient`, `balanced`, or `strict` — see [Diagnostics](04-diagnostics.md). |
|
|
152
|
-
| `severity_overrides` | Hash | `{}` | Per-rule / per-family severity, e.g. `{ call: warning, flow.always-truthy-condition: off }`. |
|
|
154
|
+
| `severity_overrides` | Hash | `{}` | Per-rule / per-family severity, e.g. `{ call: warning, flow.always-truthy-condition: off }`. A plugin's diagnostics are named by qualified ID (`plugin.<id>.<rule>`, or `plugin.<id>` for the whole plugin); a bare rule or family does not reach them. The plugin loader's own rows are `plugin_loader.load-error` / `plugin_loader.runtime-error` (or the bare `load-error` / `runtime-error`). |
|
|
153
155
|
| `baseline` | String / `false` | `nil` | Path to a `.rigor-baseline.yml`, or `false` to disable an inherited one. See [Baselines](06-baseline.md). |
|
|
154
156
|
| `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). |
|
|
155
157
|
|
|
@@ -162,6 +162,15 @@ the diagnostic from the result entirely, which makes
|
|
|
162
162
|
below — both silence a rule; the override reads as "this one
|
|
163
163
|
rule, at this severity" alongside the rest of the profile.
|
|
164
164
|
|
|
165
|
+
A plugin's diagnostic is named by its qualified ID, as in `disable:`:
|
|
166
|
+
`plugin.<id>.<rule>` for one rule (`plugin.rails-i18n.load-error: off`),
|
|
167
|
+
or `plugin.<id>` for every row of that plugin. A bare rule or a family
|
|
168
|
+
never reaches a plugin's row, so `load-error: off` leaves each plugin's
|
|
169
|
+
own `load-error` alone. The plugin loader's rows are
|
|
170
|
+
`plugin_loader.load-error` and `plugin_loader.runtime-error`, which also
|
|
171
|
+
answer to the bare `load-error` / `runtime-error`. `rigor check` warns
|
|
172
|
+
about a key it can tell reaches no row.
|
|
173
|
+
|
|
165
174
|
YAML reserves the bareword `off` as a boolean. If an override
|
|
166
175
|
that names it seems not to apply, quote it — `"off"` — and the
|
|
167
176
|
same for `on`.
|
|
@@ -252,7 +261,8 @@ named rules on that line:
|
|
|
252
261
|
config.merge(extra) # rigor:disable call.undefined-method
|
|
253
262
|
```
|
|
254
263
|
|
|
255
|
-
It accepts qualified IDs, family wildcards (`call`
|
|
264
|
+
It accepts qualified IDs, family wildcards (`call`, `flow`, `def`, `assert`,
|
|
265
|
+
`dump`, `global`, `suppression`, `static`, `effect`, `pre-eval`), a
|
|
256
266
|
comma- or space-separated list, or `all`. A plugin's diagnostic
|
|
257
267
|
is reached only by its qualified ID as printed in brackets
|
|
258
268
|
(`plugin.shoulda-matchers.unknown-column`) or by the plugin's
|
|
@@ -332,10 +342,11 @@ gem that did not resolve (`dynamic.dependency-source.*`), the
|
|
|
332
342
|
under any spelling, `plugin_loader.load-error` included. Turn
|
|
333
343
|
those off with [`severity_overrides:`](#severity-profiles) set
|
|
334
344
|
to `off`, or record them in a [baseline](06-baseline.md).
|
|
335
|
-
`severity_overrides:`
|
|
336
|
-
|
|
337
|
-
`
|
|
338
|
-
|
|
345
|
+
In `severity_overrides:` the plugin loader's rows are
|
|
346
|
+
`plugin_loader.load-error` and `plugin_loader.runtime-error`, or the
|
|
347
|
+
bare `load-error` / `runtime-error` (the qualified key wins if both
|
|
348
|
+
are set); a bare key does not reach a
|
|
349
|
+
plugin's own row of the same name (`plugin.<id>.load-error`). `rigor check` warns when a
|
|
339
350
|
`disable:` entry names one of these rows, names no rule at all,
|
|
340
351
|
or names a plugin `plugins:` does not configure (see
|
|
341
352
|
[Config validation warnings](03-configuration.md#config-validation-warnings)).
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Rigor's analysis is invisible by default — it only speaks up to
|
|
4
4
|
report a diagnostic. When you want to *see* what type the
|
|
5
|
-
engine assigns an expression, there are
|
|
6
|
-
helpers and
|
|
5
|
+
engine assigns an expression, there are six tools: two source
|
|
6
|
+
helpers and four CLI commands.
|
|
7
7
|
|
|
8
8
|
## `dump_type` — print a type from source
|
|
9
9
|
|
data/docs/manual/06-baseline.md
CHANGED
|
@@ -73,7 +73,7 @@ a diagnostic another layer has already suppressed.
|
|
|
73
73
|
| --- | --- |
|
|
74
74
|
| `rigor baseline generate` | Write a fresh baseline from the current diagnostics. Refuses to clobber an existing file without `--force`. |
|
|
75
75
|
| `rigor baseline regenerate` | Rewrite unconditionally — run it after fixing diagnostics so the file shrinks. |
|
|
76
|
-
| `rigor baseline dump` | Print the baseline, filterable with `--rule` and `--file
|
|
76
|
+
| `rigor baseline dump` | Print the baseline, filterable with `--rule` and `--file` (`--format=json` for tooling). |
|
|
77
77
|
| `rigor baseline drift` | Show how buckets have moved — `--only=over` for buckets that grew, `reducible` for ones you have already improved past, `cleared` for empty ones. |
|
|
78
78
|
| `rigor baseline prune` | Drop buckets that match nothing any more. `--dry-run` previews. |
|
|
79
79
|
|
data/docs/manual/07-plugins.md
CHANGED
|
@@ -5,7 +5,8 @@ application DSL that plain inference cannot see — Rails route
|
|
|
5
5
|
helpers, RSpec `let` bindings, dry-rb struct attributes, and
|
|
6
6
|
so on. This page is about *activating* plugins. Writing one is
|
|
7
7
|
covered by [`examples/`](../../examples/README.md) and the
|
|
8
|
-
[`rigor-plugin-author` skill](08-skills.md)
|
|
8
|
+
[`rigor-plugin-author` skill](08-skills.md); reviewing and modernising
|
|
9
|
+
an existing plugin is [`rigor-plugin-review`](08-skills.md).
|
|
9
10
|
|
|
10
11
|
## Activating a plugin
|
|
11
12
|
|
data/docs/manual/08-skills.md
CHANGED
|
@@ -87,6 +87,10 @@ is not repeated here.)
|
|
|
87
87
|
- **`rigor-monkeypatch-resolve`** — resolves an `undefined-method`
|
|
88
88
|
cluster that is really your project's own monkey-patches by wiring the
|
|
89
89
|
defining files into `pre_eval:`.
|
|
90
|
+
- **`rigor-unused-adjudicate`** — adjudicates a `rigor unused` report
|
|
91
|
+
safely before proposing dead-code removals, checking framework roots
|
|
92
|
+
and false-positive shapes. See
|
|
93
|
+
[Removing dead code with `rigor unused`](18-removing-dead-code.md).
|
|
90
94
|
- **`rigor-type-oracle`** — sources every type an agent writes from
|
|
91
95
|
Rigor (`type-of` / `annotate` / `sig-gen`) instead of from reading the
|
|
92
96
|
code, and reports the gaps rather than filling them. It is triggered by
|
|
@@ -119,6 +123,9 @@ is not repeated here.)
|
|
|
119
123
|
repository to teach Rigor an application DSL or metaprogramming
|
|
120
124
|
pattern it cannot infer. Reach for it when no bundled plugin covers a
|
|
121
125
|
framework or DSL your project depends on.
|
|
126
|
+
- **`rigor-plugin-review`** — audits an existing Rigor plugin against
|
|
127
|
+
the current authoring contract and produces a prioritized upgrade
|
|
128
|
+
path. The review/upgrade counterpart to `rigor-plugin-author`.
|
|
122
129
|
|
|
123
130
|
## Discovering skills from the CLI
|
|
124
131
|
|
|
@@ -391,6 +391,13 @@ Generate RBS skeleton signatures inferred from Ruby source files.
|
|
|
391
391
|
project's test roots: the configured `test_paths:`, or whichever of
|
|
392
392
|
`spec/` and `test/` exist.
|
|
393
393
|
|
|
394
|
+
Under `params: "observed"` the response also carries a top-level
|
|
395
|
+
`observation` object, `{"paths": [...], "missing": [...]}`: the test
|
|
396
|
+
roots that were read and the declared ones that do not exist or that
|
|
397
|
+
the collector would not read (neither a directory nor a `.rb` file). Both
|
|
398
|
+
empty means no test root was found, so every parameter stays `untyped`.
|
|
399
|
+
The key is absent under the default `untyped` policy.
|
|
400
|
+
|
|
394
401
|
**Returns:** JSON — the same as `rigor sig-gen --print --format json`.
|
|
395
402
|
|
|
396
403
|
```json
|
|
@@ -430,7 +437,7 @@ Look up the description of one or all Rigor diagnostic rules.
|
|
|
430
437
|
|
|
431
438
|
Accepted values: a canonical rule ID (`call.undefined-method`), a
|
|
432
439
|
legacy alias (`undefined-method`), or a family prefix (`call`, `flow`,
|
|
433
|
-
`assert`, `dump`, `
|
|
440
|
+
`def`, `assert`, `dump`, `global`, `suppression`, `static`, `effect`).
|
|
434
441
|
|
|
435
442
|
**Returns:** JSON — an array of rule catalog entries.
|
|
436
443
|
|
|
@@ -6,7 +6,9 @@ into a finite list of specific questions. It does not answer them.
|
|
|
6
6
|
|
|
7
7
|
The command reference — every flag, every output section — is in
|
|
8
8
|
[CLI reference](02-cli-reference.md#rigor-unused). This chapter is the
|
|
9
|
-
workflow around it.
|
|
9
|
+
workflow around it. For an AI coding agent, the bundled
|
|
10
|
+
[`rigor-unused-adjudicate`](08-skills.md) skill walks through this
|
|
11
|
+
adjudication workflow step by step.
|
|
10
12
|
|
|
11
13
|
**Before you start** you need a working `rigor check` setup: a config
|
|
12
14
|
file with `paths:`, and the plugins for your framework enabled. See
|
|
@@ -270,12 +272,21 @@ implicit, `self` or `self.class` inside a method (or a block in a class
|
|
|
270
272
|
body, such as a lambda or `define_method`), the call may run on a
|
|
271
273
|
subclass or on an includer, so a `Handler` declared in a descendant is
|
|
272
274
|
kept reachable too. Descendants are the classes written `class D < Base`
|
|
273
|
-
|
|
274
|
-
`extend`
|
|
275
|
-
`K.
|
|
276
|
-
|
|
277
|
-
`Other.instance_eval { }` (or
|
|
278
|
-
`_exec` forms) the receiver is
|
|
275
|
+
or `K = Class.new(Base)`, and the classes that `include`, `prepend` or
|
|
276
|
+
`extend` the module in their body or through a receiver Rigor can name:
|
|
277
|
+
`K.include(Mix)` in an initializer, its `send`, `public_send` and
|
|
278
|
+
`__send__` spellings, or `include Mix` inside `K.class_eval { }`. A call written directly in a
|
|
279
|
+
class body is not widened. Inside `Other.instance_eval { }` (or
|
|
280
|
+
`class_eval`, `module_eval` and their `_exec` forms) the receiver is
|
|
281
|
+
`Other`, and a method the block defines (`def`, `define_method`,
|
|
282
|
+
`define_singleton_method`) runs on `Other` or a descendant. When the
|
|
283
|
+
receiver cannot be named at all, the name is kept wherever it is
|
|
284
|
+
declared: an eval block on a local variable (`k.class_eval { }`), the
|
|
285
|
+
module's own lookups when such a receiver includes it, or a class that
|
|
286
|
+
includes a computed module (`K.include(mods.first)`). A class that gains
|
|
287
|
+
a module through a receiver Rigor cannot name (`[Foo].each { |k|
|
|
288
|
+
k.include(Consts) }`, an `include` in a `Class.new` block) is not
|
|
289
|
+
followed for its own lookups of that module's constants. A name built into a
|
|
279
290
|
local variable first (`name = "V#{version}"; const_get(name)`) is read
|
|
280
291
|
the same way. A
|
|
281
292
|
class name appearing as a string in a `.yml`, `.json` or template file
|