eslint-plugin-sonarjs
Version:
SonarJS rules for ESLint
99 lines (98 loc) • 4.35 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.rule = void 0;
const index_js_1 = require("../helpers/index.js");
const meta_js_1 = require("./meta.js");
exports.rule = {
meta: (0, index_js_1.generateMeta)(meta_js_1.meta, {
messages: {
replaceIfThenElseByReturn: 'Replace this if-then-else flow by a single return statement.',
suggest: 'Replace with single return statement',
suggestCast: 'Replace with single return statement using "!!" cast',
suggestBoolean: 'Replace with single return statement without cast (condition should be boolean!)',
},
hasSuggestions: true,
}),
create(context) {
return {
IfStatement(node) {
const parent = node.parent;
if (
// ignore `else if`
parent.type !== 'IfStatement' &&
returnsBoolean(node.consequent) &&
alternateReturnsBoolean(node)) {
context.report({
messageId: 'replaceIfThenElseByReturn',
node,
suggest: getSuggestion(node, parent),
});
}
},
};
function alternateReturnsBoolean(node) {
if (node.alternate) {
return returnsBoolean(node.alternate);
}
const { parent } = node;
if (parent?.type === 'BlockStatement') {
const ifStmtIndex = parent.body.findIndex(stmt => stmt === node);
return isSimpleReturnBooleanLiteral(parent.body[ifStmtIndex + 1]);
}
return false;
}
function returnsBoolean(statement) {
return (statement !== undefined &&
(isBlockReturningBooleanLiteral(statement) || isSimpleReturnBooleanLiteral(statement)));
}
function isBlockReturningBooleanLiteral(statement) {
return (statement.type === 'BlockStatement' &&
statement.body.length === 1 &&
isSimpleReturnBooleanLiteral(statement.body[0]));
}
function isSimpleReturnBooleanLiteral(statement) {
return (statement?.type === 'ReturnStatement' &&
statement.argument?.type === 'Literal' &&
typeof statement.argument.value === 'boolean');
}
function getSuggestion(ifStmt, parent) {
const getFix = (condition) => {
return (fixer) => {
const singleReturn = `return ${condition};`;
if (ifStmt.alternate) {
return fixer.replaceText(ifStmt, singleReturn);
}
else {
const ifStmtIndex = parent.body.findIndex(stmt => stmt === ifStmt);
const returnStmt = parent.body[ifStmtIndex + 1];
const range = [ifStmt.range[0], returnStmt.range[1]];
return fixer.replaceTextRange(range, singleReturn);
}
};
};
const shouldNegate = isReturningFalse(ifStmt.consequent);
const shouldCast = !isBooleanExpression(ifStmt.test);
const testText = context.sourceCode.getText(ifStmt.test);
if (shouldNegate) {
return [{ messageId: 'suggest', fix: getFix(`!(${testText})`) }];
}
else if (!shouldCast) {
return [{ messageId: 'suggest', fix: getFix(testText) }];
}
else {
return [
{ messageId: 'suggestCast', fix: getFix(`!!(${testText})`) },
{ messageId: 'suggestBoolean', fix: getFix(testText) },
];
}
}
function isReturningFalse(stmt) {
const returnStmt = (stmt.type === 'BlockStatement' ? stmt.body[0] : stmt);
return returnStmt.argument.value === false;
}
function isBooleanExpression(expr) {
return ((expr.type === 'UnaryExpression' || expr.type === 'BinaryExpression') &&
['!', '==', '===', '!=', '!==', '<', '<=', '>', '>=', 'in', 'instanceof'].includes(expr.operator));
}
},
};
;