method-ray 0.1.7 → 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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -0
  3. data/{rust → core}/Cargo.toml +1 -1
  4. data/core/src/analyzer/assignments.rs +499 -0
  5. data/{rust → core}/src/analyzer/attributes.rs +2 -1
  6. data/{rust → core}/src/analyzer/blocks.rs +140 -0
  7. data/{rust → core}/src/analyzer/calls.rs +7 -3
  8. data/{rust → core}/src/analyzer/definitions.rs +12 -7
  9. data/{rust → core}/src/analyzer/dispatch.rs +431 -13
  10. data/core/src/analyzer/exceptions.rs +622 -0
  11. data/{rust → core}/src/analyzer/install.rs +37 -1
  12. data/{rust → core}/src/analyzer/literals.rs +3 -17
  13. data/core/src/analyzer/loops.rs +301 -0
  14. data/{rust → core}/src/analyzer/mod.rs +4 -0
  15. data/{rust → core}/src/analyzer/operators.rs +119 -27
  16. data/{rust → core}/src/analyzer/parameters.rs +214 -5
  17. data/core/src/analyzer/super_calls.rs +285 -0
  18. data/{rust → core}/src/cache/rbs_cache.rs +0 -1
  19. data/{rust → core}/src/cli/commands.rs +3 -3
  20. data/{rust → core}/src/diagnostics/diagnostic.rs +0 -3
  21. data/{rust → core}/src/diagnostics/formatter.rs +0 -1
  22. data/{rust → core}/src/env/box_manager.rs +2 -4
  23. data/{rust → core}/src/env/global_env.rs +28 -7
  24. data/{rust → core}/src/env/local_env.rs +35 -1
  25. data/{rust → core}/src/env/method_registry.rs +117 -25
  26. data/{rust → core}/src/env/scope.rs +91 -4
  27. data/{rust → core}/src/env/vertex_manager.rs +0 -1
  28. data/{rust → core}/src/graph/box.rs +134 -8
  29. data/{rust → core}/src/graph/change_set.rs +14 -0
  30. data/{rust → core}/src/lsp/server.rs +1 -1
  31. data/{rust → core}/src/rbs/loader.rs +1 -2
  32. data/{rust → core}/src/source_map.rs +0 -1
  33. data/{rust → core}/src/types.rs +11 -1
  34. data/ext/Cargo.toml +2 -2
  35. data/lib/methodray/binary_locator.rb +2 -2
  36. data/lib/methodray/commands.rb +1 -1
  37. data/lib/methodray/version.rb +1 -1
  38. metadata +54 -50
  39. /data/{rust → core}/src/analyzer/conditionals.rs +0 -0
  40. /data/{rust → core}/src/analyzer/parentheses.rs +0 -0
  41. /data/{rust → core}/src/analyzer/returns.rs +0 -0
  42. /data/{rust → core}/src/analyzer/variables.rs +0 -0
  43. /data/{rust → core}/src/cache/mod.rs +0 -0
  44. /data/{rust → core}/src/checker.rs +0 -0
  45. /data/{rust → core}/src/cli/args.rs +0 -0
  46. /data/{rust → core}/src/cli/mod.rs +0 -0
  47. /data/{rust → core}/src/diagnostics/mod.rs +0 -0
  48. /data/{rust → core}/src/env/mod.rs +0 -0
  49. /data/{rust → core}/src/env/type_error.rs +0 -0
  50. /data/{rust → core}/src/graph/mod.rs +0 -0
  51. /data/{rust → core}/src/graph/vertex.rs +0 -0
  52. /data/{rust → core}/src/lib.rs +0 -0
  53. /data/{rust → core}/src/lsp/diagnostics.rs +0 -0
  54. /data/{rust → core}/src/lsp/main.rs +0 -0
  55. /data/{rust → core}/src/lsp/mod.rs +0 -0
  56. /data/{rust → core}/src/main.rs +0 -0
  57. /data/{rust → core}/src/parser.rs +0 -0
  58. /data/{rust → core}/src/rbs/converter.rs +0 -0
  59. /data/{rust → core}/src/rbs/error.rs +0 -0
  60. /data/{rust → core}/src/rbs/mod.rs +0 -0
@@ -5,6 +5,8 @@
5
5
  //! - Creating vertices for parameters
6
6
  //! - Registering parameters as local variables in method scope
7
7
 
8
+ use std::collections::HashMap;
9
+
8
10
  use crate::env::{GlobalEnv, LocalEnv};
9
11
  use crate::graph::{ChangeSet, VertexId};
10
12
  use crate::types::Type;
@@ -117,16 +119,18 @@ pub fn install_keyword_rest_parameter(
117
119
 
118
120
  /// Install method parameters as local variables
119
121
  ///
120
- /// Returns a Vec of VertexId for required and optional parameters (positional),
121
- /// which can be used for argument-to-parameter type propagation.
122
+ /// Returns a tuple of:
123
+ /// - Vec<VertexId>: positional parameter vertices (required and optional)
124
+ /// - HashMap<String, VertexId>: keyword parameter vertices (name → vertex)
122
125
  pub(crate) fn install_parameters(
123
126
  genv: &mut GlobalEnv,
124
127
  lenv: &mut LocalEnv,
125
128
  changes: &mut ChangeSet,
126
129
  source: &str,
127
130
  params_node: &ruby_prism::ParametersNode,
128
- ) -> Vec<VertexId> {
131
+ ) -> (Vec<VertexId>, HashMap<String, VertexId>) {
129
132
  let mut param_vtxs = Vec::new();
133
+ let mut keyword_param_vtxs: HashMap<String, VertexId> = HashMap::new();
130
134
 
131
135
  // Required parameters: def foo(a, b)
132
136
  for node in params_node.requireds().iter() {
@@ -165,8 +169,30 @@ pub(crate) fn install_parameters(
165
169
  }
166
170
  }
167
171
 
172
+ // Keyword parameters: def foo(name:, age: 0)
173
+ // Reuses install_required_parameter / install_optional_parameter
174
+ // since the logic is identical for positional and keyword parameters.
175
+ for node in params_node.keywords().iter() {
176
+ if let Some(req_kw) = node.as_required_keyword_parameter_node() {
177
+ let name = bytes_to_name(req_kw.name().as_slice());
178
+ let vtx = install_required_parameter(genv, lenv, name.clone());
179
+ keyword_param_vtxs.insert(name, vtx);
180
+ } else if let Some(opt_kw) = node.as_optional_keyword_parameter_node() {
181
+ let name = bytes_to_name(opt_kw.name().as_slice());
182
+ let default_value = opt_kw.value();
183
+ let vtx = if let Some(default_vtx) =
184
+ super::install::install_node(genv, lenv, changes, source, &default_value)
185
+ {
186
+ install_optional_parameter(genv, lenv, changes, name.clone(), default_vtx)
187
+ } else {
188
+ install_required_parameter(genv, lenv, name.clone())
189
+ };
190
+ keyword_param_vtxs.insert(name, vtx);
191
+ }
192
+ }
193
+
168
194
  // Keyword rest parameter: def foo(**kwargs)
169
- // Not included in param_vtxs (keyword args need special handling)
195
+ // Not included in keyword_param_vtxs (collects all remaining keywords)
170
196
  if let Some(kwrest_node) = params_node.keyword_rest() {
171
197
  if let Some(kwrest_param) = kwrest_node.as_keyword_rest_parameter_node() {
172
198
  if let Some(name_id) = kwrest_param.name() {
@@ -176,12 +202,42 @@ pub(crate) fn install_parameters(
176
202
  }
177
203
  }
178
204
 
179
- param_vtxs
205
+ (param_vtxs, keyword_param_vtxs)
180
206
  }
181
207
 
182
208
  #[cfg(test)]
183
209
  mod tests {
184
210
  use super::*;
211
+ use crate::analyzer::install::AstInstaller;
212
+ use crate::parser::ParseSession;
213
+
214
+ fn analyze(source: &str) -> GlobalEnv {
215
+ let session = ParseSession::new();
216
+ let parse_result = session.parse_source(source, "test.rb").unwrap();
217
+ let root = parse_result.node();
218
+ let program = root.as_program_node().unwrap();
219
+
220
+ let mut genv = GlobalEnv::new();
221
+ let mut lenv = LocalEnv::new();
222
+
223
+ let mut installer = AstInstaller::new(&mut genv, &mut lenv, source);
224
+ for stmt in &program.statements().body() {
225
+ installer.install_node(&stmt);
226
+ }
227
+ installer.finish();
228
+
229
+ genv
230
+ }
231
+
232
+ fn get_type_show(genv: &GlobalEnv, vtx: VertexId) -> String {
233
+ if let Some(vertex) = genv.get_vertex(vtx) {
234
+ vertex.show()
235
+ } else if let Some(source) = genv.get_source(vtx) {
236
+ source.ty.show()
237
+ } else {
238
+ panic!("vertex {:?} not found as either Vertex or Source", vtx);
239
+ }
240
+ }
185
241
 
186
242
  #[test]
187
243
  fn test_install_required_parameter() {
@@ -217,4 +273,157 @@ mod tests {
217
273
  assert_ne!(vtx_b, vtx_c);
218
274
  assert_ne!(vtx_a, vtx_c);
219
275
  }
276
+
277
+ #[test]
278
+ fn test_install_optional_parameter_inherits_default_type() {
279
+ let mut genv = GlobalEnv::new();
280
+ let mut lenv = LocalEnv::new();
281
+ let mut changes = ChangeSet::new();
282
+
283
+ // Default value: 0 (Integer)
284
+ let default_vtx = genv.new_source(Type::integer());
285
+ let vtx = install_optional_parameter(
286
+ &mut genv,
287
+ &mut lenv,
288
+ &mut changes,
289
+ "age".to_string(),
290
+ default_vtx,
291
+ );
292
+
293
+ assert_eq!(lenv.get_var("age"), Some(vtx));
294
+
295
+ // Type should propagate from default value
296
+ let vertex = genv.get_vertex(vtx).unwrap();
297
+ assert_eq!(vertex.show(), "Integer");
298
+ }
299
+
300
+ #[test]
301
+ fn test_required_parameter_type_propagation() {
302
+ let source = r#"
303
+ class Foo
304
+ def greet(name)
305
+ name
306
+ end
307
+ end
308
+
309
+ Foo.new.greet("Alice")
310
+ "#;
311
+ let genv = analyze(source);
312
+ let info = genv.resolve_method(&Type::instance("Foo"), "greet").unwrap();
313
+ let ret_vtx = info.return_vertex.unwrap();
314
+ assert_eq!(get_type_show(&genv, ret_vtx), "String");
315
+ }
316
+
317
+ #[test]
318
+ fn test_optional_parameter_default_type() {
319
+ let source = r#"
320
+ class Foo
321
+ def greet(name = "World")
322
+ name
323
+ end
324
+ end
325
+ "#;
326
+ let genv = analyze(source);
327
+ let info = genv.resolve_method(&Type::instance("Foo"), "greet").unwrap();
328
+ let ret_vtx = info.return_vertex.unwrap();
329
+ assert_eq!(get_type_show(&genv, ret_vtx), "String");
330
+ }
331
+
332
+ #[test]
333
+ fn test_multiple_parameters_from_call_site() {
334
+ let source = r#"
335
+ class Calc
336
+ def add(x, y)
337
+ x
338
+ end
339
+ end
340
+
341
+ Calc.new.add(1, 2)
342
+ "#;
343
+ let genv = analyze(source);
344
+ let info = genv.resolve_method(&Type::instance("Calc"), "add").unwrap();
345
+ let param_vtxs = info.param_vertices.as_ref().unwrap();
346
+ assert_eq!(param_vtxs.len(), 2);
347
+ // Verify return type is Integer (method returns x, which receives 1)
348
+ let ret_vtx = info.return_vertex.unwrap();
349
+ assert_eq!(get_type_show(&genv, ret_vtx), "Integer");
350
+ }
351
+
352
+ #[test]
353
+ fn test_keyword_parameter_propagation() {
354
+ let source = r#"
355
+ class Foo
356
+ def greet(name:)
357
+ name
358
+ end
359
+ end
360
+
361
+ Foo.new.greet(name: "Alice")
362
+ "#;
363
+ let genv = analyze(source);
364
+ let info = genv.resolve_method(&Type::instance("Foo"), "greet").unwrap();
365
+ let ret_vtx = info.return_vertex.unwrap();
366
+ assert_eq!(get_type_show(&genv, ret_vtx), "String");
367
+ }
368
+
369
+ #[test]
370
+ fn test_optional_keyword_parameter_default() {
371
+ let source = r#"
372
+ class Counter
373
+ def count(step: 1)
374
+ step
375
+ end
376
+ end
377
+ "#;
378
+ let genv = analyze(source);
379
+ let info = genv.resolve_method(&Type::instance("Counter"), "count").unwrap();
380
+ let ret_vtx = info.return_vertex.unwrap();
381
+ assert_eq!(get_type_show(&genv, ret_vtx), "Integer");
382
+ }
383
+
384
+ #[test]
385
+ fn test_mixed_positional_and_keyword_params() {
386
+ let source = r#"
387
+ class User
388
+ def initialize(id, name:)
389
+ @id = id
390
+ @name = name
391
+ end
392
+ end
393
+
394
+ User.new(1, name: "Alice")
395
+ "#;
396
+ let genv = analyze(source);
397
+ assert!(genv.type_errors.is_empty());
398
+ }
399
+
400
+ #[test]
401
+ fn test_rest_parameter() {
402
+ let source = r#"
403
+ class Foo
404
+ def bar(*args)
405
+ args
406
+ end
407
+ end
408
+ "#;
409
+ let genv = analyze(source);
410
+ let info = genv.resolve_method(&Type::instance("Foo"), "bar").unwrap();
411
+ let ret_vtx = info.return_vertex.unwrap();
412
+ assert_eq!(get_type_show(&genv, ret_vtx), "Array");
413
+ }
414
+
415
+ #[test]
416
+ fn test_no_parameters() {
417
+ let source = r#"
418
+ class Foo
419
+ def bar
420
+ "hello"
421
+ end
422
+ end
423
+ "#;
424
+ let genv = analyze(source);
425
+ let info = genv.resolve_method(&Type::instance("Foo"), "bar").unwrap();
426
+ let param_vtxs = info.param_vertices.as_ref().unwrap();
427
+ assert!(param_vtxs.is_empty());
428
+ }
220
429
  }
@@ -0,0 +1,285 @@
1
+ //! Super call handling: `super` and `super(args)`
2
+ //!
3
+ //! Ruby's `super` calls the same-named method on the parent class.
4
+ //! - `super(args)` → SuperNode: explicit arguments
5
+ //! - `super` (bare) → ForwardingSuperNode: implicit argument forwarding
6
+ //!
7
+ //! Note: ForwardingSuperNode (bare `super`) is treated as a zero-argument
8
+ //! call. In Ruby, bare `super` forwards all arguments from the enclosing
9
+ //! method, but replicating this requires parameter-vertex forwarding that
10
+ //! is not yet implemented. Return type inference is unaffected.
11
+
12
+ use ruby_prism::{ForwardingSuperNode, SuperNode};
13
+
14
+ use crate::env::{GlobalEnv, LocalEnv};
15
+ use crate::graph::{ChangeSet, VertexId};
16
+ use crate::source_map::SourceLocation as SL;
17
+ use crate::types::Type;
18
+
19
+ /// Process SuperNode: `super(args)` — explicit arguments
20
+ pub(crate) fn process_super_node(
21
+ genv: &mut GlobalEnv,
22
+ lenv: &mut LocalEnv,
23
+ changes: &mut ChangeSet,
24
+ source: &str,
25
+ super_node: &SuperNode,
26
+ ) -> Option<VertexId> {
27
+ let location = SL::from_prism_location_with_source(&super_node.location(), source);
28
+ process_super_call(genv, lenv, changes, source, super_node.arguments(), location)
29
+ }
30
+
31
+ /// Process ForwardingSuperNode: `super` — implicit argument forwarding
32
+ pub(crate) fn process_forwarding_super_node(
33
+ genv: &mut GlobalEnv,
34
+ lenv: &mut LocalEnv,
35
+ changes: &mut ChangeSet,
36
+ source: &str,
37
+ node: &ForwardingSuperNode,
38
+ ) -> Option<VertexId> {
39
+ let location = SL::from_prism_location_with_source(&node.location(), source);
40
+ process_super_call(genv, lenv, changes, source, None, location)
41
+ }
42
+
43
+ /// Resolve a super call by looking up the same-named method on the superclass.
44
+ ///
45
+ /// Returns `None` if there is no enclosing method scope (super outside a method)
46
+ /// or no explicit superclass declared on the enclosing class.
47
+ fn process_super_call(
48
+ genv: &mut GlobalEnv,
49
+ lenv: &mut LocalEnv,
50
+ changes: &mut ChangeSet,
51
+ source: &str,
52
+ arguments: Option<ruby_prism::ArgumentsNode>,
53
+ location: SL,
54
+ ) -> Option<VertexId> {
55
+ let method_name = genv.scope_manager.current_method_name()?;
56
+ let superclass_name = genv.scope_manager.current_superclass()?;
57
+ let recv_vtx = genv.new_source(Type::instance(&superclass_name));
58
+
59
+ let (arg_vtxs, kw) = if let Some(args) = arguments {
60
+ super::dispatch::collect_arguments(genv, lenv, changes, source, args.arguments().iter())
61
+ } else {
62
+ (vec![], None)
63
+ };
64
+
65
+ Some(super::calls::install_method_call(
66
+ genv,
67
+ recv_vtx,
68
+ method_name,
69
+ arg_vtxs,
70
+ kw,
71
+ Some(location),
72
+ ))
73
+ }
74
+
75
+ #[cfg(test)]
76
+ mod tests {
77
+ use crate::analyzer::install::AstInstaller;
78
+ use crate::env::{GlobalEnv, LocalEnv};
79
+ use crate::graph::VertexId;
80
+ use crate::parser::ParseSession;
81
+ use crate::types::Type;
82
+
83
+ /// Helper: parse Ruby source, process with AstInstaller, and return GlobalEnv
84
+ fn analyze(source: &str) -> GlobalEnv {
85
+ let session = ParseSession::new();
86
+ let parse_result = session.parse_source(source, "test.rb").unwrap();
87
+ let root = parse_result.node();
88
+ let program = root.as_program_node().unwrap();
89
+
90
+ let mut genv = GlobalEnv::new();
91
+ let mut lenv = LocalEnv::new();
92
+
93
+ let mut installer = AstInstaller::new(&mut genv, &mut lenv, source);
94
+ for stmt in &program.statements().body() {
95
+ installer.install_node(&stmt);
96
+ }
97
+ installer.finish();
98
+
99
+ genv
100
+ }
101
+
102
+ /// Helper: get the type string for a vertex ID
103
+ fn get_type_show(genv: &GlobalEnv, vtx: VertexId) -> String {
104
+ if let Some(vertex) = genv.get_vertex(vtx) {
105
+ vertex.show()
106
+ } else if let Some(source) = genv.get_source(vtx) {
107
+ source.ty.show()
108
+ } else {
109
+ panic!("vertex {:?} not found as either Vertex or Source", vtx);
110
+ }
111
+ }
112
+
113
+ #[test]
114
+ fn test_super_basic() {
115
+ let source = r#"
116
+ class Animal
117
+ def speak
118
+ "..."
119
+ end
120
+ end
121
+
122
+ class Dog < Animal
123
+ def speak
124
+ super
125
+ end
126
+ end
127
+ "#;
128
+ let genv = analyze(source);
129
+ let info = genv
130
+ .resolve_method(&Type::instance("Dog"), "speak")
131
+ .expect("Dog#speak should be registered");
132
+ let ret_vtx = info.return_vertex.unwrap();
133
+ assert_eq!(get_type_show(&genv, ret_vtx), "String");
134
+ }
135
+
136
+ #[test]
137
+ fn test_super_with_method_chain() {
138
+ let source = r#"
139
+ class Animal
140
+ def speak
141
+ "hello"
142
+ end
143
+ end
144
+
145
+ class Dog < Animal
146
+ def speak
147
+ super.upcase
148
+ end
149
+ end
150
+ "#;
151
+ let genv = analyze(source);
152
+ let info = genv
153
+ .resolve_method(&Type::instance("Dog"), "speak")
154
+ .expect("Dog#speak should be registered");
155
+ assert!(info.return_vertex.is_some());
156
+ }
157
+
158
+ #[test]
159
+ fn test_super_with_arguments() {
160
+ let source = r#"
161
+ class Base
162
+ def greet(name)
163
+ name
164
+ end
165
+ end
166
+
167
+ class Child < Base
168
+ def greet(name)
169
+ super(name)
170
+ end
171
+ end
172
+
173
+ Child.new.greet("Alice")
174
+ "#;
175
+ let genv = analyze(source);
176
+ let info = genv
177
+ .resolve_method(&Type::instance("Child"), "greet")
178
+ .expect("Child#greet should be registered");
179
+ let ret_vtx = info.return_vertex.unwrap();
180
+ assert_eq!(get_type_show(&genv, ret_vtx), "String");
181
+ }
182
+
183
+ #[test]
184
+ fn test_super_outside_method_ignored() {
185
+ let source = r#"
186
+ class Foo < Object
187
+ super
188
+ end
189
+ "#;
190
+ analyze(source);
191
+ }
192
+
193
+ #[test]
194
+ fn test_super_explicit_empty_args() {
195
+ let source = r#"
196
+ class Animal
197
+ def speak
198
+ "hello"
199
+ end
200
+ end
201
+
202
+ class Dog < Animal
203
+ def speak
204
+ super()
205
+ end
206
+ end
207
+ "#;
208
+ let genv = analyze(source);
209
+ let info = genv
210
+ .resolve_method(&Type::instance("Dog"), "speak")
211
+ .expect("Dog#speak should be registered");
212
+ let ret_vtx = info.return_vertex.unwrap();
213
+ assert_eq!(get_type_show(&genv, ret_vtx), "String");
214
+ }
215
+
216
+ #[test]
217
+ fn test_super_without_superclass_ignored() {
218
+ let source = r#"
219
+ class Foo
220
+ def bar
221
+ super
222
+ end
223
+ end
224
+ "#;
225
+ let genv = analyze(source);
226
+ let info = genv
227
+ .resolve_method(&Type::instance("Foo"), "bar")
228
+ .expect("Foo#bar should be registered");
229
+ assert!(info.return_vertex.is_some());
230
+ }
231
+
232
+ #[test]
233
+ fn test_super_qualified_superclass() {
234
+ let source = r#"
235
+ module Animals
236
+ class Pet
237
+ def name
238
+ "pet"
239
+ end
240
+ end
241
+ end
242
+
243
+ class Dog < Animals::Pet
244
+ def name
245
+ super
246
+ end
247
+ end
248
+ "#;
249
+ let genv = analyze(source);
250
+ let info = genv
251
+ .resolve_method(&Type::instance("Dog"), "name")
252
+ .expect("Dog#name should be registered");
253
+ let ret_vtx = info.return_vertex.unwrap();
254
+ assert_eq!(get_type_show(&genv, ret_vtx), "String");
255
+ }
256
+
257
+ #[test]
258
+ fn test_super_multi_level_inheritance() {
259
+ let source = r#"
260
+ class A
261
+ def foo
262
+ "hello"
263
+ end
264
+ end
265
+
266
+ class B < A
267
+ def foo
268
+ super
269
+ end
270
+ end
271
+
272
+ class C < B
273
+ def foo
274
+ super
275
+ end
276
+ end
277
+ "#;
278
+ let genv = analyze(source);
279
+ let info = genv
280
+ .resolve_method(&Type::instance("C"), "foo")
281
+ .expect("C#foo should be registered");
282
+ let ret_vtx = info.return_vertex.unwrap();
283
+ assert_eq!(get_type_show(&genv, ret_vtx), "String");
284
+ }
285
+ }
@@ -37,7 +37,6 @@ impl SerializableMethodInfo {
37
37
  }
38
38
  }
39
39
 
40
- #[allow(dead_code)]
41
40
  impl RbsCache {
42
41
  /// Get user cache file path (in ~/.cache/methodray/)
43
42
  pub fn cache_path() -> Result<PathBuf> {
@@ -1,7 +1,7 @@
1
1
  //! CLI command implementations
2
2
 
3
3
  use anyhow::Result;
4
- use std::path::PathBuf;
4
+ use std::path::Path;
5
5
 
6
6
  use crate::cache::RbsCache;
7
7
  use crate::checker::FileChecker;
@@ -9,7 +9,7 @@ use crate::diagnostics;
9
9
 
10
10
  /// Check a single Ruby file for type errors
11
11
  /// Returns Ok(true) if no errors, Ok(false) if errors found
12
- pub fn check_single_file(file_path: &PathBuf, verbose: bool) -> Result<bool> {
12
+ pub fn check_single_file(file_path: &Path, verbose: bool) -> Result<bool> {
13
13
  let checker = FileChecker::new()?;
14
14
  let diagnostics = checker.check_file(file_path)?;
15
15
 
@@ -38,7 +38,7 @@ pub fn check_project(_verbose: bool) -> Result<()> {
38
38
  }
39
39
 
40
40
  /// Watch a file for changes and re-check on modifications
41
- pub fn watch_file(file_path: &PathBuf) -> Result<()> {
41
+ pub fn watch_file(file_path: &Path) -> Result<()> {
42
42
  use notify::{Config, RecommendedWatcher, RecursiveMode, Watcher};
43
43
  use std::sync::mpsc::channel;
44
44
  use std::time::Duration;
@@ -2,7 +2,6 @@ use std::path::PathBuf;
2
2
 
3
3
  /// Diagnostic severity level (LSP compatible)
4
4
  #[derive(Debug, Clone, Copy, PartialEq, Eq)]
5
- #[allow(dead_code)]
6
5
  pub enum DiagnosticLevel {
7
6
  Error,
8
7
  Warning,
@@ -28,7 +27,6 @@ pub struct Location {
28
27
 
29
28
  /// Type checking diagnostic
30
29
  #[derive(Debug, Clone)]
31
- #[allow(dead_code)]
32
30
  pub struct Diagnostic {
33
31
  pub location: Location,
34
32
  pub level: DiagnosticLevel,
@@ -36,7 +34,6 @@ pub struct Diagnostic {
36
34
  pub code: Option<String>, // e.g., "E001"
37
35
  }
38
36
 
39
- #[allow(dead_code)]
40
37
  impl Diagnostic {
41
38
  /// Create an error diagnostic
42
39
  pub fn error(location: Location, message: String) -> Self {
@@ -8,7 +8,6 @@ use std::path::Path;
8
8
  /// ```text
9
9
  /// app/models/user.rb:10:5: error: undefined method `upcase` for Integer
10
10
  /// ```
11
- #[allow(dead_code)]
12
11
  pub fn format_diagnostics(diagnostics: &[Diagnostic]) -> String {
13
12
  diagnostics
14
13
  .iter()
@@ -6,7 +6,6 @@ use crate::graph::{BoxId, BoxTrait};
6
6
  use std::collections::{HashMap, HashSet, VecDeque};
7
7
 
8
8
  /// Manages boxes and their execution queue
9
- #[allow(dead_code)]
10
9
  pub struct BoxManager {
11
10
  /// All registered boxes
12
11
  pub boxes: HashMap<BoxId, Box<dyn BoxTrait>>,
@@ -24,7 +23,6 @@ impl Default for BoxManager {
24
23
  }
25
24
  }
26
25
 
27
- #[allow(dead_code)]
28
26
  impl BoxManager {
29
27
  /// Create a new empty box manager
30
28
  pub fn new() -> Self {
@@ -37,8 +35,8 @@ impl BoxManager {
37
35
  }
38
36
 
39
37
  /// Get a box by ID
40
- pub fn get(&self, id: BoxId) -> Option<&Box<dyn BoxTrait>> {
41
- self.boxes.get(&id)
38
+ pub fn get(&self, id: BoxId) -> Option<&dyn BoxTrait> {
39
+ self.boxes.get(&id).map(|b| b.as_ref())
42
40
  }
43
41
 
44
42
  /// Remove a box and return it (for temporary mutation)