From dd9fc0d1ad948f264a48e2ebdf4f7f5fb8ddb876 Mon Sep 17 00:00:00 2001
From: Copilot <198982749+Copilot@users.noreply.github.com>
Date: Sun, 15 Feb 2026 11:00:50 +0100
Subject: [PATCH] Warn on non-destructured `$props()` reads in runes mode
(#17708)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Non-destructured `$props()` access in runes mode silently skipped the
`state_referenced_locally` warning, leading to missed guidance when
users read `props` via identifiers or member expressions.
- **Analyzer behavior**
- Include `rest_prop` bindings in `state_referenced_locally` detection
so reads of `$props()` identifiers warn consistently with destructured
props.
- **Validation coverage**
- Add a validator fixture for `$props()` identifiers and update the
`props-identifier` snapshot expectations to capture the new warnings.
Example:
```svelte
```
Original prompt
>
> ----
>
> *This section details on the original issue you should resolve*
>
> False negative for `state_referenced_locally` warning on
not destructured `$props` access?
> ### Describe the bug
>
> I was looking for a workaround for sveltejs/svelte#17669 and thought
of not destructuring the `$props` directly; to my surprise there were no
warnings at all.
>
>
> ### Reproduction
>
> ```js
> const props = $props();
> const { model } = props; // missing warning
>
> const value = props.model.value; // missing warning
> ```
>
>
[Playground](https://svelte.dev/playground/untitled?version=5.50.2#H4sIAAAAAAAACn2QT4vCQAzFv0oIe1CQ9l51YY97lj1tPYxtXAam6TAT_1H63U0HUax1j3nvJeT3OmTTEBb4w2LFUY0L3FtHEYvfDuXiB28QVL8lv7zP4pGcDNrORJrSq5aFWPQMrmIVrJfPkktROQp00LQ1OehhDR8-tD7O5ku174GjcQdSM8WyNC0hz4HOniqhGk4msOW_klf54zrPNkTwzVUbgsZuz8z1G6GzYCHhQP3iDdV47Zltwv2XMEGN6Cbgk5vIGhujAj3AXstI4WxcycvivZFn7q1OxrqT5RqLvXGR-itXywVk_AEAAA)
>
> ### Logs
>
> ```shell
>
> ```
>
> ### System Info
>
> ```shell
> REPL - Svelte v.5.50.2
> ```
>
> ### Severity
>
> annoyance
>
> ## Comments on the Issue (you are @copilot in this section)
>
>
>
>
- Fixes sveltejs/svelte#17685
---
💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Rich-Harris <1162160+Rich-Harris@users.noreply.github.com>
Co-authored-by: Rich Harris
Co-authored-by: Paolo Ricciuti
---
.changeset/loose-baboons-visit.md | 5 +
.../phases/2-analyze/visitors/Identifier.js | 3 +-
.../client/index.svelte.warnings.json | 142 ++++++++++++++++++
.../server/index.svelte.warnings.json | 142 ++++++++++++++++++
.../input.svelte | 6 +
.../warnings.json | 26 ++++
6 files changed, 323 insertions(+), 1 deletion(-)
create mode 100644 .changeset/loose-baboons-visit.md
create mode 100644 packages/svelte/tests/snapshot/samples/props-identifier/_expected/client/index.svelte.warnings.json
create mode 100644 packages/svelte/tests/snapshot/samples/props-identifier/_expected/server/index.svelte.warnings.json
create mode 100644 packages/svelte/tests/validator/samples/state-referenced-locally-props-identifier/input.svelte
create mode 100644 packages/svelte/tests/validator/samples/state-referenced-locally-props-identifier/warnings.json
diff --git a/.changeset/loose-baboons-visit.md b/.changeset/loose-baboons-visit.md
new file mode 100644
index 0000000000..09c50deb5e
--- /dev/null
+++ b/.changeset/loose-baboons-visit.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: emit state_referenced_locally warning for non-destructured props
diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js
index 9daae33dd7..5c1e8031b8 100644
--- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js
+++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js
@@ -115,7 +115,8 @@ export function Identifier(node, context) {
!should_proxy(binding.initial.arguments[0], context.state.scope)))) ||
binding.kind === 'raw_state' ||
binding.kind === 'derived' ||
- binding.kind === 'prop') &&
+ binding.kind === 'prop' ||
+ binding.kind === 'rest_prop') &&
// We're only concerned with reads here
(parent.type !== 'AssignmentExpression' || parent.left !== node) &&
parent.type !== 'UpdateExpression'
diff --git a/packages/svelte/tests/snapshot/samples/props-identifier/_expected/client/index.svelte.warnings.json b/packages/svelte/tests/snapshot/samples/props-identifier/_expected/client/index.svelte.warnings.json
new file mode 100644
index 0000000000..81149a92f8
--- /dev/null
+++ b/packages/svelte/tests/snapshot/samples/props-identifier/_expected/client/index.svelte.warnings.json
@@ -0,0 +1,142 @@
+[
+ {
+ "code": "state_referenced_locally",
+ "message": "This reference only captures the initial value of `props`. Did you mean to reference it inside a closure instead?\nhttps://svelte.dev/e/state_referenced_locally",
+ "filename": "packages/svelte/tests/snapshot/samples/props-identifier/index.svelte",
+ "start": {
+ "line": 3,
+ "column": 1,
+ "character": 33
+ },
+ "end": {
+ "line": 3,
+ "column": 6,
+ "character": 38
+ },
+ "position": [
+ 33,
+ 38
+ ],
+ "frame": "1: "
+ },
+ {
+ "code": "state_referenced_locally",
+ "message": "This reference only captures the initial value of `props`. Did you mean to reference it inside a closure instead?\nhttps://svelte.dev/e/state_referenced_locally",
+ "filename": "packages/svelte/tests/snapshot/samples/props-identifier/index.svelte",
+ "start": {
+ "line": 9,
+ "column": 1,
+ "character": 120
+ },
+ "end": {
+ "line": 9,
+ "column": 6,
+ "character": 125
+ },
+ "position": [
+ 120,
+ 125
+ ],
+ "frame": " 7: props.a = true;\n 8: props[a] = true;\n 9: props;\n ^\n10: \n11: "
+ }
+]
\ No newline at end of file
diff --git a/packages/svelte/tests/snapshot/samples/props-identifier/_expected/server/index.svelte.warnings.json b/packages/svelte/tests/snapshot/samples/props-identifier/_expected/server/index.svelte.warnings.json
new file mode 100644
index 0000000000..81149a92f8
--- /dev/null
+++ b/packages/svelte/tests/snapshot/samples/props-identifier/_expected/server/index.svelte.warnings.json
@@ -0,0 +1,142 @@
+[
+ {
+ "code": "state_referenced_locally",
+ "message": "This reference only captures the initial value of `props`. Did you mean to reference it inside a closure instead?\nhttps://svelte.dev/e/state_referenced_locally",
+ "filename": "packages/svelte/tests/snapshot/samples/props-identifier/index.svelte",
+ "start": {
+ "line": 3,
+ "column": 1,
+ "character": 33
+ },
+ "end": {
+ "line": 3,
+ "column": 6,
+ "character": 38
+ },
+ "position": [
+ 33,
+ 38
+ ],
+ "frame": "1: "
+ },
+ {
+ "code": "state_referenced_locally",
+ "message": "This reference only captures the initial value of `props`. Did you mean to reference it inside a closure instead?\nhttps://svelte.dev/e/state_referenced_locally",
+ "filename": "packages/svelte/tests/snapshot/samples/props-identifier/index.svelte",
+ "start": {
+ "line": 9,
+ "column": 1,
+ "character": 120
+ },
+ "end": {
+ "line": 9,
+ "column": 6,
+ "character": 125
+ },
+ "position": [
+ 120,
+ 125
+ ],
+ "frame": " 7: props.a = true;\n 8: props[a] = true;\n 9: props;\n ^\n10: \n11: "
+ }
+]
\ No newline at end of file
diff --git a/packages/svelte/tests/validator/samples/state-referenced-locally-props-identifier/input.svelte b/packages/svelte/tests/validator/samples/state-referenced-locally-props-identifier/input.svelte
new file mode 100644
index 0000000000..980debdfaf
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/state-referenced-locally-props-identifier/input.svelte
@@ -0,0 +1,6 @@
+
diff --git a/packages/svelte/tests/validator/samples/state-referenced-locally-props-identifier/warnings.json b/packages/svelte/tests/validator/samples/state-referenced-locally-props-identifier/warnings.json
new file mode 100644
index 0000000000..b1385037e1
--- /dev/null
+++ b/packages/svelte/tests/validator/samples/state-referenced-locally-props-identifier/warnings.json
@@ -0,0 +1,26 @@
+[
+ {
+ "code": "state_referenced_locally",
+ "message": "This reference only captures the initial value of `props`. Did you mean to reference it inside a closure instead?",
+ "start": {
+ "line": 3,
+ "column": 19
+ },
+ "end": {
+ "line": 3,
+ "column": 24
+ }
+ },
+ {
+ "code": "state_referenced_locally",
+ "message": "This reference only captures the initial value of `props`. Did you mean to reference it inside a closure instead?",
+ "start": {
+ "line": 4,
+ "column": 15
+ },
+ "end": {
+ "line": 4,
+ "column": 20
+ }
+ }
+]