diff --git a/codegraph-kernel/src/tsjs/extractors.rs b/codegraph-kernel/src/tsjs/extractors.rs index 83f070b7..99fe0746 100644 --- a/codegraph-kernel/src/tsjs/extractors.rs +++ b/codegraph-kernel/src/tsjs/extractors.rs @@ -1174,14 +1174,6 @@ impl<'t> Walker<'t> { if is_literal_receiver(r.kind()) { return; } - // `holder.values.get()` has no inferred property type - // (#1566). Dropping the receiver or merely preserving it - // would allow unrelated same-name method guesses. Emit - // nothing, as for host chains (#1707); argument calls are - // visited independently. Mirrors extractCall in TS. - if self.is_unresolved_member_chain(r) { - return; - } } let recv_ident = receiver.filter(|r| { matches!(r.kind(), "identifier" | "simple_identifier" | "field_identifier") @@ -1193,6 +1185,12 @@ impl<'t> Walker<'t> { } else { callee_name = method_name.to_string(); } + } else if receiver.is_some_and(|r| self.is_unresolved_member_chain(r)) { + // Retain the call site for effects without guessing a + // project method. Mirrors the TS extraction path. + let chain = self.text(func).replace("?.", "."); + let Some(chain) = Self::plain_member_name(&chain) else { return }; + callee_name = chain; } else if let Some(field) = receiver.and_then(|r| self.this_field_of(r)) { // `this..()` — keep the field so the // resolver can read its declared type (#1496). Mirrors @@ -1245,7 +1243,11 @@ impl<'t> Walker<'t> { /// or member chain (`make`, `d.setdefault`), whitespace stripped (#1683). fn plain_inner_callee(&self, call: Node<'t>) -> Option { let inner = call.child_by_field_name("function")?; - let text: String = self.text(inner).chars().filter(|c| !c.is_whitespace()).collect(); + Self::plain_member_name(self.text(inner)) + } + + fn plain_member_name(source: &str) -> Option { + let text: String = source.chars().filter(|c| !c.is_whitespace()).collect(); if text.is_empty() { return None; }