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

Commit

Permalink
Added no-unnecessary-else Rule
Browse files Browse the repository at this point in the history
  • Loading branch information
debsmita1 committed Feb 4, 2019
1 parent 39201ac commit 5bd58d2
Show file tree
Hide file tree
Showing 5 changed files with 211 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/configs/all.ts
Expand Up @@ -151,6 +151,7 @@ export const rules = {
"unnecessary-constructor": true,
"use-default-type-parameter": true,
"use-isnan": true,
"no-unnecessary-else": true,

// Maintainability

Expand Down
1 change: 1 addition & 0 deletions src/configs/recommended.ts
Expand Up @@ -91,6 +91,7 @@ export const rules = {
"no-string-throw": true,
"no-switch-case-fall-through": false,
"no-trailing-whitespace": true,
"no-unnecessary-else": true,
"no-unnecessary-initializer": true,
"no-unsafe-finally": true,
"no-unused-expression": true,
Expand Down
96 changes: 96 additions & 0 deletions src/rules/noUnnecessaryElseRule.ts
@@ -0,0 +1,96 @@
/**
* @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 ts from "typescript";

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

export class Rule extends Lint.Rules.AbstractRule {
public static metadata: Lint.IRuleMetadata = {
description: Lint.Utils.dedent`
Disallows else block when the If block contains control flow statements, such as \`return\`, \`continue\`,
\`break\` and \`throws\`.`,
descriptionDetails: "",
optionExamples: [true],
options: null,
optionsDescription: "Not configurable.",
rationale: Lint.Utils.dedent`
When control flow statements,
such as \`return\`, \`continue\`, \`break\` and \`throws\` are written inside \`if\` block,
Then \`else\` block becomes unnecessary.`,
ruleName: "no-unnecessary-else",
type: "functionality",
typescriptOnly: false,
};

public static FAILURE_STRING(name: string): string {
return `If block contains \`${name}\` statement. Consider replacing the contents of else block outside of the block.`;
}

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithFunction(sourceFile, walk);
}
}

type JumpStatement =
| ts.BreakStatement
| ts.ContinueStatement
| ts.ThrowStatement
| ts.ReturnStatement;

function walk(ctx: Lint.WalkContext<void>): void {
let inElse = false;
ts.forEachChild(ctx.sourceFile, function cb(node: ts.Node): void {
switch (node.kind) {
case ts.SyntaxKind.IfStatement:
const { thenStatement, elseStatement } = node as ts.IfStatement;
if (elseStatement !== undefined) {
inElse = true;
}
ts.forEachChild(thenStatement, cb);
break;

case ts.SyntaxKind.BreakStatement:
case ts.SyntaxKind.ContinueStatement:
case ts.SyntaxKind.ThrowStatement:
case ts.SyntaxKind.ReturnStatement:
if (inElse) {
ctx.addFailureAtNode(
node,
Rule.FAILURE_STRING(printJumpKind(node as JumpStatement)),
);
inElse = false;
}
break;

default:
return ts.forEachChild(node, cb);
}
});
}

function printJumpKind(node: JumpStatement): string {
switch (node.kind) {
case ts.SyntaxKind.BreakStatement:
return "break";
case ts.SyntaxKind.ContinueStatement:
return "continue";
case ts.SyntaxKind.ThrowStatement:
return "throw";
case ts.SyntaxKind.ReturnStatement:
return "return";
}
}
107 changes: 107 additions & 0 deletions test/rules/no-unnecessary-else/test.ts.lint
@@ -0,0 +1,107 @@
const testReturn = () => {
if () {
return;
~~~~~~~ [return]
} else {
return;
}
}

//valid
const testReturn = () => {
if () {
return;
}
return;
}

const testReturn = () => {
if () {
return;
~~~~~~~ [return]
} else if {
if () {
return ;
} else {
return ;
}
}
return;
}

const testReturn = () => {
if () {
return;
}
if () {
return ;
~~~~~~~~ [return]
} else {
return ;
}
}

//valid
const testReturn = () => {
if () {
return;
}
if () {
return ;
}
return ;
}

function testThrow () {
if () {
throw "error";
~~~~~~~~~~~~~~ [throw]
} else {
}
}

//valid
function testThrow () {
if () {
throw "error";
}
}

function testContinue () {
for ( ) {
if () {
continue ;
~~~~~~~~~~ [continue]
} else {
}
}
}

//valid
function testContinue () {
for ( ) {
if () {
continue ;
}
}
}

function testBreak () {
if () {
break ;
~~~~~~~ [break]
} else {
}
}

//valid
function testBreak () {
if () {
break ;
}
}

[return]: If block contains `return` statement. Consider replacing the contents of else block outside of the block.
[throw]: If block contains `throw` statement. Consider replacing the contents of else block outside of the block.
[break]: If block contains `break` statement. Consider replacing the contents of else block outside of the block.
[continue]: If block contains `continue` statement. Consider replacing the contents of else block outside of the block.
6 changes: 6 additions & 0 deletions test/rules/no-unnecessary-else/tslint.json
@@ -0,0 +1,6 @@
{
"rules": {
"no-unnecessary-else": true
}
}

0 comments on commit 5bd58d2

Please sign in to comment.