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
|
@@ -25,7 +25,7 @@ module Rigor
|
|
|
25
25
|
@mutex = Mutex.new
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
# @return
|
|
28
|
+
# @return frozen snapshot of the recorded boundary-cross events. Each entry is a Data
|
|
29
29
|
# with `class_name` (String), `method_name` (Symbol), `gem_name` (String), and `rbs_display` (String
|
|
30
30
|
# — the authoritative-side type's human-facing form, embedded into the diagnostic message).
|
|
31
31
|
def entries
|
|
@@ -30,8 +30,6 @@ module Rigor
|
|
|
30
30
|
|
|
31
31
|
module_function
|
|
32
32
|
|
|
33
|
-
# @param entry [Rigor::Configuration::Dependencies::Entry]
|
|
34
|
-
# @return [Resolved, Unresolvable]
|
|
35
33
|
def resolve(entry)
|
|
36
34
|
spec = locate_gem_spec(entry.gem)
|
|
37
35
|
return Unresolvable.new(gem_name: entry.gem, reason: :not_in_bundle) if spec.nil?
|
|
@@ -13,19 +13,19 @@ module Rigor
|
|
|
13
13
|
attr_reader :resolved_gems, :unresolvable, :method_catalog, :budget_exceeded,
|
|
14
14
|
:class_to_gem, :budget_overrun_strategy, :gem_modes
|
|
15
15
|
|
|
16
|
-
# @param method_catalog
|
|
16
|
+
# @param method_catalog — => Symbol}] the flat
|
|
17
17
|
# `(class_name, method_name) → :instance | :singleton` table produced by {Walker.walk}, aggregated
|
|
18
18
|
# across every resolved gem in the run. The Index itself stays gem-agnostic — the per-gem
|
|
19
19
|
# attribution that slice 3's cache descriptor needs lives on `Resolved`, not here.
|
|
20
|
-
# @param budget_exceeded
|
|
20
|
+
# @param budget_exceeded — gem names whose {Walker} run hit the per-gem catalog cap
|
|
21
21
|
# (slice 4). The Runner consumes this list to emit one `dynamic.dependency-source.budget-exceeded`
|
|
22
22
|
# warning per gem.
|
|
23
|
-
# @param class_to_gem
|
|
23
|
+
# @param class_to_gem — reverse lookup `class_name → gem_name` (slice 5b). Built
|
|
24
24
|
# first-write-wins: when two opt-in gems re-open the same class, the first gem owns it. The
|
|
25
25
|
# dispatcher consults this map under the `:dependency_silence` budget overrun strategy so call
|
|
26
26
|
# sites on a budget-exceeded gem's classes degrade to `Dynamic[top]` instead of falling through to
|
|
27
27
|
# the user-class fallback.
|
|
28
|
-
# @param gem_modes
|
|
28
|
+
# @param gem_modes — per-gem mode table (`gem_name → :disabled | :when_missing |
|
|
29
29
|
# :full`). ADR-10 slice 5c consults this through {#mode_for} to identify call sites where
|
|
30
30
|
# gem-source and RBS both contribute under `mode: :full`. The map is keyed on `gem_name` (not
|
|
31
31
|
# class) because re-opened classes belong to the first gem they appeared in per `class_to_gem`;
|
|
@@ -56,7 +56,7 @@ module Rigor
|
|
|
56
56
|
end
|
|
57
57
|
private :normalize_catalog
|
|
58
58
|
|
|
59
|
-
# @return
|
|
59
|
+
# @return the gem that owns `class_name` (first-write-wins); `nil` when the class isn't
|
|
60
60
|
# in any opt-in gem's catalog.
|
|
61
61
|
def gem_for(class_name)
|
|
62
62
|
@class_to_gem[class_name]
|
|
@@ -28,8 +28,7 @@ module Rigor
|
|
|
28
28
|
module ReturnTypeHeuristic
|
|
29
29
|
module_function
|
|
30
30
|
|
|
31
|
-
# @
|
|
32
|
-
# @return [Rigor::Type, nil] heuristic return type, or `nil` when the body's tail expression doesn't
|
|
31
|
+
# @return heuristic return type, or `nil` when the body's tail expression doesn't
|
|
33
32
|
# match any of the recognised shapes.
|
|
34
33
|
def extract(def_node)
|
|
35
34
|
body = def_node.body
|
|
@@ -55,12 +55,12 @@ module Rigor
|
|
|
55
55
|
|
|
56
56
|
module_function
|
|
57
57
|
|
|
58
|
-
# @param gem_dir
|
|
59
|
-
# @param roots
|
|
58
|
+
# @param gem_dir — absolute path to the gem's installation directory.
|
|
59
|
+
# @param roots — subdirectory names within the gem to walk (defaults to `["lib"]` per
|
|
60
60
|
# `Configuration::Dependencies::Entry`).
|
|
61
|
-
# @param budget
|
|
61
|
+
# @param budget — per-gem catalog cap (method-definition count). When unset, defaults
|
|
62
62
|
# to `UNBOUNDED` for backwards-compatible test paths.
|
|
63
|
-
# @return
|
|
63
|
+
# @return frozen wrapper carrying the catalog (`Hash{[class_name, method_name] =>
|
|
64
64
|
# :instance | :singleton}`) and a `truncated?` flag set when the walker stopped harvesting because
|
|
65
65
|
# the budget was reached. Methods of identical name on the same class with different kinds (rare;
|
|
66
66
|
# private API mostly) carry the kind that wins the per-class first walk.
|
|
@@ -52,17 +52,16 @@ module Rigor
|
|
|
52
52
|
# otherwise rebuild (the probe loaded the plugins to key the entry, so it already knows it).
|
|
53
53
|
Served = Data.define(:table, :sources, :registry, :plugin_facts)
|
|
54
54
|
|
|
55
|
-
# @param configuration
|
|
56
|
-
# @param cache_root [String, nil]
|
|
55
|
+
# @param configuration — with effects already enabled by the caller.
|
|
57
56
|
def initialize(configuration:, cache_root:)
|
|
58
57
|
@configuration = configuration
|
|
59
58
|
@cache_root = cache_root
|
|
60
59
|
end
|
|
61
60
|
|
|
62
|
-
# @param paths
|
|
61
|
+
# @param paths — the analysed set — `configuration.paths` unioned with any path
|
|
63
62
|
# arguments, exactly what {CLI::EffectsCommand#analyze} hands the runner, because the analysed
|
|
64
63
|
# set is part of the diagnostics key.
|
|
65
|
-
# @return
|
|
64
|
+
# @return nil to decline, on any failure whatsoever.
|
|
66
65
|
def serve(paths)
|
|
67
66
|
return nil if @cache_root.nil? || !@configuration.effects_check?
|
|
68
67
|
|
|
@@ -19,8 +19,7 @@ module Rigor
|
|
|
19
19
|
|
|
20
20
|
module_function
|
|
21
21
|
|
|
22
|
-
# @
|
|
23
|
-
# @return [Boolean] true when the parsed source looks like an ERB template (parse errors expected;
|
|
22
|
+
# @return true when the parsed source looks like an ERB template (parse errors expected;
|
|
24
23
|
# analysis should skip).
|
|
25
24
|
def template?(parse_result)
|
|
26
25
|
source = parse_result.source.source
|
|
@@ -18,9 +18,14 @@ module Rigor
|
|
|
18
18
|
].freeze
|
|
19
19
|
|
|
20
20
|
class Target < Data.define(:kind, :name)
|
|
21
|
+
# #775 — a target is an immutable value keyed by its name, and the local target is built on every
|
|
22
|
+
# binding (`Scope#with_local` invalidates through it) and every local-fact read: ~350k times on
|
|
23
|
+
# the lib self-check for a few thousand distinct names. One shared instance per name.
|
|
21
24
|
def self.local(name)
|
|
22
|
-
|
|
25
|
+
name = name.to_sym
|
|
26
|
+
@local_targets[name] ||= new(kind: :local, name: name)
|
|
23
27
|
end
|
|
28
|
+
@local_targets = {}
|
|
24
29
|
|
|
25
30
|
def initialize(kind:, name:)
|
|
26
31
|
super(kind: kind.to_sym, name: name)
|
|
@@ -120,6 +120,22 @@ module Rigor
|
|
|
120
120
|
appeared.freeze
|
|
121
121
|
end
|
|
122
122
|
|
|
123
|
+
# Issue #639 — the SYMMETRIC reading of {appeared_classes}: the names whose declaration in a file
|
|
124
|
+
# appeared OR VANISHED. Appearance alone answered the miss side, which was the only side recording a
|
|
125
|
+
# `class:` edge; a bare reference that RESOLVED now records one too, and what invalidates it is the
|
|
126
|
+
# declaration going away. `paths` MUST therefore include REMOVED files — a removed file has no
|
|
127
|
+
# after-state, so its whole before-set vanished — which is the same reason
|
|
128
|
+
# {changed_constant_publications} diffs over `changed + removed`.
|
|
129
|
+
def changed_class_declarations(paths, decls_before, decls_after)
|
|
130
|
+
moved = Set.new
|
|
131
|
+
paths.each do |path|
|
|
132
|
+
before = decls_before[path] || Set.new
|
|
133
|
+
after = decls_after[path] || Set.new
|
|
134
|
+
moved.merge(after ^ before)
|
|
135
|
+
end
|
|
136
|
+
moved.freeze
|
|
137
|
+
end
|
|
138
|
+
|
|
123
139
|
# Issue #644 — the qualified constant names whose PUBLICATION could have moved in this edit: the names
|
|
124
140
|
# whose per-file census descriptor differs between the before- and after-state of any file in `paths`.
|
|
125
141
|
# The value-constant twin of {appeared_classes}, and deliberately NOT its analogue — a class either
|
|
@@ -42,22 +42,22 @@ module Rigor
|
|
|
42
42
|
end
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
# @param paths
|
|
45
|
+
# @param paths — explicit analysis roots; nil (the default) uses the configuration's
|
|
46
46
|
# `paths:`.
|
|
47
|
-
# @param environment
|
|
47
|
+
# @param environment — optional shared environment to thread into each internal
|
|
48
48
|
# Runner. Long-lived callers and specs can use this to avoid rebuilding the same RBS universe for
|
|
49
49
|
# every baseline / recheck / oracle run.
|
|
50
|
-
# @param cache_store
|
|
50
|
+
# @param cache_store — ADR-85 WD1 — the persistent cache each internal Runner
|
|
51
51
|
# exposes to the RBS-env and plugin-producer tiers. A cross-process `--incremental` recheck otherwise
|
|
52
52
|
# rebuilt a fresh runner with no store, so every plugin `#prepare` producer (the ADR-9/#74/ADR-60 WD3
|
|
53
53
|
# record-and-validate caches) recomputed per invocation — 86% of a Rails warm incremental. Threading
|
|
54
54
|
# the store lets those producers serve from disk. `nil` (the default) preserves the pre-#85 behaviour
|
|
55
55
|
# the specs assert; the whole-run ADR-45 result cache stays disabled on these runs
|
|
56
56
|
# (`Runner#run_result_cacheable?` excludes `record_dependencies` / `analyze_only`).
|
|
57
|
-
# @param plugin_requirer
|
|
57
|
+
# @param plugin_requirer — optional gem-require hook threaded into each internal Runner
|
|
58
58
|
# (mirrors {Runner}'s parameter). nil (the default, and what the CLI passes) uses `Kernel.require`;
|
|
59
59
|
# embedders and specs inject a fake so a test plugin registers without touching the real load path.
|
|
60
|
-
# @param workers
|
|
60
|
+
# @param workers — ADR-46 — the resolved fork-pool worker count threaded into every internal
|
|
61
61
|
# analyzer, so a `--incremental` recheck's closure re-analysis parallelises exactly like the standard
|
|
62
62
|
# `check` path (the recon's audit: `--workers` / `RIGOR_RACTOR_WORKERS` / `parallel.workers:` were
|
|
63
63
|
# silently ignored because `build_runner` passed no `workers:`). 0 (the default, and what the specs and
|
|
@@ -112,7 +112,7 @@ module Rigor
|
|
|
112
112
|
# and a missing invalidation edge is impossible by construction — the reason this is a table diff
|
|
113
113
|
# and not the caller→callee edge recording #204 first sketched.
|
|
114
114
|
@param_table = {}
|
|
115
|
-
|
|
115
|
+
reset_carried_state
|
|
116
116
|
# ADR-88 WD1 — the plugin fact-surface digest computed for THIS invocation (nil until a
|
|
117
117
|
# `#run_incremental` pass runs / a plugin-free project) and the reporting flags a caller (the CLI
|
|
118
118
|
# banner + `--cache-stats`) reads after `#run_incremental`. `@last_runner` is the analysis runner the
|
|
@@ -174,6 +174,7 @@ module Rigor
|
|
|
174
174
|
@effects_identity = current_effects_identity
|
|
175
175
|
@cache = per_file(runner.per_file_diagnostics)
|
|
176
176
|
@digests = @analyzed.to_h { |path| [path, pack_digest(path)] }
|
|
177
|
+
@run_level_rows = runner.run_level_rows
|
|
177
178
|
diagnostics
|
|
178
179
|
end
|
|
179
180
|
|
|
@@ -197,7 +198,8 @@ module Rigor
|
|
|
197
198
|
# The freshly collected table is handed to the runner so the run seeds from the SAME table the diff
|
|
198
199
|
# was decided on (and the collector runs once per recheck, not twice).
|
|
199
200
|
runner = build_runner(analyze_only: analyze_set, record_dependencies: true,
|
|
200
|
-
param_inferred_types: fresh_params
|
|
201
|
+
param_inferred_types: fresh_params,
|
|
202
|
+
restored_run_level_rows: @run_level_rows)
|
|
201
203
|
fresh = run_runner(runner).diagnostics
|
|
202
204
|
@last_runner = runner # ADR-88 WD1 — the post-hoc fact-surface fingerprint reads this prepared registry.
|
|
203
205
|
reused = (current & previous) - affected.to_a
|
|
@@ -407,10 +409,13 @@ module Rigor
|
|
|
407
409
|
end
|
|
408
410
|
|
|
409
411
|
# The current project file set (cheap directory expansion, no analysis), used to detect files added /
|
|
410
|
-
# removed since the last run.
|
|
412
|
+
# removed since the last run. Also where an editor buffer's binding is re-spelled onto the set, since
|
|
413
|
+
# this is where the run first learns how it spells its own members.
|
|
411
414
|
def current_files
|
|
412
415
|
runner = build_runner
|
|
413
|
-
@paths ? runner.analysis_file_set(@paths) : runner.analysis_file_set
|
|
416
|
+
files = @paths ? runner.analysis_file_set(@paths) : runner.analysis_file_set
|
|
417
|
+
rebind_buffer(files)
|
|
418
|
+
files
|
|
414
419
|
end
|
|
415
420
|
|
|
416
421
|
# Verification engine (the `--verify-incremental` gate): with NO source edit, re-analyze `subset` fresh
|
|
@@ -422,7 +427,8 @@ module Rigor
|
|
|
422
427
|
affected = subset.to_set
|
|
423
428
|
# ADR-67 WD6c lift — seed the subset run from the baseline's own table so the verification engine
|
|
424
429
|
# exercises the exact seeds the served cache entries were computed under (and skips a re-collect).
|
|
425
|
-
runner = build_runner(analyze_only: affected, param_inferred_types: @param_table
|
|
430
|
+
runner = build_runner(analyze_only: affected, param_inferred_types: @param_table,
|
|
431
|
+
restored_run_level_rows: @run_level_rows)
|
|
426
432
|
fresh = run_runner(runner).diagnostics
|
|
427
433
|
reused = @analyzed - affected.to_a
|
|
428
434
|
fresh + reused.flat_map { |path| @cache[path] || [] }
|
|
@@ -559,6 +565,9 @@ module Rigor
|
|
|
559
565
|
restore_structural_declarations(payload)
|
|
560
566
|
@return_summaries = payload.return_summaries || {}
|
|
561
567
|
@param_table = payload.param_table || {} # ADR-67 WD6c lift — the seeds the cache was built under.
|
|
568
|
+
# Issues #796 / #794 — absent (nil) in a snapshot written before schema 22 is impossible (the SCHEMA
|
|
569
|
+
# gate rejects one), so nil here means the writing run genuinely had nothing to say.
|
|
570
|
+
@run_level_rows = payload.run_level_rows
|
|
562
571
|
# ADR-103 WD13 / #382 — the effects sidecar rides its own identity, so a snapshot whose summaries
|
|
563
572
|
# were collected under a different vocabulary / catalogue / `effects:` block restores as EMPTY
|
|
564
573
|
# rather than as stale rows. {#run_incremental} turns that emptiness into a full baseline, because
|
|
@@ -592,7 +601,8 @@ module Rigor
|
|
|
592
601
|
return_summaries: marshal_safe_return_summaries,
|
|
593
602
|
param_table: marshal_safe_param_table,
|
|
594
603
|
effect_collections: marshal_safe_effect_collections,
|
|
595
|
-
effects_identity: @effects_identity
|
|
604
|
+
effects_identity: @effects_identity,
|
|
605
|
+
run_level_rows: @run_level_rows
|
|
596
606
|
)
|
|
597
607
|
end
|
|
598
608
|
|
|
@@ -606,6 +616,15 @@ module Rigor
|
|
|
606
616
|
@effects_identity = nil
|
|
607
617
|
end
|
|
608
618
|
|
|
619
|
+
# The state this session carries ACROSS processes in the snapshot rather than deriving each run: the
|
|
620
|
+
# effects sidecar above, and (issues #796 / #794) the run-level rows the last FULL run answered — the
|
|
621
|
+
# `definition-build-failed` set and the HKT-scan outcome, nil until a full run has produced them, and
|
|
622
|
+
# handed to every narrowed run this session builds, none of which can derive them from its own files.
|
|
623
|
+
def reset_carried_state
|
|
624
|
+
reset_effect_state
|
|
625
|
+
@run_level_rows = nil
|
|
626
|
+
end
|
|
627
|
+
|
|
609
628
|
# Whether the effects half of a restored snapshot permits reuse. Collection off is vacuously true —
|
|
610
629
|
# the session keeps no collections, writes none, and the pre-#382 reuse decision stands unchanged.
|
|
611
630
|
def effects_reuse_permitted?(payload)
|
|
@@ -696,6 +715,9 @@ module Rigor
|
|
|
696
715
|
@digests[path] = pack_digest(path)
|
|
697
716
|
end
|
|
698
717
|
absorb_dependency_graph(runner)
|
|
718
|
+
# Issues #796 / #794 — the recheck's own snapshots already carry the replayed rows folded together
|
|
719
|
+
# with anything its closure demanded for itself, so this is the same union the next run replays.
|
|
720
|
+
@run_level_rows = runner.run_level_rows
|
|
699
721
|
refresh_return_summaries(runner, analyze_set)
|
|
700
722
|
refresh_effect_collections(runner, analyze_set)
|
|
701
723
|
end
|
|
@@ -803,20 +825,24 @@ module Rigor
|
|
|
803
825
|
end
|
|
804
826
|
|
|
805
827
|
# ADR-46 slice 3 — the consumers to re-check because a symbol that appeared in a changed file resolves
|
|
806
|
-
# a prior missed lookup
|
|
807
|
-
# would satisfy (`toplevel:foo` for a
|
|
808
|
-
# negative-dependents of those keys.
|
|
828
|
+
# a prior missed lookup, and (issue #639) because a class DECLARATION appeared or vanished. Maps each
|
|
829
|
+
# appeared `"ClassName#method"` to the negative-dependency key it would satisfy (`toplevel:foo` for a
|
|
830
|
+
# top-level def, `method:C#m` otherwise), then unions the recorded negative-dependents of those keys.
|
|
809
831
|
def negative_affected(changed, removed, new_fingerprints, new_class_decls, new_constant_decls)
|
|
810
832
|
appeared_methods = Incremental.appeared_symbols(changed, @symbol_fingerprints, new_fingerprints)
|
|
811
|
-
|
|
833
|
+
# Issue #639 — a class DISAPPEARING satisfies the `class:` kind too, now that a resolved bare
|
|
834
|
+
# reference records the edge and not only a failed one; removed files join the diff for it.
|
|
835
|
+
moved_classes = Incremental.changed_class_declarations(
|
|
836
|
+
changed + removed, @class_decls, new_class_decls
|
|
837
|
+
)
|
|
812
838
|
# Issue #644 — a constant whose PUBLICATION moved satisfies the `constant:` kind, keyed on the same
|
|
813
|
-
# last segment the consumer recorded.
|
|
814
|
-
# second declarer restores the precise answer, and no other producer input can see that.
|
|
839
|
+
# last segment the consumer recorded. Removed files join this diff (as they do the class one above):
|
|
840
|
+
# deleting a second declarer restores the precise answer, and no other producer input can see that.
|
|
815
841
|
moved_constants = Incremental.changed_constant_publications(
|
|
816
842
|
changed + removed, @constant_decls, new_constant_decls
|
|
817
843
|
)
|
|
818
844
|
keys = appeared_methods.map { |symbol| negative_key_for(symbol) }
|
|
819
|
-
keys.concat(
|
|
845
|
+
keys.concat(moved_classes.map { |klass| "class:#{klass.split('::').last}" })
|
|
820
846
|
keys.concat(moved_constants.map { |name| "constant:#{name.split('::').last}" })
|
|
821
847
|
Incremental.negative_closure(keys, @negative_dependents)
|
|
822
848
|
end
|
|
@@ -1013,18 +1039,51 @@ module Rigor
|
|
|
1013
1039
|
candidates.reject { |path| stat_fresh?(path) }
|
|
1014
1040
|
end
|
|
1015
1041
|
|
|
1016
|
-
# A bound buffer's logical path is never fresh: the bytes to analyse live in the editor's temp file, and
|
|
1017
|
-
# the recorded entry describes the file on disk. Re-analysing it when the two happen to agree costs one
|
|
1018
|
-
# file; trusting the stat tuple would serve the editor its own stale diagnostics.
|
|
1019
1042
|
def buffer_path?(path)
|
|
1020
1043
|
!@buffer.nil? && path == @buffer.logical_path
|
|
1021
1044
|
end
|
|
1022
1045
|
|
|
1046
|
+
# Editor mode (#146) — the bound buffer's logical path is fresh exactly when the EDITOR's bytes still
|
|
1047
|
+
# hash to the digest the snapshot recorded for the file they stand in for. The recorded stat tuple
|
|
1048
|
+
# describes the file on disk, which an edit in the editor never moves, so validating through it serves
|
|
1049
|
+
# the editor its own pre-buffer answers (#960); hashing the buffer makes the substituted file changed
|
|
1050
|
+
# whenever it differs, and keeps an untouched buffer out of the closure. Anything unreadable or
|
|
1051
|
+
# unrecorded reads as changed, the conservative direction the rest of this tier takes.
|
|
1052
|
+
def buffer_fresh?(path)
|
|
1053
|
+
recorded = Cache::FileDigest.content_digest(@digests[path])
|
|
1054
|
+
return false if recorded.nil?
|
|
1055
|
+
|
|
1056
|
+
Cache::FileDigest.hexdigest(@buffer.resolve(path)) == recorded
|
|
1057
|
+
rescue StandardError
|
|
1058
|
+
false
|
|
1059
|
+
end
|
|
1060
|
+
|
|
1061
|
+
# Editor mode (#146 / #960) — `--instead-of` is spelled the way the editor spells it (absolute, or
|
|
1062
|
+
# `./`-prefixed), while the analysed set is spelled the way the run's path arguments produce. A binding
|
|
1063
|
+
# whose logical path matches no member of that set substitutes nothing at all: every reader keeps
|
|
1064
|
+
# reading the file on disk and the run answers as if no buffer had been passed. Re-spell the binding
|
|
1065
|
+
# onto the set's own member, identified by real path, once the set is known.
|
|
1066
|
+
def rebind_buffer(files)
|
|
1067
|
+
return if @buffer.nil? || files.include?(@buffer.logical_path)
|
|
1068
|
+
|
|
1069
|
+
target = real_path(@buffer.logical_path)
|
|
1070
|
+
return if target.nil?
|
|
1071
|
+
|
|
1072
|
+
match = files.find { |path| real_path(path) == target }
|
|
1073
|
+
@buffer = BufferBinding.new(logical_path: match, physical_path: @buffer.physical_path) if match
|
|
1074
|
+
end
|
|
1075
|
+
|
|
1076
|
+
def real_path(path)
|
|
1077
|
+
File.realpath(path)
|
|
1078
|
+
rescue StandardError
|
|
1079
|
+
nil
|
|
1080
|
+
end
|
|
1081
|
+
|
|
1023
1082
|
# True when `path`'s recorded stat entry proves it unchanged since the last analysis. Any stat / parse
|
|
1024
1083
|
# failure (missing entry, unreadable / vanished file) reads as NOT fresh (→ re-analyse), preserving the
|
|
1025
1084
|
# prior `digest(path) != recorded` "changed" semantics for a file that cannot be validated.
|
|
1026
1085
|
def stat_fresh?(path)
|
|
1027
|
-
return
|
|
1086
|
+
return buffer_fresh?(path) if buffer_path?(path)
|
|
1028
1087
|
|
|
1029
1088
|
entry = @digests[path]
|
|
1030
1089
|
return false if entry.nil?
|
|
@@ -79,7 +79,6 @@ module Rigor
|
|
|
79
79
|
#
|
|
80
80
|
# Keeping the opaque decision here, rather than at each cache's call site, means a new consumer cannot
|
|
81
81
|
# key on `digest` while forgetting that an opaque surface makes it meaningless.
|
|
82
|
-
# @return [String, nil]
|
|
83
82
|
def self.key_digest(registry)
|
|
84
83
|
result = from_registry(registry)
|
|
85
84
|
result.opaque? ? nil : result.digest.to_s
|
|
@@ -29,13 +29,11 @@ module Rigor
|
|
|
29
29
|
# bucket on trust.
|
|
30
30
|
Undecidable = Data.define(:fqn, :path, :line, :reason)
|
|
31
31
|
|
|
32
|
-
# @param declarations
|
|
33
|
-
# @param references [Array<Scan::Reference>]
|
|
34
|
-
# @param root_fqns [Enumerable<String>] declarations that are entry points regardless of who references
|
|
32
|
+
# @param root_fqns — declarations that are entry points regardless of who references
|
|
35
33
|
# them (config-declared globs in this slice; plugin-supplied roots are #349).
|
|
36
|
-
# @param foreign
|
|
34
|
+
# @param foreign — predicate answering "is this FQN owned by something outside the project?" —
|
|
37
35
|
# a reopened gem or stdlib class must never be a candidate (WD6). Defaults to "nothing is foreign".
|
|
38
|
-
# @param dynamic_uses
|
|
36
|
+
# @param dynamic_uses — sites where a constant is reached by name at runtime.
|
|
39
37
|
# A literal-argument site contributes a real reference; a dynamic one taints a namespace (WD4).
|
|
40
38
|
def initialize(declarations:, references:, root_fqns: [], dynamic_uses: [], foreign: ->(_fqn) { false })
|
|
41
39
|
@declarations = declarations
|
|
@@ -83,14 +83,15 @@ module Rigor
|
|
|
83
83
|
# Loads the project's configured plugins, runs every `#prepare`, and returns the union of every
|
|
84
84
|
# published `:reachability_roots` and `:reachability_references` fact.
|
|
85
85
|
#
|
|
86
|
-
# @param configuration
|
|
87
|
-
# @param plugin_requirer
|
|
88
|
-
# `Analysis::Runner` exposes, so a spec can register a plugin class without publishing a
|
|
89
|
-
#
|
|
86
|
+
# @param configuration — the loaded project configuration.
|
|
87
|
+
# @param plugin_requirer — how a plugin gem is brought into the process, called with the gem name. The
|
|
88
|
+
# same seam `Analysis::Runner` exposes, so a spec can register a plugin class without publishing a
|
|
89
|
+
# gem.
|
|
90
|
+
# @param cache_store — when given, each plugin's `#prepare` producers read
|
|
90
91
|
# and write the same ADR-60 record-and-validate slots they use under `rigor check`, instead of
|
|
91
92
|
# recomputing from scratch — a routes parse or a factory discovery is a validated cache read on
|
|
92
93
|
# every invocation after the first. Nil keeps the historical recompute-always behaviour.
|
|
93
|
-
# @return
|
|
94
|
+
# @return sorted and de-duplicated. Empty whenever the project declares no plugins, no
|
|
94
95
|
# plugin contributes anything, or anything at all goes wrong.
|
|
95
96
|
def collect(configuration:, plugin_requirer: ->(name) { require name }, cache_store: nil)
|
|
96
97
|
return Contribution.empty if configuration.plugins.empty?
|
|
@@ -46,8 +46,8 @@ module Rigor
|
|
|
46
46
|
File.fnmatch?(pattern, path, File::FNM_PATHNAME)
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
# @param relative_paths
|
|
50
|
-
# @return
|
|
49
|
+
# @param relative_paths — paths relative to `root`.
|
|
50
|
+
# @return those that belong to the project itself.
|
|
51
51
|
def own(relative_paths, root)
|
|
52
52
|
prefixes = submodule_prefixes(root)
|
|
53
53
|
relative_paths.grep_v(VENDOR_DIRS).reject { |rel| prefixes.any? { |prefix| rel.start_with?(prefix) } }
|
|
@@ -70,11 +70,6 @@ module Rigor
|
|
|
70
70
|
end
|
|
71
71
|
end
|
|
72
72
|
|
|
73
|
-
# @param path [String] the file's path, as the report should render it.
|
|
74
|
-
# @param source [String] the file's bytes.
|
|
75
|
-
# @param target_ruby [String, nil] Prism version string, threaded from the project configuration.
|
|
76
|
-
# @return [Result, nil] nil when the file does not parse (a parse error is the analyzer's business, not
|
|
77
|
-
# this scan's — it simply contributes nothing rather than half a file).
|
|
78
73
|
# A constant name is ASCII by construction, so a byte sequence that is not valid UTF-8 cannot be one.
|
|
79
74
|
# Dropping it is both correct and the only safe answer: carrying it forward crashed the whole run on
|
|
80
75
|
# the first `String#sub` downstream, which is how this surfaced — `rigor unused` on Rigor's own
|
|
@@ -86,6 +81,11 @@ module Rigor
|
|
|
86
81
|
name.valid_encoding? ? name : nil
|
|
87
82
|
end
|
|
88
83
|
|
|
84
|
+
# @param path — the file's path, as the report should render it.
|
|
85
|
+
# @param source — the file's bytes.
|
|
86
|
+
# @param target_ruby — Prism version string, threaded from the project configuration.
|
|
87
|
+
# @return nil when the file does not parse (a parse error is the analyzer's business, not
|
|
88
|
+
# this scan's — it simply contributes nothing rather than half a file).
|
|
89
89
|
def self.call(path:, source:, target_ruby: nil)
|
|
90
90
|
parsed = if target_ruby
|
|
91
91
|
Prism.parse(source, filepath: path,
|
|
@@ -43,9 +43,9 @@ module Rigor
|
|
|
43
43
|
RACY_WINDOW_NS = 2_000_000_000
|
|
44
44
|
private_constant :RACY_WINDOW_NS
|
|
45
45
|
|
|
46
|
-
# @param cache_path
|
|
46
|
+
# @param cache_path — the configuration's cache root; nil or empty is an inert
|
|
47
47
|
# cache (every fetch computes, nothing persists).
|
|
48
|
-
# @param target_ruby
|
|
48
|
+
# @param target_ruby — parse-affecting configuration, part of the identity.
|
|
49
49
|
def self.open(cache_path, target_ruby: nil)
|
|
50
50
|
return new(path: nil, header: nil) if cache_path.nil? || cache_path.to_s.empty?
|
|
51
51
|
|
|
@@ -26,8 +26,8 @@ module Rigor
|
|
|
26
26
|
module SignatureScan
|
|
27
27
|
module_function
|
|
28
28
|
|
|
29
|
-
# @param file
|
|
30
|
-
# @return
|
|
29
|
+
# @param file — path to a `.rbs` file.
|
|
30
|
+
# @return one file-level reference per distinct referenced name. Empty when the
|
|
31
31
|
# file cannot be read or parsed — a broken signature is the analyzer's business, not this scan's.
|
|
32
32
|
def call(file)
|
|
33
33
|
_, _, decls = ::RBS::Parser.parse_signature(::RBS::Buffer.new(name: file, content: File.read(file)))
|
|
@@ -7,7 +7,7 @@ module Rigor
|
|
|
7
7
|
class Result
|
|
8
8
|
attr_reader :diagnostics, :stats
|
|
9
9
|
|
|
10
|
-
# @param stats
|
|
10
|
+
# @param stats — end-of-run telemetry (target file count, RBS class
|
|
11
11
|
# breakdown, wall + RSS) collected by the Runner. Nil when stats collection wasn't requested or wasn't
|
|
12
12
|
# applicable (early-exit paths like `validate_target_ruby` failure).
|
|
13
13
|
def initialize(diagnostics: [], stats: nil)
|
|
@@ -43,8 +43,6 @@ module Rigor
|
|
|
43
43
|
|
|
44
44
|
# The diagnostics {#crashed?} answers true for, so a caller can name what it saw rather than only that
|
|
45
45
|
# it saw something.
|
|
46
|
-
#
|
|
47
|
-
# @return [Array<Rigor::Analysis::Diagnostic>]
|
|
48
46
|
def crash_diagnostics
|
|
49
47
|
diagnostics.select { |diagnostic| CrashSignature.discards_file_analysis?(diagnostic) }
|
|
50
48
|
end
|
|
@@ -642,7 +642,10 @@ module Rigor
|
|
|
642
642
|
"Later write is `nil` — the `@cache = nil` clear-idiom is allowlisted.",
|
|
643
643
|
"Either side is Union / Dynamic / IntegerRange / a shape-varied carrier.",
|
|
644
644
|
"Writes live in different classes that happen to share an ivar name.",
|
|
645
|
-
"Writes are in `def self.foo`
|
|
645
|
+
"Writes are in a singleton body — `def self.foo` or a `class << self` def — those track " \
|
|
646
|
+
"separately.",
|
|
647
|
+
"Writes are in a `def` inside an anonymous-class factory block (`Class.new do … end`, " \
|
|
648
|
+
"`Module.new`, `Struct.new`, `Data.define`): that body defines another class."
|
|
646
649
|
],
|
|
647
650
|
suppression: "`# rigor:disable ivar-write-mismatch` on the offending write.",
|
|
648
651
|
severity_authored: :error,
|
|
@@ -80,7 +80,7 @@ module Rigor
|
|
|
80
80
|
# collecting project's path-set churn is the same churn, and over-evicting costs one recompute.
|
|
81
81
|
EFFECTS_GENERATION_CAP = GENERATION_CAP
|
|
82
82
|
|
|
83
|
-
# @param rbs_config_entries
|
|
83
|
+
# @param rbs_config_entries — the RBS-derived config slots
|
|
84
84
|
# (`rbs.libraries` [+ `rbs.virtual_rbs`]). nil on any failure so a malformed key disables the cache.
|
|
85
85
|
def descriptor(configuration:, files:, explain:, rbs_config_entries:)
|
|
86
86
|
Cache::Descriptor.new(
|
|
@@ -51,17 +51,15 @@ module Rigor
|
|
|
51
51
|
# serves it instead. The decline is measured against the declarations alone, never against what they
|
|
52
52
|
# would judge to, so it costs one glob and never a wrong answer.
|
|
53
53
|
class RunCacheProbe
|
|
54
|
-
# @param
|
|
55
|
-
# @param cache_root [String]
|
|
56
|
-
# @param explain [Boolean] the `--explain` flag (folded into the key, as the runner does).
|
|
54
|
+
# @param explain — the `--explain` flag (folded into the key, as the runner does).
|
|
57
55
|
def initialize(configuration:, cache_root:, explain:)
|
|
58
56
|
@configuration = configuration
|
|
59
57
|
@cache_root = cache_root
|
|
60
58
|
@explain = explain
|
|
61
59
|
end
|
|
62
60
|
|
|
63
|
-
# @param paths
|
|
64
|
-
# @return
|
|
61
|
+
# @param paths — the analysis roots (`@argv` or `configuration.paths`).
|
|
62
|
+
# @return the cached run result with the severity profile applied and no stats
|
|
65
63
|
# (matching a cache-served `Runner#run`), or nil to DECLINE — a miss / stale / unavailable cache — so
|
|
66
64
|
# the caller loads the engine and runs the full path. Any failure declines rather than raising: the
|
|
67
65
|
# probe must never turn a servable run into a crash.
|
|
@@ -66,7 +66,7 @@ module Rigor
|
|
|
66
66
|
# via `RIGOR_LSP_POOL_MIN_BATCH` (mirrors `RIGOR_RACTOR_WORKERS`'s override shape).
|
|
67
67
|
DEFAULT_MIN_BATCH_SIZE = 16
|
|
68
68
|
|
|
69
|
-
# @return
|
|
69
|
+
# @return `RIGOR_LSP_POOL_MIN_BATCH` when set to a non-empty value, else
|
|
70
70
|
# {DEFAULT_MIN_BATCH_SIZE}.
|
|
71
71
|
def self.resolve_min_batch_size
|
|
72
72
|
env_value = ENV.fetch("RIGOR_LSP_POOL_MIN_BATCH", nil)
|
|
@@ -75,19 +75,17 @@ module Rigor
|
|
|
75
75
|
Integer(env_value)
|
|
76
76
|
end
|
|
77
77
|
|
|
78
|
-
# @param
|
|
79
|
-
# @param cache_store [Rigor::Cache::Store, nil]
|
|
80
|
-
# @param environment [Rigor::Environment] the warm, shared per-session Environment
|
|
78
|
+
# @param environment — the warm, shared per-session Environment
|
|
81
79
|
# (`ProjectContext#environment`) every job's Runner reuses instead of rebuilding.
|
|
82
|
-
# @param prebuilt
|
|
80
|
+
# @param prebuilt — the warm, shared pre-pass snapshot
|
|
83
81
|
# (`ProjectContext#project_scan`) every job's Runner adopts instead of re-scanning.
|
|
84
|
-
# @param workers
|
|
82
|
+
# @param workers — pool size. `#analyze` degrades to sequential in-process execution — one
|
|
85
83
|
# job at a time, no `fork` — when fewer than 2 bindings are submitted, fewer than `min_batch_size`
|
|
86
84
|
# bindings are submitted, `workers` is not positive, `fork` is unavailable on this platform, or
|
|
87
85
|
# `cache_store` is nil. The middle two are the fork-pool-is-not-worth-it-yet gate documented on
|
|
88
86
|
# {DEFAULT_MIN_BATCH_SIZE}; the last two mirror
|
|
89
87
|
# {PoolCoordinator#analyze_files_in_pool}'s own fork-pool preconditions.
|
|
90
|
-
# @param min_batch_size
|
|
88
|
+
# @param min_batch_size — see {DEFAULT_MIN_BATCH_SIZE}. Exposed as a constructor param
|
|
91
89
|
# (rather than read from the env internally) purely for spec control; production callers get the
|
|
92
90
|
# resolved default.
|
|
93
91
|
def initialize(configuration:, cache_store:, environment:, prebuilt:, workers:,
|
|
@@ -185,7 +183,7 @@ module Rigor
|
|
|
185
183
|
degraded
|
|
186
184
|
end
|
|
187
185
|
|
|
188
|
-
# @return
|
|
186
|
+
# @return the child's `{index => diagnostics}` payload, or nil when the child exited
|
|
189
187
|
# abnormally or wrote no readable payload. `Marshal.load` is safe here: the blob was written by our
|
|
190
188
|
# own forked child to a temp file we created.
|
|
191
189
|
def fork_worker_payload(status, out_path)
|