katakata_irb 0.2.0 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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