import * as ts from 'typescript' ; import * as Lint from 'tslint' ; import { ErrorTolerantWalker } from './utils/ErrorTolerantWalker' ; import { ExtendedMetadata } from './utils/ExtendedMetadata' ; /**
 * Implementation of the no-complex-conditionals rule.
 */ export class Rule extends Lint . Rules . AbstractRule { public static metadata : ExtendedMetadata = { ruleName : 'no-complex-conditionals' , type : 'maintainability' , description : 'Enforce the maximum complexity of conditional expressions.' , options : null , optionsDescription : '' , optionExamples : [ ] , typescriptOnly : false , issueClass : 'Non-SDL' , issueType : 'Warning' , severity : 'Moderate' , level : 'Opportunity for Excellence' , group : 'Clarity' , commonWeaknessEnumeration : '' , } ; public static FAILURE_STRING : string = 'Conditional expression is too complex. ' + 'Consider moving expression to a variable or function with a meaningful name.' ; public apply ( sourceFile ) { return this . applyWithWalker ( new NoComplexConditionalsRuleWalker ( sourceFile , this . getOptions ( ) ) ) ; } } class NoComplexConditionalsRuleWalker extends ErrorTolerantWalker { private threshold : number = 3 ; constructor ( sourceFile , options ) { super ( sourceFile , options ) ; this . parseOptions ( ) ; } protected visitIfStatement ( node ) { const { expression } = node ; const complexity = this . countComplexity ( expression ) ; if ( complexity > this . threshold ) { this . addFailureAtNode ( expression , Rule . FAILURE_STRING ) ; } super . visitIfStatement ( node ) ; } private countComplexity ( expression ) { let complexity = 0 ; const cb = ( node ) => { if ( this . increasesComplexity ( node ) ) { complexity = complexity + 1 ; } return ts . forEachChild ( node , cb ) ; } ; cb ( expression ) ; return complexity ; } private increasesComplexity ( node ) { switch ( node . kind ) { case ts . SyntaxKind . CaseClause : return ( < ts . CaseClause > node ) . statements . length > 0 ; case ts . SyntaxKind . CatchClause : case ts . SyntaxKind . ConditionalExpression : case ts . SyntaxKind . DoStatement : case ts . SyntaxKind . ForStatement : case ts . SyntaxKind . ForInStatement : case ts . SyntaxKind . ForOfStatement : case ts . SyntaxKind . IfStatement : case ts . SyntaxKind . WhileStatement : return true ; case ts . SyntaxKind . BinaryExpression : switch ( ( < ts . BinaryExpression > node ) . operatorToken . kind ) { case ts . SyntaxKind . BarBarToken : case ts . SyntaxKind . AmpersandAmpersandToken : return true ; default : return false ; } default : return false ; } } private parseOptions ( ) { this . getOptions ( ) . forEach ( ( opt ) => { if ( typeof opt === 'boolean' ) { return ; } if ( typeof opt === 'number' ) { this . threshold = opt ; return ; } } ) ; } }