katakata_irb 0.2.0 → 0.2.1

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,1168 @@
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
+ # TODO: return type of []=, field= when operator_loc.nil?
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
+ method_call receiver_type, node.name, args_types, kwargs_types, call_block_proc, scope
284
+ end
285
+ if node.call_operator == '&.'
286
+ result = scope.conditional { evaluate_method.call _1 }
287
+ if receiver_type.nillable?
288
+ KatakataIrb::Types::UnionType[result, KatakataIrb::Types::NIL]
289
+ else
290
+ result
291
+ end
292
+ else
293
+ evaluate_method.call scope
294
+ end
295
+ end
296
+
297
+ def evaluate_and_node(node, scope) = evaluate_and_or(node, scope, and_op: true)
298
+ def evaluate_or_node(node, scope) = evaluate_and_or(node, scope, and_op: false)
299
+ def evaluate_and_or(node, scope, and_op:)
300
+ left = evaluate node.left, scope
301
+ right = scope.conditional { evaluate node.right, _1 }
302
+ if and_op
303
+ KatakataIrb::Types::UnionType[right, KatakataIrb::Types::NIL, KatakataIrb::Types::FALSE]
304
+ else
305
+ KatakataIrb::Types::UnionType[left, right]
306
+ end
307
+ end
308
+
309
+ def evaluate_call_operator_write_node(node, scope) = evaluate_call_write(node, scope, :operator, node.write_name)
310
+ def evaluate_call_and_write_node(node, scope) = evaluate_call_write(node, scope, :and, node.write_name)
311
+ def evaluate_call_or_write_node(node, scope) = evaluate_call_write(node, scope, :or, node.write_name)
312
+ def evaluate_index_operator_write_node(node, scope) = evaluate_call_write(node, scope, :operator, :[]=)
313
+ def evaluate_index_and_write_node(node, scope) = evaluate_call_write(node, scope, :and, :[]=)
314
+ def evaluate_index_or_write_node(node, scope) = evaluate_call_write(node, scope, :or, :[]=)
315
+ def evaluate_call_write(node, scope, operator, write_name)
316
+ receiver_type = evaluate node.receiver, scope
317
+ if node.respond_to? :arguments
318
+ # Prism >= 0.15.0, Call{Operator,And,Or}WriteNode does not have arguments
319
+ args_types, kwargs_types, block_sym_node, has_block = evaluate_call_node_arguments node, scope
320
+ else
321
+ args_types = []
322
+ end
323
+ if block_sym_node
324
+ block_sym = block_sym_node.value
325
+ call_block_proc = ->(block_args, _self_type) do
326
+ block_receiver, *rest = block_args
327
+ block_receiver ? method_call(block_receiver || KatakataIrb::Types::OBJECT, block_sym, rest, nil, nil, scope) : KatakataIrb::Types::OBJECT
328
+ end
329
+ elsif has_block
330
+ call_block_proc = ->(_block_args, _self_type) { KatakataIrb::Types::OBJECT }
331
+ end
332
+ method = write_name.to_s.delete_suffix('=')
333
+ left = method_call receiver_type, method, args_types, kwargs_types, call_block_proc, scope
334
+ case operator
335
+ when :and
336
+ right = scope.conditional { evaluate node.value, _1 }
337
+ KatakataIrb::Types::UnionType[right, KatakataIrb::Types::NIL, KatakataIrb::Types::FALSE]
338
+ when :or
339
+ right = scope.conditional { evaluate node.value, _1 }
340
+ KatakataIrb::Types::UnionType[left, right]
341
+ else
342
+ right = evaluate node.value, scope
343
+ method_call left, node.operator, [right], nil, nil, scope, name_match: false
344
+ end
345
+ end
346
+
347
+ def evaluate_variable_operator_write(node, scope)
348
+ left = scope[node.name.to_s] || KatakataIrb::Types::OBJECT
349
+ right = evaluate node.value, scope
350
+ scope[node.name.to_s] = method_call left, node.operator, [right], nil, nil, scope, name_match: false
351
+ end
352
+ alias evaluate_global_variable_operator_write_node evaluate_variable_operator_write
353
+ alias evaluate_local_variable_operator_write_node evaluate_variable_operator_write
354
+ alias evaluate_class_variable_operator_write_node evaluate_variable_operator_write
355
+ alias evaluate_instance_variable_operator_write_node evaluate_variable_operator_write
356
+
357
+ def evaluate_variable_and_write(node, scope)
358
+ right = scope.conditional { evaluate node.value, scope }
359
+ scope[node.name.to_s] = KatakataIrb::Types::UnionType[right, KatakataIrb::Types::NIL, KatakataIrb::Types::FALSE]
360
+ end
361
+ alias evaluate_global_variable_and_write_node evaluate_variable_and_write
362
+ alias evaluate_local_variable_and_write_node evaluate_variable_and_write
363
+ alias evaluate_class_variable_and_write_node evaluate_variable_and_write
364
+ alias evaluate_instance_variable_and_write_node evaluate_variable_and_write
365
+
366
+ def evaluate_variable_or_write(node, scope)
367
+ left = scope[node.name.to_s] || KatakataIrb::Types::OBJECT
368
+ right = scope.conditional { evaluate node.value, scope }
369
+ scope[node.name.to_s] = KatakataIrb::Types::UnionType[left, right]
370
+ end
371
+ alias evaluate_global_variable_or_write_node evaluate_variable_or_write
372
+ alias evaluate_local_variable_or_write_node evaluate_variable_or_write
373
+ alias evaluate_class_variable_or_write_node evaluate_variable_or_write
374
+ alias evaluate_instance_variable_or_write_node evaluate_variable_or_write
375
+
376
+ def evaluate_constant_operator_write_node(node, scope)
377
+ left = scope[node.name.to_s] || KatakataIrb::Types::OBJECT
378
+ right = evaluate node.value, scope
379
+ scope[node.name.to_s] = method_call left, node.operator, [right], nil, nil, scope, name_match: false
380
+ end
381
+
382
+ def evaluate_constant_and_write_node(node, scope)
383
+ right = scope.conditional { evaluate node.value, scope }
384
+ scope[node.name.to_s] = KatakataIrb::Types::UnionType[right, KatakataIrb::Types::NIL, KatakataIrb::Types::FALSE]
385
+ end
386
+
387
+ def evaluate_constant_or_write_node(node, scope)
388
+ left = scope[node.name.to_s] || KatakataIrb::Types::OBJECT
389
+ right = scope.conditional { evaluate node.value, scope }
390
+ scope[node.name.to_s] = KatakataIrb::Types::UnionType[left, right]
391
+ end
392
+
393
+ def evaluate_constant_path_operator_write_node(node, scope)
394
+ left, receiver, _parent_module, name = evaluate_constant_node_info node.target, scope
395
+ right = evaluate node.value, scope
396
+ value = method_call left, node.operator, [right], nil, nil, scope, name_match: false
397
+ const_path_write receiver, name, value, scope
398
+ value
399
+ end
400
+
401
+ def evaluate_constant_path_and_write_node(node, scope)
402
+ _left, receiver, _parent_module, name = evaluate_constant_node_info node.target, scope
403
+ right = scope.conditional { evaluate node.value, scope }
404
+ value = KatakataIrb::Types::UnionType[right, KatakataIrb::Types::NIL, KatakataIrb::Types::FALSE]
405
+ const_path_write receiver, name, value, scope
406
+ value
407
+ end
408
+
409
+ def evaluate_constant_path_or_write_node(node, scope)
410
+ left, receiver, _parent_module, name = evaluate_constant_node_info node.target, scope
411
+ right = scope.conditional { evaluate node.value, scope }
412
+ value = KatakataIrb::Types::UnionType[left, right]
413
+ const_path_write receiver, name, value, scope
414
+ value
415
+ end
416
+
417
+ def evaluate_constant_path_write_node(node, scope)
418
+ receiver = evaluate node.target.parent, scope if node.target.parent
419
+ value = evaluate node.value, scope
420
+ const_path_write receiver, node.target.child.name.to_s, value, scope
421
+ value
422
+ end
423
+
424
+ def evaluate_lambda_node(node, scope)
425
+ local_table = node.locals.to_h { [_1.to_s, KatakataIrb::Types::OBJECT] }
426
+ block_scope = KatakataIrb::Scope.new scope, { **local_table, KatakataIrb::Scope::BREAK_RESULT => nil, KatakataIrb::Scope::NEXT_RESULT => nil, KatakataIrb::Scope::RETURN_RESULT => nil }
427
+ block_scope.conditional do |s|
428
+ assign_parameters node.parameters.parameters, s, [], {} if node.parameters&.parameters
429
+ evaluate node.body, s if node.body
430
+ end
431
+ block_scope.merge_jumps
432
+ scope.update block_scope
433
+ KatakataIrb::Types::ProcType.new
434
+ end
435
+
436
+ def evaluate_reference_write(node, scope)
437
+ scope[node.name.to_s] = evaluate node.value, scope
438
+ end
439
+ alias evaluate_constant_write_node evaluate_reference_write
440
+ alias evaluate_global_variable_write_node evaluate_reference_write
441
+ alias evaluate_local_variable_write_node evaluate_reference_write
442
+ alias evaluate_class_variable_write_node evaluate_reference_write
443
+ alias evaluate_instance_variable_write_node evaluate_reference_write
444
+
445
+ def evaluate_multi_write_node(node, scope)
446
+ evaluated_receivers = {}
447
+ evaluate_multi_write_receiver node, scope, evaluated_receivers
448
+ value = (
449
+ if node.value.is_a? Prism::ArrayNode
450
+ if node.value.elements.any?(Prism::SplatNode)
451
+ evaluate node.value, scope
452
+ else
453
+ node.value.elements.map do |n|
454
+ evaluate n, scope
455
+ end
456
+ end
457
+ elsif node.value
458
+ evaluate node.value, scope
459
+ else
460
+ # For syntax invalid code like `(*a).b`
461
+ KatakataIrb::Types::NIL
462
+ end
463
+ )
464
+ evaluate_multi_write node, value, scope, evaluated_receivers
465
+ value.is_a?(Array) ? KatakataIrb::Types.array_of(*value) : value
466
+ end
467
+
468
+ def evaluate_if_node(node, scope) = evaluate_if_unless(node, scope)
469
+ def evaluate_unless_node(node, scope) = evaluate_if_unless(node, scope)
470
+ def evaluate_if_unless(node, scope)
471
+ evaluate node.predicate, scope
472
+ KatakataIrb::Types::UnionType[*scope.run_branches(
473
+ -> { node.statements ? evaluate(node.statements, _1) : KatakataIrb::Types::NIL },
474
+ -> { node.consequent ? evaluate(node.consequent, _1) : KatakataIrb::Types::NIL }
475
+ )]
476
+ end
477
+
478
+ def evaluate_else_node(node, scope)
479
+ node.statements ? evaluate(node.statements, scope) : KatakataIrb::Types::NIL
480
+ end
481
+
482
+ def evaluate_while_until(node, scope)
483
+ inner_scope = KatakataIrb::Scope.new scope, { KatakataIrb::Scope::BREAK_RESULT => nil }
484
+ evaluate node.predicate, inner_scope
485
+ if node.statements
486
+ inner_scope.conditional do |s|
487
+ evaluate node.statements, s
488
+ end
489
+ end
490
+ inner_scope.merge_jumps
491
+ scope.update inner_scope
492
+ breaks = inner_scope[KatakataIrb::Scope::BREAK_RESULT]
493
+ breaks ? KatakataIrb::Types::UnionType[breaks, KatakataIrb::Types::NIL] : KatakataIrb::Types::NIL
494
+ end
495
+ alias evaluate_while_node evaluate_while_until
496
+ alias evaluate_until_node evaluate_while_until
497
+
498
+ def evaluate_break_node(node, scope) = evaluate_jump(node, scope, :break)
499
+ def evaluate_next_node(node, scope) = evaluate_jump(node, scope, :next)
500
+ def evaluate_return_node(node, scope) = evaluate_jump(node, scope, :return)
501
+ def evaluate_jump(node, scope, mode)
502
+ internal_key = (
503
+ case mode
504
+ when :break
505
+ KatakataIrb::Scope::BREAK_RESULT
506
+ when :next
507
+ KatakataIrb::Scope::NEXT_RESULT
508
+ when :return
509
+ KatakataIrb::Scope::RETURN_RESULT
510
+ end
511
+ )
512
+ jump_value = (
513
+ arguments = node.arguments&.arguments
514
+ if arguments.nil? || arguments.empty?
515
+ KatakataIrb::Types::NIL
516
+ elsif arguments.size == 1 && !arguments.first.is_a?(Prism::SplatNode)
517
+ evaluate arguments.first, scope
518
+ else
519
+ KatakataIrb::Types.array_of evaluate_list_splat_items(arguments, scope)
520
+ end
521
+ )
522
+ scope.terminate_with internal_key, jump_value
523
+ KatakataIrb::Types::NIL
524
+ end
525
+
526
+ def evaluate_yield_node(node, scope)
527
+ evaluate_list_splat_items node.arguments.arguments, scope if node.arguments
528
+ KatakataIrb::Types::OBJECT
529
+ end
530
+
531
+ def evaluate_redo_node(_node, scope)
532
+ scope.terminate
533
+ KatakataIrb::Types::NIL
534
+ end
535
+
536
+ def evaluate_retry_node(_node, scope)
537
+ scope.terminate
538
+ KatakataIrb::Types::NIL
539
+ end
540
+
541
+ def evaluate_forwarding_super_node(_node, _scope) = KatakataIrb::Types::OBJECT
542
+
543
+ def evaluate_super_node(node, scope)
544
+ evaluate_list_splat_items node.arguments.arguments, scope if node.arguments
545
+ KatakataIrb::Types::OBJECT
546
+ end
547
+
548
+ def evaluate_begin_node(node, scope)
549
+ return_type = node.statements ? evaluate(node.statements, scope) : KatakataIrb::Types::NIL
550
+ if node.rescue_clause
551
+ if node.else_clause
552
+ return_types = scope.run_branches(
553
+ ->{ evaluate node.rescue_clause, _1 },
554
+ ->{ evaluate node.else_clause, _1 }
555
+ )
556
+ else
557
+ return_types = [
558
+ return_type,
559
+ scope.conditional { evaluate node.rescue_clause, _1 }
560
+ ]
561
+ end
562
+ return_type = KatakataIrb::Types::UnionType[*return_types]
563
+ end
564
+ if node.ensure_clause&.statements
565
+ # ensure_clause is Prism::EnsureNode
566
+ evaluate node.ensure_clause.statements, scope
567
+ end
568
+ return_type
569
+ end
570
+
571
+ def evaluate_rescue_node(node, scope)
572
+ run_rescue = lambda do |s|
573
+ if node.reference
574
+ error_classes_type = evaluate_list_splat_items node.exceptions, s
575
+ error_types = error_classes_type.types.filter_map do
576
+ KatakataIrb::Types::InstanceType.new _1.module_or_class if _1.is_a?(KatakataIrb::Types::SingletonType)
577
+ end
578
+ error_types << KatakataIrb::Types::InstanceType.new(StandardError) if error_types.empty?
579
+ error_type = KatakataIrb::Types::UnionType[*error_types]
580
+ case node.reference
581
+ when Prism::LocalVariableTargetNode, Prism::InstanceVariableTargetNode, Prism::ClassVariableTargetNode, Prism::GlobalVariableTargetNode, Prism::ConstantTargetNode
582
+ s[node.reference.name.to_s] = error_type
583
+ when Prism::CallNode
584
+ evaluate node.reference, s
585
+ end
586
+ end
587
+ node.statements ? evaluate(node.statements, s) : KatakataIrb::Types::NIL
588
+ end
589
+ if node.consequent # begin; rescue A; rescue B; end
590
+ types = scope.run_branches(
591
+ run_rescue,
592
+ -> { evaluate node.consequent, _1 }
593
+ )
594
+ KatakataIrb::Types::UnionType[*types]
595
+ else
596
+ run_rescue.call scope
597
+ end
598
+ end
599
+
600
+ def evaluate_rescue_modifier_node(node, scope)
601
+ a = evaluate node.expression, scope
602
+ b = scope.conditional { evaluate node.rescue_expression, _1 }
603
+ KatakataIrb::Types::UnionType[a, b]
604
+ end
605
+
606
+ def evaluate_singleton_class_node(node, scope)
607
+ klass_types = evaluate(node.expression, scope).types.filter_map do |type|
608
+ KatakataIrb::Types::SingletonType.new type.klass if type.is_a? KatakataIrb::Types::InstanceType
609
+ end
610
+ klass_types = [KatakataIrb::Types::CLASS] if klass_types.empty?
611
+ table = node.locals.to_h { [_1.to_s, KatakataIrb::Types::NIL] }
612
+ sclass_scope = KatakataIrb::Scope.new(
613
+ scope,
614
+ { **table, KatakataIrb::Scope::BREAK_RESULT => nil, KatakataIrb::Scope::NEXT_RESULT => nil, KatakataIrb::Scope::RETURN_RESULT => nil },
615
+ trace_ivar: false,
616
+ trace_lvar: false,
617
+ self_type: KatakataIrb::Types::UnionType[*klass_types]
618
+ )
619
+ result = node.body ? evaluate(node.body, sclass_scope) : KatakataIrb::Types::NIL
620
+ scope.update sclass_scope
621
+ result
622
+ end
623
+
624
+ def evaluate_class_node(node, scope) = evaluate_class_module(node, scope, true)
625
+ def evaluate_module_node(node, scope) = evaluate_class_module(node, scope, false)
626
+ def evaluate_class_module(node, scope, is_class)
627
+ unless node.constant_path.is_a?(Prism::ConstantReadNode) || node.constant_path.is_a?(Prism::ConstantPathNode)
628
+ # Incomplete class/module `class (statement[cursor_here])::Name; end`
629
+ evaluate node.constant_path, scope
630
+ return KatakataIrb::Types::NIL
631
+ end
632
+ const_type, _receiver, parent_module, name = evaluate_constant_node_info node.constant_path, scope
633
+ if is_class
634
+ select_class_type = -> { _1.is_a?(KatakataIrb::Types::SingletonType) && _1.module_or_class.is_a?(Class) }
635
+ module_types = const_type.types.select(&select_class_type)
636
+ module_types += evaluate(node.superclass, scope).types.select(&select_class_type) if node.superclass
637
+ module_types << KatakataIrb::Types::CLASS if module_types.empty?
638
+ else
639
+ module_types = const_type.types.select { _1.is_a?(KatakataIrb::Types::SingletonType) && !_1.module_or_class.is_a?(Class) }
640
+ module_types << KatakataIrb::Types::MODULE if module_types.empty?
641
+ end
642
+ return KatakataIrb::Types::NIL unless node.body
643
+
644
+ table = node.locals.to_h { [_1.to_s, KatakataIrb::Types::NIL] }
645
+ if !name.empty? && (parent_module.is_a?(Module) || parent_module.nil?)
646
+ value = parent_module.const_get name if parent_module&.const_defined? name
647
+ unless value
648
+ value_type = scope[name]
649
+ value = value_type.module_or_class if value_type.is_a? KatakataIrb::Types::SingletonType
650
+ end
651
+
652
+ if value.is_a? Module
653
+ nesting = [value, []]
654
+ else
655
+ if parent_module
656
+ nesting = [parent_module, [name]]
657
+ else
658
+ parent_nesting, parent_path = scope.module_nesting.first
659
+ nesting = [parent_nesting, parent_path + [name]]
660
+ end
661
+ nesting_key = [nesting[0].__id__, nesting[1]].join('::')
662
+ nesting_value = is_class ? KatakataIrb::Types::CLASS : KatakataIrb::Types::MODULE
663
+ end
664
+ else
665
+ # parent_module == :unknown
666
+ # TODO: dummy module
667
+ end
668
+ module_scope = KatakataIrb::Scope.new(
669
+ scope,
670
+ { **table, KatakataIrb::Scope::BREAK_RESULT => nil, KatakataIrb::Scope::NEXT_RESULT => nil, KatakataIrb::Scope::RETURN_RESULT => nil },
671
+ trace_ivar: false,
672
+ trace_lvar: false,
673
+ self_type: KatakataIrb::Types::UnionType[*module_types],
674
+ nesting: nesting
675
+ )
676
+ module_scope[nesting_key] = nesting_value if nesting_value
677
+ result = evaluate(node.body, module_scope)
678
+ scope.update module_scope
679
+ result
680
+ end
681
+
682
+ def evaluate_for_node(node, scope)
683
+ node.statements
684
+ collection = evaluate node.collection, scope
685
+ inner_scope = KatakataIrb::Scope.new scope, { KatakataIrb::Scope::BREAK_RESULT => nil }
686
+ ary_type = method_call collection, :to_ary, [], nil, nil, nil, name_match: false
687
+ element_types = ary_type.types.filter_map do |ary|
688
+ ary.params[:Elem] if ary.is_a?(KatakataIrb::Types::InstanceType) && ary.klass == Array
689
+ end
690
+ element_type = KatakataIrb::Types::UnionType[*element_types]
691
+ inner_scope.conditional do |s|
692
+ evaluate_write node.index, element_type, s, nil
693
+ evaluate node.statements, s if node.statements
694
+ end
695
+ inner_scope.merge_jumps
696
+ scope.update inner_scope
697
+ breaks = inner_scope[KatakataIrb::Scope::BREAK_RESULT]
698
+ breaks ? KatakataIrb::Types::UnionType[breaks, collection] : collection
699
+ end
700
+
701
+ def evaluate_case_node(node, scope)
702
+ target = evaluate(node.predicate, scope) if node.predicate
703
+ # TODO
704
+ branches = node.conditions.map do |condition|
705
+ ->(s) { evaluate_case_match target, condition, s }
706
+ end
707
+ if node.consequent
708
+ branches << ->(s) { evaluate node.consequent, s }
709
+ elsif node.conditions.any? { _1.is_a? Prism::WhenNode }
710
+ branches << ->(_s) { KatakataIrb::Types::NIL }
711
+ end
712
+ KatakataIrb::Types::UnionType[*scope.run_branches(*branches)]
713
+ end
714
+
715
+ def evaluate_match_required_node(node, scope)
716
+ value_type = evaluate node.value, scope
717
+ evaluate_match_pattern value_type, node.pattern, scope
718
+ KatakataIrb::Types::NIL # void value
719
+ end
720
+
721
+ def evaluate_match_predicate_node(node, scope)
722
+ value_type = evaluate node.value, scope
723
+ scope.conditional { evaluate_match_pattern value_type, node.pattern, _1 }
724
+ KatakataIrb::Types::BOOLEAN
725
+ end
726
+
727
+ def evaluate_range_node(node, scope)
728
+ beg_type = evaluate node.left, scope if node.left
729
+ end_type = evaluate node.right, scope if node.right
730
+ elem = (KatakataIrb::Types::UnionType[*[beg_type, end_type].compact]).nonnillable
731
+ KatakataIrb::Types::InstanceType.new Range, Elem: elem
732
+ end
733
+
734
+ def evaluate_defined_node(node, scope)
735
+ scope.conditional { evaluate node.value, _1 }
736
+ KatakataIrb::Types::UnionType[KatakataIrb::Types::STRING, KatakataIrb::Types::NIL]
737
+ end
738
+
739
+ def evaluate_flip_flop_node(node, scope)
740
+ scope.conditional { evaluate node.left, _1 } if node.left
741
+ scope.conditional { evaluate node.right, _1 } if node.right
742
+ KatakataIrb::Types::BOOLEAN
743
+ end
744
+
745
+ def evaluate_multi_target_node(node, scope)
746
+ # Raw MultiTargetNode, incomplete code like `a,b`, `*a`.
747
+ evaluate_multi_write_receiver node, scope, nil
748
+ KatakataIrb::Types::NIL
749
+ end
750
+
751
+ def evaluate_implicit_node(node, scope)
752
+ evaluate node.value, scope
753
+ end
754
+
755
+ def evaluate_match_write_node(node, scope)
756
+ # /(?<a>)(?<b>)/ =~ string
757
+ evaluate node.call, scope
758
+ node.locals.each { scope[_1.to_s] = KatakataIrb::Types::UnionType[KatakataIrb::Types::STRING, KatakataIrb::Types::NIL] }
759
+ KatakataIrb::Types::BOOLEAN
760
+ end
761
+
762
+ def evaluate_match_last_line_node(_node, _scope)
763
+ KatakataIrb::Types::BOOLEAN
764
+ end
765
+
766
+ def evaluate_interpolated_match_last_line_node(node, scope)
767
+ node.parts.each { evaluate _1, scope }
768
+ KatakataIrb::Types::BOOLEAN
769
+ end
770
+
771
+ def evaluate_pre_execution_node(node, scope)
772
+ node.statements ? evaluate(node.statements, scope) : KatakataIrb::Types::NIL
773
+ end
774
+
775
+ def evaluate_post_execution_node(node, scope)
776
+ node.statements && @dig_targets.dig?(node.statements) ? evaluate(node.statements, scope) : KatakataIrb::Types::NIL
777
+ end
778
+
779
+ def evaluate_alias_method_node(_node, _scope) = KatakataIrb::Types::NIL
780
+ def evaluate_alias_global_variable_node(_node, _scope) = KatakataIrb::Types::NIL
781
+ def evaluate_undef_node(_node, _scope) = KatakataIrb::Types::NIL
782
+ def evaluate_missing_node(_node, _scope) = KatakataIrb::Types::NIL
783
+
784
+ def evaluate_call_node_arguments(call_node, scope)
785
+ # call_node.arguments is Prism::ArgumentsNode
786
+ arguments = call_node.arguments&.arguments&.dup || []
787
+ block_arg = call_node.block.expression if call_node.respond_to?(:block) && call_node.block.is_a?(Prism::BlockArgumentNode)
788
+ kwargs = arguments.pop.elements if arguments.last.is_a?(Prism::KeywordHashNode)
789
+ args_types = arguments.map do |arg|
790
+ case arg
791
+ when Prism::ForwardingArgumentsNode
792
+ # `f(a, ...)` treat like splat
793
+ nil
794
+ when Prism::SplatNode
795
+ evaluate arg.expression, scope
796
+ nil # TODO: splat
797
+ else
798
+ evaluate arg, scope
799
+ end
800
+ end
801
+ if kwargs
802
+ kwargs_types = kwargs.map do |arg|
803
+ case arg
804
+ when Prism::AssocNode
805
+ if arg.key.is_a?(Prism::SymbolNode)
806
+ [arg.key.value, evaluate(arg.value, scope)]
807
+ else
808
+ evaluate arg.key, scope
809
+ evaluate arg.value, scope
810
+ nil
811
+ end
812
+ when Prism::AssocSplatNode
813
+ evaluate arg.value, scope
814
+ nil
815
+ end
816
+ end.compact.to_h
817
+ end
818
+ if block_arg.is_a? Prism::SymbolNode
819
+ block_sym_node = block_arg
820
+ elsif block_arg
821
+ evaluate block_arg, scope
822
+ end
823
+ [args_types, kwargs_types, block_sym_node, !!block_arg]
824
+ end
825
+
826
+ def const_path_write(receiver, name, value, scope)
827
+ if receiver # receiver::A = value
828
+ singleton_type = receiver.types.find { _1.is_a? KatakataIrb::Types::SingletonType }
829
+ scope.set_const singleton_type.module_or_class, name, value if singleton_type
830
+ else # ::A = value
831
+ scope.set_const Object, name, value
832
+ end
833
+ end
834
+
835
+ def assign_required_parameter(node, value, scope)
836
+ case node
837
+ when Prism::RequiredParameterNode
838
+ scope[node.name.to_s] = value || KatakataIrb::Types::OBJECT
839
+ when Prism::RequiredDestructuredParameterNode
840
+ values = value ? sized_splat(value, :to_ary, node.parameters.size) : []
841
+ node.parameters.zip values do |n, v|
842
+ assign_required_parameter n, v, scope
843
+ end
844
+ when Prism::SplatNode
845
+ splat_value = value ? KatakataIrb::Types.array_of(value) : KatakataIrb::Types::ARRAY
846
+ assign_required_parameter node.expression, splat_value, scope
847
+ end
848
+ end
849
+
850
+ def evaluate_constant_node_info(node, scope)
851
+ case node
852
+ when Prism::ConstantPathNode
853
+ name = node.child.name.to_s
854
+ if node.parent
855
+ receiver = evaluate node.parent, scope
856
+ if receiver.is_a? KatakataIrb::Types::SingletonType
857
+ parent_module = receiver.module_or_class
858
+ end
859
+ else
860
+ parent_module = Object
861
+ end
862
+ if parent_module
863
+ type = scope.get_const(parent_module, [name]) || KatakataIrb::Types::NIL
864
+ else
865
+ parent_module = :unknown
866
+ type = KatakataIrb::Types::NIL
867
+ end
868
+ when Prism::ConstantReadNode
869
+ name = node.name.to_s
870
+ type = scope[name]
871
+ end
872
+ @dig_targets.resolve type, scope if @dig_targets.target? node
873
+ [type, receiver, parent_module, name]
874
+ end
875
+
876
+
877
+ def assign_parameters(node, scope, args, kwargs)
878
+ args = args.dup
879
+ kwargs = kwargs.dup
880
+ size = node.requireds.size + node.optionals.size + (node.rest ? 1 : 0) + node.posts.size
881
+ args = sized_splat(args.first, :to_ary, size) if size >= 2 && args.size == 1
882
+ reqs = args.shift node.requireds.size
883
+ if node.rest
884
+ # node.rest.class is Prism::RestParameterNode
885
+ posts = []
886
+ opts = args.shift node.optionals.size
887
+ rest = args
888
+ else
889
+ posts = args.pop node.posts.size
890
+ opts = args
891
+ rest = []
892
+ end
893
+ node.requireds.zip reqs do |n, v|
894
+ assign_required_parameter n, v, scope
895
+ end
896
+ node.optionals.zip opts do |n, v|
897
+ # n is Prism::OptionalParameterNode
898
+ values = [v]
899
+ values << evaluate(n.value, scope) if n.value
900
+ scope[n.name.to_s] = KatakataIrb::Types::UnionType[*values.compact]
901
+ end
902
+ node.posts.zip posts do |n, v|
903
+ assign_required_parameter n, v, scope
904
+ end
905
+ if node.rest&.name
906
+ # node.rest is Prism::RestParameterNode
907
+ scope[node.rest.name.to_s] = KatakataIrb::Types.array_of(*rest)
908
+ end
909
+ node.keywords.each do |n|
910
+ # n is Prism::KeywordParameterNode
911
+ name = n.name.to_s.delete(':')
912
+ values = [kwargs.delete(name)]
913
+ values << evaluate(n.value, scope) if n.value
914
+ scope[name] = KatakataIrb::Types::UnionType[*values.compact]
915
+ end
916
+ # node.keyword_rest is Prism::KeywordRestParameterNode or Prism::ForwardingParameterNode or Prism::NoKeywordsParameterNode
917
+ if node.keyword_rest.is_a?(Prism::KeywordRestParameterNode) && node.keyword_rest.name
918
+ scope[node.keyword_rest.name.to_s] = KatakataIrb::Types::InstanceType.new(Hash, K: KatakataIrb::Types::SYMBOL, V: KatakataIrb::Types::UnionType[*kwargs.values])
919
+ end
920
+ if node.block&.name
921
+ # node.block is Prism::BlockParameterNode
922
+ scope[node.block.name.to_s] = KatakataIrb::Types::PROC
923
+ end
924
+ end
925
+
926
+ def assign_numbered_parameters(numbered_parameters, scope, args, _kwargs)
927
+ return if numbered_parameters.empty?
928
+ max_num = numbered_parameters.map { _1[1].to_i }.max
929
+ if max_num == 1
930
+ if args.size == 0
931
+ scope['_1'] = KatakataIrb::Types::NIL
932
+ elsif args.size == 1
933
+ scope['_1'] = args.first
934
+ else
935
+ scope['_1'] = KatakataIrb::Types.array_of(*args)
936
+ end
937
+ else
938
+ args = sized_splat(args.first, :to_ary, max_num) if args.size == 1
939
+ numbered_parameters.each do |name|
940
+ index = name[1].to_i - 1
941
+ scope[name] = args[index] || KatakataIrb::Types::NIL
942
+ end
943
+ end
944
+ end
945
+
946
+ def evaluate_case_match(target, node, scope)
947
+ case node
948
+ when Prism::WhenNode
949
+ node.conditions.each { evaluate _1, scope }
950
+ node.statements ? evaluate(node.statements, scope) : KatakataIrb::Types::NIL
951
+ when Prism::InNode
952
+ pattern = node.pattern
953
+ if pattern.is_a?(Prism::IfNode) || pattern.is_a?(Prism::UnlessNode)
954
+ cond_node = pattern.predicate
955
+ pattern = pattern.statements.body.first
956
+ end
957
+ evaluate_match_pattern(target, pattern, scope)
958
+ evaluate cond_node, scope if cond_node # TODO: conditional branch
959
+ node.statements ? evaluate(node.statements, scope) : KatakataIrb::Types::NIL
960
+ end
961
+ end
962
+
963
+ def evaluate_match_pattern(value, pattern, scope)
964
+ # TODO: scope.terminate_with KatakataIrb::Scope::PATTERNMATCH_BREAK, KatakataIrb::Types::NIL
965
+ case pattern
966
+ when Prism::FindPatternNode
967
+ # TODO
968
+ evaluate_match_pattern KatakataIrb::Types::OBJECT, pattern.left, scope
969
+ pattern.requireds.each { evaluate_match_pattern KatakataIrb::Types::OBJECT, _1, scope }
970
+ evaluate_match_pattern KatakataIrb::Types::OBJECT, pattern.right, scope
971
+ when Prism::ArrayPatternNode
972
+ # TODO
973
+ pattern.requireds.each { evaluate_match_pattern KatakataIrb::Types::OBJECT, _1, scope }
974
+ evaluate_match_pattern KatakataIrb::Types::OBJECT, pattern.rest, scope if pattern.rest
975
+ pattern.posts.each { evaluate_match_pattern KatakataIrb::Types::OBJECT, _1, scope }
976
+ KatakataIrb::Types::ARRAY
977
+ when Prism::HashPatternNode
978
+ # TODO
979
+ pattern.assocs.each { evaluate_match_pattern KatakataIrb::Types::OBJECT, _1, scope }
980
+ KatakataIrb::Types::HASH
981
+ when Prism::AssocNode
982
+ evaluate_match_pattern value, pattern.value, scope if pattern.value
983
+ KatakataIrb::Types::OBJECT
984
+ when Prism::AssocSplatNode
985
+ # TODO
986
+ evaluate_match_pattern KatakataIrb::Types::HASH, pattern.value, scope
987
+ KatakataIrb::Types::OBJECT
988
+ when Prism::PinnedVariableNode
989
+ evaluate pattern.variable, scope
990
+ when Prism::PinnedExpressionNode
991
+ evaluate pattern.expression, scope
992
+ when Prism::LocalVariableTargetNode
993
+ scope[pattern.name.to_s] = value
994
+ when Prism::AlternationPatternNode
995
+ KatakataIrb::Types::UnionType[evaluate_match_pattern(value, pattern.left, scope), evaluate_match_pattern(value, pattern.right, scope)]
996
+ when Prism::CapturePatternNode
997
+ capture_type = class_or_value_to_instance evaluate_match_pattern(value, pattern.value, scope)
998
+ value = capture_type unless capture_type.types.empty? || capture_type.types == [KatakataIrb::Types::OBJECT]
999
+ evaluate_match_pattern value, pattern.target, scope
1000
+ when Prism::SplatNode
1001
+ value = KatakataIrb::Types.array_of value
1002
+ evaluate_match_pattern value, pattern.expression, scope if pattern.expression
1003
+ value
1004
+ else
1005
+ # literal node
1006
+ type = evaluate(pattern, scope)
1007
+ class_or_value_to_instance(type)
1008
+ end
1009
+ end
1010
+
1011
+ def class_or_value_to_instance(type)
1012
+ instance_types = type.types.map do |t|
1013
+ t.is_a?(KatakataIrb::Types::SingletonType) ? KatakataIrb::Types::InstanceType.new(t.module_or_class) : t
1014
+ end
1015
+ KatakataIrb::Types::UnionType[*instance_types]
1016
+ end
1017
+
1018
+ def evaluate_write(node, value, scope, evaluated_receivers)
1019
+ case node
1020
+ when Prism::MultiTargetNode
1021
+ evaluate_multi_write node, value, scope, evaluated_receivers
1022
+ when Prism::CallNode
1023
+ evaluated_receivers&.[](node.receiver) || evaluate(node.receiver, scope) if node.receiver
1024
+ when Prism::SplatNode
1025
+ evaluate_write node.expression, KatakataIrb::Types.array_of(value), scope, evaluated_receivers
1026
+ when Prism::LocalVariableTargetNode, Prism::GlobalVariableTargetNode, Prism::InstanceVariableTargetNode, Prism::ClassVariableTargetNode, Prism::ConstantTargetNode
1027
+ scope[node.name.to_s] = value
1028
+ when Prism::ConstantPathTargetNode
1029
+ receiver = evaluated_receivers&.[](node.parent) || evaluate(node.parent, scope) if node.parent
1030
+ const_path_write receiver, node.child.name.to_s, value, scope
1031
+ value
1032
+ end
1033
+ end
1034
+
1035
+ def evaluate_multi_write(node, values, scope, evaluated_receivers)
1036
+ values = sized_splat values, :to_ary, node.targets.size unless values.is_a? Array
1037
+ splat_index = node.targets.find_index { _1.is_a? Prism::SplatNode }
1038
+ if splat_index
1039
+ pre_targets = node.targets[0...splat_index]
1040
+ splat_target = node.targets[splat_index]
1041
+ post_targets = node.targets[splat_index + 1..]
1042
+ pre_values = values.shift pre_targets.size
1043
+ post_values = values.pop post_targets.size
1044
+ splat_value = KatakataIrb::Types::UnionType[*values]
1045
+ zips = pre_targets.zip(pre_values) + [[splat_target, splat_value]] + post_targets.zip(post_values)
1046
+ else
1047
+ zips = node.targets.zip(values)
1048
+ end
1049
+ zips.each do |target, value|
1050
+ evaluate_write target, value || KatakataIrb::Types::NIL, scope, evaluated_receivers
1051
+ end
1052
+ end
1053
+
1054
+ def evaluate_multi_write_receiver(node, scope, evaluated_receivers)
1055
+ case node
1056
+ when Prism::MultiWriteNode, Prism::MultiTargetNode
1057
+ node.targets.each { evaluate_multi_write_receiver _1, scope, evaluated_receivers }
1058
+ when Prism::CallNode
1059
+ if node.receiver
1060
+ receiver = evaluate(node.receiver, scope)
1061
+ evaluated_receivers[node.receiver] = receiver if evaluated_receivers
1062
+ end
1063
+ if node.arguments
1064
+ node.arguments.arguments&.each do |arg|
1065
+ if arg.is_a? Prism::SplatNode
1066
+ evaluate arg.expression, scope
1067
+ else
1068
+ evaluate arg, scope
1069
+ end
1070
+ end
1071
+ end
1072
+ when Prism::SplatNode
1073
+ evaluate_multi_write_receiver node.expression, scope, evaluated_receivers if node.expression
1074
+ end
1075
+ end
1076
+
1077
+ def evaluate_list_splat_items(list, scope)
1078
+ items = list.flat_map do |node|
1079
+ if node.is_a? Prism::SplatNode
1080
+ splat = evaluate node.expression, scope
1081
+ array_elem, non_array = partition_to_array splat.nonnillable, :to_a
1082
+ [*array_elem, *non_array]
1083
+ else
1084
+ evaluate node, scope
1085
+ end
1086
+ end.uniq
1087
+ KatakataIrb::Types::UnionType[*items]
1088
+ end
1089
+
1090
+ def sized_splat(value, method, size)
1091
+ array_elem, non_array = partition_to_array value, method
1092
+ values = [KatakataIrb::Types::UnionType[*array_elem, *non_array]]
1093
+ values += [array_elem] * (size - 1) if array_elem && size >= 1
1094
+ values
1095
+ end
1096
+
1097
+ def partition_to_array(value, method)
1098
+ arrays, non_arrays = value.types.partition { _1.is_a?(KatakataIrb::Types::InstanceType) && _1.klass == Array }
1099
+ non_arrays.select! do |type|
1100
+ to_array_result = method_call type, method, [], nil, nil, nil, name_match: false
1101
+ if to_array_result.is_a?(KatakataIrb::Types::InstanceType) && to_array_result.klass == Array
1102
+ arrays << to_array_result
1103
+ false
1104
+ else
1105
+ true
1106
+ end
1107
+ end
1108
+ array_elem = arrays.empty? ? nil : KatakataIrb::Types::UnionType[*arrays.map { _1.params[:Elem] || KatakataIrb::Types::OBJECT }]
1109
+ non_array = non_arrays.empty? ? nil : KatakataIrb::Types::UnionType[*non_arrays]
1110
+ [array_elem, non_array]
1111
+ end
1112
+
1113
+ def method_call(receiver, method_name, args, kwargs, block, scope, name_match: true)
1114
+ methods = KatakataIrb::Types.rbs_methods receiver, method_name.to_sym, args, kwargs, !!block
1115
+ block_called = false
1116
+ type_breaks = methods.map do |method, given_params, method_params|
1117
+ receiver_vars = receiver.is_a?(KatakataIrb::Types::InstanceType) ? receiver.params : {}
1118
+ free_vars = method.type.free_variables - receiver_vars.keys.to_set
1119
+ vars = receiver_vars.merge KatakataIrb::Types.match_free_variables(free_vars, method_params, given_params)
1120
+ if block && method.block
1121
+ params_type = method.block.type.required_positionals.map do |func_param|
1122
+ KatakataIrb::Types.from_rbs_type func_param.type, receiver, vars
1123
+ end
1124
+ self_type = KatakataIrb::Types.from_rbs_type method.block.self_type, receiver, vars if method.block.self_type
1125
+ block_response, breaks = block.call params_type, self_type
1126
+ block_called = true
1127
+ vars.merge! KatakataIrb::Types.match_free_variables(free_vars - vars.keys.to_set, [method.block.type.return_type], [block_response])
1128
+ end
1129
+ if KatakataIrb::Types.method_return_bottom?(method)
1130
+ [nil, breaks]
1131
+ else
1132
+ [KatakataIrb::Types.from_rbs_type(method.type.return_type, receiver, vars || {}), breaks]
1133
+ end
1134
+ end
1135
+ block&.call [], nil unless block_called
1136
+ terminates = !type_breaks.empty? && type_breaks.map(&:first).all?(&:nil?)
1137
+ types = type_breaks.map(&:first).compact
1138
+ breaks = type_breaks.map(&:last).compact
1139
+ types << OBJECT_METHODS[method_name.to_sym] if name_match && OBJECT_METHODS.has_key?(method_name.to_sym)
1140
+
1141
+ if method_name.to_sym == :new
1142
+ receiver.types.each do |type|
1143
+ if type.is_a?(KatakataIrb::Types::SingletonType) && type.module_or_class.is_a?(Class)
1144
+ types << KatakataIrb::Types::InstanceType.new(type.module_or_class)
1145
+ end
1146
+ end
1147
+ end
1148
+ scope&.terminate if terminates && breaks.empty?
1149
+ KatakataIrb::Types::UnionType[*types, *breaks]
1150
+ end
1151
+
1152
+ def evaluate_program(program, scope)
1153
+ # statements.body[0] is local variable assign code
1154
+ program.statements.body[1..].each do |statement|
1155
+ evaluate statement, scope
1156
+ end
1157
+ end
1158
+
1159
+ def self.calculate_target_type_scope(binding, parents, target)
1160
+ dig_targets = DigTarget.new(parents, target) do |type, scope|
1161
+ return type, scope
1162
+ end
1163
+ program = parents.first
1164
+ scope = KatakataIrb::Scope.from_binding(binding, program.locals)
1165
+ new(dig_targets).evaluate_program program, scope
1166
+ [KatakataIrb::Types::NIL, scope]
1167
+ end
1168
+ end