Skip to content
This repository has been archived by the owner on Mar 25, 2021. It is now read-only.

Commit

Permalink
WIP: try to check fixes against language service
Browse files Browse the repository at this point in the history
  • Loading branch information
alexeagle committed Sep 25, 2016
1 parent 1506d8b commit 43f204a
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 5 deletions.
33 changes: 31 additions & 2 deletions src/language/languageServiceHost.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,35 @@ import * as ts from "typescript";

import {createCompilerOptions} from "./utils";

export interface LanguageServiceEditableHost extends ts.LanguageServiceHost {
editFile(fileName: string, newContent: string): void;
}

export function hostFromProgram(program: ts.Program): LanguageServiceEditableHost {
const files: {[name: string]: string} = {};
const fileVersions: {[name: string]: number} = {};
return {
getCompilationSettings: () => program.getCompilerOptions(),
getCurrentDirectory: () => program.getCurrentDirectory(),
getDefaultLibFileName: () => "lib.d.ts",
getScriptFileNames: () => program.getRootFileNames(),
getScriptSnapshot: (name: string) => ts.ScriptSnapshot.fromString(
files.hasOwnProperty(name) ? files[name] :
program.getSourceFile(name).getFullText()
),
getScriptVersion: (name: string) => fileVersions.hasOwnProperty(name) ? fileVersions[name] + "" : "1",
log: () => { /* */ },
editFile(fileName: string, newContent: string) {
files[fileName] = newContent;
if (fileVersions.hasOwnProperty(fileName)) {
fileVersions[fileName]++;
} else {
fileVersions[fileName] = 0;
}
},
};
}

export function createLanguageServiceHost(fileName: string, source: string): ts.LanguageServiceHost {
return {
getCompilationSettings: () => createCompilerOptions(),
Expand All @@ -31,7 +60,7 @@ export function createLanguageServiceHost(fileName: string, source: string): ts.
};
}

export function createLanguageService(fileName: string, source: string) {
const languageServiceHost = createLanguageServiceHost(fileName, source);
export function createLanguageService(fileName: string, source: string, program?: ts.Program) {
const languageServiceHost = program ? hostFromProgram(program) : createLanguageServiceHost(fileName, source);
return ts.createLanguageService(languageServiceHost);
}
26 changes: 23 additions & 3 deletions src/rules/noUnusedVariableRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,9 @@ export class Rule extends Lint.Rules.TypedRule {
}

public applyWithProgram(sourceFile: ts.SourceFile, program?: ts.Program): Lint.RuleFailure[] {
const languageService = Lint.createLanguageService(sourceFile.fileName, sourceFile.getFullText());
return this.applyWithWalker(new NoUnusedVariablesWalker(sourceFile, this.getOptions(), languageService, program));
let languageServiceHost = program ? Lint.hostFromProgram(program) :
Lint.createLanguageServiceHost(sourceFile.fileName, sourceFile.getFullText());
return this.applyWithWalker(new NoUnusedVariablesWalker(sourceFile, this.getOptions(), languageServiceHost, program));
}
}

Expand All @@ -95,10 +96,12 @@ class NoUnusedVariablesWalker extends Lint.RuleWalker {
private ignorePattern: RegExp;
private isReactUsed: boolean;
private reactImport: ts.NamespaceImport;
private languageService: ts.LanguageService;

constructor(sourceFile: ts.SourceFile, options: Lint.IOptions,
private languageService: ts.LanguageService, private program?: ts.Program) {
private languageServiceHost: ts.LanguageServiceHost, private program?: ts.Program) {
super(sourceFile, options);
this.languageService = ts.createLanguageService(languageServiceHost);
this.skipVariableDeclaration = false;
this.skipParameterDeclaration = false;
this.hasSeenJsxElement = false;
Expand Down Expand Up @@ -424,6 +427,23 @@ class NoUnusedVariablesWalker extends Lint.RuleWalker {
if (replacements && replacements.length) {
fix = new Lint.Fix("no-unused-variable", replacements);
}
// If we have the program, we can verify that the fix doesn't introduce failures
if (this.program) {
const program = this.languageService.getProgram();
const sf = this.getSourceFile();
const existingDiags = ts.getPreEmitDiagnostics(program, sf);
if (existingDiags.length > 0) {
throw new Error("Fix existing diagnostics before running no-unused-variable");
}
(this.languageServiceHost as any).editFile(sf.fileName, fix.apply(sf.getFullText()));
const newProgram = this.languageService.getProgram();
const newSf = newProgram.getSourceFile(sf.fileName);
const newDiags = ts.getPreEmitDiagnostics(newProgram, newSf);
if (newDiags.length > 0) {
console.error("rejecting fix because it causes errors");
return;
}
}
this.addFailure(this.createFailure(position, name.length, Rule.FAILURE_STRING_FACTORY(type, name), fix));
}

Expand Down

0 comments on commit 43f204a

Please sign in to comment.