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
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../type"
|
|
4
|
+
require_relative "../rbs_type_translator"
|
|
5
|
+
|
|
6
|
+
module Rigor
|
|
7
|
+
module Inference
|
|
8
|
+
module MethodDispatcher
|
|
9
|
+
module OverloadSelector
|
|
10
|
+
module_function
|
|
11
|
+
|
|
12
|
+
# Issue #2379 — every overload a supertype argument may select, when a nominal argument is wider than the
|
|
13
|
+
# parameter of the overload the call selected, `chosen`: `Integer#-` with a `Numeric` argument matched no
|
|
14
|
+
# overload and took the first, `(Integer) -> Integer`, although the runtime takes `(Float)`, `(Rational)` or
|
|
15
|
+
# `(Complex)` for those subclasses. Answers, in affinity order, every strictly typed overload whose every
|
|
16
|
+
# parameter takes its argument or is taken by a nominal one; nil when no argument is wider than `chosen`'s
|
|
17
|
+
# parameter or `chosen` is the only such overload.
|
|
18
|
+
# rubocop:disable-next Metrics/ParameterLists -- the selection inputs.
|
|
19
|
+
def contested_matches(method_definition, chosen, arg_types:, self_type:, instance_type:, type_vars:,
|
|
20
|
+
block_required:, environment:)
|
|
21
|
+
return nil unless arg_types.any?(Type::Nominal)
|
|
22
|
+
|
|
23
|
+
shared = { arg_types: arg_types, self_type: self_type, instance_type: instance_type,
|
|
24
|
+
type_vars: type_vars, block_required: block_required,
|
|
25
|
+
param_overrides: RbsExtended.param_type_override_map(method_definition, environment: environment),
|
|
26
|
+
environment: environment, keywords_last: false }
|
|
27
|
+
return nil unless wider_argument?(chosen, shared)
|
|
28
|
+
|
|
29
|
+
overloads = ReceiverAffinity.reorder(method_definition.method_types, self_type: self_type,
|
|
30
|
+
environment: environment)
|
|
31
|
+
matches = overloads.select do |method_type|
|
|
32
|
+
engages_block_shape?(method_type, block_required) &&
|
|
33
|
+
strictly_typed_params?(method_type, shared) && overlaps?(method_type, shared)
|
|
34
|
+
end
|
|
35
|
+
matches.empty? || (matches.size == 1 && matches.first.equal?(chosen)) ? nil : matches
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class << self
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
# Whether a nominal argument is strictly wider than the parameter `method_type` binds it to: the parameter
|
|
42
|
+
# does not take the whole argument, and the argument takes the parameter (`Numeric` against `(Integer)`).
|
|
43
|
+
def wider_argument?(method_type, shared)
|
|
44
|
+
fun = method_type.type
|
|
45
|
+
arg_types = shared[:arg_types]
|
|
46
|
+
return false unless fun.respond_to?(:required_positionals) && arity_compatible?(fun, arg_types.size)
|
|
47
|
+
|
|
48
|
+
params = positional_params_for(fun, arg_types.size)
|
|
49
|
+
index = -1
|
|
50
|
+
params.any? do |param|
|
|
51
|
+
arg = arg_types[index += 1]
|
|
52
|
+
next false unless arg.is_a?(Type::Nominal)
|
|
53
|
+
|
|
54
|
+
param_type = translate_param(param, shared)
|
|
55
|
+
!param_type.accepts(arg, mode: :gradual).yes? && arg.accepts(param_type, mode: :gradual).yes?
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# The strict pass's test with a nominal argument also counting where it takes the parameter: some value of
|
|
60
|
+
# a `Numeric` reaches `(Float)`.
|
|
61
|
+
def overlaps?(method_type, shared)
|
|
62
|
+
fun = method_type.type
|
|
63
|
+
ok = KeywordArguments.accepted_by?(fun, shared, true) { |*kv| accepts_param?(*kv, shared, true, true) }
|
|
64
|
+
return false unless ok
|
|
65
|
+
|
|
66
|
+
arg_types = shared[:arg_types]
|
|
67
|
+
return false unless arity_compatible?(fun, arg_types.size)
|
|
68
|
+
|
|
69
|
+
each_param_accepts?(positional_params_for(fun, arg_types.size), arg_types) do |param, arg|
|
|
70
|
+
accepts_param?(param, arg, shared, true) ||
|
|
71
|
+
(arg.is_a?(Type::Nominal) && arg.accepts(translate_param(param, shared), mode: :gradual).yes?)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# A parameter's type as the selector reads it: the `rigor:v1:param:` override, else the translated RBS type.
|
|
76
|
+
def translate_param(param, shared)
|
|
77
|
+
shared[:param_overrides][param.name] || RbsTypeTranslator.translate(
|
|
78
|
+
param.type,
|
|
79
|
+
self_type: shared[:self_type],
|
|
80
|
+
instance_type: shared[:instance_type],
|
|
81
|
+
type_vars: shared[:type_vars],
|
|
82
|
+
alias_expander: shared[:environment]&.rbs_loader
|
|
83
|
+
)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../type"
|
|
4
|
+
|
|
5
|
+
module Rigor
|
|
6
|
+
module Inference
|
|
7
|
+
module MethodDispatcher
|
|
8
|
+
# Issue #3242 — whether a receiver's `find` / `detect` / `find_index` / `count` / `all?` / `any?` / `none?`
|
|
9
|
+
# may be other than core's. {BlockFolding}'s constant-block folds for those methods read no receiver shape, so
|
|
10
|
+
# they answer core's semantics, and they are sound only on a carrier known to be a core collection or
|
|
11
|
+
# enumerable. Anything else may define the method itself: a project class (`class Bag; def find = (yield; 8);
|
|
12
|
+
# end`), a subclass of a core class (its own `Nominal`), a class object (`def self.find`), a `Data.define` or
|
|
13
|
+
# `Struct.new` class, or a scalar that has no such method at all.
|
|
14
|
+
module CoreEnumerableReceiver
|
|
15
|
+
module_function
|
|
16
|
+
|
|
17
|
+
# The nominal receivers whose methods above are core's: Array's, Hash's, Set's, Range's own, or
|
|
18
|
+
# Enumerable's. `RBS::Unnamed::ENVClass` is `ENV`, which extends Enumerable.
|
|
19
|
+
NOMINALS = Ractor.make_shareable(
|
|
20
|
+
Set[
|
|
21
|
+
"Array", "Hash", "Set", "Range", "Enumerable", "Enumerator", "Enumerator::Lazy",
|
|
22
|
+
"Enumerator::ArithmeticSequence", "Enumerator::Chain", "Enumerator::Product", "Dir",
|
|
23
|
+
"RBS::Unnamed::ENVClass"
|
|
24
|
+
].freeze
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
# False only for a carrier known to be a core collection or enumerable: a `Tuple` / `HashShape` literal
|
|
28
|
+
# shape, a `Constant` `Range` or `Set`, a `Nominal` in {NOMINALS}, `Top`, and a `Dynamic` / `Difference` /
|
|
29
|
+
# `Refined` / `Union` / `Intersection` over those. Every other receiver is true, so the folds decline.
|
|
30
|
+
def project_receiver?(receiver)
|
|
31
|
+
case receiver
|
|
32
|
+
when Type::Tuple, Type::HashShape, Type::Top then false
|
|
33
|
+
when Type::Constant then !(receiver.value.is_a?(Range) || receiver.value.is_a?(::Set))
|
|
34
|
+
when Type::Nominal then !NOMINALS.include?(receiver.class_name)
|
|
35
|
+
when Type::Difference, Type::Refined then project_receiver?(receiver.base)
|
|
36
|
+
when Type::Union, Type::Intersection then receiver.members.any? { |member| project_receiver?(member) }
|
|
37
|
+
when Type::Dynamic then project_receiver?(receiver.static_facet)
|
|
38
|
+
else true
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../reflection"
|
|
4
|
+
require_relative "../../type"
|
|
5
|
+
require_relative "argument_nodes"
|
|
6
|
+
require_relative "overload_selector"
|
|
7
|
+
require_relative "rbs_dispatch"
|
|
8
|
+
|
|
9
|
+
module Rigor
|
|
10
|
+
module Inference
|
|
11
|
+
module MethodDispatcher
|
|
12
|
+
# Issue #2884 — `X.new` through a `def self.new` that a project, gem or collection signature declares on `X` or
|
|
13
|
+
# on an ancestor of `X`. RBS models a constructor that answers another class this way (`email_address` declares
|
|
14
|
+
# `def self.new: (...) -> Address` on `EmailAddress`), and `MethodDispatcher#meta_new` answered `Nominal[X]` for
|
|
15
|
+
# every `Singleton[X].new` before any RBS tier ran. `meta_new` asks this module only after its curated lifts
|
|
16
|
+
# (`Array`, `Set`, `Hash`, `Struct`, `Class.new`, …) have declined, and keeps its `Nominal[X]` answer whenever
|
|
17
|
+
# this one is nil.
|
|
18
|
+
#
|
|
19
|
+
# The declaration counts only when it is an explicit `def self.new` (not the `new` RBS builds from `initialize`),
|
|
20
|
+
# every one of its definitions sits outside the `rbs` gem's core and stdlib trees
|
|
21
|
+
# ({Environment::RbsLoader#declared_outside_core_stdlib?}), and the class carrying it is not one core or stdlib
|
|
22
|
+
# declares. Core declares `Data.new: () -> bot` and `Ractor.new: … -> Ractor`, stdlib `Tempfile.new`; a project
|
|
23
|
+
# class inheriting one of those keeps the `Nominal` answer the curated tiers were written around.
|
|
24
|
+
#
|
|
25
|
+
# The declaration is found on `X`'s own RBS (which carries its RBS ancestors' singleton methods), or, for a
|
|
26
|
+
# Ruby-source class with no RBS of its own, on the first RBS-known class of its singleton
|
|
27
|
+
# {Scope::ResolutionChain}. A project entry ahead of it that defines `new` ends the walk with no answer: that
|
|
28
|
+
# method is the one Ruby runs. An inherited declaration that answers the instance
|
|
29
|
+
# ({Environment::RbsLoader#answers_instance_for_subclass?}) answers `Nominal[X]`; otherwise `instance`, and an
|
|
30
|
+
# overload returning the declaring class itself, read the receiver class, and `self` reads its class object.
|
|
31
|
+
#
|
|
32
|
+
# Nil (so `Nominal[X]`) when no overload takes the arguments (argument checking of `.new` is not this tier's
|
|
33
|
+
# job), and when the return translates to an untyped, `top` or `bot` answer, which would lose the precision
|
|
34
|
+
# `Nominal[X]` already has.
|
|
35
|
+
module DeclaredConstructor
|
|
36
|
+
# An entry of the singleton chain ahead of any declaring class defines `new` itself (in Ruby source, or as an
|
|
37
|
+
# `extend`ed module's RBS instance method), so that method is the one Ruby runs and the walk answers nothing.
|
|
38
|
+
OTHER_NEW = :other_new
|
|
39
|
+
private_constant :OTHER_NEW
|
|
40
|
+
|
|
41
|
+
module_function
|
|
42
|
+
|
|
43
|
+
def try_dispatch(class_name, context)
|
|
44
|
+
environment = context.environment
|
|
45
|
+
loader = environment&.rbs_loader
|
|
46
|
+
return nil if loader.nil? || !loader.respond_to?(:honoured_singleton_new?)
|
|
47
|
+
|
|
48
|
+
definition = declared_new(class_name, environment, loader, context.scope)
|
|
49
|
+
return nil if definition.nil? || !overload_matches?(definition, class_name, context)
|
|
50
|
+
|
|
51
|
+
owner = definition.defined_in.to_s.delete_prefix("::")
|
|
52
|
+
# The same test the walk's gate reads ({Environment::RbsLoader#singleton_new_departs_from_instance?}), so
|
|
53
|
+
# an inherited declaration that answers the instance answers `Nominal[receiver]` whether or not the gate let
|
|
54
|
+
# the walk run.
|
|
55
|
+
return nil if owner != class_name && loader.answers_instance_for_subclass?(definition)
|
|
56
|
+
|
|
57
|
+
answer = RbsDispatch.declared_singleton_return(context, definition, class_name)
|
|
58
|
+
answer = repoint_owner(answer, owner, class_name)
|
|
59
|
+
answer if usable?(answer)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
class << self
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def declared_new(class_name, environment, loader, scope)
|
|
66
|
+
owner = declaring_class(class_name, environment, loader, scope)
|
|
67
|
+
return nil if owner.nil?
|
|
68
|
+
|
|
69
|
+
definition = Rigor::Reflection.singleton_method_definition(owner, :new, environment: environment)
|
|
70
|
+
definition if definition && loader.honoured_singleton_new?(definition)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# `class_name` itself when RBS knows it; else the first RBS-known class on its singleton
|
|
74
|
+
# {Scope::ResolutionChain}, read in Ruby's order. Nil when a project entry ahead of it defines `new` (a
|
|
75
|
+
# `def self.new`, or an `extend`ed module's `def new`), when the chain reaches none, and when the chain does
|
|
76
|
+
# not stand (a fork or an unplaced mixin edge, where the tables cannot say which world ran).
|
|
77
|
+
#
|
|
78
|
+
# The walk runs only when some loaded declaration fails {Environment::RbsLoader#answers_instance_for_subclass?}
|
|
79
|
+
# ({Environment::RbsLoader#singleton_new_departs_from_instance?}); `try_dispatch` answers `Nominal[X]` for an
|
|
80
|
+
# inherited declaration that passes it, so without one the walk could change nothing. When it runs
|
|
81
|
+
# it files its reads whatever it answers: a later edit that puts such a declaration on the chain changes
|
|
82
|
+
# the answer, and the caller must be re-checked then.
|
|
83
|
+
def declaring_class(class_name, environment, loader, scope)
|
|
84
|
+
return class_name if Rigor::Reflection.rbs_class_known?(class_name, environment: environment)
|
|
85
|
+
return nil if scope.nil? || !loader.singleton_new_departs_from_instance?
|
|
86
|
+
|
|
87
|
+
chain = Scope::ResolutionChain.for(scope, class_name.to_s, :singleton, :methods)
|
|
88
|
+
found = first_new_owner(chain, scope, environment)
|
|
89
|
+
verdict = chain.settle(scope, found, owner: nil) { |retro| first_new_owner(retro, scope, environment) }
|
|
90
|
+
found unless found.nil? || found == OTHER_NEW || verdict != :chain
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# The first entry on `chain` that answers `new`: an RBS-known class's name, or {OTHER_NEW} for an entry that
|
|
94
|
+
# defines it otherwise. An `extend`ed module is not a class object `new` is declared on, but one whose
|
|
95
|
+
# instance side declares `new` (in Ruby source or in RBS) puts that method ahead of the superclass's, so it
|
|
96
|
+
# stops the walk.
|
|
97
|
+
def first_new_owner(chain, scope, environment)
|
|
98
|
+
chain.search(scope) do |entry|
|
|
99
|
+
if entry.external?
|
|
100
|
+
extended_rbs_new = !entry.superclass_edge && rbs_instance_new?(entry.candidates, environment)
|
|
101
|
+
next OTHER_NEW if extended_rbs_new
|
|
102
|
+
next unless entry.superclass_edge
|
|
103
|
+
|
|
104
|
+
entry.candidates.find { |name| Rigor::Reflection.rbs_class_known?(name, environment: environment) }
|
|
105
|
+
elsif entry.side == :singleton && Rigor::Reflection.rbs_class_known?(entry.name, environment: environment)
|
|
106
|
+
entry.name
|
|
107
|
+
elsif scope.discovered_method?(entry.name, :new, entry.side) ||
|
|
108
|
+
(entry.side == :instance && rbs_instance_new?([entry.name], environment))
|
|
109
|
+
OTHER_NEW
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def rbs_instance_new?(names, environment)
|
|
115
|
+
names.any? { |name| Rigor::Reflection.instance_method_definition(name, :new, environment: environment) }
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# A genuine match only: the selector's first-overload fallback would answer a declaration the call does
|
|
119
|
+
# not take.
|
|
120
|
+
def overload_matches?(definition, class_name, context)
|
|
121
|
+
args = context.args || []
|
|
122
|
+
!OverloadSelector.select_declared(
|
|
123
|
+
definition, args, Type::Combinator.singleton_of(class_name), Type::Combinator.nominal_of(class_name),
|
|
124
|
+
{}, !context.block_type.nil?, context.environment, true,
|
|
125
|
+
ArgumentNodes.keyword_hash_last?(context.call_node, args), nil, true
|
|
126
|
+
).empty?
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# A return naming the declaring class itself (`def self.new: [T] (T) -> Box[T]`) is the constructor's own
|
|
130
|
+
# class, so a subclass inheriting it answers the subclass, as `instance` does; generic arguments name the
|
|
131
|
+
# owner's parameters and are dropped with it. A foreign return (`EmailAddress` -> `Address`) stays.
|
|
132
|
+
def repoint_owner(answer, owner, class_name)
|
|
133
|
+
return answer if owner == class_name
|
|
134
|
+
|
|
135
|
+
case answer
|
|
136
|
+
when Type::Nominal
|
|
137
|
+
answer.class_name == owner ? Type::Combinator.nominal_of(class_name) : answer
|
|
138
|
+
when Type::Union
|
|
139
|
+
Type::Combinator.union(*answer.members.map { |member| repoint_owner(member, owner, class_name) })
|
|
140
|
+
else answer
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def usable?(answer)
|
|
145
|
+
!(answer.nil? || answer.is_a?(Type::Dynamic) || answer.is_a?(Type::Top) || answer.is_a?(Type::Bot))
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
@@ -119,7 +119,7 @@ module Rigor
|
|
|
119
119
|
# `Dynamic`, the untyped carrier, a facet that is only `nil`, one wider than {MEMBER_LIMIT}, or one with a
|
|
120
120
|
# member that is not sealed, an untyped member included.
|
|
121
121
|
def facet_members(arg)
|
|
122
|
-
return nil unless arg.is_a?(Type::Dynamic) && !
|
|
122
|
+
return nil unless arg.is_a?(Type::Dynamic) && !Type::Combinator.untyped?(arg)
|
|
123
123
|
|
|
124
124
|
facet = arg.static_facet
|
|
125
125
|
members = (facet.is_a?(Type::Union) ? facet.members : [facet]).reject { |member| nil_value?(member) }
|
|
@@ -20,12 +20,12 @@ module Rigor
|
|
|
20
20
|
module_function
|
|
21
21
|
|
|
22
22
|
# The literal `untyped` carrier, `Dynamic[top]`.
|
|
23
|
-
def untyped?(type) =
|
|
23
|
+
def untyped?(type) = Type::Combinator.untyped?(type)
|
|
24
24
|
|
|
25
25
|
# The untyped carrier itself, or a union or `Dynamic` holding it at any depth.
|
|
26
26
|
def imprecise?(type)
|
|
27
27
|
case type
|
|
28
|
-
when Type::Dynamic then
|
|
28
|
+
when Type::Dynamic then Type::Combinator.untyped?(type) || imprecise?(type.static_facet)
|
|
29
29
|
when Type::Union then type.members.any? { |member| imprecise?(member) }
|
|
30
30
|
else false
|
|
31
31
|
end
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "prism"
|
|
4
|
+
|
|
5
|
+
require_relative "../../type"
|
|
6
|
+
require_relative "../../typing_state"
|
|
7
|
+
require_relative "../../memo_audit"
|
|
8
|
+
require_relative "argument_nodes"
|
|
9
|
+
|
|
10
|
+
module Rigor
|
|
11
|
+
module Inference
|
|
12
|
+
module MethodDispatcher
|
|
13
|
+
# ADR-130 WD3 — the overload-shape rule for a call on a `Rigor::Plugin` interface value. The argument rules
|
|
14
|
+
# never check a call on a `Dynamic` receiver, so nothing else would notice a call no overload of the member
|
|
15
|
+
# takes; without this rule dispatch took whichever overload the selector's fallback picked, and that
|
|
16
|
+
# overload's return made the next call wrong (`xs.each.with_index` read the block overload's
|
|
17
|
+
# `Array[Integer]`). A call resolves only through the overloads that take its shape:
|
|
18
|
+
#
|
|
19
|
+
# - a block when the call passes one, and no required block when it does not;
|
|
20
|
+
# - the positional count, within required plus optional unless the overload has a rest parameter;
|
|
21
|
+
# - no required keyword the call omits.
|
|
22
|
+
#
|
|
23
|
+
# A shape the AST cannot state, a splat (`*xs`), a keyword splat (`**opts`) or a forwarded `...`, is taken
|
|
24
|
+
# by no overload, so the call reads `untyped` as it does on master.
|
|
25
|
+
module InterfaceShape
|
|
26
|
+
# ADR-130 WD5 — the dispatch-off flag {.without_dispatch} installs (`RbsDispatch.without_interface_dispatch`).
|
|
27
|
+
DISPATCH_OFF = TypingState.frame(
|
|
28
|
+
:interface_dispatch_off, reason: "a flag installed around a dispatch-off run (sig-gen, the parity harness)",
|
|
29
|
+
witness: "spec/fixtures/typing_state_witnesses/interface_erasure.rb"
|
|
30
|
+
)
|
|
31
|
+
private_constant :DISPATCH_OFF
|
|
32
|
+
|
|
33
|
+
module_function
|
|
34
|
+
|
|
35
|
+
# Runs the block with interface dispatch switched off.
|
|
36
|
+
def without_dispatch(&)
|
|
37
|
+
MemoAudit.check_run_level_install(:interface_dispatch_off)
|
|
38
|
+
TypingState.current.with(DISPATCH_OFF, true, &)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# False inside {.without_dispatch}.
|
|
42
|
+
def dispatch? = TypingState.current[DISPATCH_OFF] != true
|
|
43
|
+
|
|
44
|
+
# `definition` narrowed to the overloads that take the call's shape (the definition itself when every
|
|
45
|
+
# overload does), or nil when none does.
|
|
46
|
+
#
|
|
47
|
+
# Nil too when more than one overload takes the shape and one of them has a parameter that mentions `self`
|
|
48
|
+
# (`(self other)`, `(Array[self] others)`): which of them the call reaches depends on whether an argument is
|
|
49
|
+
# the receiver's kind, and the interface names no class acceptance could decide that against. Read as a
|
|
50
|
+
# precise overload, `x.same(1)` against `(self) -> Integer | (Integer) -> String` answered the first one's
|
|
51
|
+
# `Integer`; the call reads `untyped` instead.
|
|
52
|
+
def accepting(definition, args, block:, call_node:)
|
|
53
|
+
shape = call_shape(args, call_node)
|
|
54
|
+
return nil if shape.nil?
|
|
55
|
+
|
|
56
|
+
positional, keywords = shape
|
|
57
|
+
defs = definition.defs.select { |type_def| takes?(type_def.type, positional, keywords, block) }
|
|
58
|
+
return nil if defs.empty?
|
|
59
|
+
return nil if defs.size > 1 && defs.any? { |type_def| self_parameter?(type_def.type.type) }
|
|
60
|
+
|
|
61
|
+
defs.size == definition.defs.size ? definition : definition.update(defs: defs)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# True when a parameter type of `function` mentions RBS `self`, at any depth.
|
|
65
|
+
def self_parameter?(function)
|
|
66
|
+
return false unless function.respond_to?(:required_positionals)
|
|
67
|
+
|
|
68
|
+
params = function.required_positionals + function.optional_positionals + function.trailing_positionals +
|
|
69
|
+
function.required_keywords.values + function.optional_keywords.values
|
|
70
|
+
params << function.rest_positionals if function.rest_positionals
|
|
71
|
+
params << function.rest_keywords if function.rest_keywords
|
|
72
|
+
params.any? { |param| mentions_self?(param.type) }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def mentions_self?(rbs_type)
|
|
76
|
+
return true if rbs_type.is_a?(RBS::Types::Bases::Self)
|
|
77
|
+
|
|
78
|
+
rbs_type.each_type { |inner| return true if mentions_self?(inner) }
|
|
79
|
+
false
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# True when the call's block is a block-pass whose value may be nil at runtime, so the call may run
|
|
83
|
+
# block-less: `&blk` unless `blk` is provably not nil ({.never_nil?}), and an anonymous `&`, which forwards
|
|
84
|
+
# the enclosing method's own block. `&:sym` is never nil.
|
|
85
|
+
def nilable_block_pass?(call_node, scope)
|
|
86
|
+
block = call_node.respond_to?(:block) ? call_node.block : nil
|
|
87
|
+
return false unless block.is_a?(Prism::BlockArgumentNode)
|
|
88
|
+
|
|
89
|
+
expression = block.expression
|
|
90
|
+
return true if expression.nil?
|
|
91
|
+
return false if expression.is_a?(Prism::SymbolNode)
|
|
92
|
+
return true if scope.nil?
|
|
93
|
+
|
|
94
|
+
!never_nil?(scope.type_of(expression))
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# The classes a value of which may be `nil`: `NilClass` and its ancestors.
|
|
98
|
+
NIL_ANCESTORS = %w[NilClass Object BasicObject Kernel].freeze
|
|
99
|
+
private_constant :NIL_ANCESTORS
|
|
100
|
+
|
|
101
|
+
# True only for a type that provably excludes `nil`: a non-nil literal, an instance of a class `nil` is not
|
|
102
|
+
# (a `Proc`, a `Method`), a class object, or a shape. `top`, `Dynamic`, `Object` and anything this does not
|
|
103
|
+
# list may be `nil` (ADR-130 WD3, as amended).
|
|
104
|
+
def never_nil?(type)
|
|
105
|
+
case type
|
|
106
|
+
when Type::Union then type.members.all? { |member| never_nil?(member) }
|
|
107
|
+
when Type::Constant then !type.value.nil?
|
|
108
|
+
when Type::Nominal then !NIL_ANCESTORS.include?(type.class_name)
|
|
109
|
+
when Type::Refined, Type::Difference then never_nil?(type.base)
|
|
110
|
+
when Type::Singleton, Type::Tuple, Type::HashShape, Type::BoundMethod, Type::IntegerRange, Type::FloatRange,
|
|
111
|
+
Type::DataInstance, Type::StructInstance, Type::DataClass, Type::StructClass then true
|
|
112
|
+
else false
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# `[positional count, keyword names or nil]`, or nil for a shape the AST cannot state, which includes a keyword
|
|
117
|
+
# hash whose keys are not all literal symbols. The keyword names are nil when the call passes no keyword hash.
|
|
118
|
+
def call_shape(args, call_node)
|
|
119
|
+
arguments = call_node.respond_to?(:arguments) ? call_node.arguments&.arguments : nil
|
|
120
|
+
return [args.size, nil] if arguments.nil? || arguments.size != args.size
|
|
121
|
+
return nil if arguments.any? { |node| opaque_argument?(node) }
|
|
122
|
+
return [args.size, nil] unless ArgumentNodes.keyword_hash_last?(call_node, args)
|
|
123
|
+
|
|
124
|
+
names = keyword_names(arguments.last)
|
|
125
|
+
names && [args.size - 1, names]
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def opaque_argument?(node)
|
|
129
|
+
case node
|
|
130
|
+
when Prism::SplatNode, Prism::ForwardingArgumentsNode then true
|
|
131
|
+
when Prism::KeywordHashNode then node.elements.any?(Prism::AssocSplatNode)
|
|
132
|
+
else false
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# The literal keyword names of a keyword hash, or nil when one is not a literal symbol.
|
|
137
|
+
def keyword_names(hash_node)
|
|
138
|
+
hash_node.elements.map do |element|
|
|
139
|
+
key = element.key
|
|
140
|
+
return nil unless key.is_a?(Prism::SymbolNode)
|
|
141
|
+
|
|
142
|
+
key.unescaped.to_sym
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def takes?(method_type, positional, keywords, block)
|
|
147
|
+
function = method_type.type
|
|
148
|
+
# `(...) -> T`: an untyped parameter list takes every call.
|
|
149
|
+
return block_taken?(method_type.block, block) unless function.respond_to?(:required_positionals)
|
|
150
|
+
|
|
151
|
+
block_taken?(method_type.block, block) && arguments_taken?(function, positional, keywords)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def block_taken?(declared, block)
|
|
155
|
+
block ? !declared.nil? : (declared.nil? || !declared.required)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def arguments_taken?(function, positional, keywords)
|
|
159
|
+
return positional_taken?(function, positional) && function.required_keywords.empty? if keywords.nil?
|
|
160
|
+
# A keyword hash an overload declares no keywords for is its last positional argument.
|
|
161
|
+
return positional_taken?(function, positional + 1) unless keywords?(function)
|
|
162
|
+
|
|
163
|
+
positional_taken?(function, positional) && function.required_keywords.keys.all? do |key|
|
|
164
|
+
keywords.include?(key)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def positional_taken?(function, count)
|
|
169
|
+
required = function.required_positionals.size + function.trailing_positionals.size
|
|
170
|
+
return false if count < required
|
|
171
|
+
|
|
172
|
+
!function.rest_positionals.nil? || count <= required + function.optional_positionals.size
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def keywords?(function)
|
|
176
|
+
!function.required_keywords.empty? || !function.optional_keywords.empty? || !function.rest_keywords.nil?
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
@@ -105,7 +105,7 @@ module Rigor
|
|
|
105
105
|
# throws)
|
|
106
106
|
# - `Hash[K, V]` / `HashShape{...}` → Tuple[K, V]
|
|
107
107
|
# (Ruby yields `[key, value]` pairs as the element)
|
|
108
|
-
# - `Constant<Array>` / `Constant<Range>`
|
|
108
|
+
# - `Constant<Array>` / `Constant<Range>`
|
|
109
109
|
# → corresponding Constant element
|
|
110
110
|
#
|
|
111
111
|
# Receivers we cannot project (Top, Dynamic, unknown nominals, IO, …) decline so the RBS tier still
|
|
@@ -11,7 +11,7 @@ module Rigor
|
|
|
11
11
|
# Kernel intrinsic shape-folding — precision tier for the `Kernel` module-functions whose return type
|
|
12
12
|
# is a function of the argument's *shape*, not just its class.
|
|
13
13
|
#
|
|
14
|
-
#
|
|
14
|
+
# `Kernel#Array` is the shape-sensitive fold. Its default RBS sig is
|
|
15
15
|
# `Array(untyped) -> Array[untyped]`, which collapses to `Array[Dynamic[top]]` for every caller. This
|
|
16
16
|
# tier short-circuits with a precise answer when the argument's type lattice tells us what the result
|
|
17
17
|
# element type MUST be:
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rigor
|
|
4
|
+
module Inference
|
|
5
|
+
module MethodDispatcher
|
|
6
|
+
module OverloadSelector
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
# The overload a call answers when no selection pass matched it. A call with a block, on a method that
|
|
10
|
+
# declares a block overload, reads the block overloads (`block_call_fallback`, #2429) unless `block_fallback`
|
|
11
|
+
# is false. Otherwise prefer an overload that does not REQUIRE a block over `overloads.first`: methods like
|
|
12
|
+
# `Array#filter` / `Enumerable#map` declare the block-bearing overload first (`() { ... } -> Array[Elem]`)
|
|
13
|
+
# and the bare-call overload second (`() -> Enumerator[...]`), and a no-block `[1, 2].filter` yields an
|
|
14
|
+
# `Enumerator`.
|
|
15
|
+
def no_match_fallback(overloads, shared, block_fallback)
|
|
16
|
+
if block_fallback && shared[:block_required] && overloads.any? { |mt| overload_has_block?(mt) }
|
|
17
|
+
return block_call_fallback(overloads, shared)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
[overloads.find { |mt| !overload_requires_block?(mt) } || overloads.first]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class << self
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
# Issue #2429 — the block-less arm's return is what the method answers without a block, so
|
|
27
|
+
# `URI.open(url, read_timeout: 3) { |io| }` read `StringIO | Tempfile` instead of the block's value. The
|
|
28
|
+
# answer is the block overload whose positional arity admits the call when only one does, or the one every
|
|
29
|
+
# such overload agrees on; otherwise none (`[]`, which the return path reads as `Dynamic[top]`).
|
|
30
|
+
# `[3, 1, 2].max_by(2.0) { }` takes `(Integer n) { } -> Array[Elem]`, not `() { } -> Elem?`, and overloads of
|
|
31
|
+
# differing returns that all admit the call are not decided by position.
|
|
32
|
+
def block_call_fallback(overloads, shared)
|
|
33
|
+
admitted = overloads.select { |mt| overload_has_block?(mt) && arity_admits?(mt, shared) }
|
|
34
|
+
return [] if admitted.empty?
|
|
35
|
+
|
|
36
|
+
admitted.map { |mt| mt.type.return_type }.uniq.size == 1 ? [admitted.first] : []
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# The call's positional count (less a keyword hash the overload takes as keywords) fits `method_type`'s
|
|
40
|
+
# arity; a splat fits when some count of its elements does (#1801).
|
|
41
|
+
def arity_admits?(method_type, shared)
|
|
42
|
+
fun = method_type.type
|
|
43
|
+
arg_types = KeywordArguments.selection_positional(method_type, shared)
|
|
44
|
+
splats = shared[:splats]
|
|
45
|
+
return arity_compatible?(fun, arg_types.size) unless splats
|
|
46
|
+
|
|
47
|
+
lists = SplatArity.expansions(fun, arg_types, splats)
|
|
48
|
+
lists.nil? || lists.any? { |list| arity_compatible?(fun, list.size) }
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|