mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-10 10:58:20 -05:00
Type query can refer to block-scoped vars before declaration
This commit is contained in:
@@ -727,7 +727,7 @@ namespace ts {
|
||||
if (declarationFile !== useFile) {
|
||||
if ((modulekind && (declarationFile.externalModuleIndicator || useFile.externalModuleIndicator)) ||
|
||||
(!compilerOptions.outFile && !compilerOptions.out)) {
|
||||
// nodes are in different files and order cannot be determines
|
||||
// nodes are in different files and order cannot be determined
|
||||
return true;
|
||||
}
|
||||
// declaration is after usage
|
||||
@@ -745,7 +745,7 @@ namespace ts {
|
||||
// still might be illegal if declaration and usage are both binding elements (eg var [a = b, b = b] = [1, 2])
|
||||
const errorBindingElement = getAncestor(usage, SyntaxKind.BindingElement) as BindingElement;
|
||||
if (errorBindingElement) {
|
||||
return getAncestorBindingPattern(errorBindingElement) !== getAncestorBindingPattern(declaration) ||
|
||||
return findAncestor(errorBindingElement, isBindingElement) !== findAncestor(declaration, isBindingElement) ||
|
||||
declaration.pos < errorBindingElement.pos;
|
||||
}
|
||||
// or it might be illegal if usage happens before parent variable is declared (eg var [a] = a)
|
||||
@@ -763,8 +763,10 @@ namespace ts {
|
||||
// 1. inside a function
|
||||
// 2. inside an instance property initializer, a reference to a non-instance property
|
||||
// 3. inside a static property initializer, a reference to a static method in the same class
|
||||
// or if usage is in a type context:
|
||||
// 1. inside a type query (typeof in type position)
|
||||
const container = getEnclosingBlockScopeContainer(declaration);
|
||||
return isUsedInFunctionOrInstanceProperty(usage, declaration, container);
|
||||
return isInTypeQuery(usage) || isUsedInFunctionOrInstanceProperty(usage, declaration, container);
|
||||
|
||||
function isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration: VariableDeclaration, usage: Node): boolean {
|
||||
const container = getEnclosingBlockScopeContainer(declaration);
|
||||
@@ -794,12 +796,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function isUsedInFunctionOrInstanceProperty(usage: Node, declaration: Node, container?: Node): boolean {
|
||||
let current = usage;
|
||||
while (current) {
|
||||
if (current === container) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !!findAncestor(usage, current => {
|
||||
if (isFunctionLike(current)) {
|
||||
return true;
|
||||
}
|
||||
@@ -821,20 +818,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
current = current.parent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function getAncestorBindingPattern(node: Node): BindingPattern {
|
||||
while (node) {
|
||||
if (isBindingPattern(node)) {
|
||||
return node;
|
||||
}
|
||||
node = node.parent;
|
||||
}
|
||||
return undefined;
|
||||
}, n => n === container);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1256,15 +1240,7 @@ namespace ts {
|
||||
* Return false if 'stopAt' node is reached or isFunctionLike(current) === true.
|
||||
*/
|
||||
function isSameScopeDescendentOf(initial: Node, parent: Node, stopAt: Node): boolean {
|
||||
if (!parent) {
|
||||
return false;
|
||||
}
|
||||
for (let current = initial; current && current !== stopAt && !isFunctionLike(current); current = current.parent) {
|
||||
if (current === parent) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return parent && !!findAncestor(initial, n => n === parent, n => n === stopAt || isFunctionLike(n));
|
||||
}
|
||||
|
||||
function getAnyImportSyntax(node: Node): AnyImportSyntax {
|
||||
@@ -1273,10 +1249,7 @@ namespace ts {
|
||||
return <ImportEqualsDeclaration>node;
|
||||
}
|
||||
|
||||
while (node && node.kind !== SyntaxKind.ImportDeclaration) {
|
||||
node = node.parent;
|
||||
}
|
||||
return <ImportDeclaration>node;
|
||||
return findAncestor(node, n => n.kind === SyntaxKind.ImportDeclaration) as ImportDeclaration;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2137,11 +2110,8 @@ namespace ts {
|
||||
return { accessibility: SymbolAccessibility.Accessible };
|
||||
|
||||
function getExternalModuleContainer(declaration: Node) {
|
||||
for (; declaration; declaration = declaration.parent) {
|
||||
if (hasExternalModuleSymbol(declaration)) {
|
||||
return getSymbolOfNode(declaration);
|
||||
}
|
||||
}
|
||||
const node = findAncestor(declaration, hasExternalModuleSymbol);
|
||||
return node && getSymbolOfNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2885,10 +2855,7 @@ namespace ts {
|
||||
|
||||
function getTypeAliasForTypeLiteral(type: Type): Symbol {
|
||||
if (type.symbol && type.symbol.flags & SymbolFlags.TypeLiteral) {
|
||||
let node = type.symbol.declarations[0].parent;
|
||||
while (node.kind === SyntaxKind.ParenthesizedType) {
|
||||
node = node.parent;
|
||||
}
|
||||
const node = findAncestor(type.symbol.declarations[0].parent, n => n.kind !== SyntaxKind.ParenthesizedType);
|
||||
if (node.kind === SyntaxKind.TypeAliasDeclaration) {
|
||||
return getSymbolOfNode(node);
|
||||
}
|
||||
@@ -3831,8 +3798,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getDeclarationContainer(node: Node): Node {
|
||||
node = getRootDeclaration(node);
|
||||
while (node) {
|
||||
node = findAncestor(getRootDeclaration(node), node => {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
case SyntaxKind.VariableDeclarationList:
|
||||
@@ -3840,13 +3806,12 @@ namespace ts {
|
||||
case SyntaxKind.NamedImports:
|
||||
case SyntaxKind.NamespaceImport:
|
||||
case SyntaxKind.ImportClause:
|
||||
node = node.parent;
|
||||
break;
|
||||
|
||||
return false;
|
||||
default:
|
||||
return node.parent;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
return node && node.parent;
|
||||
}
|
||||
|
||||
function getTypeOfPrototypeProperty(prototype: Symbol): Type {
|
||||
@@ -7918,8 +7883,7 @@ namespace ts {
|
||||
// Starting with the parent of the symbol's declaration, check if the mapper maps any of
|
||||
// the type parameters introduced by enclosing declarations. We just pick the first
|
||||
// declaration since multiple declarations will all have the same parent anyway.
|
||||
let node: Node = symbol.declarations[0];
|
||||
while (node) {
|
||||
return !!findAncestor(symbol.declarations[0], node => {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.FunctionType:
|
||||
case SyntaxKind.ConstructorType:
|
||||
@@ -7966,13 +7930,9 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
case SyntaxKind.SourceFile:
|
||||
return false;
|
||||
}
|
||||
node = node.parent;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
node => node.kind === SyntaxKind.ModuleDeclaration || node.kind === SyntaxKind.SourceFile);
|
||||
}
|
||||
|
||||
function isTopLevelTypeAlias(symbol: Symbol) {
|
||||
@@ -10416,19 +10376,10 @@ namespace ts {
|
||||
// TypeScript 1.0 spec (April 2014): 3.6.3
|
||||
// A type query consists of the keyword typeof followed by an expression.
|
||||
// The expression is restricted to a single identifier or a sequence of identifiers separated by periods
|
||||
while (node) {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.TypeQuery:
|
||||
return true;
|
||||
case SyntaxKind.Identifier:
|
||||
case SyntaxKind.QualifiedName:
|
||||
node = node.parent;
|
||||
continue;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Debug.fail("should not get here");
|
||||
return !!findAncestor(
|
||||
node,
|
||||
n => n.kind === SyntaxKind.TypeQuery,
|
||||
n => n.kind !== SyntaxKind.TypeQuery && n.kind !== SyntaxKind.Identifier && n.kind !== SyntaxKind.QualifiedName);
|
||||
}
|
||||
|
||||
// Return the flow cache key for a "dotted name" (i.e. a sequence of identifiers
|
||||
@@ -11651,15 +11602,11 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getControlFlowContainer(node: Node): Node {
|
||||
while (true) {
|
||||
node = node.parent;
|
||||
if (isFunctionLike(node) && !getImmediatelyInvokedFunctionExpression(node) ||
|
||||
node.kind === SyntaxKind.ModuleBlock ||
|
||||
node.kind === SyntaxKind.SourceFile ||
|
||||
node.kind === SyntaxKind.PropertyDeclaration) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return findAncestor(node.parent, node =>
|
||||
isFunctionLike(node) && !getImmediatelyInvokedFunctionExpression(node) ||
|
||||
node.kind === SyntaxKind.ModuleBlock ||
|
||||
node.kind === SyntaxKind.SourceFile ||
|
||||
node.kind === SyntaxKind.PropertyDeclaration);
|
||||
}
|
||||
|
||||
// Check if a parameter is assigned anywhere within its declaring function.
|
||||
@@ -11676,15 +11623,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function hasParentWithAssignmentsMarked(node: Node) {
|
||||
while (true) {
|
||||
node = node.parent;
|
||||
if (!node) {
|
||||
return false;
|
||||
}
|
||||
if (isFunctionLike(node) && getNodeLinks(node).flags & NodeCheckFlags.AssignmentsMarked) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return !!findAncestor(node.parent, node => isFunctionLike(node) && getNodeLinks(node).flags & NodeCheckFlags.AssignmentsMarked);
|
||||
}
|
||||
|
||||
function markParameterAssignments(node: Node) {
|
||||
@@ -11856,15 +11795,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function isInsideFunction(node: Node, threshold: Node): boolean {
|
||||
let current = node;
|
||||
while (current && current !== threshold) {
|
||||
if (isFunctionLike(current)) {
|
||||
return true;
|
||||
}
|
||||
current = current.parent;
|
||||
}
|
||||
|
||||
return false;
|
||||
return !!findAncestor(node, isFunctionLike, n => n === threshold);
|
||||
}
|
||||
|
||||
function checkNestedBlockScopedBinding(node: Identifier, symbol: Symbol): void {
|
||||
@@ -11916,8 +11847,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
function isAssignedInBodyOfForStatement(node: Identifier, container: ForStatement): boolean {
|
||||
let current: Node = node;
|
||||
// skip parenthesized nodes
|
||||
let current: Node = node;
|
||||
while (current.parent.kind === SyntaxKind.ParenthesizedExpression) {
|
||||
current = current.parent;
|
||||
}
|
||||
@@ -11938,15 +11869,7 @@ namespace ts {
|
||||
|
||||
// at this point we know that node is the target of assignment
|
||||
// now check that modification happens inside the statement part of the ForStatement
|
||||
while (current !== container) {
|
||||
if (current === container.statement) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
current = current.parent;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return !!findAncestor(current, n => n === container.statement, n => n === container);
|
||||
}
|
||||
|
||||
function captureLexicalThis(node: Node, container: Node): void {
|
||||
@@ -12128,12 +12051,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function isInConstructorArgumentInitializer(node: Node, constructorDecl: Node): boolean {
|
||||
for (let n = node; n && n !== constructorDecl; n = n.parent) {
|
||||
if (n.kind === SyntaxKind.Parameter) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return !!findAncestor(node, n => n.kind === SyntaxKind.Parameter, n => n === constructorDecl);
|
||||
}
|
||||
|
||||
function checkSuperExpression(node: Node): Type {
|
||||
@@ -12159,10 +12077,7 @@ namespace ts {
|
||||
// class B {
|
||||
// [super.foo()]() {}
|
||||
// }
|
||||
let current = node;
|
||||
while (current && current !== container && current.kind !== SyntaxKind.ComputedPropertyName) {
|
||||
current = current.parent;
|
||||
}
|
||||
const current = findAncestor(node, n => n.kind === SyntaxKind.ComputedPropertyName, n => n === container);
|
||||
if (current && current.kind === SyntaxKind.ComputedPropertyName) {
|
||||
error(node, Diagnostics.super_cannot_be_referenced_in_a_computed_property_name);
|
||||
}
|
||||
@@ -12766,13 +12681,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getContextualMapper(node: Node) {
|
||||
while (node) {
|
||||
if (node.contextualMapper) {
|
||||
return node.contextualMapper;
|
||||
}
|
||||
node = node.parent;
|
||||
}
|
||||
return identityMapper;
|
||||
node = findAncestor(node, n => n.contextualMapper);
|
||||
return node ? node.contextualMapper : identityMapper;
|
||||
}
|
||||
|
||||
// If the given type is an object or union type, if that type has a single signature, and if
|
||||
@@ -19010,8 +18920,7 @@ namespace ts {
|
||||
|
||||
// this function will run after checking the source file so 'CaptureThis' is correct for all nodes
|
||||
function checkIfThisIsCapturedInEnclosingScope(node: Node): void {
|
||||
let current = node;
|
||||
while (current) {
|
||||
findAncestor(node, current => {
|
||||
if (getNodeCheckFlags(current) & NodeCheckFlags.CaptureThis) {
|
||||
const isDeclaration = node.kind !== SyntaxKind.Identifier;
|
||||
if (isDeclaration) {
|
||||
@@ -19020,15 +18929,13 @@ namespace ts {
|
||||
else {
|
||||
error(node, Diagnostics.Expression_resolves_to_variable_declaration_this_that_compiler_uses_to_capture_this_reference);
|
||||
}
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
current = current.parent;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function checkIfNewTargetIsCapturedInEnclosingScope(node: Node): void {
|
||||
let current = node;
|
||||
while (current) {
|
||||
findAncestor(node, current => {
|
||||
if (getNodeCheckFlags(current) & NodeCheckFlags.CaptureNewTarget) {
|
||||
const isDeclaration = node.kind !== SyntaxKind.Identifier;
|
||||
if (isDeclaration) {
|
||||
@@ -19037,10 +18944,9 @@ namespace ts {
|
||||
else {
|
||||
error(node, Diagnostics.Expression_resolves_to_variable_declaration_newTarget_that_compiler_uses_to_capture_new_target_meta_property_reference);
|
||||
}
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
current = current.parent;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function checkCollisionWithCapturedSuperVariable(node: Node, name: Identifier) {
|
||||
@@ -19224,19 +19130,17 @@ namespace ts {
|
||||
return;
|
||||
}
|
||||
// - parameter is wrapped in function-like entity
|
||||
let current = n;
|
||||
while (current !== node.initializer) {
|
||||
if (isFunctionLike(current.parent)) {
|
||||
return;
|
||||
}
|
||||
// computed property names/initializers in instance property declaration of class like entities
|
||||
// are executed in constructor and thus deferred
|
||||
if (current.parent.kind === SyntaxKind.PropertyDeclaration &&
|
||||
!(hasModifier(current.parent, ModifierFlags.Static)) &&
|
||||
isClassLike(current.parent.parent)) {
|
||||
return;
|
||||
}
|
||||
current = current.parent;
|
||||
if (findAncestor(
|
||||
n,
|
||||
current =>
|
||||
isFunctionLike(current.parent) ||
|
||||
// computed property names/initializers in instance property declaration of class like entities
|
||||
// are executed in constructor and thus deferred
|
||||
(current.parent.kind === SyntaxKind.PropertyDeclaration &&
|
||||
!(hasModifier(current.parent, ModifierFlags.Static)) &&
|
||||
isClassLike(current.parent.parent)),
|
||||
n => n === node.initializer)) {
|
||||
return;
|
||||
}
|
||||
// fall through to report error
|
||||
}
|
||||
@@ -20034,18 +19938,15 @@ namespace ts {
|
||||
function checkLabeledStatement(node: LabeledStatement) {
|
||||
// Grammar checking
|
||||
if (!checkGrammarStatementInAmbientContext(node)) {
|
||||
let current = node.parent;
|
||||
while (current) {
|
||||
if (isFunctionLike(current)) {
|
||||
break;
|
||||
}
|
||||
if (current.kind === SyntaxKind.LabeledStatement && (<LabeledStatement>current).label.text === node.label.text) {
|
||||
const sourceFile = getSourceFileOfNode(node);
|
||||
grammarErrorOnNode(node.label, Diagnostics.Duplicate_label_0, getTextOfNodeFromSourceText(sourceFile.text, node.label));
|
||||
break;
|
||||
}
|
||||
current = current.parent;
|
||||
}
|
||||
findAncestor(node.parent,
|
||||
current => {
|
||||
if (current.kind === SyntaxKind.LabeledStatement && (<LabeledStatement>current).label.text === node.label.text) {
|
||||
const sourceFile = getSourceFileOfNode(node);
|
||||
grammarErrorOnNode(node.label, Diagnostics.Duplicate_label_0, getTextOfNodeFromSourceText(sourceFile.text, node.label));
|
||||
return true;
|
||||
}
|
||||
},
|
||||
isFunctionLike);
|
||||
}
|
||||
|
||||
// ensure that label is unique
|
||||
@@ -22298,11 +22199,7 @@ namespace ts {
|
||||
const symbolIsUmdExport = symbolFile !== referenceFile;
|
||||
return symbolIsUmdExport ? undefined : symbolFile;
|
||||
}
|
||||
for (let n = node.parent; n; n = n.parent) {
|
||||
if (isModuleOrEnumDeclaration(n) && getSymbolOfNode(n) === parentSymbol) {
|
||||
return <ModuleDeclaration | EnumDeclaration>n;
|
||||
}
|
||||
}
|
||||
return findAncestor(node.parent, n => isModuleOrEnumDeclaration(n) && getSymbolOfNode(n) === parentSymbol) as ModuleDeclaration | EnumDeclaration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,6 +225,28 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through the parent chain of a node and performs the callback on each parent until the callback
|
||||
* returns a truthy value, then returns that value.
|
||||
* If no such value is found, it applies the callback until the parent pointer is undefined or 'done' returns true
|
||||
* At that point findAncestor returns undefined.
|
||||
*/
|
||||
export function findAncestor<T>(node: Node, callback: (element: Node) => T | undefined, done?: (element: Node) => boolean): Node {
|
||||
if (node) {
|
||||
while (node) {
|
||||
if (done && done(node)) {
|
||||
break;
|
||||
}
|
||||
if (callback(node)) {
|
||||
return node;
|
||||
}
|
||||
node = node.parent;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
export function zipWith<T, U>(arrayA: T[], arrayB: U[], callback: (a: T, b: U, index: number) => void): void {
|
||||
Debug.assert(arrayA.length === arrayB.length);
|
||||
for (let i = 0; i < arrayA.length; i++) {
|
||||
|
||||
Reference in New Issue
Block a user