rigortype 0.4.6 → 0.4.8
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 +4 -2
- data/data/core_overlay/bigdecimal.rbs +33 -0
- data/data/core_overlay/resolv.rbs +30 -0
- data/data/gem_overlay/activesupport/core_ext.rbs +45 -13
- data/data/gem_overlay/addressable/addressable.rbs +40 -0
- data/data/vendored_gem_sigs/bundler/bundler.rbs +1 -1
- data/data/vendored_gem_sigs/rubygems/rubygems_extras.rbs +4 -3
- data/docs/handbook/01-getting-started.md +6 -3
- data/docs/handbook/11-sig-gen.md +85 -3
- data/docs/handbook/appendix-phpstan.md +4 -4
- data/docs/install.md +20 -0
- data/docs/manual/01-installation.md +67 -26
- data/docs/manual/02-cli-reference.md +455 -113
- data/docs/manual/03-configuration.md +76 -5
- data/docs/manual/04-diagnostics.md +5 -4
- data/docs/manual/06-baseline.md +6 -4
- data/docs/manual/08-skills.md +11 -0
- data/docs/manual/09-editor-integration.md +21 -2
- data/docs/manual/10-mcp-server.md +38 -2
- data/docs/manual/12-caching.md +4 -3
- data/docs/manual/14-rails-quickstart.md +1 -1
- data/docs/manual/19-effect-labels.md +3 -4
- data/docs/manual/plugins/rigor-actionpack.md +32 -15
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +23 -1
- data/docs/manual/plugins/rigor-sorbet.md +17 -3
- data/lib/rigor/analysis/baseline.rb +24 -27
- data/lib/rigor/analysis/check_rules/always_raises_shield.rb +147 -0
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +5 -6
- data/lib/rigor/analysis/check_rules/call_facts.rb +58 -0
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +12 -4
- data/lib/rigor/analysis/check_rules/duplicate_hash_key_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/inferred_param_guard.rb +85 -13
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +16 -4
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +6 -1
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -8
- data/lib/rigor/analysis/check_rules/plugin_api_surface.rb +195 -0
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +41 -12
- data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +27 -1
- data/lib/rigor/analysis/check_rules/rebinding_block_callees.rb +3 -3
- data/lib/rigor/analysis/check_rules/replaced_singleton_defs.rb +96 -0
- data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
- data/lib/rigor/analysis/check_rules/rule_ids.rb +9 -2
- data/lib/rigor/analysis/check_rules/rule_walk.rb +9 -7
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +3 -4
- data/lib/rigor/analysis/check_rules/special_global_setters.rb +3 -3
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules.rb +720 -141
- data/lib/rigor/analysis/crash_signature.rb +45 -3
- data/lib/rigor/analysis/dependency_recorder.rb +237 -80
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +14 -3
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/index.rb +6 -8
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
- data/lib/rigor/analysis/edit_replay.rb +93 -6
- data/lib/rigor/analysis/fact_store.rb +3 -3
- data/lib/rigor/analysis/incremental.rb +11 -1
- data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +4 -3
- data/lib/rigor/analysis/incremental_run_slot.rb +2 -2
- data/lib/rigor/analysis/incremental_session.rb +306 -53
- data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -3
- data/lib/rigor/analysis/rule_catalog.rb +39 -3
- data/lib/rigor/analysis/run_cache_key.rb +27 -3
- data/lib/rigor/analysis/run_disclosure_credit.rb +26 -6
- data/lib/rigor/analysis/run_stats.rb +2 -2
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -2
- data/lib/rigor/analysis/runner/declaration_position.rb +8 -8
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +264 -112
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +4 -9
- data/lib/rigor/analysis/runner/pool_coordinator.rb +173 -328
- data/lib/rigor/analysis/runner/project_pre_passes.rb +4 -37
- data/lib/rigor/analysis/runner/run_level_facts.rb +304 -0
- data/lib/rigor/analysis/runner/run_snapshots.rb +44 -54
- data/lib/rigor/analysis/runner/worker_payload.rb +36 -0
- data/lib/rigor/analysis/runner.rb +298 -109
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +62 -23
- data/lib/rigor/analysis/template_unit_positions.rb +2 -3
- data/lib/rigor/analysis/template_units.rb +9 -1
- data/lib/rigor/analysis/worker_session.rb +65 -71
- data/lib/rigor/bleeding_edge.rb +22 -10
- data/lib/rigor/builtins/imported_refinements.rb +6 -8
- data/lib/rigor/builtins/predefined_constant_refinements.rb +1 -1
- data/lib/rigor/builtins/regex_refinement.rb +4 -6
- data/lib/rigor/cache/descriptor.rb +6 -5
- data/lib/rigor/cache/file_digest.rb +39 -31
- data/lib/rigor/cache/incremental_snapshot.rb +95 -17
- data/lib/rigor/cache/rbs_cache_producer.rb +2 -2
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +1 -1
- data/lib/rigor/cache/rbs_class_type_param_names.rb +2 -2
- data/lib/rigor/cache/rbs_constant_table.rb +1 -1
- data/lib/rigor/cache/rbs_descriptor.rb +90 -4
- data/lib/rigor/cache/rbs_environment.rb +8 -1
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +2 -2
- data/lib/rigor/cache/rbs_known_class_names.rb +1 -1
- data/lib/rigor/cache/store.rb +11 -10
- data/lib/rigor/ci_detector.rb +1 -1
- data/lib/rigor/cli/annotate_command.rb +24 -19
- data/lib/rigor/cli/baseline_command.rb +160 -57
- data/lib/rigor/cli/catalogue.rb +256 -0
- data/lib/rigor/cli/check_command.rb +112 -104
- data/lib/rigor/cli/check_runner_factory.rb +2 -2
- data/lib/rigor/cli/command.rb +15 -1
- data/lib/rigor/cli/coverage_command.rb +21 -8
- data/lib/rigor/cli/coverage_mutation.rb +5 -7
- data/lib/rigor/cli/coverage_renderer.rb +2 -1
- data/lib/rigor/cli/coverage_scan.rb +3 -5
- data/lib/rigor/cli/diff_command.rb +25 -20
- data/lib/rigor/cli/docs_command.rb +1 -2
- data/lib/rigor/cli/doctor_command.rb +12 -12
- data/lib/rigor/cli/effects_command.rb +50 -40
- data/lib/rigor/cli/effects_diff_renderer.rb +18 -17
- data/lib/rigor/cli/effects_explain_renderer.rb +13 -12
- data/lib/rigor/cli/effects_renderer.rb +2 -1
- data/lib/rigor/cli/effects_snapshot_command.rb +26 -18
- data/lib/rigor/cli/explain_command.rb +18 -8
- data/lib/rigor/cli/fused_protection_renderer.rb +2 -1
- data/lib/rigor/cli/json_document.rb +41 -0
- data/lib/rigor/cli/lens_command.rb +29 -10
- data/lib/rigor/cli/lsp_command.rb +250 -23
- data/lib/rigor/cli/mcp_command.rb +12 -7
- data/lib/rigor/cli/mutation_fork_scan.rb +1 -1
- data/lib/rigor/cli/mutation_protection_renderer.rb +3 -2
- data/lib/rigor/cli/options.rb +40 -1
- data/lib/rigor/cli/plugin_command.rb +93 -46
- data/lib/rigor/cli/plugins_command.rb +17 -18
- data/lib/rigor/cli/plugins_renderer.rb +10 -6
- data/lib/rigor/cli/protection_fork_scan.rb +2 -2
- data/lib/rigor/cli/protection_renderer.rb +2 -1
- data/lib/rigor/cli/show_bleedingedge_command.rb +21 -15
- data/lib/rigor/cli/sig_gen_command.rb +92 -27
- data/lib/rigor/cli/skill_command.rb +32 -3
- data/lib/rigor/cli/skill_deep_probe.rb +1 -1
- data/lib/rigor/cli/skill_describe.rb +2 -1
- data/lib/rigor/cli/trace_command.rb +14 -9
- data/lib/rigor/cli/triage_command.rb +10 -11
- data/lib/rigor/cli/triage_renderer.rb +2 -1
- data/lib/rigor/cli/type_of_command.rb +16 -11
- data/lib/rigor/cli/type_of_project_scope.rb +1 -4
- data/lib/rigor/cli/type_of_renderer.rb +4 -1
- data/lib/rigor/cli/type_scan_command.rb +14 -9
- data/lib/rigor/cli/type_scan_renderer.rb +2 -1
- data/lib/rigor/cli/unused_command.rb +27 -17
- data/lib/rigor/cli/upgrade_command.rb +5 -0
- data/lib/rigor/cli.rb +214 -56
- data/lib/rigor/config_audit.rb +33 -19
- data/lib/rigor/configuration/severity_profile.rb +14 -5
- data/lib/rigor/configuration.rb +104 -35
- data/lib/rigor/effects/collector.rb +53 -21
- data/lib/rigor/effects/definition_context.rb +0 -2
- data/lib/rigor/effects/envelope.rb +1 -1
- data/lib/rigor/effects/framework_units.rb +1 -1
- data/lib/rigor/effects/mutation_classifier.rb +4 -7
- data/lib/rigor/effects/propagator.rb +1 -1
- data/lib/rigor/effects/registry.rb +2 -2
- data/lib/rigor/effects/snapshot.rb +1 -1
- data/lib/rigor/effects/taint_cause.rb +0 -2
- data/lib/rigor/effects/unit_scan.rb +9 -13
- data/lib/rigor/environment/bundle_sig_discovery.rb +64 -33
- data/lib/rigor/environment/class_registry.rb +3 -3
- data/lib/rigor/environment/constant_type_cache_holder.rb +5 -0
- data/lib/rigor/environment/core_root.rb +77 -0
- data/lib/rigor/environment/default_libraries.rb +18 -1
- data/lib/rigor/environment/deferred_signature_plan.rb +381 -0
- data/lib/rigor/environment/gated_signature_guard.rb +1 -1
- data/lib/rigor/environment/gated_signature_plan.rb +5 -2
- data/lib/rigor/environment/hkt_registry_holder.rb +6 -0
- data/lib/rigor/environment/lockfile_resolver.rb +28 -12
- data/lib/rigor/environment/member_consistency/comparator.rb +18 -1
- data/lib/rigor/environment/member_consistency.rb +56 -7
- data/lib/rigor/environment/missing_gem_constant_index.rb +3 -2
- data/lib/rigor/environment/project_path_gems.rb +124 -0
- data/lib/rigor/environment/rbs_collection_discovery.rb +123 -8
- data/lib/rigor/environment/rbs_coverage_report.rb +12 -11
- data/lib/rigor/environment/rbs_hierarchy.rb +12 -15
- data/lib/rigor/environment/rbs_loader.rb +772 -110
- data/lib/rigor/environment/reflection.rb +7 -7
- data/lib/rigor/environment/required_features.rb +0 -3
- data/lib/rigor/environment/ruby_library_roots.rb +1 -1
- data/lib/rigor/environment/vendored_signature_plan.rb +153 -0
- data/lib/rigor/environment.rb +169 -42
- data/lib/rigor/flow_contribution/merger.rb +4 -4
- data/lib/rigor/inference/acceptance.rb +136 -40
- data/lib/rigor/inference/alias_mutation_floor/foreign_locals.rb +117 -0
- data/lib/rigor/inference/alias_mutation_floor/memos.rb +85 -0
- data/lib/rigor/inference/alias_mutation_floor/reach.rb +119 -0
- data/lib/rigor/inference/alias_mutation_floor/site_collector.rb +252 -0
- data/lib/rigor/inference/alias_mutation_floor.rb +339 -0
- data/lib/rigor/inference/anonymous_meta_class.rb +18 -0
- data/lib/rigor/inference/block_auto_splat.rb +1 -1
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/block_run_count.rb +185 -0
- data/lib/rigor/inference/budget_trace.rb +8 -2
- data/lib/rigor/inference/builtins/array_catalog.rb +2 -2
- data/lib/rigor/inference/builtins/exception_catalog.rb +2 -2
- data/lib/rigor/inference/builtins/numeric_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/random_catalog.rb +2 -2
- data/lib/rigor/inference/callee_mutation_chain.rb +88 -77
- data/lib/rigor/inference/callee_write_census.rb +815 -0
- data/lib/rigor/inference/captured_locals.rb +2 -0
- data/lib/rigor/inference/census_foreign_block.rb +354 -0
- data/lib/rigor/inference/clause_widening.rb +1 -1
- data/lib/rigor/inference/closure_escape_analyzer.rb +11 -4
- data/lib/rigor/inference/construction_escape.rb +258 -0
- data/lib/rigor/inference/declaration_walk/traversal.rb +1 -1
- data/lib/rigor/inference/declaration_walk.rb +4 -0
- data/lib/rigor/inference/deep_erasure.rb +39 -7
- data/lib/rigor/inference/def_handle.rb +1 -1
- data/lib/rigor/inference/def_node_resolver.rb +33 -15
- data/lib/rigor/inference/def_return_typer.rb +47 -5
- data/lib/rigor/inference/definer_resolution.rb +19 -9
- data/lib/rigor/inference/definers_index.rb +162 -0
- data/lib/rigor/inference/direct_ivar_writes.rb +300 -0
- data/lib/rigor/inference/element_read_widening.rb +49 -3
- data/lib/rigor/inference/error_info.rb +16 -0
- data/lib/rigor/inference/expression_typer.rb +1559 -320
- data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
- data/lib/rigor/inference/flow_tracer.rb +31 -14
- data/lib/rigor/inference/fork_map.rb +3 -0
- data/lib/rigor/inference/fresh_frame_blocks.rb +52 -1
- data/lib/rigor/inference/global_write_census.rb +81 -5
- data/lib/rigor/inference/global_writer_reach.rb +260 -0
- data/lib/rigor/inference/hkt_body.rb +1 -2
- data/lib/rigor/inference/hkt_body_parser.rb +3 -5
- data/lib/rigor/inference/hkt_reducer.rb +2 -2
- data/lib/rigor/inference/hkt_registry.rb +3 -5
- data/lib/rigor/inference/in_effect_refinements.rb +7 -0
- data/lib/rigor/inference/indexed_narrowing.rb +61 -15
- data/lib/rigor/inference/intervening_ivar_reset.rb +26 -4
- data/lib/rigor/inference/ivar_writer_census.rb +154 -8
- data/lib/rigor/inference/key_presence_guard.rb +51 -24
- data/lib/rigor/inference/last_line/self_evidence.rb +30 -0
- data/lib/rigor/inference/last_line.rb +5 -2
- data/lib/rigor/inference/loop_value_record.rb +66 -14
- data/lib/rigor/inference/match_rebinding/frame.rb +57 -0
- data/lib/rigor/inference/match_rebinding/yielding_match.rb +101 -0
- data/lib/rigor/inference/match_rebinding.rb +28 -5
- data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/argument_distribution.rb +40 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +44 -30
- data/lib/rigor/inference/method_dispatcher/class_keyed_block_params.rb +65 -0
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +3 -3
- data/lib/rigor/inference/method_dispatcher/contested_overloads.rb +89 -0
- data/lib/rigor/inference/method_dispatcher/core_enumerable_receiver.rb +44 -0
- data/lib/rigor/inference/method_dispatcher/declared_constructor.rb +151 -0
- data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/imprecise_argument.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/interface_shape.rb +181 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/no_match_fallback.rb +54 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +51 -27
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +606 -78
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/self_substitute.rb +3 -3
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +9 -7
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +467 -84
- data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +0 -1
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +50 -17
- data/lib/rigor/inference/method_dispatcher/singleton_type_params.rb +104 -0
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -5
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +4 -6
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +123 -25
- data/lib/rigor/inference/method_parameter_binder.rb +136 -56
- data/lib/rigor/inference/module_function_state/spellings.rb +2 -2
- data/lib/rigor/inference/module_function_state.rb +24 -3
- data/lib/rigor/inference/multi_target_binder.rb +2 -2
- data/lib/rigor/inference/mutation_rejoin.rb +240 -4
- data/lib/rigor/inference/mutation_widening.rb +199 -29
- data/lib/rigor/inference/narrowing.rb +238 -50
- data/lib/rigor/inference/operand_effects.rb +43 -2
- data/lib/rigor/inference/optimistic_origin.rb +134 -9
- data/lib/rigor/inference/origin_lookup.rb +1 -1
- data/lib/rigor/inference/out_buffer_mutation.rb +72 -0
- data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
- data/lib/rigor/inference/pre_eval_constants.rb +1 -1
- data/lib/rigor/inference/precision_scanner.rb +1 -1
- data/lib/rigor/inference/project_method_ownership.rb +30 -9
- data/lib/rigor/inference/project_patched_methods.rb +65 -13
- data/lib/rigor/inference/project_patched_scanner.rb +120 -25
- data/lib/rigor/inference/project_patched_visibility.rb +39 -3
- data/lib/rigor/inference/project_yielder.rb +11 -17
- data/lib/rigor/inference/rbs_type_translator.rb +68 -15
- data/lib/rigor/inference/receiver_alias.rb +20 -0
- data/lib/rigor/inference/receiver_writer_call.rb +67 -0
- data/lib/rigor/inference/recursion_rebinding.rb +157 -0
- data/lib/rigor/inference/refined_dispatch/constructed_class.rb +57 -17
- data/lib/rigor/inference/refined_dispatch/own_method.rb +51 -16
- data/lib/rigor/inference/refined_dispatch.rb +13 -0
- data/lib/rigor/inference/reflective_ivar_write.rb +19 -11
- data/lib/rigor/inference/remover_holder_scan.rb +287 -0
- data/lib/rigor/inference/remover_narrowing.rb +224 -0
- data/lib/rigor/inference/rewrite_mutation.rb +53 -2
- data/lib/rigor/inference/scope_indexer/certainty.rb +0 -1
- data/lib/rigor/inference/scope_indexer/class_cvars_collector.rb +13 -6
- data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
- data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +70 -26
- data/lib/rigor/inference/scope_indexer/derived_param_value.rb +112 -0
- data/lib/rigor/inference/scope_indexer/empty_seed.rb +99 -0
- data/lib/rigor/inference/scope_indexer/ivar_contributions.rb +295 -0
- data/lib/rigor/inference/scope_indexer/mixin_accumulator.rb +136 -9
- data/lib/rigor/inference/scope_indexer.rb +2213 -226
- data/lib/rigor/inference/self_call_ivar_writes.rb +103 -12
- data/lib/rigor/inference/self_preserving_block.rb +87 -0
- data/lib/rigor/inference/set_mutation.rb +189 -0
- data/lib/rigor/inference/singleton_hook_decline.rb +42 -8
- data/lib/rigor/inference/singleton_object_constant.rb +32 -2
- data/lib/rigor/inference/statement_evaluator.rb +3268 -358
- data/lib/rigor/inference/string_mutation.rb +14 -0
- data/lib/rigor/inference/struct_fold_safety.rb +4 -6
- data/lib/rigor/inference/synthetic_method_scanner.rb +40 -17
- data/lib/rigor/inference/toplevel_def_census.rb +67 -0
- data/lib/rigor/inference/unknown_store_widening.rb +103 -4
- data/lib/rigor/inference/unthreaded_rebinds.rb +2 -3
- data/lib/rigor/inference/untyped_rejoin.rb +1 -1
- data/lib/rigor/inference/void_tail_summary.rb +29 -9
- data/lib/rigor/language_server/buffer_table.rb +3 -3
- data/lib/rigor/language_server/completion_provider.rb +26 -20
- data/lib/rigor/language_server/config_warning_announcer.rb +28 -0
- data/lib/rigor/language_server/diagnostic_publisher.rb +8 -3
- data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
- data/lib/rigor/language_server/folding_range_provider.rb +1 -2
- data/lib/rigor/language_server/hover_provider.rb +1 -7
- data/lib/rigor/language_server/hover_renderer.rb +6 -8
- data/lib/rigor/language_server/project_context.rb +39 -1
- data/lib/rigor/language_server/publish_batcher.rb +3 -4
- data/lib/rigor/language_server/selection_range_provider.rb +2 -4
- data/lib/rigor/language_server/server.rb +7 -3
- data/lib/rigor/language_server/signature_help_provider.rb +4 -4
- data/lib/rigor/lens/claims/comparison.rb +78 -0
- data/lib/rigor/lens/claims/request.rb +131 -0
- data/lib/rigor/lens/claims/type_reader.rb +216 -0
- data/lib/rigor/lens/claims.rb +208 -0
- data/lib/rigor/lens/contract_reader.rb +5 -0
- data/lib/rigor/lens/document.rb +1 -1
- data/lib/rigor/lens/file_reader.rb +21 -8
- data/lib/rigor/lens/plugins.rb +1 -1
- data/lib/rigor/lens/run.rb +8 -0
- data/lib/rigor/lens.rb +1 -1
- data/lib/rigor/library_probe.rb +68 -0
- data/lib/rigor/mcp/server.rb +73 -2
- data/lib/rigor/mcp.rb +1 -1
- data/lib/rigor/memo_audit.rb +282 -0
- data/lib/rigor/plugin/access_denied_error.rb +1 -2
- data/lib/rigor/plugin/base.rb +15 -2
- data/lib/rigor/plugin/bundled_catalog.rb +1 -12
- data/lib/rigor/plugin/io_boundary.rb +26 -4
- data/lib/rigor/plugin/loader.rb +25 -8
- data/lib/rigor/plugin/macro/heredoc_template.rb +7 -8
- data/lib/rigor/plugin/macro/trait_registry.rb +6 -9
- data/lib/rigor/plugin/manifest.rb +16 -16
- data/lib/rigor/plugin/registry.rb +16 -5
- data/lib/rigor/plugin/services.rb +2 -3
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -0
- data/lib/rigor/plugin/trust_policy.rb +48 -2
- data/lib/rigor/plugin/type_node_resolver.rb +2 -4
- data/lib/rigor/plugin.rb +6 -8
- data/lib/rigor/plugin_gap_advisory.rb +5 -1
- data/lib/rigor/project_environment.rb +16 -2
- data/lib/rigor/protection/analysis_guard.rb +1 -1
- data/lib/rigor/protection/discovery_seed.rb +44 -5
- data/lib/rigor/protection/mutation_cache.rb +4 -1
- data/lib/rigor/protection/mutator.rb +1 -1
- data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
- data/lib/rigor/rbs_extended/hkt_directives.rb +6 -10
- data/lib/rigor/rbs_extended/reporter.rb +27 -3
- data/lib/rigor/rbs_extended.rb +2 -3
- data/lib/rigor/reflection/constant_ancestors.rb +81 -22
- data/lib/rigor/reflection/declaration_collectors.rb +55 -0
- data/lib/rigor/reflection/declarations.rb +58 -0
- data/lib/rigor/reflection/project_method.rb +177 -0
- data/lib/rigor/reflection.rb +2 -0
- data/lib/rigor/rooted_glob.rb +32 -1
- data/lib/rigor/run_scoped_slots.rb +38 -0
- data/lib/rigor/runtime/jit.rb +1 -1
- data/lib/rigor/scope/discovery_index.rb +168 -8
- data/lib/rigor/scope/resolution_chain/relevance.rb +14 -1
- data/lib/rigor/scope/resolution_chain.rb +901 -54
- data/lib/rigor/scope.rb +644 -37
- data/lib/rigor/sig_gen/bot_proposal_guard.rb +306 -0
- data/lib/rigor/sig_gen/classification.rb +18 -1
- data/lib/rigor/sig_gen/effect_annotation.rb +3 -3
- data/lib/rigor/sig_gen/generator.rb +588 -66
- data/lib/rigor/sig_gen/generic_header_scan.rb +189 -0
- data/lib/rigor/sig_gen/inline_declarations.rb +2 -2
- data/lib/rigor/sig_gen/layout_index.rb +3 -3
- data/lib/rigor/sig_gen/method_candidate.rb +37 -4
- data/lib/rigor/sig_gen/observation_collector.rb +14 -7
- data/lib/rigor/sig_gen/observed_call.rb +1 -1
- data/lib/rigor/sig_gen/path_mapper.rb +6 -0
- data/lib/rigor/sig_gen/platform_guarded_files.rb +184 -0
- data/lib/rigor/sig_gen/renderer.rb +15 -9
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +62 -2
- data/lib/rigor/sig_gen/type_name_qualification.rb +69 -0
- data/lib/rigor/sig_gen/write_reporter.rb +63 -14
- data/lib/rigor/sig_gen/write_result.rb +23 -12
- data/lib/rigor/sig_gen/writer.rb +238 -37
- data/lib/rigor/sig_gen.rb +1 -0
- data/lib/rigor/source/forwardable_delegators.rb +122 -0
- data/lib/rigor/source/node_children.rb +13 -0
- data/lib/rigor/type/acceptance_router.rb +2 -2
- data/lib/rigor/type/accepts_result.rb +1 -1
- data/lib/rigor/type/app.rb +4 -6
- data/lib/rigor/type/combinator.rb +173 -21
- data/lib/rigor/type/constant.rb +4 -5
- data/lib/rigor/type/hash_shape.rb +21 -40
- data/lib/rigor/type/interface.rb +60 -0
- data/lib/rigor/type/union.rb +1 -1
- data/lib/rigor/type.rb +1 -0
- data/lib/rigor/type_node/identifier.rb +1 -2
- data/lib/rigor/type_node/resolver_chain.rb +1 -1
- data/lib/rigor/type_node/string_literal.rb +1 -2
- data/lib/rigor/type_node/symbol_literal.rb +1 -2
- data/lib/rigor/type_node/union.rb +3 -6
- data/lib/rigor/type_of_memo/serving.rb +99 -0
- data/lib/rigor/type_of_memo/stats.rb +43 -0
- data/lib/rigor/type_of_memo.rb +337 -0
- data/lib/rigor/typing_state.rb +1094 -0
- data/lib/rigor/value_semantics.rb +1 -10
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +2 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +2 -3
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -1
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +38 -36
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +4 -2
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +6 -4
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -5
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +2 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +3 -3
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +115 -36
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +3 -6
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +25 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +16 -6
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +4 -2
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +4 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +2 -1
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/association_roots.rb +3 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +2 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +21 -31
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +10 -16
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +132 -33
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +20 -6
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +7 -4
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +54 -17
- data/plugins/rigor-activerecord/sig/active_record/relation.rbs +19 -5
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +3 -4
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +1 -1
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +11 -14
- data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/symbolize_keys.rb +102 -0
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +30 -9
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +55 -13
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_collector.rb +15 -21
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_discoverer.rb +1 -6
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +3 -3
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +1 -3
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +6 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +5 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +1 -1
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/params_shape.rb +2 -3
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +5 -8
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +1 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +10 -18
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +0 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +0 -2
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +8 -7
- data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +0 -2
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +6 -7
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +12 -2
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +2 -4
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +2 -5
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +8 -2
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +6 -6
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +2 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +3 -8
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +2 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +5 -8
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +200 -4
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +1 -1
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +4 -11
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +4 -3
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +2 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +5 -5
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +2 -2
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +3 -4
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +6 -8
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +3 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +4 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +6 -7
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +210 -3
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +146 -7
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +2 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +52 -34
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +97 -24
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +2 -3
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_collector.rb +16 -16
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +4 -8
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +4 -4
- data/sig/rigor/analysis/baseline.rbs +0 -3
- data/sig/rigor/analysis/check_rules/rule_walk.rbs +6 -0
- data/sig/rigor/analysis/fact_store.rbs +2 -2
- data/sig/rigor/analysis/reachability/scan_cache.rbs +1 -1
- data/sig/rigor/cache/descriptor.rbs +14 -0
- data/sig/rigor/cli/diff_command.rbs +1 -1
- data/sig/rigor/cli/explain_command.rbs +1 -1
- data/sig/rigor/effects/registry.rbs +2 -2
- data/sig/rigor/environment.rbs +4 -1
- data/sig/rigor/inference.rbs +4 -1
- data/sig/rigor/plugin/additional_initializer.rbs +0 -5
- data/sig/rigor/plugin/base.rbs +53 -18
- data/sig/rigor/plugin/effect_attribution.rbs +3 -3
- data/sig/rigor/plugin/effect_edge.rbs +1 -1
- data/sig/rigor/plugin/effect_entry_points.rbs +3 -3
- data/sig/rigor/plugin/interfaces.rbs +51 -0
- data/sig/rigor/plugin/io_boundary.rbs +3 -3
- data/sig/rigor/plugin/manifest.rbs +9 -9
- data/sig/rigor/plugin/node_context.rbs +21 -0
- data/sig/rigor/plugin/protocol_contract.rbs +3 -7
- data/sig/rigor/reflection.rbs +37 -0
- data/sig/rigor/scope.rbs +43 -4
- data/sig/rigor/trinary.rbs +1 -0
- data/sig/rigor/type.rbs +9 -3
- data/sig/rigor.rbs +3 -2
- data/skills/rigor-ask/SKILL.md +1 -0
- data/skills/rigor-plugin-author/SKILL.md +4 -4
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +5 -5
- data/skills/rigor-plugin-review/SKILL.md +3 -3
- data/skills/rigor-project-init/references/01-detect.md +1 -0
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +17 -2
- data/skills/rigor-project-init/references/06-agent-contract.md +4 -4
- data/skills/rigor-rbs-contract/SKILL.md +147 -0
- data/skills/rigor-rbs-contract/references/01-finding-candidates.md +212 -0
- data/skills/rigor-rbs-contract/references/02-contract-rules.md +157 -0
- data/skills/rigor-rbs-contract/references/03-target-own-code.md +90 -0
- data/skills/rigor-rbs-contract/references/04-target-gem-rbs-collection.md +210 -0
- data/skills/rigor-rbs-contract/references/05-declaration-mutants.md +182 -0
- data/skills/rigor-rbs-setup/SKILL.md +3 -1
- data/skills/rigor-type-oracle/SKILL.md +26 -1
- data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +4 -3
- metadata +82 -3
|
@@ -10,7 +10,10 @@ require_relative "../run_stats"
|
|
|
10
10
|
require_relative "../../effects/signature_sources"
|
|
11
11
|
require_relative "../../rbs_extended/conformance_checker"
|
|
12
12
|
require_relative "../../runtime/jit"
|
|
13
|
+
require_relative "../../type_of_memo"
|
|
13
14
|
require_relative "../../cache/engine_source"
|
|
15
|
+
require_relative "run_level_facts"
|
|
16
|
+
require_relative "worker_payload"
|
|
14
17
|
|
|
15
18
|
module Rigor
|
|
16
19
|
module Analysis
|
|
@@ -21,7 +24,7 @@ module Rigor
|
|
|
21
24
|
# tables into the shared {RunSnapshots}, and merges per-worker reporter drains back into the runner's
|
|
22
25
|
# reporter accumulators.
|
|
23
26
|
#
|
|
24
|
-
# Sequential-equivalence contract (docs/internal-spec/
|
|
27
|
+
# Sequential-equivalence contract (docs/internal-spec/worker-session.md): the pool path MUST produce
|
|
25
28
|
# the same diagnostics, in the same order, as the sequential path. The coordinator re-orders worker
|
|
26
29
|
# results by original path order and replays per-worker reporter entries through the dedupe-on-record
|
|
27
30
|
# APIs so reporter state matches a single-session run.
|
|
@@ -169,6 +172,21 @@ module Rigor
|
|
|
169
172
|
end
|
|
170
173
|
end
|
|
171
174
|
|
|
175
|
+
# Issue #2392 — clears every table a run accumulates on the coordinator: the dependency, analysis-read,
|
|
176
|
+
# analysis-disclosure and effect graphs, and the run disclosures and trust refusals. {Runner#run_analysis}
|
|
177
|
+
# calls it once at the top of each run, beside `@snapshots.reset_for_run`, so a long-lived Runner's next
|
|
178
|
+
# run starts as a fresh Runner's would. Never called between the passes of one run, which must see each
|
|
179
|
+
# other's collections. Configuration-derived state (`@record_dependencies`, `@record_effects`, the readers,
|
|
180
|
+
# the reporters) is per-coordinator and is not touched.
|
|
181
|
+
def reset_for_run
|
|
182
|
+
@collected_dependencies = {}
|
|
183
|
+
@collected_analysis_reads = {}
|
|
184
|
+
@collected_analysis_disclosures = {}
|
|
185
|
+
@collected_effects = {}
|
|
186
|
+
@collected_run_disclosures = {}
|
|
187
|
+
@collected_trust_refusals = {}
|
|
188
|
+
end
|
|
189
|
+
|
|
172
190
|
# ADR-15 Phase 4b — pool mode is enabled when `@workers > 0`. Editor mode (`buffer:` non-nil)
|
|
173
191
|
# silently overrides pool mode to sequential: per design § "Ractor pool mode", the pool's warm-up
|
|
174
192
|
# cost dominates one-file wall time, so the pool gains nothing on a per-buffer invocation. The
|
|
@@ -181,6 +199,18 @@ module Rigor
|
|
|
181
199
|
@buffer.nil?
|
|
182
200
|
end
|
|
183
201
|
|
|
202
|
+
# ADR-130 WD2/WD5 — `RbsTypeTranslator.with_interface_erasure` and `RbsDispatch.without_interface_dispatch`
|
|
203
|
+
# are per-thread switches, which a Ractor worker would not see: a pooled run under either would analyse with
|
|
204
|
+
# the default engine and pass as the erasure baseline or the dispatch-off mode.
|
|
205
|
+
def refuse_pool_under_interface_switches
|
|
206
|
+
if Rigor::Inference::RbsTypeTranslator.interface_erasure?
|
|
207
|
+
raise ArgumentError, "RbsTypeTranslator.with_interface_erasure supports only a sequential run (workers: 0)"
|
|
208
|
+
end
|
|
209
|
+
return if Rigor::Inference::MethodDispatcher::RbsDispatch.interface_dispatch?
|
|
210
|
+
|
|
211
|
+
raise ArgumentError, "RbsDispatch.without_interface_dispatch supports only a sequential run (workers: 0)"
|
|
212
|
+
end
|
|
213
|
+
|
|
184
214
|
# ADR-15 Phase 4b — routes per-file analysis to either the sequential coordinator-side Environment
|
|
185
215
|
# (legacy path, default) or a Ractor worker pool built around {WorkerSession} (opt-in via
|
|
186
216
|
# `workers:`). The sequential path is bit-for-bit unchanged from v0.1.4 / earlier; the pool path is
|
|
@@ -208,6 +238,7 @@ module Rigor
|
|
|
208
238
|
source_files = project_files || files
|
|
209
239
|
result =
|
|
210
240
|
if pool_mode?
|
|
241
|
+
refuse_pool_under_interface_switches
|
|
211
242
|
dispatch_pool(files, source_files: source_files)
|
|
212
243
|
else
|
|
213
244
|
environment ||= resolve_sequential_environment(source_files: source_files)
|
|
@@ -271,16 +302,21 @@ module Rigor
|
|
|
271
302
|
|
|
272
303
|
env = resolve_sequential_environment(source_files: project_files)
|
|
273
304
|
end
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
305
|
+
# Each gate resolves its environment only when its first fact is read, so the carrier's `in_hand:`
|
|
306
|
+
# sees an environment the signature state just built.
|
|
307
|
+
harvest_environment_facts do |gate|
|
|
308
|
+
if gate == :annotation_carrier
|
|
309
|
+
annotation_carrier_environment(resolve, in_hand: !env.nil?)
|
|
310
|
+
else
|
|
311
|
+
signature_state_environment(resolve, project_files)
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
# `env` rather than `resolve.call`: read the demand the harvest above just made when it built an
|
|
315
|
+
# environment, and force nothing when it did not.
|
|
316
|
+
@snapshots.absorb(RunLevelFacts.drain(env, demanding: false))
|
|
281
317
|
# An empty closure analyses no file, so only a deleted file's buffer can make a tuple stale here.
|
|
282
318
|
replayed = replayed_restored_run_level_rows?(closure: [], redemand_env: resolve)
|
|
283
|
-
|
|
319
|
+
@snapshots.absorb(RunLevelFacts.drain(resolve.call, demanding: true)) unless replayed
|
|
284
320
|
prepare_rows
|
|
285
321
|
end
|
|
286
322
|
|
|
@@ -295,15 +331,15 @@ module Rigor
|
|
|
295
331
|
|
|
296
332
|
# Issue #794 — the signature-state snapshot's own gates, asked BEFORE an environment exists. Every
|
|
297
333
|
# answer is loader-free (the configuration, the plugin registry and, issue #1713, the required-feature
|
|
298
|
-
# scan), and with no gate open
|
|
299
|
-
# environment gives
|
|
334
|
+
# scan), and with no gate open the `:signature_state` collectors write the same empty state a nil
|
|
335
|
+
# environment gives them.
|
|
300
336
|
def signature_state_environment(resolve, project_files)
|
|
301
337
|
gated = plugin_signature_paths? || project_signature_paths? || vendored_features_requested?(project_files)
|
|
302
338
|
gated ? resolve.call : nil
|
|
303
339
|
end
|
|
304
340
|
|
|
305
341
|
# Issue #1713 — whether this run activates a required-feature-gated vendored directory, whose stand-down
|
|
306
|
-
# {
|
|
342
|
+
# {RunLevelFacts::Collect.vendored_standdowns} reports: the scan every build of this run gates on, or the
|
|
307
343
|
# feature named under `libraries:`.
|
|
308
344
|
def vendored_features_requested?(project_files)
|
|
309
345
|
features = project_files.nil? || project_files.empty? ? [] : required_features_for(project_files)
|
|
@@ -331,25 +367,28 @@ module Rigor
|
|
|
331
367
|
end
|
|
332
368
|
|
|
333
369
|
# Issues #796 / #794 — fold the previous full run's run-level rows into this narrowed run's
|
|
334
|
-
# snapshots. The definition-build set is UNIONed (
|
|
370
|
+
# snapshots. The definition-build set is UNIONed ({RunLevelFacts::Merge.union_by_class} dedupes by
|
|
335
371
|
# class), so a class this run demanded for itself and the replayed set agree; the HKT outcome is a
|
|
336
|
-
# single tuple whoever demanded it, so
|
|
372
|
+
# single tuple whoever demanded it, so first-wins keeps this run's own answer when it has one. Returns
|
|
337
373
|
# true when a replay was available, which is what tells {#serve_empty_closure} it owes no HKT demand
|
|
338
374
|
# of its own — and so no environment.
|
|
339
375
|
#
|
|
340
376
|
# @param closure — the files this run analyses; see {#stale_replayed_failure?}.
|
|
341
377
|
# @param redemand_env — resolves the environment {#redemand_stale_failures} re-derives a stale
|
|
342
378
|
# failure in; nil (the empty closure, which has no stale failure) re-derives nothing.
|
|
379
|
+
#
|
|
380
|
+
# Every replayable fact ({RunLevelFacts::Fact#replay}) is absorbed by its own merge rule; the
|
|
381
|
+
# definition-build set alone is filtered for staleness first.
|
|
343
382
|
def replayed_restored_run_level_rows?(closure: nil, redemand_env: nil)
|
|
344
383
|
rows = @restored_run_level_rows
|
|
345
384
|
return false if rows.nil?
|
|
346
385
|
|
|
347
|
-
|
|
386
|
+
replayed = rows.to_h
|
|
387
|
+
stale, replayable = Array(replayed[:definition_build_failures]).partition do |failure|
|
|
348
388
|
stale_replayed_failure?(failure, closure)
|
|
349
389
|
end
|
|
350
|
-
|
|
390
|
+
@snapshots.absorb(replayed.merge(definition_build_failures: replayable))
|
|
351
391
|
redemand_stale_failures(stale, redemand_env)
|
|
352
|
-
record_hkt_scan_failure(rows.hkt_scan_failure)
|
|
353
392
|
true
|
|
354
393
|
end
|
|
355
394
|
|
|
@@ -375,7 +414,7 @@ module Rigor
|
|
|
375
414
|
loader = redemand_env.call&.rbs_loader
|
|
376
415
|
return if loader.nil?
|
|
377
416
|
|
|
378
|
-
|
|
417
|
+
@snapshots.absorb(definition_build_failures: pending.filter_map { |name| loader.redemand_definition(name) })
|
|
379
418
|
end
|
|
380
419
|
|
|
381
420
|
# A replayed definition-build failure the snapshot's own fingerprint cannot vouch for. The
|
|
@@ -419,92 +458,47 @@ module Rigor
|
|
|
419
458
|
end
|
|
420
459
|
|
|
421
460
|
def analyze_files_sequentially(files, environment)
|
|
422
|
-
|
|
423
|
-
snapshot_effect_annotation_carrier(environment&.rbs_loader)
|
|
461
|
+
harvest_environment_facts { environment }
|
|
424
462
|
result = files.flat_map { |path| @analyze_file.call(path, environment) }
|
|
425
|
-
#
|
|
426
|
-
# first demand)
|
|
427
|
-
#
|
|
428
|
-
|
|
429
|
-
#
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
# run (`--verify-incremental`'s partition, an incremental recheck's closure) may contain no file
|
|
433
|
-
# that demands it — the row must not depend on which files were analysed. See {#hkt_scan_outcome}
|
|
434
|
-
# for why a Rigor-owned demand is sound here where #696 forbids it.
|
|
435
|
-
record_hkt_scan_failure(hkt_scan_outcome(environment))
|
|
436
|
-
if @collect_stats
|
|
437
|
-
loader = environment.rbs_loader
|
|
438
|
-
@snapshots.class_decl_paths = loader&.class_decl_paths || {}.freeze
|
|
439
|
-
@snapshots.signature_paths = loader&.signature_paths || [].freeze
|
|
440
|
-
end
|
|
463
|
+
# Issues #696 / #784 — AFTER the loop, deliberately. Definition builds are lazy (ADR-54 WD1: per
|
|
464
|
+
# class, on first demand) and the HKT scan is first demanded from inside a file's analysis, so a
|
|
465
|
+
# read beside the harvest above would find nothing on every run. The HKT registry is demanded here
|
|
466
|
+
# once more by the run itself, because a subset run may contain no file that demands it (see
|
|
467
|
+
# {RunLevelFacts::Collect.hkt_scan_failure} for why a Rigor-owned demand is sound there).
|
|
468
|
+
@snapshots.absorb(RunLevelFacts.drain(environment))
|
|
469
|
+
harvest_stats { environment }
|
|
441
470
|
result
|
|
442
471
|
end
|
|
443
472
|
|
|
444
|
-
#
|
|
445
|
-
#
|
|
446
|
-
#
|
|
447
|
-
#
|
|
448
|
-
#
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
# them here is free.
|
|
453
|
-
def snapshot_project_signature_state(environment)
|
|
454
|
-
# Issue #610 — NOT behind the `signature_paths:` gate below: the source a bundled plugin's `sig/`
|
|
455
|
-
# stands down against is typically an `rbs collection install` the configuration never lists
|
|
456
|
-
# there. Gated instead on a loaded plugin contributing signatures at all — the registry answers
|
|
457
|
-
# that without touching the env, and it is exactly the set the loader defers — so a project with
|
|
458
|
-
# no such plugin reads nothing off its loader here, as before.
|
|
459
|
-
@snapshots.signature_standdowns =
|
|
460
|
-
plugin_signature_paths? ? signature_standdowns_for(environment&.rbs_loader) : []
|
|
461
|
-
# Issue #1713 — NOT behind the `signature_paths:` gate either: what displaces a vendored declaration may
|
|
462
|
-
# be a gem's or a collection's copy. The loader answers without building its env when no gated
|
|
463
|
-
# directory is active.
|
|
464
|
-
@snapshots.vendored_standdowns = vendored_standdowns_for(environment&.rbs_loader)
|
|
465
|
-
unless project_signature_paths?
|
|
466
|
-
@snapshots.synthesized_namespaces = []
|
|
467
|
-
@snapshots.quarantined_signatures = []
|
|
468
|
-
@snapshots.conformance_results = []
|
|
469
|
-
@snapshots.env_build_failure = nil
|
|
470
|
-
return
|
|
471
|
-
end
|
|
473
|
+
# ADR-116 WD3 — the `:environment` run-level facts ({RunLevelFacts}), in declaration order, off the
|
|
474
|
+
# environment the block yields for each fact's gate: the project-signature state (#798 — every path
|
|
475
|
+
# takes it, pooled or not, because no per-file analysis produces it) and the #441 effect-annotation
|
|
476
|
+
# carrier. Snapshotted as small plain data (NOT the env) so the environment stays a LOCAL that goes
|
|
477
|
+
# GC-eligible when its path returns.
|
|
478
|
+
def harvest_environment_facts(&)
|
|
479
|
+
@snapshots.absorb(RunLevelFacts.harvest(:environment, harvest_context, &))
|
|
480
|
+
end
|
|
472
481
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
@snapshots.
|
|
477
|
-
@snapshots.env_build_failure = loader&.env_build_failure
|
|
478
|
-
@snapshots.conformance_results = RbsExtended::ConformanceChecker.scan(loader)
|
|
482
|
+
# `RunStats` telemetry ONLY (`class_decl_paths` / `signature_paths`), which a `--no-stats` run
|
|
483
|
+
# legitimately skips. Never gates a diagnostic slot (#798).
|
|
484
|
+
def harvest_stats(&)
|
|
485
|
+
@snapshots.absorb(RunLevelFacts.harvest(:stats, harvest_context, &)) if @collect_stats
|
|
479
486
|
end
|
|
480
487
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
# `--incremental` run reported a `.rbs` annotation and stayed silent about the identical one written
|
|
488
|
-
# as an rbs-inline comment, while a default run reported both. The environment was there either way;
|
|
489
|
-
# nothing was carrying it. {Effects::SignatureSources.annotated_carrier} reduces it to at most ONE
|
|
490
|
-
# synthesized buffer, which is all a one-`:info`-per-run pass can spend.
|
|
491
|
-
#
|
|
492
|
-
# Skipped when collection is on — then {Runner::EffectEnvelopePass} reads the loader directly and
|
|
493
|
-
# this pass is gated off — so a collecting run pays nothing, and a project with no synthesizing
|
|
494
|
-
# plugin answers with an empty array and no regex at all.
|
|
495
|
-
def snapshot_effect_annotation_carrier(loader)
|
|
496
|
-
virtual = @record_effects ? nil : loader&.virtual_rbs
|
|
497
|
-
@snapshots.effect_annotation_carrier = Effects::SignatureSources.annotated_carrier(virtual)
|
|
498
|
-
rescue StandardError
|
|
499
|
-
@snapshots.effect_annotation_carrier = [].freeze
|
|
488
|
+
def harvest_context
|
|
489
|
+
RunLevelFacts::Context.new(
|
|
490
|
+
project_signature_paths: project_signature_paths?,
|
|
491
|
+
plugin_signature_paths: plugin_signature_paths?,
|
|
492
|
+
record_effects: @record_effects
|
|
493
|
+
)
|
|
500
494
|
end
|
|
501
495
|
|
|
502
496
|
# Sequential-mode environment resolver. Returns the supplied `environment:` override (with the
|
|
503
497
|
# runner's fresh per-run reporter pair attached so dispatcher events route to THIS runner's
|
|
504
498
|
# diagnostics) when present; otherwise builds a fresh Environment per-call via
|
|
505
499
|
# {#build_runner_environment} — preserving the pre-override behaviour bit-for-bit.
|
|
506
|
-
def resolve_sequential_environment(source_files: [])
|
|
507
|
-
return build_runner_environment(source_files: source_files) unless @environment_override
|
|
500
|
+
def resolve_sequential_environment(source_files: [], eager: true)
|
|
501
|
+
return build_runner_environment(source_files: source_files, eager: eager) unless @environment_override
|
|
508
502
|
|
|
509
503
|
@environment_override.attach_reporters!(
|
|
510
504
|
rbs_extended_reporter: @rbs_extended_reporter,
|
|
@@ -532,8 +526,10 @@ module Rigor
|
|
|
532
526
|
# `project_scope_seed` its worker would need to record complete edges. Without `fork` (Windows) a
|
|
533
527
|
# recording run degrades to sequential, which records correctly through `@analyze_file`.
|
|
534
528
|
#
|
|
535
|
-
# An effects run (ADR-103 WD13) is pinned
|
|
536
|
-
#
|
|
529
|
+
# An effects run (ADR-103 WD13) is pinned the same way: the Ractor `:done` {WorkerPayload} has an
|
|
530
|
+
# `effects` slot, but the Ractor worker never collects into it (its session is built without the
|
|
531
|
+
# recording inputs, and the backend is off-default, #414), so only the fork backend fills it. Without
|
|
532
|
+
# `fork` it degrades the same way. The degrade is sound rather than merely safe:
|
|
537
533
|
# the sequential fallback still collects, so the effect graph is complete either way.
|
|
538
534
|
# @param source_files — the file list every worker's / the fallback's environment is
|
|
539
535
|
# built over — the whole project (issue #793), defaulting to `files` for direct callers.
|
|
@@ -564,8 +560,9 @@ module Rigor
|
|
|
564
560
|
# loaded plugin's `source_rbs_synthesizer` callable per project source file at env-build time.
|
|
565
561
|
# Defaults to `[]` for callers that don't have a file list yet (e.g. pre-pass-only build paths); in
|
|
566
562
|
# that case no synthesised RBS is contributed.
|
|
567
|
-
def build_runner_environment(source_files: [])
|
|
563
|
+
def build_runner_environment(source_files: [], eager: true)
|
|
568
564
|
Environment.for_project(
|
|
565
|
+
eager: eager,
|
|
569
566
|
libraries: @configuration.libraries,
|
|
570
567
|
signature_paths: @configuration.signature_paths,
|
|
571
568
|
cache_store: @cache_store,
|
|
@@ -597,18 +594,18 @@ module Rigor
|
|
|
597
594
|
end
|
|
598
595
|
|
|
599
596
|
# ADR-15 Phase 4b — Ractor pool around {WorkerSession}. Spawns `@workers` Ractors; each takes the
|
|
600
|
-
# shareable payload (
|
|
597
|
+
# shareable payload (configuration, cache root, plugin blueprints, explain flag) and
|
|
601
598
|
# builds its OWN WorkerSession internally. Files are distributed round-robin across the pool; each
|
|
602
599
|
# worker writes back to the main Ractor's mailbox via `Ractor.main.send` with one of three message
|
|
603
600
|
# kinds:
|
|
604
601
|
#
|
|
605
|
-
# - `[:prepare, diagnostics]` — once at startup, the session's `prepare_diagnostics` snapshot.
|
|
606
|
-
# coordinator keeps the FIRST worker's snapshot only (plugin `#prepare` is deterministic per
|
|
602
|
+
# - `[:prepare, diagnostics, refusals]` — once at startup, the session's `prepare_diagnostics` snapshot.
|
|
603
|
+
# The coordinator keeps the FIRST worker's snapshot only (plugin `#prepare` is deterministic per
|
|
607
604
|
# plugin, so each worker produces the same diagnostic set; surfacing them once avoids N×
|
|
608
605
|
# duplication).
|
|
609
|
-
# - `[:file, path, diagnostics]` — one per analysed file.
|
|
610
|
-
# - `[:done,
|
|
611
|
-
# merge.
|
|
606
|
+
# - `[:file, path, diagnostics, refusals]` — one per analysed file.
|
|
607
|
+
# - `[:done, payload]` — once at exit, the worker's {WorkerPayload} (its reporter snapshots, drained
|
|
608
|
+
# run-level facts and disclosures) for end-of-pool merge.
|
|
612
609
|
#
|
|
613
610
|
# The Ruby 4.0+ Ractor model uses a single per-Ractor mailbox (no `Ractor.yield`); workers push
|
|
614
611
|
# back via `Ractor.main.send`. The coordinator drains its mailbox via `Ractor.receive` until it has
|
|
@@ -695,9 +692,9 @@ module Rigor
|
|
|
695
692
|
)
|
|
696
693
|
)
|
|
697
694
|
warm_env = prewarm_rbs_cache_for_pool(source_files: source_files, locked_gems: locked_gems)
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
695
|
+
harvest_environment_facts { warm_env }
|
|
696
|
+
@snapshots.absorb(RunLevelFacts.drain(warm_env, demanding: false))
|
|
697
|
+
harvest_stats { warm_env }
|
|
701
698
|
|
|
702
699
|
configuration = @configuration
|
|
703
700
|
cache_root = @cache_store&.root
|
|
@@ -705,8 +702,7 @@ module Rigor
|
|
|
705
702
|
explain = @explain
|
|
706
703
|
# ADR-32 WD4 — the full project file list travels into every Ractor worker so each worker's
|
|
707
704
|
# WorkerSession can invoke loaded plugins' source_rbs_synthesizers at env-build time. The list is
|
|
708
|
-
# a frozen
|
|
709
|
-
# (`source_files`), not the analyzed subset this comment always described.
|
|
705
|
+
# a frozen list of strings; cheaply shareable.
|
|
710
706
|
shareable_source_files = source_files.map { |path| path.to_s.dup.freeze }.freeze
|
|
711
707
|
features = required_features_for(source_files)
|
|
712
708
|
|
|
@@ -737,7 +733,10 @@ module Rigor
|
|
|
737
733
|
# Issue #1051 — sent with `:done`, not with `:prepare`: this backend builds no parent session,
|
|
738
734
|
# so the worker's own table is the only place a disclosure exists, and it is complete only
|
|
739
735
|
# once the worker has finished its files.
|
|
740
|
-
main.send([:done,
|
|
736
|
+
main.send([:done, Rigor::Analysis::Runner::WorkerPayload.harvest(session)])
|
|
737
|
+
# `Ractor#send` answers the Ractor; the worker's own value is unread (`join` only), and G3 reads a
|
|
738
|
+
# messaging call as an address only while its value is discarded.
|
|
739
|
+
nil
|
|
741
740
|
end
|
|
742
741
|
end
|
|
743
742
|
|
|
@@ -746,8 +745,11 @@ module Rigor
|
|
|
746
745
|
# Ractor's default port, which is the same mailbox the workers push their results to.
|
|
747
746
|
pool.each { |worker| worker.monitor(Ractor.current.default_port) }
|
|
748
747
|
|
|
749
|
-
|
|
750
|
-
|
|
748
|
+
# A worker that died before its turn has a closed port: `Ractor#send` raises `Ractor::ClosedError`.
|
|
749
|
+
# That is the same death the monitor loop below counts, so it is not a failure of the run — the
|
|
750
|
+
# file simply never reports and lands in the degrade, like any file a dead worker never answered.
|
|
751
|
+
files.each_with_index { |path, index| send_to_worker(pool[index % pool.size], path) }
|
|
752
|
+
pool.each { |worker| send_to_worker(worker, nil) }
|
|
751
753
|
|
|
752
754
|
prepare_diagnostics = nil
|
|
753
755
|
prepare_refusals = nil
|
|
@@ -775,8 +777,7 @@ module Rigor
|
|
|
775
777
|
results_by_path[message[1]] = message[2]
|
|
776
778
|
refusals_by_path[message[1]] = message[3]
|
|
777
779
|
when :done
|
|
778
|
-
|
|
779
|
-
merge_run_disclosures(message[2])
|
|
780
|
+
absorb_worker_payload(message[1])
|
|
780
781
|
end
|
|
781
782
|
end
|
|
782
783
|
|
|
@@ -824,8 +825,7 @@ module Rigor
|
|
|
824
825
|
|
|
825
826
|
environment = build_runner_environment(source_files: source_files)
|
|
826
827
|
degraded.each { |path| results_by_path[path] = @analyze_file.call(path, environment) }
|
|
827
|
-
|
|
828
|
-
record_hkt_scan_failure(hkt_scan_outcome(environment))
|
|
828
|
+
@snapshots.absorb(RunLevelFacts.drain(environment))
|
|
829
829
|
end
|
|
830
830
|
|
|
831
831
|
# ADR-15 Amendment (2026-05-20) — fork-based worker pool, the active backend for `workers > 0`.
|
|
@@ -873,14 +873,13 @@ module Rigor
|
|
|
873
873
|
# a resulting definition-build failure is already sitting in the parent's loader at the moment
|
|
874
874
|
# each child copy-on-write-inherits it — every child's own `#drain_reporters` then reports it
|
|
875
875
|
# right alongside whatever its own slice demanded, with no separate plumbing needed. Left
|
|
876
|
-
# unconditional, unlike `#
|
|
876
|
+
# unconditional, unlike `#harvest_stats` below: these are diagnostic rows, not
|
|
877
877
|
# `RunStats` telemetry, and the stats gate must not decide which diagnostics a run reports (a
|
|
878
878
|
# `--workers N --no-stats` run used to say strictly LESS than the sequential path over the
|
|
879
879
|
# same project).
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
#
|
|
883
|
-
# snapshots above: `#prepare` ran on the parent when the session was built, so every disclosure a
|
|
880
|
+
harvest_environment_facts { session.environment }
|
|
881
|
+
# Issue #1051 — taken BEFORE the fork, for the same reason and with the same effect as the harvest
|
|
882
|
+
# above: `#prepare` ran on the parent when the session was built, so every disclosure a
|
|
884
883
|
# plugin registers there (the normal case — a disclosure is a fact about the project's inputs,
|
|
885
884
|
# known before a file is read) is already in this table, in the same order the sequential path
|
|
886
885
|
# would produce. Each child inherits the identical table and re-offers it in its payload, where
|
|
@@ -891,7 +890,8 @@ module Rigor
|
|
|
891
890
|
# session's record of what it already reported and drains only its own slice's, so the prepare-time
|
|
892
891
|
# refusal is counted once, as `--workers 0` counts it, rather than once per worker.
|
|
893
892
|
merge_trust_refusals(session.drain_trust_refusals)
|
|
894
|
-
|
|
893
|
+
# `RunStats` telemetry only, off the same pre-fork environment.
|
|
894
|
+
harvest_stats { session.environment }
|
|
895
895
|
|
|
896
896
|
worker_count = [@workers, files.size].min
|
|
897
897
|
slices = files.each_slice((files.size.to_f / worker_count).ceil).to_a
|
|
@@ -915,16 +915,10 @@ module Rigor
|
|
|
915
915
|
results_by_path[path] = session.analyze(path)
|
|
916
916
|
refusals_by_path[path] = session.drain_trust_refusals
|
|
917
917
|
end
|
|
918
|
-
|
|
919
|
-
#
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
# not in a child payload — fold them in alongside the successful children's.
|
|
923
|
-
if @record_dependencies
|
|
924
|
-
collect_dependencies(session.drain_dependencies, session.drain_analysis_reads,
|
|
925
|
-
session.drain_analysis_disclosures)
|
|
926
|
-
end
|
|
927
|
-
@collected_effects.merge!(session.drain_effects) if @record_effects
|
|
918
|
+
# The degraded slice ran on the parent session, so its reporters, run-level facts, disclosures,
|
|
919
|
+
# dependency records and effects live there, not in a child payload — fold them in alongside the
|
|
920
|
+
# successful children's. Its refusals were drained file by file above.
|
|
921
|
+
absorb_worker_payload(WorkerPayload.harvest(session, record_effects: @record_effects))
|
|
928
922
|
end
|
|
929
923
|
|
|
930
924
|
merge_slice_refusals(slices, refusals_by_slice, refusals_by_path)
|
|
@@ -957,34 +951,18 @@ module Rigor
|
|
|
957
951
|
# mode is opted into for large projects, so a slice reliably outlasts the remainder; a small-project
|
|
958
952
|
# worker finishes first and stays at no-YJIT parity, exactly as the sequential path would.
|
|
959
953
|
Runtime::Jit.rearm_after_fork
|
|
954
|
+
TypeOfMemo.forked
|
|
960
955
|
results = slice.to_h { |path| [path, session.analyze(path)] }
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
analysis_reads: session.drain_analysis_reads,
|
|
964
|
-
analysis_disclosures: session.drain_analysis_disclosures,
|
|
965
|
-
disclosures: session.drain_run_disclosures,
|
|
966
|
-
refusals: session.drain_trust_refusals }
|
|
967
|
-
# ADR-103 WD13 — the effects slot exists only on a collecting run, so a normal `--workers N` run
|
|
968
|
-
# marshals exactly the payload it marshalled before the feature existed.
|
|
969
|
-
payload[:effects] = session.drain_effects if @record_effects
|
|
956
|
+
# ADR-103 WD13 — the effects slot is filled only on a collecting run.
|
|
957
|
+
payload = WorkerPayload.harvest(session, results: results, record_effects: @record_effects)
|
|
970
958
|
File.binwrite(out_path, Marshal.dump(payload))
|
|
959
|
+
TypeOfMemo.write_report
|
|
971
960
|
exit!(0)
|
|
972
961
|
rescue StandardError
|
|
962
|
+
TypeOfMemo.write_report
|
|
973
963
|
exit!(1)
|
|
974
964
|
end
|
|
975
965
|
|
|
976
|
-
# Issue #798 — `RunStats` telemetry ONLY, now that `#snapshot_project_signature_state` (called
|
|
977
|
-
# unconditionally above) owns every diagnostic-bearing slot this method used to ALSO write
|
|
978
|
-
# (`quarantined_signatures`, `env_build_failure`): those must not be gated on `@collect_stats`, and
|
|
979
|
-
# this method's own name says what is left — `class_decl_paths` / `signature_paths`, read off the
|
|
980
|
-
# parent session's loader so end-of-run {RunStats} can attribute the RBS class universe, which a
|
|
981
|
-
# `--no-stats` run legitimately skips.
|
|
982
|
-
def snapshot_fork_pool_stats(session)
|
|
983
|
-
loader = session.environment.rbs_loader
|
|
984
|
-
@snapshots.class_decl_paths = loader&.class_decl_paths || {}.freeze
|
|
985
|
-
@snapshots.signature_paths = loader&.signature_paths || [].freeze
|
|
986
|
-
end
|
|
987
|
-
|
|
988
966
|
# Waits for every forked child, merges each successful payload into `results_by_path`, and returns
|
|
989
967
|
# the file paths whose worker exited abnormally (for in-process degrade).
|
|
990
968
|
# @param refusals_by_slice — issue #1900 — receives each successful payload's trust-refusal drain
|
|
@@ -995,16 +973,10 @@ module Rigor
|
|
|
995
973
|
_, status = Process.waitpid2(child[:pid])
|
|
996
974
|
payload = fork_worker_payload(status, child[:out_path])
|
|
997
975
|
if payload
|
|
998
|
-
results_by_path.merge!(payload.
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
refusals = payload.fetch(:refusals, [])
|
|
976
|
+
results_by_path.merge!(payload.results)
|
|
977
|
+
absorb_worker_payload(payload)
|
|
978
|
+
refusals = Array(payload.refusals)
|
|
1002
979
|
refusals_by_slice ? refusals_by_slice[child[:index]] = refusals : merge_trust_refusals(refusals)
|
|
1003
|
-
if @record_dependencies
|
|
1004
|
-
collect_dependencies(payload.fetch(:dependencies, {}), payload.fetch(:analysis_reads, {}),
|
|
1005
|
-
payload.fetch(:analysis_disclosures, {}))
|
|
1006
|
-
end
|
|
1007
|
-
@collected_effects.merge!(payload.fetch(:effects, {})) if @record_effects
|
|
1008
980
|
else
|
|
1009
981
|
degraded.concat(child[:slice])
|
|
1010
982
|
end
|
|
@@ -1020,7 +992,7 @@ module Rigor
|
|
|
1020
992
|
collected_analysis_disclosures.merge!(analysis_disclosures)
|
|
1021
993
|
end
|
|
1022
994
|
|
|
1023
|
-
# @return the child's
|
|
995
|
+
# @return the child's {WorkerPayload}, or nil when the child exited
|
|
1024
996
|
# abnormally or wrote no readable payload. `Marshal.load` is safe here: the blob was written by
|
|
1025
997
|
# our own forked child to a temp file we created.
|
|
1026
998
|
def fork_worker_payload(status, out_path)
|
|
@@ -1035,7 +1007,7 @@ module Rigor
|
|
|
1035
1007
|
# process — so they say so the same way, naming which backend it was (#414).
|
|
1036
1008
|
def pool_degraded_diagnostic(count, backend)
|
|
1037
1009
|
Diagnostic.new(
|
|
1038
|
-
path:
|
|
1010
|
+
path: @configuration.config_display_path, line: 1, column: 1,
|
|
1039
1011
|
message: "#{backend} pool degraded: #{count} file(s) re-analysed in-process " \
|
|
1040
1012
|
"after a worker exited abnormally",
|
|
1041
1013
|
severity: :warning, rule: "pool-degraded", source_family: :builtin
|
|
@@ -1093,8 +1065,7 @@ module Rigor
|
|
|
1093
1065
|
# sequential analysis with a `:warning` `pool-degraded` diagnostic at run start. The actual
|
|
1094
1066
|
# per-file analysis runs on the coordinator, identical to the default sequential path.
|
|
1095
1067
|
# @param source_files — issue #793 — the whole project, so this path's environment
|
|
1096
|
-
# carries the same plugin-synthesized RBS the pool workers' would.
|
|
1097
|
-
# i.e. with no synthesized RBS at all, even on a full run.
|
|
1068
|
+
# carries the same plugin-synthesized RBS the pool workers' would.
|
|
1098
1069
|
#
|
|
1099
1070
|
# Issue #1900 — it also runs `#prepare` on the coordinator-side registry it analyses with, BEFORE the
|
|
1100
1071
|
# environment build as the sequential pre-pass does. A pooled run's pre-pass skips `#prepare` (the
|
|
@@ -1105,28 +1076,21 @@ module Rigor
|
|
|
1105
1076
|
def analyze_files_sequentially_fallback(files, reason:, source_files: files)
|
|
1106
1077
|
prepare_rows = prepare_coordinator_plugins_once
|
|
1107
1078
|
environment = build_runner_environment(source_files: source_files)
|
|
1108
|
-
# Same
|
|
1109
|
-
#
|
|
1110
|
-
#
|
|
1111
|
-
#
|
|
1112
|
-
|
|
1113
|
-
snapshot_effect_annotation_carrier(environment.rbs_loader)
|
|
1079
|
+
# Same harvest, same ORDER as `#analyze_files_sequentially` (#1201: a hand-copied list here once
|
|
1080
|
+
# dropped `synthesized-namespace` and every conformance row). The conformance scan inside it demands
|
|
1081
|
+
# the definition of every `rigor:v1:conforms-to` class, so it runs BEFORE the definition-build
|
|
1082
|
+
# failures are drained below, or a failure only that scan reaches is lost.
|
|
1083
|
+
harvest_environment_facts { environment }
|
|
1114
1084
|
diagnostics = prepare_rows + files.flat_map { |path| @analyze_file.call(path, environment) }
|
|
1115
|
-
|
|
1116
|
-
#
|
|
1117
|
-
#
|
|
1118
|
-
#
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
# Issue #784 — same reasoning: this path's Environment IS the one that reached the scan, so it
|
|
1122
|
-
# must snapshot the slot too, or a run that degraded to sequential would report less than a
|
|
1123
|
-
# sequential run would. Demanded once by the run as well, for the reason at the sequential site.
|
|
1124
|
-
record_hkt_scan_failure(hkt_scan_outcome(environment))
|
|
1125
|
-
@snapshots.class_decl_paths = loader&.class_decl_paths || {}.freeze
|
|
1126
|
-
@snapshots.signature_paths = loader&.signature_paths || [].freeze
|
|
1085
|
+
# Issues #696 / #784 — this path analyses on the coordinator, so its environment IS the one that
|
|
1086
|
+
# reached the failing build and the scan, and it drains the same facts the sequential path does.
|
|
1087
|
+
# Reached whenever `fork` is unavailable (Windows) and on `--incremental` / effects runs without it:
|
|
1088
|
+
# a run that degraded to sequential must not also report less than a sequential run would.
|
|
1089
|
+
@snapshots.absorb(RunLevelFacts.drain(environment))
|
|
1090
|
+
harvest_stats { environment }
|
|
1127
1091
|
diagnostics.unshift(
|
|
1128
1092
|
Diagnostic.new(
|
|
1129
|
-
path:
|
|
1093
|
+
path: @configuration.config_display_path, line: 1, column: 1,
|
|
1130
1094
|
message: "pool mode degraded to sequential: #{reason}",
|
|
1131
1095
|
severity: :warning, rule: "pool-degraded", source_family: :builtin
|
|
1132
1096
|
)
|
|
@@ -1142,149 +1106,42 @@ module Rigor
|
|
|
1142
1106
|
Array(@prepare_coordinator_plugins.call)
|
|
1143
1107
|
end
|
|
1144
1108
|
|
|
1109
|
+
# One worker drain ({WorkerSession#drain_reporters}): each reporter absorbs its own snapshot, by
|
|
1110
|
+
# its primitives (#785, #805) so every worker's copy of an entry collapses to the single row a
|
|
1111
|
+
# `--workers=0` run prints, and the drained `:analysis` facts fold in by their merge rules — the
|
|
1112
|
+
# definition-build union (#696) and the first-wins HKT outcome (#784). A key an older drain shape
|
|
1113
|
+
# lacks records nothing.
|
|
1145
1114
|
def merge_worker_reporters(drained)
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
@boundary_cross_reporter.record(
|
|
1149
|
-
class_name: entry.class_name,
|
|
1150
|
-
method_name: entry.method_name,
|
|
1151
|
-
gem_name: entry.gem_name,
|
|
1152
|
-
rbs_display: entry.rbs_display
|
|
1153
|
-
)
|
|
1154
|
-
end
|
|
1155
|
-
# ADR-32 WD6 — merge per-worker synthesizer failures back into the coordinator's reporter. Fetched
|
|
1156
|
-
# with a default empty array so older drains (pre-slice-2) remain compatible.
|
|
1157
|
-
#
|
|
1158
|
-
# `kind:` rides along (issue #824): without it every drained entry replayed as WD6's default
|
|
1159
|
-
# `:failed`, so under `--workers N` a WD12 "parsed but not honoured" row came back as
|
|
1160
|
-
# `source-rbs-synthesis-failed` — a different id, and a message telling the user the file
|
|
1161
|
-
# contributed nothing when all but one of its annotations bound. Every worker reads the same
|
|
1162
|
-
# sources and so hands over the same entries; the reporter's own dedup collapses them. `line:` rides
|
|
1163
|
-
# along for the same reason (#1075): a consistency error is positioned at its `.rbs` member.
|
|
1164
|
-
Array(drained[:source_rbs_synthesis]).each do |entry|
|
|
1165
|
-
@source_rbs_synthesis_reporter.record(
|
|
1166
|
-
plugin_id: entry.plugin_id, path: entry.path, message: entry.message, kind: entry.kind,
|
|
1167
|
-
line: entry.line
|
|
1168
|
-
)
|
|
1169
|
-
end
|
|
1170
|
-
# Issue #696. Fetched with a default so an older drain stays compatible, exactly as the line above.
|
|
1171
|
-
record_definition_build_failures(drained[:definition_build_failures])
|
|
1172
|
-
# Issue #784. `Hash#[]` is already a nil default, exactly as `env_build_failure` is snapshotted
|
|
1173
|
-
# elsewhere — an older drain shape simply has no key and records nothing.
|
|
1174
|
-
record_hkt_scan_failure(drained[:hkt_scan_failure])
|
|
1115
|
+
RunLevelFacts.absorb_reporters(reporters, drained)
|
|
1116
|
+
@snapshots.absorb(RunLevelFacts.drained_facts(drained))
|
|
1175
1117
|
end
|
|
1176
1118
|
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
# for its two elders). Every worker reads the same `.rbs`, so each hands over the same entries, and
|
|
1186
|
-
# only a primitive-keyed dedup collapses them back to the single row a `--workers=0` run prints — an
|
|
1187
|
-
# `RBS::Location` compares equal only against a location over the same `RBS::Buffer` object, so a
|
|
1188
|
-
# location-keyed one would print N copies at `--workers=N`. The entries could not reach here carrying
|
|
1189
|
-
# a location anyway: the drain Marshals its payload and an `RBS::Location` has no `_dump`.
|
|
1190
|
-
def merge_rbs_extended_reporter(rbs)
|
|
1191
|
-
rbs.fetch(:unresolved_payloads).each do |entry|
|
|
1192
|
-
@rbs_extended_reporter.record_unresolved(
|
|
1193
|
-
payload: entry.payload, path: entry.path, line: entry.line, column: entry.column
|
|
1194
|
-
)
|
|
1195
|
-
end
|
|
1196
|
-
rbs.fetch(:lossy_projections).each do |entry|
|
|
1197
|
-
@rbs_extended_reporter.record_lossy_projection(
|
|
1198
|
-
head: entry.head, path: entry.path, line: entry.line, column: entry.column
|
|
1199
|
-
)
|
|
1200
|
-
end
|
|
1201
|
-
Array(rbs[:hkt_directive_errors]).each do |entry|
|
|
1202
|
-
@rbs_extended_reporter.record_hkt_error(
|
|
1203
|
-
message: entry.message, path: entry.path, line: entry.line, column: entry.column
|
|
1204
|
-
)
|
|
1205
|
-
end
|
|
1206
|
-
replay_deprecated_forms(rbs)
|
|
1207
|
-
end
|
|
1208
|
-
|
|
1209
|
-
# ADR-109 WD3 — the fourth stream, read with the nil default like the hkt one so an older drain
|
|
1210
|
-
# shape records nothing.
|
|
1211
|
-
def replay_deprecated_forms(rbs)
|
|
1212
|
-
Array(rbs[:deprecated_forms]).each do |entry|
|
|
1213
|
-
@rbs_extended_reporter.record_deprecated_form(
|
|
1214
|
-
payload: entry.payload, replacement: entry.replacement,
|
|
1215
|
-
path: entry.path, line: entry.line, column: entry.column
|
|
1216
|
-
)
|
|
1119
|
+
# One {WorkerPayload}, minus its per-file results and its refusals, which each backend merges in its
|
|
1120
|
+
# own file order.
|
|
1121
|
+
def absorb_worker_payload(payload)
|
|
1122
|
+
merge_worker_reporters(payload.reporters)
|
|
1123
|
+
merge_run_disclosures(payload.disclosures)
|
|
1124
|
+
if @record_dependencies
|
|
1125
|
+
collect_dependencies(payload.dependencies || {}, payload.analysis_reads || {},
|
|
1126
|
+
payload.analysis_disclosures || {})
|
|
1217
1127
|
end
|
|
1128
|
+
@collected_effects.merge!(payload.effects || {}) if @record_effects
|
|
1218
1129
|
end
|
|
1219
1130
|
|
|
1220
|
-
|
|
1221
|
-
# by class name and kept in first-seen order.
|
|
1222
|
-
#
|
|
1223
|
-
# Accumulating rather than assigning is what makes a pooled run say what a sequential one says: each
|
|
1224
|
-
# worker holds its OWN loader and its own memo (the `@state` that also gates the stderr banner, which
|
|
1225
|
-
# is why the banner repeats per worker — #295), so a class that fails in two workers arrives twice
|
|
1226
|
-
# and a class only one worker touched arrives once. The union over the workers is the set the RUN
|
|
1227
|
-
# hit; neither worker alone is.
|
|
1228
|
-
#
|
|
1229
|
-
# NOT gated on `project_signature_paths?`, unlike {#snapshot_project_signature_state}. That gate
|
|
1230
|
-
# exists because each condition there forces the otherwise-lazy env build, which would warm the cache
|
|
1231
|
-
# on a project with no sig set of its own. Nothing is forced here — this reads what the analysis
|
|
1232
|
-
# already demanded — and the failure is not a project-sig-only condition anyway: two BUNDLED sources
|
|
1233
|
-
# declaring the same method collapse a class with no `signature_paths:` in sight (the
|
|
1234
|
-
# `bigdecimal` / `BigMath` shape, #299). Gating it would suppress exactly the variant the user cannot
|
|
1235
|
-
# fix by editing their own `sig/`.
|
|
1236
|
-
def record_definition_build_failures(failures)
|
|
1237
|
-
return if failures.nil? || failures.empty?
|
|
1238
|
-
|
|
1239
|
-
# Issue #1902 — one tuple per class, chosen by {Environment::RbsLoader.definition_build_detail_order}
|
|
1240
|
-
# rather than by arrival: a fresh record and a replayed one (or two workers' records) for the same
|
|
1241
|
-
# class can name different failures, and the earlier one used to win.
|
|
1242
|
-
@snapshots.definition_build_failures =
|
|
1243
|
-
(@snapshots.definition_build_failures + failures)
|
|
1244
|
-
.group_by(&:first)
|
|
1245
|
-
.map { |_, candidates| candidates.min_by { |c| Environment::RbsLoader.definition_build_detail_order(c) } }
|
|
1246
|
-
.freeze
|
|
1247
|
-
end
|
|
1131
|
+
private
|
|
1248
1132
|
|
|
1249
|
-
#
|
|
1250
|
-
#
|
|
1251
|
-
#
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
def record_hkt_scan_failure(tuple)
|
|
1257
|
-
@snapshots.hkt_scan_failure ||= tuple
|
|
1133
|
+
# `Ractor#send` to a worker whose port is already closed (it died before this send, whether before
|
|
1134
|
+
# dispatch or part-way through it). The death is observed through the monitor notice, and the unsent
|
|
1135
|
+
# file is re-analysed by the degrade.
|
|
1136
|
+
def send_to_worker(worker, message)
|
|
1137
|
+
worker.send(message)
|
|
1138
|
+
rescue Ractor::ClosedError
|
|
1139
|
+
nil
|
|
1258
1140
|
end
|
|
1259
1141
|
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
# and then the slot is nil after the loop and the run says nothing — while `--incremental` has also
|
|
1264
|
-
# dropped the row from its per-file cache. Demanding here makes the outcome a property of the RUN,
|
|
1265
|
-
# not of which files happened to be in it.
|
|
1266
|
-
#
|
|
1267
|
-
# This is sound where #696's "a demand that is Rigor's own MUST NOT contribute" is not, and the
|
|
1268
|
-
# difference is the shape of what is recorded. #696 reports a per-class LIST whose membership is
|
|
1269
|
-
# "the classes the analysis demanded"; a Rigor-internal demand adds classes the user never asked
|
|
1270
|
-
# about and makes that list vary with configuration. The HKT scan is ONE build with ONE outcome —
|
|
1271
|
-
# the same tuple whoever demands it — so an extra demand cannot change what is reported, only
|
|
1272
|
-
# guarantee it is observed.
|
|
1273
|
-
#
|
|
1274
|
-
# What it costs, stated plainly because {#record_definition_build_failures}'s comment promises
|
|
1275
|
-
# "nothing is forced here": the demand DOES force the RBS env build when the environment has not
|
|
1276
|
-
# built it yet (the scan reads the loader). On every path that reaches this method that build is
|
|
1277
|
-
# either already done (the loop demanded it) or the same load one analysed file would have paid:
|
|
1278
|
-
# a Marshal load when a cache store exists, the parse a full run pays under `--no-cache`. Never a
|
|
1279
|
-
# build on a project with no files (see {#analyze_files}). Memoised, so on a reused Environment
|
|
1280
|
-
# this is a hash read.
|
|
1281
|
-
#
|
|
1282
|
-
# @return the recorded tuple, or nil (no environment, or the scan built)
|
|
1283
|
-
def hkt_scan_outcome(environment)
|
|
1284
|
-
return nil if environment.nil?
|
|
1285
|
-
|
|
1286
|
-
environment.hkt_registry
|
|
1287
|
-
environment.hkt_scan_failure
|
|
1142
|
+
def reporters
|
|
1143
|
+
{ rbs_extended: @rbs_extended_reporter, boundary_cross: @boundary_cross_reporter,
|
|
1144
|
+
source_rbs_synthesis: @source_rbs_synthesis_reporter }
|
|
1288
1145
|
end
|
|
1289
1146
|
|
|
1290
1147
|
# True when the project declares its own `signature_paths:` (the only place the
|
|
@@ -1294,25 +1151,13 @@ module Rigor
|
|
|
1294
1151
|
!(paths.nil? || paths.empty?)
|
|
1295
1152
|
end
|
|
1296
1153
|
|
|
1297
|
-
# Issue #610 — see {
|
|
1154
|
+
# Issue #610 — see {RunLevelFacts::Collect.signature_standdowns}. The registry's `signature_paths` is what
|
|
1298
1155
|
# `Environment.for_project` defers, so this is the loader-free form of "is anything deferred?".
|
|
1299
1156
|
def plugin_signature_paths?
|
|
1300
1157
|
registry = plugin_registry
|
|
1301
1158
|
registry.respond_to?(:signature_paths) && !registry.signature_paths.empty?
|
|
1302
1159
|
end
|
|
1303
1160
|
|
|
1304
|
-
# Empty for a loader with nothing deferred, without touching its env.
|
|
1305
|
-
def signature_standdowns_for(loader)
|
|
1306
|
-
return [] if loader.nil? || !loader.respond_to?(:deferred_signature_paths)
|
|
1307
|
-
return [] if loader.deferred_signature_paths.empty?
|
|
1308
|
-
|
|
1309
|
-
loader.signature_standdowns
|
|
1310
|
-
end
|
|
1311
|
-
|
|
1312
|
-
def vendored_standdowns_for(loader)
|
|
1313
|
-
loader.respond_to?(:vendored_standdowns) ? loader.vendored_standdowns : []
|
|
1314
|
-
end
|
|
1315
|
-
|
|
1316
1161
|
def plugin_registry
|
|
1317
1162
|
@plugin_registry_reader.call
|
|
1318
1163
|
end
|