rigortype 0.0.3 → 0.0.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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +215 -117
  3. data/data/builtins/ruby_core/comparable.yml +87 -0
  4. data/data/builtins/ruby_core/complex.yml +505 -0
  5. data/data/builtins/ruby_core/date.yml +1737 -0
  6. data/data/builtins/ruby_core/enumerable.yml +557 -0
  7. data/data/builtins/ruby_core/file.yml +9 -0
  8. data/data/builtins/ruby_core/hash.yml +936 -0
  9. data/data/builtins/ruby_core/range.yml +389 -0
  10. data/data/builtins/ruby_core/rational.yml +365 -0
  11. data/data/builtins/ruby_core/set.yml +594 -0
  12. data/data/builtins/ruby_core/string.yml +9 -0
  13. data/data/builtins/ruby_core/time.yml +752 -0
  14. data/lib/rigor/analysis/check_rules.rb +11 -3
  15. data/lib/rigor/builtins/imported_refinements.rb +192 -10
  16. data/lib/rigor/cli.rb +1 -1
  17. data/lib/rigor/inference/acceptance.rb +181 -12
  18. data/lib/rigor/inference/builtins/comparable_catalog.rb +27 -0
  19. data/lib/rigor/inference/builtins/complex_catalog.rb +41 -0
  20. data/lib/rigor/inference/builtins/date_catalog.rb +98 -0
  21. data/lib/rigor/inference/builtins/enumerable_catalog.rb +27 -0
  22. data/lib/rigor/inference/builtins/hash_catalog.rb +40 -0
  23. data/lib/rigor/inference/builtins/range_catalog.rb +46 -0
  24. data/lib/rigor/inference/builtins/rational_catalog.rb +38 -0
  25. data/lib/rigor/inference/builtins/set_catalog.rb +54 -0
  26. data/lib/rigor/inference/builtins/time_catalog.rb +64 -0
  27. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +145 -11
  28. data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +202 -1
  29. data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +95 -0
  30. data/lib/rigor/inference/method_dispatcher/overload_selector.rb +23 -7
  31. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +135 -6
  32. data/lib/rigor/inference/method_dispatcher.rb +3 -1
  33. data/lib/rigor/inference/method_parameter_binder.rb +29 -4
  34. data/lib/rigor/inference/narrowing.rb +211 -0
  35. data/lib/rigor/inference/scope_indexer.rb +87 -11
  36. data/lib/rigor/inference/statement_evaluator.rb +6 -0
  37. data/lib/rigor/rbs_extended.rb +170 -14
  38. data/lib/rigor/type/combinator.rb +90 -0
  39. data/lib/rigor/type/integer_range.rb +4 -2
  40. data/lib/rigor/type/intersection.rb +135 -0
  41. data/lib/rigor/type/refined.rb +174 -0
  42. data/lib/rigor/type.rb +2 -0
  43. data/lib/rigor/version.rb +1 -1
  44. data/sig/rigor/environment.rbs +4 -6
  45. data/sig/rigor/inference.rbs +2 -1
  46. data/sig/rigor/rbs_extended.rbs +11 -0
  47. data/sig/rigor/type.rbs +75 -35
  48. metadata +22 -1
@@ -11,6 +11,8 @@ require_relative "tuple"
11
11
  require_relative "hash_shape"
12
12
  require_relative "union"
13
13
  require_relative "difference"
14
+ require_relative "refined"
15
+ require_relative "intersection"
14
16
 
15
17
  module Rigor
16
18
  module Type
@@ -133,6 +135,65 @@ module Rigor
133
135
  )
134
136
  end
135
137
 
138
+ # Predicate-subset refinement carrier (ADR-3 OQ3 Option C,
139
+ # second half). Use `lowercase_string` /
140
+ # `uppercase_string` / `numeric_string` for the imported
141
+ # built-in shapes; raw `refined(base, predicate_id)` for
142
+ # ad-hoc refinements introduced by an `RBS::Extended`
143
+ # annotation or a plugin-contributed predicate.
144
+ def refined(base, predicate_id)
145
+ Refined.new(base, predicate_id)
146
+ end
147
+
148
+ def lowercase_string
149
+ Refined.new(nominal_of("String"), :lowercase)
150
+ end
151
+
152
+ def uppercase_string
153
+ Refined.new(nominal_of("String"), :uppercase)
154
+ end
155
+
156
+ def numeric_string
157
+ Refined.new(nominal_of("String"), :numeric)
158
+ end
159
+
160
+ def decimal_int_string
161
+ Refined.new(nominal_of("String"), :decimal_int)
162
+ end
163
+
164
+ def octal_int_string
165
+ Refined.new(nominal_of("String"), :octal_int)
166
+ end
167
+
168
+ def hex_int_string
169
+ Refined.new(nominal_of("String"), :hex_int)
170
+ end
171
+
172
+ # Normalised intersection. Flattens nested Intersections,
173
+ # drops `Top` members, collapses to `Bot` if any member is
174
+ # `Bot`, deduplicates structurally-equal members, sorts the
175
+ # survivors by `describe(:short)`, and collapses 0-/1-member
176
+ # results so a degenerate intersection never reaches the
177
+ # carrier. See ADR-3 OQ3 for the rationale; the lattice
178
+ # algebra is in
179
+ # [`value-lattice.md`](docs/type-specification/value-lattice.md).
180
+ def intersection(*members)
181
+ collapse_intersection(normalised_intersection_members(members))
182
+ end
183
+
184
+ # `non-empty-lowercase-string` = non-empty-string ∩
185
+ # lowercase-string. Composes the point-removal half
186
+ # (`Difference[String, ""]`) with the predicate-subset half
187
+ # (`Refined[String, :lowercase]`). Both members erase to
188
+ # `String` so the carrier's RBS erasure is unambiguous.
189
+ def non_empty_lowercase_string
190
+ intersection(non_empty_string, lowercase_string)
191
+ end
192
+
193
+ def non_empty_uppercase_string
194
+ intersection(non_empty_string, uppercase_string)
195
+ end
196
+
136
197
  # Constructs a heterogeneous, fixed-arity Tuple from positional
137
198
  # element types. `tuple_of()` produces the empty tuple `Tuple[]`,
138
199
  # which is structurally distinct from the raw `Nominal[Array]`.
@@ -185,6 +246,35 @@ module Rigor
185
246
  end
186
247
  end
187
248
 
249
+ # Symmetric counterparts to the Union normalisers. The
250
+ # absorbing element is `Bot` (anything intersected with
251
+ # nothing is nothing) and the identity element is `Top`
252
+ # (intersecting with the universal type is a no-op).
253
+ def normalised_intersection_members(types)
254
+ flattened = []
255
+ types.each { |t| flatten_intersection_into(flattened, t) }
256
+ return [bot] if flattened.any?(Bot)
257
+
258
+ flattened.reject! { |t| t.is_a?(Top) }
259
+ unique_members(flattened)
260
+ end
261
+
262
+ def collapse_intersection(types)
263
+ case types.size
264
+ when 0 then top
265
+ when 1 then types.first
266
+ else Intersection.new(sort_members(types))
267
+ end
268
+ end
269
+
270
+ def flatten_intersection_into(acc, type)
271
+ if type.is_a?(Intersection)
272
+ type.members.each { |m| flatten_intersection_into(acc, m) }
273
+ else
274
+ acc << type
275
+ end
276
+ end
277
+
188
278
  def resolve_class_name(class_name_or_object)
189
279
  name =
190
280
  case class_name_or_object
@@ -65,11 +65,13 @@ module Rigor
65
65
  # `:neg_infinity`). Use this in arithmetic comparisons; never compare
66
66
  # `:neg_infinity` directly with an `Integer`.
67
67
  def lower
68
- min == NEG_INFINITY ? -Float::INFINITY : min
68
+ m = min
69
+ m.is_a?(Symbol) ? -Float::INFINITY : m
69
70
  end
70
71
 
71
72
  def upper
72
- max == POS_INFINITY ? Float::INFINITY : max
73
+ m = max
74
+ m.is_a?(Symbol) ? Float::INFINITY : m
73
75
  end
74
76
 
75
77
  ALIAS_NAMES = {
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../trinary"
4
+
5
+ module Rigor
6
+ module Type
7
+ # `Intersection[M1, M2, …]` — value set is the meet of every
8
+ # member's value set. The carrier composes refinements that
9
+ # share a base, in particular the catalogued
10
+ # `non-empty-lowercase-string` (= `Difference[String, ""] &
11
+ # Refined[String, :lowercase]`) and
12
+ # `non-empty-uppercase-string` shapes from
13
+ # [`imported-built-in-types.md`](docs/type-specification/imported-built-in-types.md).
14
+ # See [ADR-3](docs/adr/3-type-representation.md) for the
15
+ # OQ3 working decision and the rationale for keeping
16
+ # Intersection a thin wrapper rather than per-shape carriers.
17
+ #
18
+ # Construction MUST go through `Type::Combinator.intersection`
19
+ # (or the per-name factories
20
+ # `Combinator.non_empty_lowercase_string` /
21
+ # `Combinator.non_empty_uppercase_string`). The factory:
22
+ #
23
+ # - flattens nested intersections,
24
+ # - drops `Top` members (Top is the identity of intersection),
25
+ # - collapses to `Bot` if any member is `Bot` (Bot is absorbing),
26
+ # - deduplicates structurally-equal members,
27
+ # - sorts the surviving members by `describe(:short)` so two
28
+ # structurally-equal intersections built in different orders
29
+ # compare equal,
30
+ # - returns `Top` for the empty intersection,
31
+ # - returns the lone member for a 1-element intersection (so
32
+ # the carrier is never inhabited by a degenerate single-member
33
+ # shape).
34
+ #
35
+ # Direct `.new` callers MUST pass an already-normalised member
36
+ # list and are expected to be tests or the combinator itself.
37
+ class Intersection
38
+ attr_reader :members
39
+
40
+ def initialize(members)
41
+ @members = members.dup.freeze
42
+ freeze
43
+ end
44
+
45
+ def describe(verbosity = :short)
46
+ named = canonical_name
47
+ return named if named
48
+
49
+ members.map { |m| m.describe(verbosity) }.join(" & ")
50
+ end
51
+
52
+ # An intersection of refinements over the same base type
53
+ # erases to that base. We use the first member's erasure
54
+ # because the v0.0.4 catalogue (`non-empty-lowercase-string`
55
+ # etc.) is restricted to same-base composition; richer
56
+ # cross-base intersections will need a stricter erasure
57
+ # rule (likely "lowest common ancestor" via the inference
58
+ # engine's class hierarchy).
59
+ def erase_to_rbs
60
+ members.first.erase_to_rbs
61
+ end
62
+
63
+ def top
64
+ Trinary.no
65
+ end
66
+
67
+ def bot
68
+ Trinary.no
69
+ end
70
+
71
+ def dynamic
72
+ Trinary.no
73
+ end
74
+
75
+ def accepts(other, mode: :gradual)
76
+ Inference::Acceptance.accepts(self, other, mode: mode)
77
+ end
78
+
79
+ def ==(other)
80
+ other.is_a?(Intersection) && members == other.members
81
+ end
82
+ alias eql? ==
83
+
84
+ def hash
85
+ [Intersection, members].hash
86
+ end
87
+
88
+ def inspect
89
+ "#<Rigor::Type::Intersection #{describe(:short)}>"
90
+ end
91
+
92
+ private
93
+
94
+ # Maps a structurally-recognised composite shape to its
95
+ # kebab-case canonical name. The recognised set is kept in
96
+ # sync with the imported-built-in catalogue
97
+ # ([`imported-built-in-types.md`](docs/type-specification/imported-built-in-types.md)).
98
+ #
99
+ # Detection is order-independent — `Combinator.intersection`
100
+ # sorts the canonical member list, but reading the registry
101
+ # the other way around (a user-authored Intersection built
102
+ # in any order) MUST still print in its canonical spelling.
103
+ def canonical_name
104
+ return nil unless members.size == 2
105
+
106
+ bases = members.map { |m| canonical_role(m) }.compact
107
+ return nil unless bases.size == 2
108
+
109
+ roles = bases.sort
110
+ case roles
111
+ when %w[lowercase non_empty_string] then "non-empty-lowercase-string"
112
+ when %w[non_empty_string uppercase] then "non-empty-uppercase-string"
113
+ end
114
+ end
115
+
116
+ # Returns a stable role tag for the recognised composite
117
+ # members so `canonical_name` can pattern-match on a sorted
118
+ # role pair regardless of construction order. Returns nil
119
+ # when the member is not part of any catalogued composite —
120
+ # any nil contribution disqualifies the canonical-name path
121
+ # and the operator-form fallback kicks in.
122
+ def canonical_role(member)
123
+ case member
124
+ when Difference
125
+ "non_empty_string" if member == Type::Combinator.non_empty_string
126
+ when Refined
127
+ case member
128
+ when Type::Combinator.lowercase_string then "lowercase"
129
+ when Type::Combinator.uppercase_string then "uppercase"
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,174 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../trinary"
4
+
5
+ module Rigor
6
+ module Type
7
+ # `Refined[base, predicate_id]` — predicate-subset half of
8
+ # the OQ3 refinement-carrier strategy
9
+ # ([ADR-3](docs/adr/3-type-representation.md), Working
10
+ # Decision Option C). Sibling of `Type::Difference`, which
11
+ # carries the point-removal half.
12
+ #
13
+ # lowercase-string = Refined[Nominal[String], :lowercase]
14
+ # uppercase-string = Refined[Nominal[String], :uppercase]
15
+ # numeric-string = Refined[Nominal[String], :numeric]
16
+ #
17
+ # The carrier wraps a base type and a `predicate_id` Symbol
18
+ # drawn from {PREDICATES}. The recogniser is invoked at
19
+ # constant-fold and acceptance time over a `Constant<base>`
20
+ # value; for non-Constant receivers the carrier is a marker
21
+ # the catalog tier consults to project `String#downcase` /
22
+ # `String#upcase` (etc.) into the matching refinement.
23
+ #
24
+ # Display routes through {CANONICAL_NAMES}: registered
25
+ # `(base_class_name, predicate_id)` pairs print in their
26
+ # kebab-case spelling (`lowercase-string`); unregistered
27
+ # combinations fall back to the `base & predicate?` operator
28
+ # form per
29
+ # [`type-operators.md`](docs/type-specification/type-operators.md).
30
+ #
31
+ # Construction MUST go through `Type::Combinator.refined` /
32
+ # the per-name factories (`Combinator.lowercase_string`,
33
+ # `Combinator.uppercase_string`, `Combinator.numeric_string`).
34
+ # Direct `.new` is an internal escape hatch for tests and
35
+ # combinator's own implementation.
36
+ class Refined
37
+ attr_reader :base, :predicate_id
38
+
39
+ def initialize(base, predicate_id)
40
+ raise ArgumentError, "predicate_id must be a Symbol" unless predicate_id.is_a?(Symbol)
41
+
42
+ @base = base
43
+ @predicate_id = predicate_id
44
+ freeze
45
+ end
46
+
47
+ def describe(verbosity = :short)
48
+ named = canonical_name
49
+ return named if named
50
+
51
+ "#{base.describe(verbosity)} & #{predicate_id}?"
52
+ end
53
+
54
+ # Erases to the base nominal: every refinement MUST erase
55
+ # to its base per [`rbs-erasure.md`](docs/type-specification/rbs-erasure.md).
56
+ def erase_to_rbs
57
+ base.erase_to_rbs
58
+ end
59
+
60
+ def top
61
+ Trinary.no
62
+ end
63
+
64
+ def bot
65
+ Trinary.no
66
+ end
67
+
68
+ def dynamic
69
+ base.respond_to?(:dynamic) ? base.dynamic : Trinary.no
70
+ end
71
+
72
+ def accepts(other, mode: :gradual)
73
+ Inference::Acceptance.accepts(self, other, mode: mode)
74
+ end
75
+
76
+ def ==(other)
77
+ other.is_a?(Refined) && base == other.base && predicate_id == other.predicate_id
78
+ end
79
+ alias eql? ==
80
+
81
+ def hash
82
+ [Refined, base, predicate_id].hash
83
+ end
84
+
85
+ def inspect
86
+ "#<Rigor::Type::Refined #{describe(:short)}>"
87
+ end
88
+
89
+ # Recognises a Ruby value against this carrier's
90
+ # predicate. The trinary return is intentional: `true` /
91
+ # `false` when the predicate registry decides, `nil`
92
+ # when the predicate is unknown to the registry, so
93
+ # callers (today {Inference::Acceptance}) can fall
94
+ # through to gradual-mode `:maybe`.
95
+ # rubocop:disable Style/ReturnNilInPredicateMethodDefinition
96
+ def matches?(value)
97
+ recogniser = PREDICATES[predicate_id]
98
+ return nil if recogniser.nil?
99
+
100
+ !!recogniser.call(value)
101
+ end
102
+ # rubocop:enable Style/ReturnNilInPredicateMethodDefinition
103
+
104
+ # `predicate_id => recogniser` table. The recogniser is
105
+ # called with a Ruby value (typically the inner `value`
106
+ # of a `Constant`) and returns truthy when the value
107
+ # satisfies the predicate. The recogniser MUST be total
108
+ # (return false rather than raise) over arbitrary input,
109
+ # so callers can pass any `Constant#value` without a
110
+ # type-prefilter.
111
+ #
112
+ # Plugin-contributed predicates land here once ADR-2 is
113
+ # in flight; today the table is closed over the v0.0.4
114
+ # built-in catalogue.
115
+ #
116
+ # Recogniser policy:
117
+ #
118
+ # - `:numeric` is deliberately conservative — only decimal
119
+ # integer and plain-decimal-fraction strings are
120
+ # recognised, mirroring `imported-built-in-types.md`'s
121
+ # "Rigor's numeric-string predicate" wording. Looser
122
+ # forms (scientific, hex, rational) MAY join later
123
+ # without breaking the registry contract.
124
+ # - `:decimal_int` is "what `Integer(s, 10)` would parse
125
+ # without remainder" — one or more decimal digits,
126
+ # optional leading sign, no whitespace, no fractional
127
+ # tail.
128
+ # - `:octal_int` and `:hex_int` REQUIRE their conventional
129
+ # prefix (`0o` / `0O` / leading `0` for octal; `0x` /
130
+ # `0X` for hex) so the predicate is disjoint from
131
+ # `:decimal_int`. A bare `"755"` is decimal-int-string,
132
+ # not octal-int-string. This matches the typical user
133
+ # intent — a refinement marks a string that "looks like
134
+ # octal", not "happens to be base-8 valid".
135
+ NUMERIC_STRING_PATTERN = /\A-?\d+(?:\.\d+)?\z/
136
+ DECIMAL_INT_STRING_PATTERN = /\A-?\d+\z/
137
+ OCTAL_INT_STRING_PATTERN = /\A-?(?:0[oO][0-7]+|0[0-7]+)\z/
138
+ HEX_INT_STRING_PATTERN = /\A-?0[xX][0-9a-fA-F]+\z/
139
+ private_constant :NUMERIC_STRING_PATTERN, :DECIMAL_INT_STRING_PATTERN,
140
+ :OCTAL_INT_STRING_PATTERN, :HEX_INT_STRING_PATTERN
141
+
142
+ PREDICATES = {
143
+ lowercase: ->(v) { v.is_a?(String) && v == v.downcase },
144
+ uppercase: ->(v) { v.is_a?(String) && v == v.upcase },
145
+ numeric: ->(v) { v.is_a?(String) && NUMERIC_STRING_PATTERN.match?(v) },
146
+ decimal_int: ->(v) { v.is_a?(String) && DECIMAL_INT_STRING_PATTERN.match?(v) },
147
+ octal_int: ->(v) { v.is_a?(String) && OCTAL_INT_STRING_PATTERN.match?(v) },
148
+ hex_int: ->(v) { v.is_a?(String) && HEX_INT_STRING_PATTERN.match?(v) }
149
+ }.freeze
150
+
151
+ # Maps `[base_class_name, predicate_id]` pairs to their
152
+ # kebab-case canonical name. Registered shapes print
153
+ # through `describe`; unregistered combinations fall back
154
+ # to the operator form.
155
+ CANONICAL_NAMES = {
156
+ ["String", :lowercase] => "lowercase-string",
157
+ ["String", :uppercase] => "uppercase-string",
158
+ ["String", :numeric] => "numeric-string",
159
+ ["String", :decimal_int] => "decimal-int-string",
160
+ ["String", :octal_int] => "octal-int-string",
161
+ ["String", :hex_int] => "hex-int-string"
162
+ }.freeze
163
+ private_constant :CANONICAL_NAMES
164
+
165
+ private
166
+
167
+ def canonical_name
168
+ return nil unless base.is_a?(Nominal)
169
+
170
+ CANONICAL_NAMES[[base.class_name, predicate_id]]
171
+ end
172
+ end
173
+ end
174
+ end
data/lib/rigor/type.rb CHANGED
@@ -21,5 +21,7 @@ require_relative "type/tuple"
21
21
  require_relative "type/hash_shape"
22
22
  require_relative "type/union"
23
23
  require_relative "type/difference"
24
+ require_relative "type/refined"
25
+ require_relative "type/intersection"
24
26
  require_relative "type/accepts_result"
25
27
  require_relative "type/combinator"
data/lib/rigor/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rigor
4
- VERSION = "0.0.3"
4
+ VERSION = "0.0.5"
5
5
  end
@@ -38,15 +38,13 @@ module Rigor
38
38
 
39
39
  def initialize: (?libraries: Array[String], ?signature_paths: Array[String | _ToPath]) -> void
40
40
  def class_known?: (String | Symbol name) -> bool
41
- def instance_definition: (String | Symbol class_name) -> untyped
42
- def instance_method: (class_name: String | Symbol, method_name: String | Symbol) -> untyped
43
- def singleton_definition: (String | Symbol class_name) -> untyped
44
- def singleton_method: (class_name: String | Symbol, method_name: String | Symbol) -> untyped
41
+ def instance_definition: (String | Symbol class_name) -> untyped?
42
+ def instance_method: (class_name: String | Symbol, method_name: String | Symbol) -> untyped?
43
+ def singleton_definition: (String | Symbol class_name) -> untyped?
44
+ def singleton_method: (class_name: String | Symbol, method_name: String | Symbol) -> untyped?
45
45
  def class_type_param_names: (String | Symbol class_name) -> Array[Symbol]
46
46
  def class_ordering: (String | Symbol lhs, String | Symbol rhs) -> ordering
47
47
  def constant_type: (String name) -> Type::t?
48
- def instance_definition: (String | Symbol class_name) -> untyped?
49
- def singleton_definition: (String | Symbol class_name) -> untyped?
50
48
  end
51
49
 
52
50
  class RbsHierarchy
@@ -85,6 +85,7 @@ module Rigor
85
85
  def self?.narrow_not_equal: (Type::t type, untyped literal) -> Type::t
86
86
  def self?.narrow_class: (Type::t type, String class_name, ?exact: bool, ?environment: Environment) -> Type::t
87
87
  def self?.narrow_not_class: (Type::t type, String class_name, ?exact: bool, ?environment: Environment) -> Type::t
88
+ def self?.narrow_not_refinement: (Type::t current_type, Type::t refinement_type) -> Type::t
88
89
  def self?.predicate_scopes: (untyped node, Scope scope) -> [Scope, Scope]
89
90
  def self?.case_when_scopes: (untyped subject, Array[untyped] conditions, Scope scope) -> [Scope, Scope]
90
91
  def self?.analyse: (untyped node, Scope scope) -> untyped
@@ -131,7 +132,7 @@ module Rigor
131
132
  module ScopeIndexer
132
133
  def self?.index: (untyped root, default_scope: Scope) -> Hash[untyped, Scope]
133
134
  def self?.build_declaration_overrides: (untyped root) -> Hash[untyped, Type::t]
134
- def self?.record_declarations: (untyped node, Array[String] qualified_prefix, Hash[untyped, Type::t] table) -> void
135
+ def self?.record_declarations: (untyped node, Array[String] qualified_prefix, Hash[untyped, Type::t] identity_table, Hash[String, Type::t] discovered) -> void
135
136
  def self?.qualified_name_for: (untyped constant_path_node) -> String?
136
137
  def self?.render_constant_path: (untyped node) -> String
137
138
  def self?.propagate: (untyped node, Hash[untyped, Scope] table, Scope parent_scope) -> void
@@ -42,5 +42,16 @@ module Rigor
42
42
 
43
43
  def self?.read_return_type_override: (untyped method_def) -> Type::t?
44
44
  def self?.parse_return_type_override: (String string) -> Type::t?
45
+
46
+ class ParamOverride
47
+ attr_reader param_name: Symbol
48
+ attr_reader type: Type::t
49
+
50
+ def self.new: (param_name: Symbol, type: Type::t) -> ParamOverride
51
+ end
52
+
53
+ def self?.read_param_type_overrides: (untyped method_def) -> Array[ParamOverride]
54
+ def self?.param_type_override_map: (untyped method_def) -> Hash[Symbol, Type::t]
55
+ def self?.parse_param_annotation: (String string) -> ParamOverride?
45
56
  end
46
57
  end