Skip to content

Commit 979d45e

Browse files
committed
Disallow let and const declarations outside blocks
1 parent 778f101 commit 979d45e

13 files changed

+1205
-26
lines changed

src/compiler/diagnosticInformationMap.generated.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,9 @@ module ts {
118118
var_let_or_const_expected: { code: 1152, category: DiagnosticCategory.Error, key: "'var', 'let' or 'const' expected." },
119119
let_variable_declarations_are_only_available_when_targeting_ECMAScript_6_and_higher: { code: 1153, category: DiagnosticCategory.Error, key: "'let' variable declarations are only available when targeting ECMAScript 6 and higher." },
120120
const_variable_declarations_are_only_available_when_targeting_ECMAScript_6_and_higher: { code: 1154, category: DiagnosticCategory.Error, key: "'const' variable declarations are only available when targeting ECMAScript 6 and higher." },
121-
Const_must_be_intialized: { code: 1155, category: DiagnosticCategory.Error, key: "Const must be intialized." },
121+
const_must_be_intialized: { code: 1155, category: DiagnosticCategory.Error, key: "const must be intialized." },
122+
const_must_be_declared_inside_a_block: { code: 1156, category: DiagnosticCategory.Error, key: "const must be declared inside a block." },
123+
let_must_be_declared_inside_a_block: { code: 1157, category: DiagnosticCategory.Error, key: "let must be declared inside a block." },
122124
Duplicate_identifier_0: { code: 2300, category: DiagnosticCategory.Error, key: "Duplicate identifier '{0}'." },
123125
Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor: { code: 2301, category: DiagnosticCategory.Error, key: "Initializer of instance member variable '{0}' cannot reference identifier '{1}' declared in the constructor." },
124126
Static_members_cannot_reference_class_type_parameters: { code: 2302, category: DiagnosticCategory.Error, key: "Static members cannot reference class type parameters." },

src/compiler/diagnosticMessages.json

+9-1
Original file line numberDiff line numberDiff line change
@@ -463,10 +463,18 @@
463463
"category": "Error",
464464
"code": 1154
465465
},
466-
"Const must be intialized.": {
466+
"const must be intialized.": {
467467
"category": "Error",
468468
"code": 1155
469469
},
470+
"const must be declared inside a block.": {
471+
"category": "Error",
472+
"code": 1156
473+
},
474+
"let must be declared inside a block.": {
475+
"category": "Error",
476+
"code": 1157
477+
},
470478

471479
"Duplicate identifier '{0}'.": {
472480
"category": "Error",

src/compiler/parser.ts

+31-24
Original file line numberDiff line numberDiff line change
@@ -2542,11 +2542,14 @@ module ts {
25422542
}
25432543

25442544
// STATEMENTS
2545+
function parseStatementAllowingLetDeclaration() {
2546+
return parseStatement(/*allowLetDeclarations*/ true);
2547+
}
25452548

25462549
function parseBlock(ignoreMissingOpenBrace: boolean, checkForStrictMode: boolean): Block {
25472550
var node = <Block>createNode(SyntaxKind.Block);
25482551
if (parseExpected(SyntaxKind.OpenBraceToken) || ignoreMissingOpenBrace) {
2549-
node.statements = parseList(ParsingContext.BlockStatements,checkForStrictMode, parseStatement);
2552+
node.statements = parseList(ParsingContext.BlockStatements, checkForStrictMode, parseStatementAllowingLetDeclaration);
25502553
parseExpected(SyntaxKind.CloseBraceToken);
25512554
}
25522555
else {
@@ -2592,8 +2595,8 @@ module ts {
25922595
parseExpected(SyntaxKind.OpenParenToken);
25932596
node.expression = parseExpression();
25942597
parseExpected(SyntaxKind.CloseParenToken);
2595-
node.thenStatement = parseStatement();
2596-
node.elseStatement = parseOptional(SyntaxKind.ElseKeyword) ? parseStatement() : undefined;
2598+
node.thenStatement = parseStatement(/*allowLetDeclarations*/ false);
2599+
node.elseStatement = parseOptional(SyntaxKind.ElseKeyword) ? parseStatement(/*allowLetDeclarations*/ false) : undefined;
25972600
return finishNode(node);
25982601
}
25992602

@@ -2603,7 +2606,7 @@ module ts {
26032606

26042607
var saveInIterationStatement = inIterationStatement;
26052608
inIterationStatement = ControlBlockContext.Nested;
2606-
node.statement = parseStatement();
2609+
node.statement = parseStatement(/*allowLetDeclarations*/ false);
26072610
inIterationStatement = saveInIterationStatement;
26082611

26092612
parseExpected(SyntaxKind.WhileKeyword);
@@ -2628,7 +2631,7 @@ module ts {
26282631

26292632
var saveInIterationStatement = inIterationStatement;
26302633
inIterationStatement = ControlBlockContext.Nested;
2631-
node.statement = parseStatement();
2634+
node.statement = parseStatement(/*allowLetDeclarations*/ false);
26322635
inIterationStatement = saveInIterationStatement;
26332636

26342637
return finishNode(node);
@@ -2692,7 +2695,7 @@ module ts {
26922695

26932696
var saveInIterationStatement = inIterationStatement;
26942697
inIterationStatement = ControlBlockContext.Nested;
2695-
forOrForInStatement.statement = parseStatement();
2698+
forOrForInStatement.statement = parseStatement(/*allowLetDeclarations*/ false);
26962699
inIterationStatement = saveInIterationStatement;
26972700

26982701
return finishNode(forOrForInStatement);
@@ -2803,7 +2806,7 @@ module ts {
28032806
parseExpected(SyntaxKind.OpenParenToken);
28042807
node.expression = parseExpression();
28052808
parseExpected(SyntaxKind.CloseParenToken);
2806-
node.statement = parseStatement();
2809+
node.statement = parseStatement(/*allowLetDeclarations*/ false);
28072810
node = finishNode(node);
28082811
if (isInStrictMode) {
28092812
// Strict mode code may not include a WithStatement. The occurrence of a WithStatement in such
@@ -2818,15 +2821,15 @@ module ts {
28182821
parseExpected(SyntaxKind.CaseKeyword);
28192822
node.expression = parseExpression();
28202823
parseExpected(SyntaxKind.ColonToken);
2821-
node.statements = parseList(ParsingContext.SwitchClauseStatements, /*checkForStrictMode*/ false, parseStatement);
2824+
node.statements = parseList(ParsingContext.SwitchClauseStatements, /*checkForStrictMode*/ false, parseStatementAllowingLetDeclaration);
28222825
return finishNode(node);
28232826
}
28242827

28252828
function parseDefaultClause(): CaseOrDefaultClause {
28262829
var node = <CaseOrDefaultClause>createNode(SyntaxKind.DefaultClause);
28272830
parseExpected(SyntaxKind.DefaultKeyword);
28282831
parseExpected(SyntaxKind.ColonToken);
2829-
node.statements = parseList(ParsingContext.SwitchClauseStatements, /*checkForStrictMode*/ false, parseStatement);
2832+
node.statements = parseList(ParsingContext.SwitchClauseStatements, /*checkForStrictMode*/ false, parseStatementAllowingLetDeclaration);
28302833
return finishNode(node);
28312834
}
28322835

@@ -2933,9 +2936,9 @@ module ts {
29332936
return token === SyntaxKind.WhileKeyword || token === SyntaxKind.DoKeyword || token === SyntaxKind.ForKeyword;
29342937
}
29352938

2936-
function parseStatementWithLabelSet(): Statement {
2939+
function parseStatementWithLabelSet(allowLetDeclarations: boolean): Statement {
29372940
labelledStatementInfo.pushCurrentLabelSet(isIterationStatementStart());
2938-
var statement = parseStatement();
2941+
var statement = parseStatement(allowLetDeclarations);
29392942
labelledStatementInfo.pop();
29402943
return statement;
29412944
}
@@ -2944,7 +2947,7 @@ module ts {
29442947
return isIdentifier() && lookAhead(() => nextToken() === SyntaxKind.ColonToken);
29452948
}
29462949

2947-
function parseLabelledStatement(): LabeledStatement {
2950+
function parseLabelledStatement(allowLetDeclarations: boolean): LabeledStatement {
29482951
var node = <LabeledStatement>createNode(SyntaxKind.LabeledStatement);
29492952
node.label = parseIdentifier();
29502953
parseExpected(SyntaxKind.ColonToken);
@@ -2956,7 +2959,7 @@ module ts {
29562959

29572960
// We only want to call parseStatementWithLabelSet when the label set is complete
29582961
// Therefore, keep parsing labels until we know we're done.
2959-
node.statement = isLabel() ? parseLabelledStatement() : parseStatementWithLabelSet();
2962+
node.statement = isLabel() ? parseLabelledStatement(allowLetDeclarations) : parseStatementWithLabelSet(allowLetDeclarations);
29602963
return finishNode(node);
29612964
}
29622965

@@ -3022,14 +3025,14 @@ module ts {
30223025
}
30233026
}
30243027

3025-
function parseStatement(): Statement {
3028+
function parseStatement(allowLetDeclarations: boolean): Statement {
30263029
switch (token) {
30273030
case SyntaxKind.OpenBraceToken:
30283031
return parseBlock(/* ignoreMissingOpenBrace */ false, /*checkForStrictMode*/ false);
30293032
case SyntaxKind.VarKeyword:
30303033
case SyntaxKind.LetKeyword:
30313034
case SyntaxKind.ConstKeyword:
3032-
return parseVariableStatement();
3035+
return parseVariableStatement(allowLetDeclarations);
30333036
case SyntaxKind.FunctionKeyword:
30343037
return parseFunctionDeclaration();
30353038
case SyntaxKind.SemicolonToken:
@@ -3063,16 +3066,12 @@ module ts {
30633066
return parseDebuggerStatement();
30643067
default:
30653068
if (isLabel()) {
3066-
return parseLabelledStatement();
3069+
return parseLabelledStatement(allowLetDeclarations);
30673070
}
30683071
return parseExpressionStatement();
30693072
}
30703073
}
30713074

3072-
function parseStatementOrFunction(): Statement {
3073-
return token === SyntaxKind.FunctionKeyword ? parseFunctionDeclaration() : parseStatement();
3074-
}
3075-
30763075
function parseAndCheckFunctionBody(isConstructor: boolean): Block {
30773076
var initialPosition = scanner.getTokenPos();
30783077
var errorCountBeforeBody = file.syntacticErrors.length;
@@ -3109,7 +3108,7 @@ module ts {
31093108
grammarErrorAtPos(initializerStart, initializerFirstTokenLength, Diagnostics.Initializers_are_not_allowed_in_ambient_contexts);
31103109
}
31113110
if (!inAmbientContext && !node.initializer && flags & NodeFlags.Const) {
3112-
grammarErrorOnNode(node, Diagnostics.Const_must_be_intialized);
3111+
grammarErrorOnNode(node, Diagnostics.const_must_be_intialized);
31133112
}
31143113
if (isInStrictMode && isEvalOrArgumentsIdentifier(node.name)) {
31153114
// It is a SyntaxError if a VariableDeclaration or VariableDeclarationNoIn occurs within strict code
@@ -3124,7 +3123,7 @@ module ts {
31243123
() => parseVariableDeclaration(flags, noIn), /*allowTrailingComma*/ false);
31253124
}
31263125

3127-
function parseVariableStatement(pos?: number, flags?: NodeFlags): VariableStatement {
3126+
function parseVariableStatement(allowLetDeclarations: boolean, pos?: number, flags?: NodeFlags): VariableStatement {
31283127
var node = <VariableStatement>createNode(SyntaxKind.VariableStatement, pos);
31293128
if (flags) node.flags = flags;
31303129
var errorCountBeforeVarStatement = file.syntacticErrors.length;
@@ -3152,6 +3151,14 @@ module ts {
31523151
grammarErrorOnNode(node, Diagnostics.const_variable_declarations_are_only_available_when_targeting_ECMAScript_6_and_higher);
31533152
}
31543153
}
3154+
else if (!allowLetDeclarations){
3155+
if (node.flags & NodeFlags.Let) {
3156+
grammarErrorOnNode(node, Diagnostics.let_must_be_declared_inside_a_block);
3157+
}
3158+
else if (node.flags & NodeFlags.Const) {
3159+
grammarErrorOnNode(node, Diagnostics.const_must_be_declared_inside_a_block);
3160+
}
3161+
}
31553162
return node;
31563163
}
31573164

@@ -3784,7 +3791,7 @@ module ts {
37843791
case SyntaxKind.VarKeyword:
37853792
case SyntaxKind.LetKeyword:
37863793
case SyntaxKind.ConstKeyword:
3787-
result = parseVariableStatement(pos, flags);
3794+
result = parseVariableStatement(/*allowLetDeclarations*/ true, pos, flags);
37883795
break;
37893796
case SyntaxKind.FunctionKeyword:
37903797
result = parseFunctionDeclaration(pos, flags);
@@ -3832,7 +3839,7 @@ module ts {
38323839
var statementStart = scanner.getTokenPos();
38333840
var statementFirstTokenLength = scanner.getTextPos() - statementStart;
38343841
var errorCountBeforeStatement = file.syntacticErrors.length;
3835-
var statement = parseStatement();
3842+
var statement = parseStatement(/*allowLetDeclarations*/ false);
38363843

38373844
if (inAmbientContext && file.syntacticErrors.length === errorCountBeforeStatement) {
38383845
grammarErrorAtPos(statementStart, statementFirstTokenLength, Diagnostics.Statements_are_not_allowed_in_ambient_contexts);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
tests/cases/compiler/constDeclarations-invalidContexts.ts(4,5): error TS1156: const must be declared inside a block.
2+
tests/cases/compiler/constDeclarations-invalidContexts.ts(6,5): error TS1156: const must be declared inside a block.
3+
tests/cases/compiler/constDeclarations-invalidContexts.ts(9,5): error TS1156: const must be declared inside a block.
4+
tests/cases/compiler/constDeclarations-invalidContexts.ts(12,5): error TS1156: const must be declared inside a block.
5+
tests/cases/compiler/constDeclarations-invalidContexts.ts(17,5): error TS1156: const must be declared inside a block.
6+
tests/cases/compiler/constDeclarations-invalidContexts.ts(20,5): error TS1156: const must be declared inside a block.
7+
tests/cases/compiler/constDeclarations-invalidContexts.ts(23,5): error TS1156: const must be declared inside a block.
8+
tests/cases/compiler/constDeclarations-invalidContexts.ts(26,12): error TS1156: const must be declared inside a block.
9+
tests/cases/compiler/constDeclarations-invalidContexts.ts(29,29): error TS1156: const must be declared inside a block.
10+
tests/cases/compiler/constDeclarations-invalidContexts.ts(16,7): error TS2410: All symbols within a 'with' block will be resolved to 'any'.
11+
12+
13+
==== tests/cases/compiler/constDeclarations-invalidContexts.ts (10 errors) ====
14+
15+
// Errors, const must be defined inside a block
16+
if (true)
17+
const c1 = 0;
18+
~~~~~~~~~~~~~
19+
!!! error TS1156: const must be declared inside a block.
20+
else
21+
const c2 = 0;
22+
~~~~~~~~~~~~~
23+
!!! error TS1156: const must be declared inside a block.
24+
25+
while (true)
26+
const c3 = 0;
27+
~~~~~~~~~~~~~
28+
!!! error TS1156: const must be declared inside a block.
29+
30+
do
31+
const c4 = 0;
32+
~~~~~~~~~~~~~
33+
!!! error TS1156: const must be declared inside a block.
34+
while (true);
35+
36+
var obj;
37+
with (obj)
38+
~~~
39+
!!! error TS2410: All symbols within a 'with' block will be resolved to 'any'.
40+
const c5 = 0;
41+
~~~~~~~~~~~~~
42+
!!! error TS1156: const must be declared inside a block.
43+
44+
for (var i = 0; i < 10; i++)
45+
const c6 = 0;
46+
~~~~~~~~~~~~~
47+
!!! error TS1156: const must be declared inside a block.
48+
49+
for (var i2 in {})
50+
const c7 = 0;
51+
~~~~~~~~~~~~~
52+
!!! error TS1156: const must be declared inside a block.
53+
54+
if (true)
55+
label: const c8 = 0;
56+
~~~~~~~~~~~~~
57+
!!! error TS1156: const must be declared inside a block.
58+
59+
while (false)
60+
label2: label3: label4: const c9 = 0;
61+
~~~~~~~~~~~~~
62+
!!! error TS1156: const must be declared inside a block.
63+
64+
65+
66+

0 commit comments

Comments
 (0)