rigortype 0.4.5 → 0.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/data/gem_overlay/activesupport/core_ext.rbs +16 -13
- 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 +5 -2
- data/docs/handbook/11-sig-gen.md +61 -5
- data/docs/handbook/appendix-phpstan.md +4 -4
- data/docs/manual/02-cli-reference.md +192 -27
- data/docs/manual/03-configuration.md +38 -4
- data/docs/manual/04-diagnostics.md +1 -1
- data/docs/manual/06-baseline.md +6 -4
- data/docs/manual/09-editor-integration.md +13 -2
- data/docs/manual/10-mcp-server.md +47 -3
- data/docs/manual/12-caching.md +9 -1
- data/docs/manual/plugins/rigor-actionmailer.md +30 -0
- data/docs/manual/plugins/rigor-actionpack.md +49 -11
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +20 -1
- data/docs/manual/plugins/rigor-devise.md +15 -0
- data/docs/manual/plugins/rigor-rspec.md +2 -1
- data/lib/rigor/analysis/baseline.rb +24 -27
- data/lib/rigor/analysis/buffer_binding.rb +14 -0
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +5 -6
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +4 -4
- data/lib/rigor/analysis/check_rules/duplicate_hash_key_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/inferred_param_guard.rb +105 -12
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +143 -15
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +4 -1
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -8
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +1 -1
- data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +66 -3
- data/lib/rigor/analysis/check_rules/rebinding_block_callees/dispatch.rb +112 -0
- data/lib/rigor/analysis/check_rules/rebinding_block_callees.rb +394 -0
- data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
- data/lib/rigor/analysis/check_rules/ruby_deprecations.rb +7 -0
- data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -1
- 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 +53 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules.rb +217 -52
- data/lib/rigor/analysis/crash_signature.rb +2 -2
- data/lib/rigor/analysis/dependency_recorder.rb +15 -4
- 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 +154 -0
- data/lib/rigor/analysis/fact_store.rb +3 -3
- data/lib/rigor/analysis/incremental.rb +37 -0
- data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +2 -2
- data/lib/rigor/analysis/incremental_run_slot.rb +2 -2
- data/lib/rigor/analysis/incremental_session.rb +202 -43
- data/lib/rigor/analysis/path_expansion.rb +41 -0
- data/lib/rigor/analysis/require_relative_closure.rb +100 -0
- data/lib/rigor/analysis/rule_catalog.rb +7 -3
- data/lib/rigor/analysis/run_disclosure_credit.rb +80 -0
- data/lib/rigor/analysis/run_disclosure_ledger.rb +155 -0
- data/lib/rigor/analysis/run_stats.rb +2 -2
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +8 -4
- data/lib/rigor/analysis/runner/declaration_position.rb +1 -1
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +204 -96
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +1 -1
- data/lib/rigor/analysis/runner/pool_coordinator.rb +157 -320
- data/lib/rigor/analysis/runner/project_pre_passes.rb +48 -6
- 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/scan_environment.rb +51 -0
- data/lib/rigor/analysis/runner/worker_payload.rb +36 -0
- data/lib/rigor/analysis/runner.rb +233 -120
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +1 -1
- 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 +38 -33
- data/lib/rigor/bleeding_edge.rb +3 -6
- 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/incremental_snapshot.rb +60 -18
- 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 +54 -3
- data/lib/rigor/cache/rbs_environment.rb +4 -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 +145 -55
- data/lib/rigor/cli/catalogue.rb +256 -0
- data/lib/rigor/cli/check_command.rb +188 -93
- 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 +27 -21
- 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 +18 -17
- 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 +17 -13
- data/lib/rigor/cli/lsp_command.rb +250 -23
- data/lib/rigor/cli/mcp_command.rb +14 -8
- 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 +60 -4
- data/lib/rigor/cli/plugin_command.rb +93 -46
- data/lib/rigor/cli/plugins_command.rb +22 -18
- data/lib/rigor/cli/plugins_renderer.rb +34 -8
- 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 +23 -16
- data/lib/rigor/cli/sig_gen_command.rb +130 -34
- 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 +1 -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 +18 -12
- data/lib/rigor/cli/type_of_project_scope.rb +24 -0
- 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 +209 -54
- data/lib/rigor/config_audit.rb +17 -6
- data/lib/rigor/configuration/severity_profile.rb +5 -5
- data/lib/rigor/configuration.rb +86 -34
- 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 +5 -9
- data/lib/rigor/environment/class_registry.rb +3 -3
- data/lib/rigor/environment/core_root.rb +77 -0
- data/lib/rigor/environment/default_libraries.rb +1 -1
- data/lib/rigor/environment/deferred_signature_plan.rb +283 -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/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_hierarchy.rb +6 -15
- data/lib/rigor/environment/rbs_loader.rb +592 -70
- data/lib/rigor/environment/reflection.rb +7 -7
- data/lib/rigor/environment/required_features.rb +136 -32
- data/lib/rigor/environment/ruby_library_roots.rb +122 -0
- data/lib/rigor/environment/vendored_signature_plan.rb +153 -0
- data/lib/rigor/environment.rb +44 -11
- data/lib/rigor/flow_contribution/merger.rb +4 -4
- data/lib/rigor/inference/acceptance.rb +93 -32
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/block_run_count.rb +178 -0
- data/lib/rigor/inference/budget_trace.rb +8 -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 +126 -0
- data/lib/rigor/inference/callee_write_census.rb +815 -0
- data/lib/rigor/inference/construction_escape.rb +217 -0
- data/lib/rigor/inference/declaration_walk/traversal.rb +1 -1
- data/lib/rigor/inference/deep_erasure.rb +170 -0
- data/lib/rigor/inference/def_handle.rb +1 -1
- data/lib/rigor/inference/def_node_resolver.rb +1 -1
- data/lib/rigor/inference/def_return_typer.rb +204 -12
- data/lib/rigor/inference/definer_resolution.rb +21 -5
- data/lib/rigor/inference/definers_index.rb +106 -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 +17 -3
- data/lib/rigor/inference/expression_typer.rb +173 -57
- data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
- data/lib/rigor/inference/global_write_census.rb +110 -1
- 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/indexed_narrowing.rb +4 -7
- data/lib/rigor/inference/intervening_ivar_reset.rb +26 -4
- data/lib/rigor/inference/ivar_writer_census.rb +246 -0
- data/lib/rigor/inference/key_presence_guard.rb +2 -1
- data/lib/rigor/inference/loop_value_record.rb +57 -0
- data/lib/rigor/inference/match_rebinding/frame.rb +65 -0
- data/lib/rigor/inference/match_rebinding/yielding_match.rb +45 -0
- data/lib/rigor/inference/match_rebinding.rb +11 -1
- data/lib/rigor/inference/memo_audit.rb +55 -0
- 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 +1 -1
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +113 -12
- data/lib/rigor/inference/method_dispatcher/contested_overloads.rb +89 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -3
- data/lib/rigor/inference/method_dispatcher/no_match_fallback.rb +54 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +39 -27
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +203 -17
- 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 +2 -2
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +59 -24
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +83 -20
- 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/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 +109 -29
- data/lib/rigor/inference/module_function_state/spellings.rb +64 -0
- data/lib/rigor/inference/module_function_state.rb +23 -1
- data/lib/rigor/inference/multi_target_binder.rb +1 -1
- data/lib/rigor/inference/mutation_rejoin.rb +240 -4
- data/lib/rigor/inference/mutation_widening.rb +162 -29
- data/lib/rigor/inference/narrowing.rb +113 -38
- data/lib/rigor/inference/object_mixins.rb +13 -1
- data/lib/rigor/inference/optimistic_origin.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/project_method_ownership.rb +6 -3
- data/lib/rigor/inference/project_seed_tables.rb +2 -1
- data/lib/rigor/inference/project_yielder.rb +122 -0
- data/lib/rigor/inference/receiver_alias.rb +20 -0
- data/lib/rigor/inference/refined_dispatch/constructed_class.rb +3 -1
- data/lib/rigor/inference/refined_dispatch/own_method.rb +2 -1
- data/lib/rigor/inference/remover_holder_scan.rb +233 -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 +9 -1
- data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
- data/lib/rigor/inference/scope_indexer/declared_ivar_types.rb +64 -0
- data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +205 -0
- 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 +135 -0
- data/lib/rigor/inference/scope_indexer/unseen_ivar_writers.rb +133 -0
- data/lib/rigor/inference/scope_indexer.rb +614 -146
- data/lib/rigor/inference/self_call_ivar_writes.rb +101 -17
- data/lib/rigor/inference/set_mutation.rb +189 -0
- data/lib/rigor/inference/singleton_hook_decline.rb +1 -2
- data/lib/rigor/inference/statement_evaluator.rb +3036 -278
- data/lib/rigor/inference/struct_fold_safety.rb +4 -6
- data/lib/rigor/inference/synthetic_method_index.rb +23 -0
- data/lib/rigor/inference/synthetic_method_scanner/project_members.rb +130 -0
- data/lib/rigor/inference/synthetic_method_scanner.rb +73 -14
- data/lib/rigor/inference/toplevel_def_census.rb +59 -0
- data/lib/rigor/inference/unknown_store_widening.rb +103 -4
- data/lib/rigor/inference/unthreaded_rebinds.rb +2 -3
- data/lib/rigor/inference/void_tail_summary.rb +2 -1
- data/lib/rigor/language_server/buffer_table.rb +9 -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 +28 -9
- 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 +98 -4
- 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 +12 -6
- data/lib/rigor/language_server/signature_help_provider.rb +4 -4
- data/lib/rigor/lens/plugins.rb +1 -1
- data/lib/rigor/mcp/server.rb +82 -3
- data/lib/rigor/mcp.rb +1 -1
- data/lib/rigor/plugin/access_denied_error.rb +1 -2
- data/lib/rigor/plugin/base.rb +26 -11
- data/lib/rigor/plugin/bundled_catalog.rb +1 -12
- data/lib/rigor/plugin/io_boundary.rb +1 -1
- 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 +38 -16
- data/lib/rigor/plugin/registry.rb +41 -3
- 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 +2 -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 +96 -2
- 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 +5 -3
- data/lib/rigor/rooted_glob.rb +25 -2
- data/lib/rigor/run_scoped_slots.rb +38 -0
- data/lib/rigor/runtime/jit.rb +1 -1
- data/lib/rigor/scope/discovery_index.rb +19 -6
- data/lib/rigor/scope/resolution_chain.rb +3 -1
- data/lib/rigor/scope.rb +379 -36
- data/lib/rigor/sig_gen/classification.rb +4 -0
- data/lib/rigor/sig_gen/effect_annotation.rb +3 -3
- data/lib/rigor/sig_gen/generator.rb +668 -84
- 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 +23 -4
- data/lib/rigor/sig_gen/observation_collector.rb +7 -6
- 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/rbs_validity.rb +1 -1
- data/lib/rigor/sig_gen/renderer.rb +21 -12
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +15 -2
- data/lib/rigor/sig_gen/type_elaborator.rb +21 -5
- 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 +217 -37
- 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 +13 -16
- data/lib/rigor/type/constant.rb +48 -7
- data/lib/rigor/type/hash_shape.rb +26 -40
- data/lib/rigor/type/struct_instance.rb +3 -2
- data/lib/rigor/type/union.rb +1 -1
- 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/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 +89 -70
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +4 -2
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +25 -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 +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +79 -19
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +6 -3
- 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 +2 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +0 -1
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +5 -4
- 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 +7 -12
- 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 +19 -6
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +26 -13
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +3 -3
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +5 -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 +7 -6
- 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 +4 -5
- 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-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 +2 -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 +1 -3
- 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 +1 -1
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +2 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +102 -11
- 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 +5 -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 +1 -1
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +2 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +17 -21
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +2 -3
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +3 -2
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +4 -4
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer.rb +1 -1
- data/sig/rigor/analysis/baseline.rbs +0 -2
- data/sig/rigor/analysis/fact_store.rbs +1 -0
- data/sig/rigor/analysis/reachability/scan_cache.rbs +1 -1
- data/sig/rigor/cli/diff_command.rbs +1 -1
- data/sig/rigor/cli/explain_command.rbs +1 -1
- data/sig/rigor/effects/file_collection.rbs +5 -6
- data/sig/rigor/effects/registry.rbs +8 -6
- data/sig/rigor/effects/summary.rbs +6 -5
- data/sig/rigor/environment.rbs +5 -3
- data/sig/rigor/inference/optimistic_origin.rbs +11 -0
- data/sig/rigor/inference.rbs +10 -1
- data/sig/rigor/plugin/additional_initializer.rbs +0 -5
- data/sig/rigor/plugin/base.rbs +1 -0
- data/sig/rigor/plugin/effect_ancestry.rbs +6 -4
- data/sig/rigor/plugin/effect_attribution.rbs +25 -10
- data/sig/rigor/plugin/effect_edge.rbs +12 -5
- data/sig/rigor/plugin/effect_entry_points.rbs +8 -3
- data/sig/rigor/plugin/io_boundary.rbs +6 -5
- data/sig/rigor/plugin/manifest.rbs +16 -18
- data/sig/rigor/plugin/protocol_contract.rbs +6 -9
- data/sig/rigor/plugin/registry.rbs +1 -1
- data/sig/rigor/scope.rbs +51 -50
- data/sig/rigor/type.rbs +0 -6
- 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/03-baseline-and-bugs.md +12 -1
- metadata +55 -3
|
@@ -11,6 +11,8 @@ require_relative "../project_scan"
|
|
|
11
11
|
require_relative "../worker_session"
|
|
12
12
|
require_relative "../template_units"
|
|
13
13
|
require_relative "../template_unit_collector"
|
|
14
|
+
require_relative "../../project_environment"
|
|
15
|
+
require_relative "scan_environment"
|
|
14
16
|
|
|
15
17
|
module Rigor
|
|
16
18
|
module Analysis
|
|
@@ -100,7 +102,7 @@ module Rigor
|
|
|
100
102
|
synthetic_method_index = Inference::SyntheticMethodScanner.scan(
|
|
101
103
|
plugin_registry: plugin_registry,
|
|
102
104
|
paths: expansion.fetch(:files),
|
|
103
|
-
environment:
|
|
105
|
+
environment: scan_environment(plugin_registry, dependency_source_index, expansion),
|
|
104
106
|
fact_store: shared_fact_store(plugin_registry),
|
|
105
107
|
buffer: @buffer
|
|
106
108
|
)
|
|
@@ -135,7 +137,9 @@ module Rigor
|
|
|
135
137
|
# collectors walk the tree, and the AST is dropped before the next file (peak RSS stays flat).
|
|
136
138
|
def discover(expansion:)
|
|
137
139
|
build_discovery(
|
|
138
|
-
Inference::ScopeIndexer.discovered_project_index_for_paths(
|
|
140
|
+
Inference::ScopeIndexer.discovered_project_index_for_paths(
|
|
141
|
+
expansion.fetch(:files), buffer: @buffer, uncertain: uncertain_paths(expansion)
|
|
142
|
+
)
|
|
139
143
|
)
|
|
140
144
|
end
|
|
141
145
|
|
|
@@ -145,11 +149,18 @@ module Rigor
|
|
|
145
149
|
# (`seed_bundles` empty) this is `#discover` plus a fresh bundle set — the discovery index is identical.
|
|
146
150
|
def discover_from_bundles(expansion:, seed_bundles:)
|
|
147
151
|
index = Inference::ScopeIndexer.discovered_project_index_incremental(
|
|
148
|
-
expansion.fetch(:files), seed_bundles: seed_bundles, buffer: @buffer
|
|
152
|
+
expansion.fetch(:files), seed_bundles: seed_bundles, buffer: @buffer, uncertain: uncertain_paths(expansion)
|
|
149
153
|
)
|
|
150
154
|
[build_discovery(index), index.fetch(:bundles)]
|
|
151
155
|
end
|
|
152
156
|
|
|
157
|
+
# Issue #2021 — the files only the `require_relative` walk added ({Runner#required_outside_paths}): their
|
|
158
|
+
# declarations resolve constants and their methods exist, but every method contribution is `possible`, so no
|
|
159
|
+
# return type, arity or visibility is read from them.
|
|
160
|
+
def uncertain_paths(expansion)
|
|
161
|
+
expansion.fetch(:uncertain, Inference::ScopeIndexer::EMPTY_PATH_SET)
|
|
162
|
+
end
|
|
163
|
+
|
|
153
164
|
# Builds the {Discovery} bundle from a `{ classes:, def_index: }` index hash (shared by the plain and
|
|
154
165
|
# seed-bundle discovery paths).
|
|
155
166
|
def build_discovery(index)
|
|
@@ -294,6 +305,37 @@ module Rigor
|
|
|
294
305
|
|
|
295
306
|
private
|
|
296
307
|
|
|
308
|
+
# Issue #476 — the environment the Tier B (trait registry) half of the synthetic-method scan reads, or
|
|
309
|
+
# nil when no loaded plugin registers a trait registry (the scan then has no use for one, and Tier C's
|
|
310
|
+
# template and nested-class lanes behave exactly as they did). The view is lazy ({ScanEnvironment}): it
|
|
311
|
+
# builds an {Environment} on the first question a matching call site asks. That environment is built
|
|
312
|
+
# WITHOUT `synthetic_method_index:` — the index is this scan's output and only the run's real
|
|
313
|
+
# environment stores it; the loader never reads it — and without the diagnostic reporters, which the
|
|
314
|
+
# run's real environment attaches (a second set here would double-report). Every input that decides
|
|
315
|
+
# what the loader resolves (libraries, signature paths, plugin signatures, the bundle / collection
|
|
316
|
+
# axes, required features) is the one the real build passes, so the module a trait names resolves to
|
|
317
|
+
# the same RBS the dispatcher later promotes against.
|
|
318
|
+
def scan_environment(plugin_registry, dependency_source_index, expansion)
|
|
319
|
+
return nil if plugin_registry.nil? || plugin_registry.empty?
|
|
320
|
+
return nil unless plugin_registry.plugins.any? { |plugin| plugin.manifest.trait_registries.any? }
|
|
321
|
+
|
|
322
|
+
files = expansion.fetch(:files)
|
|
323
|
+
ScanEnvironment.new do
|
|
324
|
+
Environment.for_project(
|
|
325
|
+
libraries: @configuration.libraries,
|
|
326
|
+
signature_paths: @configuration.signature_paths,
|
|
327
|
+
cache_store: @cache_store,
|
|
328
|
+
plugin_registry: plugin_registry,
|
|
329
|
+
dependency_source_index: dependency_source_index,
|
|
330
|
+
source_files: files,
|
|
331
|
+
required_features: ProjectEnvironment.required_features(
|
|
332
|
+
@configuration, files, sources: ProjectEnvironment.buffer_sources(@buffer)
|
|
333
|
+
),
|
|
334
|
+
**ProjectEnvironment.dependency_discovery_options(@configuration)
|
|
335
|
+
)
|
|
336
|
+
end
|
|
337
|
+
end
|
|
338
|
+
|
|
297
339
|
def trust_refusals(registry, baseline)
|
|
298
340
|
registry.plugins.filter_map do |plugin|
|
|
299
341
|
summary = plugin.io_boundary.refusal_summary
|
|
@@ -336,8 +378,8 @@ module Rigor
|
|
|
336
378
|
# Builds the {Rigor::Plugin::TrustPolicy} for this run. Trusted gems are the gem-name half of every
|
|
337
379
|
# entry in `Configuration#plugins`. Allowed read roots default to the project root (CWD), the
|
|
338
380
|
# project's signature_paths, and each trusted gem's `Gem::Specification#full_gem_path`, plus any
|
|
339
|
-
# extras the user listed under `plugins_io.allowed_paths`.
|
|
340
|
-
#
|
|
381
|
+
# extras the user listed under `plugins_io.allowed_paths`. `network_policy` is `plugins_io.network`
|
|
382
|
+
# (`:disabled` or `:allowlist`).
|
|
341
383
|
def build_trust_policy
|
|
342
384
|
trusted_gems = @configuration.plugins.map { |entry| trusted_gem_name(entry) }.uniq
|
|
343
385
|
roots = [Dir.pwd]
|
|
@@ -367,7 +409,7 @@ module Rigor
|
|
|
367
409
|
return nil if gem_name.nil? || gem_name.empty?
|
|
368
410
|
|
|
369
411
|
spec = Gem.loaded_specs[gem_name]
|
|
370
|
-
spec&.full_gem_path
|
|
412
|
+
spec&.full_gem_path
|
|
371
413
|
rescue StandardError
|
|
372
414
|
nil
|
|
373
415
|
end
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rigor
|
|
4
|
+
module Analysis
|
|
5
|
+
class Runner
|
|
6
|
+
# ADR-116 WD3 (#1195) — the run-level fact registry. A run-level fact is a fact about the RUN rather than
|
|
7
|
+
# about one file: the project-signature state, the definition-build failures, the HKT-scan outcome, the
|
|
8
|
+
# `RunStats` RBS tables, the effect-annotation carrier. Each is declared ONCE below, and every path that
|
|
9
|
+
# used to hand-list the slots — the {RunSnapshots} store, the {PoolCoordinator} backends, the
|
|
10
|
+
# {WorkerSession} drain, the {DiagnosticAggregator} rows and the incremental snapshot's
|
|
11
|
+
# `RunLevelRows` — reads the declarations instead.
|
|
12
|
+
#
|
|
13
|
+
# A {Fact} carries:
|
|
14
|
+
#
|
|
15
|
+
# - `name` and `empty` — the store key and its value before anything is recorded;
|
|
16
|
+
# - `merge` — how a recorded value folds into the slot: {Merge.assign}, {Merge.first_wins} (#784) or
|
|
17
|
+
# {Merge.union_by_class} (#696);
|
|
18
|
+
# - `source` — `:environment` (read off a coordinator-side environment before the per-file loop, so a
|
|
19
|
+
# fork child inherits the demand it makes), `:analysis` (read after the loop, and drained out of each
|
|
20
|
+
# pool worker, because the per-file analysis is what demands it) or `:stats` (`RunStats` telemetry,
|
|
21
|
+
# read only when stats are collected);
|
|
22
|
+
# - `gate` — for an `:environment` fact, which environment it is read from; an empty-closure run
|
|
23
|
+
# resolves one per gate, lazily, and every other path hands the same environment to both;
|
|
24
|
+
# - `demand` — for an `:analysis` fact, whether collecting it makes a demand of its own (the HKT
|
|
25
|
+
# registry build, #784) rather than reading what the analysis already demanded. A path that must not
|
|
26
|
+
# pay for a demand (the Ractor prewarm, an empty closure with a replay in hand) drains only the others;
|
|
27
|
+
# - `replay` — whether a narrowed incremental run replays the previous full run's value
|
|
28
|
+
# ({Cache::IncrementalSnapshot::RunLevelRows});
|
|
29
|
+
# - `row` / `position` — the {DiagnosticAggregator} method that turns the slot into diagnostics, and
|
|
30
|
+
# where that row sits in the post-analysis stream (docs/type-specification/diagnostic-policy.md fixes
|
|
31
|
+
# the order). Nil for a fact no diagnostic reads.
|
|
32
|
+
#
|
|
33
|
+
# The declaration ORDER is the collection order: {.harvest} reads the `:environment` facts in it, and
|
|
34
|
+
# that order is load-bearing (the stand-downs are read before the env-forcing reads, the conformance
|
|
35
|
+
# scan — which demands definitions — last). The row order is `position`, independently.
|
|
36
|
+
#
|
|
37
|
+
# Rows whose producer is not a slot here — the reporter streams, which the reporters snapshot and
|
|
38
|
+
# absorb themselves, and the two plugin tables the coordinator collects — are declared as {Row}s so
|
|
39
|
+
# the whole post-analysis order lives in one place.
|
|
40
|
+
#
|
|
41
|
+
# Everything here is frozen data plus module functions, never a closure: {WorkerSession#drain_reporters}
|
|
42
|
+
# reads these declarations inside a Ractor worker, and a drained value has to stay Marshal-clean (#805)
|
|
43
|
+
# for the fork backend.
|
|
44
|
+
module RunLevelFacts
|
|
45
|
+
Fact = Data.define(:name, :empty, :merge, :source, :gate, :demand, :replay, :row, :position)
|
|
46
|
+
Row = Data.define(:builder, :position)
|
|
47
|
+
|
|
48
|
+
# The predicates an `:environment` collector consults, taken by the coordinator when it harvests.
|
|
49
|
+
Context = Data.define(:project_signature_paths, :plugin_signature_paths, :record_effects)
|
|
50
|
+
|
|
51
|
+
# How a recorded value folds into its slot.
|
|
52
|
+
module Merge
|
|
53
|
+
module_function
|
|
54
|
+
|
|
55
|
+
def assign(_current, incoming)
|
|
56
|
+
incoming
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Issue #784 — the HKT scan is one build over the overlay every worker shares, so the first tuple
|
|
60
|
+
# recorded is the run's outcome.
|
|
61
|
+
def first_wins(current, incoming)
|
|
62
|
+
current.nil? ? incoming : current
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Issue #696 / #1902 — each pool worker holds its own loader, so the run's set is the union, one
|
|
66
|
+
# tuple per class chosen by {Environment::RbsLoader.definition_build_detail_order} rather than by
|
|
67
|
+
# arrival.
|
|
68
|
+
def union_by_class(current, incoming)
|
|
69
|
+
return current if incoming.nil? || incoming.empty?
|
|
70
|
+
|
|
71
|
+
(current + incoming)
|
|
72
|
+
.group_by(&:first)
|
|
73
|
+
.map { |_, candidates| candidates.min_by { |c| Environment::RbsLoader.definition_build_detail_order(c) } }
|
|
74
|
+
.freeze
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# One collector per fact, named after it: `(environment, context) -> value`.
|
|
79
|
+
module Collect
|
|
80
|
+
module_function
|
|
81
|
+
|
|
82
|
+
# Issue #610 — gated on a loaded plugin contributing signatures, not on `signature_paths:`: the
|
|
83
|
+
# source a plugin's `sig/` stands down against is typically an `rbs collection install`.
|
|
84
|
+
def signature_standdowns(environment, context)
|
|
85
|
+
return [] unless context.plugin_signature_paths
|
|
86
|
+
|
|
87
|
+
loader = environment&.rbs_loader
|
|
88
|
+
return [] if loader.nil? || !loader.respond_to?(:deferred_signature_paths)
|
|
89
|
+
return [] if loader.deferred_signature_paths.empty?
|
|
90
|
+
|
|
91
|
+
loader.signature_standdowns
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Issue #1713 — not behind the `signature_paths:` gate either; the loader answers without building
|
|
95
|
+
# its env when no gated directory is active.
|
|
96
|
+
def vendored_standdowns(environment, _context)
|
|
97
|
+
loader = environment&.rbs_loader
|
|
98
|
+
loader.respond_to?(:vendored_standdowns) ? loader.vendored_standdowns : []
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# The four below read the project's OWN `signature_paths:` state, and each forces the otherwise-lazy
|
|
102
|
+
# env build, so they are gated on the project declaring `signature_paths:`.
|
|
103
|
+
def synthesized_namespaces(environment, context)
|
|
104
|
+
return [] unless context.project_signature_paths
|
|
105
|
+
|
|
106
|
+
environment&.rbs_loader&.synthesized_namespaces || []
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Read after the env-forcing read above: the collision derivation reads the BUILT env.
|
|
110
|
+
def quarantined_signatures(environment, context)
|
|
111
|
+
return [] unless context.project_signature_paths
|
|
112
|
+
|
|
113
|
+
environment&.rbs_loader&.quarantined_signatures || []
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def env_build_failure(environment, context)
|
|
117
|
+
return nil unless context.project_signature_paths
|
|
118
|
+
|
|
119
|
+
environment&.rbs_loader&.env_build_failure
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Read LAST of the signature state: the scan demands the definition of every `rigor:v1:conforms-to`
|
|
123
|
+
# class, and a resulting definition-build failure has to be in the loader before the `:analysis`
|
|
124
|
+
# read of {.definition_build_failures}.
|
|
125
|
+
def conformance_results(environment, context)
|
|
126
|
+
return [] unless context.project_signature_paths
|
|
127
|
+
|
|
128
|
+
RbsExtended::ConformanceChecker.scan(environment&.rbs_loader)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# #441 — the inline stratum of `effect.annotations-unchecked`. Skipped when effect collection is on:
|
|
132
|
+
# {EffectEnvelopePass} reads the loader directly then.
|
|
133
|
+
def effect_annotation_carrier(environment, context)
|
|
134
|
+
virtual = context.record_effects ? nil : environment&.rbs_loader&.virtual_rbs
|
|
135
|
+
Effects::SignatureSources.annotated_carrier(virtual)
|
|
136
|
+
rescue StandardError
|
|
137
|
+
[].freeze
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Issue #696 — read after the loop: definition builds are lazy, per class, on first demand. NOT
|
|
141
|
+
# gated on `signature_paths:` like the signature state: nothing is forced here (this reads what the
|
|
142
|
+
# analysis already demanded), and two BUNDLED sources can collapse a class with no project sig set
|
|
143
|
+
# in sight (the `bigdecimal` / `BigMath` shape, #299).
|
|
144
|
+
def definition_build_failures(environment, _context)
|
|
145
|
+
environment&.rbs_loader&.definition_build_failures || []
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Issue #784 — the run demands the registry once itself, so the outcome does not depend on which
|
|
149
|
+
# files were analysed. One build, one outcome whoever demands it, so a Rigor-owned demand cannot
|
|
150
|
+
# change what is reported (unlike #696's per-class list).
|
|
151
|
+
def hkt_scan_failure(environment, _context)
|
|
152
|
+
return nil if environment.nil?
|
|
153
|
+
|
|
154
|
+
environment.hkt_registry
|
|
155
|
+
environment.hkt_scan_failure
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def class_decl_paths(environment, _context)
|
|
159
|
+
environment&.rbs_loader&.class_decl_paths || {}.freeze
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def signature_paths(environment, _context)
|
|
163
|
+
environment&.rbs_loader&.signature_paths || [].freeze
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
EMPTY_LIST = [].freeze
|
|
168
|
+
EMPTY_TABLE = {}.freeze
|
|
169
|
+
|
|
170
|
+
FACTS = [
|
|
171
|
+
Fact.new(name: :signature_standdowns, empty: EMPTY_LIST, merge: :assign, source: :environment,
|
|
172
|
+
gate: :signature_state, demand: false, replay: false,
|
|
173
|
+
row: :rbs_plugin_signature_stood_down_diagnostics, position: 60),
|
|
174
|
+
Fact.new(name: :vendored_standdowns, empty: EMPTY_LIST, merge: :assign, source: :environment,
|
|
175
|
+
gate: :signature_state, demand: false, replay: false,
|
|
176
|
+
row: :rbs_vendored_signature_stood_down_diagnostics, position: 70),
|
|
177
|
+
Fact.new(name: :synthesized_namespaces, empty: EMPTY_LIST, merge: :assign, source: :environment,
|
|
178
|
+
gate: :signature_state, demand: false, replay: false,
|
|
179
|
+
row: :rbs_synthesized_namespace_diagnostics, position: 50),
|
|
180
|
+
Fact.new(name: :quarantined_signatures, empty: EMPTY_LIST, merge: :assign, source: :environment,
|
|
181
|
+
gate: :signature_state, demand: false, replay: false,
|
|
182
|
+
row: :rbs_quarantined_signature_diagnostics, position: 10),
|
|
183
|
+
Fact.new(name: :env_build_failure, empty: nil, merge: :assign, source: :environment,
|
|
184
|
+
gate: :signature_state, demand: false, replay: false,
|
|
185
|
+
row: :rbs_environment_build_failed_diagnostics, position: 20),
|
|
186
|
+
Fact.new(name: :conformance_results, empty: EMPTY_LIST, merge: :assign, source: :environment,
|
|
187
|
+
gate: :signature_state, demand: false, replay: false,
|
|
188
|
+
row: :conforms_to_diagnostics, position: 80),
|
|
189
|
+
# Read by {EffectAnnotationResidualPass}, outside this stream.
|
|
190
|
+
Fact.new(name: :effect_annotation_carrier, empty: EMPTY_LIST, merge: :assign, source: :environment,
|
|
191
|
+
gate: :annotation_carrier, demand: false, replay: false, row: nil, position: nil),
|
|
192
|
+
Fact.new(name: :definition_build_failures, empty: EMPTY_LIST, merge: :union_by_class, source: :analysis,
|
|
193
|
+
gate: nil, demand: false, replay: true,
|
|
194
|
+
row: :rbs_definition_build_failed_diagnostics, position: 30),
|
|
195
|
+
Fact.new(name: :hkt_scan_failure, empty: nil, merge: :first_wins, source: :analysis,
|
|
196
|
+
gate: nil, demand: true, replay: true,
|
|
197
|
+
row: :rbs_hkt_scan_failed_diagnostics, position: 40),
|
|
198
|
+
Fact.new(name: :class_decl_paths, empty: EMPTY_TABLE, merge: :assign, source: :stats,
|
|
199
|
+
gate: nil, demand: false, replay: false, row: nil, position: nil),
|
|
200
|
+
Fact.new(name: :signature_paths, empty: EMPTY_LIST, merge: :assign, source: :stats,
|
|
201
|
+
gate: nil, demand: false, replay: false, row: nil, position: nil)
|
|
202
|
+
].freeze
|
|
203
|
+
|
|
204
|
+
# The rows no slot above produces. The reporter streams follow the `rbs.coverage.*` ladder and the
|
|
205
|
+
# conformance rows; the plugin-authored run disclosures (#1051) come before the engine-authored trust
|
|
206
|
+
# refusal envelope (#959), which is read last so every per-file plugin call has recorded its refusals.
|
|
207
|
+
ROWS = [
|
|
208
|
+
Row.new(builder: :unresolved_payload_diagnostics, position: 90),
|
|
209
|
+
Row.new(builder: :lossy_projection_diagnostics, position: 100),
|
|
210
|
+
Row.new(builder: :hkt_directive_diagnostics, position: 110),
|
|
211
|
+
Row.new(builder: :deprecated_form_diagnostics, position: 120),
|
|
212
|
+
Row.new(builder: :boundary_cross_diagnostics, position: 130),
|
|
213
|
+
Row.new(builder: :source_rbs_synthesis_diagnostics, position: 140),
|
|
214
|
+
Row.new(builder: :plugin_run_disclosure_diagnostics, position: 150),
|
|
215
|
+
Row.new(builder: :plugin_trust_refusal_diagnostics, position: 160)
|
|
216
|
+
].freeze
|
|
217
|
+
|
|
218
|
+
# The per-run reporter accumulators, by drain key. Each reporter snapshots and absorbs its own
|
|
219
|
+
# streams (`#snapshot` / `#absorb`), so a new stream inside one touches that reporter and its row.
|
|
220
|
+
REPORTERS = %i[rbs_extended boundary_cross source_rbs_synthesis].freeze
|
|
221
|
+
|
|
222
|
+
BY_NAME = FACTS.to_h { |fact| [fact.name, fact] }.freeze
|
|
223
|
+
|
|
224
|
+
# Read inside a Ractor worker's drain, so every constant here is deeply shareable.
|
|
225
|
+
Ractor.make_shareable(FACTS)
|
|
226
|
+
Ractor.make_shareable(ROWS)
|
|
227
|
+
Ractor.make_shareable(BY_NAME)
|
|
228
|
+
|
|
229
|
+
module_function
|
|
230
|
+
|
|
231
|
+
def facts
|
|
232
|
+
FACTS
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def fact(name)
|
|
236
|
+
BY_NAME.fetch(name)
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def names(source: nil)
|
|
240
|
+
(source ? FACTS.select { |fact| fact.source == source } : FACTS).map(&:name)
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def replayable_names
|
|
244
|
+
FACTS.select(&:replay).map(&:name)
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def empty_values
|
|
248
|
+
FACTS.to_h { |fact| [fact.name, fact.empty] }
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# The post-analysis diagnostic builders, in their contract order.
|
|
252
|
+
def row_builders
|
|
253
|
+
(FACTS.select(&:row).map { |fact| Row.new(builder: fact.row, position: fact.position) } + ROWS)
|
|
254
|
+
.sort_by(&:position)
|
|
255
|
+
.map(&:builder)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# Collects every fact of `source`, in declaration order, from the environment the block yields for
|
|
259
|
+
# its gate (one call per gate, on first need). `:analysis` and `:stats` facts take the block's
|
|
260
|
+
# environment with a nil gate.
|
|
261
|
+
def harvest(source, context)
|
|
262
|
+
environments = {}
|
|
263
|
+
FACTS.each_with_object({}) do |fact, collected|
|
|
264
|
+
next unless fact.source == source
|
|
265
|
+
|
|
266
|
+
environment = environments.fetch(fact.gate) { environments[fact.gate] = yield(fact.gate) }
|
|
267
|
+
collected[fact.name] = Collect.public_send(fact.name, environment, context)
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# The `:analysis` facts as an analysing process reports them: the {WorkerSession} drain, and the
|
|
272
|
+
# coordinator's own environment on the paths that analyse on it. Plain tuples — Marshal-clean and
|
|
273
|
+
# shareable.
|
|
274
|
+
#
|
|
275
|
+
# @param demanding — nil drains every `:analysis` fact; false only those that read what was already
|
|
276
|
+
# demanded; true only those that make a demand of their own.
|
|
277
|
+
def drain(environment, demanding: nil)
|
|
278
|
+
FACTS.each_with_object({}) do |fact, drained|
|
|
279
|
+
next unless fact.source == :analysis
|
|
280
|
+
next unless demanding.nil? || fact.demand == demanding
|
|
281
|
+
|
|
282
|
+
drained[fact.name] = Collect.public_send(fact.name, environment, nil)
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
# The `:analysis` facts out of a drained hash, for {RunSnapshots#absorb}.
|
|
287
|
+
def drained_facts(drained)
|
|
288
|
+
names(source: :analysis).each_with_object({}) do |name, facts|
|
|
289
|
+
facts[name] = drained[name] if drained.key?(name)
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def snapshot_reporters(reporters)
|
|
294
|
+
REPORTERS.to_h { |name| [name, reporters.fetch(name).snapshot] }
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
# A drain shape that lacks a reporter's key records nothing for it.
|
|
298
|
+
def absorb_reporters(reporters, drained)
|
|
299
|
+
REPORTERS.each { |name| reporters.fetch(name).absorb(drained[name]) }
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
end
|
|
@@ -1,68 +1,58 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "run_level_facts"
|
|
4
|
+
|
|
3
5
|
module Rigor
|
|
4
6
|
module Analysis
|
|
5
7
|
class Runner
|
|
6
|
-
# Mutable per-run
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
8
|
+
# Mutable per-run store for the run-level facts ({RunLevelFacts}): one slot per declared fact, keyed by
|
|
9
|
+
# its name. The analysis paths write it ({PoolCoordinator} — sequential, fork-pool, Ractor-pool,
|
|
10
|
+
# fallback, empty closure) and the rest of the run reads it back: the {DiagnosticAggregator} rows,
|
|
11
|
+
# {RunStats}, {EffectAnnotationResidualPass} and the incremental snapshot's `RunLevelRows`.
|
|
12
|
+
#
|
|
13
|
+
# A shared mutable holder keeps the write/read timing of the original inline ivars while letting the
|
|
14
|
+
# writer and the readers live in separate objects without a back-reference cycle.
|
|
11
15
|
#
|
|
12
|
-
# The
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
# back-reference cycle.
|
|
16
|
+
# The analysis paths write through {#absorb}, which folds each value in by its fact's declared merge
|
|
17
|
+
# rule. `[]=` and the per-fact `name=` writers (defined from the declarations, like the readers) are a
|
|
18
|
+
# plain assignment that bypasses the merge rule: they exist for specs that seed a slot, and no
|
|
19
|
+
# production path calls them.
|
|
17
20
|
class RunSnapshots
|
|
18
|
-
attr_accessor :class_decl_paths, :signature_paths,
|
|
19
|
-
:synthesized_namespaces, :quarantined_signatures, :signature_standdowns, :vendored_standdowns,
|
|
20
|
-
:conformance_results,
|
|
21
|
-
:env_build_failure, :definition_build_failures, :hkt_scan_failure,
|
|
22
|
-
:effect_annotation_carrier
|
|
23
|
-
|
|
24
|
-
# Constructor defaults match the {Runner} constructor: the pre-seed values `build_run_stats` /
|
|
25
|
-
# `pre_file_diagnostics` read before the first analysis path runs are frozen empties. The
|
|
26
|
-
# `env_build_failure` slot is nil (no failure) rather than an empty collection — it holds a single
|
|
27
|
-
# `[error_class, first_line, buffer_names]` tuple or nothing. Its per-class sibling
|
|
28
|
-
# `definition_build_failures` (#696) holds a LIST, because a collapsed universe fails many classes,
|
|
29
|
-
# and is accumulated ACROSS pool workers rather than assigned once — see
|
|
30
|
-
# {PoolCoordinator#merge_worker_reporters}. `hkt_scan_failure` (#784) is nil-or-tuple like
|
|
31
|
-
# `env_build_failure`, not a list like `definition_build_failures`: the scan is ONE build over the
|
|
32
|
-
# whole `signature_paths:` overlay, not a per-class one, so it has exactly one outcome per run.
|
|
33
|
-
# `signature_standdowns` (#610) is the quarantine slot's twin for the plugin-contributed files that
|
|
34
|
-
# stood down against a colliding generic arity: a list, assigned once from the loader as
|
|
35
|
-
# `quarantined_signatures` is, and derived from the final env so a cache HIT carries it too.
|
|
36
|
-
# `vendored_standdowns` (#1713) is the same shape of slot for what a required-feature-gated vendored
|
|
37
|
-
# directory left out because another signature source declares it.
|
|
38
21
|
def initialize
|
|
39
|
-
@
|
|
40
|
-
@signature_paths = [].freeze
|
|
41
|
-
@synthesized_namespaces = [].freeze
|
|
42
|
-
@quarantined_signatures = [].freeze
|
|
43
|
-
@signature_standdowns = [].freeze
|
|
44
|
-
@vendored_standdowns = [].freeze
|
|
45
|
-
@conformance_results = [].freeze
|
|
46
|
-
@env_build_failure = nil
|
|
47
|
-
@definition_build_failures = [].freeze
|
|
48
|
-
@hkt_scan_failure = nil
|
|
49
|
-
@effect_annotation_carrier = [].freeze
|
|
22
|
+
@values = RunLevelFacts.empty_values
|
|
50
23
|
end
|
|
51
24
|
|
|
52
|
-
# Per-`#run` reset
|
|
53
|
-
# from the frozen constructor defaults) at the top of each run.
|
|
25
|
+
# Per-`#run` reset to every fact's declared empty value.
|
|
54
26
|
def reset_for_run
|
|
55
|
-
@
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
@
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
@
|
|
27
|
+
@values = RunLevelFacts.empty_values
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def [](name)
|
|
31
|
+
RunLevelFacts.fact(name)
|
|
32
|
+
@values[name]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def []=(name, value)
|
|
36
|
+
RunLevelFacts.fact(name)
|
|
37
|
+
@values[name] = value
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Folds `facts` (`{name => value}`) into the store, each by its fact's merge rule.
|
|
41
|
+
def absorb(facts)
|
|
42
|
+
facts.each do |name, value|
|
|
43
|
+
merge = RunLevelFacts.fact(name).merge
|
|
44
|
+
@values[name] = RunLevelFacts::Merge.public_send(merge, @values[name], value)
|
|
45
|
+
end
|
|
46
|
+
self
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def to_h
|
|
50
|
+
@values.dup
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
RunLevelFacts.facts.each do |fact|
|
|
54
|
+
define_method(fact.name) { @values[fact.name] }
|
|
55
|
+
define_method(:"#{fact.name}=") { |value| @values[fact.name] = value }
|
|
66
56
|
end
|
|
67
57
|
end
|
|
68
58
|
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rigor
|
|
4
|
+
module Analysis
|
|
5
|
+
class Runner
|
|
6
|
+
# Issue #476 — the read-only, lazily built view of the project's RBS universe that the ADR-16 Tier B
|
|
7
|
+
# (trait registry) half of {Inference::SyntheticMethodScanner} reads.
|
|
8
|
+
#
|
|
9
|
+
# The scanner needs exactly two answers from an environment: `#rbs_loader` (to explode a trait module such
|
|
10
|
+
# as `Devise::Models::Recoverable` into its instance-method names) and `#class_ordering` (to decide
|
|
11
|
+
# whether a project class descends from an RBS-known `receiver_constraint` such as
|
|
12
|
+
# `ActiveRecord::Base`). Neither reads the synthetic-method index — `Environment.for_project` only stores
|
|
13
|
+
# that index, the RBS loader never consults it — so the chicken-and-egg the issue describes (the env
|
|
14
|
+
# consumes the scan's output) does not reach the loader: the view builds an environment WITHOUT the
|
|
15
|
+
# index, the scan runs against it, and the run's real environment is built afterwards WITH the index.
|
|
16
|
+
#
|
|
17
|
+
# The build is deferred to the first question. A scan whose trait registries never match a call site
|
|
18
|
+
# (no `devise` call in any class body) never asks, so it never pays the environment build. The build is
|
|
19
|
+
# fail-soft: a raise leaves the view answering "unknown", which is what the nil environment answered
|
|
20
|
+
# before — Tier B emits nothing rather than aborting the pre-pass.
|
|
21
|
+
class ScanEnvironment
|
|
22
|
+
# @param builder — called at most once, returns the {Rigor::Environment} (without a synthetic index).
|
|
23
|
+
def initialize(&builder)
|
|
24
|
+
@builder = builder
|
|
25
|
+
@built = false
|
|
26
|
+
@environment = nil
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def rbs_loader
|
|
30
|
+
environment&.rbs_loader
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def class_ordering(lhs, rhs)
|
|
34
|
+
env = environment
|
|
35
|
+
env.nil? ? :unknown : env.class_ordering(lhs, rhs)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def environment
|
|
41
|
+
return @environment if @built
|
|
42
|
+
|
|
43
|
+
@built = true
|
|
44
|
+
@environment = @builder.call
|
|
45
|
+
rescue StandardError
|
|
46
|
+
@environment = nil
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rigor
|
|
4
|
+
module Analysis
|
|
5
|
+
class Runner
|
|
6
|
+
# ADR-116 WD3 (#1195) — what one analysing {WorkerSession} hands back to the {PoolCoordinator}: its
|
|
7
|
+
# per-file results and every drain, taken in one place ({.harvest}) and folded in by one
|
|
8
|
+
# ({PoolCoordinator#absorb_worker_payload}). A fork child marshals it to its payload file, the Ractor
|
|
9
|
+
# backend sends it with `:done`, and the fork backend's degrade path harvests the parent session the
|
|
10
|
+
# same way.
|
|
11
|
+
#
|
|
12
|
+
# Every field is Marshal-clean (#805) and, on the Ractor backend, copyable: the drains are frozen
|
|
13
|
+
# `Data` entries and Hashes of Strings. `effects` is nil unless the run collects effects (ADR-103 WD13).
|
|
14
|
+
#
|
|
15
|
+
# The refusals are carried but not absorbed: their merge order is the file order of the whole run, so
|
|
16
|
+
# each backend merges them itself (#1900).
|
|
17
|
+
WorkerPayload = Data.define(:results, :reporters, :dependencies, :analysis_reads, :analysis_disclosures,
|
|
18
|
+
:disclosures, :refusals, :effects) do
|
|
19
|
+
# Drains `session` in the order the fork child always has: the reporters (which demand the HKT
|
|
20
|
+
# registry) before the refusal delta, so a refusal that demand makes is reported.
|
|
21
|
+
def self.harvest(session, results: {}, record_effects: false)
|
|
22
|
+
new(
|
|
23
|
+
results: results,
|
|
24
|
+
reporters: session.drain_reporters,
|
|
25
|
+
dependencies: session.drain_dependencies,
|
|
26
|
+
analysis_reads: session.drain_analysis_reads,
|
|
27
|
+
analysis_disclosures: session.drain_analysis_disclosures,
|
|
28
|
+
disclosures: session.drain_run_disclosures,
|
|
29
|
+
refusals: session.drain_trust_refusals,
|
|
30
|
+
effects: record_effects ? session.drain_effects : nil
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|