1use foldhash::HashSet;
2
3use mago_atom::Atom;
4use mago_atom::AtomMap;
5use mago_atom::AtomSet;
6
7use crate::metadata::CodebaseMetadata;
8use crate::metadata::constant::ConstantMetadata;
9use crate::metadata::flags::MetadataFlags;
10use crate::reference::ReferenceSource;
11use crate::reference::SymbolReferences;
12use crate::symbol::SymbolIdentifier;
13use crate::symbol::Symbols;
14use crate::ttype::union::populate_union_type;
15
16mod docblock;
17mod hierarchy;
18mod merge;
19mod methods;
20mod properties;
21mod signatures;
22mod sorter;
23mod templates;
24
25pub fn populate_codebase(
34 codebase: &mut CodebaseMetadata,
35 symbol_references: &mut SymbolReferences,
36 safe_symbols: AtomSet,
37 safe_symbol_members: HashSet<SymbolIdentifier>,
38) {
39 populate_codebase_inner(codebase, symbol_references, safe_symbols, safe_symbol_members, None)
40}
41
42pub fn populate_codebase_targeted(
48 codebase: &mut CodebaseMetadata,
49 symbol_references: &mut SymbolReferences,
50 safe_symbols: AtomSet,
51 safe_symbol_members: HashSet<SymbolIdentifier>,
52 dirty_symbols: HashSet<SymbolIdentifier>,
53) {
54 populate_codebase_inner(codebase, symbol_references, safe_symbols, safe_symbol_members, Some(dirty_symbols))
55}
56
57fn populate_codebase_inner(
58 codebase: &mut CodebaseMetadata,
59 symbol_references: &mut SymbolReferences,
60 safe_symbols: AtomSet,
61 safe_symbol_members: HashSet<SymbolIdentifier>,
62 dirty_symbols: Option<HashSet<SymbolIdentifier>>,
63) {
64 let mut class_likes_to_repopulate = AtomSet::default();
65 if let Some(dirty) = &dirty_symbols {
66 let mut dirty_class_names = AtomSet::default();
67 for (name, _) in dirty {
68 dirty_class_names.insert(*name);
69 }
70
71 for class_name in &dirty_class_names {
72 if let Some(metadata) = codebase.class_likes.get(class_name)
73 && (!metadata.flags.is_populated()
74 || (metadata.flags.is_user_defined() && !safe_symbols.contains(class_name)))
75 {
76 class_likes_to_repopulate.insert(*class_name);
77 }
78 }
79
80 for (class_name, metadata) in &codebase.class_likes {
86 if metadata.flags.is_user_defined()
87 && !safe_symbols.contains(class_name)
88 && !class_likes_to_repopulate.contains(class_name)
89 {
90 class_likes_to_repopulate.insert(*class_name);
91 }
92 }
93 } else {
94 for (name, metadata) in &codebase.class_likes {
95 if !metadata.flags.is_populated() || (metadata.flags.is_user_defined() && !safe_symbols.contains(name)) {
96 class_likes_to_repopulate.insert(*name);
97 }
98 }
99 }
100
101 for class_like_name in &class_likes_to_repopulate {
102 if let Some(classlike_info) = codebase.class_likes.get_mut(class_like_name) {
103 classlike_info.flags &= !MetadataFlags::POPULATED;
104 classlike_info.declaring_property_ids.clear();
105 classlike_info.appearing_property_ids.clear();
106 classlike_info.declaring_method_ids.clear();
107 classlike_info.appearing_method_ids.clear();
108 classlike_info.overridden_method_ids.clear();
109 classlike_info.overridden_property_ids.clear();
110 classlike_info.invalid_dependencies.clear();
111 }
112 }
113
114 let sorted_classes = sorter::sort_class_likes(codebase, &class_likes_to_repopulate);
115 for class_name in sorted_classes {
116 hierarchy::populate_class_like_metadata_iterative(class_name, codebase, symbol_references);
117 }
118
119 let incremental = !safe_symbols.is_empty() || !safe_symbol_members.is_empty();
120
121 if let Some(dirty) = &dirty_symbols {
122 for dirty_key in dirty {
123 if let Some(function_like_metadata) = codebase.function_likes.get_mut(dirty_key) {
124 let force_repopulation = function_like_metadata.flags.is_user_defined();
125 if function_like_metadata.flags.is_populated() && !force_repopulation {
126 continue;
127 }
128
129 let reference_source = if dirty_key.1.is_empty() || function_like_metadata.get_kind().is_closure() {
130 ReferenceSource::Symbol(true, dirty_key.0)
131 } else {
132 ReferenceSource::ClassLikeMember(true, dirty_key.0, dirty_key.1)
133 };
134
135 signatures::populate_function_like_metadata(
136 function_like_metadata,
137 &codebase.symbols,
138 &reference_source,
139 symbol_references,
140 force_repopulation,
141 );
142 }
143 }
144
145 for (name, function_like_metadata) in &mut codebase.function_likes {
150 if dirty.contains(name) {
151 continue;
152 }
153
154 let is_closure_or_arrow =
155 function_like_metadata.get_kind().is_closure() || function_like_metadata.get_kind().is_arrow_function();
156
157 let is_safe = if is_closure_or_arrow {
158 true
159 } else if name.1.is_empty() {
160 safe_symbols.contains(&name.0)
161 } else {
162 safe_symbol_members.contains(name) || safe_symbols.contains(&name.0)
163 };
164
165 let force_repopulation = function_like_metadata.flags.is_user_defined() && !is_safe;
166 if function_like_metadata.flags.is_populated() && !force_repopulation {
167 continue;
168 }
169
170 let reference_source = if name.1.is_empty() || function_like_metadata.get_kind().is_closure() {
171 ReferenceSource::Symbol(true, name.0)
172 } else {
173 ReferenceSource::ClassLikeMember(true, name.0, name.1)
174 };
175
176 signatures::populate_function_like_metadata(
177 function_like_metadata,
178 &codebase.symbols,
179 &reference_source,
180 symbol_references,
181 force_repopulation,
182 );
183 }
184 } else {
185 for (name, function_like_metadata) in &mut codebase.function_likes {
186 let is_closure_or_arrow =
187 function_like_metadata.get_kind().is_closure() || function_like_metadata.get_kind().is_arrow_function();
188
189 let is_safe = if is_closure_or_arrow {
190 true
191 } else if name.1.is_empty() {
192 safe_symbols.contains(&name.0)
193 } else {
194 safe_symbol_members.contains(name) || safe_symbols.contains(&name.0)
195 };
196
197 let force_repopulation = function_like_metadata.flags.is_user_defined() && !is_safe;
198 if incremental && function_like_metadata.flags.is_populated() && !force_repopulation {
199 continue;
200 }
201
202 let reference_source = if name.1.is_empty() || function_like_metadata.get_kind().is_closure() {
203 ReferenceSource::Symbol(true, name.0)
204 } else {
205 ReferenceSource::ClassLikeMember(true, name.0, name.1)
206 };
207
208 signatures::populate_function_like_metadata(
209 function_like_metadata,
210 &codebase.symbols,
211 &reference_source,
212 symbol_references,
213 force_repopulation,
214 );
215 }
216 }
217
218 if let Some(_dirty) = &dirty_symbols {
219 for class_name in &class_likes_to_repopulate {
220 if let Some(metadata) = codebase.class_likes.get_mut(class_name) {
221 hierarchy::populate_class_like_types(
222 *class_name,
223 metadata,
224 &codebase.symbols,
225 symbol_references,
226 true, );
228 }
229 }
230 } else {
231 for (name, metadata) in &mut codebase.class_likes {
232 let force_repopulation = metadata.flags.is_user_defined() && !safe_symbols.contains(name);
233
234 if incremental && metadata.flags.is_populated() && !force_repopulation {
235 continue;
236 }
237
238 hierarchy::populate_class_like_types(
239 *name,
240 metadata,
241 &codebase.symbols,
242 symbol_references,
243 force_repopulation,
244 );
245 }
246 }
247
248 if let Some(dirty) = &dirty_symbols {
249 let mut dirty_const_names: AtomSet = AtomSet::default();
250 for (name, member) in dirty {
251 if member.is_empty() {
252 dirty_const_names.insert(*name);
253 }
254 }
255
256 for const_name in dirty_const_names {
257 if let Some(constant) = codebase.constants.get_mut(&const_name) {
258 let force_repopulation = constant.flags.is_user_defined();
259 if constant.flags.is_populated() && !force_repopulation {
260 continue;
261 }
262
263 populate_constant(const_name, constant, &codebase.symbols, symbol_references, force_repopulation);
264 }
265 }
266 } else {
267 for (name, constant) in &mut codebase.constants {
268 let force_repopulation = constant.flags.is_user_defined() && !safe_symbols.contains(name);
269 if incremental && constant.flags.is_populated() && !force_repopulation {
270 continue;
271 }
272
273 populate_constant(*name, constant, &codebase.symbols, symbol_references, force_repopulation);
274 }
275 }
276
277 if !incremental || !class_likes_to_repopulate.is_empty() {
278 let mut direct_classlike_descendants = AtomMap::default();
279 let mut all_classlike_descendants = AtomMap::default();
280
281 for (class_like_name, class_like_metadata) in &codebase.class_likes {
282 for parent_interface in &class_like_metadata.all_parent_interfaces {
283 all_classlike_descendants
284 .entry(*parent_interface)
285 .or_insert_with(AtomSet::default)
286 .insert(*class_like_name);
287 }
288
289 for parent_interface in &class_like_metadata.direct_parent_interfaces {
290 direct_classlike_descendants
291 .entry(*parent_interface)
292 .or_insert_with(AtomSet::default)
293 .insert(*class_like_name);
294 }
295
296 for parent_class in &class_like_metadata.all_parent_classes {
297 all_classlike_descendants
298 .entry(*parent_class)
299 .or_insert_with(AtomSet::default)
300 .insert(*class_like_name);
301 }
302
303 for used_trait in &class_like_metadata.used_traits {
304 all_classlike_descendants.entry(*used_trait).or_default().insert(*class_like_name);
305 }
306
307 if let Some(parent_class) = &class_like_metadata.direct_parent_class {
308 direct_classlike_descendants
309 .entry(*parent_class)
310 .or_insert_with(AtomSet::default)
311 .insert(*class_like_name);
312 }
313 }
314
315 for (parent_name, children) in &direct_classlike_descendants {
316 if let Some(parent_metadata) = codebase.class_likes.get_mut(parent_name) {
317 parent_metadata.child_class_likes = Some(children.clone());
318 }
319 }
320
321 codebase.all_class_like_descendants = all_classlike_descendants;
322 codebase.direct_classlike_descendants = direct_classlike_descendants;
323 }
324
325 if !incremental || !class_likes_to_repopulate.is_empty() {
326 let dirty_classes = if dirty_symbols.is_some() { Some(&class_likes_to_repopulate) } else { None };
327
328 docblock::inherit_method_docblocks(codebase, &safe_symbols, dirty_classes);
329 }
330
331 codebase.safe_symbols = safe_symbols;
332 codebase.safe_symbol_members = safe_symbol_members;
333}
334
335fn populate_constant(
337 name: Atom,
338 constant: &mut ConstantMetadata,
339 symbols: &Symbols,
340 symbol_references: &mut SymbolReferences,
341 force_repopulation: bool,
342) {
343 for attribute_metadata in &constant.attributes {
344 symbol_references.add_symbol_reference_to_symbol(name, attribute_metadata.name, true);
345 }
346
347 if let Some(type_metadata) = &mut constant.type_metadata {
348 populate_union_type(
349 &mut type_metadata.type_union,
350 symbols,
351 Some(&ReferenceSource::Symbol(true, name)),
352 symbol_references,
353 force_repopulation,
354 );
355 }
356
357 if let Some(inferred_type) = &mut constant.inferred_type {
358 populate_union_type(
359 inferred_type,
360 symbols,
361 Some(&ReferenceSource::Symbol(true, name)),
362 symbol_references,
363 force_repopulation,
364 );
365 }
366
367 constant.flags |= MetadataFlags::POPULATED;
368}