typeprof 0.31.1 → 0.33.0

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -1
  3. data/doc/report_guide.md +88 -0
  4. data/lib/typeprof/cli/cli.rb +9 -3
  5. data/lib/typeprof/code_range.rb +7 -5
  6. data/lib/typeprof/core/ast/base.rb +31 -6
  7. data/lib/typeprof/core/ast/call.rb +120 -39
  8. data/lib/typeprof/core/ast/const.rb +22 -11
  9. data/lib/typeprof/core/ast/control.rb +62 -32
  10. data/lib/typeprof/core/ast/meta.rb +279 -14
  11. data/lib/typeprof/core/ast/method.rb +82 -24
  12. data/lib/typeprof/core/ast/misc.rb +41 -12
  13. data/lib/typeprof/core/ast/module.rb +38 -4
  14. data/lib/typeprof/core/ast/pattern.rb +51 -23
  15. data/lib/typeprof/core/ast/sig_decl.rb +141 -27
  16. data/lib/typeprof/core/ast/sig_type.rb +113 -32
  17. data/lib/typeprof/core/ast/value.rb +14 -6
  18. data/lib/typeprof/core/ast/variable.rb +17 -4
  19. data/lib/typeprof/core/ast.rb +98 -14
  20. data/lib/typeprof/core/builtin.rb +184 -12
  21. data/lib/typeprof/core/env/method.rb +343 -6
  22. data/lib/typeprof/core/env/method_entity.rb +18 -15
  23. data/lib/typeprof/core/env/module_entity.rb +116 -18
  24. data/lib/typeprof/core/env/static_read.rb +4 -4
  25. data/lib/typeprof/core/env/type_alias_entity.rb +1 -1
  26. data/lib/typeprof/core/env/value_entity.rb +25 -3
  27. data/lib/typeprof/core/env.rb +113 -23
  28. data/lib/typeprof/core/graph/box.rb +508 -63
  29. data/lib/typeprof/core/graph/change_set.rb +64 -46
  30. data/lib/typeprof/core/graph/filter.rb +8 -5
  31. data/lib/typeprof/core/graph/vertex.rb +20 -19
  32. data/lib/typeprof/core/service.rb +340 -24
  33. data/lib/typeprof/core/type.rb +41 -7
  34. data/lib/typeprof/core/util.rb +6 -0
  35. data/lib/typeprof/lsp/messages.rb +5 -0
  36. data/lib/typeprof/lsp/server.rb +35 -4
  37. data/lib/typeprof/version.rb +1 -1
  38. metadata +3 -2
@@ -1,29 +1,25 @@
1
1
  module TypeProf::Core
2
2
  class AST
3
3
  def self.get_rbs_comment_before(raw_node, lenv)
4
- comments = Fiber[:comments]
5
- i = comments.bsearch_index {|comment| comment.location.start_line >= raw_node.location.start_line } || comments.size
6
- lineno = raw_node.location.start_line
4
+ comments = lenv.file_context.comments
5
+ return nil unless comments
6
+ node_line = raw_node.location.start_line
7
+ last_comment_index = comments.bsearch_index {|c| c.location.start_line >= node_line } || comments.size
7
8
  rbs_comments = []
8
- while i > 0
9
- i -= 1
10
- lineno -= 1
9
+ expected_line = node_line - 1
10
+ (last_comment_index - 1).downto(0) do |i|
11
11
  comment = comments[i]
12
+ break unless comment.location.start_line == expected_line && comment.location.slice.start_with?("#:")
12
13
  comment_loc = comment.location
13
14
  comment_text = comment_loc.slice
14
- if comment_loc.start_line == lineno && comment_text.start_with?("#:")
15
- rbs_comments[comment_loc.start_line] = " " * (comment_loc.start_column + 2) + comment_text[2..]
16
- else
17
- break
18
- end
15
+ rbs_comments[comment_loc.start_line - 1] = " " * (comment_loc.start_column + 2) + comment_text[2..]
16
+ expected_line -= 1
19
17
  end
20
18
  return nil if rbs_comments.empty?
21
19
  rbs_comments = rbs_comments.map {|line| line || "" }.join("\n")
22
20
  method_type = RBS::Parser.parse_method_type(rbs_comments)
23
21
  if method_type
24
22
  AST.create_rbs_func_type(method_type, method_type.type_params, method_type.block, lenv)
25
- else
26
- nil
27
23
  end
28
24
  rescue RBS::ParsingError
29
25
  # TODO: report the error
@@ -34,23 +30,32 @@ module TypeProf::Core
34
30
  unless raw_args
35
31
  return {
36
32
  req_positionals: [],
33
+ req_multi_targets: {},
37
34
  opt_positionals: [],
38
35
  opt_positional_defaults: [],
39
36
  rest_positionals: nil,
40
37
  post_positionals: [],
38
+ post_multi_targets: {},
41
39
  req_keywords: [],
42
40
  opt_keywords: [],
43
41
  opt_keyword_defaults: [],
44
42
  rest_keywords: nil,
43
+ no_keywords: false,
45
44
  block: nil,
46
45
  }
47
46
  end
48
47
 
49
48
  args_code_ranges = []
50
49
  req_positionals = []
51
- raw_args.requireds.each do |n|
52
- args_code_ranges << TypeProf::CodeRange.from_node(n.location)
53
- req_positionals << (n.is_a?(Prism::MultiTargetNode) ? nil : n.name)
50
+ req_multi_targets = {}
51
+ raw_args.requireds.each_with_index do |n, i|
52
+ args_code_ranges << lenv.code_range_from_node(n.location)
53
+ if n.is_a?(Prism::MultiTargetNode)
54
+ req_positionals << nil
55
+ req_multi_targets[i] = n
56
+ else
57
+ req_positionals << n.name
58
+ end
54
59
  end
55
60
 
56
61
  # pre_init = args[1]
@@ -62,13 +67,22 @@ module TypeProf::Core
62
67
  opt_positional_defaults << AST.create_node(n.value, lenv)
63
68
  end
64
69
 
65
- post_positionals = raw_args.posts.map {|n| (n.is_a?(Prism::MultiTargetNode) ? nil : n.name) }
70
+ post_multi_targets = {}
71
+ post_positionals = raw_args.posts.each_with_index.map do |n, i|
72
+ if n.is_a?(Prism::MultiTargetNode)
73
+ post_multi_targets[i] = n
74
+ nil
75
+ else
76
+ n.name
77
+ end
78
+ end
66
79
 
67
80
  rest_positionals = raw_args.rest ? (raw_args.rest.name || :"*anonymous_rest") : nil
68
81
 
69
82
  req_keywords = []
70
83
  opt_keywords = []
71
84
  opt_keyword_defaults = []
85
+ no_keywords = false
72
86
 
73
87
  raw_args.keywords.each do |kw|
74
88
  case kw.type
@@ -88,7 +102,7 @@ module TypeProf::Core
88
102
  rest_positionals = :"..."
89
103
  rest_keywords = :"..."
90
104
  when Prism::NoKeywordsParameterNode
91
- # what to do?
105
+ no_keywords = true
92
106
  when nil
93
107
  # nothing to do
94
108
  else
@@ -99,14 +113,17 @@ module TypeProf::Core
99
113
 
100
114
  {
101
115
  req_positionals:,
116
+ req_multi_targets:,
102
117
  opt_positionals:,
103
118
  opt_positional_defaults:,
104
119
  rest_positionals:,
105
120
  post_positionals:,
121
+ post_multi_targets:,
106
122
  req_keywords:,
107
123
  opt_keywords:,
108
124
  opt_keyword_defaults:,
109
125
  rest_keywords:,
126
+ no_keywords:,
110
127
  block:,
111
128
  args_code_ranges:
112
129
  }
@@ -118,7 +135,7 @@ module TypeProf::Core
118
135
  # TODO: warn "def self.foo" in a metaclass
119
136
  singleton = !!raw_node.receiver || lenv.cref.scope_level == :metaclass
120
137
  mid = raw_node.name
121
- mid_code_range = TypeProf::CodeRange.from_node(raw_node.name_loc)
138
+ mid_code_range_loc = raw_node.name_loc
122
139
  @tbl = raw_node.locals
123
140
  raw_args = raw_node.parameters
124
141
  raw_body = raw_node.body
@@ -127,10 +144,10 @@ module TypeProf::Core
127
144
 
128
145
  @singleton = singleton
129
146
  @mid = mid
130
- @mid_code_range = mid_code_range
147
+ @mid_code_range_loc = mid_code_range_loc
131
148
 
132
149
  ncref = CRef.new(lenv.cref.cpath, @singleton ? :class : :instance, @mid, lenv.cref)
133
- nlenv = LocalEnv.new(@lenv.path, ncref, {}, [])
150
+ nlenv = LocalEnv.new(@lenv.file_context, ncref, {}, [])
134
151
  if raw_body
135
152
  @body = AST.create_node(raw_body, nlenv)
136
153
  else
@@ -141,14 +158,17 @@ module TypeProf::Core
141
158
 
142
159
  h = AST.parse_params(@tbl, raw_args, nlenv)
143
160
  @req_positionals = h[:req_positionals]
161
+ @req_multi_targets = h[:req_multi_targets]
144
162
  @opt_positionals = h[:opt_positionals]
145
163
  @opt_positional_defaults = h[:opt_positional_defaults]
146
164
  @rest_positionals = h[:rest_positionals]
147
165
  @post_positionals = h[:post_positionals]
166
+ @post_multi_targets = h[:post_multi_targets]
148
167
  @req_keywords = h[:req_keywords]
149
168
  @opt_keywords = h[:opt_keywords]
150
169
  @opt_keyword_defaults = h[:opt_keyword_defaults]
151
170
  @rest_keywords = h[:rest_keywords]
171
+ @no_keywords = h[:no_keywords]
152
172
  @block = h[:block]
153
173
  @args_code_ranges = h[:args_code_ranges] || []
154
174
 
@@ -157,7 +177,11 @@ module TypeProf::Core
157
177
  @reusable = !use_result
158
178
  end
159
179
 
160
- attr_reader :singleton, :mid, :mid_code_range
180
+ attr_reader :singleton, :mid
181
+
182
+ def mid_code_range
183
+ @mid_code_range ||= @lenv.code_range_from_node(@mid_code_range_loc) if @mid_code_range_loc
184
+ end
161
185
  attr_reader :tbl
162
186
  attr_reader :req_positionals
163
187
  attr_reader :opt_positionals
@@ -168,6 +192,7 @@ module TypeProf::Core
168
192
  attr_reader :opt_keywords
169
193
  attr_reader :opt_keyword_defaults
170
194
  attr_reader :rest_keywords
195
+ attr_reader :no_keywords
171
196
  attr_reader :block
172
197
  attr_reader :body
173
198
  attr_reader :rbs_method_type
@@ -182,7 +207,6 @@ module TypeProf::Core
182
207
  def attrs = {
183
208
  singleton:,
184
209
  mid:,
185
- mid_code_range:,
186
210
  tbl:,
187
211
  req_positionals:,
188
212
  opt_positionals:,
@@ -191,11 +215,12 @@ module TypeProf::Core
191
215
  req_keywords:,
192
216
  opt_keywords:,
193
217
  rest_keywords:,
218
+ no_keywords:,
194
219
  block:,
195
220
  reusable:,
196
221
  }
197
222
 
198
- def mname_code_range(_name) = @mid_code_range
223
+ def mname_code_range(_name) = mid_code_range
199
224
 
200
225
  def define(genv) # NOT define0
201
226
  return define_copy(genv) if @prev_node && @reusable
@@ -219,6 +244,8 @@ module TypeProf::Core
219
244
  opt_positionals = @opt_positionals.map {|var| @body.lenv.new_var(var, self) }
220
245
  rest_positionals = @rest_positionals ? @body.lenv.new_var(@rest_positionals, self) : nil
221
246
  post_positionals = @post_positionals.map {|var| @body.lenv.new_var(var, self) }
247
+ install_multi_targets(genv, @req_multi_targets, req_positionals)
248
+ install_multi_targets(genv, @post_multi_targets, post_positionals)
222
249
  req_keywords = @req_keywords.map {|var| @body.lenv.new_var(var, self) }
223
250
  opt_keywords = @opt_keywords.map {|var| @body.lenv.new_var(var, self) }
224
251
  rest_keywords = @rest_keywords ? @body.lenv.new_var(@rest_keywords, self) : nil
@@ -247,6 +274,24 @@ module TypeProf::Core
247
274
  block = @body.lenv.new_var(:"*given_block", self)
248
275
  end
249
276
 
277
+ forward_opt_positionals = @opt_positionals.map { Vertex.new(self) }
278
+ forward_rest_positionals = @rest_positionals ? Vertex.new(self) : nil
279
+ forward_opt_keywords = @opt_keywords.map {|_name| Vertex.new(self) }
280
+ forward_rest_keywords = @rest_keywords ? Vertex.new(self) : nil
281
+ forward_block = Vertex.new(self)
282
+ forward_activation = Vertex.new(self)
283
+ @body.lenv.forward_args = ForwardingArguments.new(
284
+ req_positionals,
285
+ forward_opt_positionals,
286
+ forward_rest_positionals,
287
+ post_positionals,
288
+ @req_keywords.zip(req_keywords),
289
+ @opt_keywords.zip(forward_opt_keywords),
290
+ forward_rest_keywords,
291
+ forward_block,
292
+ forward_activation,
293
+ )
294
+
250
295
  if @body
251
296
  @body.lenv.locals[:"*expected_method_ret"] = Vertex.new(self)
252
297
  @body.install(genv)
@@ -265,10 +310,23 @@ module TypeProf::Core
265
310
  )
266
311
 
267
312
  @changes.add_method_def_box(genv, @lenv.cref.cpath, @singleton, @mid, f_args, @body.lenv.return_boxes)
313
+ if @lenv.module_function && !@singleton
314
+ @changes.add_method_def_box(genv, @lenv.cref.cpath, true, @mid, f_args, @body.lenv.return_boxes)
315
+ end
268
316
 
269
317
  Source.new(Type::Symbol.new(genv, @mid))
270
318
  end
271
319
 
320
+ def install_multi_targets(genv, multi_targets, positionals)
321
+ multi_targets.each do |idx, raw_multi_target|
322
+ param_vtx = positionals[idx]
323
+ lefts = raw_multi_target.lefts.map do |n|
324
+ @body.lenv.new_var(n.is_a?(Prism::MultiTargetNode) ? nil : n.name, self)
325
+ end
326
+ @changes.add_masgn_box(genv, param_vtx, lefts, nil, nil)
327
+ end
328
+ end
329
+
272
330
  def last_stmt_code_range
273
331
  if @body
274
332
  if @body.is_a?(AST::StatementsNode)
@@ -6,7 +6,7 @@ module TypeProf::Core
6
6
  stmts = raw_node.body
7
7
  @stmts = stmts.map.with_index do |n, i|
8
8
  if n
9
- AST.create_node(n, lenv, i == stmts.length - 1 ? use_result : false)
9
+ AST.create_node(n, lenv, i == stmts.length - 1 ? use_result : false, true)
10
10
  else
11
11
  last = code_range.last
12
12
  DummyNilNode.new(TypeProf::CodeRange.new(last, last), lenv)
@@ -256,28 +256,59 @@ module TypeProf::Core
256
256
  vtx = @expr.install(genv)
257
257
 
258
258
  a_args = ActualArguments.new([], [], nil, nil)
259
- vtx = @changes.add_method_call_box(genv, vtx, :to_a, a_args, false).ret
260
-
261
- @changes.add_splat_box(genv, vtx).ret
259
+ # Receiver types with no `to_a` are collected here, so that each union
260
+ # member can independently fall back to wrapping itself as `[x]`.
261
+ unresolved_recv = Vertex.new(self)
262
+ to_a_box = @changes.add_method_call_box(
263
+ genv,
264
+ vtx,
265
+ :to_a,
266
+ a_args,
267
+ false,
268
+ suppress_errors: true,
269
+ unresolved_recv: unresolved_recv,
270
+ )
271
+
272
+ @changes.add_splat_box(genv, to_a_box.ret, nil, unresolved_recv).ret
262
273
  end
263
274
  end
264
275
 
265
276
  class ForNode < Node
266
277
  def initialize(raw_node, lenv)
267
278
  super(raw_node, lenv)
268
- # XXX: tentative implementation
269
- # raw_node.index
279
+ @index = AST.create_target_node(raw_node.index, lenv)
270
280
  @expr = AST.create_node(raw_node.collection, lenv)
271
281
  @body = raw_node.statements ? AST.create_node(raw_node.statements, lenv) : DummyNilNode.new(TypeProf::CodeRange.new(code_range.last, code_range.last), lenv)
272
282
  end
273
283
 
274
- attr_reader :expr, :body
284
+ attr_reader :index, :expr, :body
275
285
 
276
- def subnodes = { expr:, body: }
286
+ def subnodes = { index:, expr:, body: }
277
287
 
278
288
  def install0(genv)
279
289
  @expr.install(genv)
290
+
291
+ vars = []
292
+ @index.modified_vars(@lenv.locals.keys, vars)
293
+ @body.modified_vars(@lenv.locals.keys, vars)
294
+ vars.uniq!
295
+
296
+ old_vtxs = {}
297
+ vars.each do |var|
298
+ vtx = @lenv.get_var(var)
299
+ nvtx = vtx.new_vertex(genv, self)
300
+ old_vtxs[var] = nvtx
301
+ @lenv.set_var(var, nvtx)
302
+ end
303
+
304
+ @index.install(genv)
280
305
  @body.install(genv)
306
+
307
+ vars.each do |var|
308
+ @changes.add_edge(genv, @lenv.get_var(var), old_vtxs[var])
309
+ @lenv.set_var(var, old_vtxs[var])
310
+ end
311
+
281
312
  Source.new(genv.nil_type)
282
313
  end
283
314
  end
@@ -312,8 +343,7 @@ module TypeProf::Core
312
343
  def subnodes = { value:, pat: }
313
344
 
314
345
  def install0(genv)
315
- @value.install(genv)
316
- @pat.install(genv)
346
+ @pat.install_pattern(genv, @value.install(genv))
317
347
  Source.new(genv.nil_type)
318
348
  end
319
349
  end
@@ -330,8 +360,7 @@ module TypeProf::Core
330
360
  def subnodes = { value:, pat: }
331
361
 
332
362
  def install0(genv)
333
- @value.install(genv)
334
- @pat.install(genv)
363
+ @pat.install_pattern(genv, @value.install(genv))
335
364
  Source.new(genv.true_type, genv.false_type)
336
365
  end
337
366
  end
@@ -12,17 +12,21 @@ module TypeProf::Core
12
12
 
13
13
  if @static_cpath
14
14
  ncref = CRef.new(@static_cpath, meta ? :metaclass : :class, nil, lenv.cref)
15
- nlenv = LocalEnv.new(@lenv.path, ncref, {}, [])
15
+ nlenv = LocalEnv.new(@lenv.file_context, ncref, {}, [])
16
16
  @body = raw_scope ? AST.create_node(raw_scope, nlenv, use_result) : DummyNilNode.new(code_range, lenv)
17
17
  else
18
18
  @body = nil
19
19
  end
20
20
 
21
- @cname_code_range = meta ? nil : TypeProf::CodeRange.from_node(raw_node.constant_path)
21
+ @cname_code_range_loc = meta ? nil : raw_node.constant_path
22
22
  @mod_cdef = nil
23
23
  end
24
24
 
25
- attr_reader :tbl, :cpath, :static_cpath, :cname_code_range, :body
25
+ attr_reader :tbl, :cpath, :static_cpath, :body
26
+
27
+ def cname_code_range
28
+ @cname_code_range ||= @lenv.code_range_from_node(@cname_code_range_loc) if @cname_code_range_loc
29
+ end
26
30
 
27
31
  def subnodes = { cpath:, body: }
28
32
  def attrs = { static_cpath:, tbl: }
@@ -89,7 +93,22 @@ module TypeProf::Core
89
93
  def initialize(raw_node, lenv, use_result)
90
94
  super(raw_node, lenv, raw_node.constant_path, false, raw_node.body, use_result)
91
95
  raw_superclass = raw_node.superclass
92
- @superclass_cpath = raw_superclass ? AST.create_node(raw_superclass, lenv) : nil
96
+ if raw_superclass
97
+ # In Ruby, the superclass expression is evaluated before the class constant
98
+ # is created. When the superclass is a bare constant with the same name as
99
+ # the class being defined (e.g., `class Foo < Foo` inside a module), use the
100
+ # outer scope to avoid resolving to the class itself.
101
+ if @static_cpath && lenv.cref.outer &&
102
+ raw_superclass.type == :constant_read_node &&
103
+ raw_superclass.name == @static_cpath.last
104
+ slenv = LocalEnv.new(lenv.file_context, lenv.cref.outer, {}, [])
105
+ @superclass_cpath = AST.create_node(raw_superclass, slenv)
106
+ else
107
+ @superclass_cpath = AST.create_node(raw_superclass, lenv)
108
+ end
109
+ else
110
+ @superclass_cpath = nil
111
+ end
93
112
  end
94
113
 
95
114
  attr_reader :superclass_cpath
@@ -113,6 +132,21 @@ module TypeProf::Core
113
132
 
114
133
  def install0(genv)
115
134
  @superclass_cpath.install(genv) if @superclass_cpath
135
+ if @static_cpath && @superclass_cpath
136
+ const_read = @superclass_cpath.static_ret
137
+ if const_read && const_read.cpath
138
+ super_mod = genv.resolve_cpath(const_read.cpath)
139
+ self_mod = genv.resolve_cpath(@static_cpath)
140
+ mod = super_mod
141
+ while mod
142
+ if mod == self_mod
143
+ @changes.add_diagnostic(:code_range, "circular inheritance", @superclass_cpath)
144
+ break
145
+ end
146
+ mod = mod.superclass
147
+ end
148
+ end
149
+ end
116
150
  super(genv)
117
151
  end
118
152
  end
@@ -22,14 +22,19 @@ module TypeProf::Core
22
22
  def attrs = { rest: }
23
23
  def subnodes = { requireds:, rest_pattern:, posts: }
24
24
 
25
- def install0(genv)
26
- @requireds.each do |pat|
27
- pat.install(genv)
25
+ def install_pattern0(genv, subject)
26
+ @requireds.each_with_index do |pat, i|
27
+ pat.install_pattern(genv, @changes.add_splat_box(genv, subject, i).ret)
28
+ end
29
+ if @rest_pattern
30
+ elem = @changes.add_splat_box(genv, subject).ret
31
+ @rest_pattern.install_pattern(genv, Source.new(genv.gen_ary_type(elem)))
28
32
  end
29
- @rest_pattern.install(genv) if @rest_pattern
30
33
  @posts.each do |pat|
31
- pat.install(genv)
34
+ # TODO: precise indices for post elements (those after `*rest`)
35
+ pat.install_pattern(genv, @changes.add_splat_box(genv, subject).ret)
32
36
  end
37
+ subject
33
38
  end
34
39
  end
35
40
 
@@ -39,7 +44,14 @@ module TypeProf::Core
39
44
  @keys = raw_node.elements.map {|raw_assoc| raw_assoc.key.value.to_sym }
40
45
  @values = raw_node.elements.map {|raw_assoc| AST.create_pattern_node(raw_assoc.value, lenv) }
41
46
  @rest = !!raw_node.rest
42
- @rest_pattern = raw_node.rest && raw_node.rest.value ? AST.create_pattern_node(raw_node.rest.value, lenv) : nil
47
+ @rest_pattern = case raw_node.rest
48
+ when Prism::AssocSplatNode
49
+ AST.create_pattern_node(raw_node.rest.value, lenv) if raw_node.rest.value
50
+ when Prism::NoKeywordsParameterNode, nil
51
+ nil
52
+ else
53
+ raise
54
+ end
43
55
  end
44
56
 
45
57
  attr_reader :keys, :values, :rest, :rest_pattern
@@ -47,32 +59,37 @@ module TypeProf::Core
47
59
  def attrs = { keys:, rest: }
48
60
  def subnodes = { values:, rest_pattern: }
49
61
 
50
- def install0(genv)
62
+ def install_pattern0(genv, subject)
63
+ # TODO: extract each key's value type from `subject` (captures stay untyped for now)
51
64
  @values.each do |pat|
52
- pat.install(genv)
65
+ pat.install_pattern(genv, Vertex.new(self))
53
66
  end
54
- @rest_pattern.install(genv) if @rest_pattern
67
+ @rest_pattern.install_pattern(genv, Vertex.new(self)) if @rest_pattern
68
+ subject
55
69
  end
56
70
  end
57
71
 
58
72
  class FindPatternNode < Node
59
73
  def initialize(raw_node, lenv)
60
74
  super(raw_node, lenv)
61
- @left = raw_node.left ? AST.create_pattern_node(raw_node.left.expression, lenv) : nil
75
+ @left = raw_node.left.expression ? AST.create_pattern_node(raw_node.left.expression, lenv) : nil
62
76
  @requireds = raw_node.requireds.map {|raw_elem| AST.create_pattern_node(raw_elem, lenv) }
63
- @right = raw_node.right ? AST.create_pattern_node(raw_node.right.expression, lenv) : nil
77
+ @right = raw_node.right.expression ? AST.create_pattern_node(raw_node.right.expression, lenv) : nil
64
78
  end
65
79
 
66
80
  attr_reader :left, :requireds, :right
67
81
 
68
82
  def subnodes = { left:, requireds:, right: }
69
83
 
70
- def install0(genv)
71
- @left.install(genv) if @left
84
+ def install_pattern0(genv, subject)
85
+ elem = @changes.add_splat_box(genv, subject).ret
86
+ rest_ary = Source.new(genv.gen_ary_type(elem))
87
+ @left.install_pattern(genv, rest_ary) if @left
72
88
  @requireds.each do |pat|
73
- pat.install(genv)
89
+ pat.install_pattern(genv, elem)
74
90
  end
75
- @right.install(genv) if @right
91
+ @right.install_pattern(genv, rest_ary) if @right
92
+ subject
76
93
  end
77
94
  end
78
95
 
@@ -87,9 +104,10 @@ module TypeProf::Core
87
104
 
88
105
  def subnodes = { left:, right: }
89
106
 
90
- def install0(genv)
91
- @left.install(genv)
92
- @right.install(genv)
107
+ def install_pattern0(genv, subject)
108
+ @left.install_pattern(genv, subject)
109
+ @right.install_pattern(genv, subject)
110
+ subject
93
111
  end
94
112
  end
95
113
 
@@ -104,9 +122,18 @@ module TypeProf::Core
104
122
 
105
123
  def subnodes = { value:, target: }
106
124
 
107
- def install0(genv)
108
- @value.install(genv)
109
- @target.install(genv)
125
+ def install_pattern0(genv, subject)
126
+ @value.install_pattern(genv, subject)
127
+ # For `Const => var`, narrow the capture by the class, as `when Const` does
128
+ narrowed =
129
+ if @value.is_a?(ConstantReadNode) && @value.static_ret
130
+ filtered = subject.new_vertex(genv, self)
131
+ IsAFilter.new(genv, self, filtered, false, @value.static_ret).next_vtx
132
+ else
133
+ subject
134
+ end
135
+ @target.install_pattern(genv, narrowed)
136
+ subject
110
137
  end
111
138
  end
112
139
 
@@ -124,9 +151,10 @@ module TypeProf::Core
124
151
 
125
152
  def subnodes = { cond:, body: }
126
153
 
127
- def install0(genv)
154
+ def install_pattern0(genv, subject)
128
155
  @cond.install(genv)
129
- @body.install(genv)
156
+ @body.install_pattern(genv, subject)
157
+ subject
130
158
  end
131
159
  end
132
160