@@ -5,18 +5,56 @@ import { AbstractIfStatementWalker } from '../src/walker';
55import { isElseIf } from '../src/utils' ;
66
77const FAIL_MESSAGE = `unnecessary else` ;
8+ const FAIL_MESSAGE_BLOCK = `unnecessary else block` ;
89
910export 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
1516class 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