rigortype 0.4.6 → 0.4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +4 -2
- data/data/core_overlay/bigdecimal.rbs +33 -0
- data/data/core_overlay/resolv.rbs +30 -0
- data/data/gem_overlay/activesupport/core_ext.rbs +45 -13
- data/data/gem_overlay/addressable/addressable.rbs +40 -0
- data/data/vendored_gem_sigs/bundler/bundler.rbs +1 -1
- data/data/vendored_gem_sigs/rubygems/rubygems_extras.rbs +4 -3
- data/docs/handbook/01-getting-started.md +6 -3
- data/docs/handbook/11-sig-gen.md +85 -3
- data/docs/handbook/appendix-phpstan.md +4 -4
- data/docs/install.md +20 -0
- data/docs/manual/01-installation.md +67 -26
- data/docs/manual/02-cli-reference.md +455 -113
- data/docs/manual/03-configuration.md +76 -5
- data/docs/manual/04-diagnostics.md +5 -4
- data/docs/manual/06-baseline.md +6 -4
- data/docs/manual/08-skills.md +11 -0
- data/docs/manual/09-editor-integration.md +21 -2
- data/docs/manual/10-mcp-server.md +38 -2
- data/docs/manual/12-caching.md +4 -3
- data/docs/manual/14-rails-quickstart.md +1 -1
- data/docs/manual/19-effect-labels.md +3 -4
- data/docs/manual/plugins/rigor-actionpack.md +32 -15
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +23 -1
- data/docs/manual/plugins/rigor-sorbet.md +17 -3
- data/lib/rigor/analysis/baseline.rb +24 -27
- data/lib/rigor/analysis/check_rules/always_raises_shield.rb +147 -0
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +5 -6
- data/lib/rigor/analysis/check_rules/call_facts.rb +58 -0
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +12 -4
- data/lib/rigor/analysis/check_rules/duplicate_hash_key_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/inferred_param_guard.rb +85 -13
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +16 -4
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +6 -1
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -8
- data/lib/rigor/analysis/check_rules/plugin_api_surface.rb +195 -0
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +41 -12
- data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +27 -1
- data/lib/rigor/analysis/check_rules/rebinding_block_callees.rb +3 -3
- data/lib/rigor/analysis/check_rules/replaced_singleton_defs.rb +96 -0
- data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
- data/lib/rigor/analysis/check_rules/rule_ids.rb +9 -2
- data/lib/rigor/analysis/check_rules/rule_walk.rb +9 -7
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +3 -4
- data/lib/rigor/analysis/check_rules/special_global_setters.rb +3 -3
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules.rb +720 -141
- data/lib/rigor/analysis/crash_signature.rb +45 -3
- data/lib/rigor/analysis/dependency_recorder.rb +237 -80
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +14 -3
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/index.rb +6 -8
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
- data/lib/rigor/analysis/edit_replay.rb +93 -6
- data/lib/rigor/analysis/fact_store.rb +3 -3
- data/lib/rigor/analysis/incremental.rb +11 -1
- data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +4 -3
- data/lib/rigor/analysis/incremental_run_slot.rb +2 -2
- data/lib/rigor/analysis/incremental_session.rb +306 -53
- data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -3
- data/lib/rigor/analysis/rule_catalog.rb +39 -3
- data/lib/rigor/analysis/run_cache_key.rb +27 -3
- data/lib/rigor/analysis/run_disclosure_credit.rb +26 -6
- data/lib/rigor/analysis/run_stats.rb +2 -2
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -2
- data/lib/rigor/analysis/runner/declaration_position.rb +8 -8
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +264 -112
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +4 -9
- data/lib/rigor/analysis/runner/pool_coordinator.rb +173 -328
- data/lib/rigor/analysis/runner/project_pre_passes.rb +4 -37
- data/lib/rigor/analysis/runner/run_level_facts.rb +304 -0
- data/lib/rigor/analysis/runner/run_snapshots.rb +44 -54
- data/lib/rigor/analysis/runner/worker_payload.rb +36 -0
- data/lib/rigor/analysis/runner.rb +298 -109
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +62 -23
- data/lib/rigor/analysis/template_unit_positions.rb +2 -3
- data/lib/rigor/analysis/template_units.rb +9 -1
- data/lib/rigor/analysis/worker_session.rb +65 -71
- data/lib/rigor/bleeding_edge.rb +22 -10
- data/lib/rigor/builtins/imported_refinements.rb +6 -8
- data/lib/rigor/builtins/predefined_constant_refinements.rb +1 -1
- data/lib/rigor/builtins/regex_refinement.rb +4 -6
- data/lib/rigor/cache/descriptor.rb +6 -5
- data/lib/rigor/cache/file_digest.rb +39 -31
- data/lib/rigor/cache/incremental_snapshot.rb +95 -17
- data/lib/rigor/cache/rbs_cache_producer.rb +2 -2
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +1 -1
- data/lib/rigor/cache/rbs_class_type_param_names.rb +2 -2
- data/lib/rigor/cache/rbs_constant_table.rb +1 -1
- data/lib/rigor/cache/rbs_descriptor.rb +90 -4
- data/lib/rigor/cache/rbs_environment.rb +8 -1
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +2 -2
- data/lib/rigor/cache/rbs_known_class_names.rb +1 -1
- data/lib/rigor/cache/store.rb +11 -10
- data/lib/rigor/ci_detector.rb +1 -1
- data/lib/rigor/cli/annotate_command.rb +24 -19
- data/lib/rigor/cli/baseline_command.rb +160 -57
- data/lib/rigor/cli/catalogue.rb +256 -0
- data/lib/rigor/cli/check_command.rb +112 -104
- data/lib/rigor/cli/check_runner_factory.rb +2 -2
- data/lib/rigor/cli/command.rb +15 -1
- data/lib/rigor/cli/coverage_command.rb +21 -8
- data/lib/rigor/cli/coverage_mutation.rb +5 -7
- data/lib/rigor/cli/coverage_renderer.rb +2 -1
- data/lib/rigor/cli/coverage_scan.rb +3 -5
- data/lib/rigor/cli/diff_command.rb +25 -20
- data/lib/rigor/cli/docs_command.rb +1 -2
- data/lib/rigor/cli/doctor_command.rb +12 -12
- data/lib/rigor/cli/effects_command.rb +50 -40
- data/lib/rigor/cli/effects_diff_renderer.rb +18 -17
- data/lib/rigor/cli/effects_explain_renderer.rb +13 -12
- data/lib/rigor/cli/effects_renderer.rb +2 -1
- data/lib/rigor/cli/effects_snapshot_command.rb +26 -18
- data/lib/rigor/cli/explain_command.rb +18 -8
- data/lib/rigor/cli/fused_protection_renderer.rb +2 -1
- data/lib/rigor/cli/json_document.rb +41 -0
- data/lib/rigor/cli/lens_command.rb +29 -10
- data/lib/rigor/cli/lsp_command.rb +250 -23
- data/lib/rigor/cli/mcp_command.rb +12 -7
- data/lib/rigor/cli/mutation_fork_scan.rb +1 -1
- data/lib/rigor/cli/mutation_protection_renderer.rb +3 -2
- data/lib/rigor/cli/options.rb +40 -1
- data/lib/rigor/cli/plugin_command.rb +93 -46
- data/lib/rigor/cli/plugins_command.rb +17 -18
- data/lib/rigor/cli/plugins_renderer.rb +10 -6
- data/lib/rigor/cli/protection_fork_scan.rb +2 -2
- data/lib/rigor/cli/protection_renderer.rb +2 -1
- data/lib/rigor/cli/show_bleedingedge_command.rb +21 -15
- data/lib/rigor/cli/sig_gen_command.rb +92 -27
- data/lib/rigor/cli/skill_command.rb +32 -3
- data/lib/rigor/cli/skill_deep_probe.rb +1 -1
- data/lib/rigor/cli/skill_describe.rb +2 -1
- data/lib/rigor/cli/trace_command.rb +14 -9
- data/lib/rigor/cli/triage_command.rb +10 -11
- data/lib/rigor/cli/triage_renderer.rb +2 -1
- data/lib/rigor/cli/type_of_command.rb +16 -11
- data/lib/rigor/cli/type_of_project_scope.rb +1 -4
- data/lib/rigor/cli/type_of_renderer.rb +4 -1
- data/lib/rigor/cli/type_scan_command.rb +14 -9
- data/lib/rigor/cli/type_scan_renderer.rb +2 -1
- data/lib/rigor/cli/unused_command.rb +27 -17
- data/lib/rigor/cli/upgrade_command.rb +5 -0
- data/lib/rigor/cli.rb +214 -56
- data/lib/rigor/config_audit.rb +33 -19
- data/lib/rigor/configuration/severity_profile.rb +14 -5
- data/lib/rigor/configuration.rb +104 -35
- data/lib/rigor/effects/collector.rb +53 -21
- data/lib/rigor/effects/definition_context.rb +0 -2
- data/lib/rigor/effects/envelope.rb +1 -1
- data/lib/rigor/effects/framework_units.rb +1 -1
- data/lib/rigor/effects/mutation_classifier.rb +4 -7
- data/lib/rigor/effects/propagator.rb +1 -1
- data/lib/rigor/effects/registry.rb +2 -2
- data/lib/rigor/effects/snapshot.rb +1 -1
- data/lib/rigor/effects/taint_cause.rb +0 -2
- data/lib/rigor/effects/unit_scan.rb +9 -13
- data/lib/rigor/environment/bundle_sig_discovery.rb +64 -33
- data/lib/rigor/environment/class_registry.rb +3 -3
- data/lib/rigor/environment/constant_type_cache_holder.rb +5 -0
- data/lib/rigor/environment/core_root.rb +77 -0
- data/lib/rigor/environment/default_libraries.rb +18 -1
- data/lib/rigor/environment/deferred_signature_plan.rb +381 -0
- data/lib/rigor/environment/gated_signature_guard.rb +1 -1
- data/lib/rigor/environment/gated_signature_plan.rb +5 -2
- data/lib/rigor/environment/hkt_registry_holder.rb +6 -0
- data/lib/rigor/environment/lockfile_resolver.rb +28 -12
- data/lib/rigor/environment/member_consistency/comparator.rb +18 -1
- data/lib/rigor/environment/member_consistency.rb +56 -7
- data/lib/rigor/environment/missing_gem_constant_index.rb +3 -2
- data/lib/rigor/environment/project_path_gems.rb +124 -0
- data/lib/rigor/environment/rbs_collection_discovery.rb +123 -8
- data/lib/rigor/environment/rbs_coverage_report.rb +12 -11
- data/lib/rigor/environment/rbs_hierarchy.rb +12 -15
- data/lib/rigor/environment/rbs_loader.rb +772 -110
- data/lib/rigor/environment/reflection.rb +7 -7
- data/lib/rigor/environment/required_features.rb +0 -3
- data/lib/rigor/environment/ruby_library_roots.rb +1 -1
- data/lib/rigor/environment/vendored_signature_plan.rb +153 -0
- data/lib/rigor/environment.rb +169 -42
- data/lib/rigor/flow_contribution/merger.rb +4 -4
- data/lib/rigor/inference/acceptance.rb +136 -40
- data/lib/rigor/inference/alias_mutation_floor/foreign_locals.rb +117 -0
- data/lib/rigor/inference/alias_mutation_floor/memos.rb +85 -0
- data/lib/rigor/inference/alias_mutation_floor/reach.rb +119 -0
- data/lib/rigor/inference/alias_mutation_floor/site_collector.rb +252 -0
- data/lib/rigor/inference/alias_mutation_floor.rb +339 -0
- data/lib/rigor/inference/anonymous_meta_class.rb +18 -0
- data/lib/rigor/inference/block_auto_splat.rb +1 -1
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/block_run_count.rb +185 -0
- data/lib/rigor/inference/budget_trace.rb +8 -2
- data/lib/rigor/inference/builtins/array_catalog.rb +2 -2
- data/lib/rigor/inference/builtins/exception_catalog.rb +2 -2
- data/lib/rigor/inference/builtins/numeric_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/random_catalog.rb +2 -2
- data/lib/rigor/inference/callee_mutation_chain.rb +88 -77
- data/lib/rigor/inference/callee_write_census.rb +815 -0
- data/lib/rigor/inference/captured_locals.rb +2 -0
- data/lib/rigor/inference/census_foreign_block.rb +354 -0
- data/lib/rigor/inference/clause_widening.rb +1 -1
- data/lib/rigor/inference/closure_escape_analyzer.rb +11 -4
- data/lib/rigor/inference/construction_escape.rb +258 -0
- data/lib/rigor/inference/declaration_walk/traversal.rb +1 -1
- data/lib/rigor/inference/declaration_walk.rb +4 -0
- data/lib/rigor/inference/deep_erasure.rb +39 -7
- data/lib/rigor/inference/def_handle.rb +1 -1
- data/lib/rigor/inference/def_node_resolver.rb +33 -15
- data/lib/rigor/inference/def_return_typer.rb +47 -5
- data/lib/rigor/inference/definer_resolution.rb +19 -9
- data/lib/rigor/inference/definers_index.rb +162 -0
- data/lib/rigor/inference/direct_ivar_writes.rb +300 -0
- data/lib/rigor/inference/element_read_widening.rb +49 -3
- data/lib/rigor/inference/error_info.rb +16 -0
- data/lib/rigor/inference/expression_typer.rb +1559 -320
- data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
- data/lib/rigor/inference/flow_tracer.rb +31 -14
- data/lib/rigor/inference/fork_map.rb +3 -0
- data/lib/rigor/inference/fresh_frame_blocks.rb +52 -1
- data/lib/rigor/inference/global_write_census.rb +81 -5
- data/lib/rigor/inference/global_writer_reach.rb +260 -0
- data/lib/rigor/inference/hkt_body.rb +1 -2
- data/lib/rigor/inference/hkt_body_parser.rb +3 -5
- data/lib/rigor/inference/hkt_reducer.rb +2 -2
- data/lib/rigor/inference/hkt_registry.rb +3 -5
- data/lib/rigor/inference/in_effect_refinements.rb +7 -0
- data/lib/rigor/inference/indexed_narrowing.rb +61 -15
- data/lib/rigor/inference/intervening_ivar_reset.rb +26 -4
- data/lib/rigor/inference/ivar_writer_census.rb +154 -8
- data/lib/rigor/inference/key_presence_guard.rb +51 -24
- data/lib/rigor/inference/last_line/self_evidence.rb +30 -0
- data/lib/rigor/inference/last_line.rb +5 -2
- data/lib/rigor/inference/loop_value_record.rb +66 -14
- data/lib/rigor/inference/match_rebinding/frame.rb +57 -0
- data/lib/rigor/inference/match_rebinding/yielding_match.rb +101 -0
- data/lib/rigor/inference/match_rebinding.rb +28 -5
- data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/argument_distribution.rb +40 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +44 -30
- data/lib/rigor/inference/method_dispatcher/class_keyed_block_params.rb +65 -0
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +3 -3
- data/lib/rigor/inference/method_dispatcher/contested_overloads.rb +89 -0
- data/lib/rigor/inference/method_dispatcher/core_enumerable_receiver.rb +44 -0
- data/lib/rigor/inference/method_dispatcher/declared_constructor.rb +151 -0
- data/lib/rigor/inference/method_dispatcher/facet_distribution.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/imprecise_argument.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/interface_shape.rb +181 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/no_match_fallback.rb +54 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +51 -27
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +606 -78
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/self_substitute.rb +3 -3
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +9 -7
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +467 -84
- data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +0 -1
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +50 -17
- data/lib/rigor/inference/method_dispatcher/singleton_type_params.rb +104 -0
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -5
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +4 -6
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +123 -25
- data/lib/rigor/inference/method_parameter_binder.rb +136 -56
- data/lib/rigor/inference/module_function_state/spellings.rb +2 -2
- data/lib/rigor/inference/module_function_state.rb +24 -3
- data/lib/rigor/inference/multi_target_binder.rb +2 -2
- data/lib/rigor/inference/mutation_rejoin.rb +240 -4
- data/lib/rigor/inference/mutation_widening.rb +199 -29
- data/lib/rigor/inference/narrowing.rb +238 -50
- data/lib/rigor/inference/operand_effects.rb +43 -2
- data/lib/rigor/inference/optimistic_origin.rb +134 -9
- data/lib/rigor/inference/origin_lookup.rb +1 -1
- data/lib/rigor/inference/out_buffer_mutation.rb +72 -0
- data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
- data/lib/rigor/inference/pre_eval_constants.rb +1 -1
- data/lib/rigor/inference/precision_scanner.rb +1 -1
- data/lib/rigor/inference/project_method_ownership.rb +30 -9
- data/lib/rigor/inference/project_patched_methods.rb +65 -13
- data/lib/rigor/inference/project_patched_scanner.rb +120 -25
- data/lib/rigor/inference/project_patched_visibility.rb +39 -3
- data/lib/rigor/inference/project_yielder.rb +11 -17
- data/lib/rigor/inference/rbs_type_translator.rb +68 -15
- data/lib/rigor/inference/receiver_alias.rb +20 -0
- data/lib/rigor/inference/receiver_writer_call.rb +67 -0
- data/lib/rigor/inference/recursion_rebinding.rb +157 -0
- data/lib/rigor/inference/refined_dispatch/constructed_class.rb +57 -17
- data/lib/rigor/inference/refined_dispatch/own_method.rb +51 -16
- data/lib/rigor/inference/refined_dispatch.rb +13 -0
- data/lib/rigor/inference/reflective_ivar_write.rb +19 -11
- data/lib/rigor/inference/remover_holder_scan.rb +287 -0
- data/lib/rigor/inference/remover_narrowing.rb +224 -0
- data/lib/rigor/inference/rewrite_mutation.rb +53 -2
- data/lib/rigor/inference/scope_indexer/certainty.rb +0 -1
- data/lib/rigor/inference/scope_indexer/class_cvars_collector.rb +13 -6
- data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
- data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +70 -26
- data/lib/rigor/inference/scope_indexer/derived_param_value.rb +112 -0
- data/lib/rigor/inference/scope_indexer/empty_seed.rb +99 -0
- data/lib/rigor/inference/scope_indexer/ivar_contributions.rb +295 -0
- data/lib/rigor/inference/scope_indexer/mixin_accumulator.rb +136 -9
- data/lib/rigor/inference/scope_indexer.rb +2213 -226
- data/lib/rigor/inference/self_call_ivar_writes.rb +103 -12
- data/lib/rigor/inference/self_preserving_block.rb +87 -0
- data/lib/rigor/inference/set_mutation.rb +189 -0
- data/lib/rigor/inference/singleton_hook_decline.rb +42 -8
- data/lib/rigor/inference/singleton_object_constant.rb +32 -2
- data/lib/rigor/inference/statement_evaluator.rb +3268 -358
- data/lib/rigor/inference/string_mutation.rb +14 -0
- data/lib/rigor/inference/struct_fold_safety.rb +4 -6
- data/lib/rigor/inference/synthetic_method_scanner.rb +40 -17
- data/lib/rigor/inference/toplevel_def_census.rb +67 -0
- data/lib/rigor/inference/unknown_store_widening.rb +103 -4
- data/lib/rigor/inference/unthreaded_rebinds.rb +2 -3
- data/lib/rigor/inference/untyped_rejoin.rb +1 -1
- data/lib/rigor/inference/void_tail_summary.rb +29 -9
- data/lib/rigor/language_server/buffer_table.rb +3 -3
- data/lib/rigor/language_server/completion_provider.rb +26 -20
- data/lib/rigor/language_server/config_warning_announcer.rb +28 -0
- data/lib/rigor/language_server/diagnostic_publisher.rb +8 -3
- data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
- data/lib/rigor/language_server/folding_range_provider.rb +1 -2
- data/lib/rigor/language_server/hover_provider.rb +1 -7
- data/lib/rigor/language_server/hover_renderer.rb +6 -8
- data/lib/rigor/language_server/project_context.rb +39 -1
- data/lib/rigor/language_server/publish_batcher.rb +3 -4
- data/lib/rigor/language_server/selection_range_provider.rb +2 -4
- data/lib/rigor/language_server/server.rb +7 -3
- data/lib/rigor/language_server/signature_help_provider.rb +4 -4
- data/lib/rigor/lens/claims/comparison.rb +78 -0
- data/lib/rigor/lens/claims/request.rb +131 -0
- data/lib/rigor/lens/claims/type_reader.rb +216 -0
- data/lib/rigor/lens/claims.rb +208 -0
- data/lib/rigor/lens/contract_reader.rb +5 -0
- data/lib/rigor/lens/document.rb +1 -1
- data/lib/rigor/lens/file_reader.rb +21 -8
- data/lib/rigor/lens/plugins.rb +1 -1
- data/lib/rigor/lens/run.rb +8 -0
- data/lib/rigor/lens.rb +1 -1
- data/lib/rigor/library_probe.rb +68 -0
- data/lib/rigor/mcp/server.rb +73 -2
- data/lib/rigor/mcp.rb +1 -1
- data/lib/rigor/memo_audit.rb +282 -0
- data/lib/rigor/plugin/access_denied_error.rb +1 -2
- data/lib/rigor/plugin/base.rb +15 -2
- data/lib/rigor/plugin/bundled_catalog.rb +1 -12
- data/lib/rigor/plugin/io_boundary.rb +26 -4
- data/lib/rigor/plugin/loader.rb +25 -8
- data/lib/rigor/plugin/macro/heredoc_template.rb +7 -8
- data/lib/rigor/plugin/macro/trait_registry.rb +6 -9
- data/lib/rigor/plugin/manifest.rb +16 -16
- data/lib/rigor/plugin/registry.rb +16 -5
- data/lib/rigor/plugin/services.rb +2 -3
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -0
- data/lib/rigor/plugin/trust_policy.rb +48 -2
- data/lib/rigor/plugin/type_node_resolver.rb +2 -4
- data/lib/rigor/plugin.rb +6 -8
- data/lib/rigor/plugin_gap_advisory.rb +5 -1
- data/lib/rigor/project_environment.rb +16 -2
- data/lib/rigor/protection/analysis_guard.rb +1 -1
- data/lib/rigor/protection/discovery_seed.rb +44 -5
- data/lib/rigor/protection/mutation_cache.rb +4 -1
- data/lib/rigor/protection/mutator.rb +1 -1
- data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
- data/lib/rigor/rbs_extended/hkt_directives.rb +6 -10
- data/lib/rigor/rbs_extended/reporter.rb +27 -3
- data/lib/rigor/rbs_extended.rb +2 -3
- data/lib/rigor/reflection/constant_ancestors.rb +81 -22
- data/lib/rigor/reflection/declaration_collectors.rb +55 -0
- data/lib/rigor/reflection/declarations.rb +58 -0
- data/lib/rigor/reflection/project_method.rb +177 -0
- data/lib/rigor/reflection.rb +2 -0
- data/lib/rigor/rooted_glob.rb +32 -1
- data/lib/rigor/run_scoped_slots.rb +38 -0
- data/lib/rigor/runtime/jit.rb +1 -1
- data/lib/rigor/scope/discovery_index.rb +168 -8
- data/lib/rigor/scope/resolution_chain/relevance.rb +14 -1
- data/lib/rigor/scope/resolution_chain.rb +901 -54
- data/lib/rigor/scope.rb +644 -37
- data/lib/rigor/sig_gen/bot_proposal_guard.rb +306 -0
- data/lib/rigor/sig_gen/classification.rb +18 -1
- data/lib/rigor/sig_gen/effect_annotation.rb +3 -3
- data/lib/rigor/sig_gen/generator.rb +588 -66
- data/lib/rigor/sig_gen/generic_header_scan.rb +189 -0
- data/lib/rigor/sig_gen/inline_declarations.rb +2 -2
- data/lib/rigor/sig_gen/layout_index.rb +3 -3
- data/lib/rigor/sig_gen/method_candidate.rb +37 -4
- data/lib/rigor/sig_gen/observation_collector.rb +14 -7
- data/lib/rigor/sig_gen/observed_call.rb +1 -1
- data/lib/rigor/sig_gen/path_mapper.rb +6 -0
- data/lib/rigor/sig_gen/platform_guarded_files.rb +184 -0
- data/lib/rigor/sig_gen/renderer.rb +15 -9
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +62 -2
- data/lib/rigor/sig_gen/type_name_qualification.rb +69 -0
- data/lib/rigor/sig_gen/write_reporter.rb +63 -14
- data/lib/rigor/sig_gen/write_result.rb +23 -12
- data/lib/rigor/sig_gen/writer.rb +238 -37
- data/lib/rigor/sig_gen.rb +1 -0
- data/lib/rigor/source/forwardable_delegators.rb +122 -0
- data/lib/rigor/source/node_children.rb +13 -0
- data/lib/rigor/type/acceptance_router.rb +2 -2
- data/lib/rigor/type/accepts_result.rb +1 -1
- data/lib/rigor/type/app.rb +4 -6
- data/lib/rigor/type/combinator.rb +173 -21
- data/lib/rigor/type/constant.rb +4 -5
- data/lib/rigor/type/hash_shape.rb +21 -40
- data/lib/rigor/type/interface.rb +60 -0
- data/lib/rigor/type/union.rb +1 -1
- data/lib/rigor/type.rb +1 -0
- data/lib/rigor/type_node/identifier.rb +1 -2
- data/lib/rigor/type_node/resolver_chain.rb +1 -1
- data/lib/rigor/type_node/string_literal.rb +1 -2
- data/lib/rigor/type_node/symbol_literal.rb +1 -2
- data/lib/rigor/type_node/union.rb +3 -6
- data/lib/rigor/type_of_memo/serving.rb +99 -0
- data/lib/rigor/type_of_memo/stats.rb +43 -0
- data/lib/rigor/type_of_memo.rb +337 -0
- data/lib/rigor/typing_state.rb +1094 -0
- data/lib/rigor/value_semantics.rb +1 -10
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +2 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +2 -3
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -1
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +38 -36
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +4 -2
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +6 -4
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -5
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +2 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +3 -3
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +115 -36
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +3 -6
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +25 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +16 -6
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +4 -2
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +4 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +2 -1
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/association_roots.rb +3 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +2 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +21 -31
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +10 -16
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +132 -33
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +20 -6
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +7 -4
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +54 -17
- data/plugins/rigor-activerecord/sig/active_record/relation.rbs +19 -5
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +3 -4
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +1 -1
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +11 -14
- data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/symbolize_keys.rb +102 -0
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +30 -9
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +55 -13
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_collector.rb +15 -21
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_discoverer.rb +1 -6
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +3 -3
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +1 -3
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +6 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +5 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +1 -1
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/params_shape.rb +2 -3
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +5 -8
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +1 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +10 -18
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +0 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +0 -2
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +8 -7
- data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +0 -2
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +6 -7
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +12 -2
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +2 -4
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +2 -5
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +8 -2
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +6 -6
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +2 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +3 -8
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +2 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +5 -8
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +200 -4
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +1 -1
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +4 -11
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +4 -3
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +2 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +5 -5
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +2 -2
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +3 -4
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +6 -8
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +3 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +4 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +6 -7
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +210 -3
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +146 -7
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +2 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +52 -34
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +97 -24
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +2 -3
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_collector.rb +16 -16
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +4 -8
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +4 -4
- data/sig/rigor/analysis/baseline.rbs +0 -3
- data/sig/rigor/analysis/check_rules/rule_walk.rbs +6 -0
- data/sig/rigor/analysis/fact_store.rbs +2 -2
- data/sig/rigor/analysis/reachability/scan_cache.rbs +1 -1
- data/sig/rigor/cache/descriptor.rbs +14 -0
- data/sig/rigor/cli/diff_command.rbs +1 -1
- data/sig/rigor/cli/explain_command.rbs +1 -1
- data/sig/rigor/effects/registry.rbs +2 -2
- data/sig/rigor/environment.rbs +4 -1
- data/sig/rigor/inference.rbs +4 -1
- data/sig/rigor/plugin/additional_initializer.rbs +0 -5
- data/sig/rigor/plugin/base.rbs +53 -18
- data/sig/rigor/plugin/effect_attribution.rbs +3 -3
- data/sig/rigor/plugin/effect_edge.rbs +1 -1
- data/sig/rigor/plugin/effect_entry_points.rbs +3 -3
- data/sig/rigor/plugin/interfaces.rbs +51 -0
- data/sig/rigor/plugin/io_boundary.rbs +3 -3
- data/sig/rigor/plugin/manifest.rbs +9 -9
- data/sig/rigor/plugin/node_context.rbs +21 -0
- data/sig/rigor/plugin/protocol_contract.rbs +3 -7
- data/sig/rigor/reflection.rbs +37 -0
- data/sig/rigor/scope.rbs +43 -4
- data/sig/rigor/trinary.rbs +1 -0
- data/sig/rigor/type.rbs +9 -3
- data/sig/rigor.rbs +3 -2
- data/skills/rigor-ask/SKILL.md +1 -0
- data/skills/rigor-plugin-author/SKILL.md +4 -4
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +5 -5
- data/skills/rigor-plugin-review/SKILL.md +3 -3
- data/skills/rigor-project-init/references/01-detect.md +1 -0
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +17 -2
- data/skills/rigor-project-init/references/06-agent-contract.md +4 -4
- data/skills/rigor-rbs-contract/SKILL.md +147 -0
- data/skills/rigor-rbs-contract/references/01-finding-candidates.md +212 -0
- data/skills/rigor-rbs-contract/references/02-contract-rules.md +157 -0
- data/skills/rigor-rbs-contract/references/03-target-own-code.md +90 -0
- data/skills/rigor-rbs-contract/references/04-target-gem-rbs-collection.md +210 -0
- data/skills/rigor-rbs-contract/references/05-declaration-mutants.md +182 -0
- data/skills/rigor-rbs-setup/SKILL.md +3 -1
- data/skills/rigor-type-oracle/SKILL.md +26 -1
- data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +4 -3
- metadata +82 -3
|
@@ -8,8 +8,12 @@ require_relative "../rbs_type_translator"
|
|
|
8
8
|
require_relative "../external_ancestor_resolution"
|
|
9
9
|
require_relative "../void_origin"
|
|
10
10
|
require_relative "../optimistic_origin"
|
|
11
|
+
require_relative "argument_distribution"
|
|
12
|
+
require_relative "interface_shape"
|
|
13
|
+
require_relative "class_keyed_block_params"
|
|
11
14
|
require_relative "overload_selector"
|
|
12
15
|
require_relative "self_substitute"
|
|
16
|
+
require_relative "../../typing_state"
|
|
13
17
|
|
|
14
18
|
module Rigor
|
|
15
19
|
module Inference
|
|
@@ -33,8 +37,8 @@ module Rigor
|
|
|
33
37
|
# type-parameter names (`Array` -> `[:Elem]`, `Hash` -> `[:K, :V]`, ...) to build a substitution map;
|
|
34
38
|
# that map is then threaded through {RbsTypeTranslator} so a return type like `::Array[Elem]`
|
|
35
39
|
# resolves to `Nominal["Array", [Integer]]` rather than degrading the variable to `Dynamic[Top]`.
|
|
36
|
-
#
|
|
37
|
-
# degrade as before.
|
|
40
|
+
# A raw receiver, or one with more arguments than the class declares, leaves the map empty and free
|
|
41
|
+
# variables degrade as before; fewer arguments bind the declared prefix (#1121).
|
|
38
42
|
#
|
|
39
43
|
# Slice 5 phase 1 projects shape-carrying receivers onto their underlying nominal so the existing
|
|
40
44
|
# dispatch + substitution machinery works without duplication: `Tuple[Integer, String]` dispatches as
|
|
@@ -44,13 +48,14 @@ module Rigor
|
|
|
44
48
|
#
|
|
45
49
|
# Remaining limitations:
|
|
46
50
|
#
|
|
47
|
-
# * Keyword arguments reach `args`
|
|
48
|
-
# parameters
|
|
49
|
-
# * Method-level type parameters bind
|
|
50
|
-
#
|
|
51
|
+
# * Keyword arguments reach `args` as one trailing hash entry; the selector matches it against keyword
|
|
52
|
+
# parameters when the call's last argument is a keyword hash (#1727).
|
|
53
|
+
# * Method-level type parameters bind from the block return type (Slice 6 phase C) and from a
|
|
54
|
+
# positional parameter whose declared type is EXACTLY a type variable (issue #303 —
|
|
51
55
|
# `def foo[T]: (T) -> T` binds `T` from the first argument, and carries it into a generic return
|
|
52
|
-
# such as `-> Array[T]`)
|
|
53
|
-
#
|
|
56
|
+
# such as `-> Array[T]`), plus a `Range[A]` element (#834) and a `singleton(T)` class object (#2828).
|
|
57
|
+
# A variable reachable only through other container positions (`Array[T] arg`), a rest positional
|
|
58
|
+
# (`*T`), or a keyword parameter is still unbound and degrades to `Dynamic[Top]`.
|
|
54
59
|
# A block-return variable that a parameter also names takes the value class the argument and the
|
|
55
60
|
# block share once the call passes an argument, or stays unbound when they share none (see
|
|
56
61
|
# {compose_block_type_vars}).
|
|
@@ -69,6 +74,23 @@ module Rigor
|
|
|
69
74
|
# objects answer to methods their RBS omits (`ActionController::Base`, `Hash`, …).
|
|
70
75
|
ALLOWED_RBS_COMPLETE_ANCESTORS = ["Rigor::Plugin::Base"].freeze
|
|
71
76
|
|
|
77
|
+
# #2681 — the owners whose members every class answers to. A name only they declare is not part of an
|
|
78
|
+
# allow-listed ancestor's contract, so a `def` of it never walks for the declaration it overrides.
|
|
79
|
+
OBJECT_SURFACE_OWNERS = %w[BasicObject Object Kernel Module Class].freeze
|
|
80
|
+
|
|
81
|
+
# Per-thread memo, keyed by environment: the Set of the names an allow-listed ancestor's RBS declarations
|
|
82
|
+
# declare outside {OBJECT_SURFACE_OWNERS}. It keeps {.def_declaration} off the ancestor walk for any other
|
|
83
|
+
# `def`.
|
|
84
|
+
#
|
|
85
|
+
# ADR-129 WD1 — a run-scoped Memo: the names are a pure function of the environment the entry is keyed on.
|
|
86
|
+
RBS_COMPLETE_MEMBER_NAMES = TypingState.memo(
|
|
87
|
+
:rbs_complete_member_names,
|
|
88
|
+
run_scoped: true, audit: "rbs complete member names",
|
|
89
|
+
reason: "a WeakKeyMap keyed on the environment; the names are read from its loader " \
|
|
90
|
+
"and plugin registry alone"
|
|
91
|
+
)
|
|
92
|
+
private_constant :RBS_COMPLETE_MEMBER_NAMES
|
|
93
|
+
|
|
72
94
|
# Issue #1094 — the methods through which a value can answer Ruby's implicit `to_ary` conversion.
|
|
73
95
|
# Multiple assignment and block auto-splat convert through `rb_check_array_type`, which calls
|
|
74
96
|
# `to_ary` when defined and otherwise asks `respond_to_missing?`, dispatching to `method_missing`
|
|
@@ -216,6 +238,17 @@ module Rigor
|
|
|
216
238
|
)
|
|
217
239
|
end
|
|
218
240
|
|
|
241
|
+
# ADR-130 WD5 — interface dispatch switched off. Inside the block a `Rigor::Plugin` interface value keeps its
|
|
242
|
+
# carrier, but no call on it resolves through the interface's members: the receiver names no class, as
|
|
243
|
+
# `Dynamic[top]` names none. `rigor sig-gen` analyses here, so it writes no RBS from an interface a plugin
|
|
244
|
+
# author did not write against, and the behavioural parity gate's `:dispatch_off` mode runs here
|
|
245
|
+
# (`spec/integration/interface_parity_spec.rb`). The switch is a per-thread `TypingState` frame
|
|
246
|
+
# ({InterfaceShape.without_dispatch}), so a run under it is sequential: a pooled run raises (`PoolCoordinator`).
|
|
247
|
+
# No CLI flag or configuration key sets it.
|
|
248
|
+
def without_interface_dispatch(&) = InterfaceShape.without_dispatch(&)
|
|
249
|
+
|
|
250
|
+
def interface_dispatch? = InterfaceShape.dispatch?
|
|
251
|
+
|
|
219
252
|
# Slice 6 (Phase C sub-phase 1) probe: returns the positional block-parameter types declared by
|
|
220
253
|
# the receiving method's selected RBS overload, translated into `Rigor::Type`. Used by the
|
|
221
254
|
# StatementEvaluator to bind block parameter names before evaluating the block body.
|
|
@@ -248,10 +281,89 @@ module Rigor
|
|
|
248
281
|
)
|
|
249
282
|
end
|
|
250
283
|
|
|
284
|
+
# The RBS declaration a `def method_name` on `class_name` (`kind` side) binds its parameters from
|
|
285
|
+
# (`MethodParameterBinder`): the class's own, or nil.
|
|
286
|
+
#
|
|
287
|
+
# #2681 — when `class_name` is a Ruby-source class with no RBS, the declaration the `def` overrides on an
|
|
288
|
+
# ADR-43 allow-listed ancestor, as an empty RBS declaration of the class would inherit it. Nil whenever the
|
|
289
|
+
# call bridge would decline for a reason other than `class_name`'s own definition (no scope, no allow-listed
|
|
290
|
+
# ancestor, a nearer definer between them), and for a declaration one of {OBJECT_SURFACE_OWNERS} makes:
|
|
291
|
+
# `Kernel#load` is every class's, not that ancestor's contract. `initialize` is never bridged: a constructor
|
|
292
|
+
# does not override its superclass's contract, and a subclass routinely takes different arguments.
|
|
293
|
+
def def_declaration(environment, class_name, kind, method_name, scope)
|
|
294
|
+
direct = lookup_method_on(environment, class_name, kind, method_name)
|
|
295
|
+
return direct if direct
|
|
296
|
+
return nil if environment.nil? || scope.nil? || method_name == :initialize
|
|
297
|
+
return nil unless rbs_complete_member_name?(environment, method_name)
|
|
298
|
+
|
|
299
|
+
ancestor = allowed_rbs_complete_ancestor(environment, class_name, kind, method_name, scope,
|
|
300
|
+
overriding: true)
|
|
301
|
+
overridden = ancestor && lookup_method_on(environment, ancestor, kind, method_name)
|
|
302
|
+
overridden unless overridden.nil? || object_surface?(overridden)
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def object_surface?(method_definition)
|
|
306
|
+
OBJECT_SURFACE_OWNERS.include?(method_definition.defined_in.to_s.delete_prefix("::"))
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# A class object carries no type arguments, so a singleton declaration binds none from the receiver.
|
|
310
|
+
NO_RECEIVER_TYPE_VARS = {}.freeze
|
|
311
|
+
private_constant :NO_RECEIVER_TYPE_VARS
|
|
312
|
+
|
|
313
|
+
# Issue #2884 — the return of a call on `Singleton[class_name]` through `method_definition`, a singleton
|
|
314
|
+
# declaration {DeclaredConstructor} found on `class_name` or an ancestor, so `instance` and `self` read
|
|
315
|
+
# `class_name` whichever class wrote it. Nil when the declaration states no return
|
|
316
|
+
# (`%a{rigor:v1:inferred-return}`).
|
|
317
|
+
def declared_singleton_return(context, method_definition, class_name)
|
|
318
|
+
return nil if RbsExtended.inferred_return?(method_definition)
|
|
319
|
+
|
|
320
|
+
translate_return_type(
|
|
321
|
+
method_definition,
|
|
322
|
+
class_name: class_name, kind: :singleton, method_name: context.method_name, args: context.args || [],
|
|
323
|
+
type_vars: NO_RECEIVER_TYPE_VARS, block_type: context.block_type, environment: context.environment,
|
|
324
|
+
scope: context.scope, call_node: context.call_node
|
|
325
|
+
)
|
|
326
|
+
end
|
|
327
|
+
|
|
251
328
|
# rubocop:disable-next Metrics/ClassLength
|
|
252
329
|
class << self
|
|
253
330
|
private
|
|
254
331
|
|
|
332
|
+
# Whether some allow-listed ancestor's RBS declarations declare `method_name` outside
|
|
333
|
+
# {OBJECT_SURFACE_OWNERS}: the one-probe gate that leaves a `def` no ancestor declares, the common case
|
|
334
|
+
# in a project with no plugin class, on the cost it had before #2681. Read from the declarations, so a
|
|
335
|
+
# gem signature that fails to build reports nothing more than it did.
|
|
336
|
+
def rbs_complete_member_name?(environment, method_name)
|
|
337
|
+
rbs_complete_member_names(environment).include?(method_name)
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def rbs_complete_member_names(environment)
|
|
341
|
+
return collect_rbs_complete_member_names(environment) if MemoAudit.enabled? && MemoAudit.suspended?
|
|
342
|
+
|
|
343
|
+
table = TypingState.current.weak_key_map(RBS_COMPLETE_MEMBER_NAMES)
|
|
344
|
+
names = table[environment]
|
|
345
|
+
if names.nil?
|
|
346
|
+
return table[environment] = MemoAudit.key_audit("rbs complete member names") do
|
|
347
|
+
collect_rbs_complete_member_names(environment)
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
MemoAudit.check("rbs complete member names", names, :environment) do
|
|
352
|
+
collect_rbs_complete_member_names(environment)
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def collect_rbs_complete_member_names(environment)
|
|
357
|
+
loader = environment.rbs_loader
|
|
358
|
+
# An RBS-blind environment declares nothing, so no override takes an ancestor's parameters.
|
|
359
|
+
return Set.new.freeze if loader.nil?
|
|
360
|
+
|
|
361
|
+
ancestors = ALLOWED_RBS_COMPLETE_ANCESTORS + (environment.plugin_registry&.rbs_complete_ancestors || [])
|
|
362
|
+
ancestors.each_with_object(Set.new) do |ancestor, names|
|
|
363
|
+
names.merge(loader.declared_method_names(ancestor, skip: OBJECT_SURFACE_OWNERS))
|
|
364
|
+
end.freeze
|
|
365
|
+
end
|
|
366
|
+
|
|
255
367
|
def dispatch_for(receiver:, method_name:, args:, environment:, block_type:, self_type_override: nil, # rubocop:disable Metrics/ParameterLists
|
|
256
368
|
public_only: false, scope: nil, call_node: nil)
|
|
257
369
|
args ||= []
|
|
@@ -281,14 +393,17 @@ module Rigor
|
|
|
281
393
|
# Issue #1794 — a sequence's inherited Enumerable surface reads its Rigor-private element argument, with
|
|
282
394
|
# `self` still the sequence.
|
|
283
395
|
element = ArithmeticSequenceElements.element_receiver(receiver, method_name, environment)
|
|
284
|
-
self_type_override
|
|
396
|
+
self_type_override = sequence_self(element, receiver, self_type_override)
|
|
285
397
|
receiver = element || receiver
|
|
286
398
|
descriptor = receiver_descriptor(receiver)
|
|
287
399
|
return nil unless descriptor
|
|
288
400
|
|
|
289
401
|
class_name, kind, receiver_args = descriptor
|
|
290
|
-
|
|
291
|
-
|
|
402
|
+
if kind == :interface
|
|
403
|
+
return dispatch_interface(receiver, class_name, receiver_args, method_name, args, environment, block_type,
|
|
404
|
+
scope: scope, call_node: call_node)
|
|
405
|
+
end
|
|
406
|
+
method_definition = lookup_method(environment, class_name, kind, method_name, scope, call_node: call_node)
|
|
292
407
|
return nil unless method_definition
|
|
293
408
|
return nil if public_only && method_private?(method_definition)
|
|
294
409
|
# Issue #823 — a declaration that states the member's presence and parameters but not its
|
|
@@ -304,17 +419,11 @@ module Rigor
|
|
|
304
419
|
type_vars = build_type_vars(environment, class_name, receiver_args)
|
|
305
420
|
translate_return_type(
|
|
306
421
|
method_definition,
|
|
307
|
-
class_name: class_name,
|
|
308
|
-
|
|
309
|
-
method_name: method_name,
|
|
310
|
-
args: args,
|
|
311
|
-
type_vars: type_vars,
|
|
312
|
-
block_type: block_type,
|
|
313
|
-
environment: environment,
|
|
422
|
+
class_name: class_name, kind: kind, method_name: method_name, args: args, type_vars: type_vars,
|
|
423
|
+
block_type: block_type, environment: environment,
|
|
314
424
|
self_type_override: self_type_override ||
|
|
315
425
|
SelfSubstitute.for(receiver, receiver_args, method_name, args, block_type),
|
|
316
|
-
scope: scope,
|
|
317
|
-
call_node: call_node
|
|
426
|
+
scope: scope, call_node: call_node
|
|
318
427
|
)
|
|
319
428
|
rescue StandardError
|
|
320
429
|
# Defensive: if RBS' definition builder raises on a broken hierarchy (e.g., partially loaded
|
|
@@ -322,6 +431,55 @@ module Rigor
|
|
|
322
431
|
nil
|
|
323
432
|
end
|
|
324
433
|
|
|
434
|
+
# The `self` a call on a sequence's element keeps: the sequence (#1794), unless the caller already set one.
|
|
435
|
+
# A method rather than a two-value return, which would allocate an Array on every dispatch.
|
|
436
|
+
def sequence_self(element, receiver, self_type_override)
|
|
437
|
+
element ? (self_type_override || receiver) : self_type_override
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
# ADR-130 WD3 — a call on a `Rigor::Plugin` interface value (`Dynamic[Interface]`). A member the interface
|
|
441
|
+
# does not declare declines, so the call falls through the later tiers as one on `Dynamic[top]` does:
|
|
442
|
+
# the value's class is wider than the interface, and reporting such a call is #2686's opt-in rule. A
|
|
443
|
+
# declared member answers through the overloads that take the call's shape ({InterfaceShape}), with
|
|
444
|
+
# `self` the receiver and `instance` untyped. A block-pass that may be nil may run the call block-less, so
|
|
445
|
+
# it answers only when both shapes are taken and their returns agree.
|
|
446
|
+
# rubocop:disable-next Metrics/ParameterLists
|
|
447
|
+
def dispatch_interface(receiver, name, receiver_args, method_name, args, environment, block_type, scope:,
|
|
448
|
+
call_node:)
|
|
449
|
+
definition = interface_method(environment, name, method_name)
|
|
450
|
+
return nil if definition.nil?
|
|
451
|
+
|
|
452
|
+
type_vars = interface_type_vars(environment, name, receiver_args)
|
|
453
|
+
shape = lambda do |shape_block|
|
|
454
|
+
accepted = InterfaceShape.accepting(definition, args, block: !shape_block.nil?, call_node: call_node)
|
|
455
|
+
accepted && translate_return_type(
|
|
456
|
+
accepted, class_name: name, kind: :interface, method_name: method_name, args: args,
|
|
457
|
+
type_vars: type_vars, block_type: shape_block, environment: environment,
|
|
458
|
+
self_type_override: receiver, scope: scope, call_node: call_node
|
|
459
|
+
)
|
|
460
|
+
end
|
|
461
|
+
return shape.call(block_type) unless InterfaceShape.nilable_block_pass?(call_node, scope)
|
|
462
|
+
|
|
463
|
+
block_less = shape.call(nil)
|
|
464
|
+
block_less if block_less && block_less == shape.call(block_type || Type::Combinator.untyped)
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
# The interface's declaration of `method_name`, or nil when it declares none.
|
|
468
|
+
def interface_method(environment, name, method_name)
|
|
469
|
+
environment.rbs_loader.interface_definition(name)&.methods&.[](method_name.to_sym)
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
# The interface's own type parameters bound to the value's type arguments, as {#build_type_vars} binds a
|
|
473
|
+
# class's: a parameter left unbound (a defaulted one) reads `Dynamic[top]`.
|
|
474
|
+
def interface_type_vars(environment, name, receiver_args)
|
|
475
|
+
return NO_TYPE_VARS if receiver_args.empty?
|
|
476
|
+
|
|
477
|
+
param_names = environment.rbs_loader.interface_definition(name)&.type_params || []
|
|
478
|
+
return NO_TYPE_VARS if param_names.empty? || receiver_args.size > param_names.size
|
|
479
|
+
|
|
480
|
+
param_names.first(receiver_args.size).zip(receiver_args).to_h
|
|
481
|
+
end
|
|
482
|
+
|
|
325
483
|
# Issue #661 — `Kernel#<=>: (untyped other) -> 0?` is the IDENTITY comparison: `0` when the two
|
|
326
484
|
# are the same object, `nil` otherwise, and correct as written for a bare `Object`. Reached by
|
|
327
485
|
# inheritance it stops being a statement about the call. `1.day <=> 2.days` typed as `0?`, and a
|
|
@@ -405,10 +563,18 @@ module Rigor
|
|
|
405
563
|
# (ConstantFolding, ShapeDispatch#dispatch_integer_range) do not own.
|
|
406
564
|
["Integer", :instance, []]
|
|
407
565
|
when Type::Dynamic
|
|
408
|
-
|
|
566
|
+
facet_descriptor(receiver.static_facet)
|
|
409
567
|
end
|
|
410
568
|
end
|
|
411
569
|
|
|
570
|
+
# A `Dynamic`'s facet names what the facet names, and a `Rigor::Plugin` interface facet names the interface
|
|
571
|
+
# (ADR-130 WD3), unless dispatch is switched off, where it names nothing, as `top` does.
|
|
572
|
+
def facet_descriptor(facet)
|
|
573
|
+
return receiver_descriptor(facet) unless facet.is_a?(Type::Interface)
|
|
574
|
+
|
|
575
|
+
[facet.name, :interface, facet.type_args] if interface_dispatch?
|
|
576
|
+
end
|
|
577
|
+
|
|
412
578
|
# ADR-48 — project a `Data`/`Struct` member carrier to its tagging class (or the `Data`/`Struct`
|
|
413
579
|
# supertype) so non-member calls (`inspect`, `==`, `frozen?`, ...) resolve through RBS rather
|
|
414
580
|
# than mis-firing undefined-method. Precise member reads were already folded by DataFolding /
|
|
@@ -512,7 +678,10 @@ module Rigor
|
|
|
512
678
|
# ADR-43 WD4 — the allow-list's manifest-declared half: a loaded plugin may name its own
|
|
513
679
|
# contract classes in `rbs_complete_ancestors:` (e.g. rigor-graphql's `GraphQL::Schema::Object`),
|
|
514
680
|
# extending the engine's hard-coded seed without editing this constant.
|
|
515
|
-
|
|
681
|
+
#
|
|
682
|
+
# #2681 — `overriding: true` asks for the declaration a `def` on `class_name` overrides (its parameter
|
|
683
|
+
# binding), so `class_name`'s own definition of the name is set aside; every nearer ancestor still shadows.
|
|
684
|
+
def allowed_rbs_complete_ancestor(environment, class_name, kind, method_name, scope, overriding: false)
|
|
516
685
|
return nil if scope.nil?
|
|
517
686
|
return nil if Rigor::Reflection.rbs_class_known?(class_name, environment: environment)
|
|
518
687
|
|
|
@@ -521,15 +690,22 @@ module Rigor
|
|
|
521
690
|
# this guard the bridge would resolve e.g. `field` on `GraphQL::Schema::Object` while
|
|
522
691
|
# the runtime actually calls a user `def self.field` on an intermediate `BaseObject` —
|
|
523
692
|
# a wrong return type and a false `undefined-method`/arity reading downstream.
|
|
524
|
-
return nil if scope.discovered_method?(class_name, method_name, kind)
|
|
693
|
+
return nil if !overriding && scope.discovered_method?(class_name, method_name, kind)
|
|
525
694
|
|
|
526
695
|
registry = environment&.plugin_registry
|
|
527
696
|
each_source_ancestor_candidate(scope, class_name) do |candidate|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
697
|
+
# #2681 — the engine seed's own source (`lib/rigor/plugin/base.rb`, which this repository's plugin
|
|
698
|
+
# check discovers) is the implementation its RBS describes, not a nearer definer. A project `def` on
|
|
699
|
+
# a manifest-declared ancestor is a reopen (`class Grape::API; def self.desc`), which Ruby calls
|
|
700
|
+
# instead of the declaration, so it still shadows.
|
|
701
|
+
seed = ALLOWED_RBS_COMPLETE_ANCESTORS.include?(candidate)
|
|
702
|
+
return nil if !seed && scope.discovered_method?(candidate, method_name, kind)
|
|
703
|
+
next unless seed || registry&.rbs_complete_ancestor?(candidate)
|
|
704
|
+
|
|
705
|
+
return nil if shadowed_in_ruby_order?(scope, class_name, kind, method_name, candidate,
|
|
706
|
+
overriding: overriding)
|
|
707
|
+
|
|
708
|
+
return candidate
|
|
533
709
|
end
|
|
534
710
|
nil
|
|
535
711
|
end
|
|
@@ -543,7 +719,8 @@ module Rigor
|
|
|
543
719
|
# passed over, as the breadth-first walk always did. An unsettled chain withholds too: a mixin edge the
|
|
544
720
|
# tables cannot place (a receiver-form `Demo.include(M)` after the body, an edge in a conditional, a block or
|
|
545
721
|
# a hook) may put a definer ahead of the ancestor.
|
|
546
|
-
|
|
722
|
+
# With `overriding`, `class_name`'s own entry is the overriding `def` itself and does not shadow.
|
|
723
|
+
def shadowed_in_ruby_order?(scope, class_name, kind, method_name, ancestor, overriding: false)
|
|
547
724
|
chain = Scope::ResolutionChain.for(scope, class_name.to_s, kind, :methods)
|
|
548
725
|
return true if chain.unsettled?
|
|
549
726
|
|
|
@@ -554,6 +731,8 @@ module Rigor
|
|
|
554
731
|
return true if stop.nil?
|
|
555
732
|
|
|
556
733
|
entries.first(stop).any? do |entry|
|
|
734
|
+
next false if overriding && !entry.external? && entry.name == class_name.to_s
|
|
735
|
+
|
|
557
736
|
if entry.external?
|
|
558
737
|
rbs_declares_ahead?(scope, entry, method_name)
|
|
559
738
|
else
|
|
@@ -616,12 +795,22 @@ module Rigor
|
|
|
616
795
|
return nil if scope.nil? || kind != :instance
|
|
617
796
|
return nil if environment.nil?
|
|
618
797
|
|
|
798
|
+
if MemoAudit.enabled? && MemoAudit.suspended?
|
|
799
|
+
return compute_core_stdlib_ancestor_method(environment, class_name, method_name, scope)
|
|
800
|
+
end
|
|
801
|
+
|
|
619
802
|
memo = core_stdlib_memo(environment, scope)
|
|
620
803
|
key = [class_name.to_s, method_name.to_sym]
|
|
621
|
-
|
|
804
|
+
if memo.key?(key)
|
|
805
|
+
return MemoAudit.check("core stdlib ancestor dispatch", memo[key], key[0], key[1]) do
|
|
806
|
+
compute_core_stdlib_ancestor_method(environment, class_name, method_name, scope)
|
|
807
|
+
end
|
|
808
|
+
end
|
|
622
809
|
|
|
623
810
|
store_unless_recorded(memo, key) do
|
|
624
|
-
|
|
811
|
+
MemoAudit.key_audit("core stdlib ancestor dispatch") do
|
|
812
|
+
compute_core_stdlib_ancestor_method(environment, class_name, method_name, scope)
|
|
813
|
+
end
|
|
625
814
|
end
|
|
626
815
|
end
|
|
627
816
|
|
|
@@ -648,12 +837,23 @@ module Rigor
|
|
|
648
837
|
return nil if scope.nil? || kind != :instance
|
|
649
838
|
return nil if environment.nil?
|
|
650
839
|
|
|
840
|
+
if MemoAudit.enabled? && MemoAudit.suspended?
|
|
841
|
+
return compute_included_module_method(environment, class_name, method_name,
|
|
842
|
+
scope)
|
|
843
|
+
end
|
|
844
|
+
|
|
651
845
|
memo = mixin_ancestor_memo(environment, scope)
|
|
652
846
|
key = [class_name.to_s, method_name.to_sym]
|
|
653
|
-
|
|
847
|
+
if memo.key?(key)
|
|
848
|
+
return MemoAudit.check("mixin ancestor dispatch", memo[key], key[0], key[1]) do
|
|
849
|
+
compute_included_module_method(environment, class_name, method_name, scope)
|
|
850
|
+
end
|
|
851
|
+
end
|
|
654
852
|
|
|
655
853
|
store_unless_recorded(memo, key) do
|
|
656
|
-
|
|
854
|
+
MemoAudit.key_audit("mixin ancestor dispatch") do
|
|
855
|
+
compute_included_module_method(environment, class_name, method_name, scope)
|
|
856
|
+
end
|
|
657
857
|
end
|
|
658
858
|
end
|
|
659
859
|
|
|
@@ -681,11 +881,18 @@ module Rigor
|
|
|
681
881
|
|
|
682
882
|
# The same one-slot memo shape as {#core_stdlib_memo}; see there for why it is one slot keyed
|
|
683
883
|
# on the discovery index's identity, and what a recording run may store in it.
|
|
684
|
-
|
|
685
|
-
|
|
884
|
+
#
|
|
885
|
+
# ADR-129 WD1 — a run-scoped Memo, keyed as {#ancestor_memo_slot} says.
|
|
886
|
+
MIXIN_ANCESTOR_MEMO = TypingState.memo(
|
|
887
|
+
:mixin_ancestor_dispatch,
|
|
888
|
+
run_scoped: true, audit: "mixin ancestor dispatch",
|
|
889
|
+
reason: "one slot keyed on the identities of the discovery index and environment, and " \
|
|
890
|
+
"on whether the thread is recording; the answers read nothing else"
|
|
891
|
+
)
|
|
892
|
+
private_constant :MIXIN_ANCESTOR_MEMO
|
|
686
893
|
|
|
687
894
|
def mixin_ancestor_memo(environment, scope)
|
|
688
|
-
ancestor_memo_slot(
|
|
895
|
+
ancestor_memo_slot(MIXIN_ANCESTOR_MEMO, environment, scope)
|
|
689
896
|
end
|
|
690
897
|
|
|
691
898
|
# The declines are conjunctive, so their ORDER is free — and it is chosen so the two that walk
|
|
@@ -855,23 +1062,31 @@ module Rigor
|
|
|
855
1062
|
# the measurement. A `Scope` hands each analysed file its own discovery index, so an
|
|
856
1063
|
# identity-keyed store would pin every file's index, and every RBS definition resolved
|
|
857
1064
|
# against it, for the length of the run.
|
|
858
|
-
|
|
859
|
-
|
|
1065
|
+
#
|
|
1066
|
+
# ADR-129 WD1 — a run-scoped Memo, keyed as {#ancestor_memo_slot} says.
|
|
1067
|
+
CORE_STDLIB_ANCESTOR_MEMO = TypingState.memo(
|
|
1068
|
+
:core_stdlib_ancestor_dispatch,
|
|
1069
|
+
run_scoped: true, audit: "core stdlib ancestor dispatch",
|
|
1070
|
+
reason: "one slot keyed on the identities of the discovery index and environment, and " \
|
|
1071
|
+
"on whether the thread is recording; the answers read nothing else"
|
|
1072
|
+
)
|
|
1073
|
+
private_constant :CORE_STDLIB_ANCESTOR_MEMO
|
|
860
1074
|
|
|
861
1075
|
def core_stdlib_memo(environment, scope)
|
|
862
|
-
ancestor_memo_slot(
|
|
1076
|
+
ancestor_memo_slot(CORE_STDLIB_ANCESTOR_MEMO, environment, scope)
|
|
863
1077
|
end
|
|
864
1078
|
|
|
865
1079
|
# The slot behind both memos. Whether this thread is recording is part of its key, so a slot
|
|
866
1080
|
# filled with every answer is never read by a recording caller, which may only reuse the
|
|
867
1081
|
# answers {#store_unless_recorded} lets through.
|
|
868
|
-
def ancestor_memo_slot(
|
|
1082
|
+
def ancestor_memo_slot(memo_slot, environment, scope)
|
|
869
1083
|
discovery = scope.discovery
|
|
870
1084
|
recording = Rigor::Analysis::DependencyRecorder.recording?
|
|
871
|
-
|
|
1085
|
+
state = TypingState.current
|
|
1086
|
+
slot = state[memo_slot]
|
|
872
1087
|
unless slot && slot[0].equal?(discovery) && slot[1].equal?(environment) && slot[2] == recording
|
|
873
1088
|
slot = [discovery, environment, recording, {}]
|
|
874
|
-
|
|
1089
|
+
state[memo_slot] = slot
|
|
875
1090
|
end
|
|
876
1091
|
slot[3]
|
|
877
1092
|
end
|
|
@@ -1086,7 +1301,42 @@ module Rigor
|
|
|
1086
1301
|
block_required: !block_type.nil?,
|
|
1087
1302
|
environment: environment
|
|
1088
1303
|
)
|
|
1089
|
-
|
|
1304
|
+
if candidates.empty?
|
|
1305
|
+
return unselected_return(method_definition, self_type, instance_type, type_vars, args, block_type,
|
|
1306
|
+
scope, call_node, call_site, environment, keywords_last)
|
|
1307
|
+
end
|
|
1308
|
+
|
|
1309
|
+
selected_return(candidates, method_definition, self_type, instance_type, type_vars, args, block_type, scope,
|
|
1310
|
+
call_node, call_site, environment, keywords_last)
|
|
1311
|
+
end
|
|
1312
|
+
|
|
1313
|
+
# Issue #2429 — a block call no overload matches, whose block overloads the arity fallback cannot decide
|
|
1314
|
+
# between, selects nothing. An argument union may still select per value (#2379): `S.h(flag ? "s" : 1) { }`
|
|
1315
|
+
# takes the `String` arm for `"s"` and the `Integer` arm for `1`. Otherwise the tier declines (nil), so a
|
|
1316
|
+
# later tier, such as the project's own definition of the method, answers.
|
|
1317
|
+
# rubocop:disable Metrics/ParameterLists
|
|
1318
|
+
def unselected_return(method_definition, self_type, instance_type, type_vars, args, block_type, scope,
|
|
1319
|
+
call_node, call_site, environment, keywords_last)
|
|
1320
|
+
# rubocop:enable Metrics/ParameterLists
|
|
1321
|
+
return nil if keywords_last
|
|
1322
|
+
|
|
1323
|
+
distributed = distributed_argument_return(method_definition, nil, self_type, instance_type, type_vars, args,
|
|
1324
|
+
block_type, scope, call_node, call_site, environment)
|
|
1325
|
+
distributed.equal?(NOT_DISTRIBUTED) ? nil : distributed
|
|
1326
|
+
end
|
|
1327
|
+
|
|
1328
|
+
# The return of the overloads the call selected: per argument value where a union or supertype argument
|
|
1329
|
+
# selects more than `candidates` ({#distributed_argument_return}), else their join.
|
|
1330
|
+
# rubocop:disable Metrics/ParameterLists
|
|
1331
|
+
def selected_return(candidates, method_definition, self_type, instance_type, type_vars, args, block_type,
|
|
1332
|
+
scope, call_node, call_site, environment, keywords_last)
|
|
1333
|
+
# rubocop:enable Metrics/ParameterLists
|
|
1334
|
+
if candidates.size == 1 && !keywords_last
|
|
1335
|
+
distributed = distributed_argument_return(method_definition, candidates.first, self_type, instance_type,
|
|
1336
|
+
type_vars, args, block_type, scope, call_node, call_site,
|
|
1337
|
+
environment)
|
|
1338
|
+
return distributed unless distributed.equal?(NOT_DISTRIBUTED)
|
|
1339
|
+
end
|
|
1090
1340
|
|
|
1091
1341
|
record_dispatch_provenance(method_definition, candidates.first, scope, call_node, call_site)
|
|
1092
1342
|
join_candidate_returns(
|
|
@@ -1169,6 +1419,75 @@ module Rigor
|
|
|
1169
1419
|
end
|
|
1170
1420
|
end
|
|
1171
1421
|
|
|
1422
|
+
# Issue #2379 — a positional argument selects its overload per runtime value, as the receiver does
|
|
1423
|
+
# (`dispatch` distributes a union receiver). Selecting once for the whole argument took the first overload
|
|
1424
|
+
# it `maybe`-matched: `Integer#+` with an `Integer | Float` argument read `(Integer) -> Integer`, and with a
|
|
1425
|
+
# `Numeric` one as well. So a precise union argument selects per member ({ArgumentDistribution.lists}; a
|
|
1426
|
+
# member no overload takes keeps the first-overload fallback, as a non-matching argument does), and a
|
|
1427
|
+
# nominal argument wider than the selected overload's parameter takes every strictly typed overload it
|
|
1428
|
+
# overlaps ({OverloadSelector.contested_matches}). The returns join as a plain union, and a return that does
|
|
1429
|
+
# not translate declines the call (nil), as the #521 join does. Union arguments that combine past the list
|
|
1430
|
+
# limit answer `Dynamic[top]`: neither the first overload nor any narrower reading is sound there. When
|
|
1431
|
+
# every list selects `chosen` alone the call answers {NOT_DISTRIBUTED} and reads as before, so a precise
|
|
1432
|
+
# argument keeps its answer; so does a call holding an imprecise argument (the #521 join owns it) or a splat.
|
|
1433
|
+
# rubocop:disable Metrics/ParameterLists
|
|
1434
|
+
def distributed_argument_return(method_definition, chosen, self_type, instance_type, type_vars, args,
|
|
1435
|
+
block_type, scope, call_node, call_site, environment)
|
|
1436
|
+
# rubocop:enable Metrics/ParameterLists
|
|
1437
|
+
selector = { self_type: self_type.is_a?(Type::Dynamic) ? self_type.static_facet : self_type,
|
|
1438
|
+
instance_type: instance_type, type_vars: type_vars, block_required: !block_type.nil?,
|
|
1439
|
+
environment: environment }
|
|
1440
|
+
return NOT_DISTRIBUTED unless distributable_arguments?(method_definition, args, call_node)
|
|
1441
|
+
|
|
1442
|
+
lists = ArgumentDistribution.lists(args)
|
|
1443
|
+
return Type::Combinator.untyped if lists.equal?(ArgumentDistribution::OVER_LIMIT)
|
|
1444
|
+
|
|
1445
|
+
per_list = argument_distribution_matches(method_definition, chosen, args, lists, selector)
|
|
1446
|
+
return NOT_DISTRIBUTED if per_list.nil?
|
|
1447
|
+
|
|
1448
|
+
overloads = per_list.flat_map(&:last).uniq
|
|
1449
|
+
if overloads.size == 1
|
|
1450
|
+
record_dispatch_provenance(method_definition, overloads.first, scope, call_node, call_site)
|
|
1451
|
+
end
|
|
1452
|
+
returns = per_list.flat_map do |arg_types, matches|
|
|
1453
|
+
matches.map do |method_type|
|
|
1454
|
+
join_candidate_returns(
|
|
1455
|
+
[method_type],
|
|
1456
|
+
method_definition: method_definition,
|
|
1457
|
+
self_type: self_type, instance_type: instance_type, type_vars: type_vars,
|
|
1458
|
+
args: arg_types, block_type: block_type, scope: scope, call_node: call_node, call_site: call_site,
|
|
1459
|
+
alias_expander: environment.rbs_loader
|
|
1460
|
+
)
|
|
1461
|
+
end
|
|
1462
|
+
end
|
|
1463
|
+
returns.any?(&:nil?) ? nil : Type::Combinator.union(*returns.uniq)
|
|
1464
|
+
end
|
|
1465
|
+
|
|
1466
|
+
# Each argument list (`lists`, from {ArgumentDistribution.lists}, or the call's own) with the overloads it
|
|
1467
|
+
# selects, or nil when every list selects `chosen` alone. `selector` holds {OverloadSelector}'s keyword
|
|
1468
|
+
# inputs.
|
|
1469
|
+
def argument_distribution_matches(method_definition, chosen, args, lists, selector)
|
|
1470
|
+
return nil if lists.nil? && args.none?(Type::Nominal)
|
|
1471
|
+
|
|
1472
|
+
per_list = (lists || [args]).map do |arg_types|
|
|
1473
|
+
picked = chosen if arg_types.equal?(args)
|
|
1474
|
+
picked ||= OverloadSelector.select(method_definition, arg_types:, **selector)
|
|
1475
|
+
# #2429's block fallback may answer no overload; the call then reads as it did undistributed.
|
|
1476
|
+
return nil unless picked
|
|
1477
|
+
|
|
1478
|
+
contested = OverloadSelector.contested_matches(method_definition, picked, arg_types:, **selector)
|
|
1479
|
+
[arg_types, contested || [picked]]
|
|
1480
|
+
end
|
|
1481
|
+
per_list unless per_list.all? { |_, matches| matches.size == 1 && matches.first.equal?(chosen) }
|
|
1482
|
+
end
|
|
1483
|
+
|
|
1484
|
+
# Only a call with overloads to choose between and precise arguments it spells out one by one may select
|
|
1485
|
+
# differently per argument value: an imprecise argument is the #521 join's, a splat hides the positions.
|
|
1486
|
+
def distributable_arguments?(method_definition, args, call_node)
|
|
1487
|
+
method_definition.method_types.size > 1 && !args.empty? &&
|
|
1488
|
+
!ImpreciseArgument.any_in?(args, false) && !splat_indices(call_node, args)
|
|
1489
|
+
end
|
|
1490
|
+
|
|
1172
1491
|
def join_distributed_returns(returns, keywords)
|
|
1173
1492
|
return nil if returns.any?(&:nil?)
|
|
1174
1493
|
|
|
@@ -1412,11 +1731,24 @@ module Rigor
|
|
|
1412
1731
|
# to {#shared_value_class} where both sides have one, and to `Dynamic[top]` otherwise. The key
|
|
1413
1732
|
# stays in the map either way so {#compose_arg_type_vars} does not bind the variable from the
|
|
1414
1733
|
# argument alone.
|
|
1734
|
+
#
|
|
1735
|
+
# A block return spelled as the variable beside falsey arms (`(nil | false | U)`, `U?`) solves the
|
|
1736
|
+
# variable from the block's type less those arms ({#strip_falsey_arms}): `filter_map` keeps only
|
|
1737
|
+
# the block's truthy values, so a block answering `String | nil` binds `U` to `String`. The strip
|
|
1738
|
+
# trusts the declared method to discard those values, so a receiver class whose Ruby source
|
|
1739
|
+
# defines the method itself leaves the variable unbound: `def filter_map(&b) = map(&b).compact`
|
|
1740
|
+
# under an `include Enumerable[Integer]` declaration keeps `false`, and `Array[true]` would fold
|
|
1741
|
+
# `b.last == false` always-falsey on correct code.
|
|
1415
1742
|
def compose_block_type_vars(method_type, type_vars, block_type, args, scope:, call_node:, call_site:)
|
|
1416
1743
|
return type_vars if block_type.nil?
|
|
1417
1744
|
|
|
1418
1745
|
block_var_name = method_type_block_return_variable(method_type)
|
|
1419
1746
|
return type_vars if block_var_name.nil?
|
|
1747
|
+
|
|
1748
|
+
arms = block_return_falsey_arms(method_type)
|
|
1749
|
+
return type_vars if arms.any? && project_redefined?(scope, call_site)
|
|
1750
|
+
|
|
1751
|
+
block_type = strip_falsey_arms(block_type, arms)
|
|
1420
1752
|
return type_vars.merge(block_var_name => block_type) unless
|
|
1421
1753
|
argument_reaches_variable?(method_type, block_var_name, args)
|
|
1422
1754
|
|
|
@@ -1564,7 +1896,8 @@ module Rigor
|
|
|
1564
1896
|
#
|
|
1565
1897
|
# * only a positional parameter (required or optional) whose declared type is EXACTLY
|
|
1566
1898
|
# `RBS::Types::Variable`, plus the one container shape issue #834 admits (see
|
|
1567
|
-
# {#range_element_binding})
|
|
1899
|
+
# {#range_element_binding}) and the class-object shape issue #2828 admits ({#singleton_param_binding}) —
|
|
1900
|
+
# no general container walk, so `(Array[T]) -> T` still degrades;
|
|
1568
1901
|
# * only names the SELECTED overload declares in its own `type_params` (a class-level variable
|
|
1569
1902
|
# keeps its receiver-derived binding);
|
|
1570
1903
|
# * an existing key wins, so the receiver and the block return type both outrank an argument;
|
|
@@ -1573,7 +1906,7 @@ module Rigor
|
|
|
1573
1906
|
#
|
|
1574
1907
|
# See {#arg_binding_permitted?} for why the contribution is gated on the call site.
|
|
1575
1908
|
def compose_arg_type_vars(method_type, type_vars, args, scope:, call_node:, call_site:)
|
|
1576
|
-
bindings = arg_type_var_bindings(method_type, type_vars, args)
|
|
1909
|
+
bindings = arg_type_var_bindings(method_type, type_vars, args, scope&.environment, call_node)
|
|
1577
1910
|
return type_vars if bindings.empty?
|
|
1578
1911
|
return type_vars unless arg_binding_permitted?(scope, call_node, call_site)
|
|
1579
1912
|
|
|
@@ -1582,7 +1915,7 @@ module Rigor
|
|
|
1582
1915
|
|
|
1583
1916
|
# The candidate bindings, computed before the guard so a non-generic overload (the overwhelming
|
|
1584
1917
|
# majority) never pays for the `scope` probes.
|
|
1585
|
-
def arg_type_var_bindings(method_type, type_vars, args)
|
|
1918
|
+
def arg_type_var_bindings(method_type, type_vars, args, environment = nil, call_node = nil)
|
|
1586
1919
|
declared = declared_type_param_names(method_type)
|
|
1587
1920
|
return EMPTY_TYPE_VARS if declared.empty? || args.empty?
|
|
1588
1921
|
|
|
@@ -1594,6 +1927,11 @@ module Rigor
|
|
|
1594
1927
|
next if arg.nil?
|
|
1595
1928
|
|
|
1596
1929
|
name, bound = param_binding(param, arg, declared, type_vars)
|
|
1930
|
+
if name.nil?
|
|
1931
|
+
name, bound = singleton_param_binding(method_type, param, arg, declared, type_vars,
|
|
1932
|
+
environment)
|
|
1933
|
+
next if name && class_factory_argument?(call_node)
|
|
1934
|
+
end
|
|
1597
1935
|
next if name.nil?
|
|
1598
1936
|
|
|
1599
1937
|
bound = Type::Combinator.widen_value_pinned(bound) if upper_bounded?(method_type, name)
|
|
@@ -1629,6 +1967,80 @@ module Rigor
|
|
|
1629
1967
|
range_element_binding(param, arg, declared, type_vars)
|
|
1630
1968
|
end
|
|
1631
1969
|
|
|
1970
|
+
# Issue #2828 — a positional parameter spelled `singleton(T)` for a method-level variable `T` binds `T`
|
|
1971
|
+
# to the instance type of the class object the call passes: `Resolv::DNS#getresources: [T < Resource]
|
|
1972
|
+
# (dns_name, singleton(T)) -> Array[T]` answers `Array[Resolv::DNS::Resource::IN::MX]` for an
|
|
1973
|
+
# `IN::MX` argument. A union of class objects binds the union of their instances, as a bare `T` binds
|
|
1974
|
+
# its argument union. Every other argument — `Dynamic`, an instance, a union with a non-class member —
|
|
1975
|
+
# binds nothing, and neither does a class the declared upper bound does not provably admit: an
|
|
1976
|
+
# interface, alias or other non-class bound, or a class the environment cannot order below the bound.
|
|
1977
|
+
def singleton_param_binding(method_type, param, arg, declared, type_vars, environment)
|
|
1978
|
+
name = singleton_variable_name(param, declared, type_vars)
|
|
1979
|
+
return NO_BINDING if name.nil?
|
|
1980
|
+
|
|
1981
|
+
members = arg.is_a?(Type::Union) ? arg.members : [arg]
|
|
1982
|
+
return NO_BINDING unless members.all?(Type::Singleton)
|
|
1983
|
+
|
|
1984
|
+
within = members.all? { |member| within_upper_bound?(method_type, name, member, environment) }
|
|
1985
|
+
return NO_BINDING unless within
|
|
1986
|
+
|
|
1987
|
+
instances = members.map { |member| Type::Combinator.nominal_of(member.class_name) }
|
|
1988
|
+
[name, instances.size == 1 ? instances.first : Type::Combinator.union(*instances)]
|
|
1989
|
+
end
|
|
1990
|
+
|
|
1991
|
+
# RBS parses `singleton(T)` as a class singleton whose type name is the bare, unqualified `T`; the
|
|
1992
|
+
# parameter names a variable when that name is one the overload declares and has not yet bound.
|
|
1993
|
+
def singleton_variable_name(param, declared, type_vars)
|
|
1994
|
+
declared_type = param.type
|
|
1995
|
+
return nil unless declared_type.is_a?(RBS::Types::ClassSingleton)
|
|
1996
|
+
|
|
1997
|
+
type_name = declared_type.name
|
|
1998
|
+
return nil unless type_name.namespace.empty?
|
|
1999
|
+
|
|
2000
|
+
name = type_name.name
|
|
2001
|
+
return nil unless declared.include?(name)
|
|
2002
|
+
return nil if type_vars.key?(name)
|
|
2003
|
+
|
|
2004
|
+
name
|
|
2005
|
+
end
|
|
2006
|
+
|
|
2007
|
+
# Whether the call passes, as any positional argument, a class built by `Class.new`, `Struct.new` or
|
|
2008
|
+
# `Data.define` with a block. The engine types `Class.new(Widget) { def extra = 1 }` as `singleton(Widget)`,
|
|
2009
|
+
# so binding `T` from it would read the instance as a plain `Widget` and report `extra` as undefined. The
|
|
2010
|
+
# argument node is visible here; a local the class was assigned to is not, and still binds.
|
|
2011
|
+
def class_factory_argument?(call_node)
|
|
2012
|
+
arguments = call_node.respond_to?(:arguments) ? call_node.arguments&.arguments : nil
|
|
2013
|
+
return false if arguments.nil?
|
|
2014
|
+
|
|
2015
|
+
arguments.any? { |argument| class_factory_call?(argument) }
|
|
2016
|
+
end
|
|
2017
|
+
|
|
2018
|
+
CLASS_FACTORIES = { new: %w[Class Struct].freeze, define: %w[Data].freeze }.freeze
|
|
2019
|
+
private_constant :CLASS_FACTORIES
|
|
2020
|
+
|
|
2021
|
+
def class_factory_call?(node)
|
|
2022
|
+
return false unless node.is_a?(Prism::CallNode) && node.block.is_a?(Prism::BlockNode)
|
|
2023
|
+
|
|
2024
|
+
receivers = CLASS_FACTORIES[node.name]
|
|
2025
|
+
return false if receivers.nil?
|
|
2026
|
+
|
|
2027
|
+
receiver = node.receiver
|
|
2028
|
+
name = case receiver
|
|
2029
|
+
when Prism::ConstantReadNode then receiver.name.to_s
|
|
2030
|
+
when Prism::ConstantPathNode then receiver.parent.nil? ? receiver.name.to_s : nil
|
|
2031
|
+
end
|
|
2032
|
+
receivers.include?(name)
|
|
2033
|
+
end
|
|
2034
|
+
|
|
2035
|
+
def within_upper_bound?(method_type, name, singleton, environment)
|
|
2036
|
+
type_param = method_type.type_params.find { |tp| tp.name == name }
|
|
2037
|
+
bound = type_param.respond_to?(:upper_bound_type) ? type_param.upper_bound_type : type_param.upper_bound
|
|
2038
|
+
return true if bound.nil?
|
|
2039
|
+
return false unless bound.is_a?(RBS::Types::ClassInstance) && environment
|
|
2040
|
+
|
|
2041
|
+
%i[equal subclass].include?(environment.class_ordering(singleton.class_name, bound.name.to_s))
|
|
2042
|
+
end
|
|
2043
|
+
|
|
1632
2044
|
def declared_type_param_names(method_type)
|
|
1633
2045
|
params = method_type.respond_to?(:type_params) ? method_type.type_params : nil
|
|
1634
2046
|
return EMPTY_TYPE_PARAM_NAMES if params.nil? || params.empty?
|
|
@@ -1704,7 +2116,7 @@ module Rigor
|
|
|
1704
2116
|
# dress up an absence of evidence as an inference. The variable stays unbound and degrades as it
|
|
1705
2117
|
# did before, which is the same value anyway.
|
|
1706
2118
|
def no_static_evidence?(arg)
|
|
1707
|
-
|
|
2119
|
+
Type::Combinator.untyped?(arg)
|
|
1708
2120
|
end
|
|
1709
2121
|
|
|
1710
2122
|
# Issue #303 — the FP guard on the argument-position binding.
|
|
@@ -1751,27 +2163,89 @@ module Rigor
|
|
|
1751
2163
|
end
|
|
1752
2164
|
|
|
1753
2165
|
def block_return_variable(method_type)
|
|
2166
|
+
return_type = block_return_type(method_type)
|
|
2167
|
+
return return_type if return_type.is_a?(RBS::Types::Variable)
|
|
2168
|
+
|
|
2169
|
+
falsey_armed_variable(return_type)&.first
|
|
2170
|
+
end
|
|
2171
|
+
|
|
2172
|
+
def block_return_type(method_type)
|
|
1754
2173
|
block = method_type.respond_to?(:block) ? method_type.block : nil
|
|
1755
2174
|
return nil if block.nil?
|
|
1756
2175
|
|
|
1757
2176
|
fun = block.type
|
|
1758
|
-
|
|
2177
|
+
fun.respond_to?(:return_type) ? fun.return_type : nil
|
|
2178
|
+
end
|
|
1759
2179
|
|
|
1760
|
-
|
|
1761
|
-
|
|
2180
|
+
# The falsey arms ({#falsey_armed_variable}) the block's declared return spells beside its variable;
|
|
2181
|
+
# empty for a bare variable.
|
|
2182
|
+
def block_return_falsey_arms(method_type)
|
|
2183
|
+
falsey_armed_variable(block_return_type(method_type))&.last || EMPTY_FALSEY_ARMS
|
|
2184
|
+
end
|
|
2185
|
+
|
|
2186
|
+
EMPTY_FALSEY_ARMS = [].freeze
|
|
2187
|
+
private_constant :EMPTY_FALSEY_ARMS
|
|
2188
|
+
|
|
2189
|
+
# A block return that is one type variable beside `nil` and/or `false` and nothing else —
|
|
2190
|
+
# `Enumerable#filter_map`'s `(nil | false | U)`, or `U?` — as `[variable, arms]`, the arms a subset
|
|
2191
|
+
# of `[:nil, :false]`. Anything else, `bool` among them, is nil: a block return must only lose
|
|
2192
|
+
# values the method itself discards, and a `false`-only arm has no carrier that removes `false`
|
|
2193
|
+
# alone from a `Dynamic` block type, so it declines too.
|
|
2194
|
+
def falsey_armed_variable(return_type)
|
|
2195
|
+
members =
|
|
2196
|
+
case return_type
|
|
2197
|
+
when RBS::Types::Optional then [return_type.type, RBS::Types::Bases::Nil.new(location: nil)]
|
|
2198
|
+
when RBS::Types::Union then return_type.types
|
|
2199
|
+
else return nil
|
|
2200
|
+
end
|
|
2201
|
+
variables, arms = members.partition { |member| member.is_a?(RBS::Types::Variable) }
|
|
2202
|
+
return nil unless variables.size == 1
|
|
2203
|
+
|
|
2204
|
+
arm_names = arms.map { |arm| falsey_arm_name(arm) }
|
|
2205
|
+
return nil if arm_names.include?(nil) || !arm_names.include?(:nil)
|
|
2206
|
+
|
|
2207
|
+
[variables.first, arm_names.uniq]
|
|
2208
|
+
end
|
|
2209
|
+
|
|
2210
|
+
def falsey_arm_name(arm)
|
|
2211
|
+
case arm
|
|
2212
|
+
when RBS::Types::Bases::Nil then :nil
|
|
2213
|
+
when RBS::Types::Literal then arm.literal == false ? :false : nil # rubocop:disable Lint/BooleanSymbol
|
|
2214
|
+
end
|
|
2215
|
+
end
|
|
2216
|
+
|
|
2217
|
+
# The block's type less the falsey values the declared return spells beside the variable: its
|
|
2218
|
+
# truthy fragment for `nil | false`, its non-nil fragment for `nil` alone. A `Dynamic` block type
|
|
2219
|
+
# passes through unchanged, so an untyped block still binds `Dynamic[top]`. A block that answers
|
|
2220
|
+
# only the stripped values binds `bot`, so `filter_map { nil }` is `Array[bot]`: an Array that
|
|
2221
|
+
# holds no element.
|
|
2222
|
+
# Whether the receiver class's Ruby source defines the dispatched method, the probe
|
|
2223
|
+
# {#arg_binding_permitted?} also reads. Without a scope there is no discovery to consult.
|
|
2224
|
+
def project_redefined?(scope, call_site)
|
|
2225
|
+
return false if scope.nil?
|
|
2226
|
+
|
|
2227
|
+
class_name, method_name, kind = call_site
|
|
2228
|
+
return false if method_name.nil?
|
|
2229
|
+
|
|
2230
|
+
scope.discovered_method?(class_name, method_name, kind)
|
|
2231
|
+
end
|
|
2232
|
+
|
|
2233
|
+
def strip_falsey_arms(block_type, arms)
|
|
2234
|
+
return block_type if arms.empty?
|
|
2235
|
+
return Narrowing.narrow_truthy(block_type) if arms.include?(:false) # rubocop:disable Lint/BooleanSymbol
|
|
2236
|
+
|
|
2237
|
+
Narrowing.narrow_non_nil(block_type)
|
|
1762
2238
|
end
|
|
1763
2239
|
|
|
1764
2240
|
# ----- block parameter probe (Phase C sub-phase 1) -----
|
|
1765
2241
|
|
|
1766
2242
|
def probe_block_param_types(receiver:, method_name:, args:, environment:, scope: nil, call_node: nil)
|
|
1767
2243
|
args ||= []
|
|
1768
|
-
keywords_last = ArgumentNodes.keyword_hash_last?(call_node, args)
|
|
1769
|
-
splat = splat_indices(call_node, args)
|
|
1770
2244
|
case receiver
|
|
1771
2245
|
when Type::Union
|
|
1772
|
-
probe_block_param_types_union(receiver, method_name, args, environment, scope,
|
|
2246
|
+
probe_block_param_types_union(receiver, method_name, args, environment, scope, call_node)
|
|
1773
2247
|
else
|
|
1774
|
-
probe_block_param_types_one(receiver, method_name, args, environment, scope,
|
|
2248
|
+
probe_block_param_types_one(receiver, method_name, args, environment, scope, call_node)
|
|
1775
2249
|
end
|
|
1776
2250
|
end
|
|
1777
2251
|
|
|
@@ -1779,9 +2253,9 @@ module Rigor
|
|
|
1779
2253
|
# member resolves the same arity and types (otherwise the call sites would have to thread
|
|
1780
2254
|
# per-member binders, which the slice does not support yet). Mismatches degrade to the empty
|
|
1781
2255
|
# array so the binder defaults all params to Dynamic[Top].
|
|
1782
|
-
def probe_block_param_types_union(receiver, method_name, args, environment, scope,
|
|
2256
|
+
def probe_block_param_types_union(receiver, method_name, args, environment, scope, call_node)
|
|
1783
2257
|
results = receiver.members.map do |member|
|
|
1784
|
-
probe_block_param_types_one(member, method_name, args, environment, scope,
|
|
2258
|
+
probe_block_param_types_one(member, method_name, args, environment, scope, call_node)
|
|
1785
2259
|
end
|
|
1786
2260
|
return [] if results.empty?
|
|
1787
2261
|
return [] unless results.all? { |r| r == results.first }
|
|
@@ -1789,44 +2263,52 @@ module Rigor
|
|
|
1789
2263
|
results.first
|
|
1790
2264
|
end
|
|
1791
2265
|
|
|
1792
|
-
def probe_block_param_types_one(receiver, method_name, args, environment, scope,
|
|
2266
|
+
def probe_block_param_types_one(receiver, method_name, args, environment, scope, call_node)
|
|
1793
2267
|
receiver = ArithmeticSequenceElements.element_receiver(receiver, method_name, environment, block: true) ||
|
|
1794
2268
|
receiver
|
|
1795
2269
|
descriptor = receiver_descriptor(receiver)
|
|
1796
2270
|
return [] unless descriptor
|
|
1797
2271
|
|
|
1798
2272
|
class_name, kind, receiver_args = descriptor
|
|
1799
|
-
method_definition =
|
|
2273
|
+
method_definition = probe_definition(environment, class_name, kind, method_name, args, scope, call_node)
|
|
1800
2274
|
return [] unless method_definition
|
|
1801
2275
|
|
|
1802
|
-
type_vars =
|
|
1803
|
-
|
|
2276
|
+
type_vars =
|
|
2277
|
+
if kind == :interface
|
|
2278
|
+
interface_type_vars(environment, class_name, receiver_args)
|
|
2279
|
+
else
|
|
2280
|
+
build_type_vars(environment, class_name, receiver_args)
|
|
2281
|
+
end
|
|
2282
|
+
params = extract_block_param_types(
|
|
1804
2283
|
method_definition,
|
|
1805
|
-
class_name: class_name,
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
environment: environment,
|
|
1810
|
-
receiver: receiver,
|
|
1811
|
-
receiver_args: receiver_args,
|
|
1812
|
-
method_name: method_name,
|
|
1813
|
-
keywords_last: keywords_last, positional_splat: splat
|
|
2284
|
+
class_name: class_name, kind: kind, args: args, type_vars: type_vars, environment: environment,
|
|
2285
|
+
receiver: receiver, receiver_args: receiver_args, method_name: method_name,
|
|
2286
|
+
keywords_last: ArgumentNodes.keyword_hash_last?(call_node, args),
|
|
2287
|
+
positional_splat: splat_indices(call_node, args), scope: scope, call_node: call_node
|
|
1814
2288
|
)
|
|
2289
|
+
ClassKeyedBlockParams.rebind(method_definition, method_name, args, params)
|
|
1815
2290
|
rescue StandardError
|
|
1816
2291
|
[]
|
|
1817
2292
|
end
|
|
1818
2293
|
|
|
2294
|
+
# The declaration the block-parameter probe reads. On an interface value a block literal binds its parameters
|
|
2295
|
+
# through the overloads that take the call's shape, as the call's return does (ADR-130 WD3,
|
|
2296
|
+
# {#dispatch_interface}).
|
|
2297
|
+
def probe_definition(environment, class_name, kind, method_name, args, scope, call_node)
|
|
2298
|
+
return lookup_method(environment, class_name, kind, method_name, scope) unless kind == :interface
|
|
2299
|
+
|
|
2300
|
+
declared = interface_method(environment, class_name, method_name)
|
|
2301
|
+
declared && InterfaceShape.accepting(declared, args, block: true, call_node: call_node)
|
|
2302
|
+
end
|
|
2303
|
+
|
|
1819
2304
|
# rubocop:disable Metrics/ParameterLists
|
|
1820
2305
|
def extract_block_param_types(method_definition, class_name:, kind:, args:, type_vars:,
|
|
1821
2306
|
environment: nil, receiver: nil, receiver_args: [],
|
|
1822
|
-
method_name: nil, keywords_last: false, positional_splat: nil
|
|
2307
|
+
method_name: nil, keywords_last: false, positional_splat: nil,
|
|
2308
|
+
scope: nil, call_node: nil)
|
|
1823
2309
|
# rubocop:enable Metrics/ParameterLists
|
|
1824
2310
|
instance_type = Type::Combinator.nominal_of(class_name)
|
|
1825
|
-
self_type =
|
|
1826
|
-
case kind
|
|
1827
|
-
when :singleton then Type::Combinator.singleton_of(class_name)
|
|
1828
|
-
else instance_type
|
|
1829
|
-
end
|
|
2311
|
+
self_type = block_self_type(kind, class_name, instance_type, receiver)
|
|
1830
2312
|
|
|
1831
2313
|
# Issue #1130 — a block parameter that receives `self` (`Object#tap` yields it) must see the
|
|
1832
2314
|
# receiver's type arguments through the SAME {SelfSubstitute} verdict the return path applies
|
|
@@ -1862,13 +2344,59 @@ module Rigor
|
|
|
1862
2344
|
instance_type: instance_type,
|
|
1863
2345
|
type_vars: type_vars,
|
|
1864
2346
|
block_required: true,
|
|
1865
|
-
environment: environment
|
|
2347
|
+
environment: environment,
|
|
2348
|
+
# A call no overload matches binds no block parameter from the return path's block fallback (#2429).
|
|
2349
|
+
block_fallback: false
|
|
1866
2350
|
)
|
|
1867
2351
|
return [] unless method_type
|
|
1868
2352
|
|
|
2353
|
+
type_vars = compose_singleton_arg_type_vars(method_type, type_vars, args, environment,
|
|
2354
|
+
scope: scope, call_node: call_node,
|
|
2355
|
+
call_site: [class_name, method_name, kind])
|
|
1869
2356
|
block_params_of(method_type, self_type, instance_type, type_vars, environment) || []
|
|
1870
2357
|
end
|
|
1871
2358
|
|
|
2359
|
+
# Issue #2828 — the block-parameter twin of {#compose_arg_type_vars}, for the `singleton(T)` shape alone
|
|
2360
|
+
# ({#singleton_param_binding}): `Resolv::DNS#each_resource: [T < Resource] (dns_name, singleton(T))
|
|
2361
|
+
# { (T) -> void } -> void` yields the typeclass's instances. A bare `T` parameter stays out of the block
|
|
2362
|
+
# path: it binds the argument's own (possibly value-pinned) type, and `each_with_object([])`'s memo
|
|
2363
|
+
# would read as the empty tuple the block then appends to.
|
|
2364
|
+
def compose_singleton_arg_type_vars(method_type, type_vars, args, environment, scope:, call_node:, call_site:)
|
|
2365
|
+
declared = declared_type_param_names(method_type)
|
|
2366
|
+
return type_vars if declared.empty? || args.empty?
|
|
2367
|
+
|
|
2368
|
+
fun = method_type.type
|
|
2369
|
+
return type_vars unless fun.respond_to?(:required_positionals)
|
|
2370
|
+
|
|
2371
|
+
bindings = singleton_bindings(method_type, fun, args, declared, type_vars, environment)
|
|
2372
|
+
return type_vars if bindings.empty? || class_factory_argument?(call_node)
|
|
2373
|
+
return type_vars unless arg_binding_permitted?(scope, call_node, call_site)
|
|
2374
|
+
|
|
2375
|
+
type_vars.merge(bindings)
|
|
2376
|
+
end
|
|
2377
|
+
|
|
2378
|
+
def singleton_bindings(method_type, fun, args, declared, type_vars, environment)
|
|
2379
|
+
positionals = fun.required_positionals + fun.optional_positionals
|
|
2380
|
+
positionals.zip(args).each_with_object({}) do |(param, arg), bindings|
|
|
2381
|
+
next if arg.nil?
|
|
2382
|
+
|
|
2383
|
+
name, bound = singleton_param_binding(method_type, param, arg, declared, type_vars, environment)
|
|
2384
|
+
next if name.nil?
|
|
2385
|
+
|
|
2386
|
+
bindings[name] = bindings.key?(name) ? Type::Combinator.union(bindings[name], bound) : bound
|
|
2387
|
+
end
|
|
2388
|
+
end
|
|
2389
|
+
|
|
2390
|
+
# What RBS `self` reads in a block parameter of the member: the class's instance or singleton, and inside an
|
|
2391
|
+
# interface member the receiver (ADR-130 WD1).
|
|
2392
|
+
def block_self_type(kind, class_name, instance_type, receiver)
|
|
2393
|
+
case kind
|
|
2394
|
+
when :singleton then Type::Combinator.singleton_of(class_name)
|
|
2395
|
+
when :interface then receiver
|
|
2396
|
+
else instance_type
|
|
2397
|
+
end
|
|
2398
|
+
end
|
|
2399
|
+
|
|
1872
2400
|
# The block parameters every overload in `matches` declares alike, or no information (`[]`).
|
|
1873
2401
|
def agreed_block_params(matches, self_type, instance_type, type_vars, environment)
|
|
1874
2402
|
answers = matches.uniq.map do |method_type|
|
|
@@ -1940,7 +2468,7 @@ module Rigor
|
|
|
1940
2468
|
# rubocop:enable Metrics/ParameterLists
|
|
1941
2469
|
unless FacetDistribution.faceted?(arg_types)
|
|
1942
2470
|
return OverloadSelector.select_declared(method_definition, arg_types, selector_self, instance_type,
|
|
1943
|
-
type_vars, block_required, environment, true, true, splats)
|
|
2471
|
+
type_vars, block_required, environment, true, true, splats, false)
|
|
1944
2472
|
end
|
|
1945
2473
|
|
|
1946
2474
|
facet_matches(method_definition, arg_types, selector_self, instance_type, type_vars, block_required,
|
|
@@ -1960,7 +2488,7 @@ module Rigor
|
|
|
1960
2488
|
# The fallback list (`member` false) answers every gradual match rather than the strict pass's pick.
|
|
1961
2489
|
OverloadSelector.select_declared(method_definition, list, selector_self, instance_type, type_vars,
|
|
1962
2490
|
block_required, environment, member || :gradual, keywords_last,
|
|
1963
|
-
splats)
|
|
2491
|
+
splats, false)
|
|
1964
2492
|
end
|
|
1965
2493
|
end
|
|
1966
2494
|
|