mago_codex/populator/
mod.rs

1mod docblock;
2mod hierarchy;
3mod merge;
4mod methods;
5mod properties;
6mod signatures;
7mod sorter;
8mod templates;
9
10use ahash::HashSet;
11use mago_atom::AtomMap;
12use mago_atom::AtomSet;
13
14use crate::metadata::CodebaseMetadata;
15use crate::reference::ReferenceSource;
16use crate::reference::SymbolReferences;
17use crate::symbol::SymbolIdentifier;
18use crate::ttype::union::populate_union_type;
19
20/// Populates the codebase metadata, resolving types and inheritance.
21///
22/// This function processes class-likes, function-likes, and constants to:
23///
24/// - Resolve type signatures (populating `TUnion` and `TAtomic` types).
25/// - Calculate inheritance hierarchies (parent classes, interfaces, traits).
26/// - Determine method and property origins (declaring vs. appearing).
27/// - Build descendant maps for efficient lookup.
28///
29/// TODO(azjezz): This function is a performance bottleneck.
30pub fn populate_codebase(
31    codebase: &mut CodebaseMetadata,
32    symbol_references: &mut SymbolReferences,
33    safe_symbols: AtomSet,
34    safe_symbol_members: HashSet<SymbolIdentifier>,
35) {
36    let mut class_likes_to_repopulate = AtomSet::default();
37    for (name, metadata) in &codebase.class_likes {
38        // Repopulate if not populated OR if user-defined and not marked safe.
39        if !metadata.flags.is_populated() || (metadata.flags.is_user_defined() && !safe_symbols.contains(name)) {
40            class_likes_to_repopulate.insert(*name);
41        }
42    }
43
44    for class_like_name in &class_likes_to_repopulate {
45        if let Some(classlike_info) = codebase.class_likes.get_mut(class_like_name) {
46            classlike_info.flags &= !crate::metadata::flags::MetadataFlags::POPULATED;
47            classlike_info.declaring_property_ids.clear();
48            classlike_info.appearing_property_ids.clear();
49            classlike_info.declaring_method_ids.clear();
50            classlike_info.appearing_method_ids.clear();
51        }
52    }
53
54    let sorted_classes = sorter::sort_class_likes(codebase, &class_likes_to_repopulate);
55    for class_name in sorted_classes {
56        hierarchy::populate_class_like_metadata_iterative(class_name, codebase, symbol_references);
57    }
58
59    for (name, function_like_metadata) in &mut codebase.function_likes {
60        let force_repopulation = function_like_metadata.flags.is_user_defined() && !safe_symbols.contains(&name.0);
61
62        let reference_source = if name.1.is_empty() || function_like_metadata.get_kind().is_closure() {
63            // Top-level function or closure
64            ReferenceSource::Symbol(true, name.0)
65        } else {
66            // Class method
67            ReferenceSource::ClassLikeMember(true, name.0, name.1)
68        };
69
70        signatures::populate_function_like_metadata(
71            function_like_metadata,
72            &codebase.symbols,
73            &reference_source,
74            symbol_references,
75            force_repopulation,
76        );
77    }
78
79    for (name, metadata) in &mut codebase.class_likes {
80        let userland_force_repopulation = metadata.flags.is_user_defined() && !safe_symbols.contains(name);
81
82        hierarchy::populate_class_like_types(
83            *name,
84            metadata,
85            &codebase.symbols,
86            symbol_references,
87            userland_force_repopulation,
88        );
89    }
90
91    for (name, constant) in &mut codebase.constants {
92        for attribute_metadata in &constant.attributes {
93            symbol_references.add_symbol_reference_to_symbol(*name, attribute_metadata.name, true);
94        }
95
96        if let Some(type_metadata) = &mut constant.type_metadata {
97            populate_union_type(
98                &mut type_metadata.type_union,
99                &codebase.symbols,
100                Some(&ReferenceSource::Symbol(true, *name)),
101                symbol_references,
102                !safe_symbols.contains(name),
103            );
104        }
105
106        if let Some(inferred_type) = &mut constant.inferred_type {
107            populate_union_type(
108                inferred_type,
109                &codebase.symbols,
110                Some(&ReferenceSource::Symbol(true, *name)),
111                symbol_references,
112                !safe_symbols.contains(name),
113            );
114        }
115    }
116
117    let mut direct_classlike_descendants = AtomMap::default();
118    let mut all_classlike_descendants = AtomMap::default();
119
120    for (class_like_name, class_like_metadata) in &codebase.class_likes {
121        for parent_interface in &class_like_metadata.all_parent_interfaces {
122            all_classlike_descendants
123                .entry(*parent_interface)
124                .or_insert_with(AtomSet::default)
125                .insert(*class_like_name);
126        }
127
128        for parent_interface in &class_like_metadata.direct_parent_interfaces {
129            direct_classlike_descendants
130                .entry(*parent_interface)
131                .or_insert_with(AtomSet::default)
132                .insert(*class_like_name);
133        }
134
135        for parent_class in &class_like_metadata.all_parent_classes {
136            all_classlike_descendants.entry(*parent_class).or_insert_with(AtomSet::default).insert(*class_like_name);
137        }
138
139        for used_trait in &class_like_metadata.used_traits {
140            all_classlike_descendants.entry(*used_trait).or_default().insert(*class_like_name);
141        }
142
143        if let Some(parent_class) = &class_like_metadata.direct_parent_class {
144            direct_classlike_descendants.entry(*parent_class).or_insert_with(AtomSet::default).insert(*class_like_name);
145        }
146    }
147
148    // Perform docblock inheritance for methods with @inheritDoc or no docblock
149    docblock::inherit_method_docblocks(codebase);
150
151    codebase.all_class_like_descendants = all_classlike_descendants;
152    codebase.direct_classlike_descendants = direct_classlike_descendants;
153    codebase.safe_symbols = safe_symbols;
154    codebase.safe_symbol_members = safe_symbol_members;
155}