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
|
@@ -48,6 +48,7 @@ require_relative "runner/run_snapshots"
|
|
|
48
48
|
require_relative "runner/project_pre_passes"
|
|
49
49
|
require_relative "runner/pool_coordinator"
|
|
50
50
|
require_relative "runner/diagnostic_aggregator"
|
|
51
|
+
require_relative "template_units"
|
|
51
52
|
require_relative "runner/effect_envelope_pass"
|
|
52
53
|
require_relative "runner/effect_annotation_residual_pass"
|
|
53
54
|
require_relative "runner/buffer_pool_dispatcher"
|
|
@@ -107,6 +108,32 @@ module Rigor
|
|
|
107
108
|
@effect_table || Effects::EffectTable.empty
|
|
108
109
|
end
|
|
109
110
|
|
|
111
|
+
# ADR-103 WD9 (#391) — the envelope index this run resolved, for `rigor sig-gen`'s annotation
|
|
112
|
+
# emission to consult. Public for the same reason {#effect_table} is: a consumer outside the
|
|
113
|
+
# diagnostic stream needs what the run already computed, and rebuilding it from configuration
|
|
114
|
+
# alone would read a narrower set of strata — the accepted signatures and the rbs-inline virtual
|
|
115
|
+
# RBS both come from the built environment.
|
|
116
|
+
#
|
|
117
|
+
# Built on demand when the run never analysed a file. A WARM whole-run cache hit serves the
|
|
118
|
+
# propagated table from cache, so {#analyze_with_effects} — the only caller of
|
|
119
|
+
# {#effect_envelope_index} — never runs and the ivar stays nil. Returning an empty index there
|
|
120
|
+
# would silently drop sig-gen's "the author already declared a bound" gate on exactly the runs a
|
|
121
|
+
# user makes most often: the same `sig-gen` invocation would withhold on its first run and emit on
|
|
122
|
+
# its second. `@run_environment` is resolved before the cache is consulted, so the warm build reads
|
|
123
|
+
# the same strata the cold one does.
|
|
124
|
+
#
|
|
125
|
+
# `@run_environment` is nil only when the run resolved no environment at all — a run over zero
|
|
126
|
+
# project files, or one that never reached {#compute_run_diagnostics}. The build then falls back to
|
|
127
|
+
# the configuration-only strata (`sig/` annotations, class-level ones and `effects.envelopes:`,
|
|
128
|
+
# without the accepted signatures or the rbs-inline virtual RBS), which is the narrower answer but
|
|
129
|
+
# not a wrong one: a run with no environment analysed no file, so there is no candidate for the
|
|
130
|
+
# missing strata to have bounded.
|
|
131
|
+
def effect_envelopes
|
|
132
|
+
return @effect_envelope_index if @effect_envelope_index
|
|
133
|
+
|
|
134
|
+
effect_envelope_index(@run_environment)
|
|
135
|
+
end
|
|
136
|
+
|
|
110
137
|
# ADR-103 WD2 / WD6 / WD10 / #387 — the loaded plugins' effect contributions, compiled once per
|
|
111
138
|
# process. Memoised on first use rather than built in the constructor for two reasons: the plugin
|
|
112
139
|
# registry is adopted after construction (`apply_prebuilt` / the plugin-load pre-pass), and the
|
|
@@ -314,6 +341,10 @@ module Rigor
|
|
|
314
341
|
# check reads.
|
|
315
342
|
@no_tolerated_effects = no_tolerated_effects
|
|
316
343
|
@file_effects = {}
|
|
344
|
+
# #392 — the run's template units, synthesised once per run after the plugins load (see
|
|
345
|
+
# {#template_units}). nil until then; a project whose plugins declare no `template_globs:` resolves
|
|
346
|
+
# to the shared empty index and pays one `Array#empty?` for the whole run.
|
|
347
|
+
@template_units = nil
|
|
317
348
|
@effect_table = nil
|
|
318
349
|
@effects_served_from_cache = false
|
|
319
350
|
# #482 — set only by a run served from the summary entry: the two derived tables it carries, and
|
|
@@ -378,8 +409,16 @@ module Rigor
|
|
|
378
409
|
# Issue #682 — the `Module.nesting` each class / module declaration HEADER is written in.
|
|
379
410
|
@project_discovered_header_nestings = {}.freeze
|
|
380
411
|
@project_discovered_includes = {}.freeze
|
|
412
|
+
# Issue #1123 — the instance-side prepend table (`prepend M`, and `Recv.prepend(M)`), in
|
|
413
|
+
# instance-ancestor SEARCH order.
|
|
414
|
+
@project_discovered_prepends = {}.freeze
|
|
381
415
|
# Issue #898 — the singleton-side twin of the include table (`extend M` / `extend self`).
|
|
382
416
|
@project_discovered_extends = {}.freeze
|
|
417
|
+
@project_discovered_deferred_ranges = {}.freeze
|
|
418
|
+
# Issue #1120 — `{refined class => {method => [refining modules]}}`, every `refine X do … end` body.
|
|
419
|
+
@project_discovered_refinements = {}.freeze
|
|
420
|
+
# Issue #1367 — the project's `Inference::GlobalWriteCensus`, which the `global.*` write rules read.
|
|
421
|
+
@project_discovered_global_write_census = Set.new.freeze
|
|
383
422
|
@project_discovered_class_sources = {}.freeze
|
|
384
423
|
# Issue #644 — the cross-file VALUE-constant publication table (`{qualified name => Type::Constant}`,
|
|
385
424
|
# literal writes only) and its per-name write attribution. The first seeds `in_source_constants` on
|
|
@@ -390,8 +429,12 @@ module Rigor
|
|
|
390
429
|
# ...and the raw per-(name, path) publication census the incremental producer diffs.
|
|
391
430
|
@project_constant_writes = {}.freeze
|
|
392
431
|
@published_constant_name_set = nil
|
|
432
|
+
@constant_writer_index = nil
|
|
433
|
+
@constant_shadower_index = nil
|
|
393
434
|
@project_discovered_method_visibilities = {}.freeze
|
|
394
435
|
@project_discovered_methods = {}.freeze
|
|
436
|
+
# Issue #992 — the cross-file parameter-envelope table `call.wrong-arity` reads for an undeclared `def`.
|
|
437
|
+
@project_discovered_parameter_envelopes = {}.freeze
|
|
395
438
|
@project_data_member_layouts = {}.freeze
|
|
396
439
|
@project_struct_member_layouts = {}.freeze
|
|
397
440
|
# Issue #684 — set per run by {#project_discovery_expansion}; nil on every path that does not widen.
|
|
@@ -453,6 +496,12 @@ module Rigor
|
|
|
453
496
|
@snapshots.reset_for_run
|
|
454
497
|
# Per-run reset of the deferred-discovery memo (see `#ensure_project_discovery`).
|
|
455
498
|
@project_discovery_done = false
|
|
499
|
+
# #392 — re-derive the template units each run: their source files can change between two runs of
|
|
500
|
+
# one long-lived Runner (the LSP's), and a unit is never served from a cache of its own. Since
|
|
501
|
+
# #1038 "re-derive" is not "recompile": a `prebuilt:` runner carries the snapshot's warm index into
|
|
502
|
+
# {#template_units}, which still re-expands every claim and revalidates every template against the
|
|
503
|
+
# filesystem, and only compiles what actually moved.
|
|
504
|
+
@template_units = nil
|
|
456
505
|
# Per-run reset of the environment the cacheable path resolves, reused by the envelope pass so a
|
|
457
506
|
# run never builds two.
|
|
458
507
|
@run_environment = nil
|
|
@@ -746,7 +795,10 @@ module Rigor
|
|
|
746
795
|
@run_served_from_cache = false
|
|
747
796
|
return assemble_run_diagnostics(expansion) unless run_result_cacheable?
|
|
748
797
|
|
|
749
|
-
|
|
798
|
+
# #392 — `source_files:` is what a plugin's `source_rbs_synthesizer` is offered at env-build time,
|
|
799
|
+
# and a template unit is not Ruby the synthesiser can read (rbs-inline would be handed `.rbx`
|
|
800
|
+
# bytes). The `.rb` expansion is the right set here; the units join the ANALYSED set only.
|
|
801
|
+
environment = @pool_coordinator.resolve_sequential_environment(source_files: expansion.fetch(:files))
|
|
750
802
|
# Lazy-files descriptor: the cache KEY reads only `gems` + `configs`; the RBS signature-tree `files`
|
|
751
803
|
# are digested solely by `run_dependency_descriptor` on a MISS, so a warm HIT never walks the tree.
|
|
752
804
|
rbs_descriptor = if environment&.rbs_loader
|
|
@@ -998,7 +1050,14 @@ module Rigor
|
|
|
998
1050
|
def close_effect_graph
|
|
999
1051
|
return unless @record_effects
|
|
1000
1052
|
|
|
1001
|
-
|
|
1053
|
+
# `declined_unit_keys:` — #1065. The templates a plugin claimed and produced no unit for; a render
|
|
1054
|
+
# edge's format fallback must not resolve past one of them, because the file exists and the
|
|
1055
|
+
# framework runs it, so the other format's effects are not what that render produces.
|
|
1056
|
+
@effect_table = Effects::Propagator.propagate(
|
|
1057
|
+
effect_collection,
|
|
1058
|
+
discharge: effect_discharge,
|
|
1059
|
+
declined_unit_keys: template_units.declined_unit_keys
|
|
1060
|
+
)
|
|
1002
1061
|
rescue StandardError
|
|
1003
1062
|
@effect_table = Effects::EffectTable.empty
|
|
1004
1063
|
end
|
|
@@ -1029,6 +1088,11 @@ module Rigor
|
|
|
1029
1088
|
# untouched so its lazy build timing is unchanged.
|
|
1030
1089
|
environment = seed_pre_eval_constants(expansion, environment)
|
|
1031
1090
|
diagnostics = @diagnostic_aggregator.pre_file_diagnostics(expansion)
|
|
1091
|
+
# #392 — a template transform that raised, a template that could not be read, or a unit naming a
|
|
1092
|
+
# file it was not offered. Reported through the same `:plugin_loader` `runtime-error` envelope a
|
|
1093
|
+
# raise from `#diagnostics_for_file` uses (ADR-2 § "Plugin Trust and I/O Policy"), rather than
|
|
1094
|
+
# dropped in silence: a plugin author whose view stopped being analysed has nothing else to read.
|
|
1095
|
+
diagnostics += template_unit_failure_diagnostics
|
|
1032
1096
|
# ADR-46 — record which project files this run actually analyzed (the `analyze_only` subset, or
|
|
1033
1097
|
# all of them). The incremental orchestrator serves every analyzed-but-not-affected file from the
|
|
1034
1098
|
# per-file cache, so it needs the full analyzed set to subtract the affected closure from.
|
|
@@ -1045,6 +1109,11 @@ module Rigor
|
|
|
1045
1109
|
# override re-stamps it; and a pool backend folds `.rigor.yml`-positioned prepare / pool-degraded rows
|
|
1046
1110
|
# into the same return, which the slice drops. The run's own stream is stamped once, at the end.
|
|
1047
1111
|
diagnostics += analyze_targets(targets, environment: environment, project_files: expansion.fetch(:files))
|
|
1112
|
+
# Issue #1060 — the positioned batches plugins registered through `Plugin::Base#emit_once`, read once
|
|
1113
|
+
# every worker payload has been merged. Kept beside the per-file stream (their rows name files and
|
|
1114
|
+
# lines) but outside `#per_file_diagnostics`, which `analyze_targets` has already sliced, so no one
|
|
1115
|
+
# file's incremental cache entry carries a project-wide batch.
|
|
1116
|
+
diagnostics += @diagnostic_aggregator.plugin_run_emission_diagnostics
|
|
1048
1117
|
# ADR-103 WD12 — the effect fixpoint, in the post-pool aggregation slot beside the conformance
|
|
1049
1118
|
# results. Graph-only over a finite lattice, so it is a plain worklist to a true fixpoint; it
|
|
1050
1119
|
# contributes NO diagnostics and its result leaves through `#effect_table`, never through the
|
|
@@ -1053,6 +1122,18 @@ module Rigor
|
|
|
1053
1122
|
diagnostics + post_analysis_diagnostic_streams
|
|
1054
1123
|
end
|
|
1055
1124
|
|
|
1125
|
+
def template_unit_failure_diagnostics
|
|
1126
|
+
template_units.failures.map do |failure|
|
|
1127
|
+
Diagnostic.new(
|
|
1128
|
+
path: failure.path, line: 1, column: 1,
|
|
1129
|
+
message: "plugin #{failure.plugin_id.inspect} produced no template unit for " \
|
|
1130
|
+
"#{failure.path}: #{failure.message}",
|
|
1131
|
+
severity: :error, rule: "runtime-error", source_family: :plugin_loader
|
|
1132
|
+
)
|
|
1133
|
+
end
|
|
1134
|
+
end
|
|
1135
|
+
private :template_unit_failure_diagnostics
|
|
1136
|
+
|
|
1056
1137
|
# The rbs-coverage build-failure ladder plus every remaining post-analysis stream
|
|
1057
1138
|
# `#assemble_run_diagnostics` appends, in their fixed contract order (see the comments at each call
|
|
1058
1139
|
# below and at the streams' own definitions). Split out of `#assemble_run_diagnostics` so that
|
|
@@ -1077,6 +1158,11 @@ module Rigor
|
|
|
1077
1158
|
diagnostics += @diagnostic_aggregator.rbs_extended_reporter_diagnostics
|
|
1078
1159
|
diagnostics += @diagnostic_aggregator.boundary_cross_diagnostics
|
|
1079
1160
|
diagnostics += @diagnostic_aggregator.source_rbs_synthesis_diagnostics
|
|
1161
|
+
# Issue #1051 — read after `analyze_targets` for the same reason the refusal stream below is: a
|
|
1162
|
+
# plugin may register a disclosure from `#prepare` (the usual case) or while analysing a file, and
|
|
1163
|
+
# both have to be in before the table is harvested. Ordered before the refusals so the run-level
|
|
1164
|
+
# plugin rows read plugin-authored notice first, engine-authored envelope second.
|
|
1165
|
+
diagnostics += @diagnostic_aggregator.plugin_run_disclosure_diagnostics
|
|
1080
1166
|
# Issue #959 — read LAST (after `analyze_targets`, which already ran before this method is called),
|
|
1081
1167
|
# so every per-file plugin call has had its chance to record a refusal on its (per-plugin-instance
|
|
1082
1168
|
# memoised) `IoBoundary`, alongside the prepare-time refusals the same boundary instance may
|
|
@@ -1091,7 +1177,9 @@ module Rigor
|
|
|
1091
1177
|
# re-stamps), sliced to the rows positioned at the targets. Returns the RAW return for the run's stream,
|
|
1092
1178
|
# which is stamped once, at the end of `#run_analysis`.
|
|
1093
1179
|
def analyze_targets(targets, environment:, project_files:)
|
|
1094
|
-
raw =
|
|
1180
|
+
raw = template_units.remap(
|
|
1181
|
+
@pool_coordinator.analyze_files(targets, environment: environment, project_files: project_files)
|
|
1182
|
+
)
|
|
1095
1183
|
analysed = targets.to_set
|
|
1096
1184
|
@per_file_diagnostics = @diagnostic_aggregator.apply_severity_profile(raw)
|
|
1097
1185
|
.select { |diagnostic| analysed.include?(diagnostic.path) }
|
|
@@ -1199,7 +1287,8 @@ module Rigor
|
|
|
1199
1287
|
# reconstructs `rbs.libraries` from config) can never drift out of key agreement.
|
|
1200
1288
|
RunCacheKey.descriptor(
|
|
1201
1289
|
configuration: @configuration, files: expansion.fetch(:files),
|
|
1202
|
-
explain: @explain, rbs_config_entries: rbs_descriptor.configs
|
|
1290
|
+
explain: @explain, rbs_config_entries: rbs_descriptor.configs,
|
|
1291
|
+
template_units_digest: template_units.digest
|
|
1203
1292
|
)
|
|
1204
1293
|
end
|
|
1205
1294
|
|
|
@@ -1216,11 +1305,11 @@ module Rigor
|
|
|
1216
1305
|
|
|
1217
1306
|
def build_run_dependency_descriptor(expansion, rbs_descriptor)
|
|
1218
1307
|
entries = analyzed_file_entries(expansion) + discovery_file_entries(expansion) +
|
|
1219
|
-
pre_eval_file_entries + rbs_descriptor.files
|
|
1308
|
+
pre_eval_file_entries + template_unit_file_entries + rbs_descriptor.files
|
|
1220
1309
|
# #979 — the signature ROOTS' listings, one glob row each. `rbs_descriptor.files` covers only the
|
|
1221
1310
|
# `.rbs` files that existed while the run read them, so without these a signature file written after
|
|
1222
1311
|
# the run left the slot validating fresh and the warm run answered without it.
|
|
1223
|
-
globs = rbs_descriptor.globs
|
|
1312
|
+
globs = rbs_descriptor.globs + template_unit_glob_entries
|
|
1224
1313
|
@plugin_registry.plugins.each do |plugin|
|
|
1225
1314
|
# Read the boundary WITHOUT triggering its lazy `@io_boundary ||=` initializer: plugin instances
|
|
1226
1315
|
# are frozen after the run, and a plugin that never built a boundary read no files through it,
|
|
@@ -1268,6 +1357,30 @@ module Rigor
|
|
|
1268
1357
|
# expansion — so without this entry, editing one would leave the run-result cache serving diagnostics
|
|
1269
1358
|
# computed against the previous version. The common case (a `lib/core_ext/` file that is also under
|
|
1270
1359
|
# `paths:`) contributes a duplicate entry, which the descriptor's per-path validation absorbs.
|
|
1360
|
+
# #392 — the TEMPLATE files behind the run's units. The key slot carries the compiled Ruby's digest
|
|
1361
|
+
# (`template_units_digest`), which is what decides whether a cached answer describes this source; these
|
|
1362
|
+
# entries are the validation half, so a template edited between two runs re-globs and re-digests here
|
|
1363
|
+
# exactly as an analysed `.rb` file does. Both are needed: a transform that compiles two different
|
|
1364
|
+
# templates to the same Ruby must still not resurrect the wrong PATH in a diagnostic.
|
|
1365
|
+
# #392 — one `:names` row per claimed `template_globs:` pattern, whether or not it matched. The file
|
|
1366
|
+
# rows below cover edits to templates that EXIST; only a glob row notices one appearing or vanishing,
|
|
1367
|
+
# which is what let a project's first template stay invisible to every warm run.
|
|
1368
|
+
def template_unit_glob_entries
|
|
1369
|
+
template_units.glob_entries
|
|
1370
|
+
end
|
|
1371
|
+
|
|
1372
|
+
# Every template the run READ, successes and failures alike: a `plugin_loader` row produced by a
|
|
1373
|
+
# template that would not compile must not outlive the edit that fixes it, and a buffer-bound path is
|
|
1374
|
+
# digested at the bytes the run actually read.
|
|
1375
|
+
def template_unit_file_entries
|
|
1376
|
+
template_units.source_paths.filter_map do |path|
|
|
1377
|
+
physical = template_units.physical_path(path, @buffer)
|
|
1378
|
+
next unless File.file?(physical)
|
|
1379
|
+
|
|
1380
|
+
Cache::Descriptor::FileEntry.stat(path: physical, digest: Cache::FileDigest.hexdigest(physical))
|
|
1381
|
+
end
|
|
1382
|
+
end
|
|
1383
|
+
|
|
1271
1384
|
def pre_eval_file_entries
|
|
1272
1385
|
@configuration.pre_eval.filter_map do |path|
|
|
1273
1386
|
physical = @buffer ? @buffer.resolve(path) : path
|
|
@@ -1352,19 +1465,39 @@ module Rigor
|
|
|
1352
1465
|
@project_discovered_singleton_def_sources = discovery.discovered_singleton_def_sources
|
|
1353
1466
|
@project_discovered_superclasses = discovery.discovered_superclasses
|
|
1354
1467
|
@project_discovered_header_nestings = discovery.discovered_header_nestings
|
|
1355
|
-
|
|
1356
|
-
@
|
|
1468
|
+
apply_discovery_mixin_tables(discovery)
|
|
1469
|
+
@project_discovered_deferred_ranges = discovery.discovered_deferred_ranges
|
|
1470
|
+
apply_discovery_call_surface_tables(discovery)
|
|
1357
1471
|
@project_discovered_class_sources = discovery.discovered_class_sources
|
|
1358
1472
|
@project_constant_values = discovery.constant_values
|
|
1359
1473
|
@published_constant_name_set = nil
|
|
1474
|
+
@constant_writer_index = nil
|
|
1475
|
+
@constant_shadower_index = nil
|
|
1360
1476
|
@project_constant_sources = discovery.constant_sources
|
|
1361
1477
|
@project_constant_writes = discovery.constant_writes
|
|
1362
1478
|
@project_discovered_method_visibilities = discovery.discovered_method_visibilities
|
|
1363
1479
|
@project_discovered_methods = discovery.discovered_methods
|
|
1480
|
+
@project_discovered_parameter_envelopes = discovery.discovered_parameter_envelopes
|
|
1364
1481
|
@project_data_member_layouts = discovery.data_member_layouts
|
|
1365
1482
|
@project_struct_member_layouts = discovery.struct_member_layouts
|
|
1366
1483
|
end
|
|
1367
1484
|
|
|
1485
|
+
# The tables only check rules read beyond dispatch — the issue #1120 refinements and the issue #1367
|
|
1486
|
+
# global-write census. Extracted to keep {#apply_discovery_result} under its ABC budget.
|
|
1487
|
+
def apply_discovery_call_surface_tables(discovery)
|
|
1488
|
+
@project_discovered_refinements = discovery.discovered_refinements
|
|
1489
|
+
@project_discovered_global_write_census = discovery.discovered_global_write_census
|
|
1490
|
+
end
|
|
1491
|
+
|
|
1492
|
+
# The three mixin tables the discovery pass carries — the ADR-24 `include` map, its issue #1123
|
|
1493
|
+
# `prepend` twin, and the #898 `extend` table. Extracted to keep {#apply_discovery_result} under its
|
|
1494
|
+
# ABC budget.
|
|
1495
|
+
def apply_discovery_mixin_tables(discovery)
|
|
1496
|
+
@project_discovered_includes = discovery.discovered_includes
|
|
1497
|
+
@project_discovered_prepends = discovery.discovered_prepends
|
|
1498
|
+
@project_discovered_extends = discovery.discovered_extends
|
|
1499
|
+
end
|
|
1500
|
+
|
|
1368
1501
|
# Internal: builds the deferred cross-file discovery tables at most once per run and adopts them.
|
|
1369
1502
|
# Memoised on `@project_discovery_done` (reset at the start of `#run`). No-op under `@prebuilt` — the
|
|
1370
1503
|
# LSP path deliberately seeds an empty project scope from a snapshot that carries no discovery tables,
|
|
@@ -1498,6 +1631,25 @@ module Rigor
|
|
|
1498
1631
|
# The buffer's logical path is added to the file list even if it's not under `paths:` — per design §
|
|
1499
1632
|
# "Failure envelope": "--instead-of=Y with Y not under any paths: directory → treated as a valid
|
|
1500
1633
|
# logical identity for the buffer".
|
|
1634
|
+
# #392 — the run's template units. Built once per run, on the parent, AFTER the plugin pre-pass has
|
|
1635
|
+
# loaded the registry; a run whose plugins declare no `template_globs:` never globs and never calls a
|
|
1636
|
+
# plugin.
|
|
1637
|
+
#
|
|
1638
|
+
# #1038 — a runner built with `prebuilt:` hands the snapshot's warm index to `collect(previous:)`, so
|
|
1639
|
+
# a per-buffer publish against a long-lived {LanguageServer::ProjectContext} re-runs the transform
|
|
1640
|
+
# only for a template that actually moved on disk (and for the buffer's own, always). The globs are
|
|
1641
|
+
# still re-expanded and every surviving template is still revalidated, so an added, deleted or edited
|
|
1642
|
+
# template is seen on the next publish without the owner invalidating anything. A runner with no
|
|
1643
|
+
# `prebuilt:` — every CLI run — passes no `previous:` and builds the index exactly as before.
|
|
1644
|
+
def template_units
|
|
1645
|
+
@template_units ||= if @plugin_registry.nil?
|
|
1646
|
+
TemplateUnits.empty
|
|
1647
|
+
else
|
|
1648
|
+
TemplateUnits.collect(registry: @plugin_registry, buffer: @buffer,
|
|
1649
|
+
previous: @prebuilt&.template_units)
|
|
1650
|
+
end
|
|
1651
|
+
end
|
|
1652
|
+
|
|
1501
1653
|
def target_files(expansion)
|
|
1502
1654
|
files = expansion.fetch(:files)
|
|
1503
1655
|
# ADR-46 slice 2 — restrict the analyzed set to the affected closure while the pre-pass (run
|
|
@@ -1508,14 +1660,41 @@ module Rigor
|
|
|
1508
1660
|
# the same allowance option A makes below.
|
|
1509
1661
|
files = files.select { |path| @analyze_only.include?(path) }
|
|
1510
1662
|
files |= [@buffer.logical_path] if @buffer && @analyze_only.include?(@buffer.logical_path)
|
|
1511
|
-
return files
|
|
1663
|
+
return template_unit_targets(files)
|
|
1512
1664
|
end
|
|
1513
|
-
return files if @buffer.nil?
|
|
1665
|
+
return template_unit_targets(files) if @buffer.nil?
|
|
1514
1666
|
|
|
1515
|
-
# Editor mode option A — no closure, so the buffer's single logical path IS the analyzed set.
|
|
1667
|
+
# Editor mode option A — no closure, so the buffer's single logical path IS the analyzed set. The
|
|
1668
|
+
# template units deliberately do NOT join it: a per-buffer publish answers about the buffer the
|
|
1669
|
+
# editor is showing, and appending every view would publish diagnostics for files the editor did
|
|
1670
|
+
# not ask about (and re-parse them on every keystroke).
|
|
1516
1671
|
[@buffer.logical_path]
|
|
1517
1672
|
end
|
|
1518
1673
|
|
|
1674
|
+
# #392 — the template units join the analysed set AFTER any narrowing, so an `--incremental` recheck
|
|
1675
|
+
# re-analyses every unit even when its closure is one `.rb` file. Nothing in the ADR-46 dependency
|
|
1676
|
+
# graph names a synthesised file, so there is no edge that could put a unit in a closure — narrowing
|
|
1677
|
+
# first and appending second is what makes "always re-analysed" true rather than a comment. The cost
|
|
1678
|
+
# is one parse per template on the warm path; the alternative silently drops every template
|
|
1679
|
+
# diagnostic from the second run onwards.
|
|
1680
|
+
#
|
|
1681
|
+
# Appended rather than merged in sorted order so the `.rb` expansion's own order — and every output
|
|
1682
|
+
# that follows it — is byte-identical for a project with no units.
|
|
1683
|
+
def template_unit_targets(files)
|
|
1684
|
+
return files if template_units.empty?
|
|
1685
|
+
|
|
1686
|
+
files + (template_units.paths - files)
|
|
1687
|
+
end
|
|
1688
|
+
private :template_unit_targets
|
|
1689
|
+
|
|
1690
|
+
# #392 — the unit paths this run synthesised. Read by {IncrementalSession}, which keeps them OUT of
|
|
1691
|
+
# its analysed-file set: a unit is never served from the per-file cache and never counts as a file
|
|
1692
|
+
# that was removed from the project, because `#target_files` re-analyses it every run.
|
|
1693
|
+
def template_unit_paths
|
|
1694
|
+
template_units.paths
|
|
1695
|
+
end
|
|
1696
|
+
public :template_unit_paths
|
|
1697
|
+
|
|
1519
1698
|
# Editor mode (`buffer:` non-nil) auto-flips the cache store to `read_only: true` so multiple
|
|
1520
1699
|
# debounced editor invocations against the same project don't churn the on-disk cache or race on
|
|
1521
1700
|
# schema-version writes. Cache reads continue unchanged; misses still run the producer block but the
|
|
@@ -1552,6 +1731,7 @@ module Rigor
|
|
|
1552
1731
|
synthetic_method_index: -> { @synthetic_method_index },
|
|
1553
1732
|
project_patched_methods: -> { @project_patched_methods },
|
|
1554
1733
|
project_scope_seed: -> { project_scope_seed_tables },
|
|
1734
|
+
template_units: -> { template_units },
|
|
1555
1735
|
analyze_file: ->(path, environment) { analyze_file(path, environment) },
|
|
1556
1736
|
restored_run_level_rows: @restored_run_level_rows
|
|
1557
1737
|
)
|
|
@@ -1571,7 +1751,8 @@ module Rigor
|
|
|
1571
1751
|
env_build_failure_snapshot: -> { @snapshots.env_build_failure },
|
|
1572
1752
|
definition_build_failures_snapshot: -> { @snapshots.definition_build_failures },
|
|
1573
1753
|
hkt_scan_failure_snapshot: -> { @snapshots.hkt_scan_failure },
|
|
1574
|
-
conformance_results_snapshot: -> { @snapshots.conformance_results }
|
|
1754
|
+
conformance_results_snapshot: -> { @snapshots.conformance_results },
|
|
1755
|
+
pooled_run_disclosures: -> { @pool_coordinator.collected_run_disclosures }
|
|
1575
1756
|
)
|
|
1576
1757
|
end
|
|
1577
1758
|
|
|
@@ -1598,7 +1779,9 @@ module Rigor
|
|
|
1598
1779
|
RunStats.new(
|
|
1599
1780
|
wall_seconds: Process.clock_gettime(Process::CLOCK_MONOTONIC) - wall_started_at,
|
|
1600
1781
|
peak_rss_bytes: RunStats.peak_rss_bytes,
|
|
1601
|
-
|
|
1782
|
+
# #392 — the template units count too: they are files this run parsed and typed, and a stats line
|
|
1783
|
+
# that omitted them would under-report exactly the work a views-heavy project added.
|
|
1784
|
+
target_files: expansion.fetch(:files).size + template_units.paths.size,
|
|
1602
1785
|
rbs_classes_total: snapshot.size,
|
|
1603
1786
|
rbs_classes_project_sig: project_sig,
|
|
1604
1787
|
rbs_classes_bundled: bundled,
|
|
@@ -1755,6 +1938,12 @@ module Rigor
|
|
|
1755
1938
|
# so Prism's location data carries the LOGICAL path. Non-binding paths go through the cheaper
|
|
1756
1939
|
# `Prism.parse_file` codepath unchanged.
|
|
1757
1940
|
def parse_source(path)
|
|
1941
|
+
entry = template_units[path]
|
|
1942
|
+
if entry
|
|
1943
|
+
return Prism.parse(entry.source, filepath: path, version: @configuration.target_ruby,
|
|
1944
|
+
scopes: template_units.parse_scopes(path))
|
|
1945
|
+
end
|
|
1946
|
+
|
|
1758
1947
|
if @in_memory_sources&.key?(path)
|
|
1759
1948
|
return Prism.parse(@in_memory_sources[path], filepath: path, version: @configuration.target_ruby)
|
|
1760
1949
|
end
|
|
@@ -1807,25 +1996,53 @@ module Rigor
|
|
|
1807
1996
|
tables[:discovered_method_visibilities] = @project_discovered_method_visibilities
|
|
1808
1997
|
end
|
|
1809
1998
|
tables[:discovered_methods] = @project_discovered_methods unless @project_discovered_methods.empty?
|
|
1999
|
+
unless @project_discovered_deferred_ranges.empty?
|
|
2000
|
+
tables[:discovered_deferred_ranges] = @project_discovered_deferred_ranges
|
|
2001
|
+
end
|
|
2002
|
+
seed_call_surface_tables(tables)
|
|
1810
2003
|
seed_opt_in_pre_pass_tables(tables)
|
|
1811
2004
|
seed_member_layout_tables(tables)
|
|
1812
2005
|
seed_dependency_attribution_tables(tables)
|
|
1813
2006
|
tables
|
|
1814
2007
|
end
|
|
1815
2008
|
|
|
1816
|
-
# The
|
|
1817
|
-
# `
|
|
1818
|
-
# of {#project_scope_seed_tables} to keep it under the complexity budget.
|
|
2009
|
+
# The tables only check rules read: the issue #992 parameter envelopes (`call.wrong-arity`), the issue #1120
|
|
2010
|
+
# refinements (`call.undefined-method`), and the issue #1367 global-write census (the `global.*` write
|
|
2011
|
+
# rules). Split out of {#project_scope_seed_tables} to keep it under the complexity budget.
|
|
2012
|
+
def seed_call_surface_tables(tables)
|
|
2013
|
+
unless @project_discovered_parameter_envelopes.empty?
|
|
2014
|
+
tables[:discovered_parameter_envelopes] = @project_discovered_parameter_envelopes
|
|
2015
|
+
end
|
|
2016
|
+
tables[:discovered_refinements] = @project_discovered_refinements unless @project_discovered_refinements.empty?
|
|
2017
|
+
# Issue #1367 — read by the `global.*` write rules.
|
|
2018
|
+
return if @project_discovered_global_write_census.empty?
|
|
2019
|
+
|
|
2020
|
+
tables[:discovered_global_write_census] = @project_discovered_global_write_census
|
|
2021
|
+
end
|
|
2022
|
+
|
|
2023
|
+
# The three mixin tables: the ADR-24 instance-side `include` map, its issue #1123 `prepend` twin
|
|
2024
|
+
# (which carries the instance-ancestor order the include table's set-shaped value cannot), and the
|
|
2025
|
+
# #898 singleton-side `extend` table. Grouped here because they are read as one picture of a class's
|
|
2026
|
+
# ancestry, and split out of {#project_scope_seed_tables} to keep it under the complexity budget.
|
|
1819
2027
|
def seed_mixin_tables(tables)
|
|
1820
2028
|
tables[:discovered_includes] = @project_discovered_includes unless @project_discovered_includes.empty?
|
|
2029
|
+
tables[:discovered_prepends] = @project_discovered_prepends unless @project_discovered_prepends.empty?
|
|
1821
2030
|
tables[:discovered_extends] = @project_discovered_extends unless @project_discovered_extends.empty?
|
|
1822
2031
|
end
|
|
1823
2032
|
|
|
1824
2033
|
# ADR-46 slice 1 / issue #644 — the two SOURCE-ATTRIBUTION tables, read only by the recording accessors
|
|
1825
2034
|
# (`Scope#record_class_dependency` for the class-declaration map, `Scope#record_constant_dependency`
|
|
1826
|
-
# for the constant-write map). A normal run carries neither. Extracted to keep
|
|
2035
|
+
# for the constant-write map). A normal run carries neither of the two. Extracted to keep
|
|
1827
2036
|
# {#project_scope_seed_tables} under the complexity budget.
|
|
2037
|
+
#
|
|
2038
|
+
# Issue #617 — the census's binding writes, grouped by last segment, ride every run instead: a constant
|
|
2039
|
+
# compound write whose plain read resolves nothing reads its binding off them, a TYPE rather than an edge.
|
|
2040
|
+
# Issue #1290's shadowing writes ride beside them for the lexical ladder, for the same reason.
|
|
1828
2041
|
def seed_dependency_attribution_tables(tables)
|
|
2042
|
+
writers = constant_writer_index
|
|
2043
|
+
tables[:constant_writers] = writers unless writers.empty?
|
|
2044
|
+
shadowers = constant_shadower_index
|
|
2045
|
+
tables[:constant_shadowers] = shadowers unless shadowers.empty?
|
|
1829
2046
|
return unless @record_dependencies
|
|
1830
2047
|
|
|
1831
2048
|
tables[:discovered_class_sources] = @project_discovered_class_sources unless
|
|
@@ -1860,6 +2077,44 @@ module Rigor
|
|
|
1860
2077
|
tables[:published_constant_names] = names unless names.empty?
|
|
1861
2078
|
end
|
|
1862
2079
|
|
|
2080
|
+
# Issue #617 — the censused names that bind, grouped by last segment (`Scope#bound_constant_names`). A
|
|
2081
|
+
# name some write other than a memo `||=` touches always binds. A memo-only name binds once memos of its
|
|
2082
|
+
# segment span two files: either may load first and set the constant the other then reads, so only a
|
|
2083
|
+
# memo no other file shares keeps the idiom's reading. Files are counted expanded, because discovery can
|
|
2084
|
+
# walk one file under two spellings (`lib/b.rb`, `./lib/b.rb`).
|
|
2085
|
+
def constant_writer_index
|
|
2086
|
+
@constant_writer_index ||= begin
|
|
2087
|
+
memo_files = memo_files_by_segment
|
|
2088
|
+
@project_constant_writes.each_with_object({}) do |(name, by_path), index|
|
|
2089
|
+
segment = name.split("::").last
|
|
2090
|
+
next if memo_only?(by_path) && memo_files.fetch(segment).size < 2
|
|
2091
|
+
|
|
2092
|
+
(index[segment] ||= []) << name
|
|
2093
|
+
end.each_value(&:freeze).freeze
|
|
2094
|
+
end
|
|
2095
|
+
end
|
|
2096
|
+
|
|
2097
|
+
# Issue #1290 — the censused names some write other than a memo `||=` assigns, grouped by last segment
|
|
2098
|
+
# (`Scope#shadowing_constant_names`): the ones that exist whether or not an outer constant of the name
|
|
2099
|
+
# does, and so stop the lexical ladder above it. A memo-only name is left out however many files memoize
|
|
2100
|
+
# it, unlike in {#constant_writer_index}: `X ||= v` resolves `X` through the very lookup whose lower rung
|
|
2101
|
+
# the stop would replace, so wherever that rung answers, the memo finds it and never creates the name.
|
|
2102
|
+
def constant_shadower_index
|
|
2103
|
+
@constant_shadower_index ||= @project_constant_writes.each_with_object({}) do |(name, by_path), index|
|
|
2104
|
+
(index[name.split("::").last] ||= []) << name unless memo_only?(by_path)
|
|
2105
|
+
end.each_value(&:freeze).freeze
|
|
2106
|
+
end
|
|
2107
|
+
|
|
2108
|
+
def memo_files_by_segment
|
|
2109
|
+
@project_constant_writes.each_with_object({}) do |(name, by_path), files|
|
|
2110
|
+
set = (files[name.split("::").last] ||= Set.new)
|
|
2111
|
+
by_path.each { |path, descriptor| set << File.expand_path(path) if memo_descriptor?(descriptor) }
|
|
2112
|
+
end
|
|
2113
|
+
end
|
|
2114
|
+
|
|
2115
|
+
def memo_only?(by_path) = by_path.each_value.all? { |descriptor| memo_descriptor?(descriptor) }
|
|
2116
|
+
def memo_descriptor?(descriptor) = descriptor == Inference::ScopeIndexer::CONSTANT_MEMO
|
|
2117
|
+
|
|
1863
2118
|
# Issue #644 — the LAST SEGMENTS of the published table, the run-wide half of
|
|
1864
2119
|
# `Scope#published_constant?`. Seeded on EVERY run (unlike `constant_sources`, which only a recording
|
|
1865
2120
|
# run needs) because {CheckRules::PublishedConstantGuard} asks the question on every `if` / `unless`.
|
|
@@ -1963,10 +2218,11 @@ module Rigor
|
|
|
1963
2218
|
def analyze_with_effects(path, environment)
|
|
1964
2219
|
return analyze_file_body(path, environment) unless @record_effects
|
|
1965
2220
|
|
|
2221
|
+
entry = template_units[path]
|
|
1966
2222
|
diagnostics = nil
|
|
1967
2223
|
collection = Effects::Collector.collect_for(
|
|
1968
2224
|
path, attribution: @effect_attribution, envelopes: effect_envelope_index(environment),
|
|
1969
|
-
plugin_facts: effect_plugin_facts
|
|
2225
|
+
plugin_facts: effect_plugin_facts, unit_key: entry&.unit_key, unit_owner: entry&.self_type
|
|
1970
2226
|
) do
|
|
1971
2227
|
diagnostics = analyze_file_body(path, environment)
|
|
1972
2228
|
end
|
|
@@ -2005,7 +2261,9 @@ module Rigor
|
|
|
2005
2261
|
# is a per-FILE site, so one `Thread.current` read is already nothing. The per-dispatch site in
|
|
2006
2262
|
# `ExpressionTyper#call_type_for` is the one that pays for the integer fast path.
|
|
2007
2263
|
Effects::Collector.record_root(parse_result.value)
|
|
2008
|
-
scope =
|
|
2264
|
+
scope = template_units.seed(
|
|
2265
|
+
seed_project_scope(Scope.empty(environment: environment, source_path: path)), path
|
|
2266
|
+
)
|
|
2009
2267
|
# ADR-24 slice 4a/4 — record unresolved implicit-self calls during the typing pass ONLY (not
|
|
2010
2268
|
# CheckRules, whose own `type_of` queries would otherwise re-trigger the choke-point).
|
|
2011
2269
|
# `self_call_misses` feeds the `call.self-undefined-method` collector; the recorder is inert unless
|
|
@@ -2045,7 +2303,7 @@ module Rigor
|
|
|
2045
2303
|
# output and the plugin diagnostics are then built from that single walk's results.
|
|
2046
2304
|
def rule_and_plugin_diagnostics(path, parse_result, scope, index, self_call_misses)
|
|
2047
2305
|
root = parse_result.value
|
|
2048
|
-
node_collectors = CheckRules.build_node_collectors(path, index)
|
|
2306
|
+
node_collectors = CheckRules.build_node_collectors(path, index, root)
|
|
2049
2307
|
node_results = node_rule_results_by_plugin(path, root, scope, node_collectors, index)
|
|
2050
2308
|
diagnostics = CheckRules.diagnose(
|
|
2051
2309
|
path: path,
|