mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 12:32:08 -06:00
Refactor name resolution to separate component (#57974)
This commit is contained in:
parent
806d734047
commit
f5bc6ddc75
File diff suppressed because it is too large
Load Diff
@ -39,6 +39,7 @@ import {
|
||||
CallLikeExpression,
|
||||
CallSignatureDeclaration,
|
||||
canHaveDecorators,
|
||||
canHaveLocals,
|
||||
canHaveModifiers,
|
||||
CaseBlock,
|
||||
CaseClause,
|
||||
@ -233,16 +234,19 @@ import {
|
||||
IndexInfo,
|
||||
indexOfAnyCharCode,
|
||||
IndexSignatureDeclaration,
|
||||
InferTypeNode,
|
||||
InitializedVariableDeclaration,
|
||||
insertSorted,
|
||||
InstanceofExpression,
|
||||
InterfaceDeclaration,
|
||||
InternalEmitFlags,
|
||||
InternalSymbolName,
|
||||
isAccessor,
|
||||
isAnyDirectorySeparator,
|
||||
isArray,
|
||||
isArrayLiteralExpression,
|
||||
isArrowFunction,
|
||||
isAssertionExpression,
|
||||
isAutoAccessorPropertyDeclaration,
|
||||
isBigIntLiteral,
|
||||
isBinaryExpression,
|
||||
@ -257,6 +261,7 @@ import {
|
||||
isCommaListExpression,
|
||||
isComputedPropertyName,
|
||||
isConstructorDeclaration,
|
||||
isConstTypeReference,
|
||||
isDeclaration,
|
||||
isDecorator,
|
||||
isElementAccessExpression,
|
||||
@ -293,6 +298,7 @@ import {
|
||||
isJSDocOverloadTag,
|
||||
isJSDocParameterTag,
|
||||
isJSDocPropertyLikeTag,
|
||||
isJSDocReturnTag,
|
||||
isJSDocSatisfiesTag,
|
||||
isJSDocSignature,
|
||||
isJSDocTag,
|
||||
@ -314,15 +320,19 @@ import {
|
||||
isMethodOrAccessor,
|
||||
isModifierLike,
|
||||
isModuleDeclaration,
|
||||
isModuleOrEnumDeclaration,
|
||||
isNamedDeclaration,
|
||||
isNamespaceExport,
|
||||
isNamespaceExportDeclaration,
|
||||
isNamespaceImport,
|
||||
isNonNullExpression,
|
||||
isNoSubstitutionTemplateLiteral,
|
||||
isNullishCoalesce,
|
||||
isNumericLiteral,
|
||||
isObjectBindingPattern,
|
||||
isObjectLiteralExpression,
|
||||
isOmittedExpression,
|
||||
isOptionalChain,
|
||||
isParameter,
|
||||
isParameterPropertyDeclaration,
|
||||
isParenthesizedExpression,
|
||||
@ -348,6 +358,7 @@ import {
|
||||
isTypeLiteralNode,
|
||||
isTypeNode,
|
||||
isTypeParameterDeclaration,
|
||||
isTypeQueryNode,
|
||||
isTypeReferenceNode,
|
||||
isVariableDeclaration,
|
||||
isVariableStatement,
|
||||
@ -475,7 +486,9 @@ import {
|
||||
ResolvedModuleWithFailedLookupLocations,
|
||||
ResolvedTypeReferenceDirective,
|
||||
ResolvedTypeReferenceDirectiveWithFailedLookupLocations,
|
||||
returnFalse,
|
||||
ReturnStatement,
|
||||
returnUndefined,
|
||||
SatisfiesExpression,
|
||||
ScriptKind,
|
||||
ScriptTarget,
|
||||
@ -2306,6 +2319,11 @@ export function getErrorSpanForNode(sourceFile: SourceFile, node: Node): TextSpa
|
||||
return createTextSpanFromBounds(pos, errorNode.end);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function isGlobalSourceFile(node: Node) {
|
||||
return node.kind === SyntaxKind.SourceFile && !isExternalOrCommonJsModule(node as SourceFile);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function isExternalOrCommonJsModule(file: SourceFile): boolean {
|
||||
return (file.externalModuleIndicator || file.commonJsModuleIndicator) !== undefined;
|
||||
@ -10797,3 +10815,578 @@ export function createEvaluator({ evaluateElementAccessExpression, evaluateEntit
|
||||
}
|
||||
return evaluate;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function isConstAssertion(location: Node) {
|
||||
return (isAssertionExpression(location) && isConstTypeReference(location.type))
|
||||
|| (isJSDocTypeTag(location) && isConstTypeReference(location.typeExpression));
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function findConstructorDeclaration(node: ClassLikeDeclaration): ConstructorDeclaration | undefined {
|
||||
const members = node.members;
|
||||
for (const member of members) {
|
||||
if (member.kind === SyntaxKind.Constructor && nodeIsPresent((member as ConstructorDeclaration).body)) {
|
||||
return member as ConstructorDeclaration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function createNameResolver({
|
||||
compilerOptions,
|
||||
requireSymbol,
|
||||
argumentsSymbol,
|
||||
error,
|
||||
getSymbolOfDeclaration,
|
||||
globals,
|
||||
lookup,
|
||||
setRequiresScopeChangeCache = returnUndefined,
|
||||
getRequiresScopeChangeCache = returnUndefined,
|
||||
onPropertyWithInvalidInitializer = returnFalse,
|
||||
onFailedToResolveSymbol = returnUndefined,
|
||||
onSuccessfullyResolvedSymbol = returnUndefined,
|
||||
}: {
|
||||
compilerOptions: CompilerOptions;
|
||||
getSymbolOfDeclaration: (node: Declaration) => Symbol;
|
||||
error: (location: Node | undefined, message: DiagnosticMessage, ...args: DiagnosticArguments) => void;
|
||||
globals: SymbolTable;
|
||||
argumentsSymbol: Symbol;
|
||||
requireSymbol: Symbol;
|
||||
lookup: (symbols: SymbolTable, name: __String, meaning: SymbolFlags) => Symbol | undefined;
|
||||
setRequiresScopeChangeCache: undefined | ((node: FunctionLikeDeclaration, value: boolean) => void);
|
||||
getRequiresScopeChangeCache: undefined | ((node: FunctionLikeDeclaration) => boolean | undefined);
|
||||
onPropertyWithInvalidInitializer?: (location: Node | undefined, name: __String, declaration: PropertyDeclaration, result: Symbol | undefined) => boolean;
|
||||
onFailedToResolveSymbol?: (
|
||||
location: Node | undefined,
|
||||
name: __String | Identifier,
|
||||
meaning: SymbolFlags,
|
||||
nameNotFoundMessage: DiagnosticMessage,
|
||||
) => void;
|
||||
onSuccessfullyResolvedSymbol?: (
|
||||
location: Node | undefined,
|
||||
result: Symbol,
|
||||
meaning: SymbolFlags,
|
||||
lastLocation: Node | undefined,
|
||||
associatedDeclarationForContainingInitializerOrBindingName: ParameterDeclaration | BindingElement | undefined,
|
||||
withinDeferredContext: boolean,
|
||||
) => void;
|
||||
}) {
|
||||
/* eslint-disable no-var */
|
||||
var isolatedModulesLikeFlagName = compilerOptions.verbatimModuleSyntax ? "verbatimModuleSyntax" : "isolatedModules";
|
||||
/* eslint-disable no-var */
|
||||
var emitStandardClassFields = getEmitStandardClassFields(compilerOptions);
|
||||
var emptySymbols = createSymbolTable();
|
||||
return resolveNameHelper;
|
||||
function resolveNameHelper(
|
||||
location: Node | undefined,
|
||||
nameArg: __String | Identifier,
|
||||
meaning: SymbolFlags,
|
||||
nameNotFoundMessage: DiagnosticMessage | undefined,
|
||||
isUse: boolean,
|
||||
excludeGlobals?: boolean,
|
||||
): Symbol | undefined {
|
||||
const originalLocation = location; // needed for did-you-mean error reporting, which gathers candidates starting from the original location
|
||||
let result: Symbol | undefined;
|
||||
let lastLocation: Node | undefined;
|
||||
let lastSelfReferenceLocation: Declaration | undefined;
|
||||
let propertyWithInvalidInitializer: PropertyDeclaration | undefined;
|
||||
let associatedDeclarationForContainingInitializerOrBindingName: ParameterDeclaration | BindingElement | undefined;
|
||||
let withinDeferredContext = false;
|
||||
let grandparent: Node;
|
||||
const name = isString(nameArg) ? nameArg : (nameArg as Identifier).escapedText;
|
||||
loop:
|
||||
while (location) {
|
||||
if (name === "const" && isConstAssertion(location)) {
|
||||
// `const` in an `as const` has no symbol, but issues no error because there is no *actual* lookup of the type
|
||||
// (it refers to the constant type of the expression instead)
|
||||
return undefined;
|
||||
}
|
||||
if (isModuleOrEnumDeclaration(location) && lastLocation && location.name === lastLocation) {
|
||||
// If lastLocation is the name of a namespace or enum, skip the parent since it will have is own locals that could
|
||||
// conflict.
|
||||
lastLocation = location;
|
||||
location = location.parent;
|
||||
}
|
||||
// Locals of a source file are not in scope (because they get merged into the global symbol table)
|
||||
if (canHaveLocals(location) && location.locals && !isGlobalSourceFile(location)) {
|
||||
if (result = lookup(location.locals, name, meaning)) {
|
||||
let useResult = true;
|
||||
if (isFunctionLike(location) && lastLocation && lastLocation !== (location as FunctionLikeDeclaration).body) {
|
||||
// symbol lookup restrictions for function-like declarations
|
||||
// - Type parameters of a function are in scope in the entire function declaration, including the parameter
|
||||
// list and return type. However, local types are only in scope in the function body.
|
||||
// - parameters are only in the scope of function body
|
||||
// This restriction does not apply to JSDoc comment types because they are parented
|
||||
// at a higher level than type parameters would normally be
|
||||
if (meaning & result.flags & SymbolFlags.Type && lastLocation.kind !== SyntaxKind.JSDoc) {
|
||||
useResult = result.flags & SymbolFlags.TypeParameter
|
||||
// type parameters are visible in parameter list, return type and type parameter list
|
||||
? lastLocation === (location as FunctionLikeDeclaration).type ||
|
||||
lastLocation.kind === SyntaxKind.Parameter ||
|
||||
lastLocation.kind === SyntaxKind.JSDocParameterTag ||
|
||||
lastLocation.kind === SyntaxKind.JSDocReturnTag ||
|
||||
lastLocation.kind === SyntaxKind.TypeParameter
|
||||
// local types not visible outside the function body
|
||||
: false;
|
||||
}
|
||||
if (meaning & result.flags & SymbolFlags.Variable) {
|
||||
// expression inside parameter will lookup as normal variable scope when targeting es2015+
|
||||
if (useOuterVariableScopeInParameter(result, location, lastLocation)) {
|
||||
useResult = false;
|
||||
}
|
||||
else if (result.flags & SymbolFlags.FunctionScopedVariable) {
|
||||
// parameters are visible only inside function body, parameter list and return type
|
||||
// technically for parameter list case here we might mix parameters and variables declared in function,
|
||||
// however it is detected separately when checking initializers of parameters
|
||||
// to make sure that they reference no variables declared after them.
|
||||
useResult = lastLocation.kind === SyntaxKind.Parameter ||
|
||||
(
|
||||
lastLocation === (location as FunctionLikeDeclaration).type &&
|
||||
!!findAncestor(result.valueDeclaration, isParameter)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (location.kind === SyntaxKind.ConditionalType) {
|
||||
// A type parameter declared using 'infer T' in a conditional type is visible only in
|
||||
// the true branch of the conditional type.
|
||||
useResult = lastLocation === location.trueType;
|
||||
}
|
||||
|
||||
if (useResult) {
|
||||
break loop;
|
||||
}
|
||||
else {
|
||||
result = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
withinDeferredContext = withinDeferredContext || getIsDeferredContext(location, lastLocation);
|
||||
switch (location.kind) {
|
||||
case SyntaxKind.SourceFile:
|
||||
if (!isExternalOrCommonJsModule(location as SourceFile)) break;
|
||||
// falls through
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
const moduleExports = getSymbolOfDeclaration(location as SourceFile | ModuleDeclaration)?.exports || emptySymbols;
|
||||
if (location.kind === SyntaxKind.SourceFile || (isModuleDeclaration(location) && location.flags & NodeFlags.Ambient && !isGlobalScopeAugmentation(location))) {
|
||||
// It's an external module. First see if the module has an export default and if the local
|
||||
// name of that export default matches.
|
||||
if (result = moduleExports.get(InternalSymbolName.Default)) {
|
||||
const localSymbol = getLocalSymbolForExportDefault(result);
|
||||
if (localSymbol && (result.flags & meaning) && localSymbol.escapedName === name) {
|
||||
break loop;
|
||||
}
|
||||
result = undefined;
|
||||
}
|
||||
|
||||
// Because of module/namespace merging, a module's exports are in scope,
|
||||
// yet we never want to treat an export specifier as putting a member in scope.
|
||||
// Therefore, if the name we find is purely an export specifier, it is not actually considered in scope.
|
||||
// Two things to note about this:
|
||||
// 1. We have to check this without calling getSymbol. The problem with calling getSymbol
|
||||
// on an export specifier is that it might find the export specifier itself, and try to
|
||||
// resolve it as an alias. This will cause the checker to consider the export specifier
|
||||
// a circular alias reference when it might not be.
|
||||
// 2. We check === SymbolFlags.Alias in order to check that the symbol is *purely*
|
||||
// an alias. If we used &, we'd be throwing out symbols that have non alias aspects,
|
||||
// which is not the desired behavior.
|
||||
const moduleExport = moduleExports.get(name);
|
||||
if (
|
||||
moduleExport &&
|
||||
moduleExport.flags === SymbolFlags.Alias &&
|
||||
(getDeclarationOfKind(moduleExport, SyntaxKind.ExportSpecifier) || getDeclarationOfKind(moduleExport, SyntaxKind.NamespaceExport))
|
||||
) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ES6 exports are also visible locally (except for 'default'), but commonjs exports are not (except typedefs)
|
||||
if (name !== InternalSymbolName.Default && (result = lookup(moduleExports, name, meaning & SymbolFlags.ModuleMember))) {
|
||||
if (isSourceFile(location) && location.commonJsModuleIndicator && !result.declarations?.some(isJSDocTypeAlias)) {
|
||||
result = undefined;
|
||||
}
|
||||
else {
|
||||
break loop;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
if (result = lookup(getSymbolOfDeclaration(location as EnumDeclaration)?.exports || emptySymbols, name, meaning & SymbolFlags.EnumMember)) {
|
||||
if (nameNotFoundMessage && getIsolatedModules(compilerOptions) && !(location.flags & NodeFlags.Ambient) && getSourceFileOfNode(location) !== getSourceFileOfNode(result.valueDeclaration)) {
|
||||
error(
|
||||
originalLocation,
|
||||
Diagnostics.Cannot_access_0_from_another_file_without_qualification_when_1_is_enabled_Use_2_instead,
|
||||
unescapeLeadingUnderscores(name),
|
||||
isolatedModulesLikeFlagName,
|
||||
`${unescapeLeadingUnderscores(getSymbolOfDeclaration(location as EnumDeclaration).escapedName)}.${unescapeLeadingUnderscores(name)}`,
|
||||
);
|
||||
}
|
||||
break loop;
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
// TypeScript 1.0 spec (April 2014): 8.4.1
|
||||
// Initializer expressions for instance member variables are evaluated in the scope
|
||||
// of the class constructor body but are not permitted to reference parameters or
|
||||
// local variables of the constructor. This effectively means that entities from outer scopes
|
||||
// by the same name as a constructor parameter or local variable are inaccessible
|
||||
// in initializer expressions for instance member variables.
|
||||
if (!isStatic(location)) {
|
||||
const ctor = findConstructorDeclaration(location.parent as ClassLikeDeclaration);
|
||||
if (ctor && ctor.locals) {
|
||||
if (lookup(ctor.locals, name, meaning & SymbolFlags.Value)) {
|
||||
// Remember the property node, it will be used later to report appropriate error
|
||||
Debug.assertNode(location, isPropertyDeclaration);
|
||||
propertyWithInvalidInitializer = location;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.ClassExpression:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
// The below is used to lookup type parameters within a class or interface, as they are added to the class/interface locals
|
||||
// These can never be latebound, so the symbol's raw members are sufficient. `getMembersOfNode` cannot be used, as it would
|
||||
// trigger resolving late-bound names, which we may already be in the process of doing while we're here!
|
||||
if (result = lookup(getSymbolOfDeclaration(location as ClassLikeDeclaration | InterfaceDeclaration).members || emptySymbols, name, meaning & SymbolFlags.Type)) {
|
||||
if (!isTypeParameterSymbolDeclaredInContainer(result, location)) {
|
||||
// ignore type parameters not declared in this container
|
||||
result = undefined;
|
||||
break;
|
||||
}
|
||||
if (lastLocation && isStatic(lastLocation)) {
|
||||
// TypeScript 1.0 spec (April 2014): 3.4.1
|
||||
// The scope of a type parameter extends over the entire declaration with which the type
|
||||
// parameter list is associated, with the exception of static member declarations in classes.
|
||||
if (nameNotFoundMessage) {
|
||||
error(originalLocation, Diagnostics.Static_members_cannot_reference_class_type_parameters);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
break loop;
|
||||
}
|
||||
if (isClassExpression(location) && meaning & SymbolFlags.Class) {
|
||||
const className = location.name;
|
||||
if (className && name === className.escapedText) {
|
||||
result = location.symbol;
|
||||
break loop;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.ExpressionWithTypeArguments:
|
||||
// The type parameters of a class are not in scope in the base class expression.
|
||||
if (lastLocation === (location as ExpressionWithTypeArguments).expression && (location.parent as HeritageClause).token === SyntaxKind.ExtendsKeyword) {
|
||||
const container = location.parent.parent;
|
||||
if (isClassLike(container) && (result = lookup(getSymbolOfDeclaration(container).members!, name, meaning & SymbolFlags.Type))) {
|
||||
if (nameNotFoundMessage) {
|
||||
error(originalLocation, Diagnostics.Base_class_expressions_cannot_reference_class_type_parameters);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
break;
|
||||
// It is not legal to reference a class's own type parameters from a computed property name that
|
||||
// belongs to the class. For example:
|
||||
//
|
||||
// function foo<T>() { return '' }
|
||||
// class C<T> { // <-- Class's own type parameter T
|
||||
// [foo<T>()]() { } // <-- Reference to T from class's own computed property
|
||||
// }
|
||||
//
|
||||
case SyntaxKind.ComputedPropertyName:
|
||||
grandparent = location.parent.parent;
|
||||
if (isClassLike(grandparent) || grandparent.kind === SyntaxKind.InterfaceDeclaration) {
|
||||
// A reference to this grandparent's type parameters would be an error
|
||||
if (result = lookup(getSymbolOfDeclaration(grandparent as ClassLikeDeclaration | InterfaceDeclaration).members!, name, meaning & SymbolFlags.Type)) {
|
||||
if (nameNotFoundMessage) {
|
||||
error(originalLocation, Diagnostics.A_computed_property_name_cannot_reference_a_type_parameter_from_its_containing_type);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.ArrowFunction:
|
||||
// when targeting ES6 or higher there is no 'arguments' in an arrow function
|
||||
// for lower compile targets the resolved symbol is used to emit an error
|
||||
if (getEmitScriptTarget(compilerOptions) >= ScriptTarget.ES2015) {
|
||||
break;
|
||||
}
|
||||
// falls through
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.Constructor:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
if (meaning & SymbolFlags.Variable && name === "arguments") {
|
||||
result = argumentsSymbol;
|
||||
break loop;
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.FunctionExpression:
|
||||
if (meaning & SymbolFlags.Variable && name === "arguments") {
|
||||
result = argumentsSymbol;
|
||||
break loop;
|
||||
}
|
||||
|
||||
if (meaning & SymbolFlags.Function) {
|
||||
const functionName = (location as FunctionExpression).name;
|
||||
if (functionName && name === functionName.escapedText) {
|
||||
result = (location as FunctionExpression).symbol;
|
||||
break loop;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.Decorator:
|
||||
// Decorators are resolved at the class declaration. Resolving at the parameter
|
||||
// or member would result in looking up locals in the method.
|
||||
//
|
||||
// function y() {}
|
||||
// class C {
|
||||
// method(@y x, y) {} // <-- decorator y should be resolved at the class declaration, not the parameter.
|
||||
// }
|
||||
//
|
||||
if (location.parent && location.parent.kind === SyntaxKind.Parameter) {
|
||||
location = location.parent;
|
||||
}
|
||||
//
|
||||
// function y() {}
|
||||
// class C {
|
||||
// @y method(x, y) {} // <-- decorator y should be resolved at the class declaration, not the method.
|
||||
// }
|
||||
//
|
||||
|
||||
// class Decorators are resolved outside of the class to avoid referencing type parameters of that class.
|
||||
//
|
||||
// type T = number;
|
||||
// declare function y(x: T): any;
|
||||
// @param(1 as T) // <-- T should resolve to the type alias outside of class C
|
||||
// class C<T> {}
|
||||
if (location.parent && (isClassElement(location.parent) || location.parent.kind === SyntaxKind.ClassDeclaration)) {
|
||||
location = location.parent;
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.JSDocTypedefTag:
|
||||
case SyntaxKind.JSDocCallbackTag:
|
||||
case SyntaxKind.JSDocEnumTag:
|
||||
case SyntaxKind.JSDocImportTag:
|
||||
// js type aliases do not resolve names from their host, so skip past it
|
||||
const root = getJSDocRoot(location);
|
||||
if (root) {
|
||||
location = root.parent;
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.Parameter:
|
||||
if (
|
||||
lastLocation && (
|
||||
lastLocation === (location as ParameterDeclaration).initializer ||
|
||||
lastLocation === (location as ParameterDeclaration).name && isBindingPattern(lastLocation)
|
||||
)
|
||||
) {
|
||||
if (!associatedDeclarationForContainingInitializerOrBindingName) {
|
||||
associatedDeclarationForContainingInitializerOrBindingName = location as ParameterDeclaration;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.BindingElement:
|
||||
if (
|
||||
lastLocation && (
|
||||
lastLocation === (location as BindingElement).initializer ||
|
||||
lastLocation === (location as BindingElement).name && isBindingPattern(lastLocation)
|
||||
)
|
||||
) {
|
||||
if (isParameterDeclaration(location as BindingElement) && !associatedDeclarationForContainingInitializerOrBindingName) {
|
||||
associatedDeclarationForContainingInitializerOrBindingName = location as BindingElement;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.InferType:
|
||||
if (meaning & SymbolFlags.TypeParameter) {
|
||||
const parameterName = (location as InferTypeNode).typeParameter.name;
|
||||
if (parameterName && name === parameterName.escapedText) {
|
||||
result = (location as InferTypeNode).typeParameter.symbol;
|
||||
break loop;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.ExportSpecifier:
|
||||
// External module export bindings shouldn't be resolved to local symbols.
|
||||
if (
|
||||
lastLocation &&
|
||||
lastLocation === (location as ExportSpecifier).propertyName &&
|
||||
(location as ExportSpecifier).parent.parent.moduleSpecifier
|
||||
) {
|
||||
location = location.parent.parent.parent;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (isSelfReferenceLocation(location)) {
|
||||
lastSelfReferenceLocation = location;
|
||||
}
|
||||
lastLocation = location;
|
||||
location = isJSDocTemplateTag(location) ? getEffectiveContainerForJSDocTemplateTag(location) || location.parent :
|
||||
isJSDocParameterTag(location) || isJSDocReturnTag(location) ? getHostSignatureFromJSDoc(location) || location.parent :
|
||||
location.parent;
|
||||
}
|
||||
|
||||
// We just climbed up parents looking for the name, meaning that we started in a descendant node of `lastLocation`.
|
||||
// If `result === lastSelfReferenceLocation.symbol`, that means that we are somewhere inside `lastSelfReferenceLocation` looking up a name, and resolving to `lastLocation` itself.
|
||||
// That means that this is a self-reference of `lastLocation`, and shouldn't count this when considering whether `lastLocation` is used.
|
||||
if (isUse && result && (!lastSelfReferenceLocation || result !== lastSelfReferenceLocation.symbol)) {
|
||||
result.isReferenced! |= meaning;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
if (lastLocation) {
|
||||
Debug.assertNode(lastLocation, isSourceFile);
|
||||
if (lastLocation.commonJsModuleIndicator && name === "exports" && meaning & lastLocation.symbol.flags) {
|
||||
return lastLocation.symbol;
|
||||
}
|
||||
}
|
||||
|
||||
if (!excludeGlobals) {
|
||||
result = lookup(globals, name, meaning);
|
||||
}
|
||||
}
|
||||
if (!result) {
|
||||
if (originalLocation && isInJSFile(originalLocation) && originalLocation.parent) {
|
||||
if (isRequireCall(originalLocation.parent, /*requireStringLiteralLikeArgument*/ false)) {
|
||||
return requireSymbol;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nameNotFoundMessage) {
|
||||
if (propertyWithInvalidInitializer && onPropertyWithInvalidInitializer(originalLocation, name, propertyWithInvalidInitializer, result)) {
|
||||
return undefined;
|
||||
}
|
||||
if (!result) {
|
||||
onFailedToResolveSymbol(originalLocation, nameArg, meaning, nameNotFoundMessage);
|
||||
}
|
||||
else {
|
||||
onSuccessfullyResolvedSymbol(originalLocation, result, meaning, lastLocation, associatedDeclarationForContainingInitializerOrBindingName, withinDeferredContext);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function useOuterVariableScopeInParameter(result: Symbol, location: Node, lastLocation: Node) {
|
||||
const target = getEmitScriptTarget(compilerOptions);
|
||||
const functionLocation = location as FunctionLikeDeclaration;
|
||||
if (
|
||||
isParameter(lastLocation)
|
||||
&& functionLocation.body
|
||||
&& result.valueDeclaration
|
||||
&& result.valueDeclaration.pos >= functionLocation.body.pos
|
||||
&& result.valueDeclaration.end <= functionLocation.body.end
|
||||
) {
|
||||
// check for several cases where we introduce temporaries that require moving the name/initializer of the parameter to the body
|
||||
// - static field in a class expression
|
||||
// - optional chaining pre-es2020
|
||||
// - nullish coalesce pre-es2020
|
||||
// - spread assignment in binding pattern pre-es2017
|
||||
if (target >= ScriptTarget.ES2015) {
|
||||
let declarationRequiresScopeChange = getRequiresScopeChangeCache(functionLocation);
|
||||
if (declarationRequiresScopeChange === undefined) {
|
||||
declarationRequiresScopeChange = forEach(functionLocation.parameters, requiresScopeChange) || false;
|
||||
setRequiresScopeChangeCache(functionLocation, declarationRequiresScopeChange);
|
||||
}
|
||||
return !declarationRequiresScopeChange;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
function requiresScopeChange(node: ParameterDeclaration): boolean {
|
||||
return requiresScopeChangeWorker(node.name)
|
||||
|| !!node.initializer && requiresScopeChangeWorker(node.initializer);
|
||||
}
|
||||
|
||||
function requiresScopeChangeWorker(node: Node): boolean {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ArrowFunction:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.Constructor:
|
||||
// do not descend into these
|
||||
return false;
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
case SyntaxKind.PropertyAssignment:
|
||||
return requiresScopeChangeWorker((node as MethodDeclaration | AccessorDeclaration | PropertyAssignment).name);
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
// static properties in classes introduce temporary variables
|
||||
if (hasStaticModifier(node)) {
|
||||
return !emitStandardClassFields;
|
||||
}
|
||||
return requiresScopeChangeWorker((node as PropertyDeclaration).name);
|
||||
default:
|
||||
// null coalesce and optional chain pre-es2020 produce temporary variables
|
||||
if (isNullishCoalesce(node) || isOptionalChain(node)) {
|
||||
return target < ScriptTarget.ES2020;
|
||||
}
|
||||
if (isBindingElement(node) && node.dotDotDotToken && isObjectBindingPattern(node.parent)) {
|
||||
return target < ScriptTarget.ES2017;
|
||||
}
|
||||
if (isTypeNode(node)) return false;
|
||||
return forEachChild(node, requiresScopeChangeWorker) || false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getIsDeferredContext(location: Node, lastLocation: Node | undefined): boolean {
|
||||
if (location.kind !== SyntaxKind.ArrowFunction && location.kind !== SyntaxKind.FunctionExpression) {
|
||||
// initializers in instance property declaration of class like entities are executed in constructor and thus deferred
|
||||
return isTypeQueryNode(location) || ((
|
||||
isFunctionLikeDeclaration(location) ||
|
||||
(location.kind === SyntaxKind.PropertyDeclaration && !isStatic(location))
|
||||
) && (!lastLocation || lastLocation !== (location as SignatureDeclaration | PropertyDeclaration).name)); // A name is evaluated within the enclosing scope - so it shouldn't count as deferred
|
||||
}
|
||||
if (lastLocation && lastLocation === (location as FunctionExpression | ArrowFunction).name) {
|
||||
return false;
|
||||
}
|
||||
// generator functions and async functions are not inlined in control flow when immediately invoked
|
||||
if ((location as FunctionExpression | ArrowFunction).asteriskToken || hasSyntacticModifier(location, ModifierFlags.Async)) {
|
||||
return true;
|
||||
}
|
||||
return !getImmediatelyInvokedFunctionExpression(location);
|
||||
}
|
||||
|
||||
type SelfReferenceLocation =
|
||||
| FunctionDeclaration
|
||||
| ClassDeclaration
|
||||
| InterfaceDeclaration
|
||||
| EnumDeclaration
|
||||
| TypeAliasDeclaration
|
||||
| ModuleDeclaration;
|
||||
|
||||
function isSelfReferenceLocation(node: Node): node is SelfReferenceLocation {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
case SyntaxKind.TypeAliasDeclaration:
|
||||
case SyntaxKind.ModuleDeclaration: // For `namespace N { N; }`
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function isTypeParameterSymbolDeclaredInContainer(symbol: Symbol, container: Node) {
|
||||
if (symbol.declarations) {
|
||||
for (const decl of symbol.declarations) {
|
||||
if (decl.kind === SyntaxKind.TypeParameter) {
|
||||
const parent = isJSDocTemplateTag(decl.parent) ? getJSDocHost(decl.parent) : decl.parent;
|
||||
if (parent === container) {
|
||||
return !(isJSDocTemplateTag(decl.parent) && find((decl.parent.parent as JSDoc).tags, isJSDocTypeAlias));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user