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