diff --git a/docs/rules/no-useless-collection-argument.md b/docs/rules/no-useless-collection-argument.md index acce08a208..c11a5d91a8 100644 --- a/docs/rules/no-useless-collection-argument.md +++ b/docs/rules/no-useless-collection-argument.md @@ -2,7 +2,7 @@ 💼 This rule is enabled in the following [configs](https://github.com/sindresorhus/eslint-plugin-unicorn#recommended-config): ✅ `recommended`, ☑️ `unopinionated`. -🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). +🔧💡 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix) and manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions). diff --git a/readme.md b/readme.md index 0d5f824131..6e1c7561dc 100644 --- a/readme.md +++ b/readme.md @@ -120,7 +120,7 @@ export default [ | [no-unreadable-array-destructuring](docs/rules/no-unreadable-array-destructuring.md) | Disallow unreadable array destructuring. | ✅ ☑️ | 🔧 | | | [no-unreadable-iife](docs/rules/no-unreadable-iife.md) | Disallow unreadable IIFEs. | ✅ ☑️ | | | | [no-unused-properties](docs/rules/no-unused-properties.md) | Disallow unused object properties. | | | | -| [no-useless-collection-argument](docs/rules/no-useless-collection-argument.md) | Disallow useless values or fallbacks in `Set`, `Map`, `WeakSet`, or `WeakMap`. | ✅ ☑️ | 🔧 | | +| [no-useless-collection-argument](docs/rules/no-useless-collection-argument.md) | Disallow useless values or fallbacks in `Set`, `Map`, `WeakSet`, or `WeakMap`. | ✅ ☑️ | 🔧 | 💡 | | [no-useless-error-capture-stack-trace](docs/rules/no-useless-error-capture-stack-trace.md) | Disallow unnecessary `Error.captureStackTrace(…)`. | ✅ ☑️ | 🔧 | | | [no-useless-fallback-in-spread](docs/rules/no-useless-fallback-in-spread.md) | Disallow useless fallback when spreading in object literals. | ✅ ☑️ | 🔧 | | | [no-useless-length-check](docs/rules/no-useless-length-check.md) | Disallow useless array length check. | ✅ ☑️ | 🔧 | | diff --git a/rules/no-useless-collection-argument.js b/rules/no-useless-collection-argument.js index b6b5a4bd19..6be9a8b591 100644 --- a/rules/no-useless-collection-argument.js +++ b/rules/no-useless-collection-argument.js @@ -8,9 +8,16 @@ import { } from './ast/index.js'; import {removeParentheses, removeArgument} from './fix/index.js'; -const MESSAGE_ID = 'no-useless-collection-argument'; +/** +@import {TSESTree as ESTree} from '@typescript-eslint/types'; +@import * as ESLint from 'eslint'; +*/ + +const MESSAGE_ID_ERROR = 'no-useless-collection-argument/error'; +const MESSAGE_ID_SUGGESTION = 'no-useless-collection-argument/suggestion'; const messages = { - [MESSAGE_ID]: 'The {{description}} is useless.', + [MESSAGE_ID_ERROR]: 'The {{description}} is useless.', + [MESSAGE_ID_SUGGESTION]: 'Remove the {{description}}', }; const getDescription = node => { @@ -33,7 +40,7 @@ const getDescription = node => { const removeFallback = (node, context) => // Same code from rules/no-useless-fallback-in-spread.js - /** @param {import('eslint').Rule.RuleFixer} fixer */ + /** @param {ESLint.Rule.RuleFixer} fixer */ function * fix(fixer) { const {sourceCode} = context; const logicalExpression = node.parent; @@ -54,9 +61,9 @@ const removeFallback = (node, context) => } }; -/** @param {import('eslint').Rule.RuleContext} context */ +/** @param {ESLint.Rule.RuleContext} context */ const create = context => { - context.on('NewExpression', newExpression => { + context.on('NewExpression', (/** @type {ESTree.NewExpression} */ newExpression) => { if (!isNewExpression(newExpression, { names: ['Set', 'Map', 'WeakSet', 'WeakMap'], argumentsLength: 1, @@ -73,18 +80,40 @@ const create = context => { return; } - return { + let fix; + let shouldUseSuggestion = false; + if (isCheckingFallback) { + fix = removeFallback(node, context); + } else { + if (context.sourceCode.getCommentsInside(node).length > 0) { + shouldUseSuggestion = true; + } + + fix = fixer => removeArgument(fixer, node, context); + } + + const problem = { node, - messageId: MESSAGE_ID, + messageId: MESSAGE_ID_ERROR, data: {description}, - fix: isCheckingFallback - ? removeFallback(node, context) - : fixer => removeArgument(fixer, node, context), }; + + if (shouldUseSuggestion) { + problem.suggest = [ + { + messageId: MESSAGE_ID_SUGGESTION, + fix, + }, + ]; + } else { + problem.fix = fix; + } + + return problem; }); }; -/** @type {import('eslint').Rule.RuleModule} */ +/** @type {ESLint.Rule.RuleModule} */ const config = { create, meta: { @@ -94,6 +123,7 @@ const config = { recommended: 'unopinionated', }, fixable: 'code', + hasSuggestions: true, messages, }, }; diff --git a/test/no-useless-collection-argument.js b/test/no-useless-collection-argument.js index 848d51cdf6..f8bdf58178 100644 --- a/test/no-useless-collection-argument.js +++ b/test/no-useless-collection-argument.js @@ -71,5 +71,7 @@ test.snapshot({ 'new Set([] ?? "")', 'new Set( (( (( "" )) ?? (( [] )) )) )', 'new Set(foo ?? bar ?? [])', + // Comments + 'new Set([/**/])', ], }); diff --git a/test/package.js b/test/package.js index a796a068cc..adeb0b7a1e 100644 --- a/test/package.js +++ b/test/package.js @@ -115,7 +115,12 @@ test('Every rule file has the appropriate contents', t => { const rulePath = path.join('rules', `${ruleName}.js`); const ruleContents = fs.readFileSync(rulePath, 'utf8'); - t.true(ruleContents.includes('/** @type {import(\'eslint\').Rule.RuleModule} */'), `${ruleName} includes jsdoc comment for rule type`); + t.true( + // TODO: Use `@import` instead of `import('eslint')` + ruleContents.includes('/** @type {import(\'eslint\').Rule.RuleModule} */') + || ruleContents.includes('/** @type {ESLint.Rule.RuleModule} */'), + `${ruleName} includes jsdoc comment for rule type`, + ); } }); diff --git a/test/snapshots/no-useless-collection-argument.js.md b/test/snapshots/no-useless-collection-argument.js.md index d452c799db..5d73fe40a6 100644 --- a/test/snapshots/no-useless-collection-argument.js.md +++ b/test/snapshots/no-useless-collection-argument.js.md @@ -516,3 +516,22 @@ Generated by [AVA](https://avajs.dev). Output:␊ 1 | new Set(foo ?? bar)␊ ` + +## invalid(18): new Set([/**/]) + +> Input + + `␊ + 1 | new Set([/**/])␊ + ` + +> Error 1/1 + + `␊ + Message:␊ + > 1 | new Set([/**/])␊ + | ^^^^^^ The empty array is useless.␊ + ␊ + Suggestion 1/1: Remove the empty array:␊ + 1 | new Set()␊ + ` diff --git a/test/snapshots/no-useless-collection-argument.js.snap b/test/snapshots/no-useless-collection-argument.js.snap index ddd0140771..2c41fa7e73 100644 Binary files a/test/snapshots/no-useless-collection-argument.js.snap and b/test/snapshots/no-useless-collection-argument.js.snap differ