From 009b7a8aeea2fbe03937a1cc2ecef8db640f3cf1 Mon Sep 17 00:00:00 2001 From: Martin Vogel Date: Sun, 28 Jun 2026 04:49:43 +0200 Subject: [PATCH] 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 --- internal/cbm/lsp/c_lsp.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/internal/cbm/lsp/c_lsp.c b/internal/cbm/lsp/c_lsp.c index be91bdcaa..3f130e909 100644 --- a/internal/cbm/lsp/c_lsp.c +++ b/internal/cbm/lsp/c_lsp.c @@ -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 &&