method-ray 0.1.8 → 0.1.9
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 +18 -0
- data/{rust → core}/Cargo.toml +1 -1
- data/core/src/analyzer/assignments.rs +499 -0
- data/{rust → core}/src/analyzer/blocks.rs +140 -0
- data/{rust → core}/src/analyzer/definitions.rs +6 -5
- data/{rust → core}/src/analyzer/dispatch.rs +295 -31
- data/{rust → core}/src/analyzer/exceptions.rs +104 -3
- data/{rust → core}/src/analyzer/install.rs +16 -1
- data/{rust → core}/src/analyzer/literals.rs +3 -17
- data/{rust → core}/src/analyzer/loops.rs +126 -1
- data/{rust → core}/src/analyzer/mod.rs +1 -0
- data/{rust → core}/src/analyzer/parameters.rs +160 -0
- data/core/src/analyzer/super_calls.rs +285 -0
- data/{rust → core}/src/env/global_env.rs +18 -4
- data/{rust → core}/src/env/method_registry.rs +109 -23
- data/{rust → core}/src/env/scope.rs +78 -0
- 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 +54 -53
- data/rust/src/analyzer/assignments.rs +0 -152
- /data/{rust → core}/src/analyzer/attributes.rs +0 -0
- /data/{rust → core}/src/analyzer/calls.rs +0 -0
- /data/{rust → core}/src/analyzer/conditionals.rs +0 -0
- /data/{rust → core}/src/analyzer/operators.rs +0 -0
- /data/{rust → core}/src/analyzer/parentheses.rs +0 -0
- /data/{rust → core}/src/analyzer/returns.rs +0 -0
- /data/{rust → core}/src/analyzer/variables.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/checker.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/diagnostic.rs +0 -0
- /data/{rust → core}/src/diagnostics/formatter.rs +0 -0
- /data/{rust → core}/src/diagnostics/mod.rs +0 -0
- /data/{rust → core}/src/env/box_manager.rs +0 -0
- /data/{rust → core}/src/env/local_env.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/env/vertex_manager.rs +0 -0
- /data/{rust → core}/src/graph/box.rs +0 -0
- /data/{rust → core}/src/graph/change_set.rs +0 -0
- /data/{rust → core}/src/graph/mod.rs +0 -0
- /data/{rust → core}/src/graph/vertex.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/parser.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
|
@@ -37,6 +37,10 @@ pub struct GlobalEnv {
|
|
|
37
37
|
|
|
38
38
|
/// Scope management
|
|
39
39
|
pub scope_manager: ScopeManager,
|
|
40
|
+
|
|
41
|
+
/// Module inclusions: class_name → Vec<module_name> (in include order)
|
|
42
|
+
module_inclusions: HashMap<String, Vec<String>>,
|
|
43
|
+
|
|
40
44
|
}
|
|
41
45
|
|
|
42
46
|
impl GlobalEnv {
|
|
@@ -47,6 +51,7 @@ impl GlobalEnv {
|
|
|
47
51
|
method_registry: MethodRegistry::new(),
|
|
48
52
|
type_errors: Vec::new(),
|
|
49
53
|
scope_manager: ScopeManager::new(),
|
|
54
|
+
module_inclusions: HashMap::new(),
|
|
50
55
|
}
|
|
51
56
|
}
|
|
52
57
|
|
|
@@ -158,7 +163,16 @@ impl GlobalEnv {
|
|
|
158
163
|
|
|
159
164
|
/// Resolve method
|
|
160
165
|
pub fn resolve_method(&self, recv_ty: &Type, method_name: &str) -> Option<&MethodInfo> {
|
|
161
|
-
self.method_registry
|
|
166
|
+
self.method_registry
|
|
167
|
+
.resolve(recv_ty, method_name, &self.module_inclusions)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/// Record that a class includes a module
|
|
171
|
+
pub fn record_include(&mut self, class_name: &str, module_name: &str) {
|
|
172
|
+
self.module_inclusions
|
|
173
|
+
.entry(class_name.to_string())
|
|
174
|
+
.or_default()
|
|
175
|
+
.push(module_name.to_string());
|
|
162
176
|
}
|
|
163
177
|
|
|
164
178
|
/// Register built-in method
|
|
@@ -225,11 +239,11 @@ impl GlobalEnv {
|
|
|
225
239
|
}
|
|
226
240
|
}
|
|
227
241
|
|
|
228
|
-
/// Enter a class scope
|
|
229
|
-
pub fn enter_class(&mut self, name: String) -> ScopeId {
|
|
242
|
+
/// Enter a class scope with optional superclass
|
|
243
|
+
pub fn enter_class(&mut self, name: String, superclass: Option<&str>) -> ScopeId {
|
|
230
244
|
let scope_id = self.scope_manager.new_scope(ScopeKind::Class {
|
|
231
245
|
name: name.clone(),
|
|
232
|
-
superclass:
|
|
246
|
+
superclass: superclass.map(|s| s.to_string()),
|
|
233
247
|
});
|
|
234
248
|
self.scope_manager.enter_scope(scope_id);
|
|
235
249
|
self.register_constant_in_parent(scope_id, &name);
|
|
@@ -84,9 +84,13 @@ impl MethodRegistry {
|
|
|
84
84
|
/// Returns a list of types to search in order:
|
|
85
85
|
/// 1. Exact receiver type
|
|
86
86
|
/// 2. Generic → base class (e.g., Array[Integer] → Array)
|
|
87
|
-
/// 3.
|
|
88
|
-
/// 4.
|
|
89
|
-
|
|
87
|
+
/// 3. Included modules (last included first, matching Ruby MRO)
|
|
88
|
+
/// 4. Object (for Instance/Generic types only)
|
|
89
|
+
/// 5. Kernel (for Instance/Generic types only)
|
|
90
|
+
fn fallback_chain(
|
|
91
|
+
recv_ty: &Type,
|
|
92
|
+
inclusions: &HashMap<String, Vec<String>>,
|
|
93
|
+
) -> SmallVec<[Type; 8]> {
|
|
90
94
|
let mut chain = SmallVec::new();
|
|
91
95
|
chain.push(recv_ty.clone());
|
|
92
96
|
|
|
@@ -94,9 +98,17 @@ impl MethodRegistry {
|
|
|
94
98
|
chain.push(Type::Instance { name: name.clone() });
|
|
95
99
|
}
|
|
96
100
|
|
|
97
|
-
//
|
|
98
|
-
// due to lack of Type::Module variant.
|
|
101
|
+
// MRO for Instance/Generic: included modules → Object → Kernel
|
|
99
102
|
if matches!(recv_ty, Type::Instance { .. } | Type::Generic { .. }) {
|
|
103
|
+
// Included modules (reverse order = last included has highest priority)
|
|
104
|
+
if let Some(class_name) = recv_ty.base_class_name() {
|
|
105
|
+
if let Some(modules) = inclusions.get(class_name) {
|
|
106
|
+
for module_name in modules.iter().rev() {
|
|
107
|
+
chain.push(Type::instance(module_name));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
100
112
|
chain.push(Type::instance(OBJECT_CLASS));
|
|
101
113
|
chain.push(Type::instance(KERNEL_MODULE));
|
|
102
114
|
}
|
|
@@ -106,11 +118,17 @@ impl MethodRegistry {
|
|
|
106
118
|
|
|
107
119
|
/// Resolve a method for a receiver type
|
|
108
120
|
///
|
|
109
|
-
/// Searches the MRO fallback chain: exact type → base class (for generics)
|
|
121
|
+
/// Searches the MRO fallback chain: exact type → base class (for generics)
|
|
122
|
+
/// → included modules → Object → Kernel.
|
|
110
123
|
/// For non-instance types (Singleton, Nil, Union, Bot), only exact match is attempted.
|
|
111
|
-
pub fn resolve(
|
|
124
|
+
pub fn resolve(
|
|
125
|
+
&self,
|
|
126
|
+
recv_ty: &Type,
|
|
127
|
+
method_name: &str,
|
|
128
|
+
inclusions: &HashMap<String, Vec<String>>,
|
|
129
|
+
) -> Option<&MethodInfo> {
|
|
112
130
|
let method_key = method_name.to_string();
|
|
113
|
-
Self::fallback_chain(recv_ty)
|
|
131
|
+
Self::fallback_chain(recv_ty, inclusions)
|
|
114
132
|
.into_iter()
|
|
115
133
|
.find_map(|ty| self.methods.get(&(ty, method_key.clone())))
|
|
116
134
|
}
|
|
@@ -125,14 +143,14 @@ mod tests {
|
|
|
125
143
|
let mut registry = MethodRegistry::new();
|
|
126
144
|
registry.register(Type::string(), "length", Type::integer());
|
|
127
145
|
|
|
128
|
-
let info = registry.resolve(&Type::string(), "length").unwrap();
|
|
146
|
+
let info = registry.resolve(&Type::string(), "length", &HashMap::new()).unwrap();
|
|
129
147
|
assert_eq!(info.return_type.base_class_name(), Some("Integer"));
|
|
130
148
|
}
|
|
131
149
|
|
|
132
150
|
#[test]
|
|
133
151
|
fn test_resolve_not_found() {
|
|
134
152
|
let registry = MethodRegistry::new();
|
|
135
|
-
assert!(registry.resolve(&Type::string(), "unknown").is_none());
|
|
153
|
+
assert!(registry.resolve(&Type::string(), "unknown", &HashMap::new()).is_none());
|
|
136
154
|
}
|
|
137
155
|
|
|
138
156
|
#[test]
|
|
@@ -141,7 +159,7 @@ mod tests {
|
|
|
141
159
|
let return_vtx = VertexId(42);
|
|
142
160
|
registry.register_user_method(Type::instance("User"), "name", return_vtx, vec![], None);
|
|
143
161
|
|
|
144
|
-
let info = registry.resolve(&Type::instance("User"), "name").unwrap();
|
|
162
|
+
let info = registry.resolve(&Type::instance("User"), "name", &HashMap::new()).unwrap();
|
|
145
163
|
assert_eq!(info.return_vertex, Some(VertexId(42)));
|
|
146
164
|
assert_eq!(info.return_type, Type::Bot);
|
|
147
165
|
}
|
|
@@ -159,7 +177,7 @@ mod tests {
|
|
|
159
177
|
None,
|
|
160
178
|
);
|
|
161
179
|
|
|
162
|
-
let info = registry.resolve(&Type::instance("Calc"), "add").unwrap();
|
|
180
|
+
let info = registry.resolve(&Type::instance("Calc"), "add", &HashMap::new()).unwrap();
|
|
163
181
|
assert_eq!(info.return_vertex, Some(VertexId(10)));
|
|
164
182
|
let pvs = info.param_vertices.as_ref().unwrap();
|
|
165
183
|
assert_eq!(pvs.len(), 2);
|
|
@@ -173,7 +191,7 @@ mod tests {
|
|
|
173
191
|
fn test_resolve_falls_back_to_object() {
|
|
174
192
|
let mut registry = MethodRegistry::new();
|
|
175
193
|
registry.register(Type::instance("Object"), "nil?", Type::instance("TrueClass"));
|
|
176
|
-
let info = registry.resolve(&Type::instance("CustomClass"), "nil?").unwrap();
|
|
194
|
+
let info = registry.resolve(&Type::instance("CustomClass"), "nil?", &HashMap::new()).unwrap();
|
|
177
195
|
assert_eq!(info.return_type.base_class_name(), Some("TrueClass"));
|
|
178
196
|
}
|
|
179
197
|
|
|
@@ -181,7 +199,7 @@ mod tests {
|
|
|
181
199
|
fn test_resolve_falls_back_to_kernel() {
|
|
182
200
|
let mut registry = MethodRegistry::new();
|
|
183
201
|
registry.register(Type::instance("Kernel"), "puts", Type::Nil);
|
|
184
|
-
let info = registry.resolve(&Type::instance("MyApp"), "puts").unwrap();
|
|
202
|
+
let info = registry.resolve(&Type::instance("MyApp"), "puts", &HashMap::new()).unwrap();
|
|
185
203
|
assert_eq!(info.return_type, Type::Nil);
|
|
186
204
|
}
|
|
187
205
|
|
|
@@ -190,7 +208,7 @@ mod tests {
|
|
|
190
208
|
let mut registry = MethodRegistry::new();
|
|
191
209
|
registry.register(Type::instance("Object"), "to_s", Type::string());
|
|
192
210
|
registry.register(Type::instance("Kernel"), "to_s", Type::integer());
|
|
193
|
-
let info = registry.resolve(&Type::instance("Anything"), "to_s").unwrap();
|
|
211
|
+
let info = registry.resolve(&Type::instance("Anything"), "to_s", &HashMap::new()).unwrap();
|
|
194
212
|
assert_eq!(info.return_type.base_class_name(), Some("String"));
|
|
195
213
|
}
|
|
196
214
|
|
|
@@ -199,7 +217,7 @@ mod tests {
|
|
|
199
217
|
let mut registry = MethodRegistry::new();
|
|
200
218
|
registry.register(Type::string(), "length", Type::integer());
|
|
201
219
|
registry.register(Type::instance("Object"), "length", Type::string());
|
|
202
|
-
let info = registry.resolve(&Type::string(), "length").unwrap();
|
|
220
|
+
let info = registry.resolve(&Type::string(), "length", &HashMap::new()).unwrap();
|
|
203
221
|
assert_eq!(info.return_type.base_class_name(), Some("Integer"));
|
|
204
222
|
}
|
|
205
223
|
|
|
@@ -209,14 +227,14 @@ mod tests {
|
|
|
209
227
|
fn test_singleton_type_skips_fallback() {
|
|
210
228
|
let mut registry = MethodRegistry::new();
|
|
211
229
|
registry.register(Type::instance("Kernel"), "puts", Type::Nil);
|
|
212
|
-
assert!(registry.resolve(&Type::singleton("User"), "puts").is_none());
|
|
230
|
+
assert!(registry.resolve(&Type::singleton("User"), "puts", &HashMap::new()).is_none());
|
|
213
231
|
}
|
|
214
232
|
|
|
215
233
|
#[test]
|
|
216
234
|
fn test_nil_type_skips_fallback() {
|
|
217
235
|
let mut registry = MethodRegistry::new();
|
|
218
236
|
registry.register(Type::instance("Kernel"), "puts", Type::Nil);
|
|
219
|
-
assert!(registry.resolve(&Type::Nil, "puts").is_none());
|
|
237
|
+
assert!(registry.resolve(&Type::Nil, "puts", &HashMap::new()).is_none());
|
|
220
238
|
}
|
|
221
239
|
|
|
222
240
|
#[test]
|
|
@@ -224,14 +242,14 @@ mod tests {
|
|
|
224
242
|
let mut registry = MethodRegistry::new();
|
|
225
243
|
registry.register(Type::instance("Kernel"), "puts", Type::Nil);
|
|
226
244
|
let union_ty = Type::Union(vec![Type::string(), Type::integer()]);
|
|
227
|
-
assert!(registry.resolve(&union_ty, "puts").is_none());
|
|
245
|
+
assert!(registry.resolve(&union_ty, "puts", &HashMap::new()).is_none());
|
|
228
246
|
}
|
|
229
247
|
|
|
230
248
|
#[test]
|
|
231
249
|
fn test_bot_type_skips_fallback() {
|
|
232
250
|
let mut registry = MethodRegistry::new();
|
|
233
251
|
registry.register(Type::instance("Kernel"), "puts", Type::Nil);
|
|
234
|
-
assert!(registry.resolve(&Type::Bot, "puts").is_none());
|
|
252
|
+
assert!(registry.resolve(&Type::Bot, "puts", &HashMap::new()).is_none());
|
|
235
253
|
}
|
|
236
254
|
|
|
237
255
|
// --- Generic type fallback chain ---
|
|
@@ -241,7 +259,7 @@ mod tests {
|
|
|
241
259
|
let mut registry = MethodRegistry::new();
|
|
242
260
|
registry.register(Type::instance("Kernel"), "puts", Type::Nil);
|
|
243
261
|
let generic_type = Type::array_of(Type::integer());
|
|
244
|
-
let info = registry.resolve(&generic_type, "puts").unwrap();
|
|
262
|
+
let info = registry.resolve(&generic_type, "puts", &HashMap::new()).unwrap();
|
|
245
263
|
assert_eq!(info.return_type, Type::Nil);
|
|
246
264
|
}
|
|
247
265
|
|
|
@@ -252,7 +270,7 @@ mod tests {
|
|
|
252
270
|
registry.register(Type::instance("Kernel"), "object_id", Type::integer());
|
|
253
271
|
let generic_type = Type::array_of(Type::string());
|
|
254
272
|
// Array[String] → Array (none) → Object (none) → Kernel (exists)
|
|
255
|
-
let info = registry.resolve(&generic_type, "object_id").unwrap();
|
|
273
|
+
let info = registry.resolve(&generic_type, "object_id", &HashMap::new()).unwrap();
|
|
256
274
|
assert_eq!(info.return_type.base_class_name(), Some("Integer"));
|
|
257
275
|
}
|
|
258
276
|
|
|
@@ -262,7 +280,75 @@ mod tests {
|
|
|
262
280
|
fn test_resolve_namespaced_class_falls_back_to_object() {
|
|
263
281
|
let mut registry = MethodRegistry::new();
|
|
264
282
|
registry.register(Type::instance("Object"), "class", Type::string());
|
|
265
|
-
let info = registry.resolve(&Type::instance("Api::V1::User"), "class").unwrap();
|
|
283
|
+
let info = registry.resolve(&Type::instance("Api::V1::User"), "class", &HashMap::new()).unwrap();
|
|
284
|
+
assert_eq!(info.return_type.base_class_name(), Some("String"));
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// --- Include (mixin) fallback ---
|
|
288
|
+
|
|
289
|
+
#[test]
|
|
290
|
+
fn test_resolve_with_include() {
|
|
291
|
+
let mut registry = MethodRegistry::new();
|
|
292
|
+
registry.register(Type::instance("Greetable"), "greet", Type::string());
|
|
293
|
+
|
|
294
|
+
let mut inclusions = HashMap::new();
|
|
295
|
+
inclusions.insert("User".to_string(), vec!["Greetable".to_string()]);
|
|
296
|
+
|
|
297
|
+
let info = registry.resolve(&Type::instance("User"), "greet", &inclusions).unwrap();
|
|
266
298
|
assert_eq!(info.return_type.base_class_name(), Some("String"));
|
|
267
299
|
}
|
|
300
|
+
|
|
301
|
+
#[test]
|
|
302
|
+
fn test_resolve_include_order() {
|
|
303
|
+
// include A; include B → B's method found first (MRO: last included wins)
|
|
304
|
+
let mut registry = MethodRegistry::new();
|
|
305
|
+
registry.register(Type::instance("A"), "foo", Type::string());
|
|
306
|
+
registry.register(Type::instance("B"), "foo", Type::integer());
|
|
307
|
+
|
|
308
|
+
let mut inclusions = HashMap::new();
|
|
309
|
+
inclusions.insert("User".to_string(), vec!["A".to_string(), "B".to_string()]);
|
|
310
|
+
|
|
311
|
+
let info = registry.resolve(&Type::instance("User"), "foo", &inclusions).unwrap();
|
|
312
|
+
assert_eq!(info.return_type.base_class_name(), Some("Integer"));
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
#[test]
|
|
316
|
+
fn test_resolve_class_method_over_include() {
|
|
317
|
+
// Class's own method takes priority over included module
|
|
318
|
+
let mut registry = MethodRegistry::new();
|
|
319
|
+
registry.register(Type::instance("Greetable"), "greet", Type::string());
|
|
320
|
+
registry.register(Type::instance("User"), "greet", Type::integer());
|
|
321
|
+
|
|
322
|
+
let mut inclusions = HashMap::new();
|
|
323
|
+
inclusions.insert("User".to_string(), vec!["Greetable".to_string()]);
|
|
324
|
+
|
|
325
|
+
let info = registry.resolve(&Type::instance("User"), "greet", &inclusions).unwrap();
|
|
326
|
+
assert_eq!(info.return_type.base_class_name(), Some("Integer"));
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
#[test]
|
|
330
|
+
fn test_resolve_include_before_object() {
|
|
331
|
+
// Included module is searched before Object in MRO
|
|
332
|
+
let mut registry = MethodRegistry::new();
|
|
333
|
+
registry.register(Type::instance("Object"), "foo", Type::string());
|
|
334
|
+
registry.register(Type::instance("MyModule"), "foo", Type::integer());
|
|
335
|
+
|
|
336
|
+
let mut inclusions = HashMap::new();
|
|
337
|
+
inclusions.insert("User".to_string(), vec!["MyModule".to_string()]);
|
|
338
|
+
|
|
339
|
+
let info = registry.resolve(&Type::instance("User"), "foo", &inclusions).unwrap();
|
|
340
|
+
assert_eq!(info.return_type.base_class_name(), Some("Integer"));
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
#[test]
|
|
344
|
+
fn test_singleton_type_skips_include_fallback() {
|
|
345
|
+
// include adds instance methods only — Singleton (class-level) should NOT resolve
|
|
346
|
+
let mut registry = MethodRegistry::new();
|
|
347
|
+
registry.register(Type::instance("Greetable"), "greet", Type::string());
|
|
348
|
+
|
|
349
|
+
let mut inclusions = HashMap::new();
|
|
350
|
+
inclusions.insert("User".to_string(), vec!["Greetable".to_string()]);
|
|
351
|
+
|
|
352
|
+
assert!(registry.resolve(&Type::singleton("User"), "greet", &inclusions).is_none());
|
|
353
|
+
}
|
|
268
354
|
}
|
|
@@ -245,6 +245,28 @@ impl ScopeManager {
|
|
|
245
245
|
Some(path_segments.join("::"))
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
+
/// Get current method name from nearest enclosing method scope
|
|
249
|
+
pub fn current_method_name(&self) -> Option<String> {
|
|
250
|
+
self.walk_scopes().find_map(|scope| {
|
|
251
|
+
if let ScopeKind::Method { name, .. } = &scope.kind {
|
|
252
|
+
Some(name.clone())
|
|
253
|
+
} else {
|
|
254
|
+
None
|
|
255
|
+
}
|
|
256
|
+
})
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/// Get superclass name from nearest enclosing class scope
|
|
260
|
+
pub fn current_superclass(&self) -> Option<String> {
|
|
261
|
+
self.walk_scopes().find_map(|scope| {
|
|
262
|
+
if let ScopeKind::Class { superclass, .. } = &scope.kind {
|
|
263
|
+
superclass.clone()
|
|
264
|
+
} else {
|
|
265
|
+
None
|
|
266
|
+
}
|
|
267
|
+
})
|
|
268
|
+
}
|
|
269
|
+
|
|
248
270
|
/// Get return_vertex from the nearest enclosing method scope
|
|
249
271
|
pub fn current_method_return_vertex(&self) -> Option<VertexId> {
|
|
250
272
|
self.walk_scopes().find_map(|scope| {
|
|
@@ -593,4 +615,60 @@ mod tests {
|
|
|
593
615
|
// Inside Admin scope, User should resolve to Admin::User
|
|
594
616
|
assert_eq!(sm.lookup_constant("User"), Some("Admin::User".to_string()));
|
|
595
617
|
}
|
|
618
|
+
|
|
619
|
+
#[test]
|
|
620
|
+
fn test_current_method_name() {
|
|
621
|
+
let mut sm = ScopeManager::new();
|
|
622
|
+
let class_id = sm.new_scope(ScopeKind::Class {
|
|
623
|
+
name: "User".to_string(),
|
|
624
|
+
superclass: None,
|
|
625
|
+
});
|
|
626
|
+
sm.enter_scope(class_id);
|
|
627
|
+
let method_id = sm.new_scope(ScopeKind::Method {
|
|
628
|
+
name: "greet".to_string(),
|
|
629
|
+
receiver_type: None,
|
|
630
|
+
return_vertex: None,
|
|
631
|
+
});
|
|
632
|
+
sm.enter_scope(method_id);
|
|
633
|
+
assert_eq!(sm.current_method_name(), Some("greet".to_string()));
|
|
634
|
+
|
|
635
|
+
sm.exit_scope();
|
|
636
|
+
assert_eq!(sm.current_method_name(), None);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
#[test]
|
|
640
|
+
fn test_current_superclass() {
|
|
641
|
+
let mut sm = ScopeManager::new();
|
|
642
|
+
let class_id = sm.new_scope(ScopeKind::Class {
|
|
643
|
+
name: "Dog".to_string(),
|
|
644
|
+
superclass: Some("Animal".to_string()),
|
|
645
|
+
});
|
|
646
|
+
sm.enter_scope(class_id);
|
|
647
|
+
assert_eq!(sm.current_superclass(), Some("Animal".to_string()));
|
|
648
|
+
|
|
649
|
+
sm.exit_scope();
|
|
650
|
+
assert_eq!(sm.current_superclass(), None);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
#[test]
|
|
654
|
+
fn test_current_method_name_from_nested_block() {
|
|
655
|
+
let mut sm = ScopeManager::new();
|
|
656
|
+
let class_id = sm.new_scope(ScopeKind::Class {
|
|
657
|
+
name: "User".to_string(),
|
|
658
|
+
superclass: Some("Base".to_string()),
|
|
659
|
+
});
|
|
660
|
+
sm.enter_scope(class_id);
|
|
661
|
+
let method_id = sm.new_scope(ScopeKind::Method {
|
|
662
|
+
name: "process".to_string(),
|
|
663
|
+
receiver_type: None,
|
|
664
|
+
return_vertex: None,
|
|
665
|
+
});
|
|
666
|
+
sm.enter_scope(method_id);
|
|
667
|
+
let block_id = sm.new_scope(ScopeKind::Block);
|
|
668
|
+
sm.enter_scope(block_id);
|
|
669
|
+
|
|
670
|
+
// Inside a block, should still find enclosing method/superclass
|
|
671
|
+
assert_eq!(sm.current_method_name(), Some("process".to_string()));
|
|
672
|
+
assert_eq!(sm.current_superclass(), Some("Base".to_string()));
|
|
673
|
+
}
|
|
596
674
|
}
|
data/{rust → core}/src/types.rs
RENAMED
|
@@ -276,6 +276,17 @@ impl Type {
|
|
|
276
276
|
type_args: vec![element_type],
|
|
277
277
|
}
|
|
278
278
|
}
|
|
279
|
+
|
|
280
|
+
/// Collapse a Vec<Type> into a single Type or Union.
|
|
281
|
+
/// Returns the single element if len==1, or Union if len>1.
|
|
282
|
+
/// Panics if the vec is empty.
|
|
283
|
+
pub fn union_of(mut types: Vec<Type>) -> Self {
|
|
284
|
+
match types.len() {
|
|
285
|
+
0 => panic!("union_of called with empty types"),
|
|
286
|
+
1 => types.pop().unwrap(),
|
|
287
|
+
_ => Type::Union(types),
|
|
288
|
+
}
|
|
289
|
+
}
|
|
279
290
|
}
|
|
280
291
|
|
|
281
292
|
#[cfg(test)]
|
data/ext/Cargo.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "methodray"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.9"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
|
|
6
6
|
[lib]
|
|
@@ -18,7 +18,7 @@ default = []
|
|
|
18
18
|
cli = ["methodray-core/cli", "dep:clap", "dep:anyhow"]
|
|
19
19
|
|
|
20
20
|
[dependencies]
|
|
21
|
-
methodray-core = { path = "../
|
|
21
|
+
methodray-core = { path = "../core", features = ["ruby-ffi"] }
|
|
22
22
|
magnus = "0.8"
|
|
23
23
|
clap = { version = "4", features = ["derive"], optional = true }
|
|
24
24
|
anyhow = { version = "1", optional = true }
|
|
@@ -21,8 +21,8 @@ module MethodRay
|
|
|
21
21
|
File.expand_path(@binary_name, LIB_DIR),
|
|
22
22
|
# Development: target/release (project root)
|
|
23
23
|
File.expand_path("../../target/release/#{@binary_name}", LIB_DIR),
|
|
24
|
-
# Development:
|
|
25
|
-
File.expand_path("../../
|
|
24
|
+
# Development: core/target/release (legacy standalone binary)
|
|
25
|
+
File.expand_path("../../core/target/release/#{@legacy_binary_name}", LIB_DIR)
|
|
26
26
|
]
|
|
27
27
|
end
|
|
28
28
|
end
|
data/lib/methodray/commands.rb
CHANGED
|
@@ -46,7 +46,7 @@ module MethodRay
|
|
|
46
46
|
warn 'Error: CLI binary not found.'
|
|
47
47
|
warn ''
|
|
48
48
|
warn 'For development, build with:'
|
|
49
|
-
warn ' cd
|
|
49
|
+
warn ' cd core && cargo build --release --bin methodray --features cli'
|
|
50
50
|
warn ''
|
|
51
51
|
warn 'If installed via gem, this might be a platform compatibility issue.'
|
|
52
52
|
warn 'Please report at: https://github.com/dak2/method-ray/issues'
|
data/lib/methodray/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: method-ray
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- dak2
|
|
@@ -37,6 +37,59 @@ files:
|
|
|
37
37
|
- CHANGELOG.md
|
|
38
38
|
- LICENSE
|
|
39
39
|
- README.md
|
|
40
|
+
- core/Cargo.toml
|
|
41
|
+
- core/src/analyzer/assignments.rs
|
|
42
|
+
- core/src/analyzer/attributes.rs
|
|
43
|
+
- core/src/analyzer/blocks.rs
|
|
44
|
+
- core/src/analyzer/calls.rs
|
|
45
|
+
- core/src/analyzer/conditionals.rs
|
|
46
|
+
- core/src/analyzer/definitions.rs
|
|
47
|
+
- core/src/analyzer/dispatch.rs
|
|
48
|
+
- core/src/analyzer/exceptions.rs
|
|
49
|
+
- core/src/analyzer/install.rs
|
|
50
|
+
- core/src/analyzer/literals.rs
|
|
51
|
+
- core/src/analyzer/loops.rs
|
|
52
|
+
- core/src/analyzer/mod.rs
|
|
53
|
+
- core/src/analyzer/operators.rs
|
|
54
|
+
- core/src/analyzer/parameters.rs
|
|
55
|
+
- core/src/analyzer/parentheses.rs
|
|
56
|
+
- core/src/analyzer/returns.rs
|
|
57
|
+
- core/src/analyzer/super_calls.rs
|
|
58
|
+
- core/src/analyzer/variables.rs
|
|
59
|
+
- core/src/cache/mod.rs
|
|
60
|
+
- core/src/cache/rbs_cache.rs
|
|
61
|
+
- core/src/checker.rs
|
|
62
|
+
- core/src/cli/args.rs
|
|
63
|
+
- core/src/cli/commands.rs
|
|
64
|
+
- core/src/cli/mod.rs
|
|
65
|
+
- core/src/diagnostics/diagnostic.rs
|
|
66
|
+
- core/src/diagnostics/formatter.rs
|
|
67
|
+
- core/src/diagnostics/mod.rs
|
|
68
|
+
- core/src/env/box_manager.rs
|
|
69
|
+
- core/src/env/global_env.rs
|
|
70
|
+
- core/src/env/local_env.rs
|
|
71
|
+
- core/src/env/method_registry.rs
|
|
72
|
+
- core/src/env/mod.rs
|
|
73
|
+
- core/src/env/scope.rs
|
|
74
|
+
- core/src/env/type_error.rs
|
|
75
|
+
- core/src/env/vertex_manager.rs
|
|
76
|
+
- core/src/graph/box.rs
|
|
77
|
+
- core/src/graph/change_set.rs
|
|
78
|
+
- core/src/graph/mod.rs
|
|
79
|
+
- core/src/graph/vertex.rs
|
|
80
|
+
- core/src/lib.rs
|
|
81
|
+
- core/src/lsp/diagnostics.rs
|
|
82
|
+
- core/src/lsp/main.rs
|
|
83
|
+
- core/src/lsp/mod.rs
|
|
84
|
+
- core/src/lsp/server.rs
|
|
85
|
+
- core/src/main.rs
|
|
86
|
+
- core/src/parser.rs
|
|
87
|
+
- core/src/rbs/converter.rs
|
|
88
|
+
- core/src/rbs/error.rs
|
|
89
|
+
- core/src/rbs/loader.rs
|
|
90
|
+
- core/src/rbs/mod.rs
|
|
91
|
+
- core/src/source_map.rs
|
|
92
|
+
- core/src/types.rs
|
|
40
93
|
- exe/methodray
|
|
41
94
|
- ext/Cargo.toml
|
|
42
95
|
- ext/extconf.rb
|
|
@@ -47,58 +100,6 @@ files:
|
|
|
47
100
|
- lib/methodray/cli.rb
|
|
48
101
|
- lib/methodray/commands.rb
|
|
49
102
|
- lib/methodray/version.rb
|
|
50
|
-
- rust/Cargo.toml
|
|
51
|
-
- rust/src/analyzer/assignments.rs
|
|
52
|
-
- rust/src/analyzer/attributes.rs
|
|
53
|
-
- rust/src/analyzer/blocks.rs
|
|
54
|
-
- rust/src/analyzer/calls.rs
|
|
55
|
-
- rust/src/analyzer/conditionals.rs
|
|
56
|
-
- rust/src/analyzer/definitions.rs
|
|
57
|
-
- rust/src/analyzer/dispatch.rs
|
|
58
|
-
- rust/src/analyzer/exceptions.rs
|
|
59
|
-
- rust/src/analyzer/install.rs
|
|
60
|
-
- rust/src/analyzer/literals.rs
|
|
61
|
-
- rust/src/analyzer/loops.rs
|
|
62
|
-
- rust/src/analyzer/mod.rs
|
|
63
|
-
- rust/src/analyzer/operators.rs
|
|
64
|
-
- rust/src/analyzer/parameters.rs
|
|
65
|
-
- rust/src/analyzer/parentheses.rs
|
|
66
|
-
- rust/src/analyzer/returns.rs
|
|
67
|
-
- rust/src/analyzer/variables.rs
|
|
68
|
-
- rust/src/cache/mod.rs
|
|
69
|
-
- rust/src/cache/rbs_cache.rs
|
|
70
|
-
- rust/src/checker.rs
|
|
71
|
-
- rust/src/cli/args.rs
|
|
72
|
-
- rust/src/cli/commands.rs
|
|
73
|
-
- rust/src/cli/mod.rs
|
|
74
|
-
- rust/src/diagnostics/diagnostic.rs
|
|
75
|
-
- rust/src/diagnostics/formatter.rs
|
|
76
|
-
- rust/src/diagnostics/mod.rs
|
|
77
|
-
- rust/src/env/box_manager.rs
|
|
78
|
-
- rust/src/env/global_env.rs
|
|
79
|
-
- rust/src/env/local_env.rs
|
|
80
|
-
- rust/src/env/method_registry.rs
|
|
81
|
-
- rust/src/env/mod.rs
|
|
82
|
-
- rust/src/env/scope.rs
|
|
83
|
-
- rust/src/env/type_error.rs
|
|
84
|
-
- rust/src/env/vertex_manager.rs
|
|
85
|
-
- rust/src/graph/box.rs
|
|
86
|
-
- rust/src/graph/change_set.rs
|
|
87
|
-
- rust/src/graph/mod.rs
|
|
88
|
-
- rust/src/graph/vertex.rs
|
|
89
|
-
- rust/src/lib.rs
|
|
90
|
-
- rust/src/lsp/diagnostics.rs
|
|
91
|
-
- rust/src/lsp/main.rs
|
|
92
|
-
- rust/src/lsp/mod.rs
|
|
93
|
-
- rust/src/lsp/server.rs
|
|
94
|
-
- rust/src/main.rs
|
|
95
|
-
- rust/src/parser.rs
|
|
96
|
-
- rust/src/rbs/converter.rs
|
|
97
|
-
- rust/src/rbs/error.rs
|
|
98
|
-
- rust/src/rbs/loader.rs
|
|
99
|
-
- rust/src/rbs/mod.rs
|
|
100
|
-
- rust/src/source_map.rs
|
|
101
|
-
- rust/src/types.rs
|
|
102
103
|
homepage: https://github.com/dak2/method-ray
|
|
103
104
|
licenses:
|
|
104
105
|
- MIT
|