fix: handle default parameters scope leaks

Use separate scopes for function declarations/expressions and function
bodies. This prevents variable declarations from leaking into default
parameter initialization expressions.

Closes #17785.
pull/17788/head
mrkishi 1 week ago
parent fcdc0289db
commit b6e386ee08

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: handle default parameters scope leaks

@ -1098,25 +1098,19 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
},
FunctionExpression(node, { state, next }) {
const scope = state.scope.child();
const scope = state.scope.child(true);
scopes.set(node, scope);
if (node.id) {
scopes.set(node.id, state.scope); // so that declarations within with the same name are not confused with the function name
scope.declare(node.id, 'normal', 'function');
}
if (node.id) scope.declare(node.id, 'normal', 'function');
add_params(scope, node.params);
next({ scope });
},
FunctionDeclaration(node, { state, next }) {
if (node.id) {
scopes.set(node.id, state.scope); // so that declarations within with the same name are not confused with the function name
state.scope.declare(node.id, 'normal', 'function', node);
}
if (node.id) state.scope.declare(node.id, 'normal', 'function', node);
const scope = state.scope.child();
const scope = state.scope.child(true);
scopes.set(node, scope);
add_params(scope, node.params);
@ -1124,7 +1118,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
},
ArrowFunctionExpression(node, { state, next }) {
const scope = state.scope.child();
const scope = state.scope.child(true);
scopes.set(node, scope);
add_params(scope, node.params);
@ -1142,8 +1136,11 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
parent?.type === 'FunctionExpression' ||
parent?.type === 'ArrowFunctionExpression'
) {
// We already created a new scope for the function
context.next();
// The scopes created for the function nodes above handle the function identifier and
// parameters, but the block statement itself holds the non-porous function scope
const scope = context.state.scope.child();
scopes.set(node, scope);
context.next({ scope });
} else {
create_block_scope(node, context);
}

@ -0,0 +1,7 @@
import { test } from '../../test';
export default test({
test({ assert, logs }) {
assert.deepEqual(logs, [42, 43]);
}
});

@ -0,0 +1,12 @@
<script>
let value = $state(42);
function shadow(output = value) {
const value = 1337;
return output;
}
console.log(shadow());
value += 1;
console.log(shadow());
</script>
Loading…
Cancel
Save