irb 1.9.1 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +4 -4
- data/README.md +10 -9
- data/Rakefile +2 -2
- data/lib/irb/cmd/history.rb +47 -0
- data/lib/irb/cmd/show_cmds.rb +6 -0
- data/lib/irb/cmd/show_source.rb +9 -2
- data/lib/irb/completion.rb +21 -0
- data/lib/irb/context.rb +22 -14
- data/lib/irb/debug.rb +18 -0
- data/lib/irb/extend-command.rb +6 -0
- data/lib/irb/history.rb +1 -1
- data/lib/irb/init.rb +3 -4
- data/lib/irb/lc/help-message +1 -0
- data/lib/irb/pager.rb +12 -11
- data/lib/irb/source_finder.rb +26 -6
- data/lib/irb/version.rb +2 -2
- data/lib/irb.rb +48 -40
- metadata +3 -7
- data/lib/irb/type_completion/completor.rb +0 -235
- data/lib/irb/type_completion/methods.rb +0 -13
- data/lib/irb/type_completion/scope.rb +0 -412
- data/lib/irb/type_completion/type_analyzer.rb +0 -1169
- data/lib/irb/type_completion/types.rb +0 -426
@@ -1,1169 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'set'
|
4
|
-
require_relative 'types'
|
5
|
-
require_relative 'scope'
|
6
|
-
require 'prism'
|
7
|
-
|
8
|
-
module IRB
|
9
|
-
module TypeCompletion
|
10
|
-
class TypeAnalyzer
|
11
|
-
class DigTarget
|
12
|
-
def initialize(parents, receiver, &block)
|
13
|
-
@dig_ids = parents.to_h { [_1.__id__, true] }
|
14
|
-
@target_id = receiver.__id__
|
15
|
-
@block = block
|
16
|
-
end
|
17
|
-
|
18
|
-
def dig?(node) = @dig_ids[node.__id__]
|
19
|
-
def target?(node) = @target_id == node.__id__
|
20
|
-
def resolve(type, scope)
|
21
|
-
@block.call type, scope
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
OBJECT_METHODS = {
|
26
|
-
to_s: Types::STRING,
|
27
|
-
to_str: Types::STRING,
|
28
|
-
to_a: Types::ARRAY,
|
29
|
-
to_ary: Types::ARRAY,
|
30
|
-
to_h: Types::HASH,
|
31
|
-
to_hash: Types::HASH,
|
32
|
-
to_i: Types::INTEGER,
|
33
|
-
to_int: Types::INTEGER,
|
34
|
-
to_f: Types::FLOAT,
|
35
|
-
to_c: Types::COMPLEX,
|
36
|
-
to_r: Types::RATIONAL
|
37
|
-
}
|
38
|
-
|
39
|
-
def initialize(dig_targets)
|
40
|
-
@dig_targets = dig_targets
|
41
|
-
end
|
42
|
-
|
43
|
-
def evaluate(node, scope)
|
44
|
-
method = "evaluate_#{node.type}"
|
45
|
-
if respond_to? method
|
46
|
-
result = send method, node, scope
|
47
|
-
else
|
48
|
-
result = Types::NIL
|
49
|
-
end
|
50
|
-
@dig_targets.resolve result, scope if @dig_targets.target? node
|
51
|
-
result
|
52
|
-
end
|
53
|
-
|
54
|
-
def evaluate_program_node(node, scope)
|
55
|
-
evaluate node.statements, scope
|
56
|
-
end
|
57
|
-
|
58
|
-
def evaluate_statements_node(node, scope)
|
59
|
-
if node.body.empty?
|
60
|
-
Types::NIL
|
61
|
-
else
|
62
|
-
node.body.map { evaluate _1, scope }.last
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def evaluate_def_node(node, scope)
|
67
|
-
if node.receiver
|
68
|
-
self_type = evaluate node.receiver, scope
|
69
|
-
else
|
70
|
-
current_self_types = scope.self_type.types
|
71
|
-
self_types = current_self_types.map do |type|
|
72
|
-
if type.is_a?(Types::SingletonType) && type.module_or_class.is_a?(Class)
|
73
|
-
Types::InstanceType.new type.module_or_class
|
74
|
-
else
|
75
|
-
type
|
76
|
-
end
|
77
|
-
end
|
78
|
-
self_type = Types::UnionType[*self_types]
|
79
|
-
end
|
80
|
-
if @dig_targets.dig?(node.body) || @dig_targets.dig?(node.parameters)
|
81
|
-
params_table = node.locals.to_h { [_1.to_s, Types::NIL] }
|
82
|
-
method_scope = Scope.new(
|
83
|
-
scope,
|
84
|
-
{ **params_table, Scope::BREAK_RESULT => nil, Scope::NEXT_RESULT => nil, Scope::RETURN_RESULT => nil },
|
85
|
-
self_type: self_type,
|
86
|
-
trace_lvar: false,
|
87
|
-
trace_ivar: false
|
88
|
-
)
|
89
|
-
if node.parameters
|
90
|
-
# node.parameters is Prism::ParametersNode
|
91
|
-
assign_parameters node.parameters, method_scope, [], {}
|
92
|
-
end
|
93
|
-
|
94
|
-
if @dig_targets.dig?(node.body)
|
95
|
-
method_scope.conditional do |s|
|
96
|
-
evaluate node.body, s
|
97
|
-
end
|
98
|
-
end
|
99
|
-
method_scope.merge_jumps
|
100
|
-
scope.update method_scope
|
101
|
-
end
|
102
|
-
Types::SYMBOL
|
103
|
-
end
|
104
|
-
|
105
|
-
def evaluate_integer_node(_node, _scope) = Types::INTEGER
|
106
|
-
|
107
|
-
def evaluate_float_node(_node, _scope) = Types::FLOAT
|
108
|
-
|
109
|
-
def evaluate_rational_node(_node, _scope) = Types::RATIONAL
|
110
|
-
|
111
|
-
def evaluate_imaginary_node(_node, _scope) = Types::COMPLEX
|
112
|
-
|
113
|
-
def evaluate_string_node(_node, _scope) = Types::STRING
|
114
|
-
|
115
|
-
def evaluate_x_string_node(_node, _scope)
|
116
|
-
Types::UnionType[Types::STRING, Types::NIL]
|
117
|
-
end
|
118
|
-
|
119
|
-
def evaluate_symbol_node(_node, _scope) = Types::SYMBOL
|
120
|
-
|
121
|
-
def evaluate_regular_expression_node(_node, _scope) = Types::REGEXP
|
122
|
-
|
123
|
-
def evaluate_string_concat_node(node, scope)
|
124
|
-
evaluate node.left, scope
|
125
|
-
evaluate node.right, scope
|
126
|
-
Types::STRING
|
127
|
-
end
|
128
|
-
|
129
|
-
def evaluate_interpolated_string_node(node, scope)
|
130
|
-
node.parts.each { evaluate _1, scope }
|
131
|
-
Types::STRING
|
132
|
-
end
|
133
|
-
|
134
|
-
def evaluate_interpolated_x_string_node(node, scope)
|
135
|
-
node.parts.each { evaluate _1, scope }
|
136
|
-
Types::STRING
|
137
|
-
end
|
138
|
-
|
139
|
-
def evaluate_interpolated_symbol_node(node, scope)
|
140
|
-
node.parts.each { evaluate _1, scope }
|
141
|
-
Types::SYMBOL
|
142
|
-
end
|
143
|
-
|
144
|
-
def evaluate_interpolated_regular_expression_node(node, scope)
|
145
|
-
node.parts.each { evaluate _1, scope }
|
146
|
-
Types::REGEXP
|
147
|
-
end
|
148
|
-
|
149
|
-
def evaluate_embedded_statements_node(node, scope)
|
150
|
-
node.statements ? evaluate(node.statements, scope) : Types::NIL
|
151
|
-
Types::STRING
|
152
|
-
end
|
153
|
-
|
154
|
-
def evaluate_embedded_variable_node(node, scope)
|
155
|
-
evaluate node.variable, scope
|
156
|
-
Types::STRING
|
157
|
-
end
|
158
|
-
|
159
|
-
def evaluate_array_node(node, scope)
|
160
|
-
Types.array_of evaluate_list_splat_items(node.elements, scope)
|
161
|
-
end
|
162
|
-
|
163
|
-
def evaluate_hash_node(node, scope) = evaluate_hash(node, scope)
|
164
|
-
def evaluate_keyword_hash_node(node, scope) = evaluate_hash(node, scope)
|
165
|
-
def evaluate_hash(node, scope)
|
166
|
-
keys = []
|
167
|
-
values = []
|
168
|
-
node.elements.each do |assoc|
|
169
|
-
case assoc
|
170
|
-
when Prism::AssocNode
|
171
|
-
keys << evaluate(assoc.key, scope)
|
172
|
-
values << evaluate(assoc.value, scope)
|
173
|
-
when Prism::AssocSplatNode
|
174
|
-
next unless assoc.value # def f(**); {**}
|
175
|
-
|
176
|
-
hash = evaluate assoc.value, scope
|
177
|
-
unless hash.is_a?(Types::InstanceType) && hash.klass == Hash
|
178
|
-
hash = method_call hash, :to_hash, [], nil, nil, scope
|
179
|
-
end
|
180
|
-
if hash.is_a?(Types::InstanceType) && hash.klass == Hash
|
181
|
-
keys << hash.params[:K] if hash.params[:K]
|
182
|
-
values << hash.params[:V] if hash.params[:V]
|
183
|
-
end
|
184
|
-
end
|
185
|
-
end
|
186
|
-
if keys.empty? && values.empty?
|
187
|
-
Types::InstanceType.new Hash
|
188
|
-
else
|
189
|
-
Types::InstanceType.new Hash, K: Types::UnionType[*keys], V: Types::UnionType[*values]
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
def evaluate_parentheses_node(node, scope)
|
194
|
-
node.body ? evaluate(node.body, scope) : Types::NIL
|
195
|
-
end
|
196
|
-
|
197
|
-
def evaluate_constant_path_node(node, scope)
|
198
|
-
type, = evaluate_constant_node_info node, scope
|
199
|
-
type
|
200
|
-
end
|
201
|
-
|
202
|
-
def evaluate_self_node(_node, scope) = scope.self_type
|
203
|
-
|
204
|
-
def evaluate_true_node(_node, _scope) = Types::TRUE
|
205
|
-
|
206
|
-
def evaluate_false_node(_node, _scope) = Types::FALSE
|
207
|
-
|
208
|
-
def evaluate_nil_node(_node, _scope) = Types::NIL
|
209
|
-
|
210
|
-
def evaluate_source_file_node(_node, _scope) = Types::STRING
|
211
|
-
|
212
|
-
def evaluate_source_line_node(_node, _scope) = Types::INTEGER
|
213
|
-
|
214
|
-
def evaluate_source_encoding_node(_node, _scope) = Types::InstanceType.new(Encoding)
|
215
|
-
|
216
|
-
def evaluate_numbered_reference_read_node(_node, _scope)
|
217
|
-
Types::UnionType[Types::STRING, Types::NIL]
|
218
|
-
end
|
219
|
-
|
220
|
-
def evaluate_back_reference_read_node(_node, _scope)
|
221
|
-
Types::UnionType[Types::STRING, Types::NIL]
|
222
|
-
end
|
223
|
-
|
224
|
-
def evaluate_reference_read(node, scope)
|
225
|
-
scope[node.name.to_s] || Types::NIL
|
226
|
-
end
|
227
|
-
alias evaluate_constant_read_node evaluate_reference_read
|
228
|
-
alias evaluate_global_variable_read_node evaluate_reference_read
|
229
|
-
alias evaluate_local_variable_read_node evaluate_reference_read
|
230
|
-
alias evaluate_class_variable_read_node evaluate_reference_read
|
231
|
-
alias evaluate_instance_variable_read_node evaluate_reference_read
|
232
|
-
|
233
|
-
|
234
|
-
def evaluate_call_node(node, scope)
|
235
|
-
is_field_assign = node.name.match?(/[^<>=!\]]=\z/) || (node.name == :[]= && !node.call_operator)
|
236
|
-
receiver_type = node.receiver ? evaluate(node.receiver, scope) : scope.self_type
|
237
|
-
evaluate_method = lambda do |scope|
|
238
|
-
args_types, kwargs_types, block_sym_node, has_block = evaluate_call_node_arguments node, scope
|
239
|
-
|
240
|
-
if block_sym_node
|
241
|
-
block_sym = block_sym_node.value
|
242
|
-
if @dig_targets.target? block_sym_node
|
243
|
-
# method(args, &:completion_target)
|
244
|
-
call_block_proc = ->(block_args, _self_type) do
|
245
|
-
block_receiver = block_args.first || Types::OBJECT
|
246
|
-
@dig_targets.resolve block_receiver, scope
|
247
|
-
Types::OBJECT
|
248
|
-
end
|
249
|
-
else
|
250
|
-
call_block_proc = ->(block_args, _self_type) do
|
251
|
-
block_receiver, *rest = block_args
|
252
|
-
block_receiver ? method_call(block_receiver || Types::OBJECT, block_sym, rest, nil, nil, scope) : Types::OBJECT
|
253
|
-
end
|
254
|
-
end
|
255
|
-
elsif node.block.is_a? Prism::BlockNode
|
256
|
-
call_block_proc = ->(block_args, block_self_type) do
|
257
|
-
scope.conditional do |s|
|
258
|
-
numbered_parameters = node.block.locals.grep(/\A_[1-9]/).map(&:to_s)
|
259
|
-
params_table = node.block.locals.to_h { [_1.to_s, Types::NIL] }
|
260
|
-
table = { **params_table, Scope::BREAK_RESULT => nil, Scope::NEXT_RESULT => nil }
|
261
|
-
block_scope = Scope.new s, table, self_type: block_self_type, trace_ivar: !block_self_type
|
262
|
-
# TODO kwargs
|
263
|
-
if node.block.parameters&.parameters
|
264
|
-
# node.block.parameters is Prism::BlockParametersNode
|
265
|
-
assign_parameters node.block.parameters.parameters, block_scope, block_args, {}
|
266
|
-
elsif !numbered_parameters.empty?
|
267
|
-
assign_numbered_parameters numbered_parameters, block_scope, block_args, {}
|
268
|
-
end
|
269
|
-
result = node.block.body ? evaluate(node.block.body, block_scope) : Types::NIL
|
270
|
-
block_scope.merge_jumps
|
271
|
-
s.update block_scope
|
272
|
-
nexts = block_scope[Scope::NEXT_RESULT]
|
273
|
-
breaks = block_scope[Scope::BREAK_RESULT]
|
274
|
-
if block_scope.terminated?
|
275
|
-
[Types::UnionType[*nexts], breaks]
|
276
|
-
else
|
277
|
-
[Types::UnionType[result, *nexts], breaks]
|
278
|
-
end
|
279
|
-
end
|
280
|
-
end
|
281
|
-
elsif has_block
|
282
|
-
call_block_proc = ->(_block_args, _self_type) { Types::OBJECT }
|
283
|
-
end
|
284
|
-
result = method_call receiver_type, node.name, args_types, kwargs_types, call_block_proc, scope
|
285
|
-
if is_field_assign
|
286
|
-
args_types.last || Types::NIL
|
287
|
-
else
|
288
|
-
result
|
289
|
-
end
|
290
|
-
end
|
291
|
-
if node.call_operator == '&.'
|
292
|
-
result = scope.conditional { evaluate_method.call _1 }
|
293
|
-
if receiver_type.nillable?
|
294
|
-
Types::UnionType[result, Types::NIL]
|
295
|
-
else
|
296
|
-
result
|
297
|
-
end
|
298
|
-
else
|
299
|
-
evaluate_method.call scope
|
300
|
-
end
|
301
|
-
end
|
302
|
-
|
303
|
-
def evaluate_and_node(node, scope) = evaluate_and_or(node, scope, and_op: true)
|
304
|
-
def evaluate_or_node(node, scope) = evaluate_and_or(node, scope, and_op: false)
|
305
|
-
def evaluate_and_or(node, scope, and_op:)
|
306
|
-
left = evaluate node.left, scope
|
307
|
-
right = scope.conditional { evaluate node.right, _1 }
|
308
|
-
if and_op
|
309
|
-
Types::UnionType[right, Types::NIL, Types::FALSE]
|
310
|
-
else
|
311
|
-
Types::UnionType[left, right]
|
312
|
-
end
|
313
|
-
end
|
314
|
-
|
315
|
-
def evaluate_call_operator_write_node(node, scope) = evaluate_call_write(node, scope, :operator, node.write_name)
|
316
|
-
def evaluate_call_and_write_node(node, scope) = evaluate_call_write(node, scope, :and, node.write_name)
|
317
|
-
def evaluate_call_or_write_node(node, scope) = evaluate_call_write(node, scope, :or, node.write_name)
|
318
|
-
def evaluate_index_operator_write_node(node, scope) = evaluate_call_write(node, scope, :operator, :[]=)
|
319
|
-
def evaluate_index_and_write_node(node, scope) = evaluate_call_write(node, scope, :and, :[]=)
|
320
|
-
def evaluate_index_or_write_node(node, scope) = evaluate_call_write(node, scope, :or, :[]=)
|
321
|
-
def evaluate_call_write(node, scope, operator, write_name)
|
322
|
-
receiver_type = evaluate node.receiver, scope
|
323
|
-
if write_name == :[]=
|
324
|
-
args_types, kwargs_types, block_sym_node, has_block = evaluate_call_node_arguments node, scope
|
325
|
-
else
|
326
|
-
args_types = []
|
327
|
-
end
|
328
|
-
if block_sym_node
|
329
|
-
block_sym = block_sym_node.value
|
330
|
-
call_block_proc = ->(block_args, _self_type) do
|
331
|
-
block_receiver, *rest = block_args
|
332
|
-
block_receiver ? method_call(block_receiver || Types::OBJECT, block_sym, rest, nil, nil, scope) : Types::OBJECT
|
333
|
-
end
|
334
|
-
elsif has_block
|
335
|
-
call_block_proc = ->(_block_args, _self_type) { Types::OBJECT }
|
336
|
-
end
|
337
|
-
method = write_name.to_s.delete_suffix('=')
|
338
|
-
left = method_call receiver_type, method, args_types, kwargs_types, call_block_proc, scope
|
339
|
-
case operator
|
340
|
-
when :and
|
341
|
-
right = scope.conditional { evaluate node.value, _1 }
|
342
|
-
Types::UnionType[right, Types::NIL, Types::FALSE]
|
343
|
-
when :or
|
344
|
-
right = scope.conditional { evaluate node.value, _1 }
|
345
|
-
Types::UnionType[left, right]
|
346
|
-
else
|
347
|
-
right = evaluate node.value, scope
|
348
|
-
method_call left, node.operator, [right], nil, nil, scope, name_match: false
|
349
|
-
end
|
350
|
-
end
|
351
|
-
|
352
|
-
def evaluate_variable_operator_write(node, scope)
|
353
|
-
left = scope[node.name.to_s] || Types::OBJECT
|
354
|
-
right = evaluate node.value, scope
|
355
|
-
scope[node.name.to_s] = method_call left, node.operator, [right], nil, nil, scope, name_match: false
|
356
|
-
end
|
357
|
-
alias evaluate_global_variable_operator_write_node evaluate_variable_operator_write
|
358
|
-
alias evaluate_local_variable_operator_write_node evaluate_variable_operator_write
|
359
|
-
alias evaluate_class_variable_operator_write_node evaluate_variable_operator_write
|
360
|
-
alias evaluate_instance_variable_operator_write_node evaluate_variable_operator_write
|
361
|
-
|
362
|
-
def evaluate_variable_and_write(node, scope)
|
363
|
-
right = scope.conditional { evaluate node.value, scope }
|
364
|
-
scope[node.name.to_s] = Types::UnionType[right, Types::NIL, Types::FALSE]
|
365
|
-
end
|
366
|
-
alias evaluate_global_variable_and_write_node evaluate_variable_and_write
|
367
|
-
alias evaluate_local_variable_and_write_node evaluate_variable_and_write
|
368
|
-
alias evaluate_class_variable_and_write_node evaluate_variable_and_write
|
369
|
-
alias evaluate_instance_variable_and_write_node evaluate_variable_and_write
|
370
|
-
|
371
|
-
def evaluate_variable_or_write(node, scope)
|
372
|
-
left = scope[node.name.to_s] || Types::OBJECT
|
373
|
-
right = scope.conditional { evaluate node.value, scope }
|
374
|
-
scope[node.name.to_s] = Types::UnionType[left, right]
|
375
|
-
end
|
376
|
-
alias evaluate_global_variable_or_write_node evaluate_variable_or_write
|
377
|
-
alias evaluate_local_variable_or_write_node evaluate_variable_or_write
|
378
|
-
alias evaluate_class_variable_or_write_node evaluate_variable_or_write
|
379
|
-
alias evaluate_instance_variable_or_write_node evaluate_variable_or_write
|
380
|
-
|
381
|
-
def evaluate_constant_operator_write_node(node, scope)
|
382
|
-
left = scope[node.name.to_s] || Types::OBJECT
|
383
|
-
right = evaluate node.value, scope
|
384
|
-
scope[node.name.to_s] = method_call left, node.operator, [right], nil, nil, scope, name_match: false
|
385
|
-
end
|
386
|
-
|
387
|
-
def evaluate_constant_and_write_node(node, scope)
|
388
|
-
right = scope.conditional { evaluate node.value, scope }
|
389
|
-
scope[node.name.to_s] = Types::UnionType[right, Types::NIL, Types::FALSE]
|
390
|
-
end
|
391
|
-
|
392
|
-
def evaluate_constant_or_write_node(node, scope)
|
393
|
-
left = scope[node.name.to_s] || Types::OBJECT
|
394
|
-
right = scope.conditional { evaluate node.value, scope }
|
395
|
-
scope[node.name.to_s] = Types::UnionType[left, right]
|
396
|
-
end
|
397
|
-
|
398
|
-
def evaluate_constant_path_operator_write_node(node, scope)
|
399
|
-
left, receiver, _parent_module, name = evaluate_constant_node_info node.target, scope
|
400
|
-
right = evaluate node.value, scope
|
401
|
-
value = method_call left, node.operator, [right], nil, nil, scope, name_match: false
|
402
|
-
const_path_write receiver, name, value, scope
|
403
|
-
value
|
404
|
-
end
|
405
|
-
|
406
|
-
def evaluate_constant_path_and_write_node(node, scope)
|
407
|
-
_left, receiver, _parent_module, name = evaluate_constant_node_info node.target, scope
|
408
|
-
right = scope.conditional { evaluate node.value, scope }
|
409
|
-
value = Types::UnionType[right, Types::NIL, Types::FALSE]
|
410
|
-
const_path_write receiver, name, value, scope
|
411
|
-
value
|
412
|
-
end
|
413
|
-
|
414
|
-
def evaluate_constant_path_or_write_node(node, scope)
|
415
|
-
left, receiver, _parent_module, name = evaluate_constant_node_info node.target, scope
|
416
|
-
right = scope.conditional { evaluate node.value, scope }
|
417
|
-
value = Types::UnionType[left, right]
|
418
|
-
const_path_write receiver, name, value, scope
|
419
|
-
value
|
420
|
-
end
|
421
|
-
|
422
|
-
def evaluate_constant_path_write_node(node, scope)
|
423
|
-
receiver = evaluate node.target.parent, scope if node.target.parent
|
424
|
-
value = evaluate node.value, scope
|
425
|
-
const_path_write receiver, node.target.child.name.to_s, value, scope
|
426
|
-
value
|
427
|
-
end
|
428
|
-
|
429
|
-
def evaluate_lambda_node(node, scope)
|
430
|
-
local_table = node.locals.to_h { [_1.to_s, Types::OBJECT] }
|
431
|
-
block_scope = Scope.new scope, { **local_table, Scope::BREAK_RESULT => nil, Scope::NEXT_RESULT => nil, Scope::RETURN_RESULT => nil }
|
432
|
-
block_scope.conditional do |s|
|
433
|
-
assign_parameters node.parameters.parameters, s, [], {} if node.parameters&.parameters
|
434
|
-
evaluate node.body, s if node.body
|
435
|
-
end
|
436
|
-
block_scope.merge_jumps
|
437
|
-
scope.update block_scope
|
438
|
-
Types::PROC
|
439
|
-
end
|
440
|
-
|
441
|
-
def evaluate_reference_write(node, scope)
|
442
|
-
scope[node.name.to_s] = evaluate node.value, scope
|
443
|
-
end
|
444
|
-
alias evaluate_constant_write_node evaluate_reference_write
|
445
|
-
alias evaluate_global_variable_write_node evaluate_reference_write
|
446
|
-
alias evaluate_local_variable_write_node evaluate_reference_write
|
447
|
-
alias evaluate_class_variable_write_node evaluate_reference_write
|
448
|
-
alias evaluate_instance_variable_write_node evaluate_reference_write
|
449
|
-
|
450
|
-
def evaluate_multi_write_node(node, scope)
|
451
|
-
evaluated_receivers = {}
|
452
|
-
evaluate_multi_write_receiver node, scope, evaluated_receivers
|
453
|
-
value = (
|
454
|
-
if node.value.is_a? Prism::ArrayNode
|
455
|
-
if node.value.elements.any?(Prism::SplatNode)
|
456
|
-
evaluate node.value, scope
|
457
|
-
else
|
458
|
-
node.value.elements.map do |n|
|
459
|
-
evaluate n, scope
|
460
|
-
end
|
461
|
-
end
|
462
|
-
elsif node.value
|
463
|
-
evaluate node.value, scope
|
464
|
-
else
|
465
|
-
Types::NIL
|
466
|
-
end
|
467
|
-
)
|
468
|
-
evaluate_multi_write node, value, scope, evaluated_receivers
|
469
|
-
value.is_a?(Array) ? 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
|
-
Types::UnionType[*scope.run_branches(
|
477
|
-
-> { node.statements ? evaluate(node.statements, _1) : Types::NIL },
|
478
|
-
-> { node.consequent ? evaluate(node.consequent, _1) : Types::NIL }
|
479
|
-
)]
|
480
|
-
end
|
481
|
-
|
482
|
-
def evaluate_else_node(node, scope)
|
483
|
-
node.statements ? evaluate(node.statements, scope) : Types::NIL
|
484
|
-
end
|
485
|
-
|
486
|
-
def evaluate_while_until(node, scope)
|
487
|
-
inner_scope = Scope.new scope, { 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[Scope::BREAK_RESULT]
|
497
|
-
breaks ? Types::UnionType[breaks, Types::NIL] : 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
|
-
Scope::BREAK_RESULT
|
510
|
-
when :next
|
511
|
-
Scope::NEXT_RESULT
|
512
|
-
when :return
|
513
|
-
Scope::RETURN_RESULT
|
514
|
-
end
|
515
|
-
)
|
516
|
-
jump_value = (
|
517
|
-
arguments = node.arguments&.arguments
|
518
|
-
if arguments.nil? || arguments.empty?
|
519
|
-
Types::NIL
|
520
|
-
elsif arguments.size == 1 && !arguments.first.is_a?(Prism::SplatNode)
|
521
|
-
evaluate arguments.first, scope
|
522
|
-
else
|
523
|
-
Types.array_of evaluate_list_splat_items(arguments, scope)
|
524
|
-
end
|
525
|
-
)
|
526
|
-
scope.terminate_with internal_key, jump_value
|
527
|
-
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
|
-
Types::OBJECT
|
533
|
-
end
|
534
|
-
|
535
|
-
def evaluate_redo_node(_node, scope)
|
536
|
-
scope.terminate
|
537
|
-
Types::NIL
|
538
|
-
end
|
539
|
-
|
540
|
-
def evaluate_retry_node(_node, scope)
|
541
|
-
scope.terminate
|
542
|
-
Types::NIL
|
543
|
-
end
|
544
|
-
|
545
|
-
def evaluate_forwarding_super_node(_node, _scope) = Types::OBJECT
|
546
|
-
|
547
|
-
def evaluate_super_node(node, scope)
|
548
|
-
evaluate_list_splat_items node.arguments.arguments, scope if node.arguments
|
549
|
-
Types::OBJECT
|
550
|
-
end
|
551
|
-
|
552
|
-
def evaluate_begin_node(node, scope)
|
553
|
-
return_type = node.statements ? evaluate(node.statements, scope) : 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 = 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
|
-
Types::InstanceType.new _1.module_or_class if _1.is_a?(Types::SingletonType)
|
581
|
-
end
|
582
|
-
error_types << Types::InstanceType.new(StandardError) if error_types.empty?
|
583
|
-
error_type = 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) : 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
|
-
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
|
-
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
|
-
Types::SingletonType.new type.klass if type.is_a? Types::InstanceType
|
613
|
-
end
|
614
|
-
klass_types = [Types::CLASS] if klass_types.empty?
|
615
|
-
table = node.locals.to_h { [_1.to_s, Types::NIL] }
|
616
|
-
sclass_scope = Scope.new(
|
617
|
-
scope,
|
618
|
-
{ **table, Scope::BREAK_RESULT => nil, Scope::NEXT_RESULT => nil, Scope::RETURN_RESULT => nil },
|
619
|
-
trace_ivar: false,
|
620
|
-
trace_lvar: false,
|
621
|
-
self_type: Types::UnionType[*klass_types]
|
622
|
-
)
|
623
|
-
result = node.body ? evaluate(node.body, sclass_scope) : 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 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?(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 << Types::CLASS if module_types.empty?
|
642
|
-
else
|
643
|
-
module_types = const_type.types.select { _1.is_a?(Types::SingletonType) && !_1.module_or_class.is_a?(Class) }
|
644
|
-
module_types << Types::MODULE if module_types.empty?
|
645
|
-
end
|
646
|
-
return Types::NIL unless node.body
|
647
|
-
|
648
|
-
table = node.locals.to_h { [_1.to_s, 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? 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 ? Types::CLASS : Types::MODULE
|
667
|
-
end
|
668
|
-
else
|
669
|
-
# parent_module == :unknown
|
670
|
-
# TODO: dummy module
|
671
|
-
end
|
672
|
-
module_scope = Scope.new(
|
673
|
-
scope,
|
674
|
-
{ **table, Scope::BREAK_RESULT => nil, Scope::NEXT_RESULT => nil, Scope::RETURN_RESULT => nil },
|
675
|
-
trace_ivar: false,
|
676
|
-
trace_lvar: false,
|
677
|
-
self_type: 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 = Scope.new scope, { 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?(Types::InstanceType) && ary.klass == Array
|
693
|
-
end
|
694
|
-
element_type = 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[Scope::BREAK_RESULT]
|
702
|
-
breaks ? 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) { Types::NIL }
|
715
|
-
end
|
716
|
-
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
|
-
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
|
-
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 = (Types::UnionType[*[beg_type, end_type].compact]).nonnillable
|
735
|
-
Types::InstanceType.new Range, Elem: elem
|
736
|
-
end
|
737
|
-
|
738
|
-
def evaluate_defined_node(node, scope)
|
739
|
-
scope.conditional { evaluate node.value, _1 }
|
740
|
-
Types::UnionType[Types::STRING, 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
|
-
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
|
-
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
|
-
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] = Types::UnionType[Types::STRING, Types::NIL] }
|
769
|
-
Types::BOOLEAN
|
770
|
-
end
|
771
|
-
|
772
|
-
def evaluate_match_last_line_node(_node, _scope)
|
773
|
-
Types::BOOLEAN
|
774
|
-
end
|
775
|
-
|
776
|
-
def evaluate_interpolated_match_last_line_node(node, scope)
|
777
|
-
node.parts.each { evaluate _1, scope }
|
778
|
-
Types::BOOLEAN
|
779
|
-
end
|
780
|
-
|
781
|
-
def evaluate_pre_execution_node(node, scope)
|
782
|
-
node.statements ? evaluate(node.statements, scope) : 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) : Types::NIL
|
787
|
-
end
|
788
|
-
|
789
|
-
def evaluate_alias_method_node(_node, _scope) = Types::NIL
|
790
|
-
def evaluate_alias_global_variable_node(_node, _scope) = Types::NIL
|
791
|
-
def evaluate_undef_node(_node, _scope) = Types::NIL
|
792
|
-
def evaluate_missing_node(_node, _scope) = 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? 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
|
847
|
-
when Prism::RequiredParameterNode
|
848
|
-
scope[node.name.to_s] = value || Types::OBJECT
|
849
|
-
when Prism::MultiTargetNode
|
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 Prism::SplatNode
|
856
|
-
splat_value = value ? Types.array_of(value) : Types::ARRAY
|
857
|
-
assign_required_parameter node.expression, splat_value, scope if node.expression
|
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? 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]) || Types::NIL
|
875
|
-
else
|
876
|
-
parent_module = :unknown
|
877
|
-
type = 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] = 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] = Types.array_of(*rest)
|
919
|
-
end
|
920
|
-
node.keywords.each do |n|
|
921
|
-
name = n.name.to_s.delete(':')
|
922
|
-
values = [kwargs.delete(name)]
|
923
|
-
# n is Prism::OptionalKeywordParameterNode (has n.value) or Prism::RequiredKeywordParameterNode (does not have n.value)
|
924
|
-
values << evaluate(n.value, scope) if n.respond_to?(:value)
|
925
|
-
scope[name] = 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] = Types::InstanceType.new(Hash, K: Types::SYMBOL, V: Types::UnionType[*kwargs.values])
|
930
|
-
end
|
931
|
-
if node.block&.name
|
932
|
-
# node.block is Prism::BlockParameterNode
|
933
|
-
scope[node.block.name.to_s] = 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 || 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] || 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) : 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) : Types::NIL
|
965
|
-
end
|
966
|
-
end
|
967
|
-
|
968
|
-
def evaluate_match_pattern(value, pattern, scope)
|
969
|
-
# TODO: scope.terminate_with Scope::PATTERNMATCH_BREAK, Types::NIL
|
970
|
-
case pattern
|
971
|
-
when Prism::FindPatternNode
|
972
|
-
# TODO
|
973
|
-
evaluate_match_pattern Types::OBJECT, pattern.left, scope
|
974
|
-
pattern.requireds.each { evaluate_match_pattern Types::OBJECT, _1, scope }
|
975
|
-
evaluate_match_pattern Types::OBJECT, pattern.right, scope
|
976
|
-
when Prism::ArrayPatternNode
|
977
|
-
# TODO
|
978
|
-
pattern.requireds.each { evaluate_match_pattern Types::OBJECT, _1, scope }
|
979
|
-
evaluate_match_pattern Types::OBJECT, pattern.rest, scope if pattern.rest
|
980
|
-
pattern.posts.each { evaluate_match_pattern Types::OBJECT, _1, scope }
|
981
|
-
Types::ARRAY
|
982
|
-
when Prism::HashPatternNode
|
983
|
-
# TODO
|
984
|
-
pattern.elements.each { evaluate_match_pattern Types::OBJECT, _1, scope }
|
985
|
-
if pattern.respond_to?(:rest) && pattern.rest
|
986
|
-
evaluate_match_pattern Types::OBJECT, pattern.rest, scope
|
987
|
-
end
|
988
|
-
Types::HASH
|
989
|
-
when Prism::AssocNode
|
990
|
-
evaluate_match_pattern value, pattern.value, scope if pattern.value
|
991
|
-
Types::OBJECT
|
992
|
-
when Prism::AssocSplatNode
|
993
|
-
# TODO
|
994
|
-
evaluate_match_pattern Types::HASH, pattern.value, scope
|
995
|
-
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
|
-
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 == [Types::OBJECT]
|
1007
|
-
evaluate_match_pattern value, pattern.target, scope
|
1008
|
-
when Prism::SplatNode
|
1009
|
-
value = 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?(Types::SingletonType) ? Types::InstanceType.new(t.module_or_class) : t
|
1022
|
-
end
|
1023
|
-
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, Types.array_of(value), scope, evaluated_receivers if node.expression
|
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, Types::UnionType[*values]]] : []
|
1052
|
-
(pre_pairs + splat_pairs + post_pairs).each do |target, value|
|
1053
|
-
evaluate_write target, value || 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
|
-
next unless node.expression # def f(*); [*]
|
1085
|
-
|
1086
|
-
splat = evaluate node.expression, scope
|
1087
|
-
array_elem, non_array = partition_to_array splat.nonnillable, :to_a
|
1088
|
-
[*array_elem, *non_array]
|
1089
|
-
else
|
1090
|
-
evaluate node, scope
|
1091
|
-
end
|
1092
|
-
end.compact.uniq
|
1093
|
-
Types::UnionType[*items]
|
1094
|
-
end
|
1095
|
-
|
1096
|
-
def sized_splat(value, method, size)
|
1097
|
-
array_elem, non_array = partition_to_array value, method
|
1098
|
-
values = [Types::UnionType[*array_elem, *non_array]]
|
1099
|
-
values += [array_elem] * (size - 1) if array_elem && size >= 1
|
1100
|
-
values
|
1101
|
-
end
|
1102
|
-
|
1103
|
-
def partition_to_array(value, method)
|
1104
|
-
arrays, non_arrays = value.types.partition { _1.is_a?(Types::InstanceType) && _1.klass == Array }
|
1105
|
-
non_arrays.select! do |type|
|
1106
|
-
to_array_result = method_call type, method, [], nil, nil, nil, name_match: false
|
1107
|
-
if to_array_result.is_a?(Types::InstanceType) && to_array_result.klass == Array
|
1108
|
-
arrays << to_array_result
|
1109
|
-
false
|
1110
|
-
else
|
1111
|
-
true
|
1112
|
-
end
|
1113
|
-
end
|
1114
|
-
array_elem = arrays.empty? ? nil : Types::UnionType[*arrays.map { _1.params[:Elem] || Types::OBJECT }]
|
1115
|
-
non_array = non_arrays.empty? ? nil : Types::UnionType[*non_arrays]
|
1116
|
-
[array_elem, non_array]
|
1117
|
-
end
|
1118
|
-
|
1119
|
-
def method_call(receiver, method_name, args, kwargs, block, scope, name_match: true)
|
1120
|
-
methods = Types.rbs_methods receiver, method_name.to_sym, args, kwargs, !!block
|
1121
|
-
block_called = false
|
1122
|
-
type_breaks = methods.map do |method, given_params, method_params|
|
1123
|
-
receiver_vars = receiver.is_a?(Types::InstanceType) ? receiver.params : {}
|
1124
|
-
free_vars = method.type.free_variables - receiver_vars.keys.to_set
|
1125
|
-
vars = receiver_vars.merge Types.match_free_variables(free_vars, method_params, given_params)
|
1126
|
-
if block && method.block
|
1127
|
-
params_type = method.block.type.required_positionals.map do |func_param|
|
1128
|
-
Types.from_rbs_type func_param.type, receiver, vars
|
1129
|
-
end
|
1130
|
-
self_type = Types.from_rbs_type method.block.self_type, receiver, vars if method.block.self_type
|
1131
|
-
block_response, breaks = block.call params_type, self_type
|
1132
|
-
block_called = true
|
1133
|
-
vars.merge! Types.match_free_variables(free_vars - vars.keys.to_set, [method.block.type.return_type], [block_response])
|
1134
|
-
end
|
1135
|
-
if Types.method_return_bottom?(method)
|
1136
|
-
[nil, breaks]
|
1137
|
-
else
|
1138
|
-
[Types.from_rbs_type(method.type.return_type, receiver, vars || {}), breaks]
|
1139
|
-
end
|
1140
|
-
end
|
1141
|
-
block&.call [], nil unless block_called
|
1142
|
-
terminates = !type_breaks.empty? && type_breaks.map(&:first).all?(&:nil?)
|
1143
|
-
types = type_breaks.map(&:first).compact
|
1144
|
-
breaks = type_breaks.map(&:last).compact
|
1145
|
-
types << OBJECT_METHODS[method_name.to_sym] if name_match && OBJECT_METHODS.has_key?(method_name.to_sym)
|
1146
|
-
|
1147
|
-
if method_name.to_sym == :new
|
1148
|
-
receiver.types.each do |type|
|
1149
|
-
if type.is_a?(Types::SingletonType) && type.module_or_class.is_a?(Class)
|
1150
|
-
types << Types::InstanceType.new(type.module_or_class)
|
1151
|
-
end
|
1152
|
-
end
|
1153
|
-
end
|
1154
|
-
scope&.terminate if terminates && breaks.empty?
|
1155
|
-
Types::UnionType[*types, *breaks]
|
1156
|
-
end
|
1157
|
-
|
1158
|
-
def self.calculate_target_type_scope(binding, parents, target)
|
1159
|
-
dig_targets = DigTarget.new(parents, target) do |type, scope|
|
1160
|
-
return type, scope
|
1161
|
-
end
|
1162
|
-
program = parents.first
|
1163
|
-
scope = Scope.from_binding(binding, program.locals)
|
1164
|
-
new(dig_targets).evaluate program, scope
|
1165
|
-
[Types::NIL, scope]
|
1166
|
-
end
|
1167
|
-
end
|
1168
|
-
end
|
1169
|
-
end
|