1use ahash::HashMap;
2use ahash::HashSet;
3use mago_atom::ascii_lowercase_atom;
4use mago_atom::empty_atom;
5use serde::Deserialize;
6use serde::Serialize;
7
8use mago_atom::Atom;
9use mago_atom::AtomSet;
10
11use crate::context::ScopeContext;
12use crate::diff::CodebaseDiff;
13use crate::identifier::function_like::FunctionLikeIdentifier;
14use crate::identifier::method::MethodIdentifier;
15use crate::symbol::SymbolIdentifier;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
20pub enum ReferenceSource {
21 Symbol(bool, Atom),
25 ClassLikeMember(bool, Atom, Atom),
30}
31
32#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
35pub struct InvalidSymbols {
36 invalid_symbol_and_member_signatures: HashSet<SymbolIdentifier>,
39 invalid_symbol_and_member_bodies: HashSet<SymbolIdentifier>,
42 partially_invalid_symbols: AtomSet,
45}
46
47#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
53pub struct SymbolReferences {
54 symbol_references_to_symbols: HashMap<SymbolIdentifier, HashSet<SymbolIdentifier>>,
58
59 symbol_references_to_symbols_in_signature: HashMap<SymbolIdentifier, HashSet<SymbolIdentifier>>,
62
63 symbol_references_to_overridden_members: HashMap<SymbolIdentifier, HashSet<SymbolIdentifier>>,
66
67 functionlike_references_to_functionlike_returns: HashMap<FunctionLikeIdentifier, HashSet<FunctionLikeIdentifier>>,
70}
71
72impl SymbolReferences {
73 #[inline]
75 pub fn new() -> Self {
76 Self {
77 symbol_references_to_symbols: HashMap::default(),
78 symbol_references_to_symbols_in_signature: HashMap::default(),
79 symbol_references_to_overridden_members: HashMap::default(),
80 functionlike_references_to_functionlike_returns: HashMap::default(),
81 }
82 }
83
84 #[inline]
93 pub fn add_symbol_reference_to_class_member(
94 &mut self,
95 referencing_symbol: Atom,
96 class_member: SymbolIdentifier,
97 in_signature: bool,
98 ) {
99 self.add_symbol_reference_to_symbol(referencing_symbol, class_member.0, false);
101
102 let key = (referencing_symbol, empty_atom());
104 if in_signature {
105 self.symbol_references_to_symbols_in_signature.entry(key).or_default().insert(class_member);
106 } else {
107 self.symbol_references_to_symbols.entry(key).or_default().insert(class_member);
108 }
109 }
110
111 #[inline]
120 pub fn add_symbol_reference_to_symbol(&mut self, referencing_symbol: Atom, symbol: Atom, in_signature: bool) {
121 if referencing_symbol == symbol {
122 return;
123 }
124
125 let referencing_key = (referencing_symbol, empty_atom());
127 let referenced_key = (symbol, empty_atom());
128
129 if in_signature {
130 self.symbol_references_to_symbols_in_signature.entry(referencing_key).or_default().insert(referenced_key);
131 } else {
132 if let Some(sig_refs) = self.symbol_references_to_symbols_in_signature.get(&referencing_key)
134 && sig_refs.contains(&referenced_key)
135 {
136 return;
137 }
138 self.symbol_references_to_symbols.entry(referencing_key).or_default().insert(referenced_key);
139 }
140 }
141
142 #[inline]
152 pub fn add_class_member_reference_to_class_member(
153 &mut self,
154 referencing_class_member: SymbolIdentifier,
155 class_member: SymbolIdentifier,
156 in_signature: bool,
157 ) {
158 if referencing_class_member == class_member {
159 return;
160 }
161
162 self.add_symbol_reference_to_symbol(referencing_class_member.0, class_member.0, false);
164 self.add_class_member_reference_to_symbol(referencing_class_member, class_member.0, false);
165
166 if in_signature {
168 self.symbol_references_to_symbols_in_signature
169 .entry(referencing_class_member)
170 .or_default()
171 .insert(class_member);
172 } else {
173 self.symbol_references_to_symbols.entry(referencing_class_member).or_default().insert(class_member);
176 }
177 }
178
179 #[inline]
189 pub fn add_class_member_reference_to_symbol(
190 &mut self,
191 referencing_class_member: SymbolIdentifier,
192 symbol: Atom,
193 in_signature: bool,
194 ) {
195 if referencing_class_member.0 == symbol {
196 return;
197 }
198
199 self.add_symbol_reference_to_symbol(referencing_class_member.0, symbol, false);
201
202 let referenced_key = (symbol, empty_atom());
204
205 if in_signature {
206 self.symbol_references_to_symbols_in_signature
207 .entry(referencing_class_member)
208 .or_default()
209 .insert(referenced_key);
210 } else {
211 if let Some(sig_refs) = self.symbol_references_to_symbols_in_signature.get(&referencing_class_member)
213 && sig_refs.contains(&referenced_key)
214 {
215 return;
216 }
217 self.symbol_references_to_symbols.entry(referencing_class_member).or_default().insert(referenced_key);
218 }
219 }
220
221 #[inline]
224 pub fn add_reference_to_class_member(
225 &mut self,
226 scope: &ScopeContext<'_>,
227 class_member: SymbolIdentifier,
228 in_signature: bool,
229 ) {
230 if let Some(referencing_functionlike) = scope.get_function_like_identifier() {
231 match referencing_functionlike {
232 FunctionLikeIdentifier::Function(function_name) => {
233 self.add_symbol_reference_to_class_member(function_name, class_member, in_signature)
234 }
235 FunctionLikeIdentifier::Method(class_name, function_name) => self
236 .add_class_member_reference_to_class_member(
237 (class_name, function_name),
238 class_member,
239 in_signature,
240 ),
241 _ => {
242 }
244 }
245 } else if let Some(calling_class) = scope.get_class_like_name() {
246 self.add_symbol_reference_to_class_member(ascii_lowercase_atom(&calling_class), class_member, in_signature)
248 }
249 }
251
252 #[inline]
253 pub fn add_reference_for_method_call(&mut self, scope: &ScopeContext<'_>, method: &MethodIdentifier) {
254 self.add_reference_to_class_member(scope, (*method.get_class_name(), *method.get_method_name()), false);
255 }
256
257 #[inline]
258 pub fn add_reference_for_property_access(
259 &mut self,
260 scope: &ScopeContext<'_>,
261 class_name: Atom,
262 property_name: Atom,
263 ) {
264 self.add_reference_to_class_member(scope, (class_name, property_name), false);
265 }
266
267 #[inline]
270 pub fn add_reference_to_overridden_class_member(&mut self, scope: &ScopeContext, class_member: SymbolIdentifier) {
271 let referencing_key = if let Some(referencing_functionlike) = scope.get_function_like_identifier() {
272 match referencing_functionlike {
273 FunctionLikeIdentifier::Function(function_name) => (empty_atom(), function_name),
274 FunctionLikeIdentifier::Method(class_name, function_name) => (class_name, function_name),
275 _ => {
276 return;
278 }
279 }
280 } else if let Some(calling_class) = scope.get_class_like_name() {
281 (ascii_lowercase_atom(&calling_class), empty_atom())
282 } else {
283 return; };
285
286 self.symbol_references_to_overridden_members.entry(referencing_key).or_default().insert(class_member);
287 }
288
289 #[inline]
292 pub fn add_reference_to_symbol(&mut self, scope: &ScopeContext, symbol: Atom, in_signature: bool) {
293 if let Some(referencing_functionlike) = scope.get_function_like_identifier() {
294 match referencing_functionlike {
295 FunctionLikeIdentifier::Function(function_name) => {
296 self.add_symbol_reference_to_symbol(function_name, symbol, in_signature)
297 }
298 FunctionLikeIdentifier::Method(class_name, function_name) => {
299 self.add_class_member_reference_to_symbol((class_name, function_name), symbol, in_signature)
300 }
301 _ => {
302 }
304 }
305 } else if let Some(calling_class) = scope.get_class_like_name() {
306 self.add_symbol_reference_to_symbol(ascii_lowercase_atom(&calling_class), symbol, in_signature)
307 }
308 }
309
310 #[inline]
312 pub fn add_reference_to_functionlike_return(
313 &mut self,
314 referencing_functionlike: FunctionLikeIdentifier,
315 referenced_functionlike: FunctionLikeIdentifier,
316 ) {
317 if referencing_functionlike == referenced_functionlike {
318 return;
319 }
320
321 self.functionlike_references_to_functionlike_returns
322 .entry(referencing_functionlike)
323 .or_default()
324 .insert(referenced_functionlike);
325 }
326
327 #[inline]
330 pub fn extend(&mut self, other: Self) {
331 for (k, v) in other.symbol_references_to_symbols {
332 self.symbol_references_to_symbols.entry(k).or_default().extend(v);
333 }
334 for (k, v) in other.symbol_references_to_symbols_in_signature {
335 self.symbol_references_to_symbols_in_signature.entry(k).or_default().extend(v);
336 }
337 for (k, v) in other.symbol_references_to_overridden_members {
338 self.symbol_references_to_overridden_members.entry(k).or_default().extend(v);
339 }
340 for (k, v) in other.functionlike_references_to_functionlike_returns {
341 self.functionlike_references_to_functionlike_returns.entry(k).or_default().extend(v);
342 }
343 }
344
345 #[inline]
352 pub fn get_referenced_symbols_and_members(&self) -> HashSet<&SymbolIdentifier> {
353 let mut referenced_items = HashSet::default();
354 for refs in self.symbol_references_to_symbols.values() {
355 referenced_items.extend(refs.iter());
356 }
357 for refs in self.symbol_references_to_symbols_in_signature.values() {
358 referenced_items.extend(refs.iter());
359 }
360
361 referenced_items
362 }
363
364 #[inline]
371 pub fn get_back_references(&self) -> HashMap<SymbolIdentifier, HashSet<SymbolIdentifier>> {
372 let mut back_refs: HashMap<SymbolIdentifier, HashSet<SymbolIdentifier>> = HashMap::default();
373
374 for (referencing_item, referenced_items) in &self.symbol_references_to_symbols {
375 for referenced_item in referenced_items {
376 back_refs.entry(*referenced_item).or_default().insert(*referencing_item);
377 }
378 }
379 for (referencing_item, referenced_items) in &self.symbol_references_to_symbols_in_signature {
380 for referenced_item in referenced_items {
381 back_refs.entry(*referenced_item).or_default().insert(*referencing_item);
382 }
383 }
384 back_refs
385 }
386
387 #[inline]
398 pub fn get_references_to_symbol(&self, target_symbol: SymbolIdentifier) -> HashSet<&SymbolIdentifier> {
399 let mut referencing_items = HashSet::default();
400 for (referencing_item, referenced_items) in &self.symbol_references_to_symbols {
401 if referenced_items.contains(&target_symbol) {
402 referencing_items.insert(referencing_item);
403 }
404 }
405 for (referencing_item, referenced_items) in &self.symbol_references_to_symbols_in_signature {
406 if referenced_items.contains(&target_symbol) {
407 referencing_items.insert(referencing_item);
408 }
409 }
410 referencing_items
411 }
412
413 #[inline]
420 pub fn get_referenced_symbols_and_members_with_counts(&self) -> HashMap<SymbolIdentifier, u32> {
421 let mut counts = HashMap::default();
422 for referenced_items in self.symbol_references_to_symbols.values() {
423 for referenced_item in referenced_items {
424 *counts.entry(*referenced_item).or_insert(0) += 1;
425 }
426 }
427 for referenced_items in self.symbol_references_to_symbols_in_signature.values() {
428 for referenced_item in referenced_items {
429 *counts.entry(*referenced_item).or_insert(0) += 1;
430 }
431 }
432 counts
433 }
434
435 #[inline]
442 pub fn get_referenced_overridden_class_members(&self) -> HashMap<SymbolIdentifier, HashSet<SymbolIdentifier>> {
443 let mut back_refs: HashMap<SymbolIdentifier, HashSet<SymbolIdentifier>> = HashMap::default();
444
445 for (referencing_item, referenced_items) in &self.symbol_references_to_overridden_members {
446 for referenced_item in referenced_items {
447 back_refs.entry(*referenced_item).or_default().insert(*referencing_item);
448 }
449 }
450 back_refs
451 }
452
453 #[inline]
468 pub fn get_invalid_symbols(&self, codebase_diff: &CodebaseDiff) -> Option<(HashSet<SymbolIdentifier>, AtomSet)> {
469 let mut invalid_signatures = HashSet::default();
470 let mut partially_invalid_symbols = AtomSet::default();
471
472 for sig_ref_key in self.symbol_references_to_symbols_in_signature.keys() {
473 let containing_symbol = (sig_ref_key.0, empty_atom());
475
476 if codebase_diff.contains_add_or_delete_entry(&containing_symbol) {
477 invalid_signatures.insert(*sig_ref_key);
478 partially_invalid_symbols.insert(sig_ref_key.0);
479 }
480 }
481
482 let mut symbols_to_process = codebase_diff.get_add_or_delete().iter().copied().collect::<Vec<_>>();
484 let mut processed_symbols = HashSet::default();
485 let mut expense_counter = 0;
486
487 const EXPENSE_LIMIT: usize = 5000;
488 while let Some(invalidated_item) = symbols_to_process.pop() {
489 if processed_symbols.contains(&invalidated_item) {
490 continue;
491 }
492
493 expense_counter += 1;
494 if expense_counter > EXPENSE_LIMIT {
495 return None;
496 }
497
498 invalid_signatures.insert(invalidated_item);
500 processed_symbols.insert(invalidated_item);
501 if !invalidated_item.1.is_empty() {
502 partially_invalid_symbols.insert(invalidated_item.0);
504 }
505
506 for (referencing_item, referenced_items) in &self.symbol_references_to_symbols_in_signature {
508 if referenced_items.contains(&invalidated_item) {
509 if !processed_symbols.contains(referencing_item) {
511 symbols_to_process.push(*referencing_item);
512 }
513
514 invalid_signatures.insert(*referencing_item);
516 if !referencing_item.1.is_empty() {
517 partially_invalid_symbols.insert(referencing_item.0);
519 }
520 }
521 }
522
523 if expense_counter > EXPENSE_LIMIT {
525 return None;
526 }
527 }
528
529 let mut invalid_bodies = HashSet::default();
532
533 for (referencing_item, referenced_items) in &self.symbol_references_to_symbols {
535 if referenced_items.iter().any(|r| invalid_signatures.contains(r)) {
537 invalid_bodies.insert(*referencing_item);
538 if !referencing_item.1.is_empty() {
539 partially_invalid_symbols.insert(referencing_item.0);
541 }
542 }
543 }
544
545 for (referencing_item, referenced_items) in &self.symbol_references_to_symbols_in_signature {
549 if referenced_items.iter().any(|r| invalid_signatures.contains(r)) {
550 invalid_bodies.insert(*referencing_item);
551 if !referencing_item.1.is_empty() {
552 partially_invalid_symbols.insert(referencing_item.0);
553 }
554 }
555 }
556
557 for keep_sig_item in codebase_diff.get_keep_signature() {
559 invalid_bodies.insert(*keep_sig_item);
560 if !keep_sig_item.1.is_empty() {
561 partially_invalid_symbols.insert(keep_sig_item.0);
562 }
563 }
564
565 Some((invalid_signatures, partially_invalid_symbols))
568 }
569
570 #[inline]
576 pub fn remove_references_from_invalid_symbols(&mut self, invalid_symbols_and_members: &HashSet<SymbolIdentifier>) {
577 self.symbol_references_to_symbols
579 .retain(|referencing_item, _| !invalid_symbols_and_members.contains(referencing_item));
580 self.symbol_references_to_symbols_in_signature
581 .retain(|referencing_item, _| !invalid_symbols_and_members.contains(referencing_item));
582 self.symbol_references_to_overridden_members
583 .retain(|referencing_item, _| !invalid_symbols_and_members.contains(referencing_item));
584 }
585
586 #[inline]
588 pub fn get_symbol_references_to_symbols(&self) -> &HashMap<SymbolIdentifier, HashSet<SymbolIdentifier>> {
589 &self.symbol_references_to_symbols
590 }
591
592 #[inline]
594 pub fn get_symbol_references_to_symbols_in_signature(
595 &self,
596 ) -> &HashMap<SymbolIdentifier, HashSet<SymbolIdentifier>> {
597 &self.symbol_references_to_symbols_in_signature
598 }
599
600 #[inline]
602 pub fn get_symbol_references_to_overridden_members(&self) -> &HashMap<SymbolIdentifier, HashSet<SymbolIdentifier>> {
603 &self.symbol_references_to_overridden_members
604 }
605
606 #[inline]
608 pub fn get_functionlike_references_to_functionlike_returns(
609 &self,
610 ) -> &HashMap<FunctionLikeIdentifier, HashSet<FunctionLikeIdentifier>> {
611 &self.functionlike_references_to_functionlike_returns
612 }
613}