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