method-ray 0.1.7 → 0.1.9
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 +33 -0
- data/{rust → core}/Cargo.toml +1 -1
- data/core/src/analyzer/assignments.rs +499 -0
- data/{rust → core}/src/analyzer/attributes.rs +2 -1
- data/{rust → core}/src/analyzer/blocks.rs +140 -0
- data/{rust → core}/src/analyzer/calls.rs +7 -3
- data/{rust → core}/src/analyzer/definitions.rs +12 -7
- data/{rust → core}/src/analyzer/dispatch.rs +431 -13
- data/core/src/analyzer/exceptions.rs +622 -0
- data/{rust → core}/src/analyzer/install.rs +37 -1
- data/{rust → core}/src/analyzer/literals.rs +3 -17
- data/core/src/analyzer/loops.rs +301 -0
- data/{rust → core}/src/analyzer/mod.rs +4 -0
- data/{rust → core}/src/analyzer/operators.rs +119 -27
- data/{rust → core}/src/analyzer/parameters.rs +214 -5
- data/core/src/analyzer/super_calls.rs +285 -0
- data/{rust → core}/src/cache/rbs_cache.rs +0 -1
- data/{rust → core}/src/cli/commands.rs +3 -3
- data/{rust → core}/src/diagnostics/diagnostic.rs +0 -3
- data/{rust → core}/src/diagnostics/formatter.rs +0 -1
- data/{rust → core}/src/env/box_manager.rs +2 -4
- data/{rust → core}/src/env/global_env.rs +28 -7
- data/{rust → core}/src/env/local_env.rs +35 -1
- data/{rust → core}/src/env/method_registry.rs +117 -25
- data/{rust → core}/src/env/scope.rs +91 -4
- data/{rust → core}/src/env/vertex_manager.rs +0 -1
- data/{rust → core}/src/graph/box.rs +134 -8
- data/{rust → core}/src/graph/change_set.rs +14 -0
- data/{rust → core}/src/lsp/server.rs +1 -1
- data/{rust → core}/src/rbs/loader.rs +1 -2
- data/{rust → core}/src/source_map.rs +0 -1
- data/{rust → core}/src/types.rs +11 -1
- data/ext/Cargo.toml +2 -2
- data/lib/methodray/binary_locator.rb +2 -2
- data/lib/methodray/commands.rb +1 -1
- data/lib/methodray/version.rb +1 -1
- metadata +54 -50
- /data/{rust → core}/src/analyzer/conditionals.rs +0 -0
- /data/{rust → core}/src/analyzer/parentheses.rs +0 -0
- /data/{rust → core}/src/analyzer/returns.rs +0 -0
- /data/{rust → core}/src/analyzer/variables.rs +0 -0
- /data/{rust → core}/src/cache/mod.rs +0 -0
- /data/{rust → core}/src/checker.rs +0 -0
- /data/{rust → core}/src/cli/args.rs +0 -0
- /data/{rust → core}/src/cli/mod.rs +0 -0
- /data/{rust → core}/src/diagnostics/mod.rs +0 -0
- /data/{rust → core}/src/env/mod.rs +0 -0
- /data/{rust → core}/src/env/type_error.rs +0 -0
- /data/{rust → core}/src/graph/mod.rs +0 -0
- /data/{rust → core}/src/graph/vertex.rs +0 -0
- /data/{rust → core}/src/lib.rs +0 -0
- /data/{rust → core}/src/lsp/diagnostics.rs +0 -0
- /data/{rust → core}/src/lsp/main.rs +0 -0
- /data/{rust → core}/src/lsp/mod.rs +0 -0
- /data/{rust → core}/src/main.rs +0 -0
- /data/{rust → core}/src/parser.rs +0 -0
- /data/{rust → core}/src/rbs/converter.rs +0 -0
- /data/{rust → core}/src/rbs/error.rs +0 -0
- /data/{rust → core}/src/rbs/mod.rs +0 -0
|
@@ -128,6 +128,55 @@ fn install_block_parameter(genv: &mut GlobalEnv, lenv: &mut LocalEnv, name: Stri
|
|
|
128
128
|
#[cfg(test)]
|
|
129
129
|
mod tests {
|
|
130
130
|
use super::*;
|
|
131
|
+
use crate::analyzer::install::AstInstaller;
|
|
132
|
+
use crate::env::LocalEnv;
|
|
133
|
+
use crate::parser::ParseSession;
|
|
134
|
+
use crate::types::Type;
|
|
135
|
+
|
|
136
|
+
fn get_type_show(genv: &GlobalEnv, vtx: VertexId) -> String {
|
|
137
|
+
if let Some(vertex) = genv.get_vertex(vtx) {
|
|
138
|
+
vertex.show()
|
|
139
|
+
} else if let Some(source) = genv.get_source(vtx) {
|
|
140
|
+
source.ty.show()
|
|
141
|
+
} else {
|
|
142
|
+
panic!("vertex {:?} not found as either Vertex or Source", vtx);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
fn analyze_with_stdlib(source: &str) -> GlobalEnv {
|
|
147
|
+
let session = ParseSession::new();
|
|
148
|
+
let parse_result = session.parse_source(source, "test.rb").unwrap();
|
|
149
|
+
let root = parse_result.node();
|
|
150
|
+
let program = root.as_program_node().unwrap();
|
|
151
|
+
|
|
152
|
+
let mut genv = GlobalEnv::new();
|
|
153
|
+
|
|
154
|
+
// Register stdlib methods needed for block tests
|
|
155
|
+
genv.register_builtin_method_with_block(
|
|
156
|
+
Type::array(),
|
|
157
|
+
"each",
|
|
158
|
+
Type::array(),
|
|
159
|
+
Some(vec![Type::instance("Elem")]),
|
|
160
|
+
);
|
|
161
|
+
genv.register_builtin_method_with_block(
|
|
162
|
+
Type::string(),
|
|
163
|
+
"each_char",
|
|
164
|
+
Type::string(),
|
|
165
|
+
Some(vec![Type::string()]),
|
|
166
|
+
);
|
|
167
|
+
genv.register_builtin_method(Type::integer(), "even?", Type::instance("TrueClass"));
|
|
168
|
+
genv.register_builtin_method(Type::string(), "upcase", Type::string());
|
|
169
|
+
|
|
170
|
+
let mut lenv = LocalEnv::new();
|
|
171
|
+
|
|
172
|
+
let mut installer = AstInstaller::new(&mut genv, &mut lenv, source);
|
|
173
|
+
for stmt in &program.statements().body() {
|
|
174
|
+
installer.install_node(&stmt);
|
|
175
|
+
}
|
|
176
|
+
installer.finish();
|
|
177
|
+
|
|
178
|
+
genv
|
|
179
|
+
}
|
|
131
180
|
|
|
132
181
|
#[test]
|
|
133
182
|
fn test_enter_exit_block_scope() {
|
|
@@ -173,4 +222,95 @@ mod tests {
|
|
|
173
222
|
|
|
174
223
|
exit_block_scope(&mut genv);
|
|
175
224
|
}
|
|
225
|
+
|
|
226
|
+
#[test]
|
|
227
|
+
fn test_block_parameter_type_from_array() {
|
|
228
|
+
let source = r#"
|
|
229
|
+
class Foo
|
|
230
|
+
def bar
|
|
231
|
+
[1, 2, 3].each { |x| x.even? }
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
"#;
|
|
235
|
+
let genv = analyze_with_stdlib(source);
|
|
236
|
+
assert!(
|
|
237
|
+
genv.type_errors.is_empty(),
|
|
238
|
+
"x.even? should not produce type errors: {:?}",
|
|
239
|
+
genv.type_errors
|
|
240
|
+
);
|
|
241
|
+
// Verify bar returns Array (each returns its receiver)
|
|
242
|
+
let info = genv.resolve_method(&Type::instance("Foo"), "bar").unwrap();
|
|
243
|
+
let ret_vtx = info.return_vertex.unwrap();
|
|
244
|
+
assert_eq!(get_type_show(&genv, ret_vtx), "Array");
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
#[test]
|
|
248
|
+
fn test_block_external_variable_access() {
|
|
249
|
+
let source = r#"
|
|
250
|
+
class Foo
|
|
251
|
+
def bar
|
|
252
|
+
y = "hello"
|
|
253
|
+
[1].each { y.upcase }
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
"#;
|
|
257
|
+
let genv = analyze_with_stdlib(source);
|
|
258
|
+
assert!(
|
|
259
|
+
genv.type_errors.is_empty(),
|
|
260
|
+
"y.upcase should not produce type errors: {:?}",
|
|
261
|
+
genv.type_errors
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
#[test]
|
|
266
|
+
fn test_block_parameter_from_each_char() {
|
|
267
|
+
let source = r#"
|
|
268
|
+
class Foo
|
|
269
|
+
def bar
|
|
270
|
+
"hello".each_char { |c| c.upcase }
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
"#;
|
|
274
|
+
let genv = analyze_with_stdlib(source);
|
|
275
|
+
assert!(
|
|
276
|
+
genv.type_errors.is_empty(),
|
|
277
|
+
"c.upcase should not produce type errors: {:?}",
|
|
278
|
+
genv.type_errors
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
#[test]
|
|
283
|
+
fn test_block_body_does_not_affect_method_return() {
|
|
284
|
+
let source = r#"
|
|
285
|
+
class Foo
|
|
286
|
+
def bar
|
|
287
|
+
[1, 2].each { |x| "string" }
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
"#;
|
|
291
|
+
let genv = analyze_with_stdlib(source);
|
|
292
|
+
// each returns its receiver (Array), not the block body result (String)
|
|
293
|
+
let info = genv.resolve_method(&Type::instance("Foo"), "bar").unwrap();
|
|
294
|
+
let ret_vtx = info.return_vertex.unwrap();
|
|
295
|
+
assert_eq!(get_type_show(&genv, ret_vtx), "Array");
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
#[test]
|
|
299
|
+
fn test_nested_blocks() {
|
|
300
|
+
let source = r#"
|
|
301
|
+
class Foo
|
|
302
|
+
def bar
|
|
303
|
+
[1, 2].each { |x|
|
|
304
|
+
"hello".each_char { |c| c.upcase }
|
|
305
|
+
}
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
"#;
|
|
309
|
+
let genv = analyze_with_stdlib(source);
|
|
310
|
+
assert!(
|
|
311
|
+
genv.type_errors.is_empty(),
|
|
312
|
+
"nested block should not produce type errors: {:?}",
|
|
313
|
+
genv.type_errors
|
|
314
|
+
);
|
|
315
|
+
}
|
|
176
316
|
}
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
//! - Managing return value vertices
|
|
6
6
|
//! - Attaching source location for error reporting
|
|
7
7
|
|
|
8
|
+
use std::collections::HashMap;
|
|
9
|
+
|
|
8
10
|
use crate::env::GlobalEnv;
|
|
9
11
|
use crate::graph::{MethodCallBox, VertexId};
|
|
10
12
|
use crate::source_map::SourceLocation;
|
|
@@ -15,6 +17,7 @@ pub fn install_method_call(
|
|
|
15
17
|
recv_vtx: VertexId,
|
|
16
18
|
method_name: String,
|
|
17
19
|
arg_vtxs: Vec<VertexId>,
|
|
20
|
+
kwarg_vtxs: Option<HashMap<String, VertexId>>,
|
|
18
21
|
location: Option<SourceLocation>,
|
|
19
22
|
) -> VertexId {
|
|
20
23
|
// Create Vertex for return value
|
|
@@ -22,7 +25,8 @@ pub fn install_method_call(
|
|
|
22
25
|
|
|
23
26
|
// Create MethodCallBox with location and argument vertices
|
|
24
27
|
let box_id = genv.alloc_box_id();
|
|
25
|
-
let call_box =
|
|
28
|
+
let call_box =
|
|
29
|
+
MethodCallBox::new(box_id, recv_vtx, method_name, ret_vtx, arg_vtxs, kwarg_vtxs, location);
|
|
26
30
|
genv.register_box(box_id, Box::new(call_box));
|
|
27
31
|
|
|
28
32
|
ret_vtx
|
|
@@ -39,7 +43,7 @@ mod tests {
|
|
|
39
43
|
|
|
40
44
|
let recv_vtx = genv.new_source(Type::string());
|
|
41
45
|
let ret_vtx =
|
|
42
|
-
install_method_call(&mut genv, recv_vtx, "upcase".to_string(), vec![], None);
|
|
46
|
+
install_method_call(&mut genv, recv_vtx, "upcase".to_string(), vec![], None, None);
|
|
43
47
|
|
|
44
48
|
// Return vertex should exist
|
|
45
49
|
assert!(genv.get_vertex(ret_vtx).is_some());
|
|
@@ -51,7 +55,7 @@ mod tests {
|
|
|
51
55
|
|
|
52
56
|
let recv_vtx = genv.new_source(Type::string());
|
|
53
57
|
let _ret_vtx =
|
|
54
|
-
install_method_call(&mut genv, recv_vtx, "upcase".to_string(), vec![], None);
|
|
58
|
+
install_method_call(&mut genv, recv_vtx, "upcase".to_string(), vec![], None, None);
|
|
55
59
|
|
|
56
60
|
// Box should be added
|
|
57
61
|
assert_eq!(genv.box_count(), 1);
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
//! - Method definition scope management (def baz ... end)
|
|
7
7
|
//! - Extracting class/module names from AST nodes (including qualified names like Api::User)
|
|
8
8
|
|
|
9
|
+
use std::collections::HashMap;
|
|
10
|
+
|
|
9
11
|
use crate::env::{GlobalEnv, LocalEnv};
|
|
10
12
|
use crate::graph::{ChangeSet, VertexId};
|
|
11
13
|
use crate::types::Type;
|
|
@@ -24,7 +26,8 @@ pub(crate) fn process_class_node(
|
|
|
24
26
|
class_node: &ruby_prism::ClassNode,
|
|
25
27
|
) -> Option<VertexId> {
|
|
26
28
|
let class_name = extract_class_name(class_node);
|
|
27
|
-
|
|
29
|
+
let superclass = class_node.superclass().and_then(|sup| extract_constant_path(&sup));
|
|
30
|
+
install_class(genv, class_name, superclass.as_deref());
|
|
28
31
|
|
|
29
32
|
if let Some(body) = class_node.body() {
|
|
30
33
|
if let Some(statements) = body.as_statements_node() {
|
|
@@ -78,10 +81,10 @@ pub(crate) fn process_def_node(
|
|
|
78
81
|
let merge_vtx = genv.scope_manager.current_method_return_vertex();
|
|
79
82
|
|
|
80
83
|
// Process parameters BEFORE processing body
|
|
81
|
-
let param_vtxs = if let Some(params_node) = def_node.parameters() {
|
|
84
|
+
let (param_vtxs, keyword_param_vtxs) = if let Some(params_node) = def_node.parameters() {
|
|
82
85
|
install_parameters(genv, lenv, changes, source, ¶ms_node)
|
|
83
86
|
} else {
|
|
84
|
-
vec![]
|
|
87
|
+
(vec![], HashMap::new())
|
|
85
88
|
};
|
|
86
89
|
|
|
87
90
|
let mut last_vtx = None;
|
|
@@ -108,11 +111,13 @@ pub(crate) fn process_def_node(
|
|
|
108
111
|
} else {
|
|
109
112
|
Type::instance(&name)
|
|
110
113
|
};
|
|
114
|
+
let kw_params = (!keyword_param_vtxs.is_empty()).then_some(keyword_param_vtxs);
|
|
111
115
|
genv.register_user_method(
|
|
112
116
|
recv_type,
|
|
113
117
|
&method_name,
|
|
114
118
|
ret_vtx,
|
|
115
119
|
param_vtxs,
|
|
120
|
+
kw_params,
|
|
116
121
|
);
|
|
117
122
|
}
|
|
118
123
|
}
|
|
@@ -122,8 +127,8 @@ pub(crate) fn process_def_node(
|
|
|
122
127
|
}
|
|
123
128
|
|
|
124
129
|
/// Install class definition
|
|
125
|
-
fn install_class(genv: &mut GlobalEnv, class_name: String) {
|
|
126
|
-
genv.enter_class(class_name);
|
|
130
|
+
fn install_class(genv: &mut GlobalEnv, class_name: String, superclass: Option<&str>) {
|
|
131
|
+
genv.enter_class(class_name, superclass);
|
|
127
132
|
}
|
|
128
133
|
|
|
129
134
|
/// Install module definition
|
|
@@ -199,7 +204,7 @@ mod tests {
|
|
|
199
204
|
fn test_enter_exit_class_scope() {
|
|
200
205
|
let mut genv = GlobalEnv::new();
|
|
201
206
|
|
|
202
|
-
install_class(&mut genv, "User".to_string());
|
|
207
|
+
install_class(&mut genv, "User".to_string(), None);
|
|
203
208
|
assert_eq!(
|
|
204
209
|
genv.scope_manager.current_class_name(),
|
|
205
210
|
Some("User".to_string())
|
|
@@ -227,7 +232,7 @@ mod tests {
|
|
|
227
232
|
fn test_nested_method_scope() {
|
|
228
233
|
let mut genv = GlobalEnv::new();
|
|
229
234
|
|
|
230
|
-
install_class(&mut genv, "User".to_string());
|
|
235
|
+
install_class(&mut genv, "User".to_string(), None);
|
|
231
236
|
install_method(&mut genv, "greet".to_string());
|
|
232
237
|
|
|
233
238
|
// Still in User class context
|