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 is_closure_or_arrow = function_like_metadata.get_kind().is_closure()
125 || function_like_metadata.get_kind().is_arrow_function();
126
127 if is_closure_or_arrow {
128 continue;
129 }
130
131 let force_repopulation = function_like_metadata.flags.is_user_defined();
132 if function_like_metadata.flags.is_populated() && !force_repopulation {
133 continue;
134 }
135
136 let reference_source = if dirty_key.1.is_empty() || function_like_metadata.get_kind().is_closure() {
137 ReferenceSource::Symbol(true, dirty_key.0)
138 } else {
139 ReferenceSource::ClassLikeMember(true, dirty_key.0, dirty_key.1)
140 };
141
142 signatures::populate_function_like_metadata(
143 function_like_metadata,
144 &codebase.symbols,
145 &reference_source,
146 symbol_references,
147 force_repopulation,
148 );
149 }
150 }
151
152 for (name, function_like_metadata) in &mut codebase.function_likes {
157 if dirty.contains(name) {
158 continue;
159 }
160
161 let is_closure_or_arrow =
162 function_like_metadata.get_kind().is_closure() || function_like_metadata.get_kind().is_arrow_function();
163
164 let is_safe = if is_closure_or_arrow {
165 true
166 } else if name.1.is_empty() {
167 safe_symbols.contains(&name.0)
168 } else {
169 safe_symbol_members.contains(name) || safe_symbols.contains(&name.0)
170 };
171
172 let force_repopulation = function_like_metadata.flags.is_user_defined() && !is_safe;
173 if function_like_metadata.flags.is_populated() && !force_repopulation {
174 continue;
175 }
176
177 let reference_source = if name.1.is_empty() || function_like_metadata.get_kind().is_closure() {
178 ReferenceSource::Symbol(true, name.0)
179 } else {
180 ReferenceSource::ClassLikeMember(true, name.0, name.1)
181 };
182
183 signatures::populate_function_like_metadata(
184 function_like_metadata,
185 &codebase.symbols,
186 &reference_source,
187 symbol_references,
188 force_repopulation,
189 );
190 }
191 } else {
192 for (name, function_like_metadata) in &mut codebase.function_likes {
193 let is_closure_or_arrow =
194 function_like_metadata.get_kind().is_closure() || function_like_metadata.get_kind().is_arrow_function();
195
196 let is_safe = if is_closure_or_arrow {
197 true
198 } else if name.1.is_empty() {
199 safe_symbols.contains(&name.0)
200 } else {
201 safe_symbol_members.contains(name) || safe_symbols.contains(&name.0)
202 };
203
204 let force_repopulation = function_like_metadata.flags.is_user_defined() && !is_safe;
205 if incremental && function_like_metadata.flags.is_populated() && !force_repopulation {
206 continue;
207 }
208
209 let reference_source = if name.1.is_empty() || function_like_metadata.get_kind().is_closure() {
210 ReferenceSource::Symbol(true, name.0)
211 } else {
212 ReferenceSource::ClassLikeMember(true, name.0, name.1)
213 };
214
215 signatures::populate_function_like_metadata(
216 function_like_metadata,
217 &codebase.symbols,
218 &reference_source,
219 symbol_references,
220 force_repopulation,
221 );
222 }
223 }
224
225 if let Some(_dirty) = &dirty_symbols {
226 for class_name in &class_likes_to_repopulate {
227 if let Some(metadata) = codebase.class_likes.get_mut(class_name) {
228 hierarchy::populate_class_like_types(
229 *class_name,
230 metadata,
231 &codebase.symbols,
232 symbol_references,
233 true, );
235 }
236 }
237 } else {
238 for (name, metadata) in &mut codebase.class_likes {
239 let force_repopulation = metadata.flags.is_user_defined() && !safe_symbols.contains(name);
240
241 if incremental && metadata.flags.is_populated() && !force_repopulation {
242 continue;
243 }
244
245 hierarchy::populate_class_like_types(
246 *name,
247 metadata,
248 &codebase.symbols,
249 symbol_references,
250 force_repopulation,
251 );
252 }
253 }
254
255 if let Some(dirty) = &dirty_symbols {
256 let mut dirty_const_names: AtomSet = AtomSet::default();
257 for (name, member) in dirty {
258 if member.is_empty() {
259 dirty_const_names.insert(*name);
260 }
261 }
262
263 for const_name in dirty_const_names {
264 if let Some(constant) = codebase.constants.get_mut(&const_name) {
265 let force_repopulation = constant.flags.is_user_defined();
266 if constant.flags.is_populated() && !force_repopulation {
267 continue;
268 }
269
270 populate_constant(const_name, constant, &codebase.symbols, symbol_references, force_repopulation);
271 }
272 }
273 } else {
274 for (name, constant) in &mut codebase.constants {
275 let force_repopulation = constant.flags.is_user_defined() && !safe_symbols.contains(name);
276 if incremental && constant.flags.is_populated() && !force_repopulation {
277 continue;
278 }
279
280 populate_constant(*name, constant, &codebase.symbols, symbol_references, force_repopulation);
281 }
282 }
283
284 if !incremental || !class_likes_to_repopulate.is_empty() {
285 let mut direct_classlike_descendants = AtomMap::default();
286 let mut all_classlike_descendants = AtomMap::default();
287
288 for (class_like_name, class_like_metadata) in &codebase.class_likes {
289 for parent_interface in &class_like_metadata.all_parent_interfaces {
290 all_classlike_descendants
291 .entry(*parent_interface)
292 .or_insert_with(AtomSet::default)
293 .insert(*class_like_name);
294 }
295
296 for parent_interface in &class_like_metadata.direct_parent_interfaces {
297 direct_classlike_descendants
298 .entry(*parent_interface)
299 .or_insert_with(AtomSet::default)
300 .insert(*class_like_name);
301 }
302
303 for parent_class in &class_like_metadata.all_parent_classes {
304 all_classlike_descendants
305 .entry(*parent_class)
306 .or_insert_with(AtomSet::default)
307 .insert(*class_like_name);
308 }
309
310 for used_trait in &class_like_metadata.used_traits {
311 all_classlike_descendants.entry(*used_trait).or_default().insert(*class_like_name);
312 }
313
314 if let Some(parent_class) = &class_like_metadata.direct_parent_class {
315 direct_classlike_descendants
316 .entry(*parent_class)
317 .or_insert_with(AtomSet::default)
318 .insert(*class_like_name);
319 }
320 }
321
322 for (parent_name, children) in &direct_classlike_descendants {
323 if let Some(parent_metadata) = codebase.class_likes.get_mut(parent_name) {
324 parent_metadata.child_class_likes = Some(children.clone());
325 }
326 }
327
328 codebase.all_class_like_descendants = all_classlike_descendants;
329 codebase.direct_classlike_descendants = direct_classlike_descendants;
330 }
331
332 if !incremental || !class_likes_to_repopulate.is_empty() {
333 let dirty_classes = if dirty_symbols.is_some() { Some(&class_likes_to_repopulate) } else { None };
334
335 docblock::inherit_method_docblocks(codebase, &safe_symbols, dirty_classes);
336 }
337
338 codebase.safe_symbols = safe_symbols;
339 codebase.safe_symbol_members = safe_symbol_members;
340}
341
342fn populate_constant(
344 name: Atom,
345 constant: &mut ConstantMetadata,
346 symbols: &Symbols,
347 symbol_references: &mut SymbolReferences,
348 force_repopulation: bool,
349) {
350 for attribute_metadata in &constant.attributes {
351 symbol_references.add_symbol_reference_to_symbol(name, attribute_metadata.name, true);
352 }
353
354 if let Some(type_metadata) = &mut constant.type_metadata {
355 populate_union_type(
356 &mut type_metadata.type_union,
357 symbols,
358 Some(&ReferenceSource::Symbol(true, name)),
359 symbol_references,
360 force_repopulation,
361 );
362 }
363
364 if let Some(inferred_type) = &mut constant.inferred_type {
365 populate_union_type(
366 inferred_type,
367 symbols,
368 Some(&ReferenceSource::Symbol(true, name)),
369 symbol_references,
370 force_repopulation,
371 );
372 }
373
374 constant.flags |= MetadataFlags::POPULATED;
375}