fix(ts): an awaited call with type arguments links to its callee (#2595)

tree-sitter-typescript parses `await f<T>(x)` as a call whose callee is
`await f`, so the reference was named `await f` and matched nothing.
Both engines now peel the await, skipping a comment after it, and the
reference sits where the callee starts, as for `await f(x)`: on the
call node, which starts at await, the checks that read the source at a
call's position missed the callee, and a package-imported
`await get<T>()` linked to a project class's static get.

A/B: excalidraw +2 (compressData, decompressData), vben +6, zod and
bulletproof-react unchanged.

Fixes issue 2586.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Eric Minish
2026-10-20 17:11:12 +00:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 63476df6a6
commit 8afb970a23
5 changed files with 154 additions and 8 deletions
+27 -5
View File
@@ -1398,9 +1398,15 @@ impl<'t> Walker<'t> {
if self.stack.is_empty() {
return;
}
let func = node
.child_by_field_name("function")
.or_else(|| node.named_child(0));
let raw = node.child_by_field_name("function").or_else(|| node.named_child(0));
let func = raw.map(|f| self.peel_await_callee(f));
// Where the reference sits: the call, or the callee an `await` was
// peeled off (`await f<T>(x)` starts at `await`; the call to `f`
// starts where `f` does, as in `await f(x)`). Mirrors refStart.
let at = match (raw, func) {
(Some(r), Some(f)) if r.id() != f.id() => f,
_ => node,
};
let mut callee_name = String::new();
if let Some(func) = func {
@@ -1476,7 +1482,7 @@ impl<'t> Walker<'t> {
}
if !callee_name.is_empty() {
self.push_call_ref(&callee_name.clone(), node);
self.push_call_ref(&callee_name.clone(), at);
}
}
@@ -1498,10 +1504,26 @@ impl<'t> Walker<'t> {
Some(self.text(property).to_string())
}
/// `await f<T>(x)` parses with `await f` as the callee; the call is to
/// `f` (#2586). Peels every `await`, skipping a comment written after one.
/// TS-family only. Mirrors peelTsAwaitCallee.
fn peel_await_callee(&self, node: Node<'t>) -> Node<'t> {
if !self.variant.is_ts() {
return node;
}
let mut cur = node;
while cur.kind() == "await_expression" {
let mut walker = cur.walk();
let Some(inner) = cur.named_children(&mut walker).find(|c| c.kind() != "comment") else { break };
cur = inner;
}
cur
}
/// The callee of a call-expression receiver when it is a plain identifier
/// or member chain (`make`, `d.setdefault`), whitespace stripped (#1683).
fn plain_inner_callee(&self, call: Node<'t>) -> Option<String> {
let inner = call.child_by_field_name("function")?;
let inner = self.peel_await_callee(call.child_by_field_name("function")?);
Self::plain_member_name(self.text(inner))
}