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