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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c950d346d7e01dfcf74b912ee1677450b9ea3d95c3baac937062cc8bf4363163
4
- data.tar.gz: d9804ea668007c228bb12078ebaa3ec7e4c3b37daa372b2a801765b90978136a
3
+ metadata.gz: 1a219edf198245163ff36fbcc9e211dbfea2d5e5f41a467ee24636286967f4b9
4
+ data.tar.gz: 7a82426e9c5b0ba53a2d1ca8bab90da2a4b4eb58316af7cfb8a576dc6dc28b64
5
5
  SHA512:
6
- metadata.gz: 1a572ea77523293d586c6acd284994206f1b4b678ede730f50fe619f98762f1c215daf5b8e899400f3872769078aa033bd6cea701515533acfe11d7695e0fd26
7
- data.tar.gz: 2f20387f762e32766281f6813971b3f546f8cb6476482cbe13d3cfb1fe03e6761e2304be3a5eeade1cb8261d7e5197a1416e3f0cfad280450b54a16acdddacb1
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
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "methodray"
3
- version = "0.1.3"
3
+ version = "0.1.4"
4
4
  edition = "2021"
5
5
 
6
6
  [lib]
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MethodRay
4
- VERSION = '0.1.3'
4
+ VERSION = '0.1.4'
5
5
  end
data/rust/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "methodray-core"
3
- version = "0.1.3"
3
+ version = "0.1.4"
4
4
  edition = "2021"
5
5
 
6
6
  [lib]
@@ -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
+ }
@@ -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
- use super::parameters::install_required_parameter;
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
- pub fn exit_block_scope(genv: &mut GlobalEnv) {
118
+ fn exit_block_scope(genv: &mut GlobalEnv) {
24
119
  genv.scope_manager.exit_scope();
25
120
  }
26
121
 
27
- /// Install block parameters as local variables
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);
@@ -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 = install_method_call(&mut genv, recv_vtx, "upcase".to_string(), None);
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 = install_method_call(&mut genv, recv_vtx, "upcase".to_string(), None);
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);