mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-26 20:42:56 +08:00
fix(js,ts): a nested function takes calls to a name the file imports from a package
When a JavaScript or TypeScript file imports a name from outside the
repository, like import { resolve } from 'node:path', matchByExactName
returns no edge for a bare call of that name (the early return main added
for issue 2101). A call to a function declared inside the calling function
under that name, which shadows the import, lost its edge with it.
The candidates for such a call are now narrowed to the calling file's
declarations nested in a function or method (innerScopeShadows), instead
of giving up; a top-level one is the import's own binding. Lexical
reachability, applied later, keeps only the nested declarations visible
where the call is.
Our changes: tests for a sibling function's nested declaration and one
nested a level below the call, which neither may take the call (both hold
through the reachability rule), and for a call from a function nested in
the declaring one; the CHANGELOG bullet moved next to the JS/TS import
bullets, with credit.
Validation: the tests fail 2 of 6 on main and pass here. A/B against main
on 8 repos, kernel per arm: flask, gin, excalidraw, codegraph, pixijs,
pdf.js and three.js byte-identical; vite gains 1 right link, scanImports'
call to its nested scan() beside import { scan } from
'rolldown/experimental'. Index CPU
time on codegraph, test files full of vitest imports, within noise:
81.7 s to 81.9 s (median of 3, interleaved).
Original PR #1715 by @danusha2345.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
committed by
Colby Mchenry
co-authored by
Claude Opus 5.5
parent
0d46dba08f
commit
428b3c487d
@@ -11787,6 +11787,23 @@ function bindsAtSites(source: string, name: string, sites: LocalBindingSites): b
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The calling file's declarations of an out-of-repo imported name that sit
|
||||
* inside a function or method — only those can shadow the import. A top-level
|
||||
* one is the import's own binding (`request = require('supertest')`), not a
|
||||
* shadow of it.
|
||||
*/
|
||||
function innerScopeShadows(nodes: Node[], ref: UnresolvedRef, context: ResolutionContext): Node[] {
|
||||
const local = nodes.filter((n) => n.filePath === ref.filePath);
|
||||
if (local.length === 0) return local;
|
||||
const scopes = context.getNodesInFile(ref.filePath).filter((n) => n.kind === 'function' || n.kind === 'method');
|
||||
return local.filter((n) => scopes.some((s) =>
|
||||
s.id !== n.id &&
|
||||
s.startLine <= n.startLine &&
|
||||
(s.endLine ?? s.startLine) >= (n.endLine ?? n.startLine) &&
|
||||
(s.startLine < n.startLine || (s.endLine ?? s.startLine) > (n.endLine ?? n.startLine))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to resolve a reference by exact name match
|
||||
*/
|
||||
@@ -11814,14 +11831,16 @@ export function matchByExactName(
|
||||
/^[A-Za-z_$][\w$]*$/.test(ref.referenceName) && isOutOfRepoBinding(ref.referenceName, ref, context)) {
|
||||
return null;
|
||||
}
|
||||
// `import { useQuery } from '@tanstack/react-query'`: the call means the
|
||||
// package's, and no other file's same-named symbol. A declaration of the
|
||||
// name in this file's inner scope still shadows the import.
|
||||
let outOfRepoCall = false;
|
||||
if (bareJs) {
|
||||
const storeAction = matchJsStoreBindingCall(ref, context);
|
||||
if (storeAction) return storeAction;
|
||||
const returned = matchDestructuredCallResult(ref, context);
|
||||
if (returned) return returned;
|
||||
// `import { useQuery } from '@tanstack/react-query'`: the call means the
|
||||
// package's, and no same-named project symbol.
|
||||
if (isOutOfRepoBinding(ref.referenceName, ref, context)) return null;
|
||||
outOfRepoCall = isOutOfRepoBinding(ref.referenceName, ref, context);
|
||||
}
|
||||
// Every rule below judges one candidate on its own, so they run as ONE pass,
|
||||
// the kind/language checks before the ones that read source: a common name
|
||||
@@ -11833,7 +11852,9 @@ export function matchByExactName(
|
||||
// Read once, and only for a candidate in another file: it scans the file.
|
||||
let bareTypeName: boolean | undefined;
|
||||
const tsBareTypeName = (): boolean => (bareTypeName ??= isTsBareTypeName(ref, context));
|
||||
const sameName = context.getNodesByName(ref.referenceName);
|
||||
const sameName = outOfRepoCall
|
||||
? innerScopeShadows(context.getNodesByName(ref.referenceName), ref, context)
|
||||
: context.getNodesByName(ref.referenceName);
|
||||
// `NAME(...)` where NAME is a function-like macro somewhere in the project is
|
||||
// an expansion or a call to a same-named function — never the macro itself
|
||||
// (#1839), and never a type that happens to share the name (#2070: expat's
|
||||
|
||||
Reference in New Issue
Block a user