Skip to content

Commit 67bb820

Browse files
committed
fix(compiler-core): properly handle for loop variable declarations in expression transforms
ref #11467 (comment)
1 parent f3efff1 commit 67bb820

File tree

3 files changed

+78
-15
lines changed

3 files changed

+78
-15
lines changed

packages/compiler-core/__tests__/transforms/__snapshots__/transformExpressions.spec.ts.snap

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,21 @@ return function render(_ctx, _cache, $props, $setup, $data, $options) {
1414
}"
1515
`;
1616

17+
exports[`compiler: expression transform > should allow leak of var declarations in for loop 1`] = `
18+
"const { openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue
19+
20+
return function render(_ctx, _cache) {
21+
return (_openBlock(), _createElementBlock("div", {
22+
onClick: () => {
23+
for (var i = 0; i < _ctx.list.length; i++) {
24+
_ctx.log(i)
25+
}
26+
_ctx.error(i)
27+
}
28+
}, null, 8 /* PROPS */, ["onClick"]))
29+
}"
30+
`;
31+
1732
exports[`compiler: expression transform > should not prefix catch block param 1`] = `
1833
"const { openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue
1934
@@ -53,6 +68,7 @@ return function render(_ctx, _cache) {
5368
for (let i = 0; i < _ctx.list.length; i++) {
5469
_ctx.log(i)
5570
}
71+
_ctx.error(_ctx.i)
5672
}
5773
}, null, 8 /* PROPS */, ["onClick"]))
5874
}"
@@ -67,6 +83,7 @@ return function render(_ctx, _cache) {
6783
for (const x in _ctx.list) {
6884
_ctx.log(x)
6985
}
86+
_ctx.error(_ctx.x)
7087
}
7188
}, null, 8 /* PROPS */, ["onClick"]))
7289
}"
@@ -81,6 +98,7 @@ return function render(_ctx, _cache) {
8198
for (const x of _ctx.list) {
8299
_ctx.log(x)
83100
}
101+
_ctx.error(_ctx.x)
84102
}
85103
}, null, 8 /* PROPS */, ["onClick"]))
86104
}"

packages/compiler-core/__tests__/transforms/transformExpressions.spec.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -468,9 +468,11 @@ describe('compiler: expression transform', () => {
468468
for (const x in list) {
469469
log(x)
470470
}
471+
error(x)
471472
}"/>`,
472473
)
473-
expect(code).not.toMatch(`_ctx.x`)
474+
expect(code).not.toMatch(`log(_ctx.x)`)
475+
expect(code).toMatch(`error(_ctx.x)`)
474476
expect(code).toMatchSnapshot()
475477
})
476478

@@ -480,9 +482,11 @@ describe('compiler: expression transform', () => {
480482
for (const x of list) {
481483
log(x)
482484
}
485+
error(x)
483486
}"/>`,
484487
)
485-
expect(code).not.toMatch(`_ctx.x`)
488+
expect(code).not.toMatch(`log(_ctx.x)`)
489+
expect(code).toMatch(`error(_ctx.x)`)
486490
expect(code).toMatchSnapshot()
487491
})
488492

@@ -492,9 +496,25 @@ describe('compiler: expression transform', () => {
492496
for (let i = 0; i < list.length; i++) {
493497
log(i)
494498
}
499+
error(i)
495500
}"/>`,
496501
)
497-
expect(code).not.toMatch(`_ctx.i`)
502+
expect(code).not.toMatch(`log(_ctx.i)`)
503+
expect(code).toMatch(`error(_ctx.i)`)
504+
expect(code).toMatchSnapshot()
505+
})
506+
507+
test('should allow leak of var declarations in for loop', () => {
508+
const { code } = compile(
509+
`<div @click="() => {
510+
for (var i = 0; i < list.length; i++) {
511+
log(i)
512+
}
513+
error(i)
514+
}"/>`,
515+
)
516+
expect(code).not.toMatch(`log(_ctx.i)`)
517+
expect(code).not.toMatch(`error(_ctx.i)`)
498518
expect(code).toMatchSnapshot()
499519
})
500520

packages/compiler-core/src/babelUtils.ts

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
// do not import runtime methods
33
import type {
44
BlockStatement,
5+
ForInStatement,
6+
ForOfStatement,
7+
ForStatement,
58
Function,
69
Identifier,
710
Node,
@@ -81,6 +84,10 @@ export function walkIdentifiers(
8184
for (const id of extractIdentifiers(node.param)) {
8285
markScopeIdentifier(node, id, knownIds)
8386
}
87+
} else if (isForStatement(node)) {
88+
walkForStatement(node, false, id =>
89+
markScopeIdentifier(node, id, knownIds),
90+
)
8491
}
8592
},
8693
leave(node: Node & { scopeIds?: Set<string> }, parent: Node | null) {
@@ -196,18 +203,36 @@ export function walkBlockDeclarations(
196203
) {
197204
if (stmt.declare || !stmt.id) continue
198205
onIdent(stmt.id)
199-
} else if (
200-
stmt.type === 'ForOfStatement' ||
201-
stmt.type === 'ForInStatement' ||
202-
stmt.type === 'ForStatement'
203-
) {
204-
const variable = stmt.type === 'ForStatement' ? stmt.init : stmt.left
205-
if (variable && variable.type === 'VariableDeclaration') {
206-
for (const decl of variable.declarations) {
207-
for (const id of extractIdentifiers(decl.id)) {
208-
onIdent(id)
209-
}
210-
}
206+
} else if (isForStatement(stmt)) {
207+
walkForStatement(stmt, true, onIdent)
208+
}
209+
}
210+
}
211+
212+
function isForStatement(
213+
stmt: Node,
214+
): stmt is ForStatement | ForOfStatement | ForInStatement {
215+
return (
216+
stmt.type === 'ForOfStatement' ||
217+
stmt.type === 'ForInStatement' ||
218+
stmt.type === 'ForStatement'
219+
)
220+
}
221+
222+
function walkForStatement(
223+
stmt: ForStatement | ForOfStatement | ForInStatement,
224+
isVar: boolean,
225+
onIdent: (id: Identifier) => void,
226+
) {
227+
const variable = stmt.type === 'ForStatement' ? stmt.init : stmt.left
228+
if (
229+
variable &&
230+
variable.type === 'VariableDeclaration' &&
231+
(variable.kind === 'var' ? isVar : !isVar)
232+
) {
233+
for (const decl of variable.declarations) {
234+
for (const id of extractIdentifiers(decl.id)) {
235+
onIdent(id)
211236
}
212237
}
213238
}

0 commit comments

Comments
 (0)