Find all references to a class/interface/trait/enum across all files. Matches `ClassReference` spans whose resolved FQN equals `target_fqn`, and optionally `ClassDeclaration` spans at the declaration site.
(&self, target_fqn: &str, include_declaration: bool)
| 606 | /// Matches `ClassReference` spans whose resolved FQN equals `target_fqn`, |
| 607 | /// and optionally `ClassDeclaration` spans at the declaration site. |
| 608 | fn find_class_references(&self, target_fqn: &str, include_declaration: bool) -> Vec<Location> { |
| 609 | let mut locations = Vec::new(); |
| 610 | |
| 611 | // Normalise: strip leading backslash if present. |
| 612 | let target = strip_fqn_prefix(target_fqn); |
| 613 | let target_short = crate::util::short_name(target); |
| 614 | |
| 615 | // Snapshot user-file symbol maps (excludes vendor and stubs). |
| 616 | let snapshot = self.user_file_symbol_maps(); |
| 617 | |
| 618 | for (file_uri, symbol_map) in &snapshot { |
| 619 | // Prefer mago-names resolved_names for FQN resolution (byte-offset |
| 620 | // based, applies PHP's full name resolution rules). Falls back to |
| 621 | // the legacy use_map lazily for identifiers not tracked by |
| 622 | // mago-names (e.g. docblock-sourced references). |
| 623 | let resolved_names = self.resolved_names.read().get(file_uri).cloned(); |
| 624 | let file_namespace = self.first_file_namespace(file_uri); |
| 625 | let file_use_map = std::cell::OnceCell::new(); |
| 626 | |
| 627 | // First pass: resolved-name check to avoid unnecessary content work. |
| 628 | // Aliased imports (`use Foo as Bar; new Bar`) must still reach the |
| 629 | // full matching loop, because the textual span name is the alias. |
| 630 | let has_potential_match = symbol_map.spans.iter().any(|span| match &span.kind { |
| 631 | SymbolKind::ClassReference { name, .. } => { |
| 632 | if crate::util::short_name(name) == target_short { |
| 633 | true |
| 634 | } else { |
| 635 | let resolved = if let Some(fqn) = |
| 636 | resolved_names.as_ref().and_then(|rn| rn.get(span.start)) |
| 637 | { |
| 638 | fqn.to_string() |
| 639 | } else { |
| 640 | let use_map = file_use_map.get_or_init(|| { |
| 641 | self.file_imports |
| 642 | .read() |
| 643 | .get(file_uri) |
| 644 | .cloned() |
| 645 | .unwrap_or_default() |
| 646 | }); |
| 647 | Self::resolve_to_fqn(name, use_map, &file_namespace) |
| 648 | }; |
| 649 | class_names_match(strip_fqn_prefix(&resolved), target, target_short) |
| 650 | } |
| 651 | } |
| 652 | SymbolKind::ClassDeclaration { name } => { |
| 653 | include_declaration && name == target_short |
| 654 | } |
| 655 | SymbolKind::SelfStaticParent(ssp_kind) => *ssp_kind != SelfStaticParentKind::This, |
| 656 | _ => false, |
| 657 | }); |
| 658 | |
| 659 | if !has_potential_match { |
| 660 | continue; |
| 661 | } |
| 662 | |
| 663 | let parsed_uri = match Url::parse(file_uri) { |
| 664 | Ok(u) => u, |
| 665 | Err(_) => continue, |
no test coverage detected