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

Function merge_traits_into

src/inheritance.rs:801–1069  ·  view source on GitHub ↗

Recursively merge members from the given traits into `merged`. Traits can themselves `use` other traits (composition), so this function recurses up to `MAX_TRAIT_DEPTH` levels. Members that already exist in `merged` (by name) are skipped — this naturally implements the PHP precedence rule where the current class's own members win over trait members, and earlier-listed traits win over later ones.

(
    merged: &mut ClassInfo,
    trait_names: &[Atom],
    ctx: &TraitContext<'_>,
    class_loader: &dyn Fn(&str) -> Option<Arc<ClassInfo>>,
    depth: u32,
    dedup: &mut MergeDedup,
    self_clas

Source from the content-addressed store, hash-verified

799/// `@template T`, the inherited methods and properties have their
800/// template parameter types replaced with the concrete types.
801fn merge_traits_into(
802 merged: &mut ClassInfo,
803 trait_names: &[Atom],
804 ctx: &TraitContext<'_>,
805 class_loader: &dyn Fn(&str) -> Option<Arc<ClassInfo>>,
806 depth: u32,
807 dedup: &mut MergeDedup,
808 self_class_name: &str,
809) {
810 if depth > MAX_TRAIT_DEPTH {
811 return;
812 }
813
814 for trait_name in trait_names {
815 let trait_info = if let Some(t) = class_loader(trait_name) {
816 t
817 } else {
818 continue;
819 };
820
821 // Build a substitution map for this trait if the using class
822 // declared `@use TraitName<Type1, Type2>` and the trait has
823 // `@template` parameters.
824 let mut trait_subs =
825 build_trait_substitution_map(trait_name, &trait_info, ctx.use_generics);
826
827 // ── Convention-based HasFactory fallback ─────────────────
828 // When a model uses `HasFactory` without `@use HasFactory<X>`,
829 // derive the factory class from the naming convention
830 // (e.g. `App\Models\User` → `Database\Factories\UserFactory`)
831 // and substitute `TFactory` automatically.
832 if trait_subs.is_empty()
833 && !trait_info.template_params.is_empty()
834 && is_has_factory_trait(trait_name)
835 && extends_eloquent_model(merged, class_loader)
836 {
837 let model_fqn = merged.fqn();
838 let factory_fqn = model_to_factory_fqn(&model_fqn);
839 if class_loader(&factory_fqn).is_some() {
840 for param in &trait_info.template_params {
841 trait_subs.insert(param.to_string(), PhpType::Named(factory_fqn.clone()));
842 }
843 }
844 }
845
846 // ── Template bound fallback ─────────────────────────────
847 // When a class uses a generic trait without `@use` generics
848 // and no convention-based provider filled the map, fall back
849 // to the template parameter bounds (e.g. `@template T of object`
850 // → `object`) so inherited methods don't leak raw template names.
851 if !trait_info.template_params.is_empty() {
852 for param_name in &trait_info.template_params {
853 if !trait_subs.contains_key(param_name.to_string().as_str()) {
854 let bound = trait_info
855 .template_param_bounds
856 .get(param_name)
857 .cloned()
858 .unwrap_or_else(PhpType::mixed);

Callers 1

Calls 15

is_has_factory_traitFunction · 0.85
extends_eloquent_modelFunction · 0.85
model_to_factory_fqnFunction · 0.85
atomFunction · 0.85
fqnMethod · 0.80
cloneMethod · 0.80
as_strMethod · 0.80
pushMethod · 0.80
iterMethod · 0.80

Tested by

no test coverage detected