fix(c-lsp): tag a derived override of an inherited method as lsp_virtual_dispatch

A virtual method called on a concrete derived object (Derived d; d.speak(2),
where Derived overrides Base::speak) resolved directly to Derived.speak and was
emitted as lsp_type_dispatch. The existing virtual-dispatch path only fired when
a method was resolved THROUGH a base with a derived override; an override
resolved directly on the derived type was missed.

Extend the same notion (a derived override of an inherited method = polymorphic
dispatch) to the direct-resolution case: when a method is found directly on the
receiver type AND a base class also declares it, emit lsp_virtual_dispatch. This
mirrors the codebase's existing heuristic definition of virtual dispatch (an
override exists) rather than the textual 'virtual' keyword, so it stays
consistent. lsp_type_dispatch and lsp_base_dispatch fixtures are unaffected.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
Martin Vogel
2026-06-28 04:49:43 +02:00
parent 9ef37e89be
commit 009b7a8aee
+25
View File
@@ -2567,6 +2567,26 @@ const CBMRegisteredFunc *c_lookup_member(CLSPContext *ctx, const char *type_qn,
return c_lookup_member_depth(ctx, type_qn, member_name, 0);
}
// True if any BASE class of type_qn (not type_qn itself) declares member_name —
// i.e. a method found directly on type_qn is an OVERRIDE of an inherited method.
// This mirrors the existing virtual-dispatch notion (a derived override of a base
// method) for the case where the override is resolved directly on the derived
// type rather than through the base.
static bool c_base_declares_member(CLSPContext *ctx, const char *type_qn, const char *member_name) {
const CBMRegisteredType *rt = cbm_registry_lookup_type(ctx->registry, type_qn);
if (!rt && ctx->module_qn) {
rt = cbm_registry_lookup_type(
ctx->registry, cbm_arena_sprintf(ctx->arena, "%s.%s", ctx->module_qn, type_qn));
}
if (!rt || !rt->embedded_types)
return false;
for (int i = 0; rt->embedded_types[i]; i++) {
if (c_lookup_member(ctx, rt->embedded_types[i], member_name))
return true;
}
return false;
}
// Field type lookup
static const CBMType *c_lookup_field_type(CLSPContext *ctx, const char *type_qn,
const char *field_name, int depth) {
@@ -3413,6 +3433,11 @@ static void c_resolve_calls_in_node_inner(CLSPContext *ctx, TSNode node) {
} else {
strategy = "lsp_base_dispatch";
}
} else if (c_base_declares_member(ctx, type_qn, field_name)) {
// Method resolved directly on type_qn but also
// declared in a base → a derived override of an
// inherited (virtual) method → polymorphic dispatch.
strategy = "lsp_virtual_dispatch";
}
// Check if through smart pointer
if (is_arrow && obj_type->kind == CBM_TYPE_TEMPLATE &&