rigortype 0.1.18 → 0.2.0
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 +159 -224
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +9 -3
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +25 -0
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +32 -23
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +54 -0
- data/lib/rigor/analysis/check_rules/rule_walk.rb +151 -23
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +24 -15
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +9 -3
- data/lib/rigor/analysis/check_rules.rb +756 -132
- data/lib/rigor/analysis/dependency_source_inference/index.rb +4 -7
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +2 -18
- data/lib/rigor/analysis/dependency_source_inference.rb +3 -12
- data/lib/rigor/analysis/diagnostic.rb +8 -0
- data/lib/rigor/analysis/fact_store.rb +5 -4
- data/lib/rigor/analysis/rule_catalog.rb +153 -6
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +19 -18
- data/lib/rigor/analysis/runner/project_pre_passes.rb +13 -9
- data/lib/rigor/analysis/runner.rb +75 -27
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +3 -4
- data/lib/rigor/analysis/worker_session.rb +31 -25
- data/lib/rigor/bleeding_edge.rb +123 -0
- data/lib/rigor/builtins/predefined_constant_refinements.rb +151 -0
- data/lib/rigor/cache/descriptor.rb +86 -8
- data/lib/rigor/cache/rbs_descriptor.rb +2 -1
- data/lib/rigor/cache/store.rb +5 -3
- data/lib/rigor/cli/annotate_command.rb +122 -16
- data/lib/rigor/cli/baseline_command.rb +4 -3
- data/lib/rigor/cli/check_command.rb +118 -16
- data/lib/rigor/cli/coverage_command.rb +148 -16
- data/lib/rigor/cli/coverage_scan.rb +57 -0
- data/lib/rigor/cli/explain_command.rb +2 -0
- data/lib/rigor/cli/lsp_command.rb +3 -7
- data/lib/rigor/cli/mutation_protection_renderer.rb +63 -0
- data/lib/rigor/cli/mutation_protection_report.rb +73 -0
- data/lib/rigor/cli/options.rb +9 -0
- data/lib/rigor/cli/plugins_command.rb +4 -5
- data/lib/rigor/cli/plugins_renderer.rb +0 -2
- data/lib/rigor/cli/protection_renderer.rb +63 -0
- data/lib/rigor/cli/protection_report.rb +68 -0
- data/lib/rigor/cli/show_bleedingedge_command.rb +114 -0
- data/lib/rigor/cli/sig_gen_command.rb +2 -1
- data/lib/rigor/cli/trace_command.rb +2 -1
- data/lib/rigor/cli/triage_command.rb +8 -4
- data/lib/rigor/cli/triage_renderer.rb +15 -1
- data/lib/rigor/cli/type_of_command.rb +1 -1
- data/lib/rigor/cli/type_scan_command.rb +2 -1
- data/lib/rigor/cli.rb +12 -3
- data/lib/rigor/configuration/dependencies.rb +2 -4
- data/lib/rigor/configuration/severity_profile.rb +13 -1
- data/lib/rigor/configuration.rb +100 -6
- data/lib/rigor/environment/bundle_sig_discovery.rb +61 -13
- data/lib/rigor/environment/class_registry.rb +4 -3
- data/lib/rigor/environment/constant_type_cache_holder.rb +43 -0
- data/lib/rigor/environment/lockfile_resolver.rb +1 -1
- data/lib/rigor/environment/rbs_collection_discovery.rb +1 -2
- data/lib/rigor/environment/rbs_coverage_report.rb +2 -1
- data/lib/rigor/environment/rbs_loader.rb +74 -5
- data/lib/rigor/environment.rb +17 -7
- data/lib/rigor/flow_contribution/fact.rb +1 -1
- data/lib/rigor/flow_contribution.rb +3 -5
- data/lib/rigor/inference/acceptance.rb +17 -9
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/body_fixpoint.rb +89 -0
- data/lib/rigor/inference/budget_trace.rb +29 -2
- data/lib/rigor/inference/builtins/comparable_catalog.rb +2 -2
- data/lib/rigor/inference/builtins/enumerable_catalog.rb +2 -2
- data/lib/rigor/inference/builtins/method_catalog.rb +19 -0
- data/lib/rigor/inference/builtins/string_catalog.rb +9 -1
- data/lib/rigor/inference/expression_typer.rb +1072 -71
- data/lib/rigor/inference/hkt_body.rb +8 -11
- data/lib/rigor/inference/hkt_body_parser.rb +10 -12
- data/lib/rigor/inference/hkt_registry.rb +10 -11
- data/lib/rigor/inference/macro_block_self_type.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/array_to_h_folding.rb +60 -0
- data/lib/rigor/inference/method_dispatcher/call_context.rb +1 -4
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +210 -35
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +9 -73
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +6 -7
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +10 -16
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +25 -13
- data/lib/rigor/inference/method_dispatcher/member_shape_projection.rb +93 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +1 -3
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +24 -22
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +281 -0
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +71 -0
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +237 -24
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +303 -0
- data/lib/rigor/inference/method_dispatcher.rb +112 -49
- data/lib/rigor/inference/method_parameter_binder.rb +56 -2
- data/lib/rigor/inference/multi_target_binder.rb +46 -3
- data/lib/rigor/inference/mutation_widening.rb +147 -11
- data/lib/rigor/inference/narrowing.rb +284 -53
- data/lib/rigor/inference/parameter_inference_collector.rb +367 -0
- data/lib/rigor/inference/project_patched_methods.rb +4 -7
- data/lib/rigor/inference/project_patched_scanner.rb +2 -13
- data/lib/rigor/inference/protection_scanner.rb +86 -0
- data/lib/rigor/inference/scope_indexer.rb +821 -76
- data/lib/rigor/inference/statement_evaluator.rb +1179 -102
- data/lib/rigor/inference/struct_fold_safety.rb +181 -0
- data/lib/rigor/inference/synthetic_method.rb +7 -7
- data/lib/rigor/inference/synthetic_method_scanner.rb +1 -1
- data/lib/rigor/language_server/completion_provider.rb +6 -12
- data/lib/rigor/language_server/diagnostic_publisher.rb +4 -4
- data/lib/rigor/language_server/document_symbol_provider.rb +3 -3
- data/lib/rigor/language_server/hover_provider.rb +2 -3
- data/lib/rigor/language_server/hover_renderer.rb +2 -11
- data/lib/rigor/language_server/server.rb +9 -17
- data/lib/rigor/language_server.rb +4 -5
- data/lib/rigor/plugin/base.rb +245 -87
- data/lib/rigor/plugin/macro/block_as_method.rb +25 -25
- data/lib/rigor/plugin/macro/heredoc_template.rb +4 -7
- data/lib/rigor/plugin/macro/nested_class_template.rb +9 -7
- data/lib/rigor/plugin/macro/trait_registry.rb +3 -6
- data/lib/rigor/plugin/macro.rb +6 -8
- data/lib/rigor/plugin/manifest.rb +49 -90
- data/lib/rigor/plugin/node_rule_walk.rb +59 -14
- data/lib/rigor/plugin/registry.rb +18 -18
- data/lib/rigor/plugin/type_node_resolver.rb +6 -8
- data/lib/rigor/protection/mutation_scanner.rb +120 -0
- data/lib/rigor/protection/mutator.rb +246 -0
- data/lib/rigor/rbs_extended.rb +24 -36
- data/lib/rigor/reflection.rb +4 -7
- data/lib/rigor/scope/discovery_index.rb +16 -2
- data/lib/rigor/scope.rb +185 -16
- data/lib/rigor/sig_gen/generator.rb +8 -0
- data/lib/rigor/sig_gen/observed_call.rb +3 -3
- data/lib/rigor/sig_gen/writer.rb +40 -2
- data/lib/rigor/source/constant_path.rb +62 -0
- data/lib/rigor/source.rb +1 -0
- data/lib/rigor/triage/catalogue.rb +4 -19
- data/lib/rigor/triage.rb +69 -1
- data/lib/rigor/type/bound_method.rb +2 -11
- data/lib/rigor/type/combinator.rb +45 -3
- data/lib/rigor/type/constant.rb +2 -11
- data/lib/rigor/type/data_class.rb +2 -11
- data/lib/rigor/type/data_instance.rb +2 -11
- data/lib/rigor/type/hash_shape.rb +2 -11
- data/lib/rigor/type/integer_range.rb +2 -11
- data/lib/rigor/type/intersection.rb +2 -11
- data/lib/rigor/type/nominal.rb +2 -11
- data/lib/rigor/type/plain_lattice.rb +37 -0
- data/lib/rigor/type/refined.rb +72 -13
- data/lib/rigor/type/singleton.rb +2 -11
- data/lib/rigor/type/struct_class.rb +75 -0
- data/lib/rigor/type/struct_instance.rb +93 -0
- data/lib/rigor/type/tuple.rb +5 -15
- data/lib/rigor/type.rb +2 -0
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +3 -3
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +16 -32
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +5 -13
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +13 -32
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +11 -17
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +34 -100
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +3 -2
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +13 -30
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +4 -4
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +26 -27
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +5 -7
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +9 -8
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +9 -11
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +8 -9
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +13 -12
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +3 -4
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +8 -8
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +9 -11
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +7 -8
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +18 -49
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +12 -13
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +15 -23
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +4 -4
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +3 -3
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +10 -21
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +2 -4
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +27 -11
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +22 -35
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +4 -6
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +12 -18
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +16 -23
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +0 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +3 -4
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +1 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +1 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +21 -27
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +0 -1
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +3 -23
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +5 -4
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +8 -21
- data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +1 -1
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +2 -3
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/method_signature.rb +7 -11
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +4 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +6 -9
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +5 -15
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +52 -40
- data/sig/rigor/analysis/fact_store.rbs +3 -0
- data/sig/rigor/inference/builtins/method_catalog.rbs +1 -1
- data/sig/rigor/plugin/base.rbs +5 -2
- data/sig/rigor/plugin/manifest.rbs +1 -2
- data/sig/rigor/scope.rbs +18 -1
- data/sig/rigor/type.rbs +37 -1
- data/sig/rigor.rbs +1 -1
- data/skills/rigor-baseline-reduce/references/01-classify.md +27 -0
- data/skills/rigor-plugin-author/SKILL.md +6 -4
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +22 -17
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +18 -1
- metadata +25 -2
- data/lib/rigor/plugin/macro/external_file.rb +0 -143
|
@@ -98,9 +98,11 @@ AST walk per file — hands every matching node to the block along with a
|
|
|
98
98
|
`Rigor::Analysis::Diagnostic` (built via the `diagnostic` helper).
|
|
99
99
|
Optionally the plugin also declares `dynamic_return(receivers:)` /
|
|
100
100
|
`type_specifier(methods:)` to *supply* a return type or narrowing facts
|
|
101
|
-
for call sites the core analyzer types as `Dynamic`.
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
for call sites the core analyzer types as `Dynamic`. `#diagnostics_for_file`
|
|
102
|
+
is the file-rule surface for whole-file diagnostics a per-node walk can't
|
|
103
|
+
express. (`flow_contribution_for` was removed pre-1.0 in ADR-52 WD3 —
|
|
104
|
+
defining it now raises `ArgumentError`; use `dynamic_return` /
|
|
105
|
+
`type_specifier`. See Phase 2.)
|
|
104
106
|
|
|
105
107
|
## Phase outline
|
|
106
108
|
|
|
@@ -115,5 +117,5 @@ deprecated escape valves — see Phase 2.)
|
|
|
115
117
|
| Module | Read | Covers |
|
|
116
118
|
| --- | --- | --- |
|
|
117
119
|
| 1 | [`references/01-plan-and-scaffold.md`](references/01-plan-and-scaffold.md) | **Phase 1.** The gem vs project-private packaging split, directory trees for both, gemspec template, project-private path-gem / `RUBYLIB` activation, the `Rigor::Plugin::Base` skeleton, `.rigor.yml` `plugins:` wiring. |
|
|
118
|
-
| 2 | [`references/02-walker-and-types.md`](references/02-walker-and-types.md) | **Phase 2.** The `node_rule` engine-owned AST walk over Prism nodes, the `Base#diagnostic` helper, asking the analyzer for inferred types via `scope.type_of`, two-pass / lexical context (`node_file_context` / `NodeContext`), the optional `dynamic_return` / `type_specifier` return-type hooks (
|
|
120
|
+
| 2 | [`references/02-walker-and-types.md`](references/02-walker-and-types.md) | **Phase 2.** The `node_rule` engine-owned AST walk over Prism nodes, the `Base#diagnostic` helper, asking the analyzer for inferred types via `scope.type_of`, two-pass / lexical context (`node_file_context` / `NodeContext`), the optional `dynamic_return` / `type_specifier` return-type hooks (`flow_contribution_for` was removed pre-1.0 in ADR-52 WD3), calling the target library's pure methods directly rather than reimplementing them (ADR-39: `Plugin::Inflector` over the real `ActiveSupport::Inflector`; `Base.suggest` for did-you-mean), and shipping `sig/*.rbs` so the DSL's types are visible. |
|
|
119
121
|
| 3 | [`references/03-test-and-ship.md`](references/03-test-and-ship.md) | **Phase 3.** Testing a plugin from outside the monorepo — fixture projects driven through `rigor check --format json`, plus pure unit tests of dispatch tables — with RSpec or Minitest. Version pinning against the pre-1.0 contract. README. Publishing to RubyGems or keeping the plugin private. |
|
|
@@ -37,12 +37,15 @@ rows the rule positions with `diagnostic` (the bundled plugins follow
|
|
|
37
37
|
this `Analyzer.violations_for` split, which also makes the logic
|
|
38
38
|
unit-testable without running the whole engine).
|
|
39
39
|
|
|
40
|
-
>
|
|
41
|
-
>
|
|
42
|
-
>
|
|
43
|
-
>
|
|
44
|
-
>
|
|
45
|
-
>
|
|
40
|
+
> `diagnostics_for_file(path:, scope:, root:)` is the **file-rule**
|
|
41
|
+
> surface — use it for a genuinely file-scoped result (a single
|
|
42
|
+
> load-error row, a cross-file aggregation, or a check that needs the
|
|
43
|
+
> whole parsed file at once). It is not deprecated; it is the right tool
|
|
44
|
+
> when a check isn't per-node. Per-node checks use `node_rule` (you don't
|
|
45
|
+
> hand-roll a `def walk` / `compact_child_nodes.each` recursion — the
|
|
46
|
+
> engine owns the walk). Map a violation array to diagnostics with
|
|
47
|
+
> `diagnostics_for(violations, path:, node:)` rather than a hand-rolled
|
|
48
|
+
> `.map { diagnostic(...) }`.
|
|
46
49
|
|
|
47
50
|
### Recognising call sites
|
|
48
51
|
|
|
@@ -59,7 +62,9 @@ re-deriving the `unescaped.to_sym` shape.
|
|
|
59
62
|
validate references): declare `node_file_context { |root, scope| … }`.
|
|
60
63
|
It runs once per file before the walk and its return value is threaded
|
|
61
64
|
to every rule as `file_context`. (A *cross-file* collect belongs in
|
|
62
|
-
`#prepare` + `services.fact_store
|
|
65
|
+
`#prepare` + `services.fact_store.publish`; the consuming plugin reads
|
|
66
|
+
it with `read_fact(plugin_id:, name:)` — the nil-inclusive memo means
|
|
67
|
+
you never hand-roll an `@x_resolved` flag. See
|
|
63
68
|
[`01-plan-and-scaffold.md`](01-plan-and-scaffold.md).)
|
|
64
69
|
- **Where the node sits**: `context` (a `Rigor::Plugin::NodeContext`)
|
|
65
70
|
carries the lexical ancestor chain — `context.enclosing_def`,
|
|
@@ -213,16 +218,16 @@ plugin is confident; a wrong contribution propagates downstream.
|
|
|
213
218
|
`rigor plugins --capabilities` catalogue enumerates — run it to see
|
|
214
219
|
exactly what each loaded plugin contributes.
|
|
215
220
|
|
|
216
|
-
> **The
|
|
217
|
-
>
|
|
218
|
-
> `
|
|
219
|
-
>
|
|
220
|
-
>
|
|
221
|
-
>
|
|
222
|
-
>
|
|
223
|
-
> (ActiveStorage's `Attached::One`
|
|
224
|
-
>
|
|
225
|
-
>
|
|
221
|
+
> **The removed fat hook.** The original `flow_contribution_for(call_node:,
|
|
222
|
+
> scope:)` was **deleted pre-1.0 in ADR-52 WD3** — defining it now raises
|
|
223
|
+
> `ArgumentError` at load time. The two shapes it used to cover are
|
|
224
|
+
> expressed by `dynamic_return`'s callable gates: a **method-gated return
|
|
225
|
+
> type** (an RSpec `let(:x) { … }` binding, a Sorbet `sig`-driven return —
|
|
226
|
+
> keyed on the method, not a fixed receiver class) uses
|
|
227
|
+
> `dynamic_return methods: -> { [...] }` or `file_methods: ->(path) { [...] }`;
|
|
228
|
+
> a **dynamic per-project receiver set** (ActiveStorage's `Attached::One`
|
|
229
|
+
> on discovered model classes) uses `dynamic_return receivers: -> { [...] }`,
|
|
230
|
+
> the callable resolved once after `#prepare`. These return-type surfaces
|
|
226
231
|
> are the most contract-sensitive part of the API — implement one only
|
|
227
232
|
> if the plugin genuinely needs to sharpen call-site types; a
|
|
228
233
|
> diagnostics-only plugin skips them entirely.
|
|
@@ -23,6 +23,8 @@ The JSON shape:
|
|
|
23
23
|
{
|
|
24
24
|
"summary": { "total": 489, "error": 480, "warning": 9, "info": 0 },
|
|
25
25
|
"distribution": [ { "rule": "call.undefined-method", "count": 437 } ],
|
|
26
|
+
"selectors": [ { "receiver": "String", "method": "squish", "count": 31,
|
|
27
|
+
"files": 12, "rules": { "call.undefined-method": 31 } } ],
|
|
26
28
|
"hotspots": [ { "file": "app/models/status.rb", "count": 42,
|
|
27
29
|
"by_rule": { "call.undefined-method": 40 } } ],
|
|
28
30
|
"hints": [
|
|
@@ -32,11 +34,26 @@ The JSON shape:
|
|
|
32
34
|
}
|
|
33
35
|
```
|
|
34
36
|
|
|
35
|
-
Use the
|
|
37
|
+
Use the sections like this:
|
|
36
38
|
|
|
37
39
|
- **`summary` / `distribution`** — the scale, and which rules
|
|
38
40
|
dominate. Decides nothing on its own; feeds the mode sanity-check
|
|
39
41
|
(>100 errors → acknowledge mode is the right default).
|
|
42
|
+
- **`selectors`** — the by-(class, method) axis. Each row is a
|
|
43
|
+
dispatch target (`String#squish`) with its `count`, the `files` it
|
|
44
|
+
spans, and the `rules` that fired. Read it with `jq` to find the
|
|
45
|
+
*shape* of the problem before touching code — these are structured
|
|
46
|
+
fields, never parse the `message`:
|
|
47
|
+
```sh
|
|
48
|
+
# the 10 methods responsible for the most diagnostics
|
|
49
|
+
rigor triage --format json | jq -r '.selectors[:10][] | "\(.count)\t\(.receiver)#\(.method)"'
|
|
50
|
+
# methods missing on the same receiver across many files = one config
|
|
51
|
+
# gap (an unloaded core-ext / unseen monkey-patch), not many bugs
|
|
52
|
+
rigor triage --format json | jq '.selectors[] | select(.files >= 5)'
|
|
53
|
+
```
|
|
54
|
+
A high `count` + high `files` selector is almost always a *systemic
|
|
55
|
+
cause* (a plugin / `pre_eval:` fix clears it in bulk); a low `count`
|
|
56
|
+
selector is a candidate genuine bug.
|
|
40
57
|
- **`hotspots`** — files carrying the most diagnostics. A single hot
|
|
41
58
|
file is often one structural cause, not many bugs.
|
|
42
59
|
- **`hints`** — the heuristic catalogue. Each hint names a *likely
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rigortype
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Rigor contributors
|
|
@@ -289,6 +289,7 @@ files:
|
|
|
289
289
|
- lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb
|
|
290
290
|
- lib/rigor/analysis/check_rules/dead_assignment_collector.rb
|
|
291
291
|
- lib/rigor/analysis/check_rules/ivar_write_collector.rb
|
|
292
|
+
- lib/rigor/analysis/check_rules/main_pass_collector.rb
|
|
292
293
|
- lib/rigor/analysis/check_rules/rule_walk.rb
|
|
293
294
|
- lib/rigor/analysis/check_rules/self_closedness_scanner.rb
|
|
294
295
|
- lib/rigor/analysis/check_rules/unreachable_clause_collector.rb
|
|
@@ -318,8 +319,10 @@ files:
|
|
|
318
319
|
- lib/rigor/analysis/worker_session.rb
|
|
319
320
|
- lib/rigor/ast.rb
|
|
320
321
|
- lib/rigor/ast/type_node.rb
|
|
322
|
+
- lib/rigor/bleeding_edge.rb
|
|
321
323
|
- lib/rigor/builtins/hkt_builtins.rb
|
|
322
324
|
- lib/rigor/builtins/imported_refinements.rb
|
|
325
|
+
- lib/rigor/builtins/predefined_constant_refinements.rb
|
|
323
326
|
- lib/rigor/builtins/regex_refinement.rb
|
|
324
327
|
- lib/rigor/builtins/static_return_refinements.rb
|
|
325
328
|
- lib/rigor/cache/descriptor.rb
|
|
@@ -342,17 +345,23 @@ files:
|
|
|
342
345
|
- lib/rigor/cli/coverage_command.rb
|
|
343
346
|
- lib/rigor/cli/coverage_renderer.rb
|
|
344
347
|
- lib/rigor/cli/coverage_report.rb
|
|
348
|
+
- lib/rigor/cli/coverage_scan.rb
|
|
345
349
|
- lib/rigor/cli/diagnostic_formats.rb
|
|
346
350
|
- lib/rigor/cli/diff_command.rb
|
|
347
351
|
- lib/rigor/cli/explain_command.rb
|
|
348
352
|
- lib/rigor/cli/lsp_command.rb
|
|
349
353
|
- lib/rigor/cli/mcp_command.rb
|
|
354
|
+
- lib/rigor/cli/mutation_protection_renderer.rb
|
|
355
|
+
- lib/rigor/cli/mutation_protection_report.rb
|
|
350
356
|
- lib/rigor/cli/options.rb
|
|
351
357
|
- lib/rigor/cli/plugin_command.rb
|
|
352
358
|
- lib/rigor/cli/plugins_command.rb
|
|
353
359
|
- lib/rigor/cli/plugins_renderer.rb
|
|
354
360
|
- lib/rigor/cli/prism_colorizer.rb
|
|
361
|
+
- lib/rigor/cli/protection_renderer.rb
|
|
362
|
+
- lib/rigor/cli/protection_report.rb
|
|
355
363
|
- lib/rigor/cli/renderable.rb
|
|
364
|
+
- lib/rigor/cli/show_bleedingedge_command.rb
|
|
356
365
|
- lib/rigor/cli/sig_gen_command.rb
|
|
357
366
|
- lib/rigor/cli/skill_command.rb
|
|
358
367
|
- lib/rigor/cli/trace_command.rb
|
|
@@ -370,6 +379,7 @@ files:
|
|
|
370
379
|
- lib/rigor/environment.rb
|
|
371
380
|
- lib/rigor/environment/bundle_sig_discovery.rb
|
|
372
381
|
- lib/rigor/environment/class_registry.rb
|
|
382
|
+
- lib/rigor/environment/constant_type_cache_holder.rb
|
|
373
383
|
- lib/rigor/environment/hkt_registry_holder.rb
|
|
374
384
|
- lib/rigor/environment/lockfile_resolver.rb
|
|
375
385
|
- lib/rigor/environment/rbs_collection_discovery.rb
|
|
@@ -386,6 +396,7 @@ files:
|
|
|
386
396
|
- lib/rigor/flow_contribution/merger.rb
|
|
387
397
|
- lib/rigor/inference/acceptance.rb
|
|
388
398
|
- lib/rigor/inference/block_parameter_binder.rb
|
|
399
|
+
- lib/rigor/inference/body_fixpoint.rb
|
|
389
400
|
- lib/rigor/inference/budget_trace.rb
|
|
390
401
|
- lib/rigor/inference/builtins/array_catalog.rb
|
|
391
402
|
- lib/rigor/inference/builtins/comparable_catalog.rb
|
|
@@ -421,6 +432,7 @@ files:
|
|
|
421
432
|
- lib/rigor/inference/indexed_narrowing.rb
|
|
422
433
|
- lib/rigor/inference/macro_block_self_type.rb
|
|
423
434
|
- lib/rigor/inference/method_dispatcher.rb
|
|
435
|
+
- lib/rigor/inference/method_dispatcher/array_to_h_folding.rb
|
|
424
436
|
- lib/rigor/inference/method_dispatcher/block_folding.rb
|
|
425
437
|
- lib/rigor/inference/method_dispatcher/call_context.rb
|
|
426
438
|
- lib/rigor/inference/method_dispatcher/cgi_folding.rb
|
|
@@ -431,27 +443,33 @@ files:
|
|
|
431
443
|
- lib/rigor/inference/method_dispatcher/kernel_dispatch.rb
|
|
432
444
|
- lib/rigor/inference/method_dispatcher/literal_string_folding.rb
|
|
433
445
|
- lib/rigor/inference/method_dispatcher/math_folding.rb
|
|
446
|
+
- lib/rigor/inference/method_dispatcher/member_shape_projection.rb
|
|
434
447
|
- lib/rigor/inference/method_dispatcher/method_folding.rb
|
|
435
448
|
- lib/rigor/inference/method_dispatcher/overload_selector.rb
|
|
436
449
|
- lib/rigor/inference/method_dispatcher/rbs_dispatch.rb
|
|
437
450
|
- lib/rigor/inference/method_dispatcher/receiver_affinity.rb
|
|
451
|
+
- lib/rigor/inference/method_dispatcher/reduce_folding.rb
|
|
438
452
|
- lib/rigor/inference/method_dispatcher/regexp_folding.rb
|
|
439
453
|
- lib/rigor/inference/method_dispatcher/set_folding.rb
|
|
440
454
|
- lib/rigor/inference/method_dispatcher/shape_dispatch.rb
|
|
441
455
|
- lib/rigor/inference/method_dispatcher/shellwords_folding.rb
|
|
442
456
|
- lib/rigor/inference/method_dispatcher/singleton_folding.rb
|
|
457
|
+
- lib/rigor/inference/method_dispatcher/struct_folding.rb
|
|
443
458
|
- lib/rigor/inference/method_dispatcher/time_folding.rb
|
|
444
459
|
- lib/rigor/inference/method_dispatcher/uri_folding.rb
|
|
445
460
|
- lib/rigor/inference/method_parameter_binder.rb
|
|
446
461
|
- lib/rigor/inference/multi_target_binder.rb
|
|
447
462
|
- lib/rigor/inference/mutation_widening.rb
|
|
448
463
|
- lib/rigor/inference/narrowing.rb
|
|
464
|
+
- lib/rigor/inference/parameter_inference_collector.rb
|
|
449
465
|
- lib/rigor/inference/precision_scanner.rb
|
|
450
466
|
- lib/rigor/inference/project_patched_methods.rb
|
|
451
467
|
- lib/rigor/inference/project_patched_scanner.rb
|
|
468
|
+
- lib/rigor/inference/protection_scanner.rb
|
|
452
469
|
- lib/rigor/inference/rbs_type_translator.rb
|
|
453
470
|
- lib/rigor/inference/scope_indexer.rb
|
|
454
471
|
- lib/rigor/inference/statement_evaluator.rb
|
|
472
|
+
- lib/rigor/inference/struct_fold_safety.rb
|
|
455
473
|
- lib/rigor/inference/synthetic_method.rb
|
|
456
474
|
- lib/rigor/inference/synthetic_method_index.rb
|
|
457
475
|
- lib/rigor/inference/synthetic_method_scanner.rb
|
|
@@ -489,7 +507,6 @@ files:
|
|
|
489
507
|
- lib/rigor/plugin/loader.rb
|
|
490
508
|
- lib/rigor/plugin/macro.rb
|
|
491
509
|
- lib/rigor/plugin/macro/block_as_method.rb
|
|
492
|
-
- lib/rigor/plugin/macro/external_file.rb
|
|
493
510
|
- lib/rigor/plugin/macro/heredoc_template.rb
|
|
494
511
|
- lib/rigor/plugin/macro/nested_class_template.rb
|
|
495
512
|
- lib/rigor/plugin/macro/trait_registry.rb
|
|
@@ -502,6 +519,8 @@ files:
|
|
|
502
519
|
- lib/rigor/plugin/source_rbs_synthesis_reporter.rb
|
|
503
520
|
- lib/rigor/plugin/trust_policy.rb
|
|
504
521
|
- lib/rigor/plugin/type_node_resolver.rb
|
|
522
|
+
- lib/rigor/protection/mutation_scanner.rb
|
|
523
|
+
- lib/rigor/protection/mutator.rb
|
|
505
524
|
- lib/rigor/rbs_extended.rb
|
|
506
525
|
- lib/rigor/rbs_extended/conformance_checker.rb
|
|
507
526
|
- lib/rigor/rbs_extended/hkt_directives.rb
|
|
@@ -522,6 +541,7 @@ files:
|
|
|
522
541
|
- lib/rigor/sig_gen/write_result.rb
|
|
523
542
|
- lib/rigor/sig_gen/writer.rb
|
|
524
543
|
- lib/rigor/source.rb
|
|
544
|
+
- lib/rigor/source/constant_path.rb
|
|
525
545
|
- lib/rigor/source/literals.rb
|
|
526
546
|
- lib/rigor/source/node_locator.rb
|
|
527
547
|
- lib/rigor/source/node_walker.rb
|
|
@@ -546,8 +566,11 @@ files:
|
|
|
546
566
|
- lib/rigor/type/integer_range.rb
|
|
547
567
|
- lib/rigor/type/intersection.rb
|
|
548
568
|
- lib/rigor/type/nominal.rb
|
|
569
|
+
- lib/rigor/type/plain_lattice.rb
|
|
549
570
|
- lib/rigor/type/refined.rb
|
|
550
571
|
- lib/rigor/type/singleton.rb
|
|
572
|
+
- lib/rigor/type/struct_class.rb
|
|
573
|
+
- lib/rigor/type/struct_instance.rb
|
|
551
574
|
- lib/rigor/type/top.rb
|
|
552
575
|
- lib/rigor/type/tuple.rb
|
|
553
576
|
- lib/rigor/type/union.rb
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Rigor
|
|
4
|
-
module Plugin
|
|
5
|
-
module Macro
|
|
6
|
-
# ADR-16 Tier D declaration: "files matching `glob` are
|
|
7
|
-
# analysed as if their body were pasted at a call site whose
|
|
8
|
-
# `self` is an instance of `receiver_type` (and whose `@ivar`
|
|
9
|
-
# facts come from `bound_ivars`)."
|
|
10
|
-
#
|
|
11
|
-
# Worked motivating cases (per the per-library survey):
|
|
12
|
-
#
|
|
13
|
-
# - Redmine's `WebhookPayload#instance_eval(File.read(path), path, 1)`
|
|
14
|
-
# at `app/models/webhook_payload.rb:71`. The payload templates
|
|
15
|
-
# under `config/webhooks/*.rb` run with `self` typed as
|
|
16
|
-
# `Redmine::WebhookPayload` and ivars like `@event` / `@issue`
|
|
17
|
-
# / `@user` pre-bound by the caller.
|
|
18
|
-
# - tDiary Core's plugin loader pattern — `misc/plugin/*.rb`
|
|
19
|
-
# files loaded under `instance_eval` with the tDiary plugin
|
|
20
|
-
# instance as `self`.
|
|
21
|
-
#
|
|
22
|
-
# ## Authoring shape
|
|
23
|
-
#
|
|
24
|
-
# manifest(
|
|
25
|
-
# id: "redmine-webhook-payloads",
|
|
26
|
-
# version: "0.1.0",
|
|
27
|
-
# external_files: [
|
|
28
|
-
# Rigor::Plugin::Macro::ExternalFile.new(
|
|
29
|
-
# glob: "config/webhooks/*.rb",
|
|
30
|
-
# receiver_type: "Redmine::WebhookPayload",
|
|
31
|
-
# bound_ivars: {
|
|
32
|
-
# "@event" => "Symbol",
|
|
33
|
-
# "@issue" => "Issue?",
|
|
34
|
-
# "@user" => "User"
|
|
35
|
-
# }
|
|
36
|
-
# )
|
|
37
|
-
# ]
|
|
38
|
-
# )
|
|
39
|
-
#
|
|
40
|
-
# ## Fields
|
|
41
|
-
#
|
|
42
|
-
# - `glob` — non-empty String pattern. Interpreted relative
|
|
43
|
-
# to the project root (the directory containing `.rigor.yml`)
|
|
44
|
-
# at scan time. Slice 5a accepts any non-empty glob
|
|
45
|
-
# pattern syntactically; the engine integration (slice 5b)
|
|
46
|
-
# pins the resolution rule.
|
|
47
|
-
# - `receiver_type` — non-empty String. The class name `self`
|
|
48
|
-
# inside the loaded file binds to. Engine integration (slice
|
|
49
|
-
# 5b) narrows the file-entry scope's `self_type` to
|
|
50
|
-
# `Nominal[receiver_type]`.
|
|
51
|
-
# - `bound_ivars` — Hash<String, String>. Each key MUST start
|
|
52
|
-
# with `@`; each value is a non-empty type-name String. The
|
|
53
|
-
# engine pre-binds these as ivar facts in the file-entry
|
|
54
|
-
# scope (slice 5b).
|
|
55
|
-
#
|
|
56
|
-
# ## Slice 5a scope
|
|
57
|
-
#
|
|
58
|
-
# **This file ships the value class + manifest hook ONLY.**
|
|
59
|
-
# The engine integration that (a) adds matched files to the
|
|
60
|
-
# analysis set, (b) narrows the file-entry `self_type`, and
|
|
61
|
-
# (c) pre-binds `bound_ivars` as ivar facts is **queued for
|
|
62
|
-
# slice 5b**, gated on demonstrated demand. The survey
|
|
63
|
-
# identifies only Redmine + tDiary as concrete consumers;
|
|
64
|
-
# premature engine work is deferred until those cases (or
|
|
65
|
-
# equivalents) materialise as committed plugin targets.
|
|
66
|
-
#
|
|
67
|
-
# With only this slice landed, plugin authors CAN declare a
|
|
68
|
-
# Tier D manifest entry today — the declaration round-trips
|
|
69
|
-
# through `Manifest#to_h` (cache-key stable) and is exposed
|
|
70
|
-
# on `Manifest#external_files` — but the substrate does not
|
|
71
|
-
# yet act on it. The contract is forward-compatible: when
|
|
72
|
-
# slice 5b lands, the engine reads the same declarations and
|
|
73
|
-
# plugin gems do not need to change.
|
|
74
|
-
class ExternalFile
|
|
75
|
-
attr_reader :glob, :receiver_type, :bound_ivars
|
|
76
|
-
|
|
77
|
-
def initialize(glob:, receiver_type:, bound_ivars: {})
|
|
78
|
-
validate_glob!(glob)
|
|
79
|
-
validate_receiver_type!(receiver_type)
|
|
80
|
-
validate_bound_ivars!(bound_ivars)
|
|
81
|
-
|
|
82
|
-
@glob = glob.dup.freeze
|
|
83
|
-
@receiver_type = receiver_type.dup.freeze
|
|
84
|
-
@bound_ivars = bound_ivars.to_h { |k, v| [k.dup.freeze, v.dup.freeze] }.freeze
|
|
85
|
-
freeze
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def to_h
|
|
89
|
-
{
|
|
90
|
-
"glob" => glob,
|
|
91
|
-
"receiver_type" => receiver_type,
|
|
92
|
-
"bound_ivars" => bound_ivars
|
|
93
|
-
}
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
def ==(other)
|
|
97
|
-
other.is_a?(ExternalFile) && to_h == other.to_h
|
|
98
|
-
end
|
|
99
|
-
alias eql? ==
|
|
100
|
-
|
|
101
|
-
def hash
|
|
102
|
-
to_h.hash
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
private
|
|
106
|
-
|
|
107
|
-
def validate_glob!(value)
|
|
108
|
-
return if value.is_a?(String) && !value.empty?
|
|
109
|
-
|
|
110
|
-
raise ArgumentError,
|
|
111
|
-
"Plugin::Macro::ExternalFile#glob must be a non-empty String, got #{value.inspect}"
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
def validate_receiver_type!(value)
|
|
115
|
-
return if value.is_a?(String) && !value.empty?
|
|
116
|
-
|
|
117
|
-
raise ArgumentError,
|
|
118
|
-
"Plugin::Macro::ExternalFile#receiver_type must be a non-empty String, got #{value.inspect}"
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
def validate_bound_ivars!(value)
|
|
122
|
-
unless value.is_a?(Hash)
|
|
123
|
-
raise ArgumentError,
|
|
124
|
-
"Plugin::Macro::ExternalFile#bound_ivars must be a Hash, got #{value.inspect}"
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
value.each do |k, v|
|
|
128
|
-
unless k.is_a?(String) && k.start_with?("@") && k.length > 1
|
|
129
|
-
raise ArgumentError,
|
|
130
|
-
"Plugin::Macro::ExternalFile#bound_ivars key must be a String starting with `@`, " \
|
|
131
|
-
"got #{k.inspect}"
|
|
132
|
-
end
|
|
133
|
-
next if v.is_a?(String) && !v.empty?
|
|
134
|
-
|
|
135
|
-
raise ArgumentError,
|
|
136
|
-
"Plugin::Macro::ExternalFile#bound_ivars value must be a non-empty String, " \
|
|
137
|
-
"got #{v.inspect}"
|
|
138
|
-
end
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
end
|