Skip to content

Renamed import used as a receiver (import { X as Y }; Y.method()) does not resolve — qualified-name fallback doesn't know about the rename #1825

Description

@carlos-alm

Summary

#1730 fixed call-edge resolution for renamed imports used as a bare call (import { collectFiles as collectFilesUtil } from './helpers'; collectFilesUtil()). While verifying that fix I found a distinct, still-broken variant: a renamed import used as a receiver (import { X as Y } from './mod'; Y.method()) does not resolve at all — no calls edge is created.

This is empirically confirmed (not speculative) — see repro below with raw DB inspection.

Repro

// helpers.js
export const NamespaceObj = {
  doThing() { return 1; },
};

// consumer.js
import { NamespaceObj as NsAlias } from './helpers.js';
export function useReceiver() {
  return NsAlias.doThing();
}

After codegraph build, querying edges shows useReceiver has no outgoing calls edge to doThing / NamespaceObj.doThing at all — the call is silently dropped. (Verified with sqlite3 directly against edges/nodes tables — useReceiver produces zero rows in edges.)

Root cause

This does not go through resolveCallTargets's import-aware branch (the one #1730 fixed) — doThing itself was never imported, only NsAlias was, so importedNames.get('doThing') is never populated. The call falls through to resolveByMethodOrGlobalresolveByReceiver (src/domain/graph/resolver/strategy.ts) → resolveViaDirectQualifiedMethod:

function resolveViaDirectQualifiedMethod(
  lookup: StrategyLookup,
  effectiveReceiver: string,
  call: { name: string },
  relPath: string,
): ReadonlyArray<{ id: number; file: string }> {
  const qualifiedName = `${effectiveReceiver}.${call.name}`;
  return lookup.byName(qualifiedName) ...
}

effectiveReceiver here is the literal call-site receiver text (NsAlias), so it builds the qualified name NsAlias.doThing — but the actual definition is stored as NamespaceObj.doThing (the object literal's original name). The lookup misses because resolveByReceiver/resolveViaDirectQualifiedMethod has no way to know NsAlias is a renamed alias for NamespaceObj.

I have not checked whether the native Rust resolver (crates/codegraph-core/src/domain/graph/builder/stages/build_edges.rs, the receiver/qualified-method resolution branch of resolve_call_targets) has the identical gap — likely, given the mirrored-engine convention, but not yet confirmed.

Expected

When a call's receiver is itself a renamed import local name, receiver-based qualified-method resolution should substitute the original exported name (the same Import.renamedImports / RenamedImport data #1730 introduced) before building the qualified lookup key, so NsAlias.doThing resolves via NamespaceObj.doThing.

Scope note

Deliberately not fixed as part of #1730 — different resolution strategy entirely (resolveByReceiver/resolveViaDirectQualifiedMethod in resolver/strategy.ts, not resolveCallTargets's import-aware branch), and #1730's own repro (a bare function call) doesn't exercise this path.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions