diff --git a/CHANGELOG.md b/CHANGELOG.md
index b730f887261..af5bde5d233 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,17 @@
Change Log
===
+v3.14.0-dev.0
+---
+* [enhancement] Add optional type information to rules (#1323)
+
+Thanks to our contributors!
+* @ScottSWu
+
+v3.13.0
+---
+* Stable release containing changes from the last dev release (v3.13.0-dev.0)
+
v3.13.0-dev.0
---
* [new-rule] `ordered-imports` rule (#1325)
diff --git a/README.md b/README.md
index c4bb6b2a2d8..c1514a93528 100644
--- a/README.md
+++ b/README.md
@@ -115,6 +115,8 @@ Options:
-e, --exclude exclude globs from path expansion
-t, --format output format (prose, json, verbose, pmd, msbuild, checkstyle) [default: "prose"]
--test test that tslint produces the correct output for the specified directory
+--project path to tsconfig.json file
+--type-check enable type checking when linting a project
-v, --version current version
```
@@ -182,6 +184,14 @@ tslint accepts the following command-line options:
specified directory as the configuration file for the tests. See the
full tslint documentation for more details on how this can be used to test custom rules.
+--project:
+ The location of a tsconfig.json file that will be used to determine which
+ files will be linted.
+
+--type-check
+ Enables the type checker when running linting rules. --project must be
+ specified in order to enable type checking.
+
-v, --version:
The current version of tslint.
@@ -214,6 +224,23 @@ const linter = new Linter(fileName, fileContents, options);
const result = linter.lint();
```
+#### Type Checking
+
+To enable rules that work with the type checker, a TypeScript program object must be passed to the linter when using the programmatic API. Helper functions are provided to create a program from a `tsconfig.json` file. A project directory can be specified if project files do not lie in the same directory as the `tsconfig.json` file.
+
+```javascript
+const program = Linter.createProgram("tsconfig.json", "projectDir/");
+const files = Linter.getFileNames(program);
+const results = files.map(file => {
+ const fileContents = program.getSourceFile(file).getFullText();
+ const linter = new Linter(file, fileContents, options, program);
+ return result.lint();
+});
+```
+
+When using the CLI, the `--project` flag will automatically create a program from the specified `tsconfig.json` file. Adding `--type-check` then enables rules that require the type checker.
+
+
Core Rules
-----
[back to ToC ↑](#table-of-contents)
@@ -313,6 +340,7 @@ Core rules are included in the `tslint` package.
* `"jsx-double"` enforces double quotes for JSX attributes.
* `"avoid-escape"` allows you to use the "other" quotemark in cases where escaping would normally be required. For example, `[true, "double", "avoid-escape"]` would not report a failure on the string literal `'Hello "World"'`.
* `radix` enforces the radix parameter of `parseInt`.
+* `restrict-plus-operands` enforces the type of addition operands to be both `string` or both `number` (requires type checking).
* `semicolon` enforces consistent semicolon usage at the end of every statement. Rule options:
* `"always"` enforces semicolons at the end of every statement.
* `"never"` disallows semicolons at the end of every statement except for when they are necessary.
diff --git a/package.json b/package.json
index d8d8fd98c9c..21ca3fc1354 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "tslint",
- "version": "3.13.0-dev.0",
+ "version": "3.14.0-dev.0",
"description": "An extensible static analysis linter for the TypeScript language",
"bin": {
"tslint": "./bin/tslint"
@@ -43,7 +43,7 @@
"mocha": "^2.2.5",
"tslint": "next",
"tslint-test-config-non-relative": "file:test/external/tslint-test-config-non-relative",
- "typescript": "next"
+ "typescript": ">=2.0.0-dev"
},
"peerDependencies": {
"typescript": ">=1.7.3 || >=1.8.0-dev || >=1.9.0-dev || >=2.0.0-dev || >=2.1.0-dev"
diff --git a/scripts/buildDocs.ts b/scripts/buildDocs.ts
index c007f7dd121..79ef1c6c0bd 100644
--- a/scripts/buildDocs.ts
+++ b/scripts/buildDocs.ts
@@ -33,10 +33,11 @@
import * as fs from "fs";
import * as glob from "glob";
-import * as path from "path";
import * as yaml from "js-yaml";
-import {IRuleMetadata} from "../lib/language/rule/rule";
+import * as path from "path";
+
import {AbstractRule} from "../lib/language/rule/abstractRule";
+import {IRuleMetadata} from "../lib/language/rule/rule";
const DOCS_DIR = "../../tslint-gh-pages";
const DOCS_RULE_DIR = path.join(DOCS_DIR, "rules");
diff --git a/src/configuration.ts b/src/configuration.ts
index ce8c648cb79..ee1e98d0eff 100644
--- a/src/configuration.ts
+++ b/src/configuration.ts
@@ -15,9 +15,9 @@
* limitations under the License.
*/
+import findup = require("findup-sync");
import * as fs from "fs";
import * as path from "path";
-import * as findup from "findup-sync";
import * as resolve from "resolve";
import {arrayify, objectify, stripComments} from "./utils";
diff --git a/src/enableDisableRules.ts b/src/enableDisableRules.ts
index 8f32cb7e0d4..a01657499b3 100644
--- a/src/enableDisableRules.ts
+++ b/src/enableDisableRules.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import {scanAllTokens} from "./language/utils";
import {SkippableTokenAwareRuleWalker} from "./language/walker/skippableTokenAwareRuleWalker";
import {IEnableDisablePosition} from "./ruleLoader";
@@ -64,10 +65,23 @@ export class EnableDisableRulesWalker extends SkippableTokenAwareRuleWalker {
const isEnabled = enableOrDisableMatch[1] === "enable";
const isCurrentLine = enableOrDisableMatch[3] === "line";
const isNextLine = enableOrDisableMatch[3] === "next-line";
+
let rulesList = ["all"];
- if (commentTextParts.length > 2) {
+
+ if (commentTextParts.length === 2) {
+ // an implicit whitespace separator is used for the rules list.
+ rulesList = commentTextParts[1].split(/\s+/).slice(1);
+
+ // remove empty items and potential comment end.
+ rulesList = rulesList.filter(item => !!item && item.indexOf("*/") === -1);
+
+ // potentially there were no items, so default to `all`.
+ rulesList = rulesList.length > 0 ? rulesList : ["all"];
+ } else if (commentTextParts.length > 2) {
+ // an explicit separator was specified for the rules list.
rulesList = commentTextParts[2].split(/\s+/);
}
+
for (const ruleToAdd of rulesList) {
if (!(ruleToAdd in this.enableDisableRuleMap)) {
this.enableDisableRuleMap[ruleToAdd] = [];
diff --git a/src/language/languageServiceHost.ts b/src/language/languageServiceHost.ts
index 40ffc125464..180e110c054 100644
--- a/src/language/languageServiceHost.ts
+++ b/src/language/languageServiceHost.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import {createCompilerOptions} from "./utils";
export function createLanguageServiceHost(fileName: string, source: string): ts.LanguageServiceHost {
diff --git a/src/language/rule/abstractRule.ts b/src/language/rule/abstractRule.ts
index 1c590d35fed..4e5af465c71 100644
--- a/src/language/rule/abstractRule.ts
+++ b/src/language/rule/abstractRule.ts
@@ -16,23 +16,22 @@
*/
import * as ts from "typescript";
+
import {IOptions} from "../../lint";
import {RuleWalker} from "../walker/ruleWalker";
-import {IRule, IRuleMetadata, IDisabledInterval, RuleFailure} from "./rule";
+import {IDisabledInterval, IRule, IRuleMetadata, RuleFailure} from "./rule";
export abstract class AbstractRule implements IRule {
public static metadata: IRuleMetadata;
- private value: any;
private options: IOptions;
- constructor(ruleName: string, value: any, disabledIntervals: IDisabledInterval[]) {
+ constructor(ruleName: string, private value: any, disabledIntervals: IDisabledInterval[]) {
let ruleArguments: any[] = [];
if (Array.isArray(value) && value.length > 1) {
ruleArguments = value.slice(1);
}
- this.value = value;
this.options = {
disabledIntervals: disabledIntervals,
ruleArguments: ruleArguments,
diff --git a/src/language/rule/rule.ts b/src/language/rule/rule.ts
index 11debf0e676..59364165987 100644
--- a/src/language/rule/rule.ts
+++ b/src/language/rule/rule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import {RuleWalker} from "../walker/ruleWalker";
export interface IRuleMetadata {
@@ -61,6 +62,11 @@ export interface IRuleMetadata {
* An explanation of why the rule is useful.
*/
rationale?: string;
+
+ /**
+ * Whether or not the rule requires type info to run.
+ */
+ requiresTypeInfo?: boolean;
}
export type RuleType = "functionality" | "maintainability" | "style" | "typescript";
@@ -84,12 +90,7 @@ export interface IRule {
}
export class RuleFailurePosition {
- private position: number;
- private lineAndCharacter: ts.LineAndCharacter;
-
- constructor(position: number, lineAndCharacter: ts.LineAndCharacter) {
- this.position = position;
- this.lineAndCharacter = lineAndCharacter;
+ constructor(private position: number, private lineAndCharacter: ts.LineAndCharacter) {
}
public getPosition() {
@@ -119,25 +120,19 @@ export class RuleFailurePosition {
}
export class RuleFailure {
- private sourceFile: ts.SourceFile;
private fileName: string;
private startPosition: RuleFailurePosition;
private endPosition: RuleFailurePosition;
- private failure: string;
- private ruleName: string;
- constructor(sourceFile: ts.SourceFile,
+ constructor(private sourceFile: ts.SourceFile,
start: number,
end: number,
- failure: string,
- ruleName: string) {
+ private failure: string,
+ private ruleName: string) {
- this.sourceFile = sourceFile;
this.fileName = sourceFile.fileName;
this.startPosition = this.createFailurePosition(start);
this.endPosition = this.createFailurePosition(end);
- this.failure = failure;
- this.ruleName = ruleName;
}
public getFileName() {
diff --git a/src/language/rule/typedRule.ts b/src/language/rule/typedRule.ts
new file mode 100644
index 00000000000..05b5797bb45
--- /dev/null
+++ b/src/language/rule/typedRule.ts
@@ -0,0 +1,30 @@
+/**
+ * @license
+ * Copyright 2016 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 {AbstractRule} from "./abstractRule";
+import {RuleFailure} from "./rule";
+
+export abstract class TypedRule extends AbstractRule {
+ public apply(sourceFile: ts.SourceFile): RuleFailure[] {
+ // if no program is given to the linter, throw an error
+ throw new Error(`${this.getOptions().ruleName} requires type checking`);
+ }
+
+ public abstract applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): RuleFailure[];
+}
diff --git a/src/language/utils.ts b/src/language/utils.ts
index 4559ad28b71..3891961885c 100644
--- a/src/language/utils.ts
+++ b/src/language/utils.ts
@@ -17,6 +17,7 @@
import * as path from "path";
import * as ts from "typescript";
+
import {IDisabledInterval, RuleFailure} from "./rule/rule";
export function getSourceFile(fileName: string, source: string): ts.SourceFile {
diff --git a/src/language/walker/blockScopeAwareRuleWalker.ts b/src/language/walker/blockScopeAwareRuleWalker.ts
index 6bfd13d6215..e1e17afea24 100644
--- a/src/language/walker/blockScopeAwareRuleWalker.ts
+++ b/src/language/walker/blockScopeAwareRuleWalker.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import {ScopeAwareRuleWalker} from "./scopeAwareRuleWalker";
/**
diff --git a/src/language/walker/index.ts b/src/language/walker/index.ts
index 9d0819b9900..b762aa289ed 100644
--- a/src/language/walker/index.ts
+++ b/src/language/walker/index.ts
@@ -16,6 +16,7 @@
*/
export * from "./blockScopeAwareRuleWalker";
+export * from "./programAwareRuleWalker";
export * from "./ruleWalker";
export * from "./scopeAwareRuleWalker";
export * from "./skippableTokenAwareRuleWalker";
diff --git a/src/language/walker/programAwareRuleWalker.ts b/src/language/walker/programAwareRuleWalker.ts
new file mode 100644
index 00000000000..6b29e1fdcd4
--- /dev/null
+++ b/src/language/walker/programAwareRuleWalker.ts
@@ -0,0 +1,39 @@
+/**
+ * @license
+ * Copyright 2016 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 {IOptions} from "../../lint";
+import {RuleWalker} from "./ruleWalker";
+
+export class ProgramAwareRuleWalker extends RuleWalker {
+ private typeChecker: ts.TypeChecker;
+
+ constructor(sourceFile: ts.SourceFile, options: IOptions, private program: ts.Program) {
+ super(sourceFile, options);
+
+ this.typeChecker = program.getTypeChecker();
+ }
+
+ public getProgram(): ts.Program {
+ return this.program;
+ }
+
+ public getTypeChecker(): ts.TypeChecker {
+ return this.typeChecker;
+ }
+}
diff --git a/src/language/walker/ruleWalker.ts b/src/language/walker/ruleWalker.ts
index b74950545f8..25edcb97431 100644
--- a/src/language/walker/ruleWalker.ts
+++ b/src/language/walker/ruleWalker.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import {IOptions} from "../../lint";
import {IDisabledInterval, RuleFailure} from "../rule/rule";
import {doesIntersect} from "../utils";
@@ -26,17 +27,15 @@ export class RuleWalker extends SyntaxWalker {
private position: number;
private options: any[];
private failures: RuleFailure[];
- private sourceFile: ts.SourceFile;
private disabledIntervals: IDisabledInterval[];
private ruleName: string;
- constructor(sourceFile: ts.SourceFile, options: IOptions) {
+ constructor(private sourceFile: ts.SourceFile, options: IOptions) {
super();
this.position = 0;
this.failures = [];
this.options = options.ruleArguments;
- this.sourceFile = sourceFile;
this.limit = this.sourceFile.getFullWidth();
this.disabledIntervals = options.disabledIntervals;
this.ruleName = options.ruleName;
diff --git a/src/language/walker/scopeAwareRuleWalker.ts b/src/language/walker/scopeAwareRuleWalker.ts
index 7101ea57742..0c2b988592d 100644
--- a/src/language/walker/scopeAwareRuleWalker.ts
+++ b/src/language/walker/scopeAwareRuleWalker.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import {RuleWalker} from "./ruleWalker";
export abstract class ScopeAwareRuleWalker extends RuleWalker {
diff --git a/src/language/walker/skippableTokenAwareRuleWalker.ts b/src/language/walker/skippableTokenAwareRuleWalker.ts
index c2af57f841a..a374fad746d 100644
--- a/src/language/walker/skippableTokenAwareRuleWalker.ts
+++ b/src/language/walker/skippableTokenAwareRuleWalker.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import {IOptions} from "../../lint";
import {RuleWalker} from "./ruleWalker";
diff --git a/src/lint.ts b/src/lint.ts
index 263660cc139..a154c2f6018 100644
--- a/src/lint.ts
+++ b/src/lint.ts
@@ -17,11 +17,11 @@
import * as configuration from "./configuration";
import * as formatters from "./formatters";
-import * as linter from "./tslint";
+import {RuleFailure} from "./language/rule/rule";
import * as rules from "./rules";
import * as test from "./test";
+import * as linter from "./tslint";
import * as utils from "./utils";
-import {RuleFailure} from "./language/rule/rule";
export * from "./language/rule/rule";
export * from "./enableDisableRules";
diff --git a/src/ruleLoader.ts b/src/ruleLoader.ts
index b2af511f91d..5b47c0728fe 100644
--- a/src/ruleLoader.ts
+++ b/src/ruleLoader.ts
@@ -18,8 +18,9 @@
import * as fs from "fs";
import * as path from "path";
import {camelize} from "underscore.string";
+
import {getRulesDirectories} from "./configuration";
-import {IRule, IDisabledInterval} from "./language/rule/rule";
+import {IDisabledInterval, IRule} from "./language/rule/rule";
const moduleDirectory = path.dirname(module.filename);
const CORE_RULES_DIRECTORY = path.resolve(moduleDirectory, ".", "rules");
diff --git a/src/rules.ts b/src/rules.ts
index 8c0cde4f290..1cafb4033db 100644
--- a/src/rules.ts
+++ b/src/rules.ts
@@ -16,3 +16,4 @@
*/
export * from "./language/rule/abstractRule";
+export * from "./language/rule/typedRule";
diff --git a/src/rules/alignRule.ts b/src/rules/alignRule.ts
index f58405c82e8..40327a20731 100644
--- a/src/rules/alignRule.ts
+++ b/src/rules/alignRule.ts
@@ -15,9 +15,10 @@
* limitations under the License.
*/
-import * as Lint from "../lint";
import * as ts from "typescript";
+import * as Lint from "../lint";
+
export class Rule extends Lint.Rules.AbstractRule {
/* tslint:disable:object-literal-sort-keys */
public static metadata: Lint.IRuleMetadata = {
diff --git a/src/rules/banRule.ts b/src/rules/banRule.ts
index 2fb5dca9cb9..ec2c6b970b7 100644
--- a/src/rules/banRule.ts
+++ b/src/rules/banRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/classNameRule.ts b/src/rules/classNameRule.ts
index f14d6f50e3b..fb31837c796 100644
--- a/src/rules/classNameRule.ts
+++ b/src/rules/classNameRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/commentFormatRule.ts b/src/rules/commentFormatRule.ts
index 13b38f8f59d..3742141e7f5 100644
--- a/src/rules/commentFormatRule.ts
+++ b/src/rules/commentFormatRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
const OPTION_SPACE = "check-space";
diff --git a/src/rules/curlyRule.ts b/src/rules/curlyRule.ts
index bbde3d4a594..dc8cb3560e6 100644
--- a/src/rules/curlyRule.ts
+++ b/src/rules/curlyRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/eoflineRule.ts b/src/rules/eoflineRule.ts
index 58d806fbda2..c835290abb5 100644
--- a/src/rules/eoflineRule.ts
+++ b/src/rules/eoflineRule.ts
@@ -15,9 +15,10 @@
* limitations under the License.
*/
-import * as Lint from "../lint";
import * as ts from "typescript";
+import * as Lint from "../lint";
+
export class Rule extends Lint.Rules.AbstractRule {
/* tslint:disable:object-literal-sort-keys */
public static metadata: Lint.IRuleMetadata = {
diff --git a/src/rules/forinRule.ts b/src/rules/forinRule.ts
index c5758194ec6..7c6f6e5f2a4 100644
--- a/src/rules/forinRule.ts
+++ b/src/rules/forinRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/indentRule.ts b/src/rules/indentRule.ts
index cbb2cd47da6..0cda08317de 100644
--- a/src/rules/indentRule.ts
+++ b/src/rules/indentRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
const OPTION_USE_TABS = "tabs";
diff --git a/src/rules/interfaceNameRule.ts b/src/rules/interfaceNameRule.ts
index c9434ce5de9..c2ac0f54145 100644
--- a/src/rules/interfaceNameRule.ts
+++ b/src/rules/interfaceNameRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
const OPTION_ALWAYS = "always-prefix";
diff --git a/src/rules/jsdocFormatRule.ts b/src/rules/jsdocFormatRule.ts
index 8d9773e056c..460a612e618 100644
--- a/src/rules/jsdocFormatRule.ts
+++ b/src/rules/jsdocFormatRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/labelPositionRule.ts b/src/rules/labelPositionRule.ts
index 8f38a845d42..e55474110fd 100644
--- a/src/rules/labelPositionRule.ts
+++ b/src/rules/labelPositionRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/labelUndefinedRule.ts b/src/rules/labelUndefinedRule.ts
index cc5cbb531e9..54df05d9c33 100644
--- a/src/rules/labelUndefinedRule.ts
+++ b/src/rules/labelUndefinedRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/linebreakStyleRule.ts b/src/rules/linebreakStyleRule.ts
index d45cf2f39bd..4fcc62ec381 100644
--- a/src/rules/linebreakStyleRule.ts
+++ b/src/rules/linebreakStyleRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
const OPTION_LINEBREAK_STYLE_CRLF = "CRLF";
diff --git a/src/rules/maxLineLengthRule.ts b/src/rules/maxLineLengthRule.ts
index 36d99fa0acd..2fa48b520e8 100644
--- a/src/rules/maxLineLengthRule.ts
+++ b/src/rules/maxLineLengthRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/memberAccessRule.ts b/src/rules/memberAccessRule.ts
index c1bd92272a9..61bb205ae23 100644
--- a/src/rules/memberAccessRule.ts
+++ b/src/rules/memberAccessRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
@@ -57,10 +58,6 @@ export class Rule extends Lint.Rules.AbstractRule {
}
export class MemberAccessWalker extends Lint.RuleWalker {
- constructor(sourceFile: ts.SourceFile, options: Lint.IOptions) {
- super(sourceFile, options);
- }
-
public visitConstructorDeclaration(node: ts.ConstructorDeclaration) {
if (this.hasOption("check-constructor")) {
// constructor is only allowed to have public or nothing, but the compiler will catch this
diff --git a/src/rules/memberOrderingRule.ts b/src/rules/memberOrderingRule.ts
index c663439a3e1..886c5486636 100644
--- a/src/rules/memberOrderingRule.ts
+++ b/src/rules/memberOrderingRule.ts
@@ -14,9 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import * as Lint from "../lint";
import * as ts from "typescript";
+import * as Lint from "../lint";
+
/* start old options */
const OPTION_VARIABLES_BEFORE_FUNCTIONS = "variables-before-functions";
const OPTION_STATIC_BEFORE_INSTANCE = "static-before-instance";
diff --git a/src/rules/newParensRule.ts b/src/rules/newParensRule.ts
index d06c37684a0..a39cb0eda60 100644
--- a/src/rules/newParensRule.ts
+++ b/src/rules/newParensRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/noAngleBracketTypeAssertionRule.ts b/src/rules/noAngleBracketTypeAssertionRule.ts
index 156b9422d70..854467336c7 100644
--- a/src/rules/noAngleBracketTypeAssertionRule.ts
+++ b/src/rules/noAngleBracketTypeAssertionRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/noAnyRule.ts b/src/rules/noAnyRule.ts
index b2576d0a31f..a0b602dc707 100644
--- a/src/rules/noAnyRule.ts
+++ b/src/rules/noAnyRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/noArgRule.ts b/src/rules/noArgRule.ts
index 9ba40637956..8b34f8437ef 100644
--- a/src/rules/noArgRule.ts
+++ b/src/rules/noArgRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/noBitwiseRule.ts b/src/rules/noBitwiseRule.ts
index 4f64e916fbd..4a5b0a74795 100644
--- a/src/rules/noBitwiseRule.ts
+++ b/src/rules/noBitwiseRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/noConditionalAssignmentRule.ts b/src/rules/noConditionalAssignmentRule.ts
index f918f83cdb2..b59d87fed40 100644
--- a/src/rules/noConditionalAssignmentRule.ts
+++ b/src/rules/noConditionalAssignmentRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/noConsecutiveBlankLinesRule.ts b/src/rules/noConsecutiveBlankLinesRule.ts
index c003fbbff63..69e737fd0f9 100644
--- a/src/rules/noConsecutiveBlankLinesRule.ts
+++ b/src/rules/noConsecutiveBlankLinesRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/noConsoleRule.ts b/src/rules/noConsoleRule.ts
index 27c06d4fbf3..8c9f83fb52c 100644
--- a/src/rules/noConsoleRule.ts
+++ b/src/rules/noConsoleRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
import * as BanRule from "./banRule";
diff --git a/src/rules/noConstructRule.ts b/src/rules/noConstructRule.ts
index 389eadc96ec..b9fc89a19fa 100644
--- a/src/rules/noConstructRule.ts
+++ b/src/rules/noConstructRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/noConstructorVarsRule.ts b/src/rules/noConstructorVarsRule.ts
index 7cbe5ff3eec..446bb1e1138 100644
--- a/src/rules/noConstructorVarsRule.ts
+++ b/src/rules/noConstructorVarsRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/noDebuggerRule.ts b/src/rules/noDebuggerRule.ts
index 21c0de2f56c..a4b5a5130ba 100644
--- a/src/rules/noDebuggerRule.ts
+++ b/src/rules/noDebuggerRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/noDefaultExportRule.ts b/src/rules/noDefaultExportRule.ts
index ee6c592d703..be65e9273cb 100644
--- a/src/rules/noDefaultExportRule.ts
+++ b/src/rules/noDefaultExportRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/noDuplicateKeyRule.ts b/src/rules/noDuplicateKeyRule.ts
index 22f13d4c61a..9f8973f72ab 100644
--- a/src/rules/noDuplicateKeyRule.ts
+++ b/src/rules/noDuplicateKeyRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/noDuplicateVariableRule.ts b/src/rules/noDuplicateVariableRule.ts
index 179878fa919..17c7694d625 100644
--- a/src/rules/noDuplicateVariableRule.ts
+++ b/src/rules/noDuplicateVariableRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/noEmptyRule.ts b/src/rules/noEmptyRule.ts
index 5ffb361a47e..d3a3e20e536 100644
--- a/src/rules/noEmptyRule.ts
+++ b/src/rules/noEmptyRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/noEvalRule.ts b/src/rules/noEvalRule.ts
index 63c0cc0739f..9e6ee898efb 100644
--- a/src/rules/noEvalRule.ts
+++ b/src/rules/noEvalRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/noInferrableTypesRule.ts b/src/rules/noInferrableTypesRule.ts
index acf787ef004..99ee2f28f9f 100644
--- a/src/rules/noInferrableTypesRule.ts
+++ b/src/rules/noInferrableTypesRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
const OPTION_IGNORE_PARMS = "ignore-params";
diff --git a/src/rules/noInternalModuleRule.ts b/src/rules/noInternalModuleRule.ts
index 599b1dc0191..4e890d05ded 100644
--- a/src/rules/noInternalModuleRule.ts
+++ b/src/rules/noInternalModuleRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/noInvalidThisRule.ts b/src/rules/noInvalidThisRule.ts
index 6f4f35bcab4..21511135a96 100644
--- a/src/rules/noInvalidThisRule.ts
+++ b/src/rules/noInvalidThisRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
interface Scope {
diff --git a/src/rules/noMergeableNamespaceRule.ts b/src/rules/noMergeableNamespaceRule.ts
index c5c1dda6bce..d3466c5e286 100644
--- a/src/rules/noMergeableNamespaceRule.ts
+++ b/src/rules/noMergeableNamespaceRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
@@ -42,11 +43,8 @@ export class Rule extends Lint.Rules.AbstractRule {
}
class NoMergeableNamespaceWalker extends Lint.RuleWalker {
- private languageService: ts.LanguageService;
-
- constructor(sourceFile: ts.SourceFile, options: Lint.IOptions, languageService: ts.LanguageService) {
+ constructor(sourceFile: ts.SourceFile, options: Lint.IOptions, private languageService: ts.LanguageService) {
super(sourceFile, options);
- this.languageService = languageService;
}
public visitModuleDeclaration(node: ts.ModuleDeclaration) {
diff --git a/src/rules/noNamespaceRule.ts b/src/rules/noNamespaceRule.ts
index 171f12d3b78..980fd1adc47 100644
--- a/src/rules/noNamespaceRule.ts
+++ b/src/rules/noNamespaceRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/noNullKeywordRule.ts b/src/rules/noNullKeywordRule.ts
index 1a20fd6aed0..6a03a5ce244 100644
--- a/src/rules/noNullKeywordRule.ts
+++ b/src/rules/noNullKeywordRule.ts
@@ -18,6 +18,7 @@
// with due reference to https://github.com/Microsoft/TypeScript/blob/7813121c4d77e50aad0eed3152ef1f1156c7b574/scripts/tslint/noNullRule.ts
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/noReferenceRule.ts b/src/rules/noReferenceRule.ts
index cfb00c7cc4f..fcac6e8d74d 100644
--- a/src/rules/noReferenceRule.ts
+++ b/src/rules/noReferenceRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/noRequireImportsRule.ts b/src/rules/noRequireImportsRule.ts
index 8f5e545ce1c..f5aac5109e8 100644
--- a/src/rules/noRequireImportsRule.ts
+++ b/src/rules/noRequireImportsRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/noShadowedVariableRule.ts b/src/rules/noShadowedVariableRule.ts
index 91f06ba94be..d4c5914966a 100644
--- a/src/rules/noShadowedVariableRule.ts
+++ b/src/rules/noShadowedVariableRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/noStringLiteralRule.ts b/src/rules/noStringLiteralRule.ts
index 50b96d88486..e101df69f16 100644
--- a/src/rules/noStringLiteralRule.ts
+++ b/src/rules/noStringLiteralRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/noSwitchCaseFallThroughRule.ts b/src/rules/noSwitchCaseFallThroughRule.ts
index aa436b98de6..afae05f0b48 100644
--- a/src/rules/noSwitchCaseFallThroughRule.ts
+++ b/src/rules/noSwitchCaseFallThroughRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/noTrailingWhitespaceRule.ts b/src/rules/noTrailingWhitespaceRule.ts
index ad42f71ba34..8998a5a37dc 100644
--- a/src/rules/noTrailingWhitespaceRule.ts
+++ b/src/rules/noTrailingWhitespaceRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/noUnreachableRule.ts b/src/rules/noUnreachableRule.ts
index 83b0763afaa..f36bcc5b24f 100644
--- a/src/rules/noUnreachableRule.ts
+++ b/src/rules/noUnreachableRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/noUnusedExpressionRule.ts b/src/rules/noUnusedExpressionRule.ts
index 8c61c904c7b..287398c81e1 100644
--- a/src/rules/noUnusedExpressionRule.ts
+++ b/src/rules/noUnusedExpressionRule.ts
@@ -15,9 +15,10 @@
* limitations under the License.
*/
-import * as Lint from "../lint";
import * as ts from "typescript";
+import * as Lint from "../lint";
+
export class Rule extends Lint.Rules.AbstractRule {
/* tslint:disable:object-literal-sort-keys */
public static metadata: Lint.IRuleMetadata = {
diff --git a/src/rules/noUnusedNewRule.ts b/src/rules/noUnusedNewRule.ts
index bed03b04f66..87dc6a7daa6 100644
--- a/src/rules/noUnusedNewRule.ts
+++ b/src/rules/noUnusedNewRule.ts
@@ -15,8 +15,9 @@
* limitations under the License.
*/
-import * as Lint from "../lint";
import * as ts from "typescript";
+
+import * as Lint from "../lint";
import { NoUnusedExpressionWalker } from "./noUnusedExpressionRule";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/noUnusedVariableRule.ts b/src/rules/noUnusedVariableRule.ts
index 8dc6446bb0d..cf536202d72 100644
--- a/src/rules/noUnusedVariableRule.ts
+++ b/src/rules/noUnusedVariableRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
const OPTION_REACT = "react";
@@ -81,7 +82,6 @@ export class Rule extends Lint.Rules.AbstractRule {
}
class NoUnusedVariablesWalker extends Lint.RuleWalker {
- private languageService: ts.LanguageService;
private skipBindingElement: boolean;
private skipParameterDeclaration: boolean;
private skipVariableDeclaration: boolean;
@@ -91,9 +91,8 @@ class NoUnusedVariablesWalker extends Lint.RuleWalker {
private isReactUsed: boolean;
private reactImport: ts.NamespaceImport;
- constructor(sourceFile: ts.SourceFile, options: Lint.IOptions, languageService: ts.LanguageService) {
+ constructor(sourceFile: ts.SourceFile, options: Lint.IOptions, private languageService: ts.LanguageService) {
super(sourceFile, options);
- this.languageService = languageService;
this.skipVariableDeclaration = false;
this.skipParameterDeclaration = false;
this.hasSeenJsxElement = false;
diff --git a/src/rules/noUseBeforeDeclareRule.ts b/src/rules/noUseBeforeDeclareRule.ts
index af701ac1458..1f126607356 100644
--- a/src/rules/noUseBeforeDeclareRule.ts
+++ b/src/rules/noUseBeforeDeclareRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
@@ -45,11 +46,8 @@ export class Rule extends Lint.Rules.AbstractRule {
type VisitedVariables = {[varName: string]: boolean};
class NoUseBeforeDeclareWalker extends Lint.ScopeAwareRuleWalker {
- private languageService: ts.LanguageService;
-
- constructor(sourceFile: ts.SourceFile, options: Lint.IOptions, languageService: ts.LanguageService) {
+ constructor(sourceFile: ts.SourceFile, options: Lint.IOptions, private languageService: ts.LanguageService) {
super(sourceFile, options);
- this.languageService = languageService;
}
public createScope(): VisitedVariables {
diff --git a/src/rules/noVarKeywordRule.ts b/src/rules/noVarKeywordRule.ts
index 4ff788faf9e..79b9aa1ba0b 100644
--- a/src/rules/noVarKeywordRule.ts
+++ b/src/rules/noVarKeywordRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/noVarRequiresRule.ts b/src/rules/noVarRequiresRule.ts
index ad5e0f977f6..e6498d03773 100644
--- a/src/rules/noVarRequiresRule.ts
+++ b/src/rules/noVarRequiresRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
@@ -42,10 +43,6 @@ export class Rule extends Lint.Rules.AbstractRule {
}
class NoVarRequiresWalker extends Lint.ScopeAwareRuleWalker<{}> {
- constructor(sourceFile: ts.SourceFile, options: Lint.IOptions) {
- super(sourceFile, options);
- }
-
public createScope(): {} {
return {};
}
diff --git a/src/rules/objectLiteralSortKeysRule.ts b/src/rules/objectLiteralSortKeysRule.ts
index 85eeda20931..01406838716 100644
--- a/src/rules/objectLiteralSortKeysRule.ts
+++ b/src/rules/objectLiteralSortKeysRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/oneLineRule.ts b/src/rules/oneLineRule.ts
index d077be8612e..e7bcfc0ef2e 100644
--- a/src/rules/oneLineRule.ts
+++ b/src/rules/oneLineRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
const OPTION_BRACE = "check-open-brace";
diff --git a/src/rules/oneVariablePerDeclarationRule.ts b/src/rules/oneVariablePerDeclarationRule.ts
index 84358facb21..3a79c213f24 100644
--- a/src/rules/oneVariablePerDeclarationRule.ts
+++ b/src/rules/oneVariablePerDeclarationRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
const OPTION_IGNORE_FOR_LOOP = "ignore-for-loop";
diff --git a/src/rules/onlyArrowFunctionsRule.ts b/src/rules/onlyArrowFunctionsRule.ts
index 8d09fd7d691..9abe86eeeae 100644
--- a/src/rules/onlyArrowFunctionsRule.ts
+++ b/src/rules/onlyArrowFunctionsRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/orderedImportsRule.ts b/src/rules/orderedImportsRule.ts
index 944a4238a72..547e33e5057 100644
--- a/src/rules/orderedImportsRule.ts
+++ b/src/rules/orderedImportsRule.ts
@@ -1,6 +1,24 @@
-import * as Lint from "../lint";
+/**
+ * @license
+ * Copyright 2016 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 * as Lint from "../lint";
+
export class Rule extends Lint.Rules.AbstractRule {
/* tslint:disable:object-literal-sort-keys */
public static metadata: Lint.IRuleMetadata = {
diff --git a/src/rules/quotemarkRule.ts b/src/rules/quotemarkRule.ts
index feebaa6c88b..aabfd9eaf32 100644
--- a/src/rules/quotemarkRule.ts
+++ b/src/rules/quotemarkRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
enum QuoteMark {
diff --git a/src/rules/radixRule.ts b/src/rules/radixRule.ts
index e4b54269de0..5bfb33b9c49 100644
--- a/src/rules/radixRule.ts
+++ b/src/rules/radixRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/restrictPlusOperandsRule.ts b/src/rules/restrictPlusOperandsRule.ts
new file mode 100644
index 00000000000..50cea6b81eb
--- /dev/null
+++ b/src/rules/restrictPlusOperandsRule.ts
@@ -0,0 +1,65 @@
+/**
+ * @license
+ * Copyright 2016 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 * as Lint from "../lint";
+
+export class Rule extends Lint.Rules.TypedRule {
+ /* tslint:disable:object-literal-sort-keys */
+ public static metadata: Lint.IRuleMetadata = {
+ ruleName: "restrict-plus-operands",
+ description: "When adding two variables, operands must both be of type number or of type string.",
+ optionsDescription: "Not configurable.",
+ options: null,
+ optionExamples: ["true"],
+ type: "functionality",
+ requiresTypeInfo: true,
+ };
+ /* tslint:enable:object-literal-sort-keys */
+
+ public static MISMATCHED_TYPES_FAILURE = "Types of values used in '+' operation must match";
+ public static UNSUPPORTED_TYPE_FAILURE_FACTORY = (type: string) => `cannot add type ${type}`;
+
+ public applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] {
+ return this.applyWithWalker(new RestrictPlusOperandsWalker(sourceFile, this.getOptions(), program));
+ }
+}
+
+class RestrictPlusOperandsWalker extends Lint.ProgramAwareRuleWalker {
+ public visitBinaryExpression(node: ts.BinaryExpression) {
+ if (node.operatorToken.kind === ts.SyntaxKind.PlusToken) {
+ const tc = this.getTypeChecker();
+ const leftType = tc.typeToString(tc.getTypeAtLocation(node.left));
+ const rightType = tc.typeToString(tc.getTypeAtLocation(node.right));
+
+ const width = node.getWidth();
+ const position = node.getStart();
+
+ if (leftType !== rightType) {
+ // mismatched types
+ this.addFailure(this.createFailure(position, width, Rule.MISMATCHED_TYPES_FAILURE));
+ } else if (leftType !== "number" && leftType !== "string") {
+ // adding unsupported types
+ const failureString = Rule.UNSUPPORTED_TYPE_FAILURE_FACTORY(leftType);
+ this.addFailure(this.createFailure(position, width, failureString));
+ }
+ }
+
+ super.visitBinaryExpression(node);
+ }
+}
diff --git a/src/rules/semicolonRule.ts b/src/rules/semicolonRule.ts
index 5f2c576cbc8..2ae5abbf10f 100644
--- a/src/rules/semicolonRule.ts
+++ b/src/rules/semicolonRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
const OPTION_ALWAYS = "always";
diff --git a/src/rules/switchDefaultRule.ts b/src/rules/switchDefaultRule.ts
index e597f4e6060..a9c3e6b4279 100644
--- a/src/rules/switchDefaultRule.ts
+++ b/src/rules/switchDefaultRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/trailingCommaRule.ts b/src/rules/trailingCommaRule.ts
index 682c6c3d6e5..083922c592d 100644
--- a/src/rules/trailingCommaRule.ts
+++ b/src/rules/trailingCommaRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/tripleEqualsRule.ts b/src/rules/tripleEqualsRule.ts
index daea55fbb0a..87c4fbdfc00 100644
--- a/src/rules/tripleEqualsRule.ts
+++ b/src/rules/tripleEqualsRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
const OPTION_ALLOW_NULL_CHECK = "allow-null-check";
diff --git a/src/rules/typedefRule.ts b/src/rules/typedefRule.ts
index 5a5a6e9b2c2..0b5241cd39a 100644
--- a/src/rules/typedefRule.ts
+++ b/src/rules/typedefRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/typedefWhitespaceRule.ts b/src/rules/typedefWhitespaceRule.ts
index c9eec8ec4c7..182388fc6c1 100644
--- a/src/rules/typedefWhitespaceRule.ts
+++ b/src/rules/typedefWhitespaceRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
/* tslint:disable:object-literal-sort-keys */
diff --git a/src/rules/useIsnanRule.ts b/src/rules/useIsnanRule.ts
index 1478bb23e21..5e37d0daf62 100644
--- a/src/rules/useIsnanRule.ts
+++ b/src/rules/useIsnanRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/useStrictRule.ts b/src/rules/useStrictRule.ts
index e4d800cbb30..d7aa8335c74 100644
--- a/src/rules/useStrictRule.ts
+++ b/src/rules/useStrictRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
export class Rule extends Lint.Rules.AbstractRule {
diff --git a/src/rules/variableNameRule.ts b/src/rules/variableNameRule.ts
index d72f9597274..bd6bf1e9d3d 100644
--- a/src/rules/variableNameRule.ts
+++ b/src/rules/variableNameRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
const BANNED_KEYWORDS = ["any", "Number", "number", "String", "string", "Boolean", "boolean", "Undefined", "undefined"];
diff --git a/src/rules/whitespaceRule.ts b/src/rules/whitespaceRule.ts
index 1b294c54c58..88e59d03da8 100644
--- a/src/rules/whitespaceRule.ts
+++ b/src/rules/whitespaceRule.ts
@@ -16,6 +16,7 @@
*/
import * as ts from "typescript";
+
import * as Lint from "../lint";
const OPTION_BRANCH = "check-branch";
diff --git a/src/test.ts b/src/test.ts
index ad138941e7b..7c36c643b13 100644
--- a/src/test.ts
+++ b/src/test.ts
@@ -20,10 +20,12 @@ import * as diff from "diff";
import * as fs from "fs";
import * as glob from "glob";
import * as path from "path";
+import * as ts from "typescript";
-import * as Linter from "./tslint";
-import * as parse from "./test/parse";
+import {createCompilerOptions} from "./language/utils";
import {LintError} from "./test/lintError";
+import * as parse from "./test/parse";
+import * as Linter from "./tslint";
const FILE_EXTENSION = ".lint";
@@ -46,17 +48,43 @@ export function runTest(testDirectory: string, rulesDirectory?: string | string[
for (const fileToLint of filesToLint) {
const fileBasename = path.basename(fileToLint, FILE_EXTENSION);
+ const fileCompileName = fileBasename.replace(/\.lint$/, "");
const fileText = fs.readFileSync(fileToLint, "utf8");
const fileTextWithoutMarkup = parse.removeErrorMarkup(fileText);
const errorsFromMarkup = parse.parseErrorsFromMarkup(fileText);
+ const compilerOptions = createCompilerOptions();
+ const compilerHost: ts.CompilerHost = {
+ fileExists: () => true,
+ getCanonicalFileName: (filename: string) => filename,
+ getCurrentDirectory: () => "",
+ getDefaultLibFileName: () => ts.getDefaultLibFileName(compilerOptions),
+ getDirectories: () => [],
+ getNewLine: () => "\n",
+ getSourceFile: function (filenameToGet: string) {
+ if (filenameToGet === this.getDefaultLibFileName()) {
+ const fileText = fs.readFileSync(ts.getDefaultLibFilePath(compilerOptions)).toString();
+ return ts.createSourceFile(filenameToGet, fileText, compilerOptions.target);
+ } else if (filenameToGet === fileCompileName) {
+ return ts.createSourceFile(fileBasename, fileTextWithoutMarkup, compilerOptions.target, true);
+ }
+ },
+ readFile: () => null,
+ useCaseSensitiveFileNames: () => true,
+ writeFile: () => null,
+ };
+
+ const program = ts.createProgram([fileCompileName], compilerOptions, compilerHost);
+ // perform type checking on the program, updating nodes with symbol table references
+ ts.getPreEmitDiagnostics(program);
+
const lintOptions = {
configuration: tslintConfig,
formatter: "prose",
formattersDirectory: "",
rulesDirectory,
};
- const linter = new Linter(fileBasename, fileTextWithoutMarkup, lintOptions);
+ const linter = new Linter(fileBasename, fileTextWithoutMarkup, lintOptions, program);
const errorsFromLinter: LintError[] = linter.lint().failures.map((failure) => {
const startLineAndCharacter = failure.getStartPosition().getLineAndCharacter();
const endLineAndCharacter = failure.getEndPosition().getLineAndCharacter();
diff --git a/src/test/parse.ts b/src/test/parse.ts
index 90d0fb185f3..24366ecbc58 100644
--- a/src/test/parse.ts
+++ b/src/test/parse.ts
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-import {LintError, errorComparator, lintSyntaxError} from "./lintError";
import {
- Line,
- ErrorLine,
CodeLine,
- MultilineErrorLine,
EndErrorLine,
+ ErrorLine,
+ Line,
MessageSubstitutionLine,
+ MultilineErrorLine,
parseLine,
printLine,
} from "./lines";
+import {LintError, errorComparator, lintSyntaxError} from "./lintError";
/**
* Takes the full text of a .lint file and returns the contents of the file
diff --git a/src/tsconfig.json b/src/tsconfig.json
index f51978a973e..9d1a4a5a91b 100644
--- a/src/tsconfig.json
+++ b/src/tsconfig.json
@@ -59,9 +59,11 @@
"language/languageServiceHost.ts",
"language/rule/abstractRule.ts",
"language/rule/rule.ts",
+ "language/rule/typedRule.ts",
"language/utils.ts",
"language/walker/blockScopeAwareRuleWalker.ts",
"language/walker/index.ts",
+ "language/walker/programAwareRuleWalker.ts",
"language/walker/ruleWalker.ts",
"language/walker/scopeAwareRuleWalker.ts",
"language/walker/skippableTokenAwareRuleWalker.ts",
@@ -124,6 +126,7 @@
"rules/orderedImportsRule.ts",
"rules/quotemarkRule.ts",
"rules/radixRule.ts",
+ "rules/restrictPlusOperandsRule.ts",
"rules/semicolonRule.ts",
"rules/switchDefaultRule.ts",
"rules/trailingCommaRule.ts",
diff --git a/src/tslint-cli.ts b/src/tslint-cli.ts
index 07956698acd..36f0a869fcd 100644
--- a/src/tslint-cli.ts
+++ b/src/tslint-cli.ts
@@ -19,19 +19,21 @@ import * as fs from "fs";
import * as glob from "glob";
import * as optimist from "optimist";
import * as path from "path";
-import * as Linter from "./tslint";
+import * as ts from "typescript";
+
import {
CONFIG_FILENAME,
DEFAULT_CONFIG,
findConfiguration,
} from "./configuration";
import {consoleTestResultHandler, runTest} from "./test";
+import * as Linter from "./tslint";
let processed = optimist
.usage("Usage: $0 [options] file ...")
.check((argv: any) => {
- // at least one of file, help, version or unqualified argument must be present
- if (!(argv.h || argv.i || argv.test || argv.v || argv._.length > 0)) {
+ // at least one of file, help, version, project or unqualified argument must be present
+ if (!(argv.h || argv.i || argv.test || argv.v || argv.project || argv._.length > 0)) {
throw "Missing files";
}
@@ -80,6 +82,12 @@ let processed = optimist
"test": {
describe: "test that tslint produces the correct output for the specified directory",
},
+ "project": {
+ describe: "tsconfig.json file",
+ },
+ "type-check": {
+ describe: "enable type checking when linting a project",
+ },
"v": {
alias: "version",
describe: "current version",
@@ -185,6 +193,14 @@ tslint accepts the following commandline options:
specified directory as the configuration file for the tests. See the
full tslint documentation for more details on how this can be used to test custom rules.
+ --project:
+ The location of a tsconfig.json file that will be used to determine which
+ files will be linted.
+
+ --type-check
+ Enables the type checker when running linting rules. --project must be
+ specified in order to enable type checking.
+
-v, --version:
The current version of tslint.
@@ -201,7 +217,7 @@ if (argv.c && !fs.existsSync(argv.c)) {
}
const possibleConfigAbsolutePath = argv.c != null ? path.resolve(argv.c) : null;
-const processFile = (file: string) => {
+const processFile = (file: string, program?: ts.Program) => {
if (!fs.existsSync(file)) {
console.error(`Unable to open file: ${file}`);
process.exit(1);
@@ -231,7 +247,7 @@ const processFile = (file: string) => {
formatter: argv.t,
formattersDirectory: argv.s,
rulesDirectory: argv.r,
- });
+ }, program);
const lintResult = linter.lint();
@@ -242,8 +258,41 @@ const processFile = (file: string) => {
}
};
-const files = argv._;
+// if both files and tsconfig are present, use files
+let files = argv._;
+let program: ts.Program;
+
+if (argv.project != null) {
+ if (!fs.existsSync(argv.project)) {
+ console.error("Invalid option for project: " + argv.project);
+ process.exit(1);
+ }
+ program = Linter.createProgram(argv.project, path.dirname(argv.project));
+ if (files.length === 0) {
+ files = Linter.getFileNames(program);
+ }
+ if (argv["type-check"]) {
+ // if type checking, run the type checker
+ const diagnostics = ts.getPreEmitDiagnostics(program);
+ if (diagnostics.length > 0) {
+ const messages = diagnostics.map((diag) => {
+ // emit any error messages
+ let message = ts.DiagnosticCategory[diag.category];
+ if (diag.file) {
+ const {line, character} = diag.file.getLineAndCharacterOfPosition(diag.start);
+ message += ` at ${diag.file.fileName}:${line + 1}:${character + 1}:`;
+ }
+ message += " " + ts.flattenDiagnosticMessageText(diag.messageText, "\n");
+ return message;
+ });
+ throw new Error(messages.join("\n"));
+ }
+ } else {
+ // if not type checking, we don't need to pass in a program object
+ program = undefined;
+ }
+}
for (const file of files) {
- glob.sync(file, { ignore: argv.e }).forEach(processFile);
+ glob.sync(file, { ignore: argv.e }).forEach((file) => processFile(file, program));
}
diff --git a/src/tslint.ts b/src/tslint.ts
index baab7ee144b..a7438160e3f 100644
--- a/src/tslint.ts
+++ b/src/tslint.ts
@@ -15,9 +15,8 @@
* limitations under the License.
*/
-import { IFormatter } from "./language/formatter/formatter";
-import { RuleFailure } from "./language/rule/rule";
-import { getSourceFile } from "./language/utils";
+import * as ts from "typescript";
+
import {
DEFAULT_CONFIG,
findConfiguration,
@@ -28,31 +27,76 @@ import {
} from "./configuration";
import { EnableDisableRulesWalker } from "./enableDisableRules";
import { findFormatter } from "./formatterLoader";
-import { ILinterOptionsRaw, ILinterOptions, LintResult } from "./lint";
+import { IFormatter } from "./language/formatter/formatter";
+import { RuleFailure } from "./language/rule/rule";
+import { TypedRule } from "./language/rule/typedRule";
+import { getSourceFile } from "./language/utils";
+import { ILinterOptions, ILinterOptionsRaw, LintResult } from "./lint";
import { loadRules } from "./ruleLoader";
import { arrayify } from "./utils";
class Linter {
- public static VERSION = "3.13.0-dev.0";
+ public static VERSION = "3.14.0-dev.0";
public static findConfiguration = findConfiguration;
public static findConfigurationPath = findConfigurationPath;
public static getRulesDirectories = getRulesDirectories;
public static loadConfigurationFromPath = loadConfigurationFromPath;
- private fileName: string;
- private source: string;
private options: ILinterOptions;
- constructor(fileName: string, source: string, options: ILinterOptionsRaw) {
- this.fileName = fileName;
- this.source = source;
+ /**
+ * Creates a TypeScript program object from a tsconfig.json file path and optional project directory.
+ */
+ public static createProgram(configFile: string, projectDirectory?: string): ts.Program {
+ if (projectDirectory === undefined) {
+ const lastSeparator = configFile.lastIndexOf("/");
+ if (lastSeparator < 0) {
+ projectDirectory = ".";
+ } else {
+ projectDirectory = configFile.substring(0, lastSeparator + 1);
+ }
+ }
+
+ const {config} = ts.readConfigFile(configFile, ts.sys.readFile);
+ const parsed = ts.parseJsonConfigFileContent(config, {
+ fileExists: (path: string) => true,
+ readDirectory: ts.sys.readDirectory,
+ useCaseSensitiveFileNames: false,
+ }, projectDirectory);
+ const host = ts.createCompilerHost(parsed.options, true);
+ const program = ts.createProgram(parsed.fileNames, parsed.options, host);
+
+ return program;
+ }
+
+ /**
+ * Returns a list of source file names from a TypeScript program. This includes all referenced
+ * files and excludes declaration (".d.ts") files.
+ */
+ public static getFileNames(program: ts.Program): string[] {
+ return program.getSourceFiles().map(s => s.fileName).filter(l => l.substr(-5) !== ".d.ts");
+ }
+
+ constructor(private fileName: string,
+ private source: string,
+ options: ILinterOptionsRaw,
+ private program?: ts.Program) {
this.options = this.computeFullOptions(options);
}
public lint(): LintResult {
const failures: RuleFailure[] = [];
- const sourceFile = getSourceFile(this.fileName, this.source);
+ let sourceFile: ts.SourceFile;
+ if (this.program) {
+ sourceFile = this.program.getSourceFile(this.fileName);
+ // check if the program has been type checked
+ if (!("resolvedModules" in sourceFile)) {
+ throw new Error("Program must be type checked before linting");
+ }
+ } else {
+ sourceFile = getSourceFile(this.fileName, this.source);
+ }
// walk the code first to find all the intervals where rules are disabled
const rulesWalker = new EnableDisableRulesWalker(sourceFile, {
@@ -67,7 +111,12 @@ class Linter {
const configuredRules = loadRules(configuration, enableDisableRuleMap, rulesDirectories);
const enabledRules = configuredRules.filter((r) => r.isEnabled());
for (let rule of enabledRules) {
- const ruleFailures = rule.apply(sourceFile);
+ let ruleFailures: RuleFailure[] = [];
+ if (this.program && rule instanceof TypedRule) {
+ ruleFailures = rule.applyWithProgram(sourceFile, this.program);
+ } else {
+ ruleFailures = rule.apply(sourceFile);
+ }
for (let ruleFailure of ruleFailures) {
if (!this.containsRule(failures, ruleFailure)) {
failures.push(ruleFailure);
diff --git a/test/check-bin.sh b/test/check-bin.sh
index 07c65f364ec..f696254ac3b 100755
--- a/test/check-bin.sh
+++ b/test/check-bin.sh
@@ -108,6 +108,14 @@ expectOut $? 0 "tslint --test did not exit correctly for a passing test with cus
./bin/tslint -r test/files/custom-rules-2 --test test/files/custom-rule-cli-rule-test
expectOut $? 0 "tslint --test did not exit correctly for a passing test with custom rules from the CLI"
+# make sure tslint exits correctly when tsconfig is specified but no files are given
+./bin/tslint -c test/files/tsconfig-test/tslint.json --project test/files/tsconfig-test/tsconfig.json
+expectOut $? 0 "tslint with tsconfig did not exit correctly"
+
+# make sure tslint only lints files given if tsconfig is also specified
+./bin/tslint -c test/files/tsconfig-test/tslint.json --project test/files/tsconfig-test/tsconfig.json test/files/tsconfig-test/other.test.ts
+expectOut $? 2 "tslint with tsconfig and files did not find lint failures from given files"
+
if [ $num_failures != 0 ]; then
echo "Failed $num_failures tests"
exit 1
diff --git a/test/configurationTests.ts b/test/configurationTests.ts
index c2f018bae73..a6e25cc04f3 100644
--- a/test/configurationTests.ts
+++ b/test/configurationTests.ts
@@ -14,9 +14,10 @@
* limitations under the License.
*/
+import * as fs from "fs";
import * as os from "os";
import * as path from "path";
-import * as fs from "fs";
+
import {IConfigurationFile, extendConfigurationFile, loadConfigurationFromPath} from "../src/configuration";
describe("Configuration", () => {
diff --git a/test/files/tsconfig-test/good.test.ts b/test/files/tsconfig-test/good.test.ts
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/test/files/tsconfig-test/other.test.ts b/test/files/tsconfig-test/other.test.ts
new file mode 100644
index 00000000000..296d5492b00
--- /dev/null
+++ b/test/files/tsconfig-test/other.test.ts
@@ -0,0 +1 @@
+console.log(1);
diff --git a/test/files/tsconfig-test/tsconfig.json b/test/files/tsconfig-test/tsconfig.json
new file mode 100644
index 00000000000..fa3a484bd90
--- /dev/null
+++ b/test/files/tsconfig-test/tsconfig.json
@@ -0,0 +1,5 @@
+{
+ "files": [
+ "good.test.ts"
+ ]
+}
diff --git a/test/files/tsconfig-test/tslint.json b/test/files/tsconfig-test/tslint.json
new file mode 100644
index 00000000000..7be5efded55
--- /dev/null
+++ b/test/files/tsconfig-test/tslint.json
@@ -0,0 +1,5 @@
+{
+ "rules": {
+ "no-console": [true, "log"]
+ }
+}
diff --git a/test/formatters/checkstyleFormatterTests.ts b/test/formatters/checkstyleFormatterTests.ts
index 72aa0ef8a05..c9428068ce7 100644
--- a/test/formatters/checkstyleFormatterTests.ts
+++ b/test/formatters/checkstyleFormatterTests.ts
@@ -1,4 +1,5 @@
import * as ts from "typescript";
+
import {IFormatter, RuleFailure, TestUtils} from "../lint";
describe("Checkstyle Formatter", () => {
diff --git a/test/formatters/externalFormatterTest.ts b/test/formatters/externalFormatterTest.ts
index 69b2cf23507..12733cf73e5 100644
--- a/test/formatters/externalFormatterTest.ts
+++ b/test/formatters/externalFormatterTest.ts
@@ -15,6 +15,7 @@
*/
import * as ts from "typescript";
+
import {IFormatter, RuleFailure, TestUtils} from "../lint";
describe("External Formatter", () => {
diff --git a/test/formatters/jsonFormatterTests.ts b/test/formatters/jsonFormatterTests.ts
index 60e82b58c2c..eccdc6496f6 100644
--- a/test/formatters/jsonFormatterTests.ts
+++ b/test/formatters/jsonFormatterTests.ts
@@ -15,6 +15,7 @@
*/
import * as ts from "typescript";
+
import {IFormatter, RuleFailure, TestUtils} from "../lint";
describe("JSON Formatter", () => {
diff --git a/test/formatters/msbuildFormatterTests.ts b/test/formatters/msbuildFormatterTests.ts
index 937d97e527b..2560f8fb473 100644
--- a/test/formatters/msbuildFormatterTests.ts
+++ b/test/formatters/msbuildFormatterTests.ts
@@ -15,6 +15,7 @@
*/
import * as ts from "typescript";
+
import {IFormatter, RuleFailure, TestUtils} from "../lint";
describe("MSBuild Formatter", () => {
diff --git a/test/formatters/pmdFormatterTests.ts b/test/formatters/pmdFormatterTests.ts
index 779eeb1826b..2e95f243b39 100644
--- a/test/formatters/pmdFormatterTests.ts
+++ b/test/formatters/pmdFormatterTests.ts
@@ -15,6 +15,7 @@
*/
import * as ts from "typescript";
+
import {IFormatter, RuleFailure, TestUtils} from "../lint";
describe("PMD Formatter", () => {
diff --git a/test/formatters/proseFormatterTests.ts b/test/formatters/proseFormatterTests.ts
index b61d1b2ea94..ecde9fc00d2 100644
--- a/test/formatters/proseFormatterTests.ts
+++ b/test/formatters/proseFormatterTests.ts
@@ -15,6 +15,7 @@
*/
import * as ts from "typescript";
+
import {IFormatter, RuleFailure, TestUtils} from "../lint";
describe("Prose Formatter", () => {
diff --git a/test/formatters/verboseFormatterTests.ts b/test/formatters/verboseFormatterTests.ts
index 223ba7c2960..2cb494fa845 100644
--- a/test/formatters/verboseFormatterTests.ts
+++ b/test/formatters/verboseFormatterTests.ts
@@ -15,6 +15,7 @@
*/
import * as ts from "typescript";
+
import {IFormatter, RuleFailure, TestUtils} from "../lint";
describe("Verbose Formatter", () => {
diff --git a/test/formatters/vsoFormatterTests.ts b/test/formatters/vsoFormatterTests.ts
index ba54c6ca054..77d01d26371 100644
--- a/test/formatters/vsoFormatterTests.ts
+++ b/test/formatters/vsoFormatterTests.ts
@@ -15,6 +15,7 @@
*/
import * as ts from "typescript";
+
import {IFormatter, RuleFailure, TestUtils} from "../lint";
describe("VSO Formatter", () => {
diff --git a/test/rule-tester/parseTests.ts b/test/rule-tester/parseTests.ts
index 471735b32f4..40bc3722206 100644
--- a/test/rule-tester/parseTests.ts
+++ b/test/rule-tester/parseTests.ts
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-import * as testData from "./testData";
import * as parse from "../../src/test/parse";
+import * as testData from "./testData";
describe("Rule Test Parse", () => {
describe("removeErrorMarkup", () => {
diff --git a/test/ruleTestRunner.ts b/test/ruleTestRunner.ts
index a8406e5eee5..250cfe30d1a 100644
--- a/test/ruleTestRunner.ts
+++ b/test/ruleTestRunner.ts
@@ -18,7 +18,7 @@ import * as colors from "colors";
import * as glob from "glob";
import * as path from "path";
-import {runTest, consoleTestResultHandler} from "../src/test";
+import {consoleTestResultHandler, runTest} from "../src/test";
// needed to get colors to show up when passing through Grunt
(colors as any).enabled = true;
diff --git a/test/rules/_integration/enable-disable/test.ts.lint b/test/rules/_integration/enable-disable/test.ts.lint
index ba78e3b5740..a628d1bc4d8 100644
--- a/test/rules/_integration/enable-disable/test.ts.lint
+++ b/test/rules/_integration/enable-disable/test.ts.lint
@@ -25,6 +25,19 @@ var AAAaA = 'test'
/* tslint:enable:zasdadsa */
var AAAaA = 'test'
+/* tslint:enable quotemark */
+var AAAaA = 'test'
+ ~~~~~~ [' should be "]
+/* tslint:disable */
+var AAAaA = 'test'
+/* tslint:enable quotemark variable-name */
+var AAAaA = 'test'
+ ~~~~~ [variable name must be in camelcase or uppercase]
+ ~~~~~~ [' should be "]
+/* tslint:disable quotemark */
+var AAAaA = 'test'
+ ~~~~~ [variable name must be in camelcase or uppercase]
+
/* tslint:enable */
var re;
re = /`/;
diff --git a/test/rules/restrict-plus-operands/test.ts.lint b/test/rules/restrict-plus-operands/test.ts.lint
new file mode 100644
index 00000000000..b289818c685
--- /dev/null
+++ b/test/rules/restrict-plus-operands/test.ts.lint
@@ -0,0 +1,60 @@
+// aliases for number
+type MyNumber = number;
+type MyString = string;
+interface NumberStringPair {
+ first: MyNumber,
+ second: MyString
+}
+
+var x = 5;
+var y = "10";
+var z = 8.2;
+var w = "6.5";
+var pair: NumberStringPair = {
+ first: 5,
+ second: "10"
+};
+
+// bad
+var bad1 = 5 + "10";
+ ~~~~~~~~ [Types of values used in '+' operation must match]
+var bad2 = [] + 5;
+ ~~~~~~ [Types of values used in '+' operation must match]
+var bad3 = [] + {};
+ ~~~~~~~ [Types of values used in '+' operation must match]
+var bad4 = [] + [];
+ ~~~~~~~ [cannot add type undefined[]]
+var bad4 = 5 + [];
+ ~~~~~~ [Types of values used in '+' operation must match]
+var bad5 = "5" + {};
+ ~~~~~~~~ [Types of values used in '+' operation must match]
+var bad6 = 5.5 + "5";
+ ~~~~~~~~~ [Types of values used in '+' operation must match]
+var bad7 = "5.5" + 5;
+ ~~~~~~~~~ [Types of values used in '+' operation must match]
+var bad8 = x + y;
+ ~~~~~ [Types of values used in '+' operation must match]
+var bad9 = y + x;
+ ~~~~~ [Types of values used in '+' operation must match]
+var bad10 = x + {};
+ ~~~~~~ [Types of values used in '+' operation must match]
+var bad11 = [] + y;
+ ~~~~~~ [Types of values used in '+' operation must match]
+var bad12 = pair.first + "10";
+ ~~~~~~~~~~~~~~~~~ [Types of values used in '+' operation must match]
+var bad13 = 5 + pair.second;
+ ~~~~~~~~~~~~~~~ [Types of values used in '+' operation must match]
+var bad14 = pair + pair;
+ ~~~~~~~~~~~ [cannot add type NumberStringPair]
+
+// good
+var good1 = 5 + 10;
+var good2 = "5.5" + "10";
+var good3 = parseFloat("5.5", 10), + 10;
+var good4 = x + z;
+var good5 = y + w;
+
+var good6 = pair.first + 10;
+var good7 = pair.first + (10 as MyNumber);
+var good8 = "5.5" + pair.second;
+var good9 = ("5.5" as MyString) + pair.second;
diff --git a/test/rules/restrict-plus-operands/tslint.json b/test/rules/restrict-plus-operands/tslint.json
new file mode 100644
index 00000000000..8389b1de47a
--- /dev/null
+++ b/test/rules/restrict-plus-operands/tslint.json
@@ -0,0 +1,5 @@
+{
+ "rules": {
+ "restrict-plus-operands": true
+ }
+}
diff --git a/test/tsconfig.json b/test/tsconfig.json
index d2d960823da..54784774e94 100644
--- a/test/tsconfig.json
+++ b/test/tsconfig.json
@@ -52,9 +52,11 @@
"../src/language/languageServiceHost.ts",
"../src/language/rule/abstractRule.ts",
"../src/language/rule/rule.ts",
+ "../src/language/rule/typedRule.ts",
"../src/language/utils.ts",
"../src/language/walker/blockScopeAwareRuleWalker.ts",
"../src/language/walker/index.ts",
+ "../src/language/walker/programAwareRuleWalker.ts",
"../src/language/walker/ruleWalker.ts",
"../src/language/walker/scopeAwareRuleWalker.ts",
"../src/language/walker/skippableTokenAwareRuleWalker.ts",
@@ -99,7 +101,6 @@
"../src/rules/noInvalidThisRule.ts",
"../src/rules/noMergeableNamespaceRule.ts",
"../src/rules/noNamespaceRule.ts",
- "../src/rules/noUnusedNewRule.ts",
"../src/rules/noNullKeywordRule.ts",
"../src/rules/noReferenceRule.ts",
"../src/rules/noRequireImportsRule.ts",
@@ -109,6 +110,7 @@
"../src/rules/noTrailingWhitespaceRule.ts",
"../src/rules/noUnreachableRule.ts",
"../src/rules/noUnusedExpressionRule.ts",
+ "../src/rules/noUnusedNewRule.ts",
"../src/rules/noUnusedVariableRule.ts",
"../src/rules/noUseBeforeDeclareRule.ts",
"../src/rules/noVarKeywordRule.ts",
@@ -120,6 +122,7 @@
"../src/rules/orderedImportsRule.ts",
"../src/rules/quotemarkRule.ts",
"../src/rules/radixRule.ts",
+ "../src/rules/restrictPlusOperandsRule.ts",
"../src/rules/semicolonRule.ts",
"../src/rules/switchDefaultRule.ts",
"../src/rules/trailingCommaRule.ts",
diff --git a/test/utils.ts b/test/utils.ts
index e436ca725ed..d3f3c580105 100644
--- a/test/utils.ts
+++ b/test/utils.ts
@@ -17,6 +17,7 @@
import * as fs from "fs";
import * as path from "path";
import * as ts from "typescript";
+
import * as Lint from "./lint";
export function getSourceFile(fileName: string): ts.SourceFile {
diff --git a/typings/findup-sync/findup-sync.d.ts b/typings/findup-sync/findup-sync.d.ts
index f42ca325613..b7bf674e231 100644
--- a/typings/findup-sync/findup-sync.d.ts
+++ b/typings/findup-sync/findup-sync.d.ts
@@ -13,7 +13,6 @@ declare module 'findup-sync' {
}
function mod(pattern: string[] | string, opts?: IOptions): string;
- namespace mod {} // Literally works around a bug in TS
export = mod;
}