method-ray 0.1.3 → 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 +16 -0
- data/ext/Cargo.toml +1 -1
- data/lib/methodray/version.rb +1 -1
- data/rust/Cargo.toml +1 -1
- data/rust/src/analyzer/attributes.rs +57 -0
- data/rust/src/analyzer/blocks.rs +104 -23
- data/rust/src/analyzer/calls.rs +7 -4
- data/rust/src/analyzer/conditionals.rs +466 -0
- data/rust/src/analyzer/definitions.rs +126 -8
- data/rust/src/analyzer/dispatch.rs +746 -11
- data/rust/src/analyzer/install.rs +52 -870
- data/rust/src/analyzer/literals.rs +179 -30
- data/rust/src/analyzer/mod.rs +2 -0
- data/rust/src/analyzer/parameters.rs +64 -0
- data/rust/src/analyzer/variables.rs +12 -3
- data/rust/src/env/global_env.rs +12 -0
- data/rust/src/env/method_registry.rs +55 -0
- data/rust/src/graph/box.rs +145 -5
- metadata +3 -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,21 @@ 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
|
+
|
|
8
23
|
## [0.1.3] - 2025-02-08
|
|
9
24
|
|
|
10
25
|
### Added
|
|
@@ -54,6 +69,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
54
69
|
- Initial release
|
|
55
70
|
- `methodray check` - Static type checking for Ruby files
|
|
56
71
|
|
|
72
|
+
[0.1.4]: https://github.com/dak2/method-ray/releases/tag/v0.1.4
|
|
57
73
|
[0.1.3]: https://github.com/dak2/method-ray/releases/tag/v0.1.3
|
|
58
74
|
[0.1.2]: https://github.com/dak2/method-ray/releases/tag/v0.1.2
|
|
59
75
|
[0.1.1]: https://github.com/dak2/method-ray/releases/tag/v0.1.1
|
data/ext/Cargo.toml
CHANGED
data/lib/methodray/version.rb
CHANGED
data/rust/Cargo.toml
CHANGED
|
@@ -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
|
+
}
|
data/rust/src/analyzer/blocks.rs
CHANGED
|
@@ -6,35 +6,121 @@
|
|
|
6
6
|
//! - Managing block scope
|
|
7
7
|
|
|
8
8
|
use crate::env::{GlobalEnv, LocalEnv, ScopeKind};
|
|
9
|
-
use crate::graph::VertexId;
|
|
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);
|
|
10
53
|
|
|
11
|
-
|
|
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
|
+
}
|
|
12
110
|
|
|
13
111
|
/// Enter a new block scope
|
|
14
|
-
|
|
15
|
-
/// Creates a new scope for the block and enters it.
|
|
16
|
-
/// Block scopes inherit variables from parent scopes.
|
|
17
|
-
pub fn enter_block_scope(genv: &mut GlobalEnv) {
|
|
112
|
+
fn enter_block_scope(genv: &mut GlobalEnv) {
|
|
18
113
|
let block_scope_id = genv.scope_manager.new_scope(ScopeKind::Block);
|
|
19
114
|
genv.scope_manager.enter_scope(block_scope_id);
|
|
20
115
|
}
|
|
21
116
|
|
|
22
117
|
/// Exit the current block scope
|
|
23
|
-
|
|
118
|
+
fn exit_block_scope(genv: &mut GlobalEnv) {
|
|
24
119
|
genv.scope_manager.exit_scope();
|
|
25
120
|
}
|
|
26
121
|
|
|
27
|
-
/// Install block
|
|
28
|
-
|
|
29
|
-
/// Block parameters are registered as Bot (untyped) type since we don't
|
|
30
|
-
/// know what type will be passed from the iterator method.
|
|
31
|
-
///
|
|
32
|
-
/// # Example
|
|
33
|
-
/// ```ruby
|
|
34
|
-
/// [1, 2, 3].each { |x| x.to_s } # 'x' is a block parameter
|
|
35
|
-
/// ```
|
|
36
|
-
pub fn install_block_parameter(genv: &mut GlobalEnv, lenv: &mut LocalEnv, name: String) -> VertexId {
|
|
37
|
-
// Reuse required parameter logic (Bot type)
|
|
122
|
+
/// Install block parameter as a local variable (Bot type)
|
|
123
|
+
fn install_block_parameter(genv: &mut GlobalEnv, lenv: &mut LocalEnv, name: String) -> VertexId {
|
|
38
124
|
install_required_parameter(genv, lenv, name)
|
|
39
125
|
}
|
|
40
126
|
|
|
@@ -51,12 +137,10 @@ mod tests {
|
|
|
51
137
|
enter_block_scope(&mut genv);
|
|
52
138
|
let block_scope_id = genv.scope_manager.current_scope().id;
|
|
53
139
|
|
|
54
|
-
// Should be in a new scope
|
|
55
140
|
assert_ne!(initial_scope_id, block_scope_id);
|
|
56
141
|
|
|
57
142
|
exit_block_scope(&mut genv);
|
|
58
143
|
|
|
59
|
-
// Should be back to initial scope
|
|
60
144
|
assert_eq!(genv.scope_manager.current_scope().id, initial_scope_id);
|
|
61
145
|
}
|
|
62
146
|
|
|
@@ -69,7 +153,6 @@ mod tests {
|
|
|
69
153
|
|
|
70
154
|
let vtx = install_block_parameter(&mut genv, &mut lenv, "x".to_string());
|
|
71
155
|
|
|
72
|
-
// Parameter should be registered in LocalEnv
|
|
73
156
|
assert_eq!(lenv.get_var("x"), Some(vtx));
|
|
74
157
|
|
|
75
158
|
exit_block_scope(&mut genv);
|
|
@@ -79,14 +162,12 @@ mod tests {
|
|
|
79
162
|
fn test_block_inherits_parent_scope_vars() {
|
|
80
163
|
let mut genv = GlobalEnv::new();
|
|
81
164
|
|
|
82
|
-
// Set variable in top-level scope
|
|
83
165
|
genv.scope_manager
|
|
84
166
|
.current_scope_mut()
|
|
85
167
|
.set_local_var("outer".to_string(), VertexId(100));
|
|
86
168
|
|
|
87
169
|
enter_block_scope(&mut genv);
|
|
88
170
|
|
|
89
|
-
// Block should be able to lookup parent scope variables
|
|
90
171
|
assert_eq!(genv.scope_manager.lookup_var("outer"), Some(VertexId(100)));
|
|
91
172
|
|
|
92
173
|
exit_block_scope(&mut genv);
|
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);
|