Skip to content

Qualified cross-file calls collapse onto one namespace when the bare symbol name exists in multiple packages #478

@halindrome

Description

@halindrome

Summary

When a call is package/namespace-qualified (e.g. Perl Foo::Bar::run(...), or any ::/.-qualified callee) and the bare symbol name is defined in more than one package, the generic call resolver (cbm_registry_resolve in src/pipeline/registry.c) collapses every such call onto a single package, orphaning the others.

This makes trace_path / impact-analysis unreliable for any multiply-defined symbol: callers route to the wrong package, and the losing definitions look like dead code (in_degree 0) when they are not.

Root cause

resolve_name_lookup reduces the callee to its bare simple name (simple_name()), looks it up in the by-name index, and — when several candidates share that name — picks one via best_by_import_distance (namespace-proximity scoring). The package qualifier present in the call expression is discarded before this step, so all of Foo::Bar::run, Foo::Baz::run, Foo::Qux::run resolve to the same winner.

This is most visible for Perl, which has no cross-file LSP resolver and relies entirely on this generic registry chain, but the defect applies to any qualified callee whose bare name is non-unique.

Reproduction (shape)

package Foo::Bar;  sub run { ... }
package Foo::Baz;  sub run { ... }

# in another file:
Foo::Bar::run();   # both currently resolve to ONE of the two
Foo::Baz::run();   # packages; the other is orphaned

Observed: only one *::run definition receives inbound CALLS edges; the others get zero.
Expected: each fully-qualified call resolves to the package named in the call.

Proposed fix

Before the bare-name scorer collapses multiple candidates, disambiguate a qualified callee by matching its full qualified tail (normalizing ::.) against each candidate's qualified name at a segment boundary. If exactly one candidate matches, resolve to it; otherwise fall through to existing behavior. Language-agnostic — bare callees contain no separator and are unaffected.

PR to follow.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions