Find all classes that implement or extend the target. Scans: 1. All classes already in `uri_classes_index` (open files + autoload-discovered) 2. All classes loadable via `fqn_uri_index` 3. Class index files not yet loaded — string pre-filter then parse 4. Embedded PHP stubs — string pre-filter then lazy parse 5. User PSR-4 directories — walk for `.php` files not covered by the class index, string
(
&self,
target_short: &str,
target_fqn: &str,
class_loader: &dyn Fn(&str) -> Option<Arc<ClassInfo>>,
include_abstract: bool,
direct_only: bool,
)
| 616 | /// excluded. This mode is used by the type hierarchy protocol where |
| 617 | /// the client walks the tree one level at a time. |
| 618 | pub(crate) fn find_implementors( |
| 619 | &self, |
| 620 | target_short: &str, |
| 621 | target_fqn: &str, |
| 622 | class_loader: &dyn Fn(&str) -> Option<Arc<ClassInfo>>, |
| 623 | include_abstract: bool, |
| 624 | direct_only: bool, |
| 625 | ) -> Vec<ClassInfo> { |
| 626 | let mut result: Vec<ClassInfo> = Vec::new(); |
| 627 | // Track by FQN to avoid short-name collisions across namespaces. |
| 628 | let mut seen_fqns: HashSet<String> = HashSet::new(); |
| 629 | |
| 630 | // ── Phase 1: GTI index lookup ─────────────────────────────────── |
| 631 | // Use the reverse inheritance index for O(1) lookup of classes |
| 632 | // that directly extend/implement/use the target. Then |
| 633 | // recursively collect transitive children. |
| 634 | let gti_candidates: Vec<String> = { |
| 635 | let gti = self.gti_index.read(); |
| 636 | if direct_only { |
| 637 | gti.get(target_fqn).cloned().unwrap_or_default() |
| 638 | } else { |
| 639 | // Transitive: BFS collecting all descendants. |
| 640 | let mut all_children: Vec<String> = Vec::new(); |
| 641 | let mut queue: Vec<String> = vec![target_fqn.to_string()]; |
| 642 | let mut visited: HashSet<String> = HashSet::new(); |
| 643 | visited.insert(target_fqn.to_string()); |
| 644 | while let Some(parent) = queue.pop() { |
| 645 | if let Some(children) = gti.get(&parent) { |
| 646 | for child in children { |
| 647 | if visited.insert(child.clone()) { |
| 648 | all_children.push(child.clone()); |
| 649 | queue.push(child.clone()); |
| 650 | } |
| 651 | } |
| 652 | } |
| 653 | } |
| 654 | all_children |
| 655 | } |
| 656 | }; |
| 657 | |
| 658 | for child_fqn in >i_candidates { |
| 659 | if seen_fqns.contains(child_fqn) { |
| 660 | continue; |
| 661 | } |
| 662 | if let Some(cls) = class_loader(child_fqn) { |
| 663 | if !direct_only { |
| 664 | if cls.kind == ClassLikeKind::Interface { |
| 665 | continue; |
| 666 | } |
| 667 | if cls.is_abstract && !include_abstract { |
| 668 | continue; |
| 669 | } |
| 670 | } |
| 671 | seen_fqns.insert(child_fqn.clone()); |
| 672 | result.push(Arc::unwrap_or_clone(cls)); |
| 673 | } |
| 674 | } |
| 675 |
no test coverage detected