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

Commit

Permalink
curly: Add "as-needed" option (#2842)
Browse files Browse the repository at this point in the history
  • Loading branch information
andy-hanson authored and adidahiya committed May 31, 2017
1 parent a510150 commit 9f6588e
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 3 deletions.
46 changes: 43 additions & 3 deletions src/rules/curlyRule.ts
Expand Up @@ -15,11 +15,12 @@
* limitations under the License.
*/

import { isIfStatement, isIterationStatement, isSameLine } from "tsutils";
import { isBlock, isIfStatement, isIterationStatement, isSameLine } from "tsutils";
import * as ts from "typescript";

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

const OPTION_AS_NEEDED = "as-needed";
const OPTION_IGNORE_SAME_LINE = "ignore-same-line";

interface Options {
Expand All @@ -42,8 +43,9 @@ export class Rule extends Lint.Rules.AbstractRule {
to be executed only if \`foo === bar\`. However, he forgot braces and \`bar++\` will be executed
no matter what. This rule could prevent such a mistake.`,
optionsDescription: Lint.Utils.dedent`
The rule may be set to \`true\`, or to the following:
One of the following options may be provided:
* \`"${OPTION_AS_NEEDED}"\` forbids any unnecessary curly braces.
* \`"${OPTION_IGNORE_SAME_LINE}"\` skips checking braces for control-flow statements
that are on one line and start on the same line as their control-flow keyword
`,
Expand All @@ -52,27 +54,65 @@ export class Rule extends Lint.Rules.AbstractRule {
items: {
type: "string",
enum: [
OPTION_AS_NEEDED,
OPTION_IGNORE_SAME_LINE,
],
},
},
optionExamples: [true, [true, "ignore-same-line"]],
optionExamples: [
true,
[true, OPTION_IGNORE_SAME_LINE],
[true, OPTION_AS_NEEDED],
],
type: "functionality",
typescriptOnly: false,
};
/* tslint:enable:object-literal-sort-keys */

public static FAILURE_STRING_AS_NEEDED = "Block contains only one statement; remove the curly braces.";
public static FAILURE_STRING_FACTORY(kind: string) {
return `${kind} statements must be braced`;
}

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
if (this.ruleArguments.indexOf(OPTION_AS_NEEDED) !== -1) {
return this.applyWithFunction(sourceFile, walkAsNeeded);
}

return this.applyWithWalker(new CurlyWalker(sourceFile, this.ruleName, {
ignoreSameLine: this.ruleArguments.indexOf(OPTION_IGNORE_SAME_LINE) !== -1,
}));
}
}

function walkAsNeeded(ctx: Lint.WalkContext<void>): void {
ts.forEachChild(ctx.sourceFile, function cb(node) {
if (isBlock(node) && isBlockUnnecessary(node)) {
ctx.addFailureAtNode(Lint.childOfKind(node, ts.SyntaxKind.OpenBraceToken)!, Rule.FAILURE_STRING_AS_NEEDED);
}
ts.forEachChild(node, cb);
});
}

function isBlockUnnecessary(node: ts.Block): boolean {
const parent = node.parent!;
if (node.statements.length !== 1) { return false; }
const statement = node.statements[0];
if (isIterationStatement(parent)) { return true; }
/*
Watch out for this case:
if (so) {
if (also)
foo();
} else
bar();
*/
return isIfStatement(parent) && !(isIfStatement(statement)
&& statement.elseStatement === undefined
&& parent.thenStatement === node
&& parent.elseStatement !== undefined);
}

class CurlyWalker extends Lint.AbstractWalker<Options> {
public walk(sourceFile: ts.SourceFile) {
const cb = (node: ts.Node): void => {
Expand Down
53 changes: 53 additions & 0 deletions test/rules/curly/as-needed/test.ts.lint
@@ -0,0 +1,53 @@
if (so) {
~ [0]
foo();
} else {
~ [0]
foo();
}

while (true) {
~ [0]
foo();
}

if (so) {
~ [0]
if (also)
foo();
}

if (so) {
~ [0]
if (also)
foo();
else
foo();
} else
foo();

if (so)
bar();
else {
~ [0]
if (also)
foo();
}

// Some blocks are necessary.

if (so) {
if (also)
foo();
} else
bar();

function f() {
foo();
}

() => { foo(); };

try { foo(); } catch (e) { foo(); } finally { foo(); }

[0]: Block contains only one statement; remove the curly braces.
5 changes: 5 additions & 0 deletions test/rules/curly/as-needed/tslint.json
@@ -0,0 +1,5 @@
{
"rules": {
"curly": [true, "as-needed"]
}
}

0 comments on commit 9f6588e

Please sign in to comment.