method-ray 0.1.1

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 (58) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +23 -0
  3. data/LICENSE +21 -0
  4. data/README.md +39 -0
  5. data/exe/methodray +7 -0
  6. data/ext/Cargo.toml +24 -0
  7. data/ext/extconf.rb +40 -0
  8. data/ext/src/cli.rs +33 -0
  9. data/ext/src/lib.rs +79 -0
  10. data/lib/methodray/cli.rb +28 -0
  11. data/lib/methodray/commands.rb +78 -0
  12. data/lib/methodray/version.rb +5 -0
  13. data/lib/methodray.rb +9 -0
  14. data/rust/Cargo.toml +39 -0
  15. data/rust/src/analyzer/calls.rs +56 -0
  16. data/rust/src/analyzer/definitions.rs +70 -0
  17. data/rust/src/analyzer/dispatch.rs +134 -0
  18. data/rust/src/analyzer/install.rs +226 -0
  19. data/rust/src/analyzer/literals.rs +85 -0
  20. data/rust/src/analyzer/mod.rs +11 -0
  21. data/rust/src/analyzer/tests/integration_test.rs +136 -0
  22. data/rust/src/analyzer/tests/mod.rs +1 -0
  23. data/rust/src/analyzer/variables.rs +76 -0
  24. data/rust/src/cache/mod.rs +3 -0
  25. data/rust/src/cache/rbs_cache.rs +158 -0
  26. data/rust/src/checker.rs +139 -0
  27. data/rust/src/cli/args.rs +40 -0
  28. data/rust/src/cli/commands.rs +139 -0
  29. data/rust/src/cli/mod.rs +6 -0
  30. data/rust/src/diagnostics/diagnostic.rs +125 -0
  31. data/rust/src/diagnostics/formatter.rs +119 -0
  32. data/rust/src/diagnostics/mod.rs +5 -0
  33. data/rust/src/env/box_manager.rs +121 -0
  34. data/rust/src/env/global_env.rs +279 -0
  35. data/rust/src/env/local_env.rs +58 -0
  36. data/rust/src/env/method_registry.rs +63 -0
  37. data/rust/src/env/mod.rs +15 -0
  38. data/rust/src/env/scope.rs +330 -0
  39. data/rust/src/env/type_error.rs +23 -0
  40. data/rust/src/env/vertex_manager.rs +195 -0
  41. data/rust/src/graph/box.rs +157 -0
  42. data/rust/src/graph/change_set.rs +115 -0
  43. data/rust/src/graph/mod.rs +7 -0
  44. data/rust/src/graph/vertex.rs +167 -0
  45. data/rust/src/lib.rs +24 -0
  46. data/rust/src/lsp/diagnostics.rs +133 -0
  47. data/rust/src/lsp/main.rs +8 -0
  48. data/rust/src/lsp/mod.rs +4 -0
  49. data/rust/src/lsp/server.rs +138 -0
  50. data/rust/src/main.rs +46 -0
  51. data/rust/src/parser.rs +96 -0
  52. data/rust/src/rbs/converter.rs +82 -0
  53. data/rust/src/rbs/error.rs +37 -0
  54. data/rust/src/rbs/loader.rs +183 -0
  55. data/rust/src/rbs/mod.rs +15 -0
  56. data/rust/src/source_map.rs +102 -0
  57. data/rust/src/types.rs +75 -0
  58. metadata +119 -0
@@ -0,0 +1,119 @@
1
+ use super::diagnostic::Diagnostic;
2
+ use std::fs;
3
+ use std::path::Path;
4
+
5
+ /// Format diagnostics in LSP-compatible format
6
+ ///
7
+ /// Example output:
8
+ /// ```text
9
+ /// app/models/user.rb:10:5: error: undefined method `upcase` for Integer
10
+ /// ```
11
+ #[allow(dead_code)]
12
+ pub fn format_diagnostics(diagnostics: &[Diagnostic]) -> String {
13
+ diagnostics
14
+ .iter()
15
+ .map(|diag| {
16
+ format!(
17
+ "{}:{}:{}: {}: {}",
18
+ diag.location.file.display(),
19
+ diag.location.line,
20
+ diag.location.column,
21
+ diag.level.as_str(),
22
+ diag.message
23
+ )
24
+ })
25
+ .collect::<Vec<_>>()
26
+ .join("\n")
27
+ }
28
+
29
+ /// Format diagnostics with code snippet
30
+ ///
31
+ /// Example output:
32
+ /// ```text
33
+ /// app/models/user.rb:10:5: error: undefined method `upcase` for Integer
34
+ /// x.upcase
35
+ /// ^^^^^^
36
+ /// ```
37
+ pub fn format_diagnostics_with_source(diagnostics: &[Diagnostic], source_code: &str) -> String {
38
+ let lines: Vec<&str> = source_code.lines().collect();
39
+
40
+ diagnostics
41
+ .iter()
42
+ .map(|diag| {
43
+ let mut output = format!(
44
+ "{}:{}:{}: {}: {}",
45
+ diag.location.file.display(),
46
+ diag.location.line,
47
+ diag.location.column,
48
+ diag.level.as_str(),
49
+ diag.message
50
+ );
51
+
52
+ // Add code snippet
53
+ if diag.location.line > 0 && diag.location.line <= lines.len() {
54
+ let line_index = diag.location.line - 1; // Convert to 0-indexed
55
+ let source_line = lines[line_index];
56
+
57
+ output.push('\n');
58
+ output.push_str(" ");
59
+ output.push_str(source_line);
60
+ output.push('\n');
61
+
62
+ // Add caret indicator
63
+ let column = diag.location.column.saturating_sub(1); // Convert to 0-indexed
64
+ output.push_str(" ");
65
+ output.push_str(&" ".repeat(column));
66
+ output.push('^');
67
+ }
68
+
69
+ output
70
+ })
71
+ .collect::<Vec<_>>()
72
+ .join("\n\n")
73
+ }
74
+
75
+ /// Read source file and format diagnostics with code snippet
76
+ pub fn format_diagnostics_with_file(diagnostics: &[Diagnostic], file_path: &Path) -> String {
77
+ match fs::read_to_string(file_path) {
78
+ Ok(source) => format_diagnostics_with_source(diagnostics, &source),
79
+ Err(_) => format_diagnostics(diagnostics), // Fallback to simple format
80
+ }
81
+ }
82
+
83
+ #[cfg(test)]
84
+ mod tests {
85
+ use super::*;
86
+ use crate::diagnostics::diagnostic::{Diagnostic, Location};
87
+ use std::path::PathBuf;
88
+
89
+ #[test]
90
+ fn test_format_diagnostics() {
91
+ let diagnostics = vec![
92
+ Diagnostic::undefined_method(
93
+ Location {
94
+ file: PathBuf::from("test.rb"),
95
+ line: 10,
96
+ column: 5,
97
+ length: None,
98
+ },
99
+ "Integer",
100
+ "upcase",
101
+ ),
102
+ Diagnostic::union_partial_error(
103
+ Location {
104
+ file: PathBuf::from("test.rb"),
105
+ line: 15,
106
+ column: 3,
107
+ length: None,
108
+ },
109
+ vec!["String".to_string()],
110
+ vec!["Integer".to_string()],
111
+ "upcase",
112
+ ),
113
+ ];
114
+
115
+ let output = format_diagnostics(&diagnostics);
116
+ assert!(output.contains("test.rb:10:5: error:"));
117
+ assert!(output.contains("test.rb:15:3: warning:"));
118
+ }
119
+ }
@@ -0,0 +1,5 @@
1
+ pub mod diagnostic;
2
+ pub mod formatter;
3
+
4
+ pub use diagnostic::{Diagnostic, DiagnosticLevel, Location};
5
+ pub use formatter::format_diagnostics_with_file;
@@ -0,0 +1,121 @@
1
+ //! Box management and execution queue
2
+ //!
3
+ //! Handles registration and execution of Box instances (reactive computations).
4
+
5
+ use crate::graph::{BoxId, BoxTrait};
6
+ use std::collections::{HashMap, HashSet, VecDeque};
7
+
8
+ /// Manages boxes and their execution queue
9
+ #[allow(dead_code)]
10
+ pub struct BoxManager {
11
+ /// All registered boxes
12
+ pub boxes: HashMap<BoxId, Box<dyn BoxTrait>>,
13
+ /// Queue of boxes to be executed
14
+ pub run_queue: VecDeque<BoxId>,
15
+ /// Set to prevent duplicate queue entries
16
+ run_queue_set: HashSet<BoxId>,
17
+ /// Next box ID to allocate
18
+ pub next_box_id: usize,
19
+ }
20
+
21
+ impl Default for BoxManager {
22
+ fn default() -> Self {
23
+ Self::new()
24
+ }
25
+ }
26
+
27
+ #[allow(dead_code)]
28
+ impl BoxManager {
29
+ /// Create a new empty box manager
30
+ pub fn new() -> Self {
31
+ Self {
32
+ boxes: HashMap::new(),
33
+ run_queue: VecDeque::new(),
34
+ run_queue_set: HashSet::new(),
35
+ next_box_id: 0,
36
+ }
37
+ }
38
+
39
+ /// Get a box by ID
40
+ pub fn get(&self, id: BoxId) -> Option<&Box<dyn BoxTrait>> {
41
+ self.boxes.get(&id)
42
+ }
43
+
44
+ /// Remove a box and return it (for temporary mutation)
45
+ pub fn remove(&mut self, id: BoxId) -> Option<Box<dyn BoxTrait>> {
46
+ self.boxes.remove(&id)
47
+ }
48
+
49
+ /// Insert a box back after temporary removal
50
+ pub fn insert(&mut self, id: BoxId, box_instance: Box<dyn BoxTrait>) {
51
+ self.boxes.insert(id, box_instance);
52
+ }
53
+
54
+ /// Check if a box exists
55
+ pub fn contains(&self, id: BoxId) -> bool {
56
+ self.boxes.contains_key(&id)
57
+ }
58
+
59
+ /// Add a box to the execution queue
60
+ pub fn add_run(&mut self, box_id: BoxId) {
61
+ if !self.run_queue_set.contains(&box_id) {
62
+ self.run_queue.push_back(box_id);
63
+ self.run_queue_set.insert(box_id);
64
+ }
65
+ }
66
+
67
+ /// Pop the next box from the queue
68
+ pub fn pop_run(&mut self) -> Option<BoxId> {
69
+ if let Some(box_id) = self.run_queue.pop_front() {
70
+ self.run_queue_set.remove(&box_id);
71
+ Some(box_id)
72
+ } else {
73
+ None
74
+ }
75
+ }
76
+
77
+ /// Check if the queue is empty
78
+ pub fn queue_is_empty(&self) -> bool {
79
+ self.run_queue.is_empty()
80
+ }
81
+
82
+ /// Get the number of registered boxes
83
+ pub fn len(&self) -> usize {
84
+ self.boxes.len()
85
+ }
86
+
87
+ /// Check if there are no registered boxes
88
+ pub fn is_empty(&self) -> bool {
89
+ self.boxes.is_empty()
90
+ }
91
+ }
92
+
93
+ #[cfg(test)]
94
+ mod tests {
95
+ use super::*;
96
+
97
+ #[test]
98
+ fn test_add_run_prevents_duplicates() {
99
+ let mut manager = BoxManager::new();
100
+
101
+ let id = BoxId(0);
102
+ manager.add_run(id);
103
+ manager.add_run(id); // Should be ignored
104
+
105
+ assert_eq!(manager.run_queue.len(), 1);
106
+ }
107
+
108
+ #[test]
109
+ fn test_pop_run() {
110
+ let mut manager = BoxManager::new();
111
+
112
+ let id1 = BoxId(0);
113
+ let id2 = BoxId(1);
114
+ manager.add_run(id1);
115
+ manager.add_run(id2);
116
+
117
+ assert_eq!(manager.pop_run(), Some(id1));
118
+ assert_eq!(manager.pop_run(), Some(id2));
119
+ assert_eq!(manager.pop_run(), None);
120
+ }
121
+ }
@@ -0,0 +1,279 @@
1
+ //! Global environment: facade for the type inference engine
2
+ //!
3
+ //! This module provides a unified interface for managing vertices, boxes,
4
+ //! methods, type errors, and scopes during type inference.
5
+
6
+ use crate::env::box_manager::BoxManager;
7
+ use crate::env::method_registry::{MethodInfo, MethodRegistry};
8
+ use crate::env::scope::{Scope, ScopeId, ScopeKind, ScopeManager};
9
+ use crate::env::type_error::TypeError;
10
+ use crate::env::vertex_manager::VertexManager;
11
+ use crate::graph::{BoxId, BoxTrait, ChangeSet, EdgeUpdate, Source, Vertex, VertexId};
12
+ use crate::source_map::SourceLocation;
13
+ use crate::types::Type;
14
+
15
+ /// Global environment: core of the type inference engine
16
+ ///
17
+ /// This is a facade that coordinates the various subsystems:
18
+ /// - Vertex management (type graph nodes)
19
+ /// - Box management (reactive computations)
20
+ /// - Method registry (method definitions)
21
+ /// - Type errors (diagnostic collection)
22
+ /// - Scope management (lexical scopes)
23
+ pub struct GlobalEnv {
24
+ /// Vertex and source management
25
+ vertex_manager: VertexManager,
26
+
27
+ /// Box management and execution queue
28
+ box_manager: BoxManager,
29
+
30
+ /// Method definitions
31
+ method_registry: MethodRegistry,
32
+
33
+ /// Type errors collected during analysis
34
+ pub type_errors: Vec<TypeError>,
35
+
36
+ /// Scope management
37
+ pub scope_manager: ScopeManager,
38
+ }
39
+
40
+ #[allow(dead_code)]
41
+ impl GlobalEnv {
42
+ pub fn new() -> Self {
43
+ Self {
44
+ vertex_manager: VertexManager::new(),
45
+ box_manager: BoxManager::new(),
46
+ method_registry: MethodRegistry::new(),
47
+ type_errors: Vec::new(),
48
+ scope_manager: ScopeManager::new(),
49
+ }
50
+ }
51
+
52
+ // ===== Vertex Management =====
53
+
54
+ /// Create new Vertex
55
+ pub fn new_vertex(&mut self) -> VertexId {
56
+ self.vertex_manager.new_vertex()
57
+ }
58
+
59
+ /// Create new Source (fixed type)
60
+ pub fn new_source(&mut self, ty: Type) -> VertexId {
61
+ self.vertex_manager.new_source(ty)
62
+ }
63
+
64
+ /// Get Vertex
65
+ pub fn get_vertex(&self, id: VertexId) -> Option<&Vertex> {
66
+ self.vertex_manager.get_vertex(id)
67
+ }
68
+
69
+ /// Get Source
70
+ pub fn get_source(&self, id: VertexId) -> Option<&Source> {
71
+ self.vertex_manager.get_source(id)
72
+ }
73
+
74
+ /// Add edge (immediate type propagation)
75
+ pub fn add_edge(&mut self, src: VertexId, dst: VertexId) {
76
+ self.vertex_manager.add_edge(src, dst);
77
+ }
78
+
79
+ /// For debugging: display types of all Vertices
80
+ pub fn show_all(&self) -> String {
81
+ self.vertex_manager.show_all()
82
+ }
83
+
84
+ // ===== Box Management =====
85
+
86
+ /// Allocate a new BoxId
87
+ pub fn alloc_box_id(&mut self) -> BoxId {
88
+ let id = BoxId(self.box_manager.next_box_id);
89
+ self.box_manager.next_box_id += 1;
90
+ id
91
+ }
92
+
93
+ /// Register a Box with a pre-allocated ID and add it to the run queue
94
+ pub fn register_box(&mut self, box_id: BoxId, box_instance: Box<dyn BoxTrait>) {
95
+ self.box_manager.insert(box_id, box_instance);
96
+ self.box_manager.add_run(box_id);
97
+ }
98
+
99
+ /// Get the number of registered boxes
100
+ pub fn box_count(&self) -> usize {
101
+ self.box_manager.len()
102
+ }
103
+
104
+ /// Apply changes
105
+ pub fn apply_changes(&mut self, mut changes: ChangeSet) {
106
+ let updates = changes.reinstall();
107
+
108
+ for update in updates {
109
+ match update {
110
+ EdgeUpdate::Add { src, dst } => {
111
+ self.add_edge(src, dst);
112
+ }
113
+ EdgeUpdate::Remove { .. } => {
114
+ // TODO: Implement edge removal (in Phase 2)
115
+ }
116
+ }
117
+ }
118
+ }
119
+
120
+ /// Execute all Boxes
121
+ pub fn run_all(&mut self) {
122
+ while let Some(box_id) = self.box_manager.pop_run() {
123
+ if self.box_manager.contains(box_id) {
124
+ let mut changes = ChangeSet::new();
125
+
126
+ // Execute Box (temporarily remove to avoid &mut self borrow issue)
127
+ let mut temp_box = self.box_manager.remove(box_id).unwrap();
128
+ temp_box.run(self, &mut changes);
129
+ self.box_manager.insert(box_id, temp_box);
130
+
131
+ self.apply_changes(changes);
132
+ }
133
+ }
134
+ }
135
+
136
+ // ===== Method Registry =====
137
+
138
+ /// Resolve method
139
+ pub fn resolve_method(&self, recv_ty: &Type, method_name: &str) -> Option<&MethodInfo> {
140
+ self.method_registry.resolve(recv_ty, method_name)
141
+ }
142
+
143
+ /// Register built-in method
144
+ pub fn register_builtin_method(&mut self, recv_ty: Type, method_name: &str, ret_ty: Type) {
145
+ self.method_registry.register(recv_ty, method_name, ret_ty);
146
+ }
147
+
148
+ // ===== Type Errors =====
149
+
150
+ /// Record a type error (undefined method)
151
+ pub fn record_type_error(
152
+ &mut self,
153
+ receiver_type: Type,
154
+ method_name: String,
155
+ location: Option<SourceLocation>,
156
+ ) {
157
+ self.type_errors
158
+ .push(TypeError::new(receiver_type, method_name, location));
159
+ }
160
+
161
+ // ===== Scope Management =====
162
+
163
+ /// Enter a class scope
164
+ pub fn enter_class(&mut self, name: String) -> ScopeId {
165
+ let scope_id = self.scope_manager.new_scope(ScopeKind::Class {
166
+ name,
167
+ superclass: None,
168
+ });
169
+ self.scope_manager.enter_scope(scope_id);
170
+ scope_id
171
+ }
172
+
173
+ /// Enter a method scope
174
+ pub fn enter_method(&mut self, name: String) -> ScopeId {
175
+ let class_name = self.scope_manager.current_class_name();
176
+ let scope_id = self.scope_manager.new_scope(ScopeKind::Method {
177
+ name,
178
+ receiver_type: class_name,
179
+ });
180
+ self.scope_manager.enter_scope(scope_id);
181
+ scope_id
182
+ }
183
+
184
+ /// Exit current scope
185
+ pub fn exit_scope(&mut self) {
186
+ self.scope_manager.exit_scope();
187
+ }
188
+
189
+ /// Get current scope
190
+ pub fn current_scope(&self) -> &Scope {
191
+ self.scope_manager.current_scope()
192
+ }
193
+
194
+ /// Get current scope mutably
195
+ pub fn current_scope_mut(&mut self) -> &mut Scope {
196
+ self.scope_manager.current_scope_mut()
197
+ }
198
+ }
199
+
200
+ impl Default for GlobalEnv {
201
+ fn default() -> Self {
202
+ Self::new()
203
+ }
204
+ }
205
+
206
+ #[cfg(test)]
207
+ mod tests {
208
+ use super::*;
209
+
210
+ #[test]
211
+ fn test_global_env_new_vertex() {
212
+ let mut genv = GlobalEnv::new();
213
+
214
+ let v1 = genv.new_vertex();
215
+ let v2 = genv.new_vertex();
216
+
217
+ assert_eq!(v1.0, 0);
218
+ assert_eq!(v2.0, 1);
219
+ }
220
+
221
+ #[test]
222
+ fn test_global_env_new_source() {
223
+ let mut genv = GlobalEnv::new();
224
+
225
+ let s1 = genv.new_source(Type::string());
226
+ let s2 = genv.new_source(Type::integer());
227
+
228
+ assert_eq!(genv.get_source(s1).unwrap().ty.show(), "String");
229
+ assert_eq!(genv.get_source(s2).unwrap().ty.show(), "Integer");
230
+ }
231
+
232
+ #[test]
233
+ fn test_global_env_edge_propagation() {
234
+ let mut genv = GlobalEnv::new();
235
+
236
+ // Source<String> -> Vertex
237
+ let src = genv.new_source(Type::string());
238
+ let vtx = genv.new_vertex();
239
+
240
+ genv.add_edge(src, vtx);
241
+
242
+ // Verify type propagated to Vertex
243
+ assert_eq!(genv.get_vertex(vtx).unwrap().show(), "String");
244
+ }
245
+
246
+ #[test]
247
+ fn test_global_env_chain_propagation() {
248
+ let mut genv = GlobalEnv::new();
249
+
250
+ // Source<String> -> Vertex1 -> Vertex2
251
+ let src = genv.new_source(Type::string());
252
+ let v1 = genv.new_vertex();
253
+ let v2 = genv.new_vertex();
254
+
255
+ genv.add_edge(src, v1);
256
+ genv.add_edge(v1, v2);
257
+
258
+ // Verify type propagated to v2
259
+ assert_eq!(genv.get_vertex(v1).unwrap().show(), "String");
260
+ assert_eq!(genv.get_vertex(v2).unwrap().show(), "String");
261
+ }
262
+
263
+ #[test]
264
+ fn test_global_env_union_propagation() {
265
+ let mut genv = GlobalEnv::new();
266
+
267
+ // Source<String> -> Vertex
268
+ // Source<Integer> -> Vertex
269
+ let src1 = genv.new_source(Type::string());
270
+ let src2 = genv.new_source(Type::integer());
271
+ let vtx = genv.new_vertex();
272
+
273
+ genv.add_edge(src1, vtx);
274
+ genv.add_edge(src2, vtx);
275
+
276
+ // Verify it became Union type
277
+ assert_eq!(genv.get_vertex(vtx).unwrap().show(), "(Integer | String)");
278
+ }
279
+ }
@@ -0,0 +1,58 @@
1
+ use crate::graph::VertexId;
2
+ use std::collections::HashMap;
3
+
4
+ /// Local environment: mapping of local variable names to VertexIDs
5
+ pub struct LocalEnv {
6
+ locals: HashMap<String, VertexId>,
7
+ }
8
+
9
+ #[allow(dead_code)]
10
+ impl LocalEnv {
11
+ pub fn new() -> Self {
12
+ Self {
13
+ locals: HashMap::new(),
14
+ }
15
+ }
16
+
17
+ /// Register variable
18
+ pub fn new_var(&mut self, name: String, vtx_id: VertexId) {
19
+ self.locals.insert(name, vtx_id);
20
+ }
21
+
22
+ /// Get variable
23
+ pub fn get_var(&self, name: &str) -> Option<VertexId> {
24
+ self.locals.get(name).copied()
25
+ }
26
+
27
+ /// Get all variables
28
+ pub fn all_vars(&self) -> impl Iterator<Item = (&String, &VertexId)> {
29
+ self.locals.iter()
30
+ }
31
+ }
32
+
33
+ #[cfg(test)]
34
+ mod tests {
35
+ use super::*;
36
+
37
+ #[test]
38
+ fn test_local_env() {
39
+ let mut lenv = LocalEnv::new();
40
+
41
+ lenv.new_var("x".to_string(), VertexId(1));
42
+ lenv.new_var("y".to_string(), VertexId(2));
43
+
44
+ assert_eq!(lenv.get_var("x"), Some(VertexId(1)));
45
+ assert_eq!(lenv.get_var("y"), Some(VertexId(2)));
46
+ assert_eq!(lenv.get_var("z"), None);
47
+ }
48
+
49
+ #[test]
50
+ fn test_local_env_override() {
51
+ let mut lenv = LocalEnv::new();
52
+
53
+ lenv.new_var("x".to_string(), VertexId(1));
54
+ lenv.new_var("x".to_string(), VertexId(2)); // Override
55
+
56
+ assert_eq!(lenv.get_var("x"), Some(VertexId(2)));
57
+ }
58
+ }
@@ -0,0 +1,63 @@
1
+ //! Method registration and resolution
2
+
3
+ use crate::types::Type;
4
+ use std::collections::HashMap;
5
+
6
+ /// Method information
7
+ #[derive(Debug, Clone)]
8
+ pub struct MethodInfo {
9
+ pub return_type: Type,
10
+ }
11
+
12
+ /// Registry for method definitions
13
+ #[derive(Debug, Default)]
14
+ pub struct MethodRegistry {
15
+ methods: HashMap<(Type, String), MethodInfo>,
16
+ }
17
+
18
+ impl MethodRegistry {
19
+ /// Create a new empty registry
20
+ pub fn new() -> Self {
21
+ Self {
22
+ methods: HashMap::new(),
23
+ }
24
+ }
25
+
26
+ /// Register a method for a receiver type
27
+ pub fn register(&mut self, recv_ty: Type, method_name: &str, ret_ty: Type) {
28
+ self.methods.insert(
29
+ (recv_ty, method_name.to_string()),
30
+ MethodInfo {
31
+ return_type: ret_ty,
32
+ },
33
+ );
34
+ }
35
+
36
+ /// Resolve a method for a receiver type
37
+ pub fn resolve(&self, recv_ty: &Type, method_name: &str) -> Option<&MethodInfo> {
38
+ self.methods
39
+ .get(&(recv_ty.clone(), method_name.to_string()))
40
+ }
41
+ }
42
+
43
+ #[cfg(test)]
44
+ mod tests {
45
+ use super::*;
46
+
47
+ #[test]
48
+ fn test_register_and_resolve() {
49
+ let mut registry = MethodRegistry::new();
50
+ registry.register(Type::string(), "length", Type::integer());
51
+
52
+ let info = registry.resolve(&Type::string(), "length").unwrap();
53
+ assert!(
54
+ matches!(info.return_type, Type::Instance { ref class_name, .. } if class_name == "Integer")
55
+ );
56
+ }
57
+
58
+ #[test]
59
+ fn test_resolve_not_found() {
60
+ let registry = MethodRegistry::new();
61
+ assert!(registry.resolve(&Type::string(), "unknown").is_none());
62
+ }
63
+ }
@@ -0,0 +1,15 @@
1
+ //! Environment management for type inference
2
+ //!
3
+ //! This module provides the core data structures for managing
4
+ //! type inference state including global and local environments.
5
+
6
+ pub mod box_manager;
7
+ pub mod global_env;
8
+ pub mod local_env;
9
+ pub mod method_registry;
10
+ pub mod scope;
11
+ pub mod type_error;
12
+ pub mod vertex_manager;
13
+
14
+ pub use global_env::GlobalEnv;
15
+ pub use local_env::LocalEnv;