method-ray 0.1.8 → 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 +38 -0
- data/README.md +9 -11
- data/{rust → core}/Cargo.toml +1 -1
- data/core/src/analyzer/assignments.rs +219 -0
- data/{rust → core}/src/analyzer/blocks.rs +0 -50
- data/{rust → core}/src/analyzer/calls.rs +3 -32
- data/core/src/analyzer/conditionals.rs +190 -0
- data/core/src/analyzer/definitions.rs +205 -0
- data/core/src/analyzer/dispatch.rs +455 -0
- data/core/src/analyzer/exceptions.rs +168 -0
- data/{rust → core}/src/analyzer/install.rs +16 -1
- data/{rust → core}/src/analyzer/literals.rs +3 -71
- data/core/src/analyzer/loops.rs +94 -0
- data/{rust → core}/src/analyzer/mod.rs +1 -15
- data/core/src/analyzer/operators.rs +79 -0
- data/{rust → core}/src/analyzer/parameters.rs +4 -67
- data/core/src/analyzer/parentheses.rs +25 -0
- data/core/src/analyzer/returns.rs +39 -0
- data/core/src/analyzer/super_calls.rs +74 -0
- data/{rust → core}/src/analyzer/variables.rs +5 -25
- data/{rust → core}/src/checker.rs +0 -13
- data/{rust → core}/src/diagnostics/diagnostic.rs +0 -41
- data/{rust → core}/src/diagnostics/formatter.rs +0 -38
- data/{rust → core}/src/env/box_manager.rs +0 -30
- data/{rust → core}/src/env/global_env.rs +67 -80
- data/core/src/env/local_env.rs +42 -0
- data/core/src/env/method_registry.rs +173 -0
- data/core/src/env/scope.rs +299 -0
- data/{rust → core}/src/env/vertex_manager.rs +0 -73
- data/core/src/graph/box.rs +347 -0
- data/{rust → core}/src/graph/change_set.rs +0 -65
- data/{rust → core}/src/graph/vertex.rs +0 -69
- data/{rust → core}/src/parser.rs +0 -77
- data/{rust → core}/src/types.rs +11 -0
- data/ext/Cargo.toml +2 -2
- data/lib/methodray/binary_locator.rb +2 -2
- data/lib/methodray/commands.rb +1 -1
- data/lib/methodray/version.rb +1 -1
- metadata +58 -56
- data/rust/src/analyzer/assignments.rs +0 -152
- data/rust/src/analyzer/conditionals.rs +0 -538
- data/rust/src/analyzer/definitions.rs +0 -719
- data/rust/src/analyzer/dispatch.rs +0 -1137
- data/rust/src/analyzer/exceptions.rs +0 -521
- data/rust/src/analyzer/loops.rs +0 -176
- data/rust/src/analyzer/operators.rs +0 -284
- data/rust/src/analyzer/parentheses.rs +0 -113
- data/rust/src/analyzer/returns.rs +0 -191
- data/rust/src/env/local_env.rs +0 -92
- data/rust/src/env/method_registry.rs +0 -268
- data/rust/src/env/scope.rs +0 -596
- data/rust/src/graph/box.rs +0 -766
- /data/{rust → core}/src/analyzer/attributes.rs +0 -0
- /data/{rust → core}/src/cache/mod.rs +0 -0
- /data/{rust → core}/src/cache/rbs_cache.rs +0 -0
- /data/{rust → core}/src/cli/args.rs +0 -0
- /data/{rust → core}/src/cli/commands.rs +0 -0
- /data/{rust → core}/src/cli/mod.rs +0 -0
- /data/{rust → core}/src/diagnostics/mod.rs +0 -0
- /data/{rust → core}/src/env/mod.rs +0 -0
- /data/{rust → core}/src/env/type_error.rs +0 -0
- /data/{rust → core}/src/graph/mod.rs +0 -0
- /data/{rust → core}/src/lib.rs +0 -0
- /data/{rust → core}/src/lsp/diagnostics.rs +0 -0
- /data/{rust → core}/src/lsp/main.rs +0 -0
- /data/{rust → core}/src/lsp/mod.rs +0 -0
- /data/{rust → core}/src/lsp/server.rs +0 -0
- /data/{rust → core}/src/main.rs +0 -0
- /data/{rust → core}/src/rbs/converter.rs +0 -0
- /data/{rust → core}/src/rbs/error.rs +0 -0
- /data/{rust → core}/src/rbs/loader.rs +0 -0
- /data/{rust → core}/src/rbs/mod.rs +0 -0
- /data/{rust → core}/src/source_map.rs +0 -0
data/rust/src/env/scope.rs
DELETED
|
@@ -1,596 +0,0 @@
|
|
|
1
|
-
use crate::graph::VertexId;
|
|
2
|
-
use std::collections::HashMap;
|
|
3
|
-
|
|
4
|
-
/// Scope ID
|
|
5
|
-
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
6
|
-
pub struct ScopeId(pub usize);
|
|
7
|
-
|
|
8
|
-
/// Scope kind
|
|
9
|
-
#[derive(Debug, Clone)]
|
|
10
|
-
pub enum ScopeKind {
|
|
11
|
-
TopLevel,
|
|
12
|
-
Class {
|
|
13
|
-
name: String,
|
|
14
|
-
superclass: Option<String>,
|
|
15
|
-
},
|
|
16
|
-
Module {
|
|
17
|
-
name: String,
|
|
18
|
-
},
|
|
19
|
-
Method {
|
|
20
|
-
name: String,
|
|
21
|
-
receiver_type: Option<String>, // Receiver class/module name
|
|
22
|
-
return_vertex: Option<VertexId>, // Merge vertex for return statements
|
|
23
|
-
},
|
|
24
|
-
Block,
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/// Scope information
|
|
28
|
-
#[derive(Debug, Clone)]
|
|
29
|
-
pub struct Scope {
|
|
30
|
-
pub id: ScopeId,
|
|
31
|
-
pub kind: ScopeKind,
|
|
32
|
-
pub parent: Option<ScopeId>,
|
|
33
|
-
|
|
34
|
-
/// Local variables
|
|
35
|
-
pub local_vars: HashMap<String, VertexId>,
|
|
36
|
-
|
|
37
|
-
/// Instance variables (class/module scope only)
|
|
38
|
-
pub instance_vars: HashMap<String, VertexId>,
|
|
39
|
-
|
|
40
|
-
/// Class variables (class scope only)
|
|
41
|
-
pub class_vars: HashMap<String, VertexId>,
|
|
42
|
-
|
|
43
|
-
/// Constants (simple name → qualified name)
|
|
44
|
-
pub constants: HashMap<String, String>,
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
impl Scope {
|
|
48
|
-
pub fn new(id: ScopeId, kind: ScopeKind, parent: Option<ScopeId>) -> Self {
|
|
49
|
-
Self {
|
|
50
|
-
id,
|
|
51
|
-
kind,
|
|
52
|
-
parent,
|
|
53
|
-
local_vars: HashMap::new(),
|
|
54
|
-
instance_vars: HashMap::new(),
|
|
55
|
-
class_vars: HashMap::new(),
|
|
56
|
-
constants: HashMap::new(),
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/// Add local variable
|
|
61
|
-
pub fn set_local_var(&mut self, name: String, vtx: VertexId) {
|
|
62
|
-
self.local_vars.insert(name, vtx);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/// Get local variable
|
|
66
|
-
pub fn get_local_var(&self, name: &str) -> Option<VertexId> {
|
|
67
|
-
self.local_vars.get(name).copied()
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/// Add instance variable
|
|
71
|
-
pub fn set_instance_var(&mut self, name: String, vtx: VertexId) {
|
|
72
|
-
self.instance_vars.insert(name, vtx);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/// Get instance variable
|
|
76
|
-
pub fn get_instance_var(&self, name: &str) -> Option<VertexId> {
|
|
77
|
-
self.instance_vars.get(name).copied()
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/// Scope manager
|
|
82
|
-
#[derive(Debug)]
|
|
83
|
-
pub struct ScopeManager {
|
|
84
|
-
scopes: HashMap<ScopeId, Scope>,
|
|
85
|
-
next_id: usize,
|
|
86
|
-
current_scope: ScopeId,
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
impl Default for ScopeManager {
|
|
90
|
-
fn default() -> Self {
|
|
91
|
-
Self::new()
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
impl ScopeManager {
|
|
96
|
-
pub fn new() -> Self {
|
|
97
|
-
let top_level = Scope::new(ScopeId(0), ScopeKind::TopLevel, None);
|
|
98
|
-
|
|
99
|
-
let mut scopes = HashMap::new();
|
|
100
|
-
scopes.insert(ScopeId(0), top_level);
|
|
101
|
-
|
|
102
|
-
Self {
|
|
103
|
-
scopes,
|
|
104
|
-
next_id: 1,
|
|
105
|
-
current_scope: ScopeId(0),
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/// Create a new scope
|
|
110
|
-
pub fn new_scope(&mut self, kind: ScopeKind) -> ScopeId {
|
|
111
|
-
let id = ScopeId(self.next_id);
|
|
112
|
-
self.next_id += 1;
|
|
113
|
-
|
|
114
|
-
let scope = Scope::new(id, kind, Some(self.current_scope));
|
|
115
|
-
self.scopes.insert(id, scope);
|
|
116
|
-
|
|
117
|
-
id
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/// Enter a scope
|
|
121
|
-
pub fn enter_scope(&mut self, scope_id: ScopeId) {
|
|
122
|
-
self.current_scope = scope_id;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/// Exit current scope
|
|
126
|
-
pub fn exit_scope(&mut self) {
|
|
127
|
-
if let Some(scope) = self.scopes.get(&self.current_scope) {
|
|
128
|
-
if let Some(parent) = scope.parent {
|
|
129
|
-
self.current_scope = parent;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/// Get current scope
|
|
135
|
-
pub fn current_scope(&self) -> &Scope {
|
|
136
|
-
self.scopes.get(&self.current_scope).unwrap()
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/// Get current scope mutably
|
|
140
|
-
pub fn current_scope_mut(&mut self) -> &mut Scope {
|
|
141
|
-
self.scopes.get_mut(&self.current_scope).unwrap()
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/// Walk scopes from current scope up to the top-level, yielding each scope
|
|
145
|
-
fn walk_scopes(&self) -> impl Iterator<Item = &Scope> + '_ {
|
|
146
|
-
let scopes = &self.scopes;
|
|
147
|
-
let mut current = Some(self.current_scope);
|
|
148
|
-
std::iter::from_fn(move || {
|
|
149
|
-
let scope_id = current?;
|
|
150
|
-
let scope = scopes.get(&scope_id)?;
|
|
151
|
-
current = scope.parent;
|
|
152
|
-
Some(scope)
|
|
153
|
-
})
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/// Get scope by ID
|
|
157
|
-
pub fn get_scope(&self, id: ScopeId) -> Option<&Scope> {
|
|
158
|
-
self.scopes.get(&id)
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/// Get scope by ID mutably
|
|
162
|
-
pub fn get_scope_mut(&mut self, id: ScopeId) -> Option<&mut Scope> {
|
|
163
|
-
self.scopes.get_mut(&id)
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/// Lookup variable in current scope or parent scopes
|
|
167
|
-
pub fn lookup_var(&self, name: &str) -> Option<VertexId> {
|
|
168
|
-
self.walk_scopes().find_map(|scope| scope.get_local_var(name))
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/// Lookup constant in current scope or parent scopes (simple name → qualified name)
|
|
172
|
-
pub fn lookup_constant(&self, simple_name: &str) -> Option<String> {
|
|
173
|
-
self.walk_scopes()
|
|
174
|
-
.find_map(|scope| scope.constants.get(simple_name).cloned())
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/// Lookup instance variable in enclosing class scope
|
|
178
|
-
pub fn lookup_instance_var(&self, name: &str) -> Option<VertexId> {
|
|
179
|
-
self.walk_scopes()
|
|
180
|
-
.find(|scope| matches!(&scope.kind, ScopeKind::Class { .. }))
|
|
181
|
-
.and_then(|scope| scope.get_instance_var(name))
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/// Set instance variable in enclosing class scope
|
|
185
|
-
pub fn set_instance_var_in_class(&mut self, name: String, vtx: VertexId) {
|
|
186
|
-
let class_scope_id = self.walk_scopes()
|
|
187
|
-
.find(|scope| matches!(&scope.kind, ScopeKind::Class { .. }))
|
|
188
|
-
.map(|scope| scope.id);
|
|
189
|
-
if let Some(scope_id) = class_scope_id {
|
|
190
|
-
if let Some(scope) = self.scopes.get_mut(&scope_id) {
|
|
191
|
-
scope.set_instance_var(name, vtx);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/// Get current class name (simple name, not qualified)
|
|
197
|
-
pub fn current_class_name(&self) -> Option<String> {
|
|
198
|
-
self.walk_scopes().find_map(|scope| {
|
|
199
|
-
if let ScopeKind::Class { name, .. } = &scope.kind {
|
|
200
|
-
Some(name.clone())
|
|
201
|
-
} else {
|
|
202
|
-
None
|
|
203
|
-
}
|
|
204
|
-
})
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/// Get current module name (simple name, not qualified)
|
|
208
|
-
pub fn current_module_name(&self) -> Option<String> {
|
|
209
|
-
self.walk_scopes().find_map(|scope| {
|
|
210
|
-
if let ScopeKind::Module { name } = &scope.kind {
|
|
211
|
-
Some(name.clone())
|
|
212
|
-
} else {
|
|
213
|
-
None
|
|
214
|
-
}
|
|
215
|
-
})
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/// Get current fully qualified name by traversing all parent class/module scopes
|
|
219
|
-
///
|
|
220
|
-
/// For example, in:
|
|
221
|
-
/// ```ruby
|
|
222
|
-
/// module Api
|
|
223
|
-
/// module V1
|
|
224
|
-
/// class User
|
|
225
|
-
/// def greet; end
|
|
226
|
-
/// end
|
|
227
|
-
/// end
|
|
228
|
-
/// end
|
|
229
|
-
/// ```
|
|
230
|
-
/// When inside `greet`, this returns `Some("Api::V1::User")`
|
|
231
|
-
pub fn current_qualified_name(&self) -> Option<String> {
|
|
232
|
-
let mut path_segments: Vec<&str> = self.walk_scopes()
|
|
233
|
-
.filter_map(|scope| match &scope.kind {
|
|
234
|
-
ScopeKind::Class { name, .. } | ScopeKind::Module { name } => Some(name.as_str()),
|
|
235
|
-
_ => None,
|
|
236
|
-
})
|
|
237
|
-
.collect();
|
|
238
|
-
|
|
239
|
-
if path_segments.is_empty() {
|
|
240
|
-
return None;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// Reverse to get from outermost to innermost
|
|
244
|
-
path_segments.reverse();
|
|
245
|
-
Some(path_segments.join("::"))
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/// Get return_vertex from the nearest enclosing method scope
|
|
249
|
-
pub fn current_method_return_vertex(&self) -> Option<VertexId> {
|
|
250
|
-
self.walk_scopes().find_map(|scope| {
|
|
251
|
-
if let ScopeKind::Method { return_vertex, .. } = &scope.kind {
|
|
252
|
-
*return_vertex
|
|
253
|
-
} else {
|
|
254
|
-
None
|
|
255
|
-
}
|
|
256
|
-
})
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/// Lookup instance variable in enclosing module scope
|
|
260
|
-
pub fn lookup_instance_var_in_module(&self, name: &str) -> Option<VertexId> {
|
|
261
|
-
self.walk_scopes()
|
|
262
|
-
.find(|scope| matches!(&scope.kind, ScopeKind::Module { .. }))
|
|
263
|
-
.and_then(|scope| scope.get_instance_var(name))
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/// Set instance variable in enclosing module scope
|
|
267
|
-
pub fn set_instance_var_in_module(&mut self, name: String, vtx: VertexId) {
|
|
268
|
-
let module_scope_id = self.walk_scopes()
|
|
269
|
-
.find(|scope| matches!(&scope.kind, ScopeKind::Module { .. }))
|
|
270
|
-
.map(|scope| scope.id);
|
|
271
|
-
if let Some(scope_id) = module_scope_id {
|
|
272
|
-
if let Some(scope) = self.scopes.get_mut(&scope_id) {
|
|
273
|
-
scope.set_instance_var(name, vtx);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
#[cfg(test)]
|
|
280
|
-
mod tests {
|
|
281
|
-
use super::*;
|
|
282
|
-
|
|
283
|
-
#[test]
|
|
284
|
-
fn test_scope_manager_default() {
|
|
285
|
-
let sm = ScopeManager::default();
|
|
286
|
-
assert_eq!(sm.current_scope().id, ScopeId(0));
|
|
287
|
-
assert!(matches!(sm.current_scope().kind, ScopeKind::TopLevel));
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
#[test]
|
|
291
|
-
fn test_scope_manager_creation() {
|
|
292
|
-
let sm = ScopeManager::new();
|
|
293
|
-
assert_eq!(sm.current_scope().id, ScopeId(0));
|
|
294
|
-
assert!(matches!(sm.current_scope().kind, ScopeKind::TopLevel));
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
#[test]
|
|
298
|
-
fn test_scope_manager_new_scope() {
|
|
299
|
-
let mut sm = ScopeManager::new();
|
|
300
|
-
|
|
301
|
-
let class_id = sm.new_scope(ScopeKind::Class {
|
|
302
|
-
name: "User".to_string(),
|
|
303
|
-
superclass: None,
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
assert_eq!(class_id, ScopeId(1));
|
|
307
|
-
assert_eq!(sm.current_scope().id, ScopeId(0)); // Still in top-level
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
#[test]
|
|
311
|
-
fn test_scope_manager_enter_exit() {
|
|
312
|
-
let mut sm = ScopeManager::new();
|
|
313
|
-
|
|
314
|
-
let class_id = sm.new_scope(ScopeKind::Class {
|
|
315
|
-
name: "User".to_string(),
|
|
316
|
-
superclass: None,
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
sm.enter_scope(class_id);
|
|
320
|
-
assert_eq!(sm.current_scope().id, ScopeId(1));
|
|
321
|
-
|
|
322
|
-
sm.exit_scope();
|
|
323
|
-
assert_eq!(sm.current_scope().id, ScopeId(0));
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
#[test]
|
|
327
|
-
fn test_scope_manager_local_var() {
|
|
328
|
-
let mut sm = ScopeManager::new();
|
|
329
|
-
|
|
330
|
-
sm.current_scope_mut()
|
|
331
|
-
.set_local_var("x".to_string(), VertexId(10));
|
|
332
|
-
|
|
333
|
-
assert_eq!(sm.lookup_var("x"), Some(VertexId(10)));
|
|
334
|
-
assert_eq!(sm.lookup_var("y"), None);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
#[test]
|
|
338
|
-
fn test_scope_manager_nested_lookup() {
|
|
339
|
-
let mut sm = ScopeManager::new();
|
|
340
|
-
|
|
341
|
-
// Top level: x = 10
|
|
342
|
-
sm.current_scope_mut()
|
|
343
|
-
.set_local_var("x".to_string(), VertexId(10));
|
|
344
|
-
|
|
345
|
-
// Enter class
|
|
346
|
-
let class_id = sm.new_scope(ScopeKind::Class {
|
|
347
|
-
name: "User".to_string(),
|
|
348
|
-
superclass: None,
|
|
349
|
-
});
|
|
350
|
-
sm.enter_scope(class_id);
|
|
351
|
-
|
|
352
|
-
// Class level: y = 20
|
|
353
|
-
sm.current_scope_mut()
|
|
354
|
-
.set_local_var("y".to_string(), VertexId(20));
|
|
355
|
-
|
|
356
|
-
// Can lookup both x (from parent) and y (from current)
|
|
357
|
-
assert_eq!(sm.lookup_var("x"), Some(VertexId(10)));
|
|
358
|
-
assert_eq!(sm.lookup_var("y"), Some(VertexId(20)));
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
#[test]
|
|
362
|
-
fn test_scope_manager_current_class_name() {
|
|
363
|
-
let mut sm = ScopeManager::new();
|
|
364
|
-
|
|
365
|
-
assert_eq!(sm.current_class_name(), None);
|
|
366
|
-
|
|
367
|
-
let class_id = sm.new_scope(ScopeKind::Class {
|
|
368
|
-
name: "User".to_string(),
|
|
369
|
-
superclass: None,
|
|
370
|
-
});
|
|
371
|
-
sm.enter_scope(class_id);
|
|
372
|
-
|
|
373
|
-
assert_eq!(sm.current_class_name(), Some("User".to_string()));
|
|
374
|
-
|
|
375
|
-
// Enter method within class
|
|
376
|
-
let method_id = sm.new_scope(ScopeKind::Method {
|
|
377
|
-
name: "test".to_string(),
|
|
378
|
-
receiver_type: None,
|
|
379
|
-
return_vertex: None,
|
|
380
|
-
});
|
|
381
|
-
sm.enter_scope(method_id);
|
|
382
|
-
|
|
383
|
-
// Should still find parent class name
|
|
384
|
-
assert_eq!(sm.current_class_name(), Some("User".to_string()));
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
#[test]
|
|
388
|
-
fn test_scope_manager_module_scope() {
|
|
389
|
-
let mut sm = ScopeManager::new();
|
|
390
|
-
|
|
391
|
-
assert_eq!(sm.current_module_name(), None);
|
|
392
|
-
|
|
393
|
-
let module_id = sm.new_scope(ScopeKind::Module {
|
|
394
|
-
name: "Utils".to_string(),
|
|
395
|
-
});
|
|
396
|
-
sm.enter_scope(module_id);
|
|
397
|
-
|
|
398
|
-
assert_eq!(sm.current_module_name(), Some("Utils".to_string()));
|
|
399
|
-
|
|
400
|
-
// Enter method within module
|
|
401
|
-
let method_id = sm.new_scope(ScopeKind::Method {
|
|
402
|
-
name: "helper".to_string(),
|
|
403
|
-
receiver_type: Some("Utils".to_string()),
|
|
404
|
-
return_vertex: None,
|
|
405
|
-
});
|
|
406
|
-
sm.enter_scope(method_id);
|
|
407
|
-
|
|
408
|
-
// Should still find parent module name
|
|
409
|
-
assert_eq!(sm.current_module_name(), Some("Utils".to_string()));
|
|
410
|
-
|
|
411
|
-
sm.exit_scope(); // exit method
|
|
412
|
-
sm.exit_scope(); // exit module
|
|
413
|
-
|
|
414
|
-
assert_eq!(sm.current_module_name(), None);
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
#[test]
|
|
418
|
-
fn test_scope_manager_module_instance_var() {
|
|
419
|
-
let mut sm = ScopeManager::new();
|
|
420
|
-
|
|
421
|
-
let module_id = sm.new_scope(ScopeKind::Module {
|
|
422
|
-
name: "Config".to_string(),
|
|
423
|
-
});
|
|
424
|
-
sm.enter_scope(module_id);
|
|
425
|
-
|
|
426
|
-
// Set instance variable in module
|
|
427
|
-
sm.set_instance_var_in_module("@setting".to_string(), VertexId(100));
|
|
428
|
-
|
|
429
|
-
// Enter method within module
|
|
430
|
-
let method_id = sm.new_scope(ScopeKind::Method {
|
|
431
|
-
name: "get_setting".to_string(),
|
|
432
|
-
receiver_type: Some("Config".to_string()),
|
|
433
|
-
return_vertex: None,
|
|
434
|
-
});
|
|
435
|
-
sm.enter_scope(method_id);
|
|
436
|
-
|
|
437
|
-
// Should find instance variable from module scope
|
|
438
|
-
assert_eq!(
|
|
439
|
-
sm.lookup_instance_var_in_module("@setting"),
|
|
440
|
-
Some(VertexId(100))
|
|
441
|
-
);
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
#[test]
|
|
445
|
-
fn test_current_qualified_name_simple_class() {
|
|
446
|
-
let mut sm = ScopeManager::new();
|
|
447
|
-
|
|
448
|
-
// module Api; class User; end; end
|
|
449
|
-
let class_id = sm.new_scope(ScopeKind::Class {
|
|
450
|
-
name: "User".to_string(),
|
|
451
|
-
superclass: None,
|
|
452
|
-
});
|
|
453
|
-
sm.enter_scope(class_id);
|
|
454
|
-
|
|
455
|
-
assert_eq!(
|
|
456
|
-
sm.current_qualified_name(),
|
|
457
|
-
Some("User".to_string())
|
|
458
|
-
);
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
#[test]
|
|
462
|
-
fn test_current_qualified_name_nested_module_class() {
|
|
463
|
-
let mut sm = ScopeManager::new();
|
|
464
|
-
|
|
465
|
-
// module Api
|
|
466
|
-
let api_id = sm.new_scope(ScopeKind::Module {
|
|
467
|
-
name: "Api".to_string(),
|
|
468
|
-
});
|
|
469
|
-
sm.enter_scope(api_id);
|
|
470
|
-
|
|
471
|
-
// module V1
|
|
472
|
-
let v1_id = sm.new_scope(ScopeKind::Module {
|
|
473
|
-
name: "V1".to_string(),
|
|
474
|
-
});
|
|
475
|
-
sm.enter_scope(v1_id);
|
|
476
|
-
|
|
477
|
-
// class User
|
|
478
|
-
let user_id = sm.new_scope(ScopeKind::Class {
|
|
479
|
-
name: "User".to_string(),
|
|
480
|
-
superclass: None,
|
|
481
|
-
});
|
|
482
|
-
sm.enter_scope(user_id);
|
|
483
|
-
|
|
484
|
-
assert_eq!(
|
|
485
|
-
sm.current_qualified_name(),
|
|
486
|
-
Some("Api::V1::User".to_string())
|
|
487
|
-
);
|
|
488
|
-
|
|
489
|
-
// def greet
|
|
490
|
-
let method_id = sm.new_scope(ScopeKind::Method {
|
|
491
|
-
name: "greet".to_string(),
|
|
492
|
-
receiver_type: None,
|
|
493
|
-
return_vertex: None,
|
|
494
|
-
});
|
|
495
|
-
sm.enter_scope(method_id);
|
|
496
|
-
|
|
497
|
-
// Inside method, should still get the qualified class name
|
|
498
|
-
assert_eq!(
|
|
499
|
-
sm.current_qualified_name(),
|
|
500
|
-
Some("Api::V1::User".to_string())
|
|
501
|
-
);
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
#[test]
|
|
505
|
-
fn test_current_qualified_name_with_inline_qualified_class() {
|
|
506
|
-
let mut sm = ScopeManager::new();
|
|
507
|
-
|
|
508
|
-
// class Api::User (defined at top level with qualified name)
|
|
509
|
-
let class_id = sm.new_scope(ScopeKind::Class {
|
|
510
|
-
name: "Api::User".to_string(),
|
|
511
|
-
superclass: None,
|
|
512
|
-
});
|
|
513
|
-
sm.enter_scope(class_id);
|
|
514
|
-
|
|
515
|
-
assert_eq!(
|
|
516
|
-
sm.current_qualified_name(),
|
|
517
|
-
Some("Api::User".to_string())
|
|
518
|
-
);
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
#[test]
|
|
522
|
-
fn test_current_qualified_name_at_top_level() {
|
|
523
|
-
let sm = ScopeManager::new();
|
|
524
|
-
|
|
525
|
-
// At top level, no class/module
|
|
526
|
-
assert_eq!(sm.current_qualified_name(), None);
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
#[test]
|
|
530
|
-
fn test_constant_registration_and_lookup() {
|
|
531
|
-
let mut sm = ScopeManager::new();
|
|
532
|
-
|
|
533
|
-
// module Api
|
|
534
|
-
sm.current_scope_mut().constants.insert("Api".to_string(), "Api".to_string());
|
|
535
|
-
let api_id = sm.new_scope(ScopeKind::Module { name: "Api".to_string() });
|
|
536
|
-
sm.enter_scope(api_id);
|
|
537
|
-
|
|
538
|
-
// class User (inside Api) — register in parent scope (Api)
|
|
539
|
-
sm.current_scope_mut().constants.insert("User".to_string(), "Api::User".to_string());
|
|
540
|
-
let user_id = sm.new_scope(ScopeKind::Class {
|
|
541
|
-
name: "User".to_string(),
|
|
542
|
-
superclass: None,
|
|
543
|
-
});
|
|
544
|
-
sm.enter_scope(user_id);
|
|
545
|
-
|
|
546
|
-
assert_eq!(sm.lookup_constant("User"), Some("Api::User".to_string()));
|
|
547
|
-
assert_eq!(sm.lookup_constant("Api"), Some("Api".to_string()));
|
|
548
|
-
assert_eq!(sm.lookup_constant("Unknown"), None);
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
#[test]
|
|
552
|
-
fn test_constant_lookup_from_method_scope() {
|
|
553
|
-
let mut sm = ScopeManager::new();
|
|
554
|
-
|
|
555
|
-
sm.current_scope_mut().constants.insert("Api".to_string(), "Api".to_string());
|
|
556
|
-
let api_id = sm.new_scope(ScopeKind::Module { name: "Api".to_string() });
|
|
557
|
-
sm.enter_scope(api_id);
|
|
558
|
-
|
|
559
|
-
sm.current_scope_mut().constants.insert("User".to_string(), "Api::User".to_string());
|
|
560
|
-
let user_id = sm.new_scope(ScopeKind::Class {
|
|
561
|
-
name: "User".to_string(),
|
|
562
|
-
superclass: None,
|
|
563
|
-
});
|
|
564
|
-
sm.enter_scope(user_id);
|
|
565
|
-
|
|
566
|
-
let method_id = sm.new_scope(ScopeKind::Method {
|
|
567
|
-
name: "greet".to_string(),
|
|
568
|
-
receiver_type: None,
|
|
569
|
-
return_vertex: None,
|
|
570
|
-
});
|
|
571
|
-
sm.enter_scope(method_id);
|
|
572
|
-
|
|
573
|
-
// Should find constant by traversing parent scopes from method scope
|
|
574
|
-
assert_eq!(sm.lookup_constant("User"), Some("Api::User".to_string()));
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
#[test]
|
|
578
|
-
fn test_constant_same_name_different_namespaces() {
|
|
579
|
-
let mut sm = ScopeManager::new();
|
|
580
|
-
|
|
581
|
-
// module Api
|
|
582
|
-
let api_id = sm.new_scope(ScopeKind::Module { name: "Api".to_string() });
|
|
583
|
-
sm.enter_scope(api_id);
|
|
584
|
-
sm.current_scope_mut().constants.insert("User".to_string(), "Api::User".to_string());
|
|
585
|
-
|
|
586
|
-
sm.exit_scope();
|
|
587
|
-
|
|
588
|
-
// module Admin
|
|
589
|
-
let admin_id = sm.new_scope(ScopeKind::Module { name: "Admin".to_string() });
|
|
590
|
-
sm.enter_scope(admin_id);
|
|
591
|
-
sm.current_scope_mut().constants.insert("User".to_string(), "Admin::User".to_string());
|
|
592
|
-
|
|
593
|
-
// Inside Admin scope, User should resolve to Admin::User
|
|
594
|
-
assert_eq!(sm.lookup_constant("User"), Some("Admin::User".to_string()));
|
|
595
|
-
}
|
|
596
|
-
}
|