rigortype 0.3.8 → 0.3.9
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 +1 -1
- data/data/capability_roles/capability_roles.rbs +36 -0
- data/data/gem_overlay/activesupport/core_ext.rbs +109 -13
- data/docs/handbook/02-everyday-types.md +17 -14
- data/docs/handbook/03-narrowing.md +37 -4
- data/docs/handbook/05-methods-and-blocks.md +1 -1
- data/docs/handbook/06-classes.md +2 -2
- data/docs/handbook/07-rbs-and-extended.md +14 -1
- data/docs/handbook/11-sig-gen.md +72 -1
- data/docs/handbook/README.md +1 -1
- data/docs/handbook/appendix-elixir.md +2 -2
- data/docs/handbook/appendix-go.md +2 -2
- data/docs/handbook/appendix-java-csharp.md +2 -2
- data/docs/handbook/appendix-mypy.md +2 -2
- data/docs/handbook/appendix-phpstan.md +1 -1
- data/docs/handbook/appendix-rust.md +1 -1
- data/docs/handbook/appendix-type-theory.md +4 -4
- data/docs/handbook/appendix-typescript.md +1 -1
- data/docs/llms.txt +2 -0
- data/docs/manual/02-cli-reference.md +32 -4
- data/docs/manual/03-configuration.md +11 -0
- data/docs/manual/04-diagnostics.md +11 -0
- data/docs/manual/07-plugins.md +15 -5
- data/docs/manual/08-skills.md +23 -2
- data/docs/manual/11-ci.md +9 -0
- data/docs/manual/15-type-protection-coverage.md +8 -0
- data/docs/manual/16-rbs-extended-annotations.md +6 -1
- data/docs/manual/18-removing-dead-code.md +10 -8
- data/docs/manual/plugins/rigor-actionpack.md +18 -0
- data/docs/manual/plugins/rigor-activerecord.md +28 -0
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +32 -2
- data/docs/manual/plugins/rigor-rbs-inline.md +42 -1
- data/lib/rigor/analysis/baseline.rb +2 -2
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/dead_version_guard_arms.rb +1 -5
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +10 -2
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +15 -6
- data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -1
- data/lib/rigor/analysis/check_rules/rule_walk.rb +39 -4
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +1 -1
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +0 -1
- data/lib/rigor/analysis/check_rules.rb +200 -32
- data/lib/rigor/analysis/crash_signature.rb +8 -24
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/builder.rb +0 -2
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +0 -2
- data/lib/rigor/analysis/dependency_source_inference/index.rb +5 -5
- data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +1 -2
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
- data/lib/rigor/analysis/effects_cache_probe.rb +3 -4
- data/lib/rigor/analysis/erb_template_detector.rb +1 -2
- data/lib/rigor/analysis/fact_store.rb +6 -1
- data/lib/rigor/analysis/incremental.rb +16 -0
- data/lib/rigor/analysis/incremental_session.rb +81 -22
- data/lib/rigor/analysis/plugin_fact_fingerprint.rb +0 -1
- data/lib/rigor/analysis/reachability/graph.rb +3 -5
- data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -5
- data/lib/rigor/analysis/reachability/project_files.rb +2 -2
- data/lib/rigor/analysis/reachability/scan.rb +5 -5
- data/lib/rigor/analysis/reachability/scan_cache.rb +2 -2
- data/lib/rigor/analysis/reachability/signature_scan.rb +2 -2
- data/lib/rigor/analysis/result.rb +1 -3
- data/lib/rigor/analysis/rule_catalog.rb +4 -1
- data/lib/rigor/analysis/run_cache_key.rb +1 -1
- data/lib/rigor/analysis/run_cache_probe.rb +3 -5
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -8
- data/lib/rigor/analysis/runner/declaration_position.rb +1 -2
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +177 -26
- data/lib/rigor/analysis/runner/effect_annotation_residual_pass.rb +2 -3
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +9 -10
- data/lib/rigor/analysis/runner/pool_coordinator.rb +256 -88
- data/lib/rigor/analysis/runner/project_pre_passes.rb +12 -6
- data/lib/rigor/analysis/runner/run_snapshots.rb +6 -1
- data/lib/rigor/analysis/runner.rb +133 -35
- data/lib/rigor/analysis/worker_session.rb +9 -10
- data/lib/rigor/bleeding_edge.rb +12 -24
- data/lib/rigor/builtins/hkt_builtins.rb +2 -2
- data/lib/rigor/builtins/imported_refinements.rb +155 -21
- data/lib/rigor/builtins/predefined_constant_refinements.rb +5 -7
- data/lib/rigor/builtins/regex_refinement.rb +4 -4
- data/lib/rigor/builtins/static_return_refinements.rb +5 -6
- data/lib/rigor/cache/annotation_location.rb +2 -4
- data/lib/rigor/cache/descriptor.rb +58 -20
- data/lib/rigor/cache/engine_source.rb +4 -4
- data/lib/rigor/cache/file_digest.rb +9 -1
- data/lib/rigor/cache/incremental_snapshot.rb +40 -6
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +0 -3
- data/lib/rigor/cache/rbs_class_type_param_names.rb +0 -3
- data/lib/rigor/cache/rbs_constant_table.rb +0 -3
- data/lib/rigor/cache/rbs_descriptor.rb +79 -11
- data/lib/rigor/cache/rbs_environment.rb +8 -4
- data/lib/rigor/cache/rbs_known_class_names.rb +0 -3
- data/lib/rigor/cache/store.rb +77 -32
- data/lib/rigor/cli/annotate_command.rb +5 -6
- data/lib/rigor/cli/check_command.rb +57 -10
- data/lib/rigor/cli/check_invocation.rb +6 -11
- data/lib/rigor/cli/check_runner_factory.rb +1 -5
- data/lib/rigor/cli/coverage_command.rb +1 -1
- data/lib/rigor/cli/coverage_mutation.rb +12 -3
- data/lib/rigor/cli/coverage_scan.rb +1 -5
- data/lib/rigor/cli/diff_command.rb +1 -1
- data/lib/rigor/cli/doc_links.rb +3 -3
- data/lib/rigor/cli/docs_command.rb +2 -2
- data/lib/rigor/cli/doctor_command.rb +85 -38
- data/lib/rigor/cli/effects_command.rb +2 -2
- data/lib/rigor/cli/effects_diff_renderer.rb +3 -3
- data/lib/rigor/cli/effects_snapshot_command.rb +1 -1
- data/lib/rigor/cli/explain_command.rb +34 -1
- data/lib/rigor/cli/fused_protection_report.rb +7 -1
- data/lib/rigor/cli/lsp_command.rb +1 -1
- data/lib/rigor/cli/mcp_command.rb +1 -1
- data/lib/rigor/cli/measurement_integrity_warning.rb +4 -5
- data/lib/rigor/cli/mutation_fork_scan.rb +6 -6
- data/lib/rigor/cli/mutation_protection_report.rb +7 -1
- data/lib/rigor/cli/plugin_command.rb +1 -1
- data/lib/rigor/cli/plugins_command.rb +2 -1
- data/lib/rigor/cli/prism_colorizer.rb +2 -2
- data/lib/rigor/cli/protection_fork_scan.rb +6 -6
- data/lib/rigor/cli/show_bleedingedge_command.rb +1 -1
- data/lib/rigor/cli/sig_gen_command.rb +2 -2
- data/lib/rigor/cli/skill_command.rb +1 -1
- data/lib/rigor/cli/skill_deep_probe.rb +4 -4
- data/lib/rigor/cli/skill_describe.rb +36 -35
- data/lib/rigor/cli/trace_command.rb +4 -4
- data/lib/rigor/cli/trace_renderer.rb +5 -6
- data/lib/rigor/cli/triage_command.rb +1 -1
- data/lib/rigor/cli/type_of_command.rb +10 -6
- data/lib/rigor/cli/type_scan_command.rb +4 -4
- data/lib/rigor/cli/unused_command.rb +11 -3
- data/lib/rigor/cli/upgrade_command.rb +1 -1
- data/lib/rigor/cli.rb +68 -6
- data/lib/rigor/config_audit.rb +30 -5
- data/lib/rigor/configuration/severity_profile.rb +6 -6
- data/lib/rigor/configuration.rb +43 -4
- data/lib/rigor/effects/attribution.rb +1 -3
- data/lib/rigor/effects/config_envelopes.rb +6 -8
- data/lib/rigor/effects/definition_lines.rb +25 -6
- data/lib/rigor/effects/effect_table.rb +0 -1
- data/lib/rigor/effects/entry_points.rb +0 -2
- data/lib/rigor/effects/envelope_check.rb +8 -8
- data/lib/rigor/effects/envelope_index.rb +5 -8
- data/lib/rigor/effects/framework_units.rb +4 -6
- data/lib/rigor/effects/identity.rb +4 -6
- data/lib/rigor/effects/inline_anchor.rb +7 -7
- data/lib/rigor/effects/label_intent.rb +3 -4
- data/lib/rigor/effects/liskov_check.rb +8 -8
- data/lib/rigor/effects/method_key.rb +1 -1
- data/lib/rigor/effects/plugin_facts.rb +4 -6
- data/lib/rigor/effects/propagator.rb +2 -3
- data/lib/rigor/effects/scanner.rb +1 -1
- data/lib/rigor/effects/signature_sources.rb +3 -5
- data/lib/rigor/effects/snapshot.rb +10 -10
- data/lib/rigor/effects/snapshot_diff.rb +1 -1
- data/lib/rigor/effects/unit_scan.rb +10 -10
- data/lib/rigor/effects/unknown_label_check.rb +3 -8
- data/lib/rigor/effects/unknown_label_report.rb +3 -4
- data/lib/rigor/environment/bundle_sig_discovery.rb +6 -6
- data/lib/rigor/environment/class_registry.rb +3 -1
- data/lib/rigor/environment/failure_slot.rb +2 -2
- data/lib/rigor/environment/installed_gem_set.rb +85 -0
- data/lib/rigor/environment/lockfile_resolver.rb +51 -4
- data/lib/rigor/environment/missing_gem_constant_index.rb +4 -4
- data/lib/rigor/environment/rbs_collection_discovery.rb +5 -5
- data/lib/rigor/environment/rbs_coverage_report.rb +5 -5
- data/lib/rigor/environment/rbs_hierarchy.rb +3 -1
- data/lib/rigor/environment/rbs_loader.rb +388 -59
- data/lib/rigor/environment.rb +105 -20
- data/lib/rigor/flow_contribution/merger.rb +0 -2
- data/lib/rigor/flow_contribution.rb +11 -13
- data/lib/rigor/inference/acceptance.rb +153 -8
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/body_fixpoint.rb +5 -5
- data/lib/rigor/inference/budget_trace.rb +1 -5
- data/lib/rigor/inference/captured_locals.rb +2 -3
- data/lib/rigor/inference/closure_escape_analyzer.rb +2 -4
- data/lib/rigor/inference/coverage_scanner.rb +4 -6
- data/lib/rigor/inference/dynamic_origin.rb +1 -1
- data/lib/rigor/inference/element_read_widening.rb +168 -0
- data/lib/rigor/inference/expression_typer.rb +763 -143
- data/lib/rigor/inference/fork_map.rb +5 -7
- data/lib/rigor/inference/hkt_reducer.rb +2 -3
- data/lib/rigor/inference/hkt_registry.rb +4 -7
- data/lib/rigor/inference/index_write_widening.rb +1 -5
- data/lib/rigor/inference/macro_block_self_type.rb +1 -4
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +2 -9
- data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +305 -19
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/json_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -1
- data/lib/rigor/inference/method_dispatcher/math_folding.rb +41 -3
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +2 -5
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +61 -47
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +82 -0
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +165 -31
- data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +25 -4
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +2 -8
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +4 -5
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +85 -4
- data/lib/rigor/inference/method_dispatcher/time_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +12 -13
- data/lib/rigor/inference/method_parameter_binder.rb +4 -6
- data/lib/rigor/inference/multi_target_binder.rb +1 -3
- data/lib/rigor/inference/mutation_rejoin.rb +168 -0
- data/lib/rigor/inference/mutation_widening.rb +100 -51
- data/lib/rigor/inference/narrowing.rb +299 -46
- data/lib/rigor/inference/optimistic_origin.rb +1 -7
- data/lib/rigor/inference/origin_lookup.rb +2 -3
- data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
- data/lib/rigor/inference/pre_eval_constants.rb +6 -5
- data/lib/rigor/inference/precision_scanner.rb +3 -4
- data/lib/rigor/inference/project_patched_methods.rb +2 -2
- data/lib/rigor/inference/project_patched_scanner.rb +3 -3
- data/lib/rigor/inference/protection_scanner.rb +1 -2
- data/lib/rigor/inference/range_constant.rb +57 -0
- data/lib/rigor/inference/rbs_type_translator.rb +49 -11
- data/lib/rigor/inference/receiver_alias.rb +3 -3
- data/lib/rigor/inference/refinement_mutation.rb +72 -0
- data/lib/rigor/inference/scope_indexer.rb +688 -151
- data/lib/rigor/inference/statement_evaluator.rb +221 -50
- data/lib/rigor/inference/string_mutation.rb +60 -0
- data/lib/rigor/inference/struct_fold_safety.rb +6 -7
- data/lib/rigor/inference/synthetic_method_index.rb +1 -2
- data/lib/rigor/inference/synthetic_method_scanner.rb +4 -6
- data/lib/rigor/inference/version_guard.rb +28 -21
- data/lib/rigor/inference/void_origin.rb +3 -3
- data/lib/rigor/inference/void_tail_summary.rb +2 -4
- data/lib/rigor/language_server/buffer_table.rb +4 -4
- data/lib/rigor/language_server/completion_provider.rb +1 -1
- data/lib/rigor/language_server/debouncer.rb +1 -1
- data/lib/rigor/language_server/diagnostic_publisher.rb +4 -4
- data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
- data/lib/rigor/language_server/folding_range_provider.rb +1 -1
- data/lib/rigor/language_server/hover_provider.rb +1 -1
- data/lib/rigor/language_server/hover_renderer.rb +2 -2
- data/lib/rigor/language_server/incremental_sync.rb +6 -6
- data/lib/rigor/language_server/project_context.rb +2 -5
- data/lib/rigor/language_server/publish_batcher.rb +2 -2
- data/lib/rigor/language_server/selection_range_provider.rb +2 -2
- data/lib/rigor/language_server/server.rb +9 -9
- data/lib/rigor/language_server/signature_help_provider.rb +1 -1
- data/lib/rigor/language_server/uri.rb +1 -1
- data/lib/rigor/plugin/base.rb +90 -21
- data/lib/rigor/plugin/bundled_catalog.rb +167 -0
- data/lib/rigor/plugin/effect_attribution.rb +7 -7
- data/lib/rigor/plugin/effect_entry_points.rb +3 -3
- data/lib/rigor/plugin/fact_store.rb +6 -6
- data/lib/rigor/plugin/io_boundary.rb +93 -8
- data/lib/rigor/plugin/isolation.rb +29 -6
- data/lib/rigor/plugin/loader.rb +31 -12
- data/lib/rigor/plugin/macro/heredoc_template.rb +1 -1
- data/lib/rigor/plugin/macro/trait_registry.rb +1 -1
- data/lib/rigor/plugin/manifest.rb +23 -1
- data/lib/rigor/plugin/registry.rb +42 -12
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +12 -1
- data/lib/rigor/plugin/trust_policy.rb +2 -4
- data/lib/rigor/plugin/type_node_resolver.rb +3 -3
- data/lib/rigor/plugin_gap_advisory.rb +96 -0
- data/lib/rigor/project_environment.rb +138 -0
- data/lib/rigor/protection/analysis_guard.rb +93 -14
- data/lib/rigor/protection/closure_kill_oracle.rb +67 -23
- data/lib/rigor/protection/dependency_closure.rb +5 -8
- data/lib/rigor/protection/diagnostic_oracle.rb +2 -2
- data/lib/rigor/protection/discovery_seed.rb +11 -11
- data/lib/rigor/protection/kill_signature.rb +2 -4
- data/lib/rigor/protection/measurement_integrity.rb +1 -3
- data/lib/rigor/protection/mutation_cache.rb +6 -9
- data/lib/rigor/protection/mutation_scanner.rb +12 -15
- data/lib/rigor/protection/mutator.rb +2 -1
- data/lib/rigor/protection/test_suite_oracle.rb +5 -5
- data/lib/rigor/rbs_extended/conformance_checker.rb +4 -3
- data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
- data/lib/rigor/rbs_extended/reporter.rb +36 -10
- data/lib/rigor/rbs_extended.rb +61 -21
- data/lib/rigor/reflection/constant_path.rb +126 -0
- data/lib/rigor/reflection.rb +26 -10
- data/lib/rigor/runtime/jit.rb +6 -8
- data/lib/rigor/scope/discovery_index.rb +30 -0
- data/lib/rigor/scope.rb +203 -4
- data/lib/rigor/sig_gen/generator.rb +115 -22
- data/lib/rigor/sig_gen/layout_index.rb +3 -4
- data/lib/rigor/sig_gen/meta_class_shape.rb +3 -5
- data/lib/rigor/sig_gen/observation_collector.rb +17 -15
- data/lib/rigor/sig_gen/path_mapper.rb +4 -6
- data/lib/rigor/sig_gen/rbs_validity.rb +4 -4
- data/lib/rigor/sig_gen/renderer.rb +4 -5
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +111 -0
- data/lib/rigor/sig_gen/superclass_spelling.rb +27 -0
- data/lib/rigor/sig_gen/type_elaborator.rb +1 -3
- data/lib/rigor/sig_gen/writer.rb +23 -6
- data/lib/rigor/signature_path_audit.rb +153 -6
- data/lib/rigor/source/literals.rb +0 -23
- data/lib/rigor/source/node_children.rb +0 -2
- data/lib/rigor/source/node_locator.rb +5 -11
- data/lib/rigor/source/node_walker.rb +2 -5
- data/lib/rigor/triage/catalogue.rb +1 -3
- data/lib/rigor/triage.rb +3 -5
- data/lib/rigor/type/accepts_result.rb +24 -6
- data/lib/rigor/type/combinator.rb +68 -5
- data/lib/rigor/type/data_class.rb +2 -2
- data/lib/rigor/type/data_instance.rb +4 -4
- data/lib/rigor/type/float_range.rb +128 -0
- data/lib/rigor/type/hash_shape.rb +5 -5
- data/lib/rigor/type/integer_range.rb +13 -8
- data/lib/rigor/type/nominal.rb +8 -2
- data/lib/rigor/type/refined.rb +3 -3
- data/lib/rigor/type/struct_class.rb +3 -3
- data/lib/rigor/type/struct_instance.rb +4 -4
- data/lib/rigor/type.rb +1 -0
- data/lib/rigor/type_node/generic.rb +1 -1
- data/lib/rigor/type_node/range_literal.rb +25 -0
- data/lib/rigor/type_node/resolver_chain.rb +1 -1
- data/lib/rigor/type_node.rb +1 -0
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +0 -4
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +0 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +0 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -0
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +0 -4
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +3 -5
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +1 -4
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +1 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +33 -30
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -3
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +28 -2
- data/plugins/rigor-actionpack/sig/action_controller.rbs +71 -0
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +0 -4
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +0 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +0 -2
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +1 -0
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +3 -4
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +1 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +1 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +42 -7
- data/plugins/rigor-activerecord/sig/active_record/framework.rbs +123 -0
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +1 -0
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +9 -2
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +119 -14
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +1 -0
- data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +1 -0
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +9 -7
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +7 -3
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +1 -0
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +8 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +11 -7
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -11
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +9 -4
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +1 -0
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +0 -5
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +8 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +36 -4
- data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +1 -0
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +8 -6
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +6 -3
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +1 -0
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -1
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +0 -3
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +1 -0
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +0 -5
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +2 -2
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +0 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +0 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +1 -0
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +0 -12
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +1 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +0 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +1 -0
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/acronyms.rb +5 -5
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +1 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +3 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/grape_api_discoverer.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +9 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +2 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +1 -0
- data/plugins/rigor-railties/lib/rigor/plugin/railties.rb +1 -0
- data/plugins/rigor-rbnacl/lib/rigor/plugin/rbnacl.rb +13 -1
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +128 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +0 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +0 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +1 -5
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +2 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +1 -0
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +0 -3
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +1 -0
- data/plugins/rigor-sassc/lib/rigor/plugin/sassc.rb +1 -0
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +1 -9
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +1 -0
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +0 -4
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/effects.rb +1 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +1 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +0 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +0 -2
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +1 -0
- data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +1 -0
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +2 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +1 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +3 -9
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +4 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +1 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +4 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +7 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +5 -5
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +1 -0
- data/sig/rigor/analysis/reachability/scan_cache.rbs +12 -0
- data/sig/rigor/cache.rbs +2 -3
- data/sig/rigor/environment.rbs +5 -0
- data/sig/rigor/inference.rbs +3 -10
- data/sig/rigor/plugin/io_boundary.rbs +4 -0
- data/sig/rigor/plugin/manifest.rbs +2 -1
- data/sig/rigor/reflection.rbs +3 -1
- data/sig/rigor/scope.rbs +24 -3
- data/sig/rigor/sig_gen/skip_reason_catalog.rbs +14 -0
- data/sig/rigor/type.rbs +27 -1
- data/sig/rigor.rbs +1 -0
- data/skills/rigor-ask/SKILL.md +8 -6
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +13 -2
- data/skills/rigor-project-init/SKILL.md +6 -3
- data/skills/rigor-project-init/references/02-configure.md +10 -6
- data/skills/rigor-project-init/references/06-agent-contract.md +67 -0
- data/skills/rigor-type-oracle/SKILL.md +228 -0
- data/skills/rigor-type-oracle/references/01-oracle-commands.md +261 -0
- data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +52 -0
- data/skills/rigor-type-oracle/references/03-gap-protocol.md +128 -0
- data/skills/rigor-unused-adjudicate/SKILL.md +8 -5
- metadata +26 -3
- data/lib/rigor/cli/probe_environment.rb +0 -85
- data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
|
@@ -44,11 +44,23 @@ module Rigor
|
|
|
44
44
|
MATH_BINARY = Set[:atan2, :hypot, :ldexp].freeze
|
|
45
45
|
MATH_TUPLE_UNARY = Set[:frexp, :lgamma].freeze
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
# ADR-109 — the monotone non-decreasing functions, each with the inclusive lower edge of its
|
|
48
|
+
# domain (`nil` for every real). Over a bounded argument the image is the closed range between the
|
|
49
|
+
# images of the bounds: `Math.sqrt(Integer[0..])` is `Float[0.0..]`, `Math.exp(Float[..0.0])` is
|
|
50
|
+
# `Float[0.0..1.0]`. A bound below the domain edge raises at run time for the value that reaches
|
|
51
|
+
# it, so the fold declines. Not listed: the non-monotone functions (`sin`, `cos`, `tan`, `gamma`,
|
|
52
|
+
# `erfc` decreases, `acos` decreases) and the ones whose image needs a sign split.
|
|
53
|
+
MATH_MONOTONE_DOMAIN = {
|
|
54
|
+
sqrt: 0.0, cbrt: nil, exp: nil, expm1: nil,
|
|
55
|
+
log: 0.0, log2: 0.0, log10: 0.0, log1p: -1.0,
|
|
56
|
+
atan: nil, sinh: nil, tanh: nil, asinh: nil, erf: nil
|
|
57
|
+
}.freeze
|
|
58
|
+
|
|
59
|
+
private_constant :MATH_UNARY, :MATH_BINARY, :MATH_TUPLE_UNARY, :MATH_MONOTONE_DOMAIN
|
|
48
60
|
|
|
49
61
|
module_function
|
|
50
62
|
|
|
51
|
-
# @return
|
|
63
|
+
# @return folded result, or nil to defer.
|
|
52
64
|
def try_dispatch(context)
|
|
53
65
|
receiver = context.receiver
|
|
54
66
|
method_name = context.method_name
|
|
@@ -77,13 +89,38 @@ module Rigor
|
|
|
77
89
|
return nil unless args.size == 1
|
|
78
90
|
|
|
79
91
|
x = numeric_constant(args.first)
|
|
80
|
-
return
|
|
92
|
+
return fold_monotone_range(method_name, args.first) if x.nil?
|
|
81
93
|
|
|
82
94
|
fold_float_result(Math.public_send(method_name, x))
|
|
83
95
|
rescue Math::DomainError, RangeError
|
|
84
96
|
nil
|
|
85
97
|
end
|
|
86
98
|
|
|
99
|
+
# The bounded-argument arm of {fold_unary} / {fold_log}: an `IntegerRange` or `FloatRange`
|
|
100
|
+
# argument to a monotone function folds to the range between the images of its bounds.
|
|
101
|
+
def fold_monotone_range(method_name, arg)
|
|
102
|
+
return nil unless MATH_MONOTONE_DOMAIN.key?(method_name)
|
|
103
|
+
|
|
104
|
+
lower, upper = range_bounds(arg)
|
|
105
|
+
return nil if lower.nil?
|
|
106
|
+
|
|
107
|
+
edge = MATH_MONOTONE_DOMAIN[method_name]
|
|
108
|
+
return nil if edge && lower < edge
|
|
109
|
+
|
|
110
|
+
Type::Combinator.float_range(Math.public_send(method_name, lower), Math.public_send(method_name, upper))
|
|
111
|
+
rescue Math::DomainError, RangeError, ArgumentError
|
|
112
|
+
nil
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# `[lower, upper]` as doubles for a bounded numeric carrier, `nil` for anything else. An
|
|
116
|
+
# exclusive Float end reads as its canonical closed bound, one double below.
|
|
117
|
+
def range_bounds(arg)
|
|
118
|
+
case arg
|
|
119
|
+
when Type::FloatRange then [arg.min, arg.canonical_max]
|
|
120
|
+
when Type::IntegerRange then [arg.lower.to_f, arg.upper.to_f]
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
87
124
|
def fold_binary(method_name, args)
|
|
88
125
|
return nil unless args.size == 2
|
|
89
126
|
|
|
@@ -101,6 +138,7 @@ module Rigor
|
|
|
101
138
|
return nil unless [1, 2].include?(args.size)
|
|
102
139
|
|
|
103
140
|
values = args.map { |a| numeric_constant(a) }
|
|
141
|
+
return fold_monotone_range(:log, args.first) if args.size == 1 && values.first.nil?
|
|
104
142
|
return nil if values.any?(&:nil?)
|
|
105
143
|
|
|
106
144
|
fold_float_result(Math.log(*values))
|
|
@@ -29,11 +29,8 @@ module Rigor
|
|
|
29
29
|
# `.method("name")` with a precisely-known Symbol / String argument. Declines on every other shape
|
|
30
30
|
# so the RBS tier still answers `Method` for non-folding cases.
|
|
31
31
|
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
# the fold.
|
|
35
|
-
# @param args [Array<Rigor::Type>] caller's argument types in order. Only the single-argument case
|
|
36
|
-
# matches; other arities decline.
|
|
32
|
+
# Reads `context.receiver`, `context.method_name` (only `:method` triggers the fold) and `context.args`
|
|
33
|
+
# (only the single-argument case matches; other arities decline).
|
|
37
34
|
def try_dispatch(context)
|
|
38
35
|
receiver = context.receiver
|
|
39
36
|
method_name = context.method_name
|
|
@@ -64,21 +64,20 @@ module Rigor
|
|
|
64
64
|
}.freeze
|
|
65
65
|
private_constant :ALIAS_STRICT_NOMINALS
|
|
66
66
|
|
|
67
|
-
# @param
|
|
68
|
-
# @param arg_types [Array<Rigor::Type>] caller-provided types in positional order. Empty when
|
|
67
|
+
# @param arg_types — caller-provided types in positional order. Empty when
|
|
69
68
|
# there are no arguments.
|
|
70
|
-
# @param self_type
|
|
71
|
-
# @param instance_type
|
|
72
|
-
# @param type_vars
|
|
69
|
+
# @param self_type — substitute for `Bases::Self`.
|
|
70
|
+
# @param instance_type — substitute for `Bases::Instance`.
|
|
71
|
+
# @param type_vars — substitution map for class-level type variables
|
|
73
72
|
# (Slice 4 phase 2d). The selector threads it through to {RbsTypeTranslator} so parameter types
|
|
74
73
|
# like `::Array[Elem]` substitute Elem before the accepts check, instead of degrading the param
|
|
75
74
|
# to `Array[Dynamic[Top]]`.
|
|
76
|
-
# @param block_required
|
|
75
|
+
# @param block_required — when `true`, only overloads that declare a block clause are
|
|
77
76
|
# considered (Slice 6 phase C sub-phase 1). The fallback also prefers a block-bearing overload
|
|
78
77
|
# over `method_types.first`. When `false` (the Slice 4 phase 2c default) the selector behaves
|
|
79
78
|
# exactly as before: `find` over arity-compatible overloads, falling back to the first
|
|
80
79
|
# declaration.
|
|
81
|
-
# @return
|
|
80
|
+
# @return the chosen overload, or nil when the definition has no method
|
|
82
81
|
# types at all.
|
|
83
82
|
def select(method_definition, **) = select_candidates(method_definition, **).first
|
|
84
83
|
|
|
@@ -91,7 +90,7 @@ module Rigor
|
|
|
91
90
|
# alias-resolved, typed-gradual, arity fallback) still yields exactly one candidate, so `select`
|
|
92
91
|
# keeps its historical single answer.
|
|
93
92
|
#
|
|
94
|
-
# @return
|
|
93
|
+
# @return matching overloads; empty when the definition declares none.
|
|
95
94
|
def select_candidates(method_definition, arg_types:, self_type:, instance_type:, type_vars: {},
|
|
96
95
|
block_required: false, environment: nil)
|
|
97
96
|
overloads = method_definition.method_types
|
|
@@ -108,17 +107,14 @@ module Rigor
|
|
|
108
107
|
# an arbitrary sibling-class arm that only wins by overload-list position.
|
|
109
108
|
overloads = ReceiverAffinity.reorder(overloads, self_type: self_type, environment: environment)
|
|
110
109
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
alias_expander: alias_expander }
|
|
118
|
-
)
|
|
119
|
-
end
|
|
110
|
+
# One keyword bundle for the pass pipeline (see `run_selection_passes`); a block retry rebuilds it
|
|
111
|
+
# with the block flag cleared. Built once and passed positionally -- the previous lambda plus a
|
|
112
|
+
# `**shared` splat per pass allocated three objects per selection (#775).
|
|
113
|
+
shared = { arg_types: arg_types, self_type: self_type, instance_type: instance_type,
|
|
114
|
+
type_vars: type_vars, block_required: block_required, param_overrides: param_overrides,
|
|
115
|
+
alias_expander: environment&.rbs_loader }
|
|
120
116
|
|
|
121
|
-
matches =
|
|
117
|
+
matches = run_selection_passes(overloads, shared)
|
|
122
118
|
return matches unless matches.empty?
|
|
123
119
|
|
|
124
120
|
# A block at the call site that no block-declaring overload matched: Ruby ignores a block handed
|
|
@@ -127,7 +123,7 @@ module Rigor
|
|
|
127
123
|
# `define_command(:x) do … end` against `def define_command: (Symbol) -> Symbol`) degraded to
|
|
128
124
|
# `Dynamic[Top]` — and on a self-send suppressed the whole method's return type.
|
|
129
125
|
if block_required
|
|
130
|
-
matches =
|
|
126
|
+
matches = run_selection_passes(overloads, shared.merge(block_required: false))
|
|
131
127
|
return matches unless matches.empty?
|
|
132
128
|
end
|
|
133
129
|
|
|
@@ -165,7 +161,7 @@ module Rigor
|
|
|
165
161
|
# `shared` is the caller-assembled keyword bundle for `find_matching_overload` — hash-shaped
|
|
166
162
|
# because the pass pipeline forwards it twice and RuboCop's parameter-list budget is real.
|
|
167
163
|
def run_selection_passes(overloads, shared)
|
|
168
|
-
strict = find_matching_overload(overloads,
|
|
164
|
+
strict = find_matching_overload(overloads, shared, strict: true)
|
|
169
165
|
return strict unless strict.empty?
|
|
170
166
|
|
|
171
167
|
alias_hit = find_matching_overload_via_aliases(
|
|
@@ -177,33 +173,37 @@ module Rigor
|
|
|
177
173
|
# match keeps its historical single-winner contract. With a `Dynamic[Top]` argument in play the
|
|
178
174
|
# matches are indistinguishable by types — position alone would pick — so ALL of them come back
|
|
179
175
|
# and the dispatch layer unions their returns (#521).
|
|
180
|
-
matches = find_matching_overload(overloads,
|
|
176
|
+
matches = find_matching_overload(overloads, shared, strict: false)
|
|
181
177
|
return matches.first(1) unless shared[:arg_types].any? { |t| untyped_arg?(t) }
|
|
182
178
|
|
|
183
179
|
matches
|
|
184
180
|
end
|
|
185
181
|
|
|
186
|
-
#
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
return [] if strict && arg_types.any? { |t| untyped_arg?(t) }
|
|
182
|
+
# The shared "no overload matched" answer; every consumer only reads the list.
|
|
183
|
+
NO_MATCH = [].freeze
|
|
184
|
+
private_constant :NO_MATCH
|
|
190
185
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
186
|
+
# `shared` is the keyword bundle `select_candidates` assembled (arg_types, self_type, instance_type,
|
|
187
|
+
# type_vars, block_required, param_overrides, alias_expander).
|
|
188
|
+
def find_matching_overload(overloads, shared, strict:)
|
|
189
|
+
arg_types = shared[:arg_types]
|
|
190
|
+
return NO_MATCH if strict && arg_types.any? { |t| untyped_arg?(t) }
|
|
194
191
|
|
|
195
|
-
|
|
196
|
-
method_type, arg_types,
|
|
197
|
-
self_type: self_type, instance_type: instance_type,
|
|
198
|
-
type_vars: type_vars, param_overrides: param_overrides,
|
|
199
|
-
alias_expander: alias_expander
|
|
200
|
-
)
|
|
201
|
-
end
|
|
192
|
+
block_required = shared[:block_required]
|
|
202
193
|
# Strict keeps its historical first-match short-circuit (a dispatch hot path); the gradual
|
|
203
194
|
# pass needs the full candidate list for the #521 union.
|
|
204
|
-
|
|
195
|
+
if strict
|
|
196
|
+
found = overloads.find do |method_type|
|
|
197
|
+
engages_block_shape?(method_type, block_required) &&
|
|
198
|
+
strictly_typed_params?(method_type, arg_types.size) &&
|
|
199
|
+
matches?(method_type, shared)
|
|
200
|
+
end
|
|
201
|
+
return found ? [found] : NO_MATCH
|
|
202
|
+
end
|
|
205
203
|
|
|
206
|
-
overloads.select
|
|
204
|
+
overloads.select do |method_type|
|
|
205
|
+
engages_block_shape?(method_type, block_required) && matches?(method_type, shared)
|
|
206
|
+
end
|
|
207
207
|
end
|
|
208
208
|
|
|
209
209
|
# Whether the overload's block clause is compatible with the call site's block shape: a
|
|
@@ -243,10 +243,19 @@ module Rigor
|
|
|
243
243
|
params = positional_params_for(fun, arg_types.size)
|
|
244
244
|
next false unless params.size == arg_types.size
|
|
245
245
|
|
|
246
|
-
params
|
|
246
|
+
each_param_accepts?(params, arg_types) { |param, arg| alias_param_accepts?(param.type, arg) }
|
|
247
247
|
end
|
|
248
248
|
end
|
|
249
249
|
|
|
250
|
+
# `params.zip(arg_types).all? { |param, arg| ... }` without the pair arrays: a formal beyond the
|
|
251
|
+
# actuals meets `nil`, exactly as `zip` pads. Selection zips once per overload per pass, so the
|
|
252
|
+
# pairs were a top allocation site of dispatch (#775). The counter is a captured local, so the
|
|
253
|
+
# walk allocates nothing (a Range or an Enumerator would be one object per overload per pass).
|
|
254
|
+
def each_param_accepts?(params, arg_types)
|
|
255
|
+
index = -1
|
|
256
|
+
params.all? { |param| yield(param, arg_types[index += 1]) }
|
|
257
|
+
end
|
|
258
|
+
|
|
250
259
|
# Checks the param's RBS type against an arg using alias-strict-arm matching. Optional / Union
|
|
251
260
|
# wrappers are flattened; alias resolution is one level deep (the canonical core aliases all have
|
|
252
261
|
# non-alias strict arms).
|
|
@@ -317,23 +326,24 @@ module Rigor
|
|
|
317
326
|
end
|
|
318
327
|
end
|
|
319
328
|
|
|
320
|
-
|
|
321
|
-
|
|
329
|
+
# `shared` is the keyword bundle `select_candidates` assembled (see `find_matching_overload`).
|
|
330
|
+
def matches?(method_type, shared)
|
|
322
331
|
return false if method_type.respond_to?(:type_params) && rejects_keyword_required?(method_type)
|
|
323
332
|
|
|
333
|
+
arg_types = shared[:arg_types]
|
|
324
334
|
fun = method_type.type
|
|
325
335
|
return false unless arity_compatible?(fun, arg_types.size)
|
|
326
336
|
|
|
327
337
|
params = positional_params_for(fun, arg_types.size)
|
|
328
|
-
params
|
|
338
|
+
each_param_accepts?(params, arg_types) do |param, arg|
|
|
329
339
|
accepts_param?(
|
|
330
340
|
param,
|
|
331
341
|
arg,
|
|
332
|
-
self_type: self_type,
|
|
333
|
-
instance_type: instance_type,
|
|
334
|
-
type_vars: type_vars,
|
|
335
|
-
param_overrides: param_overrides,
|
|
336
|
-
alias_expander: alias_expander
|
|
342
|
+
self_type: shared[:self_type],
|
|
343
|
+
instance_type: shared[:instance_type],
|
|
344
|
+
type_vars: shared[:type_vars],
|
|
345
|
+
param_overrides: shared[:param_overrides],
|
|
346
|
+
alias_expander: shared[:alias_expander]
|
|
337
347
|
)
|
|
338
348
|
end
|
|
339
349
|
end
|
|
@@ -376,8 +386,12 @@ module Rigor
|
|
|
376
386
|
rest = fun.rest_positionals
|
|
377
387
|
trailing = fun.trailing_positionals
|
|
378
388
|
|
|
389
|
+
optional_needed = [actual_count - required.size - trailing.size, 0].max
|
|
390
|
+
# Nothing to append: the declaration's own list is the answer, read-only (the common
|
|
391
|
+
# required-positionals-only overload -- no copy, #775).
|
|
392
|
+
return required if optional_needed.zero? && trailing.empty? && (rest.nil? || actual_count <= required.size)
|
|
393
|
+
|
|
379
394
|
head = required.dup
|
|
380
|
-
optional_needed = [actual_count - head.size - trailing.size, 0].max
|
|
381
395
|
head.concat(optional.first(optional_needed))
|
|
382
396
|
|
|
383
397
|
absorbed_by_rest = actual_count - head.size - trailing.size
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../type"
|
|
4
|
+
require_relative "singleton_folding"
|
|
5
|
+
|
|
6
|
+
module Rigor
|
|
7
|
+
module Inference
|
|
8
|
+
module MethodDispatcher
|
|
9
|
+
# ADR-109 — the interval a random draw over a literal range lands in. `Random.rand` and
|
|
10
|
+
# `Kernel#rand` advance a generator, so no call folds to a constant ({Builtins::RANDOM_CATALOG}
|
|
11
|
+
# says why); but a draw over `a..b` returns a value the range covers, and that is a bounded
|
|
12
|
+
# carrier of the range's own class, keeping its written end:
|
|
13
|
+
#
|
|
14
|
+
# rand(1..6) -> Integer[1..6]
|
|
15
|
+
# rand(1...6) -> Integer[1..5]
|
|
16
|
+
# rand(0.0...1.0) -> Float[0.0...1.0]
|
|
17
|
+
# Random.rand(1.0..2.0) -> Float[1.0..2.0]
|
|
18
|
+
#
|
|
19
|
+
# An empty range (`rand(5..1)` returns nil, `Random.rand(5..1)` raises), an unbounded one
|
|
20
|
+
# (`Errno::EDOM`) and a non-literal argument decline to the RBS envelope. The other argument
|
|
21
|
+
# shapes (`rand`, `rand(n)`, `rand(1.5)`) are deliberately NOT folded: the corpus uses them as its
|
|
22
|
+
# "unknown Integer / Float" oracle, and their intervals belong to a change that replaces that
|
|
23
|
+
# oracle first. The `Kernel` spelling is folded by {KernelDispatch#try_rand} behind ADR-91's
|
|
24
|
+
# ownership gate; this module owns `Singleton[Random]` and the shared interval reading.
|
|
25
|
+
module RandomFolding
|
|
26
|
+
module_function
|
|
27
|
+
|
|
28
|
+
def try_dispatch(context)
|
|
29
|
+
return nil unless SingletonFolding.receiver?(context.receiver, "Random")
|
|
30
|
+
return nil unless context.method_name == :rand
|
|
31
|
+
|
|
32
|
+
interval_for_args(context.args)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# The bounded carrier a one-argument draw over a literal range lands in, or `nil` for any other
|
|
36
|
+
# argument shape.
|
|
37
|
+
def interval_for_args(args)
|
|
38
|
+
return nil unless args.size == 1
|
|
39
|
+
|
|
40
|
+
arg = args.first
|
|
41
|
+
return nil unless arg.is_a?(Type::Constant) && arg.value.is_a?(Range)
|
|
42
|
+
|
|
43
|
+
range_interval(arg.value)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def range_interval(range)
|
|
47
|
+
lower = range.begin
|
|
48
|
+
upper = range.end
|
|
49
|
+
return nil unless real_endpoint?(lower) && real_endpoint?(upper)
|
|
50
|
+
|
|
51
|
+
if lower.is_a?(Integer) && upper.is_a?(Integer)
|
|
52
|
+
integer_range_interval(lower, upper, range.exclude_end?)
|
|
53
|
+
else
|
|
54
|
+
float_range_interval(lower.to_f, upper.to_f, range.exclude_end?)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# A finite Integer or Float; a missing end is `Errno::EDOM` at run time.
|
|
59
|
+
def real_endpoint?(value)
|
|
60
|
+
case value
|
|
61
|
+
when Integer then true
|
|
62
|
+
when Float then value.finite?
|
|
63
|
+
else false
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def integer_range_interval(lower, upper, exclusive)
|
|
68
|
+
upper -= 1 if exclusive
|
|
69
|
+
return nil if lower > upper
|
|
70
|
+
|
|
71
|
+
Type::Combinator.integer_range(lower, upper)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def float_range_interval(lower, upper, exclusive)
|
|
75
|
+
return nil if lower > (exclusive ? upper.prev_float : upper)
|
|
76
|
+
|
|
77
|
+
Type::Combinator.float_range(lower, upper, exclude_end: exclusive)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require_relative "../../reflection"
|
|
4
4
|
require_relative "../../type"
|
|
5
5
|
require_relative "../../rbs_extended"
|
|
6
|
+
require_relative "../range_constant"
|
|
6
7
|
require_relative "../rbs_type_translator"
|
|
7
8
|
require_relative "../void_origin"
|
|
8
9
|
require_relative "../optimistic_origin"
|
|
@@ -71,31 +72,38 @@ module Rigor
|
|
|
71
72
|
private_constant :EMPTY_TYPE_VARS
|
|
72
73
|
EMPTY_TYPE_PARAM_NAMES = [].freeze
|
|
73
74
|
private_constant :EMPTY_TYPE_PARAM_NAMES
|
|
75
|
+
NO_BINDING = [nil, nil].freeze
|
|
76
|
+
private_constant :NO_BINDING
|
|
74
77
|
|
|
75
|
-
#
|
|
76
|
-
#
|
|
77
|
-
#
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
#
|
|
82
|
-
#
|
|
83
|
-
#
|
|
84
|
-
#
|
|
85
|
-
#
|
|
86
|
-
#
|
|
87
|
-
#
|
|
88
|
-
#
|
|
89
|
-
#
|
|
90
|
-
#
|
|
91
|
-
#
|
|
92
|
-
#
|
|
93
|
-
#
|
|
94
|
-
#
|
|
78
|
+
# Both spellings a resolved `RBS::Types::ClassInstance#name` — or a `Nominal#class_name` built
|
|
79
|
+
# from one — may carry for `Range`; core signatures absolutise, but a plugin-contributed one
|
|
80
|
+
# need not.
|
|
81
|
+
RANGE_TYPE_NAMES = ["::Range", "Range"].freeze
|
|
82
|
+
private_constant :RANGE_TYPE_NAMES
|
|
83
|
+
|
|
84
|
+
# Four fields of `context` shape the answer beyond receiver, method name and arguments.
|
|
85
|
+
#
|
|
86
|
+
# `block_type` is the inferred block return type propagated from `MethodDispatcher.dispatch`; when
|
|
87
|
+
# non-nil, the selector prefers a block-bearing overload and binds the method-level type parameter
|
|
88
|
+
# that the block's return type references to it (Slice 6 phase C sub-phase 2).
|
|
89
|
+
#
|
|
90
|
+
# `self_type_override`, when set, is the substitution for `Bases::Self` in the method's return type.
|
|
91
|
+
# `MethodDispatcher#try_user_class_fallback` uses it to preserve the ORIGINAL receiver as the
|
|
92
|
+
# substitute for `self` even though the dispatch is routed through `Nominal[Object]` — so that
|
|
93
|
+
# `Bundler::URI::Generic.dup` (which resolves through the `Object` fallback because
|
|
94
|
+
# `Bundler::URI::Generic` lacks RBS) returns `Bundler::URI::Generic` per `Kernel#dup: () -> self`
|
|
95
|
+
# rather than `Object`. Nil computes self from the resolved class_name as before.
|
|
96
|
+
#
|
|
97
|
+
# `public_only`, when true, keeps a method whose RBS accessibility is `:private` from resolving (the
|
|
98
|
+
# call yields `nil`, i.e. "no rule"). Set by the explicit-non-`self`-receiver user-class fallback so
|
|
99
|
+
# a call like `Favourite.select(...)` does not adopt the private `Kernel#select` signature.
|
|
100
|
+
#
|
|
101
|
+
# `scope`, when supplied, enables ADR-43 RBS-complete-ancestor resolution against
|
|
102
|
+
# `ALLOWED_RBS_COMPLETE_ANCESTORS`; `nil` keeps inherited calls unresolved (`Dynamic[Top]`) — the
|
|
103
|
+
# FP-safe default for open hierarchies (`< ActionController::Base`, …).
|
|
104
|
+
#
|
|
105
|
+
# @return inferred return type, or `nil` when no rule resolves (no class name,
|
|
95
106
|
# no method, dispatch on a Top/Dynamic[Top] receiver, etc.).
|
|
96
|
-
# @param scope [Rigor::Scope, nil] when supplied, enables ADR-43 RBS-complete-ancestor resolution
|
|
97
|
-
# against `ALLOWED_RBS_COMPLETE_ANCESTORS`. `nil` keeps inherited calls unresolved
|
|
98
|
-
# (`Dynamic[Top]`) — the FP-safe default for open hierarchies (`< ActionController::Base`, …).
|
|
99
107
|
def try_dispatch(context)
|
|
100
108
|
environment = context.environment
|
|
101
109
|
return nil if environment.nil?
|
|
@@ -130,7 +138,7 @@ module Rigor
|
|
|
130
138
|
#
|
|
131
139
|
# This deliberately does NOT differentiate "no overload had a block" from "the block is untyped";
|
|
132
140
|
# the binder treats both the same way (every parameter defaults to `Dynamic[Top]`).
|
|
133
|
-
# @return
|
|
141
|
+
# @return positional block parameter types.
|
|
134
142
|
def block_param_types(context)
|
|
135
143
|
environment = context.environment
|
|
136
144
|
return [] if environment.nil?
|
|
@@ -181,6 +189,15 @@ module Rigor
|
|
|
181
189
|
method_definition = lookup_method(environment, class_name, kind, method_name, scope)
|
|
182
190
|
return nil unless method_definition
|
|
183
191
|
return nil if public_only && method_private?(method_definition)
|
|
192
|
+
# Issue #823 — a declaration that states the member's presence and parameters but not its
|
|
193
|
+
# return (`%a{rigor:v1:inferred-return}`, written by `rigor-rbs-inline` on every type slot it
|
|
194
|
+
# DEFAULTED). Declining here is the whole mechanism: this tier's product IS the return type, so
|
|
195
|
+
# withholding it routes the call down the same tiers an undeclared method takes and the body
|
|
196
|
+
# gets typed. Everything the declaration DOES say survives, because the rules that read it —
|
|
197
|
+
# `call.undefined-method`, `call.wrong-arity`, argument-type checking — look the method up in
|
|
198
|
+
# the environment themselves rather than reading this tier's answer.
|
|
199
|
+
return nil if RbsExtended.inferred_return?(method_definition)
|
|
200
|
+
return SPACESHIP_ENVELOPE if inherited_identity_spaceship?(method_definition, class_name, kind, method_name)
|
|
184
201
|
|
|
185
202
|
type_vars = build_type_vars(environment, class_name, receiver_args)
|
|
186
203
|
translate_return_type(
|
|
@@ -202,6 +219,46 @@ module Rigor
|
|
|
202
219
|
nil
|
|
203
220
|
end
|
|
204
221
|
|
|
222
|
+
# Issue #661 — `Kernel#<=>: (untyped other) -> 0?` is the IDENTITY comparison: `0` when the two
|
|
223
|
+
# are the same object, `nil` otherwise, and correct as written for a bare `Object`. Reached by
|
|
224
|
+
# inheritance it stops being a statement about the call. `1.day <=> 2.days` typed as `0?`, and a
|
|
225
|
+
# `Money` whose signature says `include Comparable` — whose whole contract is that the includer
|
|
226
|
+
# defines `<=>` — typed the same, because neither declares `<=>` of its own and every class
|
|
227
|
+
# inherits Kernel's.
|
|
228
|
+
#
|
|
229
|
+
# `0?` is a VALUE claim, so the cost is not confined to the expression: narrowing
|
|
230
|
+
# `n = a <=> b; n.negative? if n` reads the truthy arm as the literal `0`, folds `0.negative?`
|
|
231
|
+
# to false, and answers `bot` for a branch the runtime takes on every ordered pair. That is one
|
|
232
|
+
# `clause.unreachable` away from a false positive on correct code.
|
|
233
|
+
#
|
|
234
|
+
# Widened rather than declined: `Integer?` is the envelope Ruby's own `<=>` convention states and
|
|
235
|
+
# the one upstream rbs writes wherever a class DOES declare the operator (`Array`, `Module`,
|
|
236
|
+
# `Complex`). It is a supertype of `0?`, so this only ever removes a conclusion — and it removes
|
|
237
|
+
# exactly the conclusions that rested on the receiver not overriding an operator its signature
|
|
238
|
+
# was never required to mention (ADR-5: a partially-declared class is not a closed world).
|
|
239
|
+
#
|
|
240
|
+
# Bounded to the three classes that OWN the identity comparison, where the claim is the truth
|
|
241
|
+
# about the receiver rather than an artifact of inheritance, and to `<=>` alone — the general
|
|
242
|
+
# question of what an inherited Object/Kernel signature may assert about a subclass is much
|
|
243
|
+
# larger, and `to_s` / `hash` / `inspect` do not carry a value-precise return to lose.
|
|
244
|
+
SPACESHIP_IDENTITY_OWNERS = %w[Kernel Object BasicObject].to_set.freeze
|
|
245
|
+
private_constant :SPACESHIP_IDENTITY_OWNERS
|
|
246
|
+
|
|
247
|
+
SPACESHIP_ENVELOPE = Type::Combinator.union(
|
|
248
|
+
Type::Combinator.nominal_of("Integer"),
|
|
249
|
+
Type::Combinator.constant_of(nil)
|
|
250
|
+
).freeze
|
|
251
|
+
private_constant :SPACESHIP_ENVELOPE
|
|
252
|
+
|
|
253
|
+
def inherited_identity_spaceship?(method_definition, class_name, kind, method_name)
|
|
254
|
+
return false unless method_name == :<=>
|
|
255
|
+
return false unless kind == :instance
|
|
256
|
+
return false if SPACESHIP_IDENTITY_OWNERS.include?(class_name.to_s.delete_prefix("::"))
|
|
257
|
+
return false unless method_definition.respond_to?(:defined_in)
|
|
258
|
+
|
|
259
|
+
SPACESHIP_IDENTITY_OWNERS.include?(method_definition.defined_in.to_s.delete_prefix("::"))
|
|
260
|
+
end
|
|
261
|
+
|
|
205
262
|
# Maps a Rigor::Type receiver to a `[class_name, kind, type_args]` triple where `kind` is either
|
|
206
263
|
# `:instance` or `:singleton` and `type_args` carries the receiver's generic instantiation (empty
|
|
207
264
|
# for raw or singleton receivers, since `Singleton[Foo]` carries no generic args today). Returns
|
|
@@ -237,6 +294,13 @@ module Rigor
|
|
|
237
294
|
# whole dispatch to Dynamic). The refinement-aware promotions (`String#upcase` →
|
|
238
295
|
# `uppercase-string`, …) run in their own catalog tier ABOVE this one, so they still win.
|
|
239
296
|
receiver_descriptor(receiver.base)
|
|
297
|
+
when Type::FloatRange
|
|
298
|
+
# ADR-109 WD4 — a bounded Float is a Float for every method the fold tiers do not own.
|
|
299
|
+
["Float", :instance, []]
|
|
300
|
+
when Type::IntegerRange
|
|
301
|
+
# #842 — a bounded Integer is an Integer for every method the fold tiers
|
|
302
|
+
# (ConstantFolding, ShapeDispatch#dispatch_integer_range) do not own.
|
|
303
|
+
["Integer", :instance, []]
|
|
240
304
|
when Type::Dynamic
|
|
241
305
|
receiver_descriptor(receiver.static_facet)
|
|
242
306
|
end
|
|
@@ -331,15 +395,20 @@ module Rigor
|
|
|
331
395
|
# disagree -- in both cases free variables in the method's return type degrade to `Dynamic[Top]`
|
|
332
396
|
# per the translator's contract.
|
|
333
397
|
def build_type_vars(environment, class_name, receiver_args)
|
|
334
|
-
return
|
|
398
|
+
return NO_TYPE_VARS if receiver_args.empty?
|
|
335
399
|
|
|
336
400
|
param_names = Rigor::Reflection.class_type_param_names(class_name, environment: environment)
|
|
337
|
-
return
|
|
338
|
-
return
|
|
401
|
+
return NO_TYPE_VARS if param_names.empty?
|
|
402
|
+
return NO_TYPE_VARS if param_names.size != receiver_args.size
|
|
339
403
|
|
|
340
404
|
param_names.zip(receiver_args).to_h
|
|
341
405
|
end
|
|
342
406
|
|
|
407
|
+
# The shared empty substitution map: most receivers carry no type arguments, and the translator
|
|
408
|
+
# only ever reads the map (#775).
|
|
409
|
+
NO_TYPE_VARS = {}.freeze
|
|
410
|
+
private_constant :NO_TYPE_VARS
|
|
411
|
+
|
|
343
412
|
# rubocop:disable Metrics/ParameterLists
|
|
344
413
|
def translate_return_type(method_definition, class_name:, kind:, args:, type_vars:, block_type:,
|
|
345
414
|
method_name: nil, environment: nil, self_type_override: nil,
|
|
@@ -505,7 +574,8 @@ module Rigor
|
|
|
505
574
|
# Envelope, deliberately the narrowest sound shape:
|
|
506
575
|
#
|
|
507
576
|
# * only a positional parameter (required or optional) whose declared type is EXACTLY
|
|
508
|
-
# `RBS::Types::Variable
|
|
577
|
+
# `RBS::Types::Variable`, plus the one container shape issue #834 admits (see
|
|
578
|
+
# {#range_element_binding}) — no general container walk, so `(Array[T]) -> T` still degrades;
|
|
509
579
|
# * only names the SELECTED overload declares in its own `type_params` (a class-level variable
|
|
510
580
|
# keeps its receiver-derived binding);
|
|
511
581
|
# * an existing key wins, so the receiver and the block return type both outrank an argument;
|
|
@@ -534,14 +604,27 @@ module Rigor
|
|
|
534
604
|
positionals.zip(args).each_with_object({}) do |(param, arg), bindings|
|
|
535
605
|
next if arg.nil?
|
|
536
606
|
|
|
537
|
-
name =
|
|
607
|
+
name, bound = param_binding(param, arg, declared, type_vars)
|
|
538
608
|
next if name.nil?
|
|
539
|
-
next if no_static_evidence?(arg)
|
|
540
609
|
|
|
541
|
-
bindings[name] = bindings.key?(name) ? Type::Combinator.union(bindings[name],
|
|
610
|
+
bindings[name] = bindings.key?(name) ? Type::Combinator.union(bindings[name], bound) : bound
|
|
542
611
|
end
|
|
543
612
|
end
|
|
544
613
|
|
|
614
|
+
# The `(variable name, bound type)` a positional parameter contributes, or {NO_BINDING} when it
|
|
615
|
+
# contributes none. The bare-variable shape is tried first because it is the overwhelmingly
|
|
616
|
+
# common one; the `Range[A]` shape is reached only when the parameter is not a bare variable.
|
|
617
|
+
def param_binding(param, arg, declared, type_vars)
|
|
618
|
+
name = variable_param_name(param, declared, type_vars)
|
|
619
|
+
unless name.nil?
|
|
620
|
+
return NO_BINDING if no_static_evidence?(arg)
|
|
621
|
+
|
|
622
|
+
return [name, arg]
|
|
623
|
+
end
|
|
624
|
+
|
|
625
|
+
range_element_binding(param, arg, declared, type_vars)
|
|
626
|
+
end
|
|
627
|
+
|
|
545
628
|
def declared_type_param_names(method_type)
|
|
546
629
|
params = method_type.respond_to?(:type_params) ? method_type.type_params : nil
|
|
547
630
|
return EMPTY_TYPE_PARAM_NAMES if params.nil? || params.empty?
|
|
@@ -562,6 +645,57 @@ module Rigor
|
|
|
562
645
|
name
|
|
563
646
|
end
|
|
564
647
|
|
|
648
|
+
# Issues #834 / #862 — the one container position the envelope admits: a `Range[A]` parameter
|
|
649
|
+
# against a Range argument that names its own element. `Comparable#clamp: [A] (Range[A]) ->
|
|
650
|
+
# (self | A)` otherwise leaves `A` unbound and `i.clamp(1..9)` answers `Dynamic[top] | Integer`,
|
|
651
|
+
# even though the argument names the element outright. It stays Range-only because the
|
|
652
|
+
# justification does not generalise: a Range is immutable and its element type is fixed at
|
|
653
|
+
# construction, whereas an `Array[T]` argument's carrier may have been widened long before the
|
|
654
|
+
# call reached here.
|
|
655
|
+
def range_element_binding(param, arg, declared, type_vars)
|
|
656
|
+
declared_type = param.type
|
|
657
|
+
return NO_BINDING unless declared_type.is_a?(RBS::Types::ClassInstance)
|
|
658
|
+
return NO_BINDING unless RANGE_TYPE_NAMES.include?(declared_type.name.to_s)
|
|
659
|
+
return NO_BINDING unless declared_type.args.size == 1
|
|
660
|
+
|
|
661
|
+
element = declared_type.args.first
|
|
662
|
+
return NO_BINDING unless element.is_a?(RBS::Types::Variable)
|
|
663
|
+
return NO_BINDING unless declared.include?(element.name)
|
|
664
|
+
return NO_BINDING if type_vars.key?(element.name)
|
|
665
|
+
|
|
666
|
+
bound = range_argument_element(arg)
|
|
667
|
+
bound.nil? ? NO_BINDING : [element.name, bound]
|
|
668
|
+
end
|
|
669
|
+
|
|
670
|
+
# The element a Range argument names, or nil when it names none.
|
|
671
|
+
#
|
|
672
|
+
# Two carriers qualify. A `Constant<Range>` contributes its endpoints lifted to their classes
|
|
673
|
+
# ({RangeConstant.element_type}), not the two values, so `clamp(1..9)` answers `Integer` rather
|
|
674
|
+
# than a `1 | 9` the runtime contradicts for every receiver already inside the bracket. A
|
|
675
|
+
# `Nominal[Range, [T]]` (issue #862 — `1..ARGV.size` has no literal endpoint to read, so
|
|
676
|
+
# `ExpressionTyper` hands back the nominal carrier) contributes `T` itself.
|
|
677
|
+
#
|
|
678
|
+
# A nominal carrier qualifies only when `T` is a Nominal or a union of Nominals. `untyped`, a
|
|
679
|
+
# `Dynamic`, or a still-unbound type variable would launder an unknown into the result — the
|
|
680
|
+
# caller's `self | A` would read as `self | unknown` while claiming to be inferred — so those
|
|
681
|
+
# keep degrading as an unbound variable does.
|
|
682
|
+
def range_argument_element(arg)
|
|
683
|
+
return RangeConstant.element_type(arg) unless arg.is_a?(Type::Nominal)
|
|
684
|
+
return nil unless RANGE_TYPE_NAMES.include?(arg.class_name)
|
|
685
|
+
return nil unless arg.type_args.size == 1
|
|
686
|
+
|
|
687
|
+
element = arg.type_args.first
|
|
688
|
+
nominal_only?(element) ? element : nil
|
|
689
|
+
end
|
|
690
|
+
|
|
691
|
+
def nominal_only?(type)
|
|
692
|
+
case type
|
|
693
|
+
when Type::Nominal then true
|
|
694
|
+
when Type::Union then type.members.all?(Type::Nominal)
|
|
695
|
+
else false
|
|
696
|
+
end
|
|
697
|
+
end
|
|
698
|
+
|
|
565
699
|
# `Dynamic[top]` is the engine's "we could not tell" answer, so binding a variable to it would
|
|
566
700
|
# dress up an absence of evidence as an inference. The variable stays unbound and degrades as it
|
|
567
701
|
# did before, which is the same value anyway.
|