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(type_metadata) = &mut constant.type_metadata {
222            populate_union_type(
223                &mut type_metadata.type_union,
224                &codebase.symbols,
225                Some(&ReferenceSource::Symbol(true, *name)),
226                symbol_references,
227                !safe_symbols.contains(name),
228            );
229        }
230
231        if let Some(inferred_type) = &mut constant.inferred_type {
232            populate_union_type(
233                inferred_type,
234                &codebase.symbols,
235                Some(&ReferenceSource::Symbol(true, *name)),
236                symbol_references,
237                !safe_symbols.contains(name),
238            );
239        }
240    }
241
242    let mut direct_classlike_descendants = AtomMap::default();
243    let mut all_classlike_descendants = AtomMap::default();
244
245    for (class_like_name, class_like_metadata) in &codebase.class_likes {
246        for parent_interface in &class_like_metadata.all_parent_interfaces {
247            all_classlike_descendants
248                .entry(*parent_interface)
249                .or_insert_with(AtomSet::default)
250                .insert(*class_like_name);
251        }
252
253        for parent_interface in &class_like_metadata.direct_parent_interfaces {
254            direct_classlike_descendants
255                .entry(*parent_interface)
256                .or_insert_with(AtomSet::default)
257                .insert(*class_like_name);
258        }
259
260        for parent_class in &class_like_metadata.all_parent_classes {
261            all_classlike_descendants.entry(*parent_class).or_insert_with(AtomSet::default).insert(*class_like_name);
262        }
263
264        for used_trait in &class_like_metadata.used_traits {
265            all_classlike_descendants.entry(*used_trait).or_default().insert(*class_like_name);
266        }
267
268        if let Some(parent_class) = &class_like_metadata.direct_parent_class {
269            direct_classlike_descendants.entry(*parent_class).or_insert_with(AtomSet::default).insert(*class_like_name);
270        }
271    }
272
273    codebase.all_class_like_descendants = all_classlike_descendants;
274    codebase.direct_classlike_descendants = direct_classlike_descendants;
275    codebase.safe_symbols = safe_symbols;
276    codebase.safe_symbol_members = safe_symbol_members;
277}
278
279/// Populates metadata for a single function or method.
280///
281/// Resolves types for return types, parameters, template parameters, etc.
282/// Adds symbol references based on attributes and types used.
283fn populate_function_like_metadata(
284    metadata: &mut FunctionLikeMetadata,
285    codebase_symbols: &Symbols,
286    reference_source: &ReferenceSource,
287    symbol_references: &mut SymbolReferences,
288    force_type_population: bool,
289) {
290    // Early exit if already populated and not forced
291    if metadata.flags.is_populated() && !force_type_population {
292        return;
293    }
294
295    for attribute_metadata in metadata.get_attributes() {
296        match reference_source {
297            ReferenceSource::Symbol(_, a) => {
298                symbol_references.add_symbol_reference_to_symbol(*a, attribute_metadata.name, true)
299            }
300            ReferenceSource::ClassLikeMember(_, a, b) => {
301                symbol_references.add_class_member_reference_to_symbol((*a, *b), attribute_metadata.name, true)
302            }
303        }
304    }
305
306    if let Some(return_type) = metadata.return_type_declaration_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    if let Some(return_type) = metadata.return_type_metadata.as_mut() {
317        populate_union_type(
318            &mut return_type.type_union,
319            codebase_symbols,
320            Some(reference_source),
321            symbol_references,
322            force_type_population,
323        );
324    }
325
326    for parameter_metadata in metadata.get_parameters_mut() {
327        if let Some(type_metadata) = parameter_metadata.type_metadata.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.out_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        if let Some(type_metadata) = parameter_metadata.default_type.as_mut() {
348            populate_union_type(
349                &mut type_metadata.type_union,
350                codebase_symbols,
351                Some(reference_source),
352                symbol_references,
353                force_type_population,
354            );
355        }
356
357        for attribute_metadata in &parameter_metadata.attributes {
358            match reference_source {
359                ReferenceSource::Symbol(in_signature, a) => {
360                    symbol_references.add_symbol_reference_to_symbol(*a, attribute_metadata.name, *in_signature)
361                }
362                ReferenceSource::ClassLikeMember(in_signature, a, b) => symbol_references
363                    .add_class_member_reference_to_symbol((*a, *b), attribute_metadata.name, *in_signature),
364            }
365        }
366    }
367
368    for (_, type_parameter_map) in &mut metadata.template_types {
369        for (_, type_parameter) in type_parameter_map {
370            if force_type_population || type_parameter.needs_population() {
371                populate_union_type(
372                    type_parameter,
373                    codebase_symbols,
374                    Some(reference_source),
375                    symbol_references,
376                    force_type_population,
377                );
378            }
379        }
380    }
381
382    if let Some(type_resolution_context) = metadata.type_resolution_context.as_mut() {
383        for (_, type_parameter_map) in type_resolution_context.get_template_definitions_mut() {
384            for (_, type_parameter) in type_parameter_map {
385                if force_type_population || type_parameter.needs_population() {
386                    populate_union_type(
387                        type_parameter,
388                        codebase_symbols,
389                        Some(reference_source),
390                        symbol_references,
391                        force_type_population,
392                    );
393                }
394            }
395        }
396    }
397
398    if let Some(method_metadata) = metadata.method_metadata.as_mut() {
399        for where_constraint in method_metadata.where_constraints.values_mut() {
400            populate_union_type(
401                &mut where_constraint.type_union,
402                codebase_symbols,
403                Some(reference_source),
404                symbol_references,
405                force_type_population,
406            );
407        }
408    }
409
410    for thrown_type in &mut metadata.thrown_types {
411        populate_union_type(
412            &mut thrown_type.type_union,
413            codebase_symbols,
414            Some(reference_source),
415            symbol_references,
416            force_type_population,
417        );
418    }
419
420    for assertions in metadata.assertions.values_mut() {
421        for assertion in assertions {
422            if let Some(assertion_type) = assertion.get_type_mut() {
423                populate_atomic_type(
424                    assertion_type,
425                    codebase_symbols,
426                    Some(reference_source),
427                    symbol_references,
428                    force_type_population,
429                );
430            }
431        }
432    }
433
434    for assertions in metadata.if_true_assertions.values_mut() {
435        for assertion in assertions {
436            if let Some(assertion_type) = assertion.get_type_mut() {
437                populate_atomic_type(
438                    assertion_type,
439                    codebase_symbols,
440                    Some(reference_source),
441                    symbol_references,
442                    force_type_population,
443                );
444            }
445        }
446    }
447
448    for assertions in metadata.if_false_assertions.values_mut() {
449        for assertion in assertions {
450            if let Some(assertion_type) = assertion.get_type_mut() {
451                populate_atomic_type(
452                    assertion_type,
453                    codebase_symbols,
454                    Some(reference_source),
455                    symbol_references,
456                    force_type_population,
457                );
458            }
459        }
460    }
461
462    metadata.flags |= MetadataFlags::POPULATED;
463}
464
465/// Populates the metadata for a single class-like (class, interface, trait).
466///
467/// This function is potentially recursive, as it populates parent classes,
468/// interfaces, and used traits before processing the current class-like.
469/// It uses a remove/insert pattern to handle mutable borrowing across recursive calls.
470fn populate_class_like_metadata(
471    classlike_name: &Atom,
472    codebase: &mut CodebaseMetadata,
473    symbol_references: &mut SymbolReferences,
474    safe_symbols: &AtomSet,
475) {
476    if let Some(metadata) = codebase.class_likes.get(classlike_name)
477        && metadata.flags.is_populated()
478    {
479        return; // Already done, exit early
480    }
481
482    let mut metadata = if let Some(metadata) = codebase.class_likes.remove(classlike_name) {
483        metadata
484    } else {
485        return;
486    };
487
488    for attribute_metadata in &metadata.attributes {
489        symbol_references.add_symbol_reference_to_symbol(metadata.name, attribute_metadata.name, true);
490    }
491
492    for property_name in metadata.get_property_names() {
493        metadata.add_declaring_property_id(property_name, *classlike_name);
494    }
495
496    for method_name in &metadata.methods {
497        metadata.appearing_method_ids.insert(*method_name, *classlike_name);
498        metadata.declaring_method_ids.insert(*method_name, *classlike_name);
499    }
500
501    let force_repopulation = !safe_symbols.contains(classlike_name);
502    for parameter_types in metadata.template_extended_offsets.values_mut() {
503        for parameter_type in parameter_types {
504            populate_union_type(
505                parameter_type,
506                &codebase.symbols,
507                Some(&ReferenceSource::Symbol(true, *classlike_name)),
508                symbol_references,
509                force_repopulation,
510            );
511        }
512    }
513
514    for trait_name in metadata.used_traits.iter().copied().collect::<Vec<_>>() {
515        populate_metadata_from_trait(&mut metadata, codebase, trait_name, symbol_references, safe_symbols);
516    }
517
518    if let Some(parent_classname) = metadata.direct_parent_class {
519        populate_metadata_from_parent_class_like(
520            &mut metadata,
521            codebase,
522            parent_classname,
523            symbol_references,
524            safe_symbols,
525        );
526    }
527
528    let direct_parent_interfaces = metadata.direct_parent_interfaces.clone();
529    for direct_parent_interface in direct_parent_interfaces {
530        populate_interface_metadata_from_parent_interface(
531            &mut metadata,
532            codebase,
533            direct_parent_interface,
534            symbol_references,
535            safe_symbols,
536        );
537    }
538
539    for required_class in metadata.require_extends.iter().copied().collect::<Vec<_>>() {
540        populate_metadata_from_required_class_like(
541            &mut metadata,
542            codebase,
543            required_class,
544            symbol_references,
545            safe_symbols,
546        );
547    }
548
549    for required_interface in metadata.require_implements.iter().copied().collect::<Vec<_>>() {
550        populate_interface_metadata_from_parent_interface(
551            &mut metadata,
552            codebase,
553            required_interface,
554            symbol_references,
555            safe_symbols,
556        );
557    }
558
559    // Apply readonly to properties if the class is readonly
560    if metadata.flags.is_readonly() {
561        for property_metadata in metadata.properties.values_mut() {
562            if !property_metadata.flags.is_static() {
563                property_metadata.flags |= MetadataFlags::READONLY;
564            }
565        }
566    }
567
568    metadata.mark_as_populated();
569    codebase.class_likes.insert(*classlike_name, metadata);
570}
571
572/// Populates interface data inherited from a parent interface.
573fn populate_interface_metadata_from_parent_interface(
574    metadata: &mut ClassLikeMetadata,
575    codebase: &mut CodebaseMetadata,
576    parent_interface: Atom,
577    symbol_references: &mut SymbolReferences,
578    safe_symbols: &AtomSet,
579) {
580    populate_class_like_metadata(&parent_interface, codebase, symbol_references, safe_symbols);
581
582    symbol_references.add_symbol_reference_to_symbol(metadata.name, parent_interface, true);
583
584    let parent_interface_metadata = if let Some(parent_meta) = codebase.class_likes.get(&parent_interface) {
585        parent_meta
586    } else {
587        metadata.invalid_dependencies.insert(parent_interface);
588        return;
589    };
590
591    for (interface_constant_name, interface_constant_metadata) in &parent_interface_metadata.constants {
592        if !metadata.constants.contains_key(interface_constant_name) {
593            metadata.constants.insert(*interface_constant_name, interface_constant_metadata.clone());
594        }
595    }
596
597    metadata.all_parent_interfaces.extend(parent_interface_metadata.all_parent_interfaces.iter().copied());
598    metadata.invalid_dependencies.extend(parent_interface_metadata.invalid_dependencies.iter().copied());
599
600    if let Some(inheritors) = &parent_interface_metadata.permitted_inheritors {
601        metadata.permitted_inheritors.get_or_insert_default().extend(inheritors.iter().copied());
602    }
603
604    // Extend template parameters based on the parent interface's templates
605    extend_template_parameters(metadata, parent_interface_metadata);
606    // Inherit methods (appearing/declaring ids) from the parent interface
607    // Pass codebase immutably if possible, or mutably if method inheritance logic needs it
608    inherit_methods_from_parent(metadata, parent_interface_metadata, codebase);
609    inherit_properties_from_parent(metadata, parent_interface_metadata);
610}
611
612/// Populates class-like data inherited from a parent class or trait.
613fn populate_metadata_from_parent_class_like(
614    metadata: &mut ClassLikeMetadata,
615    codebase: &mut CodebaseMetadata,
616    parent_class: Atom,
617    symbol_references: &mut SymbolReferences,
618    safe_symbols: &AtomSet,
619) {
620    populate_class_like_metadata(&parent_class, codebase, symbol_references, safe_symbols);
621
622    symbol_references.add_symbol_reference_to_symbol(metadata.name, parent_class, true);
623
624    let parent_metadata = if let Some(parent_meta) = codebase.class_likes.get(&parent_class) {
625        parent_meta
626    } else {
627        metadata.invalid_dependencies.insert(parent_class);
628        return;
629    };
630
631    metadata.all_parent_classes.extend(parent_metadata.all_parent_classes.iter().copied());
632    metadata.all_parent_interfaces.extend(parent_metadata.all_parent_interfaces.iter().copied());
633    metadata.used_traits.extend(parent_metadata.used_traits.iter().copied());
634    metadata.invalid_dependencies.extend(parent_metadata.invalid_dependencies.iter().copied());
635
636    if let Some(inheritors) = &parent_metadata.permitted_inheritors {
637        metadata.permitted_inheritors.get_or_insert_default().extend(inheritors.iter().copied());
638    }
639
640    extend_template_parameters(metadata, parent_metadata);
641
642    inherit_methods_from_parent(metadata, parent_metadata, codebase);
643    inherit_properties_from_parent(metadata, parent_metadata);
644
645    for (parent_constant_name, parent_constant_metadata) in &parent_metadata.constants {
646        if !metadata.constants.contains_key(parent_constant_name) {
647            metadata.constants.insert(*parent_constant_name, parent_constant_metadata.clone());
648        }
649    }
650
651    if parent_metadata.flags.has_consistent_templates() {
652        metadata.flags |= MetadataFlags::CONSISTENT_TEMPLATES;
653    }
654}
655
656/// Populates class-like data inherited from a parent class or trait.
657fn populate_metadata_from_required_class_like(
658    metadata: &mut ClassLikeMetadata,
659    codebase: &mut CodebaseMetadata,
660    parent_class: Atom,
661    symbol_references: &mut SymbolReferences,
662    safe_symbols: &AtomSet,
663) {
664    populate_class_like_metadata(&parent_class, codebase, symbol_references, safe_symbols);
665
666    symbol_references.add_symbol_reference_to_symbol(metadata.name, parent_class, true);
667
668    let parent_metadata = if let Some(parent_meta) = codebase.class_likes.get(&parent_class) {
669        parent_meta
670    } else {
671        metadata.invalid_dependencies.insert(parent_class);
672        return;
673    };
674
675    metadata.require_extends.extend(parent_metadata.all_parent_classes.iter().copied());
676    metadata.require_implements.extend(parent_metadata.all_parent_interfaces.iter().copied());
677}
678
679/// Populates class-like data inherited from a used trait.
680fn populate_metadata_from_trait(
681    metadata: &mut ClassLikeMetadata,
682    codebase: &mut CodebaseMetadata,
683    trait_name: Atom,
684    symbol_references: &mut SymbolReferences,
685    safe_symbols: &AtomSet,
686) {
687    populate_class_like_metadata(&trait_name, codebase, symbol_references, safe_symbols);
688
689    symbol_references.add_symbol_reference_to_symbol(metadata.name, trait_name, true);
690
691    let Some(trait_metadata) = codebase.class_likes.get(&trait_name) else {
692        metadata.invalid_dependencies.insert(trait_name);
693        return;
694    };
695
696    // Inherit constants (if not already defined)
697    for (trait_constant_name, trait_constant_metadata) in &trait_metadata.constants {
698        if !metadata.constants.contains_key(trait_constant_name) {
699            metadata.constants.insert(*trait_constant_name, trait_constant_metadata.clone());
700        }
701    }
702
703    // Inherit the trait's parent interfaces (direct parents of the trait become parents of the user)
704    metadata.all_parent_interfaces.extend(trait_metadata.direct_parent_interfaces.iter().copied());
705
706    // Also inherit invalid dependencies from the trait
707    metadata.invalid_dependencies.extend(trait_metadata.invalid_dependencies.iter().copied());
708
709    // Extend template parameters based on the trait's templates
710    extend_template_parameters(metadata, trait_metadata);
711
712    // Inherit methods and properties from the trait
713    inherit_methods_from_parent(metadata, trait_metadata, codebase);
714    inherit_properties_from_parent(metadata, trait_metadata);
715}
716
717/// Inherits method declarations and appearances from a parent class-like.
718/// Updates declaring_method_ids, appearing_method_ids, etc.
719fn inherit_methods_from_parent(
720    metadata: &mut ClassLikeMetadata,
721    parent_metadata: &ClassLikeMetadata,
722    codebase: &CodebaseMetadata,
723) {
724    let class_like_name = metadata.name;
725    let is_trait = metadata.kind.is_trait();
726
727    for (method_name, appearing_class_like) in &parent_metadata.appearing_method_ids {
728        if metadata.has_appearing_method(method_name) {
729            continue;
730        }
731
732        metadata
733            .appearing_method_ids
734            .insert(*method_name, if is_trait { class_like_name } else { *appearing_class_like });
735
736        if codebase.function_likes.contains_key(&(class_like_name, *method_name)) {
737            metadata.potential_declaring_method_ids.insert(*method_name, AtomSet::from_iter([class_like_name]));
738        } else {
739            if let Some(parent_potential_method_ids) = parent_metadata.get_potential_declaring_method_id(method_name) {
740                metadata.potential_declaring_method_ids.insert(*method_name, parent_potential_method_ids.clone());
741            }
742
743            metadata.add_potential_declaring_method(*method_name, class_like_name);
744            metadata.add_potential_declaring_method(*method_name, parent_metadata.name);
745        }
746    }
747
748    for (method_name, declaring_class) in &parent_metadata.inheritable_method_ids {
749        if !method_name.eq(&atom("__construct")) || parent_metadata.flags.has_consistent_constructor() {
750            if !parent_metadata.kind.is_trait() || is_method_abstract(codebase, declaring_class, method_name) {
751                metadata.add_overridden_method_parent(*method_name, *declaring_class);
752            }
753
754            if let Some(map) = metadata.overridden_method_ids.get_mut(method_name)
755                && let Some(overridden_method_ids) = parent_metadata.overridden_method_ids.get(method_name)
756            {
757                map.extend(overridden_method_ids.iter().copied());
758            }
759        }
760
761        let mut aliased_method_names = vec![*method_name];
762        if parent_metadata.kind.is_trait() {
763            aliased_method_names
764                .extend(metadata.get_trait_alias_map().iter().filter(|(_, v)| *v == method_name).map(|(k, _)| *k));
765        }
766
767        for aliased_method_name in aliased_method_names {
768            let implementing_method_id = metadata.declaring_method_ids.get(&aliased_method_name);
769            if let Some(implementing_method_id) = implementing_method_id
770                && !is_method_abstract(codebase, implementing_method_id, &aliased_method_name)
771            {
772                continue;
773            }
774
775            metadata.declaring_method_ids.insert(aliased_method_name, *declaring_class);
776            metadata.inheritable_method_ids.insert(aliased_method_name, *declaring_class);
777        }
778    }
779}
780
781/// Inherits property declarations and appearances from a parent class-like.
782/// Updates declaring_property_ids, appearing_property_ids, etc.
783fn inherit_properties_from_parent(metadata: &mut ClassLikeMetadata, parent_metadata: &ClassLikeMetadata) {
784    let classlike_name = metadata.name;
785    let is_trait = metadata.kind.is_trait();
786    let parent_is_trait = parent_metadata.kind.is_trait();
787
788    for (property_name, appearing_classlike) in &parent_metadata.appearing_property_ids {
789        if metadata.has_appearing_property(property_name) {
790            continue;
791        }
792
793        if !parent_is_trait
794            && let Some(parent_property_metadata) = parent_metadata.properties.get(property_name)
795            && parent_property_metadata.is_final()
796        {
797            continue;
798        }
799
800        metadata
801            .appearing_property_ids
802            .insert(*property_name, if is_trait { classlike_name } else { *appearing_classlike });
803    }
804
805    for (property_name, declaring_classlike) in &parent_metadata.declaring_property_ids {
806        if metadata.declaring_property_ids.contains_key(property_name) {
807            continue;
808        }
809
810        if !parent_is_trait
811            && let Some(parent_property_metadata) = parent_metadata.properties.get(property_name)
812            && parent_property_metadata.is_final()
813        {
814            continue;
815        }
816
817        metadata.declaring_property_ids.insert(*property_name, *declaring_classlike);
818    }
819
820    for (property_name, inheritable_classlike) in &parent_metadata.inheritable_property_ids {
821        let mut is_overridable = true;
822        if !parent_is_trait {
823            if let Some(parent_property_metadata) = parent_metadata.properties.get(property_name)
824                && parent_property_metadata.is_final()
825            {
826                is_overridable = false;
827            }
828
829            if is_overridable {
830                metadata.overridden_property_ids.entry(*property_name).or_default().insert(*inheritable_classlike);
831            }
832        }
833
834        if is_overridable {
835            metadata.inheritable_property_ids.insert(*property_name, *inheritable_classlike);
836        }
837    }
838}
839
840/// Extends the template parameter map of `metadata` based on `parent_metadata`.
841/// Handles resolving template types inherited from parents/traits.
842fn extend_template_parameters(metadata: &mut ClassLikeMetadata, parent_metadata: &ClassLikeMetadata) {
843    let parent_name = parent_metadata.name;
844
845    if !parent_metadata.template_types.is_empty() {
846        metadata.template_extended_parameters.entry(parent_name).or_default();
847
848        if let Some(parent_offsets) = metadata.template_extended_offsets.get(&parent_name).cloned() {
849            let parent_template_type_names = parent_metadata.get_template_type_names();
850
851            for (i, extended_type_arc) in parent_offsets.into_iter().enumerate() {
852                if let Some(mapped_name) = parent_template_type_names.get(i).copied() {
853                    metadata.add_template_extended_parameter(parent_name, mapped_name, extended_type_arc);
854                }
855            }
856
857            let current_child_extended_params = metadata.template_extended_parameters.clone();
858            for (grandparent_fqcn, type_map) in &parent_metadata.template_extended_parameters {
859                for (template_name, type_to_resolve_arc) in type_map {
860                    let resolved_type = extend_type(type_to_resolve_arc, &current_child_extended_params);
861
862                    metadata.add_template_extended_parameter(*grandparent_fqcn, *template_name, resolved_type);
863                }
864            }
865        } else {
866            for (parameter_name, parameter_type_map) in &parent_metadata.template_types {
867                for (_, parameter_type) in parameter_type_map {
868                    metadata.add_template_extended_parameter(parent_name, *parameter_name, parameter_type.clone());
869                }
870            }
871
872            metadata.extend_template_extended_parameters(parent_metadata.template_extended_parameters.clone());
873        }
874    } else {
875        // Inherit the parent's extended parameters map directly.
876        metadata.extend_template_extended_parameters(parent_metadata.template_extended_parameters.clone());
877    }
878}
879
880/// Resolves a TUnion that might contain generic parameters, using the provided
881/// extended parameter map.
882///
883/// Example: If `extended_type` is `T` (generic param) and `template_extended_parameters`
884/// maps `T` defined on `ParentClass` to `string`, this returns a `TUnion` containing `string`.
885fn extend_type(
886    extended_type: &TUnion,
887    template_extended_parameters: &AtomMap<IndexMap<Atom, TUnion, RandomState>>,
888) -> TUnion {
889    if !extended_type.has_template() {
890        return extended_type.clone();
891    }
892
893    let mut extended_types = Vec::new();
894
895    let mut worklist = extended_type.types.clone().into_owned();
896    while let Some(atomic_type) = worklist.pop() {
897        if let TAtomic::GenericParameter(TGenericParameter {
898            parameter_name,
899            defining_entity: GenericParent::ClassLike(defining_entity),
900            ..
901        }) = &atomic_type
902            && let Some(extended_parameters) = template_extended_parameters.get(defining_entity)
903            && let Some(referenced_type) = extended_parameters.get(parameter_name)
904        {
905            extended_types.extend(referenced_type.types.clone().into_owned());
906            continue;
907        }
908
909        extended_types.push(atomic_type);
910    }
911
912    TUnion::from_vec(extended_types)
913}