diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 76ce1eda18298..0e42f2eb4eea4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -109,7 +109,7 @@ module ts { getAliasedSymbol: resolveImport, isUndefinedSymbol: symbol => symbol === undefinedSymbol, isArgumentsSymbol: symbol => symbol === argumentsSymbol, - hasEarlyErrors: hasEarlyErrors + isEmitBlocked: isEmitBlocked }; var undefinedSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "undefined"); @@ -8984,6 +8984,12 @@ module ts { return getDiagnostics().length > 0 || getGlobalDiagnostics().length > 0; } + function isEmitBlocked(sourceFile?: SourceFile): boolean { + return program.getDiagnostics(sourceFile).length !== 0 || + hasEarlyErrors(sourceFile) || + (compilerOptions.noEmitOnError && getDiagnostics(sourceFile).length !== 0); + } + function hasEarlyErrors(sourceFile?: SourceFile): boolean { return forEach(getDiagnostics(sourceFile), d => d.isEarly); } @@ -9080,7 +9086,7 @@ module ts { getEnumMemberValue: getEnumMemberValue, isTopLevelValueImportWithEntityName: isTopLevelValueImportWithEntityName, hasSemanticErrors: hasSemanticErrors, - hasEarlyErrors: hasEarlyErrors, + isEmitBlocked: isEmitBlocked, isDeclarationVisible: isDeclarationVisible, isImplementationOfOverload: isImplementationOfOverload, writeTypeAtLocation: writeTypeAtLocation, diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index fed7c991b2e3d..5e61e9e1cb799 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -54,6 +54,11 @@ module ts { paramType: Diagnostics.KIND, error: Diagnostics.Argument_for_module_option_must_be_commonjs_or_amd }, + { + name: "noEmitOnError", + type: "boolean", + description: Diagnostics.Do_not_emit_outputs_if_any_type_checking_errors_were_reported, + }, { name: "noImplicitAny", type: "boolean", diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index 349601a6db8e5..bae908b27742e 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -373,6 +373,7 @@ module ts { Watch_input_files: { code: 6005, category: DiagnosticCategory.Message, key: "Watch input files." }, Redirect_output_structure_to_the_directory: { code: 6006, category: DiagnosticCategory.Message, key: "Redirect output structure to the directory." }, Do_not_erase_const_enum_declarations_in_generated_code: { code: 6007, category: DiagnosticCategory.Message, key: "Do not erase const enum declarations in generated code." }, + Do_not_emit_outputs_if_any_type_checking_errors_were_reported: { code: 6008, category: DiagnosticCategory.Message, key: "Do not emit outputs if any type checking errors were reported." }, Do_not_emit_comments_to_output: { code: 6009, category: DiagnosticCategory.Message, key: "Do not emit comments to output." }, Specify_ECMAScript_target_version_Colon_ES3_default_ES5_or_ES6_experimental: { code: 6015, category: DiagnosticCategory.Message, key: "Specify ECMAScript target version: 'ES3' (default), 'ES5', or 'ES6' (experimental)" }, Specify_module_code_generation_Colon_commonjs_or_amd: { code: 6016, category: DiagnosticCategory.Message, key: "Specify module code generation: 'commonjs' or 'amd'" }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 7359abefb6048..93638b6dadeba 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1490,6 +1490,10 @@ "category": "Message", "code": 6007 }, + "Do not emit outputs if any type checking errors were reported.": { + "category": "Message", + "code": 6008 + }, "Do not emit comments to output.": { "category": "Message", "code": 6009 diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index c9672b87cd13a..00d958604c7e8 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -3463,10 +3463,10 @@ module ts { } var hasSemanticErrors = resolver.hasSemanticErrors(); - var hasEarlyErrors = resolver.hasEarlyErrors(targetSourceFile); + var isEmitBlocked = resolver.isEmitBlocked(targetSourceFile); function emitFile(jsFilePath: string, sourceFile?: SourceFile) { - if (!hasEarlyErrors) { + if (!isEmitBlocked) { emitJavaScript(jsFilePath, sourceFile); if (!hasSemanticErrors && compilerOptions.declaration) { emitDeclarations(jsFilePath, sourceFile); @@ -3510,7 +3510,7 @@ module ts { // Check and update returnCode for syntactic and semantic var returnCode: EmitReturnStatus; - if (hasEarlyErrors) { + if (isEmitBlocked) { returnCode = EmitReturnStatus.AllOutputGenerationSkipped; } else if (hasEmitterError) { returnCode = EmitReturnStatus.EmitErrorsEncountered; diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 15f231bc74ea9..e5291c9d7f1b8 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -362,7 +362,10 @@ module ts { var checker = program.getTypeChecker(/*fullTypeCheckMode*/ true); var checkStart = new Date().getTime(); errors = checker.getDiagnostics(); - if (!checker.hasEarlyErrors()) { + if (checker.isEmitBlocked()) { + exitStatus = EmitReturnStatus.AllOutputGenerationSkipped; + } + else { var emitStart = new Date().getTime(); var emitOutput = checker.emitFiles(); var emitErrors = emitOutput.errors; @@ -370,9 +373,6 @@ module ts { var reportStart = new Date().getTime(); errors = concatenate(errors, emitErrors); } - else { - exitStatus = EmitReturnStatus.AllOutputGenerationSkipped; - } } reportDiagnostics(errors); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 411be56b8b256..810b5a759e464 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -731,8 +731,8 @@ module ts { getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature; isImplementationOfOverload(node: FunctionLikeDeclaration): boolean; isUndefinedSymbol(symbol: Symbol): boolean; - isArgumentsSymbol(symbol: Symbol): boolean; - hasEarlyErrors(sourceFile?: SourceFile): boolean; + isArgumentsSymbol(symbol: Symbol): boolean; + isEmitBlocked(sourceFile?: SourceFile): boolean; // Returns the constant value of this enum member, or 'undefined' if the enum member has a computed value. getEnumMemberValue(node: EnumMember): number; isValidPropertyAccess(node: PropertyAccess, propertyName: string): boolean; @@ -823,7 +823,7 @@ module ts { isImportDeclarationEntityNameReferenceDeclarationVisibile(entityName: EntityName): SymbolAccessiblityResult; // Returns the constant value this property access resolves to, or 'undefined' for a non-constant getConstantValue(node: PropertyAccess | IndexedAccess): number; - hasEarlyErrors(sourceFile?: SourceFile): boolean; + isEmitBlocked(sourceFile?: SourceFile): boolean; } export const enum SymbolFlags { @@ -1156,6 +1156,7 @@ module ts { locale?: string; mapRoot?: string; module?: ModuleKind; + noEmitOnError?: boolean; noErrorTruncation?: boolean; noImplicitAny?: boolean; noLib?: boolean; diff --git a/src/harness/compilerRunner.ts b/src/harness/compilerRunner.ts index 7045a1196d087..cc04d4813a1ae 100644 --- a/src/harness/compilerRunner.ts +++ b/src/harness/compilerRunner.ts @@ -175,7 +175,12 @@ class CompilerBaselineRunner extends RunnerBase { it('Correct sourcemap content for ' + fileName, () => { if (options.sourceMap) { Harness.Baseline.runBaseline('Correct sourcemap content for ' + fileName, justName.replace(/\.ts$/, '.sourcemap.txt'), () => { - return result.getSourceMapRecord(); + var record = result.getSourceMapRecord(); + if (options.noEmitOnError && result.errors.length !== 0 && record === undefined) { + // Because of the noEmitOnError option no files are created. We need to return null because baselining isn't required. + return null; + } + return record; }); } }); @@ -246,6 +251,12 @@ class CompilerBaselineRunner extends RunnerBase { } Harness.Baseline.runBaseline('Correct Sourcemap output for ' + fileName, justName.replace(/\.ts/, '.js.map'), () => { + if (options.noEmitOnError && result.errors.length !== 0 && result.sourceMaps.length === 0) { + // We need to return null here or the runBaseLine will actually create a empty file. + // Baselining isn't required here because there is no output. + return null; + } + var sourceMapCode = ''; for (var i = 0; i < result.sourceMaps.length; i++) { sourceMapCode += '//// [' + Harness.Path.getFileName(result.sourceMaps[i].fileName) + ']\r\n'; diff --git a/src/harness/harness.ts b/src/harness/harness.ts index f77cd09929dc8..d5eaa3828a15f 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -700,6 +700,10 @@ module Harness { } break; + case 'noemitonerror': + options.noEmitOnError = !!setting.value; + break; + case 'noresolve': options.noResolve = !!setting.value; break; @@ -794,16 +798,14 @@ module Harness { options.target, useCaseSensitiveFileNames)); - var hadParseErrors = program.getDiagnostics().length > 0; - var checker = program.getTypeChecker(/*fullTypeCheckMode*/ true); checker.checkProgram(); - var hasEarlyErrors = checker.hasEarlyErrors(); + var isEmitBlocked = checker.isEmitBlocked(); // only emit if there weren't parse errors var emitResult: ts.EmitResult; - if (!hadParseErrors && !hasEarlyErrors) { + if (!isEmitBlocked) { emitResult = checker.emitFiles(); } @@ -1148,7 +1150,7 @@ module Harness { var optionRegex = /^[\/]{2}\s*@(\w+)\s*:\s*(\S*)/gm; // multiple matches on multiple lines // List of allowed metadata names - var fileMetadataNames = ["filename", "comments", "declaration", "module", "nolib", "sourcemap", "target", "out", "outdir", "noimplicitany", "noresolve", "newline", "newlines", "emitbom", "errortruncation", "usecasesensitivefilenames", "preserveconstenums"]; + var fileMetadataNames = ["filename", "comments", "declaration", "module", "nolib", "sourcemap", "target", "out", "outdir", "noemitonerror","noimplicitany", "noresolve", "newline", "newlines", "emitbom", "errortruncation", "usecasesensitivefilenames", "preserveconstenums"]; function extractCompilerSettings(content: string): CompilerSetting[] { diff --git a/tests/baselines/reference/noEmitOnError.errors.txt b/tests/baselines/reference/noEmitOnError.errors.txt new file mode 100644 index 0000000000000..92a7151d74e2a --- /dev/null +++ b/tests/baselines/reference/noEmitOnError.errors.txt @@ -0,0 +1,9 @@ +tests/cases/compiler/noEmitOnError.ts(2,5): error TS2322: Type 'string' is not assignable to type 'number'. + + +==== tests/cases/compiler/noEmitOnError.ts (1 errors) ==== + + var x: number = ""; + ~ +!!! error TS2322: Type 'string' is not assignable to type 'number'. + \ No newline at end of file diff --git a/tests/cases/compiler/noEmitOnError.ts b/tests/cases/compiler/noEmitOnError.ts new file mode 100644 index 0000000000000..b171a23c5b95b --- /dev/null +++ b/tests/cases/compiler/noEmitOnError.ts @@ -0,0 +1,5 @@ +// @noemitonerror: true +// @sourcemap: true +// @declaration: true + +var x: number = "";