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
|
@@ -9,9 +9,11 @@ require_relative "../external_ancestor_resolution"
|
|
|
9
9
|
require_relative "../void_origin"
|
|
10
10
|
require_relative "../optimistic_origin"
|
|
11
11
|
require_relative "argument_distribution"
|
|
12
|
+
require_relative "interface_shape"
|
|
13
|
+
require_relative "class_keyed_block_params"
|
|
12
14
|
require_relative "overload_selector"
|
|
13
15
|
require_relative "self_substitute"
|
|
14
|
-
require_relative "../../
|
|
16
|
+
require_relative "../../typing_state"
|
|
15
17
|
|
|
16
18
|
module Rigor
|
|
17
19
|
module Inference
|
|
@@ -51,9 +53,9 @@ module Rigor
|
|
|
51
53
|
# * Method-level type parameters bind from the block return type (Slice 6 phase C) and from a
|
|
52
54
|
# positional parameter whose declared type is EXACTLY a type variable (issue #303 —
|
|
53
55
|
# `def foo[T]: (T) -> T` binds `T` from the first argument, and carries it into a generic return
|
|
54
|
-
# such as `-> Array[T]`), plus a `Range[A]` element (#834)
|
|
55
|
-
# container positions (`Array[T] arg`), a rest positional
|
|
56
|
-
# unbound and degrades to `Dynamic[Top]`.
|
|
56
|
+
# such as `-> Array[T]`), plus a `Range[A]` element (#834) and a `singleton(T)` class object (#2828).
|
|
57
|
+
# A variable reachable only through other container positions (`Array[T] arg`), a rest positional
|
|
58
|
+
# (`*T`), or a keyword parameter is still unbound and degrades to `Dynamic[Top]`.
|
|
57
59
|
# A block-return variable that a parameter also names takes the value class the argument and the
|
|
58
60
|
# block share once the call passes an argument, or stays unbound when they share none (see
|
|
59
61
|
# {compose_block_type_vars}).
|
|
@@ -72,6 +74,23 @@ module Rigor
|
|
|
72
74
|
# objects answer to methods their RBS omits (`ActionController::Base`, `Hash`, …).
|
|
73
75
|
ALLOWED_RBS_COMPLETE_ANCESTORS = ["Rigor::Plugin::Base"].freeze
|
|
74
76
|
|
|
77
|
+
# #2681 — the owners whose members every class answers to. A name only they declare is not part of an
|
|
78
|
+
# allow-listed ancestor's contract, so a `def` of it never walks for the declaration it overrides.
|
|
79
|
+
OBJECT_SURFACE_OWNERS = %w[BasicObject Object Kernel Module Class].freeze
|
|
80
|
+
|
|
81
|
+
# Per-thread memo, keyed by environment: the Set of the names an allow-listed ancestor's RBS declarations
|
|
82
|
+
# declare outside {OBJECT_SURFACE_OWNERS}. It keeps {.def_declaration} off the ancestor walk for any other
|
|
83
|
+
# `def`.
|
|
84
|
+
#
|
|
85
|
+
# ADR-129 WD1 — a run-scoped Memo: the names are a pure function of the environment the entry is keyed on.
|
|
86
|
+
RBS_COMPLETE_MEMBER_NAMES = TypingState.memo(
|
|
87
|
+
:rbs_complete_member_names,
|
|
88
|
+
run_scoped: true, audit: "rbs complete member names",
|
|
89
|
+
reason: "a WeakKeyMap keyed on the environment; the names are read from its loader " \
|
|
90
|
+
"and plugin registry alone"
|
|
91
|
+
)
|
|
92
|
+
private_constant :RBS_COMPLETE_MEMBER_NAMES
|
|
93
|
+
|
|
75
94
|
# Issue #1094 — the methods through which a value can answer Ruby's implicit `to_ary` conversion.
|
|
76
95
|
# Multiple assignment and block auto-splat convert through `rb_check_array_type`, which calls
|
|
77
96
|
# `to_ary` when defined and otherwise asks `respond_to_missing?`, dispatching to `method_missing`
|
|
@@ -219,6 +238,17 @@ module Rigor
|
|
|
219
238
|
)
|
|
220
239
|
end
|
|
221
240
|
|
|
241
|
+
# ADR-130 WD5 — interface dispatch switched off. Inside the block a `Rigor::Plugin` interface value keeps its
|
|
242
|
+
# carrier, but no call on it resolves through the interface's members: the receiver names no class, as
|
|
243
|
+
# `Dynamic[top]` names none. `rigor sig-gen` analyses here, so it writes no RBS from an interface a plugin
|
|
244
|
+
# author did not write against, and the behavioural parity gate's `:dispatch_off` mode runs here
|
|
245
|
+
# (`spec/integration/interface_parity_spec.rb`). The switch is a per-thread `TypingState` frame
|
|
246
|
+
# ({InterfaceShape.without_dispatch}), so a run under it is sequential: a pooled run raises (`PoolCoordinator`).
|
|
247
|
+
# No CLI flag or configuration key sets it.
|
|
248
|
+
def without_interface_dispatch(&) = InterfaceShape.without_dispatch(&)
|
|
249
|
+
|
|
250
|
+
def interface_dispatch? = InterfaceShape.dispatch?
|
|
251
|
+
|
|
222
252
|
# Slice 6 (Phase C sub-phase 1) probe: returns the positional block-parameter types declared by
|
|
223
253
|
# the receiving method's selected RBS overload, translated into `Rigor::Type`. Used by the
|
|
224
254
|
# StatementEvaluator to bind block parameter names before evaluating the block body.
|
|
@@ -251,10 +281,89 @@ module Rigor
|
|
|
251
281
|
)
|
|
252
282
|
end
|
|
253
283
|
|
|
284
|
+
# The RBS declaration a `def method_name` on `class_name` (`kind` side) binds its parameters from
|
|
285
|
+
# (`MethodParameterBinder`): the class's own, or nil.
|
|
286
|
+
#
|
|
287
|
+
# #2681 — when `class_name` is a Ruby-source class with no RBS, the declaration the `def` overrides on an
|
|
288
|
+
# ADR-43 allow-listed ancestor, as an empty RBS declaration of the class would inherit it. Nil whenever the
|
|
289
|
+
# call bridge would decline for a reason other than `class_name`'s own definition (no scope, no allow-listed
|
|
290
|
+
# ancestor, a nearer definer between them), and for a declaration one of {OBJECT_SURFACE_OWNERS} makes:
|
|
291
|
+
# `Kernel#load` is every class's, not that ancestor's contract. `initialize` is never bridged: a constructor
|
|
292
|
+
# does not override its superclass's contract, and a subclass routinely takes different arguments.
|
|
293
|
+
def def_declaration(environment, class_name, kind, method_name, scope)
|
|
294
|
+
direct = lookup_method_on(environment, class_name, kind, method_name)
|
|
295
|
+
return direct if direct
|
|
296
|
+
return nil if environment.nil? || scope.nil? || method_name == :initialize
|
|
297
|
+
return nil unless rbs_complete_member_name?(environment, method_name)
|
|
298
|
+
|
|
299
|
+
ancestor = allowed_rbs_complete_ancestor(environment, class_name, kind, method_name, scope,
|
|
300
|
+
overriding: true)
|
|
301
|
+
overridden = ancestor && lookup_method_on(environment, ancestor, kind, method_name)
|
|
302
|
+
overridden unless overridden.nil? || object_surface?(overridden)
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def object_surface?(method_definition)
|
|
306
|
+
OBJECT_SURFACE_OWNERS.include?(method_definition.defined_in.to_s.delete_prefix("::"))
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# A class object carries no type arguments, so a singleton declaration binds none from the receiver.
|
|
310
|
+
NO_RECEIVER_TYPE_VARS = {}.freeze
|
|
311
|
+
private_constant :NO_RECEIVER_TYPE_VARS
|
|
312
|
+
|
|
313
|
+
# Issue #2884 — the return of a call on `Singleton[class_name]` through `method_definition`, a singleton
|
|
314
|
+
# declaration {DeclaredConstructor} found on `class_name` or an ancestor, so `instance` and `self` read
|
|
315
|
+
# `class_name` whichever class wrote it. Nil when the declaration states no return
|
|
316
|
+
# (`%a{rigor:v1:inferred-return}`).
|
|
317
|
+
def declared_singleton_return(context, method_definition, class_name)
|
|
318
|
+
return nil if RbsExtended.inferred_return?(method_definition)
|
|
319
|
+
|
|
320
|
+
translate_return_type(
|
|
321
|
+
method_definition,
|
|
322
|
+
class_name: class_name, kind: :singleton, method_name: context.method_name, args: context.args || [],
|
|
323
|
+
type_vars: NO_RECEIVER_TYPE_VARS, block_type: context.block_type, environment: context.environment,
|
|
324
|
+
scope: context.scope, call_node: context.call_node
|
|
325
|
+
)
|
|
326
|
+
end
|
|
327
|
+
|
|
254
328
|
# rubocop:disable-next Metrics/ClassLength
|
|
255
329
|
class << self
|
|
256
330
|
private
|
|
257
331
|
|
|
332
|
+
# Whether some allow-listed ancestor's RBS declarations declare `method_name` outside
|
|
333
|
+
# {OBJECT_SURFACE_OWNERS}: the one-probe gate that leaves a `def` no ancestor declares, the common case
|
|
334
|
+
# in a project with no plugin class, on the cost it had before #2681. Read from the declarations, so a
|
|
335
|
+
# gem signature that fails to build reports nothing more than it did.
|
|
336
|
+
def rbs_complete_member_name?(environment, method_name)
|
|
337
|
+
rbs_complete_member_names(environment).include?(method_name)
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def rbs_complete_member_names(environment)
|
|
341
|
+
return collect_rbs_complete_member_names(environment) if MemoAudit.enabled? && MemoAudit.suspended?
|
|
342
|
+
|
|
343
|
+
table = TypingState.current.weak_key_map(RBS_COMPLETE_MEMBER_NAMES)
|
|
344
|
+
names = table[environment]
|
|
345
|
+
if names.nil?
|
|
346
|
+
return table[environment] = MemoAudit.key_audit("rbs complete member names") do
|
|
347
|
+
collect_rbs_complete_member_names(environment)
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
MemoAudit.check("rbs complete member names", names, :environment) do
|
|
352
|
+
collect_rbs_complete_member_names(environment)
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def collect_rbs_complete_member_names(environment)
|
|
357
|
+
loader = environment.rbs_loader
|
|
358
|
+
# An RBS-blind environment declares nothing, so no override takes an ancestor's parameters.
|
|
359
|
+
return Set.new.freeze if loader.nil?
|
|
360
|
+
|
|
361
|
+
ancestors = ALLOWED_RBS_COMPLETE_ANCESTORS + (environment.plugin_registry&.rbs_complete_ancestors || [])
|
|
362
|
+
ancestors.each_with_object(Set.new) do |ancestor, names|
|
|
363
|
+
names.merge(loader.declared_method_names(ancestor, skip: OBJECT_SURFACE_OWNERS))
|
|
364
|
+
end.freeze
|
|
365
|
+
end
|
|
366
|
+
|
|
258
367
|
def dispatch_for(receiver:, method_name:, args:, environment:, block_type:, self_type_override: nil, # rubocop:disable Metrics/ParameterLists
|
|
259
368
|
public_only: false, scope: nil, call_node: nil)
|
|
260
369
|
args ||= []
|
|
@@ -284,14 +393,17 @@ module Rigor
|
|
|
284
393
|
# Issue #1794 — a sequence's inherited Enumerable surface reads its Rigor-private element argument, with
|
|
285
394
|
# `self` still the sequence.
|
|
286
395
|
element = ArithmeticSequenceElements.element_receiver(receiver, method_name, environment)
|
|
287
|
-
self_type_override
|
|
396
|
+
self_type_override = sequence_self(element, receiver, self_type_override)
|
|
288
397
|
receiver = element || receiver
|
|
289
398
|
descriptor = receiver_descriptor(receiver)
|
|
290
399
|
return nil unless descriptor
|
|
291
400
|
|
|
292
401
|
class_name, kind, receiver_args = descriptor
|
|
293
|
-
|
|
294
|
-
|
|
402
|
+
if kind == :interface
|
|
403
|
+
return dispatch_interface(receiver, class_name, receiver_args, method_name, args, environment, block_type,
|
|
404
|
+
scope: scope, call_node: call_node)
|
|
405
|
+
end
|
|
406
|
+
method_definition = lookup_method(environment, class_name, kind, method_name, scope, call_node: call_node)
|
|
295
407
|
return nil unless method_definition
|
|
296
408
|
return nil if public_only && method_private?(method_definition)
|
|
297
409
|
# Issue #823 — a declaration that states the member's presence and parameters but not its
|
|
@@ -307,17 +419,11 @@ module Rigor
|
|
|
307
419
|
type_vars = build_type_vars(environment, class_name, receiver_args)
|
|
308
420
|
translate_return_type(
|
|
309
421
|
method_definition,
|
|
310
|
-
class_name: class_name,
|
|
311
|
-
|
|
312
|
-
method_name: method_name,
|
|
313
|
-
args: args,
|
|
314
|
-
type_vars: type_vars,
|
|
315
|
-
block_type: block_type,
|
|
316
|
-
environment: environment,
|
|
422
|
+
class_name: class_name, kind: kind, method_name: method_name, args: args, type_vars: type_vars,
|
|
423
|
+
block_type: block_type, environment: environment,
|
|
317
424
|
self_type_override: self_type_override ||
|
|
318
425
|
SelfSubstitute.for(receiver, receiver_args, method_name, args, block_type),
|
|
319
|
-
scope: scope,
|
|
320
|
-
call_node: call_node
|
|
426
|
+
scope: scope, call_node: call_node
|
|
321
427
|
)
|
|
322
428
|
rescue StandardError
|
|
323
429
|
# Defensive: if RBS' definition builder raises on a broken hierarchy (e.g., partially loaded
|
|
@@ -325,6 +431,55 @@ module Rigor
|
|
|
325
431
|
nil
|
|
326
432
|
end
|
|
327
433
|
|
|
434
|
+
# The `self` a call on a sequence's element keeps: the sequence (#1794), unless the caller already set one.
|
|
435
|
+
# A method rather than a two-value return, which would allocate an Array on every dispatch.
|
|
436
|
+
def sequence_self(element, receiver, self_type_override)
|
|
437
|
+
element ? (self_type_override || receiver) : self_type_override
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
# ADR-130 WD3 — a call on a `Rigor::Plugin` interface value (`Dynamic[Interface]`). A member the interface
|
|
441
|
+
# does not declare declines, so the call falls through the later tiers as one on `Dynamic[top]` does:
|
|
442
|
+
# the value's class is wider than the interface, and reporting such a call is #2686's opt-in rule. A
|
|
443
|
+
# declared member answers through the overloads that take the call's shape ({InterfaceShape}), with
|
|
444
|
+
# `self` the receiver and `instance` untyped. A block-pass that may be nil may run the call block-less, so
|
|
445
|
+
# it answers only when both shapes are taken and their returns agree.
|
|
446
|
+
# rubocop:disable-next Metrics/ParameterLists
|
|
447
|
+
def dispatch_interface(receiver, name, receiver_args, method_name, args, environment, block_type, scope:,
|
|
448
|
+
call_node:)
|
|
449
|
+
definition = interface_method(environment, name, method_name)
|
|
450
|
+
return nil if definition.nil?
|
|
451
|
+
|
|
452
|
+
type_vars = interface_type_vars(environment, name, receiver_args)
|
|
453
|
+
shape = lambda do |shape_block|
|
|
454
|
+
accepted = InterfaceShape.accepting(definition, args, block: !shape_block.nil?, call_node: call_node)
|
|
455
|
+
accepted && translate_return_type(
|
|
456
|
+
accepted, class_name: name, kind: :interface, method_name: method_name, args: args,
|
|
457
|
+
type_vars: type_vars, block_type: shape_block, environment: environment,
|
|
458
|
+
self_type_override: receiver, scope: scope, call_node: call_node
|
|
459
|
+
)
|
|
460
|
+
end
|
|
461
|
+
return shape.call(block_type) unless InterfaceShape.nilable_block_pass?(call_node, scope)
|
|
462
|
+
|
|
463
|
+
block_less = shape.call(nil)
|
|
464
|
+
block_less if block_less && block_less == shape.call(block_type || Type::Combinator.untyped)
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
# The interface's declaration of `method_name`, or nil when it declares none.
|
|
468
|
+
def interface_method(environment, name, method_name)
|
|
469
|
+
environment.rbs_loader.interface_definition(name)&.methods&.[](method_name.to_sym)
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
# The interface's own type parameters bound to the value's type arguments, as {#build_type_vars} binds a
|
|
473
|
+
# class's: a parameter left unbound (a defaulted one) reads `Dynamic[top]`.
|
|
474
|
+
def interface_type_vars(environment, name, receiver_args)
|
|
475
|
+
return NO_TYPE_VARS if receiver_args.empty?
|
|
476
|
+
|
|
477
|
+
param_names = environment.rbs_loader.interface_definition(name)&.type_params || []
|
|
478
|
+
return NO_TYPE_VARS if param_names.empty? || receiver_args.size > param_names.size
|
|
479
|
+
|
|
480
|
+
param_names.first(receiver_args.size).zip(receiver_args).to_h
|
|
481
|
+
end
|
|
482
|
+
|
|
328
483
|
# Issue #661 — `Kernel#<=>: (untyped other) -> 0?` is the IDENTITY comparison: `0` when the two
|
|
329
484
|
# are the same object, `nil` otherwise, and correct as written for a bare `Object`. Reached by
|
|
330
485
|
# inheritance it stops being a statement about the call. `1.day <=> 2.days` typed as `0?`, and a
|
|
@@ -408,10 +563,18 @@ module Rigor
|
|
|
408
563
|
# (ConstantFolding, ShapeDispatch#dispatch_integer_range) do not own.
|
|
409
564
|
["Integer", :instance, []]
|
|
410
565
|
when Type::Dynamic
|
|
411
|
-
|
|
566
|
+
facet_descriptor(receiver.static_facet)
|
|
412
567
|
end
|
|
413
568
|
end
|
|
414
569
|
|
|
570
|
+
# A `Dynamic`'s facet names what the facet names, and a `Rigor::Plugin` interface facet names the interface
|
|
571
|
+
# (ADR-130 WD3), unless dispatch is switched off, where it names nothing, as `top` does.
|
|
572
|
+
def facet_descriptor(facet)
|
|
573
|
+
return receiver_descriptor(facet) unless facet.is_a?(Type::Interface)
|
|
574
|
+
|
|
575
|
+
[facet.name, :interface, facet.type_args] if interface_dispatch?
|
|
576
|
+
end
|
|
577
|
+
|
|
415
578
|
# ADR-48 — project a `Data`/`Struct` member carrier to its tagging class (or the `Data`/`Struct`
|
|
416
579
|
# supertype) so non-member calls (`inspect`, `==`, `frozen?`, ...) resolve through RBS rather
|
|
417
580
|
# than mis-firing undefined-method. Precise member reads were already folded by DataFolding /
|
|
@@ -515,7 +678,10 @@ module Rigor
|
|
|
515
678
|
# ADR-43 WD4 — the allow-list's manifest-declared half: a loaded plugin may name its own
|
|
516
679
|
# contract classes in `rbs_complete_ancestors:` (e.g. rigor-graphql's `GraphQL::Schema::Object`),
|
|
517
680
|
# extending the engine's hard-coded seed without editing this constant.
|
|
518
|
-
|
|
681
|
+
#
|
|
682
|
+
# #2681 — `overriding: true` asks for the declaration a `def` on `class_name` overrides (its parameter
|
|
683
|
+
# binding), so `class_name`'s own definition of the name is set aside; every nearer ancestor still shadows.
|
|
684
|
+
def allowed_rbs_complete_ancestor(environment, class_name, kind, method_name, scope, overriding: false)
|
|
519
685
|
return nil if scope.nil?
|
|
520
686
|
return nil if Rigor::Reflection.rbs_class_known?(class_name, environment: environment)
|
|
521
687
|
|
|
@@ -524,15 +690,22 @@ module Rigor
|
|
|
524
690
|
# this guard the bridge would resolve e.g. `field` on `GraphQL::Schema::Object` while
|
|
525
691
|
# the runtime actually calls a user `def self.field` on an intermediate `BaseObject` —
|
|
526
692
|
# a wrong return type and a false `undefined-method`/arity reading downstream.
|
|
527
|
-
return nil if scope.discovered_method?(class_name, method_name, kind)
|
|
693
|
+
return nil if !overriding && scope.discovered_method?(class_name, method_name, kind)
|
|
528
694
|
|
|
529
695
|
registry = environment&.plugin_registry
|
|
530
696
|
each_source_ancestor_candidate(scope, class_name) do |candidate|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
697
|
+
# #2681 — the engine seed's own source (`lib/rigor/plugin/base.rb`, which this repository's plugin
|
|
698
|
+
# check discovers) is the implementation its RBS describes, not a nearer definer. A project `def` on
|
|
699
|
+
# a manifest-declared ancestor is a reopen (`class Grape::API; def self.desc`), which Ruby calls
|
|
700
|
+
# instead of the declaration, so it still shadows.
|
|
701
|
+
seed = ALLOWED_RBS_COMPLETE_ANCESTORS.include?(candidate)
|
|
702
|
+
return nil if !seed && scope.discovered_method?(candidate, method_name, kind)
|
|
703
|
+
next unless seed || registry&.rbs_complete_ancestor?(candidate)
|
|
704
|
+
|
|
705
|
+
return nil if shadowed_in_ruby_order?(scope, class_name, kind, method_name, candidate,
|
|
706
|
+
overriding: overriding)
|
|
707
|
+
|
|
708
|
+
return candidate
|
|
536
709
|
end
|
|
537
710
|
nil
|
|
538
711
|
end
|
|
@@ -546,7 +719,8 @@ module Rigor
|
|
|
546
719
|
# passed over, as the breadth-first walk always did. An unsettled chain withholds too: a mixin edge the
|
|
547
720
|
# tables cannot place (a receiver-form `Demo.include(M)` after the body, an edge in a conditional, a block or
|
|
548
721
|
# a hook) may put a definer ahead of the ancestor.
|
|
549
|
-
|
|
722
|
+
# With `overriding`, `class_name`'s own entry is the overriding `def` itself and does not shadow.
|
|
723
|
+
def shadowed_in_ruby_order?(scope, class_name, kind, method_name, ancestor, overriding: false)
|
|
550
724
|
chain = Scope::ResolutionChain.for(scope, class_name.to_s, kind, :methods)
|
|
551
725
|
return true if chain.unsettled?
|
|
552
726
|
|
|
@@ -557,6 +731,8 @@ module Rigor
|
|
|
557
731
|
return true if stop.nil?
|
|
558
732
|
|
|
559
733
|
entries.first(stop).any? do |entry|
|
|
734
|
+
next false if overriding && !entry.external? && entry.name == class_name.to_s
|
|
735
|
+
|
|
560
736
|
if entry.external?
|
|
561
737
|
rbs_declares_ahead?(scope, entry, method_name)
|
|
562
738
|
else
|
|
@@ -619,12 +795,22 @@ module Rigor
|
|
|
619
795
|
return nil if scope.nil? || kind != :instance
|
|
620
796
|
return nil if environment.nil?
|
|
621
797
|
|
|
798
|
+
if MemoAudit.enabled? && MemoAudit.suspended?
|
|
799
|
+
return compute_core_stdlib_ancestor_method(environment, class_name, method_name, scope)
|
|
800
|
+
end
|
|
801
|
+
|
|
622
802
|
memo = core_stdlib_memo(environment, scope)
|
|
623
803
|
key = [class_name.to_s, method_name.to_sym]
|
|
624
|
-
|
|
804
|
+
if memo.key?(key)
|
|
805
|
+
return MemoAudit.check("core stdlib ancestor dispatch", memo[key], key[0], key[1]) do
|
|
806
|
+
compute_core_stdlib_ancestor_method(environment, class_name, method_name, scope)
|
|
807
|
+
end
|
|
808
|
+
end
|
|
625
809
|
|
|
626
810
|
store_unless_recorded(memo, key) do
|
|
627
|
-
|
|
811
|
+
MemoAudit.key_audit("core stdlib ancestor dispatch") do
|
|
812
|
+
compute_core_stdlib_ancestor_method(environment, class_name, method_name, scope)
|
|
813
|
+
end
|
|
628
814
|
end
|
|
629
815
|
end
|
|
630
816
|
|
|
@@ -651,12 +837,23 @@ module Rigor
|
|
|
651
837
|
return nil if scope.nil? || kind != :instance
|
|
652
838
|
return nil if environment.nil?
|
|
653
839
|
|
|
840
|
+
if MemoAudit.enabled? && MemoAudit.suspended?
|
|
841
|
+
return compute_included_module_method(environment, class_name, method_name,
|
|
842
|
+
scope)
|
|
843
|
+
end
|
|
844
|
+
|
|
654
845
|
memo = mixin_ancestor_memo(environment, scope)
|
|
655
846
|
key = [class_name.to_s, method_name.to_sym]
|
|
656
|
-
|
|
847
|
+
if memo.key?(key)
|
|
848
|
+
return MemoAudit.check("mixin ancestor dispatch", memo[key], key[0], key[1]) do
|
|
849
|
+
compute_included_module_method(environment, class_name, method_name, scope)
|
|
850
|
+
end
|
|
851
|
+
end
|
|
657
852
|
|
|
658
853
|
store_unless_recorded(memo, key) do
|
|
659
|
-
|
|
854
|
+
MemoAudit.key_audit("mixin ancestor dispatch") do
|
|
855
|
+
compute_included_module_method(environment, class_name, method_name, scope)
|
|
856
|
+
end
|
|
660
857
|
end
|
|
661
858
|
end
|
|
662
859
|
|
|
@@ -684,11 +881,18 @@ module Rigor
|
|
|
684
881
|
|
|
685
882
|
# The same one-slot memo shape as {#core_stdlib_memo}; see there for why it is one slot keyed
|
|
686
883
|
# on the discovery index's identity, and what a recording run may store in it.
|
|
687
|
-
|
|
688
|
-
|
|
884
|
+
#
|
|
885
|
+
# ADR-129 WD1 — a run-scoped Memo, keyed as {#ancestor_memo_slot} says.
|
|
886
|
+
MIXIN_ANCESTOR_MEMO = TypingState.memo(
|
|
887
|
+
:mixin_ancestor_dispatch,
|
|
888
|
+
run_scoped: true, audit: "mixin ancestor dispatch",
|
|
889
|
+
reason: "one slot keyed on the identities of the discovery index and environment, and " \
|
|
890
|
+
"on whether the thread is recording; the answers read nothing else"
|
|
891
|
+
)
|
|
892
|
+
private_constant :MIXIN_ANCESTOR_MEMO
|
|
689
893
|
|
|
690
894
|
def mixin_ancestor_memo(environment, scope)
|
|
691
|
-
ancestor_memo_slot(
|
|
895
|
+
ancestor_memo_slot(MIXIN_ANCESTOR_MEMO, environment, scope)
|
|
692
896
|
end
|
|
693
897
|
|
|
694
898
|
# The declines are conjunctive, so their ORDER is free — and it is chosen so the two that walk
|
|
@@ -858,23 +1062,31 @@ module Rigor
|
|
|
858
1062
|
# the measurement. A `Scope` hands each analysed file its own discovery index, so an
|
|
859
1063
|
# identity-keyed store would pin every file's index, and every RBS definition resolved
|
|
860
1064
|
# against it, for the length of the run.
|
|
861
|
-
|
|
862
|
-
|
|
1065
|
+
#
|
|
1066
|
+
# ADR-129 WD1 — a run-scoped Memo, keyed as {#ancestor_memo_slot} says.
|
|
1067
|
+
CORE_STDLIB_ANCESTOR_MEMO = TypingState.memo(
|
|
1068
|
+
:core_stdlib_ancestor_dispatch,
|
|
1069
|
+
run_scoped: true, audit: "core stdlib ancestor dispatch",
|
|
1070
|
+
reason: "one slot keyed on the identities of the discovery index and environment, and " \
|
|
1071
|
+
"on whether the thread is recording; the answers read nothing else"
|
|
1072
|
+
)
|
|
1073
|
+
private_constant :CORE_STDLIB_ANCESTOR_MEMO
|
|
863
1074
|
|
|
864
1075
|
def core_stdlib_memo(environment, scope)
|
|
865
|
-
ancestor_memo_slot(
|
|
1076
|
+
ancestor_memo_slot(CORE_STDLIB_ANCESTOR_MEMO, environment, scope)
|
|
866
1077
|
end
|
|
867
1078
|
|
|
868
1079
|
# The slot behind both memos. Whether this thread is recording is part of its key, so a slot
|
|
869
1080
|
# filled with every answer is never read by a recording caller, which may only reuse the
|
|
870
1081
|
# answers {#store_unless_recorded} lets through.
|
|
871
|
-
def ancestor_memo_slot(
|
|
1082
|
+
def ancestor_memo_slot(memo_slot, environment, scope)
|
|
872
1083
|
discovery = scope.discovery
|
|
873
1084
|
recording = Rigor::Analysis::DependencyRecorder.recording?
|
|
874
|
-
|
|
1085
|
+
state = TypingState.current
|
|
1086
|
+
slot = state[memo_slot]
|
|
875
1087
|
unless slot && slot[0].equal?(discovery) && slot[1].equal?(environment) && slot[2] == recording
|
|
876
1088
|
slot = [discovery, environment, recording, {}]
|
|
877
|
-
|
|
1089
|
+
state[memo_slot] = slot
|
|
878
1090
|
end
|
|
879
1091
|
slot[3]
|
|
880
1092
|
end
|
|
@@ -1684,7 +1896,8 @@ module Rigor
|
|
|
1684
1896
|
#
|
|
1685
1897
|
# * only a positional parameter (required or optional) whose declared type is EXACTLY
|
|
1686
1898
|
# `RBS::Types::Variable`, plus the one container shape issue #834 admits (see
|
|
1687
|
-
# {#range_element_binding})
|
|
1899
|
+
# {#range_element_binding}) and the class-object shape issue #2828 admits ({#singleton_param_binding}) —
|
|
1900
|
+
# no general container walk, so `(Array[T]) -> T` still degrades;
|
|
1688
1901
|
# * only names the SELECTED overload declares in its own `type_params` (a class-level variable
|
|
1689
1902
|
# keeps its receiver-derived binding);
|
|
1690
1903
|
# * an existing key wins, so the receiver and the block return type both outrank an argument;
|
|
@@ -1693,7 +1906,7 @@ module Rigor
|
|
|
1693
1906
|
#
|
|
1694
1907
|
# See {#arg_binding_permitted?} for why the contribution is gated on the call site.
|
|
1695
1908
|
def compose_arg_type_vars(method_type, type_vars, args, scope:, call_node:, call_site:)
|
|
1696
|
-
bindings = arg_type_var_bindings(method_type, type_vars, args)
|
|
1909
|
+
bindings = arg_type_var_bindings(method_type, type_vars, args, scope&.environment, call_node)
|
|
1697
1910
|
return type_vars if bindings.empty?
|
|
1698
1911
|
return type_vars unless arg_binding_permitted?(scope, call_node, call_site)
|
|
1699
1912
|
|
|
@@ -1702,7 +1915,7 @@ module Rigor
|
|
|
1702
1915
|
|
|
1703
1916
|
# The candidate bindings, computed before the guard so a non-generic overload (the overwhelming
|
|
1704
1917
|
# majority) never pays for the `scope` probes.
|
|
1705
|
-
def arg_type_var_bindings(method_type, type_vars, args)
|
|
1918
|
+
def arg_type_var_bindings(method_type, type_vars, args, environment = nil, call_node = nil)
|
|
1706
1919
|
declared = declared_type_param_names(method_type)
|
|
1707
1920
|
return EMPTY_TYPE_VARS if declared.empty? || args.empty?
|
|
1708
1921
|
|
|
@@ -1714,6 +1927,11 @@ module Rigor
|
|
|
1714
1927
|
next if arg.nil?
|
|
1715
1928
|
|
|
1716
1929
|
name, bound = param_binding(param, arg, declared, type_vars)
|
|
1930
|
+
if name.nil?
|
|
1931
|
+
name, bound = singleton_param_binding(method_type, param, arg, declared, type_vars,
|
|
1932
|
+
environment)
|
|
1933
|
+
next if name && class_factory_argument?(call_node)
|
|
1934
|
+
end
|
|
1717
1935
|
next if name.nil?
|
|
1718
1936
|
|
|
1719
1937
|
bound = Type::Combinator.widen_value_pinned(bound) if upper_bounded?(method_type, name)
|
|
@@ -1749,6 +1967,80 @@ module Rigor
|
|
|
1749
1967
|
range_element_binding(param, arg, declared, type_vars)
|
|
1750
1968
|
end
|
|
1751
1969
|
|
|
1970
|
+
# Issue #2828 — a positional parameter spelled `singleton(T)` for a method-level variable `T` binds `T`
|
|
1971
|
+
# to the instance type of the class object the call passes: `Resolv::DNS#getresources: [T < Resource]
|
|
1972
|
+
# (dns_name, singleton(T)) -> Array[T]` answers `Array[Resolv::DNS::Resource::IN::MX]` for an
|
|
1973
|
+
# `IN::MX` argument. A union of class objects binds the union of their instances, as a bare `T` binds
|
|
1974
|
+
# its argument union. Every other argument — `Dynamic`, an instance, a union with a non-class member —
|
|
1975
|
+
# binds nothing, and neither does a class the declared upper bound does not provably admit: an
|
|
1976
|
+
# interface, alias or other non-class bound, or a class the environment cannot order below the bound.
|
|
1977
|
+
def singleton_param_binding(method_type, param, arg, declared, type_vars, environment)
|
|
1978
|
+
name = singleton_variable_name(param, declared, type_vars)
|
|
1979
|
+
return NO_BINDING if name.nil?
|
|
1980
|
+
|
|
1981
|
+
members = arg.is_a?(Type::Union) ? arg.members : [arg]
|
|
1982
|
+
return NO_BINDING unless members.all?(Type::Singleton)
|
|
1983
|
+
|
|
1984
|
+
within = members.all? { |member| within_upper_bound?(method_type, name, member, environment) }
|
|
1985
|
+
return NO_BINDING unless within
|
|
1986
|
+
|
|
1987
|
+
instances = members.map { |member| Type::Combinator.nominal_of(member.class_name) }
|
|
1988
|
+
[name, instances.size == 1 ? instances.first : Type::Combinator.union(*instances)]
|
|
1989
|
+
end
|
|
1990
|
+
|
|
1991
|
+
# RBS parses `singleton(T)` as a class singleton whose type name is the bare, unqualified `T`; the
|
|
1992
|
+
# parameter names a variable when that name is one the overload declares and has not yet bound.
|
|
1993
|
+
def singleton_variable_name(param, declared, type_vars)
|
|
1994
|
+
declared_type = param.type
|
|
1995
|
+
return nil unless declared_type.is_a?(RBS::Types::ClassSingleton)
|
|
1996
|
+
|
|
1997
|
+
type_name = declared_type.name
|
|
1998
|
+
return nil unless type_name.namespace.empty?
|
|
1999
|
+
|
|
2000
|
+
name = type_name.name
|
|
2001
|
+
return nil unless declared.include?(name)
|
|
2002
|
+
return nil if type_vars.key?(name)
|
|
2003
|
+
|
|
2004
|
+
name
|
|
2005
|
+
end
|
|
2006
|
+
|
|
2007
|
+
# Whether the call passes, as any positional argument, a class built by `Class.new`, `Struct.new` or
|
|
2008
|
+
# `Data.define` with a block. The engine types `Class.new(Widget) { def extra = 1 }` as `singleton(Widget)`,
|
|
2009
|
+
# so binding `T` from it would read the instance as a plain `Widget` and report `extra` as undefined. The
|
|
2010
|
+
# argument node is visible here; a local the class was assigned to is not, and still binds.
|
|
2011
|
+
def class_factory_argument?(call_node)
|
|
2012
|
+
arguments = call_node.respond_to?(:arguments) ? call_node.arguments&.arguments : nil
|
|
2013
|
+
return false if arguments.nil?
|
|
2014
|
+
|
|
2015
|
+
arguments.any? { |argument| class_factory_call?(argument) }
|
|
2016
|
+
end
|
|
2017
|
+
|
|
2018
|
+
CLASS_FACTORIES = { new: %w[Class Struct].freeze, define: %w[Data].freeze }.freeze
|
|
2019
|
+
private_constant :CLASS_FACTORIES
|
|
2020
|
+
|
|
2021
|
+
def class_factory_call?(node)
|
|
2022
|
+
return false unless node.is_a?(Prism::CallNode) && node.block.is_a?(Prism::BlockNode)
|
|
2023
|
+
|
|
2024
|
+
receivers = CLASS_FACTORIES[node.name]
|
|
2025
|
+
return false if receivers.nil?
|
|
2026
|
+
|
|
2027
|
+
receiver = node.receiver
|
|
2028
|
+
name = case receiver
|
|
2029
|
+
when Prism::ConstantReadNode then receiver.name.to_s
|
|
2030
|
+
when Prism::ConstantPathNode then receiver.parent.nil? ? receiver.name.to_s : nil
|
|
2031
|
+
end
|
|
2032
|
+
receivers.include?(name)
|
|
2033
|
+
end
|
|
2034
|
+
|
|
2035
|
+
def within_upper_bound?(method_type, name, singleton, environment)
|
|
2036
|
+
type_param = method_type.type_params.find { |tp| tp.name == name }
|
|
2037
|
+
bound = type_param.respond_to?(:upper_bound_type) ? type_param.upper_bound_type : type_param.upper_bound
|
|
2038
|
+
return true if bound.nil?
|
|
2039
|
+
return false unless bound.is_a?(RBS::Types::ClassInstance) && environment
|
|
2040
|
+
|
|
2041
|
+
%i[equal subclass].include?(environment.class_ordering(singleton.class_name, bound.name.to_s))
|
|
2042
|
+
end
|
|
2043
|
+
|
|
1752
2044
|
def declared_type_param_names(method_type)
|
|
1753
2045
|
params = method_type.respond_to?(:type_params) ? method_type.type_params : nil
|
|
1754
2046
|
return EMPTY_TYPE_PARAM_NAMES if params.nil? || params.empty?
|
|
@@ -1824,7 +2116,7 @@ module Rigor
|
|
|
1824
2116
|
# dress up an absence of evidence as an inference. The variable stays unbound and degrades as it
|
|
1825
2117
|
# did before, which is the same value anyway.
|
|
1826
2118
|
def no_static_evidence?(arg)
|
|
1827
|
-
|
|
2119
|
+
Type::Combinator.untyped?(arg)
|
|
1828
2120
|
end
|
|
1829
2121
|
|
|
1830
2122
|
# Issue #303 — the FP guard on the argument-position binding.
|
|
@@ -1949,13 +2241,11 @@ module Rigor
|
|
|
1949
2241
|
|
|
1950
2242
|
def probe_block_param_types(receiver:, method_name:, args:, environment:, scope: nil, call_node: nil)
|
|
1951
2243
|
args ||= []
|
|
1952
|
-
keywords_last = ArgumentNodes.keyword_hash_last?(call_node, args)
|
|
1953
|
-
splat = splat_indices(call_node, args)
|
|
1954
2244
|
case receiver
|
|
1955
2245
|
when Type::Union
|
|
1956
|
-
probe_block_param_types_union(receiver, method_name, args, environment, scope,
|
|
2246
|
+
probe_block_param_types_union(receiver, method_name, args, environment, scope, call_node)
|
|
1957
2247
|
else
|
|
1958
|
-
probe_block_param_types_one(receiver, method_name, args, environment, scope,
|
|
2248
|
+
probe_block_param_types_one(receiver, method_name, args, environment, scope, call_node)
|
|
1959
2249
|
end
|
|
1960
2250
|
end
|
|
1961
2251
|
|
|
@@ -1963,9 +2253,9 @@ module Rigor
|
|
|
1963
2253
|
# member resolves the same arity and types (otherwise the call sites would have to thread
|
|
1964
2254
|
# per-member binders, which the slice does not support yet). Mismatches degrade to the empty
|
|
1965
2255
|
# array so the binder defaults all params to Dynamic[Top].
|
|
1966
|
-
def probe_block_param_types_union(receiver, method_name, args, environment, scope,
|
|
2256
|
+
def probe_block_param_types_union(receiver, method_name, args, environment, scope, call_node)
|
|
1967
2257
|
results = receiver.members.map do |member|
|
|
1968
|
-
probe_block_param_types_one(member, method_name, args, environment, scope,
|
|
2258
|
+
probe_block_param_types_one(member, method_name, args, environment, scope, call_node)
|
|
1969
2259
|
end
|
|
1970
2260
|
return [] if results.empty?
|
|
1971
2261
|
return [] unless results.all? { |r| r == results.first }
|
|
@@ -1973,44 +2263,52 @@ module Rigor
|
|
|
1973
2263
|
results.first
|
|
1974
2264
|
end
|
|
1975
2265
|
|
|
1976
|
-
def probe_block_param_types_one(receiver, method_name, args, environment, scope,
|
|
2266
|
+
def probe_block_param_types_one(receiver, method_name, args, environment, scope, call_node)
|
|
1977
2267
|
receiver = ArithmeticSequenceElements.element_receiver(receiver, method_name, environment, block: true) ||
|
|
1978
2268
|
receiver
|
|
1979
2269
|
descriptor = receiver_descriptor(receiver)
|
|
1980
2270
|
return [] unless descriptor
|
|
1981
2271
|
|
|
1982
2272
|
class_name, kind, receiver_args = descriptor
|
|
1983
|
-
method_definition =
|
|
2273
|
+
method_definition = probe_definition(environment, class_name, kind, method_name, args, scope, call_node)
|
|
1984
2274
|
return [] unless method_definition
|
|
1985
2275
|
|
|
1986
|
-
type_vars =
|
|
1987
|
-
|
|
2276
|
+
type_vars =
|
|
2277
|
+
if kind == :interface
|
|
2278
|
+
interface_type_vars(environment, class_name, receiver_args)
|
|
2279
|
+
else
|
|
2280
|
+
build_type_vars(environment, class_name, receiver_args)
|
|
2281
|
+
end
|
|
2282
|
+
params = extract_block_param_types(
|
|
1988
2283
|
method_definition,
|
|
1989
|
-
class_name: class_name,
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
environment: environment,
|
|
1994
|
-
receiver: receiver,
|
|
1995
|
-
receiver_args: receiver_args,
|
|
1996
|
-
method_name: method_name,
|
|
1997
|
-
keywords_last: keywords_last, positional_splat: splat
|
|
2284
|
+
class_name: class_name, kind: kind, args: args, type_vars: type_vars, environment: environment,
|
|
2285
|
+
receiver: receiver, receiver_args: receiver_args, method_name: method_name,
|
|
2286
|
+
keywords_last: ArgumentNodes.keyword_hash_last?(call_node, args),
|
|
2287
|
+
positional_splat: splat_indices(call_node, args), scope: scope, call_node: call_node
|
|
1998
2288
|
)
|
|
2289
|
+
ClassKeyedBlockParams.rebind(method_definition, method_name, args, params)
|
|
1999
2290
|
rescue StandardError
|
|
2000
2291
|
[]
|
|
2001
2292
|
end
|
|
2002
2293
|
|
|
2294
|
+
# The declaration the block-parameter probe reads. On an interface value a block literal binds its parameters
|
|
2295
|
+
# through the overloads that take the call's shape, as the call's return does (ADR-130 WD3,
|
|
2296
|
+
# {#dispatch_interface}).
|
|
2297
|
+
def probe_definition(environment, class_name, kind, method_name, args, scope, call_node)
|
|
2298
|
+
return lookup_method(environment, class_name, kind, method_name, scope) unless kind == :interface
|
|
2299
|
+
|
|
2300
|
+
declared = interface_method(environment, class_name, method_name)
|
|
2301
|
+
declared && InterfaceShape.accepting(declared, args, block: true, call_node: call_node)
|
|
2302
|
+
end
|
|
2303
|
+
|
|
2003
2304
|
# rubocop:disable Metrics/ParameterLists
|
|
2004
2305
|
def extract_block_param_types(method_definition, class_name:, kind:, args:, type_vars:,
|
|
2005
2306
|
environment: nil, receiver: nil, receiver_args: [],
|
|
2006
|
-
method_name: nil, keywords_last: false, positional_splat: nil
|
|
2307
|
+
method_name: nil, keywords_last: false, positional_splat: nil,
|
|
2308
|
+
scope: nil, call_node: nil)
|
|
2007
2309
|
# rubocop:enable Metrics/ParameterLists
|
|
2008
2310
|
instance_type = Type::Combinator.nominal_of(class_name)
|
|
2009
|
-
self_type =
|
|
2010
|
-
case kind
|
|
2011
|
-
when :singleton then Type::Combinator.singleton_of(class_name)
|
|
2012
|
-
else instance_type
|
|
2013
|
-
end
|
|
2311
|
+
self_type = block_self_type(kind, class_name, instance_type, receiver)
|
|
2014
2312
|
|
|
2015
2313
|
# Issue #1130 — a block parameter that receives `self` (`Object#tap` yields it) must see the
|
|
2016
2314
|
# receiver's type arguments through the SAME {SelfSubstitute} verdict the return path applies
|
|
@@ -2052,9 +2350,53 @@ module Rigor
|
|
|
2052
2350
|
)
|
|
2053
2351
|
return [] unless method_type
|
|
2054
2352
|
|
|
2353
|
+
type_vars = compose_singleton_arg_type_vars(method_type, type_vars, args, environment,
|
|
2354
|
+
scope: scope, call_node: call_node,
|
|
2355
|
+
call_site: [class_name, method_name, kind])
|
|
2055
2356
|
block_params_of(method_type, self_type, instance_type, type_vars, environment) || []
|
|
2056
2357
|
end
|
|
2057
2358
|
|
|
2359
|
+
# Issue #2828 — the block-parameter twin of {#compose_arg_type_vars}, for the `singleton(T)` shape alone
|
|
2360
|
+
# ({#singleton_param_binding}): `Resolv::DNS#each_resource: [T < Resource] (dns_name, singleton(T))
|
|
2361
|
+
# { (T) -> void } -> void` yields the typeclass's instances. A bare `T` parameter stays out of the block
|
|
2362
|
+
# path: it binds the argument's own (possibly value-pinned) type, and `each_with_object([])`'s memo
|
|
2363
|
+
# would read as the empty tuple the block then appends to.
|
|
2364
|
+
def compose_singleton_arg_type_vars(method_type, type_vars, args, environment, scope:, call_node:, call_site:)
|
|
2365
|
+
declared = declared_type_param_names(method_type)
|
|
2366
|
+
return type_vars if declared.empty? || args.empty?
|
|
2367
|
+
|
|
2368
|
+
fun = method_type.type
|
|
2369
|
+
return type_vars unless fun.respond_to?(:required_positionals)
|
|
2370
|
+
|
|
2371
|
+
bindings = singleton_bindings(method_type, fun, args, declared, type_vars, environment)
|
|
2372
|
+
return type_vars if bindings.empty? || class_factory_argument?(call_node)
|
|
2373
|
+
return type_vars unless arg_binding_permitted?(scope, call_node, call_site)
|
|
2374
|
+
|
|
2375
|
+
type_vars.merge(bindings)
|
|
2376
|
+
end
|
|
2377
|
+
|
|
2378
|
+
def singleton_bindings(method_type, fun, args, declared, type_vars, environment)
|
|
2379
|
+
positionals = fun.required_positionals + fun.optional_positionals
|
|
2380
|
+
positionals.zip(args).each_with_object({}) do |(param, arg), bindings|
|
|
2381
|
+
next if arg.nil?
|
|
2382
|
+
|
|
2383
|
+
name, bound = singleton_param_binding(method_type, param, arg, declared, type_vars, environment)
|
|
2384
|
+
next if name.nil?
|
|
2385
|
+
|
|
2386
|
+
bindings[name] = bindings.key?(name) ? Type::Combinator.union(bindings[name], bound) : bound
|
|
2387
|
+
end
|
|
2388
|
+
end
|
|
2389
|
+
|
|
2390
|
+
# What RBS `self` reads in a block parameter of the member: the class's instance or singleton, and inside an
|
|
2391
|
+
# interface member the receiver (ADR-130 WD1).
|
|
2392
|
+
def block_self_type(kind, class_name, instance_type, receiver)
|
|
2393
|
+
case kind
|
|
2394
|
+
when :singleton then Type::Combinator.singleton_of(class_name)
|
|
2395
|
+
when :interface then receiver
|
|
2396
|
+
else instance_type
|
|
2397
|
+
end
|
|
2398
|
+
end
|
|
2399
|
+
|
|
2058
2400
|
# The block parameters every overload in `matches` declares alike, or no information (`[]`).
|
|
2059
2401
|
def agreed_block_params(matches, self_type, instance_type, type_vars, environment)
|
|
2060
2402
|
answers = matches.uniq.map do |method_type|
|