import * as ts from 'typescript'; import * as Lint from 'tslint'; import { ErrorTolerantWalker } from './utils/ErrorTolerantWalker'; import { ExtendedMetadata } from './utils/ExtendedMetadata'; export const FAILURE_STRING: string = 'Try-catch blocks must be at the top of the scope'; /** * Implementation of the newspaper-order rule. */ export class Rule extends Lint.Rules.AbstractRule { public static metadata: ExtendedMetadata = { ruleName: 'try-catch-first', type: 'maintainability', description: 'Try-catch blocks must be first within the scope. ' + 'Try-catch blocks are transactions and should leave your program in a consistent state, no matter what happens in the try.', options: null, optionsDescription: '', typescriptOnly: true, issueClass: 'Non-SDL', issueType: 'Warning', severity: 'Important', level: 'Opportunity for Excellence', group: 'Correctness', recommendation: 'true,', commonWeaknessEnumeration: '', }; public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { return this.applyWithWalker(new TryCatchFirstRuleWalker(sourceFile, this.getOptions())); } } class TryCatchFirstRuleWalker extends ErrorTolerantWalker { private scopeKinds: ts.SyntaxKind[] = [ ts.SyntaxKind.FunctionDeclaration, ts.SyntaxKind.ArrowFunction, ts.SyntaxKind.SourceFile, ts.SyntaxKind.MethodDeclaration, ts.SyntaxKind.GetAccessor, ts.SyntaxKind.SetAccessor, ]; protected visitTryStatement(node: ts.TryStatement): void { this.checkAndReport(node); super.visitTryStatement(node); } private checkAndReport(node: ts.TryStatement) { const block = node.parent; const { parent } = block; // console.log('checkAndReport', block, parent); // tslint:disable-line no-console const isFirst = this.scopeKinds.indexOf((parent || block).kind) !== -1; if (!isFirst) { const failureMessage = this.makeFailureMessage(); this.addFailureAt(node.getStart(), node.getWidth(), failureMessage); } } private makeFailureMessage(): string { return FAILURE_STRING; } }