@@ -385,20 +385,72 @@ namespace ts {
385
385
// return undefined if we can't find a symbol.
386
386
}
387
387
388
- /** Returns true if node1 is defined before node 2**/
389
- function isDefinedBefore(node1: Node, node2: Node): boolean {
390
- let file1 = getSourceFileOfNode(node1);
391
- let file2 = getSourceFileOfNode(node2);
392
- if (file1 === file2) {
393
- return node1.pos <= node2.pos;
388
+ function isBlockScopedNameDeclaredBeforeUse(declaration: Declaration, usage: Node): boolean {
389
+ const declarationFile = getSourceFileOfNode(declaration);
390
+ const useFile = getSourceFileOfNode(usage);
391
+ if (declarationFile !== useFile) {
392
+ if (modulekind || (!compilerOptions.outFile && !compilerOptions.out)) {
393
+ // nodes are in different files and order cannot be determines
394
+ return true;
395
+ }
396
+
397
+ const sourceFiles = host.getSourceFiles();
398
+ return indexOf(sourceFiles, declarationFile) <= indexOf(sourceFiles, useFile);
394
399
}
395
400
396
- if (!compilerOptions.outFile && !compilerOptions.out) {
397
- return true;
401
+ if (declaration.pos <= usage.pos) {
402
+ // declaration is before usage
403
+ // still might be illegal if usage is in the initializer of the variable declaration
404
+ return declaration.kind !== SyntaxKind.VariableDeclaration ||
405
+ !isImmediatelyUsedInInitializerOfBlockScopedVariable(<VariableDeclaration>declaration, usage);
398
406
}
399
407
400
- let sourceFiles = host.getSourceFiles();
401
- return sourceFiles.indexOf(file1) <= sourceFiles.indexOf(file2);
408
+ // declaration is after usage
409
+ // can be legal if usage is deferred (i.e. inside function or in initializer of instance property)
410
+ return isUsedInFunctionOrNonStaticProperty(declaration, usage);
411
+
412
+ function isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration: VariableDeclaration, usage: Node): boolean {
413
+ const container = getEnclosingBlockScopeContainer(declaration);
414
+
415
+ if (declaration.parent.parent.kind === SyntaxKind.VariableStatement ||
416
+ declaration.parent.parent.kind === SyntaxKind.ForStatement) {
417
+ // variable statement/for statement case,
418
+ // use site should not be inside variable declaration (initializer of declaration or binding element)
419
+ return isSameScopeDescendentOf(usage, declaration, container);
420
+ }
421
+ else if (declaration.parent.parent.kind === SyntaxKind.ForOfStatement ||
422
+ declaration.parent.parent.kind === SyntaxKind.ForInStatement) {
423
+ // ForIn/ForOf case - use site should not be used in expression part
424
+ let expression = (<ForInStatement | ForOfStatement>declaration.parent.parent).expression;
425
+ return isSameScopeDescendentOf(usage, expression, container);
426
+ }
427
+ }
428
+
429
+ function isUsedInFunctionOrNonStaticProperty(declaration: Declaration, usage: Node): boolean {
430
+ const container = getEnclosingBlockScopeContainer(declaration);
431
+ let current = usage;
432
+ while (current) {
433
+ if (current === container) {
434
+ return false;
435
+ }
436
+
437
+ if (isFunctionLike(current)) {
438
+ return true;
439
+ }
440
+
441
+ const initializerOfNonStaticProperty = current.parent &&
442
+ current.parent.kind === SyntaxKind.PropertyDeclaration &&
443
+ (current.parent.flags & NodeFlags.Static) === 0 &&
444
+ (<PropertyDeclaration>current.parent).initializer === current;
445
+
446
+ if (initializerOfNonStaticProperty) {
447
+ return true;
448
+ }
449
+
450
+ current = current.parent;
451
+ }
452
+ return false;
453
+ }
402
454
}
403
455
404
456
// Resolve a given name for a given meaning at a given location. An error is reported if the name was not found and
@@ -629,34 +681,7 @@ namespace ts {
629
681
630
682
Debug.assert(declaration !== undefined, "Block-scoped variable declaration is undefined");
631
683
632
- // first check if usage is lexically located after the declaration
633
- let isUsedBeforeDeclaration = !isDefinedBefore(declaration, errorLocation);
634
- if (!isUsedBeforeDeclaration) {
635
- // lexical check succeeded however code still can be illegal.
636
- // - block scoped variables cannot be used in its initializers
637
- // let x = x; // illegal but usage is lexically after definition
638
- // - in ForIn/ForOf statements variable cannot be contained in expression part
639
- // for (let x in x)
640
- // for (let x of x)
641
-
642
- // climb up to the variable declaration skipping binding patterns
643
- let variableDeclaration = <VariableDeclaration>getAncestor(declaration, SyntaxKind.VariableDeclaration);
644
- let container = getEnclosingBlockScopeContainer(variableDeclaration);
645
-
646
- if (variableDeclaration.parent.parent.kind === SyntaxKind.VariableStatement ||
647
- variableDeclaration.parent.parent.kind === SyntaxKind.ForStatement) {
648
- // variable statement/for statement case,
649
- // use site should not be inside variable declaration (initializer of declaration or binding element)
650
- isUsedBeforeDeclaration = isSameScopeDescendentOf(errorLocation, variableDeclaration, container);
651
- }
652
- else if (variableDeclaration.parent.parent.kind === SyntaxKind.ForOfStatement ||
653
- variableDeclaration.parent.parent.kind === SyntaxKind.ForInStatement) {
654
- // ForIn/ForOf case - use site should not be used in expression part
655
- let expression = (<ForInStatement | ForOfStatement>variableDeclaration.parent.parent).expression;
656
- isUsedBeforeDeclaration = isSameScopeDescendentOf(errorLocation, expression, container);
657
- }
658
- }
659
- if (isUsedBeforeDeclaration) {
684
+ if (!isBlockScopedNameDeclaredBeforeUse(<Declaration>getAncestor(declaration, SyntaxKind.VariableDeclaration), errorLocation)) {
660
685
error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationNameToString(declaration.name));
661
686
}
662
687
}
@@ -11758,10 +11783,6 @@ namespace ts {
11758
11783
checkSignatureDeclaration(node);
11759
11784
let isAsync = isAsyncFunctionLike(node);
11760
11785
if (isAsync) {
11761
- if (!compilerOptions.experimentalAsyncFunctions) {
11762
- error(node, Diagnostics.Experimental_support_for_async_functions_is_a_feature_that_is_subject_to_change_in_a_future_release_Specify_experimentalAsyncFunctions_to_remove_this_warning);
11763
- }
11764
-
11765
11786
emitAwaiter = true;
11766
11787
}
11767
11788
@@ -13350,7 +13371,7 @@ namespace ts {
13350
13371
}
13351
13372
13352
13373
// illegal case: forward reference
13353
- if (!isDefinedBefore (propertyDecl, member)) {
13374
+ if (!isBlockScopedNameDeclaredBeforeUse (propertyDecl, member)) {
13354
13375
reportError = false;
13355
13376
error(e, Diagnostics.A_member_initializer_in_a_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_enums);
13356
13377
return undefined;
0 commit comments