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