typeprof 0.30.0 → 0.31.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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +23 -4
  3. data/doc/doc.ja.md +1 -1
  4. data/lib/typeprof/cli/cli.rb +28 -10
  5. data/lib/typeprof/cli.rb +1 -0
  6. data/lib/typeprof/code_range.rb +9 -7
  7. data/lib/typeprof/core/ast/base.rb +27 -10
  8. data/lib/typeprof/core/ast/call.rb +90 -21
  9. data/lib/typeprof/core/ast/const.rb +7 -12
  10. data/lib/typeprof/core/ast/control.rb +349 -74
  11. data/lib/typeprof/core/ast/meta.rb +5 -5
  12. data/lib/typeprof/core/ast/method.rb +19 -5
  13. data/lib/typeprof/core/ast/misc.rb +21 -2
  14. data/lib/typeprof/core/ast/module.rb +10 -7
  15. data/lib/typeprof/core/ast/pattern.rb +9 -1
  16. data/lib/typeprof/core/ast/sig_decl.rb +163 -42
  17. data/lib/typeprof/core/ast/sig_type.rb +394 -24
  18. data/lib/typeprof/core/ast/value.rb +11 -3
  19. data/lib/typeprof/core/ast/variable.rb +32 -2
  20. data/lib/typeprof/core/ast.rb +15 -4
  21. data/lib/typeprof/core/builtin.rb +15 -9
  22. data/lib/typeprof/core/env/method.rb +21 -16
  23. data/lib/typeprof/core/env/method_entity.rb +11 -2
  24. data/lib/typeprof/core/env/module_entity.rb +57 -0
  25. data/lib/typeprof/core/env/narrowing.rb +131 -0
  26. data/lib/typeprof/core/env/static_read.rb +9 -8
  27. data/lib/typeprof/core/env.rb +43 -12
  28. data/lib/typeprof/core/graph/box.rb +218 -101
  29. data/lib/typeprof/core/graph/change_set.rb +44 -37
  30. data/lib/typeprof/core/graph/vertex.rb +7 -21
  31. data/lib/typeprof/core/service.rb +61 -40
  32. data/lib/typeprof/core/type.rb +52 -123
  33. data/lib/typeprof/core.rb +1 -1
  34. data/lib/typeprof/diagnostic.rb +5 -6
  35. data/lib/typeprof/lsp/messages.rb +27 -36
  36. data/lib/typeprof/lsp/server.rb +144 -25
  37. data/lib/typeprof/lsp/text.rb +1 -0
  38. data/lib/typeprof/lsp/util.rb +0 -10
  39. data/lib/typeprof/version.rb +1 -1
  40. data/typeprof.conf.jsonc +22 -0
  41. data/typeprof.gemspec +1 -0
  42. metadata +19 -5
  43. data/lib/typeprof/core/graph.rb +0 -3
@@ -13,6 +13,42 @@ module TypeProf::Core
13
13
  return nil
14
14
  end
15
15
 
16
+ # Apply multiple narrowings from the new narrowing system
17
+ def self.with_narrowing(genv, node, lenv, narrowing)
18
+ return yield if narrowing.map.empty?
19
+
20
+ # Store original vertices (only for local variables)
21
+ original_vtxs = {}
22
+ narrowing.map.each do |var, narrowing|
23
+ original_vtxs[var] = var.start_with?("@") ? nil : lenv.get_var(var)
24
+ end
25
+
26
+ # Apply all narrowings
27
+ narrowing.map.each do |var, narrowing|
28
+ if var.start_with?("@")
29
+ lenv.push_ivar_narrowing(var, narrowing)
30
+ else
31
+ original_vtx = original_vtxs[var]
32
+ narrowed_vtx = original_vtx.new_vertex(genv, node)
33
+ narrowed_vtx = narrowing.narrow(genv, node, narrowed_vtx)
34
+ lenv.set_var(var, narrowed_vtx)
35
+ end
36
+ end
37
+
38
+ result = yield
39
+
40
+ # Restore original vertices and remove instance variable filters
41
+ original_vtxs.each do |var, original_vtx|
42
+ if var.start_with?("@")
43
+ lenv.pop_ivar_narrowing(var)
44
+ else
45
+ lenv.set_var(var, original_vtx)
46
+ end
47
+ end
48
+
49
+ result
50
+ end
51
+
16
52
  class BranchNode < Node
17
53
  def initialize(raw_node, lenv)
18
54
  super(raw_node, lenv)
@@ -37,11 +73,11 @@ module TypeProf::Core
37
73
  @cond.install(genv)
38
74
 
39
75
  vars = []
40
- vars << @cond.var if @cond.is_a?(LocalVariableReadNode)
41
- var, filter_class = AST.is_a_class(@cond)
42
- vars << var if var
43
76
  @then.modified_vars(@lenv.locals.keys, vars) if @then
44
77
  @else.modified_vars(@lenv.locals.keys, vars) if @else
78
+ then_narrowing, else_narrowing = @cond.narrowings
79
+ vars.concat(then_narrowing.map.keys.reject {|var| var.start_with?("@") })
80
+ vars.concat(else_narrowing.map.keys.reject {|var| var.start_with?("@") })
45
81
  modified_vtxs = {}
46
82
  vars.uniq.each do |var|
47
83
  vtx = @lenv.get_var(var)
@@ -49,48 +85,30 @@ module TypeProf::Core
49
85
  nvtx_else = vtx.new_vertex(genv, self)
50
86
  modified_vtxs[var] = [nvtx_then, nvtx_else]
51
87
  end
52
- if @cond.is_a?(LocalVariableReadNode)
53
- nvtx_then, nvtx_else = modified_vtxs[@cond.var]
54
- nvtx_then = NilFilter.new(genv, self, nvtx_then, !self.is_a?(IfNode)).next_vtx
55
- nvtx_else = NilFilter.new(genv, self, nvtx_else, self.is_a?(IfNode)).next_vtx
56
- modified_vtxs[@cond.var] = [nvtx_then, nvtx_else]
57
- end
58
- if filter_class
59
- nvtx_then, nvtx_else = modified_vtxs[var]
60
- nvtx_then = IsAFilter.new(genv, self, nvtx_then, !self.is_a?(IfNode), filter_class).next_vtx
61
- nvtx_else = IsAFilter.new(genv, self, nvtx_else, self.is_a?(IfNode), filter_class).next_vtx
62
- modified_vtxs[var] = [nvtx_then, nvtx_else]
63
- end
64
88
 
65
- if @then
66
- modified_vtxs.each do |var, (nvtx_then, _)|
67
- @lenv.set_var(var, nvtx_then)
68
- end
69
- if @cond.is_a?(InstanceVariableReadNode)
70
- @lenv.push_read_filter(@cond.var, :non_nil)
71
- end
72
- then_val = @then.install(genv)
73
- if @cond.is_a?(InstanceVariableReadNode)
74
- @lenv.pop_read_filter(@cond.var)
75
- end
89
+ narrowing = self.is_a?(IfNode) ? then_narrowing : else_narrowing
90
+ modified_vtxs.each do |var, (nvtx_then, _)|
91
+ @lenv.set_var(var, nvtx_then)
92
+ end
93
+ then_val = AST.with_narrowing(genv, self, @lenv, narrowing) do
94
+ val = @then ? @then.install(genv) : Source.new(genv.nil_type)
76
95
  modified_vtxs.each do |var, ary|
77
96
  ary[0] = @lenv.get_var(var)
78
97
  end
79
- else
80
- then_val = Source.new(genv.nil_type)
98
+ val
81
99
  end
82
100
  @changes.add_edge(genv, then_val, ret)
83
101
 
84
- if @else
85
- modified_vtxs.each do |var, (_, nvtx_else)|
86
- @lenv.set_var(var, nvtx_else)
87
- end
88
- else_val = @else.install(genv)
102
+ narrowing = self.is_a?(IfNode) ? else_narrowing : then_narrowing
103
+ modified_vtxs.each do |var, (_, nvtx_else)|
104
+ @lenv.set_var(var, nvtx_else)
105
+ end
106
+ else_val = AST.with_narrowing(genv, self, @lenv, narrowing) do
107
+ val = @else ? @else.install(genv) : Source.new(genv.nil_type)
89
108
  modified_vtxs.each do |var, ary|
90
109
  ary[1] = @lenv.get_var(var)
91
110
  end
92
- else
93
- else_val = Source.new(genv.nil_type)
111
+ val
94
112
  end
95
113
  @changes.add_edge(genv, else_val, ret)
96
114
 
@@ -176,7 +194,7 @@ module TypeProf::Core
176
194
  class BreakNode < Node
177
195
  def initialize(raw_node, lenv)
178
196
  super(raw_node, lenv)
179
- @arg = raw_node.arguments ? AST.create_node(raw_node.arguments.arguments.first, lenv) : nil
197
+ @arg = AST.parse_return_arguments(raw_node, lenv, code_range)
180
198
  end
181
199
 
182
200
  attr_reader :arg
@@ -184,8 +202,9 @@ module TypeProf::Core
184
202
  def subnodes = { arg: }
185
203
 
186
204
  def install0(genv)
187
- _arg = @arg ? @arg.install(genv) : Source.new(genv.nil_type)
188
- # TODO: implement!
205
+ arg = @arg.install(genv)
206
+ @changes.add_edge(genv, arg, @lenv.get_break_vtx)
207
+ Source.new()
189
208
  end
190
209
  end
191
210
 
@@ -215,7 +234,7 @@ module TypeProf::Core
215
234
  def install0(genv)
216
235
  @arg.install(genv)
217
236
  if @lenv.exist_var?(:"*expected_block_ret")
218
- @lenv.add_next_box(@changes.add_escape_box(genv, @arg.ret, @lenv.get_var(:"*expected_block_ret")))
237
+ @lenv.add_next_box(@changes.add_escape_box(genv, @arg.ret))
219
238
  end
220
239
  Source.new(Type::Bot.new(genv))
221
240
  end
@@ -232,31 +251,115 @@ module TypeProf::Core
232
251
  end
233
252
  end
234
253
 
254
+ class WhenNode < Node
255
+ def initialize(raw_when_node, lenv, pivot_var = nil)
256
+ super(raw_when_node, lenv)
257
+ @conditions = raw_when_node.conditions.map {|cond| AST.create_node(cond, lenv) }
258
+ @body = raw_when_node.statements ? AST.create_node(raw_when_node.statements, lenv) : DummyNilNode.new(code_range, lenv)
259
+ @pivot_var = pivot_var
260
+ end
261
+
262
+ attr_reader :conditions, :body, :pivot_var
263
+
264
+ def subnodes = { conditions:, body: }
265
+
266
+ def install0(genv)
267
+ @conditions.each {|condition| condition.install(genv) }
268
+
269
+ # 型絞り込みが必要な場合(pivot_varが設定されている場合)
270
+ if @pivot_var && @lenv.locals.key?(:"*pivot")
271
+ original_vtx = @lenv.locals[:"*pivot"]
272
+
273
+ # 複数条件のOR(union)処理
274
+ filtered_vtxs = []
275
+
276
+ @conditions.each do |condition|
277
+ if condition.is_a?(ConstantReadNode) && condition.static_ret
278
+ # 各条件に対して独立して型絞り込みを適用
279
+ condition_vtx = original_vtx.new_vertex(genv, self)
280
+ condition_vtx = IsAFilter.new(genv, self, condition_vtx, false, condition.static_ret).next_vtx
281
+ filtered_vtxs << condition_vtx
282
+ end
283
+ end
284
+
285
+ # 複数の絞り込み結果をマージして使用
286
+ if !filtered_vtxs.empty?
287
+ merged_vtx = Vertex.new(self)
288
+ filtered_vtxs.each do |vtx|
289
+ @changes.add_edge(genv, vtx, merged_vtx)
290
+ end
291
+ @lenv.set_var(@pivot_var, merged_vtx)
292
+ end
293
+ end
294
+
295
+ @body.install(genv)
296
+ end
297
+
298
+ # else節での型除外に使用する条件を取得
299
+ def get_exclusion_conditions
300
+ @conditions.select {|condition| condition.is_a?(ConstantReadNode) && condition.static_ret }
301
+ .map {|condition| condition.static_ret }
302
+ end
303
+ end
304
+
235
305
  class CaseNode < Node
236
306
  def initialize(raw_node, lenv)
237
307
  super(raw_node, lenv)
238
308
  @pivot = raw_node.predicate ? AST.create_node(raw_node.predicate, lenv) : nil
239
- @whens = []
240
- @clauses = []
241
- raw_node.conditions.each do |raw_cond|
242
- @whens << AST.create_node(raw_cond.conditions.first, lenv) # XXX: multiple conditions
243
- @clauses << (raw_cond.statements ? AST.create_node(raw_cond.statements, lenv) : DummyNilNode.new(code_range, lenv)) # TODO: code_range for NilNode
244
- end
309
+
310
+ # pivot変数名を決定
311
+ pivot_var = @pivot.is_a?(LocalVariableReadNode) ? @pivot.var : nil
312
+
313
+ @when_nodes = raw_node.conditions.map {|raw_cond| WhenNode.new(raw_cond, lenv, pivot_var) }
245
314
  @else_clause = raw_node.else_clause && raw_node.else_clause.statements ? AST.create_node(raw_node.else_clause.statements, lenv) : DummyNilNode.new(code_range, lenv) # TODO: code_range for NilNode
246
315
  end
247
316
 
248
- attr_reader :pivot, :whens, :clauses, :else_clause
317
+ attr_reader :pivot, :when_nodes, :else_clause
249
318
 
250
- def subnodes = { pivot:, whens:, clauses:, else_clause: }
319
+ def subnodes = { pivot:, when_nodes:, else_clause: }
251
320
 
252
321
  def install0(genv)
253
322
  ret = Vertex.new(self)
254
323
  @pivot&.install(genv)
255
- @whens.zip(@clauses) do |vals, clause|
256
- vals.install(genv)
257
- @changes.add_edge(genv, clause.install(genv), ret)
324
+
325
+ # case文での型絞り込みを実装
326
+ if @pivot && @pivot.is_a?(LocalVariableReadNode)
327
+ var = @pivot.var
328
+ original_vtx = @lenv.get_var(var)
329
+
330
+ # ダミー変数に元の型情報を設定
331
+ @lenv.set_var(:"*pivot", original_vtx)
332
+
333
+ # 各when節を実行
334
+ @when_nodes.each do |when_node|
335
+ clause_result = when_node.install(genv)
336
+ @changes.add_edge(genv, clause_result, ret)
337
+ # 元の型に戻す
338
+ @lenv.set_var(var, original_vtx)
339
+ end
340
+
341
+ # else節(他のwhen節で除外された後の型)
342
+ filtered_else_vtx = original_vtx.new_vertex(genv, self)
343
+ @when_nodes.each do |when_node|
344
+ when_node.get_exclusion_conditions.each do |static_ret|
345
+ # 各when節の型を除外(negation)
346
+ filtered_else_vtx = IsAFilter.new(genv, self, filtered_else_vtx, true, static_ret).next_vtx
347
+ end
348
+ end
349
+ @lenv.set_var(var, filtered_else_vtx)
350
+ @changes.add_edge(genv, @else_clause.install(genv), ret)
351
+ @lenv.set_var(var, original_vtx)
352
+
353
+ # ダミー変数をクリア
354
+ @lenv.locals.delete(:"*pivot")
355
+ else
356
+ # pivotが変数でない場合は従来通り
357
+ @when_nodes.each do |when_node|
358
+ @changes.add_edge(genv, when_node.install(genv), ret)
359
+ end
360
+ @changes.add_edge(genv, @else_clause.install(genv), ret)
258
361
  end
259
- @changes.add_edge(genv, @else_clause.install(genv), ret)
362
+
260
363
  ret
261
364
  end
262
365
  end
@@ -292,8 +395,9 @@ module TypeProf::Core
292
395
  end
293
396
 
294
397
  class AndNode < Node
295
- def initialize(raw_node, e1 = nil, raw_e2 = nil, lenv)
398
+ def initialize(raw_node, e1 = nil, raw_e2 = raw_node.right, lenv)
296
399
  super(raw_node, lenv)
400
+
297
401
  @e1 = e1 || AST.create_node(raw_node.left, lenv)
298
402
  @e2 = AST.create_node(raw_e2 || raw_node.right, lenv)
299
403
  end
@@ -304,15 +408,39 @@ module TypeProf::Core
304
408
 
305
409
  def install0(genv)
306
410
  ret = Vertex.new(self)
307
- @changes.add_edge(genv, @e1.install(genv), ret)
308
- @changes.add_edge(genv, @e2.install(genv), ret)
411
+
412
+ v1 = @e1.install(genv)
413
+
414
+ # For AND: if left side is truthy, apply its narrowing to right side
415
+ # Use legacy detect_narrowing for now to maintain compatibility
416
+ then_narrowing, _else_narrowing = @e1.narrowings
417
+ if then_narrowing
418
+ v2 = AST.with_narrowing(genv, self, @lenv, then_narrowing) do
419
+ @e2.install(genv)
420
+ end
421
+ else
422
+ v2 = @e2.install(genv)
423
+ end
424
+
425
+ @changes.add_edge(genv, v1, ret)
426
+ @changes.add_edge(genv, v2, ret)
427
+
309
428
  ret
310
429
  end
430
+
431
+ def narrowings
432
+ @narrowings ||= begin
433
+ e1_then_narrowing, e1_else_narrowing = @e1.narrowings
434
+ e2_then_narrowing, e2_else_narrowing = @e2.narrowings
435
+ [e1_then_narrowing.and(e2_then_narrowing), e1_else_narrowing.or(e2_else_narrowing)]
436
+ end
437
+ end
311
438
  end
312
439
 
313
440
  class OrNode < Node
314
- def initialize(raw_node, e1 = nil, raw_e2 = nil, lenv)
441
+ def initialize(raw_node, e1 = nil, raw_e2 = raw_node.right, lenv)
315
442
  super(raw_node, lenv)
443
+
316
444
  @e1 = e1 || AST.create_node(raw_node.left, lenv)
317
445
  @e2 = AST.create_node(raw_e2 || raw_node.right, lenv)
318
446
  end
@@ -323,12 +451,35 @@ module TypeProf::Core
323
451
 
324
452
  def install0(genv)
325
453
  ret = Vertex.new(self)
454
+
326
455
  v1 = @e1.install(genv)
327
456
  v1 = NilFilter.new(genv, self, v1, false).next_vtx
457
+
458
+ # For OR: if left side is falsy, apply negated narrowing to right side
459
+ # Use legacy detect_narrowing for now to maintain compatibility
460
+ _then_narrowing, else_narrowing = @e1.narrowings
461
+ if else_narrowing
462
+ # For OR: negated narrowing (negate: true)
463
+ v2 = AST.with_narrowing(genv, self, @lenv, else_narrowing) do
464
+ @e2.install(genv)
465
+ end
466
+ else
467
+ v2 = @e2.install(genv)
468
+ end
469
+
328
470
  @changes.add_edge(genv, v1, ret)
329
- @changes.add_edge(genv, @e2.install(genv), ret)
471
+ @changes.add_edge(genv, v2, ret)
472
+
330
473
  ret
331
474
  end
475
+
476
+ def narrowings
477
+ @narrowings ||= begin
478
+ e1_then_narrowing, e1_else_narrowing = @e1.narrowings
479
+ e2_then_narrowing, e2_else_narrowing = @e2.narrowings
480
+ [e1_then_narrowing.or(e2_then_narrowing), e1_else_narrowing.and(e2_else_narrowing)]
481
+ end
482
+ end
332
483
  end
333
484
 
334
485
  class ReturnNode < Node
@@ -344,38 +495,86 @@ module TypeProf::Core
344
495
  def install0(genv)
345
496
  @arg.install(genv)
346
497
  e_ret = @lenv.locals[:"*expected_method_ret"]
347
- @lenv.add_return_box(@changes.add_escape_box(genv, @arg.ret, e_ret)) if e_ret
498
+ @lenv.add_return_box(@changes.add_escape_box(genv, @arg.ret)) if e_ret
348
499
  Source.new(Type::Bot.new(genv))
349
500
  end
350
501
  end
351
502
 
503
+ class RescueNode < Node
504
+ def initialize(raw_node, lenv)
505
+ super(raw_node, lenv)
506
+
507
+ @exceptions = raw_node.exceptions.map {|raw_cond| AST.create_node(raw_cond, lenv) }
508
+ @statements = AST.create_node(raw_node.statements, lenv) if raw_node.statements
509
+ if raw_node.reference && @statements
510
+ @reference = AST.create_target_node(raw_node.reference, @statements.lenv)
511
+ end
512
+ end
513
+
514
+ attr_reader :exceptions, :reference, :statements
515
+
516
+ def subnodes = { exceptions:, reference:, statements: }
517
+
518
+ def define0(genv)
519
+ @exceptions.each {|exc| exc.define(genv) }
520
+ @reference.define(genv) if @reference
521
+ @statements.define(genv) if @statements
522
+ end
523
+
524
+ def undefine0(genv)
525
+ @exceptions.each {|exc| exc.undefine(genv) }
526
+ @reference.undefine(genv) if @reference
527
+ @statements.undefine(genv) if @statements
528
+ end
529
+
530
+ def install0(genv)
531
+ cond_vtxs = @exceptions.map do |exc|
532
+ case exc
533
+ when AST::SplatNode
534
+ ary_vtx = exc.expr.install(genv)
535
+ @changes.add_splat_box(genv, ary_vtx).ret
536
+ else
537
+ exc.install(genv)
538
+ end
539
+ end
540
+
541
+ if @reference
542
+ @reference.install(genv)
543
+ cond_vtxs.each do |cond_vtx|
544
+ instance_ty_box = @changes.add_instance_type_box(genv, cond_vtx)
545
+ @changes.add_edge(genv, instance_ty_box.ret, @reference.rhs.ret)
546
+ end
547
+ end
548
+
549
+ if @statements
550
+ @statements.install(genv)
551
+ else
552
+ Source.new(genv.nil_type)
553
+ end
554
+ end
555
+ end
556
+
352
557
  class BeginNode < Node
353
558
  def initialize(raw_node, lenv)
354
559
  super(raw_node, lenv)
355
560
  @body = raw_node.statements ? AST.create_node(raw_node.statements, lenv) : DummyNilNode.new(code_range, lenv)
356
- @rescue_conds = []
561
+
357
562
  @rescue_clauses = []
358
563
  raw_res = raw_node.rescue_clause
359
564
  while raw_res
360
- raw_res.exceptions.each do |raw_cond|
361
- @rescue_conds << AST.create_node(raw_cond, lenv)
362
- end
363
- if raw_res.statements
364
- @rescue_clauses << AST.create_node(raw_res.statements, lenv)
365
- end
565
+ @rescue_clauses << AST.create_node(raw_res, lenv)
366
566
  raw_res = raw_res.subsequent
367
567
  end
368
- @else_clause = raw_node.else_clause ? AST.create_node(raw_node.else_clause.statements, lenv) : DummyNilNode.new(code_range, lenv)
369
- @ensure_clause = raw_node.ensure_clause ? AST.create_node(raw_node.ensure_clause.statements, lenv) : DummyNilNode.new(code_range, lenv)
568
+ @else_clause = AST.create_node(raw_node.else_clause.statements, lenv) if raw_node.else_clause&.statements
569
+ @ensure_clause = AST.create_node(raw_node.ensure_clause.statements, lenv) if raw_node.ensure_clause&.statements
370
570
  end
371
571
 
372
- attr_reader :body, :rescue_conds, :rescue_clauses, :else_clause, :ensure_clause
572
+ attr_reader :body, :rescue_clauses, :else_clause, :ensure_clause
373
573
 
374
- def subnodes = { body:, rescue_conds:, rescue_clauses:, else_clause:, ensure_clause: }
574
+ def subnodes = { body:, rescue_clauses:, else_clause:, ensure_clause: }
375
575
 
376
576
  def define0(genv)
377
577
  @body.define(genv)
378
- @rescue_conds.each {|cond| cond.define(genv) }
379
578
  @rescue_clauses.each {|clause| clause.define(genv) }
380
579
  @else_clause.define(genv) if @else_clause
381
580
  @ensure_clause.define(genv) if @ensure_clause
@@ -383,7 +582,6 @@ module TypeProf::Core
383
582
 
384
583
  def undefine0(genv)
385
584
  @body.undefine(genv)
386
- @rescue_conds.each {|cond| cond.undefine(genv) }
387
585
  @rescue_clauses.each {|clause| clause.undefine(genv) }
388
586
  @else_clause.undefine(genv) if @else_clause
389
587
  @ensure_clause.undefine(genv) if @ensure_clause
@@ -391,11 +589,88 @@ module TypeProf::Core
391
589
 
392
590
  def install0(genv)
393
591
  ret = Vertex.new(self)
592
+
593
+ vars = []
594
+ @body.modified_vars(@lenv.locals.keys, vars) if @body
595
+ vars.uniq!
596
+
597
+ old_vtxs = {}
598
+ vars.each do |var|
599
+ vtx = @lenv.get_var(var)
600
+ old_vtxs[var] = vtx
601
+ end
602
+
394
603
  @changes.add_edge(genv, @body.install(genv), ret)
395
- @rescue_conds.each {|cond| cond.install(genv) }
396
- @rescue_clauses.each {|clause| @changes.add_edge(genv, clause.install(genv), ret) }
397
- @changes.add_edge(genv, @else_clause.install(genv), ret) if @else_clause
398
- @ensure_clause.install(genv) if @ensure_clause
604
+
605
+ body_vtxs = {}
606
+ vars.each do |var|
607
+ body_vtxs[var] = @lenv.get_var(var)
608
+ end
609
+
610
+ clause_vtxs_list = []
611
+ @rescue_clauses.each do |clause|
612
+ vars.each do |var|
613
+ old_vtx = old_vtxs[var]
614
+ nvtx = old_vtx.new_vertex(genv, self)
615
+
616
+ @changes.add_edge(genv, body_vtxs[var], nvtx) unless body_vtxs[var] == old_vtxs[var]
617
+
618
+ @lenv.set_var(var, nvtx)
619
+ end
620
+
621
+ @changes.add_edge(genv, clause.install(genv), ret)
622
+
623
+ clause_vtxs_list << {}
624
+ vars.each do |var|
625
+ clause_vtxs_list.last[var] = @lenv.get_var(var)
626
+ end
627
+ end
628
+
629
+ if @else_clause
630
+ vars.each do |var|
631
+ @lenv.set_var(var, body_vtxs[var])
632
+ end
633
+ @changes.add_edge(genv, @else_clause.install(genv), ret)
634
+ clause_vtxs_list << {}
635
+ vars.each do |var|
636
+ clause_vtxs_list.last[var] = @lenv.get_var(var)
637
+ end
638
+ end
639
+
640
+ if @ensure_clause
641
+ vars.each do |var|
642
+ union_vtx = old_vtxs[var].new_vertex(genv, self)
643
+ @changes.add_edge(genv, body_vtxs[var], union_vtx)
644
+ clause_vtxs_list.each do |clause_vtx|
645
+ @changes.add_edge(genv, clause_vtx[var], union_vtx)
646
+ end
647
+ @lenv.set_var(var, union_vtx)
648
+ end
649
+
650
+ @ensure_clause.install(genv)
651
+
652
+ clause_vtxs_list << {}
653
+ vars.each do |var|
654
+ clause_vtxs_list.last[var] = @lenv.get_var(var)
655
+ end
656
+ end
657
+
658
+ result_vtxs = {}
659
+ vars.each do |var|
660
+ result_vtx = old_vtxs[var].new_vertex(genv, self)
661
+ result_vtxs[var] = result_vtx
662
+
663
+ @changes.add_edge(genv, body_vtxs[var], result_vtx) unless body_vtxs[var] == old_vtxs[var]
664
+
665
+ clause_vtxs_list.each do |clause_vtx|
666
+ @changes.add_edge(genv, clause_vtx[var], result_vtx)
667
+ end
668
+ end
669
+
670
+ vars.each do |var|
671
+ @lenv.set_var(var, result_vtxs[var])
672
+ end
673
+
399
674
  ret
400
675
  end
401
676
  end
@@ -6,7 +6,9 @@ module TypeProf::Core
6
6
  # TODO: error for splat
7
7
  @args = raw_node.arguments.arguments.map do |raw_arg|
8
8
  next if raw_arg.is_a?(Prism::SplatNode)
9
- AST.create_node(raw_arg, lenv)
9
+ lenv.use_strict_const_scope do
10
+ AST.create_node(raw_arg, lenv)
11
+ end
10
12
  end.compact
11
13
  # TODO: error for non-LIT
12
14
  # TODO: fine-grained hover
@@ -77,8 +79,7 @@ module TypeProf::Core
77
79
  @args.each do |arg|
78
80
  ivar_name = :"@#{ arg }"
79
81
  ivar_box = @changes.add_ivar_read_box(genv, @lenv.cref.cpath, false, ivar_name)
80
- e_ret = Vertex.new(self)
81
- ret_box = @changes.add_escape_box(genv, ivar_box.ret, e_ret)
82
+ ret_box = @changes.add_escape_box(genv, ivar_box.ret)
82
83
  @changes.add_method_def_box(genv, @lenv.cref.cpath, false, arg, FormalArguments::Empty, [ret_box])
83
84
  end
84
85
  Source.new
@@ -134,8 +135,7 @@ module TypeProf::Core
134
135
  def install0(genv)
135
136
  @args.zip(@static_ret) do |arg, ive|
136
137
  ivar_box = @changes.add_ivar_read_box(genv, @lenv.cref.cpath, false, :"@#{ arg }")
137
- e_ret = Vertex.new(self)
138
- ret_box = @changes.add_escape_box(genv, ivar_box.ret, e_ret)
138
+ ret_box = @changes.add_escape_box(genv, ivar_box.ret)
139
139
  @changes.add_method_def_box(genv, @lenv.cref.cpath, false, arg, FormalArguments::Empty, [ret_box])
140
140
 
141
141
  vtx = Vertex.new(self)
@@ -64,7 +64,7 @@ module TypeProf::Core
64
64
 
65
65
  post_positionals = raw_args.posts.map {|n| (n.is_a?(Prism::MultiTargetNode) ? nil : n.name) }
66
66
 
67
- rest_positionals = raw_args.rest&.name
67
+ rest_positionals = raw_args.rest ? (raw_args.rest.name || :"*anonymous_rest") : nil
68
68
 
69
69
  req_keywords = []
70
70
  opt_keywords = []
@@ -82,7 +82,11 @@ module TypeProf::Core
82
82
 
83
83
  case raw_args.keyword_rest
84
84
  when Prism::KeywordRestParameterNode
85
- rest_keywords = raw_args.keyword_rest.name if raw_args.keyword_rest
85
+ rest_keywords = raw_args.keyword_rest.name || :"**anonymous_keyword"
86
+ when Prism::ForwardingParameterNode
87
+ # TODO: The variable names might be subject to change when supporting the propagation of parameter values to the graph during method calls.
88
+ rest_positionals = :"..."
89
+ rest_keywords = :"..."
86
90
  when Prism::NoKeywordsParameterNode
87
91
  # what to do?
88
92
  when nil
@@ -91,7 +95,7 @@ module TypeProf::Core
91
95
  raise "unexpected keyword rest: #{ raw_args.keyword_rest.class }"
92
96
  end
93
97
 
94
- block = raw_args.block.name if raw_args.block
98
+ block = raw_args.block.name || :"*anonymous_block" if raw_args.block
95
99
 
96
100
  {
97
101
  req_positionals:,
@@ -220,6 +224,16 @@ module TypeProf::Core
220
224
  rest_keywords = @rest_keywords ? @body.lenv.new_var(@rest_keywords, self) : nil
221
225
  block = @block ? @body.lenv.new_var(@block, self) : nil
222
226
 
227
+ if rest_positionals
228
+ @changes.add_edge(genv, Source.new(genv.gen_ary_type(Vertex.new(self))), rest_positionals)
229
+ end
230
+
231
+ if rest_keywords
232
+ if @rest_keywords == :"**anonymous_keyword"
233
+ @changes.add_edge(genv, Source.new(genv.gen_hash_type(Vertex.new(self), Vertex.new(self))), rest_keywords)
234
+ end
235
+ end
236
+
223
237
  @opt_positional_defaults.zip(opt_positionals) do |expr, vtx|
224
238
  @changes.add_edge(genv, expr.install(genv), vtx)
225
239
  end
@@ -234,9 +248,9 @@ module TypeProf::Core
234
248
  end
235
249
 
236
250
  if @body
237
- e_ret = @body.lenv.locals[:"*expected_method_ret"] = Vertex.new(self)
251
+ @body.lenv.locals[:"*expected_method_ret"] = Vertex.new(self)
238
252
  @body.install(genv)
239
- @body.lenv.add_return_box(@changes.add_escape_box(genv, @body.ret, e_ret))
253
+ @body.lenv.add_return_box(@changes.add_escape_box(genv, @body.ret))
240
254
  end
241
255
 
242
256
  f_args = FormalArguments.new(