Skip to content

Commit

Permalink
no-else-after-return: add option 'allow-else-if' (#61)
Browse files Browse the repository at this point in the history
  • Loading branch information
ajafff authored Mar 28, 2018
1 parent 76de864 commit 97609c0
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 8 deletions.
34 changes: 34 additions & 0 deletions docs/no-else-after-return.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,37 @@ Works like [no-else-return from eslint](http://eslint.org/docs/rules/no-else-ret
> If an if block contains a return statement, the else block becomes unnecessary. Its contents can be placed outside of the block.
If you like this rule, I recommend you try [`no-unnecessary-else`](./no-unnecessary-else.md) for some bonus features.

### Options

#### `"allow-else-if"`

Example config:

```js
"no-else-after-return": {
"options": "allow-else-if"
}

// or

"no-else-after-return": [true, "allow-else-if"]
```

Enable this option if you prefer `else if` blocks after `return` statements:

```js
if (condition) {
return 'foo';
} else if (otherCondition) { // this is allowed with the option
return 'bar';
}

if (condition) {
return 'foo';
} else if (otherCondition) {
return 'bar';
} else { // this is still not allowed with the option
return 'baz';
}
```
34 changes: 26 additions & 8 deletions rules/noElseAfterReturnRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,45 @@ import * as Lint from 'tslint';

import { isElseIf } from '../src/utils';
import { AbstractIfStatementWalker } from '../src/walker';
import { getControlFlowEnd, isReturnStatement } from 'tsutils';
import { isIfStatement, getControlFlowEnd, isReturnStatement } from 'tsutils';

const FAIL_MESSAGE = `unnecessary else after return`;
const OPTION_ALLOW_ELSE_IF = 'allow-else-if';

interface IOptions {
allowElseIf: boolean;
}

export class Rule extends Lint.Rules.AbstractRule {
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithWalker(new IfWalker(sourceFile, this.ruleName, undefined));
return this.applyWithWalker(new IfWalker(sourceFile, this.ruleName, {
allowElseIf: this.ruleArguments.indexOf(OPTION_ALLOW_ELSE_IF) !== -1,
}));
}
}

class IfWalker extends AbstractIfStatementWalker<void> {
class IfWalker extends AbstractIfStatementWalker<IOptions> {
protected _checkIfStatement(node: ts.IfStatement) {
if (
node.elseStatement !== undefined &&
!isElseIf(node) &&
endsWithReturnStatement(node.thenStatement)
)
if (shouldCheckNode(node, this.options.allowElseIf) && endsWithReturnStatement(node.thenStatement))
this.addFailureAtNode(node.getChildAt(5 /*else*/, this.sourceFile), FAIL_MESSAGE);
}
}

function shouldCheckNode(node: ts.IfStatement, allowElseIf: boolean): boolean {
if (node.elseStatement === undefined)
return false;
if (!allowElseIf)
return !isElseIf(node);
if (isIfStatement(node.elseStatement) && isElseIf(node.elseStatement))
return false;
while (isElseIf(node)) {
node = node.parent;
if (!endsWithReturnStatement(node.thenStatement))
return false;
}
return true;
}

function endsWithReturnStatement(node: ts.Statement): boolean {
const end = getControlFlowEnd(node);
return end.end && end.statements.every(isReturnStatement);
Expand Down
72 changes: 72 additions & 0 deletions test/rules/no-else-after-return/allow-else-if/test.ts.lint
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
if (condition) {
return;
} else {
~~~~ [fail]
return;
}

if (condition) {
return;
} else if (someOtherCondition) {
return;
}

if (condition) {
return;
} else if (someOtherCondition) {
return;
} else {
~~~~ [fail]
return;
}

if (condition) {
return;
} else if (someOtherCondition) {
return;
} else if (yetAnotherCondition) {
return;
} else {
~~~~ [fail]
return;
}

if (condition) {
// nothing
} else if (someOtherCondition) {
return;
} else {
return;
}

if (condition) {
// nothing
} else if (someOtherCondition) {
return;
} else if (yetAnotherCondition) {
return;
} else {
return;
}

if (condition) {
return;
} else if (someOtherCondition) {
// nothing
} else if (yetAnotherCondition) {
return;
} else {
return;
}

if (condition) {
return;
} else if (someOtherCondition) {
return;
} else if (yetAnotherCondition) {
// nothing
} else {
return;
}

[fail]: unnecessary else after return
6 changes: 6 additions & 0 deletions test/rules/no-else-after-return/allow-else-if/tslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"rulesDirectory": "../../../../rules",
"rules": {
"no-else-after-return": [true, "allow-else-if"]
}
}

0 comments on commit 97609c0

Please sign in to comment.