fix(resolution): a call that is a braceless if/while/for/do body is a bare call (#2592)

A receiver-less JS/TS call written as the body of a braceless if, while,
for or do, like `if (e.key === "Escape") setOpen(false);`, is now read
as a bare call. In the JS family a receiver ends in `.` or `?.`, so a
`)` right before a call's name can only close a statement header; `do`
joins the bare-call keywords. Go keeps its old reading. Before, such a
call skipped the bare-call guards and could link to another file's
same-named member, like a context interface's `setOpen` property.

Fixes issue 2585.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Eric Minish
2026-10-20 05:08:42 +00:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 18248d191e
commit 8beab37c46
3 changed files with 145 additions and 2 deletions
+5 -1
View File
@@ -3473,6 +3473,10 @@ function isReceiverLessCall(ref: UnresolvedRef, context: ResolutionContext): boo
let end = Math.min(ref.column, line.length);
while (end > 0 && WHITESPACE.test(line[end - 1]!)) end--;
if (end === 0 || !RECEIVER_TAIL_CHAR.test(line[end - 1]!)) return true;
// In JS/TS a receiver ends in `.` or `?.`, so a `)` right before the name
// closes a statement header — `if (open) setOpen()`, `for (…) f()`, a header
// that wraps lines or holds a `)` in a string — and the call is bare (#2585).
if (line[end - 1] === ')' && JS_FAMILY.has(ref.language)) return true;
let start = end;
while (start > 0 && WORD_CHAR.test(line[start - 1]!)) start--;
return BARE_CALL_KEYWORDS.has(line.slice(start, end));
@@ -3490,7 +3494,7 @@ const RECEIVER_TAIL_CHAR = /[.\w$\])]/;
*/
const BARE_CALL_KEYWORDS: ReadonlySet<string> = new Set([
'return', 'await', 'yield', 'typeof', 'void', 'new', 'else', 'case', 'throw', 'in', 'of', 'instanceof', 'go', 'defer',
'if', 'switch', 'for', 'range',
'if', 'switch', 'for', 'range', 'do',
]);
/**