Skip to content

Commit

Permalink
feat(eslint-plugin): [no-unnece-cond] Add allowConstantLoopConditions (
Browse files Browse the repository at this point in the history
…#1029)

Co-authored-by: Brad Zacher <brad.zacher@gmail.com>
  • Loading branch information
a-tarasyuk and bradzacher committed Nov 21, 2019
1 parent fa88cb9 commit ceb6f1c
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 17 deletions.
10 changes: 10 additions & 0 deletions packages/eslint-plugin/docs/rules/no-unnecessary-condition.md
Expand Up @@ -53,6 +53,16 @@ function head<T>(items: T[]) {
}
```

- `allowConstantLoopConditions` (default `false`) - allows constant expressions in loops.

Example of correct code for when `allowConstantLoopConditions` is `true`:

```ts
while (true) {}
for (; true; ) {}
do {} while (true);
```

## When Not To Use It

The main downside to using this rule is the need for type information.
Expand Down
63 changes: 46 additions & 17 deletions packages/eslint-plugin/src/rules/no-unnecessary-condition.ts
Expand Up @@ -41,15 +41,9 @@ const isLiteral = (type: ts.Type): boolean =>
isLiteralType(type);
// #endregion

type ExpressionWithTest =
| TSESTree.ConditionalExpression
| TSESTree.DoWhileStatement
| TSESTree.ForStatement
| TSESTree.IfStatement
| TSESTree.WhileStatement;

export type Options = [
{
allowConstantLoopConditions?: boolean;
ignoreRhs?: boolean;
},
];
Expand All @@ -74,6 +68,9 @@ export default createRule<Options, MessageId>({
{
type: 'object',
properties: {
allowConstantLoopConditions: {
type: 'boolean',
},
ignoreRhs: {
type: 'boolean',
},
Expand All @@ -91,10 +88,11 @@ export default createRule<Options, MessageId>({
},
defaultOptions: [
{
allowConstantLoopConditions: false,
ignoreRhs: false,
},
],
create(context, [{ ignoreRhs }]) {
create(context, [{ allowConstantLoopConditions, ignoreRhs }]) {
const service = getParserServices(context);
const checker = service.program.getTypeChecker();

Expand Down Expand Up @@ -165,14 +163,13 @@ export default createRule<Options, MessageId>({
* Filters all LogicalExpressions to prevent some duplicate reports.
*/
function checkIfTestExpressionIsNecessaryConditional(
node: ExpressionWithTest,
node: TSESTree.ConditionalExpression | TSESTree.IfStatement,
): void {
if (
node.test !== null &&
node.test.type !== AST_NODE_TYPES.LogicalExpression
) {
checkNode(node.test);
if (node.test.type === AST_NODE_TYPES.LogicalExpression) {
return;
}

checkNode(node.test);
}

/**
Expand All @@ -187,14 +184,46 @@ export default createRule<Options, MessageId>({
}
}

/**
* Checks that a testable expression of a loop is necessarily conditional, reports otherwise.
*/
function checkIfLoopIsNecessaryConditional(
node:
| TSESTree.DoWhileStatement
| TSESTree.ForStatement
| TSESTree.WhileStatement,
): void {
if (
node.test === null ||
node.test.type === AST_NODE_TYPES.LogicalExpression
) {
return;
}

/**
* Allow:
* while (true) {}
* for (;true;) {}
* do {} while (true)
*/
if (
allowConstantLoopConditions &&
isBooleanLiteralType(getNodeType(node.test), true)
) {
return;
}

checkNode(node.test);
}

return {
BinaryExpression: checkIfBinaryExpressionIsNecessaryConditional,
ConditionalExpression: checkIfTestExpressionIsNecessaryConditional,
DoWhileStatement: checkIfTestExpressionIsNecessaryConditional,
ForStatement: checkIfTestExpressionIsNecessaryConditional,
DoWhileStatement: checkIfLoopIsNecessaryConditional,
ForStatement: checkIfLoopIsNecessaryConditional,
IfStatement: checkIfTestExpressionIsNecessaryConditional,
WhileStatement: checkIfTestExpressionIsNecessaryConditional,
LogicalExpression: checkLogicalExpressionForUnnecessaryConditionals,
WhileStatement: checkIfLoopIsNecessaryConditional,
};
},
});
Expand Up @@ -97,6 +97,14 @@ declare const b2: true;
if(b1 && b2) {}`,
options: [{ ignoreRhs: true }],
},
{
code: `
while(true) {}
for (;true;) {}
do {} while(true)
`,
options: [{ allowConstantLoopConditions: true }],
},
],
invalid: [
// Ensure that it's checking in all the right places
Expand Down Expand Up @@ -201,5 +209,18 @@ const t1 = (b1 && b2) ? 'yes' : 'no'`,
ruleError(9, 13, 'alwaysTruthy'),
],
},
{
code: `
while(true) {}
for (;true;) {}
do {} while(true)
`,
options: [{ allowConstantLoopConditions: false }],
errors: [
ruleError(2, 7, 'alwaysTruthy'),
ruleError(3, 7, 'alwaysTruthy'),
ruleError(4, 13, 'alwaysTruthy'),
],
},
],
});

0 comments on commit ceb6f1c

Please sign in to comment.