Skip to content

Commit cf89f5c

Browse files
committed
Add binder support for block scoped variable declarations
1 parent 6f6f4af commit cf89f5c

15 files changed

+1766
-40
lines changed

src/compiler/binder.ts

+80-32
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,14 @@ module ts {
3131

3232
var parent: Node;
3333
var container: Declaration;
34+
var blockScopeContainer: Node;
3435
var lastContainer: Declaration;
3536
var symbolCount = 0;
3637
var Symbol = objectAllocator.getSymbolConstructor();
3738

3839
if (!file.locals) {
3940
file.locals = {};
40-
container = file;
41+
container = blockScopeContainer = file;
4142
bind(file);
4243
file.symbolCount = symbolCount;
4344
}
@@ -167,12 +168,13 @@ module ts {
167168

168169
// All container nodes are kept on a linked list in declaration order. This list is used by the getLocalNameOfContainer function
169170
// in the type checker to validate that the local name used for a container is unique.
170-
function bindChildren(node: Declaration, symbolKind: SymbolFlags) {
171+
function bindChildren(node: Declaration, symbolKind: SymbolFlags, isBlockScopeContainer: boolean) {
171172
if (symbolKind & SymbolFlags.HasLocals) {
172173
node.locals = {};
173174
}
174175
var saveParent = parent;
175176
var saveContainer = container;
177+
var savedBlockScopeContainer = blockScopeContainer;
176178
parent = node;
177179
if (symbolKind & SymbolFlags.IsContainer) {
178180
container = node;
@@ -184,12 +186,16 @@ module ts {
184186
lastContainer = container;
185187
}
186188
}
189+
if (isBlockScopeContainer) {
190+
blockScopeContainer = node;
191+
}
187192
forEachChild(node, bind);
188193
container = saveContainer;
189194
parent = saveParent;
195+
blockScopeContainer = savedBlockScopeContainer;
190196
}
191197

192-
function bindDeclaration(node: Declaration, symbolKind: SymbolFlags, symbolExcludes: SymbolFlags) {
198+
function bindDeclaration(node: Declaration, symbolKind: SymbolFlags, symbolExcludes: SymbolFlags, isBlockScopeContainer: boolean) {
193199
switch (container.kind) {
194200
case SyntaxKind.ModuleDeclaration:
195201
declareModuleMember(node, symbolKind, symbolExcludes);
@@ -225,126 +231,168 @@ module ts {
225231
declareSymbol(container.symbol.exports, container.symbol, node, symbolKind, symbolExcludes);
226232
break;
227233
}
228-
bindChildren(node, symbolKind);
234+
bindChildren(node, symbolKind, isBlockScopeContainer);
229235
}
230236

231237
function bindConstructorDeclaration(node: ConstructorDeclaration) {
232-
bindDeclaration(node, SymbolFlags.Constructor, 0);
238+
bindDeclaration(node, SymbolFlags.Constructor, 0, /*isBlockScopeContainer*/ true);
233239
forEach(node.parameters, p => {
234240
if (p.flags & (NodeFlags.Public | NodeFlags.Private | NodeFlags.Protected)) {
235-
bindDeclaration(p, SymbolFlags.Property, SymbolFlags.PropertyExcludes);
241+
bindDeclaration(p, SymbolFlags.Property, SymbolFlags.PropertyExcludes, /*isBlockScopeContainer*/ false);
236242
}
237243
});
238244
}
239245

240246
function bindModuleDeclaration(node: ModuleDeclaration) {
241247
if (node.name.kind === SyntaxKind.StringLiteral) {
242-
bindDeclaration(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes);
248+
bindDeclaration(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes, /*isBlockScopeContainer*/ true);
243249
}
244250
else if (isInstantiated(node)) {
245-
bindDeclaration(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes);
251+
bindDeclaration(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes, /*isBlockScopeContainer*/ true);
246252
}
247253
else {
248-
bindDeclaration(node, SymbolFlags.NamespaceModule, SymbolFlags.NamespaceModuleExcludes);
254+
bindDeclaration(node, SymbolFlags.NamespaceModule, SymbolFlags.NamespaceModuleExcludes, /*isBlockScopeContainer*/ true);
249255
}
250256
}
251257

252-
function bindAnonymousDeclaration(node: Node, symbolKind: SymbolFlags, name: string) {
258+
function bindAnonymousDeclaration(node: Node, symbolKind: SymbolFlags, name: string, isBlockScopeContainer: boolean) {
253259
var symbol = createSymbol(symbolKind, name);
254260
addDeclarationToSymbol(symbol, node, symbolKind);
255-
bindChildren(node, symbolKind);
261+
bindChildren(node, symbolKind, isBlockScopeContainer);
256262
}
257263

258264
function bindCatchVariableDeclaration(node: CatchBlock) {
259265
var symbol = createSymbol(SymbolFlags.Variable, node.variable.text || "__missing");
260266
addDeclarationToSymbol(symbol, node, SymbolFlags.Variable);
261267
var saveParent = parent;
262-
parent = node;
268+
var savedBlockScopeContainer = blockScopeContainer;
269+
parent = blockScopeContainer = node;
263270
forEachChild(node, bind);
264271
parent = saveParent;
272+
blockScopeContainer = savedBlockScopeContainer;
273+
}
274+
275+
function bindBlockScopedVariableDeclaration(node: Declaration) {
276+
var symbolKind = SymbolFlags.Variable | SymbolFlags.BlockScoped;
277+
switch (blockScopeContainer.kind) {
278+
case SyntaxKind.ModuleDeclaration:
279+
declareModuleMember(node, symbolKind, SymbolFlags.BlockScopedExcludes);
280+
break;
281+
case SyntaxKind.SourceFile:
282+
if (isExternalModule(<SourceFile>container)) {
283+
declareModuleMember(node, symbolKind, SymbolFlags.BlockScopedExcludes);
284+
break;
285+
}
286+
default:
287+
if (!blockScopeContainer.locals) {
288+
blockScopeContainer.locals = {};
289+
}
290+
declareSymbol(blockScopeContainer.locals, undefined, node, symbolKind, SymbolFlags.BlockScopedExcludes);
291+
}
292+
293+
bindChildren(node, symbolKind, /*isBlockScopeContainer*/ false);
265294
}
266295

267296
function bind(node: Node) {
297+
var isBlockScopeContainer: boolean;
268298
node.parent = parent;
269299
switch (node.kind) {
270300
case SyntaxKind.TypeParameter:
271-
bindDeclaration(<Declaration>node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes);
301+
bindDeclaration(<Declaration>node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes, /*isBlockScopeContainer*/ false);
272302
break;
273303
case SyntaxKind.Parameter:
274-
bindDeclaration(<Declaration>node, SymbolFlags.Variable, SymbolFlags.ParameterExcludes);
304+
bindDeclaration(<Declaration>node, SymbolFlags.Variable, SymbolFlags.ParameterExcludes, /*isBlockScopeContainer*/ false);
275305
break;
276306
case SyntaxKind.VariableDeclaration:
277-
bindDeclaration(<Declaration>node, SymbolFlags.Variable, SymbolFlags.VariableExcludes);
307+
if (node.flags & NodeFlags.BlockScoped) {
308+
bindBlockScopedVariableDeclaration(<Declaration>node);
309+
}
310+
else {
311+
bindDeclaration(<Declaration>node, SymbolFlags.Variable, SymbolFlags.VariableExcludes, /*isBlockScopeContainer*/ false);
312+
}
278313
break;
279314
case SyntaxKind.Property:
280315
case SyntaxKind.PropertyAssignment:
281-
bindDeclaration(<Declaration>node, SymbolFlags.Property, SymbolFlags.PropertyExcludes);
316+
bindDeclaration(<Declaration>node, SymbolFlags.Property, SymbolFlags.PropertyExcludes, /*isBlockScopeContainer*/ false);
282317
break;
283318
case SyntaxKind.EnumMember:
284-
bindDeclaration(<Declaration>node, SymbolFlags.EnumMember, SymbolFlags.EnumMemberExcludes);
319+
bindDeclaration(<Declaration>node, SymbolFlags.EnumMember, SymbolFlags.EnumMemberExcludes, /*isBlockScopeContainer*/ false);
285320
break;
286321
case SyntaxKind.CallSignature:
287-
bindDeclaration(<Declaration>node, SymbolFlags.CallSignature, 0);
322+
bindDeclaration(<Declaration>node, SymbolFlags.CallSignature, 0, /*isBlockScopeContainer*/ false);
288323
break;
289324
case SyntaxKind.Method:
290-
bindDeclaration(<Declaration>node, SymbolFlags.Method, SymbolFlags.MethodExcludes);
325+
bindDeclaration(<Declaration>node, SymbolFlags.Method, SymbolFlags.MethodExcludes, /*isBlockScopeContainer*/ true);
291326
break;
292327
case SyntaxKind.ConstructSignature:
293-
bindDeclaration(<Declaration>node, SymbolFlags.ConstructSignature, 0);
328+
bindDeclaration(<Declaration>node, SymbolFlags.ConstructSignature, 0, /*isBlockScopeContainer*/ true);
294329
break;
295330
case SyntaxKind.IndexSignature:
296-
bindDeclaration(<Declaration>node, SymbolFlags.IndexSignature, 0);
331+
bindDeclaration(<Declaration>node, SymbolFlags.IndexSignature, 0, /*isBlockScopeContainer*/ false);
297332
break;
298333
case SyntaxKind.FunctionDeclaration:
299-
bindDeclaration(<Declaration>node, SymbolFlags.Function, SymbolFlags.FunctionExcludes);
334+
bindDeclaration(<Declaration>node, SymbolFlags.Function, SymbolFlags.FunctionExcludes, /*isBlockScopeContainer*/ true);
300335
break;
301336
case SyntaxKind.Constructor:
302337
bindConstructorDeclaration(<ConstructorDeclaration>node);
303338
break;
304339
case SyntaxKind.GetAccessor:
305-
bindDeclaration(<Declaration>node, SymbolFlags.GetAccessor, SymbolFlags.GetAccessorExcludes);
340+
bindDeclaration(<Declaration>node, SymbolFlags.GetAccessor, SymbolFlags.GetAccessorExcludes, /*isBlockScopeContainer*/ true);
306341
break;
307342
case SyntaxKind.SetAccessor:
308-
bindDeclaration(<Declaration>node, SymbolFlags.SetAccessor, SymbolFlags.SetAccessorExcludes);
343+
bindDeclaration(<Declaration>node, SymbolFlags.SetAccessor, SymbolFlags.SetAccessorExcludes, /*isBlockScopeContainer*/ true);
309344
break;
310345
case SyntaxKind.TypeLiteral:
311-
bindAnonymousDeclaration(node, SymbolFlags.TypeLiteral, "__type");
346+
bindAnonymousDeclaration(node, SymbolFlags.TypeLiteral, "__type", /*isBlockScopeContainer*/ false);
312347
break;
313348
case SyntaxKind.ObjectLiteral:
314-
bindAnonymousDeclaration(node, SymbolFlags.ObjectLiteral, "__object");
349+
bindAnonymousDeclaration(node, SymbolFlags.ObjectLiteral, "__object", /*isBlockScopeContainer*/ false);
315350
break;
316351
case SyntaxKind.FunctionExpression:
317352
case SyntaxKind.ArrowFunction:
318-
bindAnonymousDeclaration(node, SymbolFlags.Function, "__function");
353+
bindAnonymousDeclaration(node, SymbolFlags.Function, "__function", /*isBlockScopeContainer*/ true);
319354
break;
320355
case SyntaxKind.CatchBlock:
321356
bindCatchVariableDeclaration(<CatchBlock>node);
322357
break;
323358
case SyntaxKind.ClassDeclaration:
324-
bindDeclaration(<Declaration>node, SymbolFlags.Class, SymbolFlags.ClassExcludes);
359+
bindDeclaration(<Declaration>node, SymbolFlags.Class, SymbolFlags.ClassExcludes, /*isBlockScopeContainer*/ false);
325360
break;
326361
case SyntaxKind.InterfaceDeclaration:
327-
bindDeclaration(<Declaration>node, SymbolFlags.Interface, SymbolFlags.InterfaceExcludes);
362+
bindDeclaration(<Declaration>node, SymbolFlags.Interface, SymbolFlags.InterfaceExcludes, /*isBlockScopeContainer*/ false);
328363
break;
329364
case SyntaxKind.EnumDeclaration:
330-
bindDeclaration(<Declaration>node, SymbolFlags.Enum, SymbolFlags.EnumExcludes);
365+
bindDeclaration(<Declaration>node, SymbolFlags.Enum, SymbolFlags.EnumExcludes, /*isBlockScopeContainer*/ false);
331366
break;
332367
case SyntaxKind.ModuleDeclaration:
333368
bindModuleDeclaration(<ModuleDeclaration>node);
334369
break;
335370
case SyntaxKind.ImportDeclaration:
336-
bindDeclaration(<Declaration>node, SymbolFlags.Import, SymbolFlags.ImportExcludes);
371+
bindDeclaration(<Declaration>node, SymbolFlags.Import, SymbolFlags.ImportExcludes, /*isBlockScopeContainer*/ false);
337372
break;
338373
case SyntaxKind.SourceFile:
339374
if (isExternalModule(<SourceFile>node)) {
340-
bindAnonymousDeclaration(node, SymbolFlags.ValueModule, '"' + removeFileExtension((<SourceFile>node).filename) + '"');
375+
bindAnonymousDeclaration(node, SymbolFlags.ValueModule, '"' + removeFileExtension((<SourceFile>node).filename) + '"', /*isBlockScopeContainer*/ true);
341376
break;
342377
}
378+
379+
case SyntaxKind.Block:
380+
case SyntaxKind.TryBlock:
381+
case SyntaxKind.CatchBlock:
382+
case SyntaxKind.FinallyBlock:
383+
case SyntaxKind.ForStatement:
384+
case SyntaxKind.ForInStatement:
385+
case SyntaxKind.SwitchStatement:
386+
isBlockScopeContainer = true;
387+
343388
default:
344389
var saveParent = parent;
390+
var savedBlockScopeContainer = blockScopeContainer;
345391
parent = node;
392+
if (isBlockScopeContainer) blockScopeContainer = node;
346393
forEachChild(node, bind);
347394
parent = saveParent;
395+
blockScopeContainer = savedBlockScopeContainer;
348396
}
349397
}
350398
}

src/compiler/types.ts

+8-4
Original file line numberDiff line numberDiff line change
@@ -245,11 +245,12 @@ module ts {
245245
MultiLine = 0x00000100, // Multi-line array or object literal
246246
Synthetic = 0x00000200, // Synthetic node (for full fidelity)
247247
DeclarationFile = 0x00000400, // Node is a .d.ts file
248-
Let = 0x00000800,
249-
Const = 0x00001000,
248+
Let = 0x00000800, // Variable declaration
249+
Const = 0x00001000, // Variable declaration
250250

251251
Modifier = Export | Ambient | Public | Private | Protected | Static,
252-
AccessibilityModifier = Public | Private | Protected
252+
AccessibilityModifier = Public | Private | Protected,
253+
BlockScoped = Let | Const
253254
}
254255

255256
export interface Node extends TextRange {
@@ -768,6 +769,8 @@ module ts {
768769

769770
Undefined = 0x08000000, // Symbol for the undefined
770771

772+
BlockScoped = 0x10000000,
773+
771774
Value = Variable | Property | EnumMember | Function | Class | Enum | ValueModule | Method | GetAccessor | SetAccessor,
772775
Type = Class | Interface | Enum | TypeLiteral | ObjectLiteral | TypeParameter,
773776
Namespace = ValueModule | NamespaceModule,
@@ -776,7 +779,8 @@ module ts {
776779
Signature = CallSignature | ConstructSignature | IndexSignature,
777780

778781
ParameterExcludes = Value,
779-
VariableExcludes = Value & ~Variable,
782+
VariableExcludes = (Value | BlockScoped) & ~Variable,
783+
BlockScopedExcludes = Value,
780784
PropertyExcludes = Value,
781785
EnumMemberExcludes = Value,
782786
FunctionExcludes = Value & ~(Function | ValueModule),

tests/baselines/reference/constDeclarations-errors.errors.txt

+7-4
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ tests/cases/compiler/constDeclarations-errors.ts(8,18): error TS1128: Declaratio
1313
tests/cases/compiler/constDeclarations-errors.ts(10,5): error TS1109: Expression expected.
1414
tests/cases/compiler/constDeclarations-errors.ts(10,5): error TS1156: const must be declared inside a block.
1515
tests/cases/compiler/constDeclarations-errors.ts(10,28): error TS1005: ';' expected.
16-
tests/cases/compiler/constDeclarations-errors.ts(10,11): error TS2403: Subsequent variable declarations must have the same type. Variable 'c' must be of type 'any', but here has type 'number'.
16+
tests/cases/compiler/constDeclarations-errors.ts(10,18): error TS2304: Cannot find name 'c'.
17+
tests/cases/compiler/constDeclarations-errors.ts(10,25): error TS2304: Cannot find name 'c'.
1718

1819

19-
==== tests/cases/compiler/constDeclarations-errors.ts (16 errors) ====
20+
==== tests/cases/compiler/constDeclarations-errors.ts (17 errors) ====
2021

2122
// error, missing intialicer
2223
const c1;
@@ -57,6 +58,8 @@ tests/cases/compiler/constDeclarations-errors.ts(10,11): error TS2403: Subsequen
5758
!!! error TS1156: const must be declared inside a block.
5859
~
5960
!!! error TS1005: ';' expected.
60-
~
61-
!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'c' must be of type 'any', but here has type 'number'.
61+
~
62+
!!! error TS2304: Cannot find name 'c'.
63+
~
64+
!!! error TS2304: Cannot find name 'c'.
6265

0 commit comments

Comments
 (0)