From b6e386ee08fcd396a967fa682d10e985e122eb7d Mon Sep 17 00:00:00 2001 From: mrkishi Date: Mon, 23 Feb 2026 22:42:31 -0300 Subject: [PATCH] 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. --- .changeset/modern-terms-refuse.md | 5 ++++ packages/svelte/src/compiler/phases/scope.js | 23 ++++++++----------- .../function-parameter-shadowing/_config.js | 7 ++++++ .../function-parameter-shadowing/main.svelte | 12 ++++++++++ 4 files changed, 34 insertions(+), 13 deletions(-) create mode 100644 .changeset/modern-terms-refuse.md create mode 100644 packages/svelte/tests/runtime-runes/samples/function-parameter-shadowing/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/function-parameter-shadowing/main.svelte diff --git a/.changeset/modern-terms-refuse.md b/.changeset/modern-terms-refuse.md new file mode 100644 index 0000000000..2a48bce096 --- /dev/null +++ b/.changeset/modern-terms-refuse.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: handle default parameters scope leaks diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index 1e714942b3..9d563375d7 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -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); } diff --git a/packages/svelte/tests/runtime-runes/samples/function-parameter-shadowing/_config.js b/packages/svelte/tests/runtime-runes/samples/function-parameter-shadowing/_config.js new file mode 100644 index 0000000000..c35d4d7ff8 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/function-parameter-shadowing/_config.js @@ -0,0 +1,7 @@ +import { test } from '../../test'; + +export default test({ + test({ assert, logs }) { + assert.deepEqual(logs, [42, 43]); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/function-parameter-shadowing/main.svelte b/packages/svelte/tests/runtime-runes/samples/function-parameter-shadowing/main.svelte new file mode 100644 index 0000000000..2e7aef1742 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/function-parameter-shadowing/main.svelte @@ -0,0 +1,12 @@ +