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