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

Skip to content

Commit 603de04

Browse files
Zzzenmdjermanovic
andauthored
Update: treat all literals like boolean literal in no-constant-condition (#13245)
* Fix: treet all literals like boolean literal in no-constant-condition * fix unexpected short circuit * handle bigin/regex on unsupported runtime * fix bigint tests * simplify literal truthiness * handle regex literal in unsupported environments * Update lib/rules/no-constant-condition.js Co-authored-by: Milos Djermanovic <[email protected]> Co-authored-by: Milos Djermanovic <[email protected]>
1 parent 289aa6f commit 603de04

File tree

2 files changed

+77
-23
lines changed

2 files changed

+77
-23
lines changed

lib/rules/no-constant-condition.js

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@
99
// Helpers
1010
//------------------------------------------------------------------------------
1111

12-
const EQUALITY_OPERATORS = ["===", "!==", "==", "!="];
13-
const RELATIONAL_OPERATORS = [">", "<", ">=", "<=", "in", "instanceof"];
14-
1512
//------------------------------------------------------------------------------
1613
// Rule Definition
1714
//------------------------------------------------------------------------------
@@ -56,6 +53,35 @@ module.exports = {
5653
// Helpers
5754
//--------------------------------------------------------------------------
5855

56+
/**
57+
* Returns literal's value converted to the Boolean type
58+
* @param {ASTNode} node any `Literal` node
59+
* @returns {boolean | null} `true` when node is truthy, `false` when node is falsy,
60+
* `null` when it cannot be determined.
61+
*/
62+
function getBooleanValue(node) {
63+
if (node.value === null) {
64+
65+
/*
66+
* it might be a null literal or bigint/regex literal in unsupported environments .
67+
* https://github.com/estree/estree/blob/14df8a024956ea289bd55b9c2226a1d5b8a473ee/es5.md#regexpliteral
68+
* https://github.com/estree/estree/blob/14df8a024956ea289bd55b9c2226a1d5b8a473ee/es2020.md#bigintliteral
69+
*/
70+
71+
if (node.raw === "null") {
72+
return false;
73+
}
74+
75+
// regex is always truthy
76+
if (typeof node.regex === "object") {
77+
return true;
78+
}
79+
80+
return null;
81+
}
82+
83+
return !!node.value;
84+
}
5985

6086
/**
6187
* Checks if a branch node of LogicalExpression short circuits the whole condition
@@ -66,15 +92,23 @@ module.exports = {
6692
function isLogicalIdentity(node, operator) {
6793
switch (node.type) {
6894
case "Literal":
69-
return (operator === "||" && node.value === true) ||
70-
(operator === "&&" && node.value === false);
95+
return (operator === "||" && getBooleanValue(node) === true) ||
96+
(operator === "&&" && getBooleanValue(node) === false);
7197

7298
case "UnaryExpression":
7399
return (operator === "&&" && node.operator === "void");
74100

75101
case "LogicalExpression":
76-
return isLogicalIdentity(node.left, node.operator) ||
77-
isLogicalIdentity(node.right, node.operator);
102+
103+
/*
104+
* handles `a && false || b`
105+
* `false` is an identity element of `&&` but not `||`
106+
*/
107+
return operator === node.operator &&
108+
(
109+
isLogicalIdentity(node.left, node.operator) ||
110+
isLogicalIdentity(node.right, node.operator)
111+
);
78112

79113
// no default
80114
}
@@ -129,21 +163,9 @@ module.exports = {
129163
const isLeftConstant = isConstant(node.left, inBooleanPosition);
130164
const isRightConstant = isConstant(node.right, inBooleanPosition);
131165
const isLeftShortCircuit = (isLeftConstant && isLogicalIdentity(node.left, node.operator));
132-
const isRightShortCircuit = (isRightConstant && isLogicalIdentity(node.right, node.operator));
166+
const isRightShortCircuit = (inBooleanPosition && isRightConstant && isLogicalIdentity(node.right, node.operator));
133167

134168
return (isLeftConstant && isRightConstant) ||
135-
(
136-
137-
// in the case of an "OR", we need to know if the right constant value is truthy
138-
node.operator === "||" &&
139-
isRightConstant &&
140-
node.right.value &&
141-
(
142-
!node.parent ||
143-
node.parent.type !== "BinaryExpression" ||
144-
!(EQUALITY_OPERATORS.includes(node.parent.operator) || RELATIONAL_OPERATORS.includes(node.parent.operator))
145-
)
146-
) ||
147169
isLeftShortCircuit ||
148170
isRightShortCircuit;
149171
}

tests/lib/rules/no-constant-condition.js

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,11 @@ ruleTester.run("no-constant-condition", rule, {
7474
"if(true && typeof abc==='string'){}",
7575

7676
// #11181, string literals
77-
"if('str' || a){}",
7877
"if('str1' && a){}",
7978
"if(a && 'str'){}",
80-
"if('str' || abc==='str'){}",
8179

8280
// #11306
81+
"if ((foo || true) === 'baz') {}",
8382
"if ((foo || 'bar') === 'baz') {}",
8483
"if ((foo || 'bar') !== 'baz') {}",
8584
"if ((foo || 'bar') == 'baz') {}",
@@ -90,6 +89,19 @@ ruleTester.run("no-constant-condition", rule, {
9089
"if ((foo || 233) <= 666) {}",
9190
"if ((key || 'k') in obj) {}",
9291
"if ((foo || {}) instanceof obj) {}",
92+
"if ((foo || 'bar' || 'bar') === 'bar');",
93+
{
94+
code: "if ((foo || 1n) === 'baz') {}",
95+
parserOptions: { ecmaVersion: 11 }
96+
},
97+
{
98+
code: "if (a && 0n || b);",
99+
parserOptions: { ecmaVersion: 11 }
100+
},
101+
{
102+
code: "if(1n && a){};",
103+
parserOptions: { ecmaVersion: 11 }
104+
},
93105

94106
// #12225
95107
"if ('' + [y] === '' + [ty]) {}",
@@ -121,18 +133,22 @@ ruleTester.run("no-constant-condition", rule, {
121133
{ code: "for(;`foo`;);", errors: [{ messageId: "unexpected", type: "TemplateLiteral" }] },
122134
{ code: "for(;`foo${bar}`;);", errors: [{ messageId: "unexpected", type: "TemplateLiteral" }] },
123135
{ code: "do{}while(true)", errors: [{ messageId: "unexpected", type: "Literal" }] },
136+
{ code: "do{}while('1')", errors: [{ messageId: "unexpected", type: "Literal" }] },
137+
{ code: "do{}while(0)", errors: [{ messageId: "unexpected", type: "Literal" }] },
124138
{ code: "do{}while(t = -2)", errors: [{ messageId: "unexpected", type: "AssignmentExpression" }] },
125139
{ code: "do{}while(``)", errors: [{ messageId: "unexpected", type: "TemplateLiteral" }] },
126140
{ code: "do{}while(`foo`)", errors: [{ messageId: "unexpected", type: "TemplateLiteral" }] },
127141
{ code: "do{}while(`foo${bar}`)", errors: [{ messageId: "unexpected", type: "TemplateLiteral" }] },
128142
{ code: "true ? 1 : 2;", errors: [{ messageId: "unexpected", type: "Literal" }] },
143+
{ code: "1 ? 1 : 2;", errors: [{ messageId: "unexpected", type: "Literal" }] },
129144
{ code: "q = 0 ? 1 : 2;", errors: [{ messageId: "unexpected", type: "Literal" }] },
130145
{ code: "(q = 0) ? 1 : 2;", errors: [{ messageId: "unexpected", type: "AssignmentExpression" }] },
131146
{ code: "`` ? 1 : 2;", errors: [{ messageId: "unexpected", type: "TemplateLiteral" }] },
132147
{ code: "`foo` ? 1 : 2;", errors: [{ messageId: "unexpected", type: "TemplateLiteral" }] },
133148
{ code: "`foo${bar}` ? 1 : 2;", errors: [{ messageId: "unexpected", type: "TemplateLiteral" }] },
134149
{ code: "if(-2);", errors: [{ messageId: "unexpected", type: "UnaryExpression" }] },
135150
{ code: "if(true);", errors: [{ messageId: "unexpected", type: "Literal" }] },
151+
{ code: "if(1);", errors: [{ messageId: "unexpected", type: "Literal" }] },
136152
{ code: "if({});", errors: [{ messageId: "unexpected", type: "ObjectExpression" }] },
137153
{ code: "if(0 < 1);", errors: [{ messageId: "unexpected", type: "BinaryExpression" }] },
138154
{ code: "if(0 || 1);", errors: [{ messageId: "unexpected", type: "LogicalExpression" }] },
@@ -143,6 +159,7 @@ ruleTester.run("no-constant-condition", rule, {
143159
{ code: "if(`${'bar'}`);", errors: [{ messageId: "unexpected", type: "TemplateLiteral" }] },
144160
{ code: "if(`${'bar' + `foo`}`);", errors: [{ messageId: "unexpected", type: "TemplateLiteral" }] },
145161
{ code: "if(`foo${false || true}`);", errors: [{ messageId: "unexpected", type: "TemplateLiteral" }] },
162+
{ code: "if(`foo${0 || 1}`);", errors: [{ messageId: "unexpected", type: "TemplateLiteral" }] },
146163
{ code: "if(`foo${bar}`);", errors: [{ messageId: "unexpected", type: "TemplateLiteral" }] },
147164
{ code: "if(`${bar}foo`);", errors: [{ messageId: "unexpected", type: "TemplateLiteral" }] },
148165

@@ -152,6 +169,7 @@ ruleTester.run("no-constant-condition", rule, {
152169
{ code: "while(x = 1);", errors: [{ messageId: "unexpected", type: "AssignmentExpression" }] },
153170
{ code: "while(function(){});", errors: [{ messageId: "unexpected", type: "FunctionExpression" }] },
154171
{ code: "while(true);", errors: [{ messageId: "unexpected", type: "Literal" }] },
172+
{ code: "while(1);", errors: [{ messageId: "unexpected", type: "Literal" }] },
155173
{ code: "while(() => {});", errors: [{ messageId: "unexpected", type: "ArrowFunctionExpression" }] },
156174
{ code: "while(`foo`);", errors: [{ messageId: "unexpected", type: "TemplateLiteral" }] },
157175
{ code: "while(``);", errors: [{ messageId: "unexpected", type: "TemplateLiteral" }] },
@@ -180,12 +198,15 @@ ruleTester.run("no-constant-condition", rule, {
180198
// #5693
181199
{ code: "if(false && abc==='str'){}", errors: [{ messageId: "unexpected", type: "LogicalExpression" }] },
182200
{ code: "if(true || abc==='str'){}", errors: [{ messageId: "unexpected", type: "LogicalExpression" }] },
201+
{ code: "if(1 || abc==='str'){}", errors: [{ messageId: "unexpected", type: "LogicalExpression" }] },
183202
{ code: "if(abc==='str' || true){}", errors: [{ messageId: "unexpected", type: "LogicalExpression" }] },
184203
{ code: "if(abc==='str' || true || def ==='str'){}", errors: [{ messageId: "unexpected", type: "LogicalExpression" }] },
185204
{ code: "if(false || true){}", errors: [{ messageId: "unexpected", type: "LogicalExpression" }] },
186205
{ code: "if(typeof abc==='str' || true){}", errors: [{ messageId: "unexpected", type: "LogicalExpression" }] },
187206

188207
// #11181, string literals
208+
{ code: "if('str' || a){}", errors: [{ messageId: "unexpected", type: "LogicalExpression" }] },
209+
{ code: "if('str' || abc==='str'){}", errors: [{ messageId: "unexpected", type: "LogicalExpression" }] },
189210
{ code: "if('str1' || 'str2'){}", errors: [{ messageId: "unexpected", type: "LogicalExpression" }] },
190211
{ code: "if('str1' && 'str2'){}", errors: [{ messageId: "unexpected", type: "LogicalExpression" }] },
191212
{ code: "if(abc==='str' || 'str'){}", errors: [{ messageId: "unexpected", type: "LogicalExpression" }] },
@@ -272,6 +293,17 @@ ruleTester.run("no-constant-condition", rule, {
272293
{
273294
code: "if ([,] + ''){}",
274295
errors: [{ messageId: "unexpected", type: "BinaryExpression" }]
275-
}
296+
},
297+
298+
// #13238
299+
{ code: "if(/foo/ui);", parserOptions: { ecmaVersion: 11 }, errors: [{ messageId: "unexpected", type: "Literal" }] },
300+
{ code: "if(0n);", parserOptions: { ecmaVersion: 11 }, errors: [{ messageId: "unexpected", type: "Literal" }] },
301+
{ code: "if(0b0n);", parserOptions: { ecmaVersion: 11 }, errors: [{ messageId: "unexpected", type: "Literal" }] },
302+
{ code: "if(0o0n);", parserOptions: { ecmaVersion: 11 }, errors: [{ messageId: "unexpected", type: "Literal" }] },
303+
{ code: "if(0x0n);", parserOptions: { ecmaVersion: 11 }, errors: [{ messageId: "unexpected", type: "Literal" }] },
304+
{ code: "if(0b1n);", parserOptions: { ecmaVersion: 11 }, errors: [{ messageId: "unexpected", type: "Literal" }] },
305+
{ code: "if(0o1n);", parserOptions: { ecmaVersion: 11 }, errors: [{ messageId: "unexpected", type: "Literal" }] },
306+
{ code: "if(0x1n);", parserOptions: { ecmaVersion: 11 }, errors: [{ messageId: "unexpected", type: "Literal" }] },
307+
{ code: "if(0x1n || foo);", parserOptions: { ecmaVersion: 11 }, errors: [{ messageId: "unexpected", type: "LogicalExpression" }] }
276308
]
277309
});

0 commit comments

Comments
 (0)