From dc4c800ad4e668d8f808d1d42d0f4dd8497e755d Mon Sep 17 00:00:00 2001 From: Egor Aristov Date: Wed, 15 Oct 2025 19:22:57 +0300 Subject: [PATCH] remove unused packages --- kitcom/internal/tsgo/binder/binder.go | 2820 ----------------- kitcom/internal/tsgo/binder/binder_test.go | 51 - kitcom/internal/tsgo/binder/nameresolver.go | 503 --- .../internal/tsgo/binder/referenceresolver.go | 251 -- 4 files changed, 3625 deletions(-) delete mode 100644 kitcom/internal/tsgo/binder/binder.go delete mode 100644 kitcom/internal/tsgo/binder/binder_test.go delete mode 100644 kitcom/internal/tsgo/binder/nameresolver.go delete mode 100644 kitcom/internal/tsgo/binder/referenceresolver.go diff --git a/kitcom/internal/tsgo/binder/binder.go b/kitcom/internal/tsgo/binder/binder.go deleted file mode 100644 index f251cb9..0000000 --- a/kitcom/internal/tsgo/binder/binder.go +++ /dev/null @@ -1,2820 +0,0 @@ -package binder - -import ( - "slices" - "strconv" - "sync" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/debug" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/scanner" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" -) - -type ContainerFlags int32 - -const ( - // The current node is not a container, and no container manipulation should happen before - // recursing into it. - ContainerFlagsNone ContainerFlags = 0 - // The current node is a container. It should be set as the current container (and block- - // container) before recursing into it. The current node does not have locals. Examples: - // - // Classes, ObjectLiterals, TypeLiterals, Interfaces... - ContainerFlagsIsContainer ContainerFlags = 1 << 0 - // The current node is a block-scoped-container. It should be set as the current block- - // container before recursing into it. Examples: - // - // Blocks (when not parented by functions), Catch clauses, For/For-in/For-of statements... - ContainerFlagsIsBlockScopedContainer ContainerFlags = 1 << 1 - // The current node is the container of a control flow path. The current control flow should - // be saved and restored, and a new control flow initialized within the container. - ContainerFlagsIsControlFlowContainer ContainerFlags = 1 << 2 - ContainerFlagsIsFunctionLike ContainerFlags = 1 << 3 - ContainerFlagsIsFunctionExpression ContainerFlags = 1 << 4 - ContainerFlagsHasLocals ContainerFlags = 1 << 5 - ContainerFlagsIsInterface ContainerFlags = 1 << 6 - ContainerFlagsIsObjectLiteralOrClassExpressionMethodOrAccessor ContainerFlags = 1 << 7 - ContainerFlagsIsThisContainer ContainerFlags = 1 << 8 -) - -type Binder struct { - file *ast.SourceFile - bindFunc func(*ast.Node) bool - unreachableFlow *ast.FlowNode - reportedUnreachableFlow *ast.FlowNode - - container *ast.Node - thisContainer *ast.Node - blockScopeContainer *ast.Node - lastContainer *ast.Node - currentFlow *ast.FlowNode - currentBreakTarget *ast.FlowLabel - currentContinueTarget *ast.FlowLabel - currentReturnTarget *ast.FlowLabel - currentTrueTarget *ast.FlowLabel - currentFalseTarget *ast.FlowLabel - currentExceptionTarget *ast.FlowLabel - preSwitchCaseFlow *ast.FlowNode - activeLabelList *ActiveLabel - emitFlags ast.NodeFlags - seenThisKeyword bool - hasExplicitReturn bool - hasFlowEffects bool - inStrictMode bool - inAssignmentPattern bool - seenParseError bool - symbolCount int - classifiableNames collections.Set[string] - symbolPool core.Pool[ast.Symbol] - flowNodePool core.Pool[ast.FlowNode] - flowListPool core.Pool[ast.FlowList] - singleDeclarationsPool core.Pool[*ast.Node] -} - -func (b *Binder) options() core.SourceFileAffectingCompilerOptions { - return b.file.ParseOptions().CompilerOptions -} - -type ActiveLabel struct { - next *ActiveLabel - breakTarget *ast.FlowLabel - continueTarget *ast.FlowLabel - name string - referenced bool -} - -func (label *ActiveLabel) BreakTarget() *ast.FlowNode { return label.breakTarget } -func (label *ActiveLabel) ContinueTarget() *ast.FlowNode { return label.continueTarget } - -func BindSourceFile(file *ast.SourceFile) { - // This is constructed this way to make the compiler "out-line" the function, - // avoiding most work in the common case where the file has already been bound. - if !file.IsBound() { - bindSourceFile(file) - } -} - -var binderPool = sync.Pool{ - New: func() any { - b := &Binder{} - b.bindFunc = b.bind // Allocate closure once - return b - }, -} - -func getBinder() *Binder { - return binderPool.Get().(*Binder) -} - -func putBinder(b *Binder) { - *b = Binder{bindFunc: b.bindFunc} - binderPool.Put(b) -} - -func bindSourceFile(file *ast.SourceFile) { - file.BindOnce(func() { - b := getBinder() - defer putBinder(b) - b.file = file - b.inStrictMode = b.options().BindInStrictMode && !file.IsDeclarationFile || ast.IsExternalModule(file) - b.unreachableFlow = b.newFlowNode(ast.FlowFlagsUnreachable) - b.reportedUnreachableFlow = b.newFlowNode(ast.FlowFlagsUnreachable) - b.bind(file.AsNode()) - file.SymbolCount = b.symbolCount - file.ClassifiableNames = b.classifiableNames - }) -} - -func (b *Binder) newSymbol(flags ast.SymbolFlags, name string) *ast.Symbol { - b.symbolCount++ - result := b.symbolPool.New() - result.Flags = flags - result.Name = name - return result -} - -/** - * Declares a Symbol for the node and adds it to symbols. Reports errors for conflicting identifier names. - * @param symbolTable - The symbol table which node will be added to. - * @param parent - node's parent declaration. - * @param node - The declaration to be added to the symbol table - * @param includes - The SymbolFlags that node has in addition to its declaration type (eg: export, ambient, etc.) - * @param excludes - The flags which node cannot be declared alongside in a symbol table. Used to report forbidden declarations. - */ -func (b *Binder) declareSymbol(symbolTable ast.SymbolTable, parent *ast.Symbol, node *ast.Node, includes ast.SymbolFlags, excludes ast.SymbolFlags) *ast.Symbol { - return b.declareSymbolEx(symbolTable, parent, node, includes, excludes, false /*isReplaceableByMethod*/, false /*isComputedName*/) -} - -func (b *Binder) declareSymbolEx(symbolTable ast.SymbolTable, parent *ast.Symbol, node *ast.Node, includes ast.SymbolFlags, excludes ast.SymbolFlags, isReplaceableByMethod bool, isComputedName bool) *ast.Symbol { - debug.Assert(isComputedName || !ast.HasDynamicName(node)) - isDefaultExport := ast.HasSyntacticModifier(node, ast.ModifierFlagsDefault) || ast.IsExportSpecifier(node) && ast.ModuleExportNameIsDefault(node.AsExportSpecifier().Name()) - // The exported symbol for an export default function/class node is always named "default" - var name string - switch { - case isComputedName: - name = ast.InternalSymbolNameComputed - case isDefaultExport && parent != nil: - name = ast.InternalSymbolNameDefault - default: - name = b.getDeclarationName(node) - } - var symbol *ast.Symbol - if name == ast.InternalSymbolNameMissing { - symbol = b.newSymbol(ast.SymbolFlagsNone, ast.InternalSymbolNameMissing) - } else { - // Check and see if the symbol table already has a symbol with this name. If not, - // create a new symbol with this name and add it to the table. Note that we don't - // give the new symbol any flags *yet*. This ensures that it will not conflict - // with the 'excludes' flags we pass in. - // - // If we do get an existing symbol, see if it conflicts with the new symbol we're - // creating. For example, a 'var' symbol and a 'class' symbol will conflict within - // the same symbol table. If we have a conflict, report the issue on each - // declaration we have for this symbol, and then create a new symbol for this - // declaration. - // - // Note that when properties declared in Javascript constructors - // (marked by isReplaceableByMethod) conflict with another symbol, the property loses. - // Always. This allows the common Javascript pattern of overwriting a prototype method - // with an bound instance method of the same type: `this.method = this.method.bind(this)` - // - // If we created a new symbol, either because we didn't have a symbol with this name - // in the symbol table, or we conflicted with an existing symbol, then just add this - // node as the sole declaration of the new symbol. - // - // Otherwise, we'll be merging into a compatible existing symbol (for example when - // you have multiple 'vars' with the same name in the same container). In this case - // just add this node into the declarations list of the symbol. - symbol = symbolTable[name] - if includes&ast.SymbolFlagsClassifiable != 0 { - b.classifiableNames.Add(name) - } - if symbol == nil { - symbol = b.newSymbol(ast.SymbolFlagsNone, name) - symbolTable[name] = symbol - if isReplaceableByMethod { - symbol.Flags |= ast.SymbolFlagsReplaceableByMethod - } - } else if isReplaceableByMethod && symbol.Flags&ast.SymbolFlagsReplaceableByMethod == 0 { - // A symbol already exists, so don't add this as a declaration. - return symbol - } else if symbol.Flags&excludes != 0 { - if symbol.Flags&ast.SymbolFlagsReplaceableByMethod != 0 { - // Javascript constructor-declared symbols can be discarded in favor of - // prototype symbols like methods. - symbol = b.newSymbol(ast.SymbolFlagsNone, name) - symbolTable[name] = symbol - } else if !(includes&ast.SymbolFlagsVariable != 0 && symbol.Flags&ast.SymbolFlagsAssignment != 0 || - includes&ast.SymbolFlagsAssignment != 0 && symbol.Flags&ast.SymbolFlagsVariable != 0) { - // Assignment declarations are allowed to merge with variables, no matter what other flags they have. - // Report errors every position with duplicate declaration - // Report errors on previous encountered declarations - var message *diagnostics.Message - if symbol.Flags&ast.SymbolFlagsBlockScopedVariable != 0 { - message = diagnostics.Cannot_redeclare_block_scoped_variable_0 - } else { - message = diagnostics.Duplicate_identifier_0 - } - messageNeedsName := true - if symbol.Flags&ast.SymbolFlagsEnum != 0 || includes&ast.SymbolFlagsEnum != 0 { - message = diagnostics.Enum_declarations_can_only_merge_with_namespace_or_other_enum_declarations - messageNeedsName = false - } - multipleDefaultExports := false - if len(symbol.Declarations) != 0 { - // If the current node is a default export of some sort, then check if - // there are any other default exports that we need to error on. - // We'll know whether we have other default exports depending on if `symbol` already has a declaration list set. - if isDefaultExport { - message = diagnostics.A_module_cannot_have_multiple_default_exports - messageNeedsName = false - multipleDefaultExports = true - } else { - // This is to properly report an error in the case "export default { }" is after export default of class declaration or function declaration. - // Error on multiple export default in the following case: - // 1. multiple export default of class declaration or function declaration by checking NodeFlags.Default - // 2. multiple export default of export assignment. This one doesn't have NodeFlags.Default on (as export default doesn't considered as modifiers) - if len(symbol.Declarations) != 0 && ast.IsExportAssignment(node) && !node.AsExportAssignment().IsExportEquals { - message = diagnostics.A_module_cannot_have_multiple_default_exports - messageNeedsName = false - multipleDefaultExports = true - } - } - } - var declarationName *ast.Node = ast.GetNameOfDeclaration(node) - if declarationName == nil { - declarationName = node - } - var diag *ast.Diagnostic - if messageNeedsName { - diag = b.createDiagnosticForNode(declarationName, message, b.getDisplayName(node)) - } else { - diag = b.createDiagnosticForNode(declarationName, message) - } - if ast.IsTypeAliasDeclaration(node) && ast.NodeIsMissing(node.AsTypeAliasDeclaration().Type) && ast.HasSyntacticModifier(node, ast.ModifierFlagsExport) && symbol.Flags&(ast.SymbolFlagsAlias|ast.SymbolFlagsType|ast.SymbolFlagsNamespace) != 0 { - // export type T; - may have meant export type { T }? - diag.AddRelatedInfo(b.createDiagnosticForNode(node, diagnostics.Did_you_mean_0, "export type { "+node.AsTypeAliasDeclaration().Name().AsIdentifier().Text+" }")) - } - for index, declaration := range symbol.Declarations { - var decl *ast.Node = ast.GetNameOfDeclaration(declaration) - if decl == nil { - decl = declaration - } - var d *ast.Diagnostic - if messageNeedsName { - d = b.createDiagnosticForNode(decl, message, b.getDisplayName(declaration)) - } else { - d = b.createDiagnosticForNode(decl, message) - } - if multipleDefaultExports { - d.AddRelatedInfo(b.createDiagnosticForNode(declarationName, core.IfElse(index == 0, diagnostics.Another_export_default_is_here, diagnostics.X_and_here))) - } - b.addDiagnostic(d) - if multipleDefaultExports { - diag.AddRelatedInfo(b.createDiagnosticForNode(decl, diagnostics.The_first_export_default_is_here)) - } - } - b.addDiagnostic(diag) - // When get or set accessor conflicts with a non-accessor or an accessor of a different kind, we mark - // the symbol as a full accessor such that all subsequent declarations are considered conflicting. This - // for example ensures that a get accessor followed by a non-accessor followed by a set accessor with the - // same name are all marked as duplicates. - if symbol.Flags&ast.SymbolFlagsAccessor != 0 && symbol.Flags&ast.SymbolFlagsAccessor != includes&ast.SymbolFlagsAccessor { - symbol.Flags |= ast.SymbolFlagsAccessor - } - symbol = b.newSymbol(ast.SymbolFlagsNone, name) - } - } - } - b.addDeclarationToSymbol(symbol, node, includes) - if symbol.Parent == nil { - symbol.Parent = parent - } else if symbol.Parent != parent { - panic("Existing symbol parent should match new one") - } - return symbol -} - -// Should not be called on a declaration with a computed property name, -// unless it is a well known Symbol. -func (b *Binder) getDeclarationName(node *ast.Node) string { - if ast.IsExportAssignment(node) { - return core.IfElse(node.AsExportAssignment().IsExportEquals, ast.InternalSymbolNameExportEquals, ast.InternalSymbolNameDefault) - } else if ast.IsJSExportAssignment(node) { - return ast.InternalSymbolNameExportEquals - } - name := ast.GetNameOfDeclaration(node) - if name != nil { - if ast.IsAmbientModule(node) { - moduleName := name.Text() - if ast.IsGlobalScopeAugmentation(node) { - return ast.InternalSymbolNameGlobal - } - return "\"" + moduleName + "\"" - } - if ast.IsPrivateIdentifier(name) { - // containingClass exists because private names only allowed inside classes - containingClass := ast.GetContainingClass(node) - if containingClass == nil { - // we can get here in cases where there is already a parse error. - return ast.InternalSymbolNameMissing - } - return GetSymbolNameForPrivateIdentifier(containingClass.Symbol(), name.Text()) - } - if ast.IsPropertyNameLiteral(name) || ast.IsJsxNamespacedName(name) { - return name.Text() - } - if ast.IsComputedPropertyName(name) { - nameExpression := name.AsComputedPropertyName().Expression - // treat computed property names where expression is string/numeric literal as just string/numeric literal - if ast.IsStringOrNumericLiteralLike(nameExpression) { - return nameExpression.Text() - } - if ast.IsSignedNumericLiteral(nameExpression) { - unaryExpression := nameExpression.AsPrefixUnaryExpression() - return scanner.TokenToString(unaryExpression.Operator) + unaryExpression.Operand.Text() - } - panic("Only computed properties with literal names have declaration names") - } - return ast.InternalSymbolNameMissing - } - switch node.Kind { - case ast.KindConstructor: - return ast.InternalSymbolNameConstructor - case ast.KindFunctionType, ast.KindCallSignature: - return ast.InternalSymbolNameCall - case ast.KindConstructorType, ast.KindConstructSignature: - return ast.InternalSymbolNameNew - case ast.KindIndexSignature: - return ast.InternalSymbolNameIndex - case ast.KindExportDeclaration: - return ast.InternalSymbolNameExportStar - case ast.KindSourceFile: - return ast.InternalSymbolNameExportEquals - } - return ast.InternalSymbolNameMissing -} - -func (b *Binder) getDisplayName(node *ast.Node) string { - nameNode := node.Name() - if nameNode != nil { - return scanner.DeclarationNameToString(nameNode) - } - name := b.getDeclarationName(node) - if name != ast.InternalSymbolNameMissing { - return name - } - return "(Missing)" -} - -func GetSymbolNameForPrivateIdentifier(containingClassSymbol *ast.Symbol, description string) string { - return ast.InternalSymbolNamePrefix + "#" + strconv.Itoa(int(ast.GetSymbolId(containingClassSymbol))) + "@" + description -} - -func (b *Binder) declareModuleMember(node *ast.Node, symbolFlags ast.SymbolFlags, symbolExcludes ast.SymbolFlags) *ast.Symbol { - container := b.container - if node.Kind == ast.KindCommonJSExport { - container = b.file.AsNode() - } - hasExportModifier := ast.GetCombinedModifierFlags(node)&ast.ModifierFlagsExport != 0 - if symbolFlags&ast.SymbolFlagsAlias != 0 { - if node.Kind == ast.KindExportSpecifier || (node.Kind == ast.KindImportEqualsDeclaration && hasExportModifier) { - return b.declareSymbol(ast.GetExports(container.Symbol()), container.Symbol(), node, symbolFlags, symbolExcludes) - } - return b.declareSymbol(ast.GetLocals(container), nil /*parent*/, node, symbolFlags, symbolExcludes) - } - // Exported module members are given 2 symbols: A local symbol that is classified with an ExportValue flag, - // and an associated export symbol with all the correct flags set on it. There are 2 main reasons: - // - // 1. We treat locals and exports of the same name as mutually exclusive within a container. - // That means the binder will issue a Duplicate Identifier error if you mix locals and exports - // with the same name in the same container. - // TODO: Make this a more specific error and decouple it from the exclusion logic. - // 2. When we checkIdentifier in the checker, we set its resolved symbol to the local symbol, - // but return the export symbol (by calling getExportSymbolOfValueSymbolIfExported). That way - // when the emitter comes back to it, it knows not to qualify the name if it was found in a containing scope. - // - // NOTE: Nested ambient modules always should go to to 'locals' table to prevent their automatic merge - // during global merging in the checker. Why? The only case when ambient module is permitted inside another module is module augmentation - // and this case is specially handled. Module augmentations should only be merged with original module definition - // and should never be merged directly with other augmentation, and the latter case would be possible if automatic merge is allowed. - if !ast.IsAmbientModule(node) && (hasExportModifier || container.Flags&ast.NodeFlagsExportContext != 0) { - if !ast.IsLocalsContainer(container) || (ast.HasSyntacticModifier(node, ast.ModifierFlagsDefault) && b.getDeclarationName(node) == ast.InternalSymbolNameMissing) || ast.IsCommonJSExport(node) { - return b.declareSymbol(ast.GetExports(container.Symbol()), container.Symbol(), node, symbolFlags, symbolExcludes) - // No local symbol for an unnamed default! - } - exportKind := ast.SymbolFlagsNone - if symbolFlags&ast.SymbolFlagsValue != 0 { - exportKind = ast.SymbolFlagsExportValue - } - local := b.declareSymbol(ast.GetLocals(container), nil /*parent*/, node, exportKind, symbolExcludes) - local.ExportSymbol = b.declareSymbol(ast.GetExports(container.Symbol()), container.Symbol(), node, symbolFlags, symbolExcludes) - node.ExportableData().LocalSymbol = local - return local - } - return b.declareSymbol(ast.GetLocals(container), nil /*parent*/, node, symbolFlags, symbolExcludes) -} - -func (b *Binder) declareClassMember(node *ast.Node, symbolFlags ast.SymbolFlags, symbolExcludes ast.SymbolFlags) *ast.Symbol { - if ast.IsStatic(node) { - return b.declareSymbol(ast.GetExports(b.container.Symbol()), b.container.Symbol(), node, symbolFlags, symbolExcludes) - } - return b.declareSymbol(ast.GetMembers(b.container.Symbol()), b.container.Symbol(), node, symbolFlags, symbolExcludes) -} - -func (b *Binder) declareSourceFileMember(node *ast.Node, symbolFlags ast.SymbolFlags, symbolExcludes ast.SymbolFlags) *ast.Symbol { - if ast.IsExternalOrCommonJSModule(b.file) { - return b.declareModuleMember(node, symbolFlags, symbolExcludes) - } - return b.declareSymbol(ast.GetLocals(b.file.AsNode()), nil /*parent*/, node, symbolFlags, symbolExcludes) -} - -func (b *Binder) declareSymbolAndAddToSymbolTable(node *ast.Node, symbolFlags ast.SymbolFlags, symbolExcludes ast.SymbolFlags) *ast.Symbol { - switch b.container.Kind { - case ast.KindModuleDeclaration: - return b.declareModuleMember(node, symbolFlags, symbolExcludes) - case ast.KindSourceFile: - return b.declareSourceFileMember(node, symbolFlags, symbolExcludes) - case ast.KindClassExpression, ast.KindClassDeclaration: - return b.declareClassMember(node, symbolFlags, symbolExcludes) - case ast.KindEnumDeclaration: - return b.declareSymbol(ast.GetExports(b.container.Symbol()), b.container.Symbol(), node, symbolFlags, symbolExcludes) - case ast.KindTypeLiteral, ast.KindObjectLiteralExpression, ast.KindInterfaceDeclaration, ast.KindJsxAttributes: - return b.declareSymbol(ast.GetMembers(b.container.Symbol()), b.container.Symbol(), node, symbolFlags, symbolExcludes) - case ast.KindFunctionType, ast.KindConstructorType, ast.KindCallSignature, ast.KindConstructSignature, - ast.KindIndexSignature, ast.KindMethodDeclaration, ast.KindMethodSignature, ast.KindConstructor, ast.KindGetAccessor, - ast.KindSetAccessor, ast.KindFunctionDeclaration, ast.KindFunctionExpression, ast.KindArrowFunction, - ast.KindClassStaticBlockDeclaration, ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration, ast.KindMappedType: - return b.declareSymbol(ast.GetLocals(b.container), nil /*parent*/, node, symbolFlags, symbolExcludes) - } - panic("Unhandled case in declareSymbolAndAddToSymbolTable") -} - -func (b *Binder) newFlowNode(flags ast.FlowFlags) *ast.FlowNode { - result := b.flowNodePool.New() - result.Flags = flags - return result -} - -func (b *Binder) newFlowNodeEx(flags ast.FlowFlags, node *ast.Node, antecedent *ast.FlowNode) *ast.FlowNode { - result := b.newFlowNode(flags) - result.Node = node - result.Antecedent = antecedent - return result -} - -func (b *Binder) createLoopLabel() *ast.FlowLabel { - return b.newFlowNode(ast.FlowFlagsLoopLabel) -} - -func (b *Binder) createBranchLabel() *ast.FlowLabel { - return b.newFlowNode(ast.FlowFlagsBranchLabel) -} - -func (b *Binder) createReduceLabel(target *ast.FlowLabel, antecedents *ast.FlowList, antecedent *ast.FlowNode) *ast.FlowNode { - return b.newFlowNodeEx(ast.FlowFlagsReduceLabel, ast.NewFlowReduceLabelData(target, antecedents), antecedent) -} - -func (b *Binder) createFlowCondition(flags ast.FlowFlags, antecedent *ast.FlowNode, expression *ast.Node) *ast.FlowNode { - if antecedent.Flags&ast.FlowFlagsUnreachable != 0 { - return antecedent - } - if expression == nil { - if flags&ast.FlowFlagsTrueCondition != 0 { - return antecedent - } - return b.unreachableFlow - } - if (expression.Kind == ast.KindTrueKeyword && flags&ast.FlowFlagsFalseCondition != 0 || expression.Kind == ast.KindFalseKeyword && flags&ast.FlowFlagsTrueCondition != 0) && !ast.IsExpressionOfOptionalChainRoot(expression) && !ast.IsNullishCoalesce(expression.Parent) { - return b.unreachableFlow - } - if !isNarrowingExpression(expression) { - return antecedent - } - setFlowNodeReferenced(antecedent) - return b.newFlowNodeEx(flags, expression, antecedent) -} - -func (b *Binder) createFlowMutation(flags ast.FlowFlags, antecedent *ast.FlowNode, node *ast.Node) *ast.FlowNode { - setFlowNodeReferenced(antecedent) - b.hasFlowEffects = true - result := b.newFlowNodeEx(flags, node, antecedent) - if b.currentExceptionTarget != nil { - b.addAntecedent(b.currentExceptionTarget, result) - } - return result -} - -func (b *Binder) createFlowSwitchClause(antecedent *ast.FlowNode, switchStatement *ast.Node, clauseStart int, clauseEnd int) *ast.FlowNode { - setFlowNodeReferenced(antecedent) - return b.newFlowNodeEx(ast.FlowFlagsSwitchClause, ast.NewFlowSwitchClauseData(switchStatement, clauseStart, clauseEnd), antecedent) -} - -func (b *Binder) createFlowCall(antecedent *ast.FlowNode, node *ast.Node) *ast.FlowNode { - setFlowNodeReferenced(antecedent) - b.hasFlowEffects = true - return b.newFlowNodeEx(ast.FlowFlagsCall, node, antecedent) -} - -func (b *Binder) newFlowList(head *ast.FlowNode, tail *ast.FlowList) *ast.FlowList { - result := b.flowListPool.New() - result.Flow = head - result.Next = tail - return result -} - -func (b *Binder) combineFlowLists(head *ast.FlowList, tail *ast.FlowList) *ast.FlowList { - if head == nil { - return tail - } - return b.newFlowList(head.Flow, b.combineFlowLists(head.Next, tail)) -} - -func (b *Binder) newSingleDeclaration(declaration *ast.Node) []*ast.Node { - return b.singleDeclarationsPool.NewSlice1(declaration) -} - -func setFlowNodeReferenced(flow *ast.FlowNode) { - // On first reference we set the Referenced flag, thereafter we set the Shared flag - if flow.Flags&ast.FlowFlagsReferenced == 0 { - flow.Flags |= ast.FlowFlagsReferenced - } else { - flow.Flags |= ast.FlowFlagsShared - } -} - -func (b *Binder) addAntecedent(label *ast.FlowLabel, antecedent *ast.FlowNode) { - if antecedent.Flags&ast.FlowFlagsUnreachable != 0 { - return - } - // If antecedent isn't already on the Antecedents list, add it to the end of the list - var last *ast.FlowList - for list := label.Antecedents; list != nil; list = list.Next { - if list.Flow == antecedent { - return - } - last = list - } - if last == nil { - label.Antecedents = b.newFlowList(antecedent, nil) - } else { - last.Next = b.newFlowList(antecedent, nil) - } - setFlowNodeReferenced(antecedent) -} - -func (b *Binder) finishFlowLabel(label *ast.FlowLabel) *ast.FlowNode { - if label.Antecedents == nil { - return b.unreachableFlow - } - if label.Antecedents.Next == nil { - return label.Antecedents.Flow - } - return label -} - -func (b *Binder) bind(node *ast.Node) bool { - if node == nil { - return false - } - saveInStrictMode := b.inStrictMode - // Even though in the AST the jsdoc @typedef node belongs to the current node, - // its symbol might be in the same scope with the current node's symbol. Consider: - // - // /** @typedef {string | number} MyType */ - // function foo(); - // - // Here the current node is "foo", which is a container, but the scope of "MyType" should - // not be inside "foo". Therefore we always bind @typedef before bind the parent node, - // and skip binding this tag later when binding all the other jsdoc tags. - - // First we bind declaration nodes to a symbol if possible. We'll both create a symbol - // and then potentially add the symbol to an appropriate symbol table. Possible - // destination symbol tables are: - // - // 1) The 'exports' table of the current container's symbol. - // 2) The 'members' table of the current container's symbol. - // 3) The 'locals' table of the current container. - // - // However, not all symbols will end up in any of these tables. 'Anonymous' symbols - // (like TypeLiterals for example) will not be put in any table. - switch node.Kind { - case ast.KindIdentifier: - node.AsIdentifier().FlowNode = b.currentFlow - b.checkContextualIdentifier(node) - case ast.KindThisKeyword, ast.KindSuperKeyword: - node.AsKeywordExpression().FlowNode = b.currentFlow - case ast.KindQualifiedName: - if b.currentFlow != nil && ast.IsPartOfTypeQuery(node) { - node.AsQualifiedName().FlowNode = b.currentFlow - } - case ast.KindMetaProperty: - node.AsMetaProperty().FlowNode = b.currentFlow - case ast.KindPrivateIdentifier: - b.checkPrivateIdentifier(node) - case ast.KindPropertyAccessExpression, ast.KindElementAccessExpression: - if b.currentFlow != nil && isNarrowableReference(node) { - setFlowNode(node, b.currentFlow) - } - case ast.KindBinaryExpression: - switch ast.GetAssignmentDeclarationKind(node.AsBinaryExpression()) { - case ast.JSDeclarationKindProperty: - b.bindExpandoPropertyAssignment(node) - case ast.JSDeclarationKindThisProperty: - b.bindThisPropertyAssignment(node) - } - b.checkStrictModeBinaryExpression(node) - case ast.KindCatchClause: - b.checkStrictModeCatchClause(node) - case ast.KindDeleteExpression: - b.checkStrictModeDeleteExpression(node) - case ast.KindPostfixUnaryExpression: - b.checkStrictModePostfixUnaryExpression(node) - case ast.KindPrefixUnaryExpression: - b.checkStrictModePrefixUnaryExpression(node) - case ast.KindWithStatement: - b.checkStrictModeWithStatement(node) - case ast.KindLabeledStatement: - b.checkStrictModeLabeledStatement(node) - case ast.KindThisType: - b.seenThisKeyword = true - case ast.KindTypeParameter: - b.bindTypeParameter(node) - case ast.KindParameter: - b.bindParameter(node) - case ast.KindVariableDeclaration: - b.bindVariableDeclarationOrBindingElement(node) - case ast.KindBindingElement: - node.AsBindingElement().FlowNode = b.currentFlow - b.bindVariableDeclarationOrBindingElement(node) - case ast.KindCommonJSExport: - b.declareModuleMember(node, ast.SymbolFlagsFunctionScopedVariable, ast.SymbolFlagsFunctionScopedVariableExcludes) - case ast.KindPropertyDeclaration, ast.KindPropertySignature: - b.bindPropertyWorker(node) - case ast.KindPropertyAssignment, ast.KindShorthandPropertyAssignment: - b.bindPropertyOrMethodOrAccessor(node, ast.SymbolFlagsProperty, ast.SymbolFlagsPropertyExcludes) - case ast.KindEnumMember: - b.bindPropertyOrMethodOrAccessor(node, ast.SymbolFlagsEnumMember, ast.SymbolFlagsEnumMemberExcludes) - case ast.KindCallSignature, ast.KindConstructSignature, ast.KindIndexSignature: - b.declareSymbolAndAddToSymbolTable(node, ast.SymbolFlagsSignature, ast.SymbolFlagsNone) - case ast.KindMethodDeclaration, ast.KindMethodSignature: - b.bindPropertyOrMethodOrAccessor(node, ast.SymbolFlagsMethod|getOptionalSymbolFlagForNode(node), core.IfElse(ast.IsObjectLiteralMethod(node), ast.SymbolFlagsPropertyExcludes, ast.SymbolFlagsMethodExcludes)) - case ast.KindFunctionDeclaration: - b.bindFunctionDeclaration(node) - case ast.KindConstructor: - b.declareSymbolAndAddToSymbolTable(node, ast.SymbolFlagsConstructor, ast.SymbolFlagsNone) - case ast.KindGetAccessor: - b.bindPropertyOrMethodOrAccessor(node, ast.SymbolFlagsGetAccessor, ast.SymbolFlagsGetAccessorExcludes) - case ast.KindSetAccessor: - b.bindPropertyOrMethodOrAccessor(node, ast.SymbolFlagsSetAccessor, ast.SymbolFlagsSetAccessorExcludes) - case ast.KindFunctionType, ast.KindConstructorType: - b.bindFunctionOrConstructorType(node) - case ast.KindTypeLiteral, ast.KindMappedType: - b.bindAnonymousDeclaration(node, ast.SymbolFlagsTypeLiteral, ast.InternalSymbolNameType) - case ast.KindObjectLiteralExpression: - b.bindAnonymousDeclaration(node, ast.SymbolFlagsObjectLiteral, ast.InternalSymbolNameObject) - case ast.KindFunctionExpression, ast.KindArrowFunction: - b.bindFunctionExpression(node) - case ast.KindClassExpression, ast.KindClassDeclaration: - b.inStrictMode = true - b.bindClassLikeDeclaration(node) - case ast.KindInterfaceDeclaration: - b.bindBlockScopedDeclaration(node, ast.SymbolFlagsInterface, ast.SymbolFlagsInterfaceExcludes) - case ast.KindCallExpression: - b.bindCallExpression(node) - case ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration: - b.bindBlockScopedDeclaration(node, ast.SymbolFlagsTypeAlias, ast.SymbolFlagsTypeAliasExcludes) - case ast.KindEnumDeclaration: - b.bindEnumDeclaration(node) - case ast.KindModuleDeclaration: - b.bindModuleDeclaration(node) - case ast.KindImportEqualsDeclaration, ast.KindNamespaceImport, ast.KindImportSpecifier, ast.KindExportSpecifier: - b.declareSymbolAndAddToSymbolTable(node, ast.SymbolFlagsAlias, ast.SymbolFlagsAliasExcludes) - case ast.KindNamespaceExportDeclaration: - b.bindNamespaceExportDeclaration(node) - case ast.KindImportClause: - b.bindImportClause(node) - case ast.KindExportDeclaration: - b.bindExportDeclaration(node) - case ast.KindExportAssignment, ast.KindJSExportAssignment: - b.bindExportAssignment(node) - case ast.KindSourceFile: - b.updateStrictModeStatementList(node.AsSourceFile().Statements) - b.bindSourceFileIfExternalModule() - case ast.KindBlock: - if ast.IsFunctionLikeOrClassStaticBlockDeclaration(node.Parent) { - b.updateStrictModeStatementList(node.AsBlock().Statements) - } - case ast.KindModuleBlock: - b.updateStrictModeStatementList(node.AsModuleBlock().Statements) - case ast.KindJsxAttributes: - b.bindJsxAttributes(node) - case ast.KindJsxAttribute: - b.bindJsxAttribute(node, ast.SymbolFlagsProperty, ast.SymbolFlagsPropertyExcludes) - } - // Then we recurse into the children of the node to bind them as well. For certain - // symbols we do specialized work when we recurse. For example, we'll keep track of - // the current 'container' node when it changes. This helps us know which symbol table - // a local should go into for example. Since terminal nodes are known not to have - // children, as an optimization we don't process those. - thisNodeOrAnySubnodesHasError := node.Flags&ast.NodeFlagsThisNodeHasError != 0 - if node.Kind > ast.KindLastToken { - saveSeenParseError := b.seenParseError - b.seenParseError = false - containerFlags := GetContainerFlags(node) - if containerFlags == ContainerFlagsNone { - b.bindChildren(node) - } else { - b.bindContainer(node, containerFlags) - } - if b.seenParseError { - thisNodeOrAnySubnodesHasError = true - } - b.seenParseError = saveSeenParseError - } - if thisNodeOrAnySubnodesHasError { - node.Flags |= ast.NodeFlagsThisNodeOrAnySubNodesHasError - b.seenParseError = true - } - b.inStrictMode = saveInStrictMode - return false -} - -func (b *Binder) bindPropertyWorker(node *ast.Node) { - isAutoAccessor := ast.IsAutoAccessorPropertyDeclaration(node) - includes := core.IfElse(isAutoAccessor, ast.SymbolFlagsAccessor, ast.SymbolFlagsProperty) - excludes := core.IfElse(isAutoAccessor, ast.SymbolFlagsAccessorExcludes, ast.SymbolFlagsPropertyExcludes) - b.bindPropertyOrMethodOrAccessor(node, includes|getOptionalSymbolFlagForNode(node), excludes) -} - -func (b *Binder) bindSourceFileIfExternalModule() { - b.setExportContextFlag(b.file.AsNode()) - if ast.IsExternalOrCommonJSModule(b.file) { - b.bindSourceFileAsExternalModule() - } else if ast.IsJsonSourceFile(b.file) { - b.bindSourceFileAsExternalModule() - // Create symbol equivalent for the module.exports = {} - originalSymbol := b.file.Symbol - b.declareSymbol(ast.GetSymbolTable(&b.file.Symbol.Exports), b.file.Symbol, b.file.AsNode(), ast.SymbolFlagsProperty, ast.SymbolFlagsAll) - b.file.Symbol = originalSymbol - } -} - -func (b *Binder) bindSourceFileAsExternalModule() { - b.bindAnonymousDeclaration(b.file.AsNode(), ast.SymbolFlagsValueModule, "\""+tspath.RemoveFileExtension(b.file.FileName())+"\"") -} - -func (b *Binder) bindModuleDeclaration(node *ast.Node) { - b.setExportContextFlag(node) - if ast.IsAmbientModule(node) { - if ast.HasSyntacticModifier(node, ast.ModifierFlagsExport) { - b.errorOnFirstToken(node, diagnostics.X_export_modifier_cannot_be_applied_to_ambient_modules_and_module_augmentations_since_they_are_always_visible) - } - if ast.IsModuleAugmentationExternal(node) { - b.declareModuleSymbol(node) - } else { - name := node.AsModuleDeclaration().Name() - symbol := b.declareSymbolAndAddToSymbolTable(node, ast.SymbolFlagsValueModule, ast.SymbolFlagsValueModuleExcludes) - - if ast.IsStringLiteral(name) { - pattern := core.TryParsePattern(name.AsStringLiteral().Text) - if !pattern.IsValid() { - // An invalid pattern - must have multiple wildcards. - b.errorOnFirstToken(name, diagnostics.Pattern_0_can_have_at_most_one_Asterisk_character, name.AsStringLiteral().Text) - } else if pattern.StarIndex >= 0 { - b.file.PatternAmbientModules = append(b.file.PatternAmbientModules, &ast.PatternAmbientModule{Pattern: pattern, Symbol: symbol}) - } - } - } - } else { - state := b.declareModuleSymbol(node) - if state != ast.ModuleInstanceStateNonInstantiated { - symbol := node.AsModuleDeclaration().Symbol - if symbol.Flags&(ast.SymbolFlagsFunction|ast.SymbolFlagsClass|ast.SymbolFlagsRegularEnum) != 0 || state != ast.ModuleInstanceStateConstEnumOnly { - // if module was already merged with some function, class or non-const enum, treat it as non-const-enum-only - symbol.Flags &^= ast.SymbolFlagsConstEnumOnlyModule - } - } - } -} - -func (b *Binder) declareModuleSymbol(node *ast.Node) ast.ModuleInstanceState { - state := ast.GetModuleInstanceState(node) - instantiated := state != ast.ModuleInstanceStateNonInstantiated - b.declareSymbolAndAddToSymbolTable(node, core.IfElse(instantiated, ast.SymbolFlagsValueModule, ast.SymbolFlagsNamespaceModule), core.IfElse(instantiated, ast.SymbolFlagsValueModuleExcludes, ast.SymbolFlagsNamespaceModuleExcludes)) - return state -} - -func (b *Binder) bindNamespaceExportDeclaration(node *ast.Node) { - if node.Modifiers() != nil { - b.errorOnNode(node, diagnostics.Modifiers_cannot_appear_here) - } - switch { - case !ast.IsSourceFile(node.Parent): - b.errorOnNode(node, diagnostics.Global_module_exports_may_only_appear_at_top_level) - case !ast.IsExternalModule(node.Parent.AsSourceFile()): - b.errorOnNode(node, diagnostics.Global_module_exports_may_only_appear_in_module_files) - case !node.Parent.AsSourceFile().IsDeclarationFile: - b.errorOnNode(node, diagnostics.Global_module_exports_may_only_appear_in_declaration_files) - default: - b.declareSymbol(ast.GetSymbolTable(&b.file.Symbol.GlobalExports), b.file.Symbol, node, ast.SymbolFlagsAlias, ast.SymbolFlagsAliasExcludes) - } -} - -func (b *Binder) bindImportClause(node *ast.Node) { - if node.AsImportClause().Name() != nil { - b.declareSymbolAndAddToSymbolTable(node, ast.SymbolFlagsAlias, ast.SymbolFlagsAliasExcludes) - } -} - -func (b *Binder) bindExportDeclaration(node *ast.Node) { - decl := node.AsExportDeclaration() - if b.container.Symbol() == nil { - // Export * in some sort of block construct - b.bindAnonymousDeclaration(node, ast.SymbolFlagsExportStar, b.getDeclarationName(node)) - } else if decl.ExportClause == nil { - // All export * declarations are collected in an __export symbol - b.declareSymbol(ast.GetExports(b.container.Symbol()), b.container.Symbol(), node, ast.SymbolFlagsExportStar, ast.SymbolFlagsNone) - } else if ast.IsNamespaceExport(decl.ExportClause) { - b.declareSymbol(ast.GetExports(b.container.Symbol()), b.container.Symbol(), decl.ExportClause, ast.SymbolFlagsAlias, ast.SymbolFlagsAliasExcludes) - } -} - -func (b *Binder) bindExportAssignment(node *ast.Node) { - container := b.container - if ast.IsJSExportAssignment(node) { - container = b.file.AsNode() - } - if container.Symbol() == nil && ast.IsExportAssignment(node) { - // Incorrect export assignment in some sort of block construct - b.bindAnonymousDeclaration(node, ast.SymbolFlagsValue, b.getDeclarationName(node)) - } else { - flags := ast.SymbolFlagsProperty - if ast.ExportAssignmentIsAlias(node) { - flags = ast.SymbolFlagsAlias - } - // If there is an `export default x;` alias declaration, can't `export default` anything else. - // (In contrast, you can still have `export default function f() {}` and `export default interface I {}`.) - symbol := b.declareSymbol(ast.GetExports(container.Symbol()), container.Symbol(), node, flags, ast.SymbolFlagsAll) - if ast.IsJSExportAssignment(node) || node.AsExportAssignment().IsExportEquals { - // Will be an error later, since the module already has other exports. Just make sure this has a valueDeclaration set. - SetValueDeclaration(symbol, node) - } - } -} - -func (b *Binder) bindJsxAttributes(node *ast.Node) { - b.bindAnonymousDeclaration(node, ast.SymbolFlagsObjectLiteral, ast.InternalSymbolNameJSXAttributes) -} - -func (b *Binder) bindJsxAttribute(node *ast.Node, symbolFlags ast.SymbolFlags, symbolExcludes ast.SymbolFlags) { - b.declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes) -} - -func (b *Binder) setExportContextFlag(node *ast.Node) { - // A declaration source file or ambient module declaration that contains no export declarations (but possibly regular - // declarations with export modifiers) is an export context in which declarations are implicitly exported. - if node.Flags&ast.NodeFlagsAmbient != 0 && !b.hasExportDeclarations(node) { - node.Flags |= ast.NodeFlagsExportContext - } else { - node.Flags &= ^ast.NodeFlagsExportContext - } -} - -func (b *Binder) hasExportDeclarations(node *ast.Node) bool { - var statements []*ast.Node - switch node.Kind { - case ast.KindSourceFile: - statements = node.AsSourceFile().Statements.Nodes - case ast.KindModuleDeclaration: - body := node.AsModuleDeclaration().Body - if body != nil && ast.IsModuleBlock(body) { - statements = body.AsModuleBlock().Statements.Nodes - } - } - return core.Some(statements, func(s *ast.Node) bool { - return ast.IsExportDeclaration(s) || ast.IsExportAssignment(s) || ast.IsJSExportAssignment(s) - }) -} - -func (b *Binder) bindFunctionExpression(node *ast.Node) { - setFlowNode(node, b.currentFlow) - bindingName := ast.InternalSymbolNameFunction - if ast.IsFunctionExpression(node) && node.AsFunctionExpression().Name() != nil { - b.checkStrictModeFunctionName(node) - bindingName = node.AsFunctionExpression().Name().AsIdentifier().Text - } - b.bindAnonymousDeclaration(node, ast.SymbolFlagsFunction, bindingName) -} - -func (b *Binder) bindCallExpression(node *ast.Node) { - // !!! for ModuleDetectionKind.Force, external module indicator is forced to `true` in Strada for source files, in which case - // we should set the commonjs module indicator but not call b.bindSourceFileAsExternalModule - // !!! && file.externalModuleIndicator !== true (used for ModuleDetectionKind.Force) - if ast.IsInJSFile(node) && - b.file.ExternalModuleIndicator == nil && - b.file.CommonJSModuleIndicator == nil && - ast.IsRequireCall(node, false /*requireStringLiteralLikeArgument*/) { - b.file.CommonJSModuleIndicator = node - b.bindSourceFileAsExternalModule() - } -} - -func (b *Binder) bindClassLikeDeclaration(node *ast.Node) { - name := node.Name() - switch node.Kind { - case ast.KindClassDeclaration: - b.bindBlockScopedDeclaration(node, ast.SymbolFlagsClass, ast.SymbolFlagsClassExcludes) - case ast.KindClassExpression: - nameText := ast.InternalSymbolNameClass - if name != nil { - nameText = name.AsIdentifier().Text - b.classifiableNames.Add(nameText) - } - b.bindAnonymousDeclaration(node, ast.SymbolFlagsClass, nameText) - } - symbol := node.Symbol() - // TypeScript 1.0 spec (April 2014): 8.4 - // Every class automatically contains a static property member named 'prototype', the - // type of which is an instantiation of the class type with type Any supplied as a type - // argument for each type parameter. It is an error to explicitly declare a static - // property member with the name 'prototype'. - // - // Note: we check for this here because this class may be merging into a module. The - // module might have an exported variable called 'prototype'. We can't allow that as - // that would clash with the built-in 'prototype' for the class. - prototypeSymbol := b.newSymbol(ast.SymbolFlagsProperty|ast.SymbolFlagsPrototype, "prototype") - symbolExport := ast.GetExports(symbol)[prototypeSymbol.Name] - if symbolExport != nil { - b.errorOnNode(symbolExport.Declarations[0], diagnostics.Duplicate_identifier_0, ast.SymbolName(prototypeSymbol)) - } - ast.GetExports(symbol)[prototypeSymbol.Name] = prototypeSymbol - prototypeSymbol.Parent = symbol -} - -func (b *Binder) bindPropertyOrMethodOrAccessor(node *ast.Node, symbolFlags ast.SymbolFlags, symbolExcludes ast.SymbolFlags) { - if b.currentFlow != nil && ast.IsObjectLiteralOrClassExpressionMethodOrAccessor(node) { - setFlowNode(node, b.currentFlow) - } - if ast.HasDynamicName(node) { - b.bindAnonymousDeclaration(node, symbolFlags, ast.InternalSymbolNameComputed) - } else { - b.declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes) - } -} - -func (b *Binder) bindFunctionOrConstructorType(node *ast.Node) { - // For a given function symbol "<...>(...) => T" we want to generate a symbol identical - // to the one we would get for: { <...>(...): T } - // - // We do that by making an anonymous type literal symbol, and then setting the function - // symbol as its sole member. To the rest of the system, this symbol will be indistinguishable - // from an actual type literal symbol you would have gotten had you used the long form. - symbol := b.newSymbol(ast.SymbolFlagsSignature, b.getDeclarationName(node)) - b.addDeclarationToSymbol(symbol, node, ast.SymbolFlagsSignature) - typeLiteralSymbol := b.newSymbol(ast.SymbolFlagsTypeLiteral, ast.InternalSymbolNameType) - b.addDeclarationToSymbol(typeLiteralSymbol, node, ast.SymbolFlagsTypeLiteral) - typeLiteralSymbol.Members = make(ast.SymbolTable) - typeLiteralSymbol.Members[symbol.Name] = symbol -} - -func addLateBoundAssignmentDeclarationToSymbol(node *ast.Node, symbol *ast.Symbol) { - symbol.AssignmentDeclarationMembers.Add(node) -} - -func (b *Binder) bindExpandoPropertyAssignment(node *ast.Node) { - expr := node.AsBinaryExpression() - parent := expr.Left.Expression() - symbol := b.lookupEntity(parent, b.blockScopeContainer) - if symbol == nil { - symbol = b.lookupEntity(parent, b.container) - } - if symbol = getInitializerSymbol(symbol); symbol != nil { - if ast.HasDynamicName(node) { - b.bindAnonymousDeclaration(node, ast.SymbolFlagsProperty|ast.SymbolFlagsAssignment, ast.InternalSymbolNameComputed) - addLateBoundAssignmentDeclarationToSymbol(node, symbol) - } else { - b.declareSymbol(ast.GetExports(symbol), symbol, node, ast.SymbolFlagsProperty|ast.SymbolFlagsAssignment, ast.SymbolFlagsPropertyExcludes) - } - } -} - -func getInitializerSymbol(symbol *ast.Symbol) *ast.Symbol { - if symbol == nil || symbol.ValueDeclaration == nil { - return nil - } - declaration := symbol.ValueDeclaration - // For an assignment 'fn.xxx = ...', where 'fn' is a previously declared function or a previously - // declared const variable initialized with a function expression or arrow function, we add expando - // property declarations to the function's symbol. - // This also applies to class expressions and empty object literals. - switch { - case ast.IsFunctionDeclaration(declaration) || ast.IsInJSFile(declaration) && ast.IsClassDeclaration(declaration): - return symbol - case ast.IsVariableDeclaration(declaration) && - (declaration.Parent.Flags&ast.NodeFlagsConst != 0 || ast.IsInJSFile(declaration)): - initializer := declaration.Initializer() - if ast.IsExpandoInitializer(initializer) { - return initializer.Symbol() - } - case ast.IsBinaryExpression(declaration) && ast.IsInJSFile(declaration): - initializer := declaration.AsBinaryExpression().Right - if ast.IsExpandoInitializer(initializer) { - return initializer.Symbol() - } - } - return nil -} - -func (b *Binder) bindThisPropertyAssignment(node *ast.Node) { - if !ast.IsInJSFile(node) { - return - } - bin := node.AsBinaryExpression() - if ast.IsPropertyAccessExpression(bin.Left) && ast.IsPrivateIdentifier(bin.Left.AsPropertyAccessExpression().Name()) || - b.thisContainer == nil { - return - } - if classSymbol, symbolTable := b.getThisClassAndSymbolTable(); symbolTable != nil { - if ast.HasDynamicName(node) { - b.declareSymbolEx(symbolTable, classSymbol, node, ast.SymbolFlagsProperty, ast.SymbolFlagsNone, true /*isReplaceableByMethod*/, true /*isComputedName*/) - addLateBoundAssignmentDeclarationToSymbol(node, classSymbol) - } else { - b.declareSymbolEx(symbolTable, classSymbol, node, ast.SymbolFlagsProperty|ast.SymbolFlagsAssignment, ast.SymbolFlagsNone, true /*isReplaceableByMethod*/, false /*isComputedName*/) - } - } else if b.thisContainer.Kind != ast.KindFunctionDeclaration && b.thisContainer.Kind != ast.KindFunctionExpression { - // !!! constructor functions - panic("Unhandled case in bindThisPropertyAssignment: " + b.thisContainer.Kind.String()) - } -} - -func (b *Binder) getThisClassAndSymbolTable() (classSymbol *ast.Symbol, symbolTable ast.SymbolTable) { - if b.thisContainer == nil { - return nil, nil - } - switch b.thisContainer.Kind { - case ast.KindFunctionDeclaration, ast.KindFunctionExpression: - // !!! constructor functions - case ast.KindConstructor, ast.KindPropertyDeclaration, ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor, ast.KindClassStaticBlockDeclaration: - // this.property assignment in class member -- bind to the containing class - classSymbol = b.thisContainer.Parent.Symbol() - if ast.IsStatic(b.thisContainer) { - symbolTable = ast.GetExports(classSymbol) - } else { - symbolTable = ast.GetMembers(classSymbol) - } - } - return classSymbol, symbolTable -} - -func (b *Binder) bindEnumDeclaration(node *ast.Node) { - if ast.IsEnumConst(node) { - b.bindBlockScopedDeclaration(node, ast.SymbolFlagsConstEnum, ast.SymbolFlagsConstEnumExcludes) - } else { - b.bindBlockScopedDeclaration(node, ast.SymbolFlagsRegularEnum, ast.SymbolFlagsRegularEnumExcludes) - } -} - -func (b *Binder) bindVariableDeclarationOrBindingElement(node *ast.Node) { - if b.inStrictMode { - b.checkStrictModeEvalOrArguments(node, node.Name()) - } - if name := node.Name(); name != nil && !ast.IsBindingPattern(name) { - switch { - case ast.IsVariableDeclarationInitializedToRequire(node): - b.declareSymbolAndAddToSymbolTable(node, ast.SymbolFlagsAlias, ast.SymbolFlagsAliasExcludes) - case ast.IsBlockOrCatchScoped(node): - b.bindBlockScopedDeclaration(node, ast.SymbolFlagsBlockScopedVariable, ast.SymbolFlagsBlockScopedVariableExcludes) - case ast.IsPartOfParameterDeclaration(node): - // It is safe to walk up parent chain to find whether the node is a destructuring parameter declaration - // because its parent chain has already been set up, since parents are set before descending into children. - // - // If node is a binding element in parameter declaration, we need to use ParameterExcludes. - // Using ParameterExcludes flag allows the compiler to report an error on duplicate identifiers in Parameter Declaration - // For example: - // function foo([a,a]) {} // Duplicate Identifier error - // function bar(a,a) {} // Duplicate Identifier error, parameter declaration in this case is handled in bindParameter - // // which correctly set excluded symbols - b.declareSymbolAndAddToSymbolTable(node, ast.SymbolFlagsFunctionScopedVariable, ast.SymbolFlagsParameterExcludes) - default: - b.declareSymbolAndAddToSymbolTable(node, ast.SymbolFlagsFunctionScopedVariable, ast.SymbolFlagsFunctionScopedVariableExcludes) - } - } -} - -func (b *Binder) bindParameter(node *ast.Node) { - decl := node.AsParameterDeclaration() - if b.inStrictMode && node.Flags&ast.NodeFlagsAmbient == 0 { - // It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a - // strict mode FunctionLikeDeclaration or FunctionExpression(13.1) - b.checkStrictModeEvalOrArguments(node, decl.Name()) - } - if ast.IsBindingPattern(decl.Name()) { - index := slices.Index(node.Parent.Parameters(), node) - b.bindAnonymousDeclaration(node, ast.SymbolFlagsFunctionScopedVariable, "__"+strconv.Itoa(index)) - } else { - b.declareSymbolAndAddToSymbolTable(node, ast.SymbolFlagsFunctionScopedVariable, ast.SymbolFlagsParameterExcludes) - } - // If this is a property-parameter, then also declare the property symbol into the - // containing class. - if ast.IsParameterPropertyDeclaration(node, node.Parent) { - classDeclaration := node.Parent.Parent - flags := ast.SymbolFlagsProperty | core.IfElse(decl.QuestionToken != nil, ast.SymbolFlagsOptional, ast.SymbolFlagsNone) - b.declareSymbol(ast.GetMembers(classDeclaration.Symbol()), classDeclaration.Symbol(), node, flags, ast.SymbolFlagsPropertyExcludes) - } -} - -func (b *Binder) bindFunctionDeclaration(node *ast.Node) { - b.checkStrictModeFunctionName(node) - if b.inStrictMode { - b.bindBlockScopedDeclaration(node, ast.SymbolFlagsFunction, ast.SymbolFlagsFunctionExcludes) - } else { - b.declareSymbolAndAddToSymbolTable(node, ast.SymbolFlagsFunction, ast.SymbolFlagsFunctionExcludes) - } -} - -func (b *Binder) getInferTypeContainer(node *ast.Node) *ast.Node { - extendsType := ast.FindAncestor(node, func(n *ast.Node) bool { - parent := n.Parent - return parent != nil && ast.IsConditionalTypeNode(parent) && parent.AsConditionalTypeNode().ExtendsType == n - }) - if extendsType != nil { - return extendsType.Parent - } - return nil -} - -func (b *Binder) bindAnonymousDeclaration(node *ast.Node, symbolFlags ast.SymbolFlags, name string) { - symbol := b.newSymbol(symbolFlags, name) - if symbolFlags&(ast.SymbolFlagsEnumMember|ast.SymbolFlagsClassMember) != 0 { - symbol.Parent = b.container.Symbol() - } - b.addDeclarationToSymbol(symbol, node, symbolFlags) -} - -func (b *Binder) bindBlockScopedDeclaration(node *ast.Node, symbolFlags ast.SymbolFlags, symbolExcludes ast.SymbolFlags) { - switch b.blockScopeContainer.Kind { - case ast.KindModuleDeclaration: - b.declareModuleMember(node, symbolFlags, symbolExcludes) - case ast.KindSourceFile: - if ast.IsExternalOrCommonJSModule(b.container.AsSourceFile()) { - b.declareModuleMember(node, symbolFlags, symbolExcludes) - break - } - fallthrough - default: - b.declareSymbol(ast.GetLocals(b.blockScopeContainer), nil /*parent*/, node, symbolFlags, symbolExcludes) - } -} - -func (b *Binder) bindTypeParameter(node *ast.Node) { - if node.Parent.Kind == ast.KindInferType { - container := b.getInferTypeContainer(node.Parent) - if container != nil { - b.declareSymbol(ast.GetLocals(container), nil /*parent*/, node, ast.SymbolFlagsTypeParameter, ast.SymbolFlagsTypeParameterExcludes) - } else { - b.bindAnonymousDeclaration(node, ast.SymbolFlagsTypeParameter, b.getDeclarationName(node)) - } - } else { - b.declareSymbolAndAddToSymbolTable(node, ast.SymbolFlagsTypeParameter, ast.SymbolFlagsTypeParameterExcludes) - } -} - -func (b *Binder) lookupEntity(node *ast.Node, container *ast.Node) *ast.Symbol { - if ast.IsIdentifier(node) { - return b.lookupName(node.AsIdentifier().Text, container) - } - if ast.IsPropertyAccessExpression(node) && node.AsPropertyAccessExpression().Expression.Kind == ast.KindThisKeyword || - ast.IsElementAccessExpression(node) && node.AsElementAccessExpression().Expression.Kind == ast.KindThisKeyword { - if _, symbolTable := b.getThisClassAndSymbolTable(); symbolTable != nil { - if name := ast.GetElementOrPropertyAccessName(node); name != nil { - return symbolTable[name.Text()] - } - } - return nil - } - if symbol := getInitializerSymbol(b.lookupEntity(node.Expression(), container)); symbol != nil && symbol.Exports != nil { - if name := ast.GetElementOrPropertyAccessName(node); name != nil { - return symbol.Exports[name.Text()] - } - } - return nil -} - -func (b *Binder) lookupName(name string, container *ast.Node) *ast.Symbol { - if localsContainer := container.LocalsContainerData(); localsContainer != nil { - if local := localsContainer.Locals[name]; local != nil { - return core.OrElse(local.ExportSymbol, local) - } - } - - if declaration := container.DeclarationData(); declaration != nil && declaration.Symbol != nil { - return declaration.Symbol.Exports[name] - } - return nil -} - -// The binder visits every node in the syntax tree so it is a convenient place to perform a single localized -// check for reserved words used as identifiers in strict mode code, as well as `yield` or `await` in -// [Yield] or [Await] contexts, respectively. -func (b *Binder) checkContextualIdentifier(node *ast.Node) { - // Report error only if there are no parse errors in file - if len(b.file.Diagnostics()) == 0 && node.Flags&ast.NodeFlagsAmbient == 0 && node.Flags&ast.NodeFlagsJSDoc == 0 && !ast.IsIdentifierName(node) { - // strict mode identifiers - originalKeywordKind := scanner.GetIdentifierToken(node.AsIdentifier().Text) - if originalKeywordKind == ast.KindIdentifier { - return - } - if b.inStrictMode && originalKeywordKind >= ast.KindFirstFutureReservedWord && originalKeywordKind <= ast.KindLastFutureReservedWord { - b.errorOnNode(node, b.getStrictModeIdentifierMessage(node), scanner.DeclarationNameToString(node)) - } else if originalKeywordKind == ast.KindAwaitKeyword { - if ast.IsExternalModule(b.file) && ast.IsInTopLevelContext(node) { - b.errorOnNode(node, diagnostics.Identifier_expected_0_is_a_reserved_word_at_the_top_level_of_a_module, scanner.DeclarationNameToString(node)) - } else if node.Flags&ast.NodeFlagsAwaitContext != 0 { - b.errorOnNode(node, diagnostics.Identifier_expected_0_is_a_reserved_word_that_cannot_be_used_here, scanner.DeclarationNameToString(node)) - } - } else if originalKeywordKind == ast.KindYieldKeyword && node.Flags&ast.NodeFlagsYieldContext != 0 { - b.errorOnNode(node, diagnostics.Identifier_expected_0_is_a_reserved_word_that_cannot_be_used_here, scanner.DeclarationNameToString(node)) - } - } -} - -func (b *Binder) checkPrivateIdentifier(node *ast.Node) { - if node.AsPrivateIdentifier().Text == "#constructor" { - // Report error only if there are no parse errors in file - if len(b.file.Diagnostics()) == 0 { - b.errorOnNode(node, diagnostics.X_constructor_is_a_reserved_word, scanner.DeclarationNameToString(node)) - } - } -} - -func (b *Binder) getStrictModeIdentifierMessage(node *ast.Node) *diagnostics.Message { - // Provide specialized messages to help the user understand why we think they're in - // strict mode. - if ast.GetContainingClass(node) != nil { - return diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode_Class_definitions_are_automatically_in_strict_mode - } - if b.file.ExternalModuleIndicator != nil { - return diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode_Modules_are_automatically_in_strict_mode - } - return diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode -} - -func (b *Binder) updateStrictModeStatementList(statements *ast.NodeList) { - if !b.inStrictMode { - useStrictDirective := FindUseStrictPrologue(b.file, statements.Nodes) - if useStrictDirective != nil { - b.inStrictMode = true - } - } -} - -// Should be called only on prologue directives (ast.IsPrologueDirective(node) should be true) -func isUseStrictPrologueDirective(sourceFile *ast.SourceFile, node *ast.Node) bool { - nodeText := scanner.GetSourceTextOfNodeFromSourceFile(sourceFile, node.AsExpressionStatement().Expression, false /*includeTrivia*/) - // Note: the node text must be exactly "use strict" or 'use strict'. It is not ok for the - // string to contain unicode escapes (as per ES5). - return nodeText == "\"use strict\"" || nodeText == "'use strict'" -} - -func FindUseStrictPrologue(sourceFile *ast.SourceFile, statements []*ast.Node) *ast.Node { - for _, statement := range statements { - if ast.IsPrologueDirective(statement) { - if isUseStrictPrologueDirective(sourceFile, statement) { - return statement - } - } else { - return nil - } - } - - return nil -} - -func (b *Binder) checkStrictModeFunctionName(node *ast.Node) { - if b.inStrictMode && node.Flags&ast.NodeFlagsAmbient == 0 { - // It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a strict mode FunctionDeclaration or FunctionExpression (13.1)) - b.checkStrictModeEvalOrArguments(node, node.Name()) - } -} - -func (b *Binder) getStrictModeBlockScopeFunctionDeclarationMessage(node *ast.Node) *diagnostics.Message { - // Provide specialized messages to help the user understand why we think they're in strict mode. - if ast.GetContainingClass(node) != nil { - return diagnostics.Function_declarations_are_not_allowed_inside_blocks_in_strict_mode_when_targeting_ES5_Class_definitions_are_automatically_in_strict_mode - } - if b.file.ExternalModuleIndicator != nil { - return diagnostics.Function_declarations_are_not_allowed_inside_blocks_in_strict_mode_when_targeting_ES5_Modules_are_automatically_in_strict_mode - } - return diagnostics.Function_declarations_are_not_allowed_inside_blocks_in_strict_mode_when_targeting_ES5 -} - -func (b *Binder) checkStrictModeBinaryExpression(node *ast.Node) { - expr := node.AsBinaryExpression() - if b.inStrictMode && ast.IsLeftHandSideExpression(expr.Left) && ast.IsAssignmentOperator(expr.OperatorToken.Kind) { - // ECMA 262 (Annex C) The identifier eval or arguments may not appear as the LeftHandSideExpression of an - // Assignment operator(11.13) or of a PostfixExpression(11.3) - b.checkStrictModeEvalOrArguments(node, expr.Left) - } -} - -func (b *Binder) checkStrictModeCatchClause(node *ast.Node) { - // It is a SyntaxError if a TryStatement with a Catch occurs within strict code and the Identifier of the - // Catch production is eval or arguments - clause := node.AsCatchClause() - if b.inStrictMode && clause.VariableDeclaration != nil { - b.checkStrictModeEvalOrArguments(node, clause.VariableDeclaration.AsVariableDeclaration().Name()) - } -} - -func (b *Binder) checkStrictModeDeleteExpression(node *ast.Node) { - // Grammar checking - expr := node.AsDeleteExpression() - if b.inStrictMode && expr.Expression.Kind == ast.KindIdentifier { - // When a delete operator occurs within strict mode code, a SyntaxError is thrown if its - // UnaryExpression is a direct reference to a variable, function argument, or function name - b.errorOnNode(expr.Expression, diagnostics.X_delete_cannot_be_called_on_an_identifier_in_strict_mode) - } -} - -func (b *Binder) checkStrictModePostfixUnaryExpression(node *ast.Node) { - // Grammar checking - // The identifier eval or arguments may not appear as the LeftHandSideExpression of an - // Assignment operator(11.13) or of a PostfixExpression(11.3) or as the UnaryExpression - // operated upon by a Prefix Increment(11.4.4) or a Prefix Decrement(11.4.5) operator. - if b.inStrictMode { - b.checkStrictModeEvalOrArguments(node, node.AsPostfixUnaryExpression().Operand) - } -} - -func (b *Binder) checkStrictModePrefixUnaryExpression(node *ast.Node) { - // Grammar checking - if b.inStrictMode { - expr := node.AsPrefixUnaryExpression() - if expr.Operator == ast.KindPlusPlusToken || expr.Operator == ast.KindMinusMinusToken { - b.checkStrictModeEvalOrArguments(node, expr.Operand) - } - } -} - -func (b *Binder) checkStrictModeWithStatement(node *ast.Node) { - // Grammar checking for withStatement - if b.inStrictMode { - b.errorOnFirstToken(node, diagnostics.X_with_statements_are_not_allowed_in_strict_mode) - } -} - -func (b *Binder) checkStrictModeLabeledStatement(node *ast.Node) { - // Grammar checking for labeledStatement - if b.inStrictMode { - data := node.AsLabeledStatement() - if ast.IsDeclarationStatement(data.Statement) || ast.IsVariableStatement(data.Statement) { - b.errorOnFirstToken(data.Label, diagnostics.A_label_is_not_allowed_here) - } - } -} - -func isEvalOrArgumentsIdentifier(node *ast.Node) bool { - if ast.IsIdentifier(node) { - text := node.AsIdentifier().Text - return text == "eval" || text == "arguments" - } - return false -} - -func (b *Binder) checkStrictModeEvalOrArguments(contextNode *ast.Node, name *ast.Node) { - if name != nil && isEvalOrArgumentsIdentifier(name) { - // We check first if the name is inside class declaration or class expression; if so give explicit message - // otherwise report generic error message. - b.errorOnNode(name, b.getStrictModeEvalOrArgumentsMessage(contextNode), name.AsIdentifier().Text) - } -} - -func (b *Binder) getStrictModeEvalOrArgumentsMessage(node *ast.Node) *diagnostics.Message { - // Provide specialized messages to help the user understand why we think they're in strict mode - if ast.GetContainingClass(node) != nil { - 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 - } - if b.file.ExternalModuleIndicator != nil { - return diagnostics.Invalid_use_of_0_Modules_are_automatically_in_strict_mode - } - return diagnostics.Invalid_use_of_0_in_strict_mode -} - -// All container nodes are kept on a linked list in declaration order. This list is used by -// the getLocalNameOfContainer function in the type checker to validate that the local name -// used for a container is unique. -func (b *Binder) bindContainer(node *ast.Node, containerFlags ContainerFlags) { - // Before we recurse into a node's children, we first save the existing parent, container - // and block-container. Then after we pop out of processing the children, we restore - // these saved values. - saveContainer := b.container - saveThisContainer := b.thisContainer - savedBlockScopeContainer := b.blockScopeContainer - // Depending on what kind of node this is, we may have to adjust the current container - // and block-container. If the current node is a container, then it is automatically - // considered the current block-container as well. Also, for containers that we know - // may contain locals, we eagerly initialize the .locals field. We do this because - // it's highly likely that the .locals will be needed to place some child in (for example, - // a parameter, or variable declaration). - // - // However, we do not proactively create the .locals for block-containers because it's - // totally normal and common for block-containers to never actually have a block-scoped - // variable in them. We don't want to end up allocating an object for every 'block' we - // run into when most of them won't be necessary. - // - // Finally, if this is a block-container, then we clear out any existing .locals object - // it may contain within it. This happens in incremental scenarios. Because we can be - // reusing a node from a previous compilation, that node may have had 'locals' created - // for it. We must clear this so we don't accidentally move any stale data forward from - // a previous compilation. - if containerFlags&ContainerFlagsIsContainer != 0 { - b.container = node - b.blockScopeContainer = node - if containerFlags&ContainerFlagsHasLocals != 0 { - // localsContainer := node - // localsContainer.LocalsContainerData().locals = make(SymbolTable) - b.addToContainerChain(node) - } - } else if containerFlags&ContainerFlagsIsBlockScopedContainer != 0 { - b.blockScopeContainer = node - b.addToContainerChain(node) - } - if containerFlags&ContainerFlagsIsThisContainer != 0 { - b.thisContainer = node - } - if containerFlags&ContainerFlagsIsControlFlowContainer != 0 { - saveCurrentFlow := b.currentFlow - saveBreakTarget := b.currentBreakTarget - saveContinueTarget := b.currentContinueTarget - saveReturnTarget := b.currentReturnTarget - saveExceptionTarget := b.currentExceptionTarget - saveActiveLabelList := b.activeLabelList - saveHasExplicitReturn := b.hasExplicitReturn - isImmediatelyInvoked := (containerFlags&ContainerFlagsIsFunctionExpression != 0 && - !ast.HasSyntacticModifier(node, ast.ModifierFlagsAsync) && - !isGeneratorFunctionExpression(node) && - ast.GetImmediatelyInvokedFunctionExpression(node) != nil) || node.Kind == ast.KindClassStaticBlockDeclaration - // A non-async, non-generator IIFE is considered part of the containing control flow. Return statements behave - // similarly to break statements that exit to a label just past the statement body. - if !isImmediatelyInvoked { - flowStart := b.newFlowNode(ast.FlowFlagsStart) - b.currentFlow = flowStart - if containerFlags&(ContainerFlagsIsFunctionExpression|ContainerFlagsIsObjectLiteralOrClassExpressionMethodOrAccessor) != 0 { - flowStart.Node = node - } - } - // We create a return control flow graph for IIFEs and constructors. For constructors - // we use the return control flow graph in strict property initialization checks. - if isImmediatelyInvoked || node.Kind == ast.KindConstructor { - b.currentReturnTarget = b.newFlowNode(ast.FlowFlagsBranchLabel) - } else { - b.currentReturnTarget = nil - } - b.currentExceptionTarget = nil - b.currentBreakTarget = nil - b.currentContinueTarget = nil - b.activeLabelList = nil - b.hasExplicitReturn = false - b.bindChildren(node) - // Reset all reachability check related flags on node (for incremental scenarios) - node.Flags &= ^ast.NodeFlagsReachabilityCheckFlags - if b.currentFlow.Flags&ast.FlowFlagsUnreachable == 0 && containerFlags&ContainerFlagsIsFunctionLike != 0 { - bodyData := node.BodyData() - if bodyData != nil && ast.NodeIsPresent(bodyData.Body) { - node.Flags |= ast.NodeFlagsHasImplicitReturn - if b.hasExplicitReturn { - node.Flags |= ast.NodeFlagsHasExplicitReturn - } - bodyData.EndFlowNode = b.currentFlow - } - } - if node.Kind == ast.KindSourceFile { - node.Flags |= b.emitFlags - node.AsSourceFile().EndFlowNode = b.currentFlow - } - - if b.currentReturnTarget != nil { - b.addAntecedent(b.currentReturnTarget, b.currentFlow) - b.currentFlow = b.finishFlowLabel(b.currentReturnTarget) - if node.Kind == ast.KindConstructor || node.Kind == ast.KindClassStaticBlockDeclaration { - setReturnFlowNode(node, b.currentFlow) - } - } - if !isImmediatelyInvoked { - b.currentFlow = saveCurrentFlow - } - b.currentBreakTarget = saveBreakTarget - b.currentContinueTarget = saveContinueTarget - b.currentReturnTarget = saveReturnTarget - b.currentExceptionTarget = saveExceptionTarget - b.activeLabelList = saveActiveLabelList - b.hasExplicitReturn = saveHasExplicitReturn - } else if containerFlags&ContainerFlagsIsInterface != 0 { - b.seenThisKeyword = false - b.bindChildren(node) - // ContainsThis cannot overlap with HasExtendedUnicodeEscape on Identifier - if b.seenThisKeyword { - node.Flags |= ast.NodeFlagsContainsThis - } else { - node.Flags &= ^ast.NodeFlagsContainsThis - } - } else { - b.bindChildren(node) - } - b.container = saveContainer - b.thisContainer = saveThisContainer - b.blockScopeContainer = savedBlockScopeContainer -} - -func (b *Binder) bindChildren(node *ast.Node) { - saveInAssignmentPattern := b.inAssignmentPattern - // Most nodes aren't valid in an assignment pattern, so we clear the value here - // and set it before we descend into nodes that could actually be part of an assignment pattern. - b.inAssignmentPattern = false - if b.checkUnreachable(node) { - b.bindEachChild(node) - b.inAssignmentPattern = saveInAssignmentPattern - return - } - kind := node.Kind - if kind >= ast.KindFirstStatement && kind <= ast.KindLastStatement && (b.options().AllowUnreachableCode != core.TSTrue || kind == ast.KindReturnStatement) { - hasFlowNodeData := node.FlowNodeData() - if hasFlowNodeData != nil { - hasFlowNodeData.FlowNode = b.currentFlow - } - } - switch node.Kind { - case ast.KindWhileStatement: - b.bindWhileStatement(node) - case ast.KindDoStatement: - b.bindDoStatement(node) - case ast.KindForStatement: - b.bindForStatement(node) - case ast.KindForInStatement, ast.KindForOfStatement: - b.bindForInOrForOfStatement(node) - case ast.KindIfStatement: - b.bindIfStatement(node) - case ast.KindReturnStatement: - b.bindReturnStatement(node) - case ast.KindThrowStatement: - b.bindThrowStatement(node) - case ast.KindBreakStatement: - b.bindBreakStatement(node) - case ast.KindContinueStatement: - b.bindContinueStatement(node) - case ast.KindTryStatement: - b.bindTryStatement(node) - case ast.KindSwitchStatement: - b.bindSwitchStatement(node) - case ast.KindCaseBlock: - b.bindCaseBlock(node) - case ast.KindCaseClause, ast.KindDefaultClause: - b.bindCaseOrDefaultClause(node) - case ast.KindExpressionStatement: - b.bindExpressionStatement(node) - case ast.KindLabeledStatement: - b.bindLabeledStatement(node) - case ast.KindPrefixUnaryExpression: - b.bindPrefixUnaryExpressionFlow(node) - case ast.KindPostfixUnaryExpression: - b.bindPostfixUnaryExpressionFlow(node) - case ast.KindBinaryExpression: - if ast.IsDestructuringAssignment(node) { - // Carry over whether we are in an assignment pattern to - // binary expressions that could actually be an initializer - b.inAssignmentPattern = saveInAssignmentPattern - b.bindDestructuringAssignmentFlow(node) - return - } - b.bindBinaryExpressionFlow(node) - case ast.KindDeleteExpression: - b.bindDeleteExpressionFlow(node) - case ast.KindConditionalExpression: - b.bindConditionalExpressionFlow(node) - case ast.KindVariableDeclaration: - b.bindVariableDeclarationFlow(node) - case ast.KindPropertyAccessExpression, ast.KindElementAccessExpression: - b.bindAccessExpressionFlow(node) - case ast.KindCallExpression: - b.bindCallExpressionFlow(node) - case ast.KindNonNullExpression: - b.bindNonNullExpressionFlow(node) - case ast.KindSourceFile: - sourceFile := node.AsSourceFile() - b.bindEachStatementFunctionsFirst(sourceFile.Statements) - b.bind(sourceFile.EndOfFileToken) - case ast.KindBlock: - b.bindEachStatementFunctionsFirst(node.AsBlock().Statements) - case ast.KindModuleBlock: - b.bindEachStatementFunctionsFirst(node.AsModuleBlock().Statements) - case ast.KindBindingElement: - b.bindBindingElementFlow(node) - case ast.KindParameter: - b.bindParameterFlow(node) - case ast.KindObjectLiteralExpression, ast.KindArrayLiteralExpression, ast.KindPropertyAssignment, ast.KindSpreadElement: - b.inAssignmentPattern = saveInAssignmentPattern - b.bindEachChild(node) - default: - b.bindEachChild(node) - } - b.inAssignmentPattern = saveInAssignmentPattern -} - -func (b *Binder) bindEachChild(node *ast.Node) { - node.ForEachChild(b.bindFunc) -} - -func (b *Binder) bindEach(nodes []*ast.Node) { - for _, node := range nodes { - b.bind(node) - } -} - -func (b *Binder) bindNodeList(nodeList *ast.NodeList) { - if nodeList != nil { - b.bindEach(nodeList.Nodes) - } -} - -func (b *Binder) bindModifiers(modifiers *ast.ModifierList) { - if modifiers != nil { - b.bindEach(modifiers.Nodes) - } -} - -func (b *Binder) bindEachStatementFunctionsFirst(statements *ast.NodeList) { - for _, node := range statements.Nodes { - if node.Kind == ast.KindFunctionDeclaration { - b.bind(node) - } - } - for _, node := range statements.Nodes { - if node.Kind != ast.KindFunctionDeclaration { - b.bind(node) - } - } -} - -func (b *Binder) checkUnreachable(node *ast.Node) bool { - if b.currentFlow.Flags&ast.FlowFlagsUnreachable == 0 { - return false - } - if b.currentFlow == b.unreachableFlow { - // report errors on all statements except empty ones - // report errors on class declarations - // report errors on enums with preserved emit - // report errors on instantiated modules - reportError := ast.IsStatementButNotDeclaration(node) && !ast.IsEmptyStatement(node) || - ast.IsClassDeclaration(node) || - isEnumDeclarationWithPreservedEmit(node, b.options()) || - ast.IsModuleDeclaration(node) && b.shouldReportErrorOnModuleDeclaration(node) - if reportError { - b.currentFlow = b.reportedUnreachableFlow - if b.options().AllowUnreachableCode != core.TSTrue { - // unreachable code is reported if - // - user has explicitly asked about it AND - // - statement is in not ambient context (statements in ambient context is already an error - // so we should not report extras) AND - // - node is not variable statement OR - // - node is block scoped variable statement OR - // - node is not block scoped variable statement and at least one variable declaration has initializer - // Rationale: we don't want to report errors on non-initialized var's since they are hoisted - // On the other side we do want to report errors on non-initialized 'lets' because of TDZ - isError := unreachableCodeIsError(b.options()) && node.Flags&ast.NodeFlagsAmbient == 0 && (!ast.IsVariableStatement(node) || - ast.GetCombinedNodeFlags(node.AsVariableStatement().DeclarationList)&ast.NodeFlagsBlockScoped != 0 || - core.Some(node.AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes, func(d *ast.Node) bool { - return d.AsVariableDeclaration().Initializer != nil - })) - b.errorOnEachUnreachableRange(node, isError) - } - } - } - return true -} - -func (b *Binder) shouldReportErrorOnModuleDeclaration(node *ast.Node) bool { - instanceState := ast.GetModuleInstanceState(node) - return instanceState == ast.ModuleInstanceStateInstantiated || (instanceState == ast.ModuleInstanceStateConstEnumOnly && b.options().ShouldPreserveConstEnums) -} - -func (b *Binder) errorOnEachUnreachableRange(node *ast.Node, isError bool) { - if b.isExecutableStatement(node) && ast.IsBlock(node.Parent) { - statements := node.Parent.AsBlock().Statements.Nodes - index := slices.Index(statements, node) - var first, last *ast.Node - for _, s := range statements[index:] { - if b.isExecutableStatement(s) { - if first == nil { - first = s - } - last = s - } else if first != nil { - b.errorOrSuggestionOnRange(isError, first, last, diagnostics.Unreachable_code_detected) - first = nil - } - } - if first != nil { - b.errorOrSuggestionOnRange(isError, first, last, diagnostics.Unreachable_code_detected) - } - } else { - b.errorOrSuggestionOnNode(isError, node, diagnostics.Unreachable_code_detected) - } -} - -// As opposed to a pure declaration like an `interface` -func (b *Binder) isExecutableStatement(s *ast.Node) bool { - // Don't remove statements that can validly be used before they appear. - return !ast.IsFunctionDeclaration(s) && !b.isPurelyTypeDeclaration(s) && !(ast.IsVariableStatement(s) && ast.GetCombinedNodeFlags(s)&ast.NodeFlagsBlockScoped == 0 && - core.Some(s.AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes, func(d *ast.Node) bool { - return d.AsVariableDeclaration().Initializer == nil - })) -} - -func (b *Binder) isPurelyTypeDeclaration(s *ast.Node) bool { - switch s.Kind { - case ast.KindInterfaceDeclaration, ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration: - return true - case ast.KindModuleDeclaration: - return ast.GetModuleInstanceState(s) != ast.ModuleInstanceStateInstantiated - case ast.KindEnumDeclaration: - return !isEnumDeclarationWithPreservedEmit(s, b.options()) - default: - return false - } -} - -func (b *Binder) setContinueTarget(node *ast.Node, target *ast.FlowLabel) *ast.FlowLabel { - label := b.activeLabelList - for label != nil && node.Parent.Kind == ast.KindLabeledStatement { - label.continueTarget = target - label = label.next - node = node.Parent - } - return target -} - -func (b *Binder) doWithConditionalBranches(action func(b *Binder, value *ast.Node) bool, value *ast.Node, trueTarget *ast.FlowLabel, falseTarget *ast.FlowLabel) { - savedTrueTarget := b.currentTrueTarget - savedFalseTarget := b.currentFalseTarget - b.currentTrueTarget = trueTarget - b.currentFalseTarget = falseTarget - action(b, value) - b.currentTrueTarget = savedTrueTarget - b.currentFalseTarget = savedFalseTarget -} - -func (b *Binder) bindCondition(node *ast.Node, trueTarget *ast.FlowLabel, falseTarget *ast.FlowLabel) { - b.doWithConditionalBranches((*Binder).bind, node, trueTarget, falseTarget) - if node == nil || !isLogicalAssignmentExpression(node) && !ast.IsLogicalExpression(node) && !(ast.IsOptionalChain(node) && ast.IsOutermostOptionalChain(node)) { - b.addAntecedent(trueTarget, b.createFlowCondition(ast.FlowFlagsTrueCondition, b.currentFlow, node)) - b.addAntecedent(falseTarget, b.createFlowCondition(ast.FlowFlagsFalseCondition, b.currentFlow, node)) - } -} - -func (b *Binder) bindIterativeStatement(node *ast.Node, breakTarget *ast.FlowLabel, continueTarget *ast.FlowLabel) { - saveBreakTarget := b.currentBreakTarget - saveContinueTarget := b.currentContinueTarget - b.currentBreakTarget = breakTarget - b.currentContinueTarget = continueTarget - b.bind(node) - b.currentBreakTarget = saveBreakTarget - b.currentContinueTarget = saveContinueTarget -} - -func isLogicalAssignmentExpression(node *ast.Node) bool { - return ast.IsLogicalOrCoalescingAssignmentExpression(ast.SkipParentheses(node)) -} - -func (b *Binder) bindAssignmentTargetFlow(node *ast.Node) { - switch node.Kind { - case ast.KindArrayLiteralExpression: - for _, e := range node.AsArrayLiteralExpression().Elements.Nodes { - if e.Kind == ast.KindSpreadElement { - b.bindAssignmentTargetFlow(e.AsSpreadElement().Expression) - } else { - b.bindDestructuringTargetFlow(e) - } - } - case ast.KindObjectLiteralExpression: - for _, p := range node.AsObjectLiteralExpression().Properties.Nodes { - switch p.Kind { - case ast.KindPropertyAssignment: - b.bindDestructuringTargetFlow(p.AsPropertyAssignment().Initializer) - case ast.KindShorthandPropertyAssignment: - b.bindAssignmentTargetFlow(p.AsShorthandPropertyAssignment().Name()) - case ast.KindSpreadAssignment: - b.bindAssignmentTargetFlow(p.AsSpreadAssignment().Expression) - } - } - default: - if isNarrowableReference(node) { - b.currentFlow = b.createFlowMutation(ast.FlowFlagsAssignment, b.currentFlow, node) - } - } -} - -func (b *Binder) bindDestructuringTargetFlow(node *ast.Node) { - if ast.IsBinaryExpression(node) && node.AsBinaryExpression().OperatorToken.Kind == ast.KindEqualsToken { - b.bindAssignmentTargetFlow(node.AsBinaryExpression().Left) - } else { - b.bindAssignmentTargetFlow(node) - } -} - -func (b *Binder) bindWhileStatement(node *ast.Node) { - stmt := node.AsWhileStatement() - preWhileLabel := b.setContinueTarget(node, b.createLoopLabel()) - preBodyLabel := b.createBranchLabel() - postWhileLabel := b.createBranchLabel() - b.addAntecedent(preWhileLabel, b.currentFlow) - b.currentFlow = preWhileLabel - b.bindCondition(stmt.Expression, preBodyLabel, postWhileLabel) - b.currentFlow = b.finishFlowLabel(preBodyLabel) - b.bindIterativeStatement(stmt.Statement, postWhileLabel, preWhileLabel) - b.addAntecedent(preWhileLabel, b.currentFlow) - b.currentFlow = b.finishFlowLabel(postWhileLabel) -} - -func (b *Binder) bindDoStatement(node *ast.Node) { - stmt := node.AsDoStatement() - preDoLabel := b.createLoopLabel() - preConditionLabel := b.setContinueTarget(node, b.createBranchLabel()) - postDoLabel := b.createBranchLabel() - b.addAntecedent(preDoLabel, b.currentFlow) - b.currentFlow = preDoLabel - b.bindIterativeStatement(stmt.Statement, postDoLabel, preConditionLabel) - b.addAntecedent(preConditionLabel, b.currentFlow) - b.currentFlow = b.finishFlowLabel(preConditionLabel) - b.bindCondition(stmt.Expression, preDoLabel, postDoLabel) - b.currentFlow = b.finishFlowLabel(postDoLabel) -} - -func (b *Binder) bindForStatement(node *ast.Node) { - stmt := node.AsForStatement() - preLoopLabel := b.setContinueTarget(node, b.createLoopLabel()) - preBodyLabel := b.createBranchLabel() - preIncrementorLabel := b.createBranchLabel() - postLoopLabel := b.createBranchLabel() - b.bind(stmt.Initializer) - b.addAntecedent(preLoopLabel, b.currentFlow) - b.currentFlow = preLoopLabel - b.bindCondition(stmt.Condition, preBodyLabel, postLoopLabel) - b.currentFlow = b.finishFlowLabel(preBodyLabel) - b.bindIterativeStatement(stmt.Statement, postLoopLabel, preIncrementorLabel) - b.addAntecedent(preIncrementorLabel, b.currentFlow) - b.currentFlow = b.finishFlowLabel(preIncrementorLabel) - b.bind(stmt.Incrementor) - b.addAntecedent(preLoopLabel, b.currentFlow) - b.currentFlow = b.finishFlowLabel(postLoopLabel) -} - -func (b *Binder) bindForInOrForOfStatement(node *ast.Node) { - stmt := node.AsForInOrOfStatement() - preLoopLabel := b.setContinueTarget(node, b.createLoopLabel()) - postLoopLabel := b.createBranchLabel() - b.bind(stmt.Expression) - b.addAntecedent(preLoopLabel, b.currentFlow) - b.currentFlow = preLoopLabel - if node.Kind == ast.KindForOfStatement { - b.bind(stmt.AwaitModifier) - } - b.addAntecedent(postLoopLabel, b.currentFlow) - b.bind(stmt.Initializer) - if stmt.Initializer.Kind != ast.KindVariableDeclarationList { - b.bindAssignmentTargetFlow(stmt.Initializer) - } - b.bindIterativeStatement(stmt.Statement, postLoopLabel, preLoopLabel) - b.addAntecedent(preLoopLabel, b.currentFlow) - b.currentFlow = b.finishFlowLabel(postLoopLabel) -} - -func (b *Binder) bindIfStatement(node *ast.Node) { - stmt := node.AsIfStatement() - thenLabel := b.createBranchLabel() - elseLabel := b.createBranchLabel() - postIfLabel := b.createBranchLabel() - b.bindCondition(stmt.Expression, thenLabel, elseLabel) - b.currentFlow = b.finishFlowLabel(thenLabel) - b.bind(stmt.ThenStatement) - b.addAntecedent(postIfLabel, b.currentFlow) - b.currentFlow = b.finishFlowLabel(elseLabel) - b.bind(stmt.ElseStatement) - b.addAntecedent(postIfLabel, b.currentFlow) - b.currentFlow = b.finishFlowLabel(postIfLabel) -} - -func (b *Binder) bindReturnStatement(node *ast.Node) { - b.bind(node.AsReturnStatement().Expression) - if b.currentReturnTarget != nil { - b.addAntecedent(b.currentReturnTarget, b.currentFlow) - } - b.currentFlow = b.unreachableFlow - b.hasExplicitReturn = true - b.hasFlowEffects = true -} - -func (b *Binder) bindThrowStatement(node *ast.Node) { - b.bind(node.AsThrowStatement().Expression) - b.currentFlow = b.unreachableFlow - b.hasFlowEffects = true -} - -func (b *Binder) bindBreakStatement(node *ast.Node) { - b.bindBreakOrContinueStatement(node.AsBreakStatement().Label, b.currentBreakTarget, (*ActiveLabel).BreakTarget) -} - -func (b *Binder) bindContinueStatement(node *ast.Node) { - b.bindBreakOrContinueStatement(node.AsContinueStatement().Label, b.currentContinueTarget, (*ActiveLabel).ContinueTarget) -} - -func (b *Binder) bindBreakOrContinueStatement(label *ast.Node, currentTarget *ast.FlowNode, getTarget func(*ActiveLabel) *ast.FlowNode) { - b.bind(label) - if label != nil { - activeLabel := b.findActiveLabel(label.AsIdentifier().Text) - if activeLabel != nil { - activeLabel.referenced = true - b.bindBreakOrContinueFlow(getTarget(activeLabel)) - } - } else { - b.bindBreakOrContinueFlow(currentTarget) - } -} - -func (b *Binder) findActiveLabel(name string) *ActiveLabel { - for label := b.activeLabelList; label != nil; label = label.next { - if label.name == name { - return label - } - } - return nil -} - -func (b *Binder) bindBreakOrContinueFlow(flowLabel *ast.FlowLabel) { - if flowLabel != nil { - b.addAntecedent(flowLabel, b.currentFlow) - b.currentFlow = b.unreachableFlow - b.hasFlowEffects = true - } -} - -func (b *Binder) bindTryStatement(node *ast.Node) { - // We conservatively assume that *any* code in the try block can cause an exception, but we only need - // to track code that causes mutations (because only mutations widen the possible control flow type of - // a variable). The exceptionLabel is the target label for control flows that result from exceptions. - // We add all mutation flow nodes as antecedents of this label such that we can analyze them as possible - // antecedents of the start of catch or finally blocks. Furthermore, we add the current control flow to - // represent exceptions that occur before any mutations. - stmt := node.AsTryStatement() - saveReturnTarget := b.currentReturnTarget - saveExceptionTarget := b.currentExceptionTarget - normalExitLabel := b.createBranchLabel() - returnLabel := b.createBranchLabel() - exceptionLabel := b.createBranchLabel() - if stmt.FinallyBlock != nil { - b.currentReturnTarget = returnLabel - } - b.addAntecedent(exceptionLabel, b.currentFlow) - b.currentExceptionTarget = exceptionLabel - b.bind(stmt.TryBlock) - b.addAntecedent(normalExitLabel, b.currentFlow) - if stmt.CatchClause != nil { - // Start of catch clause is the target of exceptions from try block. - b.currentFlow = b.finishFlowLabel(exceptionLabel) - // The currentExceptionTarget now represents control flows from exceptions in the catch clause. - // Effectively, in a try-catch-finally, if an exception occurs in the try block, the catch block - // acts like a second try block. - exceptionLabel = b.createBranchLabel() - b.addAntecedent(exceptionLabel, b.currentFlow) - b.currentExceptionTarget = exceptionLabel - b.bind(stmt.CatchClause) - b.addAntecedent(normalExitLabel, b.currentFlow) - } - b.currentReturnTarget = saveReturnTarget - b.currentExceptionTarget = saveExceptionTarget - if stmt.FinallyBlock != nil { - // Possible ways control can reach the finally block: - // 1) Normal completion of try block of a try-finally or try-catch-finally - // 2) Normal completion of catch block (following exception in try block) of a try-catch-finally - // 3) Return in try or catch block of a try-finally or try-catch-finally - // 4) Exception in try block of a try-finally - // 5) Exception in catch block of a try-catch-finally - // When analyzing a control flow graph that starts inside a finally block we want to consider all - // five possibilities above. However, when analyzing a control flow graph that starts outside (past) - // the finally block, we only want to consider the first two (if we're past a finally block then it - // must have completed normally). Likewise, when analyzing a control flow graph from return statements - // in try or catch blocks in an IIFE, we only want to consider the third. To make this possible, we - // inject a ReduceLabel node into the control flow graph. This node contains an alternate reduced - // set of antecedents for the pre-finally label. As control flow analysis passes by a ReduceLabel - // node, the pre-finally label is temporarily switched to the reduced antecedent set. - finallyLabel := b.createBranchLabel() - finallyLabel.Antecedents = b.combineFlowLists(normalExitLabel.Antecedents, b.combineFlowLists(exceptionLabel.Antecedents, returnLabel.Antecedents)) - b.currentFlow = finallyLabel - b.bind(stmt.FinallyBlock) - if b.currentFlow.Flags&ast.FlowFlagsUnreachable != 0 { - // If the end of the finally block is unreachable, the end of the entire try statement is unreachable. - b.currentFlow = b.unreachableFlow - } else { - // If we have an IIFE return target and return statements in the try or catch blocks, add a control - // flow that goes back through the finally block and back through only the return statements. - if b.currentReturnTarget != nil && returnLabel.Antecedents != nil { - b.addAntecedent(b.currentReturnTarget, b.createReduceLabel(finallyLabel, returnLabel.Antecedents, b.currentFlow)) - } - // If we have an outer exception target (i.e. a containing try-finally or try-catch-finally), add a - // control flow that goes back through the finally block and back through each possible exception source. - if b.currentExceptionTarget != nil && exceptionLabel.Antecedents != nil { - b.addAntecedent(b.currentExceptionTarget, b.createReduceLabel(finallyLabel, exceptionLabel.Antecedents, b.currentFlow)) - } - // If the end of the finally block is reachable, but the end of the try and catch blocks are not, - // convert the current flow to unreachable. For example, 'try { return 1; } finally { ... }' should - // result in an unreachable current control flow. - if normalExitLabel.Antecedents != nil { - b.currentFlow = b.createReduceLabel(finallyLabel, normalExitLabel.Antecedents, b.currentFlow) - } else { - b.currentFlow = b.unreachableFlow - } - } - } else { - b.currentFlow = b.finishFlowLabel(normalExitLabel) - } -} - -func (b *Binder) bindSwitchStatement(node *ast.Node) { - stmt := node.AsSwitchStatement() - postSwitchLabel := b.createBranchLabel() - b.bind(stmt.Expression) - saveBreakTarget := b.currentBreakTarget - savePreSwitchCaseFlow := b.preSwitchCaseFlow - b.currentBreakTarget = postSwitchLabel - b.preSwitchCaseFlow = b.currentFlow - b.bind(stmt.CaseBlock) - b.addAntecedent(postSwitchLabel, b.currentFlow) - hasDefault := core.Some(stmt.CaseBlock.AsCaseBlock().Clauses.Nodes, func(c *ast.Node) bool { - return c.Kind == ast.KindDefaultClause - }) - if !hasDefault { - b.addAntecedent(postSwitchLabel, b.createFlowSwitchClause(b.preSwitchCaseFlow, node, 0, 0)) - } - b.currentBreakTarget = saveBreakTarget - b.preSwitchCaseFlow = savePreSwitchCaseFlow - b.currentFlow = b.finishFlowLabel(postSwitchLabel) -} - -func (b *Binder) bindCaseBlock(node *ast.Node) { - switchStatement := node.Parent - clauses := node.AsCaseBlock().Clauses.Nodes - isNarrowingSwitch := switchStatement.Expression().Kind == ast.KindTrueKeyword || isNarrowingExpression(switchStatement.Expression()) - var fallthroughFlow *ast.FlowNode = b.unreachableFlow - for i := 0; i < len(clauses); i++ { - clauseStart := i - for len(clauses[i].AsCaseOrDefaultClause().Statements.Nodes) == 0 && i+1 < len(clauses) { - if fallthroughFlow == b.unreachableFlow { - b.currentFlow = b.preSwitchCaseFlow - } - b.bind(clauses[i]) - i++ - } - preCaseLabel := b.createBranchLabel() - preCaseFlow := b.preSwitchCaseFlow - if isNarrowingSwitch { - preCaseFlow = b.createFlowSwitchClause(b.preSwitchCaseFlow, switchStatement, clauseStart, i+1) - } - b.addAntecedent(preCaseLabel, preCaseFlow) - b.addAntecedent(preCaseLabel, fallthroughFlow) - b.currentFlow = b.finishFlowLabel(preCaseLabel) - clause := clauses[i] - b.bind(clause) - fallthroughFlow = b.currentFlow - if b.currentFlow.Flags&ast.FlowFlagsUnreachable == 0 && i != len(clauses)-1 { - clause.AsCaseOrDefaultClause().FallthroughFlowNode = b.currentFlow - } - } -} - -func (b *Binder) bindCaseOrDefaultClause(node *ast.Node) { - clause := node.AsCaseOrDefaultClause() - if clause.Expression != nil { - saveCurrentFlow := b.currentFlow - b.currentFlow = b.preSwitchCaseFlow - b.bind(clause.Expression) - b.currentFlow = saveCurrentFlow - } - b.bindEach(clause.Statements.Nodes) -} - -func (b *Binder) bindExpressionStatement(node *ast.Node) { - stmt := node.AsExpressionStatement() - b.bind(stmt.Expression) - b.maybeBindExpressionFlowIfCall(stmt.Expression) -} - -func (b *Binder) maybeBindExpressionFlowIfCall(node *ast.Node) { - // A top level or comma expression call expression with a dotted function name and at least one argument - // is potentially an assertion and is therefore included in the control flow. - if ast.IsCallExpression(node) { - if node.Expression().Kind != ast.KindSuperKeyword && ast.IsDottedName(node.Expression()) { - b.currentFlow = b.createFlowCall(b.currentFlow, node) - } - } -} - -func (b *Binder) bindLabeledStatement(node *ast.Node) { - stmt := node.AsLabeledStatement() - postStatementLabel := b.createBranchLabel() - b.activeLabelList = &ActiveLabel{ - next: b.activeLabelList, - name: stmt.Label.AsIdentifier().Text, - breakTarget: postStatementLabel, - continueTarget: nil, - referenced: false, - } - b.bind(stmt.Label) - b.bind(stmt.Statement) - if !b.activeLabelList.referenced && b.options().AllowUnusedLabels != core.TSTrue { - b.errorOrSuggestionOnNode(unusedLabelIsError(b.options()), stmt.Label, diagnostics.Unused_label) - } - b.activeLabelList = b.activeLabelList.next - b.addAntecedent(postStatementLabel, b.currentFlow) - b.currentFlow = b.finishFlowLabel(postStatementLabel) -} - -func (b *Binder) bindPrefixUnaryExpressionFlow(node *ast.Node) { - expr := node.AsPrefixUnaryExpression() - if expr.Operator == ast.KindExclamationToken { - saveTrueTarget := b.currentTrueTarget - b.currentTrueTarget = b.currentFalseTarget - b.currentFalseTarget = saveTrueTarget - b.bindEachChild(node) - b.currentFalseTarget = b.currentTrueTarget - b.currentTrueTarget = saveTrueTarget - } else { - b.bindEachChild(node) - if expr.Operator == ast.KindPlusPlusToken || expr.Operator == ast.KindMinusMinusToken { - b.bindAssignmentTargetFlow(expr.Operand) - } - } -} - -func (b *Binder) bindPostfixUnaryExpressionFlow(node *ast.Node) { - expr := node.AsPostfixUnaryExpression() - b.bindEachChild(node) - if expr.Operator == ast.KindPlusPlusToken || expr.Operator == ast.KindMinusMinusToken { - b.bindAssignmentTargetFlow(expr.Operand) - } -} - -func (b *Binder) bindDestructuringAssignmentFlow(node *ast.Node) { - expr := node.AsBinaryExpression() - if b.inAssignmentPattern { - b.inAssignmentPattern = false - b.bind(expr.OperatorToken) - b.bind(expr.Right) - b.inAssignmentPattern = true - b.bind(expr.Left) - b.bind(expr.Type) - } else { - b.inAssignmentPattern = true - b.bind(expr.Left) - b.bind(expr.Type) - b.inAssignmentPattern = false - b.bind(expr.OperatorToken) - b.bind(expr.Right) - } - b.bindAssignmentTargetFlow(expr.Left) -} - -func (b *Binder) bindBinaryExpressionFlow(node *ast.Node) { - expr := node.AsBinaryExpression() - operator := expr.OperatorToken.Kind - if ast.IsLogicalOrCoalescingBinaryOperator(operator) || ast.IsLogicalOrCoalescingAssignmentOperator(operator) { - if isTopLevelLogicalExpression(node) { - postExpressionLabel := b.createBranchLabel() - saveCurrentFlow := b.currentFlow - saveHasFlowEffects := b.hasFlowEffects - b.hasFlowEffects = false - b.bindLogicalLikeExpression(node, postExpressionLabel, postExpressionLabel) - if b.hasFlowEffects { - b.currentFlow = b.finishFlowLabel(postExpressionLabel) - } else { - b.currentFlow = saveCurrentFlow - } - b.hasFlowEffects = b.hasFlowEffects || saveHasFlowEffects - } else { - b.bindLogicalLikeExpression(node, b.currentTrueTarget, b.currentFalseTarget) - } - } else { - b.bind(expr.Left) - b.bind(expr.Type) - if operator == ast.KindCommaToken { - b.maybeBindExpressionFlowIfCall(expr.Left) - } - b.bind(expr.OperatorToken) - b.bind(expr.Right) - if operator == ast.KindCommaToken { - b.maybeBindExpressionFlowIfCall(expr.Right) - } - if ast.IsAssignmentOperator(operator) && !ast.IsAssignmentTarget(node) { - b.bindAssignmentTargetFlow(expr.Left) - if operator == ast.KindEqualsToken && expr.Left.Kind == ast.KindElementAccessExpression { - elementAccess := expr.Left.AsElementAccessExpression() - if isNarrowableOperand(elementAccess.Expression) { - b.currentFlow = b.createFlowMutation(ast.FlowFlagsArrayMutation, b.currentFlow, node) - } - } - } - } -} - -func (b *Binder) bindLogicalLikeExpression(node *ast.Node, trueTarget *ast.FlowLabel, falseTarget *ast.FlowLabel) { - expr := node.AsBinaryExpression() - preRightLabel := b.createBranchLabel() - if expr.OperatorToken.Kind == ast.KindAmpersandAmpersandToken || expr.OperatorToken.Kind == ast.KindAmpersandAmpersandEqualsToken { - b.bindCondition(expr.Left, preRightLabel, falseTarget) - } else { - b.bindCondition(expr.Left, trueTarget, preRightLabel) - } - b.currentFlow = b.finishFlowLabel(preRightLabel) - b.bind(expr.OperatorToken) - if ast.IsLogicalOrCoalescingAssignmentOperator(expr.OperatorToken.Kind) { - b.doWithConditionalBranches((*Binder).bind, expr.Right, trueTarget, falseTarget) - b.bindAssignmentTargetFlow(expr.Left) - b.addAntecedent(trueTarget, b.createFlowCondition(ast.FlowFlagsTrueCondition, b.currentFlow, node)) - b.addAntecedent(falseTarget, b.createFlowCondition(ast.FlowFlagsFalseCondition, b.currentFlow, node)) - } else { - b.bindCondition(expr.Right, trueTarget, falseTarget) - } -} - -func (b *Binder) bindDeleteExpressionFlow(node *ast.Node) { - expr := node.AsDeleteExpression() - b.bindEachChild(node) - if expr.Expression.Kind == ast.KindPropertyAccessExpression { - b.bindAssignmentTargetFlow(expr.Expression) - } -} - -func (b *Binder) bindConditionalExpressionFlow(node *ast.Node) { - expr := node.AsConditionalExpression() - trueLabel := b.createBranchLabel() - falseLabel := b.createBranchLabel() - postExpressionLabel := b.createBranchLabel() - saveCurrentFlow := b.currentFlow - saveHasFlowEffects := b.hasFlowEffects - b.hasFlowEffects = false - b.bindCondition(expr.Condition, trueLabel, falseLabel) - b.currentFlow = b.finishFlowLabel(trueLabel) - b.bind(expr.QuestionToken) - b.bind(expr.WhenTrue) - b.addAntecedent(postExpressionLabel, b.currentFlow) - b.currentFlow = b.finishFlowLabel(falseLabel) - b.bind(expr.ColonToken) - b.bind(expr.WhenFalse) - b.addAntecedent(postExpressionLabel, b.currentFlow) - if b.hasFlowEffects { - b.currentFlow = b.finishFlowLabel(postExpressionLabel) - } else { - b.currentFlow = saveCurrentFlow - } - b.hasFlowEffects = b.hasFlowEffects || saveHasFlowEffects -} - -func (b *Binder) bindVariableDeclarationFlow(node *ast.Node) { - b.bindEachChild(node) - if node.AsVariableDeclaration().Initializer != nil || ast.IsForInOrOfStatement(node.Parent.Parent) { - b.bindInitializedVariableFlow(node) - } -} - -func (b *Binder) bindInitializedVariableFlow(node *ast.Node) { - var name *ast.Node - switch node.Kind { - case ast.KindVariableDeclaration: - name = node.AsVariableDeclaration().Name() - case ast.KindBindingElement: - name = node.AsBindingElement().Name() - } - if name != nil && ast.IsBindingPattern(name) { - for _, child := range name.AsBindingPattern().Elements.Nodes { - b.bindInitializedVariableFlow(child) - } - } else { - b.currentFlow = b.createFlowMutation(ast.FlowFlagsAssignment, b.currentFlow, node) - } -} - -func (b *Binder) bindAccessExpressionFlow(node *ast.Node) { - if ast.IsOptionalChain(node) { - b.bindOptionalChainFlow(node) - } else { - b.bindEachChild(node) - } -} - -func (b *Binder) bindOptionalChainFlow(node *ast.Node) { - if isTopLevelLogicalExpression(node) { - postExpressionLabel := b.createBranchLabel() - saveCurrentFlow := b.currentFlow - saveHasFlowEffects := b.hasFlowEffects - b.bindOptionalChain(node, postExpressionLabel, postExpressionLabel) - if b.hasFlowEffects { - b.currentFlow = b.finishFlowLabel(postExpressionLabel) - } else { - b.currentFlow = saveCurrentFlow - } - b.hasFlowEffects = b.hasFlowEffects || saveHasFlowEffects - } else { - b.bindOptionalChain(node, b.currentTrueTarget, b.currentFalseTarget) - } -} - -func (b *Binder) bindOptionalChain(node *ast.Node, trueTarget *ast.FlowLabel, falseTarget *ast.FlowLabel) { - // For an optional chain, we emulate the behavior of a logical expression: - // - // a?.b -> a && a.b - // a?.b.c -> a && a.b.c - // a?.b?.c -> a && a.b && a.b.c - // a?.[x = 1] -> a && a[x = 1] - // - // To do this we descend through the chain until we reach the root of a chain (the expression with a `?.`) - // and build it's CFA graph as if it were the first condition (`a && ...`). Then we bind the rest - // of the node as part of the "true" branch, and continue to do so as we ascend back up to the outermost - // chain node. We then treat the entire node as the right side of the expression. - var preChainLabel *ast.FlowLabel - if ast.IsOptionalChainRoot(node) { - preChainLabel = b.createBranchLabel() - } - b.bindOptionalExpression(node.Expression(), core.IfElse(preChainLabel != nil, preChainLabel, trueTarget), falseTarget) - if preChainLabel != nil { - b.currentFlow = b.finishFlowLabel(preChainLabel) - } - b.doWithConditionalBranches((*Binder).bindOptionalChainRest, node, trueTarget, falseTarget) - if ast.IsOutermostOptionalChain(node) { - b.addAntecedent(trueTarget, b.createFlowCondition(ast.FlowFlagsTrueCondition, b.currentFlow, node)) - b.addAntecedent(falseTarget, b.createFlowCondition(ast.FlowFlagsFalseCondition, b.currentFlow, node)) - } -} - -func (b *Binder) bindOptionalExpression(node *ast.Node, trueTarget *ast.FlowLabel, falseTarget *ast.FlowLabel) { - b.doWithConditionalBranches((*Binder).bind, node, trueTarget, falseTarget) - if !ast.IsOptionalChain(node) || ast.IsOutermostOptionalChain(node) { - b.addAntecedent(trueTarget, b.createFlowCondition(ast.FlowFlagsTrueCondition, b.currentFlow, node)) - b.addAntecedent(falseTarget, b.createFlowCondition(ast.FlowFlagsFalseCondition, b.currentFlow, node)) - } -} - -func (b *Binder) bindOptionalChainRest(node *ast.Node) bool { - switch node.Kind { - case ast.KindPropertyAccessExpression: - b.bind(node.AsPropertyAccessExpression().QuestionDotToken) - b.bind(node.AsPropertyAccessExpression().Name()) - case ast.KindElementAccessExpression: - b.bind(node.AsElementAccessExpression().QuestionDotToken) - b.bind(node.AsElementAccessExpression().ArgumentExpression) - case ast.KindCallExpression: - b.bind(node.AsCallExpression().QuestionDotToken) - b.bindNodeList(node.AsCallExpression().TypeArguments) - b.bindEach(node.AsCallExpression().Arguments.Nodes) - } - return false -} - -func (b *Binder) bindCallExpressionFlow(node *ast.Node) { - call := node.AsCallExpression() - if ast.IsOptionalChain(node) { - b.bindOptionalChainFlow(node) - } else { - // If the target of the call expression is a function expression or arrow function we have - // an immediately invoked function expression (IIFE). Initialize the flowNode property to - // the current control flow (which includes evaluation of the IIFE arguments). - expr := ast.SkipParentheses(call.Expression) - if expr.Kind == ast.KindFunctionExpression || expr.Kind == ast.KindArrowFunction { - b.bindNodeList(call.TypeArguments) - b.bindEach(call.Arguments.Nodes) - b.bind(call.Expression) - } else { - b.bindEachChild(node) - if call.Expression.Kind == ast.KindSuperKeyword { - b.currentFlow = b.createFlowCall(b.currentFlow, node) - } - } - } - if ast.IsPropertyAccessExpression(call.Expression) { - access := call.Expression.AsPropertyAccessExpression() - if ast.IsIdentifier(access.Name()) && isNarrowableOperand(access.Expression) && ast.IsPushOrUnshiftIdentifier(access.Name()) { - b.currentFlow = b.createFlowMutation(ast.FlowFlagsArrayMutation, b.currentFlow, node) - } - } -} - -func (b *Binder) bindNonNullExpressionFlow(node *ast.Node) { - if ast.IsOptionalChain(node) { - b.bindOptionalChainFlow(node) - } else { - b.bindEachChild(node) - } -} - -func (b *Binder) bindBindingElementFlow(node *ast.Node) { - // When evaluating a binding pattern, the initializer is evaluated before the binding pattern, per: - // - https://tc39.es/ecma262/#sec-destructuring-binding-patterns-runtime-semantics-iteratorbindinginitialization - // - `BindingElement: BindingPattern Initializer?` - // - https://tc39.es/ecma262/#sec-runtime-semantics-keyedbindinginitialization - // - `BindingElement: BindingPattern Initializer?` - elem := node.AsBindingElement() - b.bind(elem.DotDotDotToken) - b.bind(elem.PropertyName) - b.bindInitializer(elem.Initializer) - b.bind(elem.Name()) -} - -func (b *Binder) bindParameterFlow(node *ast.Node) { - param := node.AsParameterDeclaration() - b.bindModifiers(param.Modifiers()) - b.bind(param.DotDotDotToken) - b.bind(param.QuestionToken) - b.bind(param.Type) - b.bindInitializer(param.Initializer) - b.bind(param.Name()) -} - -// a BindingElement/Parameter does not have side effects if initializers are not evaluated and used. (see GH#49759) -func (b *Binder) bindInitializer(node *ast.Node) { - if node == nil { - return - } - entryFlow := b.currentFlow - b.bind(node) - if entryFlow == b.unreachableFlow || entryFlow == b.currentFlow { - return - } - exitFlow := b.createBranchLabel() - b.addAntecedent(exitFlow, entryFlow) - b.addAntecedent(exitFlow, b.currentFlow) - b.currentFlow = b.finishFlowLabel(exitFlow) -} - -func isEnumDeclarationWithPreservedEmit(node *ast.Node, options core.SourceFileAffectingCompilerOptions) bool { - return node.Kind == ast.KindEnumDeclaration && (!ast.IsEnumConst(node) || options.ShouldPreserveConstEnums) -} - -func setFlowNode(node *ast.Node, flowNode *ast.FlowNode) { - data := node.FlowNodeData() - if data != nil { - data.FlowNode = flowNode - } -} - -func setReturnFlowNode(node *ast.Node, returnFlowNode *ast.FlowNode) { - switch node.Kind { - case ast.KindConstructor: - node.AsConstructorDeclaration().ReturnFlowNode = returnFlowNode - case ast.KindFunctionDeclaration: - node.AsFunctionDeclaration().ReturnFlowNode = returnFlowNode - case ast.KindFunctionExpression: - node.AsFunctionExpression().ReturnFlowNode = returnFlowNode - case ast.KindClassStaticBlockDeclaration: - node.AsClassStaticBlockDeclaration().ReturnFlowNode = returnFlowNode - } -} - -func isGeneratorFunctionExpression(node *ast.Node) bool { - return ast.IsFunctionExpression(node) && node.AsFunctionExpression().AsteriskToken != nil -} - -func (b *Binder) addToContainerChain(next *ast.Node) { - if b.lastContainer != nil { - b.lastContainer.LocalsContainerData().NextContainer = next - } - b.lastContainer = next -} - -func (b *Binder) addDeclarationToSymbol(symbol *ast.Symbol, node *ast.Node, symbolFlags ast.SymbolFlags) { - symbol.Flags |= symbolFlags - node.DeclarationData().Symbol = symbol - if symbol.Declarations == nil { - symbol.Declarations = b.newSingleDeclaration(node) - } else { - symbol.Declarations = core.AppendIfUnique(symbol.Declarations, node) - } - // On merge of const enum module with class or function, reset const enum only flag (namespaces will already recalculate) - if symbol.Flags&ast.SymbolFlagsConstEnumOnlyModule != 0 && symbol.Flags&(ast.SymbolFlagsFunction|ast.SymbolFlagsClass|ast.SymbolFlagsRegularEnum) != 0 { - symbol.Flags &^= ast.SymbolFlagsConstEnumOnlyModule - } - if symbolFlags&ast.SymbolFlagsValue != 0 { - SetValueDeclaration(symbol, node) - } -} - -func SetValueDeclaration(symbol *ast.Symbol, node *ast.Node) { - valueDeclaration := symbol.ValueDeclaration - if valueDeclaration == nil || - isAssignmentDeclaration(valueDeclaration) && !isAssignmentDeclaration(node) || - valueDeclaration.Kind != node.Kind && isEffectiveModuleDeclaration(valueDeclaration) { - // Non-assignment declarations take precedence over assignment declarations and - // non-namespace declarations take precedence over namespace declarations. - symbol.ValueDeclaration = node - } -} - -/** - * Declares a Symbol for the node and adds it to symbols. Reports errors for conflicting identifier names. - * @param symbolTable - The symbol table which node will be added to. - * @param parent - node's parent declaration. - * @param node - The declaration to be added to the symbol table - * @param includes - The SymbolFlags that node has in addition to its declaration type (eg: export, ambient, etc.) - * @param excludes - The flags which node cannot be declared alongside in a symbol table. Used to report forbidden declarations. - */ - -func GetContainerFlags(node *ast.Node) ContainerFlags { - switch node.Kind { - case ast.KindClassExpression, ast.KindClassDeclaration, ast.KindEnumDeclaration, ast.KindObjectLiteralExpression, ast.KindTypeLiteral, - ast.KindJsxAttributes: - return ContainerFlagsIsContainer - case ast.KindInterfaceDeclaration: - return ContainerFlagsIsContainer | ContainerFlagsIsInterface - case ast.KindModuleDeclaration, ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration, ast.KindMappedType, ast.KindIndexSignature: - return ContainerFlagsIsContainer | ContainerFlagsHasLocals - case ast.KindSourceFile: - return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals - case ast.KindGetAccessor, ast.KindSetAccessor, ast.KindMethodDeclaration: - if ast.IsObjectLiteralOrClassExpressionMethodOrAccessor(node) { - return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals | ContainerFlagsIsFunctionLike | ContainerFlagsIsObjectLiteralOrClassExpressionMethodOrAccessor | ContainerFlagsIsThisContainer - } - fallthrough - case ast.KindConstructor, ast.KindClassStaticBlockDeclaration: - return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals | ContainerFlagsIsFunctionLike | ContainerFlagsIsThisContainer - case ast.KindMethodSignature, ast.KindCallSignature, ast.KindFunctionType, ast.KindConstructSignature, ast.KindConstructorType: - return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals | ContainerFlagsIsFunctionLike - case ast.KindFunctionDeclaration: - return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals | ContainerFlagsIsFunctionLike | ContainerFlagsIsThisContainer - case ast.KindFunctionExpression: - return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals | ContainerFlagsIsFunctionLike | ContainerFlagsIsFunctionExpression | ContainerFlagsIsThisContainer - case ast.KindArrowFunction: - return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals | ContainerFlagsIsFunctionLike | ContainerFlagsIsFunctionExpression - case ast.KindModuleBlock: - return ContainerFlagsIsControlFlowContainer - case ast.KindPropertyDeclaration: - if node.AsPropertyDeclaration().Initializer != nil { - return ContainerFlagsIsControlFlowContainer | ContainerFlagsIsThisContainer - } else { - return ContainerFlagsNone - } - case ast.KindCatchClause, ast.KindForStatement, ast.KindForInStatement, ast.KindForOfStatement, ast.KindCaseBlock: - return ContainerFlagsIsBlockScopedContainer | ContainerFlagsHasLocals - case ast.KindBlock: - if ast.IsFunctionLike(node.Parent) || ast.IsClassStaticBlockDeclaration(node.Parent) { - return ContainerFlagsNone - } else { - return ContainerFlagsIsBlockScopedContainer | ContainerFlagsHasLocals - } - } - return ContainerFlagsNone -} - -func isNarrowingExpression(expr *ast.Node) bool { - switch expr.Kind { - case ast.KindIdentifier, ast.KindThisKeyword: - return true - case ast.KindPropertyAccessExpression, ast.KindElementAccessExpression: - return containsNarrowableReference(expr) - case ast.KindCallExpression: - return hasNarrowableArgument(expr) - case ast.KindParenthesizedExpression: - return isNarrowingExpression(expr.AsParenthesizedExpression().Expression) - case ast.KindNonNullExpression: - return isNarrowingExpression(expr.AsNonNullExpression().Expression) - case ast.KindBinaryExpression: - return isNarrowingBinaryExpression(expr.AsBinaryExpression()) - case ast.KindPrefixUnaryExpression: - return expr.AsPrefixUnaryExpression().Operator == ast.KindExclamationToken && isNarrowingExpression(expr.AsPrefixUnaryExpression().Operand) - case ast.KindTypeOfExpression: - return isNarrowingExpression(expr.AsTypeOfExpression().Expression) - } - return false -} - -func containsNarrowableReference(expr *ast.Node) bool { - if isNarrowableReference(expr) { - return true - } - if expr.Flags&ast.NodeFlagsOptionalChain != 0 { - switch expr.Kind { - case ast.KindPropertyAccessExpression: - return containsNarrowableReference(expr.AsPropertyAccessExpression().Expression) - case ast.KindElementAccessExpression: - return containsNarrowableReference(expr.AsElementAccessExpression().Expression) - case ast.KindCallExpression: - return containsNarrowableReference(expr.AsCallExpression().Expression) - case ast.KindNonNullExpression: - return containsNarrowableReference(expr.AsNonNullExpression().Expression) - } - } - return false -} - -func isNarrowableReference(node *ast.Node) bool { - switch node.Kind { - case ast.KindIdentifier, ast.KindThisKeyword, ast.KindSuperKeyword, ast.KindMetaProperty: - return true - case ast.KindPropertyAccessExpression: - return isNarrowableReference(node.AsPropertyAccessExpression().Expression) - case ast.KindParenthesizedExpression: - return isNarrowableReference(node.AsParenthesizedExpression().Expression) - case ast.KindNonNullExpression: - return isNarrowableReference(node.AsNonNullExpression().Expression) - case ast.KindElementAccessExpression: - expr := node.AsElementAccessExpression() - return ast.IsStringOrNumericLiteralLike(expr.ArgumentExpression) || - ast.IsEntityNameExpression(expr.ArgumentExpression) && isNarrowableReference(expr.Expression) - case ast.KindBinaryExpression: - expr := node.AsBinaryExpression() - return expr.OperatorToken.Kind == ast.KindCommaToken && isNarrowableReference(expr.Right) || - ast.IsAssignmentOperator(expr.OperatorToken.Kind) && ast.IsLeftHandSideExpression(expr.Left) - } - return false -} - -func hasNarrowableArgument(expr *ast.Node) bool { - call := expr.AsCallExpression() - for _, argument := range call.Arguments.Nodes { - if containsNarrowableReference(argument) { - return true - } - } - if ast.IsPropertyAccessExpression(call.Expression) { - if containsNarrowableReference(call.Expression.AsPropertyAccessExpression().Expression) { - return true - } - } - return false -} - -func isNarrowingBinaryExpression(expr *ast.BinaryExpression) bool { - switch expr.OperatorToken.Kind { - case ast.KindEqualsToken, ast.KindBarBarEqualsToken, ast.KindAmpersandAmpersandEqualsToken, ast.KindQuestionQuestionEqualsToken: - return containsNarrowableReference(expr.Left) - case ast.KindEqualsEqualsToken, ast.KindExclamationEqualsToken, ast.KindEqualsEqualsEqualsToken, ast.KindExclamationEqualsEqualsToken: - left := ast.SkipParentheses(expr.Left) - right := ast.SkipParentheses(expr.Right) - return isNarrowableOperand(left) || isNarrowableOperand(right) || - isNarrowingTypeOfOperands(right, left) || isNarrowingTypeOfOperands(left, right) || - (ast.IsBooleanLiteral(right) && isNarrowingExpression(left) || ast.IsBooleanLiteral(left) && isNarrowingExpression(right)) - case ast.KindInstanceOfKeyword: - return isNarrowableOperand(expr.Left) - case ast.KindInKeyword: - return isNarrowingExpression(expr.Right) - case ast.KindCommaToken: - return isNarrowingExpression(expr.Right) - } - return false -} - -func isNarrowableOperand(expr *ast.Node) bool { - switch expr.Kind { - case ast.KindParenthesizedExpression: - return isNarrowableOperand(expr.AsParenthesizedExpression().Expression) - case ast.KindBinaryExpression: - binary := expr.AsBinaryExpression() - switch binary.OperatorToken.Kind { - case ast.KindEqualsToken: - return isNarrowableOperand(binary.Left) - case ast.KindCommaToken: - return isNarrowableOperand(binary.Right) - } - } - return containsNarrowableReference(expr) -} - -func isNarrowingTypeOfOperands(expr1 *ast.Node, expr2 *ast.Node) bool { - return ast.IsTypeOfExpression(expr1) && isNarrowableOperand(expr1.AsTypeOfExpression().Expression) && ast.IsStringLiteralLike(expr2) -} - -func (b *Binder) errorOnNode(node *ast.Node, message *diagnostics.Message, args ...any) { - b.addDiagnostic(b.createDiagnosticForNode(node, message, args...)) -} - -func (b *Binder) errorOnFirstToken(node *ast.Node, message *diagnostics.Message, args ...any) { - span := scanner.GetRangeOfTokenAtPosition(b.file, node.Pos()) - b.addDiagnostic(ast.NewDiagnostic(b.file, span, message, args...)) -} - -func (b *Binder) errorOrSuggestionOnNode(isError bool, node *ast.Node, message *diagnostics.Message) { - b.errorOrSuggestionOnRange(isError, node, node, message) -} - -func (b *Binder) errorOrSuggestionOnRange(isError bool, startNode *ast.Node, endNode *ast.Node, message *diagnostics.Message) { - textRange := core.NewTextRange(scanner.GetRangeOfTokenAtPosition(b.file, startNode.Pos()).Pos(), endNode.End()) - diagnostic := ast.NewDiagnostic(b.file, textRange, message) - if isError { - b.addDiagnostic(diagnostic) - } else { - diagnostic.SetCategory(diagnostics.CategorySuggestion) - b.file.BindSuggestionDiagnostics = append(b.file.BindSuggestionDiagnostics, diagnostic) - } -} - -// Inside the binder, we may create a diagnostic for an as-yet unbound node (with potentially no parent pointers, implying no accessible source file) -// 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) -// This version of `createDiagnosticForNode` uses the binder's context to account for this, and always yields correct diagnostics even in these situations. -func (b *Binder) createDiagnosticForNode(node *ast.Node, message *diagnostics.Message, args ...any) *ast.Diagnostic { - return ast.NewDiagnostic(b.file, scanner.GetErrorRangeForNode(b.file, node), message, args...) -} - -func (b *Binder) addDiagnostic(diagnostic *ast.Diagnostic) { - b.file.SetBindDiagnostics(append(b.file.BindDiagnostics(), diagnostic)) -} - -func isSignedNumericLiteral(node *ast.Node) bool { - if node.Kind == ast.KindPrefixUnaryExpression { - node := node.AsPrefixUnaryExpression() - return (node.Operator == ast.KindPlusToken || node.Operator == ast.KindMinusToken) && ast.IsNumericLiteral(node.Operand) - } - return false -} - -func getOptionalSymbolFlagForNode(node *ast.Node) ast.SymbolFlags { - postfixToken := getPostfixTokenFromNode(node) - return core.IfElse(postfixToken != nil && postfixToken.Kind == ast.KindQuestionToken, ast.SymbolFlagsOptional, ast.SymbolFlagsNone) -} - -func getPostfixTokenFromNode(node *ast.Node) *ast.Node { - switch node.Kind { - case ast.KindPropertyDeclaration: - return node.AsPropertyDeclaration().PostfixToken - case ast.KindPropertySignature: - return node.AsPropertySignatureDeclaration().PostfixToken - case ast.KindMethodDeclaration: - return node.AsMethodDeclaration().PostfixToken - case ast.KindMethodSignature: - return node.AsMethodSignatureDeclaration().PostfixToken - } - panic("Unhandled case in getPostfixTokenFromNode") -} - -func isAsyncFunction(node *ast.Node) bool { - switch node.Kind { - case ast.KindFunctionDeclaration, ast.KindFunctionExpression, ast.KindArrowFunction, ast.KindMethodDeclaration: - data := node.BodyData() - return data.Body != nil && data.AsteriskToken == nil && ast.HasSyntacticModifier(node, ast.ModifierFlagsAsync) - } - return false -} - -func isFunctionSymbol(symbol *ast.Symbol) bool { - d := symbol.ValueDeclaration - if d != nil { - if ast.IsFunctionDeclaration(d) { - return true - } - if ast.IsVariableDeclaration(d) { - varDecl := d.AsVariableDeclaration() - if varDecl.Initializer != nil { - return ast.IsFunctionLike(varDecl.Initializer) - } - } - } - return false -} - -func unreachableCodeIsError(options core.SourceFileAffectingCompilerOptions) bool { - return options.AllowUnreachableCode == core.TSFalse -} - -func unusedLabelIsError(options core.SourceFileAffectingCompilerOptions) bool { - return options.AllowUnusedLabels == core.TSFalse -} - -func isStatementCondition(node *ast.Node) bool { - switch node.Parent.Kind { - case ast.KindIfStatement: - return node.Parent.AsIfStatement().Expression == node - case ast.KindWhileStatement: - return node.Parent.AsWhileStatement().Expression == node - case ast.KindDoStatement: - return node.Parent.AsDoStatement().Expression == node - case ast.KindForStatement: - return node.Parent.AsForStatement().Condition == node - case ast.KindConditionalExpression: - return node.Parent.AsConditionalExpression().Condition == node - } - return false -} - -func isTopLevelLogicalExpression(node *ast.Node) bool { - for ast.IsParenthesizedExpression(node.Parent) || ast.IsPrefixUnaryExpression(node.Parent) && node.Parent.AsPrefixUnaryExpression().Operator == ast.KindExclamationToken { - node = node.Parent - } - return !isStatementCondition(node) && !ast.IsLogicalExpression(node.Parent) && !(ast.IsOptionalChain(node.Parent) && node.Parent.Expression() == node) -} - -func isAssignmentDeclaration(decl *ast.Node) bool { - return ast.IsBinaryExpression(decl) || ast.IsAccessExpression(decl) || ast.IsIdentifier(decl) || ast.IsCallExpression(decl) -} - -func isEffectiveModuleDeclaration(node *ast.Node) bool { - return ast.IsModuleDeclaration(node) || ast.IsIdentifier(node) -} diff --git a/kitcom/internal/tsgo/binder/binder_test.go b/kitcom/internal/tsgo/binder/binder_test.go deleted file mode 100644 index e0da3e5..0000000 --- a/kitcom/internal/tsgo/binder/binder_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package binder - -import ( - "runtime" - "testing" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/parser" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/testutil/fixtures" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs/osvfs" -) - -func BenchmarkBind(b *testing.B) { - for _, f := range fixtures.BenchFixtures { - b.Run(f.Name(), func(b *testing.B) { - f.SkipIfNotExist(b) - - fileName := tspath.GetNormalizedAbsolutePath(f.Path(), "/") - path := tspath.ToPath(fileName, "/", osvfs.FS().UseCaseSensitiveFileNames()) - sourceText := f.ReadFile(b) - - compilerOptions := &core.CompilerOptions{Target: core.ScriptTargetESNext, Module: core.ModuleKindNodeNext} - sourceAffecting := compilerOptions.SourceFileAffecting() - - parseOptions := ast.SourceFileParseOptions{ - FileName: fileName, - Path: path, - CompilerOptions: sourceAffecting, - JSDocParsingMode: ast.JSDocParsingModeParseAll, - } - scriptKind := core.GetScriptKindFromFileName(fileName) - - sourceFiles := make([]*ast.SourceFile, b.N) - for i := range b.N { - sourceFiles[i] = parser.ParseSourceFile(parseOptions, sourceText, scriptKind) - } - - // The above parses do a lot of work; ensure GC is finished before we start collecting performance data. - // GC must be called twice to allow things to settle. - runtime.GC() - runtime.GC() - - b.ResetTimer() - for i := range b.N { - BindSourceFile(sourceFiles[i]) - } - }) - } -} diff --git a/kitcom/internal/tsgo/binder/nameresolver.go b/kitcom/internal/tsgo/binder/nameresolver.go deleted file mode 100644 index 83ffa39..0000000 --- a/kitcom/internal/tsgo/binder/nameresolver.go +++ /dev/null @@ -1,503 +0,0 @@ -package binder - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics" -) - -type NameResolver struct { - CompilerOptions *core.CompilerOptions - GetSymbolOfDeclaration func(node *ast.Node) *ast.Symbol - Error func(location *ast.Node, message *diagnostics.Message, args ...any) *ast.Diagnostic - Globals ast.SymbolTable - ArgumentsSymbol *ast.Symbol - RequireSymbol *ast.Symbol - GetModuleSymbol func(sourceFile *ast.Node) *ast.Symbol - Lookup func(symbols ast.SymbolTable, name string, meaning ast.SymbolFlags) *ast.Symbol - SymbolReferenced func(symbol *ast.Symbol, meaning ast.SymbolFlags) - SetRequiresScopeChangeCache func(node *ast.Node, value core.Tristate) - GetRequiresScopeChangeCache func(node *ast.Node) core.Tristate - OnPropertyWithInvalidInitializer func(location *ast.Node, name string, declaration *ast.Node, result *ast.Symbol) bool - OnFailedToResolveSymbol func(location *ast.Node, name string, meaning ast.SymbolFlags, nameNotFoundMessage *diagnostics.Message) - OnSuccessfullyResolvedSymbol func(location *ast.Node, result *ast.Symbol, meaning ast.SymbolFlags, lastLocation *ast.Node, associatedDeclarationForContainingInitializerOrBindingName *ast.Node, withinDeferredContext bool) -} - -func (r *NameResolver) Resolve(location *ast.Node, name string, meaning ast.SymbolFlags, nameNotFoundMessage *diagnostics.Message, isUse bool, excludeGlobals bool) *ast.Symbol { - var result *ast.Symbol - var lastLocation *ast.Node - var lastSelfReferenceLocation *ast.Node - var propertyWithInvalidInitializer *ast.Node - var associatedDeclarationForContainingInitializerOrBindingName *ast.Node - var withinDeferredContext bool - var grandparent *ast.Node - originalLocation := location // needed for did-you-mean error reporting, which gathers candidates starting from the original location - nameIsConst := name == "const" -loop: - for location != nil { - if nameIsConst && ast.IsConstAssertion(location) { - // `const` in an `as const` has no symbol, but issues no error because there is no *actual* lookup of the type - // (it refers to the constant type of the expression instead) - return nil - } - if ast.IsModuleOrEnumDeclaration(location) && lastLocation != nil && location.Name() == lastLocation { - // If lastLocation is the name of a namespace or enum, skip the parent since it will have is own locals that could - // conflict. - lastLocation = location - location = location.Parent - } - locals := location.Locals() - // Locals of a source file are not in scope (because they get merged into the global symbol table) - if locals != nil && !ast.IsGlobalSourceFile(location) { - result = r.lookup(locals, name, meaning) - if result != nil { - useResult := true - if ast.IsFunctionLike(location) && lastLocation != nil && lastLocation != location.Body() { - // symbol lookup restrictions for function-like declarations - // - Type parameters of a function are in scope in the entire function declaration, including the parameter - // list and return type. However, local types are only in scope in the function body. - // - parameters are only in the scope of function body - if meaning&result.Flags&ast.SymbolFlagsType != 0 { - useResult = result.Flags&ast.SymbolFlagsTypeParameter != 0 && (lastLocation == location.Type() || ast.IsParameterLike(lastLocation)) - } - if meaning&result.Flags&ast.SymbolFlagsVariable != 0 { - // expression inside parameter will lookup as normal variable scope when targeting es2015+ - if r.useOuterVariableScopeInParameter(result, location, lastLocation) { - useResult = false - } else if result.Flags&ast.SymbolFlagsFunctionScopedVariable != 0 { - // parameters are visible only inside function body, parameter list and return type - // technically for parameter list case here we might mix parameters and variables declared in function, - // however it is detected separately when checking initializers of parameters - // to make sure that they reference no variables declared after them. - useResult = lastLocation.Kind == ast.KindParameter || - lastLocation.Flags&ast.NodeFlagsSynthesized != 0 || - lastLocation == location.Type() && ast.FindAncestor(result.ValueDeclaration, ast.IsParameter) != nil - } - } - } else if location.Kind == ast.KindConditionalType { - // A type parameter declared using 'infer T' in a conditional type is visible only in - // the true branch of the conditional type. - useResult = lastLocation == location.AsConditionalTypeNode().TrueType - } - if useResult { - break loop - } - result = nil - } - } - withinDeferredContext = withinDeferredContext || getIsDeferredContext(location, lastLocation) - switch location.Kind { - case ast.KindSourceFile: - if !ast.IsExternalOrCommonJSModule(location.AsSourceFile()) { - break - } - fallthrough - case ast.KindModuleDeclaration: - moduleExports := r.getSymbolOfDeclaration(location).Exports - if ast.IsSourceFile(location) || (ast.IsModuleDeclaration(location) && location.Flags&ast.NodeFlagsAmbient != 0 && !ast.IsGlobalScopeAugmentation(location)) { - // It's an external module. First see if the module has an export default and if the local - // name of that export default matches. - result = moduleExports[ast.InternalSymbolNameDefault] - if result != nil { - localSymbol := GetLocalSymbolForExportDefault(result) - if localSymbol != nil && result.Flags&meaning != 0 && localSymbol.Name == name { - break loop - } - result = nil - } - // Because of module/namespace merging, a module's exports are in scope, - // yet we never want to treat an export specifier as putting a member in scope. - // Therefore, if the name we find is purely an export specifier, it is not actually considered in scope. - // Two things to note about this: - // 1. We have to check this without calling getSymbol. The problem with calling getSymbol - // on an export specifier is that it might find the export specifier itself, and try to - // resolve it as an alias. This will cause the checker to consider the export specifier - // a circular alias reference when it might not be. - // 2. We check === SymbolFlags.Alias in order to check that the symbol is *purely* - // an alias. If we used &, we'd be throwing out symbols that have non alias aspects, - // which is not the desired behavior. - moduleExport := moduleExports[name] - if moduleExport != nil && moduleExport.Flags == ast.SymbolFlagsAlias && (ast.GetDeclarationOfKind(moduleExport, ast.KindExportSpecifier) != nil || ast.GetDeclarationOfKind(moduleExport, ast.KindNamespaceExport) != nil) { - break - } - } - if name != ast.InternalSymbolNameDefault { - result = r.lookup(moduleExports, name, meaning&ast.SymbolFlagsModuleMember) - if result != nil { - break loop - } - } - case ast.KindEnumDeclaration: - result = r.lookup(r.getSymbolOfDeclaration(location).Exports, name, meaning&ast.SymbolFlagsEnumMember) - if result != nil { - if nameNotFoundMessage != nil && r.CompilerOptions.GetIsolatedModules() && location.Flags&ast.NodeFlagsAmbient == 0 && ast.GetSourceFileOfNode(location) != ast.GetSourceFileOfNode(result.ValueDeclaration) { - isolatedModulesLikeFlagName := core.IfElse(r.CompilerOptions.VerbatimModuleSyntax == core.TSTrue, "verbatimModuleSyntax", "isolatedModules") - r.error(originalLocation, diagnostics.Cannot_access_0_from_another_file_without_qualification_when_1_is_enabled_Use_2_instead, - name, isolatedModulesLikeFlagName, r.getSymbolOfDeclaration(location).Name+"."+name) - } - break loop - } - case ast.KindPropertyDeclaration: - if !ast.IsStatic(location) { - ctor := ast.FindConstructorDeclaration(location.Parent) - if ctor != nil && ctor.Locals() != nil { - if r.lookup(ctor.Locals(), name, meaning&ast.SymbolFlagsValue) != nil { - // Remember the property node, it will be used later to report appropriate error - propertyWithInvalidInitializer = location - } - } - } - case ast.KindClassDeclaration, ast.KindClassExpression, ast.KindInterfaceDeclaration: - result = r.lookup(r.getSymbolOfDeclaration(location).Members, name, meaning&ast.SymbolFlagsType) - if result != nil { - if !isTypeParameterSymbolDeclaredInContainer(result, location) { - // ignore type parameters not declared in this container - result = nil - break - } - if lastLocation != nil && ast.IsStatic(lastLocation) { - // TypeScript 1.0 spec (April 2014): 3.4.1 - // The scope of a type parameter extends over the entire declaration with which the type - // parameter list is associated, with the exception of static member declarations in classes. - if nameNotFoundMessage != nil { - r.error(originalLocation, diagnostics.Static_members_cannot_reference_class_type_parameters) - } - return nil - } - break loop - } - if ast.IsClassExpression(location) && meaning&ast.SymbolFlagsClass != 0 { - className := location.Name() - if className != nil && name == className.Text() { - result = location.Symbol() - break loop - } - } - case ast.KindExpressionWithTypeArguments: - if lastLocation == location.AsExpressionWithTypeArguments().Expression && ast.IsHeritageClause(location.Parent) && location.Parent.AsHeritageClause().Token == ast.KindExtendsKeyword { - container := location.Parent.Parent - if ast.IsClassLike(container) { - result = r.lookup(r.getSymbolOfDeclaration(container).Members, name, meaning&ast.SymbolFlagsType) - if result != nil { - if nameNotFoundMessage != nil { - r.error(originalLocation, diagnostics.Base_class_expressions_cannot_reference_class_type_parameters) - } - return nil - } - } - } - // It is not legal to reference a class's own type parameters from a computed property name that - // belongs to the class. For example: - // - // function foo() { return '' } - // class C { // <-- Class's own type parameter T - // [foo()]() { } // <-- Reference to T from class's own computed property - // } - case ast.KindComputedPropertyName: - grandparent = location.Parent.Parent - if ast.IsClassLike(grandparent) || ast.IsInterfaceDeclaration(grandparent) { - // A reference to this grandparent's type parameters would be an error - result = r.lookup(r.getSymbolOfDeclaration(grandparent).Members, name, meaning&ast.SymbolFlagsType) - if result != nil { - if nameNotFoundMessage != nil { - r.error(originalLocation, diagnostics.A_computed_property_name_cannot_reference_a_type_parameter_from_its_containing_type) - } - return nil - } - } - case ast.KindArrowFunction: - // when targeting ES6 or higher there is no 'arguments' in an arrow function - // for lower compile targets the resolved symbol is used to emit an error - if r.CompilerOptions.GetEmitScriptTarget() >= core.ScriptTargetES2015 { - break - } - fallthrough - case ast.KindMethodDeclaration, ast.KindConstructor, ast.KindGetAccessor, ast.KindSetAccessor, ast.KindFunctionDeclaration: - if meaning&ast.SymbolFlagsVariable != 0 && name == "arguments" { - result = r.argumentsSymbol() - break loop - } - case ast.KindFunctionExpression: - if meaning&ast.SymbolFlagsVariable != 0 && name == "arguments" { - result = r.argumentsSymbol() - break loop - } - if meaning&ast.SymbolFlagsFunction != 0 { - functionName := location.AsFunctionExpression().Name() - if functionName != nil && name == functionName.AsIdentifier().Text { - result = location.AsFunctionExpression().Symbol - break loop - } - } - case ast.KindDecorator: - // Decorators are resolved at the class declaration. Resolving at the parameter - // or member would result in looking up locals in the method. - // - // function y() {} - // class C { - // method(@y x, y) {} // <-- decorator y should be resolved at the class declaration, not the parameter. - // } - // - if location.Parent != nil && location.Parent.Kind == ast.KindParameter { - location = location.Parent - } - // function y() {} - // class C { - // @y method(x, y) {} // <-- decorator y should be resolved at the class declaration, not the method. - // } - // - // class Decorators are resolved outside of the class to avoid referencing type parameters of that class. - // - // type T = number; - // declare function y(x: T): any; - // @param(1 as T) // <-- T should resolve to the type alias outside of class C - // class C {} - if location.Parent != nil && (ast.IsClassElement(location.Parent) || location.Parent.Kind == ast.KindClassDeclaration) { - location = location.Parent - } - case ast.KindParameter: - parameterDeclaration := location.AsParameterDeclaration() - if lastLocation != nil && (lastLocation == parameterDeclaration.Initializer || - lastLocation == parameterDeclaration.Name() && ast.IsBindingPattern(lastLocation)) { - if associatedDeclarationForContainingInitializerOrBindingName == nil { - associatedDeclarationForContainingInitializerOrBindingName = location - } - } - case ast.KindBindingElement: - bindingElement := location.AsBindingElement() - if lastLocation != nil && (lastLocation == bindingElement.Initializer || - lastLocation == bindingElement.Name() && ast.IsBindingPattern(lastLocation)) { - if ast.IsPartOfParameterDeclaration(location) && associatedDeclarationForContainingInitializerOrBindingName == nil { - associatedDeclarationForContainingInitializerOrBindingName = location - } - } - case ast.KindInferType: - if meaning&ast.SymbolFlagsTypeParameter != 0 { - parameterName := location.AsInferTypeNode().TypeParameter.AsTypeParameter().Name() - if parameterName != nil && name == parameterName.AsIdentifier().Text { - result = location.AsInferTypeNode().TypeParameter.AsTypeParameter().Symbol - break loop - } - } - case ast.KindExportSpecifier: - exportSpecifier := location.AsExportSpecifier() - if lastLocation != nil && lastLocation == exportSpecifier.PropertyName && location.Parent.Parent.AsExportDeclaration().ModuleSpecifier != nil { - location = location.Parent.Parent.Parent - } - } - if isSelfReferenceLocation(location, lastLocation) { - lastSelfReferenceLocation = location - } - lastLocation = location - location = location.Parent - } - // We just climbed up parents looking for the name, meaning that we started in a descendant node of `lastLocation`. - // If `result === lastSelfReferenceLocation.symbol`, that means that we are somewhere inside `lastSelfReferenceLocation` looking up a name, and resolving to `lastLocation` itself. - // That means that this is a self-reference of `lastLocation`, and shouldn't count this when considering whether `lastLocation` is used. - if isUse && result != nil && (lastSelfReferenceLocation == nil || result != lastSelfReferenceLocation.Symbol()) { - if r.SymbolReferenced != nil { - r.SymbolReferenced(result, meaning) - } - } - if result == nil { - if lastLocation != nil && - lastLocation.Kind == ast.KindSourceFile && - lastLocation.AsSourceFile().CommonJSModuleIndicator != nil && - name == "exports" && - meaning&lastLocation.Symbol().Flags != 0 { - return lastLocation.Symbol() - } - if lastLocation != nil && - r.GetModuleSymbol != nil && - lastLocation.Kind == ast.KindSourceFile && - lastLocation.AsSourceFile().CommonJSModuleIndicator != nil && - name == "module" && - originalLocation.Parent != nil && - ast.IsModuleExportsAccessExpression(originalLocation.Parent) && - meaning&lastLocation.Symbol().Flags != 0 { - return r.GetModuleSymbol(lastLocation) - } - if !excludeGlobals { - result = r.lookup(r.Globals, name, meaning|ast.SymbolFlagsGlobalLookup) - } - } - if result == nil { - if originalLocation != nil && ast.IsInJSFile(originalLocation) && originalLocation.Parent != nil { - if ast.IsRequireCall(originalLocation.Parent, false /*requireStringLiteralLikeArgument*/) { - return r.RequireSymbol - } - } - } - if nameNotFoundMessage != nil { - if propertyWithInvalidInitializer != nil && r.OnPropertyWithInvalidInitializer != nil && r.OnPropertyWithInvalidInitializer(originalLocation, name, propertyWithInvalidInitializer, result) { - return nil - } - if result == nil { - if r.OnFailedToResolveSymbol != nil { - r.OnFailedToResolveSymbol(originalLocation, name, meaning, nameNotFoundMessage) - } - } else { - if r.OnSuccessfullyResolvedSymbol != nil { - r.OnSuccessfullyResolvedSymbol(originalLocation, result, meaning, lastLocation, associatedDeclarationForContainingInitializerOrBindingName, withinDeferredContext) - } - } - } - return result -} - -func (r *NameResolver) useOuterVariableScopeInParameter(result *ast.Symbol, location *ast.Node, lastLocation *ast.Node) bool { - if ast.IsParameter(lastLocation) { - body := location.Body() - if body != nil && result.ValueDeclaration != nil && result.ValueDeclaration.Pos() >= body.Pos() && result.ValueDeclaration.End() <= body.End() { - // check for several cases where we introduce temporaries that require moving the name/initializer of the parameter to the body - // - static field in a class expression - // - optional chaining pre-es2020 - // - nullish coalesce pre-es2020 - // - spread assignment in binding pattern pre-es2017 - target := r.CompilerOptions.GetEmitScriptTarget() - if target >= core.ScriptTargetES2015 { - functionLocation := location - declarationRequiresScopeChange := core.TSUnknown - if r.GetRequiresScopeChangeCache != nil { - declarationRequiresScopeChange = r.GetRequiresScopeChangeCache(functionLocation) - } - if declarationRequiresScopeChange == core.TSUnknown { - declarationRequiresScopeChange = core.IfElse(core.Some(functionLocation.Parameters(), r.requiresScopeChange), core.TSTrue, core.TSFalse) - if r.SetRequiresScopeChangeCache != nil { - r.SetRequiresScopeChangeCache(functionLocation, declarationRequiresScopeChange) - } - } - return declarationRequiresScopeChange != core.TSTrue - } - } - } - return false -} - -func (r *NameResolver) requiresScopeChange(node *ast.Node) bool { - d := node.AsParameterDeclaration() - return r.requiresScopeChangeWorker(d.Name()) || d.Initializer != nil && r.requiresScopeChangeWorker(d.Initializer) -} - -func (r *NameResolver) requiresScopeChangeWorker(node *ast.Node) bool { - switch node.Kind { - case ast.KindArrowFunction, ast.KindFunctionExpression, ast.KindFunctionDeclaration, ast.KindConstructor: - return false - case ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor, ast.KindPropertyAssignment: - return r.requiresScopeChangeWorker(node.Name()) - case ast.KindPropertyDeclaration: - if ast.HasStaticModifier(node) { - return !r.CompilerOptions.GetEmitStandardClassFields() - } - return r.requiresScopeChangeWorker(node.AsPropertyDeclaration().Name()) - default: - if ast.IsNullishCoalesce(node) || ast.IsOptionalChain(node) { - return r.CompilerOptions.GetEmitScriptTarget() < core.ScriptTargetES2020 - } - if ast.IsBindingElement(node) && node.AsBindingElement().DotDotDotToken != nil && ast.IsObjectBindingPattern(node.Parent) { - return r.CompilerOptions.GetEmitScriptTarget() < core.ScriptTargetES2017 - } - if ast.IsTypeNode(node) { - return false - } - return node.ForEachChild(r.requiresScopeChangeWorker) - } -} - -func (r *NameResolver) error(location *ast.Node, message *diagnostics.Message, args ...any) { - if r.Error != nil { - r.Error(location, message, args...) - } - // Default implementation does not report errors -} - -func (r *NameResolver) getSymbolOfDeclaration(node *ast.Node) *ast.Symbol { - if r.GetSymbolOfDeclaration != nil { - return r.GetSymbolOfDeclaration(node) - } - - // Default implementation does not support merged symbols - return node.Symbol() -} - -func (r *NameResolver) lookup(symbols ast.SymbolTable, name string, meaning ast.SymbolFlags) *ast.Symbol { - if r.Lookup != nil { - return r.Lookup(symbols, name, meaning) - } - // Default implementation does not support following aliases or merged symbols - if meaning != 0 { - symbol := symbols[name] - if symbol != nil { - if symbol.Flags&meaning != 0 { - return symbol - } - } - } - return nil -} - -func (r *NameResolver) argumentsSymbol() *ast.Symbol { - if r.ArgumentsSymbol == nil { - // Default implementation synthesizes a transient symbol for `arguments` - r.ArgumentsSymbol = &ast.Symbol{Name: "arguments", Flags: ast.SymbolFlagsProperty | ast.SymbolFlagsTransient} - } - return r.ArgumentsSymbol -} - -func GetLocalSymbolForExportDefault(symbol *ast.Symbol) *ast.Symbol { - if !isExportDefaultSymbol(symbol) || len(symbol.Declarations) == 0 { - return nil - } - for _, decl := range symbol.Declarations { - localSymbol := decl.LocalSymbol() - if localSymbol != nil { - return localSymbol - } - } - return nil -} - -func isExportDefaultSymbol(symbol *ast.Symbol) bool { - return symbol != nil && len(symbol.Declarations) > 0 && ast.HasSyntacticModifier(symbol.Declarations[0], ast.ModifierFlagsDefault) -} - -func getIsDeferredContext(location *ast.Node, lastLocation *ast.Node) bool { - if location.Kind != ast.KindArrowFunction && location.Kind != ast.KindFunctionExpression { - // initializers in instance property declaration of class like entities are executed in constructor and thus deferred - // A name is evaluated within the enclosing scope - so it shouldn't count as deferred - return ast.IsTypeQueryNode(location) || - (ast.IsFunctionLikeDeclaration(location) || location.Kind == ast.KindPropertyDeclaration && !ast.IsStatic(location)) && - (lastLocation == nil || lastLocation != location.Name()) - } - if lastLocation != nil && lastLocation == location.Name() { - return false - } - // generator functions and async functions are not inlined in control flow when immediately invoked - if location.BodyData().AsteriskToken != nil || ast.HasSyntacticModifier(location, ast.ModifierFlagsAsync) { - return true - } - return ast.GetImmediatelyInvokedFunctionExpression(location) == nil -} - -func isTypeParameterSymbolDeclaredInContainer(symbol *ast.Symbol, container *ast.Node) bool { - for _, decl := range symbol.Declarations { - if decl.Kind == ast.KindTypeParameter { - parent := decl.Parent - if parent == container { - return true - } - } - } - return false -} - -func isSelfReferenceLocation(node *ast.Node, lastLocation *ast.Node) bool { - switch node.Kind { - case ast.KindParameter: - return lastLocation != nil && lastLocation == node.Name() - case ast.KindFunctionDeclaration, ast.KindClassDeclaration, ast.KindInterfaceDeclaration, ast.KindEnumDeclaration, - ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration, ast.KindModuleDeclaration: // For `namespace N { N; }` - return true - } - return false -} diff --git a/kitcom/internal/tsgo/binder/referenceresolver.go b/kitcom/internal/tsgo/binder/referenceresolver.go deleted file mode 100644 index 963276c..0000000 --- a/kitcom/internal/tsgo/binder/referenceresolver.go +++ /dev/null @@ -1,251 +0,0 @@ -package binder - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics" -) - -type ReferenceResolver interface { - GetReferencedExportContainer(node *ast.IdentifierNode, prefixLocals bool) *ast.Node - GetReferencedImportDeclaration(node *ast.IdentifierNode) *ast.Declaration - GetReferencedValueDeclaration(node *ast.IdentifierNode) *ast.Declaration - GetReferencedValueDeclarations(node *ast.IdentifierNode) []*ast.Declaration - GetElementAccessExpressionName(expression *ast.ElementAccessExpression) string -} - -type ReferenceResolverHooks struct { - ResolveName func(location *ast.Node, name string, meaning ast.SymbolFlags, nameNotFoundMessage *diagnostics.Message, isUse bool, excludeGlobals bool) *ast.Symbol - GetResolvedSymbol func(*ast.Node) *ast.Symbol - GetMergedSymbol func(*ast.Symbol) *ast.Symbol - GetParentOfSymbol func(*ast.Symbol) *ast.Symbol - GetSymbolOfDeclaration func(*ast.Declaration) *ast.Symbol - GetTypeOnlyAliasDeclaration func(symbol *ast.Symbol, include ast.SymbolFlags) *ast.Declaration - GetExportSymbolOfValueSymbolIfExported func(*ast.Symbol) *ast.Symbol - GetElementAccessExpressionName func(*ast.ElementAccessExpression) (string, bool) -} - -var _ ReferenceResolver = &referenceResolver{} - -type referenceResolver struct { - resolver *NameResolver - options *core.CompilerOptions - hooks ReferenceResolverHooks -} - -func NewReferenceResolver(options *core.CompilerOptions, hooks ReferenceResolverHooks) ReferenceResolver { - return &referenceResolver{ - options: options, - hooks: hooks, - } -} - -func (r *referenceResolver) getResolvedSymbol(node *ast.Node) *ast.Symbol { - if node != nil { - if r.hooks.GetResolvedSymbol != nil { - return r.hooks.GetResolvedSymbol(node) - } - } - return nil -} - -func (r *referenceResolver) getMergedSymbol(symbol *ast.Symbol) *ast.Symbol { - if symbol != nil { - if r.hooks.GetMergedSymbol != nil { - return r.hooks.GetMergedSymbol(symbol) - } - return symbol - } - return nil -} - -func (r *referenceResolver) getParentOfSymbol(symbol *ast.Symbol) *ast.Symbol { - if symbol != nil { - if r.hooks.GetParentOfSymbol != nil { - return r.hooks.GetParentOfSymbol(symbol) - } - return symbol.Parent - } - return nil -} - -func (r *referenceResolver) getSymbolOfDeclaration(declaration *ast.Declaration) *ast.Symbol { - if declaration != nil { - if r.hooks.GetSymbolOfDeclaration != nil { - return r.hooks.GetSymbolOfDeclaration(declaration) - } - return declaration.Symbol() - } - return nil -} - -func (r *referenceResolver) getReferencedValueSymbol(reference *ast.IdentifierNode, startInDeclarationContainer bool) *ast.Symbol { - resolvedSymbol := r.getResolvedSymbol(reference) - if resolvedSymbol != nil { - return resolvedSymbol - } - - location := reference - if startInDeclarationContainer && reference.Parent != nil && ast.IsDeclaration(reference.Parent) && reference.Parent.Name() == reference { - location = ast.GetDeclarationContainer(reference.Parent) - } - - if r.hooks.ResolveName != nil { - return r.hooks.ResolveName(location, reference.Text(), ast.SymbolFlagsExportValue|ast.SymbolFlagsValue|ast.SymbolFlagsAlias, nil /*nameNotFoundMessage*/, false /*isUse*/, false /*excludeGlobals*/) - } - - if r.resolver == nil { - r.resolver = &NameResolver{ - CompilerOptions: r.options, - } - } - - return r.resolver.Resolve(location, reference.Text(), ast.SymbolFlagsExportValue|ast.SymbolFlagsValue|ast.SymbolFlagsAlias, nil /*nameNotFoundMessage*/, false /*isUse*/, false /*excludeGlobals*/) -} - -func (r *referenceResolver) isTypeOnlyAliasDeclaration(symbol *ast.Symbol) bool { - if symbol != nil { - if r.hooks.GetTypeOnlyAliasDeclaration != nil { - return r.hooks.GetTypeOnlyAliasDeclaration(symbol, ast.SymbolFlagsValue) != nil - } - - node := r.getDeclarationOfAliasSymbol(symbol) - for node != nil { - switch node.Kind { - case ast.KindImportEqualsDeclaration, ast.KindExportDeclaration: - return node.IsTypeOnly() - case ast.KindImportClause, ast.KindImportSpecifier, ast.KindExportSpecifier: - if node.IsTypeOnly() { - return true - } - node = node.Parent - continue - case ast.KindNamedImports, ast.KindNamedExports: - node = node.Parent - continue - } - break - } - } - return false -} - -func (r *referenceResolver) getDeclarationOfAliasSymbol(symbol *ast.Symbol) *ast.Declaration { - return core.FindLast(symbol.Declarations, ast.IsAliasSymbolDeclaration) -} - -func (r *referenceResolver) getExportSymbolOfValueSymbolIfExported(symbol *ast.Symbol) *ast.Symbol { - if symbol != nil { - if r.hooks.GetExportSymbolOfValueSymbolIfExported != nil { - return r.hooks.GetExportSymbolOfValueSymbolIfExported(symbol) - } - if symbol.Flags&ast.SymbolFlagsExportValue != 0 && symbol.ExportSymbol != nil { - symbol = symbol.ExportSymbol - } - return r.getMergedSymbol(symbol) - } - return nil -} - -func (r *referenceResolver) GetReferencedExportContainer(node *ast.IdentifierNode, prefixLocals bool) *ast.Node /*SourceFile|ModuleDeclaration|EnumDeclaration*/ { - // When resolving the export for the name of a module or enum - // declaration, we need to start resolution at the declaration's container. - // Otherwise, we could incorrectly resolve the export as the - // declaration if it contains an exported member with the same name. - startInDeclarationContainer := node.Parent != nil && (node.Parent.Kind == ast.KindModuleDeclaration || node.Parent.Kind == ast.KindEnumDeclaration) && node == node.Parent.Name() - if symbol := r.getReferencedValueSymbol(node, startInDeclarationContainer); symbol != nil { - if symbol.Flags&ast.SymbolFlagsExportValue != 0 { - // If we reference an exported entity within the same module declaration, then whether - // we prefix depends on the kind of entity. SymbolFlags.ExportHasLocal encompasses all the - // kinds that we do NOT prefix. - exportSymbol := r.getMergedSymbol(symbol.ExportSymbol) - if !prefixLocals && exportSymbol.Flags&ast.SymbolFlagsExportHasLocal != 0 && exportSymbol.Flags&ast.SymbolFlagsVariable == 0 { - return nil - } - symbol = exportSymbol - } - parentSymbol := r.getParentOfSymbol(symbol) - if parentSymbol != nil { - if parentSymbol.Flags&ast.SymbolFlagsValueModule != 0 && parentSymbol.ValueDeclaration != nil && parentSymbol.ValueDeclaration.Kind == ast.KindSourceFile { - symbolFile := parentSymbol.ValueDeclaration.AsSourceFile() - referenceFile := ast.GetSourceFileOfNode(node) - // If `node` accesses an export and that export isn't in the same file, then symbol is a namespace export, so return nil. - symbolIsUmdExport := symbolFile != referenceFile - if symbolIsUmdExport { - return nil - } - return symbolFile.AsNode() - } - isMatchingContainer := func(n *ast.Node) bool { - return (n.Kind == ast.KindModuleDeclaration || n.Kind == ast.KindEnumDeclaration) && r.getSymbolOfDeclaration(n) == parentSymbol - } - if container := ast.FindAncestor(symbol.ValueDeclaration, isMatchingContainer); container != nil { - return container - } - return ast.FindAncestor(node.Parent, isMatchingContainer) - } - } - - return nil -} - -func (r *referenceResolver) GetReferencedImportDeclaration(node *ast.IdentifierNode) *ast.Declaration { - if symbol := r.getReferencedValueSymbol(node, false /*startInDeclarationContainer*/); symbol != nil { - // We should only get the declaration of an alias if there isn't a local value - // declaration for the symbol - if ast.IsNonLocalAlias(symbol, ast.SymbolFlagsValue /*excludes*/) && !r.isTypeOnlyAliasDeclaration(symbol) { - return r.getDeclarationOfAliasSymbol(symbol) - } - } - - return nil -} - -func (r *referenceResolver) GetReferencedValueDeclaration(node *ast.IdentifierNode) *ast.Declaration { - if symbol := r.getReferencedValueSymbol(node, false /*startInDeclarationContainer*/); symbol != nil { - return r.getExportSymbolOfValueSymbolIfExported(symbol).ValueDeclaration - } - return nil -} - -func (r *referenceResolver) GetReferencedValueDeclarations(node *ast.IdentifierNode) []*ast.Declaration { - var declarations []*ast.Declaration - if symbol := r.getReferencedValueSymbol(node, false /*startInDeclarationContainer*/); symbol != nil { - symbol = r.getExportSymbolOfValueSymbolIfExported(symbol) - for _, declaration := range symbol.Declarations { - switch declaration.Kind { - case ast.KindVariableDeclaration, - ast.KindParameter, - ast.KindBindingElement, - ast.KindPropertyDeclaration, - ast.KindPropertyAssignment, - ast.KindShorthandPropertyAssignment, - ast.KindEnumMember, - ast.KindObjectLiteralExpression, - ast.KindFunctionDeclaration, - ast.KindFunctionExpression, - ast.KindArrowFunction, - ast.KindClassDeclaration, - ast.KindClassExpression, - ast.KindEnumDeclaration, - ast.KindMethodDeclaration, - ast.KindGetAccessor, - ast.KindSetAccessor, - ast.KindModuleDeclaration: - declarations = append(declarations, declaration) - } - } - } - return declarations -} - -func (r *referenceResolver) GetElementAccessExpressionName(expression *ast.ElementAccessExpression) string { - if expression != nil { - if r.hooks.GetElementAccessExpressionName != nil { - if name, ok := r.hooks.GetElementAccessExpressionName(expression); ok { - return name - } - } - } - return "" -}