1use ahash::HashMap;
2use ahash::RandomState;
3use indexmap::IndexMap;
4use serde::Deserialize;
5use serde::Serialize;
6
7use mago_atom::Atom;
8use mago_atom::AtomMap;
9use mago_atom::AtomSet;
10use mago_reporting::Issue;
11use mago_span::Span;
12
13use crate::flags::attribute::AttributeFlags;
14use crate::identifier::method::MethodIdentifier;
15use crate::metadata::attribute::AttributeMetadata;
16use crate::metadata::class_like_constant::ClassLikeConstantMetadata;
17use crate::metadata::enum_case::EnumCaseMetadata;
18use crate::metadata::flags::MetadataFlags;
19use crate::metadata::property::PropertyMetadata;
20use crate::metadata::ttype::TypeMetadata;
21use crate::symbol::SymbolKind;
22use crate::ttype::atomic::TAtomic;
23use crate::ttype::template::GenericTemplate;
24use crate::ttype::template::variance::Variance;
25use crate::ttype::union::TUnion;
26use crate::visibility::Visibility;
27
28pub type TemplateTypes = IndexMap<Atom, GenericTemplate, RandomState>;
31
32#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
37#[non_exhaustive]
38pub struct ClassLikeMetadata {
39 pub name: Atom,
40 pub original_name: Atom,
41 pub span: Span,
42 pub direct_parent_interfaces: AtomSet,
43 pub all_parent_interfaces: AtomSet,
44 pub direct_parent_class: Option<Atom>,
45 pub require_extends: AtomSet,
46 pub require_implements: AtomSet,
47 pub all_parent_classes: AtomSet,
48 pub used_traits: AtomSet,
49 pub trait_alias_map: AtomMap<Atom>,
50 pub trait_visibility_map: AtomMap<Visibility>,
51 pub trait_final_map: AtomSet,
52 pub child_class_likes: Option<AtomSet>,
53 pub name_span: Option<Span>,
54 pub kind: SymbolKind,
55 pub template_types: TemplateTypes,
56 pub template_readonly: AtomSet,
57 pub template_variance: HashMap<usize, Variance>,
58 pub template_extended_offsets: AtomMap<Vec<TUnion>>,
59 pub template_extended_parameters: AtomMap<IndexMap<Atom, TUnion, RandomState>>,
60 pub template_type_extends_count: AtomMap<usize>,
61 pub template_type_implements_count: AtomMap<usize>,
62 pub template_type_uses_count: AtomMap<usize>,
63 pub methods: AtomSet,
64 pub pseudo_methods: AtomSet,
65 pub static_pseudo_methods: AtomSet,
66 pub declaring_method_ids: AtomMap<MethodIdentifier>,
67 pub appearing_method_ids: AtomMap<MethodIdentifier>,
68 pub inheritable_method_ids: AtomMap<MethodIdentifier>,
69 pub overridden_method_ids: AtomMap<IndexMap<Atom, MethodIdentifier, RandomState>>,
70 pub properties: AtomMap<PropertyMetadata>,
71 pub appearing_property_ids: AtomMap<Atom>,
72 pub declaring_property_ids: AtomMap<Atom>,
73 pub inheritable_property_ids: AtomMap<Atom>,
74 pub overridden_property_ids: AtomMap<AtomSet>,
75 pub initialized_properties: AtomSet,
76 pub constants: AtomMap<ClassLikeConstantMetadata>,
77 pub trait_constant_ids: AtomMap<Atom>,
78 pub enum_cases: AtomMap<EnumCaseMetadata>,
79 pub invalid_dependencies: AtomSet,
80 pub attributes: Vec<AttributeMetadata>,
81 pub enum_type: Option<TAtomic>,
82 pub has_sealed_methods: Option<bool>,
83 pub has_sealed_properties: Option<bool>,
84 pub permitted_inheritors: Option<AtomSet>,
85 pub issues: Vec<Issue>,
86 pub attribute_flags: Option<AttributeFlags>,
87 pub flags: MetadataFlags,
88 pub type_aliases: AtomMap<TypeMetadata>,
89 pub imported_type_aliases: AtomMap<(Atom, Atom, Span)>,
91 pub mixins: Vec<TUnion>,
94}
95
96impl ClassLikeMetadata {
97 #[must_use]
98 pub fn new(
99 name: Atom,
100 original_name: Atom,
101 span: Span,
102 name_span: Option<Span>,
103 flags: MetadataFlags,
104 ) -> ClassLikeMetadata {
105 ClassLikeMetadata {
106 constants: AtomMap::default(),
107 trait_constant_ids: AtomMap::default(),
108 enum_cases: AtomMap::default(),
109 flags,
110 kind: SymbolKind::Class,
111 direct_parent_interfaces: AtomSet::default(),
112 all_parent_classes: AtomSet::default(),
113 appearing_method_ids: AtomMap::default(),
114 attributes: Vec::new(),
115 all_parent_interfaces: AtomSet::default(),
116 declaring_method_ids: AtomMap::default(),
117 appearing_property_ids: AtomMap::default(),
118 declaring_property_ids: AtomMap::default(),
119 direct_parent_class: None,
120 require_extends: AtomSet::default(),
121 require_implements: AtomSet::default(),
122 inheritable_method_ids: AtomMap::default(),
123 enum_type: None,
124 inheritable_property_ids: AtomMap::default(),
125 initialized_properties: AtomSet::default(),
126 invalid_dependencies: AtomSet::default(),
127 span,
128 name_span,
129 methods: AtomSet::default(),
130 pseudo_methods: AtomSet::default(),
131 static_pseudo_methods: AtomSet::default(),
132 overridden_method_ids: AtomMap::default(),
133 overridden_property_ids: AtomMap::default(),
134 properties: AtomMap::default(),
135 template_variance: HashMap::default(),
136 template_type_extends_count: AtomMap::default(),
137 template_extended_parameters: AtomMap::default(),
138 template_extended_offsets: AtomMap::default(),
139 template_type_implements_count: AtomMap::default(),
140 template_type_uses_count: AtomMap::default(),
141 template_types: TemplateTypes::default(),
142 used_traits: AtomSet::default(),
143 trait_alias_map: AtomMap::default(),
144 trait_visibility_map: AtomMap::default(),
145 trait_final_map: AtomSet::default(),
146 name,
147 original_name,
148 child_class_likes: None,
149 template_readonly: AtomSet::default(),
150 has_sealed_methods: None,
151 has_sealed_properties: None,
152 permitted_inheritors: None,
153 issues: vec![],
154 attribute_flags: None,
155 type_aliases: AtomMap::default(),
156 imported_type_aliases: AtomMap::default(),
157 mixins: Vec::default(),
158 }
159 }
160
161 #[inline]
163 #[must_use]
164 pub fn get_trait_alias_map(&self) -> &AtomMap<Atom> {
165 &self.trait_alias_map
166 }
167
168 #[inline]
170 #[must_use]
171 pub fn get_template_type_names(&self) -> Vec<Atom> {
172 self.template_types.keys().copied().collect()
173 }
174
175 #[inline]
177 #[must_use]
178 pub fn get_template_type(&self, name: &Atom) -> Option<&GenericTemplate> {
179 self.template_types.get(name)
180 }
181
182 #[inline]
184 #[must_use]
185 pub fn get_template_type_with_index(&self, name: &Atom) -> Option<(usize, &GenericTemplate)> {
186 self.template_types.get_full(name).map(|(index, _, types)| (index, types))
187 }
188
189 #[must_use]
190 pub fn get_template_for_index(&self, index: usize) -> Option<(Atom, &GenericTemplate)> {
191 self.template_types.get_index(index).map(|(name, types)| (*name, types))
192 }
193
194 #[must_use]
195 pub fn get_template_name_for_index(&self, index: usize) -> Option<Atom> {
196 self.template_types.get_index(index).map(|(name, _)| *name)
197 }
198
199 #[must_use]
200 pub fn get_template_index_for_name(&self, name: &Atom) -> Option<usize> {
201 self.template_types.get_index_of(name)
202 }
203
204 #[inline]
206 #[must_use]
207 pub fn has_parent(&self, parent: &Atom) -> bool {
208 self.all_parent_classes.contains(parent) || self.all_parent_interfaces.contains(parent)
209 }
210
211 #[inline]
213 #[must_use]
214 pub fn has_template_extended_parameter(&self, parent: &Atom) -> bool {
215 self.template_extended_parameters.contains_key(parent)
216 }
217
218 #[inline]
220 #[must_use]
221 pub fn has_appearing_method(&self, method: &Atom) -> bool {
222 self.appearing_method_ids.contains_key(method)
223 }
224
225 #[inline]
227 #[must_use]
228 pub fn get_property_names(&self) -> AtomSet {
229 self.properties.keys().copied().collect()
230 }
231
232 #[inline]
234 #[must_use]
235 pub fn has_appearing_property(&self, name: &Atom) -> bool {
236 self.appearing_property_ids.contains_key(name)
237 }
238
239 #[inline]
241 #[must_use]
242 pub fn has_declaring_property(&self, name: &Atom) -> bool {
243 self.declaring_property_ids.contains_key(name)
244 }
245
246 #[inline]
248 pub fn take_issues(&mut self) -> Vec<Issue> {
249 std::mem::take(&mut self.issues)
250 }
251
252 #[inline]
254 pub fn add_direct_parent_interface(&mut self, interface: Atom) {
255 self.direct_parent_interfaces.insert(interface);
256 self.all_parent_interfaces.insert(interface);
257 }
258
259 #[inline]
261 pub fn add_all_parent_interface(&mut self, interface: Atom) {
262 self.all_parent_interfaces.insert(interface);
263 }
264
265 #[inline]
267 pub fn add_all_parent_interfaces(&mut self, interfaces: impl IntoIterator<Item = Atom>) {
268 self.all_parent_interfaces.extend(interfaces);
269 }
270
271 #[inline]
273 pub fn add_all_parent_classes(&mut self, classes: impl IntoIterator<Item = Atom>) {
274 self.all_parent_classes.extend(classes);
275 }
276
277 #[inline]
279 pub fn add_used_trait(&mut self, trait_name: Atom) -> bool {
280 self.used_traits.insert(trait_name)
281 }
282
283 #[inline]
285 pub fn add_used_traits(&mut self, traits: impl IntoIterator<Item = Atom>) {
286 self.used_traits.extend(traits);
287 }
288
289 #[inline]
291 pub fn add_trait_alias(&mut self, method: Atom, alias: Atom) -> Option<Atom> {
292 self.trait_alias_map.insert(method, alias)
293 }
294
295 #[inline]
297 pub fn add_trait_visibility(&mut self, method: Atom, visibility: Visibility) -> Option<Visibility> {
298 self.trait_visibility_map.insert(method, visibility)
299 }
300
301 #[inline]
303 pub fn add_template_type(&mut self, name: Atom, constraint: GenericTemplate) {
304 self.template_types.insert(name, constraint);
305 }
306
307 #[inline]
309 pub fn add_template_variance_parameter(&mut self, index: usize, variance: Variance) -> Option<Variance> {
310 self.template_variance.insert(index, variance)
311 }
312
313 #[inline]
315 pub fn add_template_extended_offset(&mut self, name: Atom, types: Vec<TUnion>) -> Option<Vec<TUnion>> {
316 self.template_extended_offsets.insert(name, types)
317 }
318
319 #[inline]
321 pub fn extend_template_extended_parameters(
322 &mut self,
323 template_extended_parameters: AtomMap<IndexMap<Atom, TUnion, RandomState>>,
324 ) {
325 self.template_extended_parameters.extend(template_extended_parameters);
326 }
327
328 #[inline]
330 pub fn add_template_extended_parameter(
331 &mut self,
332 parent_fqcn: Atom,
333 parameter_name: Atom,
334 parameter_type: TUnion,
335 ) -> Option<TUnion> {
336 self.template_extended_parameters.entry(parent_fqcn).or_default().insert(parameter_name, parameter_type)
337 }
338
339 #[inline]
341 pub fn add_declaring_method_id(
342 &mut self,
343 method: Atom,
344 declaring_method_id: MethodIdentifier,
345 ) -> Option<MethodIdentifier> {
346 self.add_appearing_method_id(method, declaring_method_id);
347 self.declaring_method_ids.insert(method, declaring_method_id)
348 }
349
350 #[inline]
352 pub fn add_appearing_method_id(
353 &mut self,
354 method: Atom,
355 appearing_method_id: MethodIdentifier,
356 ) -> Option<MethodIdentifier> {
357 self.appearing_method_ids.insert(method, appearing_method_id)
358 }
359
360 #[inline]
362 pub fn add_overridden_method_parent(
363 &mut self,
364 method: Atom,
365 parent_method_id: MethodIdentifier,
366 ) -> Option<MethodIdentifier> {
367 self.overridden_method_ids
368 .entry(method)
369 .or_default()
370 .insert(*parent_method_id.get_class_name(), parent_method_id)
371 }
372
373 #[inline]
375 pub fn add_property(&mut self, name: Atom, property_metadata: PropertyMetadata) -> Option<PropertyMetadata> {
376 let class_name = self.name;
377
378 self.add_declaring_property_id(name, class_name);
379 if property_metadata.flags.has_default() {
380 self.initialized_properties.insert(name);
381 }
382
383 if !property_metadata.is_final() {
384 self.inheritable_property_ids.insert(name, class_name);
385 }
386
387 self.properties.insert(name, property_metadata)
388 }
389
390 #[inline]
392 pub fn add_property_metadata(&mut self, property_metadata: PropertyMetadata) -> Option<PropertyMetadata> {
393 let name = property_metadata.get_name().0;
394
395 self.add_property(name, property_metadata)
396 }
397
398 #[inline]
400 pub fn add_declaring_property_id(&mut self, prop: Atom, declaring_fqcn: Atom) -> Option<Atom> {
401 self.appearing_property_ids.insert(prop, declaring_fqcn);
402 self.declaring_property_ids.insert(prop, declaring_fqcn)
403 }
404
405 #[must_use]
406 pub fn get_missing_required_interface<'a>(&self, other: &'a ClassLikeMetadata) -> Option<&'a Atom> {
407 for required_interface in &other.require_implements {
408 if self.all_parent_interfaces.contains(required_interface) {
409 continue;
410 }
411
412 if (self.flags.is_abstract() || self.kind.is_trait())
413 && self.require_implements.contains(required_interface)
414 {
415 continue; }
417
418 return Some(required_interface);
419 }
420
421 None
422 }
423
424 #[must_use]
425 pub fn get_missing_required_extends<'a>(&self, other: &'a ClassLikeMetadata) -> Option<&'a Atom> {
426 for required_extend in &other.require_extends {
427 if self.all_parent_classes.contains(required_extend) {
428 continue;
429 }
430
431 if self.kind.is_interface() && self.all_parent_interfaces.contains(required_extend) {
432 continue;
433 }
434
435 if (self.flags.is_abstract() || self.kind.is_trait()) && self.require_extends.contains(required_extend) {
436 continue; }
438
439 return Some(required_extend);
440 }
441
442 None
443 }
444
445 #[must_use]
446 pub fn is_permitted_to_inherit(&self, other: &ClassLikeMetadata) -> bool {
447 if self.kind.is_trait() || self.flags.is_abstract() {
448 return true; }
450
451 let Some(permitted_inheritors) = &other.permitted_inheritors else {
452 return true; };
454
455 if permitted_inheritors.contains(&self.name) {
456 return true; }
458
459 self.all_parent_interfaces.iter().any(|parent_interface| permitted_inheritors.contains(parent_interface))
460 || self.all_parent_classes.iter().any(|parent_class| permitted_inheritors.contains(parent_class))
461 || self.used_traits.iter().any(|used_trait| permitted_inheritors.contains(used_trait))
462 }
463
464 #[inline]
465 pub fn mark_as_populated(&mut self) {
466 self.flags |= MetadataFlags::POPULATED;
467 self.shrink_to_fit();
468 }
469
470 #[inline]
471 pub fn shrink_to_fit(&mut self) {
472 self.properties.shrink_to_fit();
473 self.initialized_properties.shrink_to_fit();
474 self.appearing_property_ids.shrink_to_fit();
475 self.declaring_property_ids.shrink_to_fit();
476 self.inheritable_property_ids.shrink_to_fit();
477 self.overridden_property_ids.shrink_to_fit();
478 self.appearing_method_ids.shrink_to_fit();
479 self.declaring_method_ids.shrink_to_fit();
480 self.inheritable_method_ids.shrink_to_fit();
481 self.overridden_method_ids.shrink_to_fit();
482 self.attributes.shrink_to_fit();
483 self.constants.shrink_to_fit();
484 self.enum_cases.shrink_to_fit();
485 self.type_aliases.shrink_to_fit();
486 }
487}