MCPcopy Create free account
hub / github.com/DeusData/codebase-memory-mcp / flatten_trait_into_class

Function flatten_trait_into_class

internal/cbm/lsp/php_lsp.c:3673–3746  ·  view source on GitHub ↗

Flatten the methods of a trait into the using class's method table. * * Tree-sitter-php emits `use Trait1, Trait2;` inside a class body as a * `use_declaration` (distinct from the namespace-level * namespace_use_declaration). The optional conflict resolution block * uses `use_instead_of_clause` (for `Trait1::foo insteadof Trait2`) and * `use_as_clause` (for `Trait2::foo as fooAlt`). * * Ph

Source from the content-addressed store, hash-verified

3671 * - skip insteadof (rare in practice; full support deferred)
3672 */
3673static void flatten_trait_into_class(PHPLSPContext *ctx, CBMTypeRegistry *reg, const char *class_qn,
3674 const char *trait_qn, const char *alias_for_method,
3675 const char *only_method_name) {
3676 if (!class_qn || !trait_qn)
3677 return;
3678 /* Resolve trait through alias-style lookup if needed. */
3679 const CBMRegisteredType *t = cbm_registry_lookup_type(reg, trait_qn);
3680 if (!t)
3681 t = lookup_type_with_project(ctx, trait_qn);
3682 const char *canonical_trait_qn = t ? t->qualified_name : trait_qn;
3683
3684 /* A trait cannot meaningfully flatten into itself. PHP itself rejects
3685 * `trait T { use T; }` ("Trait T cannot use itself"), and an aliased
3686 * `use X as Y; use Y;` can resolve back onto the enclosing trait by short
3687 * name. Without this guard the loop below copies the trait's own methods
3688 * back onto it with receiver_type == canonical_trait_qn; because each copy
3689 * then re-matches the loop's filter while cbm_registry_add_func() keeps
3690 * growing reg->func_count, the iteration never terminates — it arena-
3691 * allocates a fresh method every pass until the process exhausts all
3692 * memory (observed: 40 GB+, freezing the host). See regression test
3693 * phplsp_trait_self_use_terminates. */
3694 if (strcmp(class_qn, canonical_trait_qn) == 0)
3695 return;
3696
3697 /* Iterate registry funcs whose receiver_type is the trait.
3698 *
3699 * Self-substitution: when the trait's method has a return type that
3700 * names the trait itself (e.g. `tap(): self` registered as
3701 * NAMED(trait_qn)), rewrite it to NAMED(using_class_qn) so chains
3702 * like `$c->tap()->classMethod()` resolve correctly.
3703 *
3704 * Snapshot the count before iterating: cbm_registry_add_func() below
3705 * appends to reg->funcs, so the loop must never visit entries it adds
3706 * during iteration (a mutate-while-iterating hazard). */
3707 const int func_count_before = reg->func_count;
3708 for (int i = 0; i < func_count_before; i++) {
3709 const CBMRegisteredFunc *src = &reg->funcs[i];
3710 if (!src->receiver_type || strcmp(src->receiver_type, canonical_trait_qn) != 0) {
3711 continue;
3712 }
3713 if (only_method_name && src->short_name && strcmp(src->short_name, only_method_name) != 0) {
3714 continue;
3715 }
3716 const char *new_short = alias_for_method ? alias_for_method : src->short_name;
3717 if (!new_short)
3718 continue;
3719 CBMRegisteredFunc rf;
3720 memset(&rf, 0, sizeof(rf));
3721 rf.qualified_name = cbm_arena_sprintf(ctx->arena, "%s.%s", class_qn, new_short);
3722 rf.short_name = cbm_arena_strdup(ctx->arena, new_short);
3723 rf.receiver_type = class_qn;
3724 rf.min_params = src->min_params;
3725
3726 /* Self-substitute the return type: any NAMED(trait_qn) in the
3727 * signature's return becomes NAMED(class_qn). */
3728 const CBMType *new_sig = src->signature;
3729 if (src->signature && src->signature->kind == CBM_TYPE_FUNC &&
3730 src->signature->data.func.return_types) {

Callers 1

process_trait_useFunction · 0.85

Calls 8

cbm_registry_lookup_typeFunction · 0.85
lookup_type_with_projectFunction · 0.85
cbm_type_namedFunction · 0.85
cbm_registry_add_funcFunction · 0.85
cbm_arena_sprintfFunction · 0.50
cbm_arena_strdupFunction · 0.50
cbm_arena_allocFunction · 0.50

Tested by

no test coverage detected