Skip to content

Commit

Permalink
fix(eslint-plugin): [no-non-null-assertion] provide valid fix when me…
Browse files Browse the repository at this point in the history
…mber access is on next line (#8185)

* fix(eslint-plugin): [no-non-null-assertion] provide valid fix when member access is on next line

* test: add new case
  • Loading branch information
auvred committed Jan 8, 2024
1 parent 635bb1f commit 27d6ac1
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 28 deletions.
48 changes: 21 additions & 27 deletions packages/eslint-plugin/src/rules/no-non-null-assertion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,33 +29,19 @@ export default createRule<[], MessageIds>({
return {
TSNonNullExpression(node): void {
const suggest: TSESLint.ReportSuggestionArray<MessageIds> = [];
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 (
Expand All @@ -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 {
Expand All @@ -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!?.()
Expand Down
98 changes: 97 additions & 1 deletion packages/eslint-plugin/tests/rules/no-non-null-assertion.test.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -293,5 +293,101 @@ 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
`,
},
],
},
],
},
{
code: noFormat`
x!
// comment
/* comment */ ['y']
`,
errors: [
{
messageId: 'noNonNull',
line: 2,
column: 1,
suggestions: [
{
messageId: 'suggestOptionalChain',
output: `
x?.
// comment
/* comment */ ['y']
`,
},
],
},
],
},
],
});

0 comments on commit 27d6ac1

Please sign in to comment.