eslint-plugin-sonarjs
Advanced tools
Comparing version 0.18.0 to 0.19.0
@@ -22,11 +22,12 @@ "use strict"; | ||
// https://sonarsource.github.io/rspec/#/rspec/S1862 | ||
const nodes_1 = require("../utils/nodes"); | ||
const equivalence_1 = require("../utils/equivalence"); | ||
const locations_1 = require("../utils/locations"); | ||
const docs_url_1 = require("../utils/docs-url"); | ||
const message = 'This branch duplicates the one on line {{line}}'; | ||
const duplicatedConditionMessage = 'This condition is covered by the one on line {{line}}'; | ||
const duplicatedCaseMessage = 'This case duplicates the one on line {{line}}'; | ||
const rule = { | ||
meta: { | ||
messages: { | ||
duplicatedBranch: message, | ||
duplicatedCondition: duplicatedConditionMessage, | ||
duplicatedCase: duplicatedCaseMessage, | ||
sonarRuntime: '{{sonarRuntimeData}}', | ||
@@ -36,3 +37,3 @@ }, | ||
docs: { | ||
description: 'Related "if/else if" statements should not have the same condition', | ||
description: 'Related "if-else-if" and "switch-case" statements should not have the same condition', | ||
recommended: 'error', | ||
@@ -49,24 +50,22 @@ url: (0, docs_url_1.default)(__filename), | ||
create(context) { | ||
const sourceCode = context.getSourceCode(); | ||
return { | ||
IfStatement(node) { | ||
const ifStmt = node; | ||
const condition = ifStmt.test; | ||
let statement = ifStmt.alternate; | ||
while (statement) { | ||
if ((0, nodes_1.isIfStatement)(statement)) { | ||
if ((0, equivalence_1.areEquivalent)(condition, statement.test, context.getSourceCode())) { | ||
const line = ifStmt.loc && ifStmt.loc.start.line; | ||
if (line && condition.loc) { | ||
(0, locations_1.report)(context, { | ||
messageId: 'duplicatedBranch', | ||
data: { | ||
line, | ||
}, | ||
node: statement.test, | ||
}, [(0, locations_1.issueLocation)(condition.loc, condition.loc, 'Original')], message); | ||
} | ||
} | ||
statement = statement.alternate; | ||
} | ||
else { | ||
var _a; | ||
const { test } = node; | ||
const conditionsToCheck = test.type === 'LogicalExpression' && test.operator === '&&' | ||
? [test, ...splitByAnd(test)] | ||
: [test]; | ||
let current = node; | ||
let operandsToCheck = conditionsToCheck.map(c => splitByOr(c).map(splitByAnd)); | ||
while (((_a = current.parent) === null || _a === void 0 ? void 0 : _a.type) === 'IfStatement' && current.parent.alternate === current) { | ||
current = current.parent; | ||
const currentOrOperands = splitByOr(current.test).map(splitByAnd); | ||
operandsToCheck = operandsToCheck.map(orOperands => orOperands.filter(orOperand => !currentOrOperands.some(currentOrOperand => isSubset(currentOrOperand, orOperand, sourceCode)))); | ||
if (operandsToCheck.some(orOperands => orOperands.length === 0)) { | ||
(0, locations_1.report)(context, { | ||
messageId: 'duplicatedCondition', | ||
data: { line: current.test.loc.start.line }, | ||
node: test, | ||
}, [(0, locations_1.issueLocation)(current.test.loc, current.test.loc, 'Covering')], duplicatedConditionMessage); | ||
break; | ||
@@ -76,6 +75,58 @@ } | ||
}, | ||
SwitchStatement(node) { | ||
const switchStmt = node; | ||
const previousTests = []; | ||
for (const switchCase of switchStmt.cases) { | ||
if (switchCase.test) { | ||
const { test } = switchCase; | ||
const duplicateTest = previousTests.find(previousTest => (0, equivalence_1.areEquivalent)(test, previousTest, sourceCode)); | ||
if (duplicateTest) { | ||
(0, locations_1.report)(context, { | ||
messageId: 'duplicatedCase', | ||
data: { | ||
line: duplicateTest.loc.start.line, | ||
}, | ||
node: test, | ||
}, [(0, locations_1.issueLocation)(duplicateTest.loc, duplicateTest.loc, 'Original')], duplicatedCaseMessage); | ||
} | ||
else { | ||
previousTests.push(test); | ||
} | ||
} | ||
} | ||
}, | ||
}; | ||
}, | ||
}; | ||
const splitByOr = splitByLogicalOperator.bind(null, '||'); | ||
const splitByAnd = splitByLogicalOperator.bind(null, '&&'); | ||
function splitByLogicalOperator(operator, node) { | ||
if (node.type === 'LogicalExpression' && node.operator === operator) { | ||
return [ | ||
...splitByLogicalOperator(operator, node.left), | ||
...splitByLogicalOperator(operator, node.right), | ||
]; | ||
} | ||
return [node]; | ||
} | ||
function isSubset(first, second, sourceCode) { | ||
return first.every(fst => second.some(snd => isSubsetOf(fst, snd, sourceCode))); | ||
function isSubsetOf(first, second, sourceCode) { | ||
if (first.type !== second.type) { | ||
return false; | ||
} | ||
if (first.type === 'LogicalExpression') { | ||
const second1 = second; | ||
if ((first.operator === '||' || first.operator === '&&') && | ||
first.operator === second1.operator) { | ||
return ((isSubsetOf(first.left, second1.left, sourceCode) && | ||
isSubsetOf(first.right, second1.right, sourceCode)) || | ||
(isSubsetOf(first.left, second1.right, sourceCode) && | ||
isSubsetOf(first.right, second1.left, sourceCode))); | ||
} | ||
} | ||
return (0, equivalence_1.areEquivalent)(first, second, sourceCode); | ||
} | ||
} | ||
module.exports = rule; | ||
//# sourceMappingURL=no-identical-conditions.js.map |
{ | ||
"name": "eslint-plugin-sonarjs", | ||
"version": "0.18.0", | ||
"version": "0.19.0", | ||
"description": "SonarJS rules for ESLint", | ||
@@ -54,3 +54,3 @@ "main": "lib/index.js", | ||
"eslint-plugin-notice": "0.9.10", | ||
"eslint-plugin-sonarjs": "0.17.0", | ||
"eslint-plugin-sonarjs": "0.18.0", | ||
"jest": "28.1.3", | ||
@@ -57,0 +57,0 @@ "jest-sonar-reporter": "2.0.0", |
Sorry, the diff of this file is not supported yet
318490
4601