rigortype 0.1.3 → 0.1.5
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 +154 -33
- 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 +26 -6
- data/lib/rigor/analysis/result.rb +11 -3
- data/lib/rigor/analysis/rule_catalog.rb +2 -2
- data/lib/rigor/analysis/run_stats.rb +193 -0
- data/lib/rigor/analysis/runner.rb +498 -12
- data/lib/rigor/analysis/worker_session.rb +327 -0
- data/lib/rigor/builtins/imported_refinements.rb +364 -55
- data/lib/rigor/builtins/regex_refinement.rb +17 -12
- data/lib/rigor/cache/descriptor.rb +1 -1
- data/lib/rigor/cache/rbs_descriptor.rb +3 -1
- data/lib/rigor/cache/store.rb +39 -6
- 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 +61 -3
- data/lib/rigor/configuration/dependencies.rb +2 -2
- data/lib/rigor/configuration.rb +131 -6
- data/lib/rigor/environment/bundle_sig_discovery.rb +198 -0
- data/lib/rigor/environment/class_registry.rb +12 -3
- data/lib/rigor/environment/lockfile_resolver.rb +125 -0
- data/lib/rigor/environment/rbs_collection_discovery.rb +126 -0
- data/lib/rigor/environment/rbs_coverage_report.rb +112 -0
- data/lib/rigor/environment/rbs_loader.rb +194 -6
- data/lib/rigor/environment/reflection.rb +152 -0
- data/lib/rigor/environment.rb +109 -6
- 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/acceptance.rb +35 -1
- data/lib/rigor/inference/block_parameter_binder.rb +0 -2
- data/lib/rigor/inference/builtins/method_catalog.rb +12 -5
- data/lib/rigor/inference/builtins/numeric_catalog.rb +15 -4
- data/lib/rigor/inference/coverage_scanner.rb +1 -1
- data/lib/rigor/inference/expression_typer.rb +77 -11
- data/lib/rigor/inference/fallback.rb +1 -1
- data/lib/rigor/inference/macro_block_self_type.rb +96 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +3 -5
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +29 -41
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +1 -3
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +4 -4
- data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +135 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +7 -12
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +27 -11
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +46 -44
- data/lib/rigor/inference/method_dispatcher.rb +274 -5
- data/lib/rigor/inference/method_parameter_binder.rb +22 -14
- data/lib/rigor/inference/narrowing.rb +129 -12
- 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/inference/synthetic_method.rb +86 -0
- data/lib/rigor/inference/synthetic_method_index.rb +82 -0
- data/lib/rigor/inference/synthetic_method_scanner.rb +521 -0
- data/lib/rigor/plugin/blueprint.rb +60 -0
- data/lib/rigor/plugin/io_boundary.rb +0 -2
- data/lib/rigor/plugin/loader.rb +5 -3
- data/lib/rigor/plugin/macro/block_as_method.rb +131 -0
- data/lib/rigor/plugin/macro/external_file.rb +143 -0
- data/lib/rigor/plugin/macro/heredoc_template.rb +201 -0
- data/lib/rigor/plugin/macro/trait_registry.rb +198 -0
- data/lib/rigor/plugin/macro.rb +31 -0
- data/lib/rigor/plugin/manifest.rb +102 -10
- data/lib/rigor/plugin/registry.rb +43 -2
- data/lib/rigor/plugin/services.rb +1 -1
- data/lib/rigor/plugin/type_node_resolver.rb +52 -0
- data/lib/rigor/plugin.rb +2 -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/trinary.rb +15 -11
- data/lib/rigor/type/bot.rb +6 -3
- data/lib/rigor/type/bound_method.rb +79 -0
- data/lib/rigor/type/combinator.rb +207 -3
- data/lib/rigor/type/constant.rb +13 -0
- data/lib/rigor/type/hash_shape.rb +0 -2
- data/lib/rigor/type/integer_range.rb +7 -7
- data/lib/rigor/type/refined.rb +18 -12
- data/lib/rigor/type/top.rb +4 -3
- data/lib/rigor/type/union.rb +20 -1
- data/lib/rigor/type.rb +1 -0
- data/lib/rigor/type_node/generic.rb +68 -0
- data/lib/rigor/type_node/identifier.rb +38 -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 +32 -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 +8 -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/blueprint.rbs +7 -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 +16 -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
- data/sig/rigor.rbs +35 -2
- metadata +90 -1
|
@@ -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)
|
|
@@ -36,10 +36,10 @@ module Rigor
|
|
|
36
36
|
# the result into a `Constant<Rational>` / `Constant<Complex>`.
|
|
37
37
|
# The factory accepts the same shapes as Ruby:
|
|
38
38
|
# `Rational(a)`, `Rational(a, b)`, `Complex(a)`, `Complex(a, b)`.
|
|
39
|
-
NUMERIC_CONSTRUCTORS = {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
NUMERIC_CONSTRUCTORS = Ractor.make_shareable({
|
|
40
|
+
Rational: Ractor.make_shareable(->(*args) { Rational(*args) }),
|
|
41
|
+
Complex: Ractor.make_shareable(->(*args) { Complex(*args) })
|
|
42
|
+
})
|
|
43
43
|
private_constant :NUMERIC_CONSTRUCTORS
|
|
44
44
|
|
|
45
45
|
# `Kernel#Integer(s)` predicate-aware refinement set
|
|
@@ -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,135 @@
|
|
|
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
|
+
# `Method#curry` is treated as identity on the carrier
|
|
80
|
+
# — `<bound>.curry` keeps the same
|
|
81
|
+
# `(receiver_type, method_name)` so a subsequent
|
|
82
|
+
# `<curried>.call` still routes through the recursive
|
|
83
|
+
# dispatch below. This is correct for the dominant
|
|
84
|
+
# no-arg form (`.curry.call`); partially-applied
|
|
85
|
+
# forms (`.curry(n).call(a)`) lose precision and fall
|
|
86
|
+
# through to RBS via the trailing
|
|
87
|
+
# `Type::Combinator.untyped`. A faithful
|
|
88
|
+
# `Type::CurriedBoundMethod(receiver_type,
|
|
89
|
+
# method_name, accumulated_args)` carrier is reserved
|
|
90
|
+
# for a future slice when concrete user demand
|
|
91
|
+
# surfaces.
|
|
92
|
+
return receiver if method_name == :curry
|
|
93
|
+
|
|
94
|
+
MethodDispatcher.dispatch(
|
|
95
|
+
receiver_type: receiver.receiver_type,
|
|
96
|
+
method_name: receiver.method_name,
|
|
97
|
+
arg_types: args,
|
|
98
|
+
block_type: block_type,
|
|
99
|
+
environment: environment,
|
|
100
|
+
call_node: call_node,
|
|
101
|
+
scope: scope
|
|
102
|
+
) || Type::Combinator.untyped
|
|
103
|
+
end
|
|
104
|
+
# `Method#call` / `Method#()` and `Method#[]` are the
|
|
105
|
+
# invocation entry points on the `Method` API; the
|
|
106
|
+
# alias `===` is also `call` semantically but is more
|
|
107
|
+
# commonly used as a case-equality predicate, so we
|
|
108
|
+
# do NOT fold through it (the case/when narrowing path
|
|
109
|
+
# already special-cases `===` for branch typing).
|
|
110
|
+
# `Method#curry` rides through as identity (see the
|
|
111
|
+
# comment in `try_backward`).
|
|
112
|
+
BACKWARD_METHOD_NAMES = %i[call [] curry].freeze
|
|
113
|
+
private_constant :BACKWARD_METHOD_NAMES
|
|
114
|
+
|
|
115
|
+
def backward_method?(method_name)
|
|
116
|
+
BACKWARD_METHOD_NAMES.include?(method_name)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# `Object#method` accepts both Symbol and String at
|
|
120
|
+
# runtime (the latter coerced via `to_sym`). The
|
|
121
|
+
# `Constant<String>` form is rare in production code
|
|
122
|
+
# but cheap to support and matches Ruby's documented
|
|
123
|
+
# contract.
|
|
124
|
+
def symbol_name_of(arg)
|
|
125
|
+
return nil unless arg.is_a?(Type::Constant)
|
|
126
|
+
|
|
127
|
+
case arg.value
|
|
128
|
+
when Symbol then arg.value
|
|
129
|
+
when String then arg.value.to_sym
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
@@ -26,7 +26,7 @@ module Rigor
|
|
|
26
26
|
# `Array#[](Range) -> Array[Elem]?` overload for a Range
|
|
27
27
|
# argument. (Surfaced during v0.1.1 self-analysis; see the
|
|
28
28
|
# "Interface-strictness on overload selection" item in
|
|
29
|
-
# `docs/
|
|
29
|
+
# `docs/ROADMAP.md`.)
|
|
30
30
|
# 3. **Pass 2 — gradual fall-back.** If no fully strict overload
|
|
31
31
|
# matches, accept the first arity-and-gradual-accept match
|
|
32
32
|
# (the v0.1.1 behaviour). Alias / Interface / Intersection
|
|
@@ -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
|
|
|
@@ -155,13 +155,13 @@ module Rigor
|
|
|
155
155
|
# tier ahead of RBS sees the more precise carrier so
|
|
156
156
|
# downstream narrowing (`if size > 0; …`) actually has a
|
|
157
157
|
# range to intersect with.
|
|
158
|
-
SIZE_RETURNING_NOMINALS = {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
158
|
+
SIZE_RETURNING_NOMINALS = Ractor.make_shareable({
|
|
159
|
+
"Array" => %i[size length count],
|
|
160
|
+
"String" => %i[length size bytesize],
|
|
161
|
+
"Hash" => %i[size length count],
|
|
162
|
+
"Set" => %i[size length count],
|
|
163
|
+
"Range" => %i[size length count]
|
|
164
|
+
})
|
|
165
165
|
private_constant :SIZE_RETURNING_NOMINALS
|
|
166
166
|
|
|
167
167
|
# When the difference removes the empty value of the
|
|
@@ -323,39 +323,45 @@ module Rigor
|
|
|
323
323
|
# `dispatch_nominal_size` so size-returning calls on
|
|
324
324
|
# a `Refined[String, *]` still tighten to
|
|
325
325
|
# `non_negative_int`.
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
326
|
+
# ADR-15 Phase 4b.x — `Ractor.make_shareable` (not `.freeze`)
|
|
327
|
+
# because the keys are two-element Symbol arrays whose
|
|
328
|
+
# inner arrays are unfrozen under shallow `.freeze`.
|
|
329
|
+
# Surfaced on Discourse via `Ractor::IsolationError` when
|
|
330
|
+
# the dispatch loop's `REFINED_STRING_PROJECTIONS[[id, sym]]`
|
|
331
|
+
# lookup ran from a worker Ractor.
|
|
332
|
+
REFINED_STRING_PROJECTIONS = Ractor.make_shareable({
|
|
333
|
+
%i[lowercase downcase] => :refined_self,
|
|
334
|
+
%i[lowercase upcase] => :uppercase_string,
|
|
335
|
+
%i[uppercase upcase] => :refined_self,
|
|
336
|
+
%i[uppercase downcase] => :lowercase_string,
|
|
337
|
+
%i[numeric downcase] => :refined_self,
|
|
338
|
+
%i[numeric upcase] => :refined_self,
|
|
339
|
+
# Digit-only strings are case-invariant; the prefix
|
|
340
|
+
# letters in `0o…` / `0x…` are accepted by the
|
|
341
|
+
# predicate in either case so the predicate-subset
|
|
342
|
+
# is preserved across `#downcase` / `#upcase` even
|
|
343
|
+
# though the value-set element changes.
|
|
344
|
+
%i[decimal_int downcase] => :refined_self,
|
|
345
|
+
%i[decimal_int upcase] => :refined_self,
|
|
346
|
+
%i[octal_int downcase] => :refined_self,
|
|
347
|
+
%i[octal_int upcase] => :refined_self,
|
|
348
|
+
%i[hex_int downcase] => :refined_self,
|
|
349
|
+
%i[hex_int upcase] => :refined_self,
|
|
350
|
+
# v0.1.1 Track 1 slice 2 — `to_i` / `to_int` on a
|
|
351
|
+
# known digit-only string. `decimal-int-string`
|
|
352
|
+
# (`/\A\d+\z/`) and `numeric-string` (Rigor's
|
|
353
|
+
# numeric-string predicate, ASCII digits) are
|
|
354
|
+
# predicates over digit-only strings, so the parse
|
|
355
|
+
# is total over the carrier domain and the result
|
|
356
|
+
# is always `>= 0`. `non-negative-int` is the
|
|
357
|
+
# tightest carrier that captures both the lower
|
|
358
|
+
# bound and the integer-ness without inventing a
|
|
359
|
+
# narrower carrier.
|
|
360
|
+
%i[decimal_int to_i] => :non_negative_int,
|
|
361
|
+
%i[decimal_int to_int] => :non_negative_int,
|
|
362
|
+
%i[numeric to_i] => :non_negative_int,
|
|
363
|
+
%i[numeric to_int] => :non_negative_int
|
|
364
|
+
})
|
|
359
365
|
private_constant :REFINED_STRING_PROJECTIONS
|
|
360
366
|
|
|
361
367
|
def dispatch_refined(refined, method_name, args)
|
|
@@ -626,7 +632,6 @@ module Rigor
|
|
|
626
632
|
# (so it can serve as a Hash key). Produces a closed
|
|
627
633
|
# `HashShape` whose entries mirror the per-position
|
|
628
634
|
# pairs. Empty Tuples fold to the empty HashShape.
|
|
629
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
630
635
|
def tuple_to_h(tuple, _method_name, args)
|
|
631
636
|
return nil unless args.empty?
|
|
632
637
|
return Type::Combinator.hash_shape_of({}) if tuple.elements.empty?
|
|
@@ -637,7 +642,6 @@ module Rigor
|
|
|
637
642
|
|
|
638
643
|
Type::Combinator.hash_shape_of(pairs.to_h)
|
|
639
644
|
end
|
|
640
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
|
641
645
|
|
|
642
646
|
def tuple_to_h_pair(element)
|
|
643
647
|
return nil unless element.is_a?(Type::Tuple)
|
|
@@ -865,7 +869,6 @@ module Rigor
|
|
|
865
869
|
# `HashShape` accepts as keys). Duplicate values would
|
|
866
870
|
# alias under inversion, so Rigor declines on
|
|
867
871
|
# collisions rather than silently dropping entries.
|
|
868
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
869
872
|
def hash_invert(shape, _method_name, args)
|
|
870
873
|
return nil unless args.empty?
|
|
871
874
|
return nil unless shape.closed?
|
|
@@ -880,7 +883,6 @@ module Rigor
|
|
|
880
883
|
end
|
|
881
884
|
Type::Combinator.hash_shape_of(inverted)
|
|
882
885
|
end
|
|
883
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
884
886
|
|
|
885
887
|
# `shape.first` — returns the first `[k, v]` pair as a
|
|
886
888
|
# 2-Tuple, or `Constant[nil]` when the shape is empty.
|