diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c96b2edc5b1..1d50ab4e196 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7181,6 +7181,11 @@ module ts { return target !== unknownSymbol && ((target.flags & SymbolFlags.Value) !== 0); } + function hasSemanticErrors() { + // Return true if there is any semantic error in a file or globally + return (getDiagnostics().length > 0) || (getGlobalDiagnostics().length > 0); + } + function shouldEmitDeclarations() { // If the declaration emit and there are no errors being reported in program or by checker // declarations can be emitted @@ -7258,6 +7263,7 @@ module ts { getNodeCheckFlags: getNodeCheckFlags, getEnumMemberValue: getEnumMemberValue, isTopLevelValueImportedViaEntityName: isTopLevelValueImportedViaEntityName, + hasSemanticErrors: hasSemanticErrors, shouldEmitDeclarations: shouldEmitDeclarations, isDeclarationVisible: isDeclarationVisible, isImplementationOfOverload: isImplementationOfOverload, diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index da67c2d2f13..6a7efc5dbcc 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -48,8 +48,8 @@ module ts { } function getOwnEmitOutputFilePath(sourceFile: SourceFile, extension: string) { - if (program.getCompilerOptions().outDir) { - var emitOutputFilePathWithoutExtension = getModuleNameFromFilename(getSourceFilePathInNewDir(program.getCompilerOptions().outDir, sourceFile)); + if (compilerOptions.outDir) { + var emitOutputFilePathWithoutExtension = getModuleNameFromFilename(getSourceFilePathInNewDir(compilerOptions.outDir, sourceFile)); } else { var emitOutputFilePathWithoutExtension = getModuleNameFromFilename(sourceFile.filename); @@ -3183,7 +3183,27 @@ module ts { diagnostics.sort(compareDiagnostics); diagnostics = deduplicateSortedDiagnostics(diagnostics); + var returnCode = EmitReturnStatus.Succeeded; + + // Check if there is any diagnostic in an error category; if so, there is an emitter error + var hasEmitterError = forEach(diagnostics, diagnostic => diagnostic.category === DiagnosticCategory.Error); + + if (resolver.hasSemanticErrors() && !compilerOptions.declaration) { + // There is an semantic errror when output javascript file + // Output JS file with semantic error + returnCode = EmitReturnStatus.JSGeneratedWithSemanticErrors; + } + else if (resolver.hasSemanticErrors() && compilerOptions.declaration) { + // There is an semantic errror when output javascript and declaration file + // Output JS file with semantic error, not output declaration file + returnCode = EmitReturnStatus.DeclarationGenerationSkipped; + } + else if (hasEmitterError) { + returnCode = EmitReturnStatus.EmitErrorsEncountered; + } + return { + emitResultStatus: returnCode, errors: diagnostics, sourceMaps: sourceMapDataList }; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index c13b47a066c..e34afd7784e 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -588,7 +588,17 @@ module ts { sourceMapDecodedMappings: SourceMapSpan[]; } + // Return code used by getEmitOutput function to indicate status of the function + export enum EmitReturnStatus { + Succeeded = 0, // All outputs generated as requested (.js, .map, .d.ts), no errors reported + AllOutputGenerationSkipped = 1, // No .js generated because of syntax errors, or compiler options errors, nothing generated + JSGeneratedWithSemanticErrors = 2, // .js and .map generated with semantic errors + DeclarationGenerationSkipped = 3, // .d.ts generation skipped because of semantic errors or declaration emitter specific errors; Output .js with semantic errors + EmitErrorsEncountered = 4 // Emitter errors occured during emitting process + } + export interface EmitResult { + emitResultStatus: EmitReturnStatus; errors: Diagnostic[]; sourceMaps: SourceMapData[]; // Array of sourceMapData if compiler emitted sourcemaps } @@ -660,6 +670,7 @@ module ts { isTopLevelValueImportedViaEntityName(node: ImportDeclaration): boolean; getNodeCheckFlags(node: Node): NodeCheckFlags; getEnumMemberValue(node: EnumMember): number; + hasSemanticErrors(): boolean; shouldEmitDeclarations(): boolean; isDeclarationVisible(node: Declaration): boolean; isImplementationOfOverload(node: FunctionDeclaration): boolean; diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 2a4124c842f..94b60420bb6 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -458,7 +458,7 @@ module FourSlash { } } - public verifyEmitOutput(state: ts.EmitOutputResult, filename?: string) { + public verifyEmitOutput(state: ts.EmitReturnStatus, filename?: string) { if (this.enableDeclaration) { this.languageServiceShimHost.setCompilationSettings({ generateDeclarationFiles: true }); } @@ -474,8 +474,8 @@ module FourSlash { var emit = this.languageService.getEmitOutput(this.activeFile.fileName); - if (emit.emitOutputResult !== state) { - throw new Error("Expected emitOutputResult '" + state + "', but actual emitOutputResult '" + emit.emitOutputResult + "'"); + if (emit.emitOutputStatus !== state) { + throw new Error("Expected emitOutputResult '" + state + "', but actual emitOutputResult '" + emit.emitOutputStatus + "'"); } var passed = true; diff --git a/src/services/services.ts b/src/services/services.ts index 1c6eebb9419..c49615f6782 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -663,16 +663,9 @@ module ts { docComment: string; } - export enum EmitOutputResult { - Succeeded, - FailedBecauseOfSyntaxErrors, - FailedBecauseOfCompilerOptionsErrors, - FailedToGenerateDeclarationsBecauseOfSemanticErrors - } - export interface EmitOutput { outputFiles: OutputFile[]; - emitOutputResult: EmitOutputResult; + emitOutputStatus: EmitReturnStatus; } export enum OutputFileType { @@ -2846,56 +2839,43 @@ module ts { function getEmitOutput(filename: string): EmitOutput { synchronizeHostData(); filename = TypeScript.switchToForwardSlashes(filename); - var sourceFile = getSourceFile(filename); + var sourceFile = program.getSourceFile(filename); var compilerOptions = program.getCompilerOptions(); - var emitToSingleFile = ts.shouldEmitToOwnFile(program.getSourceFile(filename), compilerOptions); + var emitToSingleFile = ts.shouldEmitToOwnFile(sourceFile, compilerOptions); var emitDeclaration = compilerOptions.declaration; - var emitResult: EmitOutput = { + var emitOutput: EmitOutput = { outputFiles: [], - emitOutputResult: undefined, + emitOutputStatus: undefined, }; // Initialize writer for CompilerHost.writeFile writer = function (fileName: string, data: string, writeByteOrderMark: boolean) { - var outputFile: OutputFile = { + emitOutput.outputFiles.push({ name: fileName, writeByteOrderMark: writeByteOrderMark, text: data - } - emitResult.outputFiles.push(outputFile); + }); } var syntacticDiagnostics = emitToSingleFile ? program.getDiagnostics(sourceFile) : program.getDiagnostics(); - program.getGlobalDiagnostics(); + var globalSyntacticDiagnostics = program.getGlobalDiagnostics(); // If there is any syntactic error, terminate the process if (containErrors(syntacticDiagnostics)) { - emitResult.emitOutputResult = EmitOutputResult.FailedBecauseOfSyntaxErrors; - return emitResult; + emitOutput.emitOutputStatus = EmitReturnStatus.AllOutputGenerationSkipped; + return emitOutput; } // Perform semantic and force a type check before emit to ensure that all symbols are updated - var semanticDiagnostics = emitToSingleFile - ? getFullTypeCheckChecker().getDiagnostics(getSourceFile(filename).getSourceFile()) - : getFullTypeCheckChecker().getDiagnostics(); - getFullTypeCheckChecker().getGlobalDiagnostics(); - var emitOutput = getFullTypeCheckChecker().emitFiles(); + // EmitFiles will report if there is an error from TypeChecker and Emitter + var emitFilesResult = getFullTypeCheckChecker().emitFiles(); + emitOutput.emitOutputStatus = emitFilesResult.emitResultStatus; - if (emitDeclaration && containErrors(semanticDiagnostics)) { - emitResult.emitOutputResult = EmitOutputResult.FailedToGenerateDeclarationsBecauseOfSemanticErrors; - } - else if (emitDeclaration && containErrors(emitOutput.errors)) { - emitResult.emitOutputResult = EmitOutputResult.FailedToGenerateDeclarationsBecauseOfSemanticErrors; - } - else { - emitResult.emitOutputResult = EmitOutputResult.Succeeded; - } - - // Reset writer back to underfined to make sure that we produce an error message if CompilerHost.writeFile method is called when we are not in an emitting stage + // Reset writer back to underfined to make sure that we produce an error message if CompilerHost.writeFile method is called when we are not in getEmitOutput this.writer = undefined; - return emitResult; + return emitOutput; } /// Syntactic features diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index d724b75e80e..9a6e03845e8 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -44,14 +44,17 @@ enum TypingFidelity { High = FourSlash.TypingFidelity.High } -// We have to duplicate EmitOutputResult from Services.ts to expose the enum to getEmitOutput testcases in fourslah -enum EmitOutputResult { - Succeeded, - FailedBecauseOfSyntaxErrors, - FailedBecauseOfCompilerOptionsErrors, - FailedToGenerateDeclarationsBecauseOfSemanticErrors +// Return code used by getEmitOutput function to indicate status of the function +// It is a duplicate of the one in types.ts to expose it to testcases in fourslash +enum EmitReturnStatus { + Succeeded = 0, // All outputs generated as requested (.js, .map, .d.ts), no errors reported + AllOutputGenerationSkipped = 1, // No .js generated because of syntax errors, or compiler options errors, nothing generated + JSGeneratedWithSemanticErrors = 2, // .js and .map generated with semantic errors + DeclarationGenerationSkipped = 3, // .d.ts generation skipped because of semantic errors or declaration emitter specific errors; Output .js with semantic errors + EmitErrorsEncountered = 4 // Emitter errors occured during emitting process } + module FourSlashInterface { declare var FourSlash; @@ -263,7 +266,7 @@ module FourSlashInterface { FourSlash.currentTestState.verifyEval(expr, value); } - public emitOutput(expectedState: EmitOutputResult, expectedFilename?: string) { + public emitOutput(expectedState: EmitReturnStatus, expectedFilename?: string) { FourSlash.currentTestState.verifyEmitOutput(expectedState, expectedFilename); }