Repository: sveltejs/eslint-plugin-svelte
Pull request #1165 (2025-03-28 20:36:57)

feat: add fixer to `require-store-callbacks-use-set-param`
Adds a fixer which adds or changes the param to `set`.

For example:

```ts
// before
readable(val, () => ...);
readable(val, function () { ... });

// after
readable(val, (set) => ...);
readable(val, function (set) { ... });
```

changeset-bot[bot]: ###  ⚠️  No Changeset found

Latest commit: 2c83156ed43c3a0b33424c0ac797b6892f4da5d8

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. **If these changes should result in a version bump, you need to add a changeset.**

<details><summary>This PR includes no changesets</summary>

  When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

</details>

[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add a changeset to this PR](https://github.com/43081j/eslint-plugin-svelte/new/fixer-useset?filename=.changeset/forty-drinks-exercise.md&value=---%0A%22eslint-plugin-svelte%22%3A%20patch%0A---%0A%0Afeat%3A%20add%20fixer%20to%20%60require-store-callbacks-use-set-param%60%0A)


github-actions[bot]: <!-- posted by pkg.pr.new-comment.mjs -->

## Try the Instant Preview in Online Playground

[ESLint Online Playground](https://eslint-online-playground.netlify.app/?overrideDeps=%7B%22eslint-plugin-svelte%22%3A%22https%3A%2F%2Fpkg.pr.new%2Feslint-plugin-svelte%401165%22%7D#eslint-plugin-svelte%20with%20typescript)

## Install the Instant Preview to Your Local

```
npm i https://pkg.pr.new/eslint-plugin-svelte@1165
```

## Published Instant Preview Packages:

- eslint-plugin-svelte: https://pkg.pr.new/eslint-plugin-svelte@1165

[View Commit](https://github.com/sveltejs/eslint-plugin-svelte/commit/4a4575eb0f9b0ce6c7d52c5df7631b6c9a0c1090)

Patch:
diff --git a/.changeset/clear-mugs-rush.md b/.changeset/clear-mugs-rush.md
new file mode 100644
index 0000000..83198ba
--- /dev/null
+++ b/.changeset/clear-mugs-rush.md
@@ -0,0 +1,5 @@
+---
+'eslint-plugin-svelte': minor
+---
+
+Adds a suggestion to the `require-store-callbacks-use-set-param` rule to automatically rename or add function parameters.
diff --git a/README.md b/README.md
index 3308ebf..c379a3d 100644
--- a/README.md
+++ b/README.md
@@ -272,7 +272,7 @@ These rules relate to possible syntax or logic errors in Svelte code:
 | [svelte/no-shorthand-style-property-overrides](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-shorthand-style-property-overrides/) | disallow shorthand style properties that override related longhand properties | :star: |
 | [svelte/no-store-async](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-store-async/) | disallow using async/await inside svelte stores because it causes issues with the auto-unsubscribing features | :star: |
 | [svelte/no-unknown-style-directive-property](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-unknown-style-directive-property/) | disallow unknown `style:property` | :star: |
-| [svelte/require-store-callbacks-use-set-param](https://sveltejs.github.io/eslint-plugin-svelte/rules/require-store-callbacks-use-set-param/) | store callbacks must use `set` param |  |
+| [svelte/require-store-callbacks-use-set-param](https://sveltejs.github.io/eslint-plugin-svelte/rules/require-store-callbacks-use-set-param/) | store callbacks must use `set` param | :bulb: |
 | [svelte/require-store-reactive-access](https://sveltejs.github.io/eslint-plugin-svelte/rules/require-store-reactive-access/) | disallow to use of the store itself as an operand. Need to use $ prefix or get function. | :star::wrench: |
 | [svelte/valid-compile](https://sveltejs.github.io/eslint-plugin-svelte/rules/valid-compile/) | disallow warnings when compiling. |  |
 | [svelte/valid-style-parse](https://sveltejs.github.io/eslint-plugin-svelte/rules/valid-style-parse/) | require valid style element parsing |  |
diff --git a/docs/rules.md b/docs/rules.md
index 5a47dde..df2ca5e 100644
--- a/docs/rules.md
+++ b/docs/rules.md
@@ -29,7 +29,7 @@ These rules relate to possible syntax or logic errors in Svelte code:
 | [svelte/no-shorthand-style-property-overrides](./rules/no-shorthand-style-property-overrides.md) | disallow shorthand style properties that override related longhand properties                                                          | :star:         |
 | [svelte/no-store-async](./rules/no-store-async.md)                                               | disallow using async/await inside svelte stores because it causes issues with the auto-unsubscribing features                          | :star:         |
 | [svelte/no-unknown-style-directive-property](./rules/no-unknown-style-directive-property.md)     | disallow unknown `style:property`                                                                                                      | :star:         |
-| [svelte/require-store-callbacks-use-set-param](./rules/require-store-callbacks-use-set-param.md) | store callbacks must use `set` param                                                                                                   |                |
+| [svelte/require-store-callbacks-use-set-param](./rules/require-store-callbacks-use-set-param.md) | store callbacks must use `set` param                                                                                                   | :bulb:         |
 | [svelte/require-store-reactive-access](./rules/require-store-reactive-access.md)                 | disallow to use of the store itself as an operand. Need to use $ prefix or get function.                                               | :star::wrench: |
 | [svelte/valid-compile](./rules/valid-compile.md)                                                 | disallow warnings when compiling.                                                                                                      |                |
 | [svelte/valid-style-parse](./rules/valid-style-parse.md)                                         | require valid style element parsing                                                                                                    |                |
diff --git a/docs/rules/require-store-callbacks-use-set-param.md b/docs/rules/require-store-callbacks-use-set-param.md
index 4995fa4..dbe0f59 100644
--- a/docs/rules/require-store-callbacks-use-set-param.md
+++ b/docs/rules/require-store-callbacks-use-set-param.md
@@ -10,6 +10,8 @@ since: 'v2.12.0'
 
 > store callbacks must use `set` param
 
+- :bulb: Some problems reported by this rule are manually fixable by editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
+
 ## :book: Rule Details
 
 This rule disallows if `readable` / `writable` store's setter function doesn't use `set` parameter.<br>
diff --git a/packages/eslint-plugin-svelte/src/rules/derived-has-same-inputs-outputs.ts b/packages/eslint-plugin-svelte/src/rules/derived-has-same-inputs-outputs.ts
index 30f86aa..319c49d 100644
--- a/packages/eslint-plugin-svelte/src/rules/derived-has-same-inputs-outputs.ts
+++ b/packages/eslint-plugin-svelte/src/rules/derived-has-same-inputs-outputs.ts
@@ -3,34 +3,7 @@ import type { Variable } from '@typescript-eslint/scope-manager';
 import { createRule } from '../utils/index.js';
 import type { RuleContext, RuleFixer } from '../types.js';
 import { extractStoreReferences } from './reference-helpers/svelte-store.js';
-import { getScope } from '../utils/ast-utils.js';
-
-function findVariableForName(
-	context: RuleContext,
-	node: TSESTree.Node,
-	name: string,
-	expectedName: string
-): { hasConflict: boolean; variable: Variable | null } {
-	const scope = getScope(context, node);
-	let variable: Variable | null = null;
-
-	for (const ref of scope.references) {
-		if (ref.identifier.name === expectedName) {
-			return { hasConflict: true, variable: null };
-		}
-	}
-
-	for (const v of scope.variables) {
-		if (v.name === expectedName) {
-			return { hasConflict: true, variable: null };
-		}
-		if (v.name === name) {
-			variable = v;
-		}
-	}
-
-	return { hasConflict: false, variable };
-}
+import { findVariableForReplacement } from '../utils/ast-utils.js';
 
 function createFixer(node: TSESTree.Node, variable: Variable | null, name: string) {
 	return function* fix(fixer: RuleFixer) {
@@ -92,7 +65,7 @@ export default createRule('derived-has-same-inputs-outputs', {
 			if (fnParam.type !== 'Identifier') return;
 			const expectedName = `$${args.name}`;
 			if (expectedName !== fnParam.name) {
-				const { hasConflict, variable } = findVariableForName(
+				const { hasConflict, variable } = findVariableForReplacement(
 					context,
 					fn.body,
 					fnParam.name,
@@ -136,7 +109,7 @@ export default createRule('derived-has-same-inputs-outputs', {
 				if (element && element.type === 'Identifier' && argName) {
 					const expectedName = `$${argName}`;
 					if (expectedName !== element.name) {
-						const { hasConflict, variable } = findVariableForName(
+						const { hasConflict, variable } = findVariableForReplacement(
 							context,
 							fn.body,
 							element.name,
diff --git a/packages/eslint-plugin-svelte/src/rules/require-store-callbacks-use-set-param.ts b/packages/eslint-plugin-svelte/src/rules/require-store-callbacks-use-set-param.ts
index fc12fdf..6d138c8 100644
--- a/packages/eslint-plugin-svelte/src/rules/require-store-callbacks-use-set-param.ts
+++ b/packages/eslint-plugin-svelte/src/rules/require-store-callbacks-use-set-param.ts
@@ -1,5 +1,8 @@
+import { findVariableForReplacement } from '../utils/ast-utils.js';
+import type { SuggestionReportDescriptor } from '../types.js';
 import { createRule } from '../utils/index.js';
 import { extractStoreReferences } from './reference-helpers/svelte-store.js';
+import { getSourceCode } from '../utils/compat.js';
 
 export default createRule('require-store-callbacks-use-set-param', {
 	meta: {
@@ -8,9 +11,12 @@ export default createRule('require-store-callbacks-use-set-param', {
 			category: 'Possible Errors',
 			recommended: false
 		},
+		hasSuggestions: true,
 		schema: [],
 		messages: {
-			unexpected: 'Store callbacks must use `set` param.'
+			unexpected: 'Store callbacks must use `set` param.',
+			updateParam: 'Rename parameter from {{oldName}} to `set`.',
+			addParam: 'Add a `set` parameter.'
 		},
 		type: 'suggestion'
 	},
@@ -24,10 +30,48 @@ export default createRule('require-store-callbacks-use-set-param', {
 					}
 					const param = fn.params[0];
 					if (!param || (param.type === 'Identifier' && param.name !== 'set')) {
+						const { hasConflict, variable } = findVariableForReplacement(
+							context,
+							fn.body,
+							param ? param.name : null,
+							'set'
+						);
+						const suggest: SuggestionReportDescriptor[] = [];
+						if (!hasConflict) {
+							if (param) {
+								suggest.push({
+									messageId: 'updateParam',
+									data: { oldName: param.name },
+									*fix(fixer) {
+										yield fixer.replaceText(param, 'set');
+
+										if (variable) {
+											for (const ref of variable.references) {
+												yield fixer.replaceText(ref.identifier, 'set');
+											}
+										}
+									}
+								});
+							} else {
+								const token = getSourceCode(context).getTokenBefore(fn.body, {
+									filter: (token) => token.type === 'Punctuator' && token.value === '(',
+									includeComments: false
+								});
+								if (token) {
+									suggest.push({
+										messageId: 'addParam',
+										fix(fixer) {
+											return fixer.insertTextAfter(token, 'set');
+										}
+									});
+								}
+							}
+						}
 						context.report({
 							node: fn,
 							loc: fn.loc,
-							messageId: 'unexpected'
+							messageId: 'unexpected',
+							suggest
 						});
 					}
 				}
diff --git a/packages/eslint-plugin-svelte/src/utils/ast-utils.ts b/packages/eslint-plugin-svelte/src/utils/ast-utils.ts
index 1b804ba..02c5fff 100644
--- a/packages/eslint-plugin-svelte/src/utils/ast-utils.ts
+++ b/packages/eslint-plugin-svelte/src/utils/ast-utils.ts
@@ -685,3 +685,37 @@ function getSimpleNameFromNode(
 
 	return getSourceCode(context).getText(node);
 }
+
+/**
+ * Finds the variable for a given name in the specified node's scope.
+ * Also determines if the replacement name is already in use.
+ *
+ * If the `name` is set to null, this assumes you're adding a new variable
+ * and reports if it is already in use.
+ */
+export function findVariableForReplacement(
+	context: RuleContext,
+	node: TSESTree.Node,
+	name: string | null,
+	replacementName: string
+): { hasConflict: boolean; variable: Variable | null } {
+	const scope = getScope(context, node);
+	let variable: Variable | null = null;
+
+	for (const ref of scope.references) {
+		if (ref.identifier.name === replacementName) {
+			return { hasConflict: true, variable: null };
+		}
+	}
+
+	for (const v of scope.variables) {
+		if (v.name === replacementName) {
+			return { hasConflict: true, variable: null };
+		}
+		if (v.name === name) {
+			variable = v;
+		}
+	}
+
+	return { hasConflict: false, variable };
+}


Test patch:
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/require-store-callbacks-use-set-param/invalid/test01-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/require-store-callbacks-use-set-param/invalid/test01-errors.yaml
index 74e2737..2f0e157 100644
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/require-store-callbacks-use-set-param/invalid/test01-errors.yaml
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/require-store-callbacks-use-set-param/invalid/test01-errors.yaml
@@ -1,16 +1,198 @@
 - message: Store callbacks must use `set` param.
   line: 4
   column: 18
-  suggestions: null
+  suggestions:
+    - desc: Add a `set` parameter.
+      messageId: addParam
+      output: |
+        <script>
+        	import { readable, writable } from 'svelte/store';
+
+        	readable(false, (set) => true);
+        	readable(false, (foo) => true);
+
+        	writable(false, () => true);
+        	writable(false, (foo) => true);
+
+        	readable(false, (foo) => {
+        		foo;
+        		insideACallback(() => {
+        			foo;
+        		});
+        		conflictingName(() => {
+        			const foo = 303;
+        			foo;
+        		});
+        	});
+
+        	readable(false, () => {
+        		const set = 303;
+        	});
+
+        	insideACallback(() => {
+        		const set = 303;
+        		readable(false, () => {
+        			set;
+        		});
+        	});
+        </script>
 - message: Store callbacks must use `set` param.
   line: 5
   column: 18
-  suggestions: null
+  suggestions:
+    - desc: Rename parameter from foo to `set`.
+      messageId: updateParam
+      output: |
+        <script>
+        	import { readable, writable } from 'svelte/store';
+
+        	readable(false, () => true);
+        	readable(false, (set) => true);
+
+        	writable(false, () => true);
+        	writable(false, (foo) => true);
+
+        	readable(false, (foo) => {
+        		foo;
+        		insideACallback(() => {
+        			foo;
+        		});
+        		conflictingName(() => {
+        			const foo = 303;
+        			foo;
+        		});
+        	});
+
+        	readable(false, () => {
+        		const set = 303;
+        	});
+
+        	insideACallback(() => {
+        		const set = 303;
+        		readable(false, () => {
+        			set;
+        		});
+        	});
+        </script>
 - message: Store callbacks must use `set` param.
   line: 7
   column: 18
-  suggestions: null
+  suggestions:
+    - desc: Add a `set` parameter.
+      messageId: addParam
+      output: |
+        <script>
+        	import { readable, writable } from 'svelte/store';
+
+        	readable(false, () => true);
+        	readable(false, (foo) => true);
+
+        	writable(false, (set) => true);
+        	writable(false, (foo) => true);
+
+        	readable(false, (foo) => {
+        		foo;
+        		insideACallback(() => {
+        			foo;
+        		});
+        		conflictingName(() => {
+        			const foo = 303;
+        			foo;
+        		});
+        	});
+
+        	readable(false, () => {
+        		const set = 303;
+        	});
+
+        	insideACallback(() => {
+        		const set = 303;
+        		readable(false, () => {
+        			set;
+        		});
+        	});
+        </script>
 - message: Store callbacks must use `set` param.
   line: 8
   column: 18
+  suggestions:
+    - desc: Rename parameter from foo to `set`.
+      messageId: updateParam
+      output: |
+        <script>
+        	import { readable, writable } from 'svelte/store';
+
+        	readable(false, () => true);
+        	readable(false, (foo) => true);
+
+        	writable(false, () => true);
+        	writable(false, (set) => true);
+
+        	readable(false, (foo) => {
+        		foo;
+        		insideACallback(() => {
+        			foo;
+        		});
+        		conflictingName(() => {
+        			const foo = 303;
+        			foo;
+        		});
+        	});
+
+        	readable(false, () => {
+        		const set = 303;
+        	});
+
+        	insideACallback(() => {
+        		const set = 303;
+        		readable(false, () => {
+        			set;
+        		});
+        	});
+        </script>
+- message: Store callbacks must use `set` param.
+  line: 10
+  column: 18
+  suggestions:
+    - desc: Rename parameter from foo to `set`.
+      messageId: updateParam
+      output: |
+        <script>
+        	import { readable, writable } from 'svelte/store';
+
+        	readable(false, () => true);
+        	readable(false, (foo) => true);
+
+        	writable(false, () => true);
+        	writable(false, (foo) => true);
+
+        	readable(false, (set) => {
+        		set;
+        		insideACallback(() => {
+        			set;
+        		});
+        		conflictingName(() => {
+        			const foo = 303;
+        			foo;
+        		});
+        	});
+
+        	readable(false, () => {
+        		const set = 303;
+        	});
+
+        	insideACallback(() => {
+        		const set = 303;
+        		readable(false, () => {
+        			set;
+        		});
+        	});
+        </script>
+- message: Store callbacks must use `set` param.
+  line: 21
+  column: 18
+  suggestions: null
+- message: Store callbacks must use `set` param.
+  line: 27
+  column: 19
   suggestions: null
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/require-store-callbacks-use-set-param/invalid/test01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/require-store-callbacks-use-set-param/invalid/test01-input.svelte
index a412012..e89cab3 100644
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/require-store-callbacks-use-set-param/invalid/test01-input.svelte
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/require-store-callbacks-use-set-param/invalid/test01-input.svelte
@@ -6,4 +6,26 @@
 
 	writable(false, () => true);
 	writable(false, (foo) => true);
+
+	readable(false, (foo) => {
+		foo;
+		insideACallback(() => {
+			foo;
+		});
+		conflictingName(() => {
+			const foo = 303;
+			foo;
+		});
+	});
+
+	readable(false, () => {
+		const set = 303;
+	});
+
+	insideACallback(() => {
+		const set = 303;
+		readable(false, () => {
+			set;
+		});
+	});
 </script>
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/require-store-callbacks-use-set-param/invalid/test02-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/require-store-callbacks-use-set-param/invalid/test02-errors.yaml
index 74e2737..6f2236a 100644
--- a/packages/eslint-plugin-svelte/tests/fixtures/rules/require-store-callbacks-use-set-param/invalid/test02-errors.yaml
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/require-store-callbacks-use-set-param/invalid/test02-errors.yaml
@@ -1,16 +1,64 @@
 - message: Store callbacks must use `set` param.
   line: 4
   column: 18
-  suggestions: null
+  suggestions:
+    - desc: Add a `set` parameter.
+      messageId: addParam
+      output: |
+        <script>
+        	import { readable, writable } from 'svelte/store';
+
+        	readable(false, function (set) {});
+        	readable(false, (foo) => function () {});
+
+        	writable(false, () => function () {});
+        	writable(false, (foo) => function () {});
+        </script>
 - message: Store callbacks must use `set` param.
   line: 5
   column: 18
-  suggestions: null
+  suggestions:
+    - desc: Rename parameter from foo to `set`.
+      messageId: updateParam
+      output: |
+        <script>
+        	import { readable, writable } from 'svelte/store';
+
+        	readable(false, function () {});
+        	readable(false, (set) => function () {});
+
+        	writable(false, () => function () {});
+        	writable(false, (foo) => function () {});
+        </script>
 - message: Store callbacks must use `set` param.
   line: 7
   column: 18
-  suggestions: null
+  suggestions:
+    - desc: Add a `set` parameter.
+      messageId: addParam
+      output: |
+        <script>
+        	import { readable, writable } from 'svelte/store';
+
+        	readable(false, function () {});
+        	readable(false, (foo) => function () {});
+
+        	writable(false, (set) => function () {});
+        	writable(false, (foo) => function () {});
+        </script>
 - message: Store callbacks must use `set` param.
   line: 8
   column: 18
-  suggestions: null
+  suggestions:
+    - desc: Rename parameter from foo to `set`.
+      messageId: updateParam
+      output: |
+        <script>
+        	import { readable, writable } from 'svelte/store';
+
+        	readable(false, function () {});
+        	readable(false, (foo) => function () {});
+
+        	writable(false, () => function () {});
+        	writable(false, (set) => function () {});
+        </script>