MCPcopy Create free account
hub / github.com/PHPantom-dev/phpantom_lsp / resolve_class_fully_inner

Function resolve_class_fully_inner

src/virtual_members/mod.rs:756–1074  ·  view source on GitHub ↗

Shared implementation behind [`resolve_class_fully`] and [`resolve_class_fully_cached`].

(
    class: &ClassInfo,
    class_loader: &dyn Fn(&str) -> Option<Arc<ClassInfo>>,
    cache: Option<&ResolvedClassCache>,
    generic_args: &[String],
)

Source from the content-addressed store, hash-verified

754/// Shared implementation behind [`resolve_class_fully`] and
755/// [`resolve_class_fully_cached`].
756fn resolve_class_fully_inner(
757 class: &ClassInfo,
758 class_loader: &dyn Fn(&str) -> Option<Arc<ClassInfo>>,
759 cache: Option<&ResolvedClassCache>,
760 generic_args: &[String],
761) -> Arc<ClassInfo> {
762 let fqn = class.fqn();
763 let cache_key: ResolvedClassCacheKey = (fqn, generic_args.to_vec());
764
765 // ── Reload raw class from class_loader ──────────────────────────
766 // Callers sometimes pass a ClassInfo that has already been through
767 // `apply_generic_args` (e.g. `MockBuilder<Rule>` where template
768 // parameters are substituted with concrete types). If we resolve
769 // from that substituted class and cache the result under the bare
770 // FQN key `(MockBuilder, [])`, every subsequent lookup gets the
771 // contaminated version — causing non-deterministic diagnostics
772 // depending on which thread/file was processed first (B28).
773 //
774 // To prevent this, always try to reload the raw (un-substituted)
775 // class from the class_loader. The class_loader returns the
776 // parsed ClassInfo from uri_classes_index/stubs, which has unsubstituted
777 // template parameters. Fall back to the passed-in `class` only
778 // when the loader cannot find it (e.g. anonymous classes).
779 let raw_class_arc = class_loader(fqn.as_str());
780 let effective_class: &ClassInfo = match &raw_class_arc {
781 Some(raw) => raw,
782 None => class,
783 };
784
785 // ── Cache lookup ────────────────────────────────────────────────
786 if let Some(cache) = cache {
787 let map = cache.lock();
788 if let Some(cached) = map.get(&cache_key) {
789 return Arc::clone(cached);
790 }
791 }
792
793 // ── Recursion guard ─────────────────────────────────────────────
794 // If this class is already being resolved on this thread (re-entrant
795 // call from a virtual member provider or interface merge), or we have
796 // exceeded the maximum nesting depth, return a partial result with
797 // base inheritance only. This breaks the mutual recursion that
798 // causes stack overflow.
799 let depth = RESOLVE_DEPTH.with(|d| d.get());
800 if depth >= MAX_RESOLVE_DEPTH || !mark_resolving(&fqn) {
801 // Already resolving or too deep — return base-only resolution.
802 return Arc::new(resolve_class_with_inheritance(
803 effective_class,
804 class_loader,
805 ));
806 }
807 RESOLVE_DEPTH.with(|d| d.set(depth + 1));
808
809 // ── Uncached resolution ─────────────────────────────────────────
810 let mut merged = resolve_class_with_inheritance(effective_class, class_loader);
811
812 // ── Pre-provider patches ────────────────────────────────────────
813 // Inject missing `@mixin` annotations before virtual member

Callers 5

populate_from_sortedFunction · 0.85
resolve_class_fullyFunction · 0.85

Calls 15

mark_resolvingFunction · 0.85
atomFunction · 0.85
default_providersFunction · 0.85
apply_virtual_membersFunction · 0.85
apply_laravel_patchesFunction · 0.85
unmark_resolvingFunction · 0.85
fqnMethod · 0.80
as_strMethod · 0.80
setMethod · 0.80
containsMethod · 0.80

Tested by

no test coverage detected