Try to resolve a standalone function name to its definition. Searches the `global_functions` map (populated from autoload files, opened/changed files, and cached stub functions) for any of the given candidate names. If not found there, falls back to the embedded PHP stubs via `find_or_load_function` — which parses the stub lazily and caches it in `global_functions` for future lookups. When foun
(&self, candidates: &[String])
| 549 | /// are not navigable so they are skipped for go-to-definition but |
| 550 | /// still loaded into the cache for return-type resolution. |
| 551 | fn resolve_function_definition(&self, candidates: &[String]) -> Option<Location> { |
| 552 | // ── Step 1: Check global_functions (user code + cached stubs) ── |
| 553 | let found = { |
| 554 | let fmap = self.global_functions.read(); |
| 555 | let mut result = None; |
| 556 | for candidate in candidates { |
| 557 | if let Some((uri, info)) = fmap.get(candidate.as_str()) { |
| 558 | result = Some((uri.clone(), info.clone())); |
| 559 | break; |
| 560 | } |
| 561 | } |
| 562 | result |
| 563 | }; |
| 564 | |
| 565 | // ── Step 2: Try embedded PHP stubs as fallback ── |
| 566 | let (file_uri, func_info) = if let Some(pair) = found { |
| 567 | pair |
| 568 | } else { |
| 569 | // Build &str candidates for find_or_load_function. |
| 570 | let str_candidates: Vec<&str> = candidates.iter().map(|s| s.as_str()).collect(); |
| 571 | let loaded = self.find_or_load_function(&str_candidates)?; |
| 572 | |
| 573 | // After find_or_load_function, the function is cached in |
| 574 | // global_functions. Look it up to get the URI. |
| 575 | let fmap = self.global_functions.read(); |
| 576 | let mut result = None; |
| 577 | for candidate in candidates { |
| 578 | if let Some((uri, info)) = fmap.get(candidate.as_str()) { |
| 579 | result = Some((uri.clone(), info.clone())); |
| 580 | break; |
| 581 | } |
| 582 | } |
| 583 | result.unwrap_or_else(|| { |
| 584 | // Fallback: use a synthetic URI with the loaded info. |
| 585 | (format!("phpantom-stub-fn://{}", loaded.name), loaded) |
| 586 | }) |
| 587 | }; |
| 588 | |
| 589 | // Stub functions don't have real file locations — skip |
| 590 | // go-to-definition for them (they're still useful for return-type |
| 591 | // resolution via the function_loader). |
| 592 | if file_uri.starts_with("phpantom-stub-fn://") { |
| 593 | return None; |
| 594 | } |
| 595 | |
| 596 | // Read the file content (try open files first, then disk). |
| 597 | let file_content = self.get_file_content(&file_uri)?; |
| 598 | |
| 599 | // Use the stored byte offset. A name_offset of 0 means "not |
| 600 | // available" — return None in that case (should not happen for |
| 601 | // user code since the parser always sets the offset). |
| 602 | if func_info.name_offset == 0 { |
| 603 | return None; |
| 604 | } |
| 605 | let position = |
| 606 | crate::util::offset_to_position(&file_content, func_info.name_offset as usize); |
| 607 | let parsed_uri = Url::parse(&file_uri).ok()?; |
| 608 |
no test coverage detected