Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d85e291

Browse files
authored
Fix: yoda left string fix for exceptRange (fixes #12883) (#13052)
* Fix: yoda left string fix for exceptRange * Chore: aded string check for isOutsideTest yoda * Fix: removed inconsistency btn never and always * Chore: fixed false negatives * Chore: fixed false negative string <= number * Chore: simplify range checks (yoda) * Chore: fixed false negative and added test * Chore: removed un-neccesary comment for defaultValue * Chore: removed un-neccesary checks * Chore: added removed tests * Chore: linting fixes
1 parent 2ce6bed commit d85e291

File tree

2 files changed

+415
-138
lines changed

2 files changed

+415
-138
lines changed

lib/rules/yoda.js

Lines changed: 101 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const astUtils = require("./utils/ast-utils");
2020
* @returns {boolean} Whether or not it is a comparison operator.
2121
*/
2222
function isComparisonOperator(operator) {
23-
return (/^(==|===|!=|!==|<|>|<=|>=)$/u).test(operator);
23+
return /^(==|===|!=|!==|<|>|<=|>=)$/u.test(operator);
2424
}
2525

2626
/**
@@ -29,7 +29,7 @@ function isComparisonOperator(operator) {
2929
* @returns {boolean} Whether or not it is an equality operator.
3030
*/
3131
function isEqualityOperator(operator) {
32-
return (/^(==|===)$/u).test(operator);
32+
return /^(==|===)$/u.test(operator);
3333
}
3434

3535
/**
@@ -50,10 +50,12 @@ function isRangeTestOperator(operator) {
5050
* real literal and should be treated as such.
5151
*/
5252
function isNegativeNumericLiteral(node) {
53-
return (node.type === "UnaryExpression" &&
53+
return (
54+
node.type === "UnaryExpression" &&
5455
node.operator === "-" &&
5556
node.prefix &&
56-
astUtils.isNumericLiteral(node.argument));
57+
astUtils.isNumericLiteral(node.argument)
58+
);
5759
}
5860

5961
/**
@@ -71,25 +73,21 @@ function isStaticTemplateLiteral(node) {
7173
* @returns {boolean} True if the node should be treated as a single Literal node.
7274
*/
7375
function looksLikeLiteral(node) {
74-
return isNegativeNumericLiteral(node) ||
75-
isStaticTemplateLiteral(node);
76+
return isNegativeNumericLiteral(node) || isStaticTemplateLiteral(node);
7677
}
7778

7879
/**
7980
* Attempts to derive a Literal node from nodes that are treated like literals.
8081
* @param {ASTNode} node Node to normalize.
81-
* @param {number} [defaultValue] The default value to be returned if the node
82-
* is not a Literal.
8382
* @returns {ASTNode} One of the following options.
8483
* 1. The original node if the node is already a Literal
8584
* 2. A normalized Literal node with the negative number as the value if the
8685
* node represents a negative number literal.
8786
* 3. A normalized Literal node with the string as the value if the node is
8887
* a Template Literal without expression.
89-
* 4. The Literal node which has the `defaultValue` argument if it exists.
90-
* 5. Otherwise `null`.
88+
* 4. Otherwise `null`.
9189
*/
92-
function getNormalizedLiteral(node, defaultValue) {
90+
function getNormalizedLiteral(node) {
9391
if (node.type === "Literal") {
9492
return node;
9593
}
@@ -110,14 +108,6 @@ function getNormalizedLiteral(node, defaultValue) {
110108
};
111109
}
112110

113-
if (defaultValue) {
114-
return {
115-
type: "Literal",
116-
value: defaultValue,
117-
raw: String(defaultValue)
118-
};
119-
}
120-
121111
return null;
122112
}
123113

@@ -183,7 +173,7 @@ module.exports = {
183173
type: "suggestion",
184174

185175
docs: {
186-
description: "require or disallow \"Yoda\" conditions",
176+
description: 'require or disallow "Yoda" conditions',
187177
category: "Best Practices",
188178
recommended: false,
189179
url: "https://eslint.org/docs/rules/yoda"
@@ -211,16 +201,19 @@ module.exports = {
211201

212202
fixable: "code",
213203
messages: {
214-
expected: "Expected literal to be on the {{expectedSide}} side of {{operator}}."
204+
expected:
205+
"Expected literal to be on the {{expectedSide}} side of {{operator}}."
215206
}
216207
},
217208

218209
create(context) {
219210

220211
// Default to "never" (!always) if no option
221-
const always = (context.options[0] === "always");
222-
const exceptRange = (context.options[1] && context.options[1].exceptRange);
223-
const onlyEquality = (context.options[1] && context.options[1].onlyEquality);
212+
const always = context.options[0] === "always";
213+
const exceptRange =
214+
context.options[1] && context.options[1].exceptRange;
215+
const onlyEquality =
216+
context.options[1] && context.options[1].onlyEquality;
224217

225218
const sourceCode = context.getSourceCode();
226219

@@ -243,27 +236,48 @@ module.exports = {
243236
* @returns {boolean} Whether node is a "between" range test.
244237
*/
245238
function isBetweenTest() {
246-
let leftLiteral, rightLiteral;
239+
if (node.operator === "&&" && same(left.right, right.left)) {
240+
const leftLiteral = getNormalizedLiteral(left.left);
241+
const rightLiteral = getNormalizedLiteral(right.right);
242+
243+
if (leftLiteral === null && rightLiteral === null) {
244+
return false;
245+
}
247246

248-
return (node.operator === "&&" &&
249-
(leftLiteral = getNormalizedLiteral(left.left)) &&
250-
(rightLiteral = getNormalizedLiteral(right.right, Number.POSITIVE_INFINITY)) &&
251-
leftLiteral.value <= rightLiteral.value &&
252-
same(left.right, right.left));
247+
if (rightLiteral === null || leftLiteral === null) {
248+
return true;
249+
}
250+
251+
if (leftLiteral.value <= rightLiteral.value) {
252+
return true;
253+
}
254+
}
255+
return false;
253256
}
254257

255258
/**
256259
* Determines whether node is of the form `x < 0 || 1 <= x`.
257260
* @returns {boolean} Whether node is an "outside" range test.
258261
*/
259262
function isOutsideTest() {
260-
let leftLiteral, rightLiteral;
263+
if (node.operator === "||" && same(left.left, right.right)) {
264+
const leftLiteral = getNormalizedLiteral(left.right);
265+
const rightLiteral = getNormalizedLiteral(right.left);
266+
267+
if (leftLiteral === null && rightLiteral === null) {
268+
return false;
269+
}
270+
271+
if (rightLiteral === null || leftLiteral === null) {
272+
return true;
273+
}
274+
275+
if (leftLiteral.value <= rightLiteral.value) {
276+
return true;
277+
}
278+
}
261279

262-
return (node.operator === "||" &&
263-
(leftLiteral = getNormalizedLiteral(left.right, Number.NEGATIVE_INFINITY)) &&
264-
(rightLiteral = getNormalizedLiteral(right.left)) &&
265-
leftLiteral.value <= rightLiteral.value &&
266-
same(left.left, right.right));
280+
return false;
267281
}
268282

269283
/**
@@ -276,13 +290,15 @@ module.exports = {
276290
return astUtils.isParenthesised(sourceCode, node);
277291
}
278292

279-
return (node.type === "LogicalExpression" &&
293+
return (
294+
node.type === "LogicalExpression" &&
280295
left.type === "BinaryExpression" &&
281296
right.type === "BinaryExpression" &&
282297
isRangeTestOperator(left.operator) &&
283298
isRangeTestOperator(right.operator) &&
284299
(isBetweenTest() || isOutsideTest()) &&
285-
isParenWrapped());
300+
isParenWrapped()
301+
);
286302
}
287303

288304
const OPERATOR_FLIP_MAP = {
@@ -303,21 +319,52 @@ module.exports = {
303319
*/
304320
function getFlippedString(node) {
305321
const tokenBefore = sourceCode.getTokenBefore(node);
306-
const operatorToken = sourceCode.getFirstTokenBetween(node.left, node.right, token => token.value === node.operator);
307-
const textBeforeOperator = sourceCode.getText().slice(sourceCode.getTokenBefore(operatorToken).range[1], operatorToken.range[0]);
308-
const textAfterOperator = sourceCode.getText().slice(operatorToken.range[1], sourceCode.getTokenAfter(operatorToken).range[0]);
309-
const leftText = sourceCode.getText().slice(node.range[0], sourceCode.getTokenBefore(operatorToken).range[1]);
322+
const operatorToken = sourceCode.getFirstTokenBetween(
323+
node.left,
324+
node.right,
325+
token => token.value === node.operator
326+
);
327+
const textBeforeOperator = sourceCode
328+
.getText()
329+
.slice(
330+
sourceCode.getTokenBefore(operatorToken).range[1],
331+
operatorToken.range[0]
332+
);
333+
const textAfterOperator = sourceCode
334+
.getText()
335+
.slice(
336+
operatorToken.range[1],
337+
sourceCode.getTokenAfter(operatorToken).range[0]
338+
);
339+
const leftText = sourceCode
340+
.getText()
341+
.slice(
342+
node.range[0],
343+
sourceCode.getTokenBefore(operatorToken).range[1]
344+
);
310345
const firstRightToken = sourceCode.getTokenAfter(operatorToken);
311-
const rightText = sourceCode.getText().slice(firstRightToken.range[0], node.range[1]);
346+
const rightText = sourceCode
347+
.getText()
348+
.slice(firstRightToken.range[0], node.range[1]);
312349

313350
let prefix = "";
314351

315-
if (tokenBefore && tokenBefore.range[1] === node.range[0] &&
316-
!astUtils.canTokensBeAdjacent(tokenBefore, firstRightToken)) {
352+
if (
353+
tokenBefore &&
354+
tokenBefore.range[1] === node.range[0] &&
355+
!astUtils.canTokensBeAdjacent(tokenBefore, firstRightToken)
356+
) {
317357
prefix = " ";
318358
}
319359

320-
return prefix + rightText + textBeforeOperator + OPERATOR_FLIP_MAP[operatorToken.value] + textAfterOperator + leftText;
360+
return (
361+
prefix +
362+
rightText +
363+
textBeforeOperator +
364+
OPERATOR_FLIP_MAP[operatorToken.value] +
365+
textAfterOperator +
366+
leftText
367+
);
321368
}
322369

323370
//--------------------------------------------------------------------------
@@ -331,8 +378,12 @@ module.exports = {
331378

332379
// If `expectedLiteral` is not a literal, and `expectedNonLiteral` is a literal, raise an error.
333380
if (
334-
(expectedNonLiteral.type === "Literal" || looksLikeLiteral(expectedNonLiteral)) &&
335-
!(expectedLiteral.type === "Literal" || looksLikeLiteral(expectedLiteral)) &&
381+
(expectedNonLiteral.type === "Literal" ||
382+
looksLikeLiteral(expectedNonLiteral)) &&
383+
!(
384+
expectedLiteral.type === "Literal" ||
385+
looksLikeLiteral(expectedLiteral)
386+
) &&
336387
!(!isEqualityOperator(node.operator) && onlyEquality) &&
337388
isComparisonOperator(node.operator) &&
338389
!(exceptRange && isRangeTest(context.getAncestors().pop()))
@@ -344,12 +395,11 @@ module.exports = {
344395
operator: node.operator,
345396
expectedSide: always ? "left" : "right"
346397
},
347-
fix: fixer => fixer.replaceText(node, getFlippedString(node))
398+
fix: fixer =>
399+
fixer.replaceText(node, getFlippedString(node))
348400
});
349401
}
350-
351402
}
352403
};
353-
354404
}
355405
};

0 commit comments

Comments
 (0)