fix: error at compile time when a declaration in a snippet redeclares one of its parameters (#18842)

`{const item = 1}` inside `{#snippet row(item)}` compiles, and the
output declares `item` twice in one function, so it does not parse:

```js
const row = ($$anchor, item = $.noop) => {
	const item = 1;
```

`{let}`, `{@const}` and a nested snippet with the parameter's name do
the same. In `phases/scope.js`, `EachBlock` visits its body's children
in the item's own scope, so a tag that reuses the item name is a
`declaration_duplicate`. `SnippetBlock` calls `context.next({ scope:
child_scope })`, the `Fragment` visitor opens another scope for the
body, and the tag never meets the parameter.

After visiting the body, `SnippetBlock` now reports any name in the
body's scope that `child_scope` also declares. A declaration in a nested
block or element still shadows the parameter.

---------

Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
main
Nic Polumeyv 2 days ago committed by GitHub
parent 5b44f26bc2
commit 28b99aa8e6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: error at compile time when a declaration in a snippet redeclares one of its parameters

@ -1343,6 +1343,14 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
}
context.next({ scope: child_scope });
// the parameters and what the body declares end up in the same function
const body_scope = /** @type {Scope} */ (scopes.get(node.body));
for (const [name, binding] of body_scope.declarations) {
if (child_scope.declarations.has(name)) {
e.declaration_duplicate(binding.node, name);
}
}
},
Fragment: (node, context) => {

@ -0,0 +1,14 @@
[
{
"code": "declaration_duplicate",
"message": "`item` has already been declared",
"start": {
"line": 13,
"column": 8
},
"end": {
"line": 13,
"column": 12
}
}
]

@ -0,0 +1,17 @@
<!-- this is ok -->
{#snippet row1(item)}
{#if item}
{const item = 1}
<p>{item}</p>
{/if}
{/snippet}
{@render row1(5)}
<!-- this is not -->
{#snippet row2(item)}
{const item = 1}
<p>{item}</p>
{/snippet}
{@render row2(5)}
Loading…
Cancel
Save