mago_codex/ttype/
resolution.rs

1use mago_atom::Atom;
2use mago_atom::AtomMap;
3use mago_atom::AtomSet;
4use serde::Deserialize;
5use serde::Serialize;
6
7use crate::misc::GenericParent;
8use crate::ttype::union::TUnion;
9
10/// Holds contextual information necessary for resolving generic template types (`@template`).
11///
12/// This context typically includes the definitions of template parameters available in the current scope
13/// (e.g., from class or function `@template` tags) and any concrete types that these templates
14/// have been resolved to (e.g., when a generic class is instantiated or a generic method is called).
15#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
16pub struct TypeResolutionContext {
17    /// Definitions of template types available in this context, including their constraints.
18    template_definitions: Vec<(Atom, Vec<(GenericParent, TUnion)>)>,
19
20    /// Concrete types that template parameters (often from an outer scope) resolve to
21    /// within this specific context.
22    resolved_template_types: Vec<(Atom, TUnion)>,
23
24    /// Type aliases defined in the current class scope (from @type tags).
25    type_aliases: AtomSet,
26
27    /// Imported type aliases (from @import-type tags).
28    /// Maps local alias name to (source class FQCN, original type name).
29    imported_type_aliases: AtomMap<(Atom, Atom)>,
30}
31
32/// Provides a default, empty type resolution context.
33impl Default for TypeResolutionContext {
34    fn default() -> Self {
35        Self::new()
36    }
37}
38
39impl TypeResolutionContext {
40    /// Creates a new, empty `TypeResolutionContext` with no defined or resolved template types.
41    pub fn new() -> Self {
42        Self {
43            template_definitions: vec![],
44            resolved_template_types: vec![],
45            type_aliases: AtomSet::default(),
46            imported_type_aliases: AtomMap::default(),
47        }
48    }
49
50    /// Checks if this context is empty, meaning it has no template definitions or resolved types.
51    #[inline]
52    pub fn is_empty(&self) -> bool {
53        self.template_definitions.is_empty()
54            && self.resolved_template_types.is_empty()
55            && self.type_aliases.is_empty()
56            && self.imported_type_aliases.is_empty()
57    }
58
59    /// Adds a template type definition (e.g., from an `@template T of Constraint` tag).
60    ///
61    /// # Arguments
62    ///
63    /// * `name`: The name of the template parameter (e.g., `"T"`).
64    /// * `constraints`: A list of constraints, each specifying the origin (parent) and the constraint type.
65    pub fn with_template_definition(mut self, name: Atom, constraints: Vec<(GenericParent, TUnion)>) -> Self {
66        self.template_definitions.push((name, constraints));
67        self
68    }
69
70    /// Adds a mapping indicating that a template parameter resolves to a specific concrete type
71    /// within this context.
72    ///
73    /// # Arguments
74    ///
75    /// * `name`: The name of the template parameter (e.g., `"T"`).
76    /// * `resolved_type`: The concrete `TUnion` type that `name` resolves to here.
77    pub fn with_resolved_template_type(mut self, name: Atom, resolved_type: TUnion) -> Self {
78        self.resolved_template_types.push((name, resolved_type));
79        self
80    }
81
82    /// Returns a slice of the defined template parameters and their constraints for this context.
83    #[inline]
84    pub fn get_template_definitions(&self) -> &[(Atom, Vec<(GenericParent, TUnion)>)] {
85        &self.template_definitions
86    }
87
88    /// Returns a mutable slice of the defined template parameters and their constraints for this context.
89    #[inline]
90    pub fn get_template_definitions_mut(&mut self) -> &mut [(Atom, Vec<(GenericParent, TUnion)>)] {
91        &mut self.template_definitions
92    }
93
94    /// Returns a slice of the template parameters that have resolved to concrete types in this context.
95    #[inline]
96    pub fn get_resolved_template_types(&self) -> &[(Atom, TUnion)] {
97        &self.resolved_template_types
98    }
99
100    /// Returns a mutable slice of the template parameters that have resolved to concrete types in this context.
101    #[inline]
102    pub fn get_resolved_template_types_mut(&mut self) -> &mut [(Atom, TUnion)] {
103        &mut self.resolved_template_types
104    }
105
106    /// Looks up the constraints for a specific template parameter defined in this context.
107    ///
108    /// # Arguments
109    ///
110    /// * `name`: The name of the template parameter (e.g., `"T"`) to look up.
111    ///
112    /// # Returns
113    ///
114    /// `Some` containing a reference to the vector of constraints if the template is defined, `None` otherwise.
115    pub fn get_template_definition(&self, name: &str) -> Option<&Vec<(GenericParent, TUnion)>> {
116        self.template_definitions.iter().find(|(n, _)| n == name).map(|(_, constraints)| constraints)
117    }
118
119    /// Checks if a specific template parameter is defined in this context.
120    ///
121    /// # Arguments
122    ///
123    /// * `name`: The name of the template parameter (e.g., `"T"`) to check.
124    ///
125    /// # Returns
126    ///
127    /// `true` if the template parameter is defined, `false` otherwise.
128    pub fn has_template_definition(&self, name: &str) -> bool {
129        self.template_definitions.iter().any(|(n, _)| n == name)
130    }
131
132    /// Adds type aliases from a class to this context.
133    ///
134    /// # Arguments
135    ///
136    /// * `aliases`: A set of type alias names.
137    pub fn with_type_aliases(mut self, aliases: AtomSet) -> Self {
138        self.type_aliases = aliases;
139        self
140    }
141
142    /// Adds a single type alias to this context.
143    ///
144    /// # Arguments
145    ///
146    /// * `name`: The name of the type alias to add.
147    pub fn with_type_alias(mut self, name: Atom) -> Self {
148        self.type_aliases.insert(name);
149        self
150    }
151
152    /// Checks if a specific type alias is defined in this context.
153    ///
154    /// # Arguments
155    ///
156    /// * `name`: The name of the type alias to check.
157    pub fn has_type_alias(&self, name: &Atom) -> bool {
158        self.type_aliases.contains(name)
159    }
160
161    /// Adds an imported type alias to this context.
162    ///
163    /// # Arguments
164    ///
165    /// * `local_name`: The local name of the imported alias (possibly renamed with "as").
166    /// * `source_class`: The FQCN of the class where the type alias is defined.
167    /// * `original_name`: The original name of the type alias in the source class.
168    pub fn with_imported_type_alias(mut self, local_name: Atom, source_class: Atom, original_name: Atom) -> Self {
169        self.imported_type_aliases.insert(local_name, (source_class, original_name));
170        self
171    }
172
173    /// Looks up an imported type alias in this context.
174    ///
175    /// # Arguments
176    ///
177    /// * `name`: The local name of the imported alias to look up.
178    ///
179    /// # Returns
180    ///
181    /// `Some` containing a reference to (source_class, original_name) if found, `None` otherwise.
182    pub fn get_imported_type_alias(&self, name: &Atom) -> Option<&(Atom, Atom)> {
183        self.imported_type_aliases.get(name)
184    }
185
186    /// Checks if a specific imported type alias is defined in this context.
187    ///
188    /// # Arguments
189    ///
190    /// * `name`: The local name of the imported alias to check.
191    pub fn has_imported_type_alias(&self, name: &Atom) -> bool {
192        self.imported_type_aliases.contains_key(name)
193    }
194
195    /// Looks up the concrete type that a specific template parameter resolves to in this context.
196    ///
197    /// # Arguments
198    ///
199    /// * `name`: The name of the template parameter (e.g., `"T"`) to look up.
200    ///
201    /// # Returns
202    ///
203    /// `Some` containing a reference to the resolved `TUnion` type if found, `None` otherwise.
204    /// Note: If multiple entries exist for the same name (due to shadowing or errors),
205    /// this currently returns the first match found.
206    pub fn get_resolved_template_type(&self, name: &str) -> Option<&TUnion> {
207        self.resolved_template_types
208            .iter()
209            // Iterate in reverse if shadowing means the *last* added binding is correct
210            // .rev()
211            .find(|(n, _)| n == name)
212            .map(|(_, resolved_type)| resolved_type)
213    }
214
215    /// Checks if this context contains any template definitions or resolved template types.
216    #[inline]
217    pub fn has_templates(&self) -> bool {
218        !self.template_definitions.is_empty() || !self.resolved_template_types.is_empty()
219    }
220
221    /// Checks if a specific template parameter has a concrete resolved type in this context.
222    #[inline]
223    pub fn is_template_resolved(&self, name: &str) -> bool {
224        self.resolved_template_types.iter().any(|(n, _)| n == name)
225    }
226
227    /// Merges another `TypeResolutionContext` into this one, combining their template definitions
228    /// and resolved types.
229    #[inline]
230    pub fn merge(&mut self, other: TypeResolutionContext) {
231        self.template_definitions.extend(other.template_definitions);
232        self.resolved_template_types.extend(other.resolved_template_types);
233    }
234}