typeprof 0.30.1 → 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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +23 -4
  3. data/lib/typeprof/cli/cli.rb +27 -7
  4. data/lib/typeprof/code_range.rb +9 -7
  5. data/lib/typeprof/core/ast/base.rb +27 -10
  6. data/lib/typeprof/core/ast/call.rb +85 -24
  7. data/lib/typeprof/core/ast/const.rb +7 -12
  8. data/lib/typeprof/core/ast/control.rb +345 -71
  9. data/lib/typeprof/core/ast/meta.rb +5 -5
  10. data/lib/typeprof/core/ast/method.rb +15 -5
  11. data/lib/typeprof/core/ast/misc.rb +21 -2
  12. data/lib/typeprof/core/ast/module.rb +10 -7
  13. data/lib/typeprof/core/ast/pattern.rb +9 -1
  14. data/lib/typeprof/core/ast/sig_decl.rb +163 -42
  15. data/lib/typeprof/core/ast/sig_type.rb +394 -24
  16. data/lib/typeprof/core/ast/value.rb +10 -2
  17. data/lib/typeprof/core/ast/variable.rb +32 -2
  18. data/lib/typeprof/core/ast.rb +15 -4
  19. data/lib/typeprof/core/builtin.rb +15 -9
  20. data/lib/typeprof/core/env/method.rb +21 -16
  21. data/lib/typeprof/core/env/method_entity.rb +11 -2
  22. data/lib/typeprof/core/env/module_entity.rb +57 -0
  23. data/lib/typeprof/core/env/narrowing.rb +131 -0
  24. data/lib/typeprof/core/env/static_read.rb +9 -8
  25. data/lib/typeprof/core/env.rb +37 -12
  26. data/lib/typeprof/core/graph/box.rb +207 -97
  27. data/lib/typeprof/core/graph/change_set.rb +44 -37
  28. data/lib/typeprof/core/graph/vertex.rb +4 -21
  29. data/lib/typeprof/core/service.rb +30 -1
  30. data/lib/typeprof/core/type.rb +48 -123
  31. data/lib/typeprof/core.rb +1 -0
  32. data/lib/typeprof/diagnostic.rb +5 -6
  33. data/lib/typeprof/lsp/messages.rb +21 -15
  34. data/lib/typeprof/lsp/server.rb +132 -39
  35. data/lib/typeprof/lsp/text.rb +1 -0
  36. data/lib/typeprof/version.rb +1 -1
  37. data/typeprof.conf.jsonc +22 -0
  38. data/typeprof.gemspec +1 -0
  39. metadata +19 -6
@@ -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
 
@@ -216,7 +234,7 @@ module TypeProf::Core
216
234
  def install0(genv)
217
235
  @arg.install(genv)
218
236
  if @lenv.exist_var?(:"*expected_block_ret")
219
- @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))
220
238
  end
221
239
  Source.new(Type::Bot.new(genv))
222
240
  end
@@ -233,31 +251,115 @@ module TypeProf::Core
233
251
  end
234
252
  end
235
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
+
236
305
  class CaseNode < Node
237
306
  def initialize(raw_node, lenv)
238
307
  super(raw_node, lenv)
239
308
  @pivot = raw_node.predicate ? AST.create_node(raw_node.predicate, lenv) : nil
240
- @whens = []
241
- @clauses = []
242
- raw_node.conditions.each do |raw_cond|
243
- @whens << AST.create_node(raw_cond.conditions.first, lenv) # XXX: multiple conditions
244
- @clauses << (raw_cond.statements ? AST.create_node(raw_cond.statements, lenv) : DummyNilNode.new(code_range, lenv)) # TODO: code_range for NilNode
245
- 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) }
246
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
247
315
  end
248
316
 
249
- attr_reader :pivot, :whens, :clauses, :else_clause
317
+ attr_reader :pivot, :when_nodes, :else_clause
250
318
 
251
- def subnodes = { pivot:, whens:, clauses:, else_clause: }
319
+ def subnodes = { pivot:, when_nodes:, else_clause: }
252
320
 
253
321
  def install0(genv)
254
322
  ret = Vertex.new(self)
255
323
  @pivot&.install(genv)
256
- @whens.zip(@clauses) do |vals, clause|
257
- vals.install(genv)
258
- @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)
259
361
  end
260
- @changes.add_edge(genv, @else_clause.install(genv), ret)
362
+
261
363
  ret
262
364
  end
263
365
  end
@@ -293,8 +395,9 @@ module TypeProf::Core
293
395
  end
294
396
 
295
397
  class AndNode < Node
296
- def initialize(raw_node, e1 = nil, raw_e2 = nil, lenv)
398
+ def initialize(raw_node, e1 = nil, raw_e2 = raw_node.right, lenv)
297
399
  super(raw_node, lenv)
400
+
298
401
  @e1 = e1 || AST.create_node(raw_node.left, lenv)
299
402
  @e2 = AST.create_node(raw_e2 || raw_node.right, lenv)
300
403
  end
@@ -305,15 +408,39 @@ module TypeProf::Core
305
408
 
306
409
  def install0(genv)
307
410
  ret = Vertex.new(self)
308
- @changes.add_edge(genv, @e1.install(genv), ret)
309
- @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
+
310
428
  ret
311
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
312
438
  end
313
439
 
314
440
  class OrNode < Node
315
- def initialize(raw_node, e1 = nil, raw_e2 = nil, lenv)
441
+ def initialize(raw_node, e1 = nil, raw_e2 = raw_node.right, lenv)
316
442
  super(raw_node, lenv)
443
+
317
444
  @e1 = e1 || AST.create_node(raw_node.left, lenv)
318
445
  @e2 = AST.create_node(raw_e2 || raw_node.right, lenv)
319
446
  end
@@ -324,12 +451,35 @@ module TypeProf::Core
324
451
 
325
452
  def install0(genv)
326
453
  ret = Vertex.new(self)
454
+
327
455
  v1 = @e1.install(genv)
328
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
+
329
470
  @changes.add_edge(genv, v1, ret)
330
- @changes.add_edge(genv, @e2.install(genv), ret)
471
+ @changes.add_edge(genv, v2, ret)
472
+
331
473
  ret
332
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
333
483
  end
334
484
 
335
485
  class ReturnNode < Node
@@ -345,38 +495,86 @@ module TypeProf::Core
345
495
  def install0(genv)
346
496
  @arg.install(genv)
347
497
  e_ret = @lenv.locals[:"*expected_method_ret"]
348
- @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
349
499
  Source.new(Type::Bot.new(genv))
350
500
  end
351
501
  end
352
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
+
353
557
  class BeginNode < Node
354
558
  def initialize(raw_node, lenv)
355
559
  super(raw_node, lenv)
356
560
  @body = raw_node.statements ? AST.create_node(raw_node.statements, lenv) : DummyNilNode.new(code_range, lenv)
357
- @rescue_conds = []
561
+
358
562
  @rescue_clauses = []
359
563
  raw_res = raw_node.rescue_clause
360
564
  while raw_res
361
- raw_res.exceptions.each do |raw_cond|
362
- @rescue_conds << AST.create_node(raw_cond, lenv)
363
- end
364
- if raw_res.statements
365
- @rescue_clauses << AST.create_node(raw_res.statements, lenv)
366
- end
565
+ @rescue_clauses << AST.create_node(raw_res, lenv)
367
566
  raw_res = raw_res.subsequent
368
567
  end
369
- @else_clause = raw_node.else_clause&.statements ? AST.create_node(raw_node.else_clause.statements, lenv) : DummyNilNode.new(code_range, lenv)
370
- @ensure_clause = raw_node.ensure_clause&.statements ? 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
371
570
  end
372
571
 
373
- attr_reader :body, :rescue_conds, :rescue_clauses, :else_clause, :ensure_clause
572
+ attr_reader :body, :rescue_clauses, :else_clause, :ensure_clause
374
573
 
375
- def subnodes = { body:, rescue_conds:, rescue_clauses:, else_clause:, ensure_clause: }
574
+ def subnodes = { body:, rescue_clauses:, else_clause:, ensure_clause: }
376
575
 
377
576
  def define0(genv)
378
577
  @body.define(genv)
379
- @rescue_conds.each {|cond| cond.define(genv) }
380
578
  @rescue_clauses.each {|clause| clause.define(genv) }
381
579
  @else_clause.define(genv) if @else_clause
382
580
  @ensure_clause.define(genv) if @ensure_clause
@@ -384,7 +582,6 @@ module TypeProf::Core
384
582
 
385
583
  def undefine0(genv)
386
584
  @body.undefine(genv)
387
- @rescue_conds.each {|cond| cond.undefine(genv) }
388
585
  @rescue_clauses.each {|clause| clause.undefine(genv) }
389
586
  @else_clause.undefine(genv) if @else_clause
390
587
  @ensure_clause.undefine(genv) if @ensure_clause
@@ -392,11 +589,88 @@ module TypeProf::Core
392
589
 
393
590
  def install0(genv)
394
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
+
395
603
  @changes.add_edge(genv, @body.install(genv), ret)
396
- @rescue_conds.each {|cond| cond.install(genv) }
397
- @rescue_clauses.each {|clause| @changes.add_edge(genv, clause.install(genv), ret) }
398
- @changes.add_edge(genv, @else_clause.install(genv), ret) if @else_clause
399
- @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
+
400
674
  ret
401
675
  end
402
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:,
@@ -224,6 +228,12 @@ module TypeProf::Core
224
228
  @changes.add_edge(genv, Source.new(genv.gen_ary_type(Vertex.new(self))), rest_positionals)
225
229
  end
226
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
+
227
237
  @opt_positional_defaults.zip(opt_positionals) do |expr, vtx|
228
238
  @changes.add_edge(genv, expr.install(genv), vtx)
229
239
  end
@@ -238,9 +248,9 @@ module TypeProf::Core
238
248
  end
239
249
 
240
250
  if @body
241
- e_ret = @body.lenv.locals[:"*expected_method_ret"] = Vertex.new(self)
251
+ @body.lenv.locals[:"*expected_method_ret"] = Vertex.new(self)
242
252
  @body.install(genv)
243
- @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))
244
254
  end
245
255
 
246
256
  f_args = FormalArguments.new(
@@ -20,9 +20,24 @@ module TypeProf::Core
20
20
 
21
21
  def install0(genv)
22
22
  ret = nil
23
+
24
+ post_stmts = []
25
+
23
26
  @stmts.each do |stmt|
24
- ret = stmt ? stmt.install(genv) : nil
27
+ next if stmt.nil?
28
+
29
+ if stmt.is_a?(PostExecutionNode)
30
+ post_stmts << stmt
31
+ next
32
+ end
33
+
34
+ ret = stmt.install(genv)
25
35
  end
36
+
37
+ post_stmts.each do |stmt|
38
+ stmt.install(genv)
39
+ end
40
+
26
41
  if ret
27
42
  ret2 = Vertex.new(self)
28
43
  @changes.add_edge(genv, ret, ret2)
@@ -32,6 +47,10 @@ module TypeProf::Core
32
47
  end
33
48
  end
34
49
 
50
+ def narrowings
51
+ @stmts.size == 1 ? @stmts[0].narrowings : super
52
+ end
53
+
35
54
  def diff(prev_node)
36
55
  if prev_node.is_a?(StatementsNode)
37
56
  i = 0
@@ -242,7 +261,7 @@ module TypeProf::Core
242
261
  end
243
262
  end
244
263
 
245
- class MatchPreidcateNode < Node
264
+ class MatchPredicateNode < Node
246
265
  def initialize(raw_node, lenv)
247
266
  super(raw_node, lenv)
248
267
  @value = AST.create_node(raw_node.value, lenv)