fix(go): a local's scope ends at its case clause, and a func-typed result's parameters are not locals (#2597)

Main's gate for a Go call to a local func value or parameter that
shadows a package function (PR 2658) reads scopes through the Go scope
reader, and two of its readings were too wide. A name declared in one
case of a switch or select stayed in scope in the clauses after it, and
the parameter names inside a func-typed result, like
func() func(run string), *func(...) or chan<- func(...), were read as
the function's own. Either made a later call to the package function
look shadowed and drop its link.

The PR's own earlier shadowing check is left out: the gate already
covers every case its tests pin. Go A/B on gin, cobra, etcd, prometheus
and hugo is byte-identical.

Fixes issue 2580.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Eric Minish
2026-10-20 17:06:31 +00:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 23ff26be68
commit e3f23cc4bc
3 changed files with 105 additions and 4 deletions
+5 -3
View File
@@ -2743,9 +2743,10 @@ function readGoScopes(code: string): GoScopeIndex {
}
}
// Each `func`'s receiver, parameters and results, bound in its body. A
// `[]func(…)` or `chan func(…)` is a type, whatever brace follows it.
// `[]func(…)` or `chan func(…)` is a type, whatever brace follows it, and
// so is a `func(…)` result type after a signature's `)` (#2580).
for (const m of code.matchAll(/(?<![\w.])func\b/g)) {
if (/(?:\]|\bchan)[ \t]*$/.test(code.slice(Math.max(0, m.index! - 8), m.index!))) continue;
if (/(?:[\]*)]|\bchan(?:[ \t]*<-)?)[ \t]*$/.test(code.slice(Math.max(0, m.index! - 8), m.index!))) continue;
const sig = goSignature(code, m.index! + 4);
if (!sig) continue;
for (const [from, to] of sig.groups) {
@@ -2928,7 +2929,8 @@ function goScopesOf(index: GoScopeIndex, name: string): Array<[number, number, G
}
const from = goStatementEnd(index.code, d.at);
let to = block[1];
if (d.kind === 'case') {
// A `case` clause's names, header or body, end at the next clause.
{
const clause = /\n[ \t]*(?:case\b|default[ \t]*:)/g;
clause.lastIndex = from;
for (let m = clause.exec(index.code); m && m.index < to; m = clause.exec(index.code)) {