rigortype 0.4.6 → 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 +50 -3
- data/docs/handbook/appendix-phpstan.md +4 -4
- data/docs/manual/02-cli-reference.md +152 -26
- data/docs/manual/03-configuration.md +36 -3
- data/docs/manual/06-baseline.md +6 -4
- data/docs/manual/09-editor-integration.md +13 -2
- data/docs/manual/10-mcp-server.md +38 -2
- data/docs/manual/12-caching.md +4 -3
- data/docs/manual/plugins/rigor-actionpack.md +19 -11
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +20 -1
- data/lib/rigor/analysis/baseline.rb +24 -27
- 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 +85 -13
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +3 -2
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +0 -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/rebinding_block_callees.rb +3 -3
- data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
- 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 +2 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules.rb +124 -44
- data/lib/rigor/analysis/crash_signature.rb +2 -2
- data/lib/rigor/analysis/dependency_recorder.rb +4 -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 +93 -6
- data/lib/rigor/analysis/fact_store.rb +3 -3
- 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 +27 -16
- data/lib/rigor/analysis/rule_catalog.rb +2 -2
- data/lib/rigor/analysis/run_stats.rb +2 -2
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +2 -2
- data/lib/rigor/analysis/runner/declaration_position.rb +1 -1
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +193 -93
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +1 -1
- data/lib/rigor/analysis/runner/pool_coordinator.rb +147 -323
- data/lib/rigor/analysis/runner/project_pre_passes.rb +3 -3
- 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 +77 -71
- 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 +24 -31
- 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 +31 -12
- 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 +108 -102
- 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 +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 +12 -8
- 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 +84 -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 +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 +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 +209 -54
- data/lib/rigor/config_audit.rb +1 -1
- data/lib/rigor/configuration/severity_profile.rb +5 -5
- data/lib/rigor/configuration.rb +72 -30
- 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 +480 -67
- 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 +41 -10
- 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 +88 -77
- 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 +39 -7
- 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 +46 -4
- data/lib/rigor/inference/definer_resolution.rb +4 -2
- 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/expression_typer.rb +116 -49
- data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
- data/lib/rigor/inference/global_write_census.rb +76 -2
- 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 +51 -0
- data/lib/rigor/inference/key_presence_guard.rb +2 -1
- data/lib/rigor/inference/loop_value_record.rb +8 -4
- data/lib/rigor/inference/match_rebinding/frame.rb +46 -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 +3 -3
- 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 +1 -1
- 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 +9 -7
- 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 +64 -14
- data/lib/rigor/inference/module_function_state.rb +11 -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 +157 -27
- data/lib/rigor/inference/narrowing.rb +47 -33
- 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_yielder.rb +11 -17
- 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 +0 -1
- data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
- data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +58 -15
- 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.rb +137 -46
- data/lib/rigor/inference/self_call_ivar_writes.rb +84 -12
- 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 +2328 -138
- data/lib/rigor/inference/struct_fold_safety.rb +4 -6
- data/lib/rigor/inference/synthetic_method_scanner.rb +5 -6
- 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 +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/plugins.rb +1 -1
- data/lib/rigor/mcp/server.rb +73 -2
- data/lib/rigor/mcp.rb +1 -1
- data/lib/rigor/plugin/access_denied_error.rb +1 -2
- 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 +9 -13
- data/lib/rigor/plugin/registry.rb +3 -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 +44 -5
- 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 +15 -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 +19 -6
- data/lib/rigor/scope/resolution_chain.rb +3 -1
- data/lib/rigor/scope.rb +365 -31
- 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 +371 -58
- 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 +3 -5
- 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/renderer.rb +15 -9
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +15 -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 +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 +9 -13
- data/lib/rigor/type/constant.rb +4 -5
- data/lib/rigor/type/hash_shape.rb +21 -40
- 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 +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 +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +66 -19
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +4 -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-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 +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 +1 -1
- 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 +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/sig/rigor/analysis/baseline.rbs +0 -3
- 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/registry.rbs +2 -2
- data/sig/rigor/inference.rbs +4 -1
- data/sig/rigor/plugin/additional_initializer.rbs +0 -5
- data/sig/rigor/plugin/base.rbs +1 -0
- 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/io_boundary.rbs +2 -1
- data/sig/rigor/plugin/manifest.rbs +9 -9
- data/sig/rigor/plugin/protocol_contract.rbs +3 -7
- data/sig/rigor/scope.rbs +23 -3
- 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 +36 -3
|
@@ -10,6 +10,7 @@ require_relative "../source/constant_path"
|
|
|
10
10
|
require_relative "../source/node_children"
|
|
11
11
|
require_relative "../source/node_walker"
|
|
12
12
|
require_relative "../analysis/self_call_resolution_recorder"
|
|
13
|
+
require_relative "../run_scoped_slots"
|
|
13
14
|
require_relative "block_call_timing"
|
|
14
15
|
require_relative "block_repetition"
|
|
15
16
|
require_relative "project_yielder"
|
|
@@ -345,7 +346,7 @@ module Rigor
|
|
|
345
346
|
self.class.recorded_inject_accumulator(block, scope)
|
|
346
347
|
end
|
|
347
348
|
|
|
348
|
-
INJECT_ACCUMULATOR_KEY = :__rigor_inject_accumulator__
|
|
349
|
+
INJECT_ACCUMULATOR_KEY = RunScopedSlots.register(:__rigor_inject_accumulator__)
|
|
349
350
|
private_constant :INJECT_ACCUMULATOR_KEY
|
|
350
351
|
|
|
351
352
|
# The accumulator type {#record_inject_accumulator} stored for `block` when the call was typed from `scope`
|
|
@@ -360,13 +361,18 @@ module Rigor
|
|
|
360
361
|
# recording run to harvest each analyzed callee's observed call keys → return descriptors. Returns an
|
|
361
362
|
# empty hash when no bucket exists (a run that memoised nothing). Class method: the bucket lives on
|
|
362
363
|
# `Thread.current`, independent of any one `ExpressionTyper` instance.
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
364
|
+
#
|
|
365
|
+
# `bucket` defaults to the calling thread's current one; `Runner#run` keeps the bucket its run filled
|
|
366
|
+
# ({.current_return_memo}) and passes it, since the run releases the thread's slot when it ends.
|
|
367
|
+
def self.harvest_return_memo(bucket = current_return_memo)
|
|
368
|
+
return {} if bucket.nil?
|
|
366
369
|
|
|
367
|
-
|
|
370
|
+
bucket.transform_values(&:values)
|
|
368
371
|
end
|
|
369
372
|
|
|
373
|
+
# The calling thread's return memo bucket (`{ def_node => { descriptor => MemoEntry } }`), or nil.
|
|
374
|
+
def self.current_return_memo = Thread.current[RETURN_MEMO_KEY]&.last
|
|
375
|
+
|
|
370
376
|
# Issue #720 — the value the block passed by the call site currently being re-typed evaluates to, or
|
|
371
377
|
# `nil` outside such a frame. Read by {#type_of_yield}.
|
|
372
378
|
#
|
|
@@ -1100,7 +1106,6 @@ module Rigor
|
|
|
1100
1106
|
conditions = when_node.respond_to?(:conditions) ? when_node.conditions : []
|
|
1101
1107
|
body_scope, next_scope = Narrowing.case_when_scopes(node.predicate, conditions, clause_scope)
|
|
1102
1108
|
certainty = case_when_branch_certainty(subject_type, when_node)
|
|
1103
|
-
# :no — drop the branch
|
|
1104
1109
|
candidates << case_arm_type(when_node, body_scope) unless certainty == :no
|
|
1105
1110
|
if certainty == :yes
|
|
1106
1111
|
reached_yes = true
|
|
@@ -1519,9 +1524,8 @@ module Rigor
|
|
|
1519
1524
|
|
|
1520
1525
|
# Stable single-hop chain narrowing — `receiver.method` after an `is_a?` / `kind_of?` / `instance_of?`
|
|
1521
1526
|
# predicate established the narrowing on the dominated edge. The call MUST be no-arg + no-block +
|
|
1522
|
-
# rooted at a local-var / ivar read; everything else falls through to the standard dispatcher.
|
|
1523
|
-
#
|
|
1524
|
-
# single-hop scope.
|
|
1527
|
+
# rooted at a local-var / ivar read; everything else falls through to the standard dispatcher. The
|
|
1528
|
+
# single hop is Law-of-Demeter-justified.
|
|
1525
1529
|
def method_chain_narrowing_for(node)
|
|
1526
1530
|
return nil unless node.is_a?(Prism::CallNode)
|
|
1527
1531
|
return nil unless node.block.nil?
|
|
@@ -1771,7 +1775,23 @@ module Rigor
|
|
|
1771
1775
|
def call_type_for(node)
|
|
1772
1776
|
return safe_navigation_call_type(node) if node.safe_navigation?
|
|
1773
1777
|
|
|
1774
|
-
attribute_write_value(node, call_dispatch_type_for(node))
|
|
1778
|
+
memo_object_value(node, attribute_write_value(node, call_dispatch_type_for(node)))
|
|
1779
|
+
end
|
|
1780
|
+
|
|
1781
|
+
# Issue #2411 — `recv.each_with_object(memo) { |x, acc| acc[k] = v }` returns the memo as the block's
|
|
1782
|
+
# stores leave it. The statement evaluator owns that join ({StatementEvaluator#each_with_object_value}),
|
|
1783
|
+
# and reading it here gives every value position the statement's answer: a method's tail is typed through
|
|
1784
|
+
# this typer, and read off the dispatch alone it was the memo argument's literal `{}` / `[]`. A `break` ends
|
|
1785
|
+
# the call with its own value, so the block's break arms ({#call_break_arm_types}) join the memo: the join
|
|
1786
|
+
# alone read `break :stop if x == 2; a << x` as `Array[1 | 2]`, where Ruby answers `:stop`.
|
|
1787
|
+
def memo_object_value(node, result)
|
|
1788
|
+
return result unless node.name == :each_with_object && node.block.is_a?(Prism::BlockNode)
|
|
1789
|
+
|
|
1790
|
+
memo = StatementEvaluator.new(scope: scope, tracer: tracer).each_with_object_value(node, result)
|
|
1791
|
+
return memo if memo.equal?(result)
|
|
1792
|
+
|
|
1793
|
+
breaks = call_break_arm_types(node)
|
|
1794
|
+
breaks.empty? ? memo : Type::Combinator.union(memo, *breaks)
|
|
1775
1795
|
end
|
|
1776
1796
|
|
|
1777
1797
|
# The RHS-value override for plain attribute / index writes (#520); a non-write call keeps the
|
|
@@ -2388,14 +2408,12 @@ module Rigor
|
|
|
2388
2408
|
#
|
|
2389
2409
|
# - the receiver is not `Nominal[T]` for some T;
|
|
2390
2410
|
# - no def_node is recorded for that class/method (the receiver is foreign or has only an RBS sig);
|
|
2391
|
-
# - the def has no body, or has a parameter shape we cannot bind from the call's positional args
|
|
2392
|
-
# - the inference is already in progress for this (class, method, signature) tuple — recursion safety
|
|
2393
|
-
# net.
|
|
2411
|
+
# - the def has no body, or has a parameter shape we cannot bind from the call's positional args.
|
|
2394
2412
|
# v0.0.3 A — re-types a top-level (or DSL-block-nested) `def` discovered by `ScopeIndexer` under the
|
|
2395
2413
|
# `TOP_LEVEL_DEF_KEY` sentinel. Mirrors the `infer_user_method_return` shape but uses the current
|
|
2396
2414
|
# `scope.self_type` (or implicit `Object`) as the receiver carrier so the body's own self is
|
|
2397
2415
|
# consistent with the call site's. Returns nil when the parameter shape disqualifies the def, when the
|
|
2398
|
-
# body is empty, or when
|
|
2416
|
+
# body is empty, or when the inference raises.
|
|
2399
2417
|
def infer_top_level_user_method(def_node, receiver, arg_types, block_type = nil)
|
|
2400
2418
|
infer_user_method_return(def_node, receiver, arg_types, yield_type: block_type)
|
|
2401
2419
|
rescue StandardError
|
|
@@ -2442,9 +2460,9 @@ module Rigor
|
|
|
2442
2460
|
|
|
2443
2461
|
# True when `type` is a concrete value — a `Type::Constant` or a `Type::Tuple` whose elements are
|
|
2444
2462
|
# (recursively) all value-pinned. ADR-55 slice 1: a value-pinned self-call result is adopted even
|
|
2445
|
-
# inside a class/method body (where WD3 otherwise keeps non-`Bot` returns as `Dynamic[top]`). A
|
|
2446
|
-
#
|
|
2447
|
-
#
|
|
2463
|
+
# inside a class/method body (where WD3 otherwise keeps non-`Bot` returns as `Dynamic[top]`). A concrete
|
|
2464
|
+
# value at a call site is strictly more precise and can never enable an undefined-method or argument-type
|
|
2465
|
+
# false positive — it is FP-neutral by construction.
|
|
2448
2466
|
def fully_value_pinned?(type)
|
|
2449
2467
|
case type
|
|
2450
2468
|
when Type::Constant then true
|
|
@@ -2646,9 +2664,63 @@ module Rigor
|
|
|
2646
2664
|
found = scope.singleton_def_through_ancestors(class_name, method_name)
|
|
2647
2665
|
owner = found.last
|
|
2648
2666
|
found = [nil, nil] if owner && SingletonExternalAhead.withhold?(scope, class_name, method_name, owner)
|
|
2667
|
+
found = [nil, nil] if found.last && dynamic_singleton_definer_ahead?(class_name, method_name, found.last)
|
|
2649
2668
|
table[key] = found
|
|
2650
2669
|
end
|
|
2651
2670
|
|
|
2671
|
+
SINGLETON_WILDCARD_SLOT = %i[singleton *].freeze
|
|
2672
|
+
private_constant :SINGLETON_WILDCARD_SLOT
|
|
2673
|
+
|
|
2674
|
+
# Issue #2284 — a project entry nearer than the `def`'s owner on the singleton chain may define the name
|
|
2675
|
+
# without a `def`, which the def tables do not hold: `class S < B; define_singleton_method(:x) { "s" }; end`
|
|
2676
|
+
# calls S's block, not `B.x`. The farther `def` is then withheld and the call types `Dynamic`. A class's own
|
|
2677
|
+
# singleton side is read from the visibility-contest census: `define_singleton_method(:x)`,
|
|
2678
|
+
# `singleton_class.define_method(:x)` / `attr_*` and a computed name contest the entry's slot whether or not a
|
|
2679
|
+
# `def` is recorded (#1984, #2013). An `extend`ed module's side is read from `discovered_methods`, which holds
|
|
2680
|
+
# its `attr_*` and `define_method` rows, and the module's `def self.extended` hook may define any name on the
|
|
2681
|
+
# extender, so it withholds too.
|
|
2682
|
+
#
|
|
2683
|
+
# An `inherited` hook anywhere on the chain past the receiver's own entry runs when the receiver's class is
|
|
2684
|
+
# created and may define any singleton method on it (`def self.inherited(s) = s.define_singleton_method(:x)`),
|
|
2685
|
+
# so a `def` the receiver only inherits is withheld then as well: a class's `def self.inherited`, or an
|
|
2686
|
+
# `inherited` an extended module defines.
|
|
2687
|
+
def dynamic_singleton_definer_ahead?(class_name, method_name, owner)
|
|
2688
|
+
name = method_name.to_sym
|
|
2689
|
+
chain = Scope::ResolutionChain.for(scope, class_name.to_s, :singleton, :methods)
|
|
2690
|
+
return true if class_name.to_s != owner.to_s && inherited_hook_on_chain?(chain, class_name.to_s)
|
|
2691
|
+
|
|
2692
|
+
chain.entries.each do |entry|
|
|
2693
|
+
next if entry.external?
|
|
2694
|
+
return false if entry.name == owner.to_s
|
|
2695
|
+
return true if entry_defines_without_def?(entry, name)
|
|
2696
|
+
end
|
|
2697
|
+
false
|
|
2698
|
+
end
|
|
2699
|
+
|
|
2700
|
+
def inherited_hook_on_chain?(chain, receiver_name)
|
|
2701
|
+
chain.entries.any? do |entry|
|
|
2702
|
+
next false if entry.external? || entry.name == receiver_name
|
|
2703
|
+
|
|
2704
|
+
if entry.side == :singleton
|
|
2705
|
+
!scope.singleton_def_for(entry.name, :inherited).nil?
|
|
2706
|
+
else
|
|
2707
|
+
scope.discovered_method?(entry.name, :inherited, :instance)
|
|
2708
|
+
end
|
|
2709
|
+
end
|
|
2710
|
+
end
|
|
2711
|
+
|
|
2712
|
+
def entry_defines_without_def?(entry, name)
|
|
2713
|
+
unless entry.side == :singleton
|
|
2714
|
+
return scope.discovered_method?(entry.name, name, :instance) ||
|
|
2715
|
+
!scope.singleton_def_for(entry.name, :extended).nil?
|
|
2716
|
+
end
|
|
2717
|
+
|
|
2718
|
+
discovery = scope.discovery
|
|
2719
|
+
# No edge of its own: `singleton_def_through_ancestors`' chain search filed every entry ahead of the owner.
|
|
2720
|
+
discovery.contested?(:discovered_method_visibilities, [entry.name, [:singleton, name]]) ||
|
|
2721
|
+
discovery.contested?(:discovered_method_visibilities, [entry.name, SINGLETON_WILDCARD_SLOT])
|
|
2722
|
+
end
|
|
2723
|
+
|
|
2652
2724
|
# The project-side singleton-method band: a `Foo.bar` call resolved against a `class << …` / `def self.…`
|
|
2653
2725
|
# body the project itself wrote. Which of the two tiers applies is decided by the receiver carrier —
|
|
2654
2726
|
# `Singleton[Foo]` when the constant names a class or module, `Nominal[…]` when it holds an ordinary
|
|
@@ -2684,7 +2756,7 @@ module Rigor
|
|
|
2684
2756
|
# external entries this lookup passes over. Cross-file: the chain is built from the tables the runner
|
|
2685
2757
|
# seeds from the project-wide pre-pass, cycle-guarded and capped at `Scope::ANCESTOR_WALK_LIMIT`.
|
|
2686
2758
|
|
|
2687
|
-
CLASS_GRAPH_CACHE_KEY = :__rigor_class_graph_cache__
|
|
2759
|
+
CLASS_GRAPH_CACHE_KEY = RunScopedSlots.register(:__rigor_class_graph_cache__)
|
|
2688
2760
|
private_constant :CLASS_GRAPH_CACHE_KEY
|
|
2689
2761
|
|
|
2690
2762
|
# Memo for the static class-graph resolvers below. They are pure functions of the scope's *frozen*
|
|
@@ -2885,7 +2957,7 @@ module Rigor
|
|
|
2885
2957
|
dynamic_top
|
|
2886
2958
|
end
|
|
2887
2959
|
|
|
2888
|
-
OVERRIDE_GATE_CACHE_KEY = :__rigor_overridable_method_gate__
|
|
2960
|
+
OVERRIDE_GATE_CACHE_KEY = RunScopedSlots.register(:__rigor_overridable_method_gate__)
|
|
2889
2961
|
private_constant :OVERRIDE_GATE_CACHE_KEY
|
|
2890
2962
|
|
|
2891
2963
|
# Memo for {#overridden_in_project?}, nested `kind → owner → method_name`. The predicate is a pure
|
|
@@ -2958,7 +3030,7 @@ module Rigor
|
|
|
2958
3030
|
EMPTY_REDEFINERS = [].freeze
|
|
2959
3031
|
private_constant :EMPTY_REDEFINERS
|
|
2960
3032
|
|
|
2961
|
-
METHOD_DEFINERS_INDEX_KEY = :__rigor_method_definers_index__
|
|
3033
|
+
METHOD_DEFINERS_INDEX_KEY = RunScopedSlots.register(:__rigor_method_definers_index__)
|
|
2962
3034
|
private_constant :METHOD_DEFINERS_INDEX_KEY
|
|
2963
3035
|
|
|
2964
3036
|
# The second way. Two flat thread slots rather than one slot holding an array of ways: a
|
|
@@ -2966,7 +3038,7 @@ module Rigor
|
|
|
2966
3038
|
# elements are pinned, and the engine then reads the `way[0].equal?(…)` guards below as always-falsey
|
|
2967
3039
|
# (`make check` fires `flow.always-truthy-condition` on this very file). Two keys read as two plain
|
|
2968
3040
|
# `Thread.current` reads and keep the guards analysable.
|
|
2969
|
-
METHOD_DEFINERS_INDEX_ALT_KEY = :__rigor_method_definers_index_alt__
|
|
3041
|
+
METHOD_DEFINERS_INDEX_ALT_KEY = RunScopedSlots.register(:__rigor_method_definers_index_alt__)
|
|
2970
3042
|
private_constant :METHOD_DEFINERS_INDEX_ALT_KEY
|
|
2971
3043
|
|
|
2972
3044
|
# `method_name (Symbol) → [owner names]` inverted index over the instance / singleton def tables,
|
|
@@ -3000,8 +3072,9 @@ module Rigor
|
|
|
3000
3072
|
# the singleton index.
|
|
3001
3073
|
#
|
|
3002
3074
|
# DELIBERATELY not keyed on the whole {Scope::DiscoveryIndex}, unlike {#class_graph_buckets} and
|
|
3003
|
-
# {#override_gate_buckets} above. {
|
|
3004
|
-
# nothing else, so the def tables are already the complete key, and the index
|
|
3075
|
+
# {#override_gate_buckets} above. {DefinersIndex.for} reads the ONE table it is handed, and the seed that
|
|
3076
|
+
# table was merged over, and nothing else, so the def tables are already the complete key, and the index
|
|
3077
|
+
# object is a strictly
|
|
3005
3078
|
# narrower one: the project-seed scope carries a fresh index per file while its def tables stay the
|
|
3006
3079
|
# same object. Over the 200 synthetic files above, an index-keyed memo misses all 400 times where a
|
|
3007
3080
|
# def-table-keyed one misses 201 — it would rebuild the seed's 934-entry table once per file and hand
|
|
@@ -3019,17 +3092,7 @@ module Rigor
|
|
|
3019
3092
|
Thread.current[METHOD_DEFINERS_INDEX_KEY] = way
|
|
3020
3093
|
end
|
|
3021
3094
|
singleton = kind == :singleton
|
|
3022
|
-
way[singleton ? 3 : 2] ||=
|
|
3023
|
-
end
|
|
3024
|
-
|
|
3025
|
-
def build_method_definers_index(table)
|
|
3026
|
-
index = {}
|
|
3027
|
-
table.each do |class_name, methods|
|
|
3028
|
-
next if class_name == Inference::ScopeIndexer::TOP_LEVEL_DEF_KEY
|
|
3029
|
-
|
|
3030
|
-
methods.each_key { |method_name| (index[method_name] ||= []) << class_name }
|
|
3031
|
-
end
|
|
3032
|
-
index
|
|
3095
|
+
way[singleton ? 3 : 2] ||= DefinersIndex.for(singleton ? singleton_def_nodes : def_nodes)
|
|
3033
3096
|
end
|
|
3034
3097
|
|
|
3035
3098
|
# True when `candidate`'s ancestor chain (superclasses + included/prepended modules) reaches `owner` —
|
|
@@ -3110,10 +3173,11 @@ module Rigor
|
|
|
3110
3173
|
# `[receiver, *args]` descriptor tuple; a callee may appear under at most two node identities per run
|
|
3111
3174
|
# (the project-index parse every OTHER file resolves through, plus the defining file's own analysis
|
|
3112
3175
|
# parse). Only the current generation's bucket is retained (single slot) — a rolled-over run's entries
|
|
3113
|
-
# become garbage instead of accumulating across LSP runs
|
|
3114
|
-
#
|
|
3115
|
-
#
|
|
3116
|
-
|
|
3176
|
+
# become garbage instead of accumulating across LSP runs, and the run that filled the slot releases it
|
|
3177
|
+
# when it ends ({RunScopedSlots}; `Runner#run` keeps its bucket for `#return_summaries`).
|
|
3178
|
+
# `ExpressionTyper` is rebuilt per `Scope#type_of`, so the store lives on `Thread.current`; fork-pool
|
|
3179
|
+
# workers are separate processes, so it never crosses a project boundary (ADR-84 WD5).
|
|
3180
|
+
RETURN_MEMO_KEY = RunScopedSlots.register(:__rigor_user_method_return_memo__)
|
|
3117
3181
|
private_constant :RETURN_MEMO_KEY
|
|
3118
3182
|
|
|
3119
3183
|
# ADR-84 WD2 — one memo entry: the final inferred return plus, when the entry was computed under
|
|
@@ -3898,7 +3962,7 @@ module Rigor
|
|
|
3898
3962
|
# Consumes one unit from the thread-local unroll-fuel counter and returns the units that were
|
|
3899
3963
|
# available *before* this consumption (so a positive return means the extended value-key may be used).
|
|
3900
3964
|
# Fuel is per-outermost inference entry: at the top level (empty guard stack) it seeds to
|
|
3901
|
-
# `RECURSION_UNROLL_FUEL`, and the `ensure` in `
|
|
3965
|
+
# `RECURSION_UNROLL_FUEL`, and the `ensure` in `evaluate_guarded_user_method_body` clears it once the stack
|
|
3902
3966
|
# drains back to empty. On exhaustion (return 0) it records a `RECURSION_UNROLL_FUEL` hit so the caller
|
|
3903
3967
|
# keeps the plain `(receiver, method)` signature — today's behaviour.
|
|
3904
3968
|
def unroll_fuel_remaining(stack)
|
|
@@ -3961,7 +4025,7 @@ module Rigor
|
|
|
3961
4025
|
# to the receiver's nominal type, the project-wide accumulators inherited (so the body sees the same
|
|
3962
4026
|
# `discovered_classes` / `class_ivars` / etc. the caller does), and the call's `arg_types` bound to
|
|
3963
4027
|
# the parameters. Returns nil when the binding is truly ambiguous (arity mismatch, trailing `post`
|
|
3964
|
-
# params
|
|
4028
|
+
# params).
|
|
3965
4029
|
#
|
|
3966
4030
|
# Issue #524 — the binder is per-PARAMETER, not per-signature. The first iteration declined any def
|
|
3967
4031
|
# with an optional / rest / keyword / block parameter outright, so ONE `options = {}` default zeroed
|
|
@@ -4009,7 +4073,7 @@ module Rigor
|
|
|
4009
4073
|
body_scope.with_repeated_or_writes(RepeatedOrWrites.straight_line_sites(def_node.body, body_scope))
|
|
4010
4074
|
end
|
|
4011
4075
|
|
|
4012
|
-
# Issue #681 / #707 — the
|
|
4076
|
+
# Issue #681 / #707 — the typer's answer to "what `Module.nesting` was recorded for this
|
|
4013
4077
|
# def node". `Scope::DiscoveryIndex#discovered_def_nestings` is the owner; the resolver memo is that
|
|
4014
4078
|
# table's REHYDRATION for the one population it cannot hold — a def served from an ADR-85 seed bundle,
|
|
4015
4079
|
# whose node {Inference::DefNodeResolver} mints from its own parse, so no table keyed by the identities
|
|
@@ -4024,7 +4088,7 @@ module Rigor
|
|
|
4024
4088
|
# run and an `--incremental` recheck answering the same constant for the same body; the rehydration half
|
|
4025
4089
|
# is populated only inside `DefNodeResolver.with_run`, and outside it this falls back to the peel.
|
|
4026
4090
|
def recorded_def_nesting(def_node)
|
|
4027
|
-
scope.discovery.
|
|
4091
|
+
scope.discovery.recorded_def_nesting(def_node)
|
|
4028
4092
|
end
|
|
4029
4093
|
|
|
4030
4094
|
# The body scope's fold-safe set: the body's own struct locals, plus issue #525's `:self` sentinel
|
|
@@ -4353,13 +4417,15 @@ module Rigor
|
|
|
4353
4417
|
|
|
4354
4418
|
# Whether the call may run its block more than once, so a later run reads a captured binding an earlier
|
|
4355
4419
|
# run rebound — the premise of laying the #587 (b) binding ({#captured_block_bindings}) under the block.
|
|
4356
|
-
# {BlockRepetition.may_repeat?} holds the rule, which the statement pass shares (issue #1412).
|
|
4357
|
-
#
|
|
4358
|
-
#
|
|
4359
|
-
#
|
|
4420
|
+
# {BlockRepetition.may_repeat?} holds the rule, which the statement pass shares (issue #1412). A project callee
|
|
4421
|
+
# whose body may run the block more than once on one path ({ProjectYielder.block_value_may_repeat?}) also
|
|
4422
|
+
# counts: a `yield` in a loop or a core iterator's block (`xs.each { |x| v = yield(x) }`, issue #2303), or a
|
|
4423
|
+
# hand-off (`g(x, &)`, issue #2301), whose target may run it many times, so a captured local the block rebinds
|
|
4424
|
+
# must not read its entry binding. A callee that runs it once per path (`f ? yield(1) : yield(2)`,
|
|
4425
|
+
# `then { yield }`) keeps the single-run value.
|
|
4360
4426
|
def block_may_repeat?(call_node, receiver_type)
|
|
4361
4427
|
BlockRepetition.may_repeat?(method_name: call_node.name, receiver_type: receiver_type, scope: scope) ||
|
|
4362
|
-
ProjectYielder.
|
|
4428
|
+
ProjectYielder.block_value_may_repeat?(call_node, receiver_type, scope)
|
|
4363
4429
|
end
|
|
4364
4430
|
|
|
4365
4431
|
EMPTY_BREAK_ARMS = [].freeze
|
|
@@ -5569,8 +5635,9 @@ module Rigor
|
|
|
5569
5635
|
end
|
|
5570
5636
|
|
|
5571
5637
|
# Records `type` as the accumulator `block` receives when typed from this typer's scope
|
|
5572
|
-
# ({.recorded_inject_accumulator}).
|
|
5573
|
-
# the
|
|
5638
|
+
# ({.recorded_inject_accumulator}). Prism nodes compare by identity. The keys are weak but the values strong,
|
|
5639
|
+
# and a recorded scope reaches the parse tree that holds the key, so a record never goes on its own (issue
|
|
5640
|
+
# #2412): the map is a run-scoped slot ({RunScopedSlots}), emptied when the run that filled it ends.
|
|
5574
5641
|
def record_inject_accumulator(block, type)
|
|
5575
5642
|
(Thread.current[INJECT_ACCUMULATOR_KEY] ||= ObjectSpace::WeakKeyMap.new)[block] = [scope, type].freeze
|
|
5576
5643
|
end
|
|
@@ -28,13 +28,12 @@ module Rigor
|
|
|
28
28
|
# a flag threaded into `Scope`: an ADR-2 plugin-facing bypass of dependency recording would produce
|
|
29
29
|
# a silently stale warm cache that no diagnostic diff can show.
|
|
30
30
|
#
|
|
31
|
-
# There is deliberately NO memo here.
|
|
32
|
-
# not exist yet, and a memo keyed on the tables this slice happens to name would be keyed on less
|
|
31
|
+
# There is deliberately NO memo here. A memo keyed on the tables the walk names would be keyed on less
|
|
33
32
|
# than the walk reads — the ancestor chain resolves names through `known_user_class?`
|
|
34
33
|
# (`discovered_def_nodes`, `discovered_methods`) and `ancestor_name_candidates`
|
|
35
34
|
# (`discovered_header_nestings`). The chain itself is memoised per discovery index
|
|
36
|
-
# ({Scope::ResolutionChain}); the
|
|
37
|
-
#
|
|
35
|
+
# ({Scope::ResolutionChain}); the dispatch callers in {MethodDispatcher::RbsDispatch} memoise their
|
|
36
|
+
# own answers, keyed on what they read.
|
|
38
37
|
module ExternalAncestorResolution
|
|
39
38
|
# The owner a name must PRECEDE for its declaration to win an MRO. See {declared_before_object?}.
|
|
40
39
|
OBJECT_OWNER = "Object"
|
|
@@ -5,6 +5,7 @@ require "prism"
|
|
|
5
5
|
require_relative "../source/constant_path"
|
|
6
6
|
require_relative "../source/node_children"
|
|
7
7
|
require_relative "ivar_writer_census"
|
|
8
|
+
require_relative "stored_block_call"
|
|
8
9
|
|
|
9
10
|
module Rigor
|
|
10
11
|
module Inference
|
|
@@ -28,7 +29,15 @@ module Rigor
|
|
|
28
29
|
# - `[:main_mixin, name]` — a module a top-level `include`, `prepend` or `extend` mixes into `Object` (or
|
|
29
30
|
# `main`), as written. A module RBS does not know may carry any method, so it reaches every class.
|
|
30
31
|
# - the instance-variable writer entries of {IvarWriterCensus} (issue #1154): `[:ivar_set, name]`,
|
|
31
|
-
# `[:ivar_set_class, class_name]
|
|
32
|
+
# `[:ivar_set_class, class_name]`, `[:own_write, class_name, name, path]` and, issue #2079,
|
|
33
|
+
# `[:own_write_type, class_name, name, value, path]`.
|
|
34
|
+
# - `[:global_writer, name, method]` — issue #2314: a write of the global `name` inside a method body, keyed by
|
|
35
|
+
# the `def`'s name (or the literal name a `define_method` block defines), or by nil inside a lambda or a block
|
|
36
|
+
# its call keeps to run later ({StoredBlockCall}). A write in a block its call runs at once belongs to the
|
|
37
|
+
# enclosing body. A write at the top level or in a class body records nothing, and neither does a write of
|
|
38
|
+
# the frame-local `$_` or `$~`. {GlobalWriterReach} reads these.
|
|
39
|
+
# - `[:method_alias, new, old]` — issue #2314: `alias new old`, `alias_method :new, :old` or `def_delegator
|
|
40
|
+
# :@o, :old, :new`, whose new name runs a method named old. {GlobalWriterReach} follows it.
|
|
32
41
|
#
|
|
33
42
|
# The census is a `Set` of those frozen entries, merged by union across files, the project pre-pass and the
|
|
34
43
|
# `pre_eval:` files. It is recorded inside descents that already visit every node ({Collector#visit}), never
|
|
@@ -78,7 +87,7 @@ module Rigor
|
|
|
78
87
|
end
|
|
79
88
|
|
|
80
89
|
# #1154 — the {IvarWriterCensus} entries of `census`, for the incremental diff and its readers.
|
|
81
|
-
IVAR_KINDS = %i[ivar_set ivar_set_class own_write].to_set.freeze
|
|
90
|
+
IVAR_KINDS = %i[ivar_set ivar_set_class own_write own_write_type].to_set.freeze
|
|
82
91
|
|
|
83
92
|
def ivar_set_entries(census) = census.select { |entry| IVAR_KINDS.include?(entry.first) }.to_set
|
|
84
93
|
|
|
@@ -87,6 +96,7 @@ module Rigor
|
|
|
87
96
|
case entry.first
|
|
88
97
|
when :ivar_set then "ivar_set:#{entry[1]}"
|
|
89
98
|
when :ivar_set_class then "ivar_set_class:#{entry[1]}"
|
|
99
|
+
when :own_write_type then "own_write_type:#{entry[1]}##{entry[2]}"
|
|
90
100
|
else "own_write:#{entry[1]}##{entry[2]}"
|
|
91
101
|
end
|
|
92
102
|
end
|
|
@@ -140,6 +150,7 @@ module Rigor
|
|
|
140
150
|
@refine_ranges = []
|
|
141
151
|
@ivar_writers = IvarWriterCensus.new(@census)
|
|
142
152
|
@in_effect = in_effect
|
|
153
|
+
@bodies = []
|
|
143
154
|
end
|
|
144
155
|
|
|
145
156
|
# A node the collector cannot read must not fail the analysis of its file, nor of every file the pre-pass
|
|
@@ -148,6 +159,7 @@ module Rigor
|
|
|
148
159
|
# the host walk does not track it, or no class owns an instance there).
|
|
149
160
|
def visit(node, top_level:, owner: nil)
|
|
150
161
|
@ivar_writers.visit(node, owner)
|
|
162
|
+
visit_global_writer(node)
|
|
151
163
|
case node
|
|
152
164
|
when Prism::AliasGlobalVariableNode then visit_global_alias(node)
|
|
153
165
|
when Prism::DefNode then record(node, node.name)
|
|
@@ -160,6 +172,68 @@ module Rigor
|
|
|
160
172
|
|
|
161
173
|
private
|
|
162
174
|
|
|
175
|
+
# Issue #2314 — tracks the method bodies, lambdas and stored blocks of a pre-order descent as flat
|
|
176
|
+
# `start, end, label` triples (`@bodies`, innermost last), and records a global write inside one
|
|
177
|
+
# ({GlobalWriterReach}). A body ending before the write starts is closed. Only those nodes read their location.
|
|
178
|
+
def visit_global_writer(node)
|
|
179
|
+
case node
|
|
180
|
+
when Prism::DefNode then open_body(node, node.name)
|
|
181
|
+
when Prism::LambdaNode then open_body(node, nil)
|
|
182
|
+
when Prism::AliasMethodNode then record_method_alias(node.new_name, node.old_name)
|
|
183
|
+
when Prism::CallNode
|
|
184
|
+
open_stored_block(node)
|
|
185
|
+
visit_alias_call(node)
|
|
186
|
+
when Prism::GlobalVariableWriteNode, Prism::GlobalVariableOrWriteNode, Prism::GlobalVariableAndWriteNode,
|
|
187
|
+
Prism::GlobalVariableOperatorWriteNode, Prism::GlobalVariableTargetNode
|
|
188
|
+
record_global_writer(node)
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# `$_` and `$~` are the frame's own: a method's write to one never reaches its caller.
|
|
193
|
+
FRAME_LOCAL_GLOBALS = %i[$_ $~].freeze
|
|
194
|
+
private_constant :FRAME_LOCAL_GLOBALS
|
|
195
|
+
|
|
196
|
+
def record_global_writer(node)
|
|
197
|
+
return if FRAME_LOCAL_GLOBALS.include?(node.name)
|
|
198
|
+
|
|
199
|
+
start = node.location.start_offset
|
|
200
|
+
bodies = @bodies
|
|
201
|
+
bodies.pop(3) while !bodies.empty? && bodies[-2] <= start
|
|
202
|
+
index = bodies.size - 3
|
|
203
|
+
index -= 3 while index >= 0 && !(bodies[index] <= start && start < bodies[index + 1])
|
|
204
|
+
@census << [:global_writer, node.name, bodies[index + 2]].freeze if index >= 0
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def open_body(node, label)
|
|
208
|
+
location = node.location
|
|
209
|
+
@bodies.push(location.start_offset, location.end_offset, label)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# `alias_method :new, :old`, and `def_delegator :@o, :old, :new` (and its instance and single spellings), whose
|
|
213
|
+
# new name runs a method of the old name.
|
|
214
|
+
def visit_alias_call(node)
|
|
215
|
+
arguments = node.arguments&.arguments
|
|
216
|
+
case node.name
|
|
217
|
+
when :alias_method then record_method_alias(arguments[0], arguments[1]) if arguments&.size == 2
|
|
218
|
+
when :def_delegator, :def_instance_delegator, :def_single_delegator
|
|
219
|
+
record_method_alias(arguments[2], arguments[1]) if arguments&.size == 3
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def record_method_alias(new_node, old_node)
|
|
224
|
+
new_name = literal_name(new_node)
|
|
225
|
+
old_name = literal_name(old_node)
|
|
226
|
+
@census << [:method_alias, new_name, old_name].freeze if new_name && old_name && new_name != old_name
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def open_stored_block(node)
|
|
230
|
+
block = node.block
|
|
231
|
+
return unless block.is_a?(Prism::BlockNode) && StoredBlockCall.stores_block?(node)
|
|
232
|
+
|
|
233
|
+
definer = %i[define_method define_singleton_method].include?(node.name)
|
|
234
|
+
open_body(block, definer ? literal_name(node.arguments&.arguments&.first) : nil)
|
|
235
|
+
end
|
|
236
|
+
|
|
163
237
|
def visit_global_alias(node)
|
|
164
238
|
[node.new_name, node.old_name].each do |side|
|
|
165
239
|
@census << GlobalWriteCensus.alias_entry(side.name) if side.is_a?(Prism::GlobalVariableReadNode)
|