Find all references to a function across all files.
(
&self,
target_fqn: &str,
target_short: &str,
include_declaration: bool,
)
| 968 | |
| 969 | /// Find all references to a function across all files. |
| 970 | fn find_function_references( |
| 971 | &self, |
| 972 | target_fqn: &str, |
| 973 | target_short: &str, |
| 974 | include_declaration: bool, |
| 975 | ) -> Vec<Location> { |
| 976 | let mut locations = Vec::new(); |
| 977 | |
| 978 | // Input boundary: callers may pass FQNs with a leading `\`. |
| 979 | let target = strip_fqn_prefix(target_fqn); |
| 980 | |
| 981 | let snapshot = self.user_file_symbol_maps(); |
| 982 | |
| 983 | for (file_uri, symbol_map) in &snapshot { |
| 984 | // Prefer mago-names resolved_names; lazy-load use_map only |
| 985 | // when an offset is not tracked (e.g. docblock references). |
| 986 | let resolved_names = self.resolved_names.read().get(file_uri).cloned(); |
| 987 | let file_namespace = self.first_file_namespace(file_uri); |
| 988 | let file_use_map = std::cell::OnceCell::new(); |
| 989 | |
| 990 | // First pass: resolved-name check. Function imports can be aliased |
| 991 | // (`use function Foo\bar as baz; baz()`), so the call-site text |
| 992 | // alone is not enough to decide whether this file can match. |
| 993 | let has_potential_match = symbol_map.spans.iter().any(|span| { |
| 994 | if let SymbolKind::FunctionCall { name, .. } = &span.kind { |
| 995 | if name == target_short { |
| 996 | true |
| 997 | } else { |
| 998 | let resolved = if let Some(fqn) = |
| 999 | resolved_names.as_ref().and_then(|rn| rn.get(span.start)) |
| 1000 | { |
| 1001 | fqn.to_string() |
| 1002 | } else { |
| 1003 | let use_map = file_use_map.get_or_init(|| { |
| 1004 | self.file_imports |
| 1005 | .read() |
| 1006 | .get(file_uri) |
| 1007 | .cloned() |
| 1008 | .unwrap_or_default() |
| 1009 | }); |
| 1010 | Self::resolve_to_fqn(name, use_map, &file_namespace) |
| 1011 | }; |
| 1012 | let resolved_normalized = strip_fqn_prefix(&resolved); |
| 1013 | resolved_normalized == target |
| 1014 | || crate::util::short_name(resolved_normalized) == target_short |
| 1015 | } |
| 1016 | } else { |
| 1017 | false |
| 1018 | } |
| 1019 | }); |
| 1020 | |
| 1021 | if !has_potential_match { |
| 1022 | continue; |
| 1023 | } |
| 1024 | |
| 1025 | let parsed_uri = match Url::parse(file_uri) { |
| 1026 | Ok(u) => u, |
| 1027 | Err(_) => continue, |
no test coverage detected