mago_codex/metadata/
class_like.rs

1use ahash::HashMap;
2use ahash::HashSet;
3use ahash::RandomState;
4use indexmap::IndexMap;
5use serde::Deserialize;
6use serde::Serialize;
7
8use mago_interner::StringIdentifier;
9use mago_reporting::Issue;
10use mago_span::Span;
11
12use crate::flags::attribute::AttributeFlags;
13use crate::metadata::attribute::AttributeMetadata;
14use crate::metadata::class_like_constant::ClassLikeConstantMetadata;
15use crate::metadata::enum_case::EnumCaseMetadata;
16use crate::metadata::flags::MetadataFlags;
17use crate::metadata::property::PropertyMetadata;
18use crate::misc::GenericParent;
19use crate::symbol::SymbolKind;
20use crate::ttype::atomic::TAtomic;
21use crate::ttype::template::variance::Variance;
22use crate::ttype::union::TUnion;
23use crate::visibility::Visibility;
24
25type TemplateTuple = (StringIdentifier, Vec<(GenericParent, TUnion)>);
26
27/// Contains comprehensive metadata for a PHP class-like structure (class, interface, trait, enum).
28///
29/// Aggregates information about inheritance, traits, generics, methods, properties, constants,
30/// attributes, docblock tags, analysis flags, and more.
31#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
32pub struct ClassLikeMetadata {
33    pub name: StringIdentifier,
34    pub original_name: StringIdentifier,
35    pub span: Span,
36    pub direct_parent_interfaces: Vec<StringIdentifier>,
37    pub all_parent_interfaces: Vec<StringIdentifier>,
38    pub direct_parent_class: Option<StringIdentifier>,
39    pub require_extends: Vec<StringIdentifier>,
40    pub require_implements: Vec<StringIdentifier>,
41    pub all_parent_classes: Vec<StringIdentifier>,
42    pub used_traits: HashSet<StringIdentifier>,
43    pub trait_alias_map: HashMap<StringIdentifier, StringIdentifier>,
44    pub trait_visibility_map: HashMap<StringIdentifier, Visibility>,
45    pub trait_final_map: HashSet<StringIdentifier>,
46    pub child_class_likes: Option<HashSet<StringIdentifier>>,
47    pub name_span: Option<Span>,
48    pub kind: SymbolKind,
49    pub template_types: Vec<TemplateTuple>,
50    pub template_readonly: HashSet<StringIdentifier>,
51    pub template_variance: HashMap<usize, Variance>,
52    pub template_extended_offsets: HashMap<StringIdentifier, Vec<TUnion>>,
53    pub template_extended_parameters: HashMap<StringIdentifier, IndexMap<StringIdentifier, TUnion, RandomState>>,
54    pub template_type_extends_count: HashMap<StringIdentifier, usize>,
55    pub template_type_implements_count: HashMap<StringIdentifier, usize>,
56    pub template_type_uses_count: HashMap<StringIdentifier, usize>,
57    pub methods: Vec<StringIdentifier>,
58    pub pseudo_methods: Vec<StringIdentifier>,
59    pub static_pseudo_methods: Vec<StringIdentifier>,
60    pub declaring_method_ids: HashMap<StringIdentifier, StringIdentifier>,
61    pub appearing_method_ids: HashMap<StringIdentifier, StringIdentifier>,
62    pub overridden_method_ids: HashMap<StringIdentifier, HashSet<StringIdentifier>>,
63    pub inheritable_method_ids: HashMap<StringIdentifier, StringIdentifier>,
64    pub potential_declaring_method_ids: HashMap<StringIdentifier, HashSet<StringIdentifier>>,
65    pub properties: HashMap<StringIdentifier, PropertyMetadata>,
66    pub appearing_property_ids: HashMap<StringIdentifier, StringIdentifier>,
67    pub declaring_property_ids: HashMap<StringIdentifier, StringIdentifier>,
68    pub inheritable_property_ids: HashMap<StringIdentifier, StringIdentifier>,
69    pub overridden_property_ids: HashMap<StringIdentifier, Vec<StringIdentifier>>,
70    pub initialized_properties: Vec<StringIdentifier>,
71    pub constants: IndexMap<StringIdentifier, ClassLikeConstantMetadata, RandomState>,
72    pub enum_cases: IndexMap<StringIdentifier, EnumCaseMetadata, RandomState>,
73    pub invalid_dependencies: Vec<StringIdentifier>,
74    pub attributes: Vec<AttributeMetadata>,
75    pub enum_type: Option<TAtomic>,
76    pub has_sealed_methods: Option<bool>,
77    pub has_sealed_properties: Option<bool>,
78    pub permitted_inheritors: Option<HashSet<StringIdentifier>>,
79    pub issues: Vec<Issue>,
80    pub attribute_flags: Option<AttributeFlags>,
81    pub flags: MetadataFlags,
82}
83
84impl ClassLikeMetadata {
85    pub fn new(
86        name: StringIdentifier,
87        original_name: StringIdentifier,
88        span: Span,
89        name_span: Option<Span>,
90        flags: MetadataFlags,
91    ) -> ClassLikeMetadata {
92        ClassLikeMetadata {
93            constants: IndexMap::with_hasher(RandomState::new()),
94            enum_cases: IndexMap::with_hasher(RandomState::new()),
95            flags,
96            kind: SymbolKind::Class,
97            direct_parent_interfaces: vec![],
98            all_parent_classes: vec![],
99            appearing_method_ids: HashMap::default(),
100            attributes: Vec::new(),
101            all_parent_interfaces: vec![],
102            declaring_method_ids: HashMap::default(),
103            appearing_property_ids: HashMap::default(),
104            declaring_property_ids: HashMap::default(),
105            direct_parent_class: None,
106            require_extends: vec![],
107            require_implements: vec![],
108            inheritable_method_ids: HashMap::default(),
109            enum_type: None,
110            inheritable_property_ids: HashMap::default(),
111            initialized_properties: vec![],
112            invalid_dependencies: Vec::new(),
113            span,
114            name_span,
115            methods: vec![],
116            pseudo_methods: vec![],
117            static_pseudo_methods: vec![],
118            overridden_method_ids: HashMap::default(),
119            overridden_property_ids: HashMap::default(),
120            potential_declaring_method_ids: HashMap::default(),
121            properties: HashMap::default(),
122            template_variance: HashMap::default(),
123            template_type_extends_count: HashMap::default(),
124            template_extended_parameters: HashMap::default(),
125            template_extended_offsets: HashMap::default(),
126            template_type_implements_count: HashMap::default(),
127            template_type_uses_count: HashMap::default(),
128            template_types: vec![],
129            used_traits: HashSet::default(),
130            trait_alias_map: HashMap::default(),
131            trait_visibility_map: HashMap::default(),
132            trait_final_map: HashSet::default(),
133            name,
134            original_name,
135            child_class_likes: None,
136            template_readonly: HashSet::default(),
137            has_sealed_methods: None,
138            has_sealed_properties: None,
139            permitted_inheritors: None,
140            issues: vec![],
141            attribute_flags: None,
142        }
143    }
144
145    /// Returns a reference to the map of trait method aliases.
146    #[inline]
147    pub fn get_trait_alias_map(&self) -> &HashMap<StringIdentifier, StringIdentifier> {
148        &self.trait_alias_map
149    }
150
151    /// Returns a vector of the generic type parameter names.
152    #[inline]
153    pub fn get_template_type_names(&self) -> Vec<StringIdentifier> {
154        self.template_types.iter().map(|(name, _)| *name).collect()
155    }
156
157    /// Returns type parameters for a specific generic parameter name.
158    #[inline]
159    pub fn get_template_type(&self, name: &StringIdentifier) -> Option<&Vec<(GenericParent, TUnion)>> {
160        self.template_types.iter().find_map(|(param_name, types)| if param_name == name { Some(types) } else { None })
161    }
162
163    /// Returns type parameters for a specific generic parameter name with its index.
164    #[inline]
165    pub fn get_template_type_with_index(
166        &self,
167        name: &StringIdentifier,
168    ) -> Option<(usize, &Vec<(GenericParent, TUnion)>)> {
169        self.template_types
170            .iter()
171            .enumerate()
172            .find_map(|(index, (param_name, types))| if param_name == name { Some((index, types)) } else { None })
173    }
174
175    pub fn get_template_for_index(&self, index: usize) -> Option<(StringIdentifier, &Vec<(GenericParent, TUnion)>)> {
176        self.template_types.get(index).map(|(name, types)| (*name, types))
177    }
178
179    pub fn get_template_name_for_index(&self, index: usize) -> Option<StringIdentifier> {
180        self.template_types.get(index).map(|(name, _)| *name)
181    }
182
183    pub fn get_template_index_for_name(&self, name: &StringIdentifier) -> Option<usize> {
184        self.template_types.iter().position(|(param_name, _)| param_name == name)
185    }
186
187    /// Checks if a specific parent is either a parent class or interface.
188    #[inline]
189    pub fn has_parent(&self, parent: &StringIdentifier) -> bool {
190        self.all_parent_classes.contains(parent) || self.all_parent_interfaces.contains(parent)
191    }
192
193    /// Checks if a specific parent has template extended parameters.
194    #[inline]
195    pub fn has_template_extended_parameter(&self, parent: &StringIdentifier) -> bool {
196        self.template_extended_parameters.contains_key(parent)
197    }
198
199    /// Returns a slice of methods defined directly in this class-like.
200    #[inline]
201    pub fn get_methods(&self) -> &[StringIdentifier] {
202        &self.methods
203    }
204
205    /// Checks if a specific method is defined in this class-like.
206    #[inline]
207    pub fn has_method(&self, method: &StringIdentifier) -> bool {
208        self.methods.contains(method)
209    }
210
211    /// Returns a reference to the map of method name to its declaring class/trait FQCN.
212    #[inline]
213    pub fn get_declaring_method_ids(&self) -> &HashMap<StringIdentifier, StringIdentifier> {
214        &self.declaring_method_ids
215    }
216
217    /// Returns a reference to the map of method name to its appearing class/trait FQCN in this context.
218    #[inline]
219    pub fn get_appearing_method_ids(&self) -> &HashMap<StringIdentifier, StringIdentifier> {
220        &self.appearing_method_ids
221    }
222
223    /// Checks if a specific method appears in this class-like.
224    #[inline]
225    pub fn has_appearing_method(&self, method: &StringIdentifier) -> bool {
226        self.appearing_method_ids.contains_key(method)
227    }
228
229    /// Returns a reference to the map of overridden method name to the set of parent FQCNs.
230    #[inline]
231    pub fn get_overridden_method_ids(&self) -> &HashMap<StringIdentifier, HashSet<StringIdentifier>> {
232        &self.overridden_method_ids
233    }
234
235    /// Returns a reference to a specific method's overridden parent FQCNs.
236    #[inline]
237    pub fn get_overridden_method_id(&self, method: &StringIdentifier) -> Option<&HashSet<StringIdentifier>> {
238        self.overridden_method_ids.get(method)
239    }
240
241    /// Returns a mutable reference to a specific method's overridden parent FQCNs.
242    #[inline]
243    pub fn get_overridden_method_id_mut(
244        &mut self,
245        method: &StringIdentifier,
246    ) -> Option<&mut HashSet<StringIdentifier>> {
247        self.overridden_method_ids.get_mut(method)
248    }
249
250    /// Returns a reference to a specific method's potential declaring classes/traits.
251    #[inline]
252    pub fn get_potential_declaring_method_id(&self, method: &StringIdentifier) -> Option<&HashSet<StringIdentifier>> {
253        self.potential_declaring_method_ids.get(method)
254    }
255
256    /// Returns a vector of property names.
257    #[inline]
258    pub fn get_property_names(&self) -> Vec<StringIdentifier> {
259        self.properties.keys().copied().collect()
260    }
261
262    /// Checks if a specific property appears in this class-like.
263    #[inline]
264    pub fn has_appearing_property(&self, name: &StringIdentifier) -> bool {
265        self.appearing_property_ids.contains_key(name)
266    }
267
268    /// Checks if a specific property is declared in this class-like.
269    #[inline]
270    pub fn has_declaring_property(&self, name: &StringIdentifier) -> bool {
271        self.declaring_property_ids.contains_key(name)
272    }
273
274    /// Takes ownership of the issues found for this class-like structure.
275    #[inline]
276    pub fn take_issues(&mut self) -> Vec<Issue> {
277        std::mem::take(&mut self.issues)
278    }
279
280    /// Adds a single direct parent interface.
281    #[inline]
282    pub fn add_direct_parent_interface(&mut self, interface: StringIdentifier) {
283        self.direct_parent_interfaces.push(interface);
284        self.all_parent_interfaces.push(interface);
285    }
286
287    /// Adds a single interface to the list of all parent interfaces. Use with caution, normally derived.
288    #[inline]
289    pub fn add_all_parent_interface(&mut self, interface: StringIdentifier) {
290        self.all_parent_interfaces.push(interface);
291    }
292
293    /// Adds multiple interfaces to the list of all parent interfaces. Use with caution.
294    #[inline]
295    pub fn add_all_parent_interfaces(&mut self, interfaces: impl IntoIterator<Item = StringIdentifier>) {
296        self.all_parent_interfaces.extend(interfaces);
297    }
298
299    /// Adds a single required extend entry.
300    #[inline]
301    pub fn add_require_extend(&mut self, require: StringIdentifier) {
302        self.require_extends.push(require);
303    }
304
305    /// Adds a single required implement entry.
306    #[inline]
307    pub fn add_require_implement(&mut self, require: StringIdentifier) {
308        self.require_implements.push(require);
309    }
310
311    /// Adds multiple ancestor classes. Use with caution.
312    #[inline]
313    pub fn add_all_parent_classes(&mut self, classes: impl IntoIterator<Item = StringIdentifier>) {
314        self.all_parent_classes.extend(classes);
315    }
316
317    /// Adds a single used trait. Returns `true` if the trait was not already present.
318    #[inline]
319    pub fn add_used_trait(&mut self, trait_name: StringIdentifier) -> bool {
320        self.used_traits.insert(trait_name)
321    }
322
323    /// Adds multiple used traits.
324    #[inline]
325    pub fn add_used_traits(&mut self, traits: impl IntoIterator<Item = StringIdentifier>) {
326        self.used_traits.extend(traits);
327    }
328
329    /// Adds or updates a single trait alias. Returns the previous original name if one existed for the alias.
330    #[inline]
331    pub fn add_trait_alias(&mut self, method: StringIdentifier, alias: StringIdentifier) -> Option<StringIdentifier> {
332        self.trait_alias_map.insert(method, alias)
333    }
334
335    /// Adds or updates a single trait visibility override. Returns the previous visibility if one existed.
336    #[inline]
337    pub fn add_trait_visibility(&mut self, method: StringIdentifier, visibility: Visibility) -> Option<Visibility> {
338        self.trait_visibility_map.insert(method, visibility)
339    }
340
341    /// Adds a single final trait method. Returns `true` if the method was not already present.
342    #[inline]
343    pub fn add_trait_final(&mut self, method: StringIdentifier) -> bool {
344        self.trait_final_map.insert(method)
345    }
346
347    /// Adds a single template type definition.
348    #[inline]
349    pub fn add_template_type(&mut self, template: TemplateTuple) {
350        self.template_types.push(template);
351    }
352    /// Adds a single readonly template parameter. Returns `true` if the parameter was not already present.
353    #[inline]
354    pub fn add_template_readonly(&mut self, template_name: StringIdentifier) -> bool {
355        self.template_readonly.insert(template_name)
356    }
357
358    /// Adds or updates the variance for a specific parameter index. Returns the previous variance if one existed.
359    #[inline]
360    pub fn add_template_variance_parameter(&mut self, index: usize, variance: Variance) -> Option<Variance> {
361        self.template_variance.insert(index, variance)
362    }
363
364    /// Adds or replaces the offset types for a specific template parameter name.
365    #[inline]
366    pub fn add_template_extended_offset(&mut self, name: StringIdentifier, types: Vec<TUnion>) -> Option<Vec<TUnion>> {
367        self.template_extended_offsets.insert(name, types)
368    }
369
370    /// Adds or replaces the resolved parameters for a specific parent FQCN.
371    #[inline]
372    pub fn extend_template_extended_parameters(
373        &mut self,
374        template_extended_parameters: HashMap<StringIdentifier, IndexMap<StringIdentifier, TUnion, RandomState>>,
375    ) {
376        self.template_extended_parameters.extend(template_extended_parameters);
377    }
378
379    /// Adds or replaces a single resolved parameter for the parent FQCN.
380    #[inline]
381    pub fn add_template_extended_parameter(
382        &mut self,
383        parent_fqcn: StringIdentifier,
384        parameter_name: StringIdentifier,
385        parameter_type: TUnion,
386    ) -> Option<TUnion> {
387        self.template_extended_parameters.entry(parent_fqcn).or_default().insert(parameter_name, parameter_type)
388    }
389
390    /// Adds a single method name.
391    #[inline]
392    pub fn add_method(&mut self, method: StringIdentifier) {
393        self.methods.push(method);
394    }
395
396    /// Adds or updates the declaring class FQCN for a method name.
397    #[inline]
398    pub fn add_declaring_method_id(
399        &mut self,
400        method: StringIdentifier,
401        declaring_fqcn: StringIdentifier,
402    ) -> Option<StringIdentifier> {
403        self.add_appearing_method_id(method, declaring_fqcn);
404        self.declaring_method_ids.insert(method, declaring_fqcn)
405    }
406
407    /// Adds or updates the appearing class FQCN for a method name.
408    #[inline]
409    pub fn add_appearing_method_id(
410        &mut self,
411        method: StringIdentifier,
412        appearing_fqcn: StringIdentifier,
413    ) -> Option<StringIdentifier> {
414        self.appearing_method_ids.insert(method, appearing_fqcn)
415    }
416
417    /// Adds a parent FQCN to the set for an overridden method. Initializes set if needed. Returns `true` if added.
418    #[inline]
419    pub fn add_overridden_method_parent(&mut self, method: StringIdentifier, parent_fqcn: StringIdentifier) -> bool {
420        self.overridden_method_ids.entry(method).or_default().insert(parent_fqcn)
421    }
422
423    /// Adds a potential declaring FQCN to the set for a method. Initializes set if needed. Returns `true` if added.
424    #[inline]
425    pub fn add_potential_declaring_method(
426        &mut self,
427        method: StringIdentifier,
428        potential_fqcn: StringIdentifier,
429    ) -> bool {
430        self.potential_declaring_method_ids.entry(method).or_default().insert(potential_fqcn)
431    }
432
433    /// Adds or updates a property's metadata. Returns the previous metadata if the property existed.
434    #[inline]
435    pub fn add_property(
436        &mut self,
437        name: StringIdentifier,
438        property_metadata: PropertyMetadata,
439    ) -> Option<PropertyMetadata> {
440        let class_name = self.name;
441
442        self.add_declaring_property_id(name, class_name);
443        if property_metadata.flags.has_default() {
444            self.initialized_properties.push(name);
445        }
446
447        if !property_metadata.is_final() {
448            self.inheritable_property_ids.insert(name, class_name);
449        }
450
451        self.properties.insert(name, property_metadata)
452    }
453
454    /// Adds or updates a property's metadata using just the property metadata. Returns the previous metadata if the property existed.
455    #[inline]
456    pub fn add_property_metadata(&mut self, property_metadata: PropertyMetadata) -> Option<PropertyMetadata> {
457        let name = property_metadata.get_name().0;
458
459        self.add_property(name, property_metadata)
460    }
461
462    /// Adds or updates the declaring class FQCN for a property name.
463    #[inline]
464    pub fn add_declaring_property_id(
465        &mut self,
466        prop: StringIdentifier,
467        declaring_fqcn: StringIdentifier,
468    ) -> Option<StringIdentifier> {
469        self.appearing_property_ids.insert(prop, declaring_fqcn);
470
471        self.declaring_property_ids.insert(prop, declaring_fqcn)
472    }
473
474    #[inline]
475    pub fn mark_as_populated(&mut self) {
476        self.flags |= MetadataFlags::POPULATED;
477        self.shrink_to_fit();
478    }
479
480    #[inline]
481    pub fn shrink_to_fit(&mut self) {
482        self.properties.shrink_to_fit();
483        self.initialized_properties.shrink_to_fit();
484        self.appearing_property_ids.shrink_to_fit();
485        self.declaring_property_ids.shrink_to_fit();
486        self.inheritable_property_ids.shrink_to_fit();
487        self.overridden_property_ids.shrink_to_fit();
488        self.appearing_method_ids.shrink_to_fit();
489        self.declaring_method_ids.shrink_to_fit();
490        self.inheritable_method_ids.shrink_to_fit();
491        self.overridden_method_ids.shrink_to_fit();
492        self.potential_declaring_method_ids.shrink_to_fit();
493        self.attributes.shrink_to_fit();
494        self.constants.shrink_to_fit();
495        self.enum_cases.shrink_to_fit();
496    }
497}