rigortype 0.4.7 → 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 +3 -1
- data/data/core_overlay/bigdecimal.rbs +33 -0
- data/data/core_overlay/resolv.rbs +30 -0
- data/data/gem_overlay/activesupport/core_ext.rbs +29 -0
- data/data/gem_overlay/addressable/addressable.rbs +40 -0
- data/docs/handbook/01-getting-started.md +1 -1
- data/docs/handbook/11-sig-gen.md +35 -0
- data/docs/install.md +20 -0
- data/docs/manual/01-installation.md +67 -26
- data/docs/manual/02-cli-reference.md +307 -91
- data/docs/manual/03-configuration.md +41 -3
- data/docs/manual/04-diagnostics.md +5 -4
- data/docs/manual/08-skills.md +11 -0
- data/docs/manual/09-editor-integration.md +8 -0
- 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 +15 -6
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +3 -0
- data/docs/manual/plugins/rigor-sorbet.md +17 -3
- data/lib/rigor/analysis/check_rules/always_raises_shield.rb +147 -0
- data/lib/rigor/analysis/check_rules/call_facts.rb +58 -0
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +8 -0
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +13 -2
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +6 -0
- data/lib/rigor/analysis/check_rules/plugin_api_surface.rb +195 -0
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +40 -11
- data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +27 -1
- data/lib/rigor/analysis/check_rules/replaced_singleton_defs.rb +96 -0
- data/lib/rigor/analysis/check_rules/rule_ids.rb +8 -1
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules.rb +598 -99
- data/lib/rigor/analysis/crash_signature.rb +43 -1
- data/lib/rigor/analysis/dependency_recorder.rb +233 -76
- data/lib/rigor/analysis/incremental.rb +11 -1
- data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +2 -1
- data/lib/rigor/analysis/incremental_session.rb +279 -37
- data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -3
- data/lib/rigor/analysis/rule_catalog.rb +37 -1
- data/lib/rigor/analysis/run_cache_key.rb +27 -3
- data/lib/rigor/analysis/run_disclosure_credit.rb +26 -6
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +4 -0
- data/lib/rigor/analysis/runner/declaration_position.rb +7 -7
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +72 -20
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +3 -8
- data/lib/rigor/analysis/runner/pool_coordinator.rb +26 -5
- data/lib/rigor/analysis/runner/project_pre_passes.rb +4 -37
- data/lib/rigor/analysis/runner.rb +222 -39
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +61 -22
- data/lib/rigor/analysis/worker_session.rb +42 -41
- data/lib/rigor/bleeding_edge.rb +19 -4
- data/lib/rigor/cache/file_digest.rb +39 -31
- data/lib/rigor/cache/incremental_snapshot.rb +66 -7
- data/lib/rigor/cache/rbs_descriptor.rb +37 -2
- data/lib/rigor/cache/rbs_environment.rb +5 -1
- data/lib/rigor/cli/baseline_command.rb +15 -2
- data/lib/rigor/cli/check_command.rb +4 -2
- data/lib/rigor/cli/effects_snapshot_command.rb +8 -1
- data/lib/rigor/cli/lens_command.rb +17 -2
- data/lib/rigor/cli/sig_gen_command.rb +8 -0
- data/lib/rigor/cli/skill_describe.rb +1 -0
- data/lib/rigor/cli.rb +5 -2
- data/lib/rigor/config_audit.rb +32 -18
- data/lib/rigor/configuration/severity_profile.rb +9 -0
- data/lib/rigor/configuration.rb +32 -5
- data/lib/rigor/effects/collector.rb +53 -21
- data/lib/rigor/environment/bundle_sig_discovery.rb +63 -28
- data/lib/rigor/environment/constant_type_cache_holder.rb +5 -0
- data/lib/rigor/environment/default_libraries.rb +17 -0
- data/lib/rigor/environment/deferred_signature_plan.rb +104 -6
- data/lib/rigor/environment/hkt_registry_holder.rb +6 -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 +6 -0
- data/lib/rigor/environment/rbs_loader.rb +312 -63
- data/lib/rigor/environment.rb +131 -35
- data/lib/rigor/inference/acceptance.rb +50 -15
- 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_run_count.rb +9 -2
- data/lib/rigor/inference/builtins/array_catalog.rb +2 -2
- data/lib/rigor/inference/callee_mutation_chain.rb +1 -1
- 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 +56 -15
- data/lib/rigor/inference/declaration_walk.rb +4 -0
- data/lib/rigor/inference/def_node_resolver.rb +32 -14
- data/lib/rigor/inference/def_return_typer.rb +1 -1
- data/lib/rigor/inference/definer_resolution.rb +15 -7
- data/lib/rigor/inference/definers_index.rb +72 -16
- data/lib/rigor/inference/error_info.rb +16 -0
- data/lib/rigor/inference/expression_typer.rb +1459 -287
- 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 +8 -6
- data/lib/rigor/inference/in_effect_refinements.rb +7 -0
- data/lib/rigor/inference/indexed_narrowing.rb +57 -8
- data/lib/rigor/inference/ivar_writer_census.rb +106 -11
- data/lib/rigor/inference/key_presence_guard.rb +51 -25
- 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 +63 -15
- data/lib/rigor/inference/match_rebinding/frame.rb +11 -0
- data/lib/rigor/inference/match_rebinding/yielding_match.rb +74 -18
- data/lib/rigor/inference/match_rebinding.rb +21 -8
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +43 -29
- data/lib/rigor/inference/method_dispatcher/class_keyed_block_params.rb +65 -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/overload_selector.rb +12 -0
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +409 -67
- data/lib/rigor/inference/method_dispatcher/self_substitute.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +384 -64
- data/lib/rigor/inference/method_dispatcher/singleton_type_params.rb +104 -0
- data/lib/rigor/inference/method_dispatcher.rb +66 -18
- 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 +13 -2
- data/lib/rigor/inference/multi_target_binder.rb +1 -1
- data/lib/rigor/inference/mutation_widening.rb +44 -4
- data/lib/rigor/inference/narrowing.rb +201 -27
- data/lib/rigor/inference/operand_effects.rb +43 -2
- data/lib/rigor/inference/optimistic_origin.rb +133 -8
- data/lib/rigor/inference/origin_lookup.rb +1 -1
- data/lib/rigor/inference/precision_scanner.rb +1 -1
- data/lib/rigor/inference/project_method_ownership.rb +28 -10
- 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/rbs_type_translator.rb +68 -15
- 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 +56 -18
- data/lib/rigor/inference/refined_dispatch/own_method.rb +51 -17
- 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 +76 -22
- data/lib/rigor/inference/scope_indexer/class_cvars_collector.rb +13 -6
- data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +12 -11
- data/lib/rigor/inference/scope_indexer/ivar_contributions.rb +188 -28
- data/lib/rigor/inference/scope_indexer/mixin_accumulator.rb +136 -9
- data/lib/rigor/inference/scope_indexer.rb +2079 -183
- data/lib/rigor/inference/self_call_ivar_writes.rb +26 -7
- data/lib/rigor/inference/self_preserving_block.rb +87 -0
- data/lib/rigor/inference/singleton_hook_decline.rb +41 -6
- data/lib/rigor/inference/singleton_object_constant.rb +32 -2
- data/lib/rigor/inference/statement_evaluator.rb +970 -250
- data/lib/rigor/inference/string_mutation.rb +14 -0
- data/lib/rigor/inference/synthetic_method_scanner.rb +35 -11
- data/lib/rigor/inference/toplevel_def_census.rb +14 -6
- data/lib/rigor/inference/unknown_store_widening.rb +1 -1
- data/lib/rigor/inference/untyped_rejoin.rb +1 -1
- data/lib/rigor/inference/void_tail_summary.rb +29 -10
- 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/run.rb +8 -0
- data/lib/rigor/lens.rb +1 -1
- data/lib/rigor/library_probe.rb +68 -0
- data/lib/rigor/memo_audit.rb +282 -0
- data/lib/rigor/plugin/base.rb +15 -2
- data/lib/rigor/plugin/io_boundary.rb +25 -3
- data/lib/rigor/plugin/manifest.rb +7 -3
- data/lib/rigor/plugin/registry.rb +13 -2
- data/lib/rigor/plugin/trust_policy.rb +46 -0
- data/lib/rigor/protection/mutation_cache.rb +4 -1
- data/lib/rigor/reflection/constant_ancestors.rb +79 -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 +18 -1
- data/lib/rigor/run_scoped_slots.rb +20 -20
- data/lib/rigor/scope/discovery_index.rb +149 -2
- data/lib/rigor/scope/resolution_chain/relevance.rb +14 -1
- data/lib/rigor/scope/resolution_chain.rb +900 -55
- data/lib/rigor/scope.rb +307 -34
- data/lib/rigor/sig_gen/bot_proposal_guard.rb +306 -0
- data/lib/rigor/sig_gen/classification.rb +14 -1
- data/lib/rigor/sig_gen/generator.rb +222 -13
- data/lib/rigor/sig_gen/method_candidate.rb +14 -0
- data/lib/rigor/sig_gen/observation_collector.rb +11 -2
- data/lib/rigor/sig_gen/platform_guarded_files.rb +184 -0
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +47 -0
- data/lib/rigor/sig_gen/writer.rb +21 -0
- 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/combinator.rb +164 -8
- data/lib/rigor/type/interface.rb +60 -0
- data/lib/rigor/type.rb +1 -0
- 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/version.rb +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +2 -2
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +57 -25
- 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/job_discoverer.rb +16 -6
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +4 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +2 -1
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +130 -30
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +20 -5
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +7 -4
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +49 -13
- data/plugins/rigor-activerecord/sig/active_record/relation.rbs +19 -5
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +4 -2
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +13 -5
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +29 -0
- 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-ffi/lib/rigor/plugin/ffi.rb +4 -2
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +12 -2
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +6 -0
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +199 -1
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +3 -2
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +4 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +209 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +146 -7
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +52 -34
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +80 -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 +1 -6
- data/sig/rigor/analysis/check_rules/rule_walk.rbs +6 -0
- data/sig/rigor/analysis/fact_store.rbs +2 -3
- data/sig/rigor/cache/descriptor.rbs +14 -0
- data/sig/rigor/environment.rbs +4 -1
- data/sig/rigor/plugin/base.rbs +53 -19
- data/sig/rigor/plugin/interfaces.rbs +51 -0
- data/sig/rigor/plugin/io_boundary.rbs +2 -3
- data/sig/rigor/plugin/node_context.rbs +21 -0
- data/sig/rigor/reflection.rbs +37 -0
- data/sig/rigor/scope.rbs +22 -3
- 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-project-init/references/01-detect.md +1 -0
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +5 -1
- 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 +48 -2
- data/lib/rigor/inference/memo_audit.rb +0 -55
|
@@ -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
|
|
@@ -10,6 +10,7 @@ require_relative "imprecise_argument"
|
|
|
10
10
|
require_relative "keyword_arguments"
|
|
11
11
|
require_relative "no_match_fallback"
|
|
12
12
|
require_relative "proven_overload"
|
|
13
|
+
require_relative "singleton_type_params"
|
|
13
14
|
require_relative "receiver_affinity"
|
|
14
15
|
require_relative "splat_arity"
|
|
15
16
|
|
|
@@ -53,6 +54,7 @@ module Rigor
|
|
|
53
54
|
# The selector is intentionally agnostic about the dispatch kind (instance vs singleton). Both kinds
|
|
54
55
|
# share the same arity and acceptance shape; the difference is only in which `Definition` the caller
|
|
55
56
|
# fetched.
|
|
57
|
+
# rubocop:disable-next Metrics/ModuleLength -- the selection passes plus the #2828 overload rewrite wrapper.
|
|
56
58
|
module OverloadSelector
|
|
57
59
|
module_function
|
|
58
60
|
|
|
@@ -124,6 +126,16 @@ module Rigor
|
|
|
124
126
|
declared = method_definition.method_types
|
|
125
127
|
return [] if declared.empty?
|
|
126
128
|
|
|
129
|
+
# Issue #2828 — `singleton(T)` over a method type variable reads as a class object (`SingletonTypeParams`).
|
|
130
|
+
SingletonTypeParams.selecting(declared) do |overloads|
|
|
131
|
+
select_among(overloads, method_definition, arg_types, self_type, instance_type, type_vars, block_required,
|
|
132
|
+
environment, member, keywords_last, splats, block_fallback)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# rubocop:disable-next Metrics/ParameterLists -- {.select_declared}'s inputs plus the overloads it reads.
|
|
137
|
+
def select_among(declared, method_definition, arg_types, self_type, instance_type, type_vars, block_required,
|
|
138
|
+
environment, member, keywords_last, splats, block_fallback)
|
|
127
139
|
# A keyword hash only some overload takes as keywords is a plain positional `Hash` otherwise (#1737).
|
|
128
140
|
keywords_last &&= KeywordArguments.any_declares?(declared)
|
|
129
141
|
|