Skip to content

Commit dd7ca69

Browse files
committed
Create a new flag for diagnostics 'isEarly' and disable emit if this flag is set. Set the flag by default on all let and const errors to ensure we are not emitting invalid JS code.
1 parent d5fe43b commit dd7ca69

38 files changed

+164
-802
lines changed

scripts/processDiagnosticMessages.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
interface DiagnosticDetails {
44
category: string;
55
code: number;
6+
isEarly?: boolean;
67
}
78

89
interface InputDiagnosticMessageTable {
@@ -63,8 +64,9 @@ function buildInfoFileOutput(messageTable: InputDiagnosticMessageTable, nameMap:
6364
' ' + convertPropertyName(nameMap[name]) +
6465
': { code: ' + diagnosticDetails.code +
6566
', category: DiagnosticCategory.' + diagnosticDetails.category +
66-
', key: "' + name.replace('"', '\\"') +
67-
'" },\r\n';
67+
', key: "' + name.replace('"', '\\"') + '"' +
68+
(diagnosticDetails.isEarly ? ', isEarly: true' : '') +
69+
' },\r\n';
6870
}
6971

7072
result += ' };\r\n}';

src/compiler/binder.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,11 @@ module ts {
8787
}
8888
// Report errors every position with duplicate declaration
8989
// Report errors on previous encountered declarations
90+
var message = symbol.flags & SymbolFlags.BlockScopedVariable ? Diagnostics.Cannot_redeclare_block_scoped_variable_0 : Diagnostics.Duplicate_identifier_0;
9091
forEach(symbol.declarations, (declaration) => {
91-
file.semanticErrors.push(createDiagnosticForNode(declaration.name, Diagnostics.Duplicate_identifier_0, getDisplayName(declaration)));
92+
file.semanticErrors.push(createDiagnosticForNode(declaration.name, message, getDisplayName(declaration)));
9293
});
93-
file.semanticErrors.push(createDiagnosticForNode(node.name, Diagnostics.Duplicate_identifier_0, getDisplayName(node)));
94+
file.semanticErrors.push(createDiagnosticForNode(node.name, message, getDisplayName(node)));
9495

9596
symbol = createSymbol(0, name);
9697
}

src/compiler/checker.ts

+13-5
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ module ts {
109109
isImplementationOfOverload: isImplementationOfOverload,
110110
getAliasedSymbol: resolveImport,
111111
isUndefinedSymbol: symbol => symbol === undefinedSymbol,
112-
isArgumentsSymbol: symbol => symbol === argumentsSymbol
112+
isArgumentsSymbol: symbol => symbol === argumentsSymbol,
113+
hasEarlyErrors: hasEarlyErrors
113114
};
114115

115116
var undefinedSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "undefined");
@@ -228,11 +229,13 @@ module ts {
228229
recordMergedSymbol(target, source);
229230
}
230231
else {
232+
var message = target.flags & SymbolFlags.BlockScopedVariable || source.flags & SymbolFlags.BlockScopedVariable
233+
? Diagnostics.Cannot_redeclare_block_scoped_variable_0 : Diagnostics.Duplicate_identifier_0;
231234
forEach(source.declarations, node => {
232-
error(node.name ? node.name : node, Diagnostics.Duplicate_identifier_0, symbolToString(source));
235+
error(node.name ? node.name : node, message, symbolToString(source));
233236
});
234237
forEach(target.declarations, node => {
235-
error(node.name ? node.name : node, Diagnostics.Duplicate_identifier_0, symbolToString(source));
238+
error(node.name ? node.name : node, message, symbolToString(source));
236239
});
237240
}
238241
}
@@ -327,7 +330,7 @@ module ts {
327330
if (s && s.flags & SymbolFlags.BlockScopedVariable) {
328331
// Block-scoped variables can not be used before their definition
329332
var declaration = forEach(s.declarations, d => d.flags & NodeFlags.BlockScoped ? d : undefined);
330-
Debug.assert(declaration, "Bock-scoped variable declaration is undefined");
333+
Debug.assert(declaration, "Block-scoped variable declaration is undefined");
331334
var declarationSourceFile = getSourceFileOfNode(declaration);
332335
var referenceSourceFile = getSourceFileOfNode(errorLocation);
333336
if (declarationSourceFile === referenceSourceFile && declaration.pos > errorLocation.pos) {
@@ -6864,7 +6867,7 @@ module ts {
68646867
var localDeclarationSymbol = resolveName(node, node.name.text, SymbolFlags.Variable, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined);
68656868
if (localDeclarationSymbol && localDeclarationSymbol !== symbol && localDeclarationSymbol.flags & SymbolFlags.BlockScopedVariable) {
68666869
if (getDeclarationFlagsFromSymbol(localDeclarationSymbol) & NodeFlags.Const) {
6867-
error(node, Diagnostics.Cannot_redeclare_constant_0, symbolToString(localDeclarationSymbol));
6870+
error(node, Diagnostics.Cannot_redeclare_block_scoped_variable_0, symbolToString(localDeclarationSymbol));
68686871
}
68696872
}
68706873
}
@@ -8363,6 +8366,10 @@ module ts {
83638366
return getDiagnostics().length > 0 || getGlobalDiagnostics().length > 0;
83648367
}
83658368

8369+
function hasEarlyErrors(sourceFile?: SourceFile): boolean {
8370+
return forEach(getDiagnostics(sourceFile), d => d.isEarly);
8371+
}
8372+
83668373
function isReferencedImportDeclaration(node: ImportDeclaration): boolean {
83678374
var symbol = getSymbolOfNode(node);
83688375
if (getSymbolLinks(symbol).referenced) {
@@ -8446,6 +8453,7 @@ module ts {
84468453
getEnumMemberValue: getEnumMemberValue,
84478454
isTopLevelValueImportedViaEntityName: isTopLevelValueImportedViaEntityName,
84488455
hasSemanticErrors: hasSemanticErrors,
8456+
hasEarlyErrors: hasEarlyErrors,
84498457
isDeclarationVisible: isDeclarationVisible,
84508458
isImplementationOfOverload: isImplementationOfOverload,
84518459
writeTypeAtLocation: writeTypeAtLocation,

src/compiler/core.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,8 @@ module ts {
232232

233233
messageText: text,
234234
category: message.category,
235-
code: message.code
235+
code: message.code,
236+
isEarly: message.isEarly
236237
};
237238
}
238239

@@ -251,7 +252,8 @@ module ts {
251252

252253
messageText: text,
253254
category: message.category,
254-
code: message.code
255+
code: message.code,
256+
isEarly: message.isEarly
255257
};
256258
}
257259

src/compiler/diagnosticInformationMap.generated.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,6 @@ module ts {
114114
Cannot_compile_external_modules_unless_the_module_flag_is_provided: { code: 1148, category: DiagnosticCategory.Error, key: "Cannot compile external modules unless the '--module' flag is provided." },
115115
Filename_0_differs_from_already_included_filename_1_only_in_casing: { code: 1149, category: DiagnosticCategory.Error, key: "Filename '{0}' differs from already included filename '{1}' only in casing" },
116116
new_T_cannot_be_used_to_create_an_array_Use_new_Array_T_instead: { code: 1150, category: DiagnosticCategory.Error, key: "'new T[]' cannot be used to create an array. Use 'new Array<T>()' instead." },
117-
An_enum_member_cannot_have_a_numeric_name: { code: 1151, category: DiagnosticCategory.Error, key: "An enum member cannot have a numeric name." },
118117
var_let_or_const_expected: { code: 1152, category: DiagnosticCategory.Error, key: "'var', 'let' or 'const' expected." },
119118
let_declarations_are_only_available_when_targeting_ECMAScript_6_and_higher: { code: 1153, category: DiagnosticCategory.Error, key: "'let' declarations are only available when targeting ECMAScript 6 and higher." },
120119
const_declarations_are_only_available_when_targeting_ECMAScript_6_and_higher: { code: 1154, category: DiagnosticCategory.Error, key: "'const' declarations are only available when targeting ECMAScript 6 and higher." },
@@ -267,10 +266,11 @@ module ts {
267266
Property_0_is_protected_and_only_accessible_within_class_1_and_its_subclasses: { code: 2445, category: DiagnosticCategory.Error, key: "Property '{0}' is protected and only accessible within class '{1}' and its subclasses." },
268267
Property_0_is_protected_and_only_accessible_through_an_instance_of_class_1: { code: 2446, category: DiagnosticCategory.Error, key: "Property '{0}' is protected and only accessible through an instance of class '{1}'." },
269268
The_0_operator_is_not_allowed_for_boolean_types_Consider_using_1_instead: { code: 2447, category: DiagnosticCategory.Error, key: "The '{0}' operator is not allowed for boolean types. Consider using '{1}' instead." },
270-
Block_scoped_variable_0_used_before_its_declaration: { code: 2448, category: DiagnosticCategory.Error, key: "Block-scoped variable '{0}' used before its declaration." },
271-
The_operand_of_an_increment_or_decrement_operator_cannot_be_a_constant: { code: 2449, category: DiagnosticCategory.Error, key: "The operand of an increment or decrement operator cannot be a constant." },
272-
Left_hand_side_of_assignment_expression_cannot_be_a_constant: { code: 2450, category: DiagnosticCategory.Error, key: "Left-hand side of assignment expression cannot be a constant." },
273-
Cannot_redeclare_constant_0: { code: 2451, category: DiagnosticCategory.Error, key: "Cannot redeclare constant '{0}'." },
269+
Block_scoped_variable_0_used_before_its_declaration: { code: 2448, category: DiagnosticCategory.Error, key: "Block-scoped variable '{0}' used before its declaration.", isEarly: true },
270+
The_operand_of_an_increment_or_decrement_operator_cannot_be_a_constant: { code: 2449, category: DiagnosticCategory.Error, key: "The operand of an increment or decrement operator cannot be a constant.", isEarly: true },
271+
Left_hand_side_of_assignment_expression_cannot_be_a_constant: { code: 2450, category: DiagnosticCategory.Error, key: "Left-hand side of assignment expression cannot be a constant.", isEarly: true },
272+
Cannot_redeclare_block_scoped_variable_0: { code: 2451, category: DiagnosticCategory.Error, key: "Cannot redeclare block-scoped variable '{0}'.", isEarly: true },
273+
An_enum_member_cannot_have_a_numeric_name: { code: 2452, category: DiagnosticCategory.Error, key: "An enum member cannot have a numeric name." },
274274
Import_declaration_0_is_using_private_name_1: { code: 4000, category: DiagnosticCategory.Error, key: "Import declaration '{0}' is using private name '{1}'." },
275275
Type_parameter_0_of_exported_class_has_or_is_using_name_1_from_private_module_2: { code: 4001, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using name '{1}' from private module '{2}'." },
276276
Type_parameter_0_of_exported_class_has_or_is_using_private_name_1: { code: 4002, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using private name '{1}'." },

src/compiler/diagnosticMessages.json

+13-9
Original file line numberDiff line numberDiff line change
@@ -447,10 +447,6 @@
447447
"category": "Error",
448448
"code": 1150
449449
},
450-
"An enum member cannot have a numeric name.": {
451-
"category": "Error",
452-
"code": 1151
453-
},
454450
"'var', 'let' or 'const' expected.": {
455451
"category": "Error",
456452
"code": 1152
@@ -1062,19 +1058,27 @@
10621058
},
10631059
"Block-scoped variable '{0}' used before its declaration.": {
10641060
"category": "Error",
1065-
"code": 2448
1061+
"code": 2448,
1062+
"isEarly": true
10661063
},
10671064
"The operand of an increment or decrement operator cannot be a constant.": {
10681065
"category": "Error",
1069-
"code": 2449
1066+
"code": 2449,
1067+
"isEarly": true
10701068
},
10711069
"Left-hand side of assignment expression cannot be a constant.": {
10721070
"category": "Error",
1073-
"code": 2450
1071+
"code": 2450,
1072+
"isEarly": true
10741073
},
1075-
"Cannot redeclare constant '{0}'.": {
1074+
"Cannot redeclare block-scoped variable '{0}'.": {
1075+
"category": "Error",
1076+
"code": 2451,
1077+
"isEarly": true
1078+
},
1079+
"An enum member cannot have a numeric name.": {
10761080
"category": "Error",
1077-
"code": 2451
1081+
"code": 2452
10781082
},
10791083

10801084
"Import declaration '{0}' is using private name '{1}'.": {

src/compiler/emitter.ts

+9-4
Original file line numberDiff line numberDiff line change
@@ -3271,11 +3271,14 @@ module ts {
32713271
}
32723272

32733273
var hasSemanticErrors = resolver.hasSemanticErrors();
3274+
var hasEarlyErrors = resolver.hasEarlyErrors(targetSourceFile);
32743275

32753276
function emitFile(jsFilePath: string, sourceFile?: SourceFile) {
3276-
emitJavaScript(jsFilePath, sourceFile);
3277-
if (!hasSemanticErrors && compilerOptions.declaration) {
3278-
emitDeclarations(jsFilePath, sourceFile);
3277+
if (!hasEarlyErrors) {
3278+
emitJavaScript(jsFilePath, sourceFile);
3279+
if (!hasSemanticErrors && compilerOptions.declaration) {
3280+
emitDeclarations(jsFilePath, sourceFile);
3281+
}
32793282
}
32803283
}
32813284

@@ -3315,7 +3318,9 @@ module ts {
33153318

33163319
// Check and update returnCode for syntactic and semantic
33173320
var returnCode: EmitReturnStatus;
3318-
if (hasEmitterError) {
3321+
if (hasEarlyErrors) {
3322+
returnCode = EmitReturnStatus.AllOutputGenerationSkipped;
3323+
} else if (hasEmitterError) {
33193324
returnCode = EmitReturnStatus.EmitErrorsEncountered;
33203325
} else if (hasSemanticErrors && compilerOptions.declaration) {
33213326
returnCode = EmitReturnStatus.DeclarationGenerationSkipped;

src/compiler/tsc.ts

+12-7
Original file line numberDiff line numberDiff line change
@@ -356,13 +356,18 @@ module ts {
356356
else {
357357
var checker = program.getTypeChecker(/*fullTypeCheckMode*/ true);
358358
var checkStart = new Date().getTime();
359-
var semanticErrors = checker.getDiagnostics();
360-
var emitStart = new Date().getTime();
361-
var emitOutput = checker.emitFiles();
362-
var emitErrors = emitOutput.errors;
363-
exitStatus = emitOutput.emitResultStatus;
364-
var reportStart = new Date().getTime();
365-
errors = concatenate(semanticErrors, emitErrors);
359+
errors = checker.getDiagnostics();
360+
if (!checker.hasEarlyErrors()) {
361+
var emitStart = new Date().getTime();
362+
var emitOutput = checker.emitFiles();
363+
var emitErrors = emitOutput.errors;
364+
exitStatus = emitOutput.emitResultStatus;
365+
var reportStart = new Date().getTime();
366+
errors = concatenate(errors, emitErrors);
367+
}
368+
else {
369+
exitStatus = EmitReturnStatus.AllOutputGenerationSkipped;
370+
}
366371
}
367372

368373
reportDiagnostics(errors);

src/compiler/types.ts

+4
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,7 @@ module ts {
667667
isImplementationOfOverload(node: FunctionDeclaration): boolean;
668668
isUndefinedSymbol(symbol: Symbol): boolean;
669669
isArgumentsSymbol(symbol: Symbol): boolean;
670+
hasEarlyErrors(sourceFile?: SourceFile): boolean;
670671

671672
// Returns the constant value of this enum member, or 'undefined' if the enum member has a
672673
// computed value.
@@ -762,6 +763,7 @@ module ts {
762763
// Returns the constant value this property access resolves to, or 'undefined' if it does
763764
// resolve to a constant.
764765
getConstantValue(node: PropertyAccess): number;
766+
hasEarlyErrors(sourceFile?: SourceFile): boolean;
765767
}
766768

767769
export enum SymbolFlags {
@@ -1041,6 +1043,7 @@ module ts {
10411043
key: string;
10421044
category: DiagnosticCategory;
10431045
code: number;
1046+
isEarly?: boolean;
10441047
}
10451048

10461049
// A linked list of formatted diagnostic messages to be used as part of a multiline message.
@@ -1061,6 +1064,7 @@ module ts {
10611064
messageText: string;
10621065
category: DiagnosticCategory;
10631066
code: number;
1067+
isEarly?: boolean;
10641068
}
10651069

10661070
export enum DiagnosticCategory {

src/harness/harness.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -801,9 +801,11 @@ module Harness {
801801
var checker = program.getTypeChecker(/*fullTypeCheckMode*/ true);
802802
checker.checkProgram();
803803

804+
var hasEarlyErrors = checker.hasEarlyErrors();
805+
804806
// only emit if there weren't parse errors
805807
var emitResult: ts.EmitResult;
806-
if (!hadParseErrors) {
808+
if (!hadParseErrors && !hasEarlyErrors) {
807809
emitResult = checker.emitFiles();
808810
}
809811

tests/baselines/reference/constDeclarationShadowedByVarDeclaration.errors.txt

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
tests/cases/compiler/constDeclarationShadowedByVarDeclaration.ts(7,9): error TS2451: Cannot redeclare constant 'x'.
2-
tests/cases/compiler/constDeclarationShadowedByVarDeclaration.ts(15,13): error TS2451: Cannot redeclare constant 'y'.
3-
tests/cases/compiler/constDeclarationShadowedByVarDeclaration.ts(22,7): error TS2451: Cannot redeclare constant 'z'.
1+
tests/cases/compiler/constDeclarationShadowedByVarDeclaration.ts(7,9): error TS2451: Cannot redeclare block-scoped variable 'x'.
2+
tests/cases/compiler/constDeclarationShadowedByVarDeclaration.ts(15,13): error TS2451: Cannot redeclare block-scoped variable 'y'.
3+
tests/cases/compiler/constDeclarationShadowedByVarDeclaration.ts(22,7): error TS2451: Cannot redeclare block-scoped variable 'z'.
44

55

66
==== tests/cases/compiler/constDeclarationShadowedByVarDeclaration.ts (3 errors) ====
@@ -12,7 +12,7 @@ tests/cases/compiler/constDeclarationShadowedByVarDeclaration.ts(22,7): error TS
1212

1313
var x = 0;
1414
~
15-
!!! error TS2451: Cannot redeclare constant 'x'.
15+
!!! error TS2451: Cannot redeclare block-scoped variable 'x'.
1616
}
1717

1818

@@ -22,7 +22,7 @@ tests/cases/compiler/constDeclarationShadowedByVarDeclaration.ts(22,7): error TS
2222
{
2323
var y = 0;
2424
~
25-
!!! error TS2451: Cannot redeclare constant 'y'.
25+
!!! error TS2451: Cannot redeclare block-scoped variable 'y'.
2626
}
2727
}
2828

@@ -31,5 +31,5 @@ tests/cases/compiler/constDeclarationShadowedByVarDeclaration.ts(22,7): error TS
3131
const z = 0;
3232
var z = 0
3333
~
34-
!!! error TS2451: Cannot redeclare constant 'z'.
34+
!!! error TS2451: Cannot redeclare block-scoped variable 'z'.
3535
}

tests/baselines/reference/constDeclarationShadowedByVarDeclaration.js

-43
This file was deleted.

tests/baselines/reference/constDeclarations-access.js

-13
This file was deleted.

0 commit comments

Comments
 (0)