1import { 2 __String, AccessExpression, AnnotationDeclaration, AnnotationPropertyDeclaration, addRelatedInfo, append, appendIfUnique, ArrayBindingElement, ArrayLiteralExpression, 3 ArrowFunction, AssignmentDeclarationKind, BinaryExpression, BinaryOperatorToken, BindableAccessExpression, 4 BindableObjectDefinePropertyCall, BindablePropertyAssignmentExpression, BindableStaticAccessExpression, 5 BindableStaticNameExpression, BindableStaticPropertyAssignmentExpression, BindingElement, Block, 6 BreakOrContinueStatement, CallChain, CallExpression, CaseBlock, CaseClause, cast, CatchClause, ClassLikeDeclaration, 7 ClassStaticBlockDeclaration, CompilerOptions, concatenate, ConditionalExpression, ConditionalTypeNode, contains, 8 createBinaryExpressionTrampoline, createDiagnosticForNodeInSourceFile, createFileDiagnostic, createQueue, 9 createSymbolTable, Debug, Declaration, declarationNameToString, DeleteExpression, DestructuringAssignment, 10 DiagnosticCategory, DiagnosticMessage, DiagnosticRelatedInformation, Diagnostics, DiagnosticWithLocation, 11 DoStatement, DynamicNamedDeclaration, ElementAccessChain, ElementAccessExpression, EntityNameExpression, 12 EnumDeclaration, escapeLeadingUnderscores, ESMap, every, ExportAssignment, exportAssignmentIsAlias, 13 ExportDeclaration, ExportSpecifier, Expression, ExpressionStatement, findAncestor, FlowFlags, FlowLabel, FlowNode, 14 FlowReduceLabel, forEach, forEachChild, ForInOrOfStatement, ForStatement, FunctionDeclaration, FunctionExpression, 15 FunctionLikeDeclaration, GetAccessorDeclaration, getAssignedExpandoInitializer, getAssignmentDeclarationKind, 16 getAssignmentDeclarationPropertyAccessKind, getCombinedModifierFlags, getCombinedNodeFlags, getContainingClass, 17 getEffectiveContainerForJSDocTemplateTag, getElementOrPropertyAccessName, getEmitScriptTarget, 18 getEnclosingBlockScopeContainer, getErrorSpanForNode, getEscapedTextOfIdentifierOrLiteral, getExpandoInitializer, 19 getHostSignatureFromJSDoc, getImmediatelyInvokedFunctionExpression, getJSDocTypeTag, getLeftmostAccessExpression, 20 getNameOfDeclaration, getNameOrArgument, getNodeId, getRangesWhere, getRightMostAssignedExpression, 21 getSourceFileOfNode, getSourceTextOfNodeFromSourceFile, getSpanOfTokenAtPosition, getStrictOptionValue, 22 getSymbolNameForPrivateIdentifier, getTextOfIdentifierOrLiteral, getThisContainer, getTokenPosOfNode, 23 hasDynamicName, hasJSDocNodes, hasSyntacticModifier, Identifier, idText, IfStatement, ImportClause, 24 InternalSymbolName, isAliasableExpression, isAmbientModule, isAnnotationPropertyDeclaration, isAssignmentExpression, isAssignmentOperator, 25 isAssignmentTarget, isAsyncFunction, isAutoAccessorPropertyDeclaration, isBinaryExpression, 26 isBindableObjectDefinePropertyCall, isBindableStaticAccessExpression, isBindableStaticNameExpression, 27 isBindingPattern, isBlock, isBlockOrCatchScoped, isCallExpression, isClassStaticBlockDeclaration, 28 isConditionalTypeNode, isDeclaration, isDeclarationStatement, isDestructuringAssignment, isDottedName, isElementAccessExpression, 29 isEmptyObjectLiteral, isEntityNameExpression, isEnumConst, isEnumDeclaration, 30 isExportAssignment, isExportDeclaration, isExportsIdentifier, isExportSpecifier, isExpression, 31 isExpressionOfOptionalChainRoot, isExternalModule, isExternalOrCommonJsModule, isForInOrOfStatement, 32 isFunctionDeclaration, isFunctionLike, isFunctionLikeDeclaration, isFunctionLikeOrClassStaticBlockDeclaration, 33 isFunctionSymbol, isGlobalScopeAugmentation, isIdentifier, isIdentifierName, isInJSFile, isInTopLevelContext, 34 isJSDocConstructSignature, isJSDocEnumTag, isJSDocTemplateTag, isJSDocTypeAlias, isJsonSourceFile, 35 isLeftHandSideExpression, isLogicalOrCoalescingAssignmentOperator, isModuleAugmentationExternal, isModuleBlock, 36 isModuleDeclaration, isModuleExportsAccessExpression, isNamedDeclaration, isNamespaceExport, isNonNullExpression, 37 isNullishCoalesce, isObjectLiteralExpression, isObjectLiteralMethod, 38 isObjectLiteralOrClassExpressionMethodOrAccessor, isOmittedExpression, isOptionalChain, isOptionalChainRoot, 39 isOutermostOptionalChain, isParameterDeclaration, isParameterPropertyDeclaration, isParenthesizedExpression, 40 isPartOfTypeQuery, isPrefixUnaryExpression, isPrivateIdentifier, isPrologueDirective, 41 isPropertyAccessEntityNameExpression, isPropertyAccessExpression, isPropertyNameLiteral, isPrototypeAccess, 42 isPushOrUnshiftIdentifier, isRequireCall, isShorthandPropertyAssignment, isSignedNumericLiteral, isSourceFile, 43 isSpecialPropertyDeclaration, isStatement, isStatementButNotDeclaration, isStatic, isString, isStringLiteralLike, 44 isStringOrNumericLiteralLike, isThisInitializedDeclaration, isTypeAliasDeclaration, isTypeOfExpression, 45 isVariableDeclaration, isVariableDeclarationInitializedToBareOrAccessedRequire, isVariableStatement, 46 JSDocCallbackTag, JSDocClassTag, JSDocEnumTag, JSDocFunctionType, JSDocParameterTag, JSDocPropertyLikeTag, 47 JSDocSignature, JSDocTypedefTag, JSDocTypeLiteral, JsxAttribute, JsxAttributes, LabeledStatement, length, 48 LiteralLikeElementAccessExpression, Map, MappedTypeNode, MemoryDotting, MethodDeclaration, ModifierFlags, 49 ModuleBlock, ModuleDeclaration, Mutable, NamespaceExportDeclaration, Node, NodeArray, NodeFlags, nodeHasName, 50 nodeIsMissing, nodeIsPresent, NonNullChain, NonNullExpression, NumericLiteral, objectAllocator, 51 ObjectLiteralExpression, OptionalChain, ParameterDeclaration, ParenthesizedExpression, Pattern, 52 PatternAmbientModule, perfLogger, PerformanceDotting, PostfixUnaryExpression, PrefixUnaryExpression, PrivateIdentifier, 53 PropertyAccessChain, PropertyAccessExpression, PropertyDeclaration, PropertySignature, removeFileExtension, 54 ReturnStatement, ScriptTarget, Set, SetAccessorDeclaration, setParent, setParentRecursive, setValueDeclaration, 55 ShorthandPropertyAssignment, shouldPreserveConstEnums, SignatureDeclaration, skipParentheses, sliceAfter, some, 56 SourceFile, SpreadElement, Statement, StringLiteral, SwitchStatement, Symbol, SymbolFlags, symbolName, SymbolTable, 57 SyntaxKind, TextRange, ThrowStatement, TokenFlags, tokenToString, tracing, TracingNode, tryCast, tryParsePattern, 58 TryStatement, TypeLiteralNode, TypeOfExpression, TypeParameterDeclaration, unescapeLeadingUnderscores, 59 unreachableCodeIsError, unusedLabelIsError, VariableDeclaration, WhileStatement, WithStatement, 60} from "./_namespaces/ts"; 61import * as performance from "./_namespaces/ts.performance"; 62 63/** @internal */ 64export const enum ModuleInstanceState { 65 NonInstantiated = 0, 66 Instantiated = 1, 67 ConstEnumOnly = 2 68} 69 70interface ActiveLabel { 71 next: ActiveLabel | undefined; 72 name: __String; 73 breakTarget: FlowLabel; 74 continueTarget: FlowLabel | undefined; 75 referenced: boolean; 76} 77 78/** @internal */ 79export function getModuleInstanceState(node: ModuleDeclaration, visited?: ESMap<number, ModuleInstanceState | undefined>): ModuleInstanceState { 80 if (node.body && !node.body.parent) { 81 // getModuleInstanceStateForAliasTarget needs to walk up the parent chain, so parent pointers must be set on this tree already 82 setParent(node.body, node); 83 setParentRecursive(node.body, /*incremental*/ false); 84 } 85 return node.body ? getModuleInstanceStateCached(node.body, visited) : ModuleInstanceState.Instantiated; 86} 87 88function getModuleInstanceStateCached(node: Node, visited = new Map<number, ModuleInstanceState | undefined>()) { 89 const nodeId = getNodeId(node); 90 if (visited.has(nodeId)) { 91 return visited.get(nodeId) || ModuleInstanceState.NonInstantiated; 92 } 93 visited.set(nodeId, undefined); 94 const result = getModuleInstanceStateWorker(node, visited); 95 visited.set(nodeId, result); 96 return result; 97} 98 99function getModuleInstanceStateWorker(node: Node, visited: ESMap<number, ModuleInstanceState | undefined>): ModuleInstanceState { 100 // A module is uninstantiated if it contains only 101 switch (node.kind) { 102 // 1. interface declarations, type alias declarations 103 case SyntaxKind.InterfaceDeclaration: 104 case SyntaxKind.TypeAliasDeclaration: 105 return ModuleInstanceState.NonInstantiated; 106 // 2. const enum declarations 107 case SyntaxKind.EnumDeclaration: 108 if (isEnumConst(node as EnumDeclaration)) { 109 return ModuleInstanceState.ConstEnumOnly; 110 } 111 break; 112 // 3. non-exported import declarations 113 case SyntaxKind.ImportDeclaration: 114 case SyntaxKind.ImportEqualsDeclaration: 115 if (!(hasSyntacticModifier(node, ModifierFlags.Export))) { 116 return ModuleInstanceState.NonInstantiated; 117 } 118 break; 119 // 4. Export alias declarations pointing at only uninstantiated modules or things uninstantiated modules contain 120 case SyntaxKind.ExportDeclaration: 121 const exportDeclaration = node as ExportDeclaration; 122 if (!exportDeclaration.moduleSpecifier && exportDeclaration.exportClause && exportDeclaration.exportClause.kind === SyntaxKind.NamedExports) { 123 let state = ModuleInstanceState.NonInstantiated; 124 for (const specifier of exportDeclaration.exportClause.elements) { 125 const specifierState = getModuleInstanceStateForAliasTarget(specifier, visited); 126 if (specifierState > state) { 127 state = specifierState; 128 } 129 if (state === ModuleInstanceState.Instantiated) { 130 return state; 131 } 132 } 133 return state; 134 } 135 break; 136 // 5. other uninstantiated module declarations. 137 case SyntaxKind.ModuleBlock: { 138 let state = ModuleInstanceState.NonInstantiated; 139 forEachChild(node, n => { 140 const childState = getModuleInstanceStateCached(n, visited); 141 switch (childState) { 142 case ModuleInstanceState.NonInstantiated: 143 // child is non-instantiated - continue searching 144 return; 145 case ModuleInstanceState.ConstEnumOnly: 146 // child is const enum only - record state and continue searching 147 state = ModuleInstanceState.ConstEnumOnly; 148 return; 149 case ModuleInstanceState.Instantiated: 150 // child is instantiated - record state and stop 151 state = ModuleInstanceState.Instantiated; 152 return true; 153 default: 154 Debug.assertNever(childState); 155 } 156 }); 157 return state; 158 } 159 case SyntaxKind.ModuleDeclaration: 160 return getModuleInstanceState(node as ModuleDeclaration, visited); 161 case SyntaxKind.Identifier: 162 // Only jsdoc typedef definition can exist in jsdoc namespace, and it should 163 // be considered the same as type alias 164 if ((node as Identifier).isInJSDocNamespace) { 165 return ModuleInstanceState.NonInstantiated; 166 } 167 } 168 return ModuleInstanceState.Instantiated; 169} 170 171function getModuleInstanceStateForAliasTarget(specifier: ExportSpecifier, visited: ESMap<number, ModuleInstanceState | undefined>) { 172 const name = specifier.propertyName || specifier.name; 173 let p: Node | undefined = specifier.parent; 174 while (p) { 175 if (isBlock(p) || isModuleBlock(p) || isSourceFile(p)) { 176 const statements = p.statements; 177 let found: ModuleInstanceState | undefined; 178 for (const statement of statements) { 179 if (nodeHasName(statement, name)) { 180 if (!statement.parent) { 181 setParent(statement, p); 182 setParentRecursive(statement, /*incremental*/ false); 183 } 184 const state = getModuleInstanceStateCached(statement, visited); 185 if (found === undefined || state > found) { 186 found = state; 187 } 188 if (found === ModuleInstanceState.Instantiated) { 189 return found; 190 } 191 } 192 } 193 if (found !== undefined) { 194 return found; 195 } 196 } 197 p = p.parent; 198 } 199 return ModuleInstanceState.Instantiated; // Couldn't locate, assume could refer to a value 200} 201 202const enum ContainerFlags { 203 // The current node is not a container, and no container manipulation should happen before 204 // recursing into it. 205 None = 0, 206 207 // The current node is a container. It should be set as the current container (and block- 208 // container) before recursing into it. The current node does not have locals. Examples: 209 // 210 // Classes, ObjectLiterals, TypeLiterals, Interfaces... 211 IsContainer = 1 << 0, 212 213 // The current node is a block-scoped-container. It should be set as the current block- 214 // container before recursing into it. Examples: 215 // 216 // Blocks (when not parented by functions), Catch clauses, For/For-in/For-of statements... 217 IsBlockScopedContainer = 1 << 1, 218 219 // The current node is the container of a control flow path. The current control flow should 220 // be saved and restored, and a new control flow initialized within the container. 221 IsControlFlowContainer = 1 << 2, 222 223 IsFunctionLike = 1 << 3, 224 IsFunctionExpression = 1 << 4, 225 HasLocals = 1 << 5, 226 IsInterface = 1 << 6, 227 IsObjectLiteralOrClassExpressionMethodOrAccessor = 1 << 7, 228} 229 230function initFlowNode<T extends FlowNode>(node: T) { 231 Debug.attachFlowNodeDebugInfo(node); 232 return node; 233} 234 235const binder = createBinder(); 236 237/** @internal */ 238export function bindSourceFile(file: SourceFile, options: CompilerOptions) { 239 const recordInfo = MemoryDotting.recordStage(MemoryDotting.BINDE_SOURCE_FILE); 240 performance.mark("beforeBind"); 241 perfLogger.logStartBindFile("" + file.fileName); 242 binder(file, options); 243 perfLogger.logStopBindFile(); 244 performance.mark("afterBind"); 245 MemoryDotting.stopRecordStage(recordInfo); 246 performance.measure("Bind", "beforeBind", "afterBind"); 247} 248 249function createBinder(): (file: SourceFile, options: CompilerOptions) => void { 250 /* eslint-disable no-var */ 251 var file: SourceFile; 252 var options: CompilerOptions; 253 var languageVersion: ScriptTarget; 254 var parent: Node; 255 var container: Node; 256 var thisParentContainer: Node; // Container one level up 257 var blockScopeContainer: Node; 258 var lastContainer: Node; 259 var delayedTypeAliases: (JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag)[]; 260 var seenThisKeyword: boolean; 261 262 // state used by control flow analysis 263 var currentFlow: FlowNode; 264 var currentBreakTarget: FlowLabel | undefined; 265 var currentContinueTarget: FlowLabel | undefined; 266 var currentReturnTarget: FlowLabel | undefined; 267 var currentTrueTarget: FlowLabel | undefined; 268 var currentFalseTarget: FlowLabel | undefined; 269 var currentExceptionTarget: FlowLabel | undefined; 270 var preSwitchCaseFlow: FlowNode | undefined; 271 var activeLabelList: ActiveLabel | undefined; 272 var hasExplicitReturn: boolean; 273 274 // state used for emit helpers 275 var emitFlags: NodeFlags; 276 277 // If this file is an external module, then it is automatically in strict-mode according to 278 // ES6. If it is not an external module, then we'll determine if it is in strict mode or 279 // not depending on if we see "use strict" in certain places or if we hit a class/namespace 280 // or if compiler options contain alwaysStrict. 281 var inStrictMode: boolean; 282 283 // If we are binding an assignment pattern, we will bind certain expressions differently. 284 var inAssignmentPattern = false; 285 286 var symbolCount = 0; 287 288 var Symbol: new (flags: SymbolFlags, name: __String) => Symbol; 289 var classifiableNames: Set<__String>; 290 291 var unreachableFlow: FlowNode = { flags: FlowFlags.Unreachable }; 292 var reportedUnreachableFlow: FlowNode = { flags: FlowFlags.Unreachable }; 293 var bindBinaryExpressionFlow = createBindBinaryExpressionFlow(); 294 /* eslint-enable no-var */ 295 296 return bindSourceFile; 297 298 /** 299 * Inside the binder, we may create a diagnostic for an as-yet unbound node (with potentially no parent pointers, implying no accessible source file) 300 * If so, the node _must_ be in the current file (as that's the only way anything could have traversed to it to yield it as the error node) 301 * This version of `createDiagnosticForNode` uses the binder's context to account for this, and always yields correct diagnostics even in these situations. 302 */ 303 function createDiagnosticForNode(node: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): DiagnosticWithLocation { 304 return createDiagnosticForNodeInSourceFile(getSourceFileOfNode(node) || file, node, message, arg0, arg1, arg2); 305 } 306 307 function bindSourceFile(f: SourceFile, opts: CompilerOptions) { 308 file = f; 309 options = opts; 310 languageVersion = getEmitScriptTarget(options); 311 inStrictMode = bindInStrictMode(file, opts); 312 classifiableNames = new Set(); 313 symbolCount = 0; 314 315 Symbol = objectAllocator.getSymbolConstructor(); 316 317 // Attach debugging information if necessary 318 Debug.attachFlowNodeDebugInfo(unreachableFlow); 319 Debug.attachFlowNodeDebugInfo(reportedUnreachableFlow); 320 321 if (!file.locals) { 322 tracing?.push(tracing.Phase.Bind, "bindSourceFile", { path: file.path }, /*separateBeginAndEnd*/ true); 323 PerformanceDotting.start("bindSourceFile", file.fileName); 324 bind(file); 325 PerformanceDotting.stop("bindSourceFile"); 326 tracing?.pop(); 327 file.symbolCount = symbolCount; 328 file.classifiableNames = classifiableNames; 329 delayedBindJSDocTypedefTag(); 330 } 331 332 file = undefined!; 333 options = undefined!; 334 languageVersion = undefined!; 335 parent = undefined!; 336 container = undefined!; 337 thisParentContainer = undefined!; 338 blockScopeContainer = undefined!; 339 lastContainer = undefined!; 340 delayedTypeAliases = undefined!; 341 seenThisKeyword = false; 342 currentFlow = undefined!; 343 currentBreakTarget = undefined; 344 currentContinueTarget = undefined; 345 currentReturnTarget = undefined; 346 currentTrueTarget = undefined; 347 currentFalseTarget = undefined; 348 currentExceptionTarget = undefined; 349 activeLabelList = undefined; 350 hasExplicitReturn = false; 351 inAssignmentPattern = false; 352 emitFlags = NodeFlags.None; 353 } 354 355 function bindInStrictMode(file: SourceFile, opts: CompilerOptions): boolean { 356 if (getStrictOptionValue(opts, "alwaysStrict") && !file.isDeclarationFile) { 357 // bind in strict mode source files with alwaysStrict option 358 return true; 359 } 360 else { 361 return !!file.externalModuleIndicator; 362 } 363 } 364 365 function createSymbol(flags: SymbolFlags, name: __String): Symbol { 366 symbolCount++; 367 return new Symbol(flags, name); 368 } 369 370 function addDeclarationToSymbol(symbol: Symbol, node: Declaration, symbolFlags: SymbolFlags) { 371 symbol.flags |= symbolFlags; 372 373 node.symbol = symbol; 374 symbol.declarations = appendIfUnique(symbol.declarations, node); 375 376 if (symbolFlags & (SymbolFlags.Class | SymbolFlags.Enum | SymbolFlags.Module | SymbolFlags.Variable) && !symbol.exports) { 377 symbol.exports = createSymbolTable(); 378 } 379 380 if (symbolFlags & (SymbolFlags.Class | SymbolFlags.Interface | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && !symbol.members) { 381 symbol.members = createSymbolTable(); 382 } 383 384 // On merge of const enum module with class or function, reset const enum only flag (namespaces will already recalculate) 385 if (symbol.constEnumOnlyModule && (symbol.flags & (SymbolFlags.Function | SymbolFlags.Class | SymbolFlags.RegularEnum))) { 386 symbol.constEnumOnlyModule = false; 387 } 388 389 if (symbolFlags & SymbolFlags.Value) { 390 setValueDeclaration(symbol, node); 391 } 392 } 393 394 // Should not be called on a declaration with a computed property name, 395 // unless it is a well known Symbol. 396 function getDeclarationName(node: Declaration): __String | undefined { 397 if (node.kind === SyntaxKind.ExportAssignment) { 398 return (node as ExportAssignment).isExportEquals ? InternalSymbolName.ExportEquals : InternalSymbolName.Default; 399 } 400 401 const name = getNameOfDeclaration(node); 402 if (name) { 403 if (isAmbientModule(node)) { 404 const moduleName = getTextOfIdentifierOrLiteral(name as Identifier | StringLiteral); 405 return (isGlobalScopeAugmentation(node as ModuleDeclaration) ? "__global" : `"${moduleName}"`) as __String; 406 } 407 if (name.kind === SyntaxKind.ComputedPropertyName) { 408 const nameExpression = name.expression; 409 // treat computed property names where expression is string/numeric literal as just string/numeric literal 410 if (isStringOrNumericLiteralLike(nameExpression)) { 411 return escapeLeadingUnderscores(nameExpression.text); 412 } 413 if (isSignedNumericLiteral(nameExpression)) { 414 return tokenToString(nameExpression.operator) + nameExpression.operand.text as __String; 415 } 416 else { 417 Debug.fail("Only computed properties with literal names have declaration names"); 418 } 419 } 420 if (isPrivateIdentifier(name)) { 421 // containingClass exists because private names only allowed inside classes 422 const containingClass = getContainingClass(node); 423 if (!containingClass) { 424 // we can get here in cases where there is already a parse error. 425 return undefined; 426 } 427 const containingClassSymbol = containingClass.symbol; 428 return getSymbolNameForPrivateIdentifier(containingClassSymbol, name.escapedText); 429 } 430 return isPropertyNameLiteral(name) ? getEscapedTextOfIdentifierOrLiteral(name) : undefined; 431 } 432 switch (node.kind) { 433 case SyntaxKind.Constructor: 434 return InternalSymbolName.Constructor; 435 case SyntaxKind.FunctionType: 436 case SyntaxKind.CallSignature: 437 case SyntaxKind.JSDocSignature: 438 return InternalSymbolName.Call; 439 case SyntaxKind.ConstructorType: 440 case SyntaxKind.ConstructSignature: 441 return InternalSymbolName.New; 442 case SyntaxKind.IndexSignature: 443 return InternalSymbolName.Index; 444 case SyntaxKind.ExportDeclaration: 445 return InternalSymbolName.ExportStar; 446 case SyntaxKind.SourceFile: 447 // json file should behave as 448 // module.exports = ... 449 return InternalSymbolName.ExportEquals; 450 case SyntaxKind.BinaryExpression: 451 if (getAssignmentDeclarationKind(node as BinaryExpression) === AssignmentDeclarationKind.ModuleExports) { 452 // module.exports = ... 453 return InternalSymbolName.ExportEquals; 454 } 455 Debug.fail("Unknown binary declaration kind"); 456 case SyntaxKind.JSDocFunctionType: 457 return (isJSDocConstructSignature(node) ? InternalSymbolName.New : InternalSymbolName.Call); 458 case SyntaxKind.Parameter: 459 // Parameters with names are handled at the top of this function. Parameters 460 // without names can only come from JSDocFunctionTypes. 461 Debug.assert(node.parent.kind === SyntaxKind.JSDocFunctionType, "Impossible parameter parent kind", () => `parent is: ${Debug.formatSyntaxKind(node.parent.kind)}, expected JSDocFunctionType`); 462 const functionType = node.parent as JSDocFunctionType; 463 const index = functionType.parameters.indexOf(node as ParameterDeclaration); 464 return "arg" + index as __String; 465 } 466 } 467 468 function getDisplayName(node: Declaration): string { 469 return isNamedDeclaration(node) ? declarationNameToString(node.name) : unescapeLeadingUnderscores(Debug.checkDefined(getDeclarationName(node))); 470 } 471 472 /** 473 * Declares a Symbol for the node and adds it to symbols. Reports errors for conflicting identifier names. 474 * @param symbolTable - The symbol table which node will be added to. 475 * @param parent - node's parent declaration. 476 * @param node - The declaration to be added to the symbol table 477 * @param includes - The SymbolFlags that node has in addition to its declaration type (eg: export, ambient, etc.) 478 * @param excludes - The flags which node cannot be declared alongside in a symbol table. Used to report forbidden declarations. 479 */ 480 function declareSymbol(symbolTable: SymbolTable, parent: Symbol | undefined, node: Declaration, includes: SymbolFlags, excludes: SymbolFlags, isReplaceableByMethod?: boolean, isComputedName?: boolean): Symbol { 481 Debug.assert(isComputedName || !hasDynamicName(node)); 482 483 const isDefaultExport = hasSyntacticModifier(node, ModifierFlags.Default) || isExportSpecifier(node) && node.name.escapedText === "default"; 484 485 // The exported symbol for an export default function/class node is always named "default" 486 const name = isComputedName ? InternalSymbolName.Computed 487 : isDefaultExport && parent ? InternalSymbolName.Default 488 : getDeclarationName(node); 489 490 let symbol: Symbol | undefined; 491 if (name === undefined) { 492 symbol = createSymbol(SymbolFlags.None, InternalSymbolName.Missing); 493 } 494 else { 495 // Check and see if the symbol table already has a symbol with this name. If not, 496 // create a new symbol with this name and add it to the table. Note that we don't 497 // give the new symbol any flags *yet*. This ensures that it will not conflict 498 // with the 'excludes' flags we pass in. 499 // 500 // If we do get an existing symbol, see if it conflicts with the new symbol we're 501 // creating. For example, a 'var' symbol and a 'class' symbol will conflict within 502 // the same symbol table. If we have a conflict, report the issue on each 503 // declaration we have for this symbol, and then create a new symbol for this 504 // declaration. 505 // 506 // Note that when properties declared in Javascript constructors 507 // (marked by isReplaceableByMethod) conflict with another symbol, the property loses. 508 // Always. This allows the common Javascript pattern of overwriting a prototype method 509 // with an bound instance method of the same type: `this.method = this.method.bind(this)` 510 // 511 // If we created a new symbol, either because we didn't have a symbol with this name 512 // in the symbol table, or we conflicted with an existing symbol, then just add this 513 // node as the sole declaration of the new symbol. 514 // 515 // Otherwise, we'll be merging into a compatible existing symbol (for example when 516 // you have multiple 'vars' with the same name in the same container). In this case 517 // just add this node into the declarations list of the symbol. 518 symbol = symbolTable.get(name); 519 520 if (includes & SymbolFlags.Classifiable) { 521 classifiableNames.add(name); 522 } 523 524 if (!symbol) { 525 symbolTable.set(name, symbol = createSymbol(SymbolFlags.None, name)); 526 if (isReplaceableByMethod) symbol.isReplaceableByMethod = true; 527 } 528 else if (isReplaceableByMethod && !symbol.isReplaceableByMethod) { 529 // A symbol already exists, so don't add this as a declaration. 530 return symbol; 531 } 532 else if (symbol.flags & excludes) { 533 if (symbol.isReplaceableByMethod) { 534 // Javascript constructor-declared symbols can be discarded in favor of 535 // prototype symbols like methods. 536 symbolTable.set(name, symbol = createSymbol(SymbolFlags.None, name)); 537 } 538 else if (!(includes & SymbolFlags.Variable && symbol.flags & SymbolFlags.Assignment)) { 539 // Assignment declarations are allowed to merge with variables, no matter what other flags they have. 540 if (isNamedDeclaration(node)) { 541 setParent(node.name, node); 542 } 543 // Report errors every position with duplicate declaration 544 // Report errors on previous encountered declarations 545 let message = symbol.flags & SymbolFlags.BlockScopedVariable 546 ? Diagnostics.Cannot_redeclare_block_scoped_variable_0 547 : Diagnostics.Duplicate_identifier_0; 548 let messageNeedsName = true; 549 550 if (symbol.flags & SymbolFlags.Enum || includes & SymbolFlags.Enum) { 551 message = Diagnostics.Enum_declarations_can_only_merge_with_namespace_or_other_enum_declarations; 552 messageNeedsName = false; 553 } 554 555 let multipleDefaultExports = false; 556 if (length(symbol.declarations)) { 557 // If the current node is a default export of some sort, then check if 558 // there are any other default exports that we need to error on. 559 // We'll know whether we have other default exports depending on if `symbol` already has a declaration list set. 560 if (isDefaultExport) { 561 message = Diagnostics.A_module_cannot_have_multiple_default_exports; 562 messageNeedsName = false; 563 multipleDefaultExports = true; 564 } 565 else { 566 // This is to properly report an error in the case "export default { }" is after export default of class declaration or function declaration. 567 // Error on multiple export default in the following case: 568 // 1. multiple export default of class declaration or function declaration by checking NodeFlags.Default 569 // 2. multiple export default of export assignment. This one doesn't have NodeFlags.Default on (as export default doesn't considered as modifiers) 570 if (symbol.declarations && symbol.declarations.length && 571 (node.kind === SyntaxKind.ExportAssignment && !(node as ExportAssignment).isExportEquals)) { 572 message = Diagnostics.A_module_cannot_have_multiple_default_exports; 573 messageNeedsName = false; 574 multipleDefaultExports = true; 575 } 576 } 577 } 578 579 const relatedInformation: DiagnosticRelatedInformation[] = []; 580 if (isTypeAliasDeclaration(node) && nodeIsMissing(node.type) && hasSyntacticModifier(node, ModifierFlags.Export) && symbol.flags & (SymbolFlags.Alias | SymbolFlags.Type | SymbolFlags.Namespace)) { 581 // export type T; - may have meant export type { T }? 582 relatedInformation.push(createDiagnosticForNode(node, Diagnostics.Did_you_mean_0, `export type { ${unescapeLeadingUnderscores(node.name.escapedText)} }`)); 583 } 584 585 const declarationName = getNameOfDeclaration(node) || node; 586 forEach(symbol.declarations, (declaration, index) => { 587 const decl = getNameOfDeclaration(declaration) || declaration; 588 const diag = createDiagnosticForNode(decl, message, messageNeedsName ? getDisplayName(declaration) : undefined); 589 file.bindDiagnostics.push( 590 multipleDefaultExports ? addRelatedInfo(diag, createDiagnosticForNode(declarationName, index === 0 ? Diagnostics.Another_export_default_is_here : Diagnostics.and_here)) : diag 591 ); 592 if (multipleDefaultExports) { 593 relatedInformation.push(createDiagnosticForNode(decl, Diagnostics.The_first_export_default_is_here)); 594 } 595 }); 596 597 const diag = createDiagnosticForNode(declarationName, message, messageNeedsName ? getDisplayName(node) : undefined); 598 file.bindDiagnostics.push(addRelatedInfo(diag, ...relatedInformation)); 599 600 symbol = createSymbol(SymbolFlags.None, name); 601 } 602 } 603 } 604 605 addDeclarationToSymbol(symbol, node, includes); 606 if (symbol.parent) { 607 Debug.assert(symbol.parent === parent, "Existing symbol parent should match new one"); 608 } 609 else { 610 symbol.parent = parent; 611 } 612 613 return symbol; 614 } 615 616 function declareModuleMember(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): Symbol { 617 const hasExportModifier = !!(getCombinedModifierFlags(node) & ModifierFlags.Export) || jsdocTreatAsExported(node); 618 if (symbolFlags & SymbolFlags.Alias) { 619 if (node.kind === SyntaxKind.ExportSpecifier || (node.kind === SyntaxKind.ImportEqualsDeclaration && hasExportModifier)) { 620 return declareSymbol(container.symbol.exports!, container.symbol, node, symbolFlags, symbolExcludes); 621 } 622 else { 623 return declareSymbol(container.locals!, /*parent*/ undefined, node, symbolFlags, symbolExcludes); 624 } 625 } 626 else { 627 // Exported module members are given 2 symbols: A local symbol that is classified with an ExportValue flag, 628 // and an associated export symbol with all the correct flags set on it. There are 2 main reasons: 629 // 630 // 1. We treat locals and exports of the same name as mutually exclusive within a container. 631 // That means the binder will issue a Duplicate Identifier error if you mix locals and exports 632 // with the same name in the same container. 633 // TODO: Make this a more specific error and decouple it from the exclusion logic. 634 // 2. When we checkIdentifier in the checker, we set its resolved symbol to the local symbol, 635 // but return the export symbol (by calling getExportSymbolOfValueSymbolIfExported). That way 636 // when the emitter comes back to it, it knows not to qualify the name if it was found in a containing scope. 637 638 // NOTE: Nested ambient modules always should go to to 'locals' table to prevent their automatic merge 639 // during global merging in the checker. Why? The only case when ambient module is permitted inside another module is module augmentation 640 // and this case is specially handled. Module augmentations should only be merged with original module definition 641 // and should never be merged directly with other augmentation, and the latter case would be possible if automatic merge is allowed. 642 if (isJSDocTypeAlias(node)) Debug.assert(isInJSFile(node)); // We shouldn't add symbols for JSDoc nodes if not in a JS file. 643 if (!isAmbientModule(node) && (hasExportModifier || container.flags & NodeFlags.ExportContext)) { 644 if (!container.locals || (hasSyntacticModifier(node, ModifierFlags.Default) && !getDeclarationName(node))) { 645 return declareSymbol(container.symbol.exports!, container.symbol, node, symbolFlags, symbolExcludes); // No local symbol for an unnamed default! 646 } 647 const exportKind = symbolFlags & SymbolFlags.Value ? SymbolFlags.ExportValue : 0; 648 const local = declareSymbol(container.locals, /*parent*/ undefined, node, exportKind, symbolExcludes); 649 local.exportSymbol = declareSymbol(container.symbol.exports!, container.symbol, node, symbolFlags, symbolExcludes); 650 node.localSymbol = local; 651 return local; 652 } 653 else { 654 return declareSymbol(container.locals!, /*parent*/ undefined, node, symbolFlags, symbolExcludes); 655 } 656 } 657 } 658 659 function jsdocTreatAsExported(node: Node) { 660 if (node.parent && isModuleDeclaration(node)) { 661 node = node.parent; 662 } 663 if (!isJSDocTypeAlias(node)) return false; 664 // jsdoc typedef handling is a bit of a doozy, but to summarize, treat the typedef as exported if: 665 // 1. It has an explicit name (since by default typedefs are always directly exported, either at the top level or in a container), or 666 if (!isJSDocEnumTag(node) && !!node.fullName) return true; 667 // 2. The thing a nameless typedef pulls its name from is implicitly a direct export (either by assignment or actual export flag). 668 const declName = getNameOfDeclaration(node); 669 if (!declName) return false; 670 if (isPropertyAccessEntityNameExpression(declName.parent) && isTopLevelNamespaceAssignment(declName.parent)) return true; 671 if (isDeclaration(declName.parent) && getCombinedModifierFlags(declName.parent) & ModifierFlags.Export) return true; 672 // This could potentially be simplified by having `delayedBindJSDocTypedefTag` pass in an override for `hasExportModifier`, since it should 673 // already have calculated and branched on most of this. 674 return false; 675 } 676 677 // All container nodes are kept on a linked list in declaration order. This list is used by 678 // the getLocalNameOfContainer function in the type checker to validate that the local name 679 // used for a container is unique. 680 function bindContainer(node: Mutable<Node>, containerFlags: ContainerFlags) { 681 // Before we recurse into a node's children, we first save the existing parent, container 682 // and block-container. Then after we pop out of processing the children, we restore 683 // these saved values. 684 const saveContainer = container; 685 const saveThisParentContainer = thisParentContainer; 686 const savedBlockScopeContainer = blockScopeContainer; 687 688 // Depending on what kind of node this is, we may have to adjust the current container 689 // and block-container. If the current node is a container, then it is automatically 690 // considered the current block-container as well. Also, for containers that we know 691 // may contain locals, we eagerly initialize the .locals field. We do this because 692 // it's highly likely that the .locals will be needed to place some child in (for example, 693 // a parameter, or variable declaration). 694 // 695 // However, we do not proactively create the .locals for block-containers because it's 696 // totally normal and common for block-containers to never actually have a block-scoped 697 // variable in them. We don't want to end up allocating an object for every 'block' we 698 // run into when most of them won't be necessary. 699 // 700 // Finally, if this is a block-container, then we clear out any existing .locals object 701 // it may contain within it. This happens in incremental scenarios. Because we can be 702 // reusing a node from a previous compilation, that node may have had 'locals' created 703 // for it. We must clear this so we don't accidentally move any stale data forward from 704 // a previous compilation. 705 if (containerFlags & ContainerFlags.IsContainer) { 706 if (node.kind !== SyntaxKind.ArrowFunction) { 707 thisParentContainer = container; 708 } 709 container = blockScopeContainer = node; 710 if (containerFlags & ContainerFlags.HasLocals) { 711 container.locals = createSymbolTable(); 712 } 713 addToContainerChain(container); 714 } 715 else if (containerFlags & ContainerFlags.IsBlockScopedContainer) { 716 blockScopeContainer = node; 717 blockScopeContainer.locals = undefined; 718 } 719 if (containerFlags & ContainerFlags.IsControlFlowContainer) { 720 const saveCurrentFlow = currentFlow; 721 const saveBreakTarget = currentBreakTarget; 722 const saveContinueTarget = currentContinueTarget; 723 const saveReturnTarget = currentReturnTarget; 724 const saveExceptionTarget = currentExceptionTarget; 725 const saveActiveLabelList = activeLabelList; 726 const saveHasExplicitReturn = hasExplicitReturn; 727 const isImmediatelyInvoked = 728 (containerFlags & ContainerFlags.IsFunctionExpression && 729 !hasSyntacticModifier(node, ModifierFlags.Async) && 730 !(node as FunctionLikeDeclaration).asteriskToken && 731 !!getImmediatelyInvokedFunctionExpression(node)) || 732 node.kind === SyntaxKind.ClassStaticBlockDeclaration; 733 // A non-async, non-generator IIFE is considered part of the containing control flow. Return statements behave 734 // similarly to break statements that exit to a label just past the statement body. 735 if (!isImmediatelyInvoked) { 736 currentFlow = initFlowNode({ flags: FlowFlags.Start }); 737 if (containerFlags & (ContainerFlags.IsFunctionExpression | ContainerFlags.IsObjectLiteralOrClassExpressionMethodOrAccessor)) { 738 currentFlow.node = node as FunctionExpression | ArrowFunction | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration; 739 } 740 } 741 // We create a return control flow graph for IIFEs and constructors. For constructors 742 // we use the return control flow graph in strict property initialization checks. 743 currentReturnTarget = isImmediatelyInvoked || node.kind === SyntaxKind.Constructor || (isInJSFile(node) && (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression)) ? createBranchLabel() : undefined; 744 currentExceptionTarget = undefined; 745 currentBreakTarget = undefined; 746 currentContinueTarget = undefined; 747 activeLabelList = undefined; 748 hasExplicitReturn = false; 749 bindChildren(node); 750 // Reset all reachability check related flags on node (for incremental scenarios) 751 node.flags &= ~NodeFlags.ReachabilityAndEmitFlags; 752 if (!(currentFlow.flags & FlowFlags.Unreachable) && containerFlags & ContainerFlags.IsFunctionLike && nodeIsPresent((node as FunctionLikeDeclaration | ClassStaticBlockDeclaration).body)) { 753 node.flags |= NodeFlags.HasImplicitReturn; 754 if (hasExplicitReturn) node.flags |= NodeFlags.HasExplicitReturn; 755 (node as FunctionLikeDeclaration | ClassStaticBlockDeclaration).endFlowNode = currentFlow; 756 } 757 if (node.kind === SyntaxKind.SourceFile) { 758 node.flags |= emitFlags; 759 (node as SourceFile).endFlowNode = currentFlow; 760 } 761 762 if (currentReturnTarget) { 763 addAntecedent(currentReturnTarget, currentFlow); 764 currentFlow = finishFlowLabel(currentReturnTarget); 765 if (node.kind === SyntaxKind.Constructor || node.kind === SyntaxKind.ClassStaticBlockDeclaration || (isInJSFile(node) && (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression))) { 766 (node as FunctionLikeDeclaration | ClassStaticBlockDeclaration).returnFlowNode = currentFlow; 767 } 768 } 769 if (!isImmediatelyInvoked) { 770 currentFlow = saveCurrentFlow; 771 } 772 currentBreakTarget = saveBreakTarget; 773 currentContinueTarget = saveContinueTarget; 774 currentReturnTarget = saveReturnTarget; 775 currentExceptionTarget = saveExceptionTarget; 776 activeLabelList = saveActiveLabelList; 777 hasExplicitReturn = saveHasExplicitReturn; 778 } 779 else if (containerFlags & ContainerFlags.IsInterface) { 780 seenThisKeyword = false; 781 bindChildren(node); 782 node.flags = seenThisKeyword ? node.flags | NodeFlags.ContainsThis : node.flags & ~NodeFlags.ContainsThis; 783 } 784 else { 785 bindChildren(node); 786 } 787 788 container = saveContainer; 789 thisParentContainer = saveThisParentContainer; 790 blockScopeContainer = savedBlockScopeContainer; 791 } 792 793 function bindEachFunctionsFirst(nodes: NodeArray<Node> | undefined): void { 794 bindEach(nodes, n => n.kind === SyntaxKind.FunctionDeclaration ? bind(n) : undefined); 795 bindEach(nodes, n => n.kind !== SyntaxKind.FunctionDeclaration ? bind(n) : undefined); 796 } 797 798 function bindEach(nodes: NodeArray<Node> | undefined, bindFunction: (node: Node) => void = bind): void { 799 if (nodes === undefined) { 800 return; 801 } 802 803 forEach(nodes, bindFunction); 804 } 805 806 function bindEachChild(node: Node) { 807 forEachChild(node, bind, bindEach); 808 } 809 810 function bindChildren(node: Node): void { 811 const saveInAssignmentPattern = inAssignmentPattern; 812 // Most nodes aren't valid in an assignment pattern, so we clear the value here 813 // and set it before we descend into nodes that could actually be part of an assignment pattern. 814 inAssignmentPattern = false; 815 if (checkUnreachable(node)) { 816 bindEachChild(node); 817 bindJSDoc(node); 818 inAssignmentPattern = saveInAssignmentPattern; 819 return; 820 } 821 if (node.kind >= SyntaxKind.FirstStatement && node.kind <= SyntaxKind.LastStatement && !options.allowUnreachableCode) { 822 node.flowNode = currentFlow; 823 } 824 switch (node.kind) { 825 case SyntaxKind.WhileStatement: 826 bindWhileStatement(node as WhileStatement); 827 break; 828 case SyntaxKind.DoStatement: 829 bindDoStatement(node as DoStatement); 830 break; 831 case SyntaxKind.ForStatement: 832 bindForStatement(node as ForStatement); 833 break; 834 case SyntaxKind.ForInStatement: 835 case SyntaxKind.ForOfStatement: 836 bindForInOrForOfStatement(node as ForInOrOfStatement); 837 break; 838 case SyntaxKind.IfStatement: 839 bindIfStatement(node as IfStatement); 840 break; 841 case SyntaxKind.ReturnStatement: 842 case SyntaxKind.ThrowStatement: 843 bindReturnOrThrow(node as ReturnStatement | ThrowStatement); 844 break; 845 case SyntaxKind.BreakStatement: 846 case SyntaxKind.ContinueStatement: 847 bindBreakOrContinueStatement(node as BreakOrContinueStatement); 848 break; 849 case SyntaxKind.TryStatement: 850 bindTryStatement(node as TryStatement); 851 break; 852 case SyntaxKind.SwitchStatement: 853 bindSwitchStatement(node as SwitchStatement); 854 break; 855 case SyntaxKind.CaseBlock: 856 bindCaseBlock(node as CaseBlock); 857 break; 858 case SyntaxKind.CaseClause: 859 bindCaseClause(node as CaseClause); 860 break; 861 case SyntaxKind.ExpressionStatement: 862 bindExpressionStatement(node as ExpressionStatement); 863 break; 864 case SyntaxKind.LabeledStatement: 865 bindLabeledStatement(node as LabeledStatement); 866 break; 867 case SyntaxKind.PrefixUnaryExpression: 868 bindPrefixUnaryExpressionFlow(node as PrefixUnaryExpression); 869 break; 870 case SyntaxKind.PostfixUnaryExpression: 871 bindPostfixUnaryExpressionFlow(node as PostfixUnaryExpression); 872 break; 873 case SyntaxKind.BinaryExpression: 874 if (isDestructuringAssignment(node)) { 875 // Carry over whether we are in an assignment pattern to 876 // binary expressions that could actually be an initializer 877 inAssignmentPattern = saveInAssignmentPattern; 878 bindDestructuringAssignmentFlow(node); 879 return; 880 } 881 bindBinaryExpressionFlow(node as BinaryExpression); 882 break; 883 case SyntaxKind.DeleteExpression: 884 bindDeleteExpressionFlow(node as DeleteExpression); 885 break; 886 case SyntaxKind.ConditionalExpression: 887 bindConditionalExpressionFlow(node as ConditionalExpression); 888 break; 889 case SyntaxKind.VariableDeclaration: 890 bindVariableDeclarationFlow(node as VariableDeclaration); 891 break; 892 case SyntaxKind.PropertyAccessExpression: 893 case SyntaxKind.ElementAccessExpression: 894 bindAccessExpressionFlow(node as AccessExpression); 895 break; 896 case SyntaxKind.CallExpression: 897 bindCallExpressionFlow(node as CallExpression); 898 break; 899 case SyntaxKind.NonNullExpression: 900 bindNonNullExpressionFlow(node as NonNullExpression); 901 break; 902 case SyntaxKind.JSDocTypedefTag: 903 case SyntaxKind.JSDocCallbackTag: 904 case SyntaxKind.JSDocEnumTag: 905 bindJSDocTypeAlias(node as JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag); 906 break; 907 // In source files and blocks, bind functions first to match hoisting that occurs at runtime 908 case SyntaxKind.SourceFile: { 909 bindEachFunctionsFirst((node as SourceFile).statements); 910 bind((node as SourceFile).endOfFileToken); 911 break; 912 } 913 case SyntaxKind.Block: 914 case SyntaxKind.ModuleBlock: 915 bindEachFunctionsFirst((node as Block).statements); 916 break; 917 case SyntaxKind.BindingElement: 918 bindBindingElementFlow(node as BindingElement); 919 break; 920 case SyntaxKind.Parameter: 921 bindParameterFlow(node as ParameterDeclaration); 922 break; 923 case SyntaxKind.ObjectLiteralExpression: 924 case SyntaxKind.ArrayLiteralExpression: 925 case SyntaxKind.PropertyAssignment: 926 case SyntaxKind.SpreadElement: 927 // Carry over whether we are in an assignment pattern of Object and Array literals 928 // as well as their children that are valid assignment targets. 929 inAssignmentPattern = saveInAssignmentPattern; 930 // falls through 931 default: 932 bindEachChild(node); 933 break; 934 } 935 bindJSDoc(node); 936 inAssignmentPattern = saveInAssignmentPattern; 937 } 938 939 function isNarrowingExpression(expr: Expression): boolean { 940 switch (expr.kind) { 941 case SyntaxKind.Identifier: 942 case SyntaxKind.PrivateIdentifier: 943 case SyntaxKind.ThisKeyword: 944 case SyntaxKind.PropertyAccessExpression: 945 case SyntaxKind.ElementAccessExpression: 946 return containsNarrowableReference(expr); 947 case SyntaxKind.CallExpression: 948 return hasNarrowableArgument(expr as CallExpression); 949 case SyntaxKind.ParenthesizedExpression: 950 case SyntaxKind.NonNullExpression: 951 return isNarrowingExpression((expr as ParenthesizedExpression | NonNullExpression).expression); 952 case SyntaxKind.BinaryExpression: 953 return isNarrowingBinaryExpression(expr as BinaryExpression); 954 case SyntaxKind.PrefixUnaryExpression: 955 return (expr as PrefixUnaryExpression).operator === SyntaxKind.ExclamationToken && isNarrowingExpression((expr as PrefixUnaryExpression).operand); 956 case SyntaxKind.TypeOfExpression: 957 return isNarrowingExpression((expr as TypeOfExpression).expression); 958 } 959 return false; 960 } 961 962 function isNarrowableReference(expr: Expression): boolean { 963 return isDottedName(expr) 964 || (isPropertyAccessExpression(expr) || isNonNullExpression(expr) || isParenthesizedExpression(expr)) && isNarrowableReference(expr.expression) 965 || isBinaryExpression(expr) && expr.operatorToken.kind === SyntaxKind.CommaToken && isNarrowableReference(expr.right) 966 || isElementAccessExpression(expr) && (isStringOrNumericLiteralLike(expr.argumentExpression) || isEntityNameExpression(expr.argumentExpression)) && isNarrowableReference(expr.expression) 967 || isAssignmentExpression(expr) && isNarrowableReference(expr.left); 968 } 969 970 function containsNarrowableReference(expr: Expression): boolean { 971 return isNarrowableReference(expr) || isOptionalChain(expr) && containsNarrowableReference(expr.expression); 972 } 973 974 function hasNarrowableArgument(expr: CallExpression) { 975 if (expr.arguments) { 976 for (const argument of expr.arguments) { 977 if (containsNarrowableReference(argument)) { 978 return true; 979 } 980 } 981 } 982 if (expr.expression.kind === SyntaxKind.PropertyAccessExpression && 983 containsNarrowableReference((expr.expression as PropertyAccessExpression).expression)) { 984 return true; 985 } 986 return false; 987 } 988 989 function isNarrowingTypeofOperands(expr1: Expression, expr2: Expression) { 990 return isTypeOfExpression(expr1) && isNarrowableOperand(expr1.expression) && isStringLiteralLike(expr2); 991 } 992 993 function isNarrowingBinaryExpression(expr: BinaryExpression) { 994 switch (expr.operatorToken.kind) { 995 case SyntaxKind.EqualsToken: 996 case SyntaxKind.BarBarEqualsToken: 997 case SyntaxKind.AmpersandAmpersandEqualsToken: 998 case SyntaxKind.QuestionQuestionEqualsToken: 999 return containsNarrowableReference(expr.left); 1000 case SyntaxKind.EqualsEqualsToken: 1001 case SyntaxKind.ExclamationEqualsToken: 1002 case SyntaxKind.EqualsEqualsEqualsToken: 1003 case SyntaxKind.ExclamationEqualsEqualsToken: 1004 return isNarrowableOperand(expr.left) || isNarrowableOperand(expr.right) || 1005 isNarrowingTypeofOperands(expr.right, expr.left) || isNarrowingTypeofOperands(expr.left, expr.right); 1006 case SyntaxKind.InstanceOfKeyword: 1007 return isNarrowableOperand(expr.left); 1008 case SyntaxKind.InKeyword: 1009 return isNarrowingExpression(expr.right); 1010 case SyntaxKind.CommaToken: 1011 return isNarrowingExpression(expr.right); 1012 } 1013 return false; 1014 } 1015 1016 function isNarrowableOperand(expr: Expression): boolean { 1017 switch (expr.kind) { 1018 case SyntaxKind.ParenthesizedExpression: 1019 return isNarrowableOperand((expr as ParenthesizedExpression).expression); 1020 case SyntaxKind.BinaryExpression: 1021 switch ((expr as BinaryExpression).operatorToken.kind) { 1022 case SyntaxKind.EqualsToken: 1023 return isNarrowableOperand((expr as BinaryExpression).left); 1024 case SyntaxKind.CommaToken: 1025 return isNarrowableOperand((expr as BinaryExpression).right); 1026 } 1027 } 1028 return containsNarrowableReference(expr); 1029 } 1030 1031 function createBranchLabel(): FlowLabel { 1032 return initFlowNode({ flags: FlowFlags.BranchLabel, antecedents: undefined }); 1033 } 1034 1035 function createLoopLabel(): FlowLabel { 1036 return initFlowNode({ flags: FlowFlags.LoopLabel, antecedents: undefined }); 1037 } 1038 1039 function createReduceLabel(target: FlowLabel, antecedents: FlowNode[], antecedent: FlowNode): FlowReduceLabel { 1040 return initFlowNode({ flags: FlowFlags.ReduceLabel, target, antecedents, antecedent }); 1041 } 1042 1043 function setFlowNodeReferenced(flow: FlowNode) { 1044 // On first reference we set the Referenced flag, thereafter we set the Shared flag 1045 flow.flags |= flow.flags & FlowFlags.Referenced ? FlowFlags.Shared : FlowFlags.Referenced; 1046 } 1047 1048 function addAntecedent(label: FlowLabel, antecedent: FlowNode): void { 1049 if (!(antecedent.flags & FlowFlags.Unreachable) && !contains(label.antecedents, antecedent)) { 1050 (label.antecedents || (label.antecedents = [])).push(antecedent); 1051 setFlowNodeReferenced(antecedent); 1052 } 1053 } 1054 1055 function createFlowCondition(flags: FlowFlags, antecedent: FlowNode, expression: Expression | undefined): FlowNode { 1056 if (antecedent.flags & FlowFlags.Unreachable) { 1057 return antecedent; 1058 } 1059 if (!expression) { 1060 return flags & FlowFlags.TrueCondition ? antecedent : unreachableFlow; 1061 } 1062 if ((expression.kind === SyntaxKind.TrueKeyword && flags & FlowFlags.FalseCondition || 1063 expression.kind === SyntaxKind.FalseKeyword && flags & FlowFlags.TrueCondition) && 1064 !isExpressionOfOptionalChainRoot(expression) && !isNullishCoalesce(expression.parent)) { 1065 return unreachableFlow; 1066 } 1067 if (!isNarrowingExpression(expression)) { 1068 return antecedent; 1069 } 1070 setFlowNodeReferenced(antecedent); 1071 return initFlowNode({ flags, antecedent, node: expression }); 1072 } 1073 1074 function createFlowSwitchClause(antecedent: FlowNode, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number): FlowNode { 1075 setFlowNodeReferenced(antecedent); 1076 return initFlowNode({ flags: FlowFlags.SwitchClause, antecedent, switchStatement, clauseStart, clauseEnd }); 1077 } 1078 1079 function createFlowMutation(flags: FlowFlags, antecedent: FlowNode, node: Expression | VariableDeclaration | ArrayBindingElement): FlowNode { 1080 setFlowNodeReferenced(antecedent); 1081 const result = initFlowNode({ flags, antecedent, node }); 1082 if (currentExceptionTarget) { 1083 addAntecedent(currentExceptionTarget, result); 1084 } 1085 return result; 1086 } 1087 1088 function createFlowCall(antecedent: FlowNode, node: CallExpression): FlowNode { 1089 setFlowNodeReferenced(antecedent); 1090 return initFlowNode({ flags: FlowFlags.Call, antecedent, node }); 1091 } 1092 1093 function finishFlowLabel(flow: FlowLabel): FlowNode { 1094 const antecedents = flow.antecedents; 1095 if (!antecedents) { 1096 return unreachableFlow; 1097 } 1098 if (antecedents.length === 1) { 1099 return antecedents[0]; 1100 } 1101 return flow; 1102 } 1103 1104 function isStatementCondition(node: Node) { 1105 const parent = node.parent; 1106 switch (parent.kind) { 1107 case SyntaxKind.IfStatement: 1108 case SyntaxKind.WhileStatement: 1109 case SyntaxKind.DoStatement: 1110 return (parent as IfStatement | WhileStatement | DoStatement).expression === node; 1111 case SyntaxKind.ForStatement: 1112 case SyntaxKind.ConditionalExpression: 1113 return (parent as ForStatement | ConditionalExpression).condition === node; 1114 } 1115 return false; 1116 } 1117 1118 function isLogicalExpression(node: Node) { 1119 while (true) { 1120 if (node.kind === SyntaxKind.ParenthesizedExpression) { 1121 node = (node as ParenthesizedExpression).expression; 1122 } 1123 else if (node.kind === SyntaxKind.PrefixUnaryExpression && (node as PrefixUnaryExpression).operator === SyntaxKind.ExclamationToken) { 1124 node = (node as PrefixUnaryExpression).operand; 1125 } 1126 else { 1127 return node.kind === SyntaxKind.BinaryExpression && ( 1128 (node as BinaryExpression).operatorToken.kind === SyntaxKind.AmpersandAmpersandToken || 1129 (node as BinaryExpression).operatorToken.kind === SyntaxKind.BarBarToken || 1130 (node as BinaryExpression).operatorToken.kind === SyntaxKind.QuestionQuestionToken); 1131 } 1132 } 1133 } 1134 1135 function isLogicalAssignmentExpression(node: Node) { 1136 node = skipParentheses(node); 1137 return isBinaryExpression(node) && isLogicalOrCoalescingAssignmentOperator(node.operatorToken.kind); 1138 } 1139 1140 function isTopLevelLogicalExpression(node: Node): boolean { 1141 while (isParenthesizedExpression(node.parent) || 1142 isPrefixUnaryExpression(node.parent) && node.parent.operator === SyntaxKind.ExclamationToken) { 1143 node = node.parent; 1144 } 1145 return !isStatementCondition(node) && 1146 !isLogicalExpression(node.parent) && 1147 !(isOptionalChain(node.parent) && node.parent.expression === node); 1148 } 1149 1150 function doWithConditionalBranches<T>(action: (value: T) => void, value: T, trueTarget: FlowLabel, falseTarget: FlowLabel) { 1151 const savedTrueTarget = currentTrueTarget; 1152 const savedFalseTarget = currentFalseTarget; 1153 currentTrueTarget = trueTarget; 1154 currentFalseTarget = falseTarget; 1155 action(value); 1156 currentTrueTarget = savedTrueTarget; 1157 currentFalseTarget = savedFalseTarget; 1158 } 1159 1160 function bindCondition(node: Expression | undefined, trueTarget: FlowLabel, falseTarget: FlowLabel) { 1161 doWithConditionalBranches(bind, node, trueTarget, falseTarget); 1162 if (!node || !isLogicalAssignmentExpression(node) && !isLogicalExpression(node) && !(isOptionalChain(node) && isOutermostOptionalChain(node))) { 1163 addAntecedent(trueTarget, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node)); 1164 addAntecedent(falseTarget, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node)); 1165 } 1166 } 1167 1168 function bindIterativeStatement(node: Statement, breakTarget: FlowLabel, continueTarget: FlowLabel): void { 1169 const saveBreakTarget = currentBreakTarget; 1170 const saveContinueTarget = currentContinueTarget; 1171 currentBreakTarget = breakTarget; 1172 currentContinueTarget = continueTarget; 1173 bind(node); 1174 currentBreakTarget = saveBreakTarget; 1175 currentContinueTarget = saveContinueTarget; 1176 } 1177 1178 function setContinueTarget(node: Node, target: FlowLabel) { 1179 let label = activeLabelList; 1180 while (label && node.parent.kind === SyntaxKind.LabeledStatement) { 1181 label.continueTarget = target; 1182 label = label.next; 1183 node = node.parent; 1184 } 1185 return target; 1186 } 1187 1188 function bindWhileStatement(node: WhileStatement): void { 1189 const preWhileLabel = setContinueTarget(node, createLoopLabel()); 1190 const preBodyLabel = createBranchLabel(); 1191 const postWhileLabel = createBranchLabel(); 1192 addAntecedent(preWhileLabel, currentFlow); 1193 currentFlow = preWhileLabel; 1194 bindCondition(node.expression, preBodyLabel, postWhileLabel); 1195 currentFlow = finishFlowLabel(preBodyLabel); 1196 bindIterativeStatement(node.statement, postWhileLabel, preWhileLabel); 1197 addAntecedent(preWhileLabel, currentFlow); 1198 currentFlow = finishFlowLabel(postWhileLabel); 1199 } 1200 1201 function bindDoStatement(node: DoStatement): void { 1202 const preDoLabel = createLoopLabel(); 1203 const preConditionLabel = setContinueTarget(node, createBranchLabel()); 1204 const postDoLabel = createBranchLabel(); 1205 addAntecedent(preDoLabel, currentFlow); 1206 currentFlow = preDoLabel; 1207 bindIterativeStatement(node.statement, postDoLabel, preConditionLabel); 1208 addAntecedent(preConditionLabel, currentFlow); 1209 currentFlow = finishFlowLabel(preConditionLabel); 1210 bindCondition(node.expression, preDoLabel, postDoLabel); 1211 currentFlow = finishFlowLabel(postDoLabel); 1212 } 1213 1214 function bindForStatement(node: ForStatement): void { 1215 const preLoopLabel = setContinueTarget(node, createLoopLabel()); 1216 const preBodyLabel = createBranchLabel(); 1217 const postLoopLabel = createBranchLabel(); 1218 bind(node.initializer); 1219 addAntecedent(preLoopLabel, currentFlow); 1220 currentFlow = preLoopLabel; 1221 bindCondition(node.condition, preBodyLabel, postLoopLabel); 1222 currentFlow = finishFlowLabel(preBodyLabel); 1223 bindIterativeStatement(node.statement, postLoopLabel, preLoopLabel); 1224 bind(node.incrementor); 1225 addAntecedent(preLoopLabel, currentFlow); 1226 currentFlow = finishFlowLabel(postLoopLabel); 1227 } 1228 1229 function bindForInOrForOfStatement(node: ForInOrOfStatement): void { 1230 const preLoopLabel = setContinueTarget(node, createLoopLabel()); 1231 const postLoopLabel = createBranchLabel(); 1232 bind(node.expression); 1233 addAntecedent(preLoopLabel, currentFlow); 1234 currentFlow = preLoopLabel; 1235 if (node.kind === SyntaxKind.ForOfStatement) { 1236 bind(node.awaitModifier); 1237 } 1238 addAntecedent(postLoopLabel, currentFlow); 1239 bind(node.initializer); 1240 if (node.initializer.kind !== SyntaxKind.VariableDeclarationList) { 1241 bindAssignmentTargetFlow(node.initializer); 1242 } 1243 bindIterativeStatement(node.statement, postLoopLabel, preLoopLabel); 1244 addAntecedent(preLoopLabel, currentFlow); 1245 currentFlow = finishFlowLabel(postLoopLabel); 1246 } 1247 1248 function bindIfStatement(node: IfStatement): void { 1249 const thenLabel = createBranchLabel(); 1250 const elseLabel = createBranchLabel(); 1251 const postIfLabel = createBranchLabel(); 1252 bindCondition(node.expression, thenLabel, elseLabel); 1253 currentFlow = finishFlowLabel(thenLabel); 1254 bind(node.thenStatement); 1255 addAntecedent(postIfLabel, currentFlow); 1256 currentFlow = finishFlowLabel(elseLabel); 1257 bind(node.elseStatement); 1258 addAntecedent(postIfLabel, currentFlow); 1259 currentFlow = finishFlowLabel(postIfLabel); 1260 } 1261 1262 function bindReturnOrThrow(node: ReturnStatement | ThrowStatement): void { 1263 bind(node.expression); 1264 if (node.kind === SyntaxKind.ReturnStatement) { 1265 hasExplicitReturn = true; 1266 if (currentReturnTarget) { 1267 addAntecedent(currentReturnTarget, currentFlow); 1268 } 1269 } 1270 currentFlow = unreachableFlow; 1271 } 1272 1273 function findActiveLabel(name: __String) { 1274 for (let label = activeLabelList; label; label = label.next) { 1275 if (label.name === name) { 1276 return label; 1277 } 1278 } 1279 return undefined; 1280 } 1281 1282 function bindBreakOrContinueFlow(node: BreakOrContinueStatement, breakTarget: FlowLabel | undefined, continueTarget: FlowLabel | undefined) { 1283 const flowLabel = node.kind === SyntaxKind.BreakStatement ? breakTarget : continueTarget; 1284 if (flowLabel) { 1285 addAntecedent(flowLabel, currentFlow); 1286 currentFlow = unreachableFlow; 1287 } 1288 } 1289 1290 function bindBreakOrContinueStatement(node: BreakOrContinueStatement): void { 1291 bind(node.label); 1292 if (node.label) { 1293 const activeLabel = findActiveLabel(node.label.escapedText); 1294 if (activeLabel) { 1295 activeLabel.referenced = true; 1296 bindBreakOrContinueFlow(node, activeLabel.breakTarget, activeLabel.continueTarget); 1297 } 1298 } 1299 else { 1300 bindBreakOrContinueFlow(node, currentBreakTarget, currentContinueTarget); 1301 } 1302 } 1303 1304 function bindTryStatement(node: TryStatement): void { 1305 // We conservatively assume that *any* code in the try block can cause an exception, but we only need 1306 // to track code that causes mutations (because only mutations widen the possible control flow type of 1307 // a variable). The exceptionLabel is the target label for control flows that result from exceptions. 1308 // We add all mutation flow nodes as antecedents of this label such that we can analyze them as possible 1309 // antecedents of the start of catch or finally blocks. Furthermore, we add the current control flow to 1310 // represent exceptions that occur before any mutations. 1311 const saveReturnTarget = currentReturnTarget; 1312 const saveExceptionTarget = currentExceptionTarget; 1313 const normalExitLabel = createBranchLabel(); 1314 const returnLabel = createBranchLabel(); 1315 let exceptionLabel = createBranchLabel(); 1316 if (node.finallyBlock) { 1317 currentReturnTarget = returnLabel; 1318 } 1319 addAntecedent(exceptionLabel, currentFlow); 1320 currentExceptionTarget = exceptionLabel; 1321 bind(node.tryBlock); 1322 addAntecedent(normalExitLabel, currentFlow); 1323 if (node.catchClause) { 1324 // Start of catch clause is the target of exceptions from try block. 1325 currentFlow = finishFlowLabel(exceptionLabel); 1326 // The currentExceptionTarget now represents control flows from exceptions in the catch clause. 1327 // Effectively, in a try-catch-finally, if an exception occurs in the try block, the catch block 1328 // acts like a second try block. 1329 exceptionLabel = createBranchLabel(); 1330 addAntecedent(exceptionLabel, currentFlow); 1331 currentExceptionTarget = exceptionLabel; 1332 bind(node.catchClause); 1333 addAntecedent(normalExitLabel, currentFlow); 1334 } 1335 currentReturnTarget = saveReturnTarget; 1336 currentExceptionTarget = saveExceptionTarget; 1337 if (node.finallyBlock) { 1338 // Possible ways control can reach the finally block: 1339 // 1) Normal completion of try block of a try-finally or try-catch-finally 1340 // 2) Normal completion of catch block (following exception in try block) of a try-catch-finally 1341 // 3) Return in try or catch block of a try-finally or try-catch-finally 1342 // 4) Exception in try block of a try-finally 1343 // 5) Exception in catch block of a try-catch-finally 1344 // When analyzing a control flow graph that starts inside a finally block we want to consider all 1345 // five possibilities above. However, when analyzing a control flow graph that starts outside (past) 1346 // the finally block, we only want to consider the first two (if we're past a finally block then it 1347 // must have completed normally). Likewise, when analyzing a control flow graph from return statements 1348 // in try or catch blocks in an IIFE, we only want to consider the third. To make this possible, we 1349 // inject a ReduceLabel node into the control flow graph. This node contains an alternate reduced 1350 // set of antecedents for the pre-finally label. As control flow analysis passes by a ReduceLabel 1351 // node, the pre-finally label is temporarily switched to the reduced antecedent set. 1352 const finallyLabel = createBranchLabel(); 1353 finallyLabel.antecedents = concatenate(concatenate(normalExitLabel.antecedents, exceptionLabel.antecedents), returnLabel.antecedents); 1354 currentFlow = finallyLabel; 1355 bind(node.finallyBlock); 1356 if (currentFlow.flags & FlowFlags.Unreachable) { 1357 // If the end of the finally block is unreachable, the end of the entire try statement is unreachable. 1358 currentFlow = unreachableFlow; 1359 } 1360 else { 1361 // If we have an IIFE return target and return statements in the try or catch blocks, add a control 1362 // flow that goes back through the finally block and back through only the return statements. 1363 if (currentReturnTarget && returnLabel.antecedents) { 1364 addAntecedent(currentReturnTarget, createReduceLabel(finallyLabel, returnLabel.antecedents, currentFlow)); 1365 } 1366 // If we have an outer exception target (i.e. a containing try-finally or try-catch-finally), add a 1367 // control flow that goes back through the finally blok and back through each possible exception source. 1368 if (currentExceptionTarget && exceptionLabel.antecedents) { 1369 addAntecedent(currentExceptionTarget, createReduceLabel(finallyLabel, exceptionLabel.antecedents, currentFlow)); 1370 } 1371 // If the end of the finally block is reachable, but the end of the try and catch blocks are not, 1372 // convert the current flow to unreachable. For example, 'try { return 1; } finally { ... }' should 1373 // result in an unreachable current control flow. 1374 currentFlow = normalExitLabel.antecedents ? createReduceLabel(finallyLabel, normalExitLabel.antecedents, currentFlow) : unreachableFlow; 1375 } 1376 } 1377 else { 1378 currentFlow = finishFlowLabel(normalExitLabel); 1379 } 1380 } 1381 1382 function bindSwitchStatement(node: SwitchStatement): void { 1383 const postSwitchLabel = createBranchLabel(); 1384 bind(node.expression); 1385 const saveBreakTarget = currentBreakTarget; 1386 const savePreSwitchCaseFlow = preSwitchCaseFlow; 1387 currentBreakTarget = postSwitchLabel; 1388 preSwitchCaseFlow = currentFlow; 1389 bind(node.caseBlock); 1390 addAntecedent(postSwitchLabel, currentFlow); 1391 const hasDefault = forEach(node.caseBlock.clauses, c => c.kind === SyntaxKind.DefaultClause); 1392 // We mark a switch statement as possibly exhaustive if it has no default clause and if all 1393 // case clauses have unreachable end points (e.g. they all return). Note, we no longer need 1394 // this property in control flow analysis, it's there only for backwards compatibility. 1395 node.possiblyExhaustive = !hasDefault && !postSwitchLabel.antecedents; 1396 if (!hasDefault) { 1397 addAntecedent(postSwitchLabel, createFlowSwitchClause(preSwitchCaseFlow, node, 0, 0)); 1398 } 1399 currentBreakTarget = saveBreakTarget; 1400 preSwitchCaseFlow = savePreSwitchCaseFlow; 1401 currentFlow = finishFlowLabel(postSwitchLabel); 1402 } 1403 1404 function bindCaseBlock(node: CaseBlock): void { 1405 const clauses = node.clauses; 1406 const isNarrowingSwitch = isNarrowingExpression(node.parent.expression); 1407 let fallthroughFlow = unreachableFlow; 1408 for (let i = 0; i < clauses.length; i++) { 1409 const clauseStart = i; 1410 while (!clauses[i].statements.length && i + 1 < clauses.length) { 1411 bind(clauses[i]); 1412 i++; 1413 } 1414 const preCaseLabel = createBranchLabel(); 1415 addAntecedent(preCaseLabel, isNarrowingSwitch ? createFlowSwitchClause(preSwitchCaseFlow!, node.parent, clauseStart, i + 1) : preSwitchCaseFlow!); 1416 addAntecedent(preCaseLabel, fallthroughFlow); 1417 currentFlow = finishFlowLabel(preCaseLabel); 1418 const clause = clauses[i]; 1419 bind(clause); 1420 fallthroughFlow = currentFlow; 1421 if (!(currentFlow.flags & FlowFlags.Unreachable) && i !== clauses.length - 1 && options.noFallthroughCasesInSwitch) { 1422 clause.fallthroughFlowNode = currentFlow; 1423 } 1424 } 1425 } 1426 1427 function bindCaseClause(node: CaseClause): void { 1428 const saveCurrentFlow = currentFlow; 1429 currentFlow = preSwitchCaseFlow!; 1430 bind(node.expression); 1431 currentFlow = saveCurrentFlow; 1432 bindEach(node.statements); 1433 } 1434 1435 function bindExpressionStatement(node: ExpressionStatement): void { 1436 bind(node.expression); 1437 maybeBindExpressionFlowIfCall(node.expression); 1438 } 1439 1440 function maybeBindExpressionFlowIfCall(node: Expression) { 1441 // A top level or comma expression call expression with a dotted function name and at least one argument 1442 // is potentially an assertion and is therefore included in the control flow. 1443 if (node.kind === SyntaxKind.CallExpression) { 1444 const call = node as CallExpression; 1445 if (call.expression.kind !== SyntaxKind.SuperKeyword && isDottedName(call.expression)) { 1446 currentFlow = createFlowCall(currentFlow, call); 1447 } 1448 } 1449 } 1450 1451 function bindLabeledStatement(node: LabeledStatement): void { 1452 const postStatementLabel = createBranchLabel(); 1453 activeLabelList = { 1454 next: activeLabelList, 1455 name: node.label.escapedText, 1456 breakTarget: postStatementLabel, 1457 continueTarget: undefined, 1458 referenced: false 1459 }; 1460 bind(node.label); 1461 bind(node.statement); 1462 if (!activeLabelList.referenced && !options.allowUnusedLabels) { 1463 errorOrSuggestionOnNode(unusedLabelIsError(options), node.label, Diagnostics.Unused_label); 1464 } 1465 activeLabelList = activeLabelList.next; 1466 addAntecedent(postStatementLabel, currentFlow); 1467 currentFlow = finishFlowLabel(postStatementLabel); 1468 } 1469 1470 function bindDestructuringTargetFlow(node: Expression) { 1471 if (node.kind === SyntaxKind.BinaryExpression && (node as BinaryExpression).operatorToken.kind === SyntaxKind.EqualsToken) { 1472 bindAssignmentTargetFlow((node as BinaryExpression).left); 1473 } 1474 else { 1475 bindAssignmentTargetFlow(node); 1476 } 1477 } 1478 1479 function bindAssignmentTargetFlow(node: Expression) { 1480 if (isNarrowableReference(node)) { 1481 currentFlow = createFlowMutation(FlowFlags.Assignment, currentFlow, node); 1482 } 1483 else if (node.kind === SyntaxKind.ArrayLiteralExpression) { 1484 for (const e of (node as ArrayLiteralExpression).elements) { 1485 if (e.kind === SyntaxKind.SpreadElement) { 1486 bindAssignmentTargetFlow((e as SpreadElement).expression); 1487 } 1488 else { 1489 bindDestructuringTargetFlow(e); 1490 } 1491 } 1492 } 1493 else if (node.kind === SyntaxKind.ObjectLiteralExpression) { 1494 for (const p of (node as ObjectLiteralExpression).properties) { 1495 if (p.kind === SyntaxKind.PropertyAssignment) { 1496 bindDestructuringTargetFlow(p.initializer); 1497 } 1498 else if (p.kind === SyntaxKind.ShorthandPropertyAssignment) { 1499 bindAssignmentTargetFlow(p.name); 1500 } 1501 else if (p.kind === SyntaxKind.SpreadAssignment) { 1502 bindAssignmentTargetFlow(p.expression); 1503 } 1504 } 1505 } 1506 } 1507 1508 function bindLogicalLikeExpression(node: BinaryExpression, trueTarget: FlowLabel, falseTarget: FlowLabel) { 1509 const preRightLabel = createBranchLabel(); 1510 if (node.operatorToken.kind === SyntaxKind.AmpersandAmpersandToken || node.operatorToken.kind === SyntaxKind.AmpersandAmpersandEqualsToken) { 1511 bindCondition(node.left, preRightLabel, falseTarget); 1512 } 1513 else { 1514 bindCondition(node.left, trueTarget, preRightLabel); 1515 } 1516 currentFlow = finishFlowLabel(preRightLabel); 1517 bind(node.operatorToken); 1518 1519 if (isLogicalOrCoalescingAssignmentOperator(node.operatorToken.kind)) { 1520 doWithConditionalBranches(bind, node.right, trueTarget, falseTarget); 1521 bindAssignmentTargetFlow(node.left); 1522 1523 addAntecedent(trueTarget, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node)); 1524 addAntecedent(falseTarget, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node)); 1525 } 1526 else { 1527 bindCondition(node.right, trueTarget, falseTarget); 1528 } 1529 } 1530 1531 function bindPrefixUnaryExpressionFlow(node: PrefixUnaryExpression) { 1532 if (node.operator === SyntaxKind.ExclamationToken) { 1533 const saveTrueTarget = currentTrueTarget; 1534 currentTrueTarget = currentFalseTarget; 1535 currentFalseTarget = saveTrueTarget; 1536 bindEachChild(node); 1537 currentFalseTarget = currentTrueTarget; 1538 currentTrueTarget = saveTrueTarget; 1539 } 1540 else { 1541 bindEachChild(node); 1542 if (node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken) { 1543 bindAssignmentTargetFlow(node.operand); 1544 } 1545 } 1546 } 1547 1548 function bindPostfixUnaryExpressionFlow(node: PostfixUnaryExpression) { 1549 bindEachChild(node); 1550 if (node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken) { 1551 bindAssignmentTargetFlow(node.operand); 1552 } 1553 } 1554 1555 function bindDestructuringAssignmentFlow(node: DestructuringAssignment) { 1556 if (inAssignmentPattern) { 1557 inAssignmentPattern = false; 1558 bind(node.operatorToken); 1559 bind(node.right); 1560 inAssignmentPattern = true; 1561 bind(node.left); 1562 } 1563 else { 1564 inAssignmentPattern = true; 1565 bind(node.left); 1566 inAssignmentPattern = false; 1567 bind(node.operatorToken); 1568 bind(node.right); 1569 } 1570 bindAssignmentTargetFlow(node.left); 1571 } 1572 1573 function createBindBinaryExpressionFlow() { 1574 interface WorkArea { 1575 stackIndex: number; 1576 skip: boolean; 1577 inStrictModeStack: (boolean | undefined)[]; 1578 parentStack: (Node | undefined)[]; 1579 } 1580 1581 return createBinaryExpressionTrampoline(onEnter, onLeft, onOperator, onRight, onExit, /*foldState*/ undefined); 1582 1583 function onEnter(node: BinaryExpression, state: WorkArea | undefined) { 1584 if (state) { 1585 state.stackIndex++; 1586 // Emulate the work that `bind` does before reaching `bindChildren`. A normal call to 1587 // `bindBinaryExpressionFlow` will already have done this work. 1588 setParent(node, parent); 1589 const saveInStrictMode = inStrictMode; 1590 bindWorker(node); 1591 const saveParent = parent; 1592 parent = node; 1593 state.skip = false; 1594 state.inStrictModeStack[state.stackIndex] = saveInStrictMode; 1595 state.parentStack[state.stackIndex] = saveParent; 1596 } 1597 else { 1598 state = { 1599 stackIndex: 0, 1600 skip: false, 1601 inStrictModeStack: [undefined], 1602 parentStack: [undefined] 1603 }; 1604 } 1605 // TODO: bindLogicalExpression is recursive - if we want to handle deeply nested `&&` expressions 1606 // we'll need to handle the `bindLogicalExpression` scenarios in this state machine, too 1607 // For now, though, since the common cases are chained `+`, leaving it recursive is fine 1608 const operator = node.operatorToken.kind; 1609 if (operator === SyntaxKind.AmpersandAmpersandToken || 1610 operator === SyntaxKind.BarBarToken || 1611 operator === SyntaxKind.QuestionQuestionToken || 1612 isLogicalOrCoalescingAssignmentOperator(operator)) { 1613 if (isTopLevelLogicalExpression(node)) { 1614 const postExpressionLabel = createBranchLabel(); 1615 bindLogicalLikeExpression(node, postExpressionLabel, postExpressionLabel); 1616 currentFlow = finishFlowLabel(postExpressionLabel); 1617 } 1618 else { 1619 bindLogicalLikeExpression(node, currentTrueTarget!, currentFalseTarget!); 1620 } 1621 state.skip = true; 1622 } 1623 return state; 1624 } 1625 1626 function onLeft(left: Expression, state: WorkArea, node: BinaryExpression) { 1627 if (!state.skip) { 1628 const maybeBound = maybeBind(left); 1629 if (node.operatorToken.kind === SyntaxKind.CommaToken) { 1630 maybeBindExpressionFlowIfCall(left); 1631 } 1632 return maybeBound; 1633 } 1634 } 1635 1636 function onOperator(operatorToken: BinaryOperatorToken, state: WorkArea, _node: BinaryExpression) { 1637 if (!state.skip) { 1638 bind(operatorToken); 1639 } 1640 } 1641 1642 function onRight(right: Expression, state: WorkArea, node: BinaryExpression) { 1643 if (!state.skip) { 1644 const maybeBound = maybeBind(right); 1645 if (node.operatorToken.kind === SyntaxKind.CommaToken) { 1646 maybeBindExpressionFlowIfCall(right); 1647 } 1648 return maybeBound; 1649 } 1650 } 1651 1652 function onExit(node: BinaryExpression, state: WorkArea) { 1653 if (!state.skip) { 1654 const operator = node.operatorToken.kind; 1655 if (isAssignmentOperator(operator) && !isAssignmentTarget(node)) { 1656 bindAssignmentTargetFlow(node.left); 1657 if (operator === SyntaxKind.EqualsToken && node.left.kind === SyntaxKind.ElementAccessExpression) { 1658 const elementAccess = node.left as ElementAccessExpression; 1659 if (isNarrowableOperand(elementAccess.expression)) { 1660 currentFlow = createFlowMutation(FlowFlags.ArrayMutation, currentFlow, node); 1661 } 1662 } 1663 } 1664 } 1665 const savedInStrictMode = state.inStrictModeStack[state.stackIndex]; 1666 const savedParent = state.parentStack[state.stackIndex]; 1667 if (savedInStrictMode !== undefined) { 1668 inStrictMode = savedInStrictMode; 1669 } 1670 if (savedParent !== undefined) { 1671 parent = savedParent; 1672 } 1673 state.skip = false; 1674 state.stackIndex--; 1675 } 1676 1677 function maybeBind(node: Node) { 1678 if (node && isBinaryExpression(node) && !isDestructuringAssignment(node)) { 1679 return node; 1680 } 1681 bind(node); 1682 } 1683 } 1684 1685 function bindDeleteExpressionFlow(node: DeleteExpression) { 1686 bindEachChild(node); 1687 if (node.expression.kind === SyntaxKind.PropertyAccessExpression) { 1688 bindAssignmentTargetFlow(node.expression); 1689 } 1690 } 1691 1692 function bindConditionalExpressionFlow(node: ConditionalExpression) { 1693 const trueLabel = createBranchLabel(); 1694 const falseLabel = createBranchLabel(); 1695 const postExpressionLabel = createBranchLabel(); 1696 bindCondition(node.condition, trueLabel, falseLabel); 1697 currentFlow = finishFlowLabel(trueLabel); 1698 bind(node.questionToken); 1699 bind(node.whenTrue); 1700 addAntecedent(postExpressionLabel, currentFlow); 1701 currentFlow = finishFlowLabel(falseLabel); 1702 bind(node.colonToken); 1703 bind(node.whenFalse); 1704 addAntecedent(postExpressionLabel, currentFlow); 1705 currentFlow = finishFlowLabel(postExpressionLabel); 1706 } 1707 1708 function bindInitializedVariableFlow(node: VariableDeclaration | ArrayBindingElement) { 1709 const name = !isOmittedExpression(node) ? node.name : undefined; 1710 if (isBindingPattern(name)) { 1711 for (const child of name.elements) { 1712 bindInitializedVariableFlow(child); 1713 } 1714 } 1715 else { 1716 currentFlow = createFlowMutation(FlowFlags.Assignment, currentFlow, node); 1717 } 1718 } 1719 1720 function bindVariableDeclarationFlow(node: VariableDeclaration) { 1721 bindEachChild(node); 1722 if (node.initializer || isForInOrOfStatement(node.parent.parent)) { 1723 bindInitializedVariableFlow(node); 1724 } 1725 } 1726 1727 function bindBindingElementFlow(node: BindingElement) { 1728 // When evaluating a binding pattern, the initializer is evaluated before the binding pattern, per: 1729 // - https://tc39.es/ecma262/#sec-destructuring-binding-patterns-runtime-semantics-iteratorbindinginitialization 1730 // - `BindingElement: BindingPattern Initializer?` 1731 // - https://tc39.es/ecma262/#sec-runtime-semantics-keyedbindinginitialization 1732 // - `BindingElement: BindingPattern Initializer?` 1733 bind(node.dotDotDotToken); 1734 bind(node.propertyName); 1735 bindInitializer(node.initializer); 1736 bind(node.name); 1737 } 1738 1739 function bindParameterFlow(node: ParameterDeclaration) { 1740 bindEach(node.modifiers); 1741 bind(node.dotDotDotToken); 1742 bind(node.questionToken); 1743 bind(node.type); 1744 bindInitializer(node.initializer); 1745 bind(node.name); 1746 } 1747 1748 // a BindingElement/Parameter does not have side effects if initializers are not evaluated and used. (see GH#49759) 1749 function bindInitializer(node: Expression | undefined) { 1750 if (!node) { 1751 return; 1752 } 1753 const entryFlow = currentFlow; 1754 bind(node); 1755 if (entryFlow === unreachableFlow || entryFlow === currentFlow) { 1756 return; 1757 } 1758 const exitFlow = createBranchLabel(); 1759 addAntecedent(exitFlow, entryFlow); 1760 addAntecedent(exitFlow, currentFlow); 1761 currentFlow = finishFlowLabel(exitFlow); 1762 } 1763 1764 function bindJSDocTypeAlias(node: JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag) { 1765 bind(node.tagName); 1766 if (node.kind !== SyntaxKind.JSDocEnumTag && node.fullName) { 1767 // don't bind the type name yet; that's delayed until delayedBindJSDocTypedefTag 1768 setParent(node.fullName, node); 1769 setParentRecursive(node.fullName, /*incremental*/ false); 1770 } 1771 if (typeof node.comment !== "string") { 1772 bindEach(node.comment); 1773 } 1774 } 1775 1776 function bindJSDocClassTag(node: JSDocClassTag) { 1777 bindEachChild(node); 1778 const host = getHostSignatureFromJSDoc(node); 1779 if (host && host.kind !== SyntaxKind.MethodDeclaration) { 1780 addDeclarationToSymbol(host.symbol, host, SymbolFlags.Class); 1781 } 1782 } 1783 1784 function bindOptionalExpression(node: Expression, trueTarget: FlowLabel, falseTarget: FlowLabel) { 1785 doWithConditionalBranches(bind, node, trueTarget, falseTarget); 1786 if (!isOptionalChain(node) || isOutermostOptionalChain(node)) { 1787 addAntecedent(trueTarget, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node)); 1788 addAntecedent(falseTarget, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node)); 1789 } 1790 } 1791 1792 function bindOptionalChainRest(node: OptionalChain) { 1793 switch (node.kind) { 1794 case SyntaxKind.PropertyAccessExpression: 1795 bind(node.questionDotToken); 1796 bind(node.name); 1797 break; 1798 case SyntaxKind.ElementAccessExpression: 1799 bind(node.questionDotToken); 1800 bind(node.argumentExpression); 1801 break; 1802 case SyntaxKind.CallExpression: 1803 bind(node.questionDotToken); 1804 bindEach(node.typeArguments); 1805 bindEach(node.arguments); 1806 break; 1807 } 1808 } 1809 1810 function bindOptionalChain(node: OptionalChain, trueTarget: FlowLabel, falseTarget: FlowLabel) { 1811 // For an optional chain, we emulate the behavior of a logical expression: 1812 // 1813 // a?.b -> a && a.b 1814 // a?.b.c -> a && a.b.c 1815 // a?.b?.c -> a && a.b && a.b.c 1816 // a?.[x = 1] -> a && a[x = 1] 1817 // 1818 // To do this we descend through the chain until we reach the root of a chain (the expression with a `?.`) 1819 // and build it's CFA graph as if it were the first condition (`a && ...`). Then we bind the rest 1820 // of the node as part of the "true" branch, and continue to do so as we ascend back up to the outermost 1821 // chain node. We then treat the entire node as the right side of the expression. 1822 const preChainLabel = isOptionalChainRoot(node) ? createBranchLabel() : undefined; 1823 bindOptionalExpression(node.expression, preChainLabel || trueTarget, falseTarget); 1824 if (preChainLabel) { 1825 currentFlow = finishFlowLabel(preChainLabel); 1826 } 1827 doWithConditionalBranches(bindOptionalChainRest, node, trueTarget, falseTarget); 1828 if (isOutermostOptionalChain(node)) { 1829 addAntecedent(trueTarget, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node)); 1830 addAntecedent(falseTarget, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node)); 1831 } 1832 } 1833 1834 function bindOptionalChainFlow(node: OptionalChain) { 1835 if (isTopLevelLogicalExpression(node)) { 1836 const postExpressionLabel = createBranchLabel(); 1837 bindOptionalChain(node, postExpressionLabel, postExpressionLabel); 1838 currentFlow = finishFlowLabel(postExpressionLabel); 1839 } 1840 else { 1841 bindOptionalChain(node, currentTrueTarget!, currentFalseTarget!); 1842 } 1843 } 1844 1845 function bindNonNullExpressionFlow(node: NonNullExpression | NonNullChain) { 1846 if (isOptionalChain(node)) { 1847 bindOptionalChainFlow(node); 1848 } 1849 else { 1850 bindEachChild(node); 1851 } 1852 } 1853 1854 function bindAccessExpressionFlow(node: AccessExpression | PropertyAccessChain | ElementAccessChain) { 1855 if (isOptionalChain(node)) { 1856 bindOptionalChainFlow(node); 1857 } 1858 else { 1859 bindEachChild(node); 1860 } 1861 } 1862 1863 function bindCallExpressionFlow(node: CallExpression | CallChain) { 1864 if (isOptionalChain(node)) { 1865 bindOptionalChainFlow(node); 1866 } 1867 else { 1868 // If the target of the call expression is a function expression or arrow function we have 1869 // an immediately invoked function expression (IIFE). Initialize the flowNode property to 1870 // the current control flow (which includes evaluation of the IIFE arguments). 1871 const expr = skipParentheses(node.expression); 1872 if (expr.kind === SyntaxKind.FunctionExpression || expr.kind === SyntaxKind.ArrowFunction) { 1873 bindEach(node.typeArguments); 1874 bindEach(node.arguments); 1875 bind(node.expression); 1876 } 1877 else { 1878 bindEachChild(node); 1879 if (node.expression.kind === SyntaxKind.SuperKeyword) { 1880 currentFlow = createFlowCall(currentFlow, node); 1881 } 1882 } 1883 } 1884 if (node.expression.kind === SyntaxKind.PropertyAccessExpression) { 1885 const propertyAccess = node.expression as PropertyAccessExpression; 1886 if (isIdentifier(propertyAccess.name) && isNarrowableOperand(propertyAccess.expression) && isPushOrUnshiftIdentifier(propertyAccess.name)) { 1887 currentFlow = createFlowMutation(FlowFlags.ArrayMutation, currentFlow, node); 1888 } 1889 } 1890 } 1891 1892 function getContainerFlags(node: Node): ContainerFlags { 1893 switch (node.kind) { 1894 case SyntaxKind.ClassExpression: 1895 case SyntaxKind.ClassDeclaration: 1896 case SyntaxKind.StructDeclaration: 1897 case SyntaxKind.AnnotationDeclaration: 1898 case SyntaxKind.EnumDeclaration: 1899 case SyntaxKind.ObjectLiteralExpression: 1900 case SyntaxKind.TypeLiteral: 1901 case SyntaxKind.JSDocTypeLiteral: 1902 case SyntaxKind.JsxAttributes: 1903 return ContainerFlags.IsContainer; 1904 1905 case SyntaxKind.InterfaceDeclaration: 1906 return ContainerFlags.IsContainer | ContainerFlags.IsInterface; 1907 1908 case SyntaxKind.ModuleDeclaration: 1909 case SyntaxKind.TypeAliasDeclaration: 1910 case SyntaxKind.MappedType: 1911 case SyntaxKind.IndexSignature: 1912 return ContainerFlags.IsContainer | ContainerFlags.HasLocals; 1913 1914 case SyntaxKind.SourceFile: 1915 return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals; 1916 1917 case SyntaxKind.GetAccessor: 1918 case SyntaxKind.SetAccessor: 1919 case SyntaxKind.MethodDeclaration: 1920 if (isObjectLiteralOrClassExpressionMethodOrAccessor(node)) { 1921 return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike | ContainerFlags.IsObjectLiteralOrClassExpressionMethodOrAccessor; 1922 } 1923 // falls through 1924 case SyntaxKind.Constructor: 1925 case SyntaxKind.FunctionDeclaration: 1926 case SyntaxKind.MethodSignature: 1927 case SyntaxKind.CallSignature: 1928 case SyntaxKind.JSDocSignature: 1929 case SyntaxKind.JSDocFunctionType: 1930 case SyntaxKind.FunctionType: 1931 case SyntaxKind.ConstructSignature: 1932 case SyntaxKind.ConstructorType: 1933 case SyntaxKind.ClassStaticBlockDeclaration: 1934 return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike; 1935 1936 case SyntaxKind.FunctionExpression: 1937 case SyntaxKind.ArrowFunction: 1938 return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike | ContainerFlags.IsFunctionExpression; 1939 1940 case SyntaxKind.ModuleBlock: 1941 return ContainerFlags.IsControlFlowContainer; 1942 case SyntaxKind.PropertyDeclaration: 1943 return (node as PropertyDeclaration).initializer ? ContainerFlags.IsControlFlowContainer : 0; 1944 case SyntaxKind.AnnotationPropertyDeclaration: 1945 return (node as AnnotationPropertyDeclaration).initializer ? ContainerFlags.IsControlFlowContainer : 0; 1946 1947 1948 case SyntaxKind.CatchClause: 1949 case SyntaxKind.ForStatement: 1950 case SyntaxKind.ForInStatement: 1951 case SyntaxKind.ForOfStatement: 1952 case SyntaxKind.CaseBlock: 1953 return ContainerFlags.IsBlockScopedContainer; 1954 1955 case SyntaxKind.Block: 1956 // do not treat blocks directly inside a function as a block-scoped-container. 1957 // Locals that reside in this block should go to the function locals. Otherwise 'x' 1958 // would not appear to be a redeclaration of a block scoped local in the following 1959 // example: 1960 // 1961 // function foo() { 1962 // var x; 1963 // let x; 1964 // } 1965 // 1966 // If we placed 'var x' into the function locals and 'let x' into the locals of 1967 // the block, then there would be no collision. 1968 // 1969 // By not creating a new block-scoped-container here, we ensure that both 'var x' 1970 // and 'let x' go into the Function-container's locals, and we do get a collision 1971 // conflict. 1972 return isFunctionLike(node.parent) || isClassStaticBlockDeclaration(node.parent) ? ContainerFlags.None : ContainerFlags.IsBlockScopedContainer; 1973 } 1974 1975 return ContainerFlags.None; 1976 } 1977 1978 function addToContainerChain(next: Node) { 1979 if (lastContainer) { 1980 lastContainer.nextContainer = next; 1981 } 1982 1983 lastContainer = next; 1984 } 1985 1986 function declareSymbolAndAddToSymbolTable(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): Symbol | undefined { 1987 switch (container.kind) { 1988 // Modules, source files, and classes need specialized handling for how their 1989 // members are declared (for example, a member of a class will go into a specific 1990 // symbol table depending on if it is static or not). We defer to specialized 1991 // handlers to take care of declaring these child members. 1992 case SyntaxKind.ModuleDeclaration: 1993 return declareModuleMember(node, symbolFlags, symbolExcludes); 1994 1995 case SyntaxKind.SourceFile: 1996 return declareSourceFileMember(node, symbolFlags, symbolExcludes); 1997 1998 case SyntaxKind.ClassExpression: 1999 case SyntaxKind.ClassDeclaration: 2000 case SyntaxKind.StructDeclaration: 2001 case SyntaxKind.AnnotationDeclaration: 2002 return declareClassMember(node, symbolFlags, symbolExcludes); 2003 2004 case SyntaxKind.EnumDeclaration: 2005 return declareSymbol(container.symbol.exports!, container.symbol, node, symbolFlags, symbolExcludes); 2006 2007 case SyntaxKind.TypeLiteral: 2008 case SyntaxKind.JSDocTypeLiteral: 2009 case SyntaxKind.ObjectLiteralExpression: 2010 case SyntaxKind.InterfaceDeclaration: 2011 case SyntaxKind.JsxAttributes: 2012 // Interface/Object-types always have their children added to the 'members' of 2013 // their container. They are only accessible through an instance of their 2014 // container, and are never in scope otherwise (even inside the body of the 2015 // object / type / interface declaring them). An exception is type parameters, 2016 // which are in scope without qualification (similar to 'locals'). 2017 return declareSymbol(container.symbol.members!, container.symbol, node, symbolFlags, symbolExcludes); 2018 2019 case SyntaxKind.FunctionType: 2020 case SyntaxKind.ConstructorType: 2021 case SyntaxKind.CallSignature: 2022 case SyntaxKind.ConstructSignature: 2023 case SyntaxKind.JSDocSignature: 2024 case SyntaxKind.IndexSignature: 2025 case SyntaxKind.MethodDeclaration: 2026 case SyntaxKind.MethodSignature: 2027 case SyntaxKind.Constructor: 2028 case SyntaxKind.GetAccessor: 2029 case SyntaxKind.SetAccessor: 2030 case SyntaxKind.FunctionDeclaration: 2031 case SyntaxKind.FunctionExpression: 2032 case SyntaxKind.ArrowFunction: 2033 case SyntaxKind.JSDocFunctionType: 2034 case SyntaxKind.JSDocTypedefTag: 2035 case SyntaxKind.JSDocCallbackTag: 2036 case SyntaxKind.ClassStaticBlockDeclaration: 2037 case SyntaxKind.TypeAliasDeclaration: 2038 case SyntaxKind.MappedType: 2039 // All the children of these container types are never visible through another 2040 // symbol (i.e. through another symbol's 'exports' or 'members'). Instead, 2041 // they're only accessed 'lexically' (i.e. from code that exists underneath 2042 // their container in the tree). To accomplish this, we simply add their declared 2043 // symbol to the 'locals' of the container. These symbols can then be found as 2044 // the type checker walks up the containers, checking them for matching names. 2045 return declareSymbol(container.locals!, /*parent*/ undefined, node, symbolFlags, symbolExcludes); 2046 } 2047 } 2048 2049 function declareClassMember(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { 2050 return isStatic(node) 2051 ? declareSymbol(container.symbol.exports!, container.symbol, node, symbolFlags, symbolExcludes) 2052 : declareSymbol(container.symbol.members!, container.symbol, node, symbolFlags, symbolExcludes); 2053 } 2054 2055 function declareSourceFileMember(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { 2056 return isExternalModule(file) 2057 ? declareModuleMember(node, symbolFlags, symbolExcludes) 2058 : declareSymbol(file.locals!, /*parent*/ undefined, node, symbolFlags, symbolExcludes); 2059 } 2060 2061 function hasExportDeclarations(node: ModuleDeclaration | SourceFile): boolean { 2062 const body = isSourceFile(node) ? node : tryCast(node.body, isModuleBlock); 2063 return !!body && body.statements.some(s => isExportDeclaration(s) || isExportAssignment(s)); 2064 } 2065 2066 function setExportContextFlag(node: Mutable<ModuleDeclaration | SourceFile>) { 2067 // A declaration source file or ambient module declaration that contains no export declarations (but possibly regular 2068 // declarations with export modifiers) is an export context in which declarations are implicitly exported. 2069 if (node.flags & NodeFlags.Ambient && !hasExportDeclarations(node)) { 2070 node.flags |= NodeFlags.ExportContext; 2071 } 2072 else { 2073 node.flags &= ~NodeFlags.ExportContext; 2074 } 2075 } 2076 2077 function bindModuleDeclaration(node: ModuleDeclaration) { 2078 setExportContextFlag(node); 2079 if (isAmbientModule(node)) { 2080 if (hasSyntacticModifier(node, ModifierFlags.Export)) { 2081 errorOnFirstToken(node, Diagnostics.export_modifier_cannot_be_applied_to_ambient_modules_and_module_augmentations_since_they_are_always_visible); 2082 } 2083 if (isModuleAugmentationExternal(node)) { 2084 declareModuleSymbol(node); 2085 } 2086 else { 2087 let pattern: string | Pattern | undefined; 2088 if (node.name.kind === SyntaxKind.StringLiteral) { 2089 const { text } = node.name; 2090 pattern = tryParsePattern(text); 2091 if (pattern === undefined) { 2092 errorOnFirstToken(node.name, Diagnostics.Pattern_0_can_have_at_most_one_Asterisk_character, text); 2093 } 2094 } 2095 2096 const symbol = declareSymbolAndAddToSymbolTable(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes)!; 2097 file.patternAmbientModules = append<PatternAmbientModule>(file.patternAmbientModules, pattern && !isString(pattern) ? { pattern, symbol } : undefined); 2098 } 2099 } 2100 else { 2101 const state = declareModuleSymbol(node); 2102 if (state !== ModuleInstanceState.NonInstantiated) { 2103 const { symbol } = node; 2104 // if module was already merged with some function, class or non-const enum, treat it as non-const-enum-only 2105 symbol.constEnumOnlyModule = (!(symbol.flags & (SymbolFlags.Function | SymbolFlags.Class | SymbolFlags.RegularEnum))) 2106 // Current must be `const enum` only 2107 && state === ModuleInstanceState.ConstEnumOnly 2108 // Can't have been set to 'false' in a previous merged symbol. ('undefined' OK) 2109 && symbol.constEnumOnlyModule !== false; 2110 } 2111 } 2112 } 2113 2114 function declareModuleSymbol(node: ModuleDeclaration): ModuleInstanceState { 2115 const state = getModuleInstanceState(node); 2116 const instantiated = state !== ModuleInstanceState.NonInstantiated; 2117 declareSymbolAndAddToSymbolTable(node, 2118 instantiated ? SymbolFlags.ValueModule : SymbolFlags.NamespaceModule, 2119 instantiated ? SymbolFlags.ValueModuleExcludes : SymbolFlags.NamespaceModuleExcludes); 2120 return state; 2121 } 2122 2123 function bindFunctionOrConstructorType(node: SignatureDeclaration | JSDocSignature): void { 2124 // For a given function symbol "<...>(...) => T" we want to generate a symbol identical 2125 // to the one we would get for: { <...>(...): T } 2126 // 2127 // We do that by making an anonymous type literal symbol, and then setting the function 2128 // symbol as its sole member. To the rest of the system, this symbol will be indistinguishable 2129 // from an actual type literal symbol you would have gotten had you used the long form. 2130 const symbol = createSymbol(SymbolFlags.Signature, getDeclarationName(node)!); // TODO: GH#18217 2131 addDeclarationToSymbol(symbol, node, SymbolFlags.Signature); 2132 2133 const typeLiteralSymbol = createSymbol(SymbolFlags.TypeLiteral, InternalSymbolName.Type); 2134 addDeclarationToSymbol(typeLiteralSymbol, node, SymbolFlags.TypeLiteral); 2135 typeLiteralSymbol.members = createSymbolTable(); 2136 typeLiteralSymbol.members.set(symbol.escapedName, symbol); 2137 } 2138 2139 function bindObjectLiteralExpression(node: ObjectLiteralExpression) { 2140 return bindAnonymousDeclaration(node, SymbolFlags.ObjectLiteral, InternalSymbolName.Object); 2141 } 2142 2143 function bindJsxAttributes(node: JsxAttributes) { 2144 return bindAnonymousDeclaration(node, SymbolFlags.ObjectLiteral, InternalSymbolName.JSXAttributes); 2145 } 2146 2147 function bindJsxAttribute(node: JsxAttribute, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { 2148 return declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes); 2149 } 2150 2151 function bindAnonymousDeclaration(node: Declaration, symbolFlags: SymbolFlags, name: __String) { 2152 const symbol = createSymbol(symbolFlags, name); 2153 if (symbolFlags & (SymbolFlags.EnumMember | SymbolFlags.ClassMember)) { 2154 symbol.parent = container.symbol; 2155 } 2156 addDeclarationToSymbol(symbol, node, symbolFlags); 2157 return symbol; 2158 } 2159 2160 function bindBlockScopedDeclaration(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { 2161 switch (blockScopeContainer.kind) { 2162 case SyntaxKind.ModuleDeclaration: 2163 declareModuleMember(node, symbolFlags, symbolExcludes); 2164 break; 2165 case SyntaxKind.SourceFile: 2166 if (isExternalOrCommonJsModule(container as SourceFile)) { 2167 declareModuleMember(node, symbolFlags, symbolExcludes); 2168 break; 2169 } 2170 // falls through 2171 default: 2172 if (!blockScopeContainer.locals) { 2173 blockScopeContainer.locals = createSymbolTable(); 2174 addToContainerChain(blockScopeContainer); 2175 } 2176 declareSymbol(blockScopeContainer.locals, /*parent*/ undefined, node, symbolFlags, symbolExcludes); 2177 } 2178 } 2179 2180 function delayedBindJSDocTypedefTag() { 2181 if (!delayedTypeAliases) { 2182 return; 2183 } 2184 const saveContainer = container; 2185 const saveLastContainer = lastContainer; 2186 const saveBlockScopeContainer = blockScopeContainer; 2187 const saveParent = parent; 2188 const saveCurrentFlow = currentFlow; 2189 for (const typeAlias of delayedTypeAliases) { 2190 const host = typeAlias.parent.parent; 2191 container = findAncestor(host.parent, n => !!(getContainerFlags(n) & ContainerFlags.IsContainer)) || file; 2192 blockScopeContainer = getEnclosingBlockScopeContainer(host) || file; 2193 currentFlow = initFlowNode({ flags: FlowFlags.Start }); 2194 parent = typeAlias; 2195 bind(typeAlias.typeExpression); 2196 const declName = getNameOfDeclaration(typeAlias); 2197 if ((isJSDocEnumTag(typeAlias) || !typeAlias.fullName) && declName && isPropertyAccessEntityNameExpression(declName.parent)) { 2198 // typedef anchored to an A.B.C assignment - we need to bind into B's namespace under name C 2199 const isTopLevel = isTopLevelNamespaceAssignment(declName.parent); 2200 if (isTopLevel) { 2201 bindPotentiallyMissingNamespaces(file.symbol, declName.parent, isTopLevel, 2202 !!findAncestor(declName, d => isPropertyAccessExpression(d) && d.name.escapedText === "prototype"), /*containerIsClass*/ false); 2203 const oldContainer = container; 2204 switch (getAssignmentDeclarationPropertyAccessKind(declName.parent)) { 2205 case AssignmentDeclarationKind.ExportsProperty: 2206 case AssignmentDeclarationKind.ModuleExports: 2207 if (!isExternalOrCommonJsModule(file)) { 2208 container = undefined!; 2209 } 2210 else { 2211 container = file; 2212 } 2213 break; 2214 case AssignmentDeclarationKind.ThisProperty: 2215 container = declName.parent.expression; 2216 break; 2217 case AssignmentDeclarationKind.PrototypeProperty: 2218 container = (declName.parent.expression as PropertyAccessExpression).name; 2219 break; 2220 case AssignmentDeclarationKind.Property: 2221 container = isExportsOrModuleExportsOrAlias(file, declName.parent.expression) ? file 2222 : isPropertyAccessExpression(declName.parent.expression) ? declName.parent.expression.name 2223 : declName.parent.expression; 2224 break; 2225 case AssignmentDeclarationKind.None: 2226 return Debug.fail("Shouldn't have detected typedef or enum on non-assignment declaration"); 2227 } 2228 if (container) { 2229 declareModuleMember(typeAlias, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes); 2230 } 2231 container = oldContainer; 2232 } 2233 } 2234 else if (isJSDocEnumTag(typeAlias) || !typeAlias.fullName || typeAlias.fullName.kind === SyntaxKind.Identifier) { 2235 parent = typeAlias.parent; 2236 bindBlockScopedDeclaration(typeAlias, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes); 2237 } 2238 else { 2239 bind(typeAlias.fullName); 2240 } 2241 } 2242 container = saveContainer; 2243 lastContainer = saveLastContainer; 2244 blockScopeContainer = saveBlockScopeContainer; 2245 parent = saveParent; 2246 currentFlow = saveCurrentFlow; 2247 } 2248 2249 // The binder visits every node in the syntax tree so it is a convenient place to perform a single localized 2250 // check for reserved words used as identifiers in strict mode code, as well as `yield` or `await` in 2251 // [Yield] or [Await] contexts, respectively. 2252 function checkContextualIdentifier(node: Identifier) { 2253 // Report error only if there are no parse errors in file 2254 if (!file.parseDiagnostics.length && 2255 !(node.flags & NodeFlags.Ambient) && 2256 !(node.flags & NodeFlags.JSDoc) && 2257 !isIdentifierName(node)) { 2258 2259 // strict mode identifiers 2260 if (inStrictMode && 2261 node.originalKeywordKind! >= SyntaxKind.FirstFutureReservedWord && 2262 node.originalKeywordKind! <= SyntaxKind.LastFutureReservedWord) { 2263 file.bindDiagnostics.push(createDiagnosticForNode(node, 2264 getStrictModeIdentifierMessage(node), declarationNameToString(node))); 2265 } 2266 else if (node.originalKeywordKind === SyntaxKind.AwaitKeyword) { 2267 if (isExternalModule(file) && isInTopLevelContext(node)) { 2268 file.bindDiagnostics.push(createDiagnosticForNode(node, 2269 Diagnostics.Identifier_expected_0_is_a_reserved_word_at_the_top_level_of_a_module, 2270 declarationNameToString(node))); 2271 } 2272 else if (node.flags & NodeFlags.AwaitContext) { 2273 file.bindDiagnostics.push(createDiagnosticForNode(node, 2274 Diagnostics.Identifier_expected_0_is_a_reserved_word_that_cannot_be_used_here, 2275 declarationNameToString(node))); 2276 } 2277 } 2278 else if (node.originalKeywordKind === SyntaxKind.YieldKeyword && node.flags & NodeFlags.YieldContext) { 2279 file.bindDiagnostics.push(createDiagnosticForNode(node, 2280 Diagnostics.Identifier_expected_0_is_a_reserved_word_that_cannot_be_used_here, 2281 declarationNameToString(node))); 2282 } 2283 } 2284 } 2285 2286 function getStrictModeIdentifierMessage(node: Node) { 2287 // Provide specialized messages to help the user understand why we think they're in 2288 // strict mode. 2289 if (getContainingClass(node)) { 2290 return Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode_Class_definitions_are_automatically_in_strict_mode; 2291 } 2292 2293 if (file.externalModuleIndicator) { 2294 return Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode_Modules_are_automatically_in_strict_mode; 2295 } 2296 2297 return Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode; 2298 } 2299 2300 // The binder visits every node, so this is a good place to check for 2301 // the reserved private name (there is only one) 2302 function checkPrivateIdentifier(node: PrivateIdentifier) { 2303 if (node.escapedText === "#constructor") { 2304 // Report error only if there are no parse errors in file 2305 if (!file.parseDiagnostics.length) { 2306 file.bindDiagnostics.push(createDiagnosticForNode(node, 2307 Diagnostics.constructor_is_a_reserved_word, declarationNameToString(node))); 2308 } 2309 } 2310 } 2311 2312 function checkStrictModeBinaryExpression(node: BinaryExpression) { 2313 if (inStrictMode && isLeftHandSideExpression(node.left) && isAssignmentOperator(node.operatorToken.kind)) { 2314 // ECMA 262 (Annex C) The identifier eval or arguments may not appear as the LeftHandSideExpression of an 2315 // Assignment operator(11.13) or of a PostfixExpression(11.3) 2316 checkStrictModeEvalOrArguments(node, node.left as Identifier); 2317 } 2318 } 2319 2320 function checkStrictModeCatchClause(node: CatchClause) { 2321 // It is a SyntaxError if a TryStatement with a Catch occurs within strict code and the Identifier of the 2322 // Catch production is eval or arguments 2323 if (inStrictMode && node.variableDeclaration) { 2324 checkStrictModeEvalOrArguments(node, node.variableDeclaration.name); 2325 } 2326 } 2327 2328 function checkStrictModeDeleteExpression(node: DeleteExpression) { 2329 // Grammar checking 2330 if (inStrictMode && node.expression.kind === SyntaxKind.Identifier) { 2331 // When a delete operator occurs within strict mode code, a SyntaxError is thrown if its 2332 // UnaryExpression is a direct reference to a variable, function argument, or function name 2333 const span = getErrorSpanForNode(file, node.expression); 2334 file.bindDiagnostics.push(createFileDiagnostic(file, span.start, span.length, Diagnostics.delete_cannot_be_called_on_an_identifier_in_strict_mode)); 2335 } 2336 } 2337 2338 function isEvalOrArgumentsIdentifier(node: Node): boolean { 2339 return isIdentifier(node) && (node.escapedText === "eval" || node.escapedText === "arguments"); 2340 } 2341 2342 function checkStrictModeEvalOrArguments(contextNode: Node, name: Node | undefined) { 2343 if (name && name.kind === SyntaxKind.Identifier) { 2344 const identifier = name as Identifier; 2345 if (isEvalOrArgumentsIdentifier(identifier)) { 2346 // We check first if the name is inside class declaration or class expression; if so give explicit message 2347 // otherwise report generic error message. 2348 const span = getErrorSpanForNode(file, name); 2349 file.bindDiagnostics.push(createFileDiagnostic(file, span.start, span.length, 2350 getStrictModeEvalOrArgumentsMessage(contextNode), idText(identifier))); 2351 } 2352 } 2353 } 2354 2355 function getStrictModeEvalOrArgumentsMessage(node: Node) { 2356 // Provide specialized messages to help the user understand why we think they're in 2357 // strict mode. 2358 if (getContainingClass(node)) { 2359 return Diagnostics.Code_contained_in_a_class_is_evaluated_in_JavaScript_s_strict_mode_which_does_not_allow_this_use_of_0_For_more_information_see_https_Colon_Slash_Slashdeveloper_mozilla_org_Slashen_US_Slashdocs_SlashWeb_SlashJavaScript_SlashReference_SlashStrict_mode; 2360 } 2361 2362 if (file.externalModuleIndicator) { 2363 return Diagnostics.Invalid_use_of_0_Modules_are_automatically_in_strict_mode; 2364 } 2365 2366 return Diagnostics.Invalid_use_of_0_in_strict_mode; 2367 } 2368 2369 function checkStrictModeFunctionName(node: FunctionLikeDeclaration) { 2370 if (inStrictMode) { 2371 // It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a strict mode FunctionDeclaration or FunctionExpression (13.1)) 2372 checkStrictModeEvalOrArguments(node, node.name); 2373 } 2374 } 2375 2376 function getStrictModeBlockScopeFunctionDeclarationMessage(node: Node) { 2377 // Provide specialized messages to help the user understand why we think they're in 2378 // strict mode. 2379 if (getContainingClass(node)) { 2380 return Diagnostics.Function_declarations_are_not_allowed_inside_blocks_in_strict_mode_when_targeting_ES3_or_ES5_Class_definitions_are_automatically_in_strict_mode; 2381 } 2382 2383 if (file.externalModuleIndicator) { 2384 return Diagnostics.Function_declarations_are_not_allowed_inside_blocks_in_strict_mode_when_targeting_ES3_or_ES5_Modules_are_automatically_in_strict_mode; 2385 } 2386 2387 return Diagnostics.Function_declarations_are_not_allowed_inside_blocks_in_strict_mode_when_targeting_ES3_or_ES5; 2388 } 2389 2390 function checkStrictModeFunctionDeclaration(node: FunctionDeclaration) { 2391 if (languageVersion < ScriptTarget.ES2015) { 2392 // Report error if function is not top level function declaration 2393 if (blockScopeContainer.kind !== SyntaxKind.SourceFile && 2394 blockScopeContainer.kind !== SyntaxKind.ModuleDeclaration && 2395 !isFunctionLikeOrClassStaticBlockDeclaration(blockScopeContainer)) { 2396 // We check first if the name is inside class declaration or class expression; if so give explicit message 2397 // otherwise report generic error message. 2398 const errorSpan = getErrorSpanForNode(file, node); 2399 file.bindDiagnostics.push(createFileDiagnostic(file, errorSpan.start, errorSpan.length, 2400 getStrictModeBlockScopeFunctionDeclarationMessage(node))); 2401 } 2402 } 2403 } 2404 2405 function checkStrictModeNumericLiteral(node: NumericLiteral) { 2406 if (languageVersion < ScriptTarget.ES5 && inStrictMode && node.numericLiteralFlags & TokenFlags.Octal) { 2407 file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Octal_literals_are_not_allowed_in_strict_mode)); 2408 } 2409 } 2410 2411 function checkStrictModePostfixUnaryExpression(node: PostfixUnaryExpression) { 2412 // Grammar checking 2413 // The identifier eval or arguments may not appear as the LeftHandSideExpression of an 2414 // Assignment operator(11.13) or of a PostfixExpression(11.3) or as the UnaryExpression 2415 // operated upon by a Prefix Increment(11.4.4) or a Prefix Decrement(11.4.5) operator. 2416 if (inStrictMode) { 2417 checkStrictModeEvalOrArguments(node, node.operand as Identifier); 2418 } 2419 } 2420 2421 function checkStrictModePrefixUnaryExpression(node: PrefixUnaryExpression) { 2422 // Grammar checking 2423 if (inStrictMode) { 2424 if (node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken) { 2425 checkStrictModeEvalOrArguments(node, node.operand as Identifier); 2426 } 2427 } 2428 } 2429 2430 function checkStrictModeWithStatement(node: WithStatement) { 2431 // Grammar checking for withStatement 2432 if (inStrictMode) { 2433 errorOnFirstToken(node, Diagnostics.with_statements_are_not_allowed_in_strict_mode); 2434 } 2435 } 2436 2437 function checkStrictModeLabeledStatement(node: LabeledStatement) { 2438 // Grammar checking for labeledStatement 2439 if (inStrictMode && getEmitScriptTarget(options) >= ScriptTarget.ES2015) { 2440 if (isDeclarationStatement(node.statement) || isVariableStatement(node.statement)) { 2441 errorOnFirstToken(node.label, Diagnostics.A_label_is_not_allowed_here); 2442 } 2443 } 2444 } 2445 2446 function errorOnFirstToken(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any) { 2447 const span = getSpanOfTokenAtPosition(file, node.pos); 2448 file.bindDiagnostics.push(createFileDiagnostic(file, span.start, span.length, message, arg0, arg1, arg2)); 2449 } 2450 2451 function errorOrSuggestionOnNode(isError: boolean, node: Node, message: DiagnosticMessage): void { 2452 errorOrSuggestionOnRange(isError, node, node, message); 2453 } 2454 2455 function errorOrSuggestionOnRange(isError: boolean, startNode: Node, endNode: Node, message: DiagnosticMessage): void { 2456 addErrorOrSuggestionDiagnostic(isError, { pos: getTokenPosOfNode(startNode, file), end: endNode.end }, message); 2457 } 2458 2459 function addErrorOrSuggestionDiagnostic(isError: boolean, range: TextRange, message: DiagnosticMessage): void { 2460 const diag = createFileDiagnostic(file, range.pos, range.end - range.pos, message); 2461 if (isError) { 2462 file.bindDiagnostics.push(diag); 2463 } 2464 else { 2465 file.bindSuggestionDiagnostics = append(file.bindSuggestionDiagnostics, { ...diag, category: DiagnosticCategory.Suggestion }); 2466 } 2467 } 2468 2469 function bind(node: Node | undefined): void { 2470 if (!node) { 2471 return; 2472 } 2473 setParent(node, parent); 2474 if (tracing) (node as TracingNode).tracingPath = file.path; 2475 const saveInStrictMode = inStrictMode; 2476 2477 // Even though in the AST the jsdoc @typedef node belongs to the current node, 2478 // its symbol might be in the same scope with the current node's symbol. Consider: 2479 // 2480 // /** @typedef {string | number} MyType */ 2481 // function foo(); 2482 // 2483 // Here the current node is "foo", which is a container, but the scope of "MyType" should 2484 // not be inside "foo". Therefore we always bind @typedef before bind the parent node, 2485 // and skip binding this tag later when binding all the other jsdoc tags. 2486 2487 // First we bind declaration nodes to a symbol if possible. We'll both create a symbol 2488 // and then potentially add the symbol to an appropriate symbol table. Possible 2489 // destination symbol tables are: 2490 // 2491 // 1) The 'exports' table of the current container's symbol. 2492 // 2) The 'members' table of the current container's symbol. 2493 // 3) The 'locals' table of the current container. 2494 // 2495 // However, not all symbols will end up in any of these tables. 'Anonymous' symbols 2496 // (like TypeLiterals for example) will not be put in any table. 2497 bindWorker(node); 2498 // Then we recurse into the children of the node to bind them as well. For certain 2499 // symbols we do specialized work when we recurse. For example, we'll keep track of 2500 // the current 'container' node when it changes. This helps us know which symbol table 2501 // a local should go into for example. Since terminal nodes are known not to have 2502 // children, as an optimization we don't process those. 2503 if (node.kind > SyntaxKind.LastToken) { 2504 const saveParent = parent; 2505 parent = node; 2506 const containerFlags = getContainerFlags(node); 2507 if (containerFlags === ContainerFlags.None) { 2508 bindChildren(node); 2509 } 2510 else { 2511 bindContainer(node, containerFlags); 2512 } 2513 parent = saveParent; 2514 } 2515 else { 2516 const saveParent = parent; 2517 if (node.kind === SyntaxKind.EndOfFileToken) parent = node; 2518 bindJSDoc(node); 2519 parent = saveParent; 2520 } 2521 inStrictMode = saveInStrictMode; 2522 } 2523 2524 function bindJSDoc(node: Node) { 2525 if (hasJSDocNodes(node)) { 2526 if (isInJSFile(node)) { 2527 for (const j of node.jsDoc!) { 2528 bind(j); 2529 } 2530 } 2531 else { 2532 for (const j of node.jsDoc!) { 2533 setParent(j, node); 2534 setParentRecursive(j, /*incremental*/ false); 2535 } 2536 } 2537 } 2538 } 2539 2540 function updateStrictModeStatementList(statements: NodeArray<Statement>) { 2541 if (!inStrictMode) { 2542 for (const statement of statements) { 2543 if (!isPrologueDirective(statement)) { 2544 return; 2545 } 2546 2547 if (isUseStrictPrologueDirective(statement as ExpressionStatement)) { 2548 inStrictMode = true; 2549 return; 2550 } 2551 } 2552 } 2553 } 2554 2555 /// Should be called only on prologue directives (isPrologueDirective(node) should be true) 2556 function isUseStrictPrologueDirective(node: ExpressionStatement): boolean { 2557 const nodeText = getSourceTextOfNodeFromSourceFile(file, node.expression); 2558 2559 // Note: the node text must be exactly "use strict" or 'use strict'. It is not ok for the 2560 // string to contain unicode escapes (as per ES5). 2561 return nodeText === '"use strict"' || nodeText === "'use strict'"; 2562 } 2563 2564 function bindWorker(node: Node) { 2565 switch (node.kind) { 2566 /* Strict mode checks */ 2567 case SyntaxKind.Identifier: 2568 // for typedef type names with namespaces, bind the new jsdoc type symbol here 2569 // because it requires all containing namespaces to be in effect, namely the 2570 // current "blockScopeContainer" needs to be set to its immediate namespace parent. 2571 if ((node as Identifier).isInJSDocNamespace) { 2572 let parentNode = node.parent; 2573 while (parentNode && !isJSDocTypeAlias(parentNode)) { 2574 parentNode = parentNode.parent; 2575 } 2576 bindBlockScopedDeclaration(parentNode as Declaration, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes); 2577 break; 2578 } 2579 // falls through 2580 case SyntaxKind.ThisKeyword: 2581 if (currentFlow && (isExpression(node) || parent.kind === SyntaxKind.ShorthandPropertyAssignment)) { 2582 node.flowNode = currentFlow; 2583 } 2584 return checkContextualIdentifier(node as Identifier); 2585 case SyntaxKind.QualifiedName: 2586 if (currentFlow && isPartOfTypeQuery(node)) { 2587 node.flowNode = currentFlow; 2588 } 2589 break; 2590 case SyntaxKind.MetaProperty: 2591 case SyntaxKind.SuperKeyword: 2592 node.flowNode = currentFlow; 2593 break; 2594 case SyntaxKind.PrivateIdentifier: 2595 return checkPrivateIdentifier(node as PrivateIdentifier); 2596 case SyntaxKind.PropertyAccessExpression: 2597 case SyntaxKind.ElementAccessExpression: 2598 const expr = node as PropertyAccessExpression | ElementAccessExpression; 2599 if (currentFlow && isNarrowableReference(expr)) { 2600 expr.flowNode = currentFlow; 2601 } 2602 if (isSpecialPropertyDeclaration(expr)) { 2603 bindSpecialPropertyDeclaration(expr); 2604 } 2605 if (isInJSFile(expr) && 2606 file.commonJsModuleIndicator && 2607 isModuleExportsAccessExpression(expr) && 2608 !lookupSymbolForName(blockScopeContainer, "module" as __String)) { 2609 declareSymbol(file.locals!, /*parent*/ undefined, expr.expression, 2610 SymbolFlags.FunctionScopedVariable | SymbolFlags.ModuleExports, SymbolFlags.FunctionScopedVariableExcludes); 2611 } 2612 break; 2613 case SyntaxKind.BinaryExpression: 2614 const specialKind = getAssignmentDeclarationKind(node as BinaryExpression); 2615 switch (specialKind) { 2616 case AssignmentDeclarationKind.ExportsProperty: 2617 bindExportsPropertyAssignment(node as BindableStaticPropertyAssignmentExpression); 2618 break; 2619 case AssignmentDeclarationKind.ModuleExports: 2620 bindModuleExportsAssignment(node as BindablePropertyAssignmentExpression); 2621 break; 2622 case AssignmentDeclarationKind.PrototypeProperty: 2623 bindPrototypePropertyAssignment((node as BindableStaticPropertyAssignmentExpression).left, node); 2624 break; 2625 case AssignmentDeclarationKind.Prototype: 2626 bindPrototypeAssignment(node as BindableStaticPropertyAssignmentExpression); 2627 break; 2628 case AssignmentDeclarationKind.ThisProperty: 2629 bindThisPropertyAssignment(node as BindablePropertyAssignmentExpression); 2630 break; 2631 case AssignmentDeclarationKind.Property: 2632 const expression = ((node as BinaryExpression).left as AccessExpression).expression; 2633 if (isInJSFile(node) && isIdentifier(expression)) { 2634 const symbol = lookupSymbolForName(blockScopeContainer, expression.escapedText); 2635 if (isThisInitializedDeclaration(symbol?.valueDeclaration)) { 2636 bindThisPropertyAssignment(node as BindablePropertyAssignmentExpression); 2637 break; 2638 } 2639 } 2640 bindSpecialPropertyAssignment(node as BindablePropertyAssignmentExpression); 2641 break; 2642 case AssignmentDeclarationKind.None: 2643 // Nothing to do 2644 break; 2645 default: 2646 Debug.fail("Unknown binary expression special property assignment kind"); 2647 } 2648 return checkStrictModeBinaryExpression(node as BinaryExpression); 2649 case SyntaxKind.CatchClause: 2650 return checkStrictModeCatchClause(node as CatchClause); 2651 case SyntaxKind.DeleteExpression: 2652 return checkStrictModeDeleteExpression(node as DeleteExpression); 2653 case SyntaxKind.NumericLiteral: 2654 return checkStrictModeNumericLiteral(node as NumericLiteral); 2655 case SyntaxKind.PostfixUnaryExpression: 2656 return checkStrictModePostfixUnaryExpression(node as PostfixUnaryExpression); 2657 case SyntaxKind.PrefixUnaryExpression: 2658 return checkStrictModePrefixUnaryExpression(node as PrefixUnaryExpression); 2659 case SyntaxKind.WithStatement: 2660 return checkStrictModeWithStatement(node as WithStatement); 2661 case SyntaxKind.LabeledStatement: 2662 return checkStrictModeLabeledStatement(node as LabeledStatement); 2663 case SyntaxKind.ThisType: 2664 seenThisKeyword = true; 2665 return; 2666 case SyntaxKind.TypePredicate: 2667 break; // Binding the children will handle everything 2668 case SyntaxKind.TypeParameter: 2669 return bindTypeParameter(node as TypeParameterDeclaration); 2670 case SyntaxKind.Parameter: 2671 return bindParameter(node as ParameterDeclaration); 2672 case SyntaxKind.VariableDeclaration: 2673 return bindVariableDeclarationOrBindingElement(node as VariableDeclaration); 2674 case SyntaxKind.BindingElement: 2675 node.flowNode = currentFlow; 2676 return bindVariableDeclarationOrBindingElement(node as BindingElement); 2677 case SyntaxKind.PropertyDeclaration: 2678 case SyntaxKind.AnnotationPropertyDeclaration: 2679 case SyntaxKind.PropertySignature: 2680 return bindPropertyWorker(node as PropertyDeclaration | PropertySignature | AnnotationPropertyDeclaration); 2681 case SyntaxKind.PropertyAssignment: 2682 case SyntaxKind.ShorthandPropertyAssignment: 2683 return bindPropertyOrMethodOrAccessor(node as Declaration, SymbolFlags.Property, SymbolFlags.PropertyExcludes); 2684 case SyntaxKind.EnumMember: 2685 return bindPropertyOrMethodOrAccessor(node as Declaration, SymbolFlags.EnumMember, SymbolFlags.EnumMemberExcludes); 2686 2687 case SyntaxKind.CallSignature: 2688 case SyntaxKind.ConstructSignature: 2689 case SyntaxKind.IndexSignature: 2690 return declareSymbolAndAddToSymbolTable(node as Declaration, SymbolFlags.Signature, SymbolFlags.None); 2691 case SyntaxKind.MethodDeclaration: 2692 case SyntaxKind.MethodSignature: 2693 // If this is an ObjectLiteralExpression method, then it sits in the same space 2694 // as other properties in the object literal. So we use SymbolFlags.PropertyExcludes 2695 // so that it will conflict with any other object literal members with the same 2696 // name. 2697 return bindPropertyOrMethodOrAccessor(node as Declaration, SymbolFlags.Method | ((node as MethodDeclaration).questionToken ? SymbolFlags.Optional : SymbolFlags.None), 2698 isObjectLiteralMethod(node) ? SymbolFlags.PropertyExcludes : SymbolFlags.MethodExcludes); 2699 case SyntaxKind.FunctionDeclaration: 2700 return bindFunctionDeclaration(node as FunctionDeclaration); 2701 case SyntaxKind.Constructor: 2702 return declareSymbolAndAddToSymbolTable(node as Declaration, SymbolFlags.Constructor, /*symbolExcludes:*/ SymbolFlags.None); 2703 case SyntaxKind.GetAccessor: 2704 return bindPropertyOrMethodOrAccessor(node as Declaration, SymbolFlags.GetAccessor, SymbolFlags.GetAccessorExcludes); 2705 case SyntaxKind.SetAccessor: 2706 return bindPropertyOrMethodOrAccessor(node as Declaration, SymbolFlags.SetAccessor, SymbolFlags.SetAccessorExcludes); 2707 case SyntaxKind.FunctionType: 2708 case SyntaxKind.JSDocFunctionType: 2709 case SyntaxKind.JSDocSignature: 2710 case SyntaxKind.ConstructorType: 2711 return bindFunctionOrConstructorType(node as SignatureDeclaration | JSDocSignature); 2712 case SyntaxKind.TypeLiteral: 2713 case SyntaxKind.JSDocTypeLiteral: 2714 case SyntaxKind.MappedType: 2715 return bindAnonymousTypeWorker(node as TypeLiteralNode | MappedTypeNode | JSDocTypeLiteral); 2716 case SyntaxKind.JSDocClassTag: 2717 return bindJSDocClassTag(node as JSDocClassTag); 2718 case SyntaxKind.ObjectLiteralExpression: 2719 return bindObjectLiteralExpression(node as ObjectLiteralExpression); 2720 case SyntaxKind.FunctionExpression: 2721 case SyntaxKind.ArrowFunction: 2722 return bindFunctionExpression(node as FunctionExpression); 2723 2724 case SyntaxKind.CallExpression: 2725 const assignmentKind = getAssignmentDeclarationKind(node as CallExpression); 2726 switch (assignmentKind) { 2727 case AssignmentDeclarationKind.ObjectDefinePropertyValue: 2728 return bindObjectDefinePropertyAssignment(node as BindableObjectDefinePropertyCall); 2729 case AssignmentDeclarationKind.ObjectDefinePropertyExports: 2730 return bindObjectDefinePropertyExport(node as BindableObjectDefinePropertyCall); 2731 case AssignmentDeclarationKind.ObjectDefinePrototypeProperty: 2732 return bindObjectDefinePrototypeProperty(node as BindableObjectDefinePropertyCall); 2733 case AssignmentDeclarationKind.None: 2734 break; // Nothing to do 2735 default: 2736 return Debug.fail("Unknown call expression assignment declaration kind"); 2737 } 2738 if (isInJSFile(node)) { 2739 bindCallExpression(node as CallExpression); 2740 } 2741 break; 2742 2743 // Members of classes, interfaces, and modules 2744 case SyntaxKind.ClassExpression: 2745 case SyntaxKind.ClassDeclaration: 2746 case SyntaxKind.StructDeclaration: 2747 // All classes are automatically in strict mode in ES6. 2748 inStrictMode = true; 2749 return bindClassLikeDeclaration(node as ClassLikeDeclaration); 2750 case SyntaxKind.AnnotationDeclaration: 2751 return bindAnnotationDeclaration(node as AnnotationDeclaration); 2752 case SyntaxKind.InterfaceDeclaration: 2753 return bindBlockScopedDeclaration(node as Declaration, SymbolFlags.Interface, SymbolFlags.InterfaceExcludes); 2754 case SyntaxKind.TypeAliasDeclaration: 2755 return bindBlockScopedDeclaration(node as Declaration, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes); 2756 case SyntaxKind.EnumDeclaration: 2757 return bindEnumDeclaration(node as EnumDeclaration); 2758 case SyntaxKind.ModuleDeclaration: 2759 return bindModuleDeclaration(node as ModuleDeclaration); 2760 // Jsx-attributes 2761 case SyntaxKind.JsxAttributes: 2762 return bindJsxAttributes(node as JsxAttributes); 2763 case SyntaxKind.JsxAttribute: 2764 return bindJsxAttribute(node as JsxAttribute, SymbolFlags.Property, SymbolFlags.PropertyExcludes); 2765 2766 // Imports and exports 2767 case SyntaxKind.ImportEqualsDeclaration: 2768 case SyntaxKind.NamespaceImport: 2769 case SyntaxKind.ImportSpecifier: 2770 case SyntaxKind.ExportSpecifier: 2771 return declareSymbolAndAddToSymbolTable(node as Declaration, SymbolFlags.Alias, SymbolFlags.AliasExcludes); 2772 case SyntaxKind.NamespaceExportDeclaration: 2773 return bindNamespaceExportDeclaration(node as NamespaceExportDeclaration); 2774 case SyntaxKind.ImportClause: 2775 return bindImportClause(node as ImportClause); 2776 case SyntaxKind.ExportDeclaration: 2777 return bindExportDeclaration(node as ExportDeclaration); 2778 case SyntaxKind.ExportAssignment: 2779 return bindExportAssignment(node as ExportAssignment); 2780 case SyntaxKind.SourceFile: 2781 updateStrictModeStatementList((node as SourceFile).statements); 2782 return bindSourceFileIfExternalModule(); 2783 case SyntaxKind.Block: 2784 if (!isFunctionLikeOrClassStaticBlockDeclaration(node.parent)) { 2785 return; 2786 } 2787 // falls through 2788 case SyntaxKind.ModuleBlock: 2789 return updateStrictModeStatementList((node as Block | ModuleBlock).statements); 2790 2791 case SyntaxKind.JSDocParameterTag: 2792 if (node.parent.kind === SyntaxKind.JSDocSignature) { 2793 return bindParameter(node as JSDocParameterTag); 2794 } 2795 if (node.parent.kind !== SyntaxKind.JSDocTypeLiteral) { 2796 break; 2797 } 2798 // falls through 2799 case SyntaxKind.JSDocPropertyTag: 2800 const propTag = node as JSDocPropertyLikeTag; 2801 const flags = propTag.isBracketed || propTag.typeExpression && propTag.typeExpression.type.kind === SyntaxKind.JSDocOptionalType ? 2802 SymbolFlags.Property | SymbolFlags.Optional : 2803 SymbolFlags.Property; 2804 return declareSymbolAndAddToSymbolTable(propTag, flags, SymbolFlags.PropertyExcludes); 2805 case SyntaxKind.JSDocTypedefTag: 2806 case SyntaxKind.JSDocCallbackTag: 2807 case SyntaxKind.JSDocEnumTag: 2808 return (delayedTypeAliases || (delayedTypeAliases = [])).push(node as JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag); 2809 } 2810 } 2811 2812 function bindPropertyWorker(node: PropertyDeclaration | PropertySignature | AnnotationPropertyDeclaration) { 2813 const isAutoAccessor = isAutoAccessorPropertyDeclaration(node); 2814 const includes = isAutoAccessor ? SymbolFlags.Accessor : SymbolFlags.Property; 2815 const excludes = isAutoAccessor ? SymbolFlags.AccessorExcludes : SymbolFlags.PropertyExcludes; 2816 const isOptional = (!isAnnotationPropertyDeclaration(node) && node.questionToken ? SymbolFlags.Optional : SymbolFlags.None); 2817 const annotationPropHasDefaultValue = (isAnnotationPropertyDeclaration(node) && node.initializer !== undefined) ? 2818 SymbolFlags.Optional : SymbolFlags.None; 2819 return bindPropertyOrMethodOrAccessor(node, includes | isOptional | annotationPropHasDefaultValue, excludes); 2820 } 2821 2822 function bindAnonymousTypeWorker(node: TypeLiteralNode | MappedTypeNode | JSDocTypeLiteral) { 2823 return bindAnonymousDeclaration(node as Declaration, SymbolFlags.TypeLiteral, InternalSymbolName.Type); 2824 } 2825 2826 function bindSourceFileIfExternalModule() { 2827 setExportContextFlag(file); 2828 if (isExternalModule(file)) { 2829 bindSourceFileAsExternalModule(); 2830 } 2831 else if (isJsonSourceFile(file)) { 2832 bindSourceFileAsExternalModule(); 2833 // Create symbol equivalent for the module.exports = {} 2834 const originalSymbol = file.symbol; 2835 declareSymbol(file.symbol.exports!, file.symbol, file, SymbolFlags.Property, SymbolFlags.All); 2836 file.symbol = originalSymbol; 2837 } 2838 } 2839 2840 function bindSourceFileAsExternalModule() { 2841 bindAnonymousDeclaration(file, SymbolFlags.ValueModule, `"${removeFileExtension(file.fileName)}"` as __String); 2842 } 2843 2844 function bindExportAssignment(node: ExportAssignment) { 2845 if (!container.symbol || !container.symbol.exports) { 2846 // Incorrect export assignment in some sort of block construct 2847 bindAnonymousDeclaration(node, SymbolFlags.Value, getDeclarationName(node)!); 2848 } 2849 else { 2850 const flags = exportAssignmentIsAlias(node) 2851 // An export default clause with an EntityNameExpression or a class expression exports all meanings of that identifier or expression; 2852 ? SymbolFlags.Alias 2853 // An export default clause with any other expression exports a value 2854 : SymbolFlags.Property; 2855 // If there is an `export default x;` alias declaration, can't `export default` anything else. 2856 // (In contrast, you can still have `export default function f() {}` and `export default interface I {}`.) 2857 const symbol = declareSymbol(container.symbol.exports, container.symbol, node, flags, SymbolFlags.All); 2858 2859 if (node.isExportEquals) { 2860 // Will be an error later, since the module already has other exports. Just make sure this has a valueDeclaration set. 2861 setValueDeclaration(symbol, node); 2862 } 2863 } 2864 } 2865 2866 function bindNamespaceExportDeclaration(node: NamespaceExportDeclaration) { 2867 if (some(node.modifiers)) { 2868 file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Modifiers_cannot_appear_here)); 2869 } 2870 const diag = !isSourceFile(node.parent) ? Diagnostics.Global_module_exports_may_only_appear_at_top_level 2871 : !isExternalModule(node.parent) ? Diagnostics.Global_module_exports_may_only_appear_in_module_files 2872 : !node.parent.isDeclarationFile ? Diagnostics.Global_module_exports_may_only_appear_in_declaration_files 2873 : undefined; 2874 if (diag) { 2875 file.bindDiagnostics.push(createDiagnosticForNode(node, diag)); 2876 } 2877 else { 2878 file.symbol.globalExports = file.symbol.globalExports || createSymbolTable(); 2879 declareSymbol(file.symbol.globalExports, file.symbol, node, SymbolFlags.Alias, SymbolFlags.AliasExcludes); 2880 } 2881 } 2882 2883 function bindExportDeclaration(node: ExportDeclaration) { 2884 if (!container.symbol || !container.symbol.exports) { 2885 // Export * in some sort of block construct 2886 bindAnonymousDeclaration(node, SymbolFlags.ExportStar, getDeclarationName(node)!); 2887 } 2888 else if (!node.exportClause) { 2889 // All export * declarations are collected in an __export symbol 2890 declareSymbol(container.symbol.exports, container.symbol, node, SymbolFlags.ExportStar, SymbolFlags.None); 2891 } 2892 else if (isNamespaceExport(node.exportClause)) { 2893 // declareSymbol walks up parents to find name text, parent _must_ be set 2894 // but won't be set by the normal binder walk until `bindChildren` later on. 2895 setParent(node.exportClause, node); 2896 declareSymbol(container.symbol.exports, container.symbol, node.exportClause, SymbolFlags.Alias, SymbolFlags.AliasExcludes); 2897 } 2898 } 2899 2900 function bindImportClause(node: ImportClause) { 2901 if (node.name) { 2902 declareSymbolAndAddToSymbolTable(node, SymbolFlags.Alias, SymbolFlags.AliasExcludes); 2903 } 2904 } 2905 2906 function setCommonJsModuleIndicator(node: Node) { 2907 if (file.externalModuleIndicator && file.externalModuleIndicator !== true) { 2908 return false; 2909 } 2910 if (!file.commonJsModuleIndicator) { 2911 file.commonJsModuleIndicator = node; 2912 if (!file.externalModuleIndicator) { 2913 bindSourceFileAsExternalModule(); 2914 } 2915 } 2916 return true; 2917 } 2918 2919 function bindObjectDefinePropertyExport(node: BindableObjectDefinePropertyCall) { 2920 if (!setCommonJsModuleIndicator(node)) { 2921 return; 2922 } 2923 const symbol = forEachIdentifierInEntityName(node.arguments[0], /*parent*/ undefined, (id, symbol) => { 2924 if (symbol) { 2925 addDeclarationToSymbol(symbol, id, SymbolFlags.Module | SymbolFlags.Assignment); 2926 } 2927 return symbol; 2928 }); 2929 if (symbol) { 2930 const flags = SymbolFlags.Property | SymbolFlags.ExportValue; 2931 declareSymbol(symbol.exports!, symbol, node, flags, SymbolFlags.None); 2932 } 2933 } 2934 2935 function bindExportsPropertyAssignment(node: BindableStaticPropertyAssignmentExpression) { 2936 // When we create a property via 'exports.foo = bar', the 'exports.foo' property access 2937 // expression is the declaration 2938 if (!setCommonJsModuleIndicator(node)) { 2939 return; 2940 } 2941 const symbol = forEachIdentifierInEntityName(node.left.expression, /*parent*/ undefined, (id, symbol) => { 2942 if (symbol) { 2943 addDeclarationToSymbol(symbol, id, SymbolFlags.Module | SymbolFlags.Assignment); 2944 } 2945 return symbol; 2946 }); 2947 if (symbol) { 2948 const isAlias = isAliasableExpression(node.right) && (isExportsIdentifier(node.left.expression) || isModuleExportsAccessExpression(node.left.expression)); 2949 const flags = isAlias ? SymbolFlags.Alias : SymbolFlags.Property | SymbolFlags.ExportValue; 2950 setParent(node.left, node); 2951 declareSymbol(symbol.exports!, symbol, node.left, flags, SymbolFlags.None); 2952 } 2953 } 2954 2955 function bindModuleExportsAssignment(node: BindablePropertyAssignmentExpression) { 2956 // A common practice in node modules is to set 'export = module.exports = {}', this ensures that 'exports' 2957 // is still pointing to 'module.exports'. 2958 // We do not want to consider this as 'export=' since a module can have only one of these. 2959 // Similarly we do not want to treat 'module.exports = exports' as an 'export='. 2960 if (!setCommonJsModuleIndicator(node)) { 2961 return; 2962 } 2963 const assignedExpression = getRightMostAssignedExpression(node.right); 2964 if (isEmptyObjectLiteral(assignedExpression) || container === file && isExportsOrModuleExportsOrAlias(file, assignedExpression)) { 2965 return; 2966 } 2967 2968 if (isObjectLiteralExpression(assignedExpression) && every(assignedExpression.properties, isShorthandPropertyAssignment)) { 2969 forEach(assignedExpression.properties, bindExportAssignedObjectMemberAlias); 2970 return; 2971 } 2972 2973 // 'module.exports = expr' assignment 2974 const flags = exportAssignmentIsAlias(node) 2975 ? SymbolFlags.Alias // An export= with an EntityNameExpression or a ClassExpression exports all meanings of that identifier or class 2976 : SymbolFlags.Property | SymbolFlags.ExportValue | SymbolFlags.ValueModule; 2977 const symbol = declareSymbol(file.symbol.exports!, file.symbol, node, flags | SymbolFlags.Assignment, SymbolFlags.None); 2978 setValueDeclaration(symbol, node); 2979 } 2980 2981 function bindExportAssignedObjectMemberAlias(node: ShorthandPropertyAssignment) { 2982 declareSymbol(file.symbol.exports!, file.symbol, node, SymbolFlags.Alias | SymbolFlags.Assignment, SymbolFlags.None); 2983 } 2984 2985 function bindThisPropertyAssignment(node: BindablePropertyAssignmentExpression | PropertyAccessExpression | LiteralLikeElementAccessExpression) { 2986 Debug.assert(isInJSFile(node)); 2987 // private identifiers *must* be declared (even in JS files) 2988 const hasPrivateIdentifier = (isBinaryExpression(node) && isPropertyAccessExpression(node.left) && isPrivateIdentifier(node.left.name)) 2989 || (isPropertyAccessExpression(node) && isPrivateIdentifier(node.name)); 2990 if (hasPrivateIdentifier) { 2991 return; 2992 } 2993 const thisContainer = getThisContainer(node, /*includeArrowFunctions*/ false); 2994 switch (thisContainer.kind) { 2995 case SyntaxKind.FunctionDeclaration: 2996 case SyntaxKind.FunctionExpression: 2997 let constructorSymbol: Symbol | undefined = thisContainer.symbol; 2998 // For `f.prototype.m = function() { this.x = 0; }`, `this.x = 0` should modify `f`'s members, not the function expression. 2999 if (isBinaryExpression(thisContainer.parent) && thisContainer.parent.operatorToken.kind === SyntaxKind.EqualsToken) { 3000 const l = thisContainer.parent.left; 3001 if (isBindableStaticAccessExpression(l) && isPrototypeAccess(l.expression)) { 3002 constructorSymbol = lookupSymbolForPropertyAccess(l.expression.expression, thisParentContainer); 3003 } 3004 } 3005 3006 if (constructorSymbol && constructorSymbol.valueDeclaration) { 3007 // Declare a 'member' if the container is an ES5 class or ES6 constructor 3008 constructorSymbol.members = constructorSymbol.members || createSymbolTable(); 3009 // It's acceptable for multiple 'this' assignments of the same identifier to occur 3010 if (hasDynamicName(node)) { 3011 bindDynamicallyNamedThisPropertyAssignment(node, constructorSymbol, constructorSymbol.members); 3012 } 3013 else { 3014 declareSymbol(constructorSymbol.members, constructorSymbol, node, SymbolFlags.Property | SymbolFlags.Assignment, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property); 3015 } 3016 addDeclarationToSymbol(constructorSymbol, constructorSymbol.valueDeclaration, SymbolFlags.Class); 3017 } 3018 break; 3019 3020 case SyntaxKind.Constructor: 3021 case SyntaxKind.PropertyDeclaration: 3022 case SyntaxKind.AnnotationPropertyDeclaration: 3023 case SyntaxKind.MethodDeclaration: 3024 case SyntaxKind.GetAccessor: 3025 case SyntaxKind.SetAccessor: 3026 case SyntaxKind.ClassStaticBlockDeclaration: 3027 // this.foo assignment in a JavaScript class 3028 // Bind this property to the containing class 3029 const containingClass = thisContainer.parent; 3030 const symbolTable = isStatic(thisContainer) ? containingClass.symbol.exports! : containingClass.symbol.members!; 3031 if (hasDynamicName(node)) { 3032 bindDynamicallyNamedThisPropertyAssignment(node, containingClass.symbol, symbolTable); 3033 } 3034 else { 3035 declareSymbol(symbolTable, containingClass.symbol, node, SymbolFlags.Property | SymbolFlags.Assignment, SymbolFlags.None, /*isReplaceableByMethod*/ true); 3036 } 3037 break; 3038 case SyntaxKind.SourceFile: 3039 // this.property = assignment in a source file -- declare symbol in exports for a module, in locals for a script 3040 if (hasDynamicName(node)) { 3041 break; 3042 } 3043 else if ((thisContainer as SourceFile).commonJsModuleIndicator) { 3044 declareSymbol(thisContainer.symbol.exports!, thisContainer.symbol, node, SymbolFlags.Property | SymbolFlags.ExportValue, SymbolFlags.None); 3045 } 3046 else { 3047 declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes); 3048 } 3049 break; 3050 3051 default: 3052 Debug.failBadSyntaxKind(thisContainer); 3053 } 3054 } 3055 3056 function bindDynamicallyNamedThisPropertyAssignment(node: BinaryExpression | DynamicNamedDeclaration, symbol: Symbol, symbolTable: SymbolTable) { 3057 declareSymbol(symbolTable, symbol, node, SymbolFlags.Property, SymbolFlags.None, /*isReplaceableByMethod*/ true, /*isComputedName*/ true); 3058 addLateBoundAssignmentDeclarationToSymbol(node, symbol); 3059 } 3060 3061 function addLateBoundAssignmentDeclarationToSymbol(node: BinaryExpression | DynamicNamedDeclaration, symbol: Symbol | undefined) { 3062 if (symbol) { 3063 (symbol.assignmentDeclarationMembers || (symbol.assignmentDeclarationMembers = new Map())).set(getNodeId(node), node); 3064 } 3065 } 3066 3067 function bindSpecialPropertyDeclaration(node: PropertyAccessExpression | LiteralLikeElementAccessExpression) { 3068 if (node.expression.kind === SyntaxKind.ThisKeyword) { 3069 bindThisPropertyAssignment(node); 3070 } 3071 else if (isBindableStaticAccessExpression(node) && node.parent.parent.kind === SyntaxKind.SourceFile) { 3072 if (isPrototypeAccess(node.expression)) { 3073 bindPrototypePropertyAssignment(node, node.parent); 3074 } 3075 else { 3076 bindStaticPropertyAssignment(node); 3077 } 3078 } 3079 } 3080 3081 /** For `x.prototype = { p, ... }`, declare members p,... if `x` is function/class/{}, or not declared. */ 3082 function bindPrototypeAssignment(node: BindableStaticPropertyAssignmentExpression) { 3083 setParent(node.left, node); 3084 setParent(node.right, node); 3085 bindPropertyAssignment(node.left.expression, node.left, /*isPrototypeProperty*/ false, /*containerIsClass*/ true); 3086 } 3087 3088 function bindObjectDefinePrototypeProperty(node: BindableObjectDefinePropertyCall) { 3089 const namespaceSymbol = lookupSymbolForPropertyAccess((node.arguments[0] as PropertyAccessExpression).expression as EntityNameExpression); 3090 if (namespaceSymbol && namespaceSymbol.valueDeclaration) { 3091 // Ensure the namespace symbol becomes class-like 3092 addDeclarationToSymbol(namespaceSymbol, namespaceSymbol.valueDeclaration, SymbolFlags.Class); 3093 } 3094 bindPotentiallyNewExpandoMemberToNamespace(node, namespaceSymbol, /*isPrototypeProperty*/ true); 3095 } 3096 3097 /** 3098 * For `x.prototype.y = z`, declare a member `y` on `x` if `x` is a function or class, or not declared. 3099 * Note that jsdoc preceding an ExpressionStatement like `x.prototype.y;` is also treated as a declaration. 3100 */ 3101 function bindPrototypePropertyAssignment(lhs: BindableStaticAccessExpression, parent: Node) { 3102 // Look up the function in the local scope, since prototype assignments should 3103 // follow the function declaration 3104 const classPrototype = lhs.expression as BindableStaticAccessExpression; 3105 const constructorFunction = classPrototype.expression; 3106 3107 // Fix up parent pointers since we're going to use these nodes before we bind into them 3108 setParent(constructorFunction, classPrototype); 3109 setParent(classPrototype, lhs); 3110 setParent(lhs, parent); 3111 3112 bindPropertyAssignment(constructorFunction, lhs, /*isPrototypeProperty*/ true, /*containerIsClass*/ true); 3113 } 3114 3115 function bindObjectDefinePropertyAssignment(node: BindableObjectDefinePropertyCall) { 3116 let namespaceSymbol = lookupSymbolForPropertyAccess(node.arguments[0]); 3117 const isToplevel = node.parent.parent.kind === SyntaxKind.SourceFile; 3118 namespaceSymbol = bindPotentiallyMissingNamespaces(namespaceSymbol, node.arguments[0], isToplevel, /*isPrototypeProperty*/ false, /*containerIsClass*/ false); 3119 bindPotentiallyNewExpandoMemberToNamespace(node, namespaceSymbol, /*isPrototypeProperty*/ false); 3120 } 3121 3122 function bindSpecialPropertyAssignment(node: BindablePropertyAssignmentExpression) { 3123 // Class declarations in Typescript do not allow property declarations 3124 const parentSymbol = lookupSymbolForPropertyAccess(node.left.expression, container) || lookupSymbolForPropertyAccess(node.left.expression, blockScopeContainer) ; 3125 if (!isInJSFile(node) && !isFunctionSymbol(parentSymbol)) { 3126 return; 3127 } 3128 const rootExpr = getLeftmostAccessExpression(node.left); 3129 if (isIdentifier(rootExpr) && lookupSymbolForName(container, rootExpr.escapedText)?.flags! & SymbolFlags.Alias) { 3130 return; 3131 } 3132 // Fix up parent pointers since we're going to use these nodes before we bind into them 3133 setParent(node.left, node); 3134 setParent(node.right, node); 3135 if (isIdentifier(node.left.expression) && container === file && isExportsOrModuleExportsOrAlias(file, node.left.expression)) { 3136 // This can be an alias for the 'exports' or 'module.exports' names, e.g. 3137 // var util = module.exports; 3138 // util.property = function ... 3139 bindExportsPropertyAssignment(node as BindableStaticPropertyAssignmentExpression); 3140 } 3141 else if (hasDynamicName(node)) { 3142 bindAnonymousDeclaration(node, SymbolFlags.Property | SymbolFlags.Assignment, InternalSymbolName.Computed); 3143 const sym = bindPotentiallyMissingNamespaces(parentSymbol, node.left.expression, isTopLevelNamespaceAssignment(node.left), /*isPrototype*/ false, /*containerIsClass*/ false); 3144 addLateBoundAssignmentDeclarationToSymbol(node, sym); 3145 } 3146 else { 3147 bindStaticPropertyAssignment(cast(node.left, isBindableStaticNameExpression)); 3148 } 3149 } 3150 3151 /** 3152 * For nodes like `x.y = z`, declare a member 'y' on 'x' if x is a function (or IIFE) or class or {}, or not declared. 3153 * Also works for expression statements preceded by JSDoc, like / ** @type number * / x.y; 3154 */ 3155 function bindStaticPropertyAssignment(node: BindableStaticNameExpression) { 3156 Debug.assert(!isIdentifier(node)); 3157 setParent(node.expression, node); 3158 bindPropertyAssignment(node.expression, node, /*isPrototypeProperty*/ false, /*containerIsClass*/ false); 3159 } 3160 3161 function bindPotentiallyMissingNamespaces(namespaceSymbol: Symbol | undefined, entityName: BindableStaticNameExpression, isToplevel: boolean, isPrototypeProperty: boolean, containerIsClass: boolean) { 3162 if (namespaceSymbol?.flags! & SymbolFlags.Alias) { 3163 return namespaceSymbol; 3164 } 3165 if (isToplevel && !isPrototypeProperty) { 3166 // make symbols or add declarations for intermediate containers 3167 const flags = SymbolFlags.Module | SymbolFlags.Assignment; 3168 const excludeFlags = SymbolFlags.ValueModuleExcludes & ~SymbolFlags.Assignment; 3169 namespaceSymbol = forEachIdentifierInEntityName(entityName, namespaceSymbol, (id, symbol, parent) => { 3170 if (symbol) { 3171 addDeclarationToSymbol(symbol, id, flags); 3172 return symbol; 3173 } 3174 else { 3175 const table = parent ? parent.exports! : 3176 file.jsGlobalAugmentations || (file.jsGlobalAugmentations = createSymbolTable()); 3177 return declareSymbol(table, parent, id, flags, excludeFlags); 3178 } 3179 }); 3180 } 3181 if (containerIsClass && namespaceSymbol && namespaceSymbol.valueDeclaration) { 3182 addDeclarationToSymbol(namespaceSymbol, namespaceSymbol.valueDeclaration, SymbolFlags.Class); 3183 } 3184 return namespaceSymbol; 3185 } 3186 3187 function bindPotentiallyNewExpandoMemberToNamespace(declaration: BindableStaticAccessExpression | CallExpression, namespaceSymbol: Symbol | undefined, isPrototypeProperty: boolean) { 3188 if (!namespaceSymbol || !isExpandoSymbol(namespaceSymbol)) { 3189 return; 3190 } 3191 3192 // Set up the members collection if it doesn't exist already 3193 const symbolTable = isPrototypeProperty ? 3194 (namespaceSymbol.members || (namespaceSymbol.members = createSymbolTable())) : 3195 (namespaceSymbol.exports || (namespaceSymbol.exports = createSymbolTable())); 3196 3197 let includes = SymbolFlags.None; 3198 let excludes = SymbolFlags.None; 3199 // Method-like 3200 if (isFunctionLikeDeclaration(getAssignedExpandoInitializer(declaration)!)) { 3201 includes = SymbolFlags.Method; 3202 excludes = SymbolFlags.MethodExcludes; 3203 } 3204 // Maybe accessor-like 3205 else if (isCallExpression(declaration) && isBindableObjectDefinePropertyCall(declaration)) { 3206 if (some(declaration.arguments[2].properties, p => { 3207 const id = getNameOfDeclaration(p); 3208 return !!id && isIdentifier(id) && idText(id) === "set"; 3209 })) { 3210 // We mix in `SymbolFLags.Property` so in the checker `getTypeOfVariableParameterOrProperty` is used for this 3211 // symbol, instead of `getTypeOfAccessor` (which will assert as there is no real accessor declaration) 3212 includes |= SymbolFlags.SetAccessor | SymbolFlags.Property; 3213 excludes |= SymbolFlags.SetAccessorExcludes; 3214 } 3215 if (some(declaration.arguments[2].properties, p => { 3216 const id = getNameOfDeclaration(p); 3217 return !!id && isIdentifier(id) && idText(id) === "get"; 3218 })) { 3219 includes |= SymbolFlags.GetAccessor | SymbolFlags.Property; 3220 excludes |= SymbolFlags.GetAccessorExcludes; 3221 } 3222 } 3223 3224 if (includes === SymbolFlags.None) { 3225 includes = SymbolFlags.Property; 3226 excludes = SymbolFlags.PropertyExcludes; 3227 } 3228 3229 declareSymbol(symbolTable, namespaceSymbol, declaration, includes | SymbolFlags.Assignment, excludes & ~SymbolFlags.Assignment); 3230 } 3231 3232 function isTopLevelNamespaceAssignment(propertyAccess: BindableAccessExpression) { 3233 return isBinaryExpression(propertyAccess.parent) 3234 ? getParentOfBinaryExpression(propertyAccess.parent).parent.kind === SyntaxKind.SourceFile 3235 : propertyAccess.parent.parent.kind === SyntaxKind.SourceFile; 3236 } 3237 3238 function bindPropertyAssignment(name: BindableStaticNameExpression, propertyAccess: BindableStaticAccessExpression, isPrototypeProperty: boolean, containerIsClass: boolean) { 3239 let namespaceSymbol = lookupSymbolForPropertyAccess(name, container) || lookupSymbolForPropertyAccess(name, blockScopeContainer); 3240 const isToplevel = isTopLevelNamespaceAssignment(propertyAccess); 3241 namespaceSymbol = bindPotentiallyMissingNamespaces(namespaceSymbol, propertyAccess.expression, isToplevel, isPrototypeProperty, containerIsClass); 3242 bindPotentiallyNewExpandoMemberToNamespace(propertyAccess, namespaceSymbol, isPrototypeProperty); 3243 } 3244 3245 /** 3246 * Javascript expando values are: 3247 * - Functions 3248 * - classes 3249 * - namespaces 3250 * - variables initialized with function expressions 3251 * - with class expressions 3252 * - with empty object literals 3253 * - with non-empty object literals if assigned to the prototype property 3254 */ 3255 function isExpandoSymbol(symbol: Symbol): boolean { 3256 if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Class | SymbolFlags.NamespaceModule)) { 3257 return true; 3258 } 3259 const node = symbol.valueDeclaration; 3260 if (node && isCallExpression(node)) { 3261 return !!getAssignedExpandoInitializer(node); 3262 } 3263 let init = !node ? undefined : 3264 isVariableDeclaration(node) ? node.initializer : 3265 isBinaryExpression(node) ? node.right : 3266 isPropertyAccessExpression(node) && isBinaryExpression(node.parent) ? node.parent.right : 3267 undefined; 3268 init = init && getRightMostAssignedExpression(init); 3269 if (init) { 3270 const isPrototypeAssignment = isPrototypeAccess(isVariableDeclaration(node!) ? node.name : isBinaryExpression(node!) ? node.left : node!); 3271 return !!getExpandoInitializer(isBinaryExpression(init) && (init.operatorToken.kind === SyntaxKind.BarBarToken || init.operatorToken.kind === SyntaxKind.QuestionQuestionToken) ? init.right : init, isPrototypeAssignment); 3272 } 3273 return false; 3274 } 3275 3276 function getParentOfBinaryExpression(expr: Node) { 3277 while (isBinaryExpression(expr.parent)) { 3278 expr = expr.parent; 3279 } 3280 return expr.parent; 3281 } 3282 3283 function lookupSymbolForPropertyAccess(node: BindableStaticNameExpression, lookupContainer: Node = container): Symbol | undefined { 3284 if (isIdentifier(node)) { 3285 return lookupSymbolForName(lookupContainer, node.escapedText); 3286 } 3287 else { 3288 const symbol = lookupSymbolForPropertyAccess(node.expression); 3289 return symbol && symbol.exports && symbol.exports.get(getElementOrPropertyAccessName(node)); 3290 } 3291 } 3292 3293 function forEachIdentifierInEntityName(e: BindableStaticNameExpression, parent: Symbol | undefined, action: (e: Declaration, symbol: Symbol | undefined, parent: Symbol | undefined) => Symbol | undefined): Symbol | undefined { 3294 if (isExportsOrModuleExportsOrAlias(file, e)) { 3295 return file.symbol; 3296 } 3297 else if (isIdentifier(e)) { 3298 return action(e, lookupSymbolForPropertyAccess(e), parent); 3299 } 3300 else { 3301 const s = forEachIdentifierInEntityName(e.expression, parent, action); 3302 const name = getNameOrArgument(e); 3303 // unreachable 3304 if (isPrivateIdentifier(name)) { 3305 Debug.fail("unexpected PrivateIdentifier"); 3306 } 3307 return action(name, s && s.exports && s.exports.get(getElementOrPropertyAccessName(e)), s); 3308 } 3309 } 3310 3311 function bindCallExpression(node: CallExpression) { 3312 // We're only inspecting call expressions to detect CommonJS modules, so we can skip 3313 // this check if we've already seen the module indicator 3314 if (!file.commonJsModuleIndicator && isRequireCall(node, /*checkArgumentIsStringLiteralLike*/ false)) { 3315 setCommonJsModuleIndicator(node); 3316 } 3317 } 3318 3319 function bindClassLikeDeclaration(node: ClassLikeDeclaration) { 3320 if (node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.StructDeclaration) { 3321 bindBlockScopedDeclaration(node, SymbolFlags.Class, SymbolFlags.ClassExcludes); 3322 } 3323 else { 3324 const bindingName = node.name ? node.name.escapedText : InternalSymbolName.Class; 3325 bindAnonymousDeclaration(node, SymbolFlags.Class, bindingName); 3326 // Add name of class expression into the map for semantic classifier 3327 if (node.name) { 3328 classifiableNames.add(node.name.escapedText); 3329 } 3330 } 3331 3332 const { symbol } = node; 3333 3334 // TypeScript 1.0 spec (April 2014): 8.4 3335 // Every class automatically contains a static property member named 'prototype', the 3336 // type of which is an instantiation of the class type with type Any supplied as a type 3337 // argument for each type parameter. It is an error to explicitly declare a static 3338 // property member with the name 'prototype'. 3339 // 3340 // Note: we check for this here because this class may be merging into a module. The 3341 // module might have an exported variable called 'prototype'. We can't allow that as 3342 // that would clash with the built-in 'prototype' for the class. 3343 const prototypeSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Prototype, "prototype" as __String); 3344 const symbolExport = symbol.exports!.get(prototypeSymbol.escapedName); 3345 if (symbolExport) { 3346 if (node.name) { 3347 setParent(node.name, node); 3348 } 3349 file.bindDiagnostics.push(createDiagnosticForNode(symbolExport.declarations![0], Diagnostics.Duplicate_identifier_0, symbolName(prototypeSymbol))); 3350 } 3351 symbol.exports!.set(prototypeSymbol.escapedName, prototypeSymbol); 3352 prototypeSymbol.parent = symbol; 3353 } 3354 3355 function bindAnnotationDeclaration(node: AnnotationDeclaration): void { 3356 Debug.assert(node.kind === SyntaxKind.AnnotationDeclaration); 3357 3358 bindBlockScopedDeclaration(node, SymbolFlags.Class | SymbolFlags.Annotation, SymbolFlags.ClassExcludes); 3359 const { symbol } = node; 3360 const prototypeSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Prototype, "prototype" as __String); 3361 const symbolExport = symbol.exports!.get(prototypeSymbol.escapedName); 3362 if (symbolExport) { 3363 if (node.name) { 3364 setParent(node.name, node); 3365 } 3366 file.bindDiagnostics.push(createDiagnosticForNode(symbolExport.declarations![0], Diagnostics.Duplicate_identifier_0, symbolName(prototypeSymbol))); 3367 } 3368 symbol.exports!.set(prototypeSymbol.escapedName, prototypeSymbol); 3369 prototypeSymbol.parent = symbol; 3370 } 3371 3372 function bindEnumDeclaration(node: EnumDeclaration) { 3373 return isEnumConst(node) 3374 ? bindBlockScopedDeclaration(node, SymbolFlags.ConstEnum, SymbolFlags.ConstEnumExcludes) 3375 : bindBlockScopedDeclaration(node, SymbolFlags.RegularEnum, SymbolFlags.RegularEnumExcludes); 3376 } 3377 3378 function bindVariableDeclarationOrBindingElement(node: VariableDeclaration | BindingElement) { 3379 if (inStrictMode) { 3380 checkStrictModeEvalOrArguments(node, node.name); 3381 } 3382 3383 if (!isBindingPattern(node.name)) { 3384 const possibleVariableDecl = node.kind === SyntaxKind.VariableDeclaration ? node : node.parent.parent; 3385 if (isInJSFile(node) && 3386 isVariableDeclarationInitializedToBareOrAccessedRequire(possibleVariableDecl) && 3387 !getJSDocTypeTag(node) && 3388 !(getCombinedModifierFlags(node) & ModifierFlags.Export) 3389 ) { 3390 declareSymbolAndAddToSymbolTable(node as Declaration, SymbolFlags.Alias, SymbolFlags.AliasExcludes); 3391 } 3392 else if (isBlockOrCatchScoped(node)) { 3393 bindBlockScopedDeclaration(node, SymbolFlags.BlockScopedVariable, SymbolFlags.BlockScopedVariableExcludes); 3394 } 3395 else if (isParameterDeclaration(node)) { 3396 // It is safe to walk up parent chain to find whether the node is a destructuring parameter declaration 3397 // because its parent chain has already been set up, since parents are set before descending into children. 3398 // 3399 // If node is a binding element in parameter declaration, we need to use ParameterExcludes. 3400 // Using ParameterExcludes flag allows the compiler to report an error on duplicate identifiers in Parameter Declaration 3401 // For example: 3402 // function foo([a,a]) {} // Duplicate Identifier error 3403 // function bar(a,a) {} // Duplicate Identifier error, parameter declaration in this case is handled in bindParameter 3404 // // which correctly set excluded symbols 3405 declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.ParameterExcludes); 3406 } 3407 else { 3408 declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes); 3409 } 3410 } 3411 } 3412 3413 function bindParameter(node: ParameterDeclaration | JSDocParameterTag) { 3414 if (node.kind === SyntaxKind.JSDocParameterTag && container.kind !== SyntaxKind.JSDocSignature) { 3415 return; 3416 } 3417 if (inStrictMode && !(node.flags & NodeFlags.Ambient)) { 3418 // It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a 3419 // strict mode FunctionLikeDeclaration or FunctionExpression(13.1) 3420 checkStrictModeEvalOrArguments(node, node.name); 3421 } 3422 3423 if (isBindingPattern(node.name)) { 3424 bindAnonymousDeclaration(node, SymbolFlags.FunctionScopedVariable, "__" + (node as ParameterDeclaration).parent.parameters.indexOf(node as ParameterDeclaration) as __String); 3425 } 3426 else { 3427 declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.ParameterExcludes); 3428 } 3429 3430 // If this is a property-parameter, then also declare the property symbol into the 3431 // containing class. 3432 if (isParameterPropertyDeclaration(node, node.parent)) { 3433 const classDeclaration = node.parent.parent; 3434 declareSymbol(classDeclaration.symbol.members!, classDeclaration.symbol, node, SymbolFlags.Property | (node.questionToken ? SymbolFlags.Optional : SymbolFlags.None), SymbolFlags.PropertyExcludes); 3435 } 3436 } 3437 3438 function bindFunctionDeclaration(node: FunctionDeclaration) { 3439 if (!file.isDeclarationFile && !(node.flags & NodeFlags.Ambient)) { 3440 if (isAsyncFunction(node)) { 3441 emitFlags |= NodeFlags.HasAsyncFunctions; 3442 } 3443 } 3444 3445 checkStrictModeFunctionName(node); 3446 if (inStrictMode) { 3447 checkStrictModeFunctionDeclaration(node); 3448 bindBlockScopedDeclaration(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes); 3449 } 3450 else { 3451 declareSymbolAndAddToSymbolTable(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes); 3452 } 3453 } 3454 3455 function bindFunctionExpression(node: FunctionExpression) { 3456 if (!file.isDeclarationFile && !(node.flags & NodeFlags.Ambient)) { 3457 if (isAsyncFunction(node)) { 3458 emitFlags |= NodeFlags.HasAsyncFunctions; 3459 } 3460 } 3461 if (currentFlow) { 3462 node.flowNode = currentFlow; 3463 } 3464 checkStrictModeFunctionName(node); 3465 const bindingName = node.name ? node.name.escapedText : InternalSymbolName.Function; 3466 return bindAnonymousDeclaration(node, SymbolFlags.Function, bindingName); 3467 } 3468 3469 function bindPropertyOrMethodOrAccessor(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { 3470 if (!file.isDeclarationFile && !(node.flags & NodeFlags.Ambient) && isAsyncFunction(node)) { 3471 emitFlags |= NodeFlags.HasAsyncFunctions; 3472 } 3473 3474 if (currentFlow && isObjectLiteralOrClassExpressionMethodOrAccessor(node)) { 3475 node.flowNode = currentFlow; 3476 } 3477 3478 return hasDynamicName(node) 3479 ? bindAnonymousDeclaration(node, symbolFlags, InternalSymbolName.Computed) 3480 : declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes); 3481 } 3482 3483 function getInferTypeContainer(node: Node): ConditionalTypeNode | undefined { 3484 const extendsType = findAncestor(node, n => n.parent && isConditionalTypeNode(n.parent) && n.parent.extendsType === n); 3485 return extendsType && extendsType.parent as ConditionalTypeNode; 3486 } 3487 3488 function bindTypeParameter(node: TypeParameterDeclaration) { 3489 if (isJSDocTemplateTag(node.parent)) { 3490 const container = getEffectiveContainerForJSDocTemplateTag(node.parent); 3491 if (container) { 3492 if (!container.locals) { 3493 container.locals = createSymbolTable(); 3494 } 3495 declareSymbol(container.locals, /*parent*/ undefined, node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes); 3496 } 3497 else { 3498 declareSymbolAndAddToSymbolTable(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes); 3499 } 3500 } 3501 else if (node.parent.kind === SyntaxKind.InferType) { 3502 const container = getInferTypeContainer(node.parent); 3503 if (container) { 3504 if (!container.locals) { 3505 container.locals = createSymbolTable(); 3506 } 3507 declareSymbol(container.locals, /*parent*/ undefined, node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes); 3508 } 3509 else { 3510 bindAnonymousDeclaration(node, SymbolFlags.TypeParameter, getDeclarationName(node)!); // TODO: GH#18217 3511 } 3512 } 3513 else { 3514 declareSymbolAndAddToSymbolTable(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes); 3515 } 3516 } 3517 3518 // reachability checks 3519 3520 function shouldReportErrorOnModuleDeclaration(node: ModuleDeclaration): boolean { 3521 const instanceState = getModuleInstanceState(node); 3522 return instanceState === ModuleInstanceState.Instantiated || (instanceState === ModuleInstanceState.ConstEnumOnly && shouldPreserveConstEnums(options)); 3523 } 3524 3525 function checkUnreachable(node: Node): boolean { 3526 if (!(currentFlow.flags & FlowFlags.Unreachable)) { 3527 return false; 3528 } 3529 if (currentFlow === unreachableFlow) { 3530 const reportError = 3531 // report error on all statements except empty ones 3532 (isStatementButNotDeclaration(node) && node.kind !== SyntaxKind.EmptyStatement) || 3533 // report error on class declarations 3534 node.kind === SyntaxKind.ClassDeclaration || 3535 // report error on instantiated modules or const-enums only modules if preserveConstEnums is set 3536 (node.kind === SyntaxKind.ModuleDeclaration && shouldReportErrorOnModuleDeclaration(node as ModuleDeclaration)); 3537 3538 if (reportError) { 3539 currentFlow = reportedUnreachableFlow; 3540 3541 if (!options.allowUnreachableCode) { 3542 // unreachable code is reported if 3543 // - user has explicitly asked about it AND 3544 // - statement is in not ambient context (statements in ambient context is already an error 3545 // so we should not report extras) AND 3546 // - node is not variable statement OR 3547 // - node is block scoped variable statement OR 3548 // - node is not block scoped variable statement and at least one variable declaration has initializer 3549 // Rationale: we don't want to report errors on non-initialized var's since they are hoisted 3550 // On the other side we do want to report errors on non-initialized 'lets' because of TDZ 3551 const isError = 3552 unreachableCodeIsError(options) && 3553 !(node.flags & NodeFlags.Ambient) && 3554 ( 3555 !isVariableStatement(node) || 3556 !!(getCombinedNodeFlags(node.declarationList) & NodeFlags.BlockScoped) || 3557 node.declarationList.declarations.some(d => !!d.initializer) 3558 ); 3559 3560 eachUnreachableRange(node, (start, end) => errorOrSuggestionOnRange(isError, start, end, Diagnostics.Unreachable_code_detected)); 3561 } 3562 } 3563 } 3564 return true; 3565 } 3566} 3567 3568function eachUnreachableRange(node: Node, cb: (start: Node, last: Node) => void): void { 3569 if (isStatement(node) && isExecutableStatement(node) && isBlock(node.parent)) { 3570 const { statements } = node.parent; 3571 const slice = sliceAfter(statements, node); 3572 getRangesWhere(slice, isExecutableStatement, (start, afterEnd) => cb(slice[start], slice[afterEnd - 1])); 3573 } 3574 else { 3575 cb(node, node); 3576 } 3577} 3578// As opposed to a pure declaration like an `interface` 3579function isExecutableStatement(s: Statement): boolean { 3580 // Don't remove statements that can validly be used before they appear. 3581 return !isFunctionDeclaration(s) && !isPurelyTypeDeclaration(s) && !isEnumDeclaration(s) && 3582 // `var x;` may declare a variable used above 3583 !(isVariableStatement(s) && !(getCombinedNodeFlags(s) & (NodeFlags.Let | NodeFlags.Const)) && s.declarationList.declarations.some(d => !d.initializer)); 3584} 3585 3586function isPurelyTypeDeclaration(s: Statement): boolean { 3587 switch (s.kind) { 3588 case SyntaxKind.InterfaceDeclaration: 3589 case SyntaxKind.TypeAliasDeclaration: 3590 return true; 3591 case SyntaxKind.ModuleDeclaration: 3592 return getModuleInstanceState(s as ModuleDeclaration) !== ModuleInstanceState.Instantiated; 3593 case SyntaxKind.EnumDeclaration: 3594 return hasSyntacticModifier(s, ModifierFlags.Const); 3595 default: 3596 return false; 3597 } 3598} 3599 3600/** @internal */ 3601export function isExportsOrModuleExportsOrAlias(sourceFile: SourceFile, node: Expression): boolean { 3602 let i = 0; 3603 const q = createQueue<Expression>(); 3604 q.enqueue(node); 3605 while (!q.isEmpty() && i < 100) { 3606 i++; 3607 node = q.dequeue(); 3608 if (isExportsIdentifier(node) || isModuleExportsAccessExpression(node)) { 3609 return true; 3610 } 3611 else if (isIdentifier(node)) { 3612 const symbol = lookupSymbolForName(sourceFile, node.escapedText); 3613 if (!!symbol && !!symbol.valueDeclaration && isVariableDeclaration(symbol.valueDeclaration) && !!symbol.valueDeclaration.initializer) { 3614 const init = symbol.valueDeclaration.initializer; 3615 q.enqueue(init); 3616 if (isAssignmentExpression(init, /*excludeCompoundAssignment*/ true)) { 3617 q.enqueue(init.left); 3618 q.enqueue(init.right); 3619 } 3620 } 3621 } 3622 } 3623 return false; 3624} 3625 3626function lookupSymbolForName(container: Node, name: __String): Symbol | undefined { 3627 const local = container.locals && container.locals.get(name); 3628 if (local) { 3629 return local.exportSymbol || local; 3630 } 3631 if (isSourceFile(container) && container.jsGlobalAugmentations && container.jsGlobalAugmentations.has(name)) { 3632 return container.jsGlobalAugmentations.get(name); 3633 } 3634 return container.symbol && container.symbol.exports && container.symbol.exports.get(name); 3635} 3636