Skip to content
This repository has been archived by the owner on Mar 25, 2021. It is now read-only.

[new-rule]prefer-while rule #3750

Merged
merged 11 commits into from
May 3, 2018
1 change: 1 addition & 0 deletions src/configs/all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ export const rules = {
"prefer-object-spread": true,
"prefer-switch": true,
"prefer-template": true,
"prefer-while": true,
"quotemark": [
true,
"double",
Expand Down
50 changes: 50 additions & 0 deletions src/rules/code-examples/preferWhile.examples.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* @license
* Copyright 2018 Palantir Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import * as Lint from "../../index";

// tslint:disable: object-literal-sort-keys
export const codeExamples = [
{
description: "Prefer `while` loops instead of `for` loops without an initializer and incrementor.",
config: Lint.Utils.dedent`
"rules": { "prefer-while": true }
`,
pass: Lint.Utils.dedent`
for(let x = 1; x < 10; x++) {
console.log(x);
}

for (let i = 0; i < 10; x+=1) {
console.log(x);
}

for (let i = 0; i < 10;) {
i += 1;
}
`,
fail: Lint.Utils.dedent`
for(;;) {
console.log(x);
}

for(;true===true;) {
console.log(x);
}
`,
},
];
74 changes: 74 additions & 0 deletions src/rules/preferWhileRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* @license
* Copyright 2018 Palantir Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { isForStatement } from "tsutils";
import * as ts from "typescript";
import * as Lint from "../index";
import { codeExamples } from "./code-examples/preferWhile.examples";

export class Rule extends Lint.Rules.AbstractRule {
/* tslint:disable:object-literal-sort-keys */
public static metadata: Lint.IRuleMetadata = {
ruleName: "prefer-while",
description: "Prefer `while` loops instead of `for` loops without an initializer and incrementor.",
rationale: "Simplifies the readability of the loop statement, while maintaining the same functionality.",
optionsDescription: "Not configurable.",
options: null,
optionExamples: [true],
hasFix: true,
type: "style",
typescriptOnly: false,
codeExamples,
};
/* tslint:enable:object-literal-sort-keys */

public static FAILURE_STRING = "Prefer `while` loops instead of `for` loops without an initializer and incrementor.";

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
const failures: Lint.RuleFailure[] = [];

const cb = (node: ts.Node): void => {
if (isForStatement(node) && this.doesNodeViolateRule(node)) {
failures.push(this.createFailure(sourceFile, node));
}
return ts.forEachChild(node, cb);
};

ts.forEachChild(sourceFile, cb);
return failures;
}

private doesNodeViolateRule(node: ts.ForStatement) {
return (node.initializer === undefined && node.incrementor === undefined);
}

private createFailure(sourceFile: ts.SourceFile, node: ts.ForStatement): Lint.RuleFailure {
const start = node.getStart(sourceFile);
const end = node.statement.pos;

let fix: Lint.Fix;
if (node.condition === undefined) {
fix = Lint.Replacement.replaceFromTo(start, end, "while (true)");
} else {
fix = [
Lint.Replacement.replaceFromTo(start, node.condition.getStart(sourceFile), "while ("),
Lint.Replacement.deleteFromTo(node.condition.end, end - 1),
];
}
return new Lint.RuleFailure(sourceFile, start, end, Rule.FAILURE_STRING, this.ruleName, fix);
}
}
24 changes: 24 additions & 0 deletions test/rules/prefer-while/test.ts.fix
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// for loops without an initializer, termination condition, and incrementor should be updated
while (true) {
console.log(x);
}

// for loops without an initializer and incrementor should be updated
while (true===true) {
console.log(x);
}

// for loops with an initializer, termination condition, and incrementor using '++' should remain untouched
for(let x = 1; x < 10; x++) {
console.log(x);
}

// for loops with an initializer, termination condition, and incrementor using '+=' should remain untouched
for (let i = 0; i < 10; x+=1) {
console.log(x);
}

// for loops with an initializer and termination condition should remain untouched
for (let i = 0; i < 10;) {
i += 1;
}
26 changes: 26 additions & 0 deletions test/rules/prefer-while/test.ts.lint
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// for loops without an initializer, termination condition, and incrementor should be updated
for(;;) {
~~~~~~~ [Prefer `while` loops instead of `for` loops without an initializer and incrementor.]
console.log(x);
}

// for loops without an initializer and incrementor should be updated
for(;true===true;) {
~~~~~~~~~~~~~~~~~~ [Prefer `while` loops instead of `for` loops without an initializer and incrementor.]
console.log(x);
}

// for loops with an initializer, termination condition, and incrementor using '++' should remain untouched
for(let x = 1; x < 10; x++) {
console.log(x);
}

// for loops with an initializer, termination condition, and incrementor using '+=' should remain untouched
for (let i = 0; i < 10; x+=1) {
console.log(x);
}

// for loops with an initializer and termination condition should remain untouched
for (let i = 0; i < 10;) {
i += 1;
}
5 changes: 5 additions & 0 deletions test/rules/prefer-while/tslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"rules": {
"prefer-while": true
}
}