@@ -383,42 +383,72 @@ namespace ts {
383383 // return undefined if we can't find a symbol.
384384 }
385385
386- const enum RelativeLocation {
387- Unknown,
388- SameFileLocatedBefore,
389- SameFileLocatedAfter,
390- DifferentFilesLocatedBefore,
391- DifferentFilesLocatedAfter,
392- }
393-
394- function isLocatedBefore(origin: Node, target: Node): boolean {
395- switch (getRelativeLocation(origin, target)) {
396- // unknown is returned with nodes are in different files and order cannot be determined based on compilation settings
397- // optimistically assume this is ok
398- case RelativeLocation.Unknown:
399- case RelativeLocation.SameFileLocatedBefore:
400- case RelativeLocation.DifferentFilesLocatedBefore:
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
401392 return true;
402- default:
403- return false;
393+ }
394+
395+ const sourceFiles = host.getSourceFiles();
396+ return indexOf(sourceFiles, declarationFile) <= indexOf(sourceFiles, useFile);
404397 }
405- }
406398
407- /** gets relative location of target comparing to origin **/
408- function getRelativeLocation(origin: Node, target: Node): RelativeLocation {
409- let file1 = getSourceFileOfNode(origin);
410- let file2 = getSourceFileOfNode(target);
411- if (file1 === file2) {
412- return origin.pos > target.pos ? RelativeLocation.SameFileLocatedBefore : RelativeLocation.SameFileLocatedAfter;
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);
413404 }
414405
415- if (modulekind || (!compilerOptions.outFile && !compilerOptions.out)) {
416- // nodes are in different files and order cannot be determines
417- return RelativeLocation.Unknown;
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+ }
418425 }
419426
420- let sourceFiles = host.getSourceFiles();
421- return sourceFiles.indexOf(file1) > sourceFiles.indexOf(file2) ? RelativeLocation.DifferentFilesLocatedBefore : RelativeLocation.DifferentFilesLocatedAfter;
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+ }
422452 }
423453
424454 // Resolve a given name for a given meaning at a given location. An error is reported if the name was not found and
@@ -649,67 +679,7 @@ namespace ts {
649679
650680 Debug.assert(declaration !== undefined, "Block-scoped variable declaration is undefined");
651681
652- // first check if usage is lexically located after the declaration
653- let isUsedBeforeDeclaration = false;
654- switch (getRelativeLocation(declaration, errorLocation)) {
655- case RelativeLocation.DifferentFilesLocatedBefore:
656- isUsedBeforeDeclaration = true;
657- break;
658- case RelativeLocation.SameFileLocatedBefore:
659- // try to detect if forward reference to block scoped variable is inside function
660- // such forward references are permitted (they are still technically can be incorrect (i.e. in case of IIFEs)
661- // but detecting these case is more complicated task)
662- const declarationContainer = getEnclosingBlockScopeContainer(declaration);
663- let current = errorLocation;
664- while (current) {
665- if (current === declarationContainer) {
666- isUsedBeforeDeclaration = true;
667- break;
668- }
669-
670- if (isFunctionLike(current)) {
671- break;
672- }
673-
674- const isInitializerOfNonStaticProperty =
675- current.parent &&
676- current.parent.kind === SyntaxKind.PropertyDeclaration &&
677- (current.parent.flags & NodeFlags.Static) === 0 &&
678- (<PropertyDeclaration>current.parent).initializer === current;
679-
680- if (isInitializerOfNonStaticProperty) {
681- break;
682- }
683- current = current.parent;
684- }
685- break;
686- case RelativeLocation.SameFileLocatedAfter:
687- // lexical check succeeded however code still can be illegal.
688- // - block scoped variables cannot be used in its initializers
689- // let x = x; // illegal but usage is lexically after definition
690- // - in ForIn/ForOf statements variable cannot be contained in expression part
691- // for (let x in x)
692- // for (let x of x)
693-
694- // climb up to the variable declaration skipping binding patterns
695- let variableDeclaration = <VariableDeclaration>getAncestor(declaration, SyntaxKind.VariableDeclaration);
696- let container = getEnclosingBlockScopeContainer(variableDeclaration);
697-
698- if (variableDeclaration.parent.parent.kind === SyntaxKind.VariableStatement ||
699- variableDeclaration.parent.parent.kind === SyntaxKind.ForStatement) {
700- // variable statement/for statement case,
701- // use site should not be inside variable declaration (initializer of declaration or binding element)
702- isUsedBeforeDeclaration = isSameScopeDescendentOf(errorLocation, variableDeclaration, container);
703- }
704- else if (variableDeclaration.parent.parent.kind === SyntaxKind.ForOfStatement ||
705- variableDeclaration.parent.parent.kind === SyntaxKind.ForInStatement) {
706- // ForIn/ForOf case - use site should not be used in expression part
707- let expression = (<ForInStatement | ForOfStatement>variableDeclaration.parent.parent).expression;
708- isUsedBeforeDeclaration = isSameScopeDescendentOf(errorLocation, expression, container);
709- }
710- break;
711- }
712- if (isUsedBeforeDeclaration) {
682+ if (!isBlockScopedNameDeclaredBeforeUse(<Declaration>getAncestor(declaration, SyntaxKind.VariableDeclaration), errorLocation)) {
713683 error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationNameToString(declaration.name));
714684 }
715685 }
@@ -13233,6 +13203,8 @@ namespace ts {
1323313203 let nodeLinks = getNodeLinks(node);
1323413204
1323513205 if (!(nodeLinks.flags & NodeCheckFlags.EnumValuesComputed)) {
13206+ nodeLinks.flags |= NodeCheckFlags.EnumValuesComputed;
13207+
1323613208 let enumSymbol = getSymbolOfNode(node);
1323713209 let enumType = getDeclaredTypeOfSymbol(enumSymbol);
1323813210 let autoValue = 0; // set to undefined when enum member is non-constant
@@ -13270,8 +13242,6 @@ namespace ts {
1327013242 getNodeLinks(member).enumMemberValue = autoValue++;
1327113243 }
1327213244 }
13273-
13274- nodeLinks.flags |= NodeCheckFlags.EnumValuesComputed;
1327513245 }
1327613246
1327713247 function computeConstantValueForEnumMemberInitializer(initializer: Expression, enumType: Type, enumIsConst: boolean, ambient: boolean): number {
@@ -13411,12 +13381,13 @@ namespace ts {
1341113381 }
1341213382
1341313383 // illegal case: forward reference
13414- if (isLocatedBefore (propertyDecl, member)) {
13384+ if (!isBlockScopedNameDeclaredBeforeUse (propertyDecl, member)) {
1341513385 reportError = false;
1341613386 error(e, Diagnostics.A_member_initializer_in_a_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_enums);
1341713387 return undefined;
1341813388 }
1341913389
13390+ computeEnumMemberValues(<EnumDeclaration>propertyDecl.parent);
1342013391 return <number>getNodeLinks(propertyDecl).enumMemberValue;
1342113392 }
1342213393 }
0 commit comments