mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-23 06:50:54 +08:00
fix(js,ts): a callee that names nothing records no reference, and a wrapped callee is the plain call (#2742)
A TS/JS call whose callee was no name and no member access (an IIFE, a curried `connect(mapState)(View)`, `handlers[type]()`, `(0, fn)()`, a tagged template on a call) stored the callee's whole source text as the reference's name. Nothing can answer such a name, so each one parked as a failed reference, an IIFE's entire body among them. extractCall now reads the callee past the wrappers that keep it the same callee (parentheses, `!`, `as` / `satisfies`, `await`, type arguments) and records nothing when that is not a name or a dotted chain; the calls inside are visited on their own. Mirrored in the kernel (tsjs/extractors.rs callee_name_of). The reference of a wrapped callee sits at its name, where a plain call's does, and the resolver's bare-call check reads `fn!(`, `(fn)(`, `(fn as F)(` and `(fn satisfies F)(` as it reads `fn(`: SigNoz's `(callback as FrameRequestCallback)(0)` and `(callback)(0)` name the arrow's own parameter, and from the `(` they bound a generated interface's `callback` property. tree-sitter-typescript parses a prefix operator over a generic call as it parses `await f<T>()` (#2586): `!settings.get<boolean>('X')` and `void apiRequest<T>()` are calls whose callee is `!settings.get` / `void apiRequest`. The callee peel that handled `await` now peels a prefix operator too (peelTsPrefixedCallee; the kernel's peel_await_callee), so the call is to `settings.get`, and `void apiRequest<T>().then()` encodes as `apiRequest().then`, not `voidapiRequest().then`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
aa01692620
commit
ae0fc631e0
@@ -22,6 +22,8 @@ re!(styled_callee, r"^styled\b");
|
||||
re!(pascal_case, r"^[A-Z]");
|
||||
// extractCall parenthesized-conversion normalization
|
||||
re!(paren_conversion, r"^\(\s*\*?\s*([A-Za-z_][\w.]*)\s*\)$");
|
||||
// tsJsCalleeName's dotted chain (`a.b.c`); ASCII classes, as JS's `\w` is
|
||||
re!(dotted_chain, r"^[A-Za-z_$][0-9A-Za-z_$]*(?:\.[A-Za-z_$][0-9A-Za-z_$]*)+$");
|
||||
// flushFnRefCandidates SIMPLE_NAME
|
||||
re!(simple_name, r"^[A-Za-z_$][A-Za-z0-9_$]*$");
|
||||
// flushFnRefCandidates QUALIFIED_IMPORT
|
||||
|
||||
@@ -1529,7 +1529,7 @@ impl<'t> Walker<'t> {
|
||||
// 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) {
|
||||
let mut at = match (raw, func) {
|
||||
(Some(r), Some(f)) if r.id() != f.id() => f,
|
||||
_ => node,
|
||||
};
|
||||
@@ -1596,7 +1596,15 @@ impl<'t> Walker<'t> {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
callee_name = self.text(func).to_string();
|
||||
// A callee that names nothing (a call, a function, a
|
||||
// subscript) is no reference. Mirrors tsJsCalleeName.
|
||||
match self.callee_name_of(func) {
|
||||
Some((name, name_at)) => {
|
||||
callee_name = name;
|
||||
at = name_at;
|
||||
}
|
||||
None => return,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1612,6 +1620,27 @@ impl<'t> Walker<'t> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The name a non-member callee writes, past `(fn)`, `fn!`, `(fn as F)`,
|
||||
/// `await` and a tag's type arguments (`css<Props>`), or None for one that
|
||||
/// names nothing: a call, a function, a subscript, `(0, fn)`. A dotted
|
||||
/// chain keeps its text, `(a.b)()` → `a.b`. The node is where the name
|
||||
/// starts, which is where the reference sits. Mirrors tsJsCalleeName.
|
||||
fn callee_name_of(&self, func: Node<'t>) -> Option<(String, Node<'t>)> {
|
||||
let mut cur = peel_receiver(func);
|
||||
while cur.kind() == "instantiation_expression" {
|
||||
let inner = cur.child_by_field_name("function").or_else(|| cur.named_child(0))?;
|
||||
cur = peel_receiver(inner);
|
||||
}
|
||||
match cur.kind() {
|
||||
"identifier" | "super" | "import" => Some((self.text(cur).to_string(), cur)),
|
||||
"member_expression" => {
|
||||
let chain: String = self.text(cur).chars().filter(|c| !c.is_whitespace()).collect();
|
||||
util::dotted_chain().is_match(&chain).then_some((chain, cur))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// --- extractInstantiation -----------------------------------------------------------
|
||||
|
||||
/// `this.<field>` as a member_expression receiver → Some(field) (#1496, #1987).
|
||||
@@ -1630,17 +1659,27 @@ 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.
|
||||
/// `await f<T>(x)` parses with `await f` as the callee (#2586), and a
|
||||
/// prefix operator over a generic call the same way
|
||||
/// (`!settings.get<boolean>('X')`, `void apiRequest<T>()`); the call is to
|
||||
/// `f`. Peels every `await` and prefix operator, skipping a comment written
|
||||
/// after an `await`. TS-family only. Mirrors peelTsPrefixedCallee.
|
||||
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 };
|
||||
loop {
|
||||
let inner = match cur.kind() {
|
||||
"unary_expression" => cur.child_by_field_name("argument"),
|
||||
"await_expression" => {
|
||||
let mut walker = cur.walk();
|
||||
let found = cur.named_children(&mut walker).find(|c| c.kind() != "comment");
|
||||
found
|
||||
}
|
||||
_ => break,
|
||||
};
|
||||
let Some(inner) = inner else { break };
|
||||
cur = inner;
|
||||
}
|
||||
cur
|
||||
|
||||
Reference in New Issue
Block a user