2017-05-28 00:38:50 +02:00
"use strict" ;
/ * *
* @ license
* Copyright 2013 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 .
* /
Object . defineProperty ( exports , "__esModule" , { value : true } ) ;
var tslib _1 = require ( "tslib" ) ;
var tsutils _1 = require ( "tsutils" ) ;
var ts = require ( "typescript" ) ;
var Lint = require ( "../index" ) ;
2017-08-14 05:01:11 +02:00
var OPTION _AS _NEEDED = "as-needed" ;
2017-05-28 00:38:50 +02:00
var OPTION _IGNORE _SAME _LINE = "ignore-same-line" ;
2017-10-14 18:40:54 +02:00
var Rule = /** @class */ ( function ( _super ) {
2017-05-28 00:38:50 +02:00
tslib _1 . _ _extends ( Rule , _super ) ;
function Rule ( ) {
return _super !== null && _super . apply ( this , arguments ) || this ;
}
Rule . FAILURE _STRING _FACTORY = function ( kind ) {
return kind + " statements must be braced" ;
} ;
Rule . prototype . apply = function ( sourceFile ) {
2017-08-14 05:01:11 +02:00
if ( this . ruleArguments . indexOf ( OPTION _AS _NEEDED ) !== - 1 ) {
return this . applyWithFunction ( sourceFile , walkAsNeeded ) ;
}
2017-05-28 00:38:50 +02:00
return this . applyWithWalker ( new CurlyWalker ( sourceFile , this . ruleName , {
ignoreSameLine : this . ruleArguments . indexOf ( OPTION _IGNORE _SAME _LINE ) !== - 1 ,
} ) ) ;
} ;
2017-08-14 05:01:11 +02:00
/* tslint:disable:object-literal-sort-keys */
Rule . metadata = {
ruleName : "curly" ,
description : "Enforces braces for `if`/`for`/`do`/`while` statements." ,
rationale : ( _a = [ "\n ```ts\n if (foo === bar)\n foo++;\n bar++;\n ```\n\n In the code above, the author almost certainly meant for both `foo++` and `bar++`\n to be executed only if `foo === bar`. However, he forgot braces and `bar++` will be executed\n no matter what. This rule could prevent such a mistake." ] , _a . raw = [ "\n \\`\\`\\`ts\n if (foo === bar)\n foo++;\n bar++;\n \\`\\`\\`\n\n In the code above, the author almost certainly meant for both \\`foo++\\` and \\`bar++\\`\n to be executed only if \\`foo === bar\\`. However, he forgot braces and \\`bar++\\` will be executed\n no matter what. This rule could prevent such a mistake." ] , Lint . Utils . dedent ( _a ) ) ,
optionsDescription : ( _b = [ "\n One of the following options may be provided:\n\n * `\"" , "\"` forbids any unnecessary curly braces.\n * `\"" , "\"` skips checking braces for control-flow statements\n that are on one line and start on the same line as their control-flow keyword\n " ] , _b . raw = [ "\n One of the following options may be provided:\n\n * \\`\"" , "\"\\` forbids any unnecessary curly braces.\n * \\`\"" , "\"\\` skips checking braces for control-flow statements\n that are on one line and start on the same line as their control-flow keyword\n " ] , Lint . Utils . dedent ( _b , OPTION _AS _NEEDED , OPTION _IGNORE _SAME _LINE ) ) ,
options : {
type : "array" ,
items : {
type : "string" ,
enum : [
OPTION _AS _NEEDED ,
OPTION _IGNORE _SAME _LINE ,
] ,
} ,
} ,
optionExamples : [
true ,
[ true , OPTION _IGNORE _SAME _LINE ] ,
[ true , OPTION _AS _NEEDED ] ,
] ,
type : "functionality" ,
typescriptOnly : false ,
2017-12-10 21:51:33 +01:00
hasFix : true ,
2017-08-14 05:01:11 +02:00
} ;
/* tslint:enable:object-literal-sort-keys */
Rule . FAILURE _STRING _AS _NEEDED = "Block contains only one statement; remove the curly braces." ;
2017-05-28 00:38:50 +02:00
return Rule ;
} ( Lint . Rules . AbstractRule ) ) ;
exports . Rule = Rule ;
2017-08-14 05:01:11 +02:00
function walkAsNeeded ( ctx ) {
ts . forEachChild ( ctx . sourceFile , function cb ( node ) {
if ( tsutils _1 . isBlock ( node ) && isBlockUnnecessary ( node ) ) {
ctx . addFailureAtNode ( Lint . childOfKind ( node , ts . SyntaxKind . OpenBraceToken ) , Rule . FAILURE _STRING _AS _NEEDED ) ;
}
ts . forEachChild ( node , cb ) ;
} ) ;
}
function isBlockUnnecessary ( node ) {
var parent = node . parent ;
if ( node . statements . length !== 1 ) {
return false ;
}
var statement = node . statements [ 0 ] ;
if ( tsutils _1 . isIterationStatement ( parent ) ) {
return true ;
}
/ *
Watch out for this case :
if ( so ) {
if ( also )
foo ( ) ;
} else
bar ( ) ;
* /
return tsutils _1 . isIfStatement ( parent ) && ! ( tsutils _1 . isIfStatement ( statement )
&& statement . elseStatement === undefined
&& parent . thenStatement === node
&& parent . elseStatement !== undefined ) ;
}
2017-10-14 18:40:54 +02:00
var CurlyWalker = /** @class */ ( function ( _super ) {
2017-05-28 00:38:50 +02:00
tslib _1 . _ _extends ( CurlyWalker , _super ) ;
function CurlyWalker ( ) {
return _super !== null && _super . apply ( this , arguments ) || this ;
}
CurlyWalker . prototype . walk = function ( sourceFile ) {
var _this = this ;
var cb = function ( node ) {
if ( tsutils _1 . isIterationStatement ( node ) ) {
_this . checkStatement ( node . statement , node , 0 , node . end ) ;
}
else if ( tsutils _1 . isIfStatement ( node ) ) {
_this . checkStatement ( node . thenStatement , node , 0 ) ;
if ( node . elseStatement !== undefined && node . elseStatement . kind !== ts . SyntaxKind . IfStatement ) {
_this . checkStatement ( node . elseStatement , node , 5 ) ;
}
}
return ts . forEachChild ( node , cb ) ;
} ;
return ts . forEachChild ( sourceFile , cb ) ;
} ;
CurlyWalker . prototype . checkStatement = function ( statement , node , childIndex , end ) {
if ( end === void 0 ) { end = statement . end ; }
2017-12-10 21:51:33 +01:00
var sameLine = tsutils _1 . isSameLine ( this . sourceFile , statement . pos , statement . end ) ;
2017-05-28 00:38:50 +02:00
if ( statement . kind !== ts . SyntaxKind . Block &&
2017-12-10 21:51:33 +01:00
! ( this . options . ignoreSameLine && sameLine ) ) {
2017-05-28 00:38:50 +02:00
var token = node . getChildAt ( childIndex , this . sourceFile ) ;
var tokenText = ts . tokenToString ( token . kind ) ;
2017-12-10 21:51:33 +01:00
this . addFailure ( token . end - tokenText . length , end , Rule . FAILURE _STRING _FACTORY ( tokenText ) , this . createMissingBraceFix ( statement , node , sameLine ) ) ;
}
} ;
/** Generate the necessary replacement to add braces to a statement that needs them. */
CurlyWalker . prototype . createMissingBraceFix = function ( statement , node , sameLine ) {
if ( sameLine ) {
return [
Lint . Replacement . appendText ( statement . getStart ( ) , "{ " ) ,
Lint . Replacement . appendText ( statement . getEnd ( ) , " }" ) ,
] ;
}
else {
var match = /\n([\t ])/ . exec ( node . getFullText ( this . sourceFile ) ) ; // determine which character to use (tab or space)
var indentation = match === null ?
"" :
// indentation should match start of statement
match [ 1 ] . repeat ( ts . getLineAndCharacterOfPosition ( this . sourceFile , node . getStart ( this . sourceFile ) ) . character ) ;
var maybeCarriageReturn = this . sourceFile . text [ this . sourceFile . getLineEndOfPosition ( node . pos ) - 1 ] === "\r" ? "\r" : "" ;
return [
Lint . Replacement . appendText ( this . sourceFile . getLineEndOfPosition ( statement . pos ) , " {" ) ,
Lint . Replacement . appendText ( statement . getEnd ( ) , maybeCarriageReturn + "\n" + indentation + "}" ) ,
] ;
2017-05-28 00:38:50 +02:00
}
} ;
return CurlyWalker ;
} ( Lint . AbstractWalker ) ) ;
var _a , _b ;