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

Commit

Permalink
[new-rule]prefer-while rule (#3750)
Browse files Browse the repository at this point in the history
* prefer-while rule

Implements prefer-while rule in place of a for loop without an initializer and incrementer

* renamed failure object creation method

* fix json test file indentatons

* Address review comments

 
- Use isForStatement from tsutils
- Update failure string to be more descriptive
- Updated test messages

* remove uneccessary type assertion

- assertion now uneccessary with using isForStatement from tsutils

* Add rationale field to prefer-while rule metadata

* Use 'let' instead of 'const' in test

* Update rule description & failure string

- Updated the description and failure string of prefer-while
- Updated tests accordingly

* Coalesce test cases

- Combine test cases for 'bad' for loops and case for 'good' for loops
- Remove unneeded files
- Add comments to test cases

* Add additional test cases

- Added test case with incrmentor in body of loop
- Added test case with += incrementor

* Add code examples

- Added code examples for prefer-while rule
  • Loading branch information
rwaskiewicz authored and suchanlee committed May 3, 2018
1 parent 7682184 commit baa413e
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 0 deletions.
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
}
}

0 comments on commit baa413e

Please sign in to comment.