rigortype 0.3.7 → 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 +96 -15
- 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 +39 -4
- data/docs/manual/03-configuration.md +11 -0
- data/docs/manual/04-diagnostics.md +12 -1
- 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 +69 -13
- 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 +100 -29
- 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 +316 -40
- 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 +430 -77
- data/lib/rigor/analysis/runner/project_pre_passes.rb +12 -6
- data/lib/rigor/analysis/runner/run_snapshots.rb +13 -3
- data/lib/rigor/analysis/runner.rb +200 -43
- data/lib/rigor/analysis/worker_session.rb +36 -12
- data/lib/rigor/bleeding_edge.rb +12 -24
- data/lib/rigor/builtins/hkt_builtins.rb +2 -2
- data/lib/rigor/builtins/imported_refinements.rb +166 -30
- 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 +70 -0
- 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_environment_marshal_patch.rb +39 -0
- data/lib/rigor/cache/rbs_known_class_names.rb +0 -3
- data/lib/rigor/cache/store.rb +85 -33
- 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 +31 -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 +28 -0
- 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 +613 -113
- data/lib/rigor/environment.rb +189 -27
- 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 +19 -10
- data/lib/rigor/inference/hkt_sugar_translator.rb +15 -21
- 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 +67 -14
- 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 +12 -13
- data/lib/rigor/rbs_extended/hkt_directives.rb +16 -1
- data/lib/rigor/rbs_extended/reporter.rb +122 -16
- data/lib/rigor/rbs_extended.rb +67 -22
- 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 +181 -102
- 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 +17 -11
- 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 +11 -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 +28 -3
- data/lib/rigor/cli/probe_environment.rb +0 -85
- data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "tmpdir"
|
|
4
4
|
|
|
5
5
|
require_relative "../../environment"
|
|
6
|
+
require_relative "../../project_environment"
|
|
6
7
|
require_relative "../diagnostic"
|
|
7
8
|
require_relative "../worker_session"
|
|
8
9
|
require_relative "../run_stats"
|
|
@@ -29,30 +30,21 @@ module Rigor
|
|
|
29
30
|
# delegated back through an injected `analyze_file` callable so the CheckRules / recorder /
|
|
30
31
|
# plugin-emission machinery stays on the {Runner}.
|
|
31
32
|
class PoolCoordinator # rubocop:disable Metrics/ClassLength
|
|
32
|
-
# @param
|
|
33
|
-
# @param
|
|
34
|
-
# @param
|
|
35
|
-
# @param
|
|
36
|
-
# @param
|
|
37
|
-
# @param
|
|
38
|
-
# @param environment_override [Rigor::Environment, nil]
|
|
39
|
-
# @param rbs_extended_reporter [RbsExtended::Reporter]
|
|
40
|
-
# @param boundary_cross_reporter [DependencySourceInference::BoundaryCrossReporter]
|
|
41
|
-
# @param source_rbs_synthesis_reporter [Plugin::SourceRbsSynthesisReporter]
|
|
42
|
-
# @param snapshots [RunSnapshots] shared end-of-pass snapshot sink.
|
|
43
|
-
# @param plugin_registry [#call] reader for the current registry.
|
|
44
|
-
# @param dependency_source_index [#call] reader.
|
|
45
|
-
# @param synthetic_method_index [#call] reader.
|
|
46
|
-
# @param project_patched_methods [#call] reader.
|
|
47
|
-
# @param project_scope_seed [#call] reader for the cross-file pre-pass seed tables
|
|
33
|
+
# @param snapshots — shared end-of-pass snapshot sink.
|
|
34
|
+
# @param plugin_registry — reader for the current registry.
|
|
35
|
+
# @param dependency_source_index — reader.
|
|
36
|
+
# @param synthetic_method_index — reader.
|
|
37
|
+
# @param project_patched_methods — reader.
|
|
38
|
+
# @param project_scope_seed — reader for the cross-file pre-pass seed tables
|
|
48
39
|
# (`Runner#project_scope_seed_tables`).
|
|
49
|
-
# @param analyze_file
|
|
40
|
+
# @param analyze_file — `(path, environment) -> diagnostics`.
|
|
50
41
|
def initialize(configuration:, cache_store:, explain:, workers:, collect_stats:, # rubocop:disable Metrics/ParameterLists
|
|
51
42
|
buffer:, environment_override:, rbs_extended_reporter:,
|
|
52
43
|
boundary_cross_reporter:, source_rbs_synthesis_reporter:,
|
|
53
44
|
snapshots:, plugin_registry:, dependency_source_index:,
|
|
54
45
|
synthetic_method_index:, project_patched_methods:,
|
|
55
|
-
analyze_file:, project_scope_seed: -> { {} }, record_dependencies: false
|
|
46
|
+
analyze_file:, project_scope_seed: -> { {} }, record_dependencies: false,
|
|
47
|
+
restored_run_level_rows: nil)
|
|
56
48
|
@configuration = configuration
|
|
57
49
|
@cache_store = cache_store
|
|
58
50
|
@explain = explain
|
|
@@ -80,6 +72,10 @@ module Rigor
|
|
|
80
72
|
@project_patched_methods_reader = project_patched_methods
|
|
81
73
|
@project_scope_seed_reader = project_scope_seed
|
|
82
74
|
@analyze_file = analyze_file
|
|
75
|
+
# Issues #796 / #794 — the previous full run's run-level rows, handed down by
|
|
76
|
+
# {Analysis::IncrementalSession} on a NARROWED run only (a recheck's closure, a
|
|
77
|
+
# `--verify-incremental` partition). A full run leaves this nil and derives everything itself.
|
|
78
|
+
@restored_run_level_rows = restored_run_level_rows
|
|
83
79
|
end
|
|
84
80
|
|
|
85
81
|
# ADR-46 — the per-file cross-file read records the fork pool captured this run (empty unless
|
|
@@ -115,11 +111,168 @@ module Rigor
|
|
|
115
111
|
# sources. The env stays a LOCAL variable (not an ivar) so it goes GC-eligible when the method
|
|
116
112
|
# returns — holding it as long-lived state added memory pressure that surfaced as a Bus Error
|
|
117
113
|
# during the spec suite under Ruby 4.0 + rbs 4.0.2.
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
114
|
+
# @param project_files — issue #784 — the WHOLE project's analyzed file set
|
|
115
|
+
# (`expansion.fetch(:files)`), independent of any `analyze_only` narrowing of `files`; see
|
|
116
|
+
# {#serve_empty_closure} for what an empty `files` does with it.
|
|
117
|
+
def analyze_files(files, environment: nil, project_files: nil)
|
|
118
|
+
if files.empty?
|
|
119
|
+
serve_empty_closure(environment, project_files)
|
|
120
|
+
return []
|
|
121
|
+
end
|
|
122
|
+
# Issue #784 / #793 — `files` is what this run ANALYSES; `source_files` is what its environment
|
|
123
|
+
# is BUILT over, and the two are the same only on a full run. A narrowed run (`analyze_only`: an
|
|
124
|
+
# incremental closure, a `--verify-incremental` partition) used to build its environment over the
|
|
125
|
+
# subset, so plugin-synthesized virtual RBS from every excluded file was missing and the run
|
|
126
|
+
# scanned a different type universe than the full run it is compared against — a scan failure
|
|
127
|
+
# whose trigger lived in an excluded file's synthesized RBS fired on the full run and vanished on
|
|
128
|
+
# the subset. Every environment and worker this run builds now takes the whole project.
|
|
129
|
+
source_files = project_files || files
|
|
130
|
+
result =
|
|
131
|
+
if pool_mode?
|
|
132
|
+
dispatch_pool(files, source_files: source_files)
|
|
133
|
+
else
|
|
134
|
+
analyze_files_sequentially(
|
|
135
|
+
files, environment || resolve_sequential_environment(source_files: source_files)
|
|
136
|
+
)
|
|
137
|
+
end
|
|
138
|
+
# Issues #796 / #794 — a narrowed run's own demand set is a SUBSET of the full run's, so the two
|
|
139
|
+
# rows below are folded in after it, whichever backend produced the per-file stream.
|
|
140
|
+
replayed_restored_run_level_rows?(closure: files)
|
|
141
|
+
result
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Issue #784 — an EMPTY analyze set still owes the run its run-level rows: the per-file cache never
|
|
145
|
+
# holds them (`IncrementalSession` caches only `Runner#per_file_diagnostics`), so returning here
|
|
146
|
+
# without recording flips a red project green — and the shipping `--incremental` path reaches this
|
|
147
|
+
# branch with NO environment in hand on every warm recheck that changed nothing
|
|
148
|
+
# (`CheckCommand#run_incremental_check` builds its session without one).
|
|
149
|
+
#
|
|
150
|
+
# #788 rounds 6 and 9 — everything the run owes from its environment that no per-file analysis
|
|
151
|
+
# produces is taken here, the way `analyze_files_sequentially` takes it: the project-signature state
|
|
152
|
+
# (`synthesized-namespace`, `quarantined-signature`, `environment-build-failed`, the conformance
|
|
153
|
+
# results), the effect-annotation carrier the residual pass reads, and the HKT-scan outcome. Leaving
|
|
154
|
+
# any of them out turned a red project green on its second `--incremental` run. Same ORDER as the
|
|
155
|
+
# sequential path, because the order is the contract: the conformance scan inside the
|
|
156
|
+
# signature-state snapshot demands the definition of every `rigor:v1:conforms-to` class — a
|
|
157
|
+
# user-authored, invocation-independent demand that #696 counts — so the definition-build failures
|
|
158
|
+
# are read AFTER it and BEFORE the HKT outcome.
|
|
159
|
+
#
|
|
160
|
+
# Issue #794 — the environment is resolved LAZILY, by the two snapshots that need one and only when
|
|
161
|
+
# their own gates say so: a project with no `signature_paths:`, no plugin-deferred signatures and no
|
|
162
|
+
# source-RBS synthesizer now reaches the end of this branch without building an environment at all,
|
|
163
|
+
# where #788 built one unconditionally to make the HKT demand. Issue #796 — what this branch cannot
|
|
164
|
+
# derive is the part of the definition-build set the per-file ANALYSIS demanded, and #696 forbids a
|
|
165
|
+
# Rigor-owned demand from standing in for it; the previous full run's set is replayed instead.
|
|
166
|
+
# @param project_files — issue #784 — the WHOLE project's analyzed file set
|
|
167
|
+
# (`expansion.fetch(:files)`), independent of any `analyze_only` narrowing. Read here to decide
|
|
168
|
+
# whether anyone could have demanded anything at all: with no files, nobody could have, and an
|
|
169
|
+
# empty project keeps paying no env build. Keyed on the project's files rather than on
|
|
170
|
+
# `analyze_only`, because a recheck over an EMPTY project narrows to `Set[]`, which is non-nil.
|
|
171
|
+
def serve_empty_closure(environment, project_files)
|
|
172
|
+
env = environment || @environment_override
|
|
173
|
+
# Resolved over the project's OWN file list — not `[]`, which would drop every plugin-synthesized
|
|
174
|
+
# virtual RBS (`Environment.collect_virtual_rbs` short-circuits on an empty list) and scan a
|
|
175
|
+
# different type universe from the one a full run analyses.
|
|
176
|
+
resolve = lambda do
|
|
177
|
+
next env if env
|
|
178
|
+
next nil if project_files.nil? || project_files.empty?
|
|
179
|
+
|
|
180
|
+
env = resolve_sequential_environment(source_files: project_files)
|
|
181
|
+
end
|
|
182
|
+
snapshot_project_signature_state(signature_state_environment(resolve))
|
|
183
|
+
snapshot_effect_annotation_carrier(
|
|
184
|
+
annotation_carrier_environment(resolve, in_hand: !env.nil?)&.rbs_loader
|
|
185
|
+
)
|
|
186
|
+
# `env` rather than `resolve.call`: read the demand the snapshots above just made when they built
|
|
187
|
+
# an environment, and force nothing when they did not.
|
|
188
|
+
record_definition_build_failures(env&.rbs_loader&.definition_build_failures)
|
|
189
|
+
return if replayed_restored_run_level_rows?
|
|
190
|
+
|
|
191
|
+
record_hkt_scan_failure(hkt_scan_outcome(resolve.call))
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Issue #794 — the signature-state snapshot's own gates, asked BEFORE an environment exists. Both
|
|
195
|
+
# answers are loader-free (the configuration and the plugin registry), and with neither gate open
|
|
196
|
+
# {#snapshot_project_signature_state} writes the same empty state a nil environment gives it.
|
|
197
|
+
def signature_state_environment(resolve)
|
|
198
|
+
plugin_signature_paths? || project_signature_paths? ? resolve.call : nil
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Issue #794 — the same, for the #441 carrier. The carrier is a filter over the loader's
|
|
202
|
+
# `virtual_rbs`, and an environment whose loader can hold none of it answers the same empty carrier
|
|
203
|
+
# a nil environment does. Two ways it can hold some: a plugin registry that declares a synthesizer
|
|
204
|
+
# (`Environment.collect_virtual_rbs` short-circuits to `[]` without one — and ADR-93 auto-wires
|
|
205
|
+
# `rigor-rbs-inline`, so this is the shape almost every project is in), or an environment ALREADY in
|
|
206
|
+
# hand, whose loader is free to read and may carry virtual buffers from a route this coordinator
|
|
207
|
+
# never saw (a restored env cache, a caller-supplied override). Only the third shape — no
|
|
208
|
+
# synthesizer AND no environment yet — declines, which is the #794 saving: nothing is built to
|
|
209
|
+
# discover an emptiness the registry already proves.
|
|
210
|
+
def annotation_carrier_environment(resolve, in_hand: false)
|
|
211
|
+
return nil if @record_effects
|
|
212
|
+
return resolve.call if in_hand
|
|
213
|
+
|
|
214
|
+
registry = plugin_registry
|
|
215
|
+
return nil unless registry.respond_to?(:source_rbs_synthesizers)
|
|
216
|
+
return nil if registry.source_rbs_synthesizers.empty?
|
|
217
|
+
|
|
218
|
+
resolve.call
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Issues #796 / #794 — fold the previous full run's run-level rows into this narrowed run's
|
|
222
|
+
# snapshots. The definition-build set is UNIONed (`#record_definition_build_failures` dedupes by
|
|
223
|
+
# class), so a class this run demanded for itself and the replayed set agree; the HKT outcome is a
|
|
224
|
+
# single tuple whoever demanded it, so `||=` keeps this run's own answer when it has one. Returns
|
|
225
|
+
# true when a replay was available, which is what tells {#serve_empty_closure} it owes no HKT demand
|
|
226
|
+
# of its own — and so no environment.
|
|
227
|
+
#
|
|
228
|
+
# @param closure — the files this run analyses; see {#stale_replayed_failure?}.
|
|
229
|
+
def replayed_restored_run_level_rows?(closure: nil)
|
|
230
|
+
rows = @restored_run_level_rows
|
|
231
|
+
return false if rows.nil?
|
|
232
|
+
|
|
233
|
+
replayable = Array(rows.definition_build_failures).reject do |failure|
|
|
234
|
+
stale_replayed_failure?(failure, closure)
|
|
235
|
+
end
|
|
236
|
+
record_definition_build_failures(replayable)
|
|
237
|
+
record_hkt_scan_failure(rows.hkt_scan_failure)
|
|
238
|
+
true
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# A replayed definition-build failure the snapshot's own fingerprint cannot vouch for. The
|
|
242
|
+
# fingerprint digests the three signature sources it can read WITHOUT an environment — `sig:`,
|
|
243
|
+
# `Gemfile.lock` / `rbs_collection.lock.yaml`, and the configuration — but NOT the virtual RBS a
|
|
244
|
+
# source-RBS synthesizer derives from `.rb` files (under ADR-93's auto-wired `rigor-rbs-inline`,
|
|
245
|
+
# every `#:` annotation). So a duplicate declaration can be REMOVED from a `.rb` file without moving
|
|
246
|
+
# the fingerprint, and the replayed row would outlive its own cause: the edited file enters the
|
|
247
|
+
# closure, its analysis demands nothing that would re-derive the row, and the run reports a
|
|
248
|
+
# collision between files that no longer collide.
|
|
249
|
+
#
|
|
250
|
+
# The narrowed run cannot re-derive the row either (#696 forbids Rigor demanding a definition the
|
|
251
|
+
# analysis did not), so the honest answer is to DROP it and under-report rather than assert a
|
|
252
|
+
# diagnostic on a program that may now be correct — false positives outrank worst-case reading. The
|
|
253
|
+
# cost is bounded to the row's own conflicting buffers: a collision between two files neither of
|
|
254
|
+
# which this run touches still replays.
|
|
255
|
+
def stale_replayed_failure?(failure, closure)
|
|
256
|
+
return false if closure.nil?
|
|
257
|
+
|
|
258
|
+
buffers = failure.is_a?(Array) ? failure[3] : nil
|
|
259
|
+
return false if buffers.nil?
|
|
260
|
+
|
|
261
|
+
closure_paths = closure.to_set { |path| File.expand_path(path.to_s) }
|
|
262
|
+
Array(buffers).any? do |name|
|
|
263
|
+
path = virtual_buffer_path(name)
|
|
264
|
+
!path.nil? && closure_paths.include?(File.expand_path(path))
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# The source file behind a `virtual:<plugin id>:<path>` buffer name
|
|
269
|
+
# (`Environment.collect_virtual_rbs`), or nil for any other buffer — a `sig:` file, a bundled `.rbs`
|
|
270
|
+
# — which the fingerprint does cover and which therefore never goes stale under a replay.
|
|
271
|
+
def virtual_buffer_path(name)
|
|
272
|
+
parts = name.to_s.split(":", 3)
|
|
273
|
+
return nil unless parts.length == 3 && parts.first == "virtual"
|
|
121
274
|
|
|
122
|
-
|
|
275
|
+
parts.last
|
|
123
276
|
end
|
|
124
277
|
|
|
125
278
|
def analyze_files_sequentially(files, environment)
|
|
@@ -130,6 +283,13 @@ module Rigor
|
|
|
130
283
|
# first demand), so a snapshot taken beside the ones above — which run BEFORE `files.flat_map` —
|
|
131
284
|
# would read an empty list on every run, including the ones this diagnostic exists for.
|
|
132
285
|
record_definition_build_failures(environment&.rbs_loader&.definition_build_failures)
|
|
286
|
+
# Issue #784 — same timing contract, same reason: the HKT scan is first demanded from inside a
|
|
287
|
+
# file's analysis (the dispatcher's Singleton-receiver tier), so a snapshot taken any earlier
|
|
288
|
+
# would read nil on every run. And demanded HERE once more by the run itself, because a subset
|
|
289
|
+
# run (`--verify-incremental`'s partition, an incremental recheck's closure) may contain no file
|
|
290
|
+
# that demands it — the row must not depend on which files were analysed. See {#hkt_scan_outcome}
|
|
291
|
+
# for why a Rigor-owned demand is sound here where #696 forbids it.
|
|
292
|
+
record_hkt_scan_failure(hkt_scan_outcome(environment))
|
|
133
293
|
if @collect_stats
|
|
134
294
|
loader = environment.rbs_loader
|
|
135
295
|
@snapshots.class_decl_paths = loader&.class_decl_paths || {}.freeze
|
|
@@ -148,6 +308,13 @@ module Rigor
|
|
|
148
308
|
# `.rigor/cache` on a bare `--no-stats` run. The loader has already memoised each answer, so reading
|
|
149
309
|
# them here is free.
|
|
150
310
|
def snapshot_project_signature_state(environment)
|
|
311
|
+
# Issue #610 — NOT behind the `signature_paths:` gate below: the source a bundled plugin's `sig/`
|
|
312
|
+
# stands down against is typically an `rbs collection install` the configuration never lists
|
|
313
|
+
# there. Gated instead on a loaded plugin contributing signatures at all — the registry answers
|
|
314
|
+
# that without touching the env, and it is exactly the set the loader defers — so a project with
|
|
315
|
+
# no such plugin reads nothing off its loader here, as before.
|
|
316
|
+
@snapshots.signature_standdowns =
|
|
317
|
+
plugin_signature_paths? ? signature_standdowns_for(environment&.rbs_loader) : []
|
|
151
318
|
unless project_signature_paths?
|
|
152
319
|
@snapshots.synthesized_namespaces = []
|
|
153
320
|
@snapshots.quarantined_signatures = []
|
|
@@ -156,7 +323,8 @@ module Rigor
|
|
|
156
323
|
return
|
|
157
324
|
end
|
|
158
325
|
|
|
159
|
-
|
|
326
|
+
# nil-safe: the empty-closure path reaches here with no environment when the project has no files.
|
|
327
|
+
loader = environment&.rbs_loader
|
|
160
328
|
@snapshots.synthesized_namespaces = loader&.synthesized_namespaces || []
|
|
161
329
|
@snapshots.quarantined_signatures = loader&.quarantined_signatures || []
|
|
162
330
|
@snapshots.env_build_failure = loader&.env_build_failure
|
|
@@ -220,20 +388,24 @@ module Rigor
|
|
|
220
388
|
# An effects run (ADR-103 WD13) is pinned for exactly the same reason — the Ractor messages carry
|
|
221
389
|
# no side-table channel — and degrades the same way. The degrade is sound rather than merely safe:
|
|
222
390
|
# the sequential fallback still collects, so the effect graph is complete either way.
|
|
223
|
-
|
|
391
|
+
# @param source_files — the file list every worker's / the fallback's environment is
|
|
392
|
+
# built over — the whole project (issue #793), defaulting to `files` for direct callers.
|
|
393
|
+
def dispatch_pool(files, source_files: files)
|
|
224
394
|
if @record_dependencies || @record_effects
|
|
225
|
-
return analyze_files_in_fork_pool(files) if Process.respond_to?(:fork)
|
|
395
|
+
return analyze_files_in_fork_pool(files, source_files: source_files) if Process.respond_to?(:fork)
|
|
226
396
|
|
|
227
397
|
return analyze_files_sequentially_fallback(
|
|
228
|
-
files, reason: "incremental parallelism requires fork; recording sequentially"
|
|
398
|
+
files, reason: "incremental parallelism requires fork; recording sequentially",
|
|
399
|
+
source_files: source_files
|
|
229
400
|
)
|
|
230
401
|
end
|
|
231
402
|
case pool_backend
|
|
232
|
-
when :ractor then analyze_files_in_pool(files)
|
|
233
|
-
when :fork then analyze_files_in_fork_pool(files)
|
|
403
|
+
when :ractor then analyze_files_in_pool(files, source_files: source_files)
|
|
404
|
+
when :fork then analyze_files_in_fork_pool(files, source_files: source_files)
|
|
234
405
|
else
|
|
235
406
|
analyze_files_sequentially_fallback(
|
|
236
|
-
files, reason: "fork-based parallelism is unavailable on this platform"
|
|
407
|
+
files, reason: "fork-based parallelism is unavailable on this platform",
|
|
408
|
+
source_files: source_files
|
|
237
409
|
)
|
|
238
410
|
end
|
|
239
411
|
end
|
|
@@ -255,11 +427,7 @@ module Rigor
|
|
|
255
427
|
rbs_extended_reporter: @rbs_extended_reporter,
|
|
256
428
|
boundary_cross_reporter: @boundary_cross_reporter,
|
|
257
429
|
source_rbs_synthesis_reporter: @source_rbs_synthesis_reporter,
|
|
258
|
-
|
|
259
|
-
bundler_auto_detect: @configuration.bundler_auto_detect,
|
|
260
|
-
bundler_lockfile: @configuration.bundler_lockfile,
|
|
261
|
-
rbs_collection_lockfile: @configuration.rbs_collection_lockfile,
|
|
262
|
-
rbs_collection_auto_detect: @configuration.rbs_collection_auto_detect,
|
|
430
|
+
**ProjectEnvironment.dependency_discovery_options(@configuration),
|
|
263
431
|
synthetic_method_index: synthetic_method_index,
|
|
264
432
|
project_patched_methods: project_patched_methods,
|
|
265
433
|
source_files: source_files
|
|
@@ -299,7 +467,7 @@ module Rigor
|
|
|
299
467
|
# worker touches comes back as an internal analyzer error. What the code below fixes is the
|
|
300
468
|
# failure MODE, not the backend: the run used to hang forever instead of saying anything. Reviving
|
|
301
469
|
# the backend needs an upstream change, which is why `pool_backend` keeps `fork` as the default.
|
|
302
|
-
def analyze_files_in_pool(files) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
|
|
470
|
+
def analyze_files_in_pool(files, source_files: files) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
|
|
303
471
|
# Pre-warm class-level lazy memos on the MAIN Ractor. `Environment::ClassRegistry.default` is the
|
|
304
472
|
# default kwarg threaded through `Environment.new` inside each worker session; lazy-initialising
|
|
305
473
|
# it from a non-main Ractor would trip `Ractor::IsolationError`. Touching it here forces the
|
|
@@ -314,10 +482,35 @@ module Rigor
|
|
|
314
482
|
# constants.
|
|
315
483
|
if @cache_store.nil?
|
|
316
484
|
return analyze_files_sequentially_fallback(
|
|
317
|
-
files, reason: "pool mode requires a cache_store (--no-cache disables pool)"
|
|
485
|
+
files, reason: "pool mode requires a cache_store (--no-cache disables pool)",
|
|
486
|
+
source_files: source_files
|
|
318
487
|
)
|
|
319
488
|
end
|
|
320
|
-
|
|
489
|
+
# Issue #798 — the SAME gap the fork pool had: this coordinator never analyses a file itself, so
|
|
490
|
+
# without an explicit read the project-signature state (`synthesized-namespace`,
|
|
491
|
+
# `quarantined-signature`, `environment-build-failed`, the conformance results) has no producer.
|
|
492
|
+
# Unlike the fork pool's copy-on-write children, a Ractor worker builds its OWN Environment
|
|
493
|
+
# inside its own isolated Ractor, so nothing here inherits what the coordinator's environment
|
|
494
|
+
# finds. `#prewarm_rbs_cache_for_pool` already builds and fully loads exactly this
|
|
495
|
+
# coordinator-side environment (to warm the cache before any worker spawns); it now hands it
|
|
496
|
+
# back so its RBS state can be read the same way the fork pool reads its pre-fork session
|
|
497
|
+
# environment. The effect-annotation carrier (#441 — the inline stratum of
|
|
498
|
+
# `effect.annotations-unchecked`, read by the residual pass after this environment is gone) is
|
|
499
|
+
# taken off the same environment, in the same ORDER as the fork pool and the sequential path
|
|
500
|
+
# take theirs: it had no producer here either, so an annotation living only in an rbs-inline
|
|
501
|
+
# comment lost its row under this backend alone. That read is only as good as the environment's
|
|
502
|
+
# virtual RBS, which is why the prewarm is built the way the workers build theirs (see
|
|
503
|
+
# {#prewarm_rbs_cache_for_pool}). The conformance scan inside the signature-state snapshot
|
|
504
|
+
# demands the definition of every `rigor:v1:conforms-to` class — a demand no Ractor worker
|
|
505
|
+
# shares memory with — so a resulting definition-build failure is recorded explicitly right
|
|
506
|
+
# after, exactly as the empty-closure branch reads its own environment's demand (#788). No
|
|
507
|
+
# matching explicit call for the HKT-scan outcome: every worker already demands it from its OWN
|
|
508
|
+
# environment in `#drain_reporters`, and the scan has one outcome whoever demands it, so a
|
|
509
|
+
# worker's report already says what the coordinator's own demand would.
|
|
510
|
+
warm_env = prewarm_rbs_cache_for_pool(source_files: source_files)
|
|
511
|
+
snapshot_project_signature_state(warm_env)
|
|
512
|
+
snapshot_effect_annotation_carrier(warm_env&.rbs_loader)
|
|
513
|
+
record_definition_build_failures(warm_env&.rbs_loader&.definition_build_failures)
|
|
321
514
|
|
|
322
515
|
configuration = @configuration
|
|
323
516
|
cache_root = @cache_store&.root
|
|
@@ -325,8 +518,9 @@ module Rigor
|
|
|
325
518
|
explain = @explain
|
|
326
519
|
# ADR-32 WD4 — the full project file list travels into every Ractor worker so each worker's
|
|
327
520
|
# WorkerSession can invoke loaded plugins' source_rbs_synthesizers at env-build time. The list is
|
|
328
|
-
# a frozen Array<String>; cheaply shareable.
|
|
329
|
-
|
|
521
|
+
# a frozen Array<String>; cheaply shareable. Issue #793 — it IS the full project now
|
|
522
|
+
# (`source_files`), not the analyzed subset this comment always described.
|
|
523
|
+
shareable_source_files = source_files.map { |path| path.to_s.dup.freeze }.freeze
|
|
330
524
|
|
|
331
525
|
pool = Array.new(@workers) do
|
|
332
526
|
Ractor.new(configuration, cache_root, blueprints, explain, shareable_source_files) do |configuration, cache_root, blueprints, explain, shareable_source_files| # rubocop:disable Layout/LineLength
|
|
@@ -397,15 +591,28 @@ module Rigor
|
|
|
397
591
|
# The files no worker reported — every file of a worker that died, and any file in flight when
|
|
398
592
|
# it did. Re-analysed in process, exactly as the fork backend re-analyses a dead child's slice.
|
|
399
593
|
degraded = files.reject { |path| results_by_path.key?(path) }
|
|
400
|
-
|
|
401
|
-
environment = build_runner_environment(source_files: files)
|
|
402
|
-
degraded.each { |path| results_by_path[path] = @analyze_file.call(path, environment) }
|
|
403
|
-
end
|
|
594
|
+
reanalyze_degraded_in_process(degraded, results_by_path, source_files: source_files)
|
|
404
595
|
|
|
405
596
|
diagnostics = Array(prepare_diagnostics) + files.flat_map { |path| results_by_path.fetch(path, []) }
|
|
406
597
|
degraded.empty? ? diagnostics : diagnostics.unshift(pool_degraded_diagnostic(degraded.size, "ractor"))
|
|
407
598
|
end
|
|
408
599
|
|
|
600
|
+
# The Ractor backend's degrade: the files of a worker that died are re-analysed on a LOCAL
|
|
601
|
+
# environment built over the whole project. That worker never sent `:done`, so nothing drains its
|
|
602
|
+
# reporters — and the local environment has no session to drain either — so the run-level state
|
|
603
|
+
# this analysis produced is taken here, exactly as the sequential fallback takes it: the
|
|
604
|
+
# definition-build failures the re-analysis demanded (#696) and the HKT-scan outcome, demanded
|
|
605
|
+
# once more by the run itself (#784 — a rescued scan failure otherwise vanished with the worker).
|
|
606
|
+
# The fork backend needs none of this: it re-analyses on the parent {WorkerSession} and drains it.
|
|
607
|
+
def reanalyze_degraded_in_process(degraded, results_by_path, source_files:)
|
|
608
|
+
return if degraded.empty?
|
|
609
|
+
|
|
610
|
+
environment = build_runner_environment(source_files: source_files)
|
|
611
|
+
degraded.each { |path| results_by_path[path] = @analyze_file.call(path, environment) }
|
|
612
|
+
record_definition_build_failures(environment.rbs_loader&.definition_build_failures)
|
|
613
|
+
record_hkt_scan_failure(hkt_scan_outcome(environment))
|
|
614
|
+
end
|
|
615
|
+
|
|
409
616
|
# ADR-15 Amendment (2026-05-20) — fork-based worker pool, the active backend for `workers > 0`.
|
|
410
617
|
# Builds ONE {WorkerSession} on the parent, then `fork`s N children that copy-on-write inherit it.
|
|
411
618
|
# Each child analyses a contiguous slice of `files` and writes a Marshal'd `{results:, reporters:}`
|
|
@@ -418,7 +625,11 @@ module Rigor
|
|
|
418
625
|
#
|
|
419
626
|
# A child that exits non-zero (crash / unmarshalable payload) is degraded: the parent re-analyses
|
|
420
627
|
# that slice in-process and prepends a `pool-degraded` warning.
|
|
421
|
-
|
|
628
|
+
#
|
|
629
|
+
# Also snapshots the project-signature state and the effect-annotation carrier off the pre-fork
|
|
630
|
+
# `session.environment` before any child spawns (#798) — the only environment this backend's
|
|
631
|
+
# coordinator ever holds, and so the only place those diagnostic rows can be read from.
|
|
632
|
+
def analyze_files_in_fork_pool(files, source_files: files) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
422
633
|
Environment::ClassRegistry.default
|
|
423
634
|
|
|
424
635
|
session = WorkerSession.new(
|
|
@@ -429,12 +640,27 @@ module Rigor
|
|
|
429
640
|
synthetic_method_index: synthetic_method_index,
|
|
430
641
|
project_patched_methods: project_patched_methods,
|
|
431
642
|
project_scope_seed: project_scope_seed,
|
|
432
|
-
source_files:
|
|
643
|
+
source_files: source_files,
|
|
433
644
|
record_dependencies: @record_dependencies
|
|
434
645
|
)
|
|
435
646
|
# Force the full RBS load on the parent so children copy-on-write inherit a warm Environment
|
|
436
647
|
# rather than each rebuilding it after the fork.
|
|
437
648
|
session.environment.rbs_loader&.prewarm
|
|
649
|
+
# Issue #798 — same set, same ORDER as the sequential path (docs/type-specification/
|
|
650
|
+
# diagnostic-policy.md § `rbs.coverage.*`): the project-signature state
|
|
651
|
+
# (`synthesized-namespace`, `quarantined-signature`, `environment-build-failed`, the
|
|
652
|
+
# conformance results) has no per-file producer, so this coordinator-side environment is the
|
|
653
|
+
# only place a pooled run can take it from — exactly as the empty-closure branch above is the
|
|
654
|
+
# only place an empty run can (#788). Taken BEFORE the fork below: the conformance scan this
|
|
655
|
+
# runs demands the definition of every `rigor:v1:conforms-to` class, and doing that HERE means
|
|
656
|
+
# a resulting definition-build failure is already sitting in the parent's loader at the moment
|
|
657
|
+
# each child copy-on-write-inherits it — every child's own `#drain_reporters` then reports it
|
|
658
|
+
# right alongside whatever its own slice demanded, with no separate plumbing needed. Left
|
|
659
|
+
# unconditional, unlike `#snapshot_fork_pool_stats` below: these are diagnostic rows, not
|
|
660
|
+
# `RunStats` telemetry, and the stats gate must not decide which diagnostics a run reports (a
|
|
661
|
+
# `--workers N --no-stats` run used to say strictly LESS than the sequential path over the
|
|
662
|
+
# same project).
|
|
663
|
+
snapshot_project_signature_state(session.environment)
|
|
438
664
|
snapshot_effect_annotation_carrier(session.environment.rbs_loader)
|
|
439
665
|
snapshot_fork_pool_stats(session) if @collect_stats
|
|
440
666
|
|
|
@@ -488,18 +714,16 @@ module Rigor
|
|
|
488
714
|
exit!(1)
|
|
489
715
|
end
|
|
490
716
|
|
|
491
|
-
#
|
|
492
|
-
#
|
|
717
|
+
# Issue #798 — `RunStats` telemetry ONLY, now that `#snapshot_project_signature_state` (called
|
|
718
|
+
# unconditionally above) owns every diagnostic-bearing slot this method used to ALSO write
|
|
719
|
+
# (`quarantined_signatures`, `env_build_failure`): those must not be gated on `@collect_stats`, and
|
|
720
|
+
# this method's own name says what is left — `class_decl_paths` / `signature_paths`, read off the
|
|
721
|
+
# parent session's loader so end-of-run {RunStats} can attribute the RBS class universe, which a
|
|
722
|
+
# `--no-stats` run legitimately skips.
|
|
493
723
|
def snapshot_fork_pool_stats(session)
|
|
494
724
|
loader = session.environment.rbs_loader
|
|
495
725
|
@snapshots.class_decl_paths = loader&.class_decl_paths || {}.freeze
|
|
496
726
|
@snapshots.signature_paths = loader&.signature_paths || [].freeze
|
|
497
|
-
# The workers each quarantine the same broken file, but they report no diagnostics for it — the row is
|
|
498
|
-
# a whole-run one. Read it off the parent session's loader so a pooled run says exactly what a
|
|
499
|
-
# sequential one says. The same reasoning holds for a total env-build failure.
|
|
500
|
-
@snapshots.quarantined_signatures =
|
|
501
|
-
project_signature_paths? ? (loader&.quarantined_signatures || []) : []
|
|
502
|
-
@snapshots.env_build_failure = project_signature_paths? ? loader&.env_build_failure : nil
|
|
503
727
|
end
|
|
504
728
|
|
|
505
729
|
# Waits for every forked child, merges each successful payload into `results_by_path`, and returns
|
|
@@ -521,7 +745,7 @@ module Rigor
|
|
|
521
745
|
degraded
|
|
522
746
|
end
|
|
523
747
|
|
|
524
|
-
# @return
|
|
748
|
+
# @return the child's `{results:, reporters:}` payload, or nil when the child exited
|
|
525
749
|
# abnormally or wrote no readable payload. `Marshal.load` is safe here: the blob was written by
|
|
526
750
|
# our own forked child to a temp file we created.
|
|
527
751
|
def fork_worker_payload(status, out_path)
|
|
@@ -545,29 +769,57 @@ module Rigor
|
|
|
545
769
|
|
|
546
770
|
# ADR-15 Phase 4b.x — drives every cached RBS producer on the main Ractor so each worker can serve
|
|
547
771
|
# all reflection queries from disk (Marshal-load only). Builds a single coordinator-side
|
|
548
|
-
# {Environment} for this purpose
|
|
549
|
-
#
|
|
550
|
-
#
|
|
551
|
-
|
|
772
|
+
# {Environment} for this purpose and returns it fully loaded — issue #798: the caller also reads
|
|
773
|
+
# the project-signature state and the effect-annotation carrier off it, since it is the only
|
|
774
|
+
# environment this backend's coordinator ever holds. Workers still build their OWN
|
|
775
|
+
# `Environment.for_project` inside the Ractor body, which then routes through `cached_env` instead
|
|
776
|
+
# of `RBS::EnvironmentLoader.new`.
|
|
777
|
+
#
|
|
778
|
+
# Built with the loaded plugin registry over the project's WHOLE file list (#793's rule), exactly
|
|
779
|
+
# as each worker builds its own. `Environment.for_project` collects plugin-synthesized virtual RBS
|
|
780
|
+
# only when handed BOTH (`collect_virtual_rbs` short-circuits on a nil registry and on an empty
|
|
781
|
+
# list), and this used to pass neither: its loader's `virtual_rbs` read empty on every project, so
|
|
782
|
+
# a carrier snapshotted off it would have been `[]` whatever the rbs-inline comments said — the
|
|
783
|
+
# method called, the row still missing. Two more things follow from building the environment the
|
|
784
|
+
# workers build: the env-cache descriptor digests the same signature roots (a plugin's
|
|
785
|
+
# `signature_paths:`) and the same virtual set, so the slot this warms is the slot the workers
|
|
786
|
+
# read; and each synthesizer runs once here and is memoised through the cache store (ADR-32 WD5)
|
|
787
|
+
# instead of once per worker.
|
|
788
|
+
#
|
|
789
|
+
# Deliberately NOT {#build_runner_environment}: that hands the environment the run's own reporter
|
|
790
|
+
# accumulators, and an environment BUILD writes to one of them — `collect_virtual_rbs` records a
|
|
791
|
+
# synthesizer's parse failures and unhonoured annotations on the `source_rbs_synthesis` reporter.
|
|
792
|
+
# Every Ractor worker builds this same environment with reporters of its own and drains them back
|
|
793
|
+
# through {#merge_worker_reporters}, whose replay of that stream appends without dedup, so a
|
|
794
|
+
# coordinator-side reporter here would count each entry once more than the drains already do. The
|
|
795
|
+
# other two are written only by demands this environment never receives (the HKT scan, per-file
|
|
796
|
+
# dispatch); all three are withheld together so the no-double-count holds by construction rather
|
|
797
|
+
# than by which reads happen to be made. The dispatch-only per-run state (the dependency-source
|
|
798
|
+
# index, the synthetic-method / project-patched indexes) is withheld for the same reason: nothing
|
|
799
|
+
# here dispatches.
|
|
800
|
+
# @param source_files — the WHOLE project's file list, never the analyzed subset.
|
|
801
|
+
def prewarm_rbs_cache_for_pool(source_files:)
|
|
552
802
|
warm_env = Environment.for_project(
|
|
553
803
|
libraries: @configuration.libraries,
|
|
554
804
|
signature_paths: @configuration.signature_paths,
|
|
555
805
|
cache_store: @cache_store,
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
rbs_collection_lockfile: @configuration.rbs_collection_lockfile,
|
|
560
|
-
rbs_collection_auto_detect: @configuration.rbs_collection_auto_detect
|
|
806
|
+
plugin_registry: plugin_registry,
|
|
807
|
+
source_files: source_files,
|
|
808
|
+
**ProjectEnvironment.dependency_discovery_options(@configuration)
|
|
561
809
|
)
|
|
562
810
|
warm_env.rbs_loader&.prewarm
|
|
811
|
+
warm_env
|
|
563
812
|
end
|
|
564
813
|
|
|
565
814
|
# ADR-15 Phase 4b.x — pool-mode safety net. When pool mode is configured but a precondition fails
|
|
566
815
|
# (currently: `--no-cache` would force workers through `EnvironmentLoader.new`), degrade to
|
|
567
816
|
# sequential analysis with a `:warning` `pool-degraded` diagnostic at run start. The actual
|
|
568
817
|
# per-file analysis runs on the coordinator, identical to the default sequential path.
|
|
569
|
-
|
|
570
|
-
|
|
818
|
+
# @param source_files — issue #793 — the whole project, so this path's environment
|
|
819
|
+
# carries the same plugin-synthesized RBS the pool workers' would. It used to build over `[]`,
|
|
820
|
+
# i.e. with no synthesized RBS at all, even on a full run.
|
|
821
|
+
def analyze_files_sequentially_fallback(files, reason:, source_files: files)
|
|
822
|
+
environment = build_runner_environment(source_files: source_files)
|
|
571
823
|
snapshot_effect_annotation_carrier(environment.rbs_loader)
|
|
572
824
|
diagnostics = files.flat_map { |path| @analyze_file.call(path, environment) }
|
|
573
825
|
loader = environment.rbs_loader
|
|
@@ -576,8 +828,13 @@ module Rigor
|
|
|
576
828
|
# `fork` is unavailable (Windows) and on `--incremental` / effects runs without it: a run that
|
|
577
829
|
# degraded to sequential must not also report less than a sequential run would.
|
|
578
830
|
record_definition_build_failures(loader&.definition_build_failures)
|
|
831
|
+
# Issue #784 — same reasoning: this path's Environment IS the one that reached the scan, so it
|
|
832
|
+
# must snapshot the slot too, or a run that degraded to sequential would report less than a
|
|
833
|
+
# sequential run would. Demanded once by the run as well, for the reason at the sequential site.
|
|
834
|
+
record_hkt_scan_failure(hkt_scan_outcome(environment))
|
|
579
835
|
@snapshots.class_decl_paths = loader&.class_decl_paths || {}.freeze
|
|
580
836
|
@snapshots.signature_paths = loader&.signature_paths || [].freeze
|
|
837
|
+
@snapshots.signature_standdowns = plugin_signature_paths? ? signature_standdowns_for(loader) : []
|
|
581
838
|
@snapshots.quarantined_signatures =
|
|
582
839
|
project_signature_paths? ? (loader&.quarantined_signatures || []) : []
|
|
583
840
|
@snapshots.env_build_failure = project_signature_paths? ? loader&.env_build_failure : nil
|
|
@@ -591,17 +848,7 @@ module Rigor
|
|
|
591
848
|
end
|
|
592
849
|
|
|
593
850
|
def merge_worker_reporters(drained)
|
|
594
|
-
|
|
595
|
-
rbs.fetch(:unresolved_payloads).each do |entry|
|
|
596
|
-
@rbs_extended_reporter.record_unresolved(
|
|
597
|
-
payload: entry.payload, source_location: entry.source_location
|
|
598
|
-
)
|
|
599
|
-
end
|
|
600
|
-
rbs.fetch(:lossy_projections).each do |entry|
|
|
601
|
-
@rbs_extended_reporter.record_lossy_projection(
|
|
602
|
-
head: entry.head, source_location: entry.source_location
|
|
603
|
-
)
|
|
604
|
-
end
|
|
851
|
+
merge_rbs_extended_reporter(drained.fetch(:rbs_extended))
|
|
605
852
|
drained.fetch(:boundary_cross).each do |entry|
|
|
606
853
|
@boundary_cross_reporter.record(
|
|
607
854
|
class_name: entry.class_name,
|
|
@@ -612,17 +859,67 @@ module Rigor
|
|
|
612
859
|
end
|
|
613
860
|
# ADR-32 WD6 — merge per-worker synthesizer failures back into the coordinator's reporter. Fetched
|
|
614
861
|
# with a default empty array so older drains (pre-slice-2) remain compatible.
|
|
862
|
+
#
|
|
863
|
+
# `kind:` rides along (issue #824): without it every drained entry replayed as WD6's default
|
|
864
|
+
# `:failed`, so under `--workers N` a WD12 "parsed but not honoured" row came back as
|
|
865
|
+
# `source-rbs-synthesis-failed` — a different id, and a message telling the user the file
|
|
866
|
+
# contributed nothing when all but one of its annotations bound. Every worker reads the same
|
|
867
|
+
# sources and so hands over the same entries; the reporter's own dedup collapses them.
|
|
615
868
|
Array(drained[:source_rbs_synthesis]).each do |entry|
|
|
616
869
|
@source_rbs_synthesis_reporter.record(
|
|
617
|
-
plugin_id: entry.plugin_id, path: entry.path, message: entry.message
|
|
870
|
+
plugin_id: entry.plugin_id, path: entry.path, message: entry.message, kind: entry.kind
|
|
618
871
|
)
|
|
619
872
|
end
|
|
620
873
|
# Issue #696. Fetched with a default so an older drain stays compatible, exactly as the line above.
|
|
621
874
|
record_definition_build_failures(drained[:definition_build_failures])
|
|
875
|
+
# Issue #784. `Hash#[]` is already a nil default, exactly as `env_build_failure` is snapshotted
|
|
876
|
+
# elsewhere — an older drain shape simply has no key and records nothing.
|
|
877
|
+
record_hkt_scan_failure(drained[:hkt_scan_failure])
|
|
622
878
|
end
|
|
623
879
|
|
|
624
880
|
private
|
|
625
881
|
|
|
882
|
+
# Replays one worker's three {RbsExtended::Reporter} streams into the run's own reporter.
|
|
883
|
+
#
|
|
884
|
+
# `hkt_directive_errors` (issue #785) is read with `Hash#[]`'s nil default rather than `fetch`,
|
|
885
|
+
# exactly as `source_rbs_synthesis` is, so an older drain shape simply records nothing.
|
|
886
|
+
#
|
|
887
|
+
# All three streams replay by their `(path, line, column)` primitives (#785 for the hkt stream, #805
|
|
888
|
+
# for its two elders). Every worker reads the same `.rbs`, so each hands over the same entries, and
|
|
889
|
+
# only a primitive-keyed dedup collapses them back to the single row a `--workers=0` run prints — an
|
|
890
|
+
# `RBS::Location` compares equal only against a location over the same `RBS::Buffer` object, so a
|
|
891
|
+
# location-keyed one would print N copies at `--workers=N`. The entries could not reach here carrying
|
|
892
|
+
# a location anyway: the drain Marshals its payload and an `RBS::Location` has no `_dump`.
|
|
893
|
+
def merge_rbs_extended_reporter(rbs)
|
|
894
|
+
rbs.fetch(:unresolved_payloads).each do |entry|
|
|
895
|
+
@rbs_extended_reporter.record_unresolved(
|
|
896
|
+
payload: entry.payload, path: entry.path, line: entry.line, column: entry.column
|
|
897
|
+
)
|
|
898
|
+
end
|
|
899
|
+
rbs.fetch(:lossy_projections).each do |entry|
|
|
900
|
+
@rbs_extended_reporter.record_lossy_projection(
|
|
901
|
+
head: entry.head, path: entry.path, line: entry.line, column: entry.column
|
|
902
|
+
)
|
|
903
|
+
end
|
|
904
|
+
Array(rbs[:hkt_directive_errors]).each do |entry|
|
|
905
|
+
@rbs_extended_reporter.record_hkt_error(
|
|
906
|
+
message: entry.message, path: entry.path, line: entry.line, column: entry.column
|
|
907
|
+
)
|
|
908
|
+
end
|
|
909
|
+
replay_deprecated_forms(rbs)
|
|
910
|
+
end
|
|
911
|
+
|
|
912
|
+
# ADR-109 WD3 — the fourth stream, read with the nil default like the hkt one so an older drain
|
|
913
|
+
# shape records nothing.
|
|
914
|
+
def replay_deprecated_forms(rbs)
|
|
915
|
+
Array(rbs[:deprecated_forms]).each do |entry|
|
|
916
|
+
@rbs_extended_reporter.record_deprecated_form(
|
|
917
|
+
payload: entry.payload, replacement: entry.replacement,
|
|
918
|
+
path: entry.path, line: entry.line, column: entry.column
|
|
919
|
+
)
|
|
920
|
+
end
|
|
921
|
+
end
|
|
922
|
+
|
|
626
923
|
# Issue #696 — accumulate the per-class `RBS::DefinitionBuilder` failures this run observed, deduped
|
|
627
924
|
# by class name and kept in first-seen order.
|
|
628
925
|
#
|
|
@@ -646,6 +943,47 @@ module Rigor
|
|
|
646
943
|
(@snapshots.definition_build_failures + failures).uniq(&:first).freeze
|
|
647
944
|
end
|
|
648
945
|
|
|
946
|
+
# Issue #784 — first-wins, unlike {#record_definition_build_failures}'s accumulate-and-dedup. That
|
|
947
|
+
# method accumulates because each pool WORKER owns its own loader and its own per-class memo, so a
|
|
948
|
+
# collapsed class can genuinely be observed by only some workers and the run's set is the union.
|
|
949
|
+
# The HKT scan has no such per-worker variation: `Environment#hkt_registry` builds from the SAME
|
|
950
|
+
# `signature_paths:` overlay every worker was handed, so every worker that demands it either all
|
|
951
|
+
# raise identically or all succeed — there is only ever one tuple to record, and `||=` is correct
|
|
952
|
+
# (and cheap) rather than an accumulate-and-dedup this slot never needs.
|
|
953
|
+
def record_hkt_scan_failure(tuple)
|
|
954
|
+
@snapshots.hkt_scan_failure ||= tuple
|
|
955
|
+
end
|
|
956
|
+
|
|
957
|
+
# Issue #784 — demand the registry once on behalf of the run, then read the slot. The seam in
|
|
958
|
+
# {Environment#hkt_registry} is demand-driven, and nothing guarantees any analysed file demands it:
|
|
959
|
+
# a `--verify-incremental` partition or an incremental closure can miss every `Klass.method` call,
|
|
960
|
+
# and then the slot is nil after the loop and the run says nothing — while `--incremental` has also
|
|
961
|
+
# dropped the row from its per-file cache. Demanding here makes the outcome a property of the RUN,
|
|
962
|
+
# not of which files happened to be in it.
|
|
963
|
+
#
|
|
964
|
+
# This is sound where #696's "a demand that is Rigor's own MUST NOT contribute" is not, and the
|
|
965
|
+
# difference is the shape of what is recorded. #696 reports a per-class LIST whose membership is
|
|
966
|
+
# "the classes the analysis demanded"; a Rigor-internal demand adds classes the user never asked
|
|
967
|
+
# about and makes that list vary with configuration. The HKT scan is ONE build with ONE outcome —
|
|
968
|
+
# the same tuple whoever demands it — so an extra demand cannot change what is reported, only
|
|
969
|
+
# guarantee it is observed.
|
|
970
|
+
#
|
|
971
|
+
# What it costs, stated plainly because {#record_definition_build_failures}'s comment promises
|
|
972
|
+
# "nothing is forced here": the demand DOES force the RBS env build when the environment has not
|
|
973
|
+
# built it yet (the scan reads the loader). On every path that reaches this method that build is
|
|
974
|
+
# either already done (the loop demanded it) or the same load one analysed file would have paid:
|
|
975
|
+
# a Marshal load when a cache store exists, the parse a full run pays under `--no-cache`. Never a
|
|
976
|
+
# build on a project with no files (see {#analyze_files}). Memoised, so on a reused Environment
|
|
977
|
+
# this is a hash read.
|
|
978
|
+
#
|
|
979
|
+
# @return the recorded tuple, or nil (no environment, or the scan built)
|
|
980
|
+
def hkt_scan_outcome(environment)
|
|
981
|
+
return nil if environment.nil?
|
|
982
|
+
|
|
983
|
+
environment.hkt_registry
|
|
984
|
+
environment.hkt_scan_failure
|
|
985
|
+
end
|
|
986
|
+
|
|
649
987
|
# True when the project declares its own `signature_paths:` (the only place the
|
|
650
988
|
# qualified-name-without-namespace mistake lives).
|
|
651
989
|
def project_signature_paths?
|
|
@@ -653,6 +991,21 @@ module Rigor
|
|
|
653
991
|
!(paths.nil? || paths.empty?)
|
|
654
992
|
end
|
|
655
993
|
|
|
994
|
+
# Issue #610 — see {#snapshot_project_signature_state}. The registry's `signature_paths` is what
|
|
995
|
+
# `Environment.for_project` defers, so this is the loader-free form of "is anything deferred?".
|
|
996
|
+
def plugin_signature_paths?
|
|
997
|
+
registry = plugin_registry
|
|
998
|
+
registry.respond_to?(:signature_paths) && !registry.signature_paths.empty?
|
|
999
|
+
end
|
|
1000
|
+
|
|
1001
|
+
# Empty for a loader with nothing deferred, without touching its env.
|
|
1002
|
+
def signature_standdowns_for(loader)
|
|
1003
|
+
return [] if loader.nil? || !loader.respond_to?(:deferred_signature_paths)
|
|
1004
|
+
return [] if loader.deferred_signature_paths.empty?
|
|
1005
|
+
|
|
1006
|
+
loader.signature_standdowns
|
|
1007
|
+
end
|
|
1008
|
+
|
|
656
1009
|
def plugin_registry
|
|
657
1010
|
@plugin_registry_reader.call
|
|
658
1011
|
end
|