rigortype 0.1.3 → 0.1.4
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 +125 -31
- data/lib/rigor/analysis/check_rules.rb +10 -18
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +75 -0
- data/lib/rigor/analysis/dependency_source_inference/builder.rb +47 -21
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/index.rb +32 -3
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference.rb +1 -0
- data/lib/rigor/analysis/diagnostic.rb +0 -2
- data/lib/rigor/analysis/fact_store.rb +11 -3
- data/lib/rigor/analysis/rule_catalog.rb +2 -2
- data/lib/rigor/analysis/runner.rb +114 -3
- data/lib/rigor/builtins/imported_refinements.rb +360 -55
- data/lib/rigor/cache/descriptor.rb +1 -1
- data/lib/rigor/cache/store.rb +1 -1
- data/lib/rigor/cli/diff_command.rb +1 -1
- data/lib/rigor/cli/sig_gen_command.rb +173 -0
- data/lib/rigor/cli/type_of_command.rb +1 -1
- data/lib/rigor/cli/type_scan_renderer.rb +1 -1
- data/lib/rigor/cli/type_scan_report.rb +2 -2
- data/lib/rigor/cli.rb +9 -1
- data/lib/rigor/configuration/dependencies.rb +2 -2
- data/lib/rigor/configuration.rb +2 -2
- data/lib/rigor/environment.rb +35 -4
- data/lib/rigor/flow_contribution/conflict.rb +2 -2
- data/lib/rigor/flow_contribution/element.rb +1 -1
- data/lib/rigor/flow_contribution/fact.rb +1 -1
- data/lib/rigor/flow_contribution/merge_result.rb +1 -1
- data/lib/rigor/flow_contribution/merger.rb +3 -3
- data/lib/rigor/flow_contribution.rb +2 -2
- data/lib/rigor/inference/block_parameter_binder.rb +0 -2
- data/lib/rigor/inference/coverage_scanner.rb +1 -1
- data/lib/rigor/inference/expression_typer.rb +67 -11
- data/lib/rigor/inference/fallback.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +3 -5
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +0 -12
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +1 -3
- data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +118 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +6 -11
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +27 -11
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +0 -4
- data/lib/rigor/inference/method_dispatcher.rb +146 -2
- data/lib/rigor/inference/method_parameter_binder.rb +1 -3
- data/lib/rigor/inference/narrowing.rb +2 -4
- data/lib/rigor/inference/rbs_type_translator.rb +0 -2
- data/lib/rigor/inference/scope_indexer.rb +14 -9
- data/lib/rigor/inference/statement_evaluator.rb +7 -7
- data/lib/rigor/plugin/io_boundary.rb +0 -2
- data/lib/rigor/plugin/loader.rb +2 -2
- data/lib/rigor/plugin/manifest.rb +30 -9
- data/lib/rigor/plugin/registry.rb +11 -0
- data/lib/rigor/plugin/services.rb +1 -1
- data/lib/rigor/plugin/type_node_resolver.rb +52 -0
- data/lib/rigor/plugin.rb +1 -0
- data/lib/rigor/rbs_extended/reporter.rb +91 -0
- data/lib/rigor/rbs_extended.rb +131 -32
- data/lib/rigor/scope.rb +25 -8
- data/lib/rigor/sig_gen/classification.rb +36 -0
- data/lib/rigor/sig_gen/generator.rb +1048 -0
- data/lib/rigor/sig_gen/layout_index.rb +108 -0
- data/lib/rigor/sig_gen/method_candidate.rb +62 -0
- data/lib/rigor/sig_gen/observation_collector.rb +391 -0
- data/lib/rigor/sig_gen/observed_call.rb +62 -0
- data/lib/rigor/sig_gen/path_mapper.rb +116 -0
- data/lib/rigor/sig_gen/renderer.rb +157 -0
- data/lib/rigor/sig_gen/type_elaborator.rb +92 -0
- data/lib/rigor/sig_gen/write_result.rb +48 -0
- data/lib/rigor/sig_gen/writer.rb +530 -0
- data/lib/rigor/sig_gen.rb +25 -0
- data/lib/rigor/type/bound_method.rb +79 -0
- data/lib/rigor/type/combinator.rb +195 -2
- data/lib/rigor/type/constant.rb +13 -0
- data/lib/rigor/type/hash_shape.rb +0 -2
- data/lib/rigor/type/union.rb +20 -1
- data/lib/rigor/type.rb +1 -0
- data/lib/rigor/type_node/generic.rb +62 -0
- data/lib/rigor/type_node/identifier.rb +30 -0
- data/lib/rigor/type_node/indexed_access.rb +41 -0
- data/lib/rigor/type_node/integer_literal.rb +29 -0
- data/lib/rigor/type_node/name_scope.rb +52 -0
- data/lib/rigor/type_node/resolver_chain.rb +56 -0
- data/lib/rigor/type_node/string_literal.rb +29 -0
- data/lib/rigor/type_node/symbol_literal.rb +28 -0
- data/lib/rigor/type_node/union.rb +42 -0
- data/lib/rigor/type_node.rb +29 -0
- data/lib/rigor/version.rb +1 -1
- data/lib/rigor.rb +2 -0
- data/sig/rigor/analysis/check_rules/always_truthy_condition_collector.rbs +10 -0
- data/sig/rigor/analysis/check_rules/dead_assignment_collector.rbs +10 -0
- data/sig/rigor/analysis/dependency_source_inference/gem_resolver.rbs +25 -0
- data/sig/rigor/analysis/dependency_source_inference/index.rbs +9 -0
- data/sig/rigor/cli/diff_command.rbs +4 -0
- data/sig/rigor/cli/explain_command.rbs +4 -0
- data/sig/rigor/cli/sig_gen_command.rbs +4 -0
- data/sig/rigor/cli/type_scan_command.rbs +3 -0
- data/sig/rigor/environment.rbs +5 -2
- data/sig/rigor/inference/builtins/method_catalog.rbs +4 -0
- data/sig/rigor/inference/builtins/numeric_catalog.rbs +3 -0
- data/sig/rigor/inference/builtins.rbs +2 -0
- data/sig/rigor/plugin/access_denied_error.rbs +3 -0
- data/sig/rigor/plugin/base.rbs +6 -0
- data/sig/rigor/plugin/fact_store.rbs +11 -0
- data/sig/rigor/plugin/io_boundary.rbs +4 -0
- data/sig/rigor/plugin/load_error.rbs +6 -0
- data/sig/rigor/plugin/loader.rbs +20 -0
- data/sig/rigor/plugin/manifest.rbs +9 -0
- data/sig/rigor/plugin/registry.rbs +3 -0
- data/sig/rigor/plugin/services.rbs +3 -0
- data/sig/rigor/plugin/trust_policy.rbs +4 -0
- data/sig/rigor/plugin/type_node_resolver.rbs +3 -0
- data/sig/rigor/plugin.rbs +8 -0
- data/sig/rigor/scope.rbs +4 -2
- data/sig/rigor/type.rbs +28 -6
- metadata +52 -1
|
@@ -37,7 +37,7 @@ module Rigor
|
|
|
37
37
|
# tuple — element-wise block re-evaluation against
|
|
38
38
|
# `Constant<Array>` receivers (the `map` / `filter_map` /
|
|
39
39
|
# `flat_map` precision tier) is reserved for a later slice.
|
|
40
|
-
module BlockFolding
|
|
40
|
+
module BlockFolding
|
|
41
41
|
module_function
|
|
42
42
|
|
|
43
43
|
FILTER_KEEP_ON_TRUTHY = Set[:select, :filter, :take_while].freeze
|
|
@@ -69,7 +69,6 @@ module Rigor
|
|
|
69
69
|
# the call's block. `nil` means "no block at the call site"
|
|
70
70
|
# and disqualifies every rule here.
|
|
71
71
|
# @return [Rigor::Type, nil]
|
|
72
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
73
72
|
def try_fold(receiver:, method_name:, args:, block_type:)
|
|
74
73
|
return nil if receiver.nil? || block_type.nil?
|
|
75
74
|
|
|
@@ -86,7 +85,6 @@ module Rigor
|
|
|
86
85
|
fold_count(receiver, truthiness, args)
|
|
87
86
|
end
|
|
88
87
|
end
|
|
89
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
90
88
|
|
|
91
89
|
def filter_method?(method_name)
|
|
92
90
|
FILTER_KEEP_ON_TRUTHY.include?(method_name) ||
|
|
@@ -154,7 +152,7 @@ module Rigor
|
|
|
154
152
|
end
|
|
155
153
|
|
|
156
154
|
# @return [:always_true, :always_false, :bool, nil]
|
|
157
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
155
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
|
158
156
|
def predicate_decision(method_name, truthiness, emptiness)
|
|
159
157
|
case method_name
|
|
160
158
|
when :all?
|
|
@@ -177,7 +175,7 @@ module Rigor
|
|
|
177
175
|
:bool
|
|
178
176
|
end
|
|
179
177
|
end
|
|
180
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
|
178
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
|
181
179
|
|
|
182
180
|
def bool_union
|
|
183
181
|
Type::Combinator.union(
|
|
@@ -149,7 +149,6 @@ module Rigor
|
|
|
149
149
|
# and runs the format. Symbol keys are kept as
|
|
150
150
|
# Symbols (matching Ruby's `%{key}` resolution).
|
|
151
151
|
# Anything else declines so the RBS tier widens.
|
|
152
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
153
152
|
def try_fold_string_format(receiver, method_name, args)
|
|
154
153
|
return nil unless method_name == :%
|
|
155
154
|
return nil unless args.size == 1
|
|
@@ -166,7 +165,6 @@ module Rigor
|
|
|
166
165
|
rescue StandardError
|
|
167
166
|
nil
|
|
168
167
|
end
|
|
169
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
|
170
168
|
|
|
171
169
|
def format_argument_value(arg)
|
|
172
170
|
case arg
|
|
@@ -275,7 +273,6 @@ module Rigor
|
|
|
275
273
|
[result]
|
|
276
274
|
end
|
|
277
275
|
|
|
278
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
279
276
|
def try_fold_unary_set(receiver_values, method_name)
|
|
280
277
|
range_lift = try_fold_range_constant_unary(receiver_values, method_name)
|
|
281
278
|
return range_lift if range_lift
|
|
@@ -299,8 +296,6 @@ module Rigor
|
|
|
299
296
|
end
|
|
300
297
|
build_constant_type(results, source: receiver_values)
|
|
301
298
|
end
|
|
302
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
|
303
|
-
|
|
304
299
|
# v0.0.7 — `Constant<Range>#to_a` and the no-arg
|
|
305
300
|
# `first` / `last` / `min` / `max` short-circuit through a
|
|
306
301
|
# Range-specific arm that catalog dispatch cannot reach:
|
|
@@ -353,7 +348,6 @@ module Rigor
|
|
|
353
348
|
Type::Combinator.constant_of(edge == :first ? values.first : values.last)
|
|
354
349
|
end
|
|
355
350
|
|
|
356
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
357
351
|
def try_fold_binary_set(receiver_values, method_name, arg_values)
|
|
358
352
|
string_lift = try_fold_string_array_binary(receiver_values, method_name, arg_values)
|
|
359
353
|
return string_lift if string_lift
|
|
@@ -369,8 +363,6 @@ module Rigor
|
|
|
369
363
|
end
|
|
370
364
|
build_constant_type(results, source: receiver_values + arg_values)
|
|
371
365
|
end
|
|
372
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
373
|
-
|
|
374
366
|
# v0.0.7 — `Constant<String>#chars` / `bytes` / `lines` /
|
|
375
367
|
# `split` (no-arg) return a Ruby Array of foldable
|
|
376
368
|
# scalars; `foldable_constant_value?` rejects Array
|
|
@@ -425,7 +417,6 @@ module Rigor
|
|
|
425
417
|
nil
|
|
426
418
|
end
|
|
427
419
|
|
|
428
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
429
420
|
def try_fold_pathname_binary(receiver_values, method_name, arg_values)
|
|
430
421
|
return nil unless PATHNAME_PURE_BINARY.include?(method_name)
|
|
431
422
|
return nil unless receiver_values.size == 1 && arg_values.size == 1
|
|
@@ -442,7 +433,6 @@ module Rigor
|
|
|
442
433
|
rescue StandardError
|
|
443
434
|
nil
|
|
444
435
|
end
|
|
445
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
446
436
|
|
|
447
437
|
def try_fold_string_array_unary(receiver_values, method_name)
|
|
448
438
|
return nil unless STRING_ARRAY_UNARY_METHODS.include?(method_name)
|
|
@@ -459,7 +449,6 @@ module Rigor
|
|
|
459
449
|
# `Constant<String>#split(arg)` / `#scan(arg)` — lift the
|
|
460
450
|
# Array result to a Tuple when both sides are statically
|
|
461
451
|
# known and the cardinality fits.
|
|
462
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
463
452
|
def try_fold_string_array_binary(receiver_values, method_name, arg_values)
|
|
464
453
|
return nil unless STRING_ARRAY_BINARY_METHODS.include?(method_name)
|
|
465
454
|
return nil unless receiver_values.size == 1 && arg_values.size == 1
|
|
@@ -473,7 +462,6 @@ module Rigor
|
|
|
473
462
|
rescue StandardError
|
|
474
463
|
nil
|
|
475
464
|
end
|
|
476
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
|
477
465
|
|
|
478
466
|
def lift_array_result(result)
|
|
479
467
|
return nil unless result.is_a?(Array)
|
|
@@ -27,12 +27,11 @@ module Rigor
|
|
|
27
27
|
# - `a.downto(b) { |i| … }` yields the same domain `[b, a]`,
|
|
28
28
|
# just iterated in reverse. Lower bound from the
|
|
29
29
|
# argument, upper bound from the receiver.
|
|
30
|
-
module IteratorDispatch
|
|
30
|
+
module IteratorDispatch
|
|
31
31
|
module_function
|
|
32
32
|
|
|
33
33
|
# @return [Array<Rigor::Type>, nil] block-param types, or
|
|
34
34
|
# nil to fall through to the next tier.
|
|
35
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
36
35
|
def block_param_types(receiver:, method_name:, args:)
|
|
37
36
|
case method_name
|
|
38
37
|
when :times then times_block_params(receiver)
|
|
@@ -45,7 +44,6 @@ module Rigor
|
|
|
45
44
|
when :each_slice, :each_cons then slice_block_params(receiver)
|
|
46
45
|
end
|
|
47
46
|
end
|
|
48
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
|
49
47
|
|
|
50
48
|
def times_block_params(receiver)
|
|
51
49
|
return nil unless integer_rooted?(receiver)
|
|
@@ -74,7 +74,7 @@ module Rigor
|
|
|
74
74
|
private_constant :CONCAT_METHODS, :FORMAT_METHODS,
|
|
75
75
|
:LITERAL_PRESERVING_METHODS, :WIDTH_PADDING_METHODS
|
|
76
76
|
|
|
77
|
-
def try_dispatch(receiver:, method_name:, args:, **)
|
|
77
|
+
def try_dispatch(receiver:, method_name:, args:, **)
|
|
78
78
|
return fold_array_join(receiver, args) if method_name == :join
|
|
79
79
|
return fold_format(args) if FORMAT_METHODS.include?(method_name)
|
|
80
80
|
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../type"
|
|
4
|
+
|
|
5
|
+
module Rigor
|
|
6
|
+
module Inference
|
|
7
|
+
module MethodDispatcher
|
|
8
|
+
# `Method` (and friends) precision tier.
|
|
9
|
+
#
|
|
10
|
+
# Two folds make a `Method` carrier round-trip with its
|
|
11
|
+
# binding visible:
|
|
12
|
+
#
|
|
13
|
+
# 1. **Forward** — `<receiver>.method(:sym)` (or
|
|
14
|
+
# `.method("sym")`) lifts to {Type::BoundMethod}
|
|
15
|
+
# carrying the receiver type AND the resolved Symbol.
|
|
16
|
+
# Calling with a non-literal symbol-shaped argument
|
|
17
|
+
# declines so the RBS tier still answers
|
|
18
|
+
# `Nominal[Method]`.
|
|
19
|
+
# 2. **Backward** — `Type::BoundMethod#call(...)` /
|
|
20
|
+
# `#()` (Prism lowers `.()` into a CallNode whose
|
|
21
|
+
# `name` is `:call`) / `#[](...)` substitutes the
|
|
22
|
+
# bound `(receiver_type, method_name)` and recurses
|
|
23
|
+
# back into `MethodDispatcher.dispatch`. The
|
|
24
|
+
# re-entrant call lets the substituted dispatch
|
|
25
|
+
# consume every tier the original call site would
|
|
26
|
+
# have — constant folding, shape dispatch, RBS,
|
|
27
|
+
# plugin contributions, etc. The original block_type
|
|
28
|
+
# / environment / call_node / scope are threaded
|
|
29
|
+
# through unchanged so capture-sensitive tiers (the
|
|
30
|
+
# block fold) keep working.
|
|
31
|
+
#
|
|
32
|
+
# Lives ABOVE the standard precision-tier chain so the
|
|
33
|
+
# RBS tier never sees a `BoundMethod` receiver — `Method`
|
|
34
|
+
# erasure means RBS would otherwise return
|
|
35
|
+
# `Method#call: (*untyped) -> untyped`, which is exactly
|
|
36
|
+
# the precision loss the carrier exists to avoid.
|
|
37
|
+
module MethodFolding
|
|
38
|
+
module_function
|
|
39
|
+
|
|
40
|
+
# Forward fold. Returns a {Type::BoundMethod} when the
|
|
41
|
+
# call shape is `<receiver>.method(:name)` /
|
|
42
|
+
# `.method("name")` with a precisely-known Symbol /
|
|
43
|
+
# String argument. Declines on every other shape so
|
|
44
|
+
# the RBS tier still answers `Method` for non-folding
|
|
45
|
+
# cases.
|
|
46
|
+
#
|
|
47
|
+
# @param receiver [Rigor::Type] caller's receiver
|
|
48
|
+
# @param method_name [Symbol] the method being
|
|
49
|
+
# dispatched on `receiver` — only `:method` triggers
|
|
50
|
+
# the fold.
|
|
51
|
+
# @param args [Array<Rigor::Type>] caller's argument
|
|
52
|
+
# types in order. Only the single-argument case
|
|
53
|
+
# matches; other arities decline.
|
|
54
|
+
def try_forward(receiver:, method_name:, args:)
|
|
55
|
+
return nil unless method_name == :method
|
|
56
|
+
return nil if args.size != 1
|
|
57
|
+
|
|
58
|
+
bound_name = symbol_name_of(args.first)
|
|
59
|
+
return nil if bound_name.nil?
|
|
60
|
+
|
|
61
|
+
Type::Combinator.bound_method_of(receiver, bound_name)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Backward fold. Recurses into `MethodDispatcher.dispatch`
|
|
65
|
+
# with the bound `(receiver_type, method_name)`. The
|
|
66
|
+
# `block_type` / `environment` / `call_node` / `scope`
|
|
67
|
+
# are forwarded so every downstream tier (constant
|
|
68
|
+
# folding, shape dispatch, plugin contributions, …)
|
|
69
|
+
# keeps the original call site's context. Returns
|
|
70
|
+
# `Dynamic[top]` rather than `nil` when the recursive
|
|
71
|
+
# dispatch declines so the call site still ends in a
|
|
72
|
+
# well-defined type (the gradual-safety net mirrors
|
|
73
|
+
# the engine's "BoundMethod erases to `Method`,
|
|
74
|
+
# `Method#call: (*untyped) -> untyped`" RBS fallback).
|
|
75
|
+
def try_backward(receiver:, method_name:, args:, block_type:, environment:, call_node:, scope:)
|
|
76
|
+
return nil unless receiver.is_a?(Type::BoundMethod)
|
|
77
|
+
return nil unless backward_method?(method_name)
|
|
78
|
+
|
|
79
|
+
MethodDispatcher.dispatch(
|
|
80
|
+
receiver_type: receiver.receiver_type,
|
|
81
|
+
method_name: receiver.method_name,
|
|
82
|
+
arg_types: args,
|
|
83
|
+
block_type: block_type,
|
|
84
|
+
environment: environment,
|
|
85
|
+
call_node: call_node,
|
|
86
|
+
scope: scope
|
|
87
|
+
) || Type::Combinator.untyped
|
|
88
|
+
end
|
|
89
|
+
# `Method#call` / `Method#()` and `Method#[]` are the
|
|
90
|
+
# invocation entry points on the `Method` API; the
|
|
91
|
+
# alias `===` is also `call` semantically but is more
|
|
92
|
+
# commonly used as a case-equality predicate, so we
|
|
93
|
+
# do NOT fold through it (the case/when narrowing path
|
|
94
|
+
# already special-cases `===` for branch typing).
|
|
95
|
+
BACKWARD_METHOD_NAMES = %i[call []].freeze
|
|
96
|
+
private_constant :BACKWARD_METHOD_NAMES
|
|
97
|
+
|
|
98
|
+
def backward_method?(method_name)
|
|
99
|
+
BACKWARD_METHOD_NAMES.include?(method_name)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# `Object#method` accepts both Symbol and String at
|
|
103
|
+
# runtime (the latter coerced via `to_sym`). The
|
|
104
|
+
# `Constant<String>` form is rare in production code
|
|
105
|
+
# but cheap to support and matches Ruby's documented
|
|
106
|
+
# contract.
|
|
107
|
+
def symbol_name_of(arg)
|
|
108
|
+
return nil unless arg.is_a?(Type::Constant)
|
|
109
|
+
|
|
110
|
+
case arg.value
|
|
111
|
+
when Symbol then arg.value
|
|
112
|
+
when String then arg.value.to_sym
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
@@ -41,7 +41,7 @@ module Rigor
|
|
|
41
41
|
# (instance vs singleton). Both kinds share the same arity and
|
|
42
42
|
# acceptance shape; the difference is only in which `Definition`
|
|
43
43
|
# the caller fetched.
|
|
44
|
-
module OverloadSelector
|
|
44
|
+
module OverloadSelector
|
|
45
45
|
module_function
|
|
46
46
|
|
|
47
47
|
# @param method_definition [RBS::Definition::Method]
|
|
@@ -64,8 +64,8 @@ module Rigor
|
|
|
64
64
|
# back to the first declaration.
|
|
65
65
|
# @return [RBS::MethodType, nil] the chosen overload, or nil
|
|
66
66
|
# when the definition has no method types at all.
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
def select(method_definition, arg_types:, self_type:, instance_type:, type_vars: {}, block_required: false,
|
|
68
|
+
environment: nil)
|
|
69
69
|
overloads = method_definition.method_types
|
|
70
70
|
return nil if overloads.empty?
|
|
71
71
|
|
|
@@ -75,7 +75,7 @@ module Rigor
|
|
|
75
75
|
# `accepts_param?` so overload selection sees the
|
|
76
76
|
# tighter type when filtering candidates by argument
|
|
77
77
|
# compatibility.
|
|
78
|
-
param_overrides = RbsExtended.param_type_override_map(method_definition)
|
|
78
|
+
param_overrides = RbsExtended.param_type_override_map(method_definition, environment: environment)
|
|
79
79
|
|
|
80
80
|
# Pass 1: prefer overloads whose param types stay strict —
|
|
81
81
|
# no translator-induced `Dynamic[Top]` from Alias /
|
|
@@ -113,7 +113,6 @@ module Rigor
|
|
|
113
113
|
|
|
114
114
|
overloads.first
|
|
115
115
|
end
|
|
116
|
-
# rubocop:enable Metrics/ParameterLists
|
|
117
116
|
|
|
118
117
|
def overload_has_block?(method_type)
|
|
119
118
|
method_type.respond_to?(:block) && method_type.block
|
|
@@ -122,7 +121,7 @@ module Rigor
|
|
|
122
121
|
class << self
|
|
123
122
|
private
|
|
124
123
|
|
|
125
|
-
# rubocop:disable Metrics/ParameterLists
|
|
124
|
+
# rubocop:disable Metrics/ParameterLists
|
|
126
125
|
def find_matching_overload(overloads, arg_types:, self_type:, instance_type:, type_vars:, block_required:,
|
|
127
126
|
param_overrides:, strict:)
|
|
128
127
|
return nil if strict && arg_types.any? { |t| untyped_arg?(t) }
|
|
@@ -141,7 +140,7 @@ module Rigor
|
|
|
141
140
|
)
|
|
142
141
|
end
|
|
143
142
|
end
|
|
144
|
-
# rubocop:enable Metrics/ParameterLists
|
|
143
|
+
# rubocop:enable Metrics/ParameterLists
|
|
145
144
|
|
|
146
145
|
# Treats the literal `untyped` carrier (`Dynamic[Top]`)
|
|
147
146
|
# as too imprecise to drive a strict-pass match. Other
|
|
@@ -194,7 +193,6 @@ module Rigor
|
|
|
194
193
|
end
|
|
195
194
|
end
|
|
196
195
|
|
|
197
|
-
# rubocop:disable Metrics/ParameterLists
|
|
198
196
|
def matches?(method_type, arg_types, self_type:, instance_type:, type_vars:, param_overrides:)
|
|
199
197
|
return false if method_type.respond_to?(:type_params) && rejects_keyword_required?(method_type)
|
|
200
198
|
|
|
@@ -213,7 +211,6 @@ module Rigor
|
|
|
213
211
|
)
|
|
214
212
|
end
|
|
215
213
|
end
|
|
216
|
-
# rubocop:enable Metrics/ParameterLists
|
|
217
214
|
|
|
218
215
|
# Slice 4 phase 2c does not pass keyword arguments through the
|
|
219
216
|
# call site (caller passes only positional `arg_types`). An
|
|
@@ -258,7 +255,6 @@ module Rigor
|
|
|
258
255
|
head
|
|
259
256
|
end
|
|
260
257
|
|
|
261
|
-
# rubocop:disable Metrics/ParameterLists
|
|
262
258
|
def accepts_param?(param, arg, self_type:, instance_type:, type_vars:, param_overrides:)
|
|
263
259
|
param_type = param_overrides[param.name] || RbsTypeTranslator.translate(
|
|
264
260
|
param.type,
|
|
@@ -269,7 +265,6 @@ module Rigor
|
|
|
269
265
|
result = param_type.accepts(arg, mode: :gradual)
|
|
270
266
|
result.yes? || result.maybe?
|
|
271
267
|
end
|
|
272
|
-
# rubocop:enable Metrics/ParameterLists
|
|
273
268
|
end
|
|
274
269
|
end
|
|
275
270
|
end
|
|
@@ -162,7 +162,8 @@ module Rigor
|
|
|
162
162
|
kind: kind,
|
|
163
163
|
args: args,
|
|
164
164
|
type_vars: type_vars,
|
|
165
|
-
block_type: block_type
|
|
165
|
+
block_type: block_type,
|
|
166
|
+
environment: environment
|
|
166
167
|
)
|
|
167
168
|
rescue StandardError
|
|
168
169
|
# Defensive: if RBS' definition builder raises on a broken
|
|
@@ -194,6 +195,18 @@ module Rigor
|
|
|
194
195
|
["Array", :instance, tuple_type_args(receiver)]
|
|
195
196
|
when Type::HashShape
|
|
196
197
|
["Hash", :instance, hash_shape_type_args(receiver)]
|
|
198
|
+
when Type::BoundMethod
|
|
199
|
+
# `BoundMethod` is a precision-bearing alias for
|
|
200
|
+
# `Nominal[Method]`: it carries the
|
|
201
|
+
# `(receiver, method_name)` binding that
|
|
202
|
+
# `MethodFolding.try_backward` consumes at
|
|
203
|
+
# `.call` / `.()` / `[]`, but every other call
|
|
204
|
+
# site (`.owner` / `.name` / `.arity` / …) must
|
|
205
|
+
# still resolve through Method's RBS contract.
|
|
206
|
+
# Routing here keeps reflective Method methods
|
|
207
|
+
# working without forcing the carrier to
|
|
208
|
+
# collapse to a plain Nominal at construction.
|
|
209
|
+
["Method", :instance, []]
|
|
197
210
|
when Type::Dynamic
|
|
198
211
|
receiver_descriptor(receiver.static_facet)
|
|
199
212
|
end
|
|
@@ -241,15 +254,15 @@ module Rigor
|
|
|
241
254
|
param_names.zip(receiver_args).to_h
|
|
242
255
|
end
|
|
243
256
|
|
|
244
|
-
|
|
245
|
-
|
|
257
|
+
def translate_return_type(method_definition, class_name:, kind:, args:, type_vars:, block_type:,
|
|
258
|
+
environment: nil)
|
|
246
259
|
# Slice 4b-3 (ADR-7 § "Slice 4-A/4-B") — read the
|
|
247
260
|
# return-type override through the merger so future
|
|
248
261
|
# plugin / `:rbs_extended` bundles that also assert a
|
|
249
262
|
# `return_type` slot at this call site compose with
|
|
250
263
|
# the RBS::Extended directive instead of silently
|
|
251
264
|
# racing it.
|
|
252
|
-
override = merged_return_type(method_definition)
|
|
265
|
+
override = merged_return_type(method_definition, environment: environment)
|
|
253
266
|
return override if override
|
|
254
267
|
|
|
255
268
|
instance_type = Type::Combinator.nominal_of(class_name)
|
|
@@ -265,7 +278,8 @@ module Rigor
|
|
|
265
278
|
self_type: self_type,
|
|
266
279
|
instance_type: instance_type,
|
|
267
280
|
type_vars: type_vars,
|
|
268
|
-
block_required: !block_type.nil
|
|
281
|
+
block_required: !block_type.nil?,
|
|
282
|
+
environment: environment
|
|
269
283
|
)
|
|
270
284
|
return nil unless method_type
|
|
271
285
|
|
|
@@ -278,7 +292,6 @@ module Rigor
|
|
|
278
292
|
type_vars: full_type_vars
|
|
279
293
|
)
|
|
280
294
|
end
|
|
281
|
-
# rubocop:enable Metrics/ParameterLists
|
|
282
295
|
|
|
283
296
|
# ADR-7 § "Slice 4-A/4-B" — folds the
|
|
284
297
|
# `RBS::Extended` `return:` directive (and any
|
|
@@ -287,8 +300,8 @@ module Rigor
|
|
|
287
300
|
# before consuming. Returns the merged return type
|
|
288
301
|
# or nil when no contribution overrides the
|
|
289
302
|
# RBS-declared return.
|
|
290
|
-
def merged_return_type(method_definition)
|
|
291
|
-
contribution = RbsExtended.read_flow_contribution(method_definition)
|
|
303
|
+
def merged_return_type(method_definition, environment: nil)
|
|
304
|
+
contribution = RbsExtended.read_flow_contribution(method_definition, environment: environment)
|
|
292
305
|
return nil if contribution.nil?
|
|
293
306
|
|
|
294
307
|
Rigor::FlowContribution::Merger.merge([contribution]).return_type
|
|
@@ -377,13 +390,15 @@ module Rigor
|
|
|
377
390
|
class_name: class_name,
|
|
378
391
|
kind: kind,
|
|
379
392
|
args: args,
|
|
380
|
-
type_vars: type_vars
|
|
393
|
+
type_vars: type_vars,
|
|
394
|
+
environment: environment
|
|
381
395
|
)
|
|
382
396
|
rescue StandardError
|
|
383
397
|
[]
|
|
384
398
|
end
|
|
385
399
|
|
|
386
|
-
def extract_block_param_types(method_definition, class_name:, kind:, args:, type_vars
|
|
400
|
+
def extract_block_param_types(method_definition, class_name:, kind:, args:, type_vars:,
|
|
401
|
+
environment: nil)
|
|
387
402
|
instance_type = Type::Combinator.nominal_of(class_name)
|
|
388
403
|
self_type =
|
|
389
404
|
case kind
|
|
@@ -397,7 +412,8 @@ module Rigor
|
|
|
397
412
|
self_type: self_type,
|
|
398
413
|
instance_type: instance_type,
|
|
399
414
|
type_vars: type_vars,
|
|
400
|
-
block_required: true
|
|
415
|
+
block_required: true,
|
|
416
|
+
environment: environment
|
|
401
417
|
)
|
|
402
418
|
return [] unless method_type
|
|
403
419
|
|
|
@@ -626,7 +626,6 @@ module Rigor
|
|
|
626
626
|
# (so it can serve as a Hash key). Produces a closed
|
|
627
627
|
# `HashShape` whose entries mirror the per-position
|
|
628
628
|
# pairs. Empty Tuples fold to the empty HashShape.
|
|
629
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
630
629
|
def tuple_to_h(tuple, _method_name, args)
|
|
631
630
|
return nil unless args.empty?
|
|
632
631
|
return Type::Combinator.hash_shape_of({}) if tuple.elements.empty?
|
|
@@ -637,7 +636,6 @@ module Rigor
|
|
|
637
636
|
|
|
638
637
|
Type::Combinator.hash_shape_of(pairs.to_h)
|
|
639
638
|
end
|
|
640
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
|
641
639
|
|
|
642
640
|
def tuple_to_h_pair(element)
|
|
643
641
|
return nil unless element.is_a?(Type::Tuple)
|
|
@@ -865,7 +863,6 @@ module Rigor
|
|
|
865
863
|
# `HashShape` accepts as keys). Duplicate values would
|
|
866
864
|
# alias under inversion, so Rigor declines on
|
|
867
865
|
# collisions rather than silently dropping entries.
|
|
868
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
869
866
|
def hash_invert(shape, _method_name, args)
|
|
870
867
|
return nil unless args.empty?
|
|
871
868
|
return nil unless shape.closed?
|
|
@@ -880,7 +877,6 @@ module Rigor
|
|
|
880
877
|
end
|
|
881
878
|
Type::Combinator.hash_shape_of(inverted)
|
|
882
879
|
end
|
|
883
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
884
880
|
|
|
885
881
|
# `shape.first` — returns the first `[k, v]` pair as a
|
|
886
882
|
# 2-Tuple, or `Constant[nil]` when the shape is empty.
|