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
25#[allow(clippy::implicit_hasher)]
34pub fn populate_codebase(
35 codebase: &mut CodebaseMetadata,
36 symbol_references: &mut SymbolReferences,
37 safe_symbols: AtomSet,
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: AtomSet,
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: AtomSet,
63 safe_symbol_members: HashSet<SymbolIdentifier>,
64 dirty_symbols: Option<HashSet<SymbolIdentifier>>,
65) {
66 let mut class_likes_to_repopulate = AtomSet::default();
67 if let Some(dirty) = &dirty_symbols {
68 let mut dirty_class_names = AtomSet::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 = metadata.flags.is_user_defined() && !safe_symbols.contains(name);
235
236 if incremental && metadata.flags.is_populated() && !force_repopulation {
237 continue;
238 }
239
240 hierarchy::populate_class_like_types(
241 *name,
242 metadata,
243 &codebase.symbols,
244 symbol_references,
245 force_repopulation,
246 );
247 }
248 }
249
250 if let Some(dirty) = &dirty_symbols {
251 let mut dirty_const_names: AtomSet = AtomSet::default();
252 for (name, member) in dirty {
253 if member.is_empty() {
254 dirty_const_names.insert(*name);
255 }
256 }
257
258 for const_name in dirty_const_names {
259 if let Some(constant) = codebase.constants.get_mut(&const_name) {
260 let force_repopulation = constant.flags.is_user_defined();
261 if constant.flags.is_populated() && !force_repopulation {
262 continue;
263 }
264
265 populate_constant(const_name, constant, &codebase.symbols, symbol_references, force_repopulation);
266 }
267 }
268 } else {
269 for (name, constant) in &mut codebase.constants {
270 let force_repopulation = constant.flags.is_user_defined() && !safe_symbols.contains(name);
271 if incremental && constant.flags.is_populated() && !force_repopulation {
272 continue;
273 }
274
275 populate_constant(*name, constant, &codebase.symbols, symbol_references, force_repopulation);
276 }
277 }
278
279 if !incremental || !class_likes_to_repopulate.is_empty() {
280 let mut direct_classlike_descendants = AtomMap::default();
281 let mut all_classlike_descendants = AtomMap::default();
282
283 for (class_like_name, class_like_metadata) in &codebase.class_likes {
284 for parent_interface in &class_like_metadata.all_parent_interfaces {
285 all_classlike_descendants
286 .entry(*parent_interface)
287 .or_insert_with(AtomSet::default)
288 .insert(*class_like_name);
289 }
290
291 for parent_interface in &class_like_metadata.direct_parent_interfaces {
292 direct_classlike_descendants
293 .entry(*parent_interface)
294 .or_insert_with(AtomSet::default)
295 .insert(*class_like_name);
296 }
297
298 for parent_class in &class_like_metadata.all_parent_classes {
299 all_classlike_descendants
300 .entry(*parent_class)
301 .or_insert_with(AtomSet::default)
302 .insert(*class_like_name);
303 }
304
305 for used_trait in &class_like_metadata.used_traits {
306 all_classlike_descendants.entry(*used_trait).or_default().insert(*class_like_name);
307 }
308
309 if let Some(parent_class) = &class_like_metadata.direct_parent_class {
310 direct_classlike_descendants
311 .entry(*parent_class)
312 .or_insert_with(AtomSet::default)
313 .insert(*class_like_name);
314 }
315 }
316
317 for (parent_name, children) in &direct_classlike_descendants {
318 if let Some(parent_metadata) = codebase.class_likes.get_mut(parent_name) {
319 parent_metadata.child_class_likes = Some(children.clone());
320 }
321 }
322
323 codebase.all_class_like_descendants = all_classlike_descendants;
324 codebase.direct_classlike_descendants = direct_classlike_descendants;
325 }
326
327 if !incremental || !class_likes_to_repopulate.is_empty() {
328 let dirty_classes = if dirty_symbols.is_some() { Some(&class_likes_to_repopulate) } else { None };
329
330 docblock::inherit_method_docblocks(codebase, &safe_symbols, dirty_classes);
331 }
332
333 codebase.safe_symbols = safe_symbols;
334 codebase.safe_symbol_members = safe_symbol_members;
335}
336
337fn populate_constant(
339 name: Atom,
340 constant: &mut ConstantMetadata,
341 symbols: &Symbols,
342 symbol_references: &mut SymbolReferences,
343 force_repopulation: bool,
344) {
345 for attribute_metadata in &constant.attributes {
346 symbol_references.add_symbol_reference_to_symbol(name, attribute_metadata.name, true);
347 }
348
349 if let Some(type_metadata) = &mut constant.type_metadata {
350 populate_union_type(
351 &mut type_metadata.type_union,
352 symbols,
353 Some(&ReferenceSource::Symbol(true, name)),
354 symbol_references,
355 force_repopulation,
356 );
357 }
358
359 if let Some(inferred_type) = &mut constant.inferred_type {
360 populate_union_type(
361 inferred_type,
362 symbols,
363 Some(&ReferenceSource::Symbol(true, name)),
364 symbol_references,
365 force_repopulation,
366 );
367 }
368
369 constant.flags |= MetadataFlags::POPULATED;
370}