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

    bugSomething isn't workingparsing/qualityGraph extraction bugs, false positives, missing edges

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions