Refactor name resolution to separate component (#57974)

This commit is contained in:
Titian Cernicova-Dragomir 2024-04-11 17:56:20 +01:00 committed by GitHub
parent 806d734047
commit f5bc6ddc75
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 830 additions and 744 deletions

File diff suppressed because it is too large Load Diff

View File

@ -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;
}
}