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