From f8a7e8b715fac68a2ac9038dbb42823c87a9ca94 Mon Sep 17 00:00:00 2001 From: Martin Vogel Date: Sat, 27 Jun 2026 01:40:41 +0200 Subject: [PATCH] fix(lsp): join method-dispatch resolutions by bare callee short-name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The LSP-resolution join compared the LSP's class-qualified callee short-name (e.g. "inc" from "Counter.inc") against the call's RAW callee_name, which is receiver-qualified for method/qualified calls ("c.inc", "A.Helper", "c.Add"). That comparison always missed for method dispatch, so every type-aware LSP resolution (lsp_type_dispatch, cs_static_typed, lsp_method_dispatch, ...) was silently dropped and the call fell through to the weaker textual registry resolver — the LSP strategy never reached the CALLS edge's properties_json. Reduce the call's callee_name to its bare last dot-separated segment before the comparison, mirroring what is already done to callee_qn. The LSP has already performed receiver->type resolution, so matching bare method names is correct; free-function calls (already bare) are unaffected. This lets the more accurate type-aware LSP resolution win over textual registry matching for method calls. Reproduced by the repro_lsp_* per-pass strategy suites (java/cs/go/rust/kotlin/ php/ts), which asserted the LSP strategy is present on a CALLS edge and were RED. Signed-off-by: Martin Vogel --- src/pipeline/lsp_resolve.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/pipeline/lsp_resolve.h b/src/pipeline/lsp_resolve.h index 85facee81..6eba980d0 100644 --- a/src/pipeline/lsp_resolve.h +++ b/src/pipeline/lsp_resolve.h @@ -67,7 +67,17 @@ static inline const CBMResolvedCall *cbm_pipeline_find_lsp_resolution( } const char *short_name = strrchr(rc->callee_qn, '.'); short_name = short_name ? short_name + SKIP_ONE : rc->callee_qn; - if (strcmp(short_name, call->callee_name) != 0) { + /* The call's callee_name is receiver-qualified for method/qualified + * calls ("c.inc", "A.Helper"); the LSP records the resolved + * class-qualified callee_qn ("Class.inc"). Compare the bare last + * segment on BOTH sides so method-dispatch resolutions join — the LSP + * already did the receiver->type resolution, and matching the full + * "c.inc" against "inc" would always miss, silently dropping the + * type-aware LSP strategy to the weaker textual registry. Free-function + * calls (bare callee_name) are unaffected. */ + const char *call_short = strrchr(call->callee_name, '.'); + call_short = call_short ? call_short + SKIP_ONE : call->callee_name; + if (strcmp(short_name, call_short) != 0) { continue; } if (!best || rc->confidence > best->confidence) {