fix(compiler-core): identifiers in switch-case should not be inferred as references (#13923)

This commit is contained in:
Tony Wang 2025-09-24 21:33:48 +08:00 committed by GitHub
parent 565741a9b2
commit 5953c9ff90
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 89 additions and 7 deletions

View File

@ -716,4 +716,42 @@ describe('compiler: expression transform', () => {
})
})
})
describe('switch case variable declarations', () => {
test('should handle const declarations in switch case without braces', () => {
const { code } = compile(
`{{ (() => { switch (1) { case 1: const foo = "bar"; return \`\${foo}\`; } })() }}`,
)
expect(code).toMatch(`const foo = "bar";`)
expect(code).toMatch(`return \`\${foo}\`;`)
expect(code).not.toMatch(`_ctx.foo`)
})
test('should handle const declarations in switch case with braces (existing behavior)', () => {
const { code } = compile(
`{{ (() => {
switch (true) {
case true: {
const foo = "bar";
return \`\${foo}\`;
}
}
})() }}`,
)
expect(code).toMatch(`const foo = "bar";`)
expect(code).toMatch(`return \`\${foo}\`;`)
expect(code).not.toMatch(`_ctx.foo`)
})
test('should parse switch case test as local scoped variables', () => {
const { code } = compile(
`{{ (() => { switch (foo) { case bar: return \`\${bar}\`; } })() }}`,
)
expect(code).toMatch('_ctx.foo')
expect(code).toMatch(`_ctx.bar`)
})
})
})

View File

@ -10,6 +10,8 @@ import type {
Node,
ObjectProperty,
Program,
SwitchCase,
SwitchStatement,
} from '@babel/types'
import { walk } from 'estree-walker'
@ -80,14 +82,31 @@ export function walkIdentifiers(
markScopeIdentifier(node, id, knownIds),
)
}
} else if (node.type === 'SwitchStatement') {
if (node.scopeIds) {
node.scopeIds.forEach(id => markKnownIds(id, knownIds))
} else {
// record switch case block-level local variables
walkSwitchStatement(node, false, id =>
markScopeIdentifier(node, id, knownIds),
)
}
} else if (node.type === 'CatchClause' && node.param) {
for (const id of extractIdentifiers(node.param)) {
markScopeIdentifier(node, id, knownIds)
if (node.scopeIds) {
node.scopeIds.forEach(id => markKnownIds(id, knownIds))
} else {
for (const id of extractIdentifiers(node.param)) {
markScopeIdentifier(node, id, knownIds)
}
}
} else if (isForStatement(node)) {
walkForStatement(node, false, id =>
markScopeIdentifier(node, id, knownIds),
)
if (node.scopeIds) {
node.scopeIds.forEach(id => markKnownIds(id, knownIds))
} else {
walkForStatement(node, false, id =>
markScopeIdentifier(node, id, knownIds),
)
}
}
},
leave(node: Node & { scopeIds?: Set<string> }, parent: Node | null) {
@ -187,10 +206,11 @@ export function walkFunctionParams(
}
export function walkBlockDeclarations(
block: BlockStatement | Program,
block: BlockStatement | SwitchCase | Program,
onIdent: (node: Identifier) => void,
): void {
for (const stmt of block.body) {
const body = block.type === 'SwitchCase' ? block.consequent : block.body
for (const stmt of body) {
if (stmt.type === 'VariableDeclaration') {
if (stmt.declare) continue
for (const decl of stmt.declarations) {
@ -206,6 +226,8 @@ export function walkBlockDeclarations(
onIdent(stmt.id)
} else if (isForStatement(stmt)) {
walkForStatement(stmt, true, onIdent)
} else if (stmt.type === 'SwitchStatement') {
walkSwitchStatement(stmt, true, onIdent)
}
}
}
@ -239,6 +261,28 @@ function walkForStatement(
}
}
function walkSwitchStatement(
stmt: SwitchStatement,
isVar: boolean,
onIdent: (id: Identifier) => void,
) {
for (const cs of stmt.cases) {
for (const stmt of cs.consequent) {
if (
stmt.type === 'VariableDeclaration' &&
(stmt.kind === 'var' ? isVar : !isVar)
) {
for (const decl of stmt.declarations) {
for (const id of extractIdentifiers(decl.id)) {
onIdent(id)
}
}
}
}
walkBlockDeclarations(cs, onIdent)
}
}
export function extractIdentifiers(
param: Node,
nodes: Identifier[] = [],