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_reporting::Issue;
9use mago_span::Span;
10use mago_word::Word;
11use mago_word::WordMap;
12use mago_word::WordSet;
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<Word, GenericTemplate, RandomState>;
33
34#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
39#[non_exhaustive]
40pub struct ClassLikeMetadata {
41 pub name: Word,
42 pub original_name: Word,
43 pub span: Span,
44 pub direct_parent_interfaces: WordSet,
45 pub all_parent_interfaces: WordSet,
46 pub direct_parent_class: Option<Word>,
47 pub require_extends: WordSet,
48 pub require_implements: WordSet,
49 pub all_parent_classes: WordSet,
50 pub used_traits: WordSet,
51 pub trait_alias_map: WordMap<Word>,
52 pub trait_visibility_map: WordMap<Visibility>,
53 pub trait_final_map: WordSet,
54 pub child_class_likes: Option<WordSet>,
55 pub name_span: Option<Span>,
56 pub kind: SymbolKind,
57 pub template_types: TemplateTypes,
58 pub template_readonly: WordSet,
59 pub template_variance: Vec<Variance>,
60 pub template_extended_offsets: WordMap<Vec<TUnion>>,
61 pub template_extended_parameters: WordMap<IndexMap<Word, TUnion, RandomState>>,
62 pub template_extended_parameter_paths: WordMap<Vec<IndexMap<Word, TUnion, RandomState>>>,
63 pub template_type_extends_count: WordMap<usize>,
64 pub template_type_implements_count: WordMap<usize>,
65 pub template_type_uses_count: WordMap<usize>,
66 pub methods: WordSet,
67 pub pseudo_methods: WordSet,
68 pub static_pseudo_methods: WordSet,
69 pub declaring_method_ids: WordMap<MethodIdentifier>,
70 pub appearing_method_ids: WordMap<MethodIdentifier>,
71 pub inheritable_method_ids: WordMap<MethodIdentifier>,
72 pub overridden_method_ids: WordMap<IndexMap<Word, MethodIdentifier, RandomState>>,
73 pub properties: WordMap<PropertyMetadata>,
74 pub appearing_property_ids: WordMap<Word>,
75 pub declaring_property_ids: WordMap<Word>,
76 pub inheritable_property_ids: WordMap<Word>,
77 pub overridden_property_ids: WordMap<WordSet>,
78 pub initialized_properties: WordSet,
79 pub constants: WordMap<ClassLikeConstantMetadata>,
80 pub trait_constant_ids: WordMap<Word>,
81 pub enum_cases: WordMap<EnumCaseMetadata>,
82 pub invalid_dependencies: WordSet,
83 pub attributes: Vec<AttributeMetadata>,
84 pub enum_type: Option<TAtomic>,
85 pub has_sealed_methods: Option<bool>,
86 pub has_sealed_properties: Option<bool>,
87 pub permitted_inheritors: Option<WordSet>,
88 pub issues: Vec<Issue>,
89 pub attribute_flags: Option<AttributeFlags>,
90 pub flags: MetadataFlags,
91 pub type_aliases: WordMap<TypeMetadata>,
92 pub imported_type_aliases: WordMap<(Word, Word, Span)>,
94 pub mixins: Vec<TUnion>,
97 pub version_constraint: VersionConstraint,
98}
99
100impl ClassLikeMetadata {
101 #[must_use]
102 pub fn new(
103 name: Word,
104 original_name: Word,
105 span: Span,
106 name_span: Option<Span>,
107 flags: MetadataFlags,
108 ) -> ClassLikeMetadata {
109 ClassLikeMetadata {
110 constants: WordMap::default(),
111 trait_constant_ids: WordMap::default(),
112 enum_cases: WordMap::default(),
113 flags,
114 kind: SymbolKind::Class,
115 direct_parent_interfaces: WordSet::default(),
116 all_parent_classes: WordSet::default(),
117 appearing_method_ids: WordMap::default(),
118 attributes: Vec::new(),
119 all_parent_interfaces: WordSet::default(),
120 declaring_method_ids: WordMap::default(),
121 appearing_property_ids: WordMap::default(),
122 declaring_property_ids: WordMap::default(),
123 direct_parent_class: None,
124 require_extends: WordSet::default(),
125 require_implements: WordSet::default(),
126 inheritable_method_ids: WordMap::default(),
127 enum_type: None,
128 inheritable_property_ids: WordMap::default(),
129 initialized_properties: WordSet::default(),
130 invalid_dependencies: WordSet::default(),
131 span,
132 name_span,
133 methods: WordSet::default(),
134 pseudo_methods: WordSet::default(),
135 static_pseudo_methods: WordSet::default(),
136 overridden_method_ids: WordMap::default(),
137 overridden_property_ids: WordMap::default(),
138 properties: WordMap::default(),
139 template_variance: Vec::new(),
140 template_type_extends_count: WordMap::default(),
141 template_extended_parameters: WordMap::default(),
142 template_extended_parameter_paths: WordMap::default(),
143 template_extended_offsets: WordMap::default(),
144 template_type_implements_count: WordMap::default(),
145 template_type_uses_count: WordMap::default(),
146 template_types: TemplateTypes::default(),
147 used_traits: WordSet::default(),
148 trait_alias_map: WordMap::default(),
149 trait_visibility_map: WordMap::default(),
150 trait_final_map: WordSet::default(),
151 name,
152 original_name,
153 child_class_likes: None,
154 template_readonly: WordSet::default(),
155 has_sealed_methods: None,
156 has_sealed_properties: None,
157 permitted_inheritors: None,
158 issues: vec![],
159 attribute_flags: None,
160 type_aliases: WordMap::default(),
161 imported_type_aliases: WordMap::default(),
162 mixins: Vec::default(),
163 version_constraint: VersionConstraint::unconstrained(),
164 }
165 }
166
167 #[inline]
170 #[must_use]
171 pub fn is_available_in_version(&self, version: PHPVersion) -> bool {
172 self.version_constraint.allows_version(version)
173 }
174
175 #[inline]
178 #[must_use]
179 pub fn is_available_in_version_range(&self, range: PHPVersionRange) -> bool {
180 self.version_constraint.allows_version_range(range)
181 }
182
183 #[inline]
185 #[must_use]
186 pub fn get_trait_alias_map(&self) -> &WordMap<Word> {
187 &self.trait_alias_map
188 }
189
190 #[inline]
192 #[must_use]
193 pub fn get_template_type_names(&self) -> Vec<Word> {
194 self.template_types.keys().copied().collect()
195 }
196
197 #[inline]
199 #[must_use]
200 pub fn get_template_type(&self, name: Word) -> Option<&GenericTemplate> {
201 self.template_types.get(&name)
202 }
203
204 #[inline]
206 #[must_use]
207 pub fn get_template_type_with_index(&self, name: Word) -> Option<(usize, &GenericTemplate)> {
208 self.template_types.get_full(&name).map(|(index, _, types)| (index, types))
209 }
210
211 #[must_use]
212 pub fn get_template_for_index(&self, index: usize) -> Option<(Word, &GenericTemplate)> {
213 self.template_types.get_index(index).map(|(name, types)| (*name, types))
214 }
215
216 #[must_use]
217 pub fn get_template_name_for_index(&self, index: usize) -> Option<Word> {
218 self.template_types.get_index(index).map(|(name, _)| *name)
219 }
220
221 #[must_use]
222 pub fn get_template_index_for_name(&self, name: Word) -> Option<usize> {
223 self.template_types.get_index_of(&name)
224 }
225
226 #[inline]
228 #[must_use]
229 pub fn has_parent(&self, parent: Word) -> bool {
230 self.all_parent_classes.contains(&parent) || self.all_parent_interfaces.contains(&parent)
231 }
232
233 #[inline]
235 #[must_use]
236 pub fn has_template_extended_parameter(&self, parent: Word) -> bool {
237 self.template_extended_parameters.contains_key(&parent)
238 }
239
240 #[inline]
242 #[must_use]
243 pub fn has_appearing_method(&self, method: Word) -> bool {
244 self.appearing_method_ids.contains_key(&method)
245 }
246
247 #[inline]
249 #[must_use]
250 pub fn get_property_names(&self) -> WordSet {
251 self.properties.keys().copied().collect()
252 }
253
254 #[inline]
256 #[must_use]
257 pub fn has_appearing_property(&self, name: Word) -> bool {
258 self.appearing_property_ids.contains_key(&name)
259 }
260
261 #[inline]
263 #[must_use]
264 pub fn has_declaring_property(&self, name: Word) -> bool {
265 self.declaring_property_ids.contains_key(&name)
266 }
267
268 #[inline]
270 pub fn take_issues(&mut self) -> Vec<Issue> {
271 std::mem::take(&mut self.issues)
272 }
273
274 #[inline]
276 pub fn add_direct_parent_interface(&mut self, interface: Word) {
277 self.direct_parent_interfaces.insert(interface);
278 self.all_parent_interfaces.insert(interface);
279 }
280
281 #[inline]
283 pub fn add_all_parent_interface(&mut self, interface: Word) {
284 self.all_parent_interfaces.insert(interface);
285 }
286
287 #[inline]
289 pub fn add_all_parent_interfaces(&mut self, interfaces: impl IntoIterator<Item = Word>) {
290 self.all_parent_interfaces.extend(interfaces);
291 }
292
293 #[inline]
295 pub fn add_all_parent_classes(&mut self, classes: impl IntoIterator<Item = Word>) {
296 self.all_parent_classes.extend(classes);
297 }
298
299 #[inline]
301 pub fn add_used_trait(&mut self, trait_name: Word) -> bool {
302 self.used_traits.insert(trait_name)
303 }
304
305 #[inline]
307 pub fn add_used_traits(&mut self, traits: impl IntoIterator<Item = Word>) {
308 self.used_traits.extend(traits);
309 }
310
311 #[inline]
313 pub fn add_trait_alias(&mut self, method: Word, alias: Word) -> Option<Word> {
314 self.trait_alias_map.insert(method, alias)
315 }
316
317 #[inline]
319 pub fn add_trait_visibility(&mut self, method: Word, visibility: Visibility) -> Option<Visibility> {
320 self.trait_visibility_map.insert(method, visibility)
321 }
322
323 #[inline]
325 pub fn add_template_type(&mut self, name: Word, constraint: GenericTemplate) {
326 self.template_types.insert(name, constraint);
327 }
328
329 #[inline]
331 pub fn set_template_variance(&mut self, template_variance: Vec<Variance>) {
332 self.template_variance = template_variance;
333 }
334
335 #[inline]
337 pub fn add_template_extended_offset(&mut self, name: Word, types: Vec<TUnion>) -> Option<Vec<TUnion>> {
338 self.template_extended_offsets.insert(name, types)
339 }
340
341 #[inline]
343 pub fn extend_template_extended_parameters(
344 &mut self,
345 template_extended_parameters: WordMap<IndexMap<Word, TUnion, RandomState>>,
346 ) {
347 self.template_extended_parameters.extend(template_extended_parameters);
348 }
349
350 #[inline]
352 pub fn add_template_extended_parameter(
353 &mut self,
354 parent_fqcn: Word,
355 parameter_name: Word,
356 parameter_type: TUnion,
357 ) -> Option<TUnion> {
358 self.template_extended_parameters.entry(parent_fqcn).or_default().insert(parameter_name, parameter_type)
359 }
360
361 #[inline]
364 pub fn record_template_extended_path(&mut self, ancestor: Word, parameters: IndexMap<Word, TUnion, RandomState>) {
365 if parameters.is_empty() {
366 return;
367 }
368
369 let paths = self.template_extended_parameter_paths.entry(ancestor).or_default();
370 if !paths.contains(¶meters) {
371 paths.push(parameters);
372 }
373 }
374
375 #[inline]
377 pub fn add_declaring_method_id(
378 &mut self,
379 method: Word,
380 declaring_method_id: MethodIdentifier,
381 ) -> Option<MethodIdentifier> {
382 self.add_appearing_method_id(method, declaring_method_id);
383 self.declaring_method_ids.insert(method, declaring_method_id)
384 }
385
386 #[inline]
388 pub fn add_appearing_method_id(
389 &mut self,
390 method: Word,
391 appearing_method_id: MethodIdentifier,
392 ) -> Option<MethodIdentifier> {
393 self.appearing_method_ids.insert(method, appearing_method_id)
394 }
395
396 #[inline]
398 pub fn add_overridden_method_parent(
399 &mut self,
400 method: Word,
401 parent_method_id: MethodIdentifier,
402 ) -> Option<MethodIdentifier> {
403 self.overridden_method_ids
404 .entry(method)
405 .or_default()
406 .insert(parent_method_id.get_class_name(), parent_method_id)
407 }
408
409 #[inline]
411 pub fn add_property(&mut self, name: Word, property_metadata: PropertyMetadata) -> Option<PropertyMetadata> {
412 let class_name = self.name;
413
414 self.add_declaring_property_id(name, class_name);
415 if property_metadata.flags.has_default() {
416 self.initialized_properties.insert(name);
417 }
418
419 if !property_metadata.is_final() {
420 self.inheritable_property_ids.insert(name, class_name);
421 }
422
423 self.properties.insert(name, property_metadata)
424 }
425
426 #[inline]
428 pub fn add_property_metadata(&mut self, property_metadata: PropertyMetadata) -> Option<PropertyMetadata> {
429 let name = property_metadata.get_name().0;
430
431 self.add_property(name, property_metadata)
432 }
433
434 #[inline]
436 pub fn add_declaring_property_id(&mut self, prop: Word, declaring_fqcn: Word) -> Option<Word> {
437 self.appearing_property_ids.insert(prop, declaring_fqcn);
438 self.declaring_property_ids.insert(prop, declaring_fqcn)
439 }
440
441 #[must_use]
442 pub fn get_missing_required_interface<'meta>(&self, other: &'meta ClassLikeMetadata) -> Option<&'meta Word> {
443 for required_interface in &other.require_implements {
444 if self.all_parent_interfaces.contains(required_interface) {
445 continue;
446 }
447
448 if (self.flags.is_abstract() || self.kind.is_trait())
449 && self.require_implements.contains(required_interface)
450 {
451 continue; }
453
454 return Some(required_interface);
455 }
456
457 None
458 }
459
460 #[must_use]
461 pub fn get_missing_required_extends<'meta>(&self, other: &'meta ClassLikeMetadata) -> Option<&'meta Word> {
462 for required_extend in &other.require_extends {
463 if self.all_parent_classes.contains(required_extend) {
464 continue;
465 }
466
467 if self.kind.is_interface() && self.all_parent_interfaces.contains(required_extend) {
468 continue;
469 }
470
471 if (self.flags.is_abstract() || self.kind.is_trait()) && self.require_extends.contains(required_extend) {
472 continue; }
474
475 return Some(required_extend);
476 }
477
478 None
479 }
480
481 #[must_use]
482 pub fn is_permitted_to_inherit(&self, other: &ClassLikeMetadata) -> bool {
483 if self.kind.is_trait() || self.flags.is_abstract() {
484 return true; }
486
487 let Some(permitted_inheritors) = &other.permitted_inheritors else {
488 return true; };
490
491 if permitted_inheritors.contains(&self.name) {
492 return true; }
494
495 self.all_parent_interfaces.iter().any(|parent_interface| permitted_inheritors.contains(parent_interface))
496 || self.all_parent_classes.iter().any(|parent_class| permitted_inheritors.contains(parent_class))
497 || self.used_traits.iter().any(|used_trait| permitted_inheritors.contains(used_trait))
498 }
499
500 #[inline]
501 pub fn mark_as_populated(&mut self) {
502 self.flags |= MetadataFlags::POPULATED;
503 self.shrink_to_fit();
504 }
505
506 #[inline]
507 pub fn shrink_to_fit(&mut self) {
508 self.properties.shrink_to_fit();
509 self.initialized_properties.shrink_to_fit();
510 self.appearing_property_ids.shrink_to_fit();
511 self.declaring_property_ids.shrink_to_fit();
512 self.inheritable_property_ids.shrink_to_fit();
513 self.overridden_property_ids.shrink_to_fit();
514 self.appearing_method_ids.shrink_to_fit();
515 self.declaring_method_ids.shrink_to_fit();
516 self.inheritable_method_ids.shrink_to_fit();
517 self.overridden_method_ids.shrink_to_fit();
518 self.attributes.shrink_to_fit();
519 self.constants.shrink_to_fit();
520 self.enum_cases.shrink_to_fit();
521 self.type_aliases.shrink_to_fit();
522 }
523}