rigortype 0.3.9 → 0.4.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 +2 -2
- data/data/core_overlay/enumerable.rbs +51 -0
- data/data/core_overlay/enumerator.rbs +84 -0
- data/data/core_overlay/hash_rbs3.rbs +41 -0
- data/data/core_overlay/process.rbs +40 -0
- data/data/core_overlay/string_io.rbs +33 -0
- data/data/effects/core.yml +3 -3
- data/data/gem_overlay/activesupport/core_ext.rbs +127 -4
- data/docs/handbook/03-narrowing.md +95 -10
- data/docs/handbook/04-tuples-and-shapes.md +8 -6
- data/docs/handbook/07-rbs-and-extended.md +16 -11
- data/docs/handbook/10-sorbet.md +9 -10
- data/docs/handbook/11-sig-gen.md +454 -13
- data/docs/manual/02-cli-reference.md +63 -2
- data/docs/manual/03-configuration.md +7 -0
- data/docs/manual/04-diagnostics.md +4 -0
- data/docs/manual/07-plugins.md +1 -1
- data/docs/manual/10-mcp-server.md +3 -2
- data/docs/manual/16-rbs-extended-annotations.md +40 -7
- data/docs/manual/19-effect-labels.md +10 -0
- data/docs/manual/plugins/README.md +7 -0
- data/docs/manual/plugins/rigor-actioncable.md +8 -1
- data/docs/manual/plugins/rigor-actionmailer.md +7 -0
- data/docs/manual/plugins/rigor-actionpack.md +243 -0
- data/docs/manual/plugins/rigor-active-model-serializers.md +153 -0
- data/docs/manual/plugins/rigor-activejob.md +7 -0
- data/docs/manual/plugins/rigor-activerecord.md +110 -4
- data/docs/manual/plugins/rigor-activestorage.md +8 -1
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +11 -5
- data/docs/manual/plugins/rigor-grape.md +106 -0
- data/docs/manual/plugins/rigor-graphql.md +23 -2
- data/docs/manual/plugins/rigor-pundit.md +8 -1
- data/docs/manual/plugins/rigor-rails-i18n.md +10 -5
- data/docs/manual/plugins/rigor-rails-routes.md +7 -0
- data/docs/manual/plugins/rigor-rbs-inline.md +212 -25
- data/docs/manual/plugins/rigor-sidekiq.md +8 -1
- data/docs/manual/plugins/rigor-sorbet.md +21 -5
- data/exe/rigor +19 -4
- data/lib/rigor/analysis/baseline.rb +1 -1
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +167 -2
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +189 -0
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -3
- data/lib/rigor/analysis/check_rules/rule_ids.rb +20 -5
- data/lib/rigor/analysis/check_rules/rule_walk.rb +2 -1
- data/lib/rigor/analysis/check_rules/source_arity.rb +323 -0
- data/lib/rigor/analysis/check_rules/special_global_setters.rb +146 -0
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +36 -2
- data/lib/rigor/analysis/check_rules.rb +520 -49
- data/lib/rigor/analysis/dependency_recorder.rb +23 -0
- data/lib/rigor/analysis/fact_store.rb +9 -0
- data/lib/rigor/analysis/incremental.rb +26 -0
- data/lib/rigor/analysis/incremental_session.rb +36 -4
- data/lib/rigor/analysis/project_scan.rb +11 -1
- data/lib/rigor/analysis/reachability/plugin_roots.rb +0 -1
- data/lib/rigor/analysis/reachability/scan_cache.rb +1 -0
- data/lib/rigor/analysis/rule_catalog.rb +127 -0
- data/lib/rigor/analysis/run_cache_key.rb +20 -11
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +167 -11
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +9 -4
- data/lib/rigor/analysis/runner/pool_coordinator.rb +107 -15
- data/lib/rigor/analysis/runner/project_pre_passes.rb +26 -7
- data/lib/rigor/analysis/runner.rb +278 -20
- data/lib/rigor/analysis/template_unit_collector.rb +303 -0
- data/lib/rigor/analysis/template_unit_paths.rb +91 -0
- data/lib/rigor/analysis/template_unit_positions.rb +293 -0
- data/lib/rigor/analysis/template_units.rb +399 -0
- data/lib/rigor/analysis/worker_session.rb +53 -10
- data/lib/rigor/bleeding_edge.rb +0 -2
- data/lib/rigor/builtins/hkt_builtins.rb +1 -0
- data/lib/rigor/builtins/imported_refinements.rb +4 -0
- data/lib/rigor/builtins/regex_refinement.rb +17 -9
- data/lib/rigor/builtins/static_return_refinements.rb +2 -0
- data/lib/rigor/cache/descriptor.rb +6 -1
- data/lib/rigor/cache/engine_source.rb +29 -1
- data/lib/rigor/cache/incremental_snapshot.rb +33 -1
- data/lib/rigor/cache/rbs_cache_producer.rb +4 -0
- data/lib/rigor/cache/rbs_descriptor.rb +37 -7
- data/lib/rigor/cache/store.rb +0 -1
- data/lib/rigor/ci_detector.rb +1 -0
- data/lib/rigor/cli/doc_links.rb +1 -1
- data/lib/rigor/cli/docs_command.rb +4 -4
- data/lib/rigor/cli/plugin_command.rb +3 -3
- data/lib/rigor/cli/prism_colorizer.rb +0 -1
- data/lib/rigor/cli/sig_gen_command.rb +210 -29
- data/lib/rigor/cli/skill_command.rb +1 -1
- data/lib/rigor/cli/skill_describe.rb +0 -1
- data/lib/rigor/cli/type_of_command.rb +19 -5
- data/lib/rigor/cli/type_of_renderer.rb +20 -9
- data/lib/rigor/cli/type_of_template_probe.rb +189 -0
- data/lib/rigor/cli.rb +21 -3
- data/lib/rigor/configuration/severity_profile.rb +19 -3
- data/lib/rigor/configuration.rb +66 -3
- data/lib/rigor/effects/ancestry_recorder.rb +191 -0
- data/lib/rigor/effects/attribution.rb +11 -2
- data/lib/rigor/effects/callee_rule.rb +368 -0
- data/lib/rigor/effects/catalog.rb +7 -4
- data/lib/rigor/effects/collector.rb +11 -5
- data/lib/rigor/effects/config_envelopes.rb +9 -2
- data/lib/rigor/effects/definition_context.rb +179 -0
- data/lib/rigor/effects/effect_table.rb +11 -3
- data/lib/rigor/effects/envelope_check.rb +1 -1
- data/lib/rigor/effects/envelope_index.rb +15 -0
- data/lib/rigor/effects/file_collection.rb +59 -5
- data/lib/rigor/effects/framework_units.rb +1 -1
- data/lib/rigor/effects/identity.rb +16 -0
- data/lib/rigor/effects/local_ownership.rb +38 -12
- data/lib/rigor/effects/method_key.rb +21 -0
- data/lib/rigor/effects/mutation_classifier.rb +23 -12
- data/lib/rigor/effects/plugin_facts.rb +43 -31
- data/lib/rigor/effects/propagator.rb +295 -16
- data/lib/rigor/effects/registry.rb +1 -1
- data/lib/rigor/effects/scanner.rb +121 -72
- data/lib/rigor/effects/signature_sources.rb +1 -1
- data/lib/rigor/effects/snapshot.rb +2 -1
- data/lib/rigor/effects/summary.rb +27 -4
- data/lib/rigor/effects/unit_scan.rb +385 -30
- data/lib/rigor/effects/visibility.rb +101 -0
- data/lib/rigor/environment/lockfile_resolver.rb +17 -0
- data/lib/rigor/environment/member_consistency/comparator.rb +708 -0
- data/lib/rigor/environment/member_consistency.rb +298 -0
- data/lib/rigor/environment/rbs_loader.rb +399 -133
- data/lib/rigor/environment.rb +103 -38
- data/lib/rigor/hashing/xxh3.rb +264 -0
- data/lib/rigor/inference/acceptance.rb +139 -9
- data/lib/rigor/inference/block_auto_splat.rb +216 -0
- data/lib/rigor/inference/block_call_timing.rb +338 -0
- data/lib/rigor/inference/block_parameter_binder.rb +73 -27
- data/lib/rigor/inference/block_repetition.rb +71 -0
- data/lib/rigor/inference/body_fixpoint.rb +2 -1
- data/lib/rigor/inference/budget_trace.rb +2 -1
- data/lib/rigor/inference/builtins/method_catalog.rb +2 -1
- data/lib/rigor/inference/builtins/string_catalog.rb +1 -1
- data/lib/rigor/inference/captured_locals.rb +387 -15
- data/lib/rigor/inference/closure_escape_analyzer.rb +157 -13
- data/lib/rigor/inference/content_join.rb +200 -27
- data/lib/rigor/inference/def_return_typer.rb +11 -7
- data/lib/rigor/inference/define_method_block_self.rb +64 -0
- data/lib/rigor/inference/element_read_widening.rb +22 -10
- data/lib/rigor/inference/error_info.rb +196 -0
- data/lib/rigor/inference/expression_typer.rb +1532 -496
- data/lib/rigor/inference/external_ancestor_resolution.rb +267 -0
- data/lib/rigor/inference/fresh_frame_blocks.rb +127 -0
- data/lib/rigor/inference/global_write_census.rb +239 -0
- data/lib/rigor/inference/guard_rebinding.rb +447 -0
- data/lib/rigor/inference/hash_lookup_mutation.rb +88 -0
- data/lib/rigor/inference/index_write_widening.rb +16 -3
- data/lib/rigor/inference/indexed_narrowing.rb +61 -9
- data/lib/rigor/inference/jump_targets.rb +82 -0
- data/lib/rigor/inference/last_line/implicit_self.rb +210 -0
- data/lib/rigor/inference/last_line/self_evidence.rb +329 -0
- data/lib/rigor/inference/last_line.rb +340 -0
- data/lib/rigor/inference/last_status.rb +144 -0
- data/lib/rigor/inference/macro_block_self_type.rb +167 -17
- data/lib/rigor/inference/match_rebinding/calls.rb +281 -0
- data/lib/rigor/inference/match_rebinding/frame.rb +148 -0
- data/lib/rigor/inference/match_rebinding/operands.rb +236 -0
- data/lib/rigor/inference/match_rebinding/self_calls.rb +83 -0
- data/lib/rigor/inference/match_rebinding.rb +392 -0
- data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +36 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +85 -24
- data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +147 -0
- data/lib/rigor/inference/method_dispatcher/hash_transform_keys_folding.rb +191 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +4 -0
- data/lib/rigor/inference/method_dispatcher/match_data_folding.rb +159 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +113 -73
- data/lib/rigor/inference/method_dispatcher/process_folding.rb +56 -0
- data/lib/rigor/inference/method_dispatcher/proven_overload.rb +72 -0
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +881 -38
- data/lib/rigor/inference/method_dispatcher/self_substitute.rb +142 -0
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +128 -53
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +214 -13
- data/lib/rigor/inference/method_parameter_binder.rb +8 -3
- data/lib/rigor/inference/multi_target_binder.rb +340 -52
- data/lib/rigor/inference/mutation_rejoin.rb +4 -1
- data/lib/rigor/inference/mutation_widening.rb +70 -48
- data/lib/rigor/inference/narrowing.rb +540 -120
- data/lib/rigor/inference/operand_effects.rb +167 -0
- data/lib/rigor/inference/operand_walk.rb +88 -0
- data/lib/rigor/inference/optimistic_origin.rb +152 -9
- data/lib/rigor/inference/parameter_inference_collector.rb +3 -2
- data/lib/rigor/inference/project_method_ownership.rb +136 -0
- data/lib/rigor/inference/project_patched_methods.rb +7 -2
- data/lib/rigor/inference/project_patched_scanner.rb +7 -3
- data/lib/rigor/inference/receiver_alias.rb +90 -1
- data/lib/rigor/inference/receiver_blind_block.rb +219 -0
- data/lib/rigor/inference/refinement_mutation.rb +15 -11
- data/lib/rigor/inference/repeated_or_writes.rb +463 -0
- data/lib/rigor/inference/return_barrier.rb +54 -0
- data/lib/rigor/inference/rewrite_mutation.rb +120 -0
- data/lib/rigor/inference/scope_indexer.rb +4615 -551
- data/lib/rigor/inference/statement_evaluator.rb +3176 -490
- data/lib/rigor/inference/stored_block_call.rb +54 -0
- data/lib/rigor/inference/string_mutation.rb +44 -7
- data/lib/rigor/inference/unknown_store_widening.rb +200 -0
- data/lib/rigor/inference/unthreaded_rebinds.rb +282 -0
- data/lib/rigor/language_server/debouncer.rb +0 -1
- data/lib/rigor/language_server/diagnostic_publisher.rb +3 -2
- data/lib/rigor/language_server/hover_renderer.rb +3 -3
- data/lib/rigor/language_server/project_context.rb +5 -3
- data/lib/rigor/mcp/server.rb +2 -1
- data/lib/rigor/plugin/base.rb +168 -5
- data/lib/rigor/plugin/box_probe.rb +91 -0
- data/lib/rigor/plugin/bundled_catalog.rb +1 -1
- data/lib/rigor/plugin/effect_attribution.rb +58 -4
- data/lib/rigor/plugin/loader.rb +2 -1
- data/lib/rigor/plugin/macro/block_as_method.rb +45 -6
- data/lib/rigor/plugin/manifest.rb +71 -10
- data/lib/rigor/plugin/registry.rb +35 -1
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +9 -3
- data/lib/rigor/plugin/template_unit.rb +196 -0
- data/lib/rigor/plugin.rb +1 -0
- data/lib/rigor/protection/discovery_seed.rb +3 -1
- data/lib/rigor/protection/kill_signature.rb +0 -1
- data/lib/rigor/protection/mutation_cache.rb +1 -2
- data/lib/rigor/rbs_extended.rb +27 -0
- data/lib/rigor/reflection/constant_ancestors.rb +97 -0
- data/lib/rigor/reflection/constant_path.rb +19 -6
- data/lib/rigor/reflection.rb +72 -105
- data/lib/rigor/scope/discovery_index.rb +135 -2
- data/lib/rigor/scope.rb +859 -49
- data/lib/rigor/sig_gen/alias_index.rb +289 -0
- data/lib/rigor/sig_gen/classification.rb +22 -5
- data/lib/rigor/sig_gen/declaration_equivalence.rb +127 -0
- data/lib/rigor/sig_gen/effect_annotation.rb +202 -0
- data/lib/rigor/sig_gen/generator.rb +558 -31
- data/lib/rigor/sig_gen/inline_declarations.rb +184 -0
- data/lib/rigor/sig_gen/method_candidate.rb +47 -3
- data/lib/rigor/sig_gen/observation_collector.rb +1 -1
- data/lib/rigor/sig_gen/renderer.rb +124 -9
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +44 -1
- data/lib/rigor/sig_gen/write_result.rb +19 -3
- data/lib/rigor/sig_gen/writer.rb +166 -35
- data/lib/rigor/sig_gen.rb +3 -0
- data/lib/rigor/signature_path_audit.rb +1 -1
- data/lib/rigor/source/node_walker.rb +0 -3
- data/lib/rigor/source/parameter_envelope.rb +72 -0
- data/lib/rigor/source.rb +1 -0
- data/lib/rigor/type/combinator.rb +88 -12
- data/lib/rigor/type/difference.rb +1 -0
- data/lib/rigor/type/hash_shape.rb +1 -1
- data/lib/rigor/type/refined.rb +1 -0
- data/lib/rigor/version.rb +1 -1
- data/lib/rigor.rb +1 -0
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/effects.rb +1 -0
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +9 -5
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +13 -4
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +96 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/effects.rb +65 -8
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +270 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/render_locals.rb +402 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +370 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_units.rb +132 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +239 -4
- data/plugins/rigor-actionpack/sig/action_view.rbs +43 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_discoverer.rb +220 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_index.rb +56 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +263 -0
- data/plugins/rigor-active-model-serializers/lib/rigor-active-model-serializers.rb +7 -0
- data/plugins/rigor-active-model-serializers/sig/active_model_serializers.rbs +45 -0
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -0
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +9 -5
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +35 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +72 -12
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +380 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +14 -6
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +281 -35
- data/plugins/rigor-activerecord/sig/active_record/relation.rbs +206 -31
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +25 -14
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +9 -2
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +171 -5
- data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +2 -0
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +1 -0
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/types.rb +1 -1
- data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +141 -0
- data/plugins/rigor-grape/lib/rigor-grape.rb +6 -0
- data/plugins/rigor-grape/sig/grape.rbs +263 -0
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +72 -2
- data/plugins/rigor-graphql/sig/graphql.rbs +485 -0
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +2 -0
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +11 -7
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +35 -37
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +0 -1
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -0
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +1 -0
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +9 -17
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline/same_line_annotations.rb +149 -0
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +217 -36
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +1 -0
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +9 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +0 -1
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +46 -1
- data/plugins/rigor-sorbet/sig/sorbet.rbs +277 -0
- data/sig/rigor/analysis/baseline.rbs +69 -7
- data/sig/rigor/analysis/fact_store.rbs +1 -1
- data/sig/rigor/analysis/project_scan.rbs +74 -0
- data/sig/rigor/effects/config_envelopes.rbs +100 -0
- data/sig/rigor/effects/effect_table.rbs +60 -0
- data/sig/rigor/effects/envelope.rbs +97 -0
- data/sig/rigor/effects/envelope_index.rbs +33 -0
- data/sig/rigor/effects/file_collection.rbs +81 -0
- data/sig/rigor/effects/label.rbs +26 -0
- data/sig/rigor/effects/label_set.rbs +46 -0
- data/sig/rigor/effects/method_key.rbs +24 -0
- data/sig/rigor/effects/origin.rbs +50 -0
- data/sig/rigor/effects/plugin_facts.rbs +141 -0
- data/sig/rigor/effects/registry.rbs +68 -0
- data/sig/rigor/effects/summary.rbs +50 -0
- data/sig/rigor/effects/taint_cause.rbs +12 -0
- data/sig/rigor/environment.rbs +12 -10
- data/sig/rigor/inference/optimistic_origin.rbs +10 -0
- data/sig/rigor/inference.rbs +6 -4
- data/sig/rigor/plugin/additional_initializer.rbs +36 -0
- data/sig/rigor/plugin/base.rbs +30 -7
- data/sig/rigor/plugin/effect_ancestry.rbs +27 -0
- data/sig/rigor/plugin/effect_attribution.rbs +66 -0
- data/sig/rigor/plugin/effect_edge.rbs +33 -0
- data/sig/rigor/plugin/effect_entry_points.rbs +25 -0
- data/sig/rigor/plugin/io_boundary.rbs +1 -1
- data/sig/rigor/plugin/loader.rbs +3 -3
- data/sig/rigor/plugin/manifest.rbs +50 -11
- data/sig/rigor/plugin/protocol_contract.rbs +68 -0
- data/sig/rigor/plugin/registry.rbs +62 -1
- data/sig/rigor/plugin.rbs +1 -1
- data/sig/rigor/rbs_extended.rbs +1 -1
- data/sig/rigor/reflection.rbs +7 -6
- data/sig/rigor/scope.rbs +135 -20
- data/sig/rigor/sig_gen/skip_reason_catalog.rbs +14 -7
- data/sig/rigor/source.rbs +4 -4
- data/sig/rigor/testing.rbs +10 -4
- data/sig/rigor/type.rbs +6 -0
- data/sig/rigor.rbs +38 -20
- data/skills/rigor-ask/SKILL.md +8 -5
- data/skills/rigor-baseline-reduce/SKILL.md +4 -6
- data/skills/rigor-ci-setup/SKILL.md +17 -21
- data/skills/rigor-doctor/SKILL.md +24 -22
- data/skills/rigor-doctor/references/01-checks.md +97 -33
- data/skills/rigor-editor-setup/SKILL.md +6 -4
- data/skills/rigor-mcp-setup/SKILL.md +5 -4
- data/skills/rigor-monkeypatch-resolve/SKILL.md +5 -3
- data/skills/rigor-next-steps/SKILL.md +4 -2
- data/skills/rigor-plugin-author/SKILL.md +19 -23
- data/skills/rigor-plugin-author/references/01-plan-and-scaffold.md +9 -8
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +12 -25
- data/skills/rigor-plugin-author/references/03-test-and-ship.md +6 -8
- data/skills/rigor-plugin-review/SKILL.md +6 -4
- data/skills/rigor-plugin-review/references/01-best-practices-checklist.md +2 -1
- data/skills/rigor-plugin-tune/SKILL.md +4 -2
- data/skills/rigor-project-init/SKILL.md +9 -7
- data/skills/rigor-project-init/references/01-detect.md +13 -9
- data/skills/rigor-project-init/references/02-configure.md +33 -8
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +14 -14
- data/skills/rigor-project-init/references/04-sig-uplift.md +27 -14
- data/skills/rigor-protection-uplift/SKILL.md +4 -6
- data/skills/rigor-rbs-setup/SKILL.md +4 -2
- data/skills/rigor-type-oracle/SKILL.md +4 -6
- data/skills/rigor-type-oracle/references/01-oracle-commands.md +12 -8
- data/skills/rigor-type-oracle/references/03-gap-protocol.md +0 -3
- data/skills/rigor-unused-adjudicate/SKILL.md +4 -2
- data/skills/rigor-upgrade/SKILL.md +13 -8
- metadata +108 -1
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
module Rigor
|
|
2
|
+
module Effects
|
|
3
|
+
# The propagated result of the fixpoint walk: every method's direct summary plus the transitive
|
|
4
|
+
# `proven` / `undischarged` / `declared` lanes, the exhaustiveness bit, and the resolved callee keys.
|
|
5
|
+
class EffectTable
|
|
6
|
+
# One row of the closed table.
|
|
7
|
+
class Entry < ::Data
|
|
8
|
+
# sig-gen gap: #1150 — `Data.define` member reads carry no type.
|
|
9
|
+
def key: () -> String
|
|
10
|
+
# sig-gen gap: #1150
|
|
11
|
+
def direct: () -> Summary
|
|
12
|
+
# sig-gen gap: #1150
|
|
13
|
+
def proven: () -> LabelSet
|
|
14
|
+
# sig-gen gap: #1150
|
|
15
|
+
def undischarged: () -> LabelSet
|
|
16
|
+
# sig-gen gap: #1150
|
|
17
|
+
def declared: () -> LabelSet
|
|
18
|
+
# sig-gen gap: #1150
|
|
19
|
+
def exhaustive: () -> bool
|
|
20
|
+
# sig-gen gap: #1150 — `causes` are frozen `[cause, detail]` pairs.
|
|
21
|
+
def causes: () -> Array[[String, String?]]
|
|
22
|
+
# sig-gen gap: #1150 — resolved callee keys (`"Class#m"` strings).
|
|
23
|
+
def edges: () -> Array[String]
|
|
24
|
+
# sig-gen gap: #1150
|
|
25
|
+
def unclaimed: () -> bool
|
|
26
|
+
|
|
27
|
+
# sig-gen gap: #1150 — the custom `initialize` supplies defaults for `undischarged` (=
|
|
28
|
+
# `proven`), `declared` (= empty) and `unclaimed` (= false); it accepts kwargs only, so the
|
|
29
|
+
# member constructor has no positional arm.
|
|
30
|
+
def self.new: (key: String, direct: Summary, proven: LabelSet, exhaustive: bool,
|
|
31
|
+
causes: Array[[String, String?]], edges: Array[String],
|
|
32
|
+
?undischarged: LabelSet?, ?declared: LabelSet?, ?unclaimed: bool) -> instance
|
|
33
|
+
# sig-gen gap: #1150 — as above.
|
|
34
|
+
def self.[]: (key: String, direct: Summary, proven: LabelSet, exhaustive: bool,
|
|
35
|
+
causes: Array[[String, String?]], edges: Array[String],
|
|
36
|
+
?undischarged: LabelSet?, ?declared: LabelSet?, ?unclaimed: bool) -> instance
|
|
37
|
+
|
|
38
|
+
def unclaimed?: () -> bool
|
|
39
|
+
|
|
40
|
+
# The declared labels worth printing beside `proven` — those the proven lane does not already
|
|
41
|
+
# admit.
|
|
42
|
+
def rendered_declared: () -> LabelSet
|
|
43
|
+
|
|
44
|
+
def exhaustive?: () -> bool
|
|
45
|
+
def trivial?: () -> bool
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.empty: () -> EffectTable
|
|
49
|
+
|
|
50
|
+
def initialize: (Hash[String, Entry] entries) -> void
|
|
51
|
+
|
|
52
|
+
def []: (String key) -> Entry?
|
|
53
|
+
def keys: () -> Array[String]
|
|
54
|
+
def each: () { (Entry) -> untyped } -> void
|
|
55
|
+
include Enumerable[Entry]
|
|
56
|
+
def size: () -> Integer
|
|
57
|
+
def empty?: () -> bool
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
module Rigor
|
|
2
|
+
module Effects
|
|
3
|
+
# An author-declared upper bound on one method's effect labels (ADR-103 WD1). A `Data` record —
|
|
4
|
+
# see lib/rigor/effects/envelope.rb for the member semantics.
|
|
5
|
+
class Envelope < ::Data
|
|
6
|
+
# {#source} for a bound written in `.rigor.yml` rather than on a declaration.
|
|
7
|
+
CONFIG_SOURCE: :config_envelope
|
|
8
|
+
|
|
9
|
+
# sig-gen gap: #1150 — `Data.define` member reads carry no type; `build` proves each of these.
|
|
10
|
+
def owner_key: () -> String
|
|
11
|
+
|
|
12
|
+
# sig-gen gap: #1150 — as above; `LabelSet::TOP` means "no envelope" (the fail-open reading).
|
|
13
|
+
def bound: () -> LabelSet
|
|
14
|
+
|
|
15
|
+
# sig-gen gap: #1150 — as above; the four documented producers are the closed set.
|
|
16
|
+
def source: () -> (:pure_annotation | :effect_annotation | :class_annotation | :config_envelope)
|
|
17
|
+
|
|
18
|
+
# sig-gen gap: #1150 — as above (`path:line`, the `.rigor.yml` key path, or nil).
|
|
19
|
+
def location: () -> String?
|
|
20
|
+
|
|
21
|
+
# sig-gen gap: #1150 — as above (the author's own annotation text, or nil).
|
|
22
|
+
def spelling: () -> String?
|
|
23
|
+
|
|
24
|
+
# sig-gen gap: #1150 — as above (sorted, de-duplicated by `build`; #384 reads them).
|
|
25
|
+
def unknown_labels: () -> Array[String]
|
|
26
|
+
|
|
27
|
+
# sig-gen gap: #1150 — as above (every token the tag listed, recognised or not).
|
|
28
|
+
def declared_labels: () -> Array[String]
|
|
29
|
+
|
|
30
|
+
# sig-gen gap: #1150 — as above; keyword form mirrors the member declarations.
|
|
31
|
+
def self.new: (
|
|
32
|
+
owner_key: String,
|
|
33
|
+
bound: LabelSet,
|
|
34
|
+
source: (:pure_annotation | :effect_annotation | :class_annotation | :config_envelope),
|
|
35
|
+
location: String?,
|
|
36
|
+
spelling: String?,
|
|
37
|
+
unknown_labels: Array[String],
|
|
38
|
+
declared_labels: Array[String]
|
|
39
|
+
) -> instance
|
|
40
|
+
| (
|
|
41
|
+
String owner_key,
|
|
42
|
+
LabelSet bound,
|
|
43
|
+
(:pure_annotation | :effect_annotation | :class_annotation | :config_envelope) source,
|
|
44
|
+
String? location,
|
|
45
|
+
String? spelling,
|
|
46
|
+
Array[String] unknown_labels,
|
|
47
|
+
Array[String] declared_labels
|
|
48
|
+
) -> instance
|
|
49
|
+
|
|
50
|
+
# sig-gen gap: #1150 — as above.
|
|
51
|
+
def self.[]: (
|
|
52
|
+
owner_key: String,
|
|
53
|
+
bound: LabelSet,
|
|
54
|
+
source: (:pure_annotation | :effect_annotation | :class_annotation | :config_envelope),
|
|
55
|
+
location: String?,
|
|
56
|
+
spelling: String?,
|
|
57
|
+
unknown_labels: Array[String],
|
|
58
|
+
declared_labels: Array[String]
|
|
59
|
+
) -> instance
|
|
60
|
+
| (
|
|
61
|
+
String owner_key,
|
|
62
|
+
LabelSet bound,
|
|
63
|
+
(:pure_annotation | :effect_annotation | :class_annotation | :config_envelope) source,
|
|
64
|
+
String? location,
|
|
65
|
+
String? spelling,
|
|
66
|
+
Array[String] unknown_labels,
|
|
67
|
+
Array[String] declared_labels
|
|
68
|
+
) -> instance
|
|
69
|
+
|
|
70
|
+
# Normalising constructor — sorts/dedupes `unknown_labels` and freezes `declared_labels`.
|
|
71
|
+
def self.build: (
|
|
72
|
+
owner_key: String,
|
|
73
|
+
bound: LabelSet,
|
|
74
|
+
source: (:pure_annotation | :effect_annotation | :class_annotation | :config_envelope),
|
|
75
|
+
?location: String?,
|
|
76
|
+
?spelling: String?,
|
|
77
|
+
?unknown_labels: Array[String],
|
|
78
|
+
?declared_labels: Array[String]
|
|
79
|
+
) -> Envelope
|
|
80
|
+
|
|
81
|
+
# Whether this reads as ⊤ — no bound at all.
|
|
82
|
+
def top?: () -> bool
|
|
83
|
+
|
|
84
|
+
# `label` is `to_s`-compared through {LabelSet#admits?}; `mutate.local` always tolerates.
|
|
85
|
+
def tolerates?: (untyped label) -> bool
|
|
86
|
+
|
|
87
|
+
# The members of `label_set` this envelope does NOT admit, sorted.
|
|
88
|
+
def exceeded_by: (LabelSet label_set) -> Array[String]
|
|
89
|
+
|
|
90
|
+
# Whether the bound was written in `.rigor.yml`.
|
|
91
|
+
def config?: () -> bool
|
|
92
|
+
|
|
93
|
+
# The same bound on another method key — the class-level distribution seam.
|
|
94
|
+
def rebind: (String key) -> Envelope
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Rigor
|
|
2
|
+
module Effects
|
|
3
|
+
# The envelopes a call site may import as a `≤` bound (ADR-103 WD6; #386) — per-method
|
|
4
|
+
# annotation, class-level annotation, `effects.envelopes:`, then the accepted signature stratum.
|
|
5
|
+
class EnvelopeIndex
|
|
6
|
+
# The index a run with no declaration of any kind uses — and the fail-soft answer for a build
|
|
7
|
+
# that raised.
|
|
8
|
+
def self.empty: () -> EnvelopeIndex
|
|
9
|
+
|
|
10
|
+
# Reads every stratum this index serves, once per process. `environment` supplies the accepted
|
|
11
|
+
# stratum through its RBS loader (nil drops it, fail-quiet); `plugin_facts` is the plugins'
|
|
12
|
+
# compiled `Effects::PluginFacts` contributions (nil where no plugin set was loaded).
|
|
13
|
+
def self.build: (configuration: Configuration, ?environment: Rigor::Environment?,
|
|
14
|
+
?plugin_facts: PluginFacts?) -> EnvelopeIndex
|
|
15
|
+
|
|
16
|
+
def initialize: (
|
|
17
|
+
?method_envelopes: Hash[String, Envelope],
|
|
18
|
+
?class_envelopes: Hash[String, Envelope],
|
|
19
|
+
?config_entries: Array[ConfigEnvelopes::Entry],
|
|
20
|
+
?accepted: Hash[String, Envelope]
|
|
21
|
+
) -> void
|
|
22
|
+
|
|
23
|
+
# Whether no stratum has anything to say — the scan's fast path.
|
|
24
|
+
def empty?: () -> bool
|
|
25
|
+
|
|
26
|
+
# The envelope bounding `owner`'s `selector`, or nil — never a ⊤ envelope.
|
|
27
|
+
def []: (String? owner, bool singleton, untyped selector) -> Envelope?
|
|
28
|
+
|
|
29
|
+
# Whether ANY stratum carries an annotation for this key — ⊤ envelopes included (#391).
|
|
30
|
+
def annotated?: (String? owner, bool singleton, untyped selector) -> bool
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
module Rigor
|
|
2
|
+
module Effects
|
|
3
|
+
# The per-file unit of the collection window (ADR-103): the direct summaries, the call edges, and
|
|
4
|
+
# the as-written ancestry a file contributes. {merge_all} folds a run's collections in one pass.
|
|
5
|
+
class FileCollection
|
|
6
|
+
# An ancestry entry that names nothing and can never resolve (#1039).
|
|
7
|
+
OPAQUE_ANCESTOR: String
|
|
8
|
+
|
|
9
|
+
# One recorded call edge: who the receiver's class is, which lane (`:instance` / `:singleton`)
|
|
10
|
+
# the selector dispatches on, and the bits that decide what an unresolved edge seeds.
|
|
11
|
+
class Edge < ::Data
|
|
12
|
+
# sig-gen gap: #1150 — `Data.define` member reads carry no type.
|
|
13
|
+
def receiver_class: () -> String?
|
|
14
|
+
# sig-gen gap: #1150
|
|
15
|
+
def kind: () -> (:instance | :singleton)
|
|
16
|
+
# sig-gen gap: #1150
|
|
17
|
+
def selector: () -> String
|
|
18
|
+
# sig-gen gap: #1150
|
|
19
|
+
def self_call: () -> bool
|
|
20
|
+
# sig-gen gap: #1150
|
|
21
|
+
def super_call: () -> bool
|
|
22
|
+
# sig-gen gap: #1150
|
|
23
|
+
def unclaimed: () -> bool
|
|
24
|
+
# sig-gen gap: #1150
|
|
25
|
+
def constant_receiver: () -> bool
|
|
26
|
+
# sig-gen gap: #1150 — the `[cause, detail]` pair an unresolved edge seeds, when the row
|
|
27
|
+
# named a `taint:`.
|
|
28
|
+
def taint_if_unresolved: () -> [String, String?]?
|
|
29
|
+
# sig-gen gap: #1150 — the plugin's lookup-order retry selectors (#1065).
|
|
30
|
+
def fallback_selectors: () -> Array[String]?
|
|
31
|
+
|
|
32
|
+
# sig-gen gap: #1150 — member constructor. The custom `initialize` accepts kwargs only and
|
|
33
|
+
# defaults every member after `self_call`, so there is no positional arm.
|
|
34
|
+
def self.new: (receiver_class: String?, kind: (:instance | :singleton), selector: String,
|
|
35
|
+
self_call: bool, ?super_call: bool, ?unclaimed: bool,
|
|
36
|
+
?constant_receiver: bool, ?taint_if_unresolved: [String, String?]?,
|
|
37
|
+
?fallback_selectors: Array[String]?) -> instance
|
|
38
|
+
# sig-gen gap: #1150 — as above.
|
|
39
|
+
def self.[]: (receiver_class: String?, kind: (:instance | :singleton), selector: String,
|
|
40
|
+
self_call: bool, ?super_call: bool, ?unclaimed: bool,
|
|
41
|
+
?constant_receiver: bool, ?taint_if_unresolved: [String, String?]?,
|
|
42
|
+
?fallback_selectors: Array[String]?) -> instance
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# The collection a file with nothing to say contributes.
|
|
46
|
+
def self.empty: (?String? path) -> FileCollection
|
|
47
|
+
|
|
48
|
+
# sig-gen gap: #1154 — `initialize` freezes the kwarg tables rather than assigning them
|
|
49
|
+
# verbatim, so sig-gen proposes the default constant's shape for each reader.
|
|
50
|
+
attr_reader path: String?
|
|
51
|
+
# sig-gen gap: #1154 — `{ "Class#m" => Summary }`.
|
|
52
|
+
attr_reader summaries: Hash[String, Summary]
|
|
53
|
+
# sig-gen gap: #1154 — `{ "Class#m" => [Edge] }`, sorted and de-duplicated.
|
|
54
|
+
attr_reader edges: Hash[String, Array[Edge]]
|
|
55
|
+
# sig-gen gap: #1154 — `{ class_name => superclass_name }`, `OPAQUE_ANCESTOR` included.
|
|
56
|
+
attr_reader superclasses: Hash[String, String]
|
|
57
|
+
# sig-gen gap: #1154 — `{ class_name => [module_name] }`, include/prepend merged.
|
|
58
|
+
attr_reader includes: Hash[String, Array[String]]
|
|
59
|
+
|
|
60
|
+
def initialize: (?path: String?, ?summaries: Hash[String, Summary],
|
|
61
|
+
?edges: Hash[String, Array[Edge]], ?superclasses: Hash[String, String],
|
|
62
|
+
?includes: Hash[String, Array[String]], ?failed: bool) -> void
|
|
63
|
+
|
|
64
|
+
# Whether the collector gave up on this file entirely (the fail-soft path).
|
|
65
|
+
def failed?: () -> bool
|
|
66
|
+
|
|
67
|
+
# Whether this collection contributes nothing.
|
|
68
|
+
def empty?: () -> bool
|
|
69
|
+
|
|
70
|
+
# Two-collection fold — {merge_all} for a whole run.
|
|
71
|
+
def merge: (FileCollection other) -> FileCollection
|
|
72
|
+
|
|
73
|
+
# Folds a run's collections in one linear pass; order-independent except `superclasses`.
|
|
74
|
+
def self.merge_all: (Enumerable[FileCollection] collections) -> FileCollection
|
|
75
|
+
|
|
76
|
+
def ==: (untyped other) -> bool
|
|
77
|
+
alias eql? ==
|
|
78
|
+
def hash: () -> Integer
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Rigor
|
|
2
|
+
module Effects
|
|
3
|
+
# The effect-label grammar and segment-aware prefix subsumption (ADR-103 WD1). Every predicate is
|
|
4
|
+
# total — a malformed input is answered, never raised on — so the parameters are `untyped` on
|
|
5
|
+
# purpose.
|
|
6
|
+
module Label
|
|
7
|
+
PATTERN: Regexp
|
|
8
|
+
|
|
9
|
+
def self?.valid?: (untyped str) -> bool
|
|
10
|
+
|
|
11
|
+
# `[]` for a malformed label, the segments otherwise.
|
|
12
|
+
def self?.segments: (untyped label) -> Array[String]
|
|
13
|
+
|
|
14
|
+
def self?.subsumes?: (untyped bound, untyped label) -> bool
|
|
15
|
+
|
|
16
|
+
# One segment shallower; nil for a root and for a malformed label.
|
|
17
|
+
def self?.parent: (untyped label) -> String?
|
|
18
|
+
|
|
19
|
+
# Proper ancestors, outermost first; `[]` for a root and for a malformed label.
|
|
20
|
+
def self?.ancestors: (untyped label) -> Array[String]
|
|
21
|
+
|
|
22
|
+
# The outermost segment; nil for a malformed label.
|
|
23
|
+
def self?.root: (untyped label) -> String?
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module Rigor
|
|
2
|
+
module Effects
|
|
3
|
+
# An immutable, sorted, de-duplicated set of effect labels (ADR-103 WD1) — the carrier of a
|
|
4
|
+
# summary lane and of an envelope's bound. `TOP` is the unbounded sentinel: it admits every
|
|
5
|
+
# label, absorbs every join, and answers `[]` from `to_a`.
|
|
6
|
+
class LabelSet
|
|
7
|
+
# `labels` is any enumerable of label Strings (`#to_a` is called on it); `top:` is internal —
|
|
8
|
+
# it exists to build {TOP} and is not part of the public surface.
|
|
9
|
+
def initialize: (?untyped labels, ?top: bool) -> void
|
|
10
|
+
|
|
11
|
+
TOP: LabelSet
|
|
12
|
+
EMPTY: LabelSet
|
|
13
|
+
|
|
14
|
+
def top?: () -> bool
|
|
15
|
+
|
|
16
|
+
# {TOP} is not empty: it records nothing because it stands for everything.
|
|
17
|
+
def empty?: () -> bool
|
|
18
|
+
|
|
19
|
+
# The members, sorted. {TOP} yields `[]` — consult {top?} first.
|
|
20
|
+
def to_a: () -> Array[String]
|
|
21
|
+
|
|
22
|
+
# Exact membership — `include?` is not subsumption ({admits?} is).
|
|
23
|
+
def include?: (untyped label) -> bool
|
|
24
|
+
|
|
25
|
+
# Whether some member subsumes `label`. {TOP} admits every well-formed label.
|
|
26
|
+
def admits?: (untyped label) -> bool
|
|
27
|
+
|
|
28
|
+
# Union — {TOP} absorbs. May return `self`/`other` without allocating.
|
|
29
|
+
def join: (LabelSet other) -> LabelSet
|
|
30
|
+
|
|
31
|
+
# The members `other` does not already admit — the declared lane's rendering rule.
|
|
32
|
+
def excluding_subsumed_by: (LabelSet other) -> LabelSet
|
|
33
|
+
|
|
34
|
+
# The envelope check, modulo policy discharge. {TOP} is bounded only by {TOP}.
|
|
35
|
+
def subsumed_by?: (LabelSet bound_set) -> bool
|
|
36
|
+
|
|
37
|
+
def ==: (untyped other) -> bool
|
|
38
|
+
|
|
39
|
+
# `alias eql? ==` — no sig-gen shape for alias rows; unmarked residue.
|
|
40
|
+
def eql?: (untyped other) -> bool
|
|
41
|
+
|
|
42
|
+
def hash: () -> Integer
|
|
43
|
+
def inspect: () -> String
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Rigor
|
|
2
|
+
module Effects
|
|
3
|
+
# The `Owner#selector` / `Owner.selector` key spelling (ADR-103 WD14). `key` parameters accept any
|
|
4
|
+
# object — every entry point `to_s`s its input — so they are `untyped` on purpose.
|
|
5
|
+
module MethodKey
|
|
6
|
+
TEMPLATE_UNIT_PREFIX: String
|
|
7
|
+
|
|
8
|
+
# `[owner, separator, selector]`, or nil when `key` is not a method key at all.
|
|
9
|
+
def self?.split: (untyped key) -> [String, String, String]?
|
|
10
|
+
|
|
11
|
+
def self?.valid?: (untyped key) -> bool
|
|
12
|
+
|
|
13
|
+
# The owner half of a method key, or nil.
|
|
14
|
+
def self?.owner: (untyped key) -> String?
|
|
15
|
+
|
|
16
|
+
# `view:`-prefixed template-unit keys are NOT method keys (#393).
|
|
17
|
+
def self?.template_unit?: (untyped key) -> bool
|
|
18
|
+
|
|
19
|
+
# The name an `effects.envelopes:` entry selects a key by — the owner class for a method key,
|
|
20
|
+
# the unit key itself for a template unit.
|
|
21
|
+
def self?.envelope_owner: (untyped key) -> String?
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module Rigor
|
|
2
|
+
module Effects
|
|
3
|
+
# Where one bundle of labels in a summary came from (ADR-103 WD14): a callee/construct `name`
|
|
4
|
+
# plus which `source` coloured it.
|
|
5
|
+
class Origin < ::Data
|
|
6
|
+
CONSTRUCT: :construct
|
|
7
|
+
CATALOGUE: :catalogue
|
|
8
|
+
ATTRIBUTION: :attribution
|
|
9
|
+
ENVELOPE: :envelope
|
|
10
|
+
PLUGIN: :plugin
|
|
11
|
+
|
|
12
|
+
# sig-gen gap: #1150 — `Data.define` member reads carry no type; every factory below proves
|
|
13
|
+
# `name` a `to_s`d String and `source` one of the five constants.
|
|
14
|
+
def name: () -> String
|
|
15
|
+
|
|
16
|
+
# sig-gen gap: #1150 — as above; the five source constants are the closed set.
|
|
17
|
+
def source: () -> (:construct | :catalogue | :attribution | :envelope | :plugin)
|
|
18
|
+
|
|
19
|
+
# sig-gen gap: #1150 — as above.
|
|
20
|
+
def self.new: (
|
|
21
|
+
name: String,
|
|
22
|
+
source: (:construct | :catalogue | :attribution | :envelope | :plugin)
|
|
23
|
+
) -> instance
|
|
24
|
+
| (
|
|
25
|
+
String name,
|
|
26
|
+
(:construct | :catalogue | :attribution | :envelope | :plugin) source
|
|
27
|
+
) -> instance
|
|
28
|
+
|
|
29
|
+
# sig-gen gap: #1150 — as above.
|
|
30
|
+
def self.[]: (
|
|
31
|
+
name: String,
|
|
32
|
+
source: (:construct | :catalogue | :attribution | :envelope | :plugin)
|
|
33
|
+
) -> instance
|
|
34
|
+
| (
|
|
35
|
+
String name,
|
|
36
|
+
(:construct | :catalogue | :attribution | :envelope | :plugin) source
|
|
37
|
+
) -> instance
|
|
38
|
+
|
|
39
|
+
# `name` is `to_s`d, so any object is acceptable — `untyped` is the honest parameter.
|
|
40
|
+
def self.construct: (untyped name) -> Origin
|
|
41
|
+
def self.catalogue: (untyped name) -> Origin
|
|
42
|
+
def self.attribution: (untyped name) -> Origin
|
|
43
|
+
def self.envelope: (untyped name) -> Origin
|
|
44
|
+
def self.plugin: (untyped name) -> Origin
|
|
45
|
+
|
|
46
|
+
# `"source:name"` — the rendering the report and the JSON payload key on.
|
|
47
|
+
def to_s: () -> String
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
module Rigor
|
|
2
|
+
module Effects
|
|
3
|
+
# The loaded plugins' effect contributions, compiled once per process (ADR-103): attribution rows
|
|
4
|
+
# indexed by receiver spelling, framework-edge strategies, label vocabulary, entry-point presets,
|
|
5
|
+
# and plugin-claimed ancestry.
|
|
6
|
+
class PluginFacts
|
|
7
|
+
# How many ancestors the walk visits before giving up — bounds work, not correctness.
|
|
8
|
+
ANCESTRY_CAP: Integer
|
|
9
|
+
|
|
10
|
+
# One compiled attribution row. `discharge` is the RESOLVED grant — `.build` has already
|
|
11
|
+
# demoted a non-first-party plugin's `true` and recorded a warning.
|
|
12
|
+
class Row < ::Data
|
|
13
|
+
# sig-gen gap: #1150 — `Data.define` member reads carry no type.
|
|
14
|
+
def key: () -> String
|
|
15
|
+
# sig-gen gap: #1150
|
|
16
|
+
def labels: () -> LabelSet
|
|
17
|
+
# sig-gen gap: #1150 — a {Narrowing} handler name, when the call's own arguments settle the
|
|
18
|
+
# row's answer.
|
|
19
|
+
def narrow: () -> String?
|
|
20
|
+
# sig-gen gap: #1150
|
|
21
|
+
def discharge: () -> bool
|
|
22
|
+
# sig-gen gap: #1150 — the class a `self`-path row is scoped to.
|
|
23
|
+
def within: () -> String?
|
|
24
|
+
# sig-gen gap: #1150 — the taint cause the row seeds where its edge resolves to nothing.
|
|
25
|
+
def taint: () -> String?
|
|
26
|
+
# sig-gen gap: #1150
|
|
27
|
+
def plugin_id: () -> String
|
|
28
|
+
# sig-gen gap: #1150 — the receiver a UNIT rule has no call node to read off.
|
|
29
|
+
def receiver: () -> String?
|
|
30
|
+
# sig-gen gap: #1150 — the {CalleeRule} rule name (#1048), nil when the row records no edge.
|
|
31
|
+
def callee: () -> String?
|
|
32
|
+
# sig-gen gap: #1150 — the plugin's lookup-order table for the rule's answer (#1065).
|
|
33
|
+
def callee_fallbacks: () -> Hash[String, Array[String]]?
|
|
34
|
+
# sig-gen gap: #1150
|
|
35
|
+
def responds: () -> bool
|
|
36
|
+
|
|
37
|
+
# sig-gen gap: #1150 — member constructor. The custom `initialize` accepts kwargs only and
|
|
38
|
+
# defaults `receiver` / `callee` / `callee_fallbacks` / `responds`, so there is no
|
|
39
|
+
# positional arm.
|
|
40
|
+
def self.new: (key: String, labels: LabelSet, narrow: String?, discharge: bool,
|
|
41
|
+
within: String?, taint: String?, plugin_id: String, ?receiver: String?,
|
|
42
|
+
?callee: String?, ?callee_fallbacks: Hash[String, Array[String]]?,
|
|
43
|
+
?responds: bool) -> instance
|
|
44
|
+
# sig-gen gap: #1150 — as above.
|
|
45
|
+
def self.[]: (key: String, labels: LabelSet, narrow: String?, discharge: bool,
|
|
46
|
+
within: String?, taint: String?, plugin_id: String, ?receiver: String?,
|
|
47
|
+
?callee: String?, ?callee_fallbacks: Hash[String, Array[String]]?,
|
|
48
|
+
?responds: bool) -> instance
|
|
49
|
+
|
|
50
|
+
def discharge?: () -> bool
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# One compiled framework-edge strategy: the base class a plugin pointed the engine at, and
|
|
54
|
+
# which plugin pointed.
|
|
55
|
+
class Edge < ::Data
|
|
56
|
+
# sig-gen gap: #1150 — `Data.define` member reads carry no type. `target` is the closed
|
|
57
|
+
# {Plugin::EffectEdge::TARGETS} set.
|
|
58
|
+
def target: () -> (:activerecord_callbacks | :perform_now | :mailer_body)
|
|
59
|
+
# sig-gen gap: #1150
|
|
60
|
+
def receiver: () -> String
|
|
61
|
+
# sig-gen gap: #1150 — nil for a whole-class edge (`method` omitted).
|
|
62
|
+
def selector: () -> Symbol?
|
|
63
|
+
# sig-gen gap: #1150
|
|
64
|
+
def plugin_id: () -> String
|
|
65
|
+
|
|
66
|
+
# sig-gen gap: #1150 — member constructor.
|
|
67
|
+
def self.new: (target: (:activerecord_callbacks | :perform_now | :mailer_body),
|
|
68
|
+
receiver: String, selector: Symbol?, plugin_id: String) -> instance
|
|
69
|
+
| ((:activerecord_callbacks | :perform_now | :mailer_body) target, String receiver,
|
|
70
|
+
Symbol? selector, String plugin_id) -> instance
|
|
71
|
+
# sig-gen gap: #1150 — as above.
|
|
72
|
+
def self.[]: (target: (:activerecord_callbacks | :perform_now | :mailer_body),
|
|
73
|
+
receiver: String, selector: Symbol?, plugin_id: String) -> instance
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# The rows whose `callee:` names a {CalleeRule::UNIT_RULES} rule — applied once per unit.
|
|
77
|
+
# Unmarked residue: populated by `absorb_attribution`, not assigned from an `initialize`
|
|
78
|
+
# parameter, so no gap issue covers it (same shape as `Registry#additional_initializers`).
|
|
79
|
+
attr_reader unit_callee_rows: Array[Row]
|
|
80
|
+
|
|
81
|
+
# Human-readable notes about contributions accepted only in part. Surfaced by `rigor effects`;
|
|
82
|
+
# never a diagnostic. Unmarked residue — accumulated by `absorb`/`extend_registry`.
|
|
83
|
+
attr_reader warnings: Array[String]
|
|
84
|
+
|
|
85
|
+
# `{owner => [labels]}` — what {#extend_registry} folds into the run's vocabulary.
|
|
86
|
+
# Unmarked residue — built by `absorb`, not an `initialize` parameter.
|
|
87
|
+
attr_reader labels_by_owner: Hash[String, Array[String]]
|
|
88
|
+
|
|
89
|
+
# Every `effect_entry_points:` preset across the loaded set.
|
|
90
|
+
# Unmarked residue — concatenated by `absorb`, not an `initialize` parameter.
|
|
91
|
+
attr_reader entry_points: Array[Plugin::EffectEntryPoints]
|
|
92
|
+
|
|
93
|
+
# A content digest of every compiled plugin fact; {Identity} folds it in.
|
|
94
|
+
# Unmarked residue — computed by `compute_digest`, not an `initialize` parameter.
|
|
95
|
+
attr_reader digest: String
|
|
96
|
+
|
|
97
|
+
def self.empty: () -> PluginFacts
|
|
98
|
+
|
|
99
|
+
# `superclasses`/`includes` are the project's as-written ancestry tables
|
|
100
|
+
# (`Scope::DiscoveryIndex#discovered_superclasses` / `#discovered_includes`).
|
|
101
|
+
def self.build: (Plugin::Registry? plugin_registry, ?superclasses: Hash[String, String]?,
|
|
102
|
+
?includes: Hash[String, Array[String]]?) -> PluginFacts
|
|
103
|
+
|
|
104
|
+
# `contributions:` elements are `Plugin::Registry::Contribution` rows.
|
|
105
|
+
def initialize: (contributions: Array[Plugin::Registry::Contribution],
|
|
106
|
+
superclasses: Hash[String, String]?,
|
|
107
|
+
?includes: Hash[String, Array[String]]?) -> void
|
|
108
|
+
|
|
109
|
+
def empty?: () -> bool
|
|
110
|
+
|
|
111
|
+
# Whether any attribution row exists at all — the scan's fast path.
|
|
112
|
+
def attributions?: () -> bool
|
|
113
|
+
def edges?: () -> bool
|
|
114
|
+
|
|
115
|
+
# The row colouring `owner`'s `selector`, on `owner` itself or a project ancestor.
|
|
116
|
+
def class_row: (String? owner, bool singleton, String selector) -> Row?
|
|
117
|
+
|
|
118
|
+
# The row colouring receiver `path`'s `selector` (`"Rails.cache"`) — exact, no ancestry walk.
|
|
119
|
+
def path_row: (String? path, String selector) -> Row?
|
|
120
|
+
|
|
121
|
+
# The row colouring `path`'s `selector` for a self-rooted receiver (`"self.flash.now"`) inside
|
|
122
|
+
# a unit whose class is `owner_class`.
|
|
123
|
+
def self_path_row: (String? path, String selector, String? owner_class) -> Row?
|
|
124
|
+
|
|
125
|
+
# The row colouring `selector` on the RESULT of a call to `producer` (or an ancestor of it).
|
|
126
|
+
def result_row: (String? producer, String selector) -> Row?
|
|
127
|
+
|
|
128
|
+
# The framework-edge strategies of one kind, e.g. every `:activerecord_callbacks` base class
|
|
129
|
+
# the loaded plugins named.
|
|
130
|
+
def edges_for: (Symbol target) -> Array[Edge]
|
|
131
|
+
|
|
132
|
+
# Whether `class_name` is `ancestor`, or reaches it through the project's own `class … <`
|
|
133
|
+
# lines and included modules.
|
|
134
|
+
def descends_from?: (String? class_name, String? ancestor) -> bool
|
|
135
|
+
|
|
136
|
+
# `registry` extended with every plugin's `effect_labels:` under its own owner. Rejected
|
|
137
|
+
# extensions land in {#warnings} instead of raising.
|
|
138
|
+
def extend_registry: (Registry registry) -> Registry
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module Rigor
|
|
2
|
+
module Effects
|
|
3
|
+
# The effect-label vocabulary: which spellings are recognised, which have been retired, and who
|
|
4
|
+
# may open a new root (ADR-103 WD2; normative in `docs/type-specification/effect-labels.md`).
|
|
5
|
+
# A frozen value object; {#with} returns a new one rather than mutating.
|
|
6
|
+
class Registry
|
|
7
|
+
class Error < StandardError
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# An added label whose root neither exists already nor belongs to the extender.
|
|
11
|
+
class OwnershipError < Error
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# An added label that is not well-formed under {Label::PATTERN}.
|
|
15
|
+
class InvalidLabelError < Error
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
DATA_PATH: String
|
|
19
|
+
|
|
20
|
+
# How far a misspelling may be from a known label before {#suggest} declines to guess.
|
|
21
|
+
SUGGESTION_DISTANCE_CAP: Integer
|
|
22
|
+
|
|
23
|
+
# The shared registry as shipped, memoised per process.
|
|
24
|
+
def self.default: () -> Registry
|
|
25
|
+
|
|
26
|
+
# The vocabulary one run works in: the shipped registry plus `effects.labels:` and every
|
|
27
|
+
# loaded plugin's `effect_labels:` (ADR-103 WD2 / #385 / #387). Fail-soft: a rejected
|
|
28
|
+
# extension degrades to the shipped vocabulary.
|
|
29
|
+
def self.for_configuration: (Configuration configuration, ?plugin_facts: PluginFacts?) -> Registry
|
|
30
|
+
|
|
31
|
+
# Build a registry from a registry-shaped YAML file. Missing or unreadable data degrades to
|
|
32
|
+
# an empty vocabulary (fail-open).
|
|
33
|
+
def self.load_file: (untyped path) -> Registry
|
|
34
|
+
|
|
35
|
+
# sig-gen gap: #1154 — `attr_reader` over an ivar assigned in `initialize` from the
|
|
36
|
+
# parameter of the same name; sig-gen proposes `untyped` for it.
|
|
37
|
+
attr_reader vocabulary_version: Integer
|
|
38
|
+
# sig-gen gap: #1154 — every declared label, sorted; implied ancestors are recognised by
|
|
39
|
+
# {#known?} but not listed here.
|
|
40
|
+
attr_reader labels: Array[String]
|
|
41
|
+
# The roots of the vocabulary — the outermost segments {#with} treats as already owned.
|
|
42
|
+
# Unmarked residue: `@roots` is computed from `@known` in `initialize`, not assigned from a
|
|
43
|
+
# parameter, so #1154 does not cover it.
|
|
44
|
+
attr_reader roots: Array[String]
|
|
45
|
+
# sig-gen gap: #1154 — one line per root, from the data file's `descriptions:`.
|
|
46
|
+
attr_reader descriptions: Hash[String, String]
|
|
47
|
+
|
|
48
|
+
def initialize: (vocabulary_version: Integer, labels: Array[untyped],
|
|
49
|
+
?retired: Hash[untyped, untyped],
|
|
50
|
+
?descriptions: Hash[untyped, untyped]) -> void
|
|
51
|
+
|
|
52
|
+
# Whether the vocabulary recognises `label`: an exact row, or an ancestor of one.
|
|
53
|
+
def known?: (untyped label) -> bool
|
|
54
|
+
|
|
55
|
+
# The nearest recognised label to a misspelling within {SUGGESTION_DISTANCE_CAP} edits, or
|
|
56
|
+
# nil when nothing is close enough.
|
|
57
|
+
def suggest: (untyped label) -> String?
|
|
58
|
+
|
|
59
|
+
# The replacement labels for a retired spelling, or nil when it was never retired.
|
|
60
|
+
def retired: (untyped label) -> Array[String]?
|
|
61
|
+
|
|
62
|
+
# A new registry carrying `labels` on top of this one. `owner` is the identity opening a
|
|
63
|
+
# root — a plugin id, or a first-party plugin's framework root; nil is the project, which
|
|
64
|
+
# may open any root.
|
|
65
|
+
def with: (labels: Array[untyped], owner: String?) -> Registry
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|