method-ray 0.1.3 → 0.1.4
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/CHANGELOG.md +16 -0
- data/ext/Cargo.toml +1 -1
- data/lib/methodray/version.rb +1 -1
- data/rust/Cargo.toml +1 -1
- data/rust/src/analyzer/attributes.rs +57 -0
- data/rust/src/analyzer/blocks.rs +104 -23
- data/rust/src/analyzer/calls.rs +7 -4
- data/rust/src/analyzer/conditionals.rs +466 -0
- data/rust/src/analyzer/definitions.rs +126 -8
- data/rust/src/analyzer/dispatch.rs +746 -11
- data/rust/src/analyzer/install.rs +52 -870
- data/rust/src/analyzer/literals.rs +179 -30
- data/rust/src/analyzer/mod.rs +2 -0
- data/rust/src/analyzer/parameters.rs +64 -0
- data/rust/src/analyzer/variables.rs +12 -3
- data/rust/src/env/global_env.rs +12 -0
- data/rust/src/env/method_registry.rs +55 -0
- data/rust/src/graph/box.rs +145 -5
- metadata +3 -1
|
@@ -5,26 +5,16 @@
|
|
|
5
5
|
//! - Coordinating the graph construction process
|
|
6
6
|
|
|
7
7
|
use crate::env::{GlobalEnv, LocalEnv};
|
|
8
|
-
use crate::graph::{
|
|
9
|
-
use crate::types::Type;
|
|
8
|
+
use crate::graph::{ChangeSet, VertexId};
|
|
10
9
|
use ruby_prism::Node;
|
|
11
10
|
|
|
12
|
-
use super::blocks::
|
|
13
|
-
use super::
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
use super::dispatch::{
|
|
18
|
-
dispatch_needs_child, dispatch_simple, finish_ivar_write, finish_local_var_write,
|
|
19
|
-
finish_method_call, DispatchResult, NeedsChildKind,
|
|
20
|
-
};
|
|
21
|
-
use super::literals::install_literal;
|
|
22
|
-
use super::parameters::{
|
|
23
|
-
install_keyword_rest_parameter, install_optional_parameter, install_required_parameter,
|
|
24
|
-
install_rest_parameter,
|
|
25
|
-
};
|
|
11
|
+
use super::blocks::process_block_node;
|
|
12
|
+
use super::conditionals::{process_case_node, process_if_node, process_unless_node};
|
|
13
|
+
use super::definitions::{process_class_node, process_def_node, process_module_node};
|
|
14
|
+
use super::dispatch::{dispatch_needs_child, dispatch_simple, process_needs_child, DispatchResult};
|
|
15
|
+
use super::literals::install_literal_node;
|
|
26
16
|
|
|
27
|
-
/// Build graph from AST
|
|
17
|
+
/// Build graph from AST (public API wrapper)
|
|
28
18
|
pub struct AstInstaller<'a> {
|
|
29
19
|
genv: &'a mut GlobalEnv,
|
|
30
20
|
lenv: &'a mut LocalEnv,
|
|
@@ -42,885 +32,77 @@ impl<'a> AstInstaller<'a> {
|
|
|
42
32
|
}
|
|
43
33
|
}
|
|
44
34
|
|
|
45
|
-
/// Install node (returns Vertex ID)
|
|
46
35
|
pub fn install_node(&mut self, node: &Node) -> Option<VertexId> {
|
|
47
|
-
|
|
48
|
-
if let Some(class_node) = node.as_class_node() {
|
|
49
|
-
return self.install_class_node(&class_node);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Module definition
|
|
53
|
-
if let Some(module_node) = node.as_module_node() {
|
|
54
|
-
return self.install_module_node(&module_node);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Method definition
|
|
58
|
-
if let Some(def_node) = node.as_def_node() {
|
|
59
|
-
return self.install_def_node(&def_node);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Block node (standalone block, e.g., lambda { |x| x })
|
|
63
|
-
if let Some(block_node) = node.as_block_node() {
|
|
64
|
-
return self.install_block_node(&block_node);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Try simple dispatch first (no child processing needed)
|
|
68
|
-
match dispatch_simple(self.genv, self.lenv, node) {
|
|
69
|
-
DispatchResult::Vertex(vtx) => return Some(vtx),
|
|
70
|
-
DispatchResult::NotHandled => {}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Literals (String, Integer, Array, Hash, nil, true, false, Symbol)
|
|
74
|
-
if let Some(vtx) = self.install_literal_node(node) {
|
|
75
|
-
return Some(vtx);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Check if node needs child processing
|
|
79
|
-
if let Some(kind) = dispatch_needs_child(node, self.source) {
|
|
80
|
-
return self.process_needs_child(kind);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
None
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/// Install literal node
|
|
87
|
-
///
|
|
88
|
-
/// Handles all literals including Array and Hash with element type inference
|
|
89
|
-
fn install_literal_node(&mut self, node: &Node) -> Option<VertexId> {
|
|
90
|
-
// Array literals need special handling for element type inference
|
|
91
|
-
if node.as_array_node().is_some() {
|
|
92
|
-
let elements: Vec<Node> = node.as_array_node().unwrap().elements().iter().collect();
|
|
93
|
-
return self.install_array_literal_elements(elements);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Hash literals need special handling for key/value type inference
|
|
97
|
-
if node.as_hash_node().is_some() {
|
|
98
|
-
let elements: Vec<Node> = node.as_hash_node().unwrap().elements().iter().collect();
|
|
99
|
-
return self.install_hash_literal_elements(elements);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Range literals need special handling for element type inference
|
|
103
|
-
if let Some(range_node) = node.as_range_node() {
|
|
104
|
-
return self.install_range_literal(&range_node);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Other literals (String, Integer, nil, true, false, Symbol)
|
|
108
|
-
install_literal(self.genv, node)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/// Install array literal with pre-collected elements
|
|
112
|
-
fn install_array_literal_elements(&mut self, elements: Vec<Node>) -> Option<VertexId> {
|
|
113
|
-
use crate::types::Type;
|
|
114
|
-
use std::collections::HashSet;
|
|
115
|
-
|
|
116
|
-
if elements.is_empty() {
|
|
117
|
-
return Some(self.genv.new_source(Type::array()));
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
let mut element_types: HashSet<Type> = HashSet::new();
|
|
121
|
-
|
|
122
|
-
for element in &elements {
|
|
123
|
-
if let Some(vtx) = self.install_node(element) {
|
|
124
|
-
if let Some(source) = self.genv.get_source(vtx) {
|
|
125
|
-
element_types.insert(source.ty.clone());
|
|
126
|
-
} else if let Some(vertex) = self.genv.get_vertex(vtx) {
|
|
127
|
-
for ty in vertex.types.keys() {
|
|
128
|
-
element_types.insert(ty.clone());
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
let array_type = if element_types.is_empty() {
|
|
135
|
-
Type::array()
|
|
136
|
-
} else if element_types.len() == 1 {
|
|
137
|
-
let elem_type = element_types.into_iter().next().unwrap();
|
|
138
|
-
Type::array_of(elem_type)
|
|
139
|
-
} else {
|
|
140
|
-
let types_vec: Vec<Type> = element_types.into_iter().collect();
|
|
141
|
-
let union_type = Type::Union(types_vec);
|
|
142
|
-
Type::array_of(union_type)
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
Some(self.genv.new_source(array_type))
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/// Install hash literal with element type inference
|
|
149
|
-
fn install_hash_literal_elements(&mut self, elements: Vec<Node>) -> Option<VertexId> {
|
|
150
|
-
use crate::types::Type;
|
|
151
|
-
use std::collections::HashSet;
|
|
152
|
-
|
|
153
|
-
if elements.is_empty() {
|
|
154
|
-
return Some(self.genv.new_source(Type::hash()));
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
let mut key_types: HashSet<Type> = HashSet::new();
|
|
158
|
-
let mut value_types: HashSet<Type> = HashSet::new();
|
|
159
|
-
|
|
160
|
-
for element in &elements {
|
|
161
|
-
if let Some(assoc_node) = element.as_assoc_node() {
|
|
162
|
-
// Infer key type
|
|
163
|
-
if let Some(key_vtx) = self.install_node(&assoc_node.key()) {
|
|
164
|
-
if let Some(source) = self.genv.get_source(key_vtx) {
|
|
165
|
-
key_types.insert(source.ty.clone());
|
|
166
|
-
} else if let Some(vertex) = self.genv.get_vertex(key_vtx) {
|
|
167
|
-
for ty in vertex.types.keys() {
|
|
168
|
-
key_types.insert(ty.clone());
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Infer value type
|
|
174
|
-
if let Some(value_vtx) = self.install_node(&assoc_node.value()) {
|
|
175
|
-
if let Some(source) = self.genv.get_source(value_vtx) {
|
|
176
|
-
value_types.insert(source.ty.clone());
|
|
177
|
-
} else if let Some(vertex) = self.genv.get_vertex(value_vtx) {
|
|
178
|
-
for ty in vertex.types.keys() {
|
|
179
|
-
value_types.insert(ty.clone());
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
let hash_type = if key_types.is_empty() || value_types.is_empty() {
|
|
187
|
-
Type::hash()
|
|
188
|
-
} else {
|
|
189
|
-
// Build key type (single type or union)
|
|
190
|
-
let key_type = if key_types.len() == 1 {
|
|
191
|
-
key_types.into_iter().next().unwrap()
|
|
192
|
-
} else {
|
|
193
|
-
let types_vec: Vec<Type> = key_types.into_iter().collect();
|
|
194
|
-
Type::Union(types_vec)
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
// Build value type (single type or union)
|
|
198
|
-
let value_type = if value_types.len() == 1 {
|
|
199
|
-
value_types.into_iter().next().unwrap()
|
|
200
|
-
} else {
|
|
201
|
-
let types_vec: Vec<Type> = value_types.into_iter().collect();
|
|
202
|
-
Type::Union(types_vec)
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
Type::hash_of(key_type, value_type)
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
Some(self.genv.new_source(hash_type))
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/// Install range literal with element type inference
|
|
212
|
-
fn install_range_literal(&mut self, range_node: &ruby_prism::RangeNode) -> Option<VertexId> {
|
|
213
|
-
// Try to infer element type from left or right endpoint
|
|
214
|
-
let element_type = if let Some(left) = range_node.left() {
|
|
215
|
-
self.infer_range_element_type(&left)
|
|
216
|
-
} else if let Some(right) = range_node.right() {
|
|
217
|
-
self.infer_range_element_type(&right)
|
|
218
|
-
} else {
|
|
219
|
-
None
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
let range_type = match element_type {
|
|
223
|
-
Some(ty) => Type::range_of(ty),
|
|
224
|
-
None => Type::range(),
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
Some(self.genv.new_source(range_type))
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/// Infer element type from a range endpoint node
|
|
231
|
-
fn infer_range_element_type(&mut self, node: &Node) -> Option<Type> {
|
|
232
|
-
// Install the node and get its type
|
|
233
|
-
if let Some(vtx) = self.install_node(node) {
|
|
234
|
-
if let Some(source) = self.genv.get_source(vtx) {
|
|
235
|
-
return Some(source.ty.clone());
|
|
236
|
-
}
|
|
237
|
-
if let Some(vertex) = self.genv.get_vertex(vtx) {
|
|
238
|
-
// Get first type from vertex (simplified)
|
|
239
|
-
if let Some(ty) = vertex.types.keys().next() {
|
|
240
|
-
return Some(ty.clone());
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
None
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/// Process nodes that need child evaluation first
|
|
248
|
-
fn process_needs_child(&mut self, kind: NeedsChildKind) -> Option<VertexId> {
|
|
249
|
-
match kind {
|
|
250
|
-
NeedsChildKind::IvarWrite { ivar_name, value } => {
|
|
251
|
-
let value_vtx = self.install_node(&value)?;
|
|
252
|
-
Some(finish_ivar_write(self.genv, ivar_name, value_vtx))
|
|
253
|
-
}
|
|
254
|
-
NeedsChildKind::LocalVarWrite { var_name, value } => {
|
|
255
|
-
let value_vtx = self.install_node(&value)?;
|
|
256
|
-
Some(finish_local_var_write(
|
|
257
|
-
self.genv,
|
|
258
|
-
self.lenv,
|
|
259
|
-
&mut self.changes,
|
|
260
|
-
var_name,
|
|
261
|
-
value_vtx,
|
|
262
|
-
))
|
|
263
|
-
}
|
|
264
|
-
NeedsChildKind::MethodCall {
|
|
265
|
-
receiver,
|
|
266
|
-
method_name,
|
|
267
|
-
location,
|
|
268
|
-
block,
|
|
269
|
-
} => {
|
|
270
|
-
let recv_vtx = self.install_node(&receiver)?;
|
|
271
|
-
|
|
272
|
-
// Process block if present (e.g., `x.each { |i| ... }`)
|
|
273
|
-
// Collect block parameter vertex IDs for type inference
|
|
274
|
-
let mut block_param_vtxs: Vec<VertexId> = Vec::new();
|
|
275
|
-
if let Some(block_node) = block {
|
|
276
|
-
// Block may be a BlockNode or BlockArgumentNode
|
|
277
|
-
if let Some(bn) = block_node.as_block_node() {
|
|
278
|
-
block_param_vtxs = self.install_block_node_with_params(&bn);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// Create BlockParameterTypeBox if block has parameters
|
|
283
|
-
if !block_param_vtxs.is_empty() {
|
|
284
|
-
let box_id = self.genv.alloc_box_id();
|
|
285
|
-
let block_box = BlockParameterTypeBox::new(
|
|
286
|
-
box_id,
|
|
287
|
-
recv_vtx,
|
|
288
|
-
method_name.clone(),
|
|
289
|
-
block_param_vtxs,
|
|
290
|
-
);
|
|
291
|
-
self.genv.register_box(box_id, Box::new(block_box));
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
Some(finish_method_call(
|
|
295
|
-
self.genv,
|
|
296
|
-
recv_vtx,
|
|
297
|
-
method_name,
|
|
298
|
-
location,
|
|
299
|
-
))
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/// Install class definition
|
|
305
|
-
fn install_class_node(&mut self, class_node: &ruby_prism::ClassNode) -> Option<VertexId> {
|
|
306
|
-
let class_name = extract_class_name(class_node);
|
|
307
|
-
install_class(self.genv, class_name);
|
|
308
|
-
|
|
309
|
-
if let Some(body) = class_node.body() {
|
|
310
|
-
if let Some(statements) = body.as_statements_node() {
|
|
311
|
-
self.install_statements(&statements);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
exit_scope(self.genv);
|
|
316
|
-
None
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
/// Install module definition
|
|
320
|
-
fn install_module_node(&mut self, module_node: &ruby_prism::ModuleNode) -> Option<VertexId> {
|
|
321
|
-
let module_name = extract_module_name(module_node);
|
|
322
|
-
install_module(self.genv, module_name);
|
|
323
|
-
|
|
324
|
-
if let Some(body) = module_node.body() {
|
|
325
|
-
if let Some(statements) = body.as_statements_node() {
|
|
326
|
-
self.install_statements(&statements);
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
exit_scope(self.genv);
|
|
331
|
-
None
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
/// Install method definition
|
|
335
|
-
fn install_def_node(&mut self, def_node: &ruby_prism::DefNode) -> Option<VertexId> {
|
|
336
|
-
let method_name = String::from_utf8_lossy(def_node.name().as_slice()).to_string();
|
|
337
|
-
install_method(self.genv, method_name);
|
|
338
|
-
|
|
339
|
-
// Process parameters BEFORE processing body
|
|
340
|
-
// This ensures parameters are available as local variables in the method body
|
|
341
|
-
if let Some(params_node) = def_node.parameters() {
|
|
342
|
-
self.install_parameters(¶ms_node);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
if let Some(body) = def_node.body() {
|
|
346
|
-
if let Some(statements) = body.as_statements_node() {
|
|
347
|
-
self.install_statements(&statements);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
exit_scope(self.genv);
|
|
352
|
-
None
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
/// Install block node
|
|
356
|
-
///
|
|
357
|
-
/// Processes blocks like `{ |x| x.to_s }` or `do |item| item.upcase end`
|
|
358
|
-
fn install_block_node(&mut self, block_node: &ruby_prism::BlockNode) -> Option<VertexId> {
|
|
359
|
-
// Use the version that collects param vtxs, but discard them
|
|
360
|
-
self.install_block_node_with_params(block_node);
|
|
361
|
-
None
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
/// Install block node and return block parameter vertex IDs
|
|
365
|
-
///
|
|
366
|
-
/// This is used when processing method calls with blocks to collect
|
|
367
|
-
/// the block parameter vertices for type inference via BlockParameterTypeBox.
|
|
368
|
-
fn install_block_node_with_params(
|
|
369
|
-
&mut self,
|
|
370
|
-
block_node: &ruby_prism::BlockNode,
|
|
371
|
-
) -> Vec<VertexId> {
|
|
372
|
-
// Enter block scope
|
|
373
|
-
enter_block_scope(self.genv);
|
|
374
|
-
|
|
375
|
-
let mut param_vtxs = Vec::new();
|
|
376
|
-
|
|
377
|
-
// Process block parameters BEFORE processing body
|
|
378
|
-
// block_node.parameters() returns Option<Node>, need to convert to BlockParametersNode
|
|
379
|
-
if let Some(params_node) = block_node.parameters() {
|
|
380
|
-
if let Some(block_params) = params_node.as_block_parameters_node() {
|
|
381
|
-
param_vtxs = self.install_block_parameters_with_vtxs(&block_params);
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
// Process block body
|
|
386
|
-
if let Some(body) = block_node.body() {
|
|
387
|
-
if let Some(statements) = body.as_statements_node() {
|
|
388
|
-
self.install_statements(&statements);
|
|
389
|
-
} else {
|
|
390
|
-
// Single expression body
|
|
391
|
-
self.install_node(&body);
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
// Exit block scope
|
|
396
|
-
exit_block_scope(self.genv);
|
|
397
|
-
|
|
398
|
-
param_vtxs
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
/// Install block parameters as local variables
|
|
402
|
-
///
|
|
403
|
-
/// Block parameters like `|x, y|` are registered as local variables
|
|
404
|
-
/// with Bot (untyped) type.
|
|
405
|
-
#[allow(dead_code)]
|
|
406
|
-
fn install_block_parameters(&mut self, block_params: &ruby_prism::BlockParametersNode) {
|
|
407
|
-
// Just call the version that returns vtxs and discard the result
|
|
408
|
-
self.install_block_parameters_with_vtxs(block_params);
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
/// Install block parameters and return their vertex IDs
|
|
412
|
-
///
|
|
413
|
-
/// This version is used when we need to track the block parameter vertices
|
|
414
|
-
/// for type inference from the method's RBS block signature.
|
|
415
|
-
fn install_block_parameters_with_vtxs(
|
|
416
|
-
&mut self,
|
|
417
|
-
block_params: &ruby_prism::BlockParametersNode,
|
|
418
|
-
) -> Vec<VertexId> {
|
|
419
|
-
let mut vtxs = Vec::new();
|
|
420
|
-
|
|
421
|
-
// BlockParametersNode contains a ParametersNode
|
|
422
|
-
if let Some(params) = block_params.parameters() {
|
|
423
|
-
// Process required parameters (most common in blocks)
|
|
424
|
-
for node in params.requireds().iter() {
|
|
425
|
-
if let Some(req_param) = node.as_required_parameter_node() {
|
|
426
|
-
let name = String::from_utf8_lossy(req_param.name().as_slice()).to_string();
|
|
427
|
-
let vtx = install_block_parameter(self.genv, self.lenv, name);
|
|
428
|
-
vtxs.push(vtx);
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
// Optional parameters in blocks: { |x = 1| ... }
|
|
433
|
-
for node in params.optionals().iter() {
|
|
434
|
-
if let Some(opt_param) = node.as_optional_parameter_node() {
|
|
435
|
-
let name = String::from_utf8_lossy(opt_param.name().as_slice()).to_string();
|
|
436
|
-
let default_value = opt_param.value();
|
|
437
|
-
|
|
438
|
-
if let Some(default_vtx) = self.install_node(&default_value) {
|
|
439
|
-
let vtx = install_optional_parameter(
|
|
440
|
-
self.genv,
|
|
441
|
-
self.lenv,
|
|
442
|
-
&mut self.changes,
|
|
443
|
-
name,
|
|
444
|
-
default_vtx,
|
|
445
|
-
);
|
|
446
|
-
vtxs.push(vtx);
|
|
447
|
-
} else {
|
|
448
|
-
let vtx = install_block_parameter(self.genv, self.lenv, name);
|
|
449
|
-
vtxs.push(vtx);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
// Rest parameter in blocks: { |*args| ... }
|
|
455
|
-
if let Some(rest_node) = params.rest() {
|
|
456
|
-
if let Some(rest_param) = rest_node.as_rest_parameter_node() {
|
|
457
|
-
if let Some(name_id) = rest_param.name() {
|
|
458
|
-
let name = String::from_utf8_lossy(name_id.as_slice()).to_string();
|
|
459
|
-
let vtx = install_rest_parameter(self.genv, self.lenv, name);
|
|
460
|
-
vtxs.push(vtx);
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
vtxs
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
/// Install method parameters as local variables
|
|
470
|
-
fn install_parameters(&mut self, params_node: &ruby_prism::ParametersNode) {
|
|
471
|
-
// Required parameters: def foo(a, b)
|
|
472
|
-
for node in params_node.requireds().iter() {
|
|
473
|
-
if let Some(req_param) = node.as_required_parameter_node() {
|
|
474
|
-
let name = String::from_utf8_lossy(req_param.name().as_slice()).to_string();
|
|
475
|
-
install_required_parameter(self.genv, self.lenv, name);
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
// Optional parameters: def foo(a = 1, b = "hello")
|
|
480
|
-
for node in params_node.optionals().iter() {
|
|
481
|
-
if let Some(opt_param) = node.as_optional_parameter_node() {
|
|
482
|
-
let name = String::from_utf8_lossy(opt_param.name().as_slice()).to_string();
|
|
483
|
-
let default_value = opt_param.value();
|
|
484
|
-
|
|
485
|
-
// Process default value to get its type
|
|
486
|
-
if let Some(default_vtx) = self.install_node(&default_value) {
|
|
487
|
-
install_optional_parameter(
|
|
488
|
-
self.genv,
|
|
489
|
-
self.lenv,
|
|
490
|
-
&mut self.changes,
|
|
491
|
-
name,
|
|
492
|
-
default_vtx,
|
|
493
|
-
);
|
|
494
|
-
} else {
|
|
495
|
-
// Fallback to untyped if default can't be processed
|
|
496
|
-
install_required_parameter(self.genv, self.lenv, name);
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
// Rest parameter: def foo(*args)
|
|
502
|
-
if let Some(rest_node) = params_node.rest() {
|
|
503
|
-
if let Some(rest_param) = rest_node.as_rest_parameter_node() {
|
|
504
|
-
if let Some(name_id) = rest_param.name() {
|
|
505
|
-
let name = String::from_utf8_lossy(name_id.as_slice()).to_string();
|
|
506
|
-
install_rest_parameter(self.genv, self.lenv, name);
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
// Keyword rest parameter: def foo(**kwargs)
|
|
512
|
-
if let Some(kwrest_node) = params_node.keyword_rest() {
|
|
513
|
-
if let Some(kwrest_param) = kwrest_node.as_keyword_rest_parameter_node() {
|
|
514
|
-
if let Some(name_id) = kwrest_param.name() {
|
|
515
|
-
let name = String::from_utf8_lossy(name_id.as_slice()).to_string();
|
|
516
|
-
install_keyword_rest_parameter(self.genv, self.lenv, name);
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
}
|
|
36
|
+
install_node(self.genv, self.lenv, &mut self.changes, self.source, node)
|
|
520
37
|
}
|
|
521
38
|
|
|
522
|
-
/// Process multiple statements
|
|
523
|
-
fn install_statements(&mut self, statements: &ruby_prism::StatementsNode) {
|
|
524
|
-
for stmt in &statements.body() {
|
|
525
|
-
self.install_node(&stmt);
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
/// Finish installation (apply changes and execute Boxes)
|
|
530
39
|
pub fn finish(self) {
|
|
531
40
|
self.genv.apply_changes(self.changes);
|
|
532
41
|
self.genv.run_all();
|
|
533
42
|
}
|
|
534
43
|
}
|
|
535
44
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
let mut genv = GlobalEnv::new();
|
|
548
|
-
|
|
549
|
-
genv.register_builtin_method(Type::string(), "upcase", Type::string());
|
|
550
|
-
genv.register_builtin_method(Type::string(), "downcase", Type::string());
|
|
551
|
-
|
|
552
|
-
genv.register_builtin_method(Type::float(), "to_s", Type::string());
|
|
553
|
-
genv.register_builtin_method(Type::float(), "to_i", Type::integer());
|
|
554
|
-
genv.register_builtin_method(Type::float(), "round", Type::integer());
|
|
555
|
-
genv.register_builtin_method(Type::float(), "ceil", Type::integer());
|
|
556
|
-
genv.register_builtin_method(Type::float(), "floor", Type::integer());
|
|
557
|
-
genv.register_builtin_method(Type::float(), "abs", Type::float());
|
|
558
|
-
|
|
559
|
-
genv.register_builtin_method(Type::array(), "each", Type::array());
|
|
560
|
-
genv.register_builtin_method(Type::array(), "map", Type::array());
|
|
561
|
-
genv.register_builtin_method(Type::hash(), "each", Type::hash());
|
|
562
|
-
|
|
563
|
-
genv.register_builtin_method(Type::regexp(), "match", Type::instance("MatchData"));
|
|
564
|
-
genv.register_builtin_method(Type::regexp(), "match?", Type::instance("TrueClass"));
|
|
565
|
-
genv.register_builtin_method(Type::regexp(), "source", Type::string());
|
|
566
|
-
|
|
567
|
-
genv.register_builtin_method(Type::range(), "to_a", Type::array());
|
|
568
|
-
genv.register_builtin_method(Type::range(), "size", Type::integer());
|
|
569
|
-
genv.register_builtin_method(Type::range(), "count", Type::integer());
|
|
570
|
-
genv.register_builtin_method(Type::range(), "first", Type::Bot);
|
|
571
|
-
genv.register_builtin_method(Type::range(), "last", Type::Bot);
|
|
572
|
-
genv.register_builtin_method(Type::range(), "include?", Type::instance("TrueClass"));
|
|
573
|
-
genv.register_builtin_method(Type::range(), "cover?", Type::instance("TrueClass"));
|
|
574
|
-
|
|
575
|
-
let mut lenv = LocalEnv::new();
|
|
576
|
-
let mut installer = AstInstaller::new(&mut genv, &mut lenv, source);
|
|
577
|
-
|
|
578
|
-
let root = parse_result.node();
|
|
579
|
-
if let Some(program_node) = root.as_program_node() {
|
|
580
|
-
let statements = program_node.statements();
|
|
581
|
-
for stmt in &statements.body() {
|
|
582
|
-
installer.install_node(&stmt);
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
installer.finish();
|
|
587
|
-
(genv, lenv)
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
#[test]
|
|
591
|
-
fn test_install_literal() {
|
|
592
|
-
let source = r#"x = "hello""#;
|
|
593
|
-
let session = ParseSession::new();
|
|
594
|
-
let parse_result = session.parse_source(source, "test.rb").unwrap();
|
|
595
|
-
|
|
596
|
-
let mut genv = GlobalEnv::new();
|
|
597
|
-
let mut lenv = LocalEnv::new();
|
|
598
|
-
let mut installer = AstInstaller::new(&mut genv, &mut lenv, source);
|
|
599
|
-
|
|
600
|
-
let root = parse_result.node();
|
|
601
|
-
if let Some(program_node) = root.as_program_node() {
|
|
602
|
-
let statements = program_node.statements();
|
|
603
|
-
for stmt in &statements.body() {
|
|
604
|
-
installer.install_node(&stmt);
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
installer.finish();
|
|
609
|
-
|
|
610
|
-
let x_vtx = lenv.get_var("x").unwrap();
|
|
611
|
-
assert_eq!(genv.get_vertex(x_vtx).unwrap().show(), "String");
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
#[test]
|
|
615
|
-
fn test_install_multiple_vars() {
|
|
616
|
-
let source = r#"
|
|
617
|
-
x = "hello"
|
|
618
|
-
y = 42
|
|
619
|
-
"#;
|
|
620
|
-
let session = ParseSession::new();
|
|
621
|
-
let parse_result = session.parse_source(source, "test.rb").unwrap();
|
|
622
|
-
|
|
623
|
-
let mut genv = GlobalEnv::new();
|
|
624
|
-
let mut lenv = LocalEnv::new();
|
|
625
|
-
let mut installer = AstInstaller::new(&mut genv, &mut lenv, source);
|
|
626
|
-
|
|
627
|
-
let root = parse_result.node();
|
|
628
|
-
if let Some(program_node) = root.as_program_node() {
|
|
629
|
-
let statements = program_node.statements();
|
|
630
|
-
for stmt in &statements.body() {
|
|
631
|
-
installer.install_node(&stmt);
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
installer.finish();
|
|
636
|
-
|
|
637
|
-
let x_vtx = lenv.get_var("x").unwrap();
|
|
638
|
-
let y_vtx = lenv.get_var("y").unwrap();
|
|
639
|
-
|
|
640
|
-
assert_eq!(genv.get_vertex(x_vtx).unwrap().show(), "String");
|
|
641
|
-
assert_eq!(genv.get_vertex(y_vtx).unwrap().show(), "Integer");
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
#[test]
|
|
645
|
-
fn test_install_method_call() {
|
|
646
|
-
let source = r#"
|
|
647
|
-
x = "hello"
|
|
648
|
-
y = x.upcase
|
|
649
|
-
"#;
|
|
650
|
-
let session = ParseSession::new();
|
|
651
|
-
let parse_result = session.parse_source(source, "test.rb").unwrap();
|
|
652
|
-
|
|
653
|
-
let mut genv = GlobalEnv::new();
|
|
654
|
-
genv.register_builtin_method(Type::string(), "upcase", Type::string());
|
|
655
|
-
|
|
656
|
-
let mut lenv = LocalEnv::new();
|
|
657
|
-
let mut installer = AstInstaller::new(&mut genv, &mut lenv, source);
|
|
658
|
-
|
|
659
|
-
let root = parse_result.node();
|
|
660
|
-
if let Some(program_node) = root.as_program_node() {
|
|
661
|
-
let statements = program_node.statements();
|
|
662
|
-
for stmt in &statements.body() {
|
|
663
|
-
installer.install_node(&stmt);
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
installer.finish();
|
|
668
|
-
|
|
669
|
-
let x_vtx = lenv.get_var("x").unwrap();
|
|
670
|
-
let y_vtx = lenv.get_var("y").unwrap();
|
|
671
|
-
|
|
672
|
-
assert_eq!(genv.get_vertex(x_vtx).unwrap().show(), "String");
|
|
673
|
-
assert_eq!(genv.get_vertex(y_vtx).unwrap().show(), "String");
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
#[test]
|
|
677
|
-
fn test_install_module_with_method() {
|
|
678
|
-
let source = r#"
|
|
679
|
-
module Utils
|
|
680
|
-
def helper
|
|
681
|
-
x = "test"
|
|
682
|
-
end
|
|
683
|
-
end
|
|
684
|
-
"#;
|
|
685
|
-
let session = ParseSession::new();
|
|
686
|
-
let parse_result = session.parse_source(source, "test.rb").unwrap();
|
|
687
|
-
|
|
688
|
-
let mut genv = GlobalEnv::new();
|
|
689
|
-
let mut lenv = LocalEnv::new();
|
|
690
|
-
let mut installer = AstInstaller::new(&mut genv, &mut lenv, source);
|
|
691
|
-
|
|
692
|
-
let root = parse_result.node();
|
|
693
|
-
if let Some(program_node) = root.as_program_node() {
|
|
694
|
-
let statements = program_node.statements();
|
|
695
|
-
for stmt in &statements.body() {
|
|
696
|
-
installer.install_node(&stmt);
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
installer.finish();
|
|
701
|
-
|
|
702
|
-
// After processing, we should be back at top-level scope
|
|
703
|
-
assert_eq!(genv.scope_manager.current_module_name(), None);
|
|
704
|
-
assert_eq!(genv.scope_manager.current_class_name(), None);
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
#[test]
|
|
708
|
-
fn test_install_nested_module_class() {
|
|
709
|
-
let source = r#"
|
|
710
|
-
module Api
|
|
711
|
-
class User
|
|
712
|
-
def greet
|
|
713
|
-
name = "hello"
|
|
714
|
-
end
|
|
715
|
-
end
|
|
716
|
-
end
|
|
717
|
-
"#;
|
|
718
|
-
let session = ParseSession::new();
|
|
719
|
-
let parse_result = session.parse_source(source, "test.rb").unwrap();
|
|
720
|
-
|
|
721
|
-
let mut genv = GlobalEnv::new();
|
|
722
|
-
let mut lenv = LocalEnv::new();
|
|
723
|
-
let mut installer = AstInstaller::new(&mut genv, &mut lenv, source);
|
|
724
|
-
|
|
725
|
-
let root = parse_result.node();
|
|
726
|
-
if let Some(program_node) = root.as_program_node() {
|
|
727
|
-
let statements = program_node.statements();
|
|
728
|
-
for stmt in &statements.body() {
|
|
729
|
-
installer.install_node(&stmt);
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
installer.finish();
|
|
734
|
-
|
|
735
|
-
// After processing, we should be back at top-level scope
|
|
736
|
-
assert_eq!(genv.scope_manager.current_module_name(), None);
|
|
737
|
-
assert_eq!(genv.scope_manager.current_class_name(), None);
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
// ============================================
|
|
741
|
-
// Float Type Inference Tests
|
|
742
|
-
// ============================================
|
|
743
|
-
|
|
744
|
-
#[test]
|
|
745
|
-
fn test_float_literal_basic() {
|
|
746
|
-
let (genv, lenv) = analyze(r#"x = 3.14"#);
|
|
747
|
-
let x_vtx = lenv.get_var("x").unwrap();
|
|
748
|
-
assert_eq!(genv.get_vertex(x_vtx).unwrap().show(), "Float");
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
#[test]
|
|
752
|
-
fn test_float_ceil() {
|
|
753
|
-
let (genv, lenv) = analyze("x = 3.14\na = x.ceil");
|
|
754
|
-
let a_vtx = lenv.get_var("a").unwrap();
|
|
755
|
-
assert_eq!(genv.get_vertex(a_vtx).unwrap().show(), "Integer");
|
|
45
|
+
/// Install node (returns Vertex ID)
|
|
46
|
+
pub(crate) fn install_node(
|
|
47
|
+
genv: &mut GlobalEnv,
|
|
48
|
+
lenv: &mut LocalEnv,
|
|
49
|
+
changes: &mut ChangeSet,
|
|
50
|
+
source: &str,
|
|
51
|
+
node: &Node,
|
|
52
|
+
) -> Option<VertexId> {
|
|
53
|
+
if let Some(class_node) = node.as_class_node() {
|
|
54
|
+
return process_class_node(genv, lenv, changes, source, &class_node);
|
|
756
55
|
}
|
|
757
56
|
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
let (genv, lenv) = analyze("x = 3.14\nb = x.floor");
|
|
761
|
-
let b_vtx = lenv.get_var("b").unwrap();
|
|
762
|
-
assert_eq!(genv.get_vertex(b_vtx).unwrap().show(), "Integer");
|
|
57
|
+
if let Some(module_node) = node.as_module_node() {
|
|
58
|
+
return process_module_node(genv, lenv, changes, source, &module_node);
|
|
763
59
|
}
|
|
764
60
|
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
let (genv, lenv) = analyze("x = 3.14\nc = x.abs");
|
|
768
|
-
let c_vtx = lenv.get_var("c").unwrap();
|
|
769
|
-
assert_eq!(genv.get_vertex(c_vtx).unwrap().show(), "Float");
|
|
61
|
+
if let Some(def_node) = node.as_def_node() {
|
|
62
|
+
return process_def_node(genv, lenv, changes, source, &def_node);
|
|
770
63
|
}
|
|
771
64
|
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
// ============================================
|
|
775
|
-
|
|
776
|
-
#[test]
|
|
777
|
-
fn test_regexp_literal_basic() {
|
|
778
|
-
let (genv, lenv) = analyze(r#"x = /hello/"#);
|
|
779
|
-
let x_vtx = lenv.get_var("x").unwrap();
|
|
780
|
-
assert_eq!(genv.get_vertex(x_vtx).unwrap().show(), "Regexp");
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
#[test]
|
|
784
|
-
fn test_regexp_source() {
|
|
785
|
-
let (genv, lenv) = analyze("x = /hello/\na = x.source");
|
|
786
|
-
let a_vtx = lenv.get_var("a").unwrap();
|
|
787
|
-
assert_eq!(genv.get_vertex(a_vtx).unwrap().show(), "String");
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
// ============================================
|
|
791
|
-
// Range Type Inference Tests
|
|
792
|
-
// ============================================
|
|
793
|
-
|
|
794
|
-
#[test]
|
|
795
|
-
fn test_range_integer() {
|
|
796
|
-
let (genv, lenv) = analyze(r#"x = 1..5"#);
|
|
797
|
-
let x_vtx = lenv.get_var("x").unwrap();
|
|
798
|
-
assert_eq!(genv.get_vertex(x_vtx).unwrap().show(), "Range[Integer]");
|
|
65
|
+
if let Some(block_node) = node.as_block_node() {
|
|
66
|
+
return process_block_node(genv, lenv, changes, source, &block_node);
|
|
799
67
|
}
|
|
800
68
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
let (genv, lenv) = analyze(r#"x = 1...5"#);
|
|
804
|
-
let x_vtx = lenv.get_var("x").unwrap();
|
|
805
|
-
assert_eq!(genv.get_vertex(x_vtx).unwrap().show(), "Range[Integer]");
|
|
69
|
+
if let Some(if_node) = node.as_if_node() {
|
|
70
|
+
return process_if_node(genv, lenv, changes, source, &if_node);
|
|
806
71
|
}
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
fn test_range_string() {
|
|
810
|
-
let (genv, lenv) = analyze(r#"x = "a".."z""#);
|
|
811
|
-
let x_vtx = lenv.get_var("x").unwrap();
|
|
812
|
-
assert_eq!(genv.get_vertex(x_vtx).unwrap().show(), "Range[String]");
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
#[test]
|
|
816
|
-
fn test_range_float() {
|
|
817
|
-
let (genv, lenv) = analyze(r#"x = 1.0..5.0"#);
|
|
818
|
-
let x_vtx = lenv.get_var("x").unwrap();
|
|
819
|
-
assert_eq!(genv.get_vertex(x_vtx).unwrap().show(), "Range[Float]");
|
|
72
|
+
if let Some(unless_node) = node.as_unless_node() {
|
|
73
|
+
return process_unless_node(genv, lenv, changes, source, &unless_node);
|
|
820
74
|
}
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
fn test_range_to_a() {
|
|
824
|
-
let (genv, lenv) = analyze("x = 1..10\na = x.to_a");
|
|
825
|
-
let a_vtx = lenv.get_var("a").unwrap();
|
|
826
|
-
assert_eq!(genv.get_vertex(a_vtx).unwrap().show(), "Array");
|
|
75
|
+
if let Some(case_node) = node.as_case_node() {
|
|
76
|
+
return process_case_node(genv, lenv, changes, source, &case_node);
|
|
827
77
|
}
|
|
828
78
|
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
let b_vtx = lenv.get_var("b").unwrap();
|
|
833
|
-
assert_eq!(genv.get_vertex(b_vtx).unwrap().show(), "Integer");
|
|
79
|
+
match dispatch_simple(genv, lenv, node) {
|
|
80
|
+
DispatchResult::Vertex(vtx) => return Some(vtx),
|
|
81
|
+
DispatchResult::NotHandled => {}
|
|
834
82
|
}
|
|
835
83
|
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
// ============================================
|
|
839
|
-
|
|
840
|
-
#[test]
|
|
841
|
-
fn test_nested_array_integer() {
|
|
842
|
-
let (genv, lenv) = analyze(r#"x = [[1, 2], [3]]"#);
|
|
843
|
-
let x_vtx = lenv.get_var("x").unwrap();
|
|
844
|
-
assert_eq!(
|
|
845
|
-
genv.get_vertex(x_vtx).unwrap().show(),
|
|
846
|
-
"Array[Array[Integer]]"
|
|
847
|
-
);
|
|
84
|
+
if let Some(vtx) = install_literal_node(genv, lenv, changes, source, node) {
|
|
85
|
+
return Some(vtx);
|
|
848
86
|
}
|
|
849
87
|
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
let (genv, lenv) = analyze(r#"x = [[[1]]]"#);
|
|
853
|
-
let x_vtx = lenv.get_var("x").unwrap();
|
|
854
|
-
assert_eq!(
|
|
855
|
-
genv.get_vertex(x_vtx).unwrap().show(),
|
|
856
|
-
"Array[Array[Array[Integer]]]"
|
|
857
|
-
);
|
|
88
|
+
if let Some(kind) = dispatch_needs_child(node, source) {
|
|
89
|
+
return process_needs_child(genv, lenv, changes, source, kind);
|
|
858
90
|
}
|
|
859
91
|
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
let (genv, lenv) = analyze(r#"x = [[1], ["a"]]"#);
|
|
863
|
-
let x_vtx = lenv.get_var("x").unwrap();
|
|
864
|
-
let result = genv.get_vertex(x_vtx).unwrap().show();
|
|
865
|
-
assert!(
|
|
866
|
-
result == "Array[Array[Integer] | Array[String]]"
|
|
867
|
-
|| result == "Array[Array[String] | Array[Integer]]",
|
|
868
|
-
"Expected nested array with union, got: {}",
|
|
869
|
-
result
|
|
870
|
-
);
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
// ============================================
|
|
874
|
-
// Hash Type Inference Tests
|
|
875
|
-
// ============================================
|
|
876
|
-
|
|
877
|
-
#[test]
|
|
878
|
-
fn test_hash_symbol_integer() {
|
|
879
|
-
let (genv, lenv) = analyze(r#"x = { a: 1, b: 2 }"#);
|
|
880
|
-
let x_vtx = lenv.get_var("x").unwrap();
|
|
881
|
-
assert_eq!(
|
|
882
|
-
genv.get_vertex(x_vtx).unwrap().show(),
|
|
883
|
-
"Hash[Symbol, Integer]"
|
|
884
|
-
);
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
#[test]
|
|
888
|
-
fn test_hash_string_string() {
|
|
889
|
-
let (genv, lenv) = analyze(r#"x = { "k" => "v" }"#);
|
|
890
|
-
let x_vtx = lenv.get_var("x").unwrap();
|
|
891
|
-
assert_eq!(
|
|
892
|
-
genv.get_vertex(x_vtx).unwrap().show(),
|
|
893
|
-
"Hash[String, String]"
|
|
894
|
-
);
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
#[test]
|
|
898
|
-
fn test_hash_mixed_values() {
|
|
899
|
-
let (genv, lenv) = analyze(r#"x = { a: 1, b: "x" }"#);
|
|
900
|
-
let x_vtx = lenv.get_var("x").unwrap();
|
|
901
|
-
let result = genv.get_vertex(x_vtx).unwrap().show();
|
|
902
|
-
assert!(
|
|
903
|
-
result == "Hash[Symbol, Integer | String]"
|
|
904
|
-
|| result == "Hash[Symbol, String | Integer]",
|
|
905
|
-
"Expected Hash with union value type, got: {}",
|
|
906
|
-
result
|
|
907
|
-
);
|
|
908
|
-
}
|
|
909
|
-
|
|
910
|
-
#[test]
|
|
911
|
-
fn test_hash_empty() {
|
|
912
|
-
let (genv, lenv) = analyze(r#"x = {}"#);
|
|
913
|
-
let x_vtx = lenv.get_var("x").unwrap();
|
|
914
|
-
assert_eq!(genv.get_vertex(x_vtx).unwrap().show(), "Hash");
|
|
915
|
-
}
|
|
92
|
+
None
|
|
93
|
+
}
|
|
916
94
|
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
95
|
+
/// Process multiple statements (returns last expression's VertexId)
|
|
96
|
+
pub(crate) fn install_statements(
|
|
97
|
+
genv: &mut GlobalEnv,
|
|
98
|
+
lenv: &mut LocalEnv,
|
|
99
|
+
changes: &mut ChangeSet,
|
|
100
|
+
source: &str,
|
|
101
|
+
statements: &ruby_prism::StatementsNode,
|
|
102
|
+
) -> Option<VertexId> {
|
|
103
|
+
let mut last_vtx = None;
|
|
104
|
+
for stmt in &statements.body() {
|
|
105
|
+
last_vtx = install_node(genv, lenv, changes, source, &stmt);
|
|
106
|
+
}
|
|
107
|
+
last_vtx
|
|
926
108
|
}
|