Skip to content

Commit

Permalink
Fix: check template literal in yoda (fixes #12863)
Browse files Browse the repository at this point in the history
  • Loading branch information
yeonjuan committed Feb 5, 2020
1 parent 540de8e commit e58f456
Show file tree
Hide file tree
Showing 2 changed files with 184 additions and 3 deletions.
31 changes: 29 additions & 2 deletions lib/rules/yoda.js
Expand Up @@ -49,13 +49,32 @@ function isRangeTestOperator(operator) {
* @returns {boolean} True if the node is a negative number that looks like a
* real literal and should be treated as such.
*/
function looksLikeLiteral(node) {
function isNegativeNumericLiteral(node) {
return (node.type === "UnaryExpression" &&
node.operator === "-" &&
node.prefix &&
astUtils.isNumericLiteral(node.argument));
}

/**
* Determines whether a node is a Template Literal which can be determined statically.
* @param {*} node Node to test
* @returns {boolean} True if the node is a Template Literal without expression.
*/
function isStaticTemplateLiteral(node) {
return node.type === "TemplateLiteral" && node.expressions.length === 0;
}

/**
* Determines whether a non-Literal type node should be treated as a single Literal node.
* @param {*} node Node to test
* @returns {boolean} True if the node should be treated as a sinle Literal Node.
*/
function looksLikeLiteral(node) {
return isNegativeNumericLiteral(node) ||
isStaticTemplateLiteral(node);
}

/**
* Attempts to derive a Literal node from nodes that are treated like literals.
* @param {ASTNode} node Node to normalize.
Expand All @@ -73,14 +92,22 @@ function getNormalizedLiteral(node, defaultValue) {
return node;
}

if (looksLikeLiteral(node)) {
if (isNegativeNumericLiteral(node)) {
return {
type: "Literal",
value: -node.argument.value,
raw: `-${node.argument.value}`
};
}

if (isStaticTemplateLiteral(node)) {
return {
type: "Literal",
value: node.quasis[0].value.cooked,
raw: node.quasis[0].value.raw
};
}

if (defaultValue) {
return {
type: "Literal",
Expand Down
156 changes: 155 additions & 1 deletion tests/lib/rules/yoda.js
Expand Up @@ -26,13 +26,24 @@ ruleTester.run("yoda", rule, {
{ code: "if (value != 5) {}", options: ["never"] },
{ code: "if (5 & foo) {}", options: ["never"] },
{ code: "if (5 === 4) {}", options: ["never"] },
{ code: "if (value === `red`) {}", options: ["never"], parserOptions: { ecmaVersion: 2015 } },
{ code: "if (`red` === `red`) {}", options: ["never"], parserOptions: { ecmaVersion: 2015 } },
{ code: "if (`${foo}` === `red`) {}", options: ["never"], parserOptions: { ecmaVersion: 2015 } },
{ code: "if (`${\"\"}` === `red`) {}", options: ["never"], parserOptions: { ecmaVersion: 2015 } },
{ code: "if (b > `a` && b > `a`) {}", options: ["never"], parserOptions: { ecmaVersion: 2015 } },
{ code: "if (`b` > `a` && \"b\" > \"a\") {}", options: ["never"], parserOptions: { ecmaVersion: 2015 } },

// "always" mode
{ code: "if (\"blue\" === value) {}", options: ["always"] },
{ code: "if (value === value) {}", options: ["always"] },
{ code: "if (4 != value) {}", options: ["always"] },
{ code: "if (foo & 4) {}", options: ["always"] },
{ code: "if (5 === 4) {}", options: ["always"] },
{ code: "if (`red` === value) {}", options: ["always"], parserOptions: { ecmaVersion: 2015 } },
{ code: "if (`red` === `red`) {}", options: ["always"], parserOptions: { ecmaVersion: 2015 } },
{ code: "if (`red` === `${foo}`) {}", options: ["always"], parserOptions: { ecmaVersion: 2015 } },
{ code: "if (`red` === `${\"\"}`) {}", options: ["always"], parserOptions: { ecmaVersion: 2015 } },
{ code: "if (`a` > b && `a` > b) {}", options: ["always"], parserOptions: { ecmaVersion: 2015 } },

// Range exception
{
Expand Down Expand Up @@ -91,6 +102,10 @@ ruleTester.run("yoda", rule, {
}, {
code: "if (0 <= a.b && a[\"b\"] <= 100) {}",
options: ["never", { exceptRange: true }]
}, {
code: "if (0 <= a.b && a[`b`] <= 100) {}",
options: ["never", { exceptRange: true }],
parserOptions: { ecmaVersion: 2015 }
}, {
code: "if (-1n < x && x <= 1n) {}",
options: ["never", { exceptRange: true }],
Expand All @@ -107,16 +122,38 @@ ruleTester.run("yoda", rule, {
code: "if (x < -1n || 1n <= x) {}",
options: ["always", { exceptRange: true }],
parserOptions: { ecmaVersion: 2020 }
}, {
code: "if (x < `1` || `1` < x) {}",
options: ["always", { exceptRange: true }],
parserOptions: { ecmaVersion: 2020 }
}, {
code: "if (1 <= a['/(?<zero>0)/'] && a[/(?<zero>0)/] <= 100) {}",
options: ["never", { exceptRange: true }],
parserOptions: { ecmaVersion: 2018 }
}, {
code: "if (x <= `bar` || `foo` < x) {}",
options: ["always", { exceptRange: true }],
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 }
},

// onlyEquality
{ code: "if (0 < x && x <= 1) {}", options: ["never", { onlyEquality: true }] },
{ code: "if (x !== 'foo' && 'foo' !== x) {}", options: ["never", { onlyEquality: true }] },
{ code: "if (x < 2 && x !== -3) {}", options: ["always", { onlyEquality: true }] }
{ code: "if (x < 2 && x !== -3) {}", options: ["always", { onlyEquality: true }] },
{ code: "if (x !== `foo` && `foo` !== x) {}", options: ["never", { onlyEquality: true }], parserOptions: { ecmaVersion: 2015 } },
{ code: "if (x < `2` && x !== `-3`) {}", options: ["always", { onlyEquality: true }], parserOptions: { ecmaVersion: 2015 } }
],
invalid: [

Expand Down Expand Up @@ -193,6 +230,32 @@ ruleTester.run("yoda", rule, {
}
]
},
{
code: "if (`red` <= value) {}",
output: "if (value >= `red`) {}",
options: ["never"],
parserOptions: { ecmaVersion: 2015 },
errors: [
{
messageId: "expected",
data: { expectedSide: "right", operator: "<=" },
type: "BinaryExpression"
}
]
},
{
code: "if (`red` <= `${foo}`) {}",
output: "if (`${foo}` >= `red`) {}",
options: ["never"],
parserOptions: { ecmaVersion: 2015 },
errors: [
{
messageId: "expected",
data: { expectedSide: "right", operator: "<=" },
type: "BinaryExpression"
}
]
},
{
code: "if (true >= value) {}",
output: "if (value <= true) {}",
Expand Down Expand Up @@ -253,6 +316,19 @@ ruleTester.run("yoda", rule, {
}
]
},
{
code: "if (value == `red`) {}",
output: "if (`red` == value) {}",
options: ["always"],
parserOptions: { ecmaVersion: 2015 },
errors: [
{
messageId: "expected",
data: { expectedSide: "left", operator: "==" },
type: "BinaryExpression"
}
]
},
{
code: "if (value === true) {}",
output: "if (true === value) {}",
Expand Down Expand Up @@ -362,6 +438,19 @@ ruleTester.run("yoda", rule, {
}
]
},
{
code: "var a = (b < `0` && `0` <= b);",
output: "var a = (`0` > b && `0` <= b);",
options: ["always", { exceptRange: true }],
parserOptions: { ecmaVersion: 2015 },
errors: [
{
messageId: "expected",
data: { expectedSide: "left", operator: "<" },
type: "BinaryExpression"
}
]
},
{
code: "if (0 <= a[b] && a['b'] < 1) {}",
output: "if (a[b] >= 0 && a['b'] < 1) {}",
Expand All @@ -374,6 +463,32 @@ ruleTester.run("yoda", rule, {
}
]
},
{
code: "if (0 <= a[b] && a[`b`] < 1) {}",
output: "if (a[b] >= 0 && a[`b`] < 1) {}",
options: ["never", { exceptRange: true }],
parserOptions: { ecmaVersion: 2015 },
errors: [
{
messageId: "expected",
data: { expectedSide: "right", operator: "<=" },
type: "BinaryExpression"
}
]
},
{
code: "if (`0` <= a[b] && a[`b`] < `1`) {}",
output: "if (a[b] >= `0` && a[`b`] < `1`) {}",
options: ["never", { exceptRange: true }],
parserOptions: { ecmaVersion: 2015 },
errors: [
{
messageId: "expected",
data: { expectedSide: "right", operator: "<=" },
type: "BinaryExpression"
}
]
},
{
code: "if (0 <= a[b] && a.b < 1) {}",
output: "if (a[b] >= 0 && a.b < 1) {}",
Expand Down Expand Up @@ -422,6 +537,19 @@ ruleTester.run("yoda", rule, {
}
]
},
{
code: "if (0 <= a[``] && a[null] < 1) {}",
output: "if (a[``] >= 0 && a[null] < 1) {}",
options: ["never", { exceptRange: true }],
parserOptions: { ecmaVersion: 2015 },
errors: [
{
messageId: "expected",
data: { expectedSide: "right", operator: "<=" },
type: "BinaryExpression"
}
]
},
{
code: "if (0 <= a[''] && a[b] < 1) {}",
output: "if (a[''] >= 0 && a[b] < 1) {}",
Expand All @@ -446,6 +574,19 @@ ruleTester.run("yoda", rule, {
}
]
},
{
code: "if (0 <= a[``] && a[b()] < 1) {}",
output: "if (a[``] >= 0 && a[b()] < 1) {}",
options: ["never", { exceptRange: true }],
parserOptions: { ecmaVersion: 2015 },
errors: [
{
messageId: "expected",
data: { expectedSide: "right", operator: "<=" },
type: "BinaryExpression"
}
]
},
{
code: "if (0 <= a[b()] && a[b()] < 1) {}",
output: "if (a[b()] >= 0 && a[b()] < 1) {}",
Expand Down Expand Up @@ -507,6 +648,19 @@ ruleTester.run("yoda", rule, {
}
]
},
{
code: "foo(a === `3`);",
output: "foo(`3` === a);",
options: ["always", { onlyEquality: true }],
parserOptions: { ecmaVersion: 2015 },
errors: [
{
messageId: "expected",
data: { expectedSide: "left", operator: "===" },
type: "BinaryExpression"
}
]
},
{
code: "if (0 <= x && x < 1) {}",
output: "if (x >= 0 && x < 1) {}",
Expand Down

0 comments on commit e58f456

Please sign in to comment.