diff --git a/docs/rules/semicolon/index.html b/docs/rules/semicolon/index.html
index 8b62432c4b1..095bfb1ee96 100644
--- a/docs/rules/semicolon/index.html
+++ b/docs/rules/semicolon/index.html
@@ -9,7 +9,6 @@
* `"never"` disallows semicolons at the end of every statement except for when they are necessary.
The following arguments may be optionaly provided:
-
* `"ignore-interfaces"` skips checking semicolons at the end of interface members.
* `"ignore-bound-class-methods"` skips checking semicolons at the end of bound class methods.
options:
@@ -52,4 +51,4 @@
],
"additionalItems": false
}
----
+---
\ No newline at end of file
diff --git a/src/language/rule/abstractRule.ts b/src/language/rule/abstractRule.ts
index 3d7f7aed9ec..242562560d3 100644
--- a/src/language/rule/abstractRule.ts
+++ b/src/language/rule/abstractRule.ts
@@ -17,7 +17,8 @@
import * as ts from "typescript";
-import {RuleWalker} from "../walker/ruleWalker";
+import {doesIntersect} from "../utils";
+import {IWalker} from "../walker";
import {IDisabledInterval, IOptions, IRule, IRuleMetadata, RuleFailure} from "./rule";
export abstract class AbstractRule implements IRule {
@@ -36,7 +37,7 @@ export abstract class AbstractRule implements IRule {
return false;
}
- constructor(ruleName: string, private value: any, disabledIntervals: IDisabledInterval[]) {
+ constructor(ruleName: string, private value: any, private disabledIntervals: IDisabledInterval[]) {
let ruleArguments: any[] = [];
if (Array.isArray(value) && value.length > 1) {
@@ -56,12 +57,23 @@ export abstract class AbstractRule implements IRule {
public abstract apply(sourceFile: ts.SourceFile, languageService: ts.LanguageService): RuleFailure[];
- public applyWithWalker(walker: RuleWalker): RuleFailure[] {
+ public applyWithWalker(walker: IWalker): RuleFailure[] {
walker.walk(walker.getSourceFile());
- return walker.getFailures();
+ return this.filterFailures(walker.getFailures());
}
public isEnabled(): boolean {
return AbstractRule.isRuleEnabled(this.value);
}
+
+ protected filterFailures(failures: RuleFailure[]): RuleFailure[] {
+ const result: RuleFailure[] = [];
+ for (const failure of failures) {
+ // don't add failures for a rule if the failure intersects an interval where that rule is disabled
+ if (!doesIntersect(failure, this.disabledIntervals) && !result.some((f) => f.equals(failure))) {
+ result.push(failure);
+ }
+ }
+ return result;
+ }
}
diff --git a/src/language/rule/rule.ts b/src/language/rule/rule.ts
index 5e969b3710e..16ae16950d8 100644
--- a/src/language/rule/rule.ts
+++ b/src/language/rule/rule.ts
@@ -17,7 +17,7 @@
import * as ts from "typescript";
-import {RuleWalker} from "../walker/ruleWalker";
+import {IWalker} from "../walker";
export interface IRuleMetadata {
/**
@@ -96,7 +96,7 @@ export interface IRule {
getOptions(): IOptions;
isEnabled(): boolean;
apply(sourceFile: ts.SourceFile, languageService: ts.LanguageService): RuleFailure[];
- applyWithWalker(walker: RuleWalker): RuleFailure[];
+ applyWithWalker(walker: IWalker): RuleFailure[];
}
export class Replacement {
diff --git a/src/language/utils.ts b/src/language/utils.ts
index 6a17f363acb..826283e904b 100644
--- a/src/language/utils.ts
+++ b/src/language/utils.ts
@@ -196,3 +196,36 @@ export function unwrapParentheses(node: ts.Expression) {
}
return node;
}
+
+export function isScopeBoundary(node: ts.Node): boolean {
+ return node.kind === ts.SyntaxKind.FunctionDeclaration
+ || node.kind === ts.SyntaxKind.FunctionExpression
+ || node.kind === ts.SyntaxKind.PropertyAssignment
+ || node.kind === ts.SyntaxKind.ShorthandPropertyAssignment
+ || node.kind === ts.SyntaxKind.MethodDeclaration
+ || node.kind === ts.SyntaxKind.Constructor
+ || node.kind === ts.SyntaxKind.ModuleDeclaration
+ || node.kind === ts.SyntaxKind.ArrowFunction
+ || node.kind === ts.SyntaxKind.ParenthesizedExpression
+ || node.kind === ts.SyntaxKind.ClassDeclaration
+ || node.kind === ts.SyntaxKind.ClassExpression
+ || node.kind === ts.SyntaxKind.InterfaceDeclaration
+ || node.kind === ts.SyntaxKind.GetAccessor
+ || node.kind === ts.SyntaxKind.SetAccessor
+ || node.kind === ts.SyntaxKind.SourceFile && ts.isExternalModule(node as ts.SourceFile);
+}
+
+export function isBlockScopeBoundary(node: ts.Node): boolean {
+ return isScopeBoundary(node)
+ || node.kind === ts.SyntaxKind.Block
+ || node.kind === ts.SyntaxKind.DoStatement
+ || node.kind === ts.SyntaxKind.WhileStatement
+ || node.kind === ts.SyntaxKind.ForStatement
+ || node.kind === ts.SyntaxKind.ForInStatement
+ || node.kind === ts.SyntaxKind.ForOfStatement
+ || node.kind === ts.SyntaxKind.WithStatement
+ || node.kind === ts.SyntaxKind.SwitchStatement
+ || node.parent !== undefined
+ && (node.parent.kind === ts.SyntaxKind.TryStatement
+ || node.parent.kind === ts.SyntaxKind.IfStatement);
+}
diff --git a/src/language/walker/blockScopeAwareRuleWalker.ts b/src/language/walker/blockScopeAwareRuleWalker.ts
index 75f862ee61a..185ab14e723 100644
--- a/src/language/walker/blockScopeAwareRuleWalker.ts
+++ b/src/language/walker/blockScopeAwareRuleWalker.ts
@@ -17,6 +17,7 @@
import * as ts from "typescript";
+import {isBlockScopeBoundary} from "../utils";
import {ScopeAwareRuleWalker} from "./scopeAwareRuleWalker";
/**
@@ -30,7 +31,7 @@ export abstract class BlockScopeAwareRuleWalker extends ScopeAwareRuleWalk
super(sourceFile, options);
// initialize with global scope if file is not a module
- this.blockScopeStack = this.fileIsModule ? [] : [this.createBlockScope(sourceFile)];
+ this.blockScopeStack = ts.isExternalModule(sourceFile) ? [] : [this.createBlockScope(sourceFile)];
}
public abstract createBlockScope(node: ts.Node): U;
@@ -85,20 +86,6 @@ export abstract class BlockScopeAwareRuleWalker extends ScopeAwareRuleWalk
}
private isBlockScopeBoundary(node: ts.Node): boolean {
- return super.isScopeBoundary(node)
- || node.kind === ts.SyntaxKind.Block
- || node.kind === ts.SyntaxKind.DoStatement
- || node.kind === ts.SyntaxKind.WhileStatement
- || node.kind === ts.SyntaxKind.ForStatement
- || node.kind === ts.SyntaxKind.ForInStatement
- || node.kind === ts.SyntaxKind.ForOfStatement
- || node.kind === ts.SyntaxKind.WithStatement
- || node.kind === ts.SyntaxKind.SwitchStatement
- || isParentKind(node, ts.SyntaxKind.TryStatement)
- || isParentKind(node, ts.SyntaxKind.IfStatement);
+ return isBlockScopeBoundary(node);
}
}
-
-function isParentKind(node: ts.Node, kind: ts.SyntaxKind) {
- return node.parent != null && node.parent.kind === kind;
-}
diff --git a/src/language/walker/index.ts b/src/language/walker/index.ts
index b762aa289ed..bf1ab5df58b 100644
--- a/src/language/walker/index.ts
+++ b/src/language/walker/index.ts
@@ -21,3 +21,4 @@ export * from "./ruleWalker";
export * from "./scopeAwareRuleWalker";
export * from "./skippableTokenAwareRuleWalker";
export * from "./syntaxWalker";
+export * from "./walker";
diff --git a/src/language/walker/ruleWalker.ts b/src/language/walker/ruleWalker.ts
index 0f98f2e41cc..bc43d53c058 100644
--- a/src/language/walker/ruleWalker.ts
+++ b/src/language/walker/ruleWalker.ts
@@ -17,26 +17,22 @@
import * as ts from "typescript";
-import {Fix, IDisabledInterval, IOptions, Replacement, RuleFailure} from "../rule/rule";
-import {doesIntersect} from "../utils";
+import {Fix, IOptions, Replacement, RuleFailure} from "../rule/rule";
import {SyntaxWalker} from "./syntaxWalker";
+import {IWalker} from "./walker";
-export class RuleWalker extends SyntaxWalker {
+export class RuleWalker extends SyntaxWalker implements IWalker {
private limit: number;
- private position: number;
private options?: any[];
private failures: RuleFailure[];
- private disabledIntervals: IDisabledInterval[];
private ruleName: string;
constructor(private sourceFile: ts.SourceFile, options: IOptions) {
super();
- this.position = 0;
this.failures = [];
this.options = options.ruleArguments;
this.limit = this.sourceFile.getFullWidth();
- this.disabledIntervals = options.disabledIntervals;
this.ruleName = options.ruleName;
}
@@ -68,8 +64,8 @@ export class RuleWalker extends SyntaxWalker {
}
}
- public skip(node: ts.Node) {
- this.position += node.getFullWidth();
+ public skip(_node: ts.Node) {
+ return; // TODO remove this method in next major version
}
public createFailure(start: number, width: number, failure: string, fix?: Fix): RuleFailure {
@@ -79,10 +75,7 @@ export class RuleWalker extends SyntaxWalker {
}
public addFailure(failure: RuleFailure) {
- // don't add failures for a rule if the failure intersects an interval where that rule is disabled
- if (!this.existsFailure(failure) && !doesIntersect(failure, this.disabledIntervals)) {
- this.failures.push(failure);
- }
+ this.failures.push(failure);
}
/** Add a failure with any arbitrary span. Prefer `addFailureAtNode` if possible. */
@@ -97,7 +90,7 @@ export class RuleWalker extends SyntaxWalker {
/** Add a failure using a node's span. */
public addFailureAtNode(node: ts.Node, failure: string, fix?: Fix) {
- this.addFailureAt(node.getStart(), node.getWidth(), failure, fix);
+ this.addFailureAt(node.getStart(this.sourceFile), node.getWidth(this.sourceFile), failure, fix);
}
public createReplacement(start: number, length: number, text: string): Replacement {
@@ -112,7 +105,11 @@ export class RuleWalker extends SyntaxWalker {
return this.createReplacement(start, length, "");
}
- private existsFailure(failure: RuleFailure) {
- return this.failures.some((f) => f.equals(failure));
+ public getRuleName(): string {
+ return this.ruleName;
+ }
+
+ public createFix(replacements: Replacement[]): Fix {
+ return new Fix(this.ruleName, replacements);
}
}
diff --git a/src/language/walker/scopeAwareRuleWalker.ts b/src/language/walker/scopeAwareRuleWalker.ts
index aa01d4af19e..d7ab2d002d3 100644
--- a/src/language/walker/scopeAwareRuleWalker.ts
+++ b/src/language/walker/scopeAwareRuleWalker.ts
@@ -17,19 +17,17 @@
import * as ts from "typescript";
+import {isScopeBoundary} from "../utils";
import {RuleWalker} from "./ruleWalker";
export abstract class ScopeAwareRuleWalker extends RuleWalker {
- protected fileIsModule: boolean;
private scopeStack: T[];
constructor(sourceFile: ts.SourceFile, options?: any) {
super(sourceFile, options);
- this.fileIsModule = ts.isExternalModule(sourceFile);
-
// initialize with global scope if file is not a module
- this.scopeStack = this.fileIsModule ? [] : [this.createScope(sourceFile)];
+ this.scopeStack = ts.isExternalModule(sourceFile) ? [] : [this.createScope(sourceFile)];
}
public abstract createScope(node: ts.Node): T;
@@ -40,7 +38,7 @@ export abstract class ScopeAwareRuleWalker extends RuleWalker {
// get all scopes available at this depth
public getAllScopes(): T[] {
- return this.scopeStack.slice();
+ return this.scopeStack;
}
public getCurrentDepth(): number {
@@ -74,20 +72,6 @@ export abstract class ScopeAwareRuleWalker extends RuleWalker {
}
protected isScopeBoundary(node: ts.Node): boolean {
- return node.kind === ts.SyntaxKind.FunctionDeclaration
- || node.kind === ts.SyntaxKind.FunctionExpression
- || node.kind === ts.SyntaxKind.PropertyAssignment
- || node.kind === ts.SyntaxKind.ShorthandPropertyAssignment
- || node.kind === ts.SyntaxKind.MethodDeclaration
- || node.kind === ts.SyntaxKind.Constructor
- || node.kind === ts.SyntaxKind.ModuleDeclaration
- || node.kind === ts.SyntaxKind.ArrowFunction
- || node.kind === ts.SyntaxKind.ParenthesizedExpression
- || node.kind === ts.SyntaxKind.ClassDeclaration
- || node.kind === ts.SyntaxKind.ClassExpression
- || node.kind === ts.SyntaxKind.InterfaceDeclaration
- || node.kind === ts.SyntaxKind.GetAccessor
- || node.kind === ts.SyntaxKind.SetAccessor
- || (node.kind === ts.SyntaxKind.SourceFile && this.fileIsModule);
+ return isScopeBoundary(node);
}
}
diff --git a/src/language/walker/walker.ts b/src/language/walker/walker.ts
new file mode 100644
index 00000000000..3c07585e418
--- /dev/null
+++ b/src/language/walker/walker.ts
@@ -0,0 +1,26 @@
+/**
+ * @license
+ * Copyright 2017 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.
+ */
+
+import * as ts from "typescript";
+
+import {RuleFailure} from "../rule/rule";
+
+export interface IWalker {
+ getSourceFile(): ts.SourceFile;
+ walk(node: ts.Node): void;
+ getFailures(): RuleFailure[];
+}