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