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

Update: check template literal in yoda (fixes #12863) #12876

Merged
merged 6 commits into from Feb 9, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 {ASTNode} 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 node should be treated as a single Literal node.
* @param {ASTNode} node Node to test
* @returns {boolean} True if the node should be treated as a single 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)) {
mdjermanovic marked this conversation as resolved.
Show resolved Hide resolved
return {
type: "Literal",
value: node.quasis[0].value.cooked,
raw: node.quasis[0].value.raw
yeonjuan marked this conversation as resolved.
Show resolved Hide resolved
};
}

if (defaultValue) {
return {
type: "Literal",
Expand Down
185 changes: 184 additions & 1 deletion tests/lib/rules/yoda.js
Expand Up @@ -26,13 +26,27 @@ 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 (`${\"red\"}` === foo) {}", 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 (foo === `${\"red\"}`) {}", options: ["always"], parserOptions: { ecmaVersion: 2015 } },
{ code: "if (`a` > b && `a` > b) {}", options: ["always"], parserOptions: { ecmaVersion: 2015 } },
{ code: "if (`b` > `a` && \"b\" > \"a\") {}", options: ["always"], parserOptions: { ecmaVersion: 2015 } },

// Range exception
{
Expand Down Expand Up @@ -91,6 +105,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 +125,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 }
yeonjuan marked this conversation as resolved.
Show resolved Hide resolved
}, {
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 +233,45 @@ 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 (`red` <= `${\"red\"}`) {}",
output: "if (`${\"red\"}` >= `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 +332,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 All @@ -278,6 +370,19 @@ ruleTester.run("yoda", rule, {
}
]
},
{
code: "if (`${\"red\"}` <= `red`) {}",
output: "if (`red` >= `${\"red\"}`) {}",
options: ["always"],
parserOptions: { ecmaVersion: 2015 },
errors: [
{
messageId: "expected",
data: { expectedSide: "left", operator: "<=" },
type: "BinaryExpression"
}
]
},
{
code: "if (a < 0 && 0 <= b && b < 1) {}",
output: "if (a < 0 && b >= 0 && b < 1) {}",
Expand Down Expand Up @@ -362,6 +467,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 +492,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 +566,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 +603,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 +677,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