Repository: sveltejs/svelte (JavaScript)

Issue:
Svelte 5: compiler crash when using function in both event handler and markup
### Describe the bug

Using a function as both the handler for certain events and in a markup expression causes the compiler to throw an error.

By "certain events," I mean that, in the reproduction, the error occurs if you use (for example) `onchange`, `oninput`, or `onclick`, but does *not* occur if you use `onblur` or `onsubmit`.

### Reproduction

[REPL](https://svelte-5-preview.vercel.app/#H4sIAAAAAAAAA0WMQQrDIBBFryKzaqDQvZhAz1G7CGZsB5Jx0LFQxLvXrLp4i_c-_AaRdixgHw14PRAs3EXgCvqVU8oHd8XhJdUczuJKyCS6ePYaKwelxCZeJtO6Z3f7r-yIpapJHN4rv3BusS8Dz-PuSBtFwg2s5or92X8bhvCOiQAAAA==)

```svelte
<script>
	function f() {}
</script>

<input onchange={f}>{f}
```

### Logs

Locally using SvelteKit:
```
[vite-plugin-svelte] [plugin vite-plugin-svelte] Cannot read properties of undefined (reading 'length')
file: /redacted/src/routes/+page.svelte
TypeError: Cannot read properties of undefined (reading 'length')
    at is_expression_attribute (file:///redacted/node_modules/.pnpm/svelte@5.0.0-next.158/node_modules/svelte/src/compiler/utils/ast.js:42:19)
    at is_event_attribute (file:///redacted/node_modules/.pnpm/svelte@5.0.0-next.158/node_modules/svelte/src/compiler/utils/ast.js:53:9)
    at get_delegated_event (file:///redacted/node_modules/.pnpm/svelte@5.0.0-next.158/node_modules/svelte/src/compiler/phases/2-analyze/index.js:127:6)
    at Attribute (file:///redacted/node_modules/.pnpm/svelte@5.0.0-next.158/node_modules/svelte/src/compiler/phases/2-analyze/index.js:1189:28)
    at go (file:///redacted/node_modules/.pnpm/svelte@5.0.0-next.158/node_modules/svelte/src/compiler/phases/visitors.js:49:5)
    at visitor (file:///redacted/node_modules/.pnpm/svelte@5.0.0-next.158/node_modules/svelte/src/compiler/phases/visitors.js:64:4)
    at Object.next (file:///redacted/node_modules/.pnpm/zimmerframe@1.1.2/node_modules/zimmerframe/src/walk.js:107:21)
    at go (file:///redacted/node_modules/.pnpm/svelte@5.0.0-next.158/node_modules/svelte/src/compiler/phases/visitors.js:45:29)
    at go (file:///redacted/node_modules/.pnpm/svelte@5.0.0-next.158/node_modules/svelte/src/compiler/phases/visitors.js:60:6)
    at next (file:///redacted/node_modules/.pnpm/svelte@5.0.0-next.158/node_modules/svelte/src/compiler/phases/visitors.js:55:7)
```
In the reproduction:

```
TypeError: e.value is undefined
    po compiler/index.js@5.0.0-next.158:1
    uo compiler/index.js@5.0.0-next.158:1
    s compiler/index.js@5.0.0-next.158:1
    Attribute compiler/index.js@5.0.0-next.158:1
    n compiler/index.js@5.0.0-next.158:1
    o compiler/index.js@5.0.0-next.158:1
    next compiler/index.js@5.0.0-next.158:1
    n compiler/index.js@5.0.0-next.158:1
    n compiler/index.js@5.0.0-next.158:1
    next compiler/index.js@5.0.0-next.158:1
    _ compiler/index.js@5.0.0-next.158:1
    n compiler/index.js@5.0.0-next.158:1
    o compiler/index.js@5.0.0-next.158:1
    e compiler/index.js@5.0.0-next.158:1
    next compiler/index.js@5.0.0-next.158:1
    next compiler/index.js@5.0.0-next.158:1
    n compiler/index.js@5.0.0-next.158:1
    n compiler/index.js@5.0.0-next.158:1
    next compiler/index.js@5.0.0-next.158:1
    n compiler/index.js@5.0.0-next.158:1
    n compiler/index.js@5.0.0-next.158:1
    next compiler/index.js@5.0.0-next.158:1
    RegularElement compiler/index.js@5.0.0-next.158:1
    n compiler/index.js@5.0.0-next.158:1
    o compiler/index.js@5.0.0-next.158:1
    n compiler/index.js@5.0.0-next.158:1
    o compiler/index.js@5.0.0-next.158:1
    next compiler/index.js@5.0.0-next.158:1
    n compiler/index.js@5.0.0-next.158:1
    n compiler/index.js@5.0.0-next.158:1
    next compiler/index.js@5.0.0-next.158:1
    _ compiler/index.js@5.0.0-next.158:1
    n compiler/index.js@5.0.0-next.158:1
    o compiler/index.js@5.0.0-next.158:1
    e compiler/index.js@5.0.0-next.158:1
    next compiler/index.js@5.0.0-next.158:1
    next compiler/index.js@5.0.0-next.158:1
    i compiler/index.js@5.0.0-next.158:1
    next compiler/index.js@5.0.0-next.158:1
    n compiler/index.js@5.0.0-next.158:1
    n compiler/index.js@5.0.0-next.158:1
    next compiler/index.js@5.0.0-next.158:1
    _ compiler/index.js@5.0.0-next.158:1
    n compiler/index.js@5.0.0-next.158:1
    o compiler/index.js@5.0.0-next.158:1
    e compiler/index.js@5.0.0-next.158:1
    t compiler/index.js@5.0.0-next.158:1
    qS compiler/index.js@5.0.0-next.158:1
    compile compiler/index.js@5.0.0-next.158:1
    Immutable 2
        transform
        runHook
index-ZdIW0gts.js:83:550
```


### System Info

```shell
System:
    OS: Linux 6.9 Arch Linux
    CPU: (12) x64 AMD Ryzen 5 3600X 6-Core Processor
    Memory: 5.40 GB / 15.57 GB
    Container: Yes # (No?)
    Shell: 5.2.26 - /usr/bin/bash
  Binaries:
    Node: 22.3.0 - /usr/bin/node
    npm: 10.8.1 - /usr/bin/npm
    pnpm: 9.4.0 - /usr/bin/pnpm
  Browsers:
    # (I'm using Librewolf 126.0.1)
    Chromium: 126.0.6478.55
  npmPackages:
    svelte: 5.0.0-next.158 => 5.0.0-next.158
```


### Severity

annoyance


Discussion:

Patch:
diff --git a/.changeset/hot-rivers-punch.md b/.changeset/hot-rivers-punch.md
new file mode 100644
index 000000000000..4669e6d3c941
--- /dev/null
+++ b/.changeset/hot-rivers-punch.md
@@ -0,0 +1,5 @@
+---
+"svelte": patch
+---
+
+fix: prevent misidentification of bindings as delegatable event handlers if used outside event attribute
diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js
index 16c0e8f8342c..ad39beb4eb7f 100644
--- a/packages/svelte/src/compiler/phases/2-analyze/index.js
+++ b/packages/svelte/src/compiler/phases/2-analyze/index.js
@@ -111,23 +111,24 @@ function get_delegated_event(event_name, handler, context) {
 		if (binding != null) {
 			for (const { path } of binding.references) {
 				const parent = path.at(-1);
-				if (parent == null) {
-					return non_hoistable;
-				}
+				if (parent == null) return non_hoistable;
+
+				const grandparent = path.at(-2);
 
 				/** @type {import('#compiler').RegularElement | null} */
 				let element = null;
 				/** @type {string | null} */
 				let event_name = null;
 				if (parent.type === 'OnDirective') {
-					element = /** @type {import('#compiler').RegularElement} */ (path.at(-2));
+					element = /** @type {import('#compiler').RegularElement} */ (grandparent);
 					event_name = parent.name;
 				} else if (
 					parent.type === 'ExpressionTag' &&
-					is_event_attribute(/** @type {import('#compiler').Attribute} */ (path.at(-2)))
+					grandparent?.type === 'Attribute' &&
+					is_event_attribute(grandparent)
 				) {
 					element = /** @type {import('#compiler').RegularElement} */ (path.at(-3));
-					const attribute = /** @type {import('#compiler').Attribute} */ (path.at(-2));
+					const attribute = /** @type {import('#compiler').Attribute} */ (grandparent);
 					event_name = get_attribute_event_name(attribute.name);
 				}
 


Test patch:
diff --git a/packages/svelte/tests/runtime-runes/samples/event-on-3/_config.js b/packages/svelte/tests/runtime-runes/samples/event-on-3/_config.js
new file mode 100644
index 000000000000..5460c761ac6e
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/event-on-3/_config.js
@@ -0,0 +1,7 @@
+import { test } from '../../test';
+
+export default test({
+	test() {
+		// Compiler shouldn't error
+	}
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/event-on-3/main.svelte b/packages/svelte/tests/runtime-runes/samples/event-on-3/main.svelte
new file mode 100644
index 000000000000..4fa22572277b
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/event-on-3/main.svelte
@@ -0,0 +1,5 @@
+<script>
+	function f() {}
+</script>
+
+<input onchange={f}>{f}