Do not publicly expose a way on a Program instance to get typecheckers with differing behavior.

Now, you can only get the non-diagnostics, pull-type-checker from the Program instance.
If you want diagnostics, you simply ask the Program instance for the diagnostics you want.
This commit is contained in:
Cyrus Najmabadi
2015-02-04 16:11:38 -08:00
parent 5b049feb36
commit b6d083fa40
25 changed files with 2145 additions and 69 deletions

View File

@@ -88,7 +88,6 @@ module ts {
verifyCompilerOptions();
errors.sort(compareDiagnostics);
var diagnosticsProducingTypeChecker: TypeChecker;
var noDiagnosticsTypeChecker: TypeChecker;
var emitHost: EmitHost;
@@ -104,10 +103,16 @@ module ts {
getTypeCheckerGlobalDiagnostics,
getDeclarationDiagnostics,
getTypeChecker,
getDiagnosticsProducingTypeChecker,
getCommonSourceDirectory: () => commonSourceDirectory,
emitFiles: invokeEmitter,
isEmitBlocked,
getCurrentDirectory: host.getCurrentDirectory,
getEmitResolver: () => getDiagnosticsProducingTypeChecker().getEmitResolver(),
getNodeCount: () => getDiagnosticsProducingTypeChecker().getNodeCount(),
getIdentifierCount: () => getDiagnosticsProducingTypeChecker().getIdentifierCount(),
getSymbolCount: () => getDiagnosticsProducingTypeChecker().getSymbolCount(),
getTypeCount: () => getDiagnosticsProducingTypeChecker().getTypeCount(),
};
return program;
@@ -127,13 +132,8 @@ module ts {
return diagnosticsProducingTypeChecker || (diagnosticsProducingTypeChecker = createTypeChecker(program, /*produceDiagnostics:*/ true));
}
function getTypeChecker(produceDiagnostics: boolean) {
if (produceDiagnostics) {
return getDiagnosticsProducingTypeChecker();
}
else {
return noDiagnosticsTypeChecker || (noDiagnosticsTypeChecker = createTypeChecker(program, produceDiagnostics));
}
function getTypeChecker() {
return noDiagnosticsTypeChecker || (noDiagnosticsTypeChecker = createTypeChecker(program, /*produceDiagnostics:*/ false));
}
function getDeclarationDiagnostics(targetSourceFile: SourceFile): Diagnostic[]{

View File

@@ -336,9 +336,8 @@ module ts {
exitStatus = EmitReturnStatus.AllOutputGenerationSkipped;
}
else {
var checker = program.getTypeChecker(/*fullTypeCheckMode*/ true);
var checkStart = new Date().getTime();
errors = checker.getDiagnostics();
errors = program.getTypeCheckerDiagnostics();
if (program.isEmitBlocked()) {
exitStatus = EmitReturnStatus.AllOutputGenerationSkipped;
}
@@ -367,10 +366,10 @@ module ts {
var memoryUsed = sys.getMemoryUsage ? sys.getMemoryUsage() : -1;
reportCountStatistic("Files", program.getSourceFiles().length);
reportCountStatistic("Lines", countLines(program));
reportCountStatistic("Nodes", checker ? checker.getNodeCount() : 0);
reportCountStatistic("Identifiers", checker ? checker.getIdentifierCount() : 0);
reportCountStatistic("Symbols", checker ? checker.getSymbolCount() : 0);
reportCountStatistic("Types", checker ? checker.getTypeCount() : 0);
reportCountStatistic("Nodes", program.getNodeCount());
reportCountStatistic("Identifiers", program.getIdentifierCount());
reportCountStatistic("Symbols", program.getSymbolCount());
reportCountStatistic("Types", program.getTypeCount());
if (memoryUsed >= 0) {
reportStatisticalValue("Memory used", Math.round(memoryUsed / 1000) + "K");
}

View File

@@ -930,6 +930,7 @@ module ts {
export interface Program extends ScriptReferenceHost {
getSourceFiles(): SourceFile[];
getCompilerHost(): CompilerHost;
getEmitResolver(): EmitResolver;
// These will merge with the below diagnostics function in a followup checkin.
getTypeCheckerDiagnostics(sourceFile?: SourceFile): Diagnostic[];
@@ -947,11 +948,20 @@ module ts {
//
// If 'produceDiagnostics' is false, then any calls to get diagnostics from the TypeChecker
// will throw an invalid operation exception.
getTypeChecker(produceDiagnostics: boolean): TypeChecker;
getTypeChecker(): TypeChecker;
getCommonSourceDirectory(): string;
emitFiles(targetSourceFile?: SourceFile): EmitResult;
isEmitBlocked(sourceFile?: SourceFile): boolean;
// For testing purposes only. Should not be used by any other consumers (including the
// language service).
/* @internal */ getDiagnosticsProducingTypeChecker(): TypeChecker;
/* @internal */ getNodeCount(): number;
/* @internal */ getIdentifierCount(): number;
/* @internal */ getSymbolCount(): number;
/* @internal */ getTypeCount(): number;
}
export interface SourceMapSpan {
@@ -1000,7 +1010,6 @@ module ts {
}
export interface TypeChecker {
getEmitResolver(): EmitResolver;
getTypeOfSymbolAtLocation(symbol: Symbol, node: Node): Type;
getDeclaredTypeOfSymbol(symbol: Symbol): Type;
getPropertiesOfType(type: Type): Symbol[];
@@ -1033,6 +1042,7 @@ module ts {
// Should not be called directly. Should only be accessed through the Program instance.
/* @internal */ getDiagnostics(sourceFile?: SourceFile): Diagnostic[];
/* @internal */ getGlobalDiagnostics(): Diagnostic[];
/* @internal */ getEmitResolver(): EmitResolver;
/* @internal */ getNodeCount(): number;
/* @internal */ getIdentifierCount(): number;

View File

@@ -256,11 +256,40 @@ class CompilerBaselineRunner extends RunnerBase {
it('Correct type baselines for ' + fileName, () => {
// NEWTODO: Type baselines
if (result.errors.length === 0) {
Harness.Baseline.runBaseline('Correct expression types for ' + fileName, justName.replace(/\.ts/, '.types'), () => {
// The full walker simulates the types that you would get from doing a full
// compile. The pull walker simulates the types you get when you just do
// a type query for a random node (like how the LS would do it). Most of the
// time, these will be the same. However, occasionally, they can be different.
// Specifically, when the compiler internally depends on symbol IDs to order
// things, then we may see different results because symbols can be created in a
// different order with 'pull' operations, and thus can produce slightly differing
// output.
//
// For example, with a full type check, we may see a type outputed as: number | string
// But with a pull type check, we may see it as: string | number
//
// These types are equivalent, but depend on what order the compiler observed
// certain parts of the program.
var fullWalker = new TypeWriterWalker(program, /*fullTypeCheck:*/ true);
var pullWalker = new TypeWriterWalker(program, /*fullTypeCheck:*/ false);
var fullTypes = generateTypes(fullWalker);
var pullTypes = generateTypes(pullWalker);
if (fullTypes !== pullTypes) {
Harness.Baseline.runBaseline('Correct full expression types for ' + fileName, justName.replace(/\.ts/, '.types'), () => fullTypes);
Harness.Baseline.runBaseline('Correct pull expression types for ' + fileName, justName.replace(/\.ts/, '.types.pull'), () => pullTypes);
}
else {
Harness.Baseline.runBaseline('Correct expression types for ' + fileName, justName.replace(/\.ts/, '.types'), () => fullTypes);
}
function generateTypes(walker: TypeWriterWalker): string {
var allFiles = toBeCompiled.concat(otherFiles).filter(file => !!program.getSourceFile(file.unitName));
var typeLines: string[] = [];
var typeMap: { [fileName: string]: { [lineNum: number]: string[]; } } = {};
var walker = new TypeWriterWalker(program);
allFiles.forEach(file => {
var codeLines = file.content.split('\n');
walker.getTypes(file.unitName).forEach(result => {
@@ -299,7 +328,7 @@ class CompilerBaselineRunner extends RunnerBase {
});
return typeLines.join('');
});
}
}
});
});

View File

@@ -1081,8 +1081,6 @@ module Harness {
(fn, contents, writeByteOrderMark) => fileOutputs.push({ fileName: fn, code: contents, writeByteOrderMark: writeByteOrderMark }),
options.target, useCaseSensitiveFileNames, currentDirectory));
var checker = program.getTypeChecker(/*produceDiagnostics*/ true);
var isEmitBlocked = program.isEmitBlocked();
// only emit if there weren't parse errors
@@ -1092,7 +1090,7 @@ module Harness {
}
var errors: HarnessDiagnostic[] = [];
program.getDiagnostics().concat(checker.getDiagnostics()).concat(emitResult ? emitResult.diagnostics : []).forEach(err => {
program.getDiagnostics().concat(program.getTypeCheckerDiagnostics()).concat(emitResult ? emitResult.diagnostics : []).forEach(err => {
// TODO: new compiler formats errors after this point to add . and newlines so we'll just do it manually for now
errors.push(getMinimalDiagnostic(err));
});

View File

@@ -130,8 +130,7 @@ class ProjectRunner extends RunnerBase {
var errors = program.getDiagnostics();
var sourceMapData: ts.SourceMapData[] = null;
if (!errors.length) {
var checker = program.getTypeChecker(/*produceDiagnostics:*/ true);
errors = checker.getDiagnostics();
errors = program.getTypeCheckerDiagnostics();
var emitResult = program.emitFiles();
errors = ts.concatenate(errors, emitResult.diagnostics);
sourceMapData = emitResult.sourceMaps;

View File

@@ -12,10 +12,12 @@ class TypeWriterWalker {
private checker: ts.TypeChecker;
constructor(private program: ts.Program) {
constructor(private program: ts.Program, fullTypeCheck: boolean) {
// Consider getting both the diagnostics checker and the non-diagnostics checker to verify
// they are consistent.
this.checker = program.getTypeChecker(/*produceDiagnostics:*/ true);
this.checker = fullTypeCheck
? program.getDiagnosticsProducingTypeChecker()
: program.getTypeChecker();
}
public getTypes(fileName: string): TypeWriterResult[] {

View File

@@ -2024,10 +2024,6 @@ module ts {
return sourceFile;
}
function getDiagnosticsProducingTypeChecker() {
return program.getTypeChecker(/*produceDiagnostics:*/ true);
}
function getRuleProvider(options: FormatCodeOptions) {
// Ensure rules are initialized and up to date wrt to formatting options
if (!ruleProvider) {
@@ -2076,7 +2072,7 @@ module ts {
}
program = newProgram;
typeInfoResolver = program.getTypeChecker(/*produceDiagnostics*/ false);
typeInfoResolver = program.getTypeChecker();
return;
@@ -2155,7 +2151,7 @@ module ts {
*/
function cleanupSemanticCache(): void {
if (program) {
typeInfoResolver = program.getTypeChecker(/*produceDiagnostics*/ false);
typeInfoResolver = program.getTypeChecker();
}
}
@@ -4673,7 +4669,7 @@ module ts {
var emitHost = createEmitHostFromProgram(program);
emitHost.writeFile = writeFile;
var emitOutput = emitFiles(getDiagnosticsProducingTypeChecker().getEmitResolver(), emitHost, sourceFile);
var emitOutput = emitFiles(program.getEmitResolver(), emitHost, sourceFile);
return {
outputFiles,