rigortype 0.3.8 → 0.3.9
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 +1 -1
- data/data/capability_roles/capability_roles.rbs +36 -0
- data/data/gem_overlay/activesupport/core_ext.rbs +109 -13
- data/docs/handbook/02-everyday-types.md +17 -14
- data/docs/handbook/03-narrowing.md +37 -4
- data/docs/handbook/05-methods-and-blocks.md +1 -1
- data/docs/handbook/06-classes.md +2 -2
- data/docs/handbook/07-rbs-and-extended.md +14 -1
- data/docs/handbook/11-sig-gen.md +72 -1
- data/docs/handbook/README.md +1 -1
- data/docs/handbook/appendix-elixir.md +2 -2
- data/docs/handbook/appendix-go.md +2 -2
- data/docs/handbook/appendix-java-csharp.md +2 -2
- data/docs/handbook/appendix-mypy.md +2 -2
- data/docs/handbook/appendix-phpstan.md +1 -1
- data/docs/handbook/appendix-rust.md +1 -1
- data/docs/handbook/appendix-type-theory.md +4 -4
- data/docs/handbook/appendix-typescript.md +1 -1
- data/docs/llms.txt +2 -0
- data/docs/manual/02-cli-reference.md +32 -4
- data/docs/manual/03-configuration.md +11 -0
- data/docs/manual/04-diagnostics.md +11 -0
- data/docs/manual/07-plugins.md +15 -5
- data/docs/manual/08-skills.md +23 -2
- data/docs/manual/11-ci.md +9 -0
- data/docs/manual/15-type-protection-coverage.md +8 -0
- data/docs/manual/16-rbs-extended-annotations.md +6 -1
- data/docs/manual/18-removing-dead-code.md +10 -8
- data/docs/manual/plugins/rigor-actionpack.md +18 -0
- data/docs/manual/plugins/rigor-activerecord.md +28 -0
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +32 -2
- data/docs/manual/plugins/rigor-rbs-inline.md +42 -1
- data/lib/rigor/analysis/baseline.rb +2 -2
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/dead_version_guard_arms.rb +1 -5
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +10 -2
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +15 -6
- data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -1
- data/lib/rigor/analysis/check_rules/rule_walk.rb +39 -4
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +1 -1
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +0 -1
- data/lib/rigor/analysis/check_rules.rb +200 -32
- data/lib/rigor/analysis/crash_signature.rb +8 -24
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/builder.rb +0 -2
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +0 -2
- data/lib/rigor/analysis/dependency_source_inference/index.rb +5 -5
- data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +1 -2
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
- data/lib/rigor/analysis/effects_cache_probe.rb +3 -4
- data/lib/rigor/analysis/erb_template_detector.rb +1 -2
- data/lib/rigor/analysis/fact_store.rb +6 -1
- data/lib/rigor/analysis/incremental.rb +16 -0
- data/lib/rigor/analysis/incremental_session.rb +81 -22
- data/lib/rigor/analysis/plugin_fact_fingerprint.rb +0 -1
- data/lib/rigor/analysis/reachability/graph.rb +3 -5
- data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -5
- data/lib/rigor/analysis/reachability/project_files.rb +2 -2
- data/lib/rigor/analysis/reachability/scan.rb +5 -5
- data/lib/rigor/analysis/reachability/scan_cache.rb +2 -2
- data/lib/rigor/analysis/reachability/signature_scan.rb +2 -2
- data/lib/rigor/analysis/result.rb +1 -3
- data/lib/rigor/analysis/rule_catalog.rb +4 -1
- data/lib/rigor/analysis/run_cache_key.rb +1 -1
- data/lib/rigor/analysis/run_cache_probe.rb +3 -5
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -8
- data/lib/rigor/analysis/runner/declaration_position.rb +1 -2
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +177 -26
- data/lib/rigor/analysis/runner/effect_annotation_residual_pass.rb +2 -3
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +9 -10
- data/lib/rigor/analysis/runner/pool_coordinator.rb +256 -88
- data/lib/rigor/analysis/runner/project_pre_passes.rb +12 -6
- data/lib/rigor/analysis/runner/run_snapshots.rb +6 -1
- data/lib/rigor/analysis/runner.rb +133 -35
- data/lib/rigor/analysis/worker_session.rb +9 -10
- data/lib/rigor/bleeding_edge.rb +12 -24
- data/lib/rigor/builtins/hkt_builtins.rb +2 -2
- data/lib/rigor/builtins/imported_refinements.rb +155 -21
- data/lib/rigor/builtins/predefined_constant_refinements.rb +5 -7
- data/lib/rigor/builtins/regex_refinement.rb +4 -4
- data/lib/rigor/builtins/static_return_refinements.rb +5 -6
- data/lib/rigor/cache/annotation_location.rb +2 -4
- data/lib/rigor/cache/descriptor.rb +58 -20
- data/lib/rigor/cache/engine_source.rb +4 -4
- data/lib/rigor/cache/file_digest.rb +9 -1
- data/lib/rigor/cache/incremental_snapshot.rb +40 -6
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +0 -3
- data/lib/rigor/cache/rbs_class_type_param_names.rb +0 -3
- data/lib/rigor/cache/rbs_constant_table.rb +0 -3
- data/lib/rigor/cache/rbs_descriptor.rb +79 -11
- data/lib/rigor/cache/rbs_environment.rb +8 -4
- data/lib/rigor/cache/rbs_known_class_names.rb +0 -3
- data/lib/rigor/cache/store.rb +77 -32
- data/lib/rigor/cli/annotate_command.rb +5 -6
- data/lib/rigor/cli/check_command.rb +57 -10
- data/lib/rigor/cli/check_invocation.rb +6 -11
- data/lib/rigor/cli/check_runner_factory.rb +1 -5
- data/lib/rigor/cli/coverage_command.rb +1 -1
- data/lib/rigor/cli/coverage_mutation.rb +12 -3
- data/lib/rigor/cli/coverage_scan.rb +1 -5
- data/lib/rigor/cli/diff_command.rb +1 -1
- data/lib/rigor/cli/doc_links.rb +3 -3
- data/lib/rigor/cli/docs_command.rb +2 -2
- data/lib/rigor/cli/doctor_command.rb +85 -38
- data/lib/rigor/cli/effects_command.rb +2 -2
- data/lib/rigor/cli/effects_diff_renderer.rb +3 -3
- data/lib/rigor/cli/effects_snapshot_command.rb +1 -1
- data/lib/rigor/cli/explain_command.rb +34 -1
- data/lib/rigor/cli/fused_protection_report.rb +7 -1
- data/lib/rigor/cli/lsp_command.rb +1 -1
- data/lib/rigor/cli/mcp_command.rb +1 -1
- data/lib/rigor/cli/measurement_integrity_warning.rb +4 -5
- data/lib/rigor/cli/mutation_fork_scan.rb +6 -6
- data/lib/rigor/cli/mutation_protection_report.rb +7 -1
- data/lib/rigor/cli/plugin_command.rb +1 -1
- data/lib/rigor/cli/plugins_command.rb +2 -1
- data/lib/rigor/cli/prism_colorizer.rb +2 -2
- data/lib/rigor/cli/protection_fork_scan.rb +6 -6
- data/lib/rigor/cli/show_bleedingedge_command.rb +1 -1
- data/lib/rigor/cli/sig_gen_command.rb +2 -2
- data/lib/rigor/cli/skill_command.rb +1 -1
- data/lib/rigor/cli/skill_deep_probe.rb +4 -4
- data/lib/rigor/cli/skill_describe.rb +36 -35
- data/lib/rigor/cli/trace_command.rb +4 -4
- data/lib/rigor/cli/trace_renderer.rb +5 -6
- data/lib/rigor/cli/triage_command.rb +1 -1
- data/lib/rigor/cli/type_of_command.rb +10 -6
- data/lib/rigor/cli/type_scan_command.rb +4 -4
- data/lib/rigor/cli/unused_command.rb +11 -3
- data/lib/rigor/cli/upgrade_command.rb +1 -1
- data/lib/rigor/cli.rb +68 -6
- data/lib/rigor/config_audit.rb +30 -5
- data/lib/rigor/configuration/severity_profile.rb +6 -6
- data/lib/rigor/configuration.rb +43 -4
- data/lib/rigor/effects/attribution.rb +1 -3
- data/lib/rigor/effects/config_envelopes.rb +6 -8
- data/lib/rigor/effects/definition_lines.rb +25 -6
- data/lib/rigor/effects/effect_table.rb +0 -1
- data/lib/rigor/effects/entry_points.rb +0 -2
- data/lib/rigor/effects/envelope_check.rb +8 -8
- data/lib/rigor/effects/envelope_index.rb +5 -8
- data/lib/rigor/effects/framework_units.rb +4 -6
- data/lib/rigor/effects/identity.rb +4 -6
- data/lib/rigor/effects/inline_anchor.rb +7 -7
- data/lib/rigor/effects/label_intent.rb +3 -4
- data/lib/rigor/effects/liskov_check.rb +8 -8
- data/lib/rigor/effects/method_key.rb +1 -1
- data/lib/rigor/effects/plugin_facts.rb +4 -6
- data/lib/rigor/effects/propagator.rb +2 -3
- data/lib/rigor/effects/scanner.rb +1 -1
- data/lib/rigor/effects/signature_sources.rb +3 -5
- data/lib/rigor/effects/snapshot.rb +10 -10
- data/lib/rigor/effects/snapshot_diff.rb +1 -1
- data/lib/rigor/effects/unit_scan.rb +10 -10
- data/lib/rigor/effects/unknown_label_check.rb +3 -8
- data/lib/rigor/effects/unknown_label_report.rb +3 -4
- data/lib/rigor/environment/bundle_sig_discovery.rb +6 -6
- data/lib/rigor/environment/class_registry.rb +3 -1
- data/lib/rigor/environment/failure_slot.rb +2 -2
- data/lib/rigor/environment/installed_gem_set.rb +85 -0
- data/lib/rigor/environment/lockfile_resolver.rb +51 -4
- data/lib/rigor/environment/missing_gem_constant_index.rb +4 -4
- data/lib/rigor/environment/rbs_collection_discovery.rb +5 -5
- data/lib/rigor/environment/rbs_coverage_report.rb +5 -5
- data/lib/rigor/environment/rbs_hierarchy.rb +3 -1
- data/lib/rigor/environment/rbs_loader.rb +388 -59
- data/lib/rigor/environment.rb +105 -20
- data/lib/rigor/flow_contribution/merger.rb +0 -2
- data/lib/rigor/flow_contribution.rb +11 -13
- data/lib/rigor/inference/acceptance.rb +153 -8
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/body_fixpoint.rb +5 -5
- data/lib/rigor/inference/budget_trace.rb +1 -5
- data/lib/rigor/inference/captured_locals.rb +2 -3
- data/lib/rigor/inference/closure_escape_analyzer.rb +2 -4
- data/lib/rigor/inference/coverage_scanner.rb +4 -6
- data/lib/rigor/inference/dynamic_origin.rb +1 -1
- data/lib/rigor/inference/element_read_widening.rb +168 -0
- data/lib/rigor/inference/expression_typer.rb +763 -143
- data/lib/rigor/inference/fork_map.rb +5 -7
- data/lib/rigor/inference/hkt_reducer.rb +2 -3
- data/lib/rigor/inference/hkt_registry.rb +4 -7
- data/lib/rigor/inference/index_write_widening.rb +1 -5
- data/lib/rigor/inference/macro_block_self_type.rb +1 -4
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +2 -9
- data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +305 -19
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/json_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -1
- data/lib/rigor/inference/method_dispatcher/math_folding.rb +41 -3
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +2 -5
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +61 -47
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +82 -0
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +165 -31
- data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +25 -4
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +2 -8
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +4 -5
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +85 -4
- data/lib/rigor/inference/method_dispatcher/time_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +12 -13
- data/lib/rigor/inference/method_parameter_binder.rb +4 -6
- data/lib/rigor/inference/multi_target_binder.rb +1 -3
- data/lib/rigor/inference/mutation_rejoin.rb +168 -0
- data/lib/rigor/inference/mutation_widening.rb +100 -51
- data/lib/rigor/inference/narrowing.rb +299 -46
- data/lib/rigor/inference/optimistic_origin.rb +1 -7
- data/lib/rigor/inference/origin_lookup.rb +2 -3
- data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
- data/lib/rigor/inference/pre_eval_constants.rb +6 -5
- data/lib/rigor/inference/precision_scanner.rb +3 -4
- data/lib/rigor/inference/project_patched_methods.rb +2 -2
- data/lib/rigor/inference/project_patched_scanner.rb +3 -3
- data/lib/rigor/inference/protection_scanner.rb +1 -2
- data/lib/rigor/inference/range_constant.rb +57 -0
- data/lib/rigor/inference/rbs_type_translator.rb +49 -11
- data/lib/rigor/inference/receiver_alias.rb +3 -3
- data/lib/rigor/inference/refinement_mutation.rb +72 -0
- data/lib/rigor/inference/scope_indexer.rb +688 -151
- data/lib/rigor/inference/statement_evaluator.rb +221 -50
- data/lib/rigor/inference/string_mutation.rb +60 -0
- data/lib/rigor/inference/struct_fold_safety.rb +6 -7
- data/lib/rigor/inference/synthetic_method_index.rb +1 -2
- data/lib/rigor/inference/synthetic_method_scanner.rb +4 -6
- data/lib/rigor/inference/version_guard.rb +28 -21
- data/lib/rigor/inference/void_origin.rb +3 -3
- data/lib/rigor/inference/void_tail_summary.rb +2 -4
- data/lib/rigor/language_server/buffer_table.rb +4 -4
- data/lib/rigor/language_server/completion_provider.rb +1 -1
- data/lib/rigor/language_server/debouncer.rb +1 -1
- data/lib/rigor/language_server/diagnostic_publisher.rb +4 -4
- data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
- data/lib/rigor/language_server/folding_range_provider.rb +1 -1
- data/lib/rigor/language_server/hover_provider.rb +1 -1
- data/lib/rigor/language_server/hover_renderer.rb +2 -2
- data/lib/rigor/language_server/incremental_sync.rb +6 -6
- data/lib/rigor/language_server/project_context.rb +2 -5
- data/lib/rigor/language_server/publish_batcher.rb +2 -2
- data/lib/rigor/language_server/selection_range_provider.rb +2 -2
- data/lib/rigor/language_server/server.rb +9 -9
- data/lib/rigor/language_server/signature_help_provider.rb +1 -1
- data/lib/rigor/language_server/uri.rb +1 -1
- data/lib/rigor/plugin/base.rb +90 -21
- data/lib/rigor/plugin/bundled_catalog.rb +167 -0
- data/lib/rigor/plugin/effect_attribution.rb +7 -7
- data/lib/rigor/plugin/effect_entry_points.rb +3 -3
- data/lib/rigor/plugin/fact_store.rb +6 -6
- data/lib/rigor/plugin/io_boundary.rb +93 -8
- data/lib/rigor/plugin/isolation.rb +29 -6
- data/lib/rigor/plugin/loader.rb +31 -12
- data/lib/rigor/plugin/macro/heredoc_template.rb +1 -1
- data/lib/rigor/plugin/macro/trait_registry.rb +1 -1
- data/lib/rigor/plugin/manifest.rb +23 -1
- data/lib/rigor/plugin/registry.rb +42 -12
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +12 -1
- data/lib/rigor/plugin/trust_policy.rb +2 -4
- data/lib/rigor/plugin/type_node_resolver.rb +3 -3
- data/lib/rigor/plugin_gap_advisory.rb +96 -0
- data/lib/rigor/project_environment.rb +138 -0
- data/lib/rigor/protection/analysis_guard.rb +93 -14
- data/lib/rigor/protection/closure_kill_oracle.rb +67 -23
- data/lib/rigor/protection/dependency_closure.rb +5 -8
- data/lib/rigor/protection/diagnostic_oracle.rb +2 -2
- data/lib/rigor/protection/discovery_seed.rb +11 -11
- data/lib/rigor/protection/kill_signature.rb +2 -4
- data/lib/rigor/protection/measurement_integrity.rb +1 -3
- data/lib/rigor/protection/mutation_cache.rb +6 -9
- data/lib/rigor/protection/mutation_scanner.rb +12 -15
- data/lib/rigor/protection/mutator.rb +2 -1
- data/lib/rigor/protection/test_suite_oracle.rb +5 -5
- data/lib/rigor/rbs_extended/conformance_checker.rb +4 -3
- data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
- data/lib/rigor/rbs_extended/reporter.rb +36 -10
- data/lib/rigor/rbs_extended.rb +61 -21
- data/lib/rigor/reflection/constant_path.rb +126 -0
- data/lib/rigor/reflection.rb +26 -10
- data/lib/rigor/runtime/jit.rb +6 -8
- data/lib/rigor/scope/discovery_index.rb +30 -0
- data/lib/rigor/scope.rb +203 -4
- data/lib/rigor/sig_gen/generator.rb +115 -22
- data/lib/rigor/sig_gen/layout_index.rb +3 -4
- data/lib/rigor/sig_gen/meta_class_shape.rb +3 -5
- data/lib/rigor/sig_gen/observation_collector.rb +17 -15
- data/lib/rigor/sig_gen/path_mapper.rb +4 -6
- data/lib/rigor/sig_gen/rbs_validity.rb +4 -4
- data/lib/rigor/sig_gen/renderer.rb +4 -5
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +111 -0
- data/lib/rigor/sig_gen/superclass_spelling.rb +27 -0
- data/lib/rigor/sig_gen/type_elaborator.rb +1 -3
- data/lib/rigor/sig_gen/writer.rb +23 -6
- data/lib/rigor/signature_path_audit.rb +153 -6
- data/lib/rigor/source/literals.rb +0 -23
- data/lib/rigor/source/node_children.rb +0 -2
- data/lib/rigor/source/node_locator.rb +5 -11
- data/lib/rigor/source/node_walker.rb +2 -5
- data/lib/rigor/triage/catalogue.rb +1 -3
- data/lib/rigor/triage.rb +3 -5
- data/lib/rigor/type/accepts_result.rb +24 -6
- data/lib/rigor/type/combinator.rb +68 -5
- data/lib/rigor/type/data_class.rb +2 -2
- data/lib/rigor/type/data_instance.rb +4 -4
- data/lib/rigor/type/float_range.rb +128 -0
- data/lib/rigor/type/hash_shape.rb +5 -5
- data/lib/rigor/type/integer_range.rb +13 -8
- data/lib/rigor/type/nominal.rb +8 -2
- data/lib/rigor/type/refined.rb +3 -3
- data/lib/rigor/type/struct_class.rb +3 -3
- data/lib/rigor/type/struct_instance.rb +4 -4
- data/lib/rigor/type.rb +1 -0
- data/lib/rigor/type_node/generic.rb +1 -1
- data/lib/rigor/type_node/range_literal.rb +25 -0
- data/lib/rigor/type_node/resolver_chain.rb +1 -1
- data/lib/rigor/type_node.rb +1 -0
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +0 -4
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +0 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +0 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -0
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +0 -4
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +3 -5
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +1 -4
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +1 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +33 -30
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -3
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +28 -2
- data/plugins/rigor-actionpack/sig/action_controller.rbs +71 -0
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +0 -4
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +0 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +0 -2
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +1 -0
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +3 -4
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +1 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +1 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +42 -7
- data/plugins/rigor-activerecord/sig/active_record/framework.rbs +123 -0
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +1 -0
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +9 -2
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +119 -14
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +1 -0
- data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +1 -0
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +9 -7
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +7 -3
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +1 -0
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +8 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +11 -7
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -11
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +9 -4
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +1 -0
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +0 -5
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +8 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +36 -4
- data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +1 -0
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +8 -6
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +6 -3
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +1 -0
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -1
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +0 -3
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +1 -0
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +0 -5
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +2 -2
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +0 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +0 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +1 -0
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +0 -12
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +1 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +0 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +1 -0
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/acronyms.rb +5 -5
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +1 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +3 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/grape_api_discoverer.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +9 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +2 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +1 -0
- data/plugins/rigor-railties/lib/rigor/plugin/railties.rb +1 -0
- data/plugins/rigor-rbnacl/lib/rigor/plugin/rbnacl.rb +13 -1
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +128 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +0 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +0 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +1 -5
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +2 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +1 -0
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +0 -3
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +1 -0
- data/plugins/rigor-sassc/lib/rigor/plugin/sassc.rb +1 -0
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +1 -9
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +1 -0
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +0 -4
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/effects.rb +1 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +1 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +0 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +0 -2
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +1 -0
- data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +1 -0
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +2 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +1 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +3 -9
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +4 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +1 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +4 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +7 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +5 -5
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +1 -0
- data/sig/rigor/analysis/reachability/scan_cache.rbs +12 -0
- data/sig/rigor/cache.rbs +2 -3
- data/sig/rigor/environment.rbs +5 -0
- data/sig/rigor/inference.rbs +3 -10
- data/sig/rigor/plugin/io_boundary.rbs +4 -0
- data/sig/rigor/plugin/manifest.rbs +2 -1
- data/sig/rigor/reflection.rbs +3 -1
- data/sig/rigor/scope.rbs +24 -3
- data/sig/rigor/sig_gen/skip_reason_catalog.rbs +14 -0
- data/sig/rigor/type.rbs +27 -1
- data/sig/rigor.rbs +1 -0
- data/skills/rigor-ask/SKILL.md +8 -6
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +13 -2
- data/skills/rigor-project-init/SKILL.md +6 -3
- data/skills/rigor-project-init/references/02-configure.md +10 -6
- data/skills/rigor-project-init/references/06-agent-contract.md +67 -0
- data/skills/rigor-type-oracle/SKILL.md +228 -0
- data/skills/rigor-type-oracle/references/01-oracle-commands.md +261 -0
- data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +52 -0
- data/skills/rigor-type-oracle/references/03-gap-protocol.md +128 -0
- data/skills/rigor-unused-adjudicate/SKILL.md +8 -5
- metadata +26 -3
- data/lib/rigor/cli/probe_environment.rb +0 -85
- data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
|
@@ -31,11 +31,12 @@ module Rigor
|
|
|
31
31
|
# - **No-argument refinement names** (`non-empty-string`, `non-zero-int`,
|
|
32
32
|
# `lowercase-string`, …) live in `REGISTRY` and resolve through `lookup(name)`.
|
|
33
33
|
# - **Parameterised refinement payloads** (`non-empty-array[Integer]`,
|
|
34
|
-
# `non-empty-hash[Symbol, Integer]`, `int<5, 10>`)
|
|
35
|
-
# full grammar is documented on `Parser`. The two
|
|
36
|
-
# no-arg head names; the parameterised head names live
|
|
37
|
-
# (square-bracket form, type args)
|
|
38
|
-
#
|
|
34
|
+
# `non-empty-hash[Symbol, Integer]`, `Integer[5..10]`, and the deprecated `int<5, 10>`)
|
|
35
|
+
# are accepted by `parse(payload)`. The full grammar is documented on `Parser`. The two
|
|
36
|
+
# surfaces share `REGISTRY` for the no-arg head names; the parameterised head names live
|
|
37
|
+
# in `PARAMETERISED_TYPE_BUILDERS` (square-bracket form, type args), `RANGE_HEAD_BUILDERS`
|
|
38
|
+
# (a numeric class over a range literal) and `PARAMETERISED_INT_BUILDERS` (the deprecated
|
|
39
|
+
# angle-bracket form, integer bounds).
|
|
39
40
|
module ImportedRefinements
|
|
40
41
|
REGISTRY = {
|
|
41
42
|
"non-empty-string" => -> { Type::Combinator.non_empty_string },
|
|
@@ -58,7 +59,10 @@ module Rigor
|
|
|
58
59
|
"non-empty-lowercase-string" => -> { Type::Combinator.non_empty_lowercase_string },
|
|
59
60
|
"non-empty-uppercase-string" => -> { Type::Combinator.non_empty_uppercase_string },
|
|
60
61
|
"literal-string" => -> { Type::Combinator.literal_string },
|
|
61
|
-
"non-empty-literal-string" => -> { Type::Combinator.non_empty_literal_string }
|
|
62
|
+
"non-empty-literal-string" => -> { Type::Combinator.non_empty_literal_string },
|
|
63
|
+
# ADR-109 WD4 — names for the two Float ranges people mean most; each is an alias of a range.
|
|
64
|
+
"non-nan-float" => -> { Type::Combinator.non_nan_float },
|
|
65
|
+
"finite-float" => -> { Type::Combinator.finite_float }
|
|
62
66
|
}.freeze
|
|
63
67
|
private_constant :REGISTRY
|
|
64
68
|
|
|
@@ -139,13 +143,52 @@ module Rigor
|
|
|
139
143
|
}.freeze
|
|
140
144
|
private_constant :PARAMETERISED_TYPE_BUILDERS
|
|
141
145
|
|
|
142
|
-
# `
|
|
146
|
+
# `Class[range]` — a numeric class head bounded by a Ruby range literal (ADR-109). The
|
|
147
|
+
# builder receives the `Range` value and returns the carrier, or `nil` when the range is
|
|
148
|
+
# empty or its endpoints are not literals of the head class; `nil` surfaces as the ordinary
|
|
149
|
+
# unresolved-payload diagnostic. `Float` joins this table with the `FloatRange` carrier.
|
|
150
|
+
RANGE_HEAD_BUILDERS = {
|
|
151
|
+
"Integer" => lambda { |range|
|
|
152
|
+
lo = range.begin
|
|
153
|
+
hi = range.end
|
|
154
|
+
return nil unless (lo.nil? || lo.is_a?(Integer)) && (hi.nil? || hi.is_a?(Integer))
|
|
155
|
+
|
|
156
|
+
# `1...10` is the set `1..9`: Integer ranges canonicalise to the closed form.
|
|
157
|
+
hi -= 1 if range.exclude_end? && hi.is_a?(Integer)
|
|
158
|
+
return nil if lo.is_a?(Integer) && hi.is_a?(Integer) && lo > hi
|
|
159
|
+
|
|
160
|
+
Type::Combinator.integer_range(lo || Type::IntegerRange::NEG_INFINITY, hi || Type::IntegerRange::POS_INFINITY)
|
|
161
|
+
},
|
|
162
|
+
# ADR-109 WD4 — `Float[R]` is the set of Floats `R.cover?` accepts. A missing or `nil` endpoint
|
|
163
|
+
# is the infinity on that side, except that `nil..nil` covers NaN in Ruby and so is `Float`
|
|
164
|
+
# itself; an Integer endpoint coerces (`(0..1).cover?(0.5)` is true); an exclusive end is kept
|
|
165
|
+
# (the carrier canonicalises it for equality), and an empty range declines.
|
|
166
|
+
"Float" => lambda { |range|
|
|
167
|
+
return Type::Combinator.nominal_of("Float") if range.begin.nil? && range.end.nil?
|
|
168
|
+
|
|
169
|
+
lo = ImportedRefinements.float_range_endpoint(range.begin, -Float::INFINITY)
|
|
170
|
+
hi = ImportedRefinements.float_range_endpoint(range.end, Float::INFINITY)
|
|
171
|
+
return nil if lo.nil? || hi.nil?
|
|
172
|
+
|
|
173
|
+
exclusive = range.exclude_end? && !range.end.nil?
|
|
174
|
+
return nil if lo > (exclusive ? hi.prev_float : hi)
|
|
175
|
+
|
|
176
|
+
Type::Combinator.float_range(lo, hi, exclude_end: exclusive)
|
|
177
|
+
}
|
|
178
|
+
}.freeze
|
|
179
|
+
private_constant :RANGE_HEAD_BUILDERS
|
|
180
|
+
|
|
181
|
+
# `name<min, max>` — the PHPStan-style integer-bound form ADR-109 deprecates: still parsed so
|
|
182
|
+
# signatures written against v0.1–v0.3 keep resolving, never displayed. Each builder takes an
|
|
143
183
|
# `Array<Integer>` and returns a `Rigor::Type` (or `nil`). Bounds are signed integer
|
|
144
|
-
# literals
|
|
145
|
-
#
|
|
184
|
+
# literals. A reversed pair (`min` > `max`) is declined as `nil` like every other shape
|
|
185
|
+
# mismatch, so the caller surfaces `dynamic.rbs-extended.unresolved` at the annotation;
|
|
186
|
+
# `Type::IntegerRange` raises on it instead, and that `ArgumentError` used to escape as an
|
|
187
|
+
# `internal analyzer error` that abandoned the whole Ruby file.
|
|
146
188
|
PARAMETERISED_INT_BUILDERS = {
|
|
147
189
|
"int" => lambda { |bounds|
|
|
148
190
|
return nil unless bounds.size == 2
|
|
191
|
+
return nil if bounds[0] > bounds[1]
|
|
149
192
|
|
|
150
193
|
Type::Combinator.integer_range(bounds[0], bounds[1])
|
|
151
194
|
}
|
|
@@ -154,32 +197,32 @@ module Rigor
|
|
|
154
197
|
|
|
155
198
|
module_function
|
|
156
199
|
|
|
157
|
-
# @param name
|
|
158
|
-
# @return
|
|
200
|
+
# @param name — kebab-case refinement name.
|
|
201
|
+
# @return the matching refinement carrier, or `nil` if the name is
|
|
159
202
|
# not registered.
|
|
160
203
|
def lookup(name)
|
|
161
204
|
builder = REGISTRY[name.to_s]
|
|
162
205
|
builder&.call
|
|
163
206
|
end
|
|
164
207
|
|
|
165
|
-
# @param payload
|
|
208
|
+
# @param payload — the trailing payload of a `rigor:v1:return:` (or sibling)
|
|
166
209
|
# directive. Accepts the bare-name forms `lookup` already handles plus the
|
|
167
210
|
# parameterised forms documented on {Parser}.
|
|
168
|
-
# @param name_scope
|
|
211
|
+
# @param name_scope —
|
|
169
212
|
# ADR-13 slice 3 — when provided, the parser consults the scope's `#resolver` chain
|
|
170
213
|
# after the built-in registry and built-in parametric forms but before the RBS
|
|
171
214
|
# Nominal fallback. `nil` (default) preserves the slice-1 / slice-2 behaviour of
|
|
172
215
|
# consulting only built-ins + RBS.
|
|
173
|
-
# @param reporter
|
|
216
|
+
# @param reporter —
|
|
174
217
|
# ADR-13 slice 3b — collector that the Resolver feeds `dynamic.shape.lossy-projection`
|
|
175
218
|
# events into when a shape-projection head (`pick_of`, `omit_of`, `partial_of`,
|
|
176
219
|
# `required_of`, `readonly_of`) is applied to a carrier that does not preserve shape
|
|
177
220
|
# information. `nil` (default) suppresses event accumulation; legacy call sites that
|
|
178
221
|
# have no reporter to thread keep the pre-slice-3b silent fall-through.
|
|
179
|
-
# @param source_location
|
|
222
|
+
# @param source_location — location attribution for the events the
|
|
180
223
|
# Resolver records. Carries the annotation's filename / line / column so the runner
|
|
181
224
|
# can stamp diagnostics with the user-visible source site.
|
|
182
|
-
# @return
|
|
225
|
+
# @return the resolved refinement carrier, or `nil` when the payload
|
|
183
226
|
# is unparseable or names a refinement / class no registered source resolved.
|
|
184
227
|
def parse(payload, name_scope: nil, reporter: nil, source_location: nil)
|
|
185
228
|
Parser.new(
|
|
@@ -201,6 +244,20 @@ module Rigor
|
|
|
201
244
|
PARAMETERISED_INT_BUILDERS[name]
|
|
202
245
|
end
|
|
203
246
|
|
|
247
|
+
def range_head_builder(name)
|
|
248
|
+
RANGE_HEAD_BUILDERS[name]
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# A Float-head range endpoint as a double: `nil` (absent) is `default`, an Integer literal
|
|
252
|
+
# coerces, a non-NaN Float stands, anything else declines with `nil`.
|
|
253
|
+
def float_range_endpoint(value, default)
|
|
254
|
+
case value
|
|
255
|
+
when nil then default
|
|
256
|
+
when Integer then value.to_f
|
|
257
|
+
when Float then value.nan? ? nil : value
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
|
|
204
261
|
def known?(name)
|
|
205
262
|
REGISTRY.key?(name.to_s) ||
|
|
206
263
|
PARAMETERISED_TYPE_BUILDERS.key?(name.to_s) ||
|
|
@@ -219,8 +276,10 @@ module Rigor
|
|
|
219
276
|
# parametric := simple_name '[' type_arg_list ']'
|
|
220
277
|
# | simple_name '<' int_bound_list '>'
|
|
221
278
|
# type_arg_list := type_arg (',' type_arg)*
|
|
222
|
-
# type_arg := type | class_name
|
|
279
|
+
# type_arg := range_literal | type | class_name
|
|
223
280
|
# class_name := /[A-Z][A-Za-z0-9_]*(?:::[A-Z][A-Za-z0-9_]*)*/
|
|
281
|
+
# range_literal := endpoint? ('..' | '...') endpoint? (at least one side written)
|
|
282
|
+
# endpoint := signed_int | 'nil'
|
|
224
283
|
# int_bound_list := signed_int (',' signed_int)*
|
|
225
284
|
# signed_int := /-?\d+/
|
|
226
285
|
#
|
|
@@ -274,7 +333,16 @@ module Rigor
|
|
|
274
333
|
# delimiter.
|
|
275
334
|
SYMBOL_LITERAL = /:(?<value>[a-zA-Z_][a-zA-Z0-9_]*[?!=]?)/
|
|
276
335
|
STRING_LITERAL = /"(?<value>[^"\\]*)"/
|
|
277
|
-
|
|
336
|
+
# ADR-109 — a Ruby range literal at type-arg position, `1..10` / `1...10` / `1..` / `..10` /
|
|
337
|
+
# `nil..nil`, and for the Float head `0.0...1.0` / `1e-3..` / `-Float::INFINITY..Float::MAX`.
|
|
338
|
+
# Tried before the class-name and integer scans so `Float::INFINITY..` and `1..10` are not
|
|
339
|
+
# consumed as a class name or a bare integer; the operator is mandatory, so a bare integer or
|
|
340
|
+
# constant never matches. `..` with nothing on either side is not Ruby and is rejected after
|
|
341
|
+
# the match.
|
|
342
|
+
RANGE_ENDPOINT = /-?(?:\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|Float::INFINITY|Float::MAX)|nil/
|
|
343
|
+
RANGE_LITERAL = /(?<lo>#{RANGE_ENDPOINT})?\s*(?<op>\.\.\.?)\s*(?<hi>#{RANGE_ENDPOINT})?/
|
|
344
|
+
private_constant :SIMPLE_NAME, :CLASS_NAME, :SIGNED_INT, :SYMBOL_LITERAL, :STRING_LITERAL,
|
|
345
|
+
:RANGE_ENDPOINT, :RANGE_LITERAL
|
|
278
346
|
|
|
279
347
|
def parse_type_ast
|
|
280
348
|
if (class_name = @scanner.scan(CLASS_NAME))
|
|
@@ -382,7 +450,9 @@ module Rigor
|
|
|
382
450
|
end
|
|
383
451
|
|
|
384
452
|
def parse_single_type_arg_ast
|
|
385
|
-
if (
|
|
453
|
+
if (range = scan_range_literal)
|
|
454
|
+
range
|
|
455
|
+
elsif (class_name = @scanner.scan(CLASS_NAME))
|
|
386
456
|
parse_class_arg_tail_ast(class_name)
|
|
387
457
|
elsif (literal = @scanner.scan(SIGNED_INT))
|
|
388
458
|
TypeNode::IntegerLiteral.new(value: Integer(literal))
|
|
@@ -421,6 +491,36 @@ module Rigor
|
|
|
421
491
|
Integer(literal)
|
|
422
492
|
end
|
|
423
493
|
|
|
494
|
+
# Returns a {TypeNode::RangeLiteral} when the scanner sits on a Ruby range literal, `nil`
|
|
495
|
+
# (without consuming input) otherwise. `nil` and an absent endpoint both become Ruby's
|
|
496
|
+
# `nil` endpoint; the two spellings denote the same range.
|
|
497
|
+
def scan_range_literal
|
|
498
|
+
return nil unless @scanner.scan(RANGE_LITERAL)
|
|
499
|
+
|
|
500
|
+
lo = @scanner[:lo]
|
|
501
|
+
hi = @scanner[:hi]
|
|
502
|
+
return nil if lo.nil? && hi.nil?
|
|
503
|
+
|
|
504
|
+
TypeNode::RangeLiteral.new(
|
|
505
|
+
value: Range.new(range_endpoint(lo), range_endpoint(hi), @scanner[:op] == "...")
|
|
506
|
+
)
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
# The endpoint as the Ruby value the token denotes: an Integer literal stays an Integer (the
|
|
510
|
+
# `Integer` head needs to tell `1` from `1.0`), a Float literal or one of the four spelled
|
|
511
|
+
# constants becomes the double.
|
|
512
|
+
def range_endpoint(token)
|
|
513
|
+
case token
|
|
514
|
+
when nil, "nil" then nil
|
|
515
|
+
when "Float::INFINITY" then Float::INFINITY
|
|
516
|
+
when "-Float::INFINITY" then -Float::INFINITY
|
|
517
|
+
when "Float::MAX" then Float::MAX
|
|
518
|
+
when "-Float::MAX" then -Float::MAX
|
|
519
|
+
when /[.eE]/ then Float(token)
|
|
520
|
+
else Integer(token)
|
|
521
|
+
end
|
|
522
|
+
end
|
|
523
|
+
|
|
424
524
|
def skip_ws
|
|
425
525
|
@scanner.skip(/\s+/)
|
|
426
526
|
end
|
|
@@ -463,7 +563,7 @@ module Rigor
|
|
|
463
563
|
# `StringLiteral`) carries a Ruby value that lifts directly to a `Constant<value>`
|
|
464
564
|
# carrier through the same helper.
|
|
465
565
|
LITERAL_AST_NODES = [
|
|
466
|
-
TypeNode::IntegerLiteral, TypeNode::SymbolLiteral, TypeNode::StringLiteral
|
|
566
|
+
TypeNode::IntegerLiteral, TypeNode::SymbolLiteral, TypeNode::StringLiteral, TypeNode::RangeLiteral
|
|
467
567
|
].freeze
|
|
468
568
|
private_constant :LITERAL_AST_NODES
|
|
469
569
|
|
|
@@ -519,6 +619,11 @@ module Rigor
|
|
|
519
619
|
end
|
|
520
620
|
|
|
521
621
|
def resolve_generic(node)
|
|
622
|
+
# ADR-109 — `Integer[1..10]`. A range literal under a numeric head is that head's range
|
|
623
|
+
# refinement or nothing: an empty or malformed range declines here rather than falling
|
|
624
|
+
# through to a `Nominal[Integer, [Constant<Range>]]` the RBS fallback would otherwise build.
|
|
625
|
+
return resolve_range_head(node) if range_headed?(node)
|
|
626
|
+
|
|
522
627
|
builtin = try_builtin_parametric(node)
|
|
523
628
|
return builtin unless builtin.nil?
|
|
524
629
|
|
|
@@ -547,6 +652,19 @@ module Rigor
|
|
|
547
652
|
try_parametric_type_builder(node) || try_parametric_int_builder(node)
|
|
548
653
|
end
|
|
549
654
|
|
|
655
|
+
def range_headed?(node)
|
|
656
|
+
!ImportedRefinements.range_head_builder(node.head).nil? &&
|
|
657
|
+
node.args.any?(TypeNode::RangeLiteral)
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
# Exactly one range literal under the head; `Integer[1..10, 3]` is not a range refinement.
|
|
661
|
+
# Anything else under the same head (`Integer[Foo]`) keeps its pre-ADR-109 path.
|
|
662
|
+
def resolve_range_head(node)
|
|
663
|
+
return nil unless node.args.size == 1
|
|
664
|
+
|
|
665
|
+
ImportedRefinements.range_head_builder(node.head).call(node.args.first.value)
|
|
666
|
+
end
|
|
667
|
+
|
|
550
668
|
def try_parametric_type_builder(node)
|
|
551
669
|
builder = ImportedRefinements.parametric_type_builder(node.head)
|
|
552
670
|
return nil if builder.nil?
|
|
@@ -585,7 +703,23 @@ module Rigor
|
|
|
585
703
|
bounds = node.args.map { |a| a.is_a?(TypeNode::IntegerLiteral) ? a.value : nil }
|
|
586
704
|
return nil if bounds.any?(&:nil?)
|
|
587
705
|
|
|
588
|
-
builder.call(bounds)
|
|
706
|
+
result = builder.call(bounds)
|
|
707
|
+
record_deprecated_form(node, bounds, result) unless result.nil?
|
|
708
|
+
result
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
# ADR-109 WD3 — the angle-bracket form still resolves, and the run says so once per annotation with
|
|
712
|
+
# the spelling the same type now displays as. Recorded only when the payload resolved: an
|
|
713
|
+
# unresolvable one already surfaces as `dynamic.rbs-extended.unresolved`, and a second row for it
|
|
714
|
+
# would name a replacement for something that never parsed.
|
|
715
|
+
def record_deprecated_form(node, bounds, result)
|
|
716
|
+
return if @reporter.nil?
|
|
717
|
+
|
|
718
|
+
path, line, column = RbsExtended::Reporter.position_of(@source_location)
|
|
719
|
+
@reporter.record_deprecated_form(
|
|
720
|
+
payload: "#{node.head}<#{bounds.join(', ')}>", replacement: result.describe,
|
|
721
|
+
path: path, line: line, column: column
|
|
722
|
+
)
|
|
589
723
|
end
|
|
590
724
|
|
|
591
725
|
def resolve_args(args)
|
|
@@ -117,8 +117,7 @@ module Rigor
|
|
|
117
117
|
|
|
118
118
|
# --- private ------------------------------------------------------
|
|
119
119
|
|
|
120
|
-
# @param value
|
|
121
|
-
# @return [Rigor::Type]
|
|
120
|
+
# @param value — a non-empty string
|
|
122
121
|
def self.classify_string(value)
|
|
123
122
|
if Type::Refined.ruby_numeric_literal?(value)
|
|
124
123
|
NUMERIC_STRING
|
|
@@ -133,8 +132,7 @@ module Rigor
|
|
|
133
132
|
# Called ONLY with the names in {RUNTIME_STRING_CONSTANTS} — Rigor's own source — and only
|
|
134
133
|
# while this file is being loaded. It is never handed a name from the analysed program.
|
|
135
134
|
#
|
|
136
|
-
# @param name
|
|
137
|
-
# @return [String, nil]
|
|
135
|
+
# @param name — a qualified constant path without a leading "::"
|
|
138
136
|
def self.runtime_string_value(name)
|
|
139
137
|
mod = ::Object
|
|
140
138
|
name.split("::").each do |part|
|
|
@@ -161,7 +159,7 @@ module Rigor
|
|
|
161
159
|
end
|
|
162
160
|
private_class_method :runtime_string_value
|
|
163
161
|
|
|
164
|
-
# @return
|
|
162
|
+
# @return frozen, built once at load time.
|
|
165
163
|
def self.build_runtime_string_refinements
|
|
166
164
|
RUNTIME_STRING_CONSTANTS.each_with_object({}) do |name, table|
|
|
167
165
|
value = runtime_string_value(name)
|
|
@@ -175,9 +173,9 @@ module Rigor
|
|
|
175
173
|
|
|
176
174
|
# --- public API ---------------------------------------------------
|
|
177
175
|
|
|
178
|
-
# @param name
|
|
176
|
+
# @param name — unqualified constant name (e.g. `"Math::PI"`,
|
|
179
177
|
# `"RUBY_VERSION"`, `"Ruby::ENGINE"`)
|
|
180
|
-
# @return
|
|
178
|
+
# @return refined type, or nil to fall through
|
|
181
179
|
def self.lookup(name)
|
|
182
180
|
FOLDED_CONSTANTS[name] || RUNTIME_STRING_REFINEMENTS[name]
|
|
183
181
|
end
|
|
@@ -62,11 +62,11 @@ module Rigor
|
|
|
62
62
|
|
|
63
63
|
module_function
|
|
64
64
|
|
|
65
|
-
# @param body
|
|
65
|
+
# @param body — a regex sub-pattern, typically the inner body of a
|
|
66
66
|
# `(?<name>body)` named capture. Anchors (`\A`, `\z`, `^`, `$`) are not stripped — the
|
|
67
67
|
# recogniser table targets bodies that the regex engine treats as anchored to the
|
|
68
68
|
# capture group bounds.
|
|
69
|
-
# @return
|
|
69
|
+
# @return the matching imported refinement carrier, or `nil` if
|
|
70
70
|
# `body` is not a recognised shape.
|
|
71
71
|
def for_capture_body(body)
|
|
72
72
|
return nil if body.nil? || body.empty?
|
|
@@ -100,8 +100,8 @@ module Rigor
|
|
|
100
100
|
# free-whitespace/`#`-comment flag from the `source` alone, so consumers bail on it before
|
|
101
101
|
# calling here.
|
|
102
102
|
#
|
|
103
|
-
# @param source
|
|
104
|
-
# @return
|
|
103
|
+
# @param source — the full regex source string.
|
|
104
|
+
# @return the matching imported refinement carrier, or `nil`.
|
|
105
105
|
def for_whole_pattern(source)
|
|
106
106
|
return nil if source.nil?
|
|
107
107
|
return nil unless source.start_with?('\A') && source.end_with?('\z')
|
|
@@ -97,16 +97,15 @@ module Rigor
|
|
|
97
97
|
|
|
98
98
|
# Looks up a refined return type for the given call.
|
|
99
99
|
#
|
|
100
|
-
# @param owner_class_name
|
|
100
|
+
# @param owner_class_name — the class on which the method is defined
|
|
101
101
|
# (e.g., `"Kernel"`). Pass `nil` when the caller hasn't resolved a defining owner
|
|
102
102
|
# yet — the lookup will then fall back to matching by `(method_name, kind)` against
|
|
103
103
|
# entries whose owner is currently in the table.
|
|
104
|
-
# @param
|
|
105
|
-
# @param kind [Symbol] one of `:singleton`, `:instance`. The caller passes the shape of
|
|
104
|
+
# @param kind — one of `:singleton`, `:instance`. The caller passes the shape of
|
|
106
105
|
# the actual call site; the table stores `:both` for entries that match either.
|
|
107
|
-
# @param arg_types
|
|
106
|
+
# @param arg_types — positional argument types. Forwarded to the
|
|
108
107
|
# handler so future entries can discriminate on argument shape.
|
|
109
|
-
# @return
|
|
108
|
+
# @return the refined return type, or `nil` when no override
|
|
110
109
|
# matches.
|
|
111
110
|
def self.lookup(owner_class_name:, method_name:, kind:, arg_types: [])
|
|
112
111
|
return nil if owner_class_name.nil?
|
|
@@ -127,7 +126,7 @@ module Rigor
|
|
|
127
126
|
end.freeze
|
|
128
127
|
private_constant :OWNERS_BY_METHOD
|
|
129
128
|
|
|
130
|
-
# @return
|
|
129
|
+
# @return the candidate owner class names for a bare method-name
|
|
131
130
|
# lookup. Empty when no override names this method.
|
|
132
131
|
NO_OWNERS = [].freeze
|
|
133
132
|
private_constant :NO_OWNERS
|
|
@@ -41,8 +41,7 @@ module Rigor
|
|
|
41
41
|
|
|
42
42
|
module_function
|
|
43
43
|
|
|
44
|
-
# @
|
|
45
|
-
# @return [Array, nil] `[name, start_line, start_column, end_line, end_column]`, or nil when there is
|
|
44
|
+
# @return `[name, start_line, start_column, end_line, end_column]`, or nil when there is
|
|
46
45
|
# no location to carry. Fail-soft: a location whose buffer cannot answer is dumped as nil rather
|
|
47
46
|
# than failing the whole environment's dump.
|
|
48
47
|
def dump(location)
|
|
@@ -55,8 +54,7 @@ module Rigor
|
|
|
55
54
|
nil
|
|
56
55
|
end
|
|
57
56
|
|
|
58
|
-
# @param payload
|
|
59
|
-
# @return [RBS::Location, nil]
|
|
57
|
+
# @param payload — what {dump} produced.
|
|
60
58
|
def load(payload)
|
|
61
59
|
return nil if payload.nil?
|
|
62
60
|
|
|
@@ -48,7 +48,12 @@ module Rigor
|
|
|
48
48
|
# added to a schema-less project) invalidates the warm entry. A pre-8 entry carries no absence rows
|
|
49
49
|
# and would validate fresh across exactly that edit, so cached entries must read as misses once and
|
|
50
50
|
# rebuild carrying the rows.
|
|
51
|
-
|
|
51
|
+
# v9: #979 — the run-result dependency descriptor now carries a names-only {GlobEntry} directory
|
|
52
|
+
# listing per RBS signature root, so a `.rbs` file APPEARING under one (a `sig/roles.rbs` declaring the
|
|
53
|
+
# interface a `conforms-to` names) invalidates the warm entry. A pre-9 entry carries no signature-root
|
|
54
|
+
# row and would validate fresh across exactly that edit — the #577 reasoning, one slot over — so cached
|
|
55
|
+
# entries must read as misses once and rebuild carrying the rows.
|
|
56
|
+
SCHEMA_VERSION = 9
|
|
52
57
|
|
|
53
58
|
# Per-slot entry value objects. Constructors validate enums / required fields and freeze the resulting
|
|
54
59
|
# struct so no caller can mutate after the entry is in a Descriptor.
|
|
@@ -114,7 +119,7 @@ module Rigor
|
|
|
114
119
|
new(path: path, comparator: :exists, value: PRESENT)
|
|
115
120
|
end
|
|
116
121
|
|
|
117
|
-
# @return
|
|
122
|
+
# @return whether this row records the path as absent (see {.absent}).
|
|
118
123
|
def absent?
|
|
119
124
|
comparator == :exists && value == ABSENT
|
|
120
125
|
end
|
|
@@ -235,31 +240,58 @@ module Rigor
|
|
|
235
240
|
FIELD_SEPARATOR = "\0"
|
|
236
241
|
private_constant :ROW_SEPARATOR, :FIELD_SEPARATOR
|
|
237
242
|
|
|
243
|
+
# `:stat` is the ADR-87 WD2 signature above — it answers "did anything under this glob change",
|
|
244
|
+
# appearance and edit alike. `:names` (#979) answers only "did the SET of matching paths change":
|
|
245
|
+
# its rows are the sorted relative names, so a file whose bytes are unchanged never moves the
|
|
246
|
+
# signature however its stat tuple moved. A row exists to catch appearance/disappearance only —
|
|
247
|
+
# edits to the files it lists are already carried by their own `:stat` {FileEntry} rows — and a
|
|
248
|
+
# names row must therefore survive what those rows survive. A `:stat` glob over a signature tree
|
|
249
|
+
# would not: ADR-87 WD1 lets a `:stat` file row fall back to its recorded content digest when the
|
|
250
|
+
# tuple moves, so a `git checkout`, a `bundle install` or a CI cache restored over a fresh checkout
|
|
251
|
+
# keeps the file rows fresh while it would move every glob signature, turning every such run into a
|
|
252
|
+
# full re-analysis. `:names` also reads no stat at all, so it costs a `Dir.glob` and nothing else.
|
|
253
|
+
VALID_MODES = %i[stat names].freeze
|
|
254
|
+
|
|
238
255
|
attr_reader :root, :pattern, :value
|
|
239
256
|
|
|
240
|
-
value_fields :root, :pattern, :value
|
|
257
|
+
value_fields :root, :pattern, :mode, :value
|
|
258
|
+
|
|
259
|
+
def initialize(root:, pattern:, value:, mode: :stat)
|
|
260
|
+
raise ArgumentError, "GlobEntry mode must be one of #{VALID_MODES.inspect}, got #{mode.inspect}" \
|
|
261
|
+
unless VALID_MODES.include?(mode)
|
|
241
262
|
|
|
242
|
-
def initialize(root:, pattern:, value:)
|
|
243
263
|
@root = root.to_s.dup.freeze
|
|
244
264
|
@pattern = pattern.to_s.dup.freeze
|
|
265
|
+
@mode = mode
|
|
245
266
|
@value = value.to_s.dup.freeze
|
|
246
267
|
freeze
|
|
247
268
|
end
|
|
248
269
|
|
|
270
|
+
# Defaulted rather than an `attr_reader` because an entry Marshal-loaded from a store written before
|
|
271
|
+
# the field existed carries no `@mode` ivar; every such entry was recorded under the stat signature.
|
|
272
|
+
def mode
|
|
273
|
+
@mode || :stat
|
|
274
|
+
end
|
|
275
|
+
|
|
249
276
|
# Builds the entry for the glob's CURRENT filesystem state.
|
|
250
|
-
def self.compute(root:, pattern:)
|
|
251
|
-
new(root: root, pattern: pattern,
|
|
277
|
+
def self.compute(root:, pattern:, mode: :stat)
|
|
278
|
+
new(root: root, pattern: pattern, mode: mode,
|
|
279
|
+
value: signature_for(root: root, pattern: pattern, mode: mode))
|
|
252
280
|
end
|
|
253
281
|
|
|
254
282
|
# The aggregate signature the entry's `value` carries: SHA-256 over the sorted per-file rows. In the
|
|
255
283
|
# default (`:stat`) mode a row is the file's `(path, size, mtime_ns, ctime_ns, inode)` tuple — NO
|
|
256
284
|
# content read; in strict mode a row is `(path, content-sha256)`, restoring the pre-ADR-87 authority.
|
|
285
|
+
# In `:names` mode a row is the path alone, stat-free, so only the matching SET moves the signature.
|
|
257
286
|
# Per-file stat failures (a file vanishing between the glob and the stat) drop the row — same race
|
|
258
287
|
# posture as {Descriptor#file_entry_fresh?}. `Dir.glob` returns sorted entries by default so the row
|
|
259
288
|
# order — and therefore the signature — is stable.
|
|
260
|
-
def self.signature_for(root:, pattern:)
|
|
289
|
+
def self.signature_for(root:, pattern:, mode: :stat)
|
|
290
|
+
paths = Dir.glob(File.join(root, pattern))
|
|
291
|
+
return Digest::SHA256.hexdigest(paths.map { |path| "#{path}#{ROW_SEPARATOR}" }.join) if mode == :names
|
|
292
|
+
|
|
261
293
|
strict = FileDigest.strict_validation?
|
|
262
|
-
rows =
|
|
294
|
+
rows = paths.filter_map do |path|
|
|
263
295
|
st = File.stat(path)
|
|
264
296
|
next nil unless st.file?
|
|
265
297
|
|
|
@@ -276,23 +308,30 @@ module Rigor
|
|
|
276
308
|
end
|
|
277
309
|
|
|
278
310
|
# ADR-87 WD2 — fresh iff re-globbing + re-stat-ing reproduces the recorded signature. Zero file-content
|
|
279
|
-
# bytes are read in the default mode
|
|
280
|
-
# signature recorded under the opposite validation mode simply
|
|
311
|
+
# bytes are read in the default mode, and no stat at all in `:names`. Any failure reads as stale
|
|
312
|
+
# (recompute), never a crash. A stored signature recorded under the opposite validation mode simply
|
|
313
|
+
# mismatches and recomputes.
|
|
281
314
|
def self.fresh?(entry)
|
|
282
|
-
signature_for(root: entry.root, pattern: entry.pattern) == entry.value
|
|
315
|
+
signature_for(root: entry.root, pattern: entry.pattern, mode: entry.mode) == entry.value
|
|
283
316
|
rescue StandardError
|
|
284
317
|
false
|
|
285
318
|
end
|
|
286
319
|
|
|
287
|
-
# Composition key — {.compose} unions per (root, pattern) slot; two contributions for the same
|
|
288
|
-
# must agree on the value or {Conflict} is raised. Within one run the same glob stat-reads
|
|
289
|
-
# so contributions never conflict.
|
|
320
|
+
# Composition key — {.compose} unions per (root, pattern, mode) slot; two contributions for the same
|
|
321
|
+
# slot must agree on the value or {Conflict} is raised. Within one run the same glob stat-reads
|
|
322
|
+
# identically, so contributions never conflict. The mode is part of the key because the two signatures
|
|
323
|
+
# over one (root, pattern) are different questions, not rival answers to the same one.
|
|
290
324
|
def slot_key
|
|
291
|
-
"#{root}\0#{pattern}"
|
|
325
|
+
"#{root}\0#{pattern}\0#{mode}"
|
|
292
326
|
end
|
|
293
327
|
|
|
294
328
|
def to_h
|
|
295
|
-
{ "root" => root, "pattern" => pattern, "value" => value }
|
|
329
|
+
h = { "root" => root, "pattern" => pattern, "value" => value }
|
|
330
|
+
# Omitted in the default mode so a descriptor carrying only stat globs keeps its pre-#979 canonical
|
|
331
|
+
# bytes: this FIELD moves no producer's key. (Every key still moved once with SCHEMA_VERSION 9,
|
|
332
|
+
# which the storage layer mixes in; that is the #979 migration, not the mode.)
|
|
333
|
+
h["mode"] = mode.to_s unless mode == :stat
|
|
334
|
+
h
|
|
296
335
|
end
|
|
297
336
|
end
|
|
298
337
|
|
|
@@ -352,9 +391,8 @@ module Rigor
|
|
|
352
391
|
dependencies: dependencies, globs: globs)
|
|
353
392
|
end
|
|
354
393
|
|
|
355
|
-
# @param
|
|
356
|
-
# @
|
|
357
|
-
# @return [String] hex SHA-256 cache key for the value
|
|
394
|
+
# @param params — inputs the producer was called with
|
|
395
|
+
# @return hex SHA-256 cache key for the value
|
|
358
396
|
def cache_key_for(producer_id:, params: {})
|
|
359
397
|
payload = {
|
|
360
398
|
"schema_version" => SCHEMA_VERSION,
|
|
@@ -377,7 +415,7 @@ module Rigor
|
|
|
377
415
|
"dependencies" => sort_entries(dependencies, "gem_name").map(&:to_h),
|
|
378
416
|
"files" => sort_entries(files, "path").map(&:to_h),
|
|
379
417
|
"gems" => sort_entries(gems, "name").map(&:to_h),
|
|
380
|
-
"globs" => globs.sort_by { |e| [e.root, e.pattern] }.map(&:to_h),
|
|
418
|
+
"globs" => globs.sort_by { |e| [e.root, e.pattern, e.mode.to_s] }.map(&:to_h),
|
|
381
419
|
"plugins" => sort_entries(plugins, "id").map(&:to_h)
|
|
382
420
|
}
|
|
383
421
|
end
|
|
@@ -65,15 +65,15 @@ module Rigor
|
|
|
65
65
|
|
|
66
66
|
module_function
|
|
67
67
|
|
|
68
|
-
# @return
|
|
68
|
+
# @return the gem root — the directory holding `lib/`, three levels above this file.
|
|
69
69
|
def root
|
|
70
70
|
@root ||= File.expand_path("../../..", __dir__)
|
|
71
71
|
end
|
|
72
72
|
|
|
73
|
-
# @param root
|
|
74
|
-
# @return
|
|
73
|
+
# @param root — the gem root, defaulted through {.root} so a spec can relocate the tree.
|
|
74
|
+
# @return a digest identifying the engine's current source, or nil when the tree is
|
|
75
75
|
# version-pinned and the caller should add no slot at all.
|
|
76
|
-
# @raise
|
|
76
|
+
# @raise Unavailable — when a mutable tree's source cannot be read.
|
|
77
77
|
def identity(root = self.root)
|
|
78
78
|
return nil if version_pinned?(root)
|
|
79
79
|
|
|
@@ -74,7 +74,7 @@ module Rigor
|
|
|
74
74
|
memo[path] ||= Digest::SHA256.file(path).hexdigest
|
|
75
75
|
end
|
|
76
76
|
|
|
77
|
-
# @return
|
|
77
|
+
# @return whether digest-always validation is forced for this run. The env var wins over the
|
|
78
78
|
# run-scoped config flag so an operator can bypass the stat tier without editing `.rigor.yml`.
|
|
79
79
|
def self.strict_validation?
|
|
80
80
|
return true if ENV[STRICT_ENV] == "1"
|
|
@@ -112,6 +112,14 @@ module Rigor
|
|
|
112
112
|
hexdigest(path) == digest
|
|
113
113
|
end
|
|
114
114
|
|
|
115
|
+
# The content-digest field of a packed `:stat` entry ({.pack_stat}); nil when the entry is absent or not
|
|
116
|
+
# well-formed. Exposed for the caller that must compare the recorded digest against bytes it hashes
|
|
117
|
+
# itself rather than against the file on disk — the incremental session's editor-mode freshness check,
|
|
118
|
+
# whose authority is the editor's buffer.
|
|
119
|
+
def self.content_digest(packed)
|
|
120
|
+
parse_stat(packed)&.first
|
|
121
|
+
end
|
|
122
|
+
|
|
115
123
|
# The VALIDATION-side stat, served from the per-run table when one is installed — a collecting run
|
|
116
124
|
# validates the effects entry and the diagnostics entry against the same ~thousands-of-files
|
|
117
125
|
# dependency descriptor, and the second pass is pure repetition under the run's own stable-filesystem
|