mago_codex/
populator.rs

1use ahash::HashSet;
2use ahash::RandomState;
3use indexmap::IndexMap;
4
5use mago_atom::Atom;
6use mago_atom::AtomMap;
7use mago_atom::AtomSet;
8use mago_atom::atom;
9
10use crate::is_method_abstract;
11use crate::metadata::CodebaseMetadata;
12use crate::metadata::class_like::ClassLikeMetadata;
13use crate::metadata::flags::MetadataFlags;
14use crate::metadata::function_like::FunctionLikeMetadata;
15use crate::misc::GenericParent;
16use crate::reference::ReferenceSource;
17use crate::reference::SymbolReferences;
18use crate::symbol::SymbolIdentifier;
19use crate::symbol::Symbols;
20use crate::ttype::TType;
21use crate::ttype::atomic::TAtomic;
22use crate::ttype::atomic::generic::TGenericParameter;
23use crate::ttype::atomic::populate_atomic_type;
24use crate::ttype::union::TUnion;
25use crate::ttype::union::populate_union_type;
26
27/// Populates the codebase metadata, resolving types and inheritance.
28///
29/// This function processes class-likes, function-likes, and constants to:
30///
31/// - Resolve type signatures (populating TUnion and TAtomic types).
32/// - Calculate inheritance hierarchies (parent classes, interfaces, traits).
33/// - Determine method and property origins (declaring vs. appearing).
34/// - Build descendant maps for efficient lookup.
35///
36/// TODO(azjezz): This function is a performance bottleneck.
37pub fn populate_codebase(
38    codebase: &mut CodebaseMetadata,
39    symbol_references: &mut SymbolReferences,
40    safe_symbols: AtomSet,
41    safe_symbol_members: HashSet<SymbolIdentifier>,
42) {
43    let mut class_likes_to_repopulate = Vec::new();
44    for (name, metadata) in codebase.class_likes.iter() {
45        // Repopulate if not populated OR if user-defined and not marked safe.
46        if !metadata.flags.is_populated() || (metadata.flags.is_user_defined() && !safe_symbols.contains(name)) {
47            class_likes_to_repopulate.push(*name);
48        }
49    }
50
51    for class_like_name in &class_likes_to_repopulate {
52        if let Some(classlike_info) = codebase.class_likes.get_mut(class_like_name) {
53            classlike_info.flags &= !MetadataFlags::POPULATED;
54            classlike_info.declaring_property_ids.clear();
55            classlike_info.appearing_property_ids.clear();
56            classlike_info.declaring_method_ids.clear();
57            classlike_info.appearing_method_ids.clear();
58        }
59    }
60
61    for class_name in &class_likes_to_repopulate {
62        populate_class_like_metadata(class_name, codebase, symbol_references, &safe_symbols);
63    }
64
65    for (name, function_like_metadata) in codebase.function_likes.iter_mut() {
66        let force_repopulation = function_like_metadata.flags.is_user_defined() && !safe_symbols.contains(&name.0);
67
68        let reference_source = if name.1.is_empty() || function_like_metadata.get_kind().is_closure() {
69            // Top-level function or closure
70            ReferenceSource::Symbol(true, name.0)
71        } else {
72            // Class method
73            ReferenceSource::ClassLikeMember(true, name.0, name.1)
74        };
75
76        populate_function_like_metadata(
77            function_like_metadata,
78            &codebase.symbols,
79            &reference_source,
80            symbol_references,
81            force_repopulation,
82        );
83    }
84
85    for (name, metadata) in codebase.class_likes.iter_mut() {
86        let userland_force_repopulation = metadata.flags.is_user_defined() && !safe_symbols.contains(name);
87        let class_like_reference_source = ReferenceSource::Symbol(true, *name);
88
89        for (property_name, property_metadata) in &mut metadata.properties {
90            let property_reference_source = ReferenceSource::ClassLikeMember(true, *name, *property_name);
91
92            if let Some(signature) = property_metadata.type_declaration_metadata.as_mut() {
93                populate_union_type(
94                    &mut signature.type_union,
95                    &codebase.symbols,
96                    Some(&property_reference_source),
97                    symbol_references,
98                    userland_force_repopulation,
99                );
100            }
101
102            if let Some(signature) = property_metadata.type_metadata.as_mut() {
103                populate_union_type(
104                    &mut signature.type_union,
105                    &codebase.symbols,
106                    Some(&property_reference_source),
107                    symbol_references,
108                    userland_force_repopulation,
109                );
110            }
111
112            if let Some(default) = property_metadata.default_type_metadata.as_mut() {
113                populate_union_type(
114                    &mut default.type_union,
115                    &codebase.symbols,
116                    Some(&property_reference_source),
117                    symbol_references,
118                    userland_force_repopulation,
119                );
120            }
121        }
122
123        for map in metadata.template_extended_parameters.values_mut() {
124            for (_, v) in map {
125                if v.needs_population() || userland_force_repopulation {
126                    populate_union_type(
127                        v,
128                        &codebase.symbols,
129                        Some(&class_like_reference_source),
130                        symbol_references,
131                        userland_force_repopulation,
132                    );
133                }
134            }
135        }
136
137        for (_, map) in &mut metadata.template_types {
138            for (_, v) in map {
139                if v.needs_population() || userland_force_repopulation {
140                    populate_union_type(
141                        v,
142                        &codebase.symbols,
143                        Some(&class_like_reference_source),
144                        symbol_references,
145                        userland_force_repopulation,
146                    );
147                }
148            }
149        }
150
151        for (constant_name, constant) in &mut metadata.constants {
152            let constant_reference_source = ReferenceSource::ClassLikeMember(true, *name, *constant_name);
153
154            for attribute_metadata in &constant.attributes {
155                symbol_references.add_class_member_reference_to_symbol(
156                    (*name, *constant_name),
157                    attribute_metadata.name,
158                    true,
159                );
160            }
161
162            if let Some(signature) = &mut constant.type_metadata {
163                populate_union_type(
164                    &mut signature.type_union,
165                    &codebase.symbols,
166                    Some(&constant_reference_source),
167                    symbol_references,
168                    userland_force_repopulation,
169                );
170            }
171
172            if let Some(inferred) = &mut constant.inferred_type {
173                populate_atomic_type(
174                    inferred,
175                    &codebase.symbols,
176                    Some(&constant_reference_source),
177                    symbol_references,
178                    userland_force_repopulation,
179                );
180            }
181        }
182
183        for (enum_case_name, enum_case) in &mut metadata.enum_cases {
184            let enum_case_reference_source = ReferenceSource::ClassLikeMember(true, *name, *enum_case_name);
185
186            for attribute_metadata in &enum_case.attributes {
187                symbol_references.add_class_member_reference_to_symbol(
188                    (*name, *enum_case_name),
189                    attribute_metadata.name,
190                    true,
191                );
192            }
193
194            if let Some(value_type) = &mut enum_case.value_type {
195                populate_atomic_type(
196                    value_type,
197                    &codebase.symbols,
198                    Some(&enum_case_reference_source),
199                    symbol_references,
200                    userland_force_repopulation,
201                );
202            }
203        }
204
205        if let Some(enum_type) = &mut metadata.enum_type {
206            populate_atomic_type(
207                enum_type,
208                &codebase.symbols,
209                Some(&ReferenceSource::Symbol(true, *name)),
210                symbol_references,
211                userland_force_repopulation,
212            );
213        }
214    }
215
216    for (name, constant) in &mut codebase.constants {
217        for attribute_metadata in &constant.attributes {
218            symbol_references.add_symbol_reference_to_symbol(*name, attribute_metadata.name, true);
219        }
220
221        if let Some(inferred_type) = &mut constant.inferred_type {
222            populate_union_type(
223                inferred_type,
224                &codebase.symbols,
225                Some(&ReferenceSource::Symbol(true, *name)),
226                symbol_references,
227                !safe_symbols.contains(name),
228            );
229        }
230    }
231
232    let mut direct_classlike_descendants = AtomMap::default();
233    let mut all_classlike_descendants = AtomMap::default();
234
235    for (class_like_name, class_like_metadata) in &codebase.class_likes {
236        for parent_interface in &class_like_metadata.all_parent_interfaces {
237            all_classlike_descendants
238                .entry(*parent_interface)
239                .or_insert_with(AtomSet::default)
240                .insert(*class_like_name);
241        }
242
243        for parent_interface in &class_like_metadata.direct_parent_interfaces {
244            direct_classlike_descendants
245                .entry(*parent_interface)
246                .or_insert_with(AtomSet::default)
247                .insert(*class_like_name);
248        }
249
250        for parent_class in &class_like_metadata.all_parent_classes {
251            all_classlike_descendants.entry(*parent_class).or_insert_with(AtomSet::default).insert(*class_like_name);
252        }
253
254        for used_trait in &class_like_metadata.used_traits {
255            all_classlike_descendants.entry(*used_trait).or_default().insert(*class_like_name);
256        }
257
258        if let Some(parent_class) = &class_like_metadata.direct_parent_class {
259            direct_classlike_descendants.entry(*parent_class).or_insert_with(AtomSet::default).insert(*class_like_name);
260        }
261    }
262
263    codebase.all_class_like_descendants = all_classlike_descendants;
264    codebase.direct_classlike_descendants = direct_classlike_descendants;
265    codebase.safe_symbols = safe_symbols;
266    codebase.safe_symbol_members = safe_symbol_members;
267}
268
269/// Populates metadata for a single function or method.
270///
271/// Resolves types for return types, parameters, template parameters, etc.
272/// Adds symbol references based on attributes and types used.
273fn populate_function_like_metadata(
274    metadata: &mut FunctionLikeMetadata,
275    codebase_symbols: &Symbols,
276    reference_source: &ReferenceSource,
277    symbol_references: &mut SymbolReferences,
278    force_type_population: bool,
279) {
280    // Early exit if already populated and not forced
281    if metadata.flags.is_populated() && !force_type_population {
282        return;
283    }
284
285    for attribute_metadata in metadata.get_attributes() {
286        match reference_source {
287            ReferenceSource::Symbol(_, a) => {
288                symbol_references.add_symbol_reference_to_symbol(*a, attribute_metadata.name, true)
289            }
290            ReferenceSource::ClassLikeMember(_, a, b) => {
291                symbol_references.add_class_member_reference_to_symbol((*a, *b), attribute_metadata.name, true)
292            }
293        }
294    }
295
296    if let Some(return_type) = metadata.return_type_declaration_metadata.as_mut() {
297        populate_union_type(
298            &mut return_type.type_union,
299            codebase_symbols,
300            Some(reference_source),
301            symbol_references,
302            force_type_population,
303        );
304    }
305
306    if let Some(return_type) = metadata.return_type_metadata.as_mut() {
307        populate_union_type(
308            &mut return_type.type_union,
309            codebase_symbols,
310            Some(reference_source),
311            symbol_references,
312            force_type_population,
313        );
314    }
315
316    for parameter_metadata in metadata.get_parameters_mut() {
317        if let Some(type_metadata) = parameter_metadata.type_metadata.as_mut() {
318            populate_union_type(
319                &mut type_metadata.type_union,
320                codebase_symbols,
321                Some(reference_source),
322                symbol_references,
323                force_type_population,
324            );
325        }
326
327        if let Some(type_metadata) = parameter_metadata.out_type.as_mut() {
328            populate_union_type(
329                &mut type_metadata.type_union,
330                codebase_symbols,
331                Some(reference_source),
332                symbol_references,
333                force_type_population,
334            );
335        }
336
337        if let Some(type_metadata) = parameter_metadata.default_type.as_mut() {
338            populate_union_type(
339                &mut type_metadata.type_union,
340                codebase_symbols,
341                Some(reference_source),
342                symbol_references,
343                force_type_population,
344            );
345        }
346
347        for attribute_metadata in &parameter_metadata.attributes {
348            match reference_source {
349                ReferenceSource::Symbol(in_signature, a) => {
350                    symbol_references.add_symbol_reference_to_symbol(*a, attribute_metadata.name, *in_signature)
351                }
352                ReferenceSource::ClassLikeMember(in_signature, a, b) => symbol_references
353                    .add_class_member_reference_to_symbol((*a, *b), attribute_metadata.name, *in_signature),
354            }
355        }
356    }
357
358    for (_, type_parameter_map) in &mut metadata.template_types {
359        for (_, type_parameter) in type_parameter_map {
360            if force_type_population || type_parameter.needs_population() {
361                populate_union_type(
362                    type_parameter,
363                    codebase_symbols,
364                    Some(reference_source),
365                    symbol_references,
366                    force_type_population,
367                );
368            }
369        }
370    }
371
372    if let Some(type_resolution_context) = metadata.type_resolution_context.as_mut() {
373        for (_, type_parameter_map) in type_resolution_context.get_template_definitions_mut() {
374            for (_, type_parameter) in type_parameter_map {
375                if force_type_population || type_parameter.needs_population() {
376                    populate_union_type(
377                        type_parameter,
378                        codebase_symbols,
379                        Some(reference_source),
380                        symbol_references,
381                        force_type_population,
382                    );
383                }
384            }
385        }
386    }
387
388    if let Some(method_metadata) = metadata.method_metadata.as_mut() {
389        for where_constraint in method_metadata.where_constraints.values_mut() {
390            populate_union_type(
391                &mut where_constraint.type_union,
392                codebase_symbols,
393                Some(reference_source),
394                symbol_references,
395                force_type_population,
396            );
397        }
398    }
399
400    for thrown_type in &mut metadata.thrown_types {
401        populate_union_type(
402            &mut thrown_type.type_union,
403            codebase_symbols,
404            Some(reference_source),
405            symbol_references,
406            force_type_population,
407        );
408    }
409
410    for assertions in metadata.assertions.values_mut() {
411        for assertion in assertions {
412            if let Some(assertion_type) = assertion.get_type_mut() {
413                populate_atomic_type(
414                    assertion_type,
415                    codebase_symbols,
416                    Some(reference_source),
417                    symbol_references,
418                    force_type_population,
419                );
420            }
421        }
422    }
423
424    for assertions in metadata.if_true_assertions.values_mut() {
425        for assertion in assertions {
426            if let Some(assertion_type) = assertion.get_type_mut() {
427                populate_atomic_type(
428                    assertion_type,
429                    codebase_symbols,
430                    Some(reference_source),
431                    symbol_references,
432                    force_type_population,
433                );
434            }
435        }
436    }
437
438    for assertions in metadata.if_false_assertions.values_mut() {
439        for assertion in assertions {
440            if let Some(assertion_type) = assertion.get_type_mut() {
441                populate_atomic_type(
442                    assertion_type,
443                    codebase_symbols,
444                    Some(reference_source),
445                    symbol_references,
446                    force_type_population,
447                );
448            }
449        }
450    }
451
452    metadata.flags |= MetadataFlags::POPULATED;
453}
454
455/// Populates the metadata for a single class-like (class, interface, trait).
456///
457/// This function is potentially recursive, as it populates parent classes,
458/// interfaces, and used traits before processing the current class-like.
459/// It uses a remove/insert pattern to handle mutable borrowing across recursive calls.
460fn populate_class_like_metadata(
461    classlike_name: &Atom,
462    codebase: &mut CodebaseMetadata,
463    symbol_references: &mut SymbolReferences,
464    safe_symbols: &AtomSet,
465) {
466    if let Some(metadata) = codebase.class_likes.get(classlike_name)
467        && metadata.flags.is_populated()
468    {
469        return; // Already done, exit early
470    }
471
472    let mut metadata = if let Some(metadata) = codebase.class_likes.remove(classlike_name) {
473        metadata
474    } else {
475        return;
476    };
477
478    for attribute_metadata in &metadata.attributes {
479        symbol_references.add_symbol_reference_to_symbol(metadata.name, attribute_metadata.name, true);
480    }
481
482    for property_name in metadata.get_property_names() {
483        metadata.add_declaring_property_id(property_name, *classlike_name);
484    }
485
486    for method_name in &metadata.methods {
487        metadata.appearing_method_ids.insert(*method_name, *classlike_name);
488        metadata.declaring_method_ids.insert(*method_name, *classlike_name);
489    }
490
491    let force_repopulation = !safe_symbols.contains(classlike_name);
492    for parameter_types in metadata.template_extended_offsets.values_mut() {
493        for parameter_type in parameter_types {
494            populate_union_type(
495                parameter_type,
496                &codebase.symbols,
497                Some(&ReferenceSource::Symbol(true, *classlike_name)),
498                symbol_references,
499                force_repopulation,
500            );
501        }
502    }
503
504    for trait_name in metadata.used_traits.iter().copied().collect::<Vec<_>>() {
505        populate_metadata_from_trait(&mut metadata, codebase, trait_name, symbol_references, safe_symbols);
506    }
507
508    if let Some(parent_classname) = metadata.direct_parent_class {
509        populate_metadata_from_parent_class_like(
510            &mut metadata,
511            codebase,
512            parent_classname,
513            symbol_references,
514            safe_symbols,
515        );
516    }
517
518    let direct_parent_interfaces = metadata.direct_parent_interfaces.clone();
519    for direct_parent_interface in direct_parent_interfaces {
520        populate_interface_metadata_from_parent_interface(
521            &mut metadata,
522            codebase,
523            direct_parent_interface,
524            symbol_references,
525            safe_symbols,
526        );
527    }
528
529    for required_class in metadata.require_extends.iter().copied().collect::<Vec<_>>() {
530        populate_metadata_from_required_class_like(
531            &mut metadata,
532            codebase,
533            required_class,
534            symbol_references,
535            safe_symbols,
536        );
537    }
538
539    for required_interface in metadata.require_implements.iter().copied().collect::<Vec<_>>() {
540        populate_interface_metadata_from_parent_interface(
541            &mut metadata,
542            codebase,
543            required_interface,
544            symbol_references,
545            safe_symbols,
546        );
547    }
548
549    // Apply readonly to properties if the class is readonly
550    if metadata.flags.is_readonly() {
551        for property_metadata in metadata.properties.values_mut() {
552            if !property_metadata.flags.is_static() {
553                property_metadata.flags |= MetadataFlags::READONLY;
554            }
555        }
556    }
557
558    metadata.mark_as_populated();
559    codebase.class_likes.insert(*classlike_name, metadata);
560}
561
562/// Populates interface data inherited from a parent interface.
563fn populate_interface_metadata_from_parent_interface(
564    metadata: &mut ClassLikeMetadata,
565    codebase: &mut CodebaseMetadata,
566    parent_interface: Atom,
567    symbol_references: &mut SymbolReferences,
568    safe_symbols: &AtomSet,
569) {
570    populate_class_like_metadata(&parent_interface, codebase, symbol_references, safe_symbols);
571
572    symbol_references.add_symbol_reference_to_symbol(metadata.name, parent_interface, true);
573
574    let parent_interface_metadata = if let Some(parent_meta) = codebase.class_likes.get(&parent_interface) {
575        parent_meta
576    } else {
577        metadata.invalid_dependencies.insert(parent_interface);
578        return;
579    };
580
581    for (interface_constant_name, interface_constant_metadata) in &parent_interface_metadata.constants {
582        if !metadata.constants.contains_key(interface_constant_name) {
583            metadata.constants.insert(*interface_constant_name, interface_constant_metadata.clone());
584        }
585    }
586
587    metadata.all_parent_interfaces.extend(parent_interface_metadata.all_parent_interfaces.iter().copied());
588    metadata.invalid_dependencies.extend(parent_interface_metadata.invalid_dependencies.iter().copied());
589
590    if let Some(inheritors) = &parent_interface_metadata.permitted_inheritors {
591        metadata.permitted_inheritors.get_or_insert_default().extend(inheritors.iter().copied());
592    }
593
594    // Extend template parameters based on the parent interface's templates
595    extend_template_parameters(metadata, parent_interface_metadata);
596    // Inherit methods (appearing/declaring ids) from the parent interface
597    // Pass codebase immutably if possible, or mutably if method inheritance logic needs it
598    inherit_methods_from_parent(metadata, parent_interface_metadata, codebase);
599    inherit_properties_from_parent(metadata, parent_interface_metadata);
600}
601
602/// Populates class-like data inherited from a parent class or trait.
603fn populate_metadata_from_parent_class_like(
604    metadata: &mut ClassLikeMetadata,
605    codebase: &mut CodebaseMetadata,
606    parent_class: Atom,
607    symbol_references: &mut SymbolReferences,
608    safe_symbols: &AtomSet,
609) {
610    populate_class_like_metadata(&parent_class, codebase, symbol_references, safe_symbols);
611
612    symbol_references.add_symbol_reference_to_symbol(metadata.name, parent_class, true);
613
614    let parent_metadata = if let Some(parent_meta) = codebase.class_likes.get(&parent_class) {
615        parent_meta
616    } else {
617        metadata.invalid_dependencies.insert(parent_class);
618        return;
619    };
620
621    metadata.all_parent_classes.extend(parent_metadata.all_parent_classes.iter().copied());
622    metadata.all_parent_interfaces.extend(parent_metadata.all_parent_interfaces.iter().copied());
623    metadata.used_traits.extend(parent_metadata.used_traits.iter().copied());
624    metadata.invalid_dependencies.extend(parent_metadata.invalid_dependencies.iter().copied());
625
626    if let Some(inheritors) = &parent_metadata.permitted_inheritors {
627        metadata.permitted_inheritors.get_or_insert_default().extend(inheritors.iter().copied());
628    }
629
630    extend_template_parameters(metadata, parent_metadata);
631
632    inherit_methods_from_parent(metadata, parent_metadata, codebase);
633    inherit_properties_from_parent(metadata, parent_metadata);
634
635    for (parent_constant_name, parent_constant_metadata) in &parent_metadata.constants {
636        if !metadata.constants.contains_key(parent_constant_name) {
637            metadata.constants.insert(*parent_constant_name, parent_constant_metadata.clone());
638        }
639    }
640
641    if parent_metadata.flags.has_consistent_templates() {
642        metadata.flags |= MetadataFlags::CONSISTENT_TEMPLATES;
643    }
644}
645
646/// Populates class-like data inherited from a parent class or trait.
647fn populate_metadata_from_required_class_like(
648    metadata: &mut ClassLikeMetadata,
649    codebase: &mut CodebaseMetadata,
650    parent_class: Atom,
651    symbol_references: &mut SymbolReferences,
652    safe_symbols: &AtomSet,
653) {
654    populate_class_like_metadata(&parent_class, codebase, symbol_references, safe_symbols);
655
656    symbol_references.add_symbol_reference_to_symbol(metadata.name, parent_class, true);
657
658    let parent_metadata = if let Some(parent_meta) = codebase.class_likes.get(&parent_class) {
659        parent_meta
660    } else {
661        metadata.invalid_dependencies.insert(parent_class);
662        return;
663    };
664
665    metadata.require_extends.extend(parent_metadata.all_parent_classes.iter().copied());
666    metadata.require_implements.extend(parent_metadata.all_parent_interfaces.iter().copied());
667}
668
669/// Populates class-like data inherited from a used trait.
670fn populate_metadata_from_trait(
671    metadata: &mut ClassLikeMetadata,
672    codebase: &mut CodebaseMetadata,
673    trait_name: Atom,
674    symbol_references: &mut SymbolReferences,
675    safe_symbols: &AtomSet,
676) {
677    populate_class_like_metadata(&trait_name, codebase, symbol_references, safe_symbols);
678
679    symbol_references.add_symbol_reference_to_symbol(metadata.name, trait_name, true);
680
681    let Some(trait_metadata) = codebase.class_likes.get(&trait_name) else {
682        metadata.invalid_dependencies.insert(trait_name);
683        return;
684    };
685
686    // Inherit constants (if not already defined)
687    for (trait_constant_name, trait_constant_metadata) in &trait_metadata.constants {
688        if !metadata.constants.contains_key(trait_constant_name) {
689            metadata.constants.insert(*trait_constant_name, trait_constant_metadata.clone());
690        }
691    }
692
693    // Inherit the trait's parent interfaces (direct parents of the trait become parents of the user)
694    metadata.all_parent_interfaces.extend(trait_metadata.direct_parent_interfaces.iter().copied());
695
696    // Also inherit invalid dependencies from the trait
697    metadata.invalid_dependencies.extend(trait_metadata.invalid_dependencies.iter().copied());
698
699    // Extend template parameters based on the trait's templates
700    extend_template_parameters(metadata, trait_metadata);
701
702    // Inherit methods and properties from the trait
703    inherit_methods_from_parent(metadata, trait_metadata, codebase);
704    inherit_properties_from_parent(metadata, trait_metadata);
705}
706
707/// Inherits method declarations and appearances from a parent class-like.
708/// Updates declaring_method_ids, appearing_method_ids, etc.
709fn inherit_methods_from_parent(
710    metadata: &mut ClassLikeMetadata,
711    parent_metadata: &ClassLikeMetadata,
712    codebase: &CodebaseMetadata,
713) {
714    let class_like_name = metadata.name;
715    let is_trait = metadata.kind.is_trait();
716
717    for (method_name, appearing_class_like) in &parent_metadata.appearing_method_ids {
718        if metadata.has_appearing_method(method_name) {
719            continue;
720        }
721
722        metadata
723            .appearing_method_ids
724            .insert(*method_name, if is_trait { class_like_name } else { *appearing_class_like });
725
726        if codebase.function_likes.contains_key(&(class_like_name, *method_name)) {
727            metadata.potential_declaring_method_ids.insert(*method_name, AtomSet::from_iter([class_like_name]));
728        } else {
729            if let Some(parent_potential_method_ids) = parent_metadata.get_potential_declaring_method_id(method_name) {
730                metadata.potential_declaring_method_ids.insert(*method_name, parent_potential_method_ids.clone());
731            }
732
733            metadata.add_potential_declaring_method(*method_name, class_like_name);
734            metadata.add_potential_declaring_method(*method_name, parent_metadata.name);
735        }
736    }
737
738    for (method_name, declaring_class) in &parent_metadata.inheritable_method_ids {
739        if !method_name.eq(&atom("__construct")) || parent_metadata.flags.has_consistent_constructor() {
740            if !parent_metadata.kind.is_trait() || is_method_abstract(codebase, declaring_class, method_name) {
741                metadata.add_overridden_method_parent(*method_name, *declaring_class);
742            }
743
744            if let Some(map) = metadata.overridden_method_ids.get_mut(method_name)
745                && let Some(overridden_method_ids) = parent_metadata.overridden_method_ids.get(method_name)
746            {
747                map.extend(overridden_method_ids.iter().copied());
748            }
749        }
750
751        let mut aliased_method_names = vec![*method_name];
752        if parent_metadata.kind.is_trait() {
753            aliased_method_names
754                .extend(metadata.get_trait_alias_map().iter().filter(|(_, v)| *v == method_name).map(|(k, _)| *k));
755        }
756
757        for aliased_method_name in aliased_method_names {
758            let implementing_method_id = metadata.declaring_method_ids.get(&aliased_method_name);
759            if let Some(implementing_method_id) = implementing_method_id
760                && !is_method_abstract(codebase, implementing_method_id, &aliased_method_name)
761            {
762                continue;
763            }
764
765            metadata.declaring_method_ids.insert(aliased_method_name, *declaring_class);
766            metadata.inheritable_method_ids.insert(aliased_method_name, *declaring_class);
767        }
768    }
769}
770
771/// Inherits property declarations and appearances from a parent class-like.
772/// Updates declaring_property_ids, appearing_property_ids, etc.
773fn inherit_properties_from_parent(metadata: &mut ClassLikeMetadata, parent_metadata: &ClassLikeMetadata) {
774    let classlike_name = metadata.name;
775    let is_trait = metadata.kind.is_trait();
776    let parent_is_trait = parent_metadata.kind.is_trait();
777
778    for (property_name, appearing_classlike) in &parent_metadata.appearing_property_ids {
779        if metadata.has_appearing_property(property_name) {
780            continue;
781        }
782
783        if !parent_is_trait
784            && let Some(parent_property_metadata) = parent_metadata.properties.get(property_name)
785            && parent_property_metadata.is_final()
786        {
787            continue;
788        }
789
790        metadata
791            .appearing_property_ids
792            .insert(*property_name, if is_trait { classlike_name } else { *appearing_classlike });
793    }
794
795    for (property_name, declaring_classlike) in &parent_metadata.declaring_property_ids {
796        if metadata.declaring_property_ids.contains_key(property_name) {
797            continue;
798        }
799
800        if !parent_is_trait
801            && let Some(parent_property_metadata) = parent_metadata.properties.get(property_name)
802            && parent_property_metadata.is_final()
803        {
804            continue;
805        }
806
807        metadata.declaring_property_ids.insert(*property_name, *declaring_classlike);
808    }
809
810    for (property_name, inheritable_classlike) in &parent_metadata.inheritable_property_ids {
811        let mut is_overridable = true;
812        if !parent_is_trait {
813            if let Some(parent_property_metadata) = parent_metadata.properties.get(property_name)
814                && parent_property_metadata.is_final()
815            {
816                is_overridable = false;
817            }
818
819            if is_overridable {
820                metadata.overridden_property_ids.entry(*property_name).or_default().insert(*inheritable_classlike);
821            }
822        }
823
824        if is_overridable {
825            metadata.inheritable_property_ids.insert(*property_name, *inheritable_classlike);
826        }
827    }
828}
829
830/// Extends the template parameter map of `metadata` based on `parent_metadata`.
831/// Handles resolving template types inherited from parents/traits.
832fn extend_template_parameters(metadata: &mut ClassLikeMetadata, parent_metadata: &ClassLikeMetadata) {
833    let parent_name = parent_metadata.name;
834
835    if !parent_metadata.template_types.is_empty() {
836        metadata.template_extended_parameters.entry(parent_name).or_default();
837
838        if let Some(parent_offsets) = metadata.template_extended_offsets.get(&parent_name).cloned() {
839            let parent_template_type_names = parent_metadata.get_template_type_names();
840
841            for (i, extended_type_arc) in parent_offsets.into_iter().enumerate() {
842                if let Some(mapped_name) = parent_template_type_names.get(i).copied() {
843                    metadata.add_template_extended_parameter(parent_name, mapped_name, extended_type_arc);
844                }
845            }
846
847            let current_child_extended_params = metadata.template_extended_parameters.clone();
848            for (grandparent_fqcn, type_map) in &parent_metadata.template_extended_parameters {
849                for (template_name, type_to_resolve_arc) in type_map {
850                    let resolved_type = extend_type(type_to_resolve_arc, &current_child_extended_params);
851
852                    metadata.add_template_extended_parameter(*grandparent_fqcn, *template_name, resolved_type);
853                }
854            }
855        } else {
856            for (parameter_name, parameter_type_map) in &parent_metadata.template_types {
857                for (_, parameter_type) in parameter_type_map {
858                    metadata.add_template_extended_parameter(parent_name, *parameter_name, parameter_type.clone());
859                }
860            }
861
862            metadata.extend_template_extended_parameters(parent_metadata.template_extended_parameters.clone());
863        }
864    } else {
865        // Inherit the parent's extended parameters map directly.
866        metadata.extend_template_extended_parameters(parent_metadata.template_extended_parameters.clone());
867    }
868}
869
870/// Resolves a TUnion that might contain generic parameters, using the provided
871/// extended parameter map.
872///
873/// Example: If `extended_type` is `T` (generic param) and `template_extended_parameters`
874/// maps `T` defined on `ParentClass` to `string`, this returns a `TUnion` containing `string`.
875fn extend_type(
876    extended_type: &TUnion,
877    template_extended_parameters: &AtomMap<IndexMap<Atom, TUnion, RandomState>>,
878) -> TUnion {
879    if !extended_type.has_template() {
880        return extended_type.clone();
881    }
882
883    let mut extended_types = Vec::new();
884
885    let mut worklist = extended_type.types.clone().into_owned();
886    while let Some(atomic_type) = worklist.pop() {
887        if let TAtomic::GenericParameter(TGenericParameter {
888            parameter_name,
889            defining_entity: GenericParent::ClassLike(defining_entity),
890            ..
891        }) = &atomic_type
892            && let Some(extended_parameters) = template_extended_parameters.get(defining_entity)
893            && let Some(referenced_type) = extended_parameters.get(parameter_name)
894        {
895            extended_types.extend(referenced_type.types.clone().into_owned());
896            continue;
897        }
898
899        extended_types.push(atomic_type);
900    }
901
902    TUnion::from_vec(extended_types)
903}