Skip to content
This repository was archived by the owner on Oct 3, 2024. It is now read-only.

Commit ea8c431

Browse files
Fix suggestion for 'prefer-single-boolean-return' (#344)
1 parent 94005e9 commit ea8c431

File tree

2 files changed

+169
-14
lines changed

2 files changed

+169
-14
lines changed

src/rules/prefer-single-boolean-return.ts

Lines changed: 53 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ const rule: TSESLint.RuleModule<string, string[]> = {
3232
messages: {
3333
replaceIfThenElseByReturn: 'Replace this if-then-else flow by a single return statement.',
3434
suggestIfThenElseReplacement: 'Replace with single return statement',
35+
suggestUnsafeIfThenElseReplacement:
36+
'Replace with single return statement (beware of return value usages)',
3537
},
3638
schema: [],
3739
type: 'suggestion',
@@ -95,24 +97,61 @@ const rule: TSESLint.RuleModule<string, string[]> = {
9597
return isReturnStatement(statement) && isBooleanLiteral(statement.argument || undefined);
9698
}
9799

98-
function getSuggestion(ifStmt: TSESTree.IfStatement): TSESLint.ReportSuggestionArray<string> {
99-
return [
100+
function getSuggestion(ifStmt: TSESTree.IfStatement) {
101+
const getFix = (condition: string) => {
102+
return (fixer: TSESLint.RuleFixer) => {
103+
const singleReturn = `return ${condition};`;
104+
if (ifStmt.alternate) {
105+
return fixer.replaceText(ifStmt, singleReturn);
106+
} else {
107+
const parent = ifStmt.parent as TSESTree.BlockStatement;
108+
const ifStmtIndex = parent.body.findIndex(stmt => stmt === ifStmt);
109+
const returnStmt = parent.body[ifStmtIndex + 1];
110+
const range: [number, number] = [ifStmt.range[0], returnStmt.range[1]];
111+
return fixer.replaceTextRange(range, singleReturn);
112+
}
113+
};
114+
};
115+
const shouldNegate = isReturningFalse(ifStmt.consequent);
116+
const shouldCast = !isBooleanExpression(ifStmt.test);
117+
const testText = context.getSourceCode().getText(ifStmt.test);
118+
let safeCondition: string;
119+
if (shouldNegate) {
120+
safeCondition = `!(${testText})`;
121+
} else if (shouldCast) {
122+
safeCondition = `!!(${testText})`;
123+
} else {
124+
safeCondition = testText;
125+
}
126+
const suggestions: TSESLint.ReportSuggestionArray<string> = [
100127
{
101128
messageId: 'suggestIfThenElseReplacement',
102-
fix: fixer => {
103-
const singleReturn = `return ${context.getSourceCode().getText(ifStmt.test)};`;
104-
if (ifStmt.alternate) {
105-
return fixer.replaceText(ifStmt, singleReturn);
106-
} else {
107-
const parent = ifStmt.parent as TSESTree.BlockStatement;
108-
const ifStmtIndex = parent.body.findIndex(stmt => stmt === ifStmt);
109-
const returnStmt = parent.body[ifStmtIndex + 1];
110-
const range: [number, number] = [ifStmt.range[0], returnStmt.range[1]];
111-
return fixer.replaceTextRange(range, singleReturn);
112-
}
113-
},
129+
fix: getFix(safeCondition),
114130
},
115131
];
132+
if (shouldCast && !shouldNegate) {
133+
suggestions.push({
134+
messageId: 'suggestUnsafeIfThenElseReplacement',
135+
fix: getFix(testText),
136+
});
137+
}
138+
return suggestions;
139+
}
140+
141+
function isReturningFalse(stmt: TSESTree.Statement): boolean {
142+
const returnStmt = (
143+
stmt.type === 'BlockStatement' ? stmt.body[0] : stmt
144+
) as TSESTree.ReturnStatement;
145+
return (returnStmt.argument as TSESTree.Literal).value === false;
146+
}
147+
148+
function isBooleanExpression(expr: TSESTree.Expression) {
149+
return (
150+
(expr.type === 'UnaryExpression' || expr.type === 'BinaryExpression') &&
151+
['!', '==', '===', '!=', '!==', '<', '<=', '>', '>=', 'in', 'instanceof'].includes(
152+
expr.operator,
153+
)
154+
);
116155
}
117156
},
118157
};

tests/rules/prefer-single-boolean-return.test.ts

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,16 @@ function foo() {
246246
{
247247
messageId: 'suggestIfThenElseReplacement',
248248
output: `
249+
function foo() {
250+
if (bar()) {
251+
return !!(baz());
252+
}
253+
return qux();
254+
}`,
255+
},
256+
{
257+
messageId: 'suggestUnsafeIfThenElseReplacement',
258+
output: `
249259
function foo() {
250260
if (bar()) {
251261
return baz();
@@ -275,11 +285,117 @@ function foo() {
275285
{
276286
messageId: 'suggestIfThenElseReplacement',
277287
output: `
288+
function foo() {
289+
if (bar()) {
290+
return !!(baz());
291+
}
292+
return qux();
293+
}`,
294+
},
295+
{
296+
messageId: 'suggestUnsafeIfThenElseReplacement',
297+
output: `
278298
function foo() {
279299
if (bar()) {
280300
return baz();
281301
}
282302
return qux();
303+
}`,
304+
},
305+
],
306+
},
307+
],
308+
},
309+
{
310+
code: `
311+
function foo() {
312+
if (!bar()) {
313+
return true;
314+
} else {
315+
return false;
316+
}
317+
}`,
318+
errors: [
319+
{
320+
messageId: 'replaceIfThenElseByReturn',
321+
suggestions: [
322+
{
323+
messageId: 'suggestIfThenElseReplacement',
324+
output: `
325+
function foo() {
326+
return !bar();
327+
}`,
328+
},
329+
],
330+
},
331+
],
332+
},
333+
{
334+
code: `
335+
function foo() {
336+
if (bar() > 0) {
337+
return true;
338+
} else {
339+
return false;
340+
}
341+
}`,
342+
errors: [
343+
{
344+
messageId: 'replaceIfThenElseByReturn',
345+
suggestions: [
346+
{
347+
messageId: 'suggestIfThenElseReplacement',
348+
output: `
349+
function foo() {
350+
return bar() > 0;
351+
}`,
352+
},
353+
],
354+
},
355+
],
356+
},
357+
{
358+
code: `
359+
function foo() {
360+
if (baz() > 0) {
361+
return false;
362+
} else {
363+
return true;
364+
}
365+
}`,
366+
errors: [
367+
{
368+
messageId: 'replaceIfThenElseByReturn',
369+
suggestions: [
370+
{
371+
messageId: 'suggestIfThenElseReplacement',
372+
output: `
373+
function foo() {
374+
return !(baz() > 0);
375+
}`,
376+
},
377+
],
378+
},
379+
],
380+
},
381+
{
382+
code: `
383+
function foo() {
384+
if (baz()) {
385+
return false;
386+
} else {
387+
return true;
388+
}
389+
}`,
390+
errors: [
391+
{
392+
messageId: 'replaceIfThenElseByReturn',
393+
suggestions: [
394+
{
395+
messageId: 'suggestIfThenElseReplacement',
396+
output: `
397+
function foo() {
398+
return !(baz());
283399
}`,
284400
},
285401
],

0 commit comments

Comments
 (0)