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