method-ray 0.1.7 → 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.
@@ -1,9 +1,10 @@
1
1
  //! Method registration and resolution
2
2
 
3
+ use std::collections::HashMap;
4
+
3
5
  use crate::graph::VertexId;
4
6
  use crate::types::Type;
5
7
  use smallvec::SmallVec;
6
- use std::collections::HashMap;
7
8
 
8
9
  const OBJECT_CLASS: &str = "Object";
9
10
  const KERNEL_MODULE: &str = "Kernel";
@@ -15,6 +16,7 @@ pub struct MethodInfo {
15
16
  pub block_param_types: Option<Vec<Type>>,
16
17
  pub return_vertex: Option<VertexId>,
17
18
  pub param_vertices: Option<Vec<VertexId>>,
19
+ pub keyword_param_vertices: Option<HashMap<String, VertexId>>,
18
20
  }
19
21
 
20
22
  /// Registry for method definitions
@@ -51,6 +53,7 @@ impl MethodRegistry {
51
53
  block_param_types,
52
54
  return_vertex: None,
53
55
  param_vertices: None,
56
+ keyword_param_vertices: None,
54
57
  },
55
58
  );
56
59
  }
@@ -62,6 +65,7 @@ impl MethodRegistry {
62
65
  method_name: &str,
63
66
  return_vertex: VertexId,
64
67
  param_vertices: Vec<VertexId>,
68
+ keyword_param_vertices: Option<HashMap<String, VertexId>>,
65
69
  ) {
66
70
  self.methods.insert(
67
71
  (recv_ty, method_name.to_string()),
@@ -70,6 +74,7 @@ impl MethodRegistry {
70
74
  block_param_types: None,
71
75
  return_vertex: Some(return_vertex),
72
76
  param_vertices: Some(param_vertices),
77
+ keyword_param_vertices,
73
78
  },
74
79
  );
75
80
  }
@@ -134,7 +139,7 @@ mod tests {
134
139
  fn test_register_user_method_and_resolve() {
135
140
  let mut registry = MethodRegistry::new();
136
141
  let return_vtx = VertexId(42);
137
- registry.register_user_method(Type::instance("User"), "name", return_vtx, vec![]);
142
+ registry.register_user_method(Type::instance("User"), "name", return_vtx, vec![], None);
138
143
 
139
144
  let info = registry.resolve(&Type::instance("User"), "name").unwrap();
140
145
  assert_eq!(info.return_vertex, Some(VertexId(42)));
@@ -151,6 +156,7 @@ mod tests {
151
156
  "add",
152
157
  return_vtx,
153
158
  param_vtxs,
159
+ None,
154
160
  );
155
161
 
156
162
  let info = registry.resolve(&Type::instance("Calc"), "add").unwrap();
@@ -7,7 +7,6 @@ pub struct ScopeId(pub usize);
7
7
 
8
8
  /// Scope kind
9
9
  #[derive(Debug, Clone)]
10
- #[allow(dead_code)]
11
10
  pub enum ScopeKind {
12
11
  TopLevel,
13
12
  Class {
@@ -27,7 +26,6 @@ pub enum ScopeKind {
27
26
 
28
27
  /// Scope information
29
28
  #[derive(Debug, Clone)]
30
- #[allow(dead_code)]
31
29
  pub struct Scope {
32
30
  pub id: ScopeId,
33
31
  pub kind: ScopeKind,
@@ -46,7 +44,6 @@ pub struct Scope {
46
44
  pub constants: HashMap<String, String>,
47
45
  }
48
46
 
49
- #[allow(dead_code)]
50
47
  impl Scope {
51
48
  pub fn new(id: ScopeId, kind: ScopeKind, parent: Option<ScopeId>) -> Self {
52
49
  Self {
@@ -89,7 +86,12 @@ pub struct ScopeManager {
89
86
  current_scope: ScopeId,
90
87
  }
91
88
 
92
- #[allow(dead_code)]
89
+ impl Default for ScopeManager {
90
+ fn default() -> Self {
91
+ Self::new()
92
+ }
93
+ }
94
+
93
95
  impl ScopeManager {
94
96
  pub fn new() -> Self {
95
97
  let top_level = Scope::new(ScopeId(0), ScopeKind::TopLevel, None);
@@ -278,6 +280,13 @@ impl ScopeManager {
278
280
  mod tests {
279
281
  use super::*;
280
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
+
281
290
  #[test]
282
291
  fn test_scope_manager_creation() {
283
292
  let sm = ScopeManager::new();
@@ -17,7 +17,6 @@ pub struct VertexManager {
17
17
  next_vertex_id: usize,
18
18
  }
19
19
 
20
- #[allow(dead_code)]
21
20
  impl VertexManager {
22
21
  /// Create a new empty vertex manager
23
22
  pub fn new() -> Self {
@@ -1,3 +1,5 @@
1
+ use std::collections::HashMap;
2
+
1
3
  use crate::env::GlobalEnv;
2
4
  use crate::graph::change_set::ChangeSet;
3
5
  use crate::graph::vertex::VertexId;
@@ -9,7 +11,6 @@ use crate::types::Type;
9
11
  pub struct BoxId(pub usize);
10
12
 
11
13
  /// Box trait: represents constraints such as method calls
12
- #[allow(dead_code)]
13
14
  pub trait BoxTrait: Send + Sync {
14
15
  fn id(&self) -> BoxId;
15
16
  fn run(&mut self, genv: &mut GlobalEnv, changes: &mut ChangeSet);
@@ -28,14 +29,30 @@ fn propagate_arguments(
28
29
  }
29
30
  }
30
31
 
32
+ /// Propagate keyword argument types to keyword parameter vertices by name
33
+ fn propagate_keyword_arguments(
34
+ kwarg_vtxs: Option<&HashMap<String, VertexId>>,
35
+ kw_param_vtxs: Option<&HashMap<String, VertexId>>,
36
+ changes: &mut ChangeSet,
37
+ ) {
38
+ let (Some(args), Some(params)) = (kwarg_vtxs, kw_param_vtxs) else {
39
+ return;
40
+ };
41
+ for (name, arg_vtx) in args {
42
+ if let Some(&param_vtx) = params.get(name) {
43
+ changes.add_edge(*arg_vtx, param_vtx);
44
+ }
45
+ }
46
+ }
47
+
31
48
  /// Box representing a method call
32
- #[allow(dead_code)]
33
49
  pub struct MethodCallBox {
34
50
  id: BoxId,
35
51
  recv: VertexId,
36
52
  method_name: String,
37
53
  ret: VertexId,
38
54
  arg_vtxs: Vec<VertexId>,
55
+ kwarg_vtxs: Option<HashMap<String, VertexId>>,
39
56
  location: Option<SourceLocation>, // Source code location
40
57
  /// Number of times this box has been rescheduled
41
58
  reschedule_count: u8,
@@ -51,6 +68,7 @@ impl MethodCallBox {
51
68
  method_name: String,
52
69
  ret: VertexId,
53
70
  arg_vtxs: Vec<VertexId>,
71
+ kwarg_vtxs: Option<HashMap<String, VertexId>>,
54
72
  location: Option<SourceLocation>,
55
73
  ) -> Self {
56
74
  Self {
@@ -59,6 +77,7 @@ impl MethodCallBox {
59
77
  method_name,
60
78
  ret,
61
79
  arg_vtxs,
80
+ kwarg_vtxs,
62
81
  location,
63
82
  reschedule_count: 0,
64
83
  }
@@ -90,6 +109,11 @@ impl MethodCallBox {
90
109
  method_info.param_vertices.as_deref(),
91
110
  changes,
92
111
  );
112
+ propagate_keyword_arguments(
113
+ self.kwarg_vtxs.as_ref(),
114
+ method_info.keyword_param_vertices.as_ref(),
115
+ changes,
116
+ );
93
117
  } else {
94
118
  // Builtin/RBS method: create source with fixed return type
95
119
  let ret_src_id = genv.new_source(method_info.return_type.clone());
@@ -118,10 +142,17 @@ impl MethodCallBox {
118
142
  let ret_src = genv.new_source(instance_type.clone());
119
143
  changes.add_edge(ret_src, self.ret);
120
144
 
121
- let init_params = genv
122
- .resolve_method(&instance_type, "initialize")
123
- .and_then(|info| info.param_vertices.as_deref());
124
- propagate_arguments(&self.arg_vtxs, init_params, changes);
145
+ let init_info = genv.resolve_method(&instance_type, "initialize");
146
+ propagate_arguments(
147
+ &self.arg_vtxs,
148
+ init_info.and_then(|info| info.param_vertices.as_deref()),
149
+ changes,
150
+ );
151
+ propagate_keyword_arguments(
152
+ self.kwarg_vtxs.as_ref(),
153
+ init_info.and_then(|info| info.keyword_param_vertices.as_ref()),
154
+ changes,
155
+ );
125
156
  } else {
126
157
  self.report_type_error(recv_ty, genv);
127
158
  }
@@ -166,7 +197,6 @@ impl BoxTrait for MethodCallBox {
166
197
  /// When a method with a block is called (e.g., `str.each_char { |c| ... }`),
167
198
  /// this box resolves the block parameter types from the method's RBS definition
168
199
  /// and propagates them to the block parameter vertices.
169
- #[allow(dead_code)]
170
200
  pub struct BlockParameterTypeBox {
171
201
  id: BoxId,
172
202
  /// Receiver vertex of the method call
@@ -325,6 +355,7 @@ mod tests {
325
355
  "upcase".to_string(),
326
356
  ret_vtx,
327
357
  vec![],
358
+ None,
328
359
  None, // No location in test
329
360
  );
330
361
 
@@ -357,6 +388,7 @@ mod tests {
357
388
  "unknown_method".to_string(),
358
389
  ret_vtx,
359
390
  vec![],
391
+ None,
360
392
  None, // No location in test
361
393
  );
362
394
 
@@ -552,7 +584,7 @@ mod tests {
552
584
  let body_src = genv.new_source(Type::string());
553
585
 
554
586
  // Register user-defined method User#name with return_vertex
555
- genv.register_user_method(Type::instance("User"), "name", body_src, vec![]);
587
+ genv.register_user_method(Type::instance("User"), "name", body_src, vec![], None);
556
588
 
557
589
  // Simulate: user.name (receiver has type User)
558
590
  let recv_vtx = genv.new_vertex();
@@ -568,6 +600,7 @@ mod tests {
568
600
  ret_vtx,
569
601
  vec![],
570
602
  None,
603
+ None,
571
604
  );
572
605
  genv.register_box(box_id, Box::new(call_box));
573
606
 
@@ -598,6 +631,7 @@ mod tests {
598
631
  inner_ret_vtx,
599
632
  vec![],
600
633
  None,
634
+ None,
601
635
  );
602
636
  genv.register_box(inner_box_id, Box::new(inner_call));
603
637
 
@@ -607,6 +641,7 @@ mod tests {
607
641
  "format",
608
642
  inner_ret_vtx,
609
643
  vec![param_vtx],
644
+ None,
610
645
  );
611
646
 
612
647
  // 5. Simulate call: Formatter.new.format(42)
@@ -625,6 +660,7 @@ mod tests {
625
660
  call_ret_vtx,
626
661
  vec![arg_vtx],
627
662
  None,
663
+ None,
628
664
  );
629
665
  genv.register_box(call_box_id, Box::new(call_box));
630
666
 
@@ -637,4 +673,94 @@ mod tests {
637
673
  // Return type should be String (Integer#to_s -> String)
638
674
  assert_eq!(genv.get_vertex(call_ret_vtx).unwrap().show(), "String");
639
675
  }
676
+
677
+ #[test]
678
+ fn test_keyword_arg_propagation() {
679
+ let mut genv = GlobalEnv::new();
680
+
681
+ // Simulate: def greet(name:); name; end
682
+ let param_vtx = genv.new_vertex();
683
+ let mut kw_params = HashMap::new();
684
+ kw_params.insert("name".to_string(), param_vtx);
685
+
686
+ genv.register_user_method(
687
+ Type::instance("Greeter"),
688
+ "greet",
689
+ param_vtx, // return vertex = param (returns name)
690
+ vec![],
691
+ Some(kw_params),
692
+ );
693
+
694
+ // Simulate call: Greeter.new.greet(name: "Alice")
695
+ let recv_vtx = genv.new_vertex();
696
+ let recv_src = genv.new_source(Type::instance("Greeter"));
697
+ genv.add_edge(recv_src, recv_vtx);
698
+
699
+ let arg_vtx = genv.new_source(Type::string());
700
+ let mut kwarg_vtxs = HashMap::new();
701
+ kwarg_vtxs.insert("name".to_string(), arg_vtx);
702
+
703
+ let ret_vtx = genv.new_vertex();
704
+ let box_id = genv.alloc_box_id();
705
+ let call_box = MethodCallBox::new(
706
+ box_id,
707
+ recv_vtx,
708
+ "greet".to_string(),
709
+ ret_vtx,
710
+ vec![],
711
+ Some(kwarg_vtxs),
712
+ None,
713
+ );
714
+ genv.register_box(box_id, Box::new(call_box));
715
+
716
+ genv.run_all();
717
+
718
+ // param_vtx should have String type (propagated from keyword argument)
719
+ assert_eq!(genv.get_vertex(param_vtx).unwrap().show(), "String");
720
+ assert_eq!(genv.get_vertex(ret_vtx).unwrap().show(), "String");
721
+ }
722
+
723
+ #[test]
724
+ fn test_keyword_arg_name_mismatch_skipped() {
725
+ let mut genv = GlobalEnv::new();
726
+
727
+ let param_vtx = genv.new_vertex();
728
+ let mut kw_params = HashMap::new();
729
+ kw_params.insert("name".to_string(), param_vtx);
730
+
731
+ genv.register_user_method(
732
+ Type::instance("Greeter"),
733
+ "greet",
734
+ param_vtx,
735
+ vec![],
736
+ Some(kw_params),
737
+ );
738
+
739
+ let recv_vtx = genv.new_vertex();
740
+ let recv_src = genv.new_source(Type::instance("Greeter"));
741
+ genv.add_edge(recv_src, recv_vtx);
742
+
743
+ // Wrong keyword name: "title" instead of "name"
744
+ let arg_vtx = genv.new_source(Type::string());
745
+ let mut kwarg_vtxs = HashMap::new();
746
+ kwarg_vtxs.insert("title".to_string(), arg_vtx);
747
+
748
+ let ret_vtx = genv.new_vertex();
749
+ let box_id = genv.alloc_box_id();
750
+ let call_box = MethodCallBox::new(
751
+ box_id,
752
+ recv_vtx,
753
+ "greet".to_string(),
754
+ ret_vtx,
755
+ vec![],
756
+ Some(kwarg_vtxs),
757
+ None,
758
+ );
759
+ genv.register_box(box_id, Box::new(call_box));
760
+
761
+ genv.run_all();
762
+
763
+ // param_vtx should remain untyped (name mismatch → no propagation)
764
+ assert_eq!(genv.get_vertex(param_vtx).unwrap().show(), "untyped");
765
+ }
640
766
  }
@@ -10,6 +10,12 @@ pub struct ChangeSet {
10
10
  reschedule_boxes: Vec<BoxId>,
11
11
  }
12
12
 
13
+ impl Default for ChangeSet {
14
+ fn default() -> Self {
15
+ Self::new()
16
+ }
17
+ }
18
+
13
19
  impl ChangeSet {
14
20
  pub fn new() -> Self {
15
21
  Self {
@@ -75,6 +81,14 @@ pub enum EdgeUpdate {
75
81
  mod tests {
76
82
  use super::*;
77
83
 
84
+ #[test]
85
+ fn test_change_set_default() {
86
+ let mut cs = ChangeSet::default();
87
+ cs.add_edge(VertexId(1), VertexId(2));
88
+ let updates = cs.reinstall();
89
+ assert_eq!(updates.len(), 1);
90
+ }
91
+
78
92
  #[test]
79
93
  fn test_change_set_add() {
80
94
  let mut cs = ChangeSet::new();
@@ -132,7 +132,7 @@ pub async fn run_server() {
132
132
  let stdin = tokio::io::stdin();
133
133
  let stdout = tokio::io::stdout();
134
134
 
135
- let (service, socket) = LspService::new(|client| MethodRayServer::new(client));
135
+ let (service, socket) = LspService::new(MethodRayServer::new);
136
136
 
137
137
  Server::new(stdin, stdout, socket).serve(service).await;
138
138
  }
@@ -155,8 +155,7 @@ pub fn register_rbs_methods(genv: &mut GlobalEnv, ruby: &Ruby) -> Result<usize,
155
155
  cache.to_method_infos()
156
156
  } else {
157
157
  eprintln!("Cache invalid, reloading from RBS...");
158
- let methods = load_and_cache_rbs_methods(ruby, methodray_version, &rbs_version)?;
159
- methods
158
+ load_and_cache_rbs_methods(ruby, methodray_version, &rbs_version)?
160
159
  }
161
160
  } else {
162
161
  eprintln!("No cache found, loading from RBS...");
@@ -30,7 +30,6 @@ pub struct SourceLocation {
30
30
  pub length: usize,
31
31
  }
32
32
 
33
- #[allow(dead_code)]
34
33
  impl SourceLocation {
35
34
  pub fn new(line: usize, column: usize, length: usize) -> Self {
36
35
  Self {
data/rust/src/types.rs CHANGED
@@ -125,7 +125,6 @@ impl From<String> for QualifiedName {
125
125
 
126
126
  /// Type system for graph-based type inference
127
127
  #[derive(Clone, Debug, PartialEq, Eq, Hash)]
128
- #[allow(dead_code)]
129
128
  pub enum Type {
130
129
  /// Instance type: String, Integer, Api::User, etc.
131
130
  Instance { name: QualifiedName },
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.7
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - dak2
@@ -48,14 +48,17 @@ files:
48
48
  - lib/methodray/commands.rb
49
49
  - lib/methodray/version.rb
50
50
  - rust/Cargo.toml
51
+ - rust/src/analyzer/assignments.rs
51
52
  - rust/src/analyzer/attributes.rs
52
53
  - rust/src/analyzer/blocks.rs
53
54
  - rust/src/analyzer/calls.rs
54
55
  - rust/src/analyzer/conditionals.rs
55
56
  - rust/src/analyzer/definitions.rs
56
57
  - rust/src/analyzer/dispatch.rs
58
+ - rust/src/analyzer/exceptions.rs
57
59
  - rust/src/analyzer/install.rs
58
60
  - rust/src/analyzer/literals.rs
61
+ - rust/src/analyzer/loops.rs
59
62
  - rust/src/analyzer/mod.rs
60
63
  - rust/src/analyzer/operators.rs
61
64
  - rust/src/analyzer/parameters.rs