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