typeprof 0.31.1 → 0.32.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 (37) 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 +18 -6
  7. data/lib/typeprof/core/ast/call.rb +96 -32
  8. data/lib/typeprof/core/ast/const.rb +12 -9
  9. data/lib/typeprof/core/ast/control.rb +60 -30
  10. data/lib/typeprof/core/ast/meta.rb +194 -2
  11. data/lib/typeprof/core/ast/method.rb +74 -20
  12. data/lib/typeprof/core/ast/misc.rb +27 -7
  13. data/lib/typeprof/core/ast/module.rb +33 -3
  14. data/lib/typeprof/core/ast/sig_decl.rb +85 -24
  15. data/lib/typeprof/core/ast/sig_type.rb +77 -31
  16. data/lib/typeprof/core/ast/value.rb +14 -6
  17. data/lib/typeprof/core/ast/variable.rb +11 -4
  18. data/lib/typeprof/core/ast.rb +95 -14
  19. data/lib/typeprof/core/builtin.rb +184 -12
  20. data/lib/typeprof/core/env/method.rb +171 -6
  21. data/lib/typeprof/core/env/method_entity.rb +18 -15
  22. data/lib/typeprof/core/env/module_entity.rb +56 -18
  23. data/lib/typeprof/core/env/static_read.rb +4 -4
  24. data/lib/typeprof/core/env/type_alias_entity.rb +1 -1
  25. data/lib/typeprof/core/env/value_entity.rb +25 -3
  26. data/lib/typeprof/core/env.rb +79 -17
  27. data/lib/typeprof/core/graph/box.rb +379 -52
  28. data/lib/typeprof/core/graph/change_set.rb +59 -46
  29. data/lib/typeprof/core/graph/filter.rb +8 -5
  30. data/lib/typeprof/core/graph/vertex.rb +20 -19
  31. data/lib/typeprof/core/service.rb +317 -23
  32. data/lib/typeprof/core/type.rb +41 -7
  33. data/lib/typeprof/core/util.rb +6 -0
  34. data/lib/typeprof/lsp/messages.rb +5 -0
  35. data/lib/typeprof/lsp/server.rb +35 -4
  36. data/lib/typeprof/version.rb +1 -1
  37. metadata +3 -2
@@ -203,7 +203,7 @@ module TypeProf::Core
203
203
 
204
204
  def install0(genv)
205
205
  arg = @arg.install(genv)
206
- @changes.add_edge(genv, arg, @lenv.get_break_vtx)
206
+ @changes.add_edge(genv, arg.new_vertex(genv, self), @lenv.get_break_vtx)
207
207
  Source.new()
208
208
  end
209
209
  end
@@ -236,7 +236,7 @@ module TypeProf::Core
236
236
  if @lenv.exist_var?(:"*expected_block_ret")
237
237
  @lenv.add_next_box(@changes.add_escape_box(genv, @arg.ret))
238
238
  end
239
- Source.new(Type::Bot.new(genv))
239
+ Source.new(genv.bot_type)
240
240
  end
241
241
  end
242
242
 
@@ -322,44 +322,74 @@ module TypeProf::Core
322
322
  ret = Vertex.new(self)
323
323
  @pivot&.install(genv)
324
324
 
325
- # case文での型絞り込みを実装
326
- if @pivot && @pivot.is_a?(LocalVariableReadNode)
327
- var = @pivot.var
328
- original_vtx = @lenv.get_var(var)
325
+ # Collect modified variables across all branches
326
+ vars = []
327
+ @when_nodes.each {|wn| wn.body.modified_vars(@lenv.locals.keys, vars) }
328
+ @else_clause.modified_vars(@lenv.locals.keys, vars) if @else_clause
329
+ vars.uniq!
329
330
 
330
- # ダミー変数に元の型情報を設定
331
- @lenv.set_var(:"*pivot", original_vtx)
331
+ # Save original variable vertices
332
+ saved_vtxs = {}
333
+ vars.each do |var|
334
+ saved_vtxs[var] = @lenv.get_var(var)
335
+ end
332
336
 
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
337
+ # Prepare per-branch result vertices
338
+ branch_vtxs = []
339
+
340
+ # Setup pivot narrowing if applicable
341
+ pivot_var = @pivot.is_a?(LocalVariableReadNode) ? @pivot.var : nil
342
+ if pivot_var
343
+ original_pivot = @lenv.get_var(pivot_var)
344
+ @lenv.set_var(:"*pivot", original_pivot)
345
+ end
346
+
347
+ # Install each when branch
348
+ @when_nodes.each do |when_node|
349
+ # Reset variables to original for each branch
350
+ saved_vtxs.each {|var, vtx| @lenv.set_var(var, vtx.new_vertex(genv, self)) }
351
+ @lenv.set_var(pivot_var, original_pivot) if pivot_var
352
+
353
+ clause_val = when_node.install(genv)
354
+ @changes.add_edge(genv, clause_val, ret)
340
355
 
341
- # else節(他のwhen節で除外された後の型)
342
- filtered_else_vtx = original_vtx.new_vertex(genv, self)
356
+ modified = {}
357
+ vars.each {|var| modified[var] = @lenv.get_var(var) }
358
+ branch_vtxs << [clause_val, modified]
359
+ end
360
+
361
+ # Install else branch
362
+ saved_vtxs.each {|var, vtx| @lenv.set_var(var, vtx.new_vertex(genv, self)) }
363
+ if pivot_var
364
+ # Apply exclusion filters for else
365
+ filtered_else_vtx = original_pivot.new_vertex(genv, self)
343
366
  @when_nodes.each do |when_node|
344
367
  when_node.get_exclusion_conditions.each do |static_ret|
345
- # 各when節の型を除外(negation)
346
368
  filtered_else_vtx = IsAFilter.new(genv, self, filtered_else_vtx, true, static_ret).next_vtx
347
369
  end
348
370
  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)
371
+ @lenv.set_var(pivot_var, filtered_else_vtx)
372
+ end
373
+ else_val = @else_clause.install(genv)
374
+ @changes.add_edge(genv, else_val, ret)
352
375
 
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)
376
+ else_modified = {}
377
+ vars.each {|var| else_modified[var] = @lenv.get_var(var) }
378
+ branch_vtxs << [else_val, else_modified]
379
+
380
+ # Join all branches
381
+ vars.each do |var|
382
+ joined = Vertex.new(self)
383
+ branch_vtxs.each do |branch_val, modified|
384
+ vtx = BotFilter.new(genv, self, modified[var], branch_val).next_vtx
385
+ @changes.add_edge(genv, vtx, joined)
359
386
  end
360
- @changes.add_edge(genv, @else_clause.install(genv), ret)
387
+ @lenv.set_var(var, joined)
361
388
  end
362
389
 
390
+ # Cleanup
391
+ @lenv.locals.delete(:"*pivot") if pivot_var
392
+
363
393
  ret
364
394
  end
365
395
  end
@@ -496,7 +526,7 @@ module TypeProf::Core
496
526
  @arg.install(genv)
497
527
  e_ret = @lenv.locals[:"*expected_method_ret"]
498
528
  @lenv.add_return_box(@changes.add_escape_box(genv, @arg.ret)) if e_ret
499
- Source.new(Type::Bot.new(genv))
529
+ Source.new(genv.bot_type)
500
530
  end
501
531
  end
502
532
 
@@ -681,7 +711,7 @@ module TypeProf::Core
681
711
  end
682
712
 
683
713
  def install0(genv)
684
- Source.new(Type::Bot.new(genv))
714
+ Source.new(genv.bot_type)
685
715
  end
686
716
  end
687
717
 
@@ -72,7 +72,7 @@ module TypeProf::Core
72
72
  def mname_code_range(name)
73
73
  idx = @args.index(name.to_sym) # TODO: support string args
74
74
  node = @raw_node.arguments.arguments[idx].location
75
- TypeProf::CodeRange.from_node(node)
75
+ @lenv.code_range_from_node(node)
76
76
  end
77
77
 
78
78
  def install0(genv)
@@ -86,6 +86,62 @@ module TypeProf::Core
86
86
  end
87
87
  end
88
88
 
89
+ class AttrWriterMetaNode < Node
90
+ def initialize(raw_node, lenv)
91
+ super(raw_node, lenv)
92
+ @args = []
93
+ raw_node.arguments.arguments.each do |raw_arg|
94
+ @args << raw_arg.value.to_sym if raw_arg.type == :symbol_node
95
+ end
96
+ end
97
+
98
+ attr_reader :args
99
+
100
+ def attrs = { args: }
101
+
102
+ def mname_code_range(name)
103
+ idx = @args.index(name.to_sym)
104
+ node = @raw_node.arguments.arguments[idx].location
105
+ @lenv.code_range_from_node(node)
106
+ end
107
+
108
+ def define0(genv)
109
+ @args.map do |arg|
110
+ mod = genv.resolve_ivar(lenv.cref.cpath, false, :"@#{ arg }")
111
+ mod.add_def(self)
112
+ mod
113
+ end
114
+ end
115
+
116
+ def define_copy(genv)
117
+ @args.map do |arg|
118
+ mod = genv.resolve_ivar(lenv.cref.cpath, false, :"@#{ arg }")
119
+ mod.add_def(self)
120
+ mod.remove_def(@prev_node)
121
+ mod
122
+ end
123
+ super(genv)
124
+ end
125
+
126
+ def undefine0(genv)
127
+ @args.each do |arg|
128
+ mod = genv.resolve_ivar(lenv.cref.cpath, false, :"@#{ arg }")
129
+ mod.remove_def(self)
130
+ end
131
+ end
132
+
133
+ def install0(genv)
134
+ @args.zip(@static_ret) do |arg, ive|
135
+ vtx = Vertex.new(self)
136
+ @changes.add_edge(genv, vtx, ive.vtx)
137
+ ret_box = @changes.add_escape_box(genv, vtx)
138
+ f_args = FormalArguments.new([vtx], [], nil, [], [], [], nil, nil)
139
+ @changes.add_method_def_box(genv, @lenv.cref.cpath, false, :"#{ arg }=", f_args, [ret_box])
140
+ end
141
+ Source.new
142
+ end
143
+ end
144
+
89
145
  class AttrAccessorMetaNode < Node
90
146
  def initialize(raw_node, lenv)
91
147
  super(raw_node, lenv)
@@ -104,7 +160,7 @@ module TypeProf::Core
104
160
  def mname_code_range(name)
105
161
  idx = @args.index(name.to_sym) # TODO: support string args
106
162
  node = @raw_node.arguments.arguments[idx].location
107
- TypeProf::CodeRange.from_node(node)
163
+ @lenv.code_range_from_node(node)
108
164
  end
109
165
 
110
166
  def define0(genv)
@@ -146,5 +202,141 @@ module TypeProf::Core
146
202
  Source.new
147
203
  end
148
204
  end
205
+ class ModuleFunctionMetaNode < Node
206
+ def install0(genv)
207
+ @lenv.module_function = true
208
+ Source.new
209
+ end
210
+ end
211
+
212
+ class StructNewNode < Node
213
+ def initialize(raw_node, members, kind, lenv)
214
+ super(raw_node, lenv)
215
+ case raw_node.type
216
+ when :constant_write_node
217
+ @static_cpath = lenv.cref.cpath + [raw_node.name]
218
+ when :constant_path_write_node
219
+ @static_cpath = AST.parse_cpath(raw_node.target, lenv.cref)
220
+ else
221
+ raise
222
+ end
223
+ @members = members
224
+ @kind = kind # :struct or :data
225
+
226
+ # Parse block body if present (Struct.new(:foo) do ... end)
227
+ raw_value = raw_node.value
228
+ if raw_value.block && raw_value.block.type == :block_node && raw_value.block.body
229
+ ncref = CRef.new(@static_cpath, :instance, nil, lenv.cref)
230
+ nlenv = LocalEnv.new(lenv.file_context, ncref, {}, [])
231
+ @block_body = AST.create_node(raw_value.block.body, nlenv)
232
+ end
233
+ end
234
+
235
+ attr_reader :static_cpath, :members, :kind, :block_body
236
+
237
+ def subnodes = { block_body: }
238
+ def attrs = { static_cpath:, members:, kind: }
239
+
240
+ # Interface expected by MethodDefBox
241
+ def req_positionals = @kind == :struct ? @members : []
242
+ def opt_positionals = []
243
+ def rest_positionals = nil
244
+ def post_positionals = []
245
+ def req_keywords = @kind == :data ? @members : []
246
+ def opt_keywords = []
247
+ def rest_keywords = nil
248
+ def no_keywords = @kind == :struct
249
+
250
+ def define0(genv)
251
+ mod = genv.resolve_cpath(@static_cpath)
252
+ # add_module_def internally calls get_const(name).add_def(self)
253
+ cdef = mod.add_module_def(genv, self)
254
+ @members.each do |member|
255
+ ive = genv.resolve_ivar(@static_cpath, false, member)
256
+ ive.add_def(self)
257
+ end
258
+ @block_body.define(genv) if @block_body
259
+ cdef
260
+ end
261
+
262
+ def define_copy(genv)
263
+ mod = genv.resolve_cpath(@static_cpath)
264
+ mod.add_module_def(genv, self)
265
+ mod.remove_module_def(genv, @prev_node)
266
+ @members.each do |member|
267
+ ive = genv.resolve_ivar(@static_cpath, false, member)
268
+ ive.add_def(self)
269
+ ive.remove_def(@prev_node)
270
+ end
271
+ super(genv)
272
+ end
273
+
274
+ def undefine0(genv)
275
+ mod = genv.resolve_cpath(@static_cpath)
276
+ mod.remove_module_def(genv, self)
277
+ @members.each do |member|
278
+ ive = genv.resolve_ivar(@static_cpath, false, member)
279
+ ive.remove_def(self)
280
+ end
281
+ @block_body.undefine(genv) if @block_body
282
+ end
283
+
284
+ def install0(genv)
285
+ # Register the class singleton type as the constant value
286
+ mod_val = Source.new(Type::Singleton.new(genv, genv.resolve_cpath(@static_cpath)))
287
+ if @static_cpath
288
+ @changes.add_edge(genv, mod_val, @static_ret.vtx)
289
+ end
290
+
291
+ cpath = @static_cpath
292
+ @members.each do |member|
293
+ # Use bare `:member` (not `:@member`) so the slot can't collide with a
294
+ # user-written @member ivar — Struct/Data fields are not real ivars.
295
+ ivar_box = @changes.add_ivar_read_box(genv, cpath, false, member)
296
+ ret_box = @changes.add_escape_box(genv, ivar_box.ret)
297
+ @changes.add_method_def_box(genv, cpath, false, member, FormalArguments::Empty, [ret_box])
298
+
299
+ if @kind == :struct
300
+ # attr_writer (Struct only, Data is frozen)
301
+ ive = genv.resolve_ivar(cpath, false, member)
302
+ vtx = Vertex.new(self)
303
+ @changes.add_edge(genv, vtx, ive.vtx)
304
+ writer_ret = @changes.add_escape_box(genv, vtx)
305
+ f_args = FormalArguments.new([vtx], [], nil, [], [], [], nil, nil)
306
+ @changes.add_method_def_box(genv, cpath, false, :"#{ member }=", f_args, [writer_ret])
307
+ end
308
+ end
309
+
310
+ # initialize
311
+ init_vtxs = @members.map do |member|
312
+ ive = genv.resolve_ivar(cpath, false, member)
313
+ vtx = Vertex.new(self)
314
+ @changes.add_edge(genv, vtx, ive.vtx)
315
+ vtx
316
+ end
317
+ init_ret = @changes.add_escape_box(genv, Source.new(genv.nil_type))
318
+ if @kind == :struct
319
+ init_f_args = FormalArguments.new(init_vtxs, [], nil, [], [], [], nil, nil)
320
+ else
321
+ # Data.define uses keyword arguments
322
+ init_f_args = FormalArguments.new([], [], nil, [], init_vtxs, [], nil, nil)
323
+ end
324
+ @changes.add_method_def_box(genv, cpath, false, :initialize, init_f_args, [init_ret])
325
+
326
+ # Struct.[] is an alias for Struct.new
327
+ if @kind == :struct
328
+ self_ret = @changes.add_escape_box(genv, Source.new(Type::Instance.new(genv, genv.resolve_cpath(cpath), [])))
329
+ @changes.add_method_def_box(genv, cpath, true, :[], init_f_args, [self_ret])
330
+ end
331
+
332
+ # Install block body (additional method definitions)
333
+ if @block_body
334
+ @block_body.lenv.locals[:"*self"] = @block_body.lenv.cref.get_self(genv)
335
+ @block_body.install(genv)
336
+ end
337
+
338
+ mod_val
339
+ end
340
+ end
149
341
  end
150
342
  end
@@ -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 = lenv.code_range_from_node(raw_node.name_loc)
122
139
  @tbl = raw_node.locals
123
140
  raw_args = raw_node.parameters
124
141
  raw_body = raw_node.body
@@ -130,7 +147,7 @@ module TypeProf::Core
130
147
  @mid_code_range = mid_code_range
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
 
@@ -168,6 +188,7 @@ module TypeProf::Core
168
188
  attr_reader :opt_keywords
169
189
  attr_reader :opt_keyword_defaults
170
190
  attr_reader :rest_keywords
191
+ attr_reader :no_keywords
171
192
  attr_reader :block
172
193
  attr_reader :body
173
194
  attr_reader :rbs_method_type
@@ -191,6 +212,7 @@ module TypeProf::Core
191
212
  req_keywords:,
192
213
  opt_keywords:,
193
214
  rest_keywords:,
215
+ no_keywords:,
194
216
  block:,
195
217
  reusable:,
196
218
  }
@@ -219,6 +241,8 @@ module TypeProf::Core
219
241
  opt_positionals = @opt_positionals.map {|var| @body.lenv.new_var(var, self) }
220
242
  rest_positionals = @rest_positionals ? @body.lenv.new_var(@rest_positionals, self) : nil
221
243
  post_positionals = @post_positionals.map {|var| @body.lenv.new_var(var, self) }
244
+ install_multi_targets(genv, @req_multi_targets, req_positionals)
245
+ install_multi_targets(genv, @post_multi_targets, post_positionals)
222
246
  req_keywords = @req_keywords.map {|var| @body.lenv.new_var(var, self) }
223
247
  opt_keywords = @opt_keywords.map {|var| @body.lenv.new_var(var, self) }
224
248
  rest_keywords = @rest_keywords ? @body.lenv.new_var(@rest_keywords, self) : nil
@@ -247,6 +271,23 @@ module TypeProf::Core
247
271
  block = @body.lenv.new_var(:"*given_block", self)
248
272
  end
249
273
 
274
+ forward_opt_positionals = @opt_positionals.map do
275
+ elem_vtx = Vertex.new(self)
276
+ [Source.new(genv.gen_ary_type(elem_vtx)), elem_vtx]
277
+ end
278
+ forward_opt_keywords = @opt_keywords.map {|_name| Vertex.new(self) }
279
+ @body.lenv.forward_args = ForwardingArguments.new(
280
+ req_positionals,
281
+ forward_opt_positionals.map(&:first),
282
+ forward_opt_positionals.map(&:last),
283
+ rest_positionals,
284
+ post_positionals,
285
+ @req_keywords.zip(req_keywords),
286
+ @opt_keywords.zip(forward_opt_keywords),
287
+ rest_keywords,
288
+ block,
289
+ )
290
+
250
291
  if @body
251
292
  @body.lenv.locals[:"*expected_method_ret"] = Vertex.new(self)
252
293
  @body.install(genv)
@@ -265,10 +306,23 @@ module TypeProf::Core
265
306
  )
266
307
 
267
308
  @changes.add_method_def_box(genv, @lenv.cref.cpath, @singleton, @mid, f_args, @body.lenv.return_boxes)
309
+ if @lenv.module_function && !@singleton
310
+ @changes.add_method_def_box(genv, @lenv.cref.cpath, true, @mid, f_args, @body.lenv.return_boxes)
311
+ end
268
312
 
269
313
  Source.new(Type::Symbol.new(genv, @mid))
270
314
  end
271
315
 
316
+ def install_multi_targets(genv, multi_targets, positionals)
317
+ multi_targets.each do |idx, raw_multi_target|
318
+ param_vtx = positionals[idx]
319
+ lefts = raw_multi_target.lefts.map do |n|
320
+ @body.lenv.new_var(n.is_a?(Prism::MultiTargetNode) ? nil : n.name, self)
321
+ end
322
+ @changes.add_masgn_box(genv, param_vtx, lefts, nil, nil)
323
+ end
324
+ end
325
+
272
326
  def last_stmt_code_range
273
327
  if @body
274
328
  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,48 @@ 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
259
+ to_a_vtx = @changes.add_method_call_box(genv, vtx, :to_a, a_args, false, suppress_errors: true).ret
260
260
 
261
- @changes.add_splat_box(genv, vtx).ret
261
+ @changes.add_splat_box(genv, to_a_vtx, nil, vtx).ret
262
262
  end
263
263
  end
264
264
 
265
265
  class ForNode < Node
266
266
  def initialize(raw_node, lenv)
267
267
  super(raw_node, lenv)
268
- # XXX: tentative implementation
269
- # raw_node.index
268
+ @index = AST.create_target_node(raw_node.index, lenv)
270
269
  @expr = AST.create_node(raw_node.collection, lenv)
271
270
  @body = raw_node.statements ? AST.create_node(raw_node.statements, lenv) : DummyNilNode.new(TypeProf::CodeRange.new(code_range.last, code_range.last), lenv)
272
271
  end
273
272
 
274
- attr_reader :expr, :body
273
+ attr_reader :index, :expr, :body
275
274
 
276
- def subnodes = { expr:, body: }
275
+ def subnodes = { index:, expr:, body: }
277
276
 
278
277
  def install0(genv)
279
278
  @expr.install(genv)
279
+
280
+ vars = []
281
+ @index.modified_vars(@lenv.locals.keys, vars)
282
+ @body.modified_vars(@lenv.locals.keys, vars)
283
+ vars.uniq!
284
+
285
+ old_vtxs = {}
286
+ vars.each do |var|
287
+ vtx = @lenv.get_var(var)
288
+ nvtx = vtx.new_vertex(genv, self)
289
+ old_vtxs[var] = nvtx
290
+ @lenv.set_var(var, nvtx)
291
+ end
292
+
293
+ @index.install(genv)
280
294
  @body.install(genv)
295
+
296
+ vars.each do |var|
297
+ @changes.add_edge(genv, @lenv.get_var(var), old_vtxs[var])
298
+ @lenv.set_var(var, old_vtxs[var])
299
+ end
300
+
281
301
  Source.new(genv.nil_type)
282
302
  end
283
303
  end