method-ray 0.1.6 → 0.1.8
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 +30 -0
- data/ext/Cargo.toml +1 -1
- data/lib/methodray/version.rb +1 -1
- data/rust/Cargo.toml +1 -1
- data/rust/src/analyzer/assignments.rs +152 -0
- data/rust/src/analyzer/attributes.rs +2 -1
- data/rust/src/analyzer/blocks.rs +4 -3
- data/rust/src/analyzer/calls.rs +7 -3
- data/rust/src/analyzer/definitions.rs +10 -5
- data/rust/src/analyzer/dispatch.rs +265 -24
- data/rust/src/analyzer/exceptions.rs +521 -0
- data/rust/src/analyzer/install.rs +22 -1
- data/rust/src/analyzer/loops.rs +176 -0
- data/rust/src/analyzer/mod.rs +32 -0
- data/rust/src/analyzer/operators.rs +119 -27
- data/rust/src/analyzer/parameters.rs +60 -9
- data/rust/src/cache/rbs_cache.rs +0 -1
- data/rust/src/cli/commands.rs +3 -3
- data/rust/src/diagnostics/diagnostic.rs +0 -3
- data/rust/src/diagnostics/formatter.rs +0 -1
- data/rust/src/env/box_manager.rs +2 -4
- data/rust/src/env/global_env.rs +43 -5
- data/rust/src/env/local_env.rs +35 -1
- data/rust/src/env/method_registry.rs +140 -17
- data/rust/src/env/scope.rs +143 -164
- data/rust/src/env/vertex_manager.rs +0 -1
- data/rust/src/graph/box.rs +217 -84
- data/rust/src/graph/change_set.rs +14 -0
- data/rust/src/lsp/server.rs +1 -1
- data/rust/src/rbs/loader.rs +1 -2
- data/rust/src/source_map.rs +0 -1
- data/rust/src/types.rs +0 -1
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f44709f8354439522722c8ea65da30463fae9de68d9be1aa07c8dac9627a03ce
|
|
4
|
+
data.tar.gz: 2a09885854020892cf93a1c85e83735a23be60240fcbee09f77fa8c9fbf58801
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4be6a23881adf54e88039c4e62255f09aa52005cf4085e6cbdc1910f642dae8e763ec7993445051bd3cff20693b5576e417f47b0864359c60db6becf1c21e204
|
|
7
|
+
data.tar.gz: cc9b502af18acc8463e9cddf95cc16b8ed6095ffc34d826642fe8193b2f4cc16aa7490a96f930493fe30fc4af21412cf2ff515f8f45e4d04bf9ec704f256ea26
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,34 @@ 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.8] - 2026-03-09
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- while/until loop support to type inference ([#46](https://github.com/dak2/method-ray/pull/46))
|
|
13
|
+
- Not operator (!) support to type inference ([#47](https://github.com/dak2/method-ray/pull/47))
|
|
14
|
+
- begin/rescue/ensure exception handling support to type inference ([#48](https://github.com/dak2/method-ray/pull/48))
|
|
15
|
+
- Keyword argument support to type inference ([#49](https://github.com/dak2/method-ray/pull/49))
|
|
16
|
+
- Multiple assignment support to type inference ([#50](https://github.com/dak2/method-ray/pull/50))
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- Resolve all Clippy warnings for cleaner, more idiomatic Rust ([#51](https://github.com/dak2/method-ray/pull/51))
|
|
21
|
+
|
|
22
|
+
## [0.1.7] - 2026-03-07
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- Kernel/Object methods loaded from RBS to reduce false positives ([#39](https://github.com/dak2/method-ray/pull/39))
|
|
27
|
+
- Object/Kernel fallback chain for method resolution ([#40](https://github.com/dak2/method-ray/pull/40))
|
|
28
|
+
- Constant namespace resolution for ConstantReadNode in nested scopes ([#41](https://github.com/dak2/method-ray/pull/41))
|
|
29
|
+
- Cargo test added to CI workflow ([#38](https://github.com/dak2/method-ray/pull/38))
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
|
|
33
|
+
- Extract `bytes_to_name` helper to consolidate 17 UTF-8 conversion sites ([#42](https://github.com/dak2/method-ray/pull/42))
|
|
34
|
+
- Refactor MethodCallBox by extracting helper methods ([#43](https://github.com/dak2/method-ray/pull/43))
|
|
35
|
+
|
|
8
36
|
## [0.1.6] - 2026-02-23
|
|
9
37
|
|
|
10
38
|
### Fixed
|
|
@@ -95,6 +123,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
95
123
|
- Initial release
|
|
96
124
|
- `methodray check` - Static type checking for Ruby files
|
|
97
125
|
|
|
126
|
+
[0.1.8]: https://github.com/dak2/method-ray/releases/tag/v0.1.8
|
|
127
|
+
[0.1.7]: https://github.com/dak2/method-ray/releases/tag/v0.1.7
|
|
98
128
|
[0.1.6]: https://github.com/dak2/method-ray/releases/tag/v0.1.6
|
|
99
129
|
[0.1.5]: https://github.com/dak2/method-ray/releases/tag/v0.1.5
|
|
100
130
|
[0.1.4]: https://github.com/dak2/method-ray/releases/tag/v0.1.4
|
data/ext/Cargo.toml
CHANGED
data/lib/methodray/version.rb
CHANGED
data/rust/Cargo.toml
CHANGED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
//! Multiple Assignment Handlers - Processing Ruby multiple assignment
|
|
2
|
+
//!
|
|
3
|
+
//! v0.1.8 scope: Only RHS as ArrayNode (multiple literal values) is supported.
|
|
4
|
+
//! TODO: Support RHS as single expression (array decomposition)
|
|
5
|
+
//! TODO: Support splat target (*rest) as Array type
|
|
6
|
+
//! TODO: Support RHS as method return value decomposition
|
|
7
|
+
//! TODO: When LHS is longer than RHS, register trailing targets as NilClass
|
|
8
|
+
|
|
9
|
+
use crate::env::{GlobalEnv, LocalEnv};
|
|
10
|
+
use crate::graph::{ChangeSet, VertexId};
|
|
11
|
+
|
|
12
|
+
use super::bytes_to_name;
|
|
13
|
+
use super::variables::install_local_var_write;
|
|
14
|
+
|
|
15
|
+
/// Process multiple assignment node (e.g., `a, b = 1, "hello"`)
|
|
16
|
+
pub(crate) fn process_multi_write_node(
|
|
17
|
+
genv: &mut GlobalEnv,
|
|
18
|
+
lenv: &mut LocalEnv,
|
|
19
|
+
changes: &mut ChangeSet,
|
|
20
|
+
source: &str,
|
|
21
|
+
node: &ruby_prism::MultiWriteNode,
|
|
22
|
+
) -> Option<VertexId> {
|
|
23
|
+
let value = node.value();
|
|
24
|
+
let mut last_vtx = None;
|
|
25
|
+
|
|
26
|
+
if let Some(array_node) = value.as_array_node() {
|
|
27
|
+
for (target, rhs_elem) in node.lefts().iter().zip(array_node.elements().iter()) {
|
|
28
|
+
if let Some(target_node) = target.as_local_variable_target_node() {
|
|
29
|
+
let var_name = bytes_to_name(target_node.name().as_slice());
|
|
30
|
+
let rhs_vtx =
|
|
31
|
+
super::install::install_node(genv, lenv, changes, source, &rhs_elem);
|
|
32
|
+
if let Some(rv) = rhs_vtx {
|
|
33
|
+
last_vtx = Some(install_local_var_write(genv, lenv, changes, var_name, rv));
|
|
34
|
+
} else {
|
|
35
|
+
let var_vtx = genv.new_vertex();
|
|
36
|
+
lenv.new_var(var_name, var_vtx);
|
|
37
|
+
last_vtx = Some(var_vtx);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
} else {
|
|
42
|
+
for target in node.lefts().iter() {
|
|
43
|
+
if let Some(target_node) = target.as_local_variable_target_node() {
|
|
44
|
+
let var_name = bytes_to_name(target_node.name().as_slice());
|
|
45
|
+
let var_vtx = genv.new_vertex();
|
|
46
|
+
lenv.new_var(var_name, var_vtx);
|
|
47
|
+
last_vtx = Some(var_vtx);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
last_vtx
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
#[cfg(test)]
|
|
56
|
+
mod tests {
|
|
57
|
+
use crate::analyzer::install::AstInstaller;
|
|
58
|
+
use crate::env::{GlobalEnv, LocalEnv};
|
|
59
|
+
use crate::graph::VertexId;
|
|
60
|
+
use crate::parser::ParseSession;
|
|
61
|
+
|
|
62
|
+
fn analyze(source: &str) -> (GlobalEnv, LocalEnv) {
|
|
63
|
+
let session = ParseSession::new();
|
|
64
|
+
let parse_result = session.parse_source(source, "test.rb").unwrap();
|
|
65
|
+
let root = parse_result.node();
|
|
66
|
+
let program = root.as_program_node().unwrap();
|
|
67
|
+
|
|
68
|
+
let mut genv = GlobalEnv::new();
|
|
69
|
+
let mut lenv = LocalEnv::new();
|
|
70
|
+
|
|
71
|
+
let mut installer = AstInstaller::new(&mut genv, &mut lenv, source);
|
|
72
|
+
for stmt in &program.statements().body() {
|
|
73
|
+
installer.install_node(&stmt);
|
|
74
|
+
}
|
|
75
|
+
installer.finish();
|
|
76
|
+
|
|
77
|
+
(genv, lenv)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
fn get_type_show(genv: &GlobalEnv, vtx: VertexId) -> String {
|
|
81
|
+
if let Some(vertex) = genv.get_vertex(vtx) {
|
|
82
|
+
vertex.show()
|
|
83
|
+
} else if let Some(source) = genv.get_source(vtx) {
|
|
84
|
+
source.ty.show()
|
|
85
|
+
} else {
|
|
86
|
+
panic!("vertex {:?} not found as either Vertex or Source", vtx);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
#[test]
|
|
91
|
+
fn test_multi_write_integer_and_string() {
|
|
92
|
+
let source = r#"a, b = 1, "hello""#;
|
|
93
|
+
let (genv, lenv) = analyze(source);
|
|
94
|
+
|
|
95
|
+
let a_vtx = lenv.get_var("a").expect("a should be registered");
|
|
96
|
+
assert_eq!(get_type_show(&genv, a_vtx), "Integer");
|
|
97
|
+
|
|
98
|
+
let b_vtx = lenv.get_var("b").expect("b should be registered");
|
|
99
|
+
assert_eq!(get_type_show(&genv, b_vtx), "String");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
#[test]
|
|
103
|
+
fn test_multi_write_all_integer() {
|
|
104
|
+
let source = "a, b, c = 1, 2, 3";
|
|
105
|
+
let (genv, lenv) = analyze(source);
|
|
106
|
+
|
|
107
|
+
let a_vtx = lenv.get_var("a").expect("a should be registered");
|
|
108
|
+
assert_eq!(get_type_show(&genv, a_vtx), "Integer");
|
|
109
|
+
|
|
110
|
+
let b_vtx = lenv.get_var("b").expect("b should be registered");
|
|
111
|
+
assert_eq!(get_type_show(&genv, b_vtx), "Integer");
|
|
112
|
+
|
|
113
|
+
let c_vtx = lenv.get_var("c").expect("c should be registered");
|
|
114
|
+
assert_eq!(get_type_show(&genv, c_vtx), "Integer");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
#[test]
|
|
118
|
+
fn test_multi_write_variable_reference_after_assignment() {
|
|
119
|
+
let source = r#"
|
|
120
|
+
a, b = 1, "hello"
|
|
121
|
+
x = a
|
|
122
|
+
"#;
|
|
123
|
+
let (genv, lenv) = analyze(source);
|
|
124
|
+
|
|
125
|
+
let x_vtx = lenv.get_var("x").expect("x should be registered");
|
|
126
|
+
assert_eq!(get_type_show(&genv, x_vtx), "Integer");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
#[test]
|
|
130
|
+
fn test_multi_write_lhs_longer_than_rhs() {
|
|
131
|
+
let source = "a, b, c = 1, 2";
|
|
132
|
+
let (_, lenv) = analyze(source);
|
|
133
|
+
|
|
134
|
+
assert!(lenv.get_var("a").is_some(), "a should be registered");
|
|
135
|
+
assert!(lenv.get_var("b").is_some(), "b should be registered");
|
|
136
|
+
// KNOWN LIMITATION (v0.1.8): In Ruby, c receives nil, but zip skips it here
|
|
137
|
+
assert!(
|
|
138
|
+
lenv.get_var("c").is_none(),
|
|
139
|
+
"c should not be registered (zip skips)"
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
#[test]
|
|
144
|
+
fn test_multi_write_does_not_panic_on_non_array_rhs() {
|
|
145
|
+
let source = "a, b = some_expr";
|
|
146
|
+
let (_, lenv) = analyze(source);
|
|
147
|
+
|
|
148
|
+
// Variables should be registered (untyped) without panic
|
|
149
|
+
assert!(lenv.get_var("a").is_some(), "a should be registered");
|
|
150
|
+
assert!(lenv.get_var("b").is_some(), "b should be registered");
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -39,7 +39,7 @@ pub(crate) fn process_attr_declaration(
|
|
|
39
39
|
|
|
40
40
|
// Register getter (attr_reader / attr_accessor)
|
|
41
41
|
if matches!(kind, AttrKind::Reader | AttrKind::Accessor) {
|
|
42
|
-
genv.register_user_method(recv_ty.clone(), &attr_name, ivar_vtx, vec![]);
|
|
42
|
+
genv.register_user_method(recv_ty.clone(), &attr_name, ivar_vtx, vec![], None);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
// Register setter (attr_writer / attr_accessor)
|
|
@@ -51,6 +51,7 @@ pub(crate) fn process_attr_declaration(
|
|
|
51
51
|
&format!("{}=", attr_name),
|
|
52
52
|
ivar_vtx,
|
|
53
53
|
vec![param_vtx],
|
|
54
|
+
None,
|
|
54
55
|
);
|
|
55
56
|
}
|
|
56
57
|
}
|
data/rust/src/analyzer/blocks.rs
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
use crate::env::{GlobalEnv, LocalEnv, ScopeKind};
|
|
9
9
|
use crate::graph::{ChangeSet, VertexId};
|
|
10
10
|
|
|
11
|
+
use super::bytes_to_name;
|
|
11
12
|
use super::parameters::{install_optional_parameter, install_required_parameter, install_rest_parameter};
|
|
12
13
|
|
|
13
14
|
/// Process block node
|
|
@@ -68,7 +69,7 @@ fn install_block_parameters_with_vtxs(
|
|
|
68
69
|
// Required parameters (most common in blocks)
|
|
69
70
|
for node in params.requireds().iter() {
|
|
70
71
|
if let Some(req_param) = node.as_required_parameter_node() {
|
|
71
|
-
let name =
|
|
72
|
+
let name = bytes_to_name(req_param.name().as_slice());
|
|
72
73
|
let vtx = install_block_parameter(genv, lenv, name);
|
|
73
74
|
vtxs.push(vtx);
|
|
74
75
|
}
|
|
@@ -77,7 +78,7 @@ fn install_block_parameters_with_vtxs(
|
|
|
77
78
|
// Optional parameters: { |x = 1| ... }
|
|
78
79
|
for node in params.optionals().iter() {
|
|
79
80
|
if let Some(opt_param) = node.as_optional_parameter_node() {
|
|
80
|
-
let name =
|
|
81
|
+
let name = bytes_to_name(opt_param.name().as_slice());
|
|
81
82
|
let default_value = opt_param.value();
|
|
82
83
|
|
|
83
84
|
if let Some(default_vtx) =
|
|
@@ -97,7 +98,7 @@ fn install_block_parameters_with_vtxs(
|
|
|
97
98
|
if let Some(rest_node) = params.rest() {
|
|
98
99
|
if let Some(rest_param) = rest_node.as_rest_parameter_node() {
|
|
99
100
|
if let Some(name_id) = rest_param.name() {
|
|
100
|
-
let name =
|
|
101
|
+
let name = bytes_to_name(name_id.as_slice());
|
|
101
102
|
let vtx = install_rest_parameter(genv, lenv, name);
|
|
102
103
|
vtxs.push(vtx);
|
|
103
104
|
}
|
data/rust/src/analyzer/calls.rs
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
//! - Managing return value vertices
|
|
6
6
|
//! - Attaching source location for error reporting
|
|
7
7
|
|
|
8
|
+
use std::collections::HashMap;
|
|
9
|
+
|
|
8
10
|
use crate::env::GlobalEnv;
|
|
9
11
|
use crate::graph::{MethodCallBox, VertexId};
|
|
10
12
|
use crate::source_map::SourceLocation;
|
|
@@ -15,6 +17,7 @@ pub fn install_method_call(
|
|
|
15
17
|
recv_vtx: VertexId,
|
|
16
18
|
method_name: String,
|
|
17
19
|
arg_vtxs: Vec<VertexId>,
|
|
20
|
+
kwarg_vtxs: Option<HashMap<String, VertexId>>,
|
|
18
21
|
location: Option<SourceLocation>,
|
|
19
22
|
) -> VertexId {
|
|
20
23
|
// Create Vertex for return value
|
|
@@ -22,7 +25,8 @@ pub fn install_method_call(
|
|
|
22
25
|
|
|
23
26
|
// Create MethodCallBox with location and argument vertices
|
|
24
27
|
let box_id = genv.alloc_box_id();
|
|
25
|
-
let call_box =
|
|
28
|
+
let call_box =
|
|
29
|
+
MethodCallBox::new(box_id, recv_vtx, method_name, ret_vtx, arg_vtxs, kwarg_vtxs, location);
|
|
26
30
|
genv.register_box(box_id, Box::new(call_box));
|
|
27
31
|
|
|
28
32
|
ret_vtx
|
|
@@ -39,7 +43,7 @@ mod tests {
|
|
|
39
43
|
|
|
40
44
|
let recv_vtx = genv.new_source(Type::string());
|
|
41
45
|
let ret_vtx =
|
|
42
|
-
install_method_call(&mut genv, recv_vtx, "upcase".to_string(), vec![], None);
|
|
46
|
+
install_method_call(&mut genv, recv_vtx, "upcase".to_string(), vec![], None, None);
|
|
43
47
|
|
|
44
48
|
// Return vertex should exist
|
|
45
49
|
assert!(genv.get_vertex(ret_vtx).is_some());
|
|
@@ -51,7 +55,7 @@ mod tests {
|
|
|
51
55
|
|
|
52
56
|
let recv_vtx = genv.new_source(Type::string());
|
|
53
57
|
let _ret_vtx =
|
|
54
|
-
install_method_call(&mut genv, recv_vtx, "upcase".to_string(), vec![], None);
|
|
58
|
+
install_method_call(&mut genv, recv_vtx, "upcase".to_string(), vec![], None, None);
|
|
55
59
|
|
|
56
60
|
// Box should be added
|
|
57
61
|
assert_eq!(genv.box_count(), 1);
|
|
@@ -6,11 +6,14 @@
|
|
|
6
6
|
//! - Method definition scope management (def baz ... end)
|
|
7
7
|
//! - Extracting class/module names from AST nodes (including qualified names like Api::User)
|
|
8
8
|
|
|
9
|
+
use std::collections::HashMap;
|
|
10
|
+
|
|
9
11
|
use crate::env::{GlobalEnv, LocalEnv};
|
|
10
12
|
use crate::graph::{ChangeSet, VertexId};
|
|
11
13
|
use crate::types::Type;
|
|
12
14
|
use ruby_prism::Node;
|
|
13
15
|
|
|
16
|
+
use super::bytes_to_name;
|
|
14
17
|
use super::install::install_statements;
|
|
15
18
|
use super::parameters::install_parameters;
|
|
16
19
|
|
|
@@ -64,7 +67,7 @@ pub(crate) fn process_def_node(
|
|
|
64
67
|
source: &str,
|
|
65
68
|
def_node: &ruby_prism::DefNode,
|
|
66
69
|
) -> Option<VertexId> {
|
|
67
|
-
let method_name =
|
|
70
|
+
let method_name = bytes_to_name(def_node.name().as_slice());
|
|
68
71
|
|
|
69
72
|
// Check if this is a class method (def self.foo)
|
|
70
73
|
let is_class_method = def_node
|
|
@@ -77,10 +80,10 @@ pub(crate) fn process_def_node(
|
|
|
77
80
|
let merge_vtx = genv.scope_manager.current_method_return_vertex();
|
|
78
81
|
|
|
79
82
|
// Process parameters BEFORE processing body
|
|
80
|
-
let param_vtxs = if let Some(params_node) = def_node.parameters() {
|
|
83
|
+
let (param_vtxs, keyword_param_vtxs) = if let Some(params_node) = def_node.parameters() {
|
|
81
84
|
install_parameters(genv, lenv, changes, source, ¶ms_node)
|
|
82
85
|
} else {
|
|
83
|
-
vec![]
|
|
86
|
+
(vec![], HashMap::new())
|
|
84
87
|
};
|
|
85
88
|
|
|
86
89
|
let mut last_vtx = None;
|
|
@@ -107,11 +110,13 @@ pub(crate) fn process_def_node(
|
|
|
107
110
|
} else {
|
|
108
111
|
Type::instance(&name)
|
|
109
112
|
};
|
|
113
|
+
let kw_params = (!keyword_param_vtxs.is_empty()).then_some(keyword_param_vtxs);
|
|
110
114
|
genv.register_user_method(
|
|
111
115
|
recv_type,
|
|
112
116
|
&method_name,
|
|
113
117
|
ret_vtx,
|
|
114
118
|
param_vtxs,
|
|
119
|
+
kw_params,
|
|
115
120
|
);
|
|
116
121
|
}
|
|
117
122
|
}
|
|
@@ -163,7 +168,7 @@ fn extract_module_name(module_node: &ruby_prism::ModuleNode) -> String {
|
|
|
163
168
|
pub(crate) fn extract_constant_path(node: &Node) -> Option<String> {
|
|
164
169
|
// Simple constant read: `User`
|
|
165
170
|
if let Some(constant_read) = node.as_constant_read_node() {
|
|
166
|
-
return Some(
|
|
171
|
+
return Some(bytes_to_name(constant_read.name().as_slice()));
|
|
167
172
|
}
|
|
168
173
|
|
|
169
174
|
// Constant path: `Api::User` or `Api::V1::User`
|
|
@@ -171,7 +176,7 @@ pub(crate) fn extract_constant_path(node: &Node) -> Option<String> {
|
|
|
171
176
|
// name() returns Option<ConstantId>, use as_slice() to get &[u8]
|
|
172
177
|
let name = constant_path
|
|
173
178
|
.name()
|
|
174
|
-
.map(|id|
|
|
179
|
+
.map(|id| bytes_to_name(id.as_slice()))?;
|
|
175
180
|
|
|
176
181
|
// Get parent path if exists
|
|
177
182
|
if let Some(parent_node) = constant_path.parent() {
|