Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 05f7dd5

Browse filesBrowse files
mdjermanovicbtmills
authored andcommitted
Update: Add suggestions for no-unsafe-negation (fixes #12591) (#12609)
1 parent d3e43f1 commit 05f7dd5
Copy full SHA for 05f7dd5

File tree

Expand file treeCollapse file tree

2 files changed

+183
-37
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

2 files changed

+183
-37
lines changed
Open diff view settings
Collapse file

‎lib/rules/no-unsafe-negation.js‎

Copy file name to clipboardExpand all lines: lib/rules/no-unsafe-negation.js
+30-5Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ module.exports = {
5454
description: "disallow negating the left operand of relational operators",
5555
category: "Possible Errors",
5656
recommended: true,
57-
url: "https://eslint.org/docs/rules/no-unsafe-negation"
57+
url: "https://eslint.org/docs/rules/no-unsafe-negation",
58+
suggestion: true
5859
},
5960

6061
schema: [
@@ -69,9 +70,13 @@ module.exports = {
6970
additionalProperties: false
7071
}
7172
],
73+
7274
fixable: null,
75+
7376
messages: {
74-
unexpected: "Unexpected negating the left operand of '{{operator}}' operator."
77+
unexpected: "Unexpected negating the left operand of '{{operator}}' operator.",
78+
suggestNegatedExpression: "Negate '{{operator}}' expression instead of its left operand. This changes the current behavior.",
79+
suggestParenthesisedNegation: "Wrap negation in '()' to make the intention explicit. This preserves the current behavior."
7580
}
7681
},
7782

@@ -82,18 +87,38 @@ module.exports = {
8287

8388
return {
8489
BinaryExpression(node) {
85-
const orderingRelationRuleApplies = enforceForOrderingRelations && isOrderingRelationalOperator(node.operator);
90+
const operator = node.operator;
91+
const orderingRelationRuleApplies = enforceForOrderingRelations && isOrderingRelationalOperator(operator);
8692

8793
if (
88-
(isInOrInstanceOfOperator(node.operator) || orderingRelationRuleApplies) &&
94+
(isInOrInstanceOfOperator(operator) || orderingRelationRuleApplies) &&
8995
isNegation(node.left) &&
9096
!astUtils.isParenthesised(sourceCode, node.left)
9197
) {
9298
context.report({
9399
node,
94100
loc: node.left.loc,
95101
messageId: "unexpected",
96-
data: { operator: node.operator }
102+
data: { operator },
103+
suggest: [
104+
{
105+
messageId: "suggestNegatedExpression",
106+
data: { operator },
107+
fix(fixer) {
108+
const negationToken = sourceCode.getFirstToken(node.left);
109+
const fixRange = [negationToken.range[1], node.range[1]];
110+
const text = sourceCode.text.slice(fixRange[0], fixRange[1]);
111+
112+
return fixer.replaceTextRange(fixRange, `(${text})`);
113+
}
114+
},
115+
{
116+
messageId: "suggestParenthesisedNegation",
117+
fix(fixer) {
118+
return fixer.replaceText(node.left, `(${sourceCode.getText(node.left)})`);
119+
}
120+
}
121+
]
97122
});
98123
}
99124
}
Collapse file

‎tests/lib/rules/no-unsafe-negation.js‎

Copy file name to clipboardExpand all lines: tests/lib/rules/no-unsafe-negation.js
+153-32Lines changed: 153 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,6 @@ const rule = require("../../../lib/rules/no-unsafe-negation"),
1717
//------------------------------------------------------------------------------
1818

1919
const ruleTester = new RuleTester();
20-
const unexpectedInError = { messageId: "unexpected", data: { operator: "in" } };
21-
const unexpectedInstanceofError = {
22-
messageId: "unexpected",
23-
data: { operator: "instanceof" }
24-
};
25-
const unexpectedLessThanOperatorError = {
26-
messageId: "unexpected",
27-
data: { operator: "<" }
28-
};
29-
const unexpectedMoreThanOperatorError = {
30-
messageId: "unexpected",
31-
data: { operator: ">" }
32-
};
33-
const unexpectedMoreThanOrEqualOperatorError = {
34-
messageId: "unexpected",
35-
data: { operator: ">=" }
36-
};
37-
const unexpectedLessThanOrEqualOperatorError = {
38-
messageId: "unexpected",
39-
data: { operator: "<=" }
40-
};
4120

4221
ruleTester.run("no-unsafe-negation", rule, {
4322
valid: [
@@ -83,52 +62,194 @@ ruleTester.run("no-unsafe-negation", rule, {
8362
invalid: [
8463
{
8564
code: "!a in b",
86-
errors: [unexpectedInError]
65+
errors: [{
66+
message: "Unexpected negating the left operand of 'in' operator.",
67+
suggestions: [
68+
{
69+
desc: "Negate 'in' expression instead of its left operand. This changes the current behavior.",
70+
output: "!(a in b)"
71+
},
72+
{
73+
desc: "Wrap negation in '()' to make the intention explicit. This preserves the current behavior.",
74+
output: "(!a) in b"
75+
}
76+
]
77+
}]
8778
},
8879
{
8980
code: "(!a in b)",
90-
errors: [unexpectedInError]
81+
errors: [{
82+
messageId: "unexpected",
83+
data: { operator: "in" },
84+
suggestions: [
85+
{
86+
messageId: "suggestNegatedExpression",
87+
output: "(!(a in b))"
88+
},
89+
{
90+
messageId: "suggestParenthesisedNegation",
91+
output: "((!a) in b)"
92+
}
93+
]
94+
}]
9195
},
9296
{
9397
code: "!(a) in b",
94-
errors: [unexpectedInError]
98+
errors: [{
99+
messageId: "unexpected",
100+
data: { operator: "in" },
101+
suggestions: [
102+
{
103+
messageId: "suggestNegatedExpression",
104+
output: "!((a) in b)"
105+
},
106+
{
107+
messageId: "suggestParenthesisedNegation",
108+
output: "(!(a)) in b"
109+
}
110+
]
111+
}]
95112
},
96113
{
97114
code: "!a instanceof b",
98-
errors: [unexpectedInstanceofError]
115+
errors: [{
116+
messageId: "unexpected",
117+
data: { operator: "instanceof" },
118+
suggestions: [
119+
{
120+
messageId: "suggestNegatedExpression",
121+
output: "!(a instanceof b)"
122+
},
123+
{
124+
messageId: "suggestParenthesisedNegation",
125+
output: "(!a) instanceof b"
126+
}
127+
]
128+
}]
99129
},
100130
{
101131
code: "(!a instanceof b)",
102-
errors: [unexpectedInstanceofError]
132+
errors: [{
133+
messageId: "unexpected",
134+
data: { operator: "instanceof" },
135+
suggestions: [
136+
{
137+
messageId: "suggestNegatedExpression",
138+
output: "(!(a instanceof b))"
139+
},
140+
{
141+
messageId: "suggestParenthesisedNegation",
142+
output: "((!a) instanceof b)"
143+
}
144+
]
145+
}]
103146
},
104147
{
105148
code: "!(a) instanceof b",
106-
errors: [unexpectedInstanceofError]
149+
errors: [{
150+
messageId: "unexpected",
151+
data: { operator: "instanceof" },
152+
suggestions: [
153+
{
154+
messageId: "suggestNegatedExpression",
155+
output: "!((a) instanceof b)"
156+
},
157+
{
158+
messageId: "suggestParenthesisedNegation",
159+
output: "(!(a)) instanceof b"
160+
}
161+
]
162+
}]
107163
},
108164
{
109165
code: "if (! a < b) {}",
110166
options: [{ enforceForOrderingRelations: true }],
111-
errors: [unexpectedLessThanOperatorError]
167+
errors: [{
168+
messageId: "unexpected",
169+
data: { operator: "<" },
170+
suggestions: [
171+
{
172+
messageId: "suggestNegatedExpression",
173+
output: "if (!( a < b)) {}"
174+
},
175+
{
176+
messageId: "suggestParenthesisedNegation",
177+
output: "if ((! a) < b) {}"
178+
}
179+
]
180+
}]
112181
},
113182
{
114183
code: "while (! a > b) {}",
115184
options: [{ enforceForOrderingRelations: true }],
116-
errors: [unexpectedMoreThanOperatorError]
185+
errors: [{
186+
messageId: "unexpected",
187+
data: { operator: ">" },
188+
suggestions: [
189+
{
190+
messageId: "suggestNegatedExpression",
191+
output: "while (!( a > b)) {}"
192+
},
193+
{
194+
messageId: "suggestParenthesisedNegation",
195+
output: "while ((! a) > b) {}"
196+
}
197+
]
198+
}]
117199
},
118200
{
119201
code: "foo = ! a <= b;",
120202
options: [{ enforceForOrderingRelations: true }],
121-
errors: [unexpectedLessThanOrEqualOperatorError]
203+
errors: [{
204+
messageId: "unexpected",
205+
data: { operator: "<=" },
206+
suggestions: [
207+
{
208+
messageId: "suggestNegatedExpression",
209+
output: "foo = !( a <= b);"
210+
},
211+
{
212+
messageId: "suggestParenthesisedNegation",
213+
output: "foo = (! a) <= b;"
214+
}
215+
]
216+
}]
122217
},
123218
{
124219
code: "foo = ! a >= b;",
125220
options: [{ enforceForOrderingRelations: true }],
126-
errors: [unexpectedMoreThanOrEqualOperatorError]
221+
errors: [{
222+
messageId: "unexpected",
223+
data: { operator: ">=" },
224+
suggestions: [
225+
{
226+
messageId: "suggestNegatedExpression",
227+
output: "foo = !( a >= b);"
228+
},
229+
{
230+
messageId: "suggestParenthesisedNegation",
231+
output: "foo = (! a) >= b;"
232+
}
233+
]
234+
}]
127235
},
128236
{
129237
code: "! a <= b",
130238
options: [{ enforceForOrderingRelations: true }],
131-
errors: [unexpectedLessThanOrEqualOperatorError]
239+
errors: [{
240+
messageId: "unexpected",
241+
data: { operator: "<=" },
242+
suggestions: [
243+
{
244+
messageId: "suggestNegatedExpression",
245+
output: "!( a <= b)"
246+
},
247+
{
248+
messageId: "suggestParenthesisedNegation",
249+
output: "(! a) <= b"
250+
}
251+
]
252+
}]
132253
}
133254
]
134255
});

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.