Skip to content

Commit

Permalink
Add optional type information to rules
Browse files Browse the repository at this point in the history
Relates to palantir#1323
  • Loading branch information
ScottSWu committed Jun 30, 2016
1 parent d3350ad commit 620a742
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 13 deletions.
28 changes: 28 additions & 0 deletions src/language/rule/typedRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* @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.
*/

import * as ts from "typescript";
import {RuleFailure} from "./rule";
import {AbstractRule} from "./abstractRule";

export abstract class TypedRule extends AbstractRule {
public apply(sourceFile: ts.SourceFile): RuleFailure[] {
return [];
}

public abstract applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): RuleFailure[];
}
1 change: 1 addition & 0 deletions src/language/walker/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/

export * from "./blockScopeAwareRuleWalker";
export * from "./programAwareRuleWalker";
export * from "./ruleWalker";
export * from "./scopeAwareRuleWalker";
export * from "./skippableTokenAwareRuleWalker";
Expand Down
40 changes: 40 additions & 0 deletions src/language/walker/programAwareRuleWalker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* @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.
*/

import * as ts from "typescript";
import {IOptions} from "../../lint";
import {RuleWalker} from "./ruleWalker";

export class ProgramAwareRuleWalker extends RuleWalker {
protected program: ts.Program;
protected typeChecker: ts.TypeChecker;

constructor(sourceFile: ts.SourceFile, options: IOptions, program: ts.Program) {
super(sourceFile, options);

this.program = program;
this.typeChecker = program.getTypeChecker();
}

public getProgram(): ts.Program {
return this.program;
}

public getTypeChecker(): ts.TypeChecker {
return this.typeChecker;
}
}
14 changes: 7 additions & 7 deletions src/language/walker/ruleWalker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ import {doesIntersect} from "../utils";
import {SyntaxWalker} from "./syntaxWalker";

export class RuleWalker extends SyntaxWalker {
private limit: number;
private position: number;
private options: any[];
private failures: RuleFailure[];
private sourceFile: ts.SourceFile;
private disabledIntervals: IDisabledInterval[];
private ruleName: string;
protected limit: number;
protected position: number;
protected options: any[];
protected failures: RuleFailure[];
protected sourceFile: ts.SourceFile;
protected disabledIntervals: IDisabledInterval[];
protected ruleName: string;

constructor(sourceFile: ts.SourceFile, options: IOptions) {
super();
Expand Down
1 change: 1 addition & 0 deletions src/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@
*/

export * from "./language/rule/abstractRule";
export * from "./language/rule/typedRule";
28 changes: 27 additions & 1 deletion src/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {LintError} from "./test/lintError";
import {createCompilerOptions} from "./language/utils";

const FILE_EXTENSION = ".lint";

Expand All @@ -46,17 +48,41 @@ 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),
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);
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();
Expand Down
3 changes: 3 additions & 0 deletions src/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -121,6 +123,7 @@
"rules/oneLineRule.ts",
"rules/oneVariablePerDeclarationRule.ts",
"rules/onlyArrowFunctionsRule.ts",
"rules/orderedImportsRule.ts",
"rules/quotemarkRule.ts",
"rules/radixRule.ts",
"rules/semicolonRule.ts",
Expand Down
20 changes: 17 additions & 3 deletions src/tslint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
* limitations under the License.
*/

import * as ts from "typescript";
import { IFormatter } from "./language/formatter/formatter";
import { RuleFailure } from "./language/rule/rule";
import { TypedRule } from "./language/rule/typedRule";
import { getSourceFile } from "./language/utils";
import {
DEFAULT_CONFIG,
Expand All @@ -42,17 +44,24 @@ class Linter {

private fileName: string;
private source: string;
private program: ts.Program;
private options: ILinterOptions;

constructor(fileName: string, source: string, options: ILinterOptionsRaw) {
constructor(fileName: string, source: string, options: ILinterOptionsRaw, program?: ts.Program) {
this.fileName = fileName;
this.source = source;
this.program = 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);
} 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, {
Expand All @@ -67,7 +76,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);
Expand Down
6 changes: 4 additions & 2 deletions test/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand All @@ -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",
Expand Down Expand Up @@ -159,4 +161,4 @@
"rule-tester/testData.ts",
"rule-tester/utilsTests.ts"
]
}
}

0 comments on commit 620a742

Please sign in to comment.