Skip to content

Commit b00d6e8

Browse files
eileajafff
authored andcommitted
Implement fix for noUnnecessaryElseRule (#103)
1 parent e5de338 commit b00d6e8

File tree

3 files changed

+457
-35
lines changed

3 files changed

+457
-35
lines changed

rules/noUnnecessaryElseRule.ts

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,56 @@ import { AbstractIfStatementWalker } from '../src/walker';
55
import { isElseIf } from '../src/utils';
66

77
const FAIL_MESSAGE = `unnecessary else`;
8+
const FAIL_MESSAGE_BLOCK = `unnecessary else block`;
89

910
export class Rule extends Lint.Rules.AbstractRule {
10-
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
11-
return this.applyWithWalker(new IfWalker(sourceFile, this.ruleName, undefined));
12-
}
11+
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
12+
return this.applyWithWalker(new IfWalker(sourceFile, this.ruleName, undefined));
13+
}
1314
}
1415

1516
class IfWalker extends AbstractIfStatementWalker<void> {
16-
protected _checkIfStatement(node: ts.IfStatement) {
17-
if (node.elseStatement !== undefined &&
18-
!isElseIf(node) &&
19-
utils.endsControlFlow(node.thenStatement))
20-
this.addFailureAtNode(node.getChildAt(5 /*else*/, this.sourceFile), FAIL_MESSAGE);
17+
protected _checkIfStatement(node: ts.IfStatement): void {
18+
if (isElseIf(node))
19+
return;
20+
21+
const elseStatement = node.elseStatement;
22+
if (isElseStatement(elseStatement) && utils.endsControlFlow(node.thenStatement)) {
23+
if (elseStatement !== undefined && utils.isBlock(elseStatement) && !hasLocals(elseStatement)) {
24+
// remove else scope if safe: keep blocks where local variables change scope when unwrapped
25+
const fixBlock = [
26+
Lint.Replacement.deleteFromTo(node.thenStatement.end, elseStatement.statements.pos), // removes `else {`
27+
Lint.Replacement.deleteText(elseStatement.end - 1, 1), // deletes `}`
28+
];
29+
this.addFailureAtNode(node.getChildAt(5 /*else*/, this.sourceFile), FAIL_MESSAGE_BLOCK, fixBlock);
30+
} else {
31+
// remove else only
32+
const fixElse = Lint.Replacement.deleteFromTo(node.thenStatement.getEnd(), elseStatement.getStart());
33+
this.addFailureAtNode(node.getChildAt(5 /*else*/, this.sourceFile), FAIL_MESSAGE, fixElse);
34+
}
2135
}
36+
}
37+
}
38+
39+
function isElseStatement(node: ts.Statement | undefined): node is ts.Statement {
40+
return node !== undefined;
41+
}
42+
43+
function hasLocals(node: ts.Block): boolean {
44+
for (const statement of node.statements) {
45+
switch (statement.kind) {
46+
case ts.SyntaxKind.VariableStatement:
47+
if (utils.isBlockScopedVariableDeclarationList((<ts.VariableStatement>statement).declarationList))
48+
return true;
49+
break;
50+
51+
case ts.SyntaxKind.ClassDeclaration:
52+
case ts.SyntaxKind.EnumDeclaration:
53+
case ts.SyntaxKind.InterfaceDeclaration:
54+
case ts.SyntaxKind.TypeAliasDeclaration:
55+
return true;
56+
}
57+
}
58+
59+
return false;
2260
}

0 commit comments

Comments
 (0)