method-ray 0.1.2 → 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 +41 -0
- data/README.md +27 -1
- data/ext/Cargo.toml +1 -1
- data/ext/src/lib.rs +7 -6
- data/lib/methodray/binary_locator.rb +29 -0
- data/lib/methodray/commands.rb +3 -20
- data/lib/methodray/version.rb +1 -1
- data/lib/methodray.rb +1 -1
- data/rust/Cargo.toml +3 -1
- data/rust/src/analyzer/attributes.rs +57 -0
- data/rust/src/analyzer/blocks.rs +175 -0
- data/rust/src/analyzer/calls.rs +7 -4
- data/rust/src/analyzer/conditionals.rs +466 -0
- data/rust/src/analyzer/definitions.rs +280 -13
- data/rust/src/analyzer/dispatch.rs +754 -11
- data/rust/src/analyzer/install.rs +58 -176
- data/rust/src/analyzer/literals.rs +201 -37
- data/rust/src/analyzer/mod.rs +4 -3
- data/rust/src/analyzer/parameters.rs +218 -0
- data/rust/src/analyzer/variables.rs +16 -8
- data/rust/src/cache/rbs_cache.rs +11 -4
- data/rust/src/checker.rs +20 -8
- data/rust/src/env/global_env.rs +42 -2
- data/rust/src/env/method_registry.rs +86 -4
- data/rust/src/env/mod.rs +1 -0
- data/rust/src/env/scope.rs +291 -25
- data/rust/src/graph/box.rs +478 -4
- data/rust/src/graph/change_set.rs +14 -0
- data/rust/src/graph/mod.rs +1 -1
- data/rust/src/lib.rs +2 -1
- data/rust/src/parser.rs +99 -39
- data/rust/src/rbs/converter.rs +16 -11
- data/rust/src/rbs/loader.rs +35 -5
- data/rust/src/rbs/mod.rs +4 -3
- data/rust/src/types.rs +344 -9
- metadata +6 -3
- data/rust/src/analyzer/tests/integration_test.rs +0 -136
- data/rust/src/analyzer/tests/mod.rs +0 -1
|
@@ -8,13 +8,13 @@ use crate::env::{GlobalEnv, LocalEnv};
|
|
|
8
8
|
use crate::graph::{ChangeSet, VertexId};
|
|
9
9
|
use ruby_prism::Node;
|
|
10
10
|
|
|
11
|
-
use super::
|
|
12
|
-
use super::
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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;
|
|
16
16
|
|
|
17
|
-
/// Build graph from AST
|
|
17
|
+
/// Build graph from AST (public API wrapper)
|
|
18
18
|
pub struct AstInstaller<'a> {
|
|
19
19
|
genv: &'a mut GlobalEnv,
|
|
20
20
|
lenv: &'a mut LocalEnv,
|
|
@@ -32,195 +32,77 @@ impl<'a> AstInstaller<'a> {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
/// Install node (returns Vertex ID)
|
|
36
35
|
pub fn install_node(&mut self, node: &Node) -> Option<VertexId> {
|
|
37
|
-
|
|
38
|
-
if let Some(class_node) = node.as_class_node() {
|
|
39
|
-
return self.install_class_node(&class_node);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Method definition
|
|
43
|
-
if let Some(def_node) = node.as_def_node() {
|
|
44
|
-
return self.install_def_node(&def_node);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Try simple dispatch first (no child processing needed)
|
|
48
|
-
match dispatch_simple(self.genv, self.lenv, node) {
|
|
49
|
-
DispatchResult::Vertex(vtx) => return Some(vtx),
|
|
50
|
-
DispatchResult::NotHandled => {}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Check if node needs child processing
|
|
54
|
-
if let Some(kind) = dispatch_needs_child(node, self.source) {
|
|
55
|
-
return self.process_needs_child(kind);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
None
|
|
36
|
+
install_node(self.genv, self.lenv, &mut self.changes, self.source, node)
|
|
59
37
|
}
|
|
60
38
|
|
|
61
|
-
/// Process nodes that need child evaluation first
|
|
62
|
-
fn process_needs_child(&mut self, kind: NeedsChildKind) -> Option<VertexId> {
|
|
63
|
-
match kind {
|
|
64
|
-
NeedsChildKind::IvarWrite { ivar_name, value } => {
|
|
65
|
-
let value_vtx = self.install_node(&value)?;
|
|
66
|
-
Some(finish_ivar_write(self.genv, ivar_name, value_vtx))
|
|
67
|
-
}
|
|
68
|
-
NeedsChildKind::LocalVarWrite { var_name, value } => {
|
|
69
|
-
let value_vtx = self.install_node(&value)?;
|
|
70
|
-
Some(finish_local_var_write(
|
|
71
|
-
self.genv,
|
|
72
|
-
self.lenv,
|
|
73
|
-
&mut self.changes,
|
|
74
|
-
var_name,
|
|
75
|
-
value_vtx,
|
|
76
|
-
))
|
|
77
|
-
}
|
|
78
|
-
NeedsChildKind::MethodCall {
|
|
79
|
-
receiver,
|
|
80
|
-
method_name,
|
|
81
|
-
location,
|
|
82
|
-
} => {
|
|
83
|
-
let recv_vtx = self.install_node(&receiver)?;
|
|
84
|
-
Some(finish_method_call(
|
|
85
|
-
self.genv,
|
|
86
|
-
recv_vtx,
|
|
87
|
-
method_name,
|
|
88
|
-
location,
|
|
89
|
-
))
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/// Install class definition
|
|
95
|
-
fn install_class_node(&mut self, class_node: &ruby_prism::ClassNode) -> Option<VertexId> {
|
|
96
|
-
let class_name = extract_class_name(class_node);
|
|
97
|
-
install_class(self.genv, class_name);
|
|
98
|
-
|
|
99
|
-
if let Some(body) = class_node.body() {
|
|
100
|
-
if let Some(statements) = body.as_statements_node() {
|
|
101
|
-
self.install_statements(&statements);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
exit_scope(self.genv);
|
|
106
|
-
None
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/// Install method definition
|
|
110
|
-
fn install_def_node(&mut self, def_node: &ruby_prism::DefNode) -> Option<VertexId> {
|
|
111
|
-
let method_name = String::from_utf8_lossy(def_node.name().as_slice()).to_string();
|
|
112
|
-
install_method(self.genv, method_name);
|
|
113
|
-
|
|
114
|
-
if let Some(body) = def_node.body() {
|
|
115
|
-
if let Some(statements) = body.as_statements_node() {
|
|
116
|
-
self.install_statements(&statements);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
exit_scope(self.genv);
|
|
121
|
-
None
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/// Process multiple statements
|
|
125
|
-
fn install_statements(&mut self, statements: &ruby_prism::StatementsNode) {
|
|
126
|
-
for stmt in &statements.body() {
|
|
127
|
-
self.install_node(&stmt);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/// Finish installation (apply changes and execute Boxes)
|
|
132
39
|
pub fn finish(self) {
|
|
133
40
|
self.genv.apply_changes(self.changes);
|
|
134
41
|
self.genv.run_all();
|
|
135
42
|
}
|
|
136
43
|
}
|
|
137
44
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
let mut genv = GlobalEnv::new();
|
|
150
|
-
let mut lenv = LocalEnv::new();
|
|
151
|
-
let mut installer = AstInstaller::new(&mut genv, &mut lenv, source);
|
|
152
|
-
|
|
153
|
-
let root = parse_result.node();
|
|
154
|
-
if let Some(program_node) = root.as_program_node() {
|
|
155
|
-
let statements = program_node.statements();
|
|
156
|
-
for stmt in &statements.body() {
|
|
157
|
-
installer.install_node(&stmt);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
installer.finish();
|
|
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);
|
|
55
|
+
}
|
|
162
56
|
|
|
163
|
-
|
|
164
|
-
|
|
57
|
+
if let Some(module_node) = node.as_module_node() {
|
|
58
|
+
return process_module_node(genv, lenv, changes, source, &module_node);
|
|
165
59
|
}
|
|
166
60
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
x = "hello"
|
|
171
|
-
y = 42
|
|
172
|
-
"#;
|
|
173
|
-
let parse_result = parse_ruby_source(source, "test.rb".to_string()).unwrap();
|
|
174
|
-
|
|
175
|
-
let mut genv = GlobalEnv::new();
|
|
176
|
-
let mut lenv = LocalEnv::new();
|
|
177
|
-
let mut installer = AstInstaller::new(&mut genv, &mut lenv, source);
|
|
178
|
-
|
|
179
|
-
let root = parse_result.node();
|
|
180
|
-
if let Some(program_node) = root.as_program_node() {
|
|
181
|
-
let statements = program_node.statements();
|
|
182
|
-
for stmt in &statements.body() {
|
|
183
|
-
installer.install_node(&stmt);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
61
|
+
if let Some(def_node) = node.as_def_node() {
|
|
62
|
+
return process_def_node(genv, lenv, changes, source, &def_node);
|
|
63
|
+
}
|
|
186
64
|
|
|
187
|
-
|
|
65
|
+
if let Some(block_node) = node.as_block_node() {
|
|
66
|
+
return process_block_node(genv, lenv, changes, source, &block_node);
|
|
67
|
+
}
|
|
188
68
|
|
|
189
|
-
|
|
190
|
-
|
|
69
|
+
if let Some(if_node) = node.as_if_node() {
|
|
70
|
+
return process_if_node(genv, lenv, changes, source, &if_node);
|
|
71
|
+
}
|
|
72
|
+
if let Some(unless_node) = node.as_unless_node() {
|
|
73
|
+
return process_unless_node(genv, lenv, changes, source, &unless_node);
|
|
74
|
+
}
|
|
75
|
+
if let Some(case_node) = node.as_case_node() {
|
|
76
|
+
return process_case_node(genv, lenv, changes, source, &case_node);
|
|
77
|
+
}
|
|
191
78
|
|
|
192
|
-
|
|
193
|
-
|
|
79
|
+
match dispatch_simple(genv, lenv, node) {
|
|
80
|
+
DispatchResult::Vertex(vtx) => return Some(vtx),
|
|
81
|
+
DispatchResult::NotHandled => {}
|
|
194
82
|
}
|
|
195
83
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
x = "hello"
|
|
200
|
-
y = x.upcase
|
|
201
|
-
"#;
|
|
202
|
-
let parse_result = parse_ruby_source(source, "test.rb".to_string()).unwrap();
|
|
203
|
-
|
|
204
|
-
let mut genv = GlobalEnv::new();
|
|
205
|
-
genv.register_builtin_method(Type::string(), "upcase", Type::string());
|
|
206
|
-
|
|
207
|
-
let mut lenv = LocalEnv::new();
|
|
208
|
-
let mut installer = AstInstaller::new(&mut genv, &mut lenv, source);
|
|
209
|
-
|
|
210
|
-
let root = parse_result.node();
|
|
211
|
-
if let Some(program_node) = root.as_program_node() {
|
|
212
|
-
let statements = program_node.statements();
|
|
213
|
-
for stmt in &statements.body() {
|
|
214
|
-
installer.install_node(&stmt);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
84
|
+
if let Some(vtx) = install_literal_node(genv, lenv, changes, source, node) {
|
|
85
|
+
return Some(vtx);
|
|
86
|
+
}
|
|
217
87
|
|
|
218
|
-
|
|
88
|
+
if let Some(kind) = dispatch_needs_child(node, source) {
|
|
89
|
+
return process_needs_child(genv, lenv, changes, source, kind);
|
|
90
|
+
}
|
|
219
91
|
|
|
220
|
-
|
|
221
|
-
|
|
92
|
+
None
|
|
93
|
+
}
|
|
222
94
|
|
|
223
|
-
|
|
224
|
-
|
|
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);
|
|
225
106
|
}
|
|
107
|
+
last_vtx
|
|
226
108
|
}
|
|
@@ -1,63 +1,217 @@
|
|
|
1
1
|
//! Literal Handlers - Processing Ruby literal values
|
|
2
2
|
//!
|
|
3
3
|
//! This module is responsible for:
|
|
4
|
-
//! - String, Integer,
|
|
4
|
+
//! - String, Integer, Float, Regexp literals
|
|
5
5
|
//! - nil, true, false, Symbol literals
|
|
6
|
+
//! - Array, Hash, Range literals with element type inference
|
|
6
7
|
//! - Creating Source vertices with fixed types
|
|
7
8
|
|
|
8
|
-
use crate::env::GlobalEnv;
|
|
9
|
-
use crate::graph::VertexId;
|
|
9
|
+
use crate::env::{GlobalEnv, LocalEnv};
|
|
10
|
+
use crate::graph::{ChangeSet, VertexId};
|
|
10
11
|
use crate::types::Type;
|
|
11
12
|
use ruby_prism::Node;
|
|
13
|
+
use std::collections::HashSet;
|
|
12
14
|
|
|
13
|
-
/// Install literal
|
|
14
|
-
pub fn
|
|
15
|
-
|
|
15
|
+
/// Install literal node (including complex types: Array, Hash, Range)
|
|
16
|
+
pub(crate) fn install_literal_node(
|
|
17
|
+
genv: &mut GlobalEnv,
|
|
18
|
+
lenv: &mut LocalEnv,
|
|
19
|
+
changes: &mut ChangeSet,
|
|
20
|
+
source: &str,
|
|
21
|
+
node: &Node,
|
|
22
|
+
) -> Option<VertexId> {
|
|
23
|
+
if node.as_array_node().is_some() {
|
|
24
|
+
let elements: Vec<Node> = node.as_array_node().unwrap().elements().iter().collect();
|
|
25
|
+
return install_array_literal_elements(genv, lenv, changes, source, elements);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if node.as_hash_node().is_some() {
|
|
29
|
+
let elements: Vec<Node> = node.as_hash_node().unwrap().elements().iter().collect();
|
|
30
|
+
return install_hash_literal_elements(genv, lenv, changes, source, elements);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if let Some(range_node) = node.as_range_node() {
|
|
34
|
+
return install_range_literal(genv, lenv, changes, source, &range_node);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
install_simple_literal(genv, node)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/// Install simple literal nodes (String, Integer, Float, nil, true, false, Symbol, Regexp)
|
|
41
|
+
fn install_simple_literal(genv: &mut GlobalEnv, node: &Node) -> Option<VertexId> {
|
|
16
42
|
if node.as_string_node().is_some() {
|
|
17
43
|
return Some(genv.new_source(Type::string()));
|
|
18
44
|
}
|
|
19
|
-
|
|
20
|
-
// 42
|
|
21
45
|
if node.as_integer_node().is_some() {
|
|
22
46
|
return Some(genv.new_source(Type::integer()));
|
|
23
47
|
}
|
|
48
|
+
if node.as_float_node().is_some() {
|
|
49
|
+
return Some(genv.new_source(Type::float()));
|
|
50
|
+
}
|
|
51
|
+
if node.as_nil_node().is_some() {
|
|
52
|
+
return Some(genv.new_source(Type::Nil));
|
|
53
|
+
}
|
|
54
|
+
if node.as_true_node().is_some() {
|
|
55
|
+
return Some(genv.new_source(Type::instance("TrueClass")));
|
|
56
|
+
}
|
|
57
|
+
if node.as_false_node().is_some() {
|
|
58
|
+
return Some(genv.new_source(Type::instance("FalseClass")));
|
|
59
|
+
}
|
|
60
|
+
if node.as_symbol_node().is_some() {
|
|
61
|
+
return Some(genv.new_source(Type::symbol()));
|
|
62
|
+
}
|
|
63
|
+
if node.as_regular_expression_node().is_some() {
|
|
64
|
+
return Some(genv.new_source(Type::regexp()));
|
|
65
|
+
}
|
|
66
|
+
None
|
|
67
|
+
}
|
|
24
68
|
|
|
25
|
-
|
|
26
|
-
|
|
69
|
+
/// Install array literal with element type inference
|
|
70
|
+
fn install_array_literal_elements(
|
|
71
|
+
genv: &mut GlobalEnv,
|
|
72
|
+
lenv: &mut LocalEnv,
|
|
73
|
+
changes: &mut ChangeSet,
|
|
74
|
+
source: &str,
|
|
75
|
+
elements: Vec<Node>,
|
|
76
|
+
) -> Option<VertexId> {
|
|
77
|
+
if elements.is_empty() {
|
|
27
78
|
return Some(genv.new_source(Type::array()));
|
|
28
79
|
}
|
|
29
80
|
|
|
30
|
-
|
|
31
|
-
if node.as_hash_node().is_some() {
|
|
32
|
-
return Some(genv.new_source(Type::hash()));
|
|
33
|
-
}
|
|
81
|
+
let mut element_types: HashSet<Type> = HashSet::new();
|
|
34
82
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
83
|
+
for element in &elements {
|
|
84
|
+
if let Some(vtx) = super::install::install_node(genv, lenv, changes, source, element) {
|
|
85
|
+
if let Some(src) = genv.get_source(vtx) {
|
|
86
|
+
element_types.insert(src.ty.clone());
|
|
87
|
+
} else if let Some(vertex) = genv.get_vertex(vtx) {
|
|
88
|
+
for ty in vertex.types.keys() {
|
|
89
|
+
element_types.insert(ty.clone());
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
38
93
|
}
|
|
39
94
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
95
|
+
let array_type = if element_types.is_empty() {
|
|
96
|
+
Type::array()
|
|
97
|
+
} else if element_types.len() == 1 {
|
|
98
|
+
let elem_type = element_types.into_iter().next().unwrap();
|
|
99
|
+
Type::array_of(elem_type)
|
|
100
|
+
} else {
|
|
101
|
+
let types_vec: Vec<Type> = element_types.into_iter().collect();
|
|
102
|
+
let union_type = Type::Union(types_vec);
|
|
103
|
+
Type::array_of(union_type)
|
|
104
|
+
};
|
|
46
105
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
106
|
+
Some(genv.new_source(array_type))
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/// Install hash literal with key/value type inference
|
|
110
|
+
fn install_hash_literal_elements(
|
|
111
|
+
genv: &mut GlobalEnv,
|
|
112
|
+
lenv: &mut LocalEnv,
|
|
113
|
+
changes: &mut ChangeSet,
|
|
114
|
+
source: &str,
|
|
115
|
+
elements: Vec<Node>,
|
|
116
|
+
) -> Option<VertexId> {
|
|
117
|
+
if elements.is_empty() {
|
|
118
|
+
return Some(genv.new_source(Type::hash()));
|
|
52
119
|
}
|
|
53
120
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
121
|
+
let mut key_types: HashSet<Type> = HashSet::new();
|
|
122
|
+
let mut value_types: HashSet<Type> = HashSet::new();
|
|
123
|
+
|
|
124
|
+
for element in &elements {
|
|
125
|
+
if let Some(assoc_node) = element.as_assoc_node() {
|
|
126
|
+
if let Some(key_vtx) =
|
|
127
|
+
super::install::install_node(genv, lenv, changes, source, &assoc_node.key())
|
|
128
|
+
{
|
|
129
|
+
if let Some(src) = genv.get_source(key_vtx) {
|
|
130
|
+
key_types.insert(src.ty.clone());
|
|
131
|
+
} else if let Some(vertex) = genv.get_vertex(key_vtx) {
|
|
132
|
+
for ty in vertex.types.keys() {
|
|
133
|
+
key_types.insert(ty.clone());
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if let Some(value_vtx) =
|
|
139
|
+
super::install::install_node(genv, lenv, changes, source, &assoc_node.value())
|
|
140
|
+
{
|
|
141
|
+
if let Some(src) = genv.get_source(value_vtx) {
|
|
142
|
+
value_types.insert(src.ty.clone());
|
|
143
|
+
} else if let Some(vertex) = genv.get_vertex(value_vtx) {
|
|
144
|
+
for ty in vertex.types.keys() {
|
|
145
|
+
value_types.insert(ty.clone());
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
59
150
|
}
|
|
60
151
|
|
|
152
|
+
let hash_type = if key_types.is_empty() || value_types.is_empty() {
|
|
153
|
+
Type::hash()
|
|
154
|
+
} else {
|
|
155
|
+
let key_type = if key_types.len() == 1 {
|
|
156
|
+
key_types.into_iter().next().unwrap()
|
|
157
|
+
} else {
|
|
158
|
+
let types_vec: Vec<Type> = key_types.into_iter().collect();
|
|
159
|
+
Type::Union(types_vec)
|
|
160
|
+
};
|
|
161
|
+
let value_type = if value_types.len() == 1 {
|
|
162
|
+
value_types.into_iter().next().unwrap()
|
|
163
|
+
} else {
|
|
164
|
+
let types_vec: Vec<Type> = value_types.into_iter().collect();
|
|
165
|
+
Type::Union(types_vec)
|
|
166
|
+
};
|
|
167
|
+
Type::hash_of(key_type, value_type)
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
Some(genv.new_source(hash_type))
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/// Install range literal with element type inference
|
|
174
|
+
fn install_range_literal(
|
|
175
|
+
genv: &mut GlobalEnv,
|
|
176
|
+
lenv: &mut LocalEnv,
|
|
177
|
+
changes: &mut ChangeSet,
|
|
178
|
+
source: &str,
|
|
179
|
+
range_node: &ruby_prism::RangeNode,
|
|
180
|
+
) -> Option<VertexId> {
|
|
181
|
+
let element_type = if let Some(left) = range_node.left() {
|
|
182
|
+
infer_range_element_type(genv, lenv, changes, source, &left)
|
|
183
|
+
} else if let Some(right) = range_node.right() {
|
|
184
|
+
infer_range_element_type(genv, lenv, changes, source, &right)
|
|
185
|
+
} else {
|
|
186
|
+
None
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
let range_type = match element_type {
|
|
190
|
+
Some(ty) => Type::range_of(ty),
|
|
191
|
+
None => Type::range(),
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
Some(genv.new_source(range_type))
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/// Infer element type from a range endpoint node
|
|
198
|
+
fn infer_range_element_type(
|
|
199
|
+
genv: &mut GlobalEnv,
|
|
200
|
+
lenv: &mut LocalEnv,
|
|
201
|
+
changes: &mut ChangeSet,
|
|
202
|
+
source: &str,
|
|
203
|
+
node: &Node,
|
|
204
|
+
) -> Option<Type> {
|
|
205
|
+
if let Some(vtx) = super::install::install_node(genv, lenv, changes, source, node) {
|
|
206
|
+
if let Some(src) = genv.get_source(vtx) {
|
|
207
|
+
return Some(src.ty.clone());
|
|
208
|
+
}
|
|
209
|
+
if let Some(vertex) = genv.get_vertex(vtx) {
|
|
210
|
+
if let Some(ty) = vertex.types.keys().next() {
|
|
211
|
+
return Some(ty.clone());
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
61
215
|
None
|
|
62
216
|
}
|
|
63
217
|
|
|
@@ -68,9 +222,6 @@ mod tests {
|
|
|
68
222
|
#[test]
|
|
69
223
|
fn test_install_string_literal() {
|
|
70
224
|
let mut genv = GlobalEnv::new();
|
|
71
|
-
|
|
72
|
-
// Create a mock string node - we test via integration instead
|
|
73
|
-
// Unit test just verifies the type creation
|
|
74
225
|
let vtx = genv.new_source(Type::string());
|
|
75
226
|
assert_eq!(genv.get_source(vtx).unwrap().ty.show(), "String");
|
|
76
227
|
}
|
|
@@ -78,8 +229,21 @@ mod tests {
|
|
|
78
229
|
#[test]
|
|
79
230
|
fn test_install_integer_literal() {
|
|
80
231
|
let mut genv = GlobalEnv::new();
|
|
81
|
-
|
|
82
232
|
let vtx = genv.new_source(Type::integer());
|
|
83
233
|
assert_eq!(genv.get_source(vtx).unwrap().ty.show(), "Integer");
|
|
84
234
|
}
|
|
235
|
+
|
|
236
|
+
#[test]
|
|
237
|
+
fn test_install_float_literal() {
|
|
238
|
+
let mut genv = GlobalEnv::new();
|
|
239
|
+
let vtx = genv.new_source(Type::float());
|
|
240
|
+
assert_eq!(genv.get_source(vtx).unwrap().ty.show(), "Float");
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
#[test]
|
|
244
|
+
fn test_install_regexp_literal() {
|
|
245
|
+
let mut genv = GlobalEnv::new();
|
|
246
|
+
let vtx = genv.new_source(Type::regexp());
|
|
247
|
+
assert_eq!(genv.get_source(vtx).unwrap().ty.show(), "Regexp");
|
|
248
|
+
}
|
|
85
249
|
}
|
data/rust/src/analyzer/mod.rs
CHANGED