Skip to content

Commit ef3ceb4

Browse files
fix(perl-lsp): split qualified calls on last "::" so multi-level packages resolve
perl_resolve_function_call split a package-qualified call name on the FIRST "::" via strstr. For multi-level packages (Foo::Bar::sub) this yielded pkg "Foo" and a sub name still containing "::" ("Bar::sub"), which never resolved. The call then fell through to the generic bare-name fallback, which matches on the bare sub name only — collapsing distinct packages' same-named subs onto a single winner and orphaning the rest. Single-level calls (Foo::sub) were unaffected, which is why the existing test missed it. Split on the LAST "::" instead so the full package name reaches perl_lookup_method, which resolves to the correct fully-qualified sub. Adds perllsp_static_multilevel_package_call covering the multi-level form. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Shane McCarron <shane.mccarron@corvexconnect.com>
1 parent 4e5be9d commit ef3ceb4

2 files changed

Lines changed: 34 additions & 1 deletion

File tree

internal/cbm/lsp/perl_lsp.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,14 @@ static void perl_resolve_function_call(PerlLSPContext *ctx, TSNode call) {
610610
return;
611611

612612
const CBMRegisteredFunc *f = NULL;
613-
char *colons = strstr(name, "::");
613+
/* Split on the LAST "::" so multi-level packages keep their full name
614+
* (Foo::Bar::sub -> pkg "Foo::Bar", sub "sub"). strstr would stop at the
615+
* first "::", yielding pkg "Foo" and a sub name that still contains "::" —
616+
* that never resolves, so the call falls through to the bare-name fallback,
617+
* which collapses distinct packages' same-named subs onto one winner. */
618+
char *colons = NULL;
619+
for (char *p = strstr(name, "::"); p; p = strstr(p + 2, "::"))
620+
colons = p;
614621
if (colons) {
615622
size_t plen = (size_t)(colons - name);
616623
char *pkg = cbm_arena_strndup(ctx->arena, name, plen);

tests/test_perl_lsp.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,31 @@ TEST(perllsp_static_package_call) {
142142
PASS();
143143
}
144144

145+
/* ── 3b. Multi-level static package call (Foo::Bar::sub()) ──────── */
146+
/* Regression: the resolver split the qualified name on the FIRST "::", so a
147+
* call to Foo::Bar::sub() was mis-parsed as pkg "Foo" / sub "Bar::sub" and
148+
* never resolved — falling through to the bare-name fallback, which collapsed
149+
* distinct packages' same-named subs onto one winner. Splitting on the LAST
150+
* "::" keeps the full package name so perl_lookup_method resolves correctly. */
151+
TEST(perllsp_static_multilevel_package_call) {
152+
const char *src = "package Acme::Widget;\n"
153+
"sub render { return 1; }\n"
154+
"package main;\n"
155+
"sub caller_sub {\n"
156+
" Acme::Widget::render();\n"
157+
"}\n";
158+
CBMFileResult *r = extract_perl(src);
159+
ASSERT(r);
160+
/* Before the fix this edge was absent (LSP emitted nothing for the
161+
* multi-level qualified call). */
162+
const CBMResolvedCall *rc =
163+
find_resolved_with_strategy(r, "main.caller_sub", "main.render", "perl_static_call");
164+
ASSERT(rc != NULL);
165+
ASSERT(rc->strategy != NULL);
166+
cbm_free_result(r);
167+
PASS();
168+
}
169+
145170
/* ── 4. $self method dispatch ($self = shift) ──────────────────── */
146171

147172
TEST(perllsp_self_method) {
@@ -353,6 +378,7 @@ SUITE(perl_lsp) {
353378
RUN_TEST(perllsp_method_via_bless_assignment);
354379
RUN_TEST(perllsp_constructor_class_method);
355380
RUN_TEST(perllsp_static_package_call);
381+
RUN_TEST(perllsp_static_multilevel_package_call);
356382
RUN_TEST(perllsp_self_method);
357383
RUN_TEST(perllsp_isa_inheritance);
358384
RUN_TEST(perllsp_use_parent_inheritance);

0 commit comments

Comments
 (0)