method-ray 0.1.9 → 0.1.10
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 +20 -0
- data/README.md +9 -11
- data/core/Cargo.toml +1 -1
- data/core/src/analyzer/assignments.rs +0 -280
- data/core/src/analyzer/blocks.rs +0 -190
- data/core/src/analyzer/calls.rs +3 -32
- data/core/src/analyzer/conditionals.rs +0 -348
- data/core/src/analyzer/definitions.rs +11 -526
- data/core/src/analyzer/dispatch.rs +54 -1000
- data/core/src/analyzer/exceptions.rs +0 -454
- data/core/src/analyzer/literals.rs +0 -54
- data/core/src/analyzer/loops.rs +0 -207
- data/core/src/analyzer/mod.rs +0 -15
- data/core/src/analyzer/operators.rs +0 -205
- data/core/src/analyzer/parameters.rs +4 -227
- data/core/src/analyzer/parentheses.rs +0 -88
- data/core/src/analyzer/returns.rs +0 -152
- data/core/src/analyzer/super_calls.rs +1 -212
- data/core/src/analyzer/variables.rs +5 -25
- data/core/src/checker.rs +0 -13
- data/core/src/diagnostics/diagnostic.rs +0 -41
- data/core/src/diagnostics/formatter.rs +0 -38
- data/core/src/env/box_manager.rs +0 -30
- data/core/src/env/global_env.rs +52 -79
- data/core/src/env/local_env.rs +0 -50
- data/core/src/env/method_registry.rs +52 -233
- data/core/src/env/scope.rs +0 -375
- data/core/src/env/vertex_manager.rs +0 -73
- data/core/src/graph/box.rs +20 -439
- data/core/src/graph/change_set.rs +0 -65
- data/core/src/graph/vertex.rs +0 -69
- data/core/src/parser.rs +0 -77
- data/ext/Cargo.toml +1 -1
- data/lib/methodray/version.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 228f256ea9848bd9b2447e097aee95f59f47db2bd252611a7dfa30363be34a23
|
|
4
|
+
data.tar.gz: 0ff2217ee68021004331bd580f26739ce067f223ea113b741ab0636f2771e30e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3c274c68e498eed4f432b60c4a68a6ca469b7850db539954f584d89bd96cbf3612e03a9a9f43a1b8a8941be6302142e7f34e886b279cde41bc22a0376ba48ac7
|
|
7
|
+
data.tar.gz: 57e66add8decf8c951f890f140f76fca86b642af91a7df9d7622c5144414b20f158444838aeca5082a46efa185280747a32117980654ae5f03d0c1dc23bbcd35
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.1.10] - 2026-03-24
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Add safe navigation operator (`&.`) support ([#65](https://github.com/dak2/method-ray/pull/65))
|
|
13
|
+
- Add inheritance chain method resolution for user-defined classes ([#66](https://github.com/dak2/method-ray/pull/66))
|
|
14
|
+
- Add extend support for module methods as class methods ([#67](https://github.com/dak2/method-ray/pull/67))
|
|
15
|
+
- Add pull request template ([#82](https://github.com/dak2/method-ray/pull/82))
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- Migrate Rust integration tests to Ruby integration tests ([#68](https://github.com/dak2/method-ray/pull/68), [#69](https://github.com/dak2/method-ray/pull/69), [#70](https://github.com/dak2/method-ray/pull/70), [#71](https://github.com/dak2/method-ray/pull/71), [#72](https://github.com/dak2/method-ray/pull/72), [#73](https://github.com/dak2/method-ray/pull/73), [#74](https://github.com/dak2/method-ray/pull/74), [#75](https://github.com/dak2/method-ray/pull/75))
|
|
20
|
+
- Remove redundant Rust unit tests ([#77](https://github.com/dak2/method-ray/pull/77), [#78](https://github.com/dak2/method-ray/pull/78), [#80](https://github.com/dak2/method-ray/pull/80), [#83](https://github.com/dak2/method-ray/pull/83), [#84](https://github.com/dak2/method-ray/pull/84), [#85](https://github.com/dak2/method-ray/pull/85), [#86](https://github.com/dak2/method-ray/pull/86))
|
|
21
|
+
- Simplify README to focus on core value proposition ([#76](https://github.com/dak2/method-ray/pull/76))
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- Fix release workflow to include gem assets in GitHub Release ([#63](https://github.com/dak2/method-ray/pull/63), [#64](https://github.com/dak2/method-ray/pull/64))
|
|
26
|
+
|
|
8
27
|
## [0.1.9] - 2026-03-15
|
|
9
28
|
|
|
10
29
|
### Added
|
|
@@ -140,6 +159,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
140
159
|
- Initial release
|
|
141
160
|
- `methodray check` - Static type checking for Ruby files
|
|
142
161
|
|
|
162
|
+
[0.1.10]: https://github.com/dak2/method-ray/releases/tag/v0.1.10
|
|
143
163
|
[0.1.9]: https://github.com/dak2/method-ray/releases/tag/v0.1.9
|
|
144
164
|
[0.1.8]: https://github.com/dak2/method-ray/releases/tag/v0.1.8
|
|
145
165
|
[0.1.7]: https://github.com/dak2/method-ray/releases/tag/v0.1.7
|
data/README.md
CHANGED
|
@@ -16,28 +16,26 @@ gem install methodray
|
|
|
16
16
|
|
|
17
17
|
## Quick Start
|
|
18
18
|
|
|
19
|
-
###
|
|
20
|
-
|
|
21
|
-
1. Install the [Method-Ray VSCode extension](https://github.com/dak2/method-ray-vscode)
|
|
22
|
-
2. Open a Ruby file in VSCode
|
|
23
|
-
3. Errors will be highlighted automatically
|
|
24
|
-
|
|
25
|
-
### CLI
|
|
19
|
+
### Checking Methods
|
|
26
20
|
|
|
27
21
|
```bash
|
|
28
22
|
# Check a single file
|
|
29
23
|
bundle exec methodray check app/models/user.rb
|
|
24
|
+
```
|
|
30
25
|
|
|
31
|
-
|
|
26
|
+
### Watching for File Changes, Re-checking Methods
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# Watch a file for changes and re-check on save
|
|
32
30
|
bundle exec methodray watch app/models/user.rb
|
|
33
31
|
```
|
|
34
32
|
|
|
35
|
-
#### Example
|
|
36
|
-
|
|
37
|
-
`methodray check <file>`: Performs static type checking on the specified Ruby file.
|
|
33
|
+
#### Example Usage
|
|
38
34
|
|
|
35
|
+
`bundle exec methodray check app/models/user.rb`
|
|
39
36
|
|
|
40
37
|
```ruby
|
|
38
|
+
# app/models/user.rb
|
|
41
39
|
class User
|
|
42
40
|
def greeting
|
|
43
41
|
name = "Alice"
|
data/core/Cargo.toml
CHANGED
|
@@ -217,283 +217,3 @@ pub(crate) fn process_multi_write_node(
|
|
|
217
217
|
|
|
218
218
|
last_vtx
|
|
219
219
|
}
|
|
220
|
-
|
|
221
|
-
#[cfg(test)]
|
|
222
|
-
mod tests {
|
|
223
|
-
use crate::analyzer::install::AstInstaller;
|
|
224
|
-
use crate::env::{GlobalEnv, LocalEnv};
|
|
225
|
-
use crate::graph::VertexId;
|
|
226
|
-
use crate::parser::ParseSession;
|
|
227
|
-
|
|
228
|
-
fn analyze(source: &str) -> (GlobalEnv, LocalEnv) {
|
|
229
|
-
let session = ParseSession::new();
|
|
230
|
-
let parse_result = session.parse_source(source, "test.rb").unwrap();
|
|
231
|
-
let root = parse_result.node();
|
|
232
|
-
let program = root.as_program_node().unwrap();
|
|
233
|
-
|
|
234
|
-
let mut genv = GlobalEnv::new();
|
|
235
|
-
let mut lenv = LocalEnv::new();
|
|
236
|
-
|
|
237
|
-
let mut installer = AstInstaller::new(&mut genv, &mut lenv, source);
|
|
238
|
-
for stmt in &program.statements().body() {
|
|
239
|
-
installer.install_node(&stmt);
|
|
240
|
-
}
|
|
241
|
-
installer.finish();
|
|
242
|
-
|
|
243
|
-
(genv, lenv)
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
fn get_type_show(genv: &GlobalEnv, vtx: VertexId) -> String {
|
|
247
|
-
if let Some(vertex) = genv.get_vertex(vtx) {
|
|
248
|
-
vertex.show()
|
|
249
|
-
} else if let Some(source) = genv.get_source(vtx) {
|
|
250
|
-
source.ty.show()
|
|
251
|
-
} else {
|
|
252
|
-
panic!("vertex {:?} not found as either Vertex or Source", vtx);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
#[test]
|
|
257
|
-
fn test_multi_write_integer_and_string() {
|
|
258
|
-
let source = r#"a, b = 1, "hello""#;
|
|
259
|
-
let (genv, lenv) = analyze(source);
|
|
260
|
-
|
|
261
|
-
let a_vtx = lenv.get_var("a").expect("a should be registered");
|
|
262
|
-
assert_eq!(get_type_show(&genv, a_vtx), "Integer");
|
|
263
|
-
|
|
264
|
-
let b_vtx = lenv.get_var("b").expect("b should be registered");
|
|
265
|
-
assert_eq!(get_type_show(&genv, b_vtx), "String");
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
#[test]
|
|
269
|
-
fn test_multi_write_all_integer() {
|
|
270
|
-
let source = "a, b, c = 1, 2, 3";
|
|
271
|
-
let (genv, lenv) = analyze(source);
|
|
272
|
-
|
|
273
|
-
let a_vtx = lenv.get_var("a").expect("a should be registered");
|
|
274
|
-
assert_eq!(get_type_show(&genv, a_vtx), "Integer");
|
|
275
|
-
|
|
276
|
-
let b_vtx = lenv.get_var("b").expect("b should be registered");
|
|
277
|
-
assert_eq!(get_type_show(&genv, b_vtx), "Integer");
|
|
278
|
-
|
|
279
|
-
let c_vtx = lenv.get_var("c").expect("c should be registered");
|
|
280
|
-
assert_eq!(get_type_show(&genv, c_vtx), "Integer");
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
#[test]
|
|
284
|
-
fn test_multi_write_variable_reference_after_assignment() {
|
|
285
|
-
let source = r#"
|
|
286
|
-
a, b = 1, "hello"
|
|
287
|
-
x = a
|
|
288
|
-
"#;
|
|
289
|
-
let (genv, lenv) = analyze(source);
|
|
290
|
-
|
|
291
|
-
let x_vtx = lenv.get_var("x").expect("x should be registered");
|
|
292
|
-
assert_eq!(get_type_show(&genv, x_vtx), "Integer");
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
#[test]
|
|
296
|
-
fn test_multi_write_lhs_longer_than_rhs() {
|
|
297
|
-
let source = "a, b, c = 1, 2";
|
|
298
|
-
let (genv, lenv) = analyze(source);
|
|
299
|
-
|
|
300
|
-
let a_vtx = lenv.get_var("a").expect("a should be registered");
|
|
301
|
-
assert_eq!(get_type_show(&genv, a_vtx), "Integer");
|
|
302
|
-
|
|
303
|
-
let b_vtx = lenv.get_var("b").expect("b should be registered");
|
|
304
|
-
assert_eq!(get_type_show(&genv, b_vtx), "Integer");
|
|
305
|
-
|
|
306
|
-
let c_vtx = lenv.get_var("c").expect("c should be registered with nil");
|
|
307
|
-
assert_eq!(get_type_show(&genv, c_vtx), "nil");
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
#[test]
|
|
311
|
-
fn test_multi_write_does_not_panic_on_non_array_rhs() {
|
|
312
|
-
let source = "a, b = some_expr";
|
|
313
|
-
let (_, lenv) = analyze(source);
|
|
314
|
-
|
|
315
|
-
// Variables should be registered (untyped) without panic
|
|
316
|
-
assert!(lenv.get_var("a").is_some(), "a should be registered");
|
|
317
|
-
assert!(lenv.get_var("b").is_some(), "b should be registered");
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
#[test]
|
|
321
|
-
fn test_multi_write_splat_basic() {
|
|
322
|
-
let source = "first, *rest = 1, 2, 3";
|
|
323
|
-
let (genv, lenv) = analyze(source);
|
|
324
|
-
|
|
325
|
-
let first_vtx = lenv.get_var("first").expect("first should be registered");
|
|
326
|
-
assert_eq!(get_type_show(&genv, first_vtx), "Integer");
|
|
327
|
-
|
|
328
|
-
let rest_vtx = lenv.get_var("rest").expect("rest should be registered");
|
|
329
|
-
assert_eq!(get_type_show(&genv, rest_vtx), "Array[Integer]");
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
#[test]
|
|
333
|
-
fn test_multi_write_splat_mixed_types() {
|
|
334
|
-
let source = r#"first, *rest = 1, "hello", :sym"#;
|
|
335
|
-
let (genv, lenv) = analyze(source);
|
|
336
|
-
|
|
337
|
-
let first_vtx = lenv.get_var("first").expect("first should be registered");
|
|
338
|
-
assert_eq!(get_type_show(&genv, first_vtx), "Integer");
|
|
339
|
-
|
|
340
|
-
let rest_vtx = lenv.get_var("rest").expect("rest should be registered");
|
|
341
|
-
let type_str = get_type_show(&genv, rest_vtx);
|
|
342
|
-
assert!(
|
|
343
|
-
type_str.contains("Array"),
|
|
344
|
-
"should be Array type: {}",
|
|
345
|
-
type_str
|
|
346
|
-
);
|
|
347
|
-
assert!(
|
|
348
|
-
type_str.contains("String"),
|
|
349
|
-
"should contain String: {}",
|
|
350
|
-
type_str
|
|
351
|
-
);
|
|
352
|
-
assert!(
|
|
353
|
-
type_str.contains("Symbol"),
|
|
354
|
-
"should contain Symbol: {}",
|
|
355
|
-
type_str
|
|
356
|
-
);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
#[test]
|
|
360
|
-
fn test_multi_write_splat_empty() {
|
|
361
|
-
let source = "first, *rest = 1";
|
|
362
|
-
let (genv, lenv) = analyze(source);
|
|
363
|
-
|
|
364
|
-
let first_vtx = lenv.get_var("first").expect("first should be registered");
|
|
365
|
-
assert_eq!(get_type_show(&genv, first_vtx), "Integer");
|
|
366
|
-
|
|
367
|
-
let rest_vtx = lenv.get_var("rest").expect("rest should be registered");
|
|
368
|
-
assert_eq!(get_type_show(&genv, rest_vtx), "Array[untyped]");
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
#[test]
|
|
372
|
-
fn test_multi_write_splat_with_rights() {
|
|
373
|
-
let source = "first, *rest, last = 1, 2, 3, 4";
|
|
374
|
-
let (genv, lenv) = analyze(source);
|
|
375
|
-
|
|
376
|
-
let first_vtx = lenv.get_var("first").expect("first should be registered");
|
|
377
|
-
assert_eq!(get_type_show(&genv, first_vtx), "Integer");
|
|
378
|
-
|
|
379
|
-
let rest_vtx = lenv.get_var("rest").expect("rest should be registered");
|
|
380
|
-
assert_eq!(get_type_show(&genv, rest_vtx), "Array[Integer]");
|
|
381
|
-
|
|
382
|
-
let last_vtx = lenv.get_var("last").expect("last should be registered");
|
|
383
|
-
assert_eq!(get_type_show(&genv, last_vtx), "Integer");
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
#[test]
|
|
387
|
-
fn test_multi_write_splat_only() {
|
|
388
|
-
let source = "*all = 1, 2, 3";
|
|
389
|
-
let (genv, lenv) = analyze(source);
|
|
390
|
-
|
|
391
|
-
let all_vtx = lenv.get_var("all").expect("all should be registered");
|
|
392
|
-
assert_eq!(get_type_show(&genv, all_vtx), "Array[Integer]");
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
#[test]
|
|
396
|
-
fn test_multi_write_splat_rights_no_lefts() {
|
|
397
|
-
let source = "*rest, last = 1, 2, 3";
|
|
398
|
-
let (genv, lenv) = analyze(source);
|
|
399
|
-
|
|
400
|
-
let rest_vtx = lenv.get_var("rest").expect("rest should be registered");
|
|
401
|
-
assert_eq!(get_type_show(&genv, rest_vtx), "Array[Integer]");
|
|
402
|
-
|
|
403
|
-
let last_vtx = lenv.get_var("last").expect("last should be registered");
|
|
404
|
-
assert_eq!(get_type_show(&genv, last_vtx), "Integer");
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
#[test]
|
|
408
|
-
fn test_multi_write_array_literal_rhs() {
|
|
409
|
-
// Explicit array literal RHS is decomposed element-by-element (same as comma-separated form)
|
|
410
|
-
let source = r#"a, b = [1, "hi"]"#;
|
|
411
|
-
let (genv, lenv) = analyze(source);
|
|
412
|
-
|
|
413
|
-
let a_vtx = lenv.get_var("a").expect("a should be registered");
|
|
414
|
-
assert_eq!(get_type_show(&genv, a_vtx), "Integer");
|
|
415
|
-
|
|
416
|
-
let b_vtx = lenv.get_var("b").expect("b should be registered");
|
|
417
|
-
assert_eq!(get_type_show(&genv, b_vtx), "String");
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
#[test]
|
|
421
|
-
fn test_multi_write_splat_lefts_exceed_rhs() {
|
|
422
|
-
// Edge case: more left targets than RHS elements with splat
|
|
423
|
-
let source = "a, b, c, *rest = 1, 2";
|
|
424
|
-
let (genv, lenv) = analyze(source);
|
|
425
|
-
|
|
426
|
-
let a_vtx = lenv.get_var("a").expect("a should be registered");
|
|
427
|
-
assert_eq!(get_type_show(&genv, a_vtx), "Integer");
|
|
428
|
-
|
|
429
|
-
let b_vtx = lenv.get_var("b").expect("b should be registered");
|
|
430
|
-
assert_eq!(get_type_show(&genv, b_vtx), "Integer");
|
|
431
|
-
|
|
432
|
-
let c_vtx = lenv.get_var("c").expect("c should be registered");
|
|
433
|
-
assert_eq!(get_type_show(&genv, c_vtx), "nil");
|
|
434
|
-
|
|
435
|
-
let rest_vtx = lenv.get_var("rest").expect("rest should be registered");
|
|
436
|
-
assert_eq!(get_type_show(&genv, rest_vtx), "Array[untyped]");
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
#[test]
|
|
440
|
-
fn test_multi_write_splat_with_rights_insufficient_rhs() {
|
|
441
|
-
// Edge case: lefts + rights > total elements, splat between them
|
|
442
|
-
let source = "a, *rest, z = 1";
|
|
443
|
-
let (genv, lenv) = analyze(source);
|
|
444
|
-
|
|
445
|
-
let a_vtx = lenv.get_var("a").expect("a should be registered");
|
|
446
|
-
assert_eq!(get_type_show(&genv, a_vtx), "Integer");
|
|
447
|
-
|
|
448
|
-
let rest_vtx = lenv.get_var("rest").expect("rest should be registered");
|
|
449
|
-
assert_eq!(get_type_show(&genv, rest_vtx), "Array[untyped]");
|
|
450
|
-
|
|
451
|
-
let z_vtx = lenv.get_var("z").expect("z should be registered");
|
|
452
|
-
assert_eq!(get_type_show(&genv, z_vtx), "nil");
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
#[test]
|
|
456
|
-
fn test_multi_write_rights_exceed_rhs() {
|
|
457
|
-
// Edge case: more right targets than available elements
|
|
458
|
-
let source = r#"*rest, x, y, z = "a", 1"#;
|
|
459
|
-
let (genv, lenv) = analyze(source);
|
|
460
|
-
|
|
461
|
-
let rest_vtx = lenv.get_var("rest").expect("rest should be registered");
|
|
462
|
-
assert_eq!(get_type_show(&genv, rest_vtx), "Array[untyped]");
|
|
463
|
-
|
|
464
|
-
let x_vtx = lenv.get_var("x").expect("x should be registered");
|
|
465
|
-
assert_eq!(get_type_show(&genv, x_vtx), "nil");
|
|
466
|
-
|
|
467
|
-
let y_vtx = lenv.get_var("y").expect("y should be registered");
|
|
468
|
-
assert_eq!(get_type_show(&genv, y_vtx), "String");
|
|
469
|
-
|
|
470
|
-
let z_vtx = lenv.get_var("z").expect("z should be registered");
|
|
471
|
-
assert_eq!(get_type_show(&genv, z_vtx), "Integer");
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
#[test]
|
|
475
|
-
fn test_multi_write_scalar_rhs() {
|
|
476
|
-
// Single non-array expression: first target gets value, rest get nil
|
|
477
|
-
let source = "a, b = 42";
|
|
478
|
-
let (genv, lenv) = analyze(source);
|
|
479
|
-
|
|
480
|
-
let a_vtx = lenv.get_var("a").expect("a should be registered");
|
|
481
|
-
assert_eq!(get_type_show(&genv, a_vtx), "Integer");
|
|
482
|
-
|
|
483
|
-
let b_vtx = lenv.get_var("b").expect("b should be registered");
|
|
484
|
-
assert_eq!(get_type_show(&genv, b_vtx), "nil");
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
#[test]
|
|
488
|
-
fn test_multi_write_rhs_longer_than_lhs() {
|
|
489
|
-
// Extra RHS elements are silently discarded
|
|
490
|
-
let source = "a, b = 1, 2, 3, 4";
|
|
491
|
-
let (genv, lenv) = analyze(source);
|
|
492
|
-
|
|
493
|
-
let a_vtx = lenv.get_var("a").expect("a should be registered");
|
|
494
|
-
assert_eq!(get_type_show(&genv, a_vtx), "Integer");
|
|
495
|
-
|
|
496
|
-
let b_vtx = lenv.get_var("b").expect("b should be registered");
|
|
497
|
-
assert_eq!(get_type_show(&genv, b_vtx), "Integer");
|
|
498
|
-
}
|
|
499
|
-
}
|
data/core/src/analyzer/blocks.rs
CHANGED
|
@@ -124,193 +124,3 @@ fn exit_block_scope(genv: &mut GlobalEnv) {
|
|
|
124
124
|
fn install_block_parameter(genv: &mut GlobalEnv, lenv: &mut LocalEnv, name: String) -> VertexId {
|
|
125
125
|
install_required_parameter(genv, lenv, name)
|
|
126
126
|
}
|
|
127
|
-
|
|
128
|
-
#[cfg(test)]
|
|
129
|
-
mod tests {
|
|
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
|
-
}
|
|
180
|
-
|
|
181
|
-
#[test]
|
|
182
|
-
fn test_enter_exit_block_scope() {
|
|
183
|
-
let mut genv = GlobalEnv::new();
|
|
184
|
-
|
|
185
|
-
let initial_scope_id = genv.scope_manager.current_scope().id;
|
|
186
|
-
|
|
187
|
-
enter_block_scope(&mut genv);
|
|
188
|
-
let block_scope_id = genv.scope_manager.current_scope().id;
|
|
189
|
-
|
|
190
|
-
assert_ne!(initial_scope_id, block_scope_id);
|
|
191
|
-
|
|
192
|
-
exit_block_scope(&mut genv);
|
|
193
|
-
|
|
194
|
-
assert_eq!(genv.scope_manager.current_scope().id, initial_scope_id);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
#[test]
|
|
198
|
-
fn test_install_block_parameter() {
|
|
199
|
-
let mut genv = GlobalEnv::new();
|
|
200
|
-
let mut lenv = LocalEnv::new();
|
|
201
|
-
|
|
202
|
-
enter_block_scope(&mut genv);
|
|
203
|
-
|
|
204
|
-
let vtx = install_block_parameter(&mut genv, &mut lenv, "x".to_string());
|
|
205
|
-
|
|
206
|
-
assert_eq!(lenv.get_var("x"), Some(vtx));
|
|
207
|
-
|
|
208
|
-
exit_block_scope(&mut genv);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
#[test]
|
|
212
|
-
fn test_block_inherits_parent_scope_vars() {
|
|
213
|
-
let mut genv = GlobalEnv::new();
|
|
214
|
-
|
|
215
|
-
genv.scope_manager
|
|
216
|
-
.current_scope_mut()
|
|
217
|
-
.set_local_var("outer".to_string(), VertexId(100));
|
|
218
|
-
|
|
219
|
-
enter_block_scope(&mut genv);
|
|
220
|
-
|
|
221
|
-
assert_eq!(genv.scope_manager.lookup_var("outer"), Some(VertexId(100)));
|
|
222
|
-
|
|
223
|
-
exit_block_scope(&mut genv);
|
|
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
|
-
}
|
|
316
|
-
}
|
data/core/src/analyzer/calls.rs
CHANGED
|
@@ -12,13 +12,14 @@ use crate::graph::{MethodCallBox, VertexId};
|
|
|
12
12
|
use crate::source_map::SourceLocation;
|
|
13
13
|
|
|
14
14
|
/// Install method call and return the return value's VertexId
|
|
15
|
-
pub fn install_method_call(
|
|
15
|
+
pub(crate) fn install_method_call(
|
|
16
16
|
genv: &mut GlobalEnv,
|
|
17
17
|
recv_vtx: VertexId,
|
|
18
18
|
method_name: String,
|
|
19
19
|
arg_vtxs: Vec<VertexId>,
|
|
20
20
|
kwarg_vtxs: Option<HashMap<String, VertexId>>,
|
|
21
21
|
location: Option<SourceLocation>,
|
|
22
|
+
safe_navigation: bool,
|
|
22
23
|
) -> VertexId {
|
|
23
24
|
// Create Vertex for return value
|
|
24
25
|
let ret_vtx = genv.new_vertex();
|
|
@@ -26,38 +27,8 @@ pub fn install_method_call(
|
|
|
26
27
|
// Create MethodCallBox with location and argument vertices
|
|
27
28
|
let box_id = genv.alloc_box_id();
|
|
28
29
|
let call_box =
|
|
29
|
-
MethodCallBox::new(box_id, recv_vtx, method_name, ret_vtx, arg_vtxs, kwarg_vtxs, location);
|
|
30
|
+
MethodCallBox::new(box_id, recv_vtx, method_name, ret_vtx, arg_vtxs, kwarg_vtxs, location, safe_navigation);
|
|
30
31
|
genv.register_box(box_id, Box::new(call_box));
|
|
31
32
|
|
|
32
33
|
ret_vtx
|
|
33
34
|
}
|
|
34
|
-
|
|
35
|
-
#[cfg(test)]
|
|
36
|
-
mod tests {
|
|
37
|
-
use super::*;
|
|
38
|
-
use crate::types::Type;
|
|
39
|
-
|
|
40
|
-
#[test]
|
|
41
|
-
fn test_install_method_call_creates_vertex() {
|
|
42
|
-
let mut genv = GlobalEnv::new();
|
|
43
|
-
|
|
44
|
-
let recv_vtx = genv.new_source(Type::string());
|
|
45
|
-
let ret_vtx =
|
|
46
|
-
install_method_call(&mut genv, recv_vtx, "upcase".to_string(), vec![], None, None);
|
|
47
|
-
|
|
48
|
-
// Return vertex should exist
|
|
49
|
-
assert!(genv.get_vertex(ret_vtx).is_some());
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
#[test]
|
|
53
|
-
fn test_install_method_call_adds_box() {
|
|
54
|
-
let mut genv = GlobalEnv::new();
|
|
55
|
-
|
|
56
|
-
let recv_vtx = genv.new_source(Type::string());
|
|
57
|
-
let _ret_vtx =
|
|
58
|
-
install_method_call(&mut genv, recv_vtx, "upcase".to_string(), vec![], None, None);
|
|
59
|
-
|
|
60
|
-
// Box should be added
|
|
61
|
-
assert_eq!(genv.box_count(), 1);
|
|
62
|
-
}
|
|
63
|
-
}
|