Repository: sveltejs/svelte (JavaScript)
Date: 2019-08-03 04:31:12+00:00
Task type: Bug Fix

Issue:
References to each index in keying expression seen as undeclared
From https://github.com/sveltejs/eslint-plugin-svelte3/issues/37

Something like

```svelte
{#each foo as bar, i (i)}hey{/each}
```

produces an erroneous warning that `i` is not defined. The key seems to be created correctly from the each block's `ctx.i` - there's just an incorrect warning from the compiler about it.


Issue discussion:
My first stab at a solution for this was to simply add the EachBlock's index to its scope before creating the Expression for its key:

```diff
diff --git a/src/compiler/compile/nodes/EachBlock.ts b/src/compiler/compile/nodes/EachBlock.ts
index adaa46b8..12e6cccb 100644
--- a/src/compiler/compile/nodes/EachBlock.ts
+++ b/src/compiler/compile/nodes/EachBlock.ts
@@ -83,16 +83,16 @@ export default class EachBlock extends AbstractBlock {
 			this.scope.add(context.key.name, this.expression.dependencies, this);
 		});
 
-		this.key = info.key
-			? new Expression(component, this, this.scope, info.key)
-			: null;
-
 		if (this.index) {
 			// index can only change if this is a keyed each block
 			const dependencies = this.key ? this.expression.dependencies : new Set([]);
 			this.scope.add(this.index, dependencies, this);
 		}
 
+		this.key = info.key
+			? new Expression(component, this, this.scope, info.key)
+			: null;
+
 		this.has_animation = false;
 
 		this.children = map_children(component, this, this.scope, info.children);
```

but this is making the each-block-keyed and each-block-keyed-object-identity runtime tests fail and I'm not sure why.

---

Edit: Whoops, the `this.index` handling was looking at `this.key` to see whether there was a key set. With the above change, this of course needs to be looking at `info.key` instead. With that, there are no failing tests.

Solution patch:
diff --git a/src/compiler/compile/nodes/EachBlock.ts b/src/compiler/compile/nodes/EachBlock.ts
--- a/src/compiler/compile/nodes/EachBlock.ts
+++ b/src/compiler/compile/nodes/EachBlock.ts
@@ -83,16 +83,16 @@ export default class EachBlock extends AbstractBlock {
 			this.scope.add(context.key.name, this.expression.dependencies, this);
 		});
 
-		this.key = info.key
-			? new Expression(component, this, this.scope, info.key)
-			: null;
-
 		if (this.index) {
 			// index can only change if this is a keyed each block
-			const dependencies = this.key ? this.expression.dependencies : new Set([]);
+			const dependencies = info.key ? this.expression.dependencies : new Set([]);
 			this.scope.add(this.index, dependencies, this);
 		}
 
+		this.key = info.key
+			? new Expression(component, this, this.scope, info.key)
+			: null;
+
 		this.has_animation = false;
 
 		this.children = map_children(component, this, this.scope, info.children);


Test patch:
diff --git a/test/validator/samples/undefined-value/input.svelte b/test/validator/samples/undefined-value/input.svelte
--- a/test/validator/samples/undefined-value/input.svelte
+++ b/test/validator/samples/undefined-value/input.svelte
@@ -1,6 +1,6 @@
-<script>
-	// script block prevents auto-declaration
-</script>
-
 <p>{potato}</p>
-<p>{Math.max(1, 2)}</p>
\ No newline at end of file
+<p>{Math.max(1, 2)}</p>
+
+{#each window.something as foo, i (foo.x + i)}
+	hello
+{/each}
diff --git a/test/validator/samples/undefined-value/warnings.json b/test/validator/samples/undefined-value/warnings.json
--- a/test/validator/samples/undefined-value/warnings.json
+++ b/test/validator/samples/undefined-value/warnings.json
@@ -1,15 +1,15 @@
 [{
 	"code": "missing-declaration",
-	"message": "'potato' is not defined",
-	"pos": 67,
+	"message": "'potato' is not defined. Consider adding a <script> block with 'export let potato' to declare a prop",
+	"pos": 4,
 	"start": {
-		"line": 5,
+		"line": 1,
 		"column": 4,
-		"character": 67
+		"character": 4
 	},
 	"end": {
-		"line": 5,
+		"line": 1,
 		"column": 10,
-		"character": 73
+		"character": 10
 	}
 }]
\ No newline at end of file