Repository navigation
Skip non-scope ancestors quickly in NameResolver.Resolve - #65017
Jake Bailey (jakebailey) wants to merge 1 commit into
Conversation
Resolve walks every ancestor of the reference location, and for each one it fetches locals, evaluates getIsDeferredContext, runs a large switch on the kind, and checks isSelfReferenceLocation. Most ancestors (expressions, statements, type nodes, and so on) are not scopes, so all of that work does nothing for them. Classify node kinds up front: kinds that the loop body never acts on are climbed past directly, and simple block-like scopes (blocks, loops, case blocks, catch clauses, mapped types) only need a plain lookup in their locals. Everything else, including any locals container not explicitly listed, still runs the full loop body, so resolution results are unchanged. A `const` name keeps using the full path since every ancestor must be checked for a const assertion. Co-authored-by: Steven Chong <25894545+teamchong@users.noreply.github.com>
|
TypeScript Bot (@typescript-bot) perf test this faster |
There was a problem hiding this comment.
🟢 Approval recommended
The ancestor-traversal optimization has no active review issues blocking approval.
No open issues 🎉
What changed in this PR
This PR optimizes ancestor traversal in the native compiler’s name resolver by skipping work for nodes that do not affect name resolution.
Changes:
- Classifies node kinds into skip, locals-only lookup, or full-resolution paths.
- Preserves full traversal for
constnames and special-case scopes. - Adds a kind-based helper for identifying locals containers.
| File | Description |
|---|---|
tsc/internal/binder/nameresolver.go |
Adds node-kind classification and fast paths for ancestor traversal. |
tsc/internal/ast/ast.go |
Adds IsLocalsContainerKind to support scope classification. |
Review effort: Balanced
|
Jake Bailey (@jakebailey) Here they are:
tscComparison Report - baseline..pr
System info unknown
Hosts
Scenarios
Developer Information: |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Ryan Cavanaugh (RyanCavanaugh)
left a comment
There was a problem hiding this comment.
Reviewed at 2545a54. The classification preserves full handling for special scopes, deferred contexts, and self references, updates lastLocation when skipping ancestors, and retains const-assertion traversal. Targeted compiler baselines covering parameter scope, arguments, computed/static members, decorators, infer constraints, and self references passed. No blocking issue found.
|
This also seems like virtually no difference... |
Resolve walks every ancestor of the reference location, and for each one it fetches locals, evaluates getIsDeferredContext, runs a large switch on the kind, and checks isSelfReferenceLocation. Most ancestors (expressions, statements, type nodes, and so on) are not scopes, so all of that work does nothing for them.
Classify node kinds up front: kinds that the loop body never acts on are climbed past directly, and simple block-like scopes (blocks, loops, case blocks, catch clauses, mapped types) only need a plain lookup in their locals. Everything else, including any locals container not explicitly listed, still runs the full loop body, so resolution results are unchanged. A
constname keeps using the full path since every ancestor must be checked for a const assertion.Thanks Steven (@teamchong) for finding this!