Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
873c1df
Add es6 target
mhegazy Oct 11, 2014
778f101
Add basic parsing support for let and const
mhegazy Oct 13, 2014
979d45e
Disallow let and const declarations outside blocks
mhegazy Oct 13, 2014
6f6f4af
Fix line endings
mhegazy Oct 13, 2014
cf89f5c
Add binder support for block scoped variable declarations
mhegazy Oct 14, 2014
1dde985
Do not allow use of block-scoped variable before its definition
mhegazy Oct 14, 2014
318575c
Ensure duplicate let/const declarations accross files are reported
mhegazy Oct 14, 2014
cffc62a
Report duplicate identifier errors on all locations for merged declar…
mhegazy Oct 14, 2014
f5c2740
Flag assignments to a const
mhegazy Oct 14, 2014
82f5fb4
Flag const declarations shodowed by var redeclarations
mhegazy Oct 15, 2014
3e45601
Allow const in for statements
mhegazy Oct 15, 2014
03a100d
Do not allow let and const declarations to be exported from a module
mhegazy Oct 15, 2014
6154923
Fix emitting for const in for loops
mhegazy Oct 15, 2014
e15f4e6
Merge branch 'master' into letAndConst
mhegazy Oct 16, 2014
60bb37b
Add language service support for const
mhegazy Oct 16, 2014
fd469d6
Fix search for shadowed const declarations by a var declarations to s…
mhegazy Oct 17, 2014
4ef68b9
Respond to code review comments
mhegazy Oct 17, 2014
a5a6c6f
Allow const and let declarations to be exported in modules. Also ensu…
mhegazy Oct 17, 2014
0a59cdd
Treat blockScoped variable declarations as a separate category when i…
mhegazy Oct 17, 2014
0e7d8b6
Merge branch 'master' into letAndConst
mhegazy Oct 20, 2014
dd5c89d
Update error messages
mhegazy Oct 20, 2014
91f4098
Simplify the binder logic for managing blockScopeContainer
mhegazy Oct 20, 2014
d5fe43b
allow let and const declarations in module bodies under labels
mhegazy Oct 20, 2014
dd7ca69
Create a new flag for diagnostics 'isEarly' and disable emit if this …
mhegazy Oct 21, 2014
373dc76
respond to code review comments
mhegazy Oct 21, 2014
9353c11
Merge branch 'master' into letAndConst
mhegazy Oct 23, 2014
d1858d0
Merge branch 'master' into letAndConst
mhegazy Oct 23, 2014
e4a2084
Ensure let and const declarations in labels are parsed correctelly
mhegazy Oct 24, 2014
67c78a2
Only check for collisions with variabels and not properties
mhegazy Oct 24, 2014
51e101c
Merge branch 'master' into letAndConst
mhegazy Oct 24, 2014
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Disallow let and const declarations outside blocks
  • Loading branch information
mhegazy committed Oct 13, 2014
commit 979d45eb670ebc1e68f4029e1d055c28c73430f6
4 changes: 3 additions & 1 deletion src/compiler/diagnosticInformationMap.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,9 @@ module ts {
var_let_or_const_expected: { code: 1152, category: DiagnosticCategory.Error, key: "'var', 'let' or 'const' expected." },
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." },
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." },
Const_must_be_intialized: { code: 1155, category: DiagnosticCategory.Error, key: "Const must be intialized." },
const_must_be_intialized: { code: 1155, category: DiagnosticCategory.Error, key: "const must be intialized." },
const_must_be_declared_inside_a_block: { code: 1156, category: DiagnosticCategory.Error, key: "const must be declared inside a block." },
let_must_be_declared_inside_a_block: { code: 1157, category: DiagnosticCategory.Error, key: "let must be declared inside a block." },
Duplicate_identifier_0: { code: 2300, category: DiagnosticCategory.Error, key: "Duplicate identifier '{0}'." },
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." },
Static_members_cannot_reference_class_type_parameters: { code: 2302, category: DiagnosticCategory.Error, key: "Static members cannot reference class type parameters." },
Expand Down
10 changes: 9 additions & 1 deletion src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -463,10 +463,18 @@
"category": "Error",
"code": 1154
},
"Const must be intialized.": {
"const must be intialized.": {
"category": "Error",
"code": 1155
},
"const must be declared inside a block.": {
"category": "Error",
"code": 1156
},
"let must be declared inside a block.": {
"category": "Error",
"code": 1157
},

"Duplicate identifier '{0}'.": {
"category": "Error",
Expand Down
55 changes: 31 additions & 24 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2542,11 +2542,14 @@ module ts {
}

// STATEMENTS
function parseStatementAllowingLetDeclaration() {
return parseStatement(/*allowLetDeclarations*/ true);
}

function parseBlock(ignoreMissingOpenBrace: boolean, checkForStrictMode: boolean): Block {
var node = <Block>createNode(SyntaxKind.Block);
if (parseExpected(SyntaxKind.OpenBraceToken) || ignoreMissingOpenBrace) {
node.statements = parseList(ParsingContext.BlockStatements,checkForStrictMode, parseStatement);
node.statements = parseList(ParsingContext.BlockStatements, checkForStrictMode, parseStatementAllowingLetDeclaration);
parseExpected(SyntaxKind.CloseBraceToken);
}
else {
Expand Down Expand Up @@ -2592,8 +2595,8 @@ module ts {
parseExpected(SyntaxKind.OpenParenToken);
node.expression = parseExpression();
parseExpected(SyntaxKind.CloseParenToken);
node.thenStatement = parseStatement();
node.elseStatement = parseOptional(SyntaxKind.ElseKeyword) ? parseStatement() : undefined;
node.thenStatement = parseStatement(/*allowLetDeclarations*/ false);
node.elseStatement = parseOptional(SyntaxKind.ElseKeyword) ? parseStatement(/*allowLetDeclarations*/ false) : undefined;
return finishNode(node);
}

Expand All @@ -2603,7 +2606,7 @@ module ts {

var saveInIterationStatement = inIterationStatement;
inIterationStatement = ControlBlockContext.Nested;
node.statement = parseStatement();
node.statement = parseStatement(/*allowLetDeclarations*/ false);
inIterationStatement = saveInIterationStatement;

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

var saveInIterationStatement = inIterationStatement;
inIterationStatement = ControlBlockContext.Nested;
node.statement = parseStatement();
node.statement = parseStatement(/*allowLetDeclarations*/ false);
inIterationStatement = saveInIterationStatement;

return finishNode(node);
Expand Down Expand Up @@ -2692,7 +2695,7 @@ module ts {

var saveInIterationStatement = inIterationStatement;
inIterationStatement = ControlBlockContext.Nested;
forOrForInStatement.statement = parseStatement();
forOrForInStatement.statement = parseStatement(/*allowLetDeclarations*/ false);
inIterationStatement = saveInIterationStatement;

return finishNode(forOrForInStatement);
Expand Down Expand Up @@ -2803,7 +2806,7 @@ module ts {
parseExpected(SyntaxKind.OpenParenToken);
node.expression = parseExpression();
parseExpected(SyntaxKind.CloseParenToken);
node.statement = parseStatement();
node.statement = parseStatement(/*allowLetDeclarations*/ false);
node = finishNode(node);
if (isInStrictMode) {
// Strict mode code may not include a WithStatement. The occurrence of a WithStatement in such
Expand All @@ -2818,15 +2821,15 @@ module ts {
parseExpected(SyntaxKind.CaseKeyword);
node.expression = parseExpression();
parseExpected(SyntaxKind.ColonToken);
node.statements = parseList(ParsingContext.SwitchClauseStatements, /*checkForStrictMode*/ false, parseStatement);
node.statements = parseList(ParsingContext.SwitchClauseStatements, /*checkForStrictMode*/ false, parseStatementAllowingLetDeclaration);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i thought we would not allow 'let' here as we're not in a block scope. or do you check for that later?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think you are correct. the ES6 spec makes it clear that lexical scopes do not include switch, try or finally blocks, section 8.1:

A Lexical Environment is a specification type used to define the association of Identifiers to specific variables and functions based upon the lexical nesting structure of ECMAScript code. A Lexical Environment consists of an Environment Record and a possibly null reference to an outer Lexical Environment. Usually a Lexical Environment is associated with some specific syntactic structure of ECMAScript code such as a FunctionDeclaration, a BlockStatement, or a Catch clause of a TryStatement and a new Lexical Environment is created each time such code is evaluated.

I will follow up to see if this is intentional or just an error in the spec and update this accordingly.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They say "such as", which I normally interpret as "including but not limited to".

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah. checked with @bterlson and he confirmed that that is not the intention. so keeping Switch, try and finally blocks as valid lexical scopes

return finishNode(node);
}

function parseDefaultClause(): CaseOrDefaultClause {
var node = <CaseOrDefaultClause>createNode(SyntaxKind.DefaultClause);
parseExpected(SyntaxKind.DefaultKeyword);
parseExpected(SyntaxKind.ColonToken);
node.statements = parseList(ParsingContext.SwitchClauseStatements, /*checkForStrictMode*/ false, parseStatement);
node.statements = parseList(ParsingContext.SwitchClauseStatements, /*checkForStrictMode*/ false, parseStatementAllowingLetDeclaration);
return finishNode(node);
}

Expand Down Expand Up @@ -2933,9 +2936,9 @@ module ts {
return token === SyntaxKind.WhileKeyword || token === SyntaxKind.DoKeyword || token === SyntaxKind.ForKeyword;
}

function parseStatementWithLabelSet(): Statement {
function parseStatementWithLabelSet(allowLetDeclarations: boolean): Statement {
labelledStatementInfo.pushCurrentLabelSet(isIterationStatementStart());
var statement = parseStatement();
var statement = parseStatement(allowLetDeclarations);
labelledStatementInfo.pop();
return statement;
}
Expand All @@ -2944,7 +2947,7 @@ module ts {
return isIdentifier() && lookAhead(() => nextToken() === SyntaxKind.ColonToken);
}

function parseLabelledStatement(): LabeledStatement {
function parseLabelledStatement(allowLetDeclarations: boolean): LabeledStatement {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parseLabeledStatement

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done, though it is still correct :)

var node = <LabeledStatement>createNode(SyntaxKind.LabeledStatement);
node.label = parseIdentifier();
parseExpected(SyntaxKind.ColonToken);
Expand All @@ -2956,7 +2959,7 @@ module ts {

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

Expand Down Expand Up @@ -3022,14 +3025,14 @@ module ts {
}
}

function parseStatement(): Statement {
function parseStatement(allowLetDeclarations: boolean): Statement {
switch (token) {
case SyntaxKind.OpenBraceToken:
return parseBlock(/* ignoreMissingOpenBrace */ false, /*checkForStrictMode*/ false);
case SyntaxKind.VarKeyword:
case SyntaxKind.LetKeyword:
case SyntaxKind.ConstKeyword:
return parseVariableStatement();
return parseVariableStatement(allowLetDeclarations);
case SyntaxKind.FunctionKeyword:
return parseFunctionDeclaration();
case SyntaxKind.SemicolonToken:
Expand Down Expand Up @@ -3063,16 +3066,12 @@ module ts {
return parseDebuggerStatement();
default:
if (isLabel()) {
return parseLabelledStatement();
return parseLabelledStatement(allowLetDeclarations);
}
return parseExpressionStatement();
}
}

function parseStatementOrFunction(): Statement {
return token === SyntaxKind.FunctionKeyword ? parseFunctionDeclaration() : parseStatement();
}

function parseAndCheckFunctionBody(isConstructor: boolean): Block {
var initialPosition = scanner.getTokenPos();
var errorCountBeforeBody = file.syntacticErrors.length;
Expand Down Expand Up @@ -3109,7 +3108,7 @@ module ts {
grammarErrorAtPos(initializerStart, initializerFirstTokenLength, Diagnostics.Initializers_are_not_allowed_in_ambient_contexts);
}
if (!inAmbientContext && !node.initializer && flags & NodeFlags.Const) {
grammarErrorOnNode(node, Diagnostics.Const_must_be_intialized);
grammarErrorOnNode(node, Diagnostics.const_must_be_intialized);
}
if (isInStrictMode && isEvalOrArgumentsIdentifier(node.name)) {
// It is a SyntaxError if a VariableDeclaration or VariableDeclarationNoIn occurs within strict code
Expand All @@ -3124,7 +3123,7 @@ module ts {
() => parseVariableDeclaration(flags, noIn), /*allowTrailingComma*/ false);
}

function parseVariableStatement(pos?: number, flags?: NodeFlags): VariableStatement {
function parseVariableStatement(allowLetDeclarations: boolean, pos?: number, flags?: NodeFlags): VariableStatement {
var node = <VariableStatement>createNode(SyntaxKind.VariableStatement, pos);
if (flags) node.flags = flags;
var errorCountBeforeVarStatement = file.syntacticErrors.length;
Expand Down Expand Up @@ -3152,6 +3151,14 @@ module ts {
grammarErrorOnNode(node, Diagnostics.const_variable_declarations_are_only_available_when_targeting_ECMAScript_6_and_higher);
}
}
else if (!allowLetDeclarations){
if (node.flags & NodeFlags.Let) {
grammarErrorOnNode(node, Diagnostics.let_must_be_declared_inside_a_block);
}
else if (node.flags & NodeFlags.Const) {
grammarErrorOnNode(node, Diagnostics.const_must_be_declared_inside_a_block);
}
}
return node;
}

Expand Down Expand Up @@ -3784,7 +3791,7 @@ module ts {
case SyntaxKind.VarKeyword:
case SyntaxKind.LetKeyword:
case SyntaxKind.ConstKeyword:
result = parseVariableStatement(pos, flags);
result = parseVariableStatement(/*allowLetDeclarations*/ true, pos, flags);
break;
case SyntaxKind.FunctionKeyword:
result = parseFunctionDeclaration(pos, flags);
Expand Down Expand Up @@ -3832,7 +3839,7 @@ module ts {
var statementStart = scanner.getTokenPos();
var statementFirstTokenLength = scanner.getTextPos() - statementStart;
var errorCountBeforeStatement = file.syntacticErrors.length;
var statement = parseStatement();
var statement = parseStatement(/*allowLetDeclarations*/ false);

if (inAmbientContext && file.syntacticErrors.length === errorCountBeforeStatement) {
grammarErrorAtPos(statementStart, statementFirstTokenLength, Diagnostics.Statements_are_not_allowed_in_ambient_contexts);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
tests/cases/compiler/constDeclarations-invalidContexts.ts(4,5): error TS1156: const must be declared inside a block.
tests/cases/compiler/constDeclarations-invalidContexts.ts(6,5): error TS1156: const must be declared inside a block.
tests/cases/compiler/constDeclarations-invalidContexts.ts(9,5): error TS1156: const must be declared inside a block.
tests/cases/compiler/constDeclarations-invalidContexts.ts(12,5): error TS1156: const must be declared inside a block.
tests/cases/compiler/constDeclarations-invalidContexts.ts(17,5): error TS1156: const must be declared inside a block.
tests/cases/compiler/constDeclarations-invalidContexts.ts(20,5): error TS1156: const must be declared inside a block.
tests/cases/compiler/constDeclarations-invalidContexts.ts(23,5): error TS1156: const must be declared inside a block.
tests/cases/compiler/constDeclarations-invalidContexts.ts(26,12): error TS1156: const must be declared inside a block.
tests/cases/compiler/constDeclarations-invalidContexts.ts(29,29): error TS1156: const must be declared inside a block.
tests/cases/compiler/constDeclarations-invalidContexts.ts(16,7): error TS2410: All symbols within a 'with' block will be resolved to 'any'.


==== tests/cases/compiler/constDeclarations-invalidContexts.ts (10 errors) ====

// Errors, const must be defined inside a block
if (true)
const c1 = 0;
~~~~~~~~~~~~~
!!! error TS1156: const must be declared inside a block.
else
const c2 = 0;
~~~~~~~~~~~~~
!!! error TS1156: const must be declared inside a block.

while (true)
const c3 = 0;
~~~~~~~~~~~~~
!!! error TS1156: const must be declared inside a block.

do
const c4 = 0;
~~~~~~~~~~~~~
!!! error TS1156: const must be declared inside a block.
while (true);

var obj;
with (obj)
~~~
!!! error TS2410: All symbols within a 'with' block will be resolved to 'any'.
const c5 = 0;
~~~~~~~~~~~~~
!!! error TS1156: const must be declared inside a block.

for (var i = 0; i < 10; i++)
const c6 = 0;
~~~~~~~~~~~~~
!!! error TS1156: const must be declared inside a block.

for (var i2 in {})
const c7 = 0;
~~~~~~~~~~~~~
!!! error TS1156: const must be declared inside a block.

if (true)
label: const c8 = 0;
~~~~~~~~~~~~~
!!! error TS1156: const must be declared inside a block.

while (false)
label2: label3: label4: const c9 = 0;
~~~~~~~~~~~~~
!!! error TS1156: const must be declared inside a block.




Loading