Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: yoda left string fix for exceptRange (fixes #12883) #13052

Merged
merged 13 commits into from Apr 24, 2020
Merged
60 changes: 37 additions & 23 deletions lib/rules/yoda.js
Expand Up @@ -78,18 +78,15 @@ function looksLikeLiteral(node) {
/**
* Attempts to derive a Literal node from nodes that are treated like literals.
* @param {ASTNode} node Node to normalize.
* @param {number} [defaultValue] The default value to be returned if the node
* is not a Literal.
* @returns {ASTNode} One of the following options.
* 1. The original node if the node is already a Literal
* 2. A normalized Literal node with the negative number as the value if the
* node represents a negative number literal.
* 3. A normalized Literal node with the string as the value if the node is
* a Template Literal without expression.
* 4. The Literal node which has the `defaultValue` argument if it exists.
* 5. Otherwise `null`.
* 4. Otherwise `null`.
*/
function getNormalizedLiteral(node, defaultValue) {
function getNormalizedLiteral(node) {
anikethsaha marked this conversation as resolved.
Show resolved Hide resolved
if (node.type === "Literal") {
return node;
}
Expand All @@ -110,14 +107,6 @@ function getNormalizedLiteral(node, defaultValue) {
};
}

if (defaultValue) {
return {
type: "Literal",
value: defaultValue,
raw: String(defaultValue)
};
}

return null;
}

Expand Down Expand Up @@ -245,11 +234,23 @@ module.exports = {
function isBetweenTest() {
let leftLiteral, rightLiteral;

return (node.operator === "&&" &&
(leftLiteral = getNormalizedLiteral(left.left)) &&
(rightLiteral = getNormalizedLiteral(right.right, Number.POSITIVE_INFINITY)) &&
leftLiteral.value <= rightLiteral.value &&
same(left.right, right.left));
if (node.operator === "&&" && same(left.right, right.left)) {
leftLiteral = getNormalizedLiteral(left.left);
rightLiteral = getNormalizedLiteral(right.right);
anikethsaha marked this conversation as resolved.
Show resolved Hide resolved

if (leftLiteral === null && rightLiteral === null) {
return false;
}

if (leftLiteral !== null && rightLiteral === null || rightLiteral !== null && leftLiteral === null) {
anikethsaha marked this conversation as resolved.
Show resolved Hide resolved
mdjermanovic marked this conversation as resolved.
Show resolved Hide resolved
return true;
}
anikethsaha marked this conversation as resolved.
Show resolved Hide resolved

if (leftLiteral.value <= rightLiteral.value) {
return true;
}
}
return false;
}

/**
Expand All @@ -259,11 +260,24 @@ module.exports = {
function isOutsideTest() {
let leftLiteral, rightLiteral;

return (node.operator === "||" &&
(leftLiteral = getNormalizedLiteral(left.right, Number.NEGATIVE_INFINITY)) &&
(rightLiteral = getNormalizedLiteral(right.left)) &&
leftLiteral.value <= rightLiteral.value &&
same(left.left, right.right));
if (node.operator === "||" && same(left.left, right.right)) {
leftLiteral = getNormalizedLiteral(left.right);
rightLiteral = getNormalizedLiteral(right.left);

if (leftLiteral === null && rightLiteral === null) {
return false;
}

if (leftLiteral !== null && rightLiteral === null || rightLiteral !== null && leftLiteral === null) {
return true;
}

if ((leftLiteral.value <= rightLiteral.value)) {
return true;
}
}

return false;
}

/**
Expand Down
183 changes: 149 additions & 34 deletions tests/lib/rules/yoda.js
Expand Up @@ -50,17 +50,28 @@ ruleTester.run("yoda", rule, {

// Range exception
{
code: "if (0 < x && x <= 1) {}",
code: "if (\"a\" < x && x < MAX ) {}",
options: ["never", { exceptRange: true }]
}, {
code: "if (x < 0 || 1 <= x) {}",
},
{
code: "if (1 < x && x < MAX ) {}",
options: ["never", { exceptRange: true }]
},
{
code: "if ('a' < x && x < MAX ) {}",
options: ["never", { exceptRange: true }]
},
{
code: "if (x < `x` || `x` <= x) {}",
options: ["never", { exceptRange: true }],
parserOptions: { ecmaVersion: 2015 }
},
{
code: "if (0 < x && x <= 1) {}",
options: ["never", { exceptRange: true }]
}, {
code: "if (0 <= x && x < 1) {}",
options: ["always", { exceptRange: true }]
}, {
code: "if (x <= 'bar' || 'foo' < x) {}",
options: ["always", { exceptRange: true }]
}, {
code: "if ('blue' < x.y && x.y < 'green') {}",
options: ["never", { exceptRange: true }]
Expand All @@ -72,6 +83,9 @@ ruleTester.run("yoda", rule, {
code: "if (0 < x[''] && x[``] < 100) {}",
options: ["never", { exceptRange: true }],
parserOptions: { ecmaVersion: 2015 }
}, {
code: "if (a < 4 || (b[c[0]].d['e'] < 0 || 1 <= b[c[0]].d['e'])) {}",
options: ["never", { exceptRange: true }]
}, {
code: "if (0 <= x['y'] && x['y'] <= 100) {}",
options: ["never", { exceptRange: true }]
Expand All @@ -81,9 +95,6 @@ ruleTester.run("yoda", rule, {
}, {
code: "if ((0 < a && a < 1) && b < 0) {}",
options: ["never", { exceptRange: true }]
}, {
code: "if (a < 4 || (b[c[0]].d['e'] < 0 || 1 <= b[c[0]].d['e'])) {}",
options: ["never", { exceptRange: true }]
}, {
code: "if (-1 < x && x < 0) {}",
options: ["never", { exceptRange: true }]
Expand All @@ -96,10 +107,12 @@ ruleTester.run("yoda", rule, {
}, {
code: "if (ZERO <= index && index < 100) {}",
options: ["never", { exceptRange: true }]
}, {
},
{
code: "if (value <= MIN || 10 < value) {}",
options: ["never", { exceptRange: true }]
}, {
},
{
code: "if (value <= 0 || MAX < value) {}",
options: ["never", { exceptRange: true }]
}, {
Expand All @@ -113,18 +126,10 @@ ruleTester.run("yoda", rule, {
code: "if (-1n < x && x <= 1n) {}",
options: ["never", { exceptRange: true }],
parserOptions: { ecmaVersion: 2020 }
}, {
code: "if (x < -1n || 1n <= x) {}",
options: ["never", { exceptRange: true }],
parserOptions: { ecmaVersion: 2020 }
}, {
code: "if (-1n <= x && x < 1n) {}",
options: ["always", { exceptRange: true }],
parserOptions: { ecmaVersion: 2020 }
}, {
code: "if (x < -1n || 1n <= x) {}",
options: ["always", { exceptRange: true }],
parserOptions: { ecmaVersion: 2020 }
}, {
code: "if (x < `1` || `1` < x) {}",
options: ["always", { exceptRange: true }],
Expand All @@ -134,23 +139,72 @@ ruleTester.run("yoda", rule, {
options: ["never", { exceptRange: true }],
parserOptions: { ecmaVersion: 2018 }
}, {
code: "if (x <= `bar` || `foo` < x) {}",
anikethsaha marked this conversation as resolved.
Show resolved Hide resolved
code: "if ('a' < x && x < MAX ) {}",
options: ["always", { exceptRange: true }],
parserOptions: { ecmaVersion: 2015 }
}, {
code: "if (`blue` < x.y && x.y < `green`) {}",
code: "if ('a' < x && x < MAX ) {}",
options: ["always"],
parserOptions: { ecmaVersion: 2015 }
},
{
code: "if (MIN < x && x < 'a' ) {}",
options: ["never", { exceptRange: true }],
parserOptions: { ecmaVersion: 2015 }
}, {
code: "if (MIN < x && x < 'a' ) {}",
options: ["never"],
parserOptions: { ecmaVersion: 2015 }
},
{
code: "if (`blue` < x.y && x.y < `green`) {}",
options: ["never", { exceptRange: true }],
parserOptions: { ecmaVersion: 2015 }
},
{
code: "if (0 <= x[`y`] && x[`y`] <= 100) {}",
options: ["never", { exceptRange: true }],
parserOptions: { ecmaVersion: 2015 }
}, {
code: "if (0 <= x[`y`] && x[\"y\"] <= 100) {}",
options: ["never", { exceptRange: true }],
parserOptions: { ecmaVersion: 2015 }
}, {
code: "if ('a' <= x && x < 'b') {}",
options: ["never", { exceptRange: true }]
},
{
code: "if (x < -1n || 1n <= x) {}",
options: ["never", { exceptRange: true }],
parserOptions: { ecmaVersion: 2020 }
},
{
code: "if (x < -1n || 1n <= x) {}",
options: ["always", { exceptRange: true }],
parserOptions: { ecmaVersion: 2020 }
},
{
code: "if (1 < a && a <= 2) {}",
options: ["never", { exceptRange: true }]
},
{
code: "if (x < -1 || 1 < x) {}",
options: ["never", { exceptRange: true }]
},
{
code: "if (x <= 'bar' || 'foo' < x) {}",
options: ["always", { exceptRange: true }]
},
{
code: "if (x < 0 || 1 <= x) {}",
options: ["never", { exceptRange: true }]
},
{
code: "if('a' <= x && x < MAX) {}",
options: ["never", { exceptRange: true }]
},


// onlyEquality
{ code: "if (0 < x && x <= 1) {}", options: ["never", { onlyEquality: true }] },
{ code: "if (x !== 'foo' && 'foo' !== x) {}", options: ["never", { onlyEquality: true }] },
Expand All @@ -159,7 +213,19 @@ ruleTester.run("yoda", rule, {
{ code: "if (x < `2` && x !== `-3`) {}", options: ["always", { onlyEquality: true }], parserOptions: { ecmaVersion: 2015 } }
],
invalid: [
{
code: "if (x <= 'foo' || 'bar' < x) {}",
output: "if ('foo' >= x || 'bar' < x) {}",
options: ["always", { exceptRange: true }],
errors: [
{
messageId: "expected",
data: { expectedSide: "left", operator: "<=" },
type: "BinaryExpression"
}
]

},
{
code: "if (\"red\" == value) {}",
output: "if (value == \"red\") {}",
Expand Down Expand Up @@ -641,19 +707,6 @@ ruleTester.run("yoda", rule, {
}
]
},
{
code: "if (`green` < x.y && x.y < `blue`) {}",
output: "if (x.y > `green` && x.y < `blue`) {}",
options: ["never", { exceptRange: true }],
anikethsaha marked this conversation as resolved.
Show resolved Hide resolved
parserOptions: { ecmaVersion: 2015 },
errors: [
{
messageId: "expected",
data: { expectedSide: "right", operator: "<" },
type: "BinaryExpression"
}
]
},
{
code: "if (3 == a) {}",
output: "if (a == 3) {}",
Expand Down Expand Up @@ -992,6 +1045,68 @@ ruleTester.run("yoda", rule, {
type: "BinaryExpression"
}
]
},
{
code: "if (`green` < x.y && x.y < `blue`) {}",
anikethsaha marked this conversation as resolved.
Show resolved Hide resolved
output: "if (`green` < x.y && `blue` > x.y) {}",
options: ["always", { exceptRange: true }],
parserOptions: { ecmaVersion: 2015 },
errors: [
{
messageId: "expected",
data: { expectedSide: "left", operator: "<" },
type: "BinaryExpression"
}
]
},
{
code: "if('a' <= x && x < 'b') {}",
anikethsaha marked this conversation as resolved.
Show resolved Hide resolved
output: "if('a' <= x && 'b' > x) {}",
options: ["always"],
errors: [
{
messageId: "expected",
data: { expectedSide: "left", operator: "<" },
type: "BinaryExpression"
}
]
},
{
code: "if ('b' <= x && x < 'a') {}",
anikethsaha marked this conversation as resolved.
Show resolved Hide resolved
output: "if (x >= 'b' && x < 'a') {}",
options: ["never", { exceptRange: true }],
errors: [
{
messageId: "expected",
data: { expectedSide: "right", operator: "<=" },
type: "BinaryExpression"
}
]
},
{
code: "if('a' <= x && x < 1) {}",
output: "if(x >= 'a' && x < 1) {}",
options: ["never", { exceptRange: true }],
errors: [
{
messageId: "expected",
data: { expectedSide: "right", operator: "<=" },
type: "BinaryExpression"
}
]
},
{
code: "if (0 < a && b < max) {}",
output: "if (a > 0 && b < max) {}",
options: ["never", { exceptRange: true }],
errors: [
{
messageId: "expected",
data: { expectedSide: "right", operator: "<" },
type: "BinaryExpression"
}
]
}

]
});