1use ahash::HashMap;
2use ahash::HashSet;
3use serde::Deserialize;
4use serde::Serialize;
5
6use mago_interner::StringIdentifier;
7
8use crate::context::ScopeContext;
9use crate::diff::CodebaseDiff;
10use crate::identifier::function_like::FunctionLikeIdentifier;
11use crate::identifier::method::MethodIdentifier;
12use crate::symbol::SymbolIdentifier;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
17pub enum ReferenceSource {
18 Symbol(bool, StringIdentifier),
22 ClassLikeMember(bool, StringIdentifier, StringIdentifier),
27}
28
29#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
32pub struct InvalidSymbols {
33 invalid_symbol_and_member_signatures: HashSet<SymbolIdentifier>,
36 invalid_symbol_and_member_bodies: HashSet<SymbolIdentifier>,
39 partially_invalid_symbols: HashSet<StringIdentifier>,
42}
43
44#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)] pub struct SymbolReferences {
51 symbol_references_to_symbols: HashMap<SymbolIdentifier, HashSet<SymbolIdentifier>>,
55
56 symbol_references_to_symbols_in_signature: HashMap<SymbolIdentifier, HashSet<SymbolIdentifier>>,
59
60 symbol_references_to_overridden_members: HashMap<SymbolIdentifier, HashSet<SymbolIdentifier>>,
63
64 functionlike_references_to_functionlike_returns: HashMap<FunctionLikeIdentifier, HashSet<FunctionLikeIdentifier>>,
67}
68
69impl SymbolReferences {
70 #[inline]
72 pub fn new() -> Self {
73 Self {
74 symbol_references_to_symbols: HashMap::default(),
75 symbol_references_to_symbols_in_signature: HashMap::default(),
76 symbol_references_to_overridden_members: HashMap::default(),
77 functionlike_references_to_functionlike_returns: HashMap::default(),
78 }
79 }
80
81 #[inline]
90 pub fn add_symbol_reference_to_class_member(
91 &mut self,
92 referencing_symbol: StringIdentifier,
93 class_member: SymbolIdentifier,
94 in_signature: bool,
95 ) {
96 self.add_symbol_reference_to_symbol(referencing_symbol, class_member.0, false);
98
99 let key = (referencing_symbol, StringIdentifier::empty());
101 if in_signature {
102 self.symbol_references_to_symbols_in_signature.entry(key).or_default().insert(class_member);
103 } else {
104 self.symbol_references_to_symbols.entry(key).or_default().insert(class_member);
105 }
106 }
107
108 #[inline]
117 pub fn add_symbol_reference_to_symbol(
118 &mut self,
119 referencing_symbol: StringIdentifier,
120 symbol: StringIdentifier,
121 in_signature: bool,
122 ) {
123 if referencing_symbol == symbol {
124 return;
125 }
126
127 let referencing_key = (referencing_symbol, StringIdentifier::empty());
129 let referenced_key = (symbol, StringIdentifier::empty());
130
131 if in_signature {
132 self.symbol_references_to_symbols_in_signature.entry(referencing_key).or_default().insert(referenced_key);
133 } else {
134 if let Some(sig_refs) = self.symbol_references_to_symbols_in_signature.get(&referencing_key)
136 && sig_refs.contains(&referenced_key)
137 {
138 return;
139 }
140 self.symbol_references_to_symbols.entry(referencing_key).or_default().insert(referenced_key);
141 }
142 }
143
144 #[inline]
154 pub fn add_class_member_reference_to_class_member(
155 &mut self,
156 referencing_class_member: SymbolIdentifier,
157 class_member: SymbolIdentifier,
158 in_signature: bool,
159 ) {
160 if referencing_class_member == class_member {
161 return;
162 }
163
164 self.add_symbol_reference_to_symbol(referencing_class_member.0, class_member.0, false);
166 self.add_class_member_reference_to_symbol(referencing_class_member, class_member.0, false);
167
168 if in_signature {
170 self.symbol_references_to_symbols_in_signature
171 .entry(referencing_class_member)
172 .or_default()
173 .insert(class_member);
174 } else {
175 self.symbol_references_to_symbols.entry(referencing_class_member).or_default().insert(class_member);
178 }
179 }
180
181 #[inline]
191 pub fn add_class_member_reference_to_symbol(
192 &mut self,
193 referencing_class_member: SymbolIdentifier,
194 symbol: StringIdentifier,
195 in_signature: bool,
196 ) {
197 if referencing_class_member.0 == symbol {
198 return;
199 }
200
201 self.add_symbol_reference_to_symbol(referencing_class_member.0, symbol, false);
203
204 let referenced_key = (symbol, StringIdentifier::empty());
206
207 if in_signature {
208 self.symbol_references_to_symbols_in_signature
209 .entry(referencing_class_member)
210 .or_default()
211 .insert(referenced_key);
212 } else {
213 if let Some(sig_refs) = self.symbol_references_to_symbols_in_signature.get(&referencing_class_member)
215 && sig_refs.contains(&referenced_key)
216 {
217 return;
218 }
219 self.symbol_references_to_symbols.entry(referencing_class_member).or_default().insert(referenced_key);
220 }
221 }
222
223 #[inline]
226 pub fn add_reference_to_class_member(
227 &mut self,
228 scope: &ScopeContext<'_>,
229 class_member: SymbolIdentifier,
230 in_signature: bool,
231 ) {
232 if let Some(referencing_functionlike) = scope.get_function_like_identifier() {
233 match referencing_functionlike {
234 FunctionLikeIdentifier::Function(function_name) => {
235 self.add_symbol_reference_to_class_member(function_name, class_member, in_signature)
236 }
237 FunctionLikeIdentifier::Method(class_name, function_name) => self
238 .add_class_member_reference_to_class_member(
239 (class_name, function_name),
240 class_member,
241 in_signature,
242 ),
243 _ => {
244 }
246 }
247 } else if let Some(calling_class) = scope.get_class_like_name() {
248 self.add_symbol_reference_to_class_member(*calling_class, class_member, in_signature)
250 }
251 }
253
254 #[inline]
255 pub fn add_reference_for_method_call(&mut self, scope: &ScopeContext<'_>, method: &MethodIdentifier) {
256 self.add_reference_to_class_member(scope, (*method.get_class_name(), *method.get_method_name()), false);
257 }
258
259 #[inline]
260 pub fn add_reference_for_property_access(
261 &mut self,
262 scope: &ScopeContext<'_>,
263 class_name: StringIdentifier,
264 property_name: StringIdentifier,
265 ) {
266 self.add_reference_to_class_member(scope, (class_name, property_name), false);
267 }
268
269 #[inline]
272 pub fn add_reference_to_overridden_class_member(&mut self, scope: &ScopeContext, class_member: SymbolIdentifier) {
273 let referencing_key = if let Some(referencing_functionlike) = scope.get_function_like_identifier() {
274 match referencing_functionlike {
275 FunctionLikeIdentifier::Function(function_name) => (StringIdentifier::empty(), function_name),
276 FunctionLikeIdentifier::Method(class_name, function_name) => (class_name, function_name),
277 _ => {
278 return;
280 }
281 }
282 } else if let Some(calling_class) = scope.get_class_like_name() {
283 (*calling_class, StringIdentifier::empty())
284 } else {
285 return; };
287
288 self.symbol_references_to_overridden_members.entry(referencing_key).or_default().insert(class_member);
289 }
290
291 #[inline]
294 pub fn add_reference_to_symbol(&mut self, scope: &ScopeContext, symbol: StringIdentifier, in_signature: bool) {
295 if let Some(referencing_functionlike) = scope.get_function_like_identifier() {
296 match referencing_functionlike {
297 FunctionLikeIdentifier::Function(function_name) => {
298 self.add_symbol_reference_to_symbol(function_name, symbol, in_signature)
299 }
300 FunctionLikeIdentifier::Method(class_name, function_name) => {
301 self.add_class_member_reference_to_symbol((class_name, function_name), symbol, in_signature)
302 }
303 _ => {
304 }
306 }
307 } else if let Some(calling_class) = scope.get_class_like_name() {
308 self.add_symbol_reference_to_symbol(*calling_class, symbol, in_signature)
309 }
310 }
311
312 #[inline]
314 pub fn add_reference_to_functionlike_return(
315 &mut self,
316 referencing_functionlike: FunctionLikeIdentifier,
317 referenced_functionlike: FunctionLikeIdentifier,
318 ) {
319 if referencing_functionlike == referenced_functionlike {
320 return;
321 }
322
323 self.functionlike_references_to_functionlike_returns
324 .entry(referencing_functionlike)
325 .or_default()
326 .insert(referenced_functionlike);
327 }
328
329 #[inline]
332 pub fn extend(&mut self, other: Self) {
333 for (k, v) in other.symbol_references_to_symbols {
334 self.symbol_references_to_symbols.entry(k).or_default().extend(v);
335 }
336 for (k, v) in other.symbol_references_to_symbols_in_signature {
337 self.symbol_references_to_symbols_in_signature.entry(k).or_default().extend(v);
338 }
339 for (k, v) in other.symbol_references_to_overridden_members {
340 self.symbol_references_to_overridden_members.entry(k).or_default().extend(v);
341 }
342 for (k, v) in other.functionlike_references_to_functionlike_returns {
343 self.functionlike_references_to_functionlike_returns.entry(k).or_default().extend(v);
344 }
345 }
346
347 #[inline]
354 pub fn get_referenced_symbols_and_members(&self) -> HashSet<&SymbolIdentifier> {
355 let mut referenced_items = HashSet::default();
356 for refs in self.symbol_references_to_symbols.values() {
357 referenced_items.extend(refs.iter());
358 }
359 for refs in self.symbol_references_to_symbols_in_signature.values() {
360 referenced_items.extend(refs.iter());
361 }
362
363 referenced_items
364 }
365
366 #[inline]
373 pub fn get_back_references(&self) -> HashMap<SymbolIdentifier, HashSet<SymbolIdentifier>> {
374 let mut back_refs: HashMap<SymbolIdentifier, HashSet<SymbolIdentifier>> = HashMap::default();
375
376 for (referencing_item, referenced_items) in &self.symbol_references_to_symbols {
377 for referenced_item in referenced_items {
378 back_refs.entry(*referenced_item).or_default().insert(*referencing_item);
379 }
380 }
381 for (referencing_item, referenced_items) in &self.symbol_references_to_symbols_in_signature {
382 for referenced_item in referenced_items {
383 back_refs.entry(*referenced_item).or_default().insert(*referencing_item);
384 }
385 }
386 back_refs
387 }
388
389 #[inline]
400 pub fn get_references_to_symbol(&self, target_symbol: SymbolIdentifier) -> HashSet<&SymbolIdentifier> {
401 let mut referencing_items = HashSet::default();
402 for (referencing_item, referenced_items) in &self.symbol_references_to_symbols {
403 if referenced_items.contains(&target_symbol) {
404 referencing_items.insert(referencing_item);
405 }
406 }
407 for (referencing_item, referenced_items) in &self.symbol_references_to_symbols_in_signature {
408 if referenced_items.contains(&target_symbol) {
409 referencing_items.insert(referencing_item);
410 }
411 }
412 referencing_items
413 }
414
415 #[inline]
422 pub fn get_referenced_symbols_and_members_with_counts(&self) -> HashMap<SymbolIdentifier, u32> {
423 let mut counts = HashMap::default();
424 for referenced_items in self.symbol_references_to_symbols.values() {
425 for referenced_item in referenced_items {
426 *counts.entry(*referenced_item).or_insert(0) += 1;
427 }
428 }
429 for referenced_items in self.symbol_references_to_symbols_in_signature.values() {
430 for referenced_item in referenced_items {
431 *counts.entry(*referenced_item).or_insert(0) += 1;
432 }
433 }
434 counts
435 }
436
437 #[inline]
444 pub fn get_referenced_overridden_class_members(&self) -> HashMap<SymbolIdentifier, HashSet<SymbolIdentifier>> {
445 let mut back_refs: HashMap<SymbolIdentifier, HashSet<SymbolIdentifier>> = HashMap::default();
446
447 for (referencing_item, referenced_items) in &self.symbol_references_to_overridden_members {
448 for referenced_item in referenced_items {
449 back_refs.entry(*referenced_item).or_default().insert(*referencing_item);
450 }
451 }
452 back_refs
453 }
454
455 #[inline]
470 pub fn get_invalid_symbols(
471 &self,
472 codebase_diff: &CodebaseDiff,
473 ) -> Option<(HashSet<SymbolIdentifier>, HashSet<StringIdentifier>)> {
474 let mut invalid_signatures = HashSet::default();
475 let mut partially_invalid_symbols = HashSet::default();
476
477 for sig_ref_key in self.symbol_references_to_symbols_in_signature.keys() {
478 let containing_symbol = (sig_ref_key.0, StringIdentifier::empty());
480
481 if codebase_diff.contains_add_or_delete_entry(&containing_symbol) {
482 invalid_signatures.insert(*sig_ref_key);
483 partially_invalid_symbols.insert(sig_ref_key.0);
484 }
485 }
486
487 let mut symbols_to_process = codebase_diff.get_add_or_delete().iter().copied().collect::<Vec<_>>();
489 let mut processed_symbols = HashSet::default();
490 let mut expense_counter = 0;
491
492 const EXPENSE_LIMIT: usize = 5000;
493 while let Some(invalidated_item) = symbols_to_process.pop() {
494 if processed_symbols.contains(&invalidated_item) {
495 continue;
496 }
497
498 expense_counter += 1;
499 if expense_counter > EXPENSE_LIMIT {
500 return None;
501 }
502
503 invalid_signatures.insert(invalidated_item);
505 processed_symbols.insert(invalidated_item);
506 if !invalidated_item.1.is_empty() {
507 partially_invalid_symbols.insert(invalidated_item.0);
509 }
510
511 for (referencing_item, referenced_items) in &self.symbol_references_to_symbols_in_signature {
513 if referenced_items.contains(&invalidated_item) {
514 if !processed_symbols.contains(referencing_item) {
516 symbols_to_process.push(*referencing_item);
517 }
518
519 invalid_signatures.insert(*referencing_item);
521 if !referencing_item.1.is_empty() {
522 partially_invalid_symbols.insert(referencing_item.0);
524 }
525 }
526 }
527
528 if expense_counter > EXPENSE_LIMIT {
530 return None;
531 }
532 }
533
534 let mut invalid_bodies = HashSet::default();
537
538 for (referencing_item, referenced_items) in &self.symbol_references_to_symbols {
540 if referenced_items.iter().any(|r| invalid_signatures.contains(r)) {
542 invalid_bodies.insert(*referencing_item);
543 if !referencing_item.1.is_empty() {
544 partially_invalid_symbols.insert(referencing_item.0);
546 }
547 }
548 }
549
550 for (referencing_item, referenced_items) in &self.symbol_references_to_symbols_in_signature {
554 if referenced_items.iter().any(|r| invalid_signatures.contains(r)) {
555 invalid_bodies.insert(*referencing_item);
556 if !referencing_item.1.is_empty() {
557 partially_invalid_symbols.insert(referencing_item.0);
558 }
559 }
560 }
561
562 for keep_sig_item in codebase_diff.get_keep_signature() {
564 invalid_bodies.insert(*keep_sig_item);
565 if !keep_sig_item.1.is_empty() {
566 partially_invalid_symbols.insert(keep_sig_item.0);
567 }
568 }
569
570 Some((invalid_signatures, partially_invalid_symbols))
573 }
574
575 #[inline]
581 pub fn remove_references_from_invalid_symbols(&mut self, invalid_symbols_and_members: &HashSet<SymbolIdentifier>) {
582 self.symbol_references_to_symbols
584 .retain(|referencing_item, _| !invalid_symbols_and_members.contains(referencing_item));
585 self.symbol_references_to_symbols_in_signature
586 .retain(|referencing_item, _| !invalid_symbols_and_members.contains(referencing_item));
587 self.symbol_references_to_overridden_members
588 .retain(|referencing_item, _| !invalid_symbols_and_members.contains(referencing_item));
589 }
590
591 #[inline]
593 pub fn get_symbol_references_to_symbols(&self) -> &HashMap<SymbolIdentifier, HashSet<SymbolIdentifier>> {
594 &self.symbol_references_to_symbols
595 }
596
597 #[inline]
599 pub fn get_symbol_references_to_symbols_in_signature(
600 &self,
601 ) -> &HashMap<SymbolIdentifier, HashSet<SymbolIdentifier>> {
602 &self.symbol_references_to_symbols_in_signature
603 }
604
605 #[inline]
607 pub fn get_symbol_references_to_overridden_members(&self) -> &HashMap<SymbolIdentifier, HashSet<SymbolIdentifier>> {
608 &self.symbol_references_to_overridden_members
609 }
610
611 #[inline]
613 pub fn get_functionlike_references_to_functionlike_returns(
614 &self,
615 ) -> &HashMap<FunctionLikeIdentifier, HashSet<FunctionLikeIdentifier>> {
616 &self.functionlike_references_to_functionlike_returns
617 }
618}