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
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
//! - Creating vertices for parameters
|
|
6
6
|
//! - Registering parameters as local variables in method scope
|
|
7
7
|
|
|
8
|
+
use std::collections::HashMap;
|
|
9
|
+
|
|
8
10
|
use crate::env::{GlobalEnv, LocalEnv};
|
|
9
11
|
use crate::graph::{ChangeSet, VertexId};
|
|
10
12
|
use crate::types::Type;
|
|
@@ -117,16 +119,18 @@ pub fn install_keyword_rest_parameter(
|
|
|
117
119
|
|
|
118
120
|
/// Install method parameters as local variables
|
|
119
121
|
///
|
|
120
|
-
/// Returns a
|
|
121
|
-
///
|
|
122
|
+
/// Returns a tuple of:
|
|
123
|
+
/// - Vec<VertexId>: positional parameter vertices (required and optional)
|
|
124
|
+
/// - HashMap<String, VertexId>: keyword parameter vertices (name → vertex)
|
|
122
125
|
pub(crate) fn install_parameters(
|
|
123
126
|
genv: &mut GlobalEnv,
|
|
124
127
|
lenv: &mut LocalEnv,
|
|
125
128
|
changes: &mut ChangeSet,
|
|
126
129
|
source: &str,
|
|
127
130
|
params_node: &ruby_prism::ParametersNode,
|
|
128
|
-
) -> Vec<VertexId> {
|
|
131
|
+
) -> (Vec<VertexId>, HashMap<String, VertexId>) {
|
|
129
132
|
let mut param_vtxs = Vec::new();
|
|
133
|
+
let mut keyword_param_vtxs: HashMap<String, VertexId> = HashMap::new();
|
|
130
134
|
|
|
131
135
|
// Required parameters: def foo(a, b)
|
|
132
136
|
for node in params_node.requireds().iter() {
|
|
@@ -165,8 +169,30 @@ pub(crate) fn install_parameters(
|
|
|
165
169
|
}
|
|
166
170
|
}
|
|
167
171
|
|
|
172
|
+
// Keyword parameters: def foo(name:, age: 0)
|
|
173
|
+
// Reuses install_required_parameter / install_optional_parameter
|
|
174
|
+
// since the logic is identical for positional and keyword parameters.
|
|
175
|
+
for node in params_node.keywords().iter() {
|
|
176
|
+
if let Some(req_kw) = node.as_required_keyword_parameter_node() {
|
|
177
|
+
let name = bytes_to_name(req_kw.name().as_slice());
|
|
178
|
+
let vtx = install_required_parameter(genv, lenv, name.clone());
|
|
179
|
+
keyword_param_vtxs.insert(name, vtx);
|
|
180
|
+
} else if let Some(opt_kw) = node.as_optional_keyword_parameter_node() {
|
|
181
|
+
let name = bytes_to_name(opt_kw.name().as_slice());
|
|
182
|
+
let default_value = opt_kw.value();
|
|
183
|
+
let vtx = if let Some(default_vtx) =
|
|
184
|
+
super::install::install_node(genv, lenv, changes, source, &default_value)
|
|
185
|
+
{
|
|
186
|
+
install_optional_parameter(genv, lenv, changes, name.clone(), default_vtx)
|
|
187
|
+
} else {
|
|
188
|
+
install_required_parameter(genv, lenv, name.clone())
|
|
189
|
+
};
|
|
190
|
+
keyword_param_vtxs.insert(name, vtx);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
168
194
|
// Keyword rest parameter: def foo(**kwargs)
|
|
169
|
-
// Not included in
|
|
195
|
+
// Not included in keyword_param_vtxs (collects all remaining keywords)
|
|
170
196
|
if let Some(kwrest_node) = params_node.keyword_rest() {
|
|
171
197
|
if let Some(kwrest_param) = kwrest_node.as_keyword_rest_parameter_node() {
|
|
172
198
|
if let Some(name_id) = kwrest_param.name() {
|
|
@@ -176,12 +202,42 @@ pub(crate) fn install_parameters(
|
|
|
176
202
|
}
|
|
177
203
|
}
|
|
178
204
|
|
|
179
|
-
param_vtxs
|
|
205
|
+
(param_vtxs, keyword_param_vtxs)
|
|
180
206
|
}
|
|
181
207
|
|
|
182
208
|
#[cfg(test)]
|
|
183
209
|
mod tests {
|
|
184
210
|
use super::*;
|
|
211
|
+
use crate::analyzer::install::AstInstaller;
|
|
212
|
+
use crate::parser::ParseSession;
|
|
213
|
+
|
|
214
|
+
fn analyze(source: &str) -> GlobalEnv {
|
|
215
|
+
let session = ParseSession::new();
|
|
216
|
+
let parse_result = session.parse_source(source, "test.rb").unwrap();
|
|
217
|
+
let root = parse_result.node();
|
|
218
|
+
let program = root.as_program_node().unwrap();
|
|
219
|
+
|
|
220
|
+
let mut genv = GlobalEnv::new();
|
|
221
|
+
let mut lenv = LocalEnv::new();
|
|
222
|
+
|
|
223
|
+
let mut installer = AstInstaller::new(&mut genv, &mut lenv, source);
|
|
224
|
+
for stmt in &program.statements().body() {
|
|
225
|
+
installer.install_node(&stmt);
|
|
226
|
+
}
|
|
227
|
+
installer.finish();
|
|
228
|
+
|
|
229
|
+
genv
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
fn get_type_show(genv: &GlobalEnv, vtx: VertexId) -> String {
|
|
233
|
+
if let Some(vertex) = genv.get_vertex(vtx) {
|
|
234
|
+
vertex.show()
|
|
235
|
+
} else if let Some(source) = genv.get_source(vtx) {
|
|
236
|
+
source.ty.show()
|
|
237
|
+
} else {
|
|
238
|
+
panic!("vertex {:?} not found as either Vertex or Source", vtx);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
185
241
|
|
|
186
242
|
#[test]
|
|
187
243
|
fn test_install_required_parameter() {
|
|
@@ -217,4 +273,157 @@ mod tests {
|
|
|
217
273
|
assert_ne!(vtx_b, vtx_c);
|
|
218
274
|
assert_ne!(vtx_a, vtx_c);
|
|
219
275
|
}
|
|
276
|
+
|
|
277
|
+
#[test]
|
|
278
|
+
fn test_install_optional_parameter_inherits_default_type() {
|
|
279
|
+
let mut genv = GlobalEnv::new();
|
|
280
|
+
let mut lenv = LocalEnv::new();
|
|
281
|
+
let mut changes = ChangeSet::new();
|
|
282
|
+
|
|
283
|
+
// Default value: 0 (Integer)
|
|
284
|
+
let default_vtx = genv.new_source(Type::integer());
|
|
285
|
+
let vtx = install_optional_parameter(
|
|
286
|
+
&mut genv,
|
|
287
|
+
&mut lenv,
|
|
288
|
+
&mut changes,
|
|
289
|
+
"age".to_string(),
|
|
290
|
+
default_vtx,
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
assert_eq!(lenv.get_var("age"), Some(vtx));
|
|
294
|
+
|
|
295
|
+
// Type should propagate from default value
|
|
296
|
+
let vertex = genv.get_vertex(vtx).unwrap();
|
|
297
|
+
assert_eq!(vertex.show(), "Integer");
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
#[test]
|
|
301
|
+
fn test_required_parameter_type_propagation() {
|
|
302
|
+
let source = r#"
|
|
303
|
+
class Foo
|
|
304
|
+
def greet(name)
|
|
305
|
+
name
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
Foo.new.greet("Alice")
|
|
310
|
+
"#;
|
|
311
|
+
let genv = analyze(source);
|
|
312
|
+
let info = genv.resolve_method(&Type::instance("Foo"), "greet").unwrap();
|
|
313
|
+
let ret_vtx = info.return_vertex.unwrap();
|
|
314
|
+
assert_eq!(get_type_show(&genv, ret_vtx), "String");
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
#[test]
|
|
318
|
+
fn test_optional_parameter_default_type() {
|
|
319
|
+
let source = r#"
|
|
320
|
+
class Foo
|
|
321
|
+
def greet(name = "World")
|
|
322
|
+
name
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
"#;
|
|
326
|
+
let genv = analyze(source);
|
|
327
|
+
let info = genv.resolve_method(&Type::instance("Foo"), "greet").unwrap();
|
|
328
|
+
let ret_vtx = info.return_vertex.unwrap();
|
|
329
|
+
assert_eq!(get_type_show(&genv, ret_vtx), "String");
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
#[test]
|
|
333
|
+
fn test_multiple_parameters_from_call_site() {
|
|
334
|
+
let source = r#"
|
|
335
|
+
class Calc
|
|
336
|
+
def add(x, y)
|
|
337
|
+
x
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
Calc.new.add(1, 2)
|
|
342
|
+
"#;
|
|
343
|
+
let genv = analyze(source);
|
|
344
|
+
let info = genv.resolve_method(&Type::instance("Calc"), "add").unwrap();
|
|
345
|
+
let param_vtxs = info.param_vertices.as_ref().unwrap();
|
|
346
|
+
assert_eq!(param_vtxs.len(), 2);
|
|
347
|
+
// Verify return type is Integer (method returns x, which receives 1)
|
|
348
|
+
let ret_vtx = info.return_vertex.unwrap();
|
|
349
|
+
assert_eq!(get_type_show(&genv, ret_vtx), "Integer");
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
#[test]
|
|
353
|
+
fn test_keyword_parameter_propagation() {
|
|
354
|
+
let source = r#"
|
|
355
|
+
class Foo
|
|
356
|
+
def greet(name:)
|
|
357
|
+
name
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
Foo.new.greet(name: "Alice")
|
|
362
|
+
"#;
|
|
363
|
+
let genv = analyze(source);
|
|
364
|
+
let info = genv.resolve_method(&Type::instance("Foo"), "greet").unwrap();
|
|
365
|
+
let ret_vtx = info.return_vertex.unwrap();
|
|
366
|
+
assert_eq!(get_type_show(&genv, ret_vtx), "String");
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
#[test]
|
|
370
|
+
fn test_optional_keyword_parameter_default() {
|
|
371
|
+
let source = r#"
|
|
372
|
+
class Counter
|
|
373
|
+
def count(step: 1)
|
|
374
|
+
step
|
|
375
|
+
end
|
|
376
|
+
end
|
|
377
|
+
"#;
|
|
378
|
+
let genv = analyze(source);
|
|
379
|
+
let info = genv.resolve_method(&Type::instance("Counter"), "count").unwrap();
|
|
380
|
+
let ret_vtx = info.return_vertex.unwrap();
|
|
381
|
+
assert_eq!(get_type_show(&genv, ret_vtx), "Integer");
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
#[test]
|
|
385
|
+
fn test_mixed_positional_and_keyword_params() {
|
|
386
|
+
let source = r#"
|
|
387
|
+
class User
|
|
388
|
+
def initialize(id, name:)
|
|
389
|
+
@id = id
|
|
390
|
+
@name = name
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
User.new(1, name: "Alice")
|
|
395
|
+
"#;
|
|
396
|
+
let genv = analyze(source);
|
|
397
|
+
assert!(genv.type_errors.is_empty());
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
#[test]
|
|
401
|
+
fn test_rest_parameter() {
|
|
402
|
+
let source = r#"
|
|
403
|
+
class Foo
|
|
404
|
+
def bar(*args)
|
|
405
|
+
args
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
"#;
|
|
409
|
+
let genv = analyze(source);
|
|
410
|
+
let info = genv.resolve_method(&Type::instance("Foo"), "bar").unwrap();
|
|
411
|
+
let ret_vtx = info.return_vertex.unwrap();
|
|
412
|
+
assert_eq!(get_type_show(&genv, ret_vtx), "Array");
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
#[test]
|
|
416
|
+
fn test_no_parameters() {
|
|
417
|
+
let source = r#"
|
|
418
|
+
class Foo
|
|
419
|
+
def bar
|
|
420
|
+
"hello"
|
|
421
|
+
end
|
|
422
|
+
end
|
|
423
|
+
"#;
|
|
424
|
+
let genv = analyze(source);
|
|
425
|
+
let info = genv.resolve_method(&Type::instance("Foo"), "bar").unwrap();
|
|
426
|
+
let param_vtxs = info.param_vertices.as_ref().unwrap();
|
|
427
|
+
assert!(param_vtxs.is_empty());
|
|
428
|
+
}
|
|
220
429
|
}
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
//! Super call handling: `super` and `super(args)`
|
|
2
|
+
//!
|
|
3
|
+
//! Ruby's `super` calls the same-named method on the parent class.
|
|
4
|
+
//! - `super(args)` → SuperNode: explicit arguments
|
|
5
|
+
//! - `super` (bare) → ForwardingSuperNode: implicit argument forwarding
|
|
6
|
+
//!
|
|
7
|
+
//! Note: ForwardingSuperNode (bare `super`) is treated as a zero-argument
|
|
8
|
+
//! call. In Ruby, bare `super` forwards all arguments from the enclosing
|
|
9
|
+
//! method, but replicating this requires parameter-vertex forwarding that
|
|
10
|
+
//! is not yet implemented. Return type inference is unaffected.
|
|
11
|
+
|
|
12
|
+
use ruby_prism::{ForwardingSuperNode, SuperNode};
|
|
13
|
+
|
|
14
|
+
use crate::env::{GlobalEnv, LocalEnv};
|
|
15
|
+
use crate::graph::{ChangeSet, VertexId};
|
|
16
|
+
use crate::source_map::SourceLocation as SL;
|
|
17
|
+
use crate::types::Type;
|
|
18
|
+
|
|
19
|
+
/// Process SuperNode: `super(args)` — explicit arguments
|
|
20
|
+
pub(crate) fn process_super_node(
|
|
21
|
+
genv: &mut GlobalEnv,
|
|
22
|
+
lenv: &mut LocalEnv,
|
|
23
|
+
changes: &mut ChangeSet,
|
|
24
|
+
source: &str,
|
|
25
|
+
super_node: &SuperNode,
|
|
26
|
+
) -> Option<VertexId> {
|
|
27
|
+
let location = SL::from_prism_location_with_source(&super_node.location(), source);
|
|
28
|
+
process_super_call(genv, lenv, changes, source, super_node.arguments(), location)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/// Process ForwardingSuperNode: `super` — implicit argument forwarding
|
|
32
|
+
pub(crate) fn process_forwarding_super_node(
|
|
33
|
+
genv: &mut GlobalEnv,
|
|
34
|
+
lenv: &mut LocalEnv,
|
|
35
|
+
changes: &mut ChangeSet,
|
|
36
|
+
source: &str,
|
|
37
|
+
node: &ForwardingSuperNode,
|
|
38
|
+
) -> Option<VertexId> {
|
|
39
|
+
let location = SL::from_prism_location_with_source(&node.location(), source);
|
|
40
|
+
process_super_call(genv, lenv, changes, source, None, location)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/// Resolve a super call by looking up the same-named method on the superclass.
|
|
44
|
+
///
|
|
45
|
+
/// Returns `None` if there is no enclosing method scope (super outside a method)
|
|
46
|
+
/// or no explicit superclass declared on the enclosing class.
|
|
47
|
+
fn process_super_call(
|
|
48
|
+
genv: &mut GlobalEnv,
|
|
49
|
+
lenv: &mut LocalEnv,
|
|
50
|
+
changes: &mut ChangeSet,
|
|
51
|
+
source: &str,
|
|
52
|
+
arguments: Option<ruby_prism::ArgumentsNode>,
|
|
53
|
+
location: SL,
|
|
54
|
+
) -> Option<VertexId> {
|
|
55
|
+
let method_name = genv.scope_manager.current_method_name()?;
|
|
56
|
+
let superclass_name = genv.scope_manager.current_superclass()?;
|
|
57
|
+
let recv_vtx = genv.new_source(Type::instance(&superclass_name));
|
|
58
|
+
|
|
59
|
+
let (arg_vtxs, kw) = if let Some(args) = arguments {
|
|
60
|
+
super::dispatch::collect_arguments(genv, lenv, changes, source, args.arguments().iter())
|
|
61
|
+
} else {
|
|
62
|
+
(vec![], None)
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
Some(super::calls::install_method_call(
|
|
66
|
+
genv,
|
|
67
|
+
recv_vtx,
|
|
68
|
+
method_name,
|
|
69
|
+
arg_vtxs,
|
|
70
|
+
kw,
|
|
71
|
+
Some(location),
|
|
72
|
+
))
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
#[cfg(test)]
|
|
76
|
+
mod tests {
|
|
77
|
+
use crate::analyzer::install::AstInstaller;
|
|
78
|
+
use crate::env::{GlobalEnv, LocalEnv};
|
|
79
|
+
use crate::graph::VertexId;
|
|
80
|
+
use crate::parser::ParseSession;
|
|
81
|
+
use crate::types::Type;
|
|
82
|
+
|
|
83
|
+
/// Helper: parse Ruby source, process with AstInstaller, and return GlobalEnv
|
|
84
|
+
fn analyze(source: &str) -> GlobalEnv {
|
|
85
|
+
let session = ParseSession::new();
|
|
86
|
+
let parse_result = session.parse_source(source, "test.rb").unwrap();
|
|
87
|
+
let root = parse_result.node();
|
|
88
|
+
let program = root.as_program_node().unwrap();
|
|
89
|
+
|
|
90
|
+
let mut genv = GlobalEnv::new();
|
|
91
|
+
let mut lenv = LocalEnv::new();
|
|
92
|
+
|
|
93
|
+
let mut installer = AstInstaller::new(&mut genv, &mut lenv, source);
|
|
94
|
+
for stmt in &program.statements().body() {
|
|
95
|
+
installer.install_node(&stmt);
|
|
96
|
+
}
|
|
97
|
+
installer.finish();
|
|
98
|
+
|
|
99
|
+
genv
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/// Helper: get the type string for a vertex ID
|
|
103
|
+
fn get_type_show(genv: &GlobalEnv, vtx: VertexId) -> String {
|
|
104
|
+
if let Some(vertex) = genv.get_vertex(vtx) {
|
|
105
|
+
vertex.show()
|
|
106
|
+
} else if let Some(source) = genv.get_source(vtx) {
|
|
107
|
+
source.ty.show()
|
|
108
|
+
} else {
|
|
109
|
+
panic!("vertex {:?} not found as either Vertex or Source", vtx);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
#[test]
|
|
114
|
+
fn test_super_basic() {
|
|
115
|
+
let source = r#"
|
|
116
|
+
class Animal
|
|
117
|
+
def speak
|
|
118
|
+
"..."
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
class Dog < Animal
|
|
123
|
+
def speak
|
|
124
|
+
super
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
"#;
|
|
128
|
+
let genv = analyze(source);
|
|
129
|
+
let info = genv
|
|
130
|
+
.resolve_method(&Type::instance("Dog"), "speak")
|
|
131
|
+
.expect("Dog#speak should be registered");
|
|
132
|
+
let ret_vtx = info.return_vertex.unwrap();
|
|
133
|
+
assert_eq!(get_type_show(&genv, ret_vtx), "String");
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
#[test]
|
|
137
|
+
fn test_super_with_method_chain() {
|
|
138
|
+
let source = r#"
|
|
139
|
+
class Animal
|
|
140
|
+
def speak
|
|
141
|
+
"hello"
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
class Dog < Animal
|
|
146
|
+
def speak
|
|
147
|
+
super.upcase
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
"#;
|
|
151
|
+
let genv = analyze(source);
|
|
152
|
+
let info = genv
|
|
153
|
+
.resolve_method(&Type::instance("Dog"), "speak")
|
|
154
|
+
.expect("Dog#speak should be registered");
|
|
155
|
+
assert!(info.return_vertex.is_some());
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
#[test]
|
|
159
|
+
fn test_super_with_arguments() {
|
|
160
|
+
let source = r#"
|
|
161
|
+
class Base
|
|
162
|
+
def greet(name)
|
|
163
|
+
name
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
class Child < Base
|
|
168
|
+
def greet(name)
|
|
169
|
+
super(name)
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
Child.new.greet("Alice")
|
|
174
|
+
"#;
|
|
175
|
+
let genv = analyze(source);
|
|
176
|
+
let info = genv
|
|
177
|
+
.resolve_method(&Type::instance("Child"), "greet")
|
|
178
|
+
.expect("Child#greet should be registered");
|
|
179
|
+
let ret_vtx = info.return_vertex.unwrap();
|
|
180
|
+
assert_eq!(get_type_show(&genv, ret_vtx), "String");
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
#[test]
|
|
184
|
+
fn test_super_outside_method_ignored() {
|
|
185
|
+
let source = r#"
|
|
186
|
+
class Foo < Object
|
|
187
|
+
super
|
|
188
|
+
end
|
|
189
|
+
"#;
|
|
190
|
+
analyze(source);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
#[test]
|
|
194
|
+
fn test_super_explicit_empty_args() {
|
|
195
|
+
let source = r#"
|
|
196
|
+
class Animal
|
|
197
|
+
def speak
|
|
198
|
+
"hello"
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
class Dog < Animal
|
|
203
|
+
def speak
|
|
204
|
+
super()
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
"#;
|
|
208
|
+
let genv = analyze(source);
|
|
209
|
+
let info = genv
|
|
210
|
+
.resolve_method(&Type::instance("Dog"), "speak")
|
|
211
|
+
.expect("Dog#speak should be registered");
|
|
212
|
+
let ret_vtx = info.return_vertex.unwrap();
|
|
213
|
+
assert_eq!(get_type_show(&genv, ret_vtx), "String");
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
#[test]
|
|
217
|
+
fn test_super_without_superclass_ignored() {
|
|
218
|
+
let source = r#"
|
|
219
|
+
class Foo
|
|
220
|
+
def bar
|
|
221
|
+
super
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
"#;
|
|
225
|
+
let genv = analyze(source);
|
|
226
|
+
let info = genv
|
|
227
|
+
.resolve_method(&Type::instance("Foo"), "bar")
|
|
228
|
+
.expect("Foo#bar should be registered");
|
|
229
|
+
assert!(info.return_vertex.is_some());
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
#[test]
|
|
233
|
+
fn test_super_qualified_superclass() {
|
|
234
|
+
let source = r#"
|
|
235
|
+
module Animals
|
|
236
|
+
class Pet
|
|
237
|
+
def name
|
|
238
|
+
"pet"
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
class Dog < Animals::Pet
|
|
244
|
+
def name
|
|
245
|
+
super
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
"#;
|
|
249
|
+
let genv = analyze(source);
|
|
250
|
+
let info = genv
|
|
251
|
+
.resolve_method(&Type::instance("Dog"), "name")
|
|
252
|
+
.expect("Dog#name should be registered");
|
|
253
|
+
let ret_vtx = info.return_vertex.unwrap();
|
|
254
|
+
assert_eq!(get_type_show(&genv, ret_vtx), "String");
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
#[test]
|
|
258
|
+
fn test_super_multi_level_inheritance() {
|
|
259
|
+
let source = r#"
|
|
260
|
+
class A
|
|
261
|
+
def foo
|
|
262
|
+
"hello"
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
class B < A
|
|
267
|
+
def foo
|
|
268
|
+
super
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
class C < B
|
|
273
|
+
def foo
|
|
274
|
+
super
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
"#;
|
|
278
|
+
let genv = analyze(source);
|
|
279
|
+
let info = genv
|
|
280
|
+
.resolve_method(&Type::instance("C"), "foo")
|
|
281
|
+
.expect("C#foo should be registered");
|
|
282
|
+
let ret_vtx = info.return_vertex.unwrap();
|
|
283
|
+
assert_eq!(get_type_show(&genv, ret_vtx), "String");
|
|
284
|
+
}
|
|
285
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
//! CLI command implementations
|
|
2
2
|
|
|
3
3
|
use anyhow::Result;
|
|
4
|
-
use std::path::
|
|
4
|
+
use std::path::Path;
|
|
5
5
|
|
|
6
6
|
use crate::cache::RbsCache;
|
|
7
7
|
use crate::checker::FileChecker;
|
|
@@ -9,7 +9,7 @@ use crate::diagnostics;
|
|
|
9
9
|
|
|
10
10
|
/// Check a single Ruby file for type errors
|
|
11
11
|
/// Returns Ok(true) if no errors, Ok(false) if errors found
|
|
12
|
-
pub fn check_single_file(file_path: &
|
|
12
|
+
pub fn check_single_file(file_path: &Path, verbose: bool) -> Result<bool> {
|
|
13
13
|
let checker = FileChecker::new()?;
|
|
14
14
|
let diagnostics = checker.check_file(file_path)?;
|
|
15
15
|
|
|
@@ -38,7 +38,7 @@ pub fn check_project(_verbose: bool) -> Result<()> {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/// Watch a file for changes and re-check on modifications
|
|
41
|
-
pub fn watch_file(file_path: &
|
|
41
|
+
pub fn watch_file(file_path: &Path) -> Result<()> {
|
|
42
42
|
use notify::{Config, RecommendedWatcher, RecursiveMode, Watcher};
|
|
43
43
|
use std::sync::mpsc::channel;
|
|
44
44
|
use std::time::Duration;
|
|
@@ -2,7 +2,6 @@ use std::path::PathBuf;
|
|
|
2
2
|
|
|
3
3
|
/// Diagnostic severity level (LSP compatible)
|
|
4
4
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
5
|
-
#[allow(dead_code)]
|
|
6
5
|
pub enum DiagnosticLevel {
|
|
7
6
|
Error,
|
|
8
7
|
Warning,
|
|
@@ -28,7 +27,6 @@ pub struct Location {
|
|
|
28
27
|
|
|
29
28
|
/// Type checking diagnostic
|
|
30
29
|
#[derive(Debug, Clone)]
|
|
31
|
-
#[allow(dead_code)]
|
|
32
30
|
pub struct Diagnostic {
|
|
33
31
|
pub location: Location,
|
|
34
32
|
pub level: DiagnosticLevel,
|
|
@@ -36,7 +34,6 @@ pub struct Diagnostic {
|
|
|
36
34
|
pub code: Option<String>, // e.g., "E001"
|
|
37
35
|
}
|
|
38
36
|
|
|
39
|
-
#[allow(dead_code)]
|
|
40
37
|
impl Diagnostic {
|
|
41
38
|
/// Create an error diagnostic
|
|
42
39
|
pub fn error(location: Location, message: String) -> Self {
|
|
@@ -6,7 +6,6 @@ use crate::graph::{BoxId, BoxTrait};
|
|
|
6
6
|
use std::collections::{HashMap, HashSet, VecDeque};
|
|
7
7
|
|
|
8
8
|
/// Manages boxes and their execution queue
|
|
9
|
-
#[allow(dead_code)]
|
|
10
9
|
pub struct BoxManager {
|
|
11
10
|
/// All registered boxes
|
|
12
11
|
pub boxes: HashMap<BoxId, Box<dyn BoxTrait>>,
|
|
@@ -24,7 +23,6 @@ impl Default for BoxManager {
|
|
|
24
23
|
}
|
|
25
24
|
}
|
|
26
25
|
|
|
27
|
-
#[allow(dead_code)]
|
|
28
26
|
impl BoxManager {
|
|
29
27
|
/// Create a new empty box manager
|
|
30
28
|
pub fn new() -> Self {
|
|
@@ -37,8 +35,8 @@ impl BoxManager {
|
|
|
37
35
|
}
|
|
38
36
|
|
|
39
37
|
/// Get a box by ID
|
|
40
|
-
pub fn get(&self, id: BoxId) -> Option<&
|
|
41
|
-
self.boxes.get(&id)
|
|
38
|
+
pub fn get(&self, id: BoxId) -> Option<&dyn BoxTrait> {
|
|
39
|
+
self.boxes.get(&id).map(|b| b.as_ref())
|
|
42
40
|
}
|
|
43
41
|
|
|
44
42
|
/// Remove a box and return it (for temporary mutation)
|