From bc2c0fd7898d21bdde268987bcc7cc776bc768ac Mon Sep 17 00:00:00 2001 From: auvred Date: Thu, 4 Jan 2024 10:54:00 +0000 Subject: [PATCH 1/2] fix(eslint-plugin): [no-non-null-assertion] provide valid fix when member access is on next line --- .../src/rules/no-non-null-assertion.ts | 48 ++++++------ .../tests/rules/no-non-null-assertion.test.ts | 74 ++++++++++++++++++- 2 files changed, 94 insertions(+), 28 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-non-null-assertion.ts b/packages/eslint-plugin/src/rules/no-non-null-assertion.ts index 25617ca41f25..f80d0ee00914 100644 --- a/packages/eslint-plugin/src/rules/no-non-null-assertion.ts +++ b/packages/eslint-plugin/src/rules/no-non-null-assertion.ts @@ -29,33 +29,19 @@ export default createRule<[], MessageIds>({ return { TSNonNullExpression(node): void { const suggest: TSESLint.ReportSuggestionArray = []; - function convertTokenToOptional( - replacement: '?.' | '?', - ): TSESLint.ReportFixFunction { - return (fixer: TSESLint.RuleFixer): TSESLint.RuleFix | null => { - const operator = sourceCode.getTokenAfter( - node.expression, - isNonNullAssertionPunctuator, - ); - if (operator) { - return fixer.replaceText(operator, replacement); - } - return null; - }; + // it always exists in non-null assertion + const nonNullOperator = sourceCode.getTokenAfter( + node.expression, + isNonNullAssertionPunctuator, + )!; + + function replaceTokenWithOptional(): TSESLint.ReportFixFunction { + return fixer => fixer.replaceText(nonNullOperator, '?.'); } - function removeToken(): TSESLint.ReportFixFunction { - return (fixer: TSESLint.RuleFixer): TSESLint.RuleFix | null => { - const operator = sourceCode.getTokenAfter( - node.expression, - isNonNullAssertionPunctuator, - ); - if (operator) { - return fixer.remove(operator); - } - return null; - }; + function removeToken(): TSESLint.ReportFixFunction { + return fixer => fixer.remove(nonNullOperator); } if ( @@ -67,13 +53,21 @@ export default createRule<[], MessageIds>({ // it is x![y]?.z suggest.push({ messageId: 'suggestOptionalChain', - fix: convertTokenToOptional('?.'), + fix: replaceTokenWithOptional(), }); } else { // it is x!.y?.z suggest.push({ messageId: 'suggestOptionalChain', - fix: convertTokenToOptional('?'), + fix(fixer) { + // x!.y?.z + // ^ punctuator + const punctuator = sourceCode.getTokenAfter(nonNullOperator)!; + return [ + fixer.remove(nonNullOperator), + fixer.insertTextBefore(punctuator, '?'), + ]; + }, }); } } else { @@ -99,7 +93,7 @@ export default createRule<[], MessageIds>({ // it is x.y?.z!() suggest.push({ messageId: 'suggestOptionalChain', - fix: convertTokenToOptional('?.'), + fix: replaceTokenWithOptional(), }); } else { // it is x.y.z!?.() diff --git a/packages/eslint-plugin/tests/rules/no-non-null-assertion.test.ts b/packages/eslint-plugin/tests/rules/no-non-null-assertion.test.ts index 10dbb0d09c3c..0400ec452e27 100644 --- a/packages/eslint-plugin/tests/rules/no-non-null-assertion.test.ts +++ b/packages/eslint-plugin/tests/rules/no-non-null-assertion.test.ts @@ -1,4 +1,4 @@ -import { RuleTester } from '@typescript-eslint/rule-tester'; +import { noFormat, RuleTester } from '@typescript-eslint/rule-tester'; import rule from '../../src/rules/no-non-null-assertion'; @@ -293,5 +293,77 @@ ruleTester.run('no-non-null-assertion', rule, { }, ], }, + { + code: noFormat` +x! +.y + `, + errors: [ + { + messageId: 'noNonNull', + line: 2, + column: 1, + suggestions: [ + { + messageId: 'suggestOptionalChain', + output: ` +x +?.y + `, + }, + ], + }, + ], + }, + { + code: noFormat` +x! +// comment +.y + `, + errors: [ + { + messageId: 'noNonNull', + line: 2, + column: 1, + suggestions: [ + { + messageId: 'suggestOptionalChain', + output: ` +x +// comment +?.y + `, + }, + ], + }, + ], + }, + { + code: noFormat` +x! + // comment + . /* comment */ + y + `, + errors: [ + { + messageId: 'noNonNull', + line: 2, + column: 1, + suggestions: [ + { + messageId: 'suggestOptionalChain', + output: ` +x + // comment + ?. /* comment */ + y + `, + }, + ], + }, + ], + }, ], }); From d46a14281ac38d3c92f4c55bfd6635ef6ced3f34 Mon Sep 17 00:00:00 2001 From: auvred Date: Thu, 4 Jan 2024 11:05:06 +0000 Subject: [PATCH 2/2] test: add new case --- .../tests/rules/no-non-null-assertion.test.ts | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/packages/eslint-plugin/tests/rules/no-non-null-assertion.test.ts b/packages/eslint-plugin/tests/rules/no-non-null-assertion.test.ts index 0400ec452e27..d1a93c058d12 100644 --- a/packages/eslint-plugin/tests/rules/no-non-null-assertion.test.ts +++ b/packages/eslint-plugin/tests/rules/no-non-null-assertion.test.ts @@ -365,5 +365,29 @@ x }, ], }, + { + code: noFormat` +x! + // comment + /* comment */ ['y'] + `, + errors: [ + { + messageId: 'noNonNull', + line: 2, + column: 1, + suggestions: [ + { + messageId: 'suggestOptionalChain', + output: ` +x?. + // comment + /* comment */ ['y'] + `, + }, + ], + }, + ], + }, ], });