-
Notifications
You must be signed in to change notification settings - Fork 72
/
no-identical-expressions.ts
116 lines (105 loc) · 3.56 KB
/
no-identical-expressions.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/*
* eslint-plugin-sonarjs
* Copyright (C) 2018-2021 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// https://jira.sonarsource.com/browse/RSPEC-1764
import { TSESTree } from '@typescript-eslint/experimental-utils';
import { Rule } from '../utils/types';
import { isIdentifier, isLiteral } from '../utils/nodes';
import { areEquivalent } from '../utils/equivalence';
import { report, issueLocation, IssueLocation } from '../utils/locations';
import docsUrl from '../utils/docs-url';
const EQUALITY_OPERATOR_TOKEN_KINDS = new Set(['==', '===', '!=', '!==']);
// consider only binary expressions with these operators
const RELEVANT_OPERATOR_TOKEN_KINDS = new Set([
'&&',
'||',
'/',
'-',
'<<',
'>>',
'<',
'<=',
'>',
'>=',
]);
const message = (operator: string) =>
`Correct one of the identical sub-expressions on both sides of operator "${operator}"`;
function hasRelevantOperator(node: TSESTree.BinaryExpression | TSESTree.LogicalExpression) {
return (
RELEVANT_OPERATOR_TOKEN_KINDS.has(node.operator) ||
(EQUALITY_OPERATOR_TOKEN_KINDS.has(node.operator) && !hasIdentifierOperands(node))
);
}
function hasIdentifierOperands(node: TSESTree.BinaryExpression | TSESTree.LogicalExpression) {
return isIdentifier(node.left) && isIdentifier(node.right);
}
function isOneOntoOneShifting(node: TSESTree.BinaryExpression | TSESTree.LogicalExpression) {
return node.operator === '<<' && isLiteral(node.left) && node.left.value === 1;
}
const rule: Rule.RuleModule = {
meta: {
type: 'problem',
docs: {
description: 'Identical expressions should not be used on both sides of a binary operator',
category: 'Possible Errors',
recommended: 'error',
url: docsUrl(__filename),
},
schema: [
{
// internal parameter
enum: ['sonar-runtime'],
},
],
},
create(context: Rule.RuleContext) {
return {
LogicalExpression(node: TSESTree.Node) {
check(node as TSESTree.LogicalExpression);
},
BinaryExpression(node: TSESTree.Node) {
check(node as TSESTree.BinaryExpression);
},
};
function check(expr: TSESTree.BinaryExpression | TSESTree.LogicalExpression) {
if (
hasRelevantOperator(expr) &&
!isOneOntoOneShifting(expr) &&
areEquivalent(expr.left, expr.right, context.getSourceCode())
) {
const secondaryLocations: IssueLocation[] = [];
if (expr.left.loc) {
secondaryLocations.push(issueLocation(expr.left.loc));
}
report(
context,
{
message: message(expr.operator),
node: isSonarRuntime() ? expr.right : expr,
},
secondaryLocations,
);
}
}
function isSonarRuntime() {
return context.options[context.options.length - 1] === 'sonar-runtime';
}
},
};
export = rule;