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
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1a219edf198245163ff36fbcc9e211dbfea2d5e5f41a467ee24636286967f4b9
|
|
4
|
+
data.tar.gz: 7a82426e9c5b0ba53a2d1ca8bab90da2a4b4eb58316af7cfb8a576dc6dc28b64
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cac68a8608631ce81f145b17743ff7ec519e24f1ac2b92c90732d3f9fedd4badb26715fd5120b8a648fd574313383ac2dbd02d71cdfe98004ffafe5714e39cf3
|
|
7
|
+
data.tar.gz: ded1386c832dfc9784c360d3685ce3431437d7cae1c155e02dc6606e3e155789f9f2ad42d5e4285aed6c8cb1dbf8231ac8f81392bf25a0536e3fd18e882537f3
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,45 @@ 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.4] - 2026-02-16
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Method return type inference for user-defined methods ([#18](https://github.com/dak2/method-ray/pull/18))
|
|
13
|
+
- Parameter type propagation from call-site arguments to method parameters ([#19](https://github.com/dak2/method-ray/pull/19))
|
|
14
|
+
- Receiver-less method call support (ImplicitSelfCall) ([#20](https://github.com/dak2/method-ray/pull/20))
|
|
15
|
+
- `attr_reader`/`attr_writer`/`attr_accessor` support for type inference ([#21](https://github.com/dak2/method-ray/pull/21))
|
|
16
|
+
- `if`/`unless`/`case` conditional type inference ([#22](https://github.com/dak2/method-ray/pull/22))
|
|
17
|
+
- `ConstantReadNode`/`ConstantPathNode` support for type inference ([#23](https://github.com/dak2/method-ray/pull/23))
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- Split install.rs and integration tests into focused modules ([#17](https://github.com/dak2/method-ray/pull/17))
|
|
22
|
+
|
|
23
|
+
## [0.1.3] - 2025-02-08
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- Method parameter type inference support ([#3](https://github.com/dak2/method-ray/pull/3))
|
|
28
|
+
- Block parameter type variable resolution ([#4](https://github.com/dak2/method-ray/pull/4))
|
|
29
|
+
- Module scope support ([#6](https://github.com/dak2/method-ray/pull/6))
|
|
30
|
+
- Fully qualified name support for nested classes/modules ([#7](https://github.com/dak2/method-ray/pull/7))
|
|
31
|
+
- Float type support ([#8](https://github.com/dak2/method-ray/pull/8))
|
|
32
|
+
- Regexp type support ([#9](https://github.com/dak2/method-ray/pull/9))
|
|
33
|
+
- Range type support ([#10](https://github.com/dak2/method-ray/pull/10))
|
|
34
|
+
- Generic type inference for Range, Hash, and nested Array ([#11](https://github.com/dak2/method-ray/pull/11))
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
|
|
38
|
+
- Call operator location ([#12](https://github.com/dak2/method-ray/pull/12))
|
|
39
|
+
- Memory leak ([#13](https://github.com/dak2/method-ray/pull/13))
|
|
40
|
+
|
|
41
|
+
### Changed
|
|
42
|
+
|
|
43
|
+
- Extract BinaryLocator class from Commands module ([#5](https://github.com/dak2/method-ray/pull/5))
|
|
44
|
+
- Migrate Rust integration tests to Ruby CLI and Rust unit tests ([#14](https://github.com/dak2/method-ray/pull/14))
|
|
45
|
+
- Remove unnecessary test files and logs ([#1](https://github.com/dak2/method-ray/pull/1), [#15](https://github.com/dak2/method-ray/pull/15))
|
|
46
|
+
|
|
8
47
|
## [0.1.2] - 2025-01-19
|
|
9
48
|
|
|
10
49
|
### Added
|
|
@@ -30,6 +69,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
30
69
|
- Initial release
|
|
31
70
|
- `methodray check` - Static type checking for Ruby files
|
|
32
71
|
|
|
72
|
+
[0.1.4]: https://github.com/dak2/method-ray/releases/tag/v0.1.4
|
|
73
|
+
[0.1.3]: https://github.com/dak2/method-ray/releases/tag/v0.1.3
|
|
33
74
|
[0.1.2]: https://github.com/dak2/method-ray/releases/tag/v0.1.2
|
|
34
75
|
[0.1.1]: https://github.com/dak2/method-ray/releases/tag/v0.1.1
|
|
35
76
|
[0.1.0]: https://github.com/dak2/method-ray/releases/tag/v0.1.0
|
data/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
A fast static callable method checker for Ruby code.
|
|
4
4
|
|
|
5
|
+
No type annotations required, just check callable methods in your Ruby files.
|
|
6
|
+
|
|
5
7
|
## Requirements
|
|
6
8
|
|
|
7
9
|
Method-Ray supports Ruby 3.4 or later.
|
|
@@ -14,7 +16,7 @@ gem install methodray
|
|
|
14
16
|
|
|
15
17
|
## Quick Start
|
|
16
18
|
|
|
17
|
-
### VSCode Extension
|
|
19
|
+
### VSCode Extension (under development)
|
|
18
20
|
|
|
19
21
|
1. Install the [Method-Ray VSCode extension](https://github.com/dak2/method-ray-vscode)
|
|
20
22
|
2. Open a Ruby file in VSCode
|
|
@@ -30,6 +32,30 @@ bundle exec methodray check app/models/user.rb
|
|
|
30
32
|
bundle exec methodray watch app/models/user.rb
|
|
31
33
|
```
|
|
32
34
|
|
|
35
|
+
#### Example
|
|
36
|
+
|
|
37
|
+
`methodray check <file>`: Performs static type checking on the specified Ruby file.
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
class User
|
|
42
|
+
def greeting
|
|
43
|
+
name = "Alice"
|
|
44
|
+
message = name.abs
|
|
45
|
+
message
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
This will output:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
$ bundle exec methodray check app/models/user.rb
|
|
54
|
+
app/models/user.rb:4:19: error: undefined method `abs` for String
|
|
55
|
+
message = name.abs
|
|
56
|
+
^
|
|
57
|
+
```
|
|
58
|
+
|
|
33
59
|
## Contributing
|
|
34
60
|
|
|
35
61
|
Bug reports and pull requests are welcome on GitHub at this repository!
|
data/ext/Cargo.toml
CHANGED
data/ext/src/lib.rs
CHANGED
|
@@ -6,7 +6,8 @@ use magnus::{function, method, prelude::*, Error, Ruby};
|
|
|
6
6
|
use methodray_core::{
|
|
7
7
|
analyzer::AstInstaller,
|
|
8
8
|
env::{GlobalEnv, LocalEnv},
|
|
9
|
-
parser,
|
|
9
|
+
parser::ParseSession,
|
|
10
|
+
rbs,
|
|
10
11
|
};
|
|
11
12
|
|
|
12
13
|
#[magnus::wrap(class = "MethodRay::Analyzer")]
|
|
@@ -27,11 +28,11 @@ impl Analyzer {
|
|
|
27
28
|
/// Execute type inference
|
|
28
29
|
fn infer_types(&self, source: String) -> Result<String, Error> {
|
|
29
30
|
// Parse
|
|
30
|
-
let
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
let session = ParseSession::new();
|
|
32
|
+
let parse_result = session.parse_source(&source, "source.rb").map_err(|e| {
|
|
33
|
+
let ruby = unsafe { Ruby::get_unchecked() };
|
|
34
|
+
Error::new(ruby.exception_runtime_error(), e.to_string())
|
|
35
|
+
})?;
|
|
35
36
|
|
|
36
37
|
// Build graph
|
|
37
38
|
let mut genv = GlobalEnv::new();
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MethodRay
|
|
4
|
+
class BinaryLocator
|
|
5
|
+
LIB_DIR = __dir__
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@binary_name = Gem.win_platform? ? 'methodray-cli.exe' : 'methodray-cli'
|
|
9
|
+
@legacy_binary_name = Gem.win_platform? ? 'methodray.exe' : 'methodray'
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def find
|
|
13
|
+
candidates.find { |path| File.executable?(path) }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def candidates
|
|
19
|
+
[
|
|
20
|
+
# CLI binary built during gem install (lib/methodray directory)
|
|
21
|
+
File.expand_path(@binary_name, LIB_DIR),
|
|
22
|
+
# Development: target/release (project root)
|
|
23
|
+
File.expand_path("../../target/release/#{@binary_name}", LIB_DIR),
|
|
24
|
+
# Development: rust/target/release (legacy standalone binary)
|
|
25
|
+
File.expand_path("../../rust/target/release/#{@legacy_binary_name}", LIB_DIR)
|
|
26
|
+
]
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
data/lib/methodray/commands.rb
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative 'binary_locator'
|
|
4
|
+
|
|
3
5
|
module MethodRay
|
|
4
6
|
module Commands
|
|
5
|
-
COMMANDS_DIR = __dir__
|
|
6
|
-
|
|
7
7
|
class << self
|
|
8
8
|
def help
|
|
9
9
|
puts <<~HELP
|
|
@@ -41,7 +41,7 @@ module MethodRay
|
|
|
41
41
|
private
|
|
42
42
|
|
|
43
43
|
def exec_rust_cli(command, args)
|
|
44
|
-
binary_path =
|
|
44
|
+
binary_path = BinaryLocator.new.find
|
|
45
45
|
|
|
46
46
|
unless binary_path
|
|
47
47
|
warn 'Error: CLI binary not found.'
|
|
@@ -56,23 +56,6 @@ module MethodRay
|
|
|
56
56
|
|
|
57
57
|
exec(binary_path, command, *args)
|
|
58
58
|
end
|
|
59
|
-
|
|
60
|
-
def find_rust_binary
|
|
61
|
-
# Platform-specific binary name
|
|
62
|
-
cli_binary = Gem.win_platform? ? 'methodray-cli.exe' : 'methodray-cli'
|
|
63
|
-
legacy_binary = Gem.win_platform? ? 'methodray.exe' : 'methodray'
|
|
64
|
-
|
|
65
|
-
candidates = [
|
|
66
|
-
# CLI binary built during gem install (lib/methodray directory)
|
|
67
|
-
File.expand_path(cli_binary, COMMANDS_DIR),
|
|
68
|
-
# Development: target/release (project root)
|
|
69
|
-
File.expand_path("../../target/release/#{cli_binary}", COMMANDS_DIR),
|
|
70
|
-
# Development: rust/target/release (legacy standalone binary)
|
|
71
|
-
File.expand_path("../../rust/target/release/#{legacy_binary}", COMMANDS_DIR)
|
|
72
|
-
]
|
|
73
|
-
|
|
74
|
-
candidates.find { |path| File.executable?(path) }
|
|
75
|
-
end
|
|
76
59
|
end
|
|
77
60
|
end
|
|
78
61
|
end
|
data/lib/methodray/version.rb
CHANGED
data/lib/methodray.rb
CHANGED
data/rust/Cargo.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "methodray-core"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.4"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
|
|
6
6
|
[lib]
|
|
@@ -18,6 +18,8 @@ path = "src/lsp/main.rs"
|
|
|
18
18
|
required-features = ["lsp"]
|
|
19
19
|
|
|
20
20
|
[dependencies]
|
|
21
|
+
bumpalo = "3.14"
|
|
22
|
+
smallvec = "1.13"
|
|
21
23
|
rayon = "1.10"
|
|
22
24
|
walkdir = "2.5"
|
|
23
25
|
anyhow = "1.0"
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
//! Attribute accessor support - synthesize getter/setter methods for attr_reader/attr_writer/attr_accessor
|
|
2
|
+
|
|
3
|
+
use crate::env::GlobalEnv;
|
|
4
|
+
use crate::types::Type;
|
|
5
|
+
|
|
6
|
+
use super::dispatch::AttrKind;
|
|
7
|
+
|
|
8
|
+
/// Register synthesized getter/setter methods for attr_reader/attr_writer/attr_accessor.
|
|
9
|
+
///
|
|
10
|
+
/// - attr_reader :name → registers User#name (getter, return = @name vertex)
|
|
11
|
+
/// - attr_writer :name → registers User#name= (setter, param → @name edge)
|
|
12
|
+
/// - attr_accessor :name → registers both getter and setter
|
|
13
|
+
///
|
|
14
|
+
/// If @name has no VertexId yet, one is pre-allocated so that later assignments
|
|
15
|
+
/// (e.g., `@name = "Alice"` in initialize) propagate into the same vertex.
|
|
16
|
+
pub(crate) fn process_attr_declaration(
|
|
17
|
+
genv: &mut GlobalEnv,
|
|
18
|
+
kind: AttrKind,
|
|
19
|
+
attr_names: Vec<String>,
|
|
20
|
+
) {
|
|
21
|
+
let Some(class_name) = genv.scope_manager.current_class_name() else {
|
|
22
|
+
return;
|
|
23
|
+
};
|
|
24
|
+
let recv_ty = Type::instance(&class_name);
|
|
25
|
+
|
|
26
|
+
for attr_name in attr_names {
|
|
27
|
+
let ivar_name = format!("@{}", attr_name);
|
|
28
|
+
|
|
29
|
+
// Get or pre-allocate VertexId for @name
|
|
30
|
+
let ivar_vtx = match genv.scope_manager.lookup_instance_var(&ivar_name) {
|
|
31
|
+
Some(vtx) => vtx,
|
|
32
|
+
None => {
|
|
33
|
+
let vtx = genv.new_vertex();
|
|
34
|
+
genv.scope_manager
|
|
35
|
+
.set_instance_var_in_class(ivar_name, vtx);
|
|
36
|
+
vtx
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Register getter (attr_reader / attr_accessor)
|
|
41
|
+
if matches!(kind, AttrKind::Reader | AttrKind::Accessor) {
|
|
42
|
+
genv.register_user_method(recv_ty.clone(), &attr_name, ivar_vtx, vec![]);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Register setter (attr_writer / attr_accessor)
|
|
46
|
+
if matches!(kind, AttrKind::Writer | AttrKind::Accessor) {
|
|
47
|
+
let param_vtx = genv.new_vertex();
|
|
48
|
+
genv.add_edge(param_vtx, ivar_vtx);
|
|
49
|
+
genv.register_user_method(
|
|
50
|
+
recv_ty.clone(),
|
|
51
|
+
&format!("{}=", attr_name),
|
|
52
|
+
ivar_vtx,
|
|
53
|
+
vec![param_vtx],
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
//! Block Handlers - Processing Ruby blocks
|
|
2
|
+
//!
|
|
3
|
+
//! This module is responsible for:
|
|
4
|
+
//! - Processing BlockNode (e.g., `{ |x| x.to_s }` or `do |x| x.to_s end`)
|
|
5
|
+
//! - Registering block parameters as local variables
|
|
6
|
+
//! - Managing block scope
|
|
7
|
+
|
|
8
|
+
use crate::env::{GlobalEnv, LocalEnv, ScopeKind};
|
|
9
|
+
use crate::graph::{ChangeSet, VertexId};
|
|
10
|
+
|
|
11
|
+
use super::parameters::{install_optional_parameter, install_required_parameter, install_rest_parameter};
|
|
12
|
+
|
|
13
|
+
/// Process block node
|
|
14
|
+
pub(crate) fn process_block_node(
|
|
15
|
+
genv: &mut GlobalEnv,
|
|
16
|
+
lenv: &mut LocalEnv,
|
|
17
|
+
changes: &mut ChangeSet,
|
|
18
|
+
source: &str,
|
|
19
|
+
block_node: &ruby_prism::BlockNode,
|
|
20
|
+
) -> Option<VertexId> {
|
|
21
|
+
process_block_node_with_params(genv, lenv, changes, source, block_node);
|
|
22
|
+
None
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/// Process block node and return block parameter vertex IDs
|
|
26
|
+
pub(crate) fn process_block_node_with_params(
|
|
27
|
+
genv: &mut GlobalEnv,
|
|
28
|
+
lenv: &mut LocalEnv,
|
|
29
|
+
changes: &mut ChangeSet,
|
|
30
|
+
source: &str,
|
|
31
|
+
block_node: &ruby_prism::BlockNode,
|
|
32
|
+
) -> Vec<VertexId> {
|
|
33
|
+
enter_block_scope(genv);
|
|
34
|
+
|
|
35
|
+
let mut param_vtxs = Vec::new();
|
|
36
|
+
|
|
37
|
+
if let Some(params_node) = block_node.parameters() {
|
|
38
|
+
if let Some(block_params) = params_node.as_block_parameters_node() {
|
|
39
|
+
param_vtxs =
|
|
40
|
+
install_block_parameters_with_vtxs(genv, lenv, changes, source, &block_params);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if let Some(body) = block_node.body() {
|
|
45
|
+
if let Some(statements) = body.as_statements_node() {
|
|
46
|
+
super::install::install_statements(genv, lenv, changes, source, &statements);
|
|
47
|
+
} else {
|
|
48
|
+
super::install::install_node(genv, lenv, changes, source, &body);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
exit_block_scope(genv);
|
|
53
|
+
|
|
54
|
+
param_vtxs
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/// Install block parameters and return their vertex IDs
|
|
58
|
+
fn install_block_parameters_with_vtxs(
|
|
59
|
+
genv: &mut GlobalEnv,
|
|
60
|
+
lenv: &mut LocalEnv,
|
|
61
|
+
changes: &mut ChangeSet,
|
|
62
|
+
source: &str,
|
|
63
|
+
block_params: &ruby_prism::BlockParametersNode,
|
|
64
|
+
) -> Vec<VertexId> {
|
|
65
|
+
let mut vtxs = Vec::new();
|
|
66
|
+
|
|
67
|
+
if let Some(params) = block_params.parameters() {
|
|
68
|
+
// Required parameters (most common in blocks)
|
|
69
|
+
for node in params.requireds().iter() {
|
|
70
|
+
if let Some(req_param) = node.as_required_parameter_node() {
|
|
71
|
+
let name = String::from_utf8_lossy(req_param.name().as_slice()).to_string();
|
|
72
|
+
let vtx = install_block_parameter(genv, lenv, name);
|
|
73
|
+
vtxs.push(vtx);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Optional parameters: { |x = 1| ... }
|
|
78
|
+
for node in params.optionals().iter() {
|
|
79
|
+
if let Some(opt_param) = node.as_optional_parameter_node() {
|
|
80
|
+
let name = String::from_utf8_lossy(opt_param.name().as_slice()).to_string();
|
|
81
|
+
let default_value = opt_param.value();
|
|
82
|
+
|
|
83
|
+
if let Some(default_vtx) =
|
|
84
|
+
super::install::install_node(genv, lenv, changes, source, &default_value)
|
|
85
|
+
{
|
|
86
|
+
let vtx =
|
|
87
|
+
install_optional_parameter(genv, lenv, changes, name, default_vtx);
|
|
88
|
+
vtxs.push(vtx);
|
|
89
|
+
} else {
|
|
90
|
+
let vtx = install_block_parameter(genv, lenv, name);
|
|
91
|
+
vtxs.push(vtx);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Rest parameter: { |*args| ... }
|
|
97
|
+
if let Some(rest_node) = params.rest() {
|
|
98
|
+
if let Some(rest_param) = rest_node.as_rest_parameter_node() {
|
|
99
|
+
if let Some(name_id) = rest_param.name() {
|
|
100
|
+
let name = String::from_utf8_lossy(name_id.as_slice()).to_string();
|
|
101
|
+
let vtx = install_rest_parameter(genv, lenv, name);
|
|
102
|
+
vtxs.push(vtx);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
vtxs
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/// Enter a new block scope
|
|
112
|
+
fn enter_block_scope(genv: &mut GlobalEnv) {
|
|
113
|
+
let block_scope_id = genv.scope_manager.new_scope(ScopeKind::Block);
|
|
114
|
+
genv.scope_manager.enter_scope(block_scope_id);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/// Exit the current block scope
|
|
118
|
+
fn exit_block_scope(genv: &mut GlobalEnv) {
|
|
119
|
+
genv.scope_manager.exit_scope();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/// Install block parameter as a local variable (Bot type)
|
|
123
|
+
fn install_block_parameter(genv: &mut GlobalEnv, lenv: &mut LocalEnv, name: String) -> VertexId {
|
|
124
|
+
install_required_parameter(genv, lenv, name)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
#[cfg(test)]
|
|
128
|
+
mod tests {
|
|
129
|
+
use super::*;
|
|
130
|
+
|
|
131
|
+
#[test]
|
|
132
|
+
fn test_enter_exit_block_scope() {
|
|
133
|
+
let mut genv = GlobalEnv::new();
|
|
134
|
+
|
|
135
|
+
let initial_scope_id = genv.scope_manager.current_scope().id;
|
|
136
|
+
|
|
137
|
+
enter_block_scope(&mut genv);
|
|
138
|
+
let block_scope_id = genv.scope_manager.current_scope().id;
|
|
139
|
+
|
|
140
|
+
assert_ne!(initial_scope_id, block_scope_id);
|
|
141
|
+
|
|
142
|
+
exit_block_scope(&mut genv);
|
|
143
|
+
|
|
144
|
+
assert_eq!(genv.scope_manager.current_scope().id, initial_scope_id);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
#[test]
|
|
148
|
+
fn test_install_block_parameter() {
|
|
149
|
+
let mut genv = GlobalEnv::new();
|
|
150
|
+
let mut lenv = LocalEnv::new();
|
|
151
|
+
|
|
152
|
+
enter_block_scope(&mut genv);
|
|
153
|
+
|
|
154
|
+
let vtx = install_block_parameter(&mut genv, &mut lenv, "x".to_string());
|
|
155
|
+
|
|
156
|
+
assert_eq!(lenv.get_var("x"), Some(vtx));
|
|
157
|
+
|
|
158
|
+
exit_block_scope(&mut genv);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
#[test]
|
|
162
|
+
fn test_block_inherits_parent_scope_vars() {
|
|
163
|
+
let mut genv = GlobalEnv::new();
|
|
164
|
+
|
|
165
|
+
genv.scope_manager
|
|
166
|
+
.current_scope_mut()
|
|
167
|
+
.set_local_var("outer".to_string(), VertexId(100));
|
|
168
|
+
|
|
169
|
+
enter_block_scope(&mut genv);
|
|
170
|
+
|
|
171
|
+
assert_eq!(genv.scope_manager.lookup_var("outer"), Some(VertexId(100)));
|
|
172
|
+
|
|
173
|
+
exit_block_scope(&mut genv);
|
|
174
|
+
}
|
|
175
|
+
}
|
data/rust/src/analyzer/calls.rs
CHANGED
|
@@ -14,14 +14,15 @@ pub fn install_method_call(
|
|
|
14
14
|
genv: &mut GlobalEnv,
|
|
15
15
|
recv_vtx: VertexId,
|
|
16
16
|
method_name: String,
|
|
17
|
+
arg_vtxs: Vec<VertexId>,
|
|
17
18
|
location: Option<SourceLocation>,
|
|
18
19
|
) -> VertexId {
|
|
19
20
|
// Create Vertex for return value
|
|
20
21
|
let ret_vtx = genv.new_vertex();
|
|
21
22
|
|
|
22
|
-
// Create MethodCallBox with location
|
|
23
|
+
// Create MethodCallBox with location and argument vertices
|
|
23
24
|
let box_id = genv.alloc_box_id();
|
|
24
|
-
let call_box = MethodCallBox::new(box_id, recv_vtx, method_name, ret_vtx, location);
|
|
25
|
+
let call_box = MethodCallBox::new(box_id, recv_vtx, method_name, ret_vtx, arg_vtxs, location);
|
|
25
26
|
genv.register_box(box_id, Box::new(call_box));
|
|
26
27
|
|
|
27
28
|
ret_vtx
|
|
@@ -37,7 +38,8 @@ mod tests {
|
|
|
37
38
|
let mut genv = GlobalEnv::new();
|
|
38
39
|
|
|
39
40
|
let recv_vtx = genv.new_source(Type::string());
|
|
40
|
-
let ret_vtx =
|
|
41
|
+
let ret_vtx =
|
|
42
|
+
install_method_call(&mut genv, recv_vtx, "upcase".to_string(), vec![], None);
|
|
41
43
|
|
|
42
44
|
// Return vertex should exist
|
|
43
45
|
assert!(genv.get_vertex(ret_vtx).is_some());
|
|
@@ -48,7 +50,8 @@ mod tests {
|
|
|
48
50
|
let mut genv = GlobalEnv::new();
|
|
49
51
|
|
|
50
52
|
let recv_vtx = genv.new_source(Type::string());
|
|
51
|
-
let _ret_vtx =
|
|
53
|
+
let _ret_vtx =
|
|
54
|
+
install_method_call(&mut genv, recv_vtx, "upcase".to_string(), vec![], None);
|
|
52
55
|
|
|
53
56
|
// Box should be added
|
|
54
57
|
assert_eq!(genv.box_count(), 1);
|