katakata_irb 0.2.0 → 0.2.2

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.
@@ -0,0 +1,1172 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+ require_relative 'types'
5
+ require_relative 'scope'
6
+ require 'prism'
7
+
8
+ class KatakataIrb::TypeAnalyzer
9
+ class DigTarget
10
+ def initialize(parents, receiver, &block)
11
+ @dig_ids = parents.to_h { [_1.__id__, true] }
12
+ @target_id = receiver.__id__
13
+ @block = block
14
+ end
15
+
16
+ def dig?(node) = @dig_ids[node.__id__]
17
+ def target?(node) = @target_id == node.__id__
18
+ def resolve(type, scope)
19
+ @block.call type, scope
20
+ end
21
+ end
22
+
23
+ OBJECT_METHODS = {
24
+ to_s: KatakataIrb::Types::STRING,
25
+ to_str: KatakataIrb::Types::STRING,
26
+ to_a: KatakataIrb::Types::ARRAY,
27
+ to_ary: KatakataIrb::Types::ARRAY,
28
+ to_h: KatakataIrb::Types::HASH,
29
+ to_hash: KatakataIrb::Types::HASH,
30
+ to_i: KatakataIrb::Types::INTEGER,
31
+ to_int: KatakataIrb::Types::INTEGER,
32
+ to_f: KatakataIrb::Types::FLOAT,
33
+ to_c: KatakataIrb::Types::COMPLEX,
34
+ to_r: KatakataIrb::Types::RATIONAL
35
+ }
36
+
37
+ def initialize(dig_targets)
38
+ @dig_targets = dig_targets
39
+ end
40
+
41
+ def evaluate(node, scope)
42
+ method = "evaluate_#{node.type}"
43
+ if respond_to? method
44
+ result = send method, node, scope
45
+ else
46
+ KatakataIrb.log_puts
47
+ KatakataIrb.log_puts :NOMATCH
48
+ KatakataIrb.log_puts node.inspect
49
+ result = KatakataIrb::Types::NIL
50
+ end
51
+ @dig_targets.resolve result, scope if @dig_targets.target? node
52
+ result
53
+ end
54
+
55
+ def evaluate_program_node(node, scope)
56
+ evaluate node.statements, scope
57
+ end
58
+
59
+ def evaluate_statements_node(node, scope)
60
+ if node.body.empty?
61
+ KatakataIrb::Types::NIL
62
+ else
63
+ node.body.map { evaluate _1, scope }.last
64
+ end
65
+ end
66
+
67
+ def evaluate_def_node(node, scope)
68
+ if node.receiver
69
+ self_type = evaluate node.receiver, scope
70
+ else
71
+ current_self_types = scope.self_type.types
72
+ self_types = current_self_types.map do |type|
73
+ if type.is_a?(KatakataIrb::Types::SingletonType) && type.module_or_class.is_a?(Class)
74
+ KatakataIrb::Types::InstanceType.new type.module_or_class
75
+ else
76
+ type
77
+ end
78
+ end
79
+ self_type = KatakataIrb::Types::UnionType[*self_types]
80
+ end
81
+ if @dig_targets.dig?(node.body) || @dig_targets.dig?(node.parameters)
82
+ params_table = node.locals.to_h { [_1.to_s, KatakataIrb::Types::NIL] }
83
+ method_scope = KatakataIrb::Scope.new(
84
+ scope,
85
+ { **params_table, KatakataIrb::Scope::BREAK_RESULT => nil, KatakataIrb::Scope::NEXT_RESULT => nil, KatakataIrb::Scope::RETURN_RESULT => nil },
86
+ self_type: self_type,
87
+ trace_lvar: false,
88
+ trace_ivar: false
89
+ )
90
+ if node.parameters
91
+ # node.parameters is Prism::ParametersNode
92
+ assign_parameters node.parameters, method_scope, [], {}
93
+ end
94
+
95
+ if @dig_targets.dig?(node.body)
96
+ method_scope.conditional do |s|
97
+ evaluate node.body, s
98
+ end
99
+ end
100
+ method_scope.merge_jumps
101
+ scope.update method_scope
102
+ end
103
+ KatakataIrb::Types::SYMBOL
104
+ end
105
+
106
+ def evaluate_integer_node(_node, _scope) = KatakataIrb::Types::INTEGER
107
+
108
+ def evaluate_float_node(_node, _scope) = KatakataIrb::Types::FLOAT
109
+
110
+ def evaluate_rational_node(_node, _scope) = KatakataIrb::Types::RATIONAL
111
+
112
+ def evaluate_imaginary_node(_node, _scope) = KatakataIrb::Types::COMPLEX
113
+
114
+ def evaluate_string_node(_node, _scope) = KatakataIrb::Types::STRING
115
+
116
+ def evaluate_x_string_node(_node, _scope)
117
+ KatakataIrb::Types::UnionType[KatakataIrb::Types::STRING, KatakataIrb::Types::NIL]
118
+ end
119
+
120
+ def evaluate_symbol_node(_node, _scope) = KatakataIrb::Types::SYMBOL
121
+
122
+ def evaluate_regular_expression_node(_node, _scope) = KatakataIrb::Types::REGEXP
123
+
124
+ def evaluate_string_concat_node(node, scope)
125
+ evaluate node.left, scope
126
+ evaluate node.right, scope
127
+ KatakataIrb::Types::STRING
128
+ end
129
+
130
+ def evaluate_interpolated_string_node(node, scope)
131
+ node.parts.each { evaluate _1, scope }
132
+ KatakataIrb::Types::STRING
133
+ end
134
+
135
+ def evaluate_interpolated_x_string_node(node, scope)
136
+ node.parts.each { evaluate _1, scope }
137
+ KatakataIrb::Types::STRING
138
+ end
139
+
140
+ def evaluate_interpolated_symbol_node(node, scope)
141
+ node.parts.each { evaluate _1, scope }
142
+ KatakataIrb::Types::SYMBOL
143
+ end
144
+
145
+ def evaluate_interpolated_regular_expression_node(node, scope)
146
+ node.parts.each { evaluate _1, scope }
147
+ KatakataIrb::Types::REGEXP
148
+ end
149
+
150
+ def evaluate_embedded_statements_node(node, scope)
151
+ node.statements ? evaluate(node.statements, scope) : KatakataIrb::Types::NIL
152
+ KatakataIrb::Types::STRING
153
+ end
154
+
155
+ def evaluate_embedded_variable_node(node, scope)
156
+ evaluate node.variable, scope
157
+ KatakataIrb::Types::STRING
158
+ end
159
+
160
+ def evaluate_array_node(node, scope)
161
+ KatakataIrb::Types.array_of evaluate_list_splat_items(node.elements, scope)
162
+ end
163
+
164
+ def evaluate_hash_node(node, scope) = evaluate_hash(node, scope)
165
+ def evaluate_keyword_hash_node(node, scope) = evaluate_hash(node, scope)
166
+ def evaluate_hash(node, scope)
167
+ keys = []
168
+ values = []
169
+ node.elements.each do |assoc|
170
+ case assoc
171
+ when Prism::AssocNode
172
+ keys << evaluate(assoc.key, scope)
173
+ values << evaluate(assoc.value, scope)
174
+ when Prism::AssocSplatNode
175
+ hash = evaluate assoc.value, scope
176
+ unless hash.is_a?(KatakataIrb::Types::InstanceType) && hash.klass == Hash
177
+ hash = method_call hash, :to_hash, [], nil, nil, scope
178
+ end
179
+ if hash.is_a?(KatakataIrb::Types::InstanceType) && hash.klass == Hash
180
+ keys << hash.params[:K] if hash.params[:K]
181
+ values << hash.params[:V] if hash.params[:V]
182
+ end
183
+ end
184
+ end
185
+ if keys.empty? && values.empty?
186
+ KatakataIrb::Types::InstanceType.new Hash
187
+ else
188
+ KatakataIrb::Types::InstanceType.new Hash, K: KatakataIrb::Types::UnionType[*keys], V: KatakataIrb::Types::UnionType[*values]
189
+ end
190
+ end
191
+
192
+ def evaluate_parentheses_node(node, scope)
193
+ node.body ? evaluate(node.body, scope) : KatakataIrb::Types::NIL
194
+ end
195
+
196
+ def evaluate_constant_path_node(node, scope)
197
+ type, = evaluate_constant_node_info node, scope
198
+ type
199
+ end
200
+
201
+ def evaluate_self_node(_node, scope) = scope.self_type
202
+
203
+ def evaluate_true_node(_node, _scope) = KatakataIrb::Types::TRUE
204
+
205
+ def evaluate_false_node(_node, _scope) = KatakataIrb::Types::FALSE
206
+
207
+ def evaluate_nil_node(_node, _scope) = KatakataIrb::Types::NIL
208
+
209
+ def evaluate_source_file_node(_node, _scope) = KatakataIrb::Types::STRING
210
+
211
+ def evaluate_source_line_node(_node, _scope) = KatakataIrb::Types::INTEGER
212
+
213
+ def evaluate_source_encoding_node(_node, _scope) = KatakataIrb::Types::InstanceType.new(Encoding)
214
+
215
+ def evaluate_numbered_reference_read_node(_node, _scope)
216
+ KatakataIrb::Types::UnionType[KatakataIrb::Types::STRING, KatakataIrb::Types::NIL]
217
+ end
218
+
219
+ def evaluate_back_reference_read_node(_node, _scope)
220
+ KatakataIrb::Types::UnionType[KatakataIrb::Types::STRING, KatakataIrb::Types::NIL]
221
+ end
222
+
223
+ def evaluate_reference_read(node, scope)
224
+ scope[node.name.to_s] || KatakataIrb::Types::NIL
225
+ end
226
+ alias evaluate_constant_read_node evaluate_reference_read
227
+ alias evaluate_global_variable_read_node evaluate_reference_read
228
+ alias evaluate_local_variable_read_node evaluate_reference_read
229
+ alias evaluate_class_variable_read_node evaluate_reference_read
230
+ alias evaluate_instance_variable_read_node evaluate_reference_read
231
+
232
+
233
+ def evaluate_call_node(node, scope)
234
+ is_field_assign = node.name.match?(/[^<>=!\]]=\z/) || (node.name == :[]= && !node.call_operator)
235
+ receiver_type = node.receiver ? evaluate(node.receiver, scope) : scope.self_type
236
+ evaluate_method = lambda do |scope|
237
+ args_types, kwargs_types, block_sym_node, has_block = evaluate_call_node_arguments node, scope
238
+
239
+ if block_sym_node
240
+ block_sym = block_sym_node.value
241
+ if @dig_targets.target? block_sym_node
242
+ # method(args, &:completion_target)
243
+ call_block_proc = ->(block_args, _self_type) do
244
+ block_receiver = block_args.first || KatakataIrb::Types::OBJECT
245
+ @dig_targets.resolve block_receiver, scope
246
+ KatakataIrb::Types::OBJECT
247
+ end
248
+ else
249
+ call_block_proc = ->(block_args, _self_type) do
250
+ block_receiver, *rest = block_args
251
+ block_receiver ? method_call(block_receiver || KatakataIrb::Types::OBJECT, block_sym, rest, nil, nil, scope) : KatakataIrb::Types::OBJECT
252
+ end
253
+ end
254
+ elsif node.block.is_a? Prism::BlockNode
255
+ call_block_proc = ->(block_args, block_self_type) do
256
+ scope.conditional do |s|
257
+ numbered_parameters = node.block.locals.grep(/\A_[1-9]/).map(&:to_s)
258
+ params_table = node.block.locals.to_h { [_1.to_s, KatakataIrb::Types::NIL] }
259
+ table = { **params_table, KatakataIrb::Scope::BREAK_RESULT => nil, KatakataIrb::Scope::NEXT_RESULT => nil }
260
+ block_scope = KatakataIrb::Scope.new s, table, self_type: block_self_type, trace_ivar: !block_self_type
261
+ # TODO kwargs
262
+ if node.block.parameters&.parameters
263
+ # node.block.parameters is Prism::BlockParametersNode
264
+ assign_parameters node.block.parameters.parameters, block_scope, block_args, {}
265
+ elsif !numbered_parameters.empty?
266
+ assign_numbered_parameters numbered_parameters, block_scope, block_args, {}
267
+ end
268
+ result = node.block.body ? evaluate(node.block.body, block_scope) : KatakataIrb::Types::NIL
269
+ block_scope.merge_jumps
270
+ s.update block_scope
271
+ nexts = block_scope[KatakataIrb::Scope::NEXT_RESULT]
272
+ breaks = block_scope[KatakataIrb::Scope::BREAK_RESULT]
273
+ if block_scope.terminated?
274
+ [KatakataIrb::Types::UnionType[*nexts], breaks]
275
+ else
276
+ [KatakataIrb::Types::UnionType[result, *nexts], breaks]
277
+ end
278
+ end
279
+ end
280
+ elsif has_block
281
+ call_block_proc = ->(_block_args, _self_type) { KatakataIrb::Types::OBJECT }
282
+ end
283
+ result = method_call receiver_type, node.name, args_types, kwargs_types, call_block_proc, scope
284
+ if is_field_assign
285
+ args_types.last || KatakataIrb::Types::NIL
286
+ else
287
+ result
288
+ end
289
+ end
290
+ if node.call_operator == '&.'
291
+ result = scope.conditional { evaluate_method.call _1 }
292
+ if receiver_type.nillable?
293
+ KatakataIrb::Types::UnionType[result, KatakataIrb::Types::NIL]
294
+ else
295
+ result
296
+ end
297
+ else
298
+ evaluate_method.call scope
299
+ end
300
+ end
301
+
302
+ def evaluate_and_node(node, scope) = evaluate_and_or(node, scope, and_op: true)
303
+ def evaluate_or_node(node, scope) = evaluate_and_or(node, scope, and_op: false)
304
+ def evaluate_and_or(node, scope, and_op:)
305
+ left = evaluate node.left, scope
306
+ right = scope.conditional { evaluate node.right, _1 }
307
+ if and_op
308
+ KatakataIrb::Types::UnionType[right, KatakataIrb::Types::NIL, KatakataIrb::Types::FALSE]
309
+ else
310
+ KatakataIrb::Types::UnionType[left, right]
311
+ end
312
+ end
313
+
314
+ def evaluate_call_operator_write_node(node, scope) = evaluate_call_write(node, scope, :operator, node.write_name)
315
+ def evaluate_call_and_write_node(node, scope) = evaluate_call_write(node, scope, :and, node.write_name)
316
+ def evaluate_call_or_write_node(node, scope) = evaluate_call_write(node, scope, :or, node.write_name)
317
+ def evaluate_index_operator_write_node(node, scope) = evaluate_call_write(node, scope, :operator, :[]=)
318
+ def evaluate_index_and_write_node(node, scope) = evaluate_call_write(node, scope, :and, :[]=)
319
+ def evaluate_index_or_write_node(node, scope) = evaluate_call_write(node, scope, :or, :[]=)
320
+ def evaluate_call_write(node, scope, operator, write_name)
321
+ receiver_type = evaluate node.receiver, scope
322
+ if write_name == :[]=
323
+ args_types, kwargs_types, block_sym_node, has_block = evaluate_call_node_arguments node, scope
324
+ else
325
+ args_types = []
326
+ end
327
+ if block_sym_node
328
+ block_sym = block_sym_node.value
329
+ call_block_proc = ->(block_args, _self_type) do
330
+ block_receiver, *rest = block_args
331
+ block_receiver ? method_call(block_receiver || KatakataIrb::Types::OBJECT, block_sym, rest, nil, nil, scope) : KatakataIrb::Types::OBJECT
332
+ end
333
+ elsif has_block
334
+ call_block_proc = ->(_block_args, _self_type) { KatakataIrb::Types::OBJECT }
335
+ end
336
+ method = write_name.to_s.delete_suffix('=')
337
+ left = method_call receiver_type, method, args_types, kwargs_types, call_block_proc, scope
338
+ case operator
339
+ when :and
340
+ right = scope.conditional { evaluate node.value, _1 }
341
+ KatakataIrb::Types::UnionType[right, KatakataIrb::Types::NIL, KatakataIrb::Types::FALSE]
342
+ when :or
343
+ right = scope.conditional { evaluate node.value, _1 }
344
+ KatakataIrb::Types::UnionType[left, right]
345
+ else
346
+ right = evaluate node.value, scope
347
+ method_call left, node.operator, [right], nil, nil, scope, name_match: false
348
+ end
349
+ end
350
+
351
+ def evaluate_variable_operator_write(node, scope)
352
+ left = scope[node.name.to_s] || KatakataIrb::Types::OBJECT
353
+ right = evaluate node.value, scope
354
+ scope[node.name.to_s] = method_call left, node.operator, [right], nil, nil, scope, name_match: false
355
+ end
356
+ alias evaluate_global_variable_operator_write_node evaluate_variable_operator_write
357
+ alias evaluate_local_variable_operator_write_node evaluate_variable_operator_write
358
+ alias evaluate_class_variable_operator_write_node evaluate_variable_operator_write
359
+ alias evaluate_instance_variable_operator_write_node evaluate_variable_operator_write
360
+
361
+ def evaluate_variable_and_write(node, scope)
362
+ right = scope.conditional { evaluate node.value, scope }
363
+ scope[node.name.to_s] = KatakataIrb::Types::UnionType[right, KatakataIrb::Types::NIL, KatakataIrb::Types::FALSE]
364
+ end
365
+ alias evaluate_global_variable_and_write_node evaluate_variable_and_write
366
+ alias evaluate_local_variable_and_write_node evaluate_variable_and_write
367
+ alias evaluate_class_variable_and_write_node evaluate_variable_and_write
368
+ alias evaluate_instance_variable_and_write_node evaluate_variable_and_write
369
+
370
+ def evaluate_variable_or_write(node, scope)
371
+ left = scope[node.name.to_s] || KatakataIrb::Types::OBJECT
372
+ right = scope.conditional { evaluate node.value, scope }
373
+ scope[node.name.to_s] = KatakataIrb::Types::UnionType[left, right]
374
+ end
375
+ alias evaluate_global_variable_or_write_node evaluate_variable_or_write
376
+ alias evaluate_local_variable_or_write_node evaluate_variable_or_write
377
+ alias evaluate_class_variable_or_write_node evaluate_variable_or_write
378
+ alias evaluate_instance_variable_or_write_node evaluate_variable_or_write
379
+
380
+ def evaluate_constant_operator_write_node(node, scope)
381
+ left = scope[node.name.to_s] || KatakataIrb::Types::OBJECT
382
+ right = evaluate node.value, scope
383
+ scope[node.name.to_s] = method_call left, node.operator, [right], nil, nil, scope, name_match: false
384
+ end
385
+
386
+ def evaluate_constant_and_write_node(node, scope)
387
+ right = scope.conditional { evaluate node.value, scope }
388
+ scope[node.name.to_s] = KatakataIrb::Types::UnionType[right, KatakataIrb::Types::NIL, KatakataIrb::Types::FALSE]
389
+ end
390
+
391
+ def evaluate_constant_or_write_node(node, scope)
392
+ left = scope[node.name.to_s] || KatakataIrb::Types::OBJECT
393
+ right = scope.conditional { evaluate node.value, scope }
394
+ scope[node.name.to_s] = KatakataIrb::Types::UnionType[left, right]
395
+ end
396
+
397
+ def evaluate_constant_path_operator_write_node(node, scope)
398
+ left, receiver, _parent_module, name = evaluate_constant_node_info node.target, scope
399
+ right = evaluate node.value, scope
400
+ value = method_call left, node.operator, [right], nil, nil, scope, name_match: false
401
+ const_path_write receiver, name, value, scope
402
+ value
403
+ end
404
+
405
+ def evaluate_constant_path_and_write_node(node, scope)
406
+ _left, receiver, _parent_module, name = evaluate_constant_node_info node.target, scope
407
+ right = scope.conditional { evaluate node.value, scope }
408
+ value = KatakataIrb::Types::UnionType[right, KatakataIrb::Types::NIL, KatakataIrb::Types::FALSE]
409
+ const_path_write receiver, name, value, scope
410
+ value
411
+ end
412
+
413
+ def evaluate_constant_path_or_write_node(node, scope)
414
+ left, receiver, _parent_module, name = evaluate_constant_node_info node.target, scope
415
+ right = scope.conditional { evaluate node.value, scope }
416
+ value = KatakataIrb::Types::UnionType[left, right]
417
+ const_path_write receiver, name, value, scope
418
+ value
419
+ end
420
+
421
+ def evaluate_constant_path_write_node(node, scope)
422
+ receiver = evaluate node.target.parent, scope if node.target.parent
423
+ value = evaluate node.value, scope
424
+ const_path_write receiver, node.target.child.name.to_s, value, scope
425
+ value
426
+ end
427
+
428
+ def evaluate_lambda_node(node, scope)
429
+ local_table = node.locals.to_h { [_1.to_s, KatakataIrb::Types::OBJECT] }
430
+ block_scope = KatakataIrb::Scope.new scope, { **local_table, KatakataIrb::Scope::BREAK_RESULT => nil, KatakataIrb::Scope::NEXT_RESULT => nil, KatakataIrb::Scope::RETURN_RESULT => nil }
431
+ block_scope.conditional do |s|
432
+ assign_parameters node.parameters.parameters, s, [], {} if node.parameters&.parameters
433
+ evaluate node.body, s if node.body
434
+ end
435
+ block_scope.merge_jumps
436
+ scope.update block_scope
437
+ KatakataIrb::Types::PROC
438
+ end
439
+
440
+ def evaluate_reference_write(node, scope)
441
+ scope[node.name.to_s] = evaluate node.value, scope
442
+ end
443
+ alias evaluate_constant_write_node evaluate_reference_write
444
+ alias evaluate_global_variable_write_node evaluate_reference_write
445
+ alias evaluate_local_variable_write_node evaluate_reference_write
446
+ alias evaluate_class_variable_write_node evaluate_reference_write
447
+ alias evaluate_instance_variable_write_node evaluate_reference_write
448
+
449
+ def evaluate_multi_write_node(node, scope)
450
+ evaluated_receivers = {}
451
+ evaluate_multi_write_receiver node, scope, evaluated_receivers
452
+ value = (
453
+ if node.value.is_a? Prism::ArrayNode
454
+ if node.value.elements.any?(Prism::SplatNode)
455
+ evaluate node.value, scope
456
+ else
457
+ node.value.elements.map do |n|
458
+ evaluate n, scope
459
+ end
460
+ end
461
+ elsif node.value
462
+ evaluate node.value, scope
463
+ else
464
+ # For syntax invalid code like `(*a).b`
465
+ KatakataIrb::Types::NIL
466
+ end
467
+ )
468
+ evaluate_multi_write node, value, scope, evaluated_receivers
469
+ value.is_a?(Array) ? KatakataIrb::Types.array_of(*value) : value
470
+ end
471
+
472
+ def evaluate_if_node(node, scope) = evaluate_if_unless(node, scope)
473
+ def evaluate_unless_node(node, scope) = evaluate_if_unless(node, scope)
474
+ def evaluate_if_unless(node, scope)
475
+ evaluate node.predicate, scope
476
+ KatakataIrb::Types::UnionType[*scope.run_branches(
477
+ -> { node.statements ? evaluate(node.statements, _1) : KatakataIrb::Types::NIL },
478
+ -> { node.consequent ? evaluate(node.consequent, _1) : KatakataIrb::Types::NIL }
479
+ )]
480
+ end
481
+
482
+ def evaluate_else_node(node, scope)
483
+ node.statements ? evaluate(node.statements, scope) : KatakataIrb::Types::NIL
484
+ end
485
+
486
+ def evaluate_while_until(node, scope)
487
+ inner_scope = KatakataIrb::Scope.new scope, { KatakataIrb::Scope::BREAK_RESULT => nil }
488
+ evaluate node.predicate, inner_scope
489
+ if node.statements
490
+ inner_scope.conditional do |s|
491
+ evaluate node.statements, s
492
+ end
493
+ end
494
+ inner_scope.merge_jumps
495
+ scope.update inner_scope
496
+ breaks = inner_scope[KatakataIrb::Scope::BREAK_RESULT]
497
+ breaks ? KatakataIrb::Types::UnionType[breaks, KatakataIrb::Types::NIL] : KatakataIrb::Types::NIL
498
+ end
499
+ alias evaluate_while_node evaluate_while_until
500
+ alias evaluate_until_node evaluate_while_until
501
+
502
+ def evaluate_break_node(node, scope) = evaluate_jump(node, scope, :break)
503
+ def evaluate_next_node(node, scope) = evaluate_jump(node, scope, :next)
504
+ def evaluate_return_node(node, scope) = evaluate_jump(node, scope, :return)
505
+ def evaluate_jump(node, scope, mode)
506
+ internal_key = (
507
+ case mode
508
+ when :break
509
+ KatakataIrb::Scope::BREAK_RESULT
510
+ when :next
511
+ KatakataIrb::Scope::NEXT_RESULT
512
+ when :return
513
+ KatakataIrb::Scope::RETURN_RESULT
514
+ end
515
+ )
516
+ jump_value = (
517
+ arguments = node.arguments&.arguments
518
+ if arguments.nil? || arguments.empty?
519
+ KatakataIrb::Types::NIL
520
+ elsif arguments.size == 1 && !arguments.first.is_a?(Prism::SplatNode)
521
+ evaluate arguments.first, scope
522
+ else
523
+ KatakataIrb::Types.array_of evaluate_list_splat_items(arguments, scope)
524
+ end
525
+ )
526
+ scope.terminate_with internal_key, jump_value
527
+ KatakataIrb::Types::NIL
528
+ end
529
+
530
+ def evaluate_yield_node(node, scope)
531
+ evaluate_list_splat_items node.arguments.arguments, scope if node.arguments
532
+ KatakataIrb::Types::OBJECT
533
+ end
534
+
535
+ def evaluate_redo_node(_node, scope)
536
+ scope.terminate
537
+ KatakataIrb::Types::NIL
538
+ end
539
+
540
+ def evaluate_retry_node(_node, scope)
541
+ scope.terminate
542
+ KatakataIrb::Types::NIL
543
+ end
544
+
545
+ def evaluate_forwarding_super_node(_node, _scope) = KatakataIrb::Types::OBJECT
546
+
547
+ def evaluate_super_node(node, scope)
548
+ evaluate_list_splat_items node.arguments.arguments, scope if node.arguments
549
+ KatakataIrb::Types::OBJECT
550
+ end
551
+
552
+ def evaluate_begin_node(node, scope)
553
+ return_type = node.statements ? evaluate(node.statements, scope) : KatakataIrb::Types::NIL
554
+ if node.rescue_clause
555
+ if node.else_clause
556
+ return_types = scope.run_branches(
557
+ ->{ evaluate node.rescue_clause, _1 },
558
+ ->{ evaluate node.else_clause, _1 }
559
+ )
560
+ else
561
+ return_types = [
562
+ return_type,
563
+ scope.conditional { evaluate node.rescue_clause, _1 }
564
+ ]
565
+ end
566
+ return_type = KatakataIrb::Types::UnionType[*return_types]
567
+ end
568
+ if node.ensure_clause&.statements
569
+ # ensure_clause is Prism::EnsureNode
570
+ evaluate node.ensure_clause.statements, scope
571
+ end
572
+ return_type
573
+ end
574
+
575
+ def evaluate_rescue_node(node, scope)
576
+ run_rescue = lambda do |s|
577
+ if node.reference
578
+ error_classes_type = evaluate_list_splat_items node.exceptions, s
579
+ error_types = error_classes_type.types.filter_map do
580
+ KatakataIrb::Types::InstanceType.new _1.module_or_class if _1.is_a?(KatakataIrb::Types::SingletonType)
581
+ end
582
+ error_types << KatakataIrb::Types::InstanceType.new(StandardError) if error_types.empty?
583
+ error_type = KatakataIrb::Types::UnionType[*error_types]
584
+ case node.reference
585
+ when Prism::LocalVariableTargetNode, Prism::InstanceVariableTargetNode, Prism::ClassVariableTargetNode, Prism::GlobalVariableTargetNode, Prism::ConstantTargetNode
586
+ s[node.reference.name.to_s] = error_type
587
+ when Prism::CallNode
588
+ evaluate node.reference, s
589
+ end
590
+ end
591
+ node.statements ? evaluate(node.statements, s) : KatakataIrb::Types::NIL
592
+ end
593
+ if node.consequent # begin; rescue A; rescue B; end
594
+ types = scope.run_branches(
595
+ run_rescue,
596
+ -> { evaluate node.consequent, _1 }
597
+ )
598
+ KatakataIrb::Types::UnionType[*types]
599
+ else
600
+ run_rescue.call scope
601
+ end
602
+ end
603
+
604
+ def evaluate_rescue_modifier_node(node, scope)
605
+ a = evaluate node.expression, scope
606
+ b = scope.conditional { evaluate node.rescue_expression, _1 }
607
+ KatakataIrb::Types::UnionType[a, b]
608
+ end
609
+
610
+ def evaluate_singleton_class_node(node, scope)
611
+ klass_types = evaluate(node.expression, scope).types.filter_map do |type|
612
+ KatakataIrb::Types::SingletonType.new type.klass if type.is_a? KatakataIrb::Types::InstanceType
613
+ end
614
+ klass_types = [KatakataIrb::Types::CLASS] if klass_types.empty?
615
+ table = node.locals.to_h { [_1.to_s, KatakataIrb::Types::NIL] }
616
+ sclass_scope = KatakataIrb::Scope.new(
617
+ scope,
618
+ { **table, KatakataIrb::Scope::BREAK_RESULT => nil, KatakataIrb::Scope::NEXT_RESULT => nil, KatakataIrb::Scope::RETURN_RESULT => nil },
619
+ trace_ivar: false,
620
+ trace_lvar: false,
621
+ self_type: KatakataIrb::Types::UnionType[*klass_types]
622
+ )
623
+ result = node.body ? evaluate(node.body, sclass_scope) : KatakataIrb::Types::NIL
624
+ scope.update sclass_scope
625
+ result
626
+ end
627
+
628
+ def evaluate_class_node(node, scope) = evaluate_class_module(node, scope, true)
629
+ def evaluate_module_node(node, scope) = evaluate_class_module(node, scope, false)
630
+ def evaluate_class_module(node, scope, is_class)
631
+ unless node.constant_path.is_a?(Prism::ConstantReadNode) || node.constant_path.is_a?(Prism::ConstantPathNode)
632
+ # Incomplete class/module `class (statement[cursor_here])::Name; end`
633
+ evaluate node.constant_path, scope
634
+ return KatakataIrb::Types::NIL
635
+ end
636
+ const_type, _receiver, parent_module, name = evaluate_constant_node_info node.constant_path, scope
637
+ if is_class
638
+ select_class_type = -> { _1.is_a?(KatakataIrb::Types::SingletonType) && _1.module_or_class.is_a?(Class) }
639
+ module_types = const_type.types.select(&select_class_type)
640
+ module_types += evaluate(node.superclass, scope).types.select(&select_class_type) if node.superclass
641
+ module_types << KatakataIrb::Types::CLASS if module_types.empty?
642
+ else
643
+ module_types = const_type.types.select { _1.is_a?(KatakataIrb::Types::SingletonType) && !_1.module_or_class.is_a?(Class) }
644
+ module_types << KatakataIrb::Types::MODULE if module_types.empty?
645
+ end
646
+ return KatakataIrb::Types::NIL unless node.body
647
+
648
+ table = node.locals.to_h { [_1.to_s, KatakataIrb::Types::NIL] }
649
+ if !name.empty? && (parent_module.is_a?(Module) || parent_module.nil?)
650
+ value = parent_module.const_get name if parent_module&.const_defined? name
651
+ unless value
652
+ value_type = scope[name]
653
+ value = value_type.module_or_class if value_type.is_a? KatakataIrb::Types::SingletonType
654
+ end
655
+
656
+ if value.is_a? Module
657
+ nesting = [value, []]
658
+ else
659
+ if parent_module
660
+ nesting = [parent_module, [name]]
661
+ else
662
+ parent_nesting, parent_path = scope.module_nesting.first
663
+ nesting = [parent_nesting, parent_path + [name]]
664
+ end
665
+ nesting_key = [nesting[0].__id__, nesting[1]].join('::')
666
+ nesting_value = is_class ? KatakataIrb::Types::CLASS : KatakataIrb::Types::MODULE
667
+ end
668
+ else
669
+ # parent_module == :unknown
670
+ # TODO: dummy module
671
+ end
672
+ module_scope = KatakataIrb::Scope.new(
673
+ scope,
674
+ { **table, KatakataIrb::Scope::BREAK_RESULT => nil, KatakataIrb::Scope::NEXT_RESULT => nil, KatakataIrb::Scope::RETURN_RESULT => nil },
675
+ trace_ivar: false,
676
+ trace_lvar: false,
677
+ self_type: KatakataIrb::Types::UnionType[*module_types],
678
+ nesting: nesting
679
+ )
680
+ module_scope[nesting_key] = nesting_value if nesting_value
681
+ result = evaluate(node.body, module_scope)
682
+ scope.update module_scope
683
+ result
684
+ end
685
+
686
+ def evaluate_for_node(node, scope)
687
+ node.statements
688
+ collection = evaluate node.collection, scope
689
+ inner_scope = KatakataIrb::Scope.new scope, { KatakataIrb::Scope::BREAK_RESULT => nil }
690
+ ary_type = method_call collection, :to_ary, [], nil, nil, nil, name_match: false
691
+ element_types = ary_type.types.filter_map do |ary|
692
+ ary.params[:Elem] if ary.is_a?(KatakataIrb::Types::InstanceType) && ary.klass == Array
693
+ end
694
+ element_type = KatakataIrb::Types::UnionType[*element_types]
695
+ inner_scope.conditional do |s|
696
+ evaluate_write node.index, element_type, s, nil
697
+ evaluate node.statements, s if node.statements
698
+ end
699
+ inner_scope.merge_jumps
700
+ scope.update inner_scope
701
+ breaks = inner_scope[KatakataIrb::Scope::BREAK_RESULT]
702
+ breaks ? KatakataIrb::Types::UnionType[breaks, collection] : collection
703
+ end
704
+
705
+ def evaluate_case_node(node, scope)
706
+ target = evaluate(node.predicate, scope) if node.predicate
707
+ # TODO
708
+ branches = node.conditions.map do |condition|
709
+ ->(s) { evaluate_case_match target, condition, s }
710
+ end
711
+ if node.consequent
712
+ branches << ->(s) { evaluate node.consequent, s }
713
+ elsif node.conditions.any? { _1.is_a? Prism::WhenNode }
714
+ branches << ->(_s) { KatakataIrb::Types::NIL }
715
+ end
716
+ KatakataIrb::Types::UnionType[*scope.run_branches(*branches)]
717
+ end
718
+
719
+ def evaluate_match_required_node(node, scope)
720
+ value_type = evaluate node.value, scope
721
+ evaluate_match_pattern value_type, node.pattern, scope
722
+ KatakataIrb::Types::NIL # void value
723
+ end
724
+
725
+ def evaluate_match_predicate_node(node, scope)
726
+ value_type = evaluate node.value, scope
727
+ scope.conditional { evaluate_match_pattern value_type, node.pattern, _1 }
728
+ KatakataIrb::Types::BOOLEAN
729
+ end
730
+
731
+ def evaluate_range_node(node, scope)
732
+ beg_type = evaluate node.left, scope if node.left
733
+ end_type = evaluate node.right, scope if node.right
734
+ elem = (KatakataIrb::Types::UnionType[*[beg_type, end_type].compact]).nonnillable
735
+ KatakataIrb::Types::InstanceType.new Range, Elem: elem
736
+ end
737
+
738
+ def evaluate_defined_node(node, scope)
739
+ scope.conditional { evaluate node.value, _1 }
740
+ KatakataIrb::Types::UnionType[KatakataIrb::Types::STRING, KatakataIrb::Types::NIL]
741
+ end
742
+
743
+ def evaluate_flip_flop_node(node, scope)
744
+ scope.conditional { evaluate node.left, _1 } if node.left
745
+ scope.conditional { evaluate node.right, _1 } if node.right
746
+ KatakataIrb::Types::BOOLEAN
747
+ end
748
+
749
+ def evaluate_multi_target_node(node, scope)
750
+ # Raw MultiTargetNode, incomplete code like `a,b`, `*a`.
751
+ evaluate_multi_write_receiver node, scope, nil
752
+ KatakataIrb::Types::NIL
753
+ end
754
+
755
+ def evaluate_splat_node(node, scope)
756
+ # Raw SplatNode, incomplete code like `*a.`
757
+ evaluate_multi_write_receiver node.expression, scope, nil if node.expression
758
+ KatakataIrb::Types::NIL
759
+ end
760
+
761
+ def evaluate_implicit_node(node, scope)
762
+ evaluate node.value, scope
763
+ end
764
+
765
+ def evaluate_match_write_node(node, scope)
766
+ # /(?<a>)(?<b>)/ =~ string
767
+ evaluate node.call, scope
768
+ node.locals.each { scope[_1.to_s] = KatakataIrb::Types::UnionType[KatakataIrb::Types::STRING, KatakataIrb::Types::NIL] }
769
+ KatakataIrb::Types::BOOLEAN
770
+ end
771
+
772
+ def evaluate_match_last_line_node(_node, _scope)
773
+ KatakataIrb::Types::BOOLEAN
774
+ end
775
+
776
+ def evaluate_interpolated_match_last_line_node(node, scope)
777
+ node.parts.each { evaluate _1, scope }
778
+ KatakataIrb::Types::BOOLEAN
779
+ end
780
+
781
+ def evaluate_pre_execution_node(node, scope)
782
+ node.statements ? evaluate(node.statements, scope) : KatakataIrb::Types::NIL
783
+ end
784
+
785
+ def evaluate_post_execution_node(node, scope)
786
+ node.statements && @dig_targets.dig?(node.statements) ? evaluate(node.statements, scope) : KatakataIrb::Types::NIL
787
+ end
788
+
789
+ def evaluate_alias_method_node(_node, _scope) = KatakataIrb::Types::NIL
790
+ def evaluate_alias_global_variable_node(_node, _scope) = KatakataIrb::Types::NIL
791
+ def evaluate_undef_node(_node, _scope) = KatakataIrb::Types::NIL
792
+ def evaluate_missing_node(_node, _scope) = KatakataIrb::Types::NIL
793
+
794
+ def evaluate_call_node_arguments(call_node, scope)
795
+ # call_node.arguments is Prism::ArgumentsNode
796
+ arguments = call_node.arguments&.arguments&.dup || []
797
+ block_arg = call_node.block.expression if call_node.block.is_a?(Prism::BlockArgumentNode)
798
+ kwargs = arguments.pop.elements if arguments.last.is_a?(Prism::KeywordHashNode)
799
+ args_types = arguments.map do |arg|
800
+ case arg
801
+ when Prism::ForwardingArgumentsNode
802
+ # `f(a, ...)` treat like splat
803
+ nil
804
+ when Prism::SplatNode
805
+ evaluate arg.expression, scope if arg.expression
806
+ nil # TODO: splat
807
+ else
808
+ evaluate arg, scope
809
+ end
810
+ end
811
+ if kwargs
812
+ kwargs_types = kwargs.map do |arg|
813
+ case arg
814
+ when Prism::AssocNode
815
+ if arg.key.is_a?(Prism::SymbolNode)
816
+ [arg.key.value, evaluate(arg.value, scope)]
817
+ else
818
+ evaluate arg.key, scope
819
+ evaluate arg.value, scope
820
+ nil
821
+ end
822
+ when Prism::AssocSplatNode
823
+ evaluate arg.value, scope if arg.value
824
+ nil
825
+ end
826
+ end.compact.to_h
827
+ end
828
+ if block_arg.is_a? Prism::SymbolNode
829
+ block_sym_node = block_arg
830
+ elsif block_arg
831
+ evaluate block_arg, scope
832
+ end
833
+ [args_types, kwargs_types, block_sym_node, !!block_arg]
834
+ end
835
+
836
+ def const_path_write(receiver, name, value, scope)
837
+ if receiver # receiver::A = value
838
+ singleton_type = receiver.types.find { _1.is_a? KatakataIrb::Types::SingletonType }
839
+ scope.set_const singleton_type.module_or_class, name, value if singleton_type
840
+ else # ::A = value
841
+ scope.set_const Object, name, value
842
+ end
843
+ end
844
+
845
+ def assign_required_parameter(node, value, scope)
846
+ case node.type
847
+ when :required_parameter_node
848
+ scope[node.name.to_s] = value || KatakataIrb::Types::OBJECT
849
+ when :multi_target_node
850
+ parameters = [*node.lefts, *node.rest, *node.rights]
851
+ values = value ? sized_splat(value, :to_ary, parameters.size) : []
852
+ parameters.zip values do |n, v|
853
+ assign_required_parameter n, v, scope
854
+ end
855
+ when :splat_node
856
+ splat_value = value ? KatakataIrb::Types.array_of(value) : KatakataIrb::Types::ARRAY
857
+ assign_required_parameter node.expression, splat_value, scope
858
+ end
859
+ end
860
+
861
+ def evaluate_constant_node_info(node, scope)
862
+ case node
863
+ when Prism::ConstantPathNode
864
+ name = node.child.name.to_s
865
+ if node.parent
866
+ receiver = evaluate node.parent, scope
867
+ if receiver.is_a? KatakataIrb::Types::SingletonType
868
+ parent_module = receiver.module_or_class
869
+ end
870
+ else
871
+ parent_module = Object
872
+ end
873
+ if parent_module
874
+ type = scope.get_const(parent_module, [name]) || KatakataIrb::Types::NIL
875
+ else
876
+ parent_module = :unknown
877
+ type = KatakataIrb::Types::NIL
878
+ end
879
+ when Prism::ConstantReadNode
880
+ name = node.name.to_s
881
+ type = scope[name]
882
+ end
883
+ @dig_targets.resolve type, scope if @dig_targets.target? node
884
+ [type, receiver, parent_module, name]
885
+ end
886
+
887
+
888
+ def assign_parameters(node, scope, args, kwargs)
889
+ args = args.dup
890
+ kwargs = kwargs.dup
891
+ size = node.requireds.size + node.optionals.size + (node.rest ? 1 : 0) + node.posts.size
892
+ args = sized_splat(args.first, :to_ary, size) if size >= 2 && args.size == 1
893
+ reqs = args.shift node.requireds.size
894
+ if node.rest
895
+ # node.rest is Prism::RestParameterNode
896
+ posts = []
897
+ opts = args.shift node.optionals.size
898
+ rest = args
899
+ else
900
+ posts = args.pop node.posts.size
901
+ opts = args
902
+ rest = []
903
+ end
904
+ node.requireds.zip reqs do |n, v|
905
+ assign_required_parameter n, v, scope
906
+ end
907
+ node.optionals.zip opts do |n, v|
908
+ # n is Prism::OptionalParameterNode
909
+ values = [v]
910
+ values << evaluate(n.value, scope) if n.value
911
+ scope[n.name.to_s] = KatakataIrb::Types::UnionType[*values.compact]
912
+ end
913
+ node.posts.zip posts do |n, v|
914
+ assign_required_parameter n, v, scope
915
+ end
916
+ if node.rest&.name
917
+ # node.rest is Prism::RestParameterNode
918
+ scope[node.rest.name.to_s] = KatakataIrb::Types.array_of(*rest)
919
+ end
920
+ node.keywords.each do |n|
921
+ # n is Prism::KeywordParameterNode
922
+ name = n.name.to_s.delete(':')
923
+ values = [kwargs.delete(name)]
924
+ values << evaluate(n.value, scope) if n.value
925
+ scope[name] = KatakataIrb::Types::UnionType[*values.compact]
926
+ end
927
+ # node.keyword_rest is Prism::KeywordRestParameterNode or Prism::ForwardingParameterNode or Prism::NoKeywordsParameterNode
928
+ if node.keyword_rest.is_a?(Prism::KeywordRestParameterNode) && node.keyword_rest.name
929
+ scope[node.keyword_rest.name.to_s] = KatakataIrb::Types::InstanceType.new(Hash, K: KatakataIrb::Types::SYMBOL, V: KatakataIrb::Types::UnionType[*kwargs.values])
930
+ end
931
+ if node.block&.name
932
+ # node.block is Prism::BlockParameterNode
933
+ scope[node.block.name.to_s] = KatakataIrb::Types::PROC
934
+ end
935
+ end
936
+
937
+ def assign_numbered_parameters(numbered_parameters, scope, args, _kwargs)
938
+ return if numbered_parameters.empty?
939
+ max_num = numbered_parameters.map { _1[1].to_i }.max
940
+ if max_num == 1
941
+ scope['_1'] = args.first || KatakataIrb::Types::NIL
942
+ else
943
+ args = sized_splat(args.first, :to_ary, max_num) if args.size == 1
944
+ numbered_parameters.each do |name|
945
+ index = name[1].to_i - 1
946
+ scope[name] = args[index] || KatakataIrb::Types::NIL
947
+ end
948
+ end
949
+ end
950
+
951
+ def evaluate_case_match(target, node, scope)
952
+ case node
953
+ when Prism::WhenNode
954
+ node.conditions.each { evaluate _1, scope }
955
+ node.statements ? evaluate(node.statements, scope) : KatakataIrb::Types::NIL
956
+ when Prism::InNode
957
+ pattern = node.pattern
958
+ if pattern.is_a?(Prism::IfNode) || pattern.is_a?(Prism::UnlessNode)
959
+ cond_node = pattern.predicate
960
+ pattern = pattern.statements.body.first
961
+ end
962
+ evaluate_match_pattern(target, pattern, scope)
963
+ evaluate cond_node, scope if cond_node # TODO: conditional branch
964
+ node.statements ? evaluate(node.statements, scope) : KatakataIrb::Types::NIL
965
+ end
966
+ end
967
+
968
+ def evaluate_match_pattern(value, pattern, scope)
969
+ # TODO: scope.terminate_with KatakataIrb::Scope::PATTERNMATCH_BREAK, KatakataIrb::Types::NIL
970
+ case pattern
971
+ when Prism::FindPatternNode
972
+ # TODO
973
+ evaluate_match_pattern KatakataIrb::Types::OBJECT, pattern.left, scope
974
+ pattern.requireds.each { evaluate_match_pattern KatakataIrb::Types::OBJECT, _1, scope }
975
+ evaluate_match_pattern KatakataIrb::Types::OBJECT, pattern.right, scope
976
+ when Prism::ArrayPatternNode
977
+ # TODO
978
+ pattern.requireds.each { evaluate_match_pattern KatakataIrb::Types::OBJECT, _1, scope }
979
+ evaluate_match_pattern KatakataIrb::Types::OBJECT, pattern.rest, scope if pattern.rest
980
+ pattern.posts.each { evaluate_match_pattern KatakataIrb::Types::OBJECT, _1, scope }
981
+ KatakataIrb::Types::ARRAY
982
+ when Prism::HashPatternNode
983
+ # TODO
984
+ pattern.elements.each { evaluate_match_pattern KatakataIrb::Types::OBJECT, _1, scope }
985
+ if pattern.respond_to?(:rest) && pattern.rest
986
+ evaluate_match_pattern KatakataIrb::Types::OBJECT, pattern.rest, scope
987
+ end
988
+ KatakataIrb::Types::HASH
989
+ when Prism::AssocNode
990
+ evaluate_match_pattern value, pattern.value, scope if pattern.value
991
+ KatakataIrb::Types::OBJECT
992
+ when Prism::AssocSplatNode
993
+ # TODO
994
+ evaluate_match_pattern KatakataIrb::Types::HASH, pattern.value, scope
995
+ KatakataIrb::Types::OBJECT
996
+ when Prism::PinnedVariableNode
997
+ evaluate pattern.variable, scope
998
+ when Prism::PinnedExpressionNode
999
+ evaluate pattern.expression, scope
1000
+ when Prism::LocalVariableTargetNode
1001
+ scope[pattern.name.to_s] = value
1002
+ when Prism::AlternationPatternNode
1003
+ KatakataIrb::Types::UnionType[evaluate_match_pattern(value, pattern.left, scope), evaluate_match_pattern(value, pattern.right, scope)]
1004
+ when Prism::CapturePatternNode
1005
+ capture_type = class_or_value_to_instance evaluate_match_pattern(value, pattern.value, scope)
1006
+ value = capture_type unless capture_type.types.empty? || capture_type.types == [KatakataIrb::Types::OBJECT]
1007
+ evaluate_match_pattern value, pattern.target, scope
1008
+ when Prism::SplatNode
1009
+ value = KatakataIrb::Types.array_of value
1010
+ evaluate_match_pattern value, pattern.expression, scope if pattern.expression
1011
+ value
1012
+ else
1013
+ # literal node
1014
+ type = evaluate(pattern, scope)
1015
+ class_or_value_to_instance(type)
1016
+ end
1017
+ end
1018
+
1019
+ def class_or_value_to_instance(type)
1020
+ instance_types = type.types.map do |t|
1021
+ t.is_a?(KatakataIrb::Types::SingletonType) ? KatakataIrb::Types::InstanceType.new(t.module_or_class) : t
1022
+ end
1023
+ KatakataIrb::Types::UnionType[*instance_types]
1024
+ end
1025
+
1026
+ def evaluate_write(node, value, scope, evaluated_receivers)
1027
+ case node
1028
+ when Prism::MultiTargetNode
1029
+ evaluate_multi_write node, value, scope, evaluated_receivers
1030
+ when Prism::CallNode
1031
+ evaluated_receivers&.[](node.receiver) || evaluate(node.receiver, scope) if node.receiver
1032
+ when Prism::SplatNode
1033
+ evaluate_write node.expression, KatakataIrb::Types.array_of(value), scope, evaluated_receivers
1034
+ when Prism::LocalVariableTargetNode, Prism::GlobalVariableTargetNode, Prism::InstanceVariableTargetNode, Prism::ClassVariableTargetNode, Prism::ConstantTargetNode
1035
+ scope[node.name.to_s] = value
1036
+ when Prism::ConstantPathTargetNode
1037
+ receiver = evaluated_receivers&.[](node.parent) || evaluate(node.parent, scope) if node.parent
1038
+ const_path_write receiver, node.child.name.to_s, value, scope
1039
+ value
1040
+ end
1041
+ end
1042
+
1043
+ def evaluate_multi_write(node, values, scope, evaluated_receivers)
1044
+ pre_targets = node.lefts
1045
+ splat_target = node.rest
1046
+ post_targets = node.rights
1047
+ size = pre_targets.size + (splat_target ? 1 : 0) + post_targets.size
1048
+ values = values.is_a?(Array) ? values.dup : sized_splat(values, :to_ary, size)
1049
+ pre_pairs = pre_targets.zip(values.shift(pre_targets.size))
1050
+ post_pairs = post_targets.zip(values.pop(post_targets.size))
1051
+ splat_pairs = splat_target ? [[splat_target, KatakataIrb::Types::UnionType[*values]]] : []
1052
+ (pre_pairs + splat_pairs + post_pairs).each do |target, value|
1053
+ evaluate_write target, value || KatakataIrb::Types::NIL, scope, evaluated_receivers
1054
+ end
1055
+ end
1056
+
1057
+ def evaluate_multi_write_receiver(node, scope, evaluated_receivers)
1058
+ case node
1059
+ when Prism::MultiWriteNode, Prism::MultiTargetNode
1060
+ targets = [*node.lefts, *node.rest, *node.rights]
1061
+ targets.each { evaluate_multi_write_receiver _1, scope, evaluated_receivers }
1062
+ when Prism::CallNode
1063
+ if node.receiver
1064
+ receiver = evaluate(node.receiver, scope)
1065
+ evaluated_receivers[node.receiver] = receiver if evaluated_receivers
1066
+ end
1067
+ if node.arguments
1068
+ node.arguments.arguments&.each do |arg|
1069
+ if arg.is_a? Prism::SplatNode
1070
+ evaluate arg.expression, scope
1071
+ else
1072
+ evaluate arg, scope
1073
+ end
1074
+ end
1075
+ end
1076
+ when Prism::SplatNode
1077
+ evaluate_multi_write_receiver node.expression, scope, evaluated_receivers if node.expression
1078
+ end
1079
+ end
1080
+
1081
+ def evaluate_list_splat_items(list, scope)
1082
+ items = list.flat_map do |node|
1083
+ if node.is_a? Prism::SplatNode
1084
+ splat = evaluate node.expression, scope
1085
+ array_elem, non_array = partition_to_array splat.nonnillable, :to_a
1086
+ [*array_elem, *non_array]
1087
+ else
1088
+ evaluate node, scope
1089
+ end
1090
+ end.uniq
1091
+ KatakataIrb::Types::UnionType[*items]
1092
+ end
1093
+
1094
+ def sized_splat(value, method, size)
1095
+ array_elem, non_array = partition_to_array value, method
1096
+ values = [KatakataIrb::Types::UnionType[*array_elem, *non_array]]
1097
+ values += [array_elem] * (size - 1) if array_elem && size >= 1
1098
+ values
1099
+ end
1100
+
1101
+ def partition_to_array(value, method)
1102
+ arrays, non_arrays = value.types.partition { _1.is_a?(KatakataIrb::Types::InstanceType) && _1.klass == Array }
1103
+ non_arrays.select! do |type|
1104
+ to_array_result = method_call type, method, [], nil, nil, nil, name_match: false
1105
+ if to_array_result.is_a?(KatakataIrb::Types::InstanceType) && to_array_result.klass == Array
1106
+ arrays << to_array_result
1107
+ false
1108
+ else
1109
+ true
1110
+ end
1111
+ end
1112
+ array_elem = arrays.empty? ? nil : KatakataIrb::Types::UnionType[*arrays.map { _1.params[:Elem] || KatakataIrb::Types::OBJECT }]
1113
+ non_array = non_arrays.empty? ? nil : KatakataIrb::Types::UnionType[*non_arrays]
1114
+ [array_elem, non_array]
1115
+ end
1116
+
1117
+ def method_call(receiver, method_name, args, kwargs, block, scope, name_match: true)
1118
+ methods = KatakataIrb::Types.rbs_methods receiver, method_name.to_sym, args, kwargs, !!block
1119
+ block_called = false
1120
+ type_breaks = methods.map do |method, given_params, method_params|
1121
+ receiver_vars = receiver.is_a?(KatakataIrb::Types::InstanceType) ? receiver.params : {}
1122
+ free_vars = method.type.free_variables - receiver_vars.keys.to_set
1123
+ vars = receiver_vars.merge KatakataIrb::Types.match_free_variables(free_vars, method_params, given_params)
1124
+ if block && method.block
1125
+ params_type = method.block.type.required_positionals.map do |func_param|
1126
+ KatakataIrb::Types.from_rbs_type func_param.type, receiver, vars
1127
+ end
1128
+ self_type = KatakataIrb::Types.from_rbs_type method.block.self_type, receiver, vars if method.block.self_type
1129
+ block_response, breaks = block.call params_type, self_type
1130
+ block_called = true
1131
+ vars.merge! KatakataIrb::Types.match_free_variables(free_vars - vars.keys.to_set, [method.block.type.return_type], [block_response])
1132
+ end
1133
+ if KatakataIrb::Types.method_return_bottom?(method)
1134
+ [nil, breaks]
1135
+ else
1136
+ [KatakataIrb::Types.from_rbs_type(method.type.return_type, receiver, vars || {}), breaks]
1137
+ end
1138
+ end
1139
+ block&.call [], nil unless block_called
1140
+ terminates = !type_breaks.empty? && type_breaks.map(&:first).all?(&:nil?)
1141
+ types = type_breaks.map(&:first).compact
1142
+ breaks = type_breaks.map(&:last).compact
1143
+ types << OBJECT_METHODS[method_name.to_sym] if name_match && OBJECT_METHODS.has_key?(method_name.to_sym)
1144
+
1145
+ if method_name.to_sym == :new
1146
+ receiver.types.each do |type|
1147
+ if type.is_a?(KatakataIrb::Types::SingletonType) && type.module_or_class.is_a?(Class)
1148
+ types << KatakataIrb::Types::InstanceType.new(type.module_or_class)
1149
+ end
1150
+ end
1151
+ end
1152
+ scope&.terminate if terminates && breaks.empty?
1153
+ KatakataIrb::Types::UnionType[*types, *breaks]
1154
+ end
1155
+
1156
+ def evaluate_program(program, scope)
1157
+ # statements.body[0] is local variable assign code
1158
+ program.statements.body[1..].each do |statement|
1159
+ evaluate statement, scope
1160
+ end
1161
+ end
1162
+
1163
+ def self.calculate_target_type_scope(binding, parents, target)
1164
+ dig_targets = DigTarget.new(parents, target) do |type, scope|
1165
+ return type, scope
1166
+ end
1167
+ program = parents.first
1168
+ scope = KatakataIrb::Scope.from_binding(binding, program.locals)
1169
+ new(dig_targets).evaluate_program program, scope
1170
+ [KatakataIrb::Types::NIL, scope]
1171
+ end
1172
+ end