From 96c3c90d9ad69eb55443c61b1fbbffe2ff4c6012 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 16 Dec 2014 13:28:38 -0800 Subject: [PATCH] Rename typechecker parameter related to whether or not we produce diagnostics. Comment what the flag means. --- Jakefile | 2 ++ src/compiler/checker.ts | 66 ++++++++++++++++++++--------------- src/compiler/program.ts | 16 ++++----- src/compiler/types.ts | 10 +++++- src/harness/fourslash.ts | 2 +- src/harness/harness.ts | 2 +- src/harness/projectsRunner.ts | 2 +- src/services/services.ts | 16 ++++----- 8 files changed, 67 insertions(+), 49 deletions(-) diff --git a/Jakefile b/Jakefile index 0425b0a0bf9..4ca4cf19600 100644 --- a/Jakefile +++ b/Jakefile @@ -39,6 +39,7 @@ var compilerSources = [ "binder.ts", "checker.ts", "emitter.ts", + "program.ts", "commandLineParser.ts", "tsc.ts", "diagnosticInformationMap.generated.ts" @@ -56,6 +57,7 @@ var servicesSources = [ "binder.ts", "checker.ts", "emitter.ts", + "program.ts", "diagnosticInformationMap.generated.ts" ].map(function (f) { return path.join(compilerDirectory, f); diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8f133d73cda..b9fb09a361c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9,7 +9,7 @@ module ts { /// If fullTypeCheck === true, then the typechecker should do every possible check to produce all errors /// If fullTypeCheck === false, the typechecker can take shortcuts and skip checks that only produce errors. /// NOTE: checks that somehow affect decisions being made during typechecking should be executed in both cases. - export function createTypeChecker(program: Program, fullTypeCheck: boolean): TypeChecker { + export function createTypeChecker(program: Program, produceDiagnostics: boolean): TypeChecker { var Symbol = objectAllocator.getSymbolConstructor(); var Type = objectAllocator.getTypeConstructor(); var Signature = objectAllocator.getSignatureConstructor(); @@ -4168,7 +4168,7 @@ module ts { } function reportErrorsFromWidening(declaration: Declaration, type: Type) { - if (fullTypeCheck && compilerOptions.noImplicitAny && type.flags & TypeFlags.Unwidened) { + if (produceDiagnostics && compilerOptions.noImplicitAny && type.flags & TypeFlags.Unwidened) { // Report implicit any error within type if possible, otherwise report error on declaration if (!reportWideningErrorsInType(type)) { reportImplicitAnyError(declaration, type); @@ -5957,7 +5957,7 @@ module ts { // Pick the first candidate that matches the arity. This way we can get a contextual type for cases like: // declare function f(a: { xa: number; xb: number; }); // f({ | - if (!fullTypeCheck) { + if (!produceDiagnostics) { for (var i = 0, n = candidates.length; i < n; i++) { if (hasCorrectArity(node, args, candidates[i])) { return candidates[i]; @@ -6254,7 +6254,7 @@ module ts { function checkTypeAssertion(node: TypeAssertion): Type { var exprType = checkExpression(node.expression); var targetType = getTypeFromTypeNode(node.type); - if (fullTypeCheck && targetType !== unknownType) { + if (produceDiagnostics && targetType !== unknownType) { var widenedType = getWidenedType(exprType); if (!(isTypeAssignableTo(targetType, widenedType))) { checkTypeAssignableTo(exprType, targetType, node, Diagnostics.Neither_type_0_nor_type_1_is_assignable_to_the_other); @@ -6340,7 +6340,7 @@ module ts { // must have at least one return statement somewhere in its body. // An exception to this rule is if the function implementation consists of a single 'throw' statement. function checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(func: FunctionLikeDeclaration, returnType: Type): void { - if (!fullTypeCheck) { + if (!produceDiagnostics) { return; } @@ -6406,7 +6406,7 @@ module ts { } } - if (fullTypeCheck && node.kind !== SyntaxKind.MethodDeclaration && node.kind !== SyntaxKind.MethodSignature) { + if (produceDiagnostics && node.kind !== SyntaxKind.MethodDeclaration && node.kind !== SyntaxKind.MethodSignature) { checkCollisionWithCapturedSuperVariable(node, (node).name); checkCollisionWithCapturedThisVariable(node,(node).name); } @@ -6813,7 +6813,7 @@ module ts { } function checkAssignmentOperator(valueType: Type): void { - if (fullTypeCheck && operator >= SyntaxKind.FirstAssignment && operator <= SyntaxKind.LastAssignment) { + if (produceDiagnostics && operator >= SyntaxKind.FirstAssignment && operator <= SyntaxKind.LastAssignment) { // TypeScript 1.0 spec (April 2014): 4.17 // An assignment of the form // VarExpr = ValueExpr @@ -6996,7 +6996,7 @@ module ts { function checkTypeParameter(node: TypeParameterDeclaration) { checkSourceElement(node.constraint); - if (fullTypeCheck) { + if (produceDiagnostics) { checkTypeParameterHasIllegalReferencesInConstraint(node); checkTypeNameIsReserved(node.name, Diagnostics.Type_parameter_name_cannot_be_0); } @@ -7025,7 +7025,7 @@ module ts { if (node.type) { checkSourceElement(node.type); } - if (fullTypeCheck) { + if (produceDiagnostics) { checkCollisionWithArgumentsInGeneratedCode(node); if (compilerOptions.noImplicitAny && !node.type) { switch (node.kind) { @@ -7109,7 +7109,7 @@ module ts { return; } - if (!fullTypeCheck) { + if (!produceDiagnostics) { return; } @@ -7177,7 +7177,7 @@ module ts { } function checkAccessorDeclaration(node: AccessorDeclaration) { - if (fullTypeCheck) { + if (produceDiagnostics) { if (node.kind === SyntaxKind.GetAccessor) { if (!isInAmbientContext(node) && node.body && !(bodyContainsAReturnStatement(node.body) || bodyContainsSingleThrowStatement(node.body))) { error(node.name, Diagnostics.A_get_accessor_must_return_a_value_or_consist_of_a_single_throw_statement); @@ -7220,7 +7220,7 @@ module ts { for (var i = 0; i < len; i++) { checkSourceElement(node.typeArguments[i]); var constraint = getConstraintOfTypeParameter((type).target.typeParameters[i]); - if (fullTypeCheck && constraint) { + if (produceDiagnostics && constraint) { var typeArgument = (type).typeArguments[i]; checkTypeAssignableTo(typeArgument, constraint, node, Diagnostics.Type_0_does_not_satisfy_the_constraint_1); } @@ -7234,7 +7234,7 @@ module ts { function checkTypeLiteral(node: TypeLiteralNode) { forEach(node.members, checkSourceElement); - if (fullTypeCheck) { + if (produceDiagnostics) { var type = getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node); checkIndexConstraints(type); checkTypeForDuplicateIndexSignatures(node); @@ -7258,7 +7258,7 @@ module ts { } function checkSpecializedSignatureDeclaration(signatureDeclarationNode: SignatureDeclaration): void { - if (!fullTypeCheck) { + if (!produceDiagnostics) { return; } var signature = getSignatureFromDeclaration(signatureDeclarationNode); @@ -7314,7 +7314,7 @@ module ts { } function checkFunctionOrConstructorSymbol(symbol: Symbol): void { - if (!fullTypeCheck) { + if (!produceDiagnostics) { return; } @@ -7524,7 +7524,7 @@ module ts { } function checkExportsOnMergedDeclarations(node: Node): void { - if (!fullTypeCheck) { + if (!produceDiagnostics) { return; } @@ -7599,7 +7599,7 @@ module ts { function checkFunctionDeclaration(node: FunctionDeclaration): void { checkFunctionLikeDeclaration(node); - if (fullTypeCheck) { + if (produceDiagnostics) { checkCollisionWithCapturedSuperVariable(node, node.name); checkCollisionWithCapturedThisVariable(node, node.name); checkCollisionWithRequireExportsInGeneratedCode(node, node.name); @@ -7999,7 +7999,7 @@ module ts { function checkSwitchStatement(node: SwitchStatement) { var expressionType = checkExpression(node.expression); forEach(node.clauses, clause => { - if (fullTypeCheck && clause.kind === SyntaxKind.CaseClause) { + if (produceDiagnostics && clause.kind === SyntaxKind.CaseClause) { var caseClause = clause; // TypeScript 1.0 spec (April 2014):5.9 // In a 'switch' statement, each 'case' expression must be of a type that is assignable to or from the type of the 'switch' expression. @@ -8119,7 +8119,7 @@ module ts { var node = typeParameterDeclarations[i]; checkTypeParameter(node); - if (fullTypeCheck) { + if (produceDiagnostics) { for (var j = 0; j < i; j++) { if (typeParameterDeclarations[j].symbol === node.symbol) { error(node.name, Diagnostics.Duplicate_identifier_0, declarationNameToString(node.name)); @@ -8145,7 +8145,7 @@ module ts { checkTypeReference(baseTypeNode); } if (type.baseTypes.length) { - if (fullTypeCheck) { + if (produceDiagnostics) { var baseType = type.baseTypes[0]; checkTypeAssignableTo(type, baseType, node.name, Diagnostics.Class_0_incorrectly_extends_base_class_1); var staticBaseType = getTypeOfSymbol(baseType.symbol); @@ -8166,7 +8166,7 @@ module ts { if (implementedTypeNodes) { forEach(implementedTypeNodes, typeRefNode => { checkTypeReference(typeRefNode); - if (fullTypeCheck) { + if (produceDiagnostics) { var t = getTypeFromTypeReferenceNode(typeRefNode); if (t !== unknownType) { var declaredType = (t.flags & TypeFlags.Reference) ? (t).target : t; @@ -8182,7 +8182,7 @@ module ts { } forEach(node.members, checkSourceElement); - if (fullTypeCheck) { + if (produceDiagnostics) { checkIndexConstraints(type); checkTypeForDuplicateIndexSignatures(node); } @@ -8335,7 +8335,7 @@ module ts { function checkInterfaceDeclaration(node: InterfaceDeclaration) { checkTypeParameters(node.typeParameters); - if (fullTypeCheck) { + if (produceDiagnostics) { checkTypeNameIsReserved(node.name, Diagnostics.Interface_name_cannot_be_0); checkExportsOnMergedDeclarations(node); @@ -8362,7 +8362,7 @@ module ts { forEach(getInterfaceBaseTypeNodes(node), checkTypeReference); forEach(node.members, checkSourceElement); - if (fullTypeCheck) { + if (produceDiagnostics) { checkTypeForDuplicateIndexSignatures(node); } } @@ -8531,7 +8531,7 @@ module ts { } function checkEnumDeclaration(node: EnumDeclaration) { - if (!fullTypeCheck) { + if (!produceDiagnostics) { return; } @@ -8598,7 +8598,7 @@ module ts { } function checkModuleDeclaration(node: ModuleDeclaration) { - if (fullTypeCheck) { + if (produceDiagnostics) { checkCollisionWithCapturedThisVariable(node, node.name); checkCollisionWithRequireExportsInGeneratedCode(node, node.name); checkExportsOnMergedDeclarations(node); @@ -8919,7 +8919,7 @@ module ts { } function getSortedDiagnostics(): Diagnostic[]{ - Debug.assert(fullTypeCheck, "diagnostics are available only in the full typecheck mode"); + Debug.assert(produceDiagnostics, "diagnostics are available only in the full typecheck mode"); if (diagnosticsModified) { diagnostics.sort(compareDiagnostics); @@ -8929,7 +8929,8 @@ module ts { return diagnostics; } - function getDiagnostics(sourceFile?: SourceFile): Diagnostic[]{ + function getDiagnostics(sourceFile?: SourceFile): Diagnostic[] { + throwIfNonDiagnosticsProducing(); if (sourceFile) { checkSourceFile(sourceFile); return filter(getSortedDiagnostics(), d => d.file === sourceFile); @@ -8938,10 +8939,17 @@ module ts { return getSortedDiagnostics(); } - function getGlobalDiagnostics(): Diagnostic[] { + function getGlobalDiagnostics(): Diagnostic[]{ + throwIfNonDiagnosticsProducing(); return filter(getSortedDiagnostics(), d => !d.file); } + function throwIfNonDiagnosticsProducing() { + if (!produceDiagnostics) { + throw new Error("Trying to get diagnostics from a type checker that does not produce them."); + } + } + // Language service support function isInsideWithStatementBody(node: Node): boolean { diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 7fb77dc9bdd..897b9d104bb 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -89,15 +89,15 @@ module ts { verifyCompilerOptions(); errors.sort(compareDiagnostics); - var fullTypeChecker: TypeChecker; - var nonFullTypeChecker: TypeChecker; + var diagnosticsProducingTypeChecker: TypeChecker; + var noDiagnosticsTypeChecker: TypeChecker; - function getTypeChecker(full: boolean) { - if (full) { - return fullTypeChecker || (fullTypeChecker = createTypeChecker(program, full)); + function getTypeChecker(produceDiagnostics: boolean) { + if (produceDiagnostics) { + return diagnosticsProducingTypeChecker || (diagnosticsProducingTypeChecker = createTypeChecker(program, produceDiagnostics)); } else { - return nonFullTypeChecker || (nonFullTypeChecker = createTypeChecker(program, full)); + return noDiagnosticsTypeChecker || (noDiagnosticsTypeChecker = createTypeChecker(program, produceDiagnostics)); } } @@ -116,14 +116,14 @@ module ts { return program; function getDeclarationDiagnostics(targetSourceFile: SourceFile): Diagnostic[]{ - var fullTypeChecker = getTypeChecker(/*full:*/ true); + var fullTypeChecker = getTypeChecker(/*produceDiagnostics:*/true); fullTypeChecker.getDiagnostics(targetSourceFile); var resolver = fullTypeChecker.getEmitResolver(); return ts.getDeclarationDiagnostics(program, resolver, targetSourceFile); } function invokeEmitter(targetSourceFile?: SourceFile) { - var resolver = getTypeChecker(/*full:*/ true).getEmitResolver(); + var resolver = getTypeChecker(/*produceDiagnostics:*/true).getEmitResolver(); return emitFiles(resolver, targetSourceFile); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index b164e71d0c4..7189db3781b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -917,7 +917,15 @@ module ts { getGlobalDiagnostics(): Diagnostic[]; getDeclarationDiagnostics(sourceFile: SourceFile): Diagnostic[]; - getTypeChecker(fullTypeCheckMode: boolean): TypeChecker; + // Gets a type checker that can be used to semantically analyze source fils in the program. + // The 'produceDiagnostics' flag determines if the checker will produce diagnostics while + // analyzing the code. It can be set to 'false' to make many type checking operaitons + // faster. With this flag set, the checker can avoid codepaths only necessary to produce + // diagnostics, but not necessary to answer semantic questions about the code. + // + // If 'produceDiagnostics' is false, then any calls to get diagnostics from the TypeChecker + // will throw an invalid operation exception. + getTypeChecker(produceDiagnostics: boolean): TypeChecker; getCommonSourceDirectory(): string; emitFiles(targetSourceFile?: SourceFile): EmitResult; } diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 2dbbfe877c8..14903033bac 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2196,7 +2196,7 @@ module FourSlash { ts.sys.useCaseSensitiveFileNames); // TODO (drosen): We need to enforce checking on these tests. var program = ts.createProgram([Harness.Compiler.fourslashFilename, fileName], { out: "fourslashTestOutput.js", noResolve: true, target: ts.ScriptTarget.ES3 }, host); - var checker = ts.createTypeChecker(program, /*fullTypeCheckMode*/ true); + var checker = ts.createTypeChecker(program, /*produceDiagnostics*/ true); var errors = program.getDiagnostics().concat(checker.getDiagnostics()); if (errors.length > 0) { diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 4e4a01ccef1..2885045a362 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -1044,7 +1044,7 @@ module Harness { options.target, useCaseSensitiveFileNames)); - var checker = program.getTypeChecker(/*fullTypeCheckMode*/ true); + var checker = program.getTypeChecker(/*produceDiagnostics*/ true); var isEmitBlocked = checker.isEmitBlocked(); diff --git a/src/harness/projectsRunner.ts b/src/harness/projectsRunner.ts index e22a412b53e..52467fdffd6 100644 --- a/src/harness/projectsRunner.ts +++ b/src/harness/projectsRunner.ts @@ -130,7 +130,7 @@ class ProjectRunner extends RunnerBase { var errors = program.getDiagnostics(); var sourceMapData: ts.SourceMapData[] = null; if (!errors.length) { - var checker = program.getTypeChecker(/*fullTypeCheck*/ true); + var checker = program.getTypeChecker(/*produceDiagnostics:*/ true); errors = checker.getDiagnostics(); var emitResult = program.emitFiles(); errors = ts.concatenate(errors, emitResult.diagnostics); diff --git a/src/services/services.ts b/src/services/services.ts index c127947b7f9..7e8a4b3b6a8 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2217,7 +2217,7 @@ module ts { // the sole purpose of this checker is to return semantic diagnostics // creation is deferred - use getFullTypeCheckChecker to get instance - var fullTypeCheckChecker_doNotAccessDirectly: TypeChecker; + var diagnosticsProducingTypeChecker_doNotAccessDirectly: TypeChecker; var useCaseSensitivefilenames = false; var sourceFilesByName: Map = {}; @@ -2239,8 +2239,8 @@ module ts { return lookUp(sourceFilesByName, getCanonicalFileName(filename)); } - function getFullTypeCheckChecker() { - return fullTypeCheckChecker_doNotAccessDirectly || (fullTypeCheckChecker_doNotAccessDirectly = program.getTypeChecker(/*fullTypeCheck*/ true)); + function getDiagnosticsProducingTypeChecker() { + return diagnosticsProducingTypeChecker_doNotAccessDirectly || (diagnosticsProducingTypeChecker_doNotAccessDirectly = program.getTypeChecker(/*produceDiagnostics:*/ true)); } function getRuleProvider(options: FormatCodeOptions) { @@ -2380,8 +2380,8 @@ module ts { // Now create a new compiler program = createProgram(hostfilenames, compilationSettings, createCompilerHost()); - typeInfoResolver = program.getTypeChecker(/*fullTypeCheckMode*/ false); - fullTypeCheckChecker_doNotAccessDirectly = undefined; + typeInfoResolver = program.getTypeChecker(/*produceDiagnostics*/ false); + diagnosticsProducingTypeChecker_doNotAccessDirectly = undefined; } /** @@ -2391,8 +2391,8 @@ module ts { */ function cleanupSemanticCache(): void { if (program) { - typeInfoResolver = program.getTypeChecker(/*fullTypeCheckMode*/ false); - fullTypeCheckChecker_doNotAccessDirectly = undefined; + typeInfoResolver = program.getTypeChecker(/*produceDiagnostics*/ false); + diagnosticsProducingTypeChecker_doNotAccessDirectly = undefined; } } @@ -2421,7 +2421,7 @@ module ts { filename = normalizeSlashes(filename) var compilerOptions = program.getCompilerOptions(); - var checker = getFullTypeCheckChecker(); + var checker = getDiagnosticsProducingTypeChecker(); var targetSourceFile = getSourceFile(filename); // Only perform the action per file regardless of '-out' flag as LanguageServiceHost is expected to call this function per file.