Repository: ota-meshi/eslint-plugin-svelte (ts)

Issue:
False positive for svelte-ignore above containing element
### Before You File a Bug Report Please Confirm You Have Done The Following...

- [X] I have tried restarting my IDE and the issue persists.
- [X] I have updated to the latest version of the packages.

### What version of ESLint are you using?

8.36.0

### What version of `eslint-plugin-svelte` are you using?

2.23.0

### What did you do?

<details>
<summary>Configuration</summary>

```javascript
module.exports = {
  root: true,
  extends: [
    'plugin:svelte/recommended',
  ],
};
```
</details>

Slightly modified from https://daisyui.com/components/dropdown/#dropdown-menu:
```svelte
<!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
<div class="dropdown">
  <label tabindex="0">Click</label>
  <ul tabindex="0">
  </ul>
</div>
```


### What did you expect to happen?

svelte-ignore respected at that level. Works with eslint-plugin-svelte3

### What actually happened?

```
/tmp/repro-svelte-ignore-container/test.svelte
  1:20  error  svelte-ignore comment is used, but not warned                                                         svelte/no-unused-svelte-ignore
  1:54  error  svelte-ignore comment is used, but not warned                                                         svelte/no-unused-svelte-ignore
  3:3   error  A11y: noninteractive element cannot have nonnegative tabIndex value(a11y-no-noninteractive-tabindex)  svelte/valid-compile
  3:3   error  A11y: A form label must be associated with a control.(a11y-label-has-associated-control)              svelte/valid-compile
  4:3   error  A11y: noninteractive element cannot have nonnegative tabIndex value(a11y-no-noninteractive-tabindex)  svelte/valid-compile

✖ 5 problems (5 errors, 0 warnings)
```

### Link to **GitHub Repo** with Minimal Reproducible Example

https://github.com/polyzen/repro-svelte-ignore-container

### Additional comments

_No response_

Pull request:
fix: false positive for containing element in `svelte/no-unused-svelte-ignore`
fixes #419

Patch:
diff --git a/.changeset/heavy-cycles-explain.md b/.changeset/heavy-cycles-explain.md
new file mode 100644
index 0000000..246e369
--- /dev/null
+++ b/.changeset/heavy-cycles-explain.md
@@ -0,0 +1,5 @@
+---
+"eslint-plugin-svelte": patch
+---
+
+fix: false positive for containing element in `svelte/no-unused-svelte-ignore`
diff --git a/src/shared/svelte-compile-warns/extract-leading-comments.ts b/src/shared/svelte-compile-warns/extract-leading-comments.ts
index 83ed3c7..cedb4b6 100644
--- a/src/shared/svelte-compile-warns/extract-leading-comments.ts
+++ b/src/shared/svelte-compile-warns/extract-leading-comments.ts
@@ -17,7 +17,7 @@ export function extractLeadingComments(
       }
       const astToken = token as AST.Token
       if (astToken.type === "HTMLText") {
-        return Boolean(astToken.value.trim())
+        return false
       }
       return astToken.type !== "HTMLComment"
     },
diff --git a/src/shared/svelte-compile-warns/index.ts b/src/shared/svelte-compile-warns/index.ts
index 85c8dbe..ac6f037 100644
--- a/src/shared/svelte-compile-warns/index.ts
+++ b/src/shared/svelte-compile-warns/index.ts
@@ -2,7 +2,7 @@ import type { AST } from "svelte-eslint-parser"
 import * as compiler from "svelte/compiler"
 import type { SourceMapMappings } from "@jridgewell/sourcemap-codec"
 import { decode } from "@jridgewell/sourcemap-codec"
-import type { RuleContext } from "../../types"
+import type { ASTNodeWithParent, RuleContext } from "../../types"
 import { LinesAndColumns } from "../../utils/lines-and-columns"
 import type { TransformResult } from "./transform/types"
 import {
@@ -21,6 +21,18 @@ import { getLangValue } from "../../utils/ast-utils"
 import path from "path"
 import fs from "fs"
 
+type WarningTargetNode =
+  | (AST.SvelteProgram & ASTNodeWithParent)
+  | (AST.SvelteElement & ASTNodeWithParent)
+  | (AST.SvelteStyleElement & ASTNodeWithParent)
+  | (AST.SvelteScriptElement["body"][number] & ASTNodeWithParent)
+type IgnoreTargetNode =
+  | WarningTargetNode
+  | (AST.SvelteIfBlock & ASTNodeWithParent)
+  | (AST.SvelteKeyBlock & ASTNodeWithParent)
+  | (AST.SvelteEachBlock & ASTNodeWithParent)
+  | (AST.SvelteAwaitBlock & ASTNodeWithParent)
+
 const STYLE_TRANSFORMS: Record<
   string,
   typeof transformWithPostCSS | undefined
@@ -477,21 +489,22 @@ function processIgnore(
     if (!warning.code) {
       continue
     }
-    const node = getWarningNode(warning)
-    if (!node) {
-      continue
-    }
-    for (const comment of extractLeadingComments(context, node).reverse()) {
-      const ignoreItem = ignoreComments.find(
-        (item) => item.token === comment && item.code === warning.code,
-      )
-      if (ignoreItem) {
-        unusedIgnores.delete(ignoreItem)
-        remainingWarning.delete(warning)
-        break
+    let node: IgnoreTargetNode | null = getWarningNode(warning)
+    while (node) {
+      for (const comment of extractLeadingComments(context, node).reverse()) {
+        const ignoreItem = ignoreComments.find(
+          (item) => item.token === comment && item.code === warning.code,
+        )
+        if (ignoreItem) {
+          unusedIgnores.delete(ignoreItem)
+          remainingWarning.delete(warning)
+          break
+        }
       }
+      node = getIgnoreParent(node)
     }
   }
+
   // Stripped styles are ignored from compilation and cannot determine css errors.
   for (const node of stripStyleElements) {
     for (const comment of extractLeadingComments(context, node).reverse()) {
@@ -509,8 +522,42 @@ function processIgnore(
     unusedIgnores: [...unusedIgnores],
   }
 
+  /** Get ignore target parent node */
+  function getIgnoreParent(node: IgnoreTargetNode): IgnoreTargetNode | null {
+    if (
+      node.type !== "SvelteElement" &&
+      node.type !== "SvelteIfBlock" &&
+      node.type !== "SvelteKeyBlock" &&
+      node.type !== "SvelteEachBlock" &&
+      node.type !== "SvelteAwaitBlock"
+    ) {
+      return null
+    }
+    const parent = node.parent
+    if (parent.type === "SvelteElseBlock") {
+      return parent.parent // SvelteIfBlock or SvelteEachBlock
+    }
+    if (
+      parent.type === "SvelteAwaitPendingBlock" ||
+      parent.type === "SvelteAwaitThenBlock" ||
+      parent.type === "SvelteAwaitCatchBlock"
+    ) {
+      return parent.parent // SvelteAwaitBlock
+    }
+    if (
+      parent.type !== "SvelteElement" &&
+      parent.type !== "SvelteIfBlock" &&
+      parent.type !== "SvelteKeyBlock" &&
+      parent.type !== "SvelteEachBlock"
+      // && parent.type !== "SvelteAwaitBlock"
+    ) {
+      return null
+    }
+    return parent
+  }
+
   /** Get warning node */
-  function getWarningNode(warning: Warning) {
+  function getWarningNode(warning: Warning): WarningTargetNode | null {
     const indexes = getWarningIndexes(warning)
     if (indexes.start != null) {
       const node = getWarningTargetNodeFromIndex(indexes.start)
@@ -534,7 +581,9 @@ function processIgnore(
   /**
    * Get warning target node from the given index
    */
-  function getWarningTargetNodeFromIndex(index: number) {
+  function getWarningTargetNodeFromIndex(
+    index: number,
+  ): WarningTargetNode | null {
     let targetNode = sourceCode.getNodeByRangeIndex(index)
     while (targetNode) {
       if (
@@ -548,7 +597,7 @@ function processIgnore(
           targetNode.parent.type === "Program" ||
           targetNode.parent.type === "SvelteScriptElement"
         ) {
-          return targetNode
+          return targetNode as WarningTargetNode
         }
       } else {
         return null
diff --git a/typings/estree/index.d.ts b/typings/estree/index.d.ts
index 56df768..306dd6e 100644
--- a/typings/estree/index.d.ts
+++ b/typings/estree/index.d.ts
@@ -5,6 +5,7 @@
 import type { TSESTree } from "@typescript-eslint/types"
 
 export type Node = TSESTree.Node
+export type Program = TSESTree.Program
 export type Expression = TSESTree.Expression
 export type Statement = TSESTree.Statement
 export type Pattern = TSESTree.Pattern


Test patch:
diff --git a/tests/fixtures/rules/no-unused-svelte-ignore/invalid/invalid-svelte-ignore01-errors.yaml b/tests/fixtures/rules/no-unused-svelte-ignore/invalid/invalid-svelte-ignore01-errors.yaml
new file mode 100644
index 0000000..be0f705
--- /dev/null
+++ b/tests/fixtures/rules/no-unused-svelte-ignore/invalid/invalid-svelte-ignore01-errors.yaml
@@ -0,0 +1,8 @@
+- message: svelte-ignore comment is used, but not warned
+  line: 4
+  column: 24
+  suggestions: null
+- message: svelte-ignore comment is used, but not warned
+  line: 4
+  column: 58
+  suggestions: null
diff --git a/tests/fixtures/rules/no-unused-svelte-ignore/invalid/invalid-svelte-ignore01-input.svelte b/tests/fixtures/rules/no-unused-svelte-ignore/invalid/invalid-svelte-ignore01-input.svelte
new file mode 100644
index 0000000..56a9e02
--- /dev/null
+++ b/tests/fixtures/rules/no-unused-svelte-ignore/invalid/invalid-svelte-ignore01-input.svelte
@@ -0,0 +1,9 @@
+<div>
+  {#if true}
+    A
+    <!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+  {:else}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {/if}
+</div>
diff --git a/tests/fixtures/rules/no-unused-svelte-ignore/invalid/invalid-svelte-ignore02-errors.yaml b/tests/fixtures/rules/no-unused-svelte-ignore/invalid/invalid-svelte-ignore02-errors.yaml
new file mode 100644
index 0000000..be0f705
--- /dev/null
+++ b/tests/fixtures/rules/no-unused-svelte-ignore/invalid/invalid-svelte-ignore02-errors.yaml
@@ -0,0 +1,8 @@
+- message: svelte-ignore comment is used, but not warned
+  line: 4
+  column: 24
+  suggestions: null
+- message: svelte-ignore comment is used, but not warned
+  line: 4
+  column: 58
+  suggestions: null
diff --git a/tests/fixtures/rules/no-unused-svelte-ignore/invalid/invalid-svelte-ignore02-input.svelte b/tests/fixtures/rules/no-unused-svelte-ignore/invalid/invalid-svelte-ignore02-input.svelte
new file mode 100644
index 0000000..d059281
--- /dev/null
+++ b/tests/fixtures/rules/no-unused-svelte-ignore/invalid/invalid-svelte-ignore02-input.svelte
@@ -0,0 +1,9 @@
+<div>
+  {#each [] as e}
+    A
+    <!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+  {:else}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {/each}
+</div>
diff --git a/tests/fixtures/rules/no-unused-svelte-ignore/invalid/invalid-svelte-ignore03-errors.yaml b/tests/fixtures/rules/no-unused-svelte-ignore/invalid/invalid-svelte-ignore03-errors.yaml
new file mode 100644
index 0000000..68aa845
--- /dev/null
+++ b/tests/fixtures/rules/no-unused-svelte-ignore/invalid/invalid-svelte-ignore03-errors.yaml
@@ -0,0 +1,24 @@
+- message: svelte-ignore comment is used, but not warned
+  line: 3
+  column: 24
+  suggestions: null
+- message: svelte-ignore comment is used, but not warned
+  line: 3
+  column: 58
+  suggestions: null
+- message: svelte-ignore comment is used, but not warned
+  line: 7
+  column: 24
+  suggestions: null
+- message: svelte-ignore comment is used, but not warned
+  line: 7
+  column: 58
+  suggestions: null
+- message: svelte-ignore comment is used, but not warned
+  line: 15
+  column: 24
+  suggestions: null
+- message: svelte-ignore comment is used, but not warned
+  line: 15
+  column: 58
+  suggestions: null
diff --git a/tests/fixtures/rules/no-unused-svelte-ignore/invalid/invalid-svelte-ignore03-input.svelte b/tests/fixtures/rules/no-unused-svelte-ignore/invalid/invalid-svelte-ignore03-input.svelte
new file mode 100644
index 0000000..62e33f5
--- /dev/null
+++ b/tests/fixtures/rules/no-unused-svelte-ignore/invalid/invalid-svelte-ignore03-input.svelte
@@ -0,0 +1,20 @@
+<div>
+  {#await Promise.resolve(42)}
+    <!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+  {:then name}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+    <!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+  {:catch name}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {/await}
+</div>
+<div>
+  {#await Promise.resolve(42)}
+    <!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+  {:then name}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {/await}
+</div>
diff --git a/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore01-input.svelte b/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore01-input.svelte
new file mode 100644
index 0000000..bef1144
--- /dev/null
+++ b/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore01-input.svelte
@@ -0,0 +1,5 @@
+<!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+<div class="dropdown">
+  <label tabindex="0">Click</label>
+  <ul tabindex="0" />
+</div>
diff --git a/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore02-input.svelte b/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore02-input.svelte
new file mode 100644
index 0000000..ec4c607
--- /dev/null
+++ b/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore02-input.svelte
@@ -0,0 +1,6 @@
+<!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+TEXT
+<div class="dropdown">
+  <label tabindex="0">Click</label>
+  <ul tabindex="0" />
+</div>
diff --git a/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore03-input.svelte b/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore03-input.svelte
new file mode 100644
index 0000000..46ca368
--- /dev/null
+++ b/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore03-input.svelte
@@ -0,0 +1,12 @@
+<!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+<!-- comment -->
+<div class="dropdown">
+  <label tabindex="0">Click</label>
+  <ul tabindex="0" />
+</div>
+<!-- svelte-ignore a11y-label-has-associated-control -->
+<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
+<div class="dropdown">
+  <label tabindex="0">Click</label>
+  <ul tabindex="0" />
+</div>
diff --git a/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore04-input.svelte b/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore04-input.svelte
new file mode 100644
index 0000000..701dde5
--- /dev/null
+++ b/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore04-input.svelte
@@ -0,0 +1,17 @@
+<div>
+  <!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+  {#if true}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {/if}
+</div>
+<div>
+  <!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+  {#if true}
+    A
+  {:else}
+    <div />
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {/if}
+</div>
diff --git a/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore05-input.svelte b/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore05-input.svelte
new file mode 100644
index 0000000..7d1ec60
--- /dev/null
+++ b/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore05-input.svelte
@@ -0,0 +1,7 @@
+<div>
+  <!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+  {#key 42}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {/key}
+</div>
diff --git a/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore06-input.svelte b/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore06-input.svelte
new file mode 100644
index 0000000..ab95cbf
--- /dev/null
+++ b/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore06-input.svelte
@@ -0,0 +1,17 @@
+<div>
+  <!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+  {#each [] as e}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {/each}
+</div>
+<div>
+  <!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+  {#each [] as e}
+    A
+  {:else}
+    <div />
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {/each}
+</div>
diff --git a/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore07-input.svelte b/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore07-input.svelte
new file mode 100644
index 0000000..cd96ffc
--- /dev/null
+++ b/tests/fixtures/rules/no-unused-svelte-ignore/valid/svelte-ignore07-input.svelte
@@ -0,0 +1,44 @@
+<div>
+  <!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+  {#await Promise.resolve(42)}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {/await}
+</div>
+<div>
+  <!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+  {#await Promise.resolve(42)}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {:then name}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {:catch name}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {/await}
+</div>
+<div>
+  <!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+  {#await Promise.resolve(42)}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {:then name}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {/await}
+</div>
+<div>
+  <!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+  {#await Promise.resolve(42) then n}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {/await}
+</div>
+<div>
+  <!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+  {#await Promise.resolve(42) catch n}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {/await}
+</div>
diff --git a/tests/fixtures/rules/valid-compile/invalid/invalid-svelte-ignore01-errors.yaml b/tests/fixtures/rules/valid-compile/invalid/invalid-svelte-ignore01-errors.yaml
new file mode 100644
index 0000000..fd95963
--- /dev/null
+++ b/tests/fixtures/rules/valid-compile/invalid/invalid-svelte-ignore01-errors.yaml
@@ -0,0 +1,15 @@
+- message: "A11y: noninteractive element cannot have nonnegative tabIndex
+    value(a11y-no-noninteractive-tabindex)"
+  line: 6
+  column: 5
+  suggestions: null
+- message: "A11y: A form label must be associated with a
+    control.(a11y-label-has-associated-control)"
+  line: 6
+  column: 5
+  suggestions: null
+- message: "A11y: noninteractive element cannot have nonnegative tabIndex
+    value(a11y-no-noninteractive-tabindex)"
+  line: 7
+  column: 5
+  suggestions: null
diff --git a/tests/fixtures/rules/valid-compile/invalid/invalid-svelte-ignore01-input.svelte b/tests/fixtures/rules/valid-compile/invalid/invalid-svelte-ignore01-input.svelte
new file mode 100644
index 0000000..56a9e02
--- /dev/null
+++ b/tests/fixtures/rules/valid-compile/invalid/invalid-svelte-ignore01-input.svelte
@@ -0,0 +1,9 @@
+<div>
+  {#if true}
+    A
+    <!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+  {:else}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {/if}
+</div>
diff --git a/tests/fixtures/rules/valid-compile/invalid/invalid-svelte-ignore02-errors.yaml b/tests/fixtures/rules/valid-compile/invalid/invalid-svelte-ignore02-errors.yaml
new file mode 100644
index 0000000..fd95963
--- /dev/null
+++ b/tests/fixtures/rules/valid-compile/invalid/invalid-svelte-ignore02-errors.yaml
@@ -0,0 +1,15 @@
+- message: "A11y: noninteractive element cannot have nonnegative tabIndex
+    value(a11y-no-noninteractive-tabindex)"
+  line: 6
+  column: 5
+  suggestions: null
+- message: "A11y: A form label must be associated with a
+    control.(a11y-label-has-associated-control)"
+  line: 6
+  column: 5
+  suggestions: null
+- message: "A11y: noninteractive element cannot have nonnegative tabIndex
+    value(a11y-no-noninteractive-tabindex)"
+  line: 7
+  column: 5
+  suggestions: null
diff --git a/tests/fixtures/rules/valid-compile/invalid/invalid-svelte-ignore02-input.svelte b/tests/fixtures/rules/valid-compile/invalid/invalid-svelte-ignore02-input.svelte
new file mode 100644
index 0000000..d059281
--- /dev/null
+++ b/tests/fixtures/rules/valid-compile/invalid/invalid-svelte-ignore02-input.svelte
@@ -0,0 +1,9 @@
+<div>
+  {#each [] as e}
+    A
+    <!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+  {:else}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {/each}
+</div>
diff --git a/tests/fixtures/rules/valid-compile/invalid/invalid-svelte-ignore03-errors.yaml b/tests/fixtures/rules/valid-compile/invalid/invalid-svelte-ignore03-errors.yaml
new file mode 100644
index 0000000..7ed6236
--- /dev/null
+++ b/tests/fixtures/rules/valid-compile/invalid/invalid-svelte-ignore03-errors.yaml
@@ -0,0 +1,53 @@
+- message: Empty block(empty-block)
+  line: 2
+  column: 31
+  suggestions: null
+- message: "A11y: noninteractive element cannot have nonnegative tabIndex
+    value(a11y-no-noninteractive-tabindex)"
+  line: 5
+  column: 5
+  suggestions: null
+- message: "A11y: A form label must be associated with a
+    control.(a11y-label-has-associated-control)"
+  line: 5
+  column: 5
+  suggestions: null
+- message: "A11y: noninteractive element cannot have nonnegative tabIndex
+    value(a11y-no-noninteractive-tabindex)"
+  line: 6
+  column: 5
+  suggestions: null
+- message: "A11y: noninteractive element cannot have nonnegative tabIndex
+    value(a11y-no-noninteractive-tabindex)"
+  line: 9
+  column: 5
+  suggestions: null
+- message: "A11y: A form label must be associated with a
+    control.(a11y-label-has-associated-control)"
+  line: 9
+  column: 5
+  suggestions: null
+- message: "A11y: noninteractive element cannot have nonnegative tabIndex
+    value(a11y-no-noninteractive-tabindex)"
+  line: 10
+  column: 5
+  suggestions: null
+- message: Empty block(empty-block)
+  line: 14
+  column: 31
+  suggestions: null
+- message: "A11y: noninteractive element cannot have nonnegative tabIndex
+    value(a11y-no-noninteractive-tabindex)"
+  line: 17
+  column: 5
+  suggestions: null
+- message: "A11y: A form label must be associated with a
+    control.(a11y-label-has-associated-control)"
+  line: 17
+  column: 5
+  suggestions: null
+- message: "A11y: noninteractive element cannot have nonnegative tabIndex
+    value(a11y-no-noninteractive-tabindex)"
+  line: 18
+  column: 5
+  suggestions: null
diff --git a/tests/fixtures/rules/valid-compile/invalid/invalid-svelte-ignore03-input.svelte b/tests/fixtures/rules/valid-compile/invalid/invalid-svelte-ignore03-input.svelte
new file mode 100644
index 0000000..62e33f5
--- /dev/null
+++ b/tests/fixtures/rules/valid-compile/invalid/invalid-svelte-ignore03-input.svelte
@@ -0,0 +1,20 @@
+<div>
+  {#await Promise.resolve(42)}
+    <!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+  {:then name}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+    <!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+  {:catch name}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {/await}
+</div>
+<div>
+  {#await Promise.resolve(42)}
+    <!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+  {:then name}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {/await}
+</div>
diff --git a/tests/fixtures/rules/valid-compile/valid/svelte-ignore01-input.svelte b/tests/fixtures/rules/valid-compile/valid/svelte-ignore01-input.svelte
new file mode 100644
index 0000000..bef1144
--- /dev/null
+++ b/tests/fixtures/rules/valid-compile/valid/svelte-ignore01-input.svelte
@@ -0,0 +1,5 @@
+<!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+<div class="dropdown">
+  <label tabindex="0">Click</label>
+  <ul tabindex="0" />
+</div>
diff --git a/tests/fixtures/rules/valid-compile/valid/svelte-ignore02-input.svelte b/tests/fixtures/rules/valid-compile/valid/svelte-ignore02-input.svelte
new file mode 100644
index 0000000..ec4c607
--- /dev/null
+++ b/tests/fixtures/rules/valid-compile/valid/svelte-ignore02-input.svelte
@@ -0,0 +1,6 @@
+<!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+TEXT
+<div class="dropdown">
+  <label tabindex="0">Click</label>
+  <ul tabindex="0" />
+</div>
diff --git a/tests/fixtures/rules/valid-compile/valid/svelte-ignore03-input.svelte b/tests/fixtures/rules/valid-compile/valid/svelte-ignore03-input.svelte
new file mode 100644
index 0000000..46ca368
--- /dev/null
+++ b/tests/fixtures/rules/valid-compile/valid/svelte-ignore03-input.svelte
@@ -0,0 +1,12 @@
+<!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+<!-- comment -->
+<div class="dropdown">
+  <label tabindex="0">Click</label>
+  <ul tabindex="0" />
+</div>
+<!-- svelte-ignore a11y-label-has-associated-control -->
+<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
+<div class="dropdown">
+  <label tabindex="0">Click</label>
+  <ul tabindex="0" />
+</div>
diff --git a/tests/fixtures/rules/valid-compile/valid/svelte-ignore04-input.svelte b/tests/fixtures/rules/valid-compile/valid/svelte-ignore04-input.svelte
new file mode 100644
index 0000000..701dde5
--- /dev/null
+++ b/tests/fixtures/rules/valid-compile/valid/svelte-ignore04-input.svelte
@@ -0,0 +1,17 @@
+<div>
+  <!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+  {#if true}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {/if}
+</div>
+<div>
+  <!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+  {#if true}
+    A
+  {:else}
+    <div />
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {/if}
+</div>
diff --git a/tests/fixtures/rules/valid-compile/valid/svelte-ignore05-input.svelte b/tests/fixtures/rules/valid-compile/valid/svelte-ignore05-input.svelte
new file mode 100644
index 0000000..7d1ec60
--- /dev/null
+++ b/tests/fixtures/rules/valid-compile/valid/svelte-ignore05-input.svelte
@@ -0,0 +1,7 @@
+<div>
+  <!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+  {#key 42}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {/key}
+</div>
diff --git a/tests/fixtures/rules/valid-compile/valid/svelte-ignore06-input.svelte b/tests/fixtures/rules/valid-compile/valid/svelte-ignore06-input.svelte
new file mode 100644
index 0000000..ab95cbf
--- /dev/null
+++ b/tests/fixtures/rules/valid-compile/valid/svelte-ignore06-input.svelte
@@ -0,0 +1,17 @@
+<div>
+  <!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+  {#each [] as e}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {/each}
+</div>
+<div>
+  <!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+  {#each [] as e}
+    A
+  {:else}
+    <div />
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {/each}
+</div>
diff --git a/tests/fixtures/rules/valid-compile/valid/svelte-ignore07-input.svelte b/tests/fixtures/rules/valid-compile/valid/svelte-ignore07-input.svelte
new file mode 100644
index 0000000..cd96ffc
--- /dev/null
+++ b/tests/fixtures/rules/valid-compile/valid/svelte-ignore07-input.svelte
@@ -0,0 +1,44 @@
+<div>
+  <!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+  {#await Promise.resolve(42)}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {/await}
+</div>
+<div>
+  <!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+  {#await Promise.resolve(42)}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {:then name}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {:catch name}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {/await}
+</div>
+<div>
+  <!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+  {#await Promise.resolve(42)}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {:then name}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {/await}
+</div>
+<div>
+  <!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+  {#await Promise.resolve(42) then n}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {/await}
+</div>
+<div>
+  <!-- svelte-ignore a11y-label-has-associated-control a11y-no-noninteractive-tabindex -->
+  {#await Promise.resolve(42) catch n}
+    <label tabindex="0">Click</label>
+    <ul tabindex="0" />
+  {/await}
+</div>