-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Let and const support #904
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit
Hold shift + click to select a range
873c1df
Add es6 target
mhegazy 778f101
Add basic parsing support for let and const
mhegazy 979d45e
Disallow let and const declarations outside blocks
mhegazy 6f6f4af
Fix line endings
mhegazy cf89f5c
Add binder support for block scoped variable declarations
mhegazy 1dde985
Do not allow use of block-scoped variable before its definition
mhegazy 318575c
Ensure duplicate let/const declarations accross files are reported
mhegazy cffc62a
Report duplicate identifier errors on all locations for merged declar…
mhegazy f5c2740
Flag assignments to a const
mhegazy 82f5fb4
Flag const declarations shodowed by var redeclarations
mhegazy 3e45601
Allow const in for statements
mhegazy 03a100d
Do not allow let and const declarations to be exported from a module
mhegazy 6154923
Fix emitting for const in for loops
mhegazy e15f4e6
Merge branch 'master' into letAndConst
mhegazy 60bb37b
Add language service support for const
mhegazy fd469d6
Fix search for shadowed const declarations by a var declarations to s…
mhegazy 4ef68b9
Respond to code review comments
mhegazy a5a6c6f
Allow const and let declarations to be exported in modules. Also ensu…
mhegazy 0a59cdd
Treat blockScoped variable declarations as a separate category when i…
mhegazy 0e7d8b6
Merge branch 'master' into letAndConst
mhegazy dd5c89d
Update error messages
mhegazy 91f4098
Simplify the binder logic for managing blockScopeContainer
mhegazy d5fe43b
allow let and const declarations in module bodies under labels
mhegazy dd7ca69
Create a new flag for diagnostics 'isEarly' and disable emit if this …
mhegazy 373dc76
respond to code review comments
mhegazy 9353c11
Merge branch 'master' into letAndConst
mhegazy d1858d0
Merge branch 'master' into letAndConst
mhegazy e4a2084
Ensure let and const declarations in labels are parsed correctelly
mhegazy 67c78a2
Only check for collisions with variabels and not properties
mhegazy 51e101c
Merge branch 'master' into letAndConst
mhegazy File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Disallow let and const declarations outside blocks
- Loading branch information
commit 979d45eb670ebc1e68f4029e1d055c28c73430f6
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 { | ||
|
|
@@ -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); | ||
| } | ||
|
|
||
|
|
@@ -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); | ||
|
|
@@ -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); | ||
|
|
@@ -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); | ||
|
|
@@ -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 | ||
|
|
@@ -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); | ||
| 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); | ||
| } | ||
|
|
||
|
|
@@ -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; | ||
| } | ||
|
|
@@ -2944,7 +2947,7 @@ module ts { | |
| return isIdentifier() && lookAhead(() => nextToken() === SyntaxKind.ColonToken); | ||
| } | ||
|
|
||
| function parseLabelledStatement(): LabeledStatement { | ||
| function parseLabelledStatement(allowLetDeclarations: boolean): LabeledStatement { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. parseLabeledStatement
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
|
|
@@ -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); | ||
| } | ||
|
|
||
|
|
@@ -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: | ||
|
|
@@ -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; | ||
|
|
@@ -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 | ||
|
|
@@ -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; | ||
|
|
@@ -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; | ||
| } | ||
|
|
||
|
|
@@ -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); | ||
|
|
@@ -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); | ||
|
|
||
66 changes: 66 additions & 0 deletions
66
tests/baselines/reference/constDeclarations-invalidContexts.errors.txt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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. | ||
|
|
||
|
|
||
|
|
||
|
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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:
I will follow up to see if this is intentional or just an error in the spec and update this accordingly.
There was a problem hiding this comment.
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".
There was a problem hiding this comment.
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