diff --git a/.changeset/wet-bats-exercise.md b/.changeset/wet-bats-exercise.md
new file mode 100644
index 0000000000..550ad83375
--- /dev/null
+++ b/.changeset/wet-bats-exercise.md
@@ -0,0 +1,5 @@
+---
+"svelte": patch
+---
+
+fix: preserve component function context for nested components
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js
index 2ddafb24d9..d8ae6749f4 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js
@@ -886,7 +886,12 @@ function serialize_inline_component(node, component_name, context) {
if (slot_name === 'default' && !has_children_prop) {
push_prop(
- b.init('children', context.state.options.dev ? b.call('$.wrap_snippet', slot_fn) : slot_fn)
+ b.init(
+ 'children',
+ context.state.options.dev
+ ? b.call('$.wrap_snippet', slot_fn, b.id(context.state.analysis.name))
+ : slot_fn
+ )
);
// We additionally add the default slot as a boolean, so that the slot render function on the other
// side knows it should get the content to render from $$props.children
@@ -2699,7 +2704,7 @@ export const template_visitors = {
let snippet = b.arrow(args, body);
if (context.state.options.dev) {
- snippet = b.call('$.wrap_snippet', snippet);
+ snippet = b.call('$.wrap_snippet', snippet, b.id(context.state.analysis.name));
}
const declaration = b.var(node.expression, snippet);
diff --git a/packages/svelte/src/internal/client/dom/blocks/snippet.js b/packages/svelte/src/internal/client/dom/blocks/snippet.js
index 5bf13a45d8..611733feb1 100644
--- a/packages/svelte/src/internal/client/dom/blocks/snippet.js
+++ b/packages/svelte/src/internal/client/dom/blocks/snippet.js
@@ -39,15 +39,13 @@ export function snippet(get_snippet, node, ...args) {
* In development, wrap the snippet function so that it passes validation, and so that the
* correct component context is set for ownership checks
* @param {(node: import('#client').TemplateNode, ...args: any[]) => import('#client').Dom} fn
- * @returns
+ * @param {any} component
*/
-export function wrap_snippet(fn) {
- let component = /** @type {import('#client').ComponentContext} */ (current_component_context);
-
+export function wrap_snippet(fn, component) {
return add_snippet_symbol(
(/** @type {import('#client').TemplateNode} */ node, /** @type {any[]} */ ...args) => {
var previous_component_function = dev_current_component_function;
- set_dev_current_component_function(component.function);
+ set_dev_current_component_function(component);
try {
return fn(node, ...args);
diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-6/Component1.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-6/Component1.svelte
new file mode 100644
index 0000000000..17d34e7cf6
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-6/Component1.svelte
@@ -0,0 +1,5 @@
+
+
+{@render children()}
diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-6/Component2.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-6/Component2.svelte
new file mode 100644
index 0000000000..17d34e7cf6
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-6/Component2.svelte
@@ -0,0 +1,5 @@
+
+
+{@render children()}
diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-6/Component3.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-6/Component3.svelte
new file mode 100644
index 0000000000..57ef96fed5
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-6/Component3.svelte
@@ -0,0 +1,5 @@
+
+
+
diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-6/_config.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-6/_config.js
new file mode 100644
index 0000000000..216f4c138a
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-6/_config.js
@@ -0,0 +1,23 @@
+import { flushSync } from 'svelte';
+import { test } from '../../test';
+
+// Tests that nested snippets preserve correct component function context so we don't get false positive warnings
+export default test({
+ html: ``,
+
+ compileOptions: {
+ dev: true
+ },
+
+ test({ assert, target, warnings }) {
+ const button = target.querySelector('button');
+
+ button?.click();
+ flushSync();
+
+ assert.htmlEqual(target.innerHTML, ``);
+ assert.deepEqual(warnings, []);
+ },
+
+ warnings: []
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-6/main.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-6/main.svelte
new file mode 100644
index 0000000000..d2f53f5363
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-6/main.svelte
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+