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