diff --git a/cmd/tsgo/main.go b/cmd/tsgo/main.go index 5f5318b0cc..118ba19ea3 100644 --- a/cmd/tsgo/main.go +++ b/cmd/tsgo/main.go @@ -2,9 +2,60 @@ package main import ( - "github.com/microsoft/typescript-go/internal/compiler" + "flag" + "fmt" + "runtime" + "strings" + "time" + + ts "github.com/microsoft/typescript-go/internal/compiler" ) +var quiet = false +var singleThreaded = false +var parseAndBindOnly = false + +func printDiagnostic(d *ts.Diagnostic, level int) { + file := d.File() + if file != nil { + line, character := ts.GetLineAndCharacterOfPosition(file, d.Loc().Pos()) + fmt.Printf("%v%v(%v,%v): error TS%v: %v\n", strings.Repeat(" ", level*2), file.FileName(), line+1, character+1, d.Code(), d.Message()) + } else { + fmt.Printf("%verror TS%v: %v\n", strings.Repeat(" ", level*2), d.Code(), d.Message()) + } + for _, r := range d.RelatedInformation() { + printDiagnostic(r, level+1) + } +} + func main() { - compiler.CreateSourceFile() + flag.BoolVar(&quiet, "q", false, "Quiet output") + flag.BoolVar(&singleThreaded, "s", false, "Single threaded") + flag.BoolVar(&parseAndBindOnly, "p", false, "Parse and bind only") + flag.Parse() + compilerOptions := &ts.CompilerOptions{Target: ts.ScriptTargetESNext, ModuleKind: ts.ModuleKindNodeNext} + programOptions := ts.ProgramOptions{RootPath: flag.Arg(0), Options: compilerOptions, SingleThreaded: singleThreaded} + startTime := time.Now() + program := ts.NewProgram(programOptions) + diagnostics := program.GetSyntacticDiagnostics(nil) + if len(diagnostics) == 0 { + if parseAndBindOnly { + diagnostics = program.GetBindDiagnostics(nil) + } else { + diagnostics = program.GetSemanticDiagnostics(nil) + } + } + compileTime := time.Since(startTime) + var memStats runtime.MemStats + runtime.GC() + runtime.GC() + runtime.ReadMemStats(&memStats) + if !quiet { + for _, diagnostic := range diagnostics { + printDiagnostic(diagnostic, 0) + } + } + fmt.Printf("Files: %v\n", len(program.SourceFiles())) + fmt.Printf("Compile time: %v\n", compileTime) + fmt.Printf("Memory used: %vK\n", memStats.Alloc/1024) } diff --git a/internal/compiler/ast.go b/internal/compiler/ast.go new file mode 100644 index 0000000000..12e22b8503 --- /dev/null +++ b/internal/compiler/ast.go @@ -0,0 +1,4126 @@ +package compiler + +import "reflect" + +// Visitor + +type Visitor func(*Node) bool + +func visit(v Visitor, node *Node) bool { + if node != nil { + return v(node) + } + return false +} + +func visitNodes(v Visitor, nodes []*Node) bool { + for _, node := range nodes { + if v(node) { + return true + } + } + return false +} + +// In situations where conversions from a pointer type may produce a typed nil, this function can be used +// to check that the interface truly references an existing struct. +func exists(i interface{}) bool { + return !(i == nil || reflect.ValueOf(i).IsNil()) +} + +// NodeFactory + +type NodeFactory struct { + identifierPool Pool[Identifier] +} + +func (f *NodeFactory) NewNode(kind SyntaxKind, data NodeData) *Node { + n := data.AsNode() + n.kind = kind + n.data = data + return n +} + +// AST Node +// Interface values stored in AST nodes are never typed nil values. Construction code must ensure that +// interface valued properties either store a true nil or a reference to a non-nil struct. + +type Node struct { + kind SyntaxKind + flags NodeFlags + loc TextRange + id NodeId + parent *Node + data NodeData +} + +// Node accessors + +func (n *Node) Pos() int { return n.loc.Pos() } +func (n *Node) End() int { return n.loc.End() } +func (n *Node) ForEachChild(v Visitor) bool { return n.data.ForEachChild(v) } +func (n *Node) Symbol() *Symbol { return n.data.Symbol() } +func (n *Node) LocalSymbol() *Symbol { return n.data.LocalSymbol() } +func (n *Node) Modifiers() *ModifierListNode { return n.data.Modifiers() } +func (n *Node) Name() *DeclarationName { return n.data.Name() } +func (n *Node) FlowNodeData() *FlowNodeBase { return n.data.FlowNodeData() } +func (n *Node) DeclarationData() *DeclarationBase { return n.data.DeclarationData() } +func (n *Node) ExportableData() *ExportableBase { return n.data.ExportableData() } +func (n *Node) LocalsContainerData() *LocalsContainerBase { return n.data.LocalsContainerData() } +func (n *Node) FunctionLikeData() *FunctionLikeBase { return n.data.FunctionLikeData() } +func (n *Node) ClassLikeData() *ClassLikeBase { return n.data.ClassLikeData() } +func (n *Node) BodyData() *BodyBase { return n.data.BodyData() } + +// Node casts + +func (n *Node) AsIdentifier() *Identifier { + return n.data.(*Identifier) +} +func (n *Node) AsPrivateIdentifier() *PrivateIdentifier { + return n.data.(*PrivateIdentifier) +} +func (n *Node) AsQualifiedName() *QualifiedName { + return n.data.(*QualifiedName) +} +func (n *Node) AsModifierList() *ModifierList { + return n.data.(*ModifierList) +} +func (n *Node) AsSourceFile() *SourceFile { + return n.data.(*SourceFile) +} +func (n *Node) AsPrefixUnaryExpression() *PrefixUnaryExpression { + return n.data.(*PrefixUnaryExpression) +} +func (n *Node) AsPostfixUnaryExpression() *PostfixUnaryExpression { + return n.data.(*PostfixUnaryExpression) +} +func (n *Node) AsParenthesizedExpression() *ParenthesizedExpression { + return n.data.(*ParenthesizedExpression) +} +func (n *Node) AsTypeAssertion() *TypeAssertion { + return n.data.(*TypeAssertion) +} +func (n *Node) AsAsExpression() *AsExpression { + return n.data.(*AsExpression) +} +func (n *Node) AsSatisfiesExpression() *SatisfiesExpression { + return n.data.(*SatisfiesExpression) +} +func (n *Node) AsExpressionWithTypeArguments() *ExpressionWithTypeArguments { + return n.data.(*ExpressionWithTypeArguments) +} +func (n *Node) AsNonNullExpression() *NonNullExpression { + return n.data.(*NonNullExpression) +} +func (n *Node) AsBindingElement() *BindingElement { + return n.data.(*BindingElement) +} +func (n *Node) AsImportSpecifier() *ImportSpecifier { + return n.data.(*ImportSpecifier) +} +func (n *Node) AsArrowFunction() *ArrowFunction { + return n.data.(*ArrowFunction) +} +func (n *Node) AsCallExpression() *CallExpression { + return n.data.(*CallExpression) +} +func (n *Node) AsPropertyAccessExpression() *PropertyAccessExpression { + return n.data.(*PropertyAccessExpression) +} +func (n *Node) AsElementAccessExpression() *ElementAccessExpression { + return n.data.(*ElementAccessExpression) +} +func (n *Node) AsComputedPropertyName() *ComputedPropertyName { + return n.data.(*ComputedPropertyName) +} +func (n *Node) AsBinaryExpression() *BinaryExpression { + return n.data.(*BinaryExpression) +} +func (n *Node) AsModuleDeclaration() *ModuleDeclaration { + return n.data.(*ModuleDeclaration) +} +func (n *Node) AsStringLiteral() *StringLiteral { + return n.data.(*StringLiteral) +} +func (n *Node) AsNumericLiteral() *NumericLiteral { + return n.data.(*NumericLiteral) +} +func (n *Node) AsBigintLiteral() *BigintLiteral { + return n.data.(*BigintLiteral) +} +func (n *Node) AsNoSubstitutionTemplateLiteral() *NoSubstitutionTemplateLiteral { + return n.data.(*NoSubstitutionTemplateLiteral) +} +func (n *Node) AsVariableDeclaration() *VariableDeclaration { + return n.data.(*VariableDeclaration) +} +func (n *Node) AsExportAssignment() *ExportAssignment { + return n.data.(*ExportAssignment) +} +func (n *Node) AsObjectLiteralExpression() *ObjectLiteralExpression { + return n.data.(*ObjectLiteralExpression) +} +func (n *Node) AsIfStatement() *IfStatement { + return n.data.(*IfStatement) +} +func (n *Node) AsWhileStatement() *WhileStatement { + return n.data.(*WhileStatement) +} +func (n *Node) AsDoStatement() *DoStatement { + return n.data.(*DoStatement) +} +func (n *Node) AsForStatement() *ForStatement { + return n.data.(*ForStatement) +} +func (n *Node) AsConditionalExpression() *ConditionalExpression { + return n.data.(*ConditionalExpression) +} +func (n *Node) AsForInOrOfStatement() *ForInOrOfStatement { + return n.data.(*ForInOrOfStatement) +} +func (n *Node) AsShorthandPropertyAssignment() *ShorthandPropertyAssignment { + return n.data.(*ShorthandPropertyAssignment) +} +func (n *Node) AsPropertyAssignment() *PropertyAssignment { + return n.data.(*PropertyAssignment) +} +func (n *Node) AsExpressionStatement() *ExpressionStatement { + return n.data.(*ExpressionStatement) +} +func (n *Node) AsBlock() *Block { + return n.data.(*Block) +} +func (n *Node) AsModuleBlock() *ModuleBlock { + return n.data.(*ModuleBlock) +} +func (n *Node) AsVariableStatement() *VariableStatement { + return n.data.(*VariableStatement) +} +func (n *Node) AsVariableDeclarationList() *VariableDeclarationList { + return n.data.(*VariableDeclarationList) +} +func (n *Node) AsMetaProperty() *MetaProperty { + return n.data.(*MetaProperty) +} +func (n *Node) AsTypeReference() *TypeReferenceNode { + return n.data.(*TypeReferenceNode) +} +func (n *Node) AsConstructorDeclaration() *ConstructorDeclaration { + return n.data.(*ConstructorDeclaration) +} +func (n *Node) AsConditionalTypeNode() *ConditionalTypeNode { + return n.data.(*ConditionalTypeNode) +} +func (n *Node) AsClassExpression() *ClassExpression { + return n.data.(*ClassExpression) +} +func (n *Node) AsHeritageClause() *HeritageClause { + return n.data.(*HeritageClause) +} +func (n *Node) AsFunctionExpression() *FunctionExpression { + return n.data.(*FunctionExpression) +} +func (n *Node) AsParameterDeclaration() *ParameterDeclaration { + return n.data.(*ParameterDeclaration) +} +func (n *Node) AsInferTypeNode() *InferTypeNode { + return n.data.(*InferTypeNode) +} +func (n *Node) AsTypeParameter() *TypeParameterDeclaration { + return n.data.(*TypeParameterDeclaration) +} +func (n *Node) AsExportSpecifier() *ExportSpecifier { + return n.data.(*ExportSpecifier) +} +func (n *Node) AsExportDeclaration() *ExportDeclaration { + return n.data.(*ExportDeclaration) +} +func (n *Node) AsPropertyDeclaration() *PropertyDeclaration { + return n.data.(*PropertyDeclaration) +} +func (n *Node) AsImportClause() *ImportClause { + return n.data.(*ImportClause) +} +func (n *Node) AsImportEqualsDeclaration() *ImportEqualsDeclaration { + return n.data.(*ImportEqualsDeclaration) +} +func (n *Node) AsNamespaceImport() *NamespaceImport { + return n.data.(*NamespaceImport) +} +func (n *Node) AsPropertySignatureDeclaration() *PropertySignatureDeclaration { + return n.data.(*PropertySignatureDeclaration) +} +func (n *Node) AsEnumMember() *EnumMember { + return n.data.(*EnumMember) +} +func (n *Node) AsReturnStatement() *ReturnStatement { + return n.data.(*ReturnStatement) +} +func (n *Node) AsWithStatement() *WithStatement { + return n.data.(*WithStatement) +} +func (n *Node) AsSwitchStatement() *SwitchStatement { + return n.data.(*SwitchStatement) +} +func (n *Node) AsCaseOrDefaultClause() *CaseOrDefaultClause { + return n.data.(*CaseOrDefaultClause) +} +func (n *Node) AsThrowStatement() *ThrowStatement { + return n.data.(*ThrowStatement) +} +func (n *Node) AsTemplateSpan() *TemplateSpan { + return n.data.(*TemplateSpan) +} +func (n *Node) AsImportTypeNode() *ImportTypeNode { + return n.data.(*ImportTypeNode) +} +func (n *Node) AsNewExpression() *NewExpression { + return n.data.(*NewExpression) +} +func (n *Node) AsTaggedTemplateExpression() *TaggedTemplateExpression { + return n.data.(*TaggedTemplateExpression) +} +func (n *Node) AsTypeArgumentList() *TypeArgumentList { + return n.data.(*TypeArgumentList) +} +func (n *Node) AsJsxOpeningElement() *JsxOpeningElement { + return n.data.(*JsxOpeningElement) +} +func (n *Node) AsJsxSelfClosingElement() *JsxSelfClosingElement { + return n.data.(*JsxSelfClosingElement) +} +func (n *Node) AsJsxClosingElement() *JsxClosingElement { + return n.data.(*JsxClosingElement) +} +func (n *Node) AsImportDeclaration() *ImportDeclaration { + return n.data.(*ImportDeclaration) +} +func (n *Node) AsExternalModuleReference() *ExternalModuleReference { + return n.data.(*ExternalModuleReference) +} +func (n *Node) AsLiteralTypeNode() *LiteralTypeNode { + return n.data.(*LiteralTypeNode) +} +func (n *Node) AsJsxNamespacedName() *JsxNamespacedName { + return n.data.(*JsxNamespacedName) +} +func (n *Node) AsTypeParameterList() *TypeParameterList { + return n.data.(*TypeParameterList) +} +func (n *Node) AsClassDeclaration() *ClassDeclaration { + return n.data.(*ClassDeclaration) +} +func (n *Node) AsInterfaceDeclaration() *InterfaceDeclaration { + return n.data.(*InterfaceDeclaration) +} +func (n *Node) AsTypeAliasDeclaration() *TypeAliasDeclaration { + return n.data.(*TypeAliasDeclaration) +} +func (n *Node) AsJsxAttribute() *JsxAttribute { + return n.data.(*JsxAttribute) +} +func (n *Node) AsParenthesizedTypeNode() *ParenthesizedTypeNode { + return n.data.(*ParenthesizedTypeNode) +} +func (n *Node) AsTypePredicateNode() *TypePredicateNode { + return n.data.(*TypePredicateNode) +} +func (n *Node) AsTypeOperatorNode() *TypeOperatorNode { + return n.data.(*TypeOperatorNode) +} +func (n *Node) AsMappedTypeNode() *MappedTypeNode { + return n.data.(*MappedTypeNode) +} +func (n *Node) AsArrayLiteralExpression() *ArrayLiteralExpression { + return n.data.(*ArrayLiteralExpression) +} +func (n *Node) AsMethodDeclaration() *MethodDeclaration { + return n.data.(*MethodDeclaration) +} +func (n *Node) AsMethodSignatureDeclaration() *MethodSignatureDeclaration { + return n.data.(*MethodSignatureDeclaration) +} +func (n *Node) AsTemplateLiteralTypeSpan() *TemplateLiteralTypeSpan { + return n.data.(*TemplateLiteralTypeSpan) +} +func (n *Node) AsJsxElement() *JsxElement { + return n.data.(*JsxElement) +} +func (n *Node) AsKeywordExpression() *KeywordExpression { + return n.data.(*KeywordExpression) +} +func (n *Node) AsCatchClause() *CatchClause { + return n.data.(*CatchClause) +} +func (n *Node) AsDeleteExpression() *DeleteExpression { + return n.data.(*DeleteExpression) +} +func (n *Node) AsLabeledStatement() *LabeledStatement { + return n.data.(*LabeledStatement) +} +func (n *Node) AsNamespaceExportDeclaration() *NamespaceExportDeclaration { + return n.data.(*NamespaceExportDeclaration) +} +func (n *Node) AsNamedExports() *NamedExports { + return n.data.(*NamedExports) +} +func (n *Node) AsBreakStatement() *BreakStatement { + return n.data.(*BreakStatement) +} +func (n *Node) AsContinueStatement() *ContinueStatement { + return n.data.(*ContinueStatement) +} +func (n *Node) AsCaseBlock() *CaseBlock { + return n.data.(*CaseBlock) +} +func (n *Node) AsTryStatement() *TryStatement { + return n.data.(*TryStatement) +} +func (n *Node) AsBindingPattern() *BindingPattern { + return n.data.(*BindingPattern) +} +func (n *Node) AsFunctionDeclaration() *FunctionDeclaration { + return n.data.(*FunctionDeclaration) +} +func (n *Node) AsTypeOfExpression() *TypeOfExpression { + return n.data.(*TypeOfExpression) +} +func (n *Node) AsSpreadElement() *SpreadElement { + return n.data.(*SpreadElement) +} +func (n *Node) AsSpreadAssignment() *SpreadAssignment { + return n.data.(*SpreadAssignment) +} +func (n *Node) AsArrayTypeNode() *ArrayTypeNode { + return n.data.(*ArrayTypeNode) +} +func (n *Node) AsTupleTypeNode() *TupleTypeNode { + return n.data.(*TupleTypeNode) +} + +// NodeData + +type NodeData interface { + AsNode() *Node + ForEachChild(v Visitor) bool + Symbol() *Symbol + LocalSymbol() *Symbol + Modifiers() *ModifierListNode + Name() *DeclarationName + FlowNodeData() *FlowNodeBase + DeclarationData() *DeclarationBase + ExportableData() *ExportableBase + LocalsContainerData() *LocalsContainerBase + FunctionLikeData() *FunctionLikeBase + ClassLikeData() *ClassLikeBase + BodyData() *BodyBase +} + +// NodeDefault + +type NodeDefault struct { + Node +} + +func (node *NodeDefault) AsNode() *Node { return &node.Node } +func (node *NodeDefault) ForEachChild(v Visitor) bool { return false } +func (node *NodeDefault) Symbol() *Symbol { return nil } +func (node *NodeDefault) LocalSymbol() *Symbol { return nil } +func (node *NodeDefault) Modifiers() *ModifierListNode { return nil } +func (node *NodeDefault) Name() *DeclarationName { return nil } +func (node *NodeDefault) FlowNodeData() *FlowNodeBase { return nil } +func (node *NodeDefault) DeclarationData() *DeclarationBase { return nil } +func (node *NodeDefault) ExportableData() *ExportableBase { return nil } +func (node *NodeDefault) LocalsContainerData() *LocalsContainerBase { return nil } +func (node *NodeDefault) FunctionLikeData() *FunctionLikeBase { return nil } +func (node *NodeDefault) ClassLikeData() *ClassLikeBase { return nil } +func (node *NodeDefault) BodyData() *BodyBase { return nil } + +// NodeBase + +type NodeBase struct { + NodeDefault +} + +// Aliases for Node unions + +type Statement = Node // Node with StatementBase +type Declaration = Node // Node with DeclarationBase +type Expression = Node // Node with ExpressionBase +type TypeNode = Node // Node with TypeNodeBase +type TypeElement = Node // Node with TypeElementBase +type ClassElement = Node // Node with ClassElementBase +type NamedMember = Node // Node with NamedMemberBase +type ObjectLiteralElement = Node // Node with ObjectLiteralElementBase +type BlockOrExpression = Node // Block | Expression +type AccessExpression = Node // PropertyAccessExpression | ElementAccessExpression +type DeclarationName = Node // Identifier | PrivateIdentifier | StringLiteral | NumericLiteral | BigIntLiteral | NoSubstitutionTemplateLiteral | ComputedPropertyName | BindingPattern | ElementAccessExpression +type ModuleName = Node // Identifier | StringLiteral +type ModuleExportName = Node // Identifier | StringLiteral +type PropertyName = Node // Identifier | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | ComputedPropertyName | PrivateIdentifier | BigIntLiteral +type ModuleBody = Node // ModuleBlock | ModuleDeclaration +type ForInitializer = Node // Expression | MissingDeclaration | VariableDeclarationList +type ModuleReference = Node // Identifier | QualifiedName | ExternalModuleReference +type NamedImportBindings = Node // NamespaceImport | NamedImports +type NamedExportBindings = Node // NamespaceExport | NamedExports +type MemberName = Node // Identifier | PrivateIdentifier +type EntityName = Node // Identifier | QualifiedName +type BindingName = Node // Identifier | BindingPattern +type ModifierLike = Node // Modifier | Decorator +type JsxAttributeLike = Node // JsxAttribute | JsxSpreadAttribute +type JsxAttributeName = Node // Identifier | JsxNamespacedName +type ClassLikeDeclaration = Node // ClassDeclaration | ClassExpression +type AccessorDeclaration = Node // GetAccessorDeclaration | SetAccessorDeclaration +type LiteralLikeNode = Node // StringLiteral | NumericLiteral | BigintLiteral | RegularExpressionLiteral | TemplateLiteralLikeNode | JsxText +type LiteralExpression = Node // StringLiteral | NumericLiteral | BigintLiteral | RegularExpressionLiteral | NoSubstitutionTemplateLiteral +type UnionOrIntersectionTypeNode = Node // UnionTypeNode | IntersectionTypeNode +type TemplateLiteralLikeNode = Node // TemplateHead | TemplateMiddle | TemplateTail +type TemplateMiddleOrTail = Node // TemplateMiddle | TemplateTail + +// Aliases for node signletons + +type IdentifierNode = Node +type ModifierListNode = Node +type TokenNode = Node +type BlockNode = Node +type CatchClauseNode = Node +type CaseBlockNode = Node +type CaseOrDefaultClauseNode = Node +type VariableDeclarationNode = Node +type VariableDeclarationListNode = Node +type BindingElementNode = Node +type TypeParameterListNode = Node +type ParameterDeclarationNode = Node +type HeritageClauseNode = Node +type ExpressionWithTypeArgumentsNode = Node +type EnumMemberNode = Node +type ImportClauseNode = Node +type ImportAttributesNode = Node +type ImportSpecifierNode = Node +type ExportSpecifierNode = Node + +// DeclarationBase + +type DeclarationBase struct { + symbol *Symbol // Symbol declared by node (initialized by binding) +} + +func (node *DeclarationBase) Symbol() *Symbol { return node.symbol } +func (node *DeclarationBase) DeclarationData() *DeclarationBase { return node } + +// DeclarationBase + +type ExportableBase struct { + localSymbol *Symbol // Local symbol declared by node (initialized by binding only for exported nodes) +} + +func (node *ExportableBase) LocalSymbol() *Symbol { return node.localSymbol } +func (node *ExportableBase) ExportableData() *ExportableBase { return node } + +// ModifiersBase + +type ModifiersBase struct { + modifiers *ModifierListNode +} + +func (node *ModifiersBase) Modifiers() *ModifierListNode { return node.modifiers } + +// LocalsContainerBase + +type LocalsContainerBase struct { + locals SymbolTable // Locals associated with node (initialized by binding) + nextContainer *Node // Next container in declaration order (initialized by binding) +} + +func (node *LocalsContainerBase) LocalsContainerData() *LocalsContainerBase { return node } + +func isLocalsContainer(node *Node) bool { + return node.LocalsContainerData() != nil +} + +// FunctionLikeBase + +type FunctionLikeBase struct { + LocalsContainerBase + typeParameters *TypeParameterListNode // Optional + parameters []*ParameterDeclarationNode + returnType *TypeNode // Optional +} + +func (node *FunctionLikeBase) LocalsContainerData() *LocalsContainerBase { + return &node.LocalsContainerBase +} +func (node *FunctionLikeBase) FunctionLikeData() *FunctionLikeBase { return node } +func (node *FunctionLikeBase) BodyData() *BodyBase { return nil } + +// BodyBase + +type BodyBase struct { + asteriskToken *TokenNode + body *BlockOrExpression // Optional, can be Expression only in arrow functions + endFlowNode *FlowNode +} + +func (node *BodyBase) BodyData() *BodyBase { return node } + +// FunctionLikeWithBodyBase + +type FunctionLikeWithBodyBase struct { + FunctionLikeBase + BodyBase +} + +func (node *FunctionLikeWithBodyBase) LocalsContainerData() *LocalsContainerBase { + return &node.LocalsContainerBase +} +func (node *FunctionLikeWithBodyBase) FunctionLikeData() *FunctionLikeBase { + return &node.FunctionLikeBase +} +func (node *FunctionLikeWithBodyBase) BodyData() *BodyBase { return &node.BodyBase } + +// FlowNodeBase + +type FlowNodeBase struct { + flowNode *FlowNode +} + +func (node *FlowNodeBase) FlowNodeData() *FlowNodeBase { return node } + +// Token + +type Token struct { + NodeBase +} + +func (f *NodeFactory) NewToken(kind SyntaxKind) *Node { + return f.NewNode(kind, &Token{}) +} + +// Identifier + +type Identifier struct { + ExpressionBase + FlowNodeBase + text string +} + +func (f *NodeFactory) NewIdentifier(text string) *Node { + data := f.identifierPool.New() + data.text = text + return f.NewNode(SyntaxKindIdentifier, data) +} + +func isIdentifier(node *Node) bool { + return node.kind == SyntaxKindIdentifier +} + +// PrivateIdentifier + +type PrivateIdentifier struct { + ExpressionBase + text string +} + +func (f *NodeFactory) NewPrivateIdentifier(text string) *Node { + data := &PrivateIdentifier{} + data.text = text + return f.NewNode(SyntaxKindPrivateIdentifier, data) +} + +func isPrivateIdentifier(node *Node) bool { + return node.kind == SyntaxKindPrivateIdentifier +} + +// QualifiedName + +type QualifiedName struct { + NodeBase + FlowNodeBase + left *EntityName + right *IdentifierNode +} + +func (f *NodeFactory) NewQualifiedName(left *EntityName, right *IdentifierNode) *Node { + data := &QualifiedName{} + data.left = left + data.right = right + return f.NewNode(SyntaxKindQualifiedName, data) +} + +func (node *QualifiedName) ForEachChild(v Visitor) bool { + return visit(v, node.left) || visit(v, node.right) +} + +func isQualifiedName(node *Node) bool { + return node.kind == SyntaxKindQualifiedName +} + +// TypeParameterDeclaration + +type TypeParameterDeclaration struct { + NodeBase + DeclarationBase + ModifiersBase + name *IdentifierNode // Identifier + constraint *TypeNode // Optional + defaultType *TypeNode // Optional + expression *Node // For error recovery purposes +} + +func (f *NodeFactory) NewTypeParameterDeclaration(modifiers *Node, name *IdentifierNode, constraint *TypeNode, defaultType *TypeNode) *Node { + data := &TypeParameterDeclaration{} + data.modifiers = modifiers + data.name = name + data.constraint = constraint + data.defaultType = defaultType + return f.NewNode(SyntaxKindTypeParameter, data) +} + +func (node *TypeParameterDeclaration) Kind() SyntaxKind { + return SyntaxKindTypeParameter +} + +func (node *TypeParameterDeclaration) ForEachChild(v Visitor) bool { + return visit(v, node.modifiers) || visit(v, node.name) || visit(v, node.constraint) || visit(v, node.defaultType) +} + +func (node *TypeParameterDeclaration) Name() *DeclarationName { + return node.name +} + +func isTypeParameterDeclaration(node *Node) bool { + return node.kind == SyntaxKindTypeParameter +} + +// ComputedPropertyName + +type ComputedPropertyName struct { + NodeBase + expression *Node +} + +func (f *NodeFactory) NewComputedPropertyName(expression *Node) *Node { + data := &ComputedPropertyName{} + data.expression = expression + return f.NewNode(SyntaxKindComputedPropertyName, data) +} + +func (node *ComputedPropertyName) ForEachChild(v Visitor) bool { + return visit(v, node.expression) +} + +func isComputedPropertyName(node *Node) bool { + return node.kind == SyntaxKindComputedPropertyName +} + +// Modifier + +func (f *NodeFactory) NewModifier(kind SyntaxKind) *Node { + return f.NewToken(kind) +} + +// Decorator + +type Decorator struct { + NodeBase + expression *Node +} + +func (f *NodeFactory) NewDecorator(expression *Node) *Node { + data := &Decorator{} + data.expression = expression + return f.NewNode(SyntaxKindDecorator, data) +} + +func (node *Decorator) ForEachChild(v Visitor) bool { + return visit(v, node.expression) +} + +// ModifierList + +type ModifierList struct { + NodeBase + modifiers []*ModifierLike + modifierFlags ModifierFlags +} + +func (f *NodeFactory) NewModifierList(modifiers []*ModifierLike, modifierFlags ModifierFlags) *Node { + data := &ModifierList{} + data.modifiers = modifiers + data.modifierFlags = modifierFlags + return f.NewNode(SyntaxKindModifierList, data) +} + +func (node *ModifierList) ForEachChild(v Visitor) bool { + return visitNodes(v, node.modifiers) +} + +// StatementBase + +type StatementBase struct { + NodeBase + FlowNodeBase +} + +// EmptyStatement + +type EmptyStatement struct { + StatementBase +} + +func (f *NodeFactory) NewEmptyStatement() *Node { + return f.NewNode(SyntaxKindEmptyStatement, &EmptyStatement{}) +} + +func isEmptyStatement(node *Node) bool { + return node.kind == SyntaxKindEmptyStatement +} + +// IfStatement + +type IfStatement struct { + StatementBase + expression *Node + thenStatement *Statement + elseStatement *Statement // Optional +} + +func (f *NodeFactory) NewIfStatement(expression *Node, thenStatement *Statement, elseStatement *Statement) *Node { + data := &IfStatement{} + data.expression = expression + data.thenStatement = thenStatement + data.elseStatement = elseStatement + return f.NewNode(SyntaxKindIfStatement, data) +} + +func (node *IfStatement) ForEachChild(v Visitor) bool { + return visit(v, node.expression) || visit(v, node.thenStatement) || visit(v, node.elseStatement) +} + +// DoStatement + +type DoStatement struct { + StatementBase + statement *Statement + expression *Node +} + +func (f *NodeFactory) NewDoStatement(statement *Statement, expression *Node) *Node { + data := &DoStatement{} + data.statement = statement + data.expression = expression + return f.NewNode(SyntaxKindDoStatement, data) +} + +func (node *DoStatement) ForEachChild(v Visitor) bool { + return visit(v, node.statement) || visit(v, node.expression) +} + +// WhileStatement + +type WhileStatement struct { + StatementBase + expression *Node + statement *Statement +} + +func (f *NodeFactory) NewWhileStatement(expression *Node, statement *Statement) *Node { + data := &WhileStatement{} + data.expression = expression + data.statement = statement + return f.NewNode(SyntaxKindWhileStatement, data) +} + +func (node *WhileStatement) ForEachChild(v Visitor) bool { + return visit(v, node.expression) || visit(v, node.statement) +} + +// ForStatement + +type ForStatement struct { + StatementBase + LocalsContainerBase + initializer *ForInitializer // Optional + condition *Node // Optional + incrementor *Node // Optional + statement *Statement +} + +func (f *NodeFactory) NewForStatement(initializer *ForInitializer, condition *Node, incrementor *Node, statement *Statement) *Node { + data := &ForStatement{} + data.initializer = initializer + data.condition = condition + data.incrementor = incrementor + data.statement = statement + return f.NewNode(SyntaxKindForStatement, data) +} + +func (node *ForStatement) ForEachChild(v Visitor) bool { + return visit(v, node.initializer) || visit(v, node.condition) || visit(v, node.incrementor) || visit(v, node.statement) +} + +// ForInOrOfStatement + +type ForInOrOfStatement struct { + StatementBase + LocalsContainerBase + kind SyntaxKind // SyntaxKindForInStatement | SyntaxKindForOfStatement + awaitModifier *Node // Optional + initializer *ForInitializer + expression *Node + statement *Statement +} + +func (f *NodeFactory) NewForInOrOfStatement(kind SyntaxKind, awaitModifier *Node, initializer *ForInitializer, expression *Node, statement *Statement) *Node { + data := &ForInOrOfStatement{} + data.kind = kind + data.awaitModifier = awaitModifier + data.initializer = initializer + data.expression = expression + data.statement = statement + return f.NewNode(kind, data) +} + +func (node *ForInOrOfStatement) ForEachChild(v Visitor) bool { + return visit(v, node.awaitModifier) || visit(v, node.initializer) || visit(v, node.expression) || visit(v, node.statement) +} + +func isForInOrOfStatement(node *Node) bool { + return node.kind == SyntaxKindForInStatement || node.kind == SyntaxKindForOfStatement +} + +// BreakStatement + +type BreakStatement struct { + StatementBase + label *IdentifierNode // Optional +} + +func (f *NodeFactory) NewBreakStatement(label *IdentifierNode) *Node { + data := &BreakStatement{} + data.label = label + return f.NewNode(SyntaxKindBreakStatement, data) +} + +func (node *BreakStatement) ForEachChild(v Visitor) bool { + return visit(v, node.label) +} + +// ContinueStatement + +type ContinueStatement struct { + StatementBase + label *IdentifierNode // Optional +} + +func (f *NodeFactory) NewContinueStatement(label *IdentifierNode) *Node { + data := &ContinueStatement{} + data.label = label + return f.NewNode(SyntaxKindContinueStatement, data) +} + +func (node *ContinueStatement) ForEachChild(v Visitor) bool { + return visit(v, node.label) +} + +// ReturnStatement + +type ReturnStatement struct { + StatementBase + expression *Node // Optional +} + +func (f *NodeFactory) NewReturnStatement(expression *Node) *Node { + data := &ReturnStatement{} + data.expression = expression + return f.NewNode(SyntaxKindReturnStatement, data) +} + +func (node *ReturnStatement) ForEachChild(v Visitor) bool { + return visit(v, node.expression) +} + +// WithStatement + +type WithStatement struct { + StatementBase + expression *Node + statement *Statement +} + +func (f *NodeFactory) NewWithStatement(expression *Node, statement *Statement) *Node { + data := &WithStatement{} + data.expression = expression + data.statement = statement + return f.NewNode(SyntaxKindWithStatement, data) +} + +func (node *WithStatement) ForEachChild(v Visitor) bool { + return visit(v, node.expression) || visit(v, node.statement) +} + +// SwitchStatement + +type SwitchStatement struct { + StatementBase + expression *Node + caseBlock *CaseBlockNode +} + +func (f *NodeFactory) NewSwitchStatement(expression *Node, caseBlock *CaseBlockNode) *Node { + data := &SwitchStatement{} + data.expression = expression + data.caseBlock = caseBlock + return f.NewNode(SyntaxKindSwitchStatement, data) +} + +func (node *SwitchStatement) ForEachChild(v Visitor) bool { + return visit(v, node.expression) || visit(v, node.caseBlock) +} + +// CaseBlock + +type CaseBlock struct { + NodeBase + LocalsContainerBase + clauses []*CaseOrDefaultClauseNode +} + +func (f *NodeFactory) NewCaseBlock(clauses []*CaseOrDefaultClauseNode) *Node { + data := &CaseBlock{} + data.clauses = clauses + return f.NewNode(SyntaxKindCaseBlock, data) +} + +func (node *CaseBlock) ForEachChild(v Visitor) bool { + return visitNodes(v, node.clauses) +} + +// CaseOrDefaultClause + +type CaseOrDefaultClause struct { + NodeBase + expression *Node // nil in default clause + statements []*Statement + fallthroughFlowNode *FlowNode +} + +func (f *NodeFactory) NewCaseOrDefaultClause(kind SyntaxKind, expression *Node, statements []*Statement) *Node { + data := &CaseOrDefaultClause{} + data.expression = expression + data.statements = statements + return f.NewNode(kind, data) +} + +func (node *CaseOrDefaultClause) ForEachChild(v Visitor) bool { + return visit(v, node.expression) || visitNodes(v, node.statements) +} + +// ThrowStatement + +type ThrowStatement struct { + StatementBase + expression *Node +} + +func (f *NodeFactory) NewThrowStatement(expression *Node) *Node { + data := &ThrowStatement{} + data.expression = expression + return f.NewNode(SyntaxKindThrowStatement, data) +} + +func (node *ThrowStatement) ForEachChild(v Visitor) bool { + return visit(v, node.expression) +} + +// TryStatement + +type TryStatement struct { + StatementBase + tryBlock *BlockNode + catchClause *CatchClauseNode // Optional + finallyBlock *BlockNode // Optional +} + +func (f *NodeFactory) NewTryStatement(tryBlock *BlockNode, catchClause *CatchClauseNode, finallyBlock *BlockNode) *Node { + data := &TryStatement{} + data.tryBlock = tryBlock + data.catchClause = catchClause + data.finallyBlock = finallyBlock + return f.NewNode(SyntaxKindTryStatement, data) +} + +func (node *TryStatement) Kind() SyntaxKind { + return SyntaxKindTryStatement +} + +func (node *TryStatement) ForEachChild(v Visitor) bool { + return visit(v, node.tryBlock) || visit(v, node.catchClause) || visit(v, node.finallyBlock) +} + +// CatchClause + +type CatchClause struct { + NodeBase + LocalsContainerBase + variableDeclaration *VariableDeclarationNode // Optional + block *BlockNode +} + +func (f *NodeFactory) NewCatchClause(variableDeclaration *VariableDeclarationNode, block *BlockNode) *Node { + data := &CatchClause{} + data.variableDeclaration = variableDeclaration + data.block = block + return f.NewNode(SyntaxKindCatchClause, data) +} + +func (node *CatchClause) ForEachChild(v Visitor) bool { + return visit(v, node.variableDeclaration) || visit(v, node.block) +} + +// DebuggerStatement + +type DebuggerStatement struct { + StatementBase +} + +func (f *NodeFactory) NewDebuggerStatement() *Node { + return f.NewNode(SyntaxKindDebuggerStatement, &DebuggerStatement{}) +} + +// LabeledStatement + +type LabeledStatement struct { + StatementBase + label *IdentifierNode + statement *Statement +} + +func (f *NodeFactory) NewLabeledStatement(label *IdentifierNode, statement *Statement) *Node { + data := &LabeledStatement{} + data.label = label + data.statement = statement + return f.NewNode(SyntaxKindLabeledStatement, data) +} + +func (node *LabeledStatement) ForEachChild(v Visitor) bool { + return visit(v, node.label) || visit(v, node.statement) +} + +// ExpressionStatement + +type ExpressionStatement struct { + StatementBase + expression *Node +} + +func (f *NodeFactory) NewExpressionStatement(expression *Node) *Node { + data := &ExpressionStatement{} + data.expression = expression + return f.NewNode(SyntaxKindExpressionStatement, data) +} + +func (node *ExpressionStatement) ForEachChild(v Visitor) bool { + return visit(v, node.expression) +} + +func isExpressionStatement(node *Node) bool { + return node.kind == SyntaxKindExpressionStatement +} + +// Block + +type Block struct { + StatementBase + LocalsContainerBase + statements []*Statement + multiline bool +} + +func (f *NodeFactory) NewBlock(statements []*Statement, multiline bool) *Node { + data := &Block{} + data.statements = statements + data.multiline = multiline + return f.NewNode(SyntaxKindBlock, data) +} + +func (node *Block) ForEachChild(v Visitor) bool { + return visitNodes(v, node.statements) +} + +func isBlock(node *Node) bool { + return node.kind == SyntaxKindBlock +} + +// VariableStatement + +type VariableStatement struct { + StatementBase + ModifiersBase + declarationList *VariableDeclarationListNode +} + +func (f *NodeFactory) NewVariableStatement(modifiers *ModifierListNode, declarationList *VariableDeclarationListNode) *Node { + data := &VariableStatement{} + data.modifiers = modifiers + data.declarationList = declarationList + return f.NewNode(SyntaxKindVariableStatement, data) +} + +func (node *VariableStatement) ForEachChild(v Visitor) bool { + return visit(v, node.modifiers) || visit(v, node.declarationList) +} + +func isVariableStatement(node *Node) bool { + return node.kind == SyntaxKindVariableStatement +} + +// VariableDeclaration + +type VariableDeclaration struct { + NodeBase + DeclarationBase + ExportableBase + name *BindingName + exclamationToken *TokenNode // Optional + typeNode *TypeNode // Optional + initializer *Node // Optional +} + +func (f *NodeFactory) NewVariableDeclaration(name *BindingName, exclamationToken *TokenNode, typeNode *TypeNode, initializer *Node) *Node { + data := &VariableDeclaration{} + data.name = name + data.exclamationToken = exclamationToken + data.typeNode = typeNode + data.initializer = initializer + return f.NewNode(SyntaxKindVariableDeclaration, data) +} + +func (node *VariableDeclaration) ForEachChild(v Visitor) bool { + return visit(v, node.name) || visit(v, node.exclamationToken) || visit(v, node.typeNode) || visit(v, node.initializer) +} + +func (node *VariableDeclaration) Name() *DeclarationName { + return node.name +} + +func isVariableDeclaration(node *Node) bool { + return node.kind == SyntaxKindVariableDeclaration +} + +// VariableDeclarationList + +type VariableDeclarationList struct { + NodeBase + declarations []*VariableDeclarationNode +} + +func (f *NodeFactory) NewVariableDeclarationList(flags NodeFlags, declarations []*VariableDeclarationNode) *Node { + data := &VariableDeclarationList{} + data.declarations = declarations + node := f.NewNode(SyntaxKindVariableDeclarationList, data) + node.flags = flags + return node +} + +func (node *VariableDeclarationList) ForEachChild(v Visitor) bool { + return visitNodes(v, node.declarations) +} + +// BindingPattern (SyntaxBindObjectBindingPattern | SyntaxKindArrayBindingPattern) + +type BindingPattern struct { + NodeBase + elements []*BindingElementNode +} + +func (f *NodeFactory) NewBindingPattern(kind SyntaxKind, elements []*BindingElementNode) *Node { + data := &BindingPattern{} + data.elements = elements + return f.NewNode(kind, data) +} + +func (node *BindingPattern) ForEachChild(v Visitor) bool { + return visitNodes(v, node.elements) +} + +func isObjectBindingPattern(node *Node) bool { + return node.kind == SyntaxKindObjectBindingPattern +} + +func isArrayBindingPattern(node *Node) bool { + return node.kind == SyntaxKindArrayBindingPattern +} + +// ParameterDeclaration + +type ParameterDeclaration struct { + NodeBase + DeclarationBase + ModifiersBase + dotDotDotToken *TokenNode // Present on rest parameter + name *BindingName // Declared parameter name + questionToken *TokenNode // Present on optional parameter + typeNode *TypeNode // Optional + initializer *Node // Optional +} + +func (f *NodeFactory) NewParameterDeclaration(modifiers *ModifierListNode, dotDotDotToken *TokenNode, name *BindingName, questionToken *TokenNode, typeNode *TypeNode, initializer *Node) *Node { + data := &ParameterDeclaration{} + data.modifiers = modifiers + data.dotDotDotToken = dotDotDotToken + data.name = name + data.questionToken = questionToken + data.typeNode = typeNode + data.initializer = initializer + return f.NewNode(SyntaxKindParameter, data) +} + +func (node *ParameterDeclaration) ForEachChild(v Visitor) bool { + return visit(v, node.modifiers) || visit(v, node.dotDotDotToken) || visit(v, node.name) || + visit(v, node.questionToken) || visit(v, node.typeNode) || visit(v, node.initializer) +} + +func (node *ParameterDeclaration) Name() *DeclarationName { + return node.name +} + +func isParameter(node *Node) bool { + return node.kind == SyntaxKindParameter +} + +// BindingElement + +type BindingElement struct { + NodeBase + DeclarationBase + ExportableBase + FlowNodeBase + dotDotDotToken *TokenNode // Present on rest element (in object binding pattern) + propertyName *PropertyName // Optional binding property name in object binding pattern + name *BindingName // Optional (nil for missing element) + initializer *Node // Optional +} + +func (f *NodeFactory) NewBindingElement(dotDotDotToken *TokenNode, propertyName *PropertyName, name *BindingName, initializer *Node) *Node { + data := &BindingElement{} + data.dotDotDotToken = dotDotDotToken + data.propertyName = propertyName + data.name = name + data.initializer = initializer + return f.NewNode(SyntaxKindBindingElement, data) +} + +func (node *BindingElement) ForEachChild(v Visitor) bool { + return visit(v, node.propertyName) || visit(v, node.dotDotDotToken) || visit(v, node.name) || visit(v, node.initializer) +} + +func (node *BindingElement) Name() *DeclarationName { + return node.name +} + +func isBindingElement(node *Node) bool { + return node.kind == SyntaxKindBindingElement +} + +// MissingDeclaration + +type MissingDeclaration struct { + StatementBase + DeclarationBase + ModifiersBase +} + +func (f *NodeFactory) NewMissingDeclaration(modifiers *ModifierListNode) *Node { + data := &MissingDeclaration{} + data.modifiers = modifiers + return f.NewNode(SyntaxKindMissingDeclaration, data) +} + +func (node *MissingDeclaration) ForEachChild(v Visitor) bool { + return visit(v, node.modifiers) +} + +// FunctionDeclaration + +type FunctionDeclaration struct { + StatementBase + DeclarationBase + ExportableBase + ModifiersBase + FunctionLikeWithBodyBase + name *IdentifierNode + returnFlowNode *FlowNode +} + +func (f *NodeFactory) NewFunctionDeclaration(modifiers *ModifierListNode, asteriskToken *TokenNode, name *IdentifierNode, typeParameters *TypeParameterListNode, parameters []*ParameterDeclarationNode, returnType *TypeNode, body *BlockNode) *Node { + data := &FunctionDeclaration{} + data.modifiers = modifiers + data.asteriskToken = asteriskToken + data.name = name + data.typeParameters = typeParameters + data.parameters = parameters + data.returnType = returnType + data.body = body + return f.NewNode(SyntaxKindFunctionDeclaration, data) +} + +func (node *FunctionDeclaration) ForEachChild(v Visitor) bool { + return visit(v, node.modifiers) || visit(v, node.asteriskToken) || visit(v, node.name) || visit(v, node.typeParameters) || + visitNodes(v, node.parameters) || visit(v, node.returnType) || visit(v, node.body) +} + +func (node *FunctionDeclaration) Name() *DeclarationName { + return node.name +} + +func (node *FunctionDeclaration) BodyData() *BodyBase { return &node.BodyBase } + +func isFunctionDeclaration(node *Node) bool { + return node.kind == SyntaxKindFunctionDeclaration +} + +// ClassLikeDeclarationBase + +type ClassLikeBase struct { + DeclarationBase + ExportableBase + ModifiersBase + name *IdentifierNode + typeParameters *TypeParameterListNode + heritageClauses []*HeritageClauseNode + members []*ClassElement +} + +func (node *ClassLikeBase) ForEachChild(v Visitor) bool { + return visit(v, node.modifiers) || visit(v, node.name) || visit(v, node.typeParameters) || + visitNodes(v, node.heritageClauses) || visitNodes(v, node.members) +} + +func (node *ClassLikeBase) Name() *DeclarationName { + return node.name +} + +func (node *ClassLikeBase) ClassLikeData() *ClassLikeBase { return node } + +// ClassDeclaration + +type ClassDeclaration struct { + StatementBase + ClassLikeBase +} + +func (f *NodeFactory) NewClassDeclaration(modifiers *ModifierListNode, name *IdentifierNode, typeParameters *TypeParameterListNode, heritageClauses []*HeritageClauseNode, members []*ClassElement) *Node { + data := &ClassDeclaration{} + data.modifiers = modifiers + data.name = name + data.typeParameters = typeParameters + data.heritageClauses = heritageClauses + data.members = members + return f.NewNode(SyntaxKindClassDeclaration, data) +} + +func isClassDeclaration(node *Node) bool { + return node.kind == SyntaxKindClassDeclaration +} + +// ClassExpression + +type ClassExpression struct { + ExpressionBase + ClassLikeBase +} + +func (f *NodeFactory) NewClassExpression(modifiers *ModifierListNode, name *IdentifierNode, typeParameters *TypeParameterListNode, heritageClauses []*HeritageClauseNode, members []*ClassElement) *Node { + data := &ClassExpression{} + data.modifiers = modifiers + data.name = name + data.typeParameters = typeParameters + data.heritageClauses = heritageClauses + data.members = members + return f.NewNode(SyntaxKindClassExpression, data) +} + +func (node *ClassExpression) Kind() SyntaxKind { return SyntaxKindClassExpression } + +func isClassExpression(node *Node) bool { + return node.kind == SyntaxKindClassExpression +} + +// HeritageClause + +type HeritageClause struct { + NodeBase + token SyntaxKind + types []*ExpressionWithTypeArgumentsNode +} + +func (f *NodeFactory) NewHeritageClause(token SyntaxKind, types []*ExpressionWithTypeArgumentsNode) *Node { + data := &HeritageClause{} + data.token = token + data.types = types + return f.NewNode(SyntaxKindHeritageClause, data) +} + +func (node *HeritageClause) ForEachChild(v Visitor) bool { + return visitNodes(v, node.types) +} + +func isHeritageClause(node *Node) bool { + return node.kind == SyntaxKindHeritageClause +} + +// InterfaceDeclaration + +type InterfaceDeclaration struct { + StatementBase + DeclarationBase + ExportableBase + ModifiersBase + name *IdentifierNode + typeParameters *TypeParameterListNode + heritageClauses []*HeritageClauseNode + members []*TypeElement +} + +func (f *NodeFactory) NewInterfaceDeclaration(modifiers *ModifierListNode, name *IdentifierNode, typeParameters *TypeParameterListNode, heritageClauses []*HeritageClauseNode, members []*TypeElement) *Node { + data := &InterfaceDeclaration{} + data.modifiers = modifiers + data.name = name + data.typeParameters = typeParameters + data.heritageClauses = heritageClauses + data.members = members + return f.NewNode(SyntaxKindInterfaceDeclaration, data) +} + +func (node *InterfaceDeclaration) ForEachChild(v Visitor) bool { + return visit(v, node.modifiers) || visit(v, node.name) || visit(v, node.typeParameters) || + visitNodes(v, node.heritageClauses) || visitNodes(v, node.members) +} + +func (node *InterfaceDeclaration) Name() *DeclarationName { + return node.name +} + +func isInterfaceDeclaration(node *Node) bool { + return node.kind == SyntaxKindInterfaceDeclaration +} + +// TypeAliasDeclaration + +type TypeAliasDeclaration struct { + StatementBase + DeclarationBase + ExportableBase + ModifiersBase + LocalsContainerBase + name *IdentifierNode + typeParameters *TypeParameterListNode + typeNode *TypeNode +} + +func (f *NodeFactory) NewTypeAliasDeclaration(modifiers *ModifierListNode, name *IdentifierNode, typeParameters *TypeParameterListNode, typeNode *TypeNode) *Node { + data := &TypeAliasDeclaration{} + data.modifiers = modifiers + data.name = name + data.typeParameters = typeParameters + data.typeNode = typeNode + return f.NewNode(SyntaxKindTypeAliasDeclaration, data) +} + +func (node *TypeAliasDeclaration) ForEachChild(v Visitor) bool { + return visit(v, node.modifiers) || visit(v, node.name) || visit(v, node.typeParameters) || visit(v, node.typeNode) +} + +func (node *TypeAliasDeclaration) Name() *DeclarationName { + return node.name +} + +func isTypeAliasDeclaration(node *Node) bool { + return node.kind == SyntaxKindTypeAliasDeclaration +} + +// EnumMember + +type EnumMember struct { + NodeBase + NamedMemberBase + initializer *Node // Optional +} + +func (f *NodeFactory) NewEnumMember(name *PropertyName, initializer *Node) *Node { + data := &EnumMember{} + data.name = name + data.initializer = initializer + return f.NewNode(SyntaxKindEnumMember, data) +} + +func (node *EnumMember) ForEachChild(v Visitor) bool { + return visit(v, node.name) || visit(v, node.initializer) +} + +func (node *EnumMember) Name() *DeclarationName { + return node.name +} + +// EnumDeclaration + +type EnumDeclaration struct { + StatementBase + DeclarationBase + ExportableBase + ModifiersBase + name *IdentifierNode + members []*EnumMemberNode +} + +func (f *NodeFactory) NewEnumDeclaration(modifiers *ModifierListNode, name *IdentifierNode, members []*EnumMemberNode) *Node { + data := &EnumDeclaration{} + data.modifiers = modifiers + data.name = name + data.members = members + return f.NewNode(SyntaxKindEnumDeclaration, data) +} + +func (node *EnumDeclaration) ForEachChild(v Visitor) bool { + return visit(v, node.modifiers) || visit(v, node.name) || visitNodes(v, node.members) +} + +func (node *EnumDeclaration) Name() *DeclarationName { + return node.name +} + +func isEnumDeclaration(node *Node) bool { + return node.kind == SyntaxKindEnumDeclaration +} + +// ModuleBlock + +type ModuleBlock struct { + StatementBase + statements []*Statement +} + +func (f *NodeFactory) NewModuleBlock(statements []*Statement) *Node { + data := &ModuleBlock{} + data.statements = statements + return f.NewNode(SyntaxKindModuleBlock, data) +} + +func (node *ModuleBlock) ForEachChild(v Visitor) bool { + return visitNodes(v, node.statements) +} + +func isModuleBlock(node *Node) bool { + return node.kind == SyntaxKindModuleBlock +} + +// ModuleDeclaration + +type ModuleDeclaration struct { + StatementBase + DeclarationBase + ExportableBase + ModifiersBase + LocalsContainerBase + name *ModuleName + body *ModuleBody // Optional (may be nil in ambient module declaration) +} + +func (f *NodeFactory) NewModuleDeclaration(modifiers *ModifierListNode, name *ModuleName, body *ModuleBody, flags NodeFlags) *Node { + data := &ModuleDeclaration{} + data.modifiers = modifiers + data.name = name + data.body = body + node := f.NewNode(SyntaxKindModuleDeclaration, data) + node.flags |= flags & (NodeFlagsNamespace | NodeFlagsNestedNamespace | NodeFlagsGlobalAugmentation) + return node +} + +func (node *ModuleDeclaration) ForEachChild(v Visitor) bool { + return visit(v, node.modifiers) || visit(v, node.name) || visit(v, node.body) +} + +func (node *ModuleDeclaration) Name() *DeclarationName { + return node.name +} + +func isModuleDeclaration(node *Node) bool { + return node.kind == SyntaxKindModuleDeclaration +} + +// ModuleEqualsDeclaration + +type ImportEqualsDeclaration struct { + StatementBase + DeclarationBase + ExportableBase + ModifiersBase + modifiers *ModifierListNode + isTypeOnly bool + name *IdentifierNode + // 'EntityName' for an internal module reference, 'ExternalModuleReference' for an external + // module reference. + moduleReference *ModuleReference +} + +func (f *NodeFactory) NewImportEqualsDeclaration(modifiers *ModifierListNode, isTypeOnly bool, name *IdentifierNode, moduleReference *ModuleReference) *Node { + data := &ImportEqualsDeclaration{} + data.modifiers = modifiers + data.isTypeOnly = isTypeOnly + data.name = name + data.moduleReference = moduleReference + return f.NewNode(SyntaxKindImportEqualsDeclaration, data) +} + +func (node *ImportEqualsDeclaration) ForEachChild(v Visitor) bool { + return visit(v, node.modifiers) || visit(v, node.name) || visit(v, node.moduleReference) +} + +func (node *ImportEqualsDeclaration) Name() *DeclarationName { + return node.name +} + +func isImportEqualsDeclaration(node *Node) bool { + return node.kind == SyntaxKindImportEqualsDeclaration +} + +// ImportDeclaration + +type ImportDeclaration struct { + StatementBase + ModifiersBase + importClause *ImportClauseNode + moduleSpecifier *Node + attributes *ImportAttributesNode +} + +func (f *NodeFactory) NewImportDeclaration(modifiers *ModifierListNode, importClause *ImportClauseNode, moduleSpecifier *Node, attributes *ImportAttributesNode) *Node { + data := &ImportDeclaration{} + data.modifiers = modifiers + data.importClause = importClause + data.moduleSpecifier = moduleSpecifier + data.attributes = attributes + return f.NewNode(SyntaxKindImportDeclaration, data) +} + +func (node *ImportDeclaration) ForEachChild(v Visitor) bool { + return visit(v, node.modifiers) || visit(v, node.importClause) || visit(v, node.moduleSpecifier) || visit(v, node.attributes) +} + +func isImportDeclaration(node *Node) bool { + return node.kind == SyntaxKindImportDeclaration +} + +// ImportSpecifier + +type ImportSpecifier struct { + NodeBase + DeclarationBase + ExportableBase + isTypeOnly bool + propertyName *ModuleExportName + name *IdentifierNode +} + +func (f *NodeFactory) NewImportSpecifier(isTypeOnly bool, propertyName *ModuleExportName, name *IdentifierNode) *Node { + data := &ImportSpecifier{} + data.isTypeOnly = isTypeOnly + data.propertyName = propertyName + data.name = name + return f.NewNode(SyntaxKindImportSpecifier, data) +} + +func (node *ImportSpecifier) ForEachChild(v Visitor) bool { + return visit(v, node.propertyName) || visit(v, node.name) +} + +func (node *ImportSpecifier) Name() *DeclarationName { + return node.name +} + +func isImportSpecifier(node *Node) bool { + return node.kind == SyntaxKindImportSpecifier +} + +// ExternalModuleReference + +type ExternalModuleReference struct { + NodeBase + expression *Node +} + +func (f *NodeFactory) NewExternalModuleReference(expression *Node) *Node { + data := &ExternalModuleReference{} + data.expression = expression + return f.NewNode(SyntaxKindExternalModuleReference, data) +} + +func (node *ExternalModuleReference) ForEachChild(v Visitor) bool { + return visit(v, node.expression) +} + +func isExternalModuleReference(node *Node) bool { + return node.kind == SyntaxKindExternalModuleReference +} + +// ImportClause + +type ImportClause struct { + NodeBase + DeclarationBase + ExportableBase + isTypeOnly bool + namedBindings *NamedImportBindings // Optional, named bindings + name *IdentifierNode // Optional, default binding +} + +func (f *NodeFactory) NewImportClause(isTypeOnly bool, name *IdentifierNode, namedBindings *NamedImportBindings) *Node { + data := &ImportClause{} + data.isTypeOnly = isTypeOnly + data.name = name + data.namedBindings = namedBindings + return f.NewNode(SyntaxKindImportClause, data) +} + +func (node *ImportClause) ForEachChild(v Visitor) bool { + return visit(v, node.name) || visit(v, node.namedBindings) +} + +func (node *ImportClause) Name() *DeclarationName { + return node.name +} + +// NamespaceImport + +type NamespaceImport struct { + NodeBase + DeclarationBase + ExportableBase + name *IdentifierNode +} + +func (f *NodeFactory) NewNamespaceImport(name *IdentifierNode) *Node { + data := &NamespaceImport{} + data.name = name + return f.NewNode(SyntaxKindNamespaceImport, data) +} + +func (node *NamespaceImport) ForEachChild(v Visitor) bool { + return visit(v, node.name) +} + +func (node *NamespaceImport) Name() *DeclarationName { + return node.name +} + +func isNamespaceImport(node *Node) bool { + return node.kind == SyntaxKindNamespaceImport +} + +// NamedImports + +type NamedImports struct { + NodeBase + elements []*ImportSpecifierNode +} + +func (f *NodeFactory) NewNamedImports(elements []*ImportSpecifierNode) *Node { + data := &NamedImports{} + data.elements = elements + return f.NewNode(SyntaxKindNamedImports, data) +} + +func (node *NamedImports) ForEachChild(v Visitor) bool { + return visitNodes(v, node.elements) +} + +// ExportAssignment + +// This is either an `export =` or an `export default` declaration. +// Unless `isExportEquals` is set, this node was parsed as an `export default`. +type ExportAssignment struct { + StatementBase + DeclarationBase + ModifiersBase + isExportEquals bool + expression *Node +} + +func (f *NodeFactory) NewExportAssignment(modifiers *ModifierListNode, isExportEquals bool, expression *Node) *Node { + data := &ExportAssignment{} + data.modifiers = modifiers + data.isExportEquals = isExportEquals + data.expression = expression + return f.NewNode(SyntaxKindExportAssignment, data) +} + +func (node *ExportAssignment) ForEachChild(v Visitor) bool { + return visit(v, node.modifiers) || visit(v, node.expression) +} + +func isExportAssignment(node *Node) bool { + return node.kind == SyntaxKindExportAssignment +} + +// NamespaceExportDeclaration + +type NamespaceExportDeclaration struct { + StatementBase + DeclarationBase + ModifiersBase + name *IdentifierNode +} + +func (f *NodeFactory) NewNamespaceExportDeclaration(modifiers *ModifierListNode, name *IdentifierNode) *Node { + data := &NamespaceExportDeclaration{} + data.modifiers = modifiers + data.name = name + return f.NewNode(SyntaxKindNamespaceExportDeclaration, data) +} + +func (node *NamespaceExportDeclaration) ForEachChild(v Visitor) bool { + return visit(v, node.modifiers) || visit(v, node.name) +} + +func (node *NamespaceExportDeclaration) Name() *DeclarationName { + return node.name +} + +func isNamespaceExportDeclaration(node *Node) bool { + return node.kind == SyntaxKindNamespaceExportDeclaration +} + +// ExportDeclaration + +type ExportDeclaration struct { + StatementBase + DeclarationBase + ModifiersBase + isTypeOnly bool + exportClause *NamedExportBindings // Optional + moduleSpecifier *Node // Optional + attributes *ImportAttributesNode // Optional +} + +func (f *NodeFactory) NewExportDeclaration(modifiers *ModifierListNode, isTypeOnly bool, exportClause *NamedExportBindings, moduleSpecifier *Node, attributes *ImportAttributesNode) *Node { + data := &ExportDeclaration{} + data.modifiers = modifiers + data.isTypeOnly = isTypeOnly + data.exportClause = exportClause + data.moduleSpecifier = moduleSpecifier + data.attributes = attributes + return f.NewNode(SyntaxKindExportDeclaration, data) +} + +func (node *ExportDeclaration) ForEachChild(v Visitor) bool { + return visit(v, node.modifiers) || visit(v, node.exportClause) || visit(v, node.moduleSpecifier) || visit(v, node.attributes) +} + +func isExportDeclaration(node *Node) bool { + return node.kind == SyntaxKindExportDeclaration +} + +// NamespaceExport + +type NamespaceExport struct { + NodeBase + DeclarationBase + name *ModuleExportName +} + +func (f *NodeFactory) NewNamespaceExport(name *ModuleExportName) *Node { + data := &NamespaceExport{} + data.name = name + return f.NewNode(SyntaxKindNamespaceExport, data) +} + +func (node *NamespaceExport) ForEachChild(v Visitor) bool { + return visit(v, node.name) +} + +func (node *NamespaceExport) Name() *DeclarationName { + return node.name +} + +func isNamespaceExport(node *Node) bool { + return node.kind == SyntaxKindNamespaceExport +} + +// NamedExports + +type NamedExports struct { + NodeBase + elements []*ExportSpecifierNode +} + +func (f *NodeFactory) NewNamedExports(elements []*ExportSpecifierNode) *Node { + data := &NamedExports{} + data.elements = elements + return f.NewNode(SyntaxKindNamedExports, data) +} + +func (node *NamedExports) ForEachChild(v Visitor) bool { + return visitNodes(v, node.elements) +} + +// ExportSpecifier + +type ExportSpecifier struct { + NodeBase + DeclarationBase + ExportableBase + isTypeOnly bool + propertyName *ModuleExportName // Optional, name preceding 'as' keyword + name *ModuleExportName +} + +func (f *NodeFactory) NewExportSpecifier(isTypeOnly bool, propertyName *ModuleExportName, name *ModuleExportName) *Node { + data := &ExportSpecifier{} + data.isTypeOnly = isTypeOnly + data.propertyName = propertyName + data.name = name + return f.NewNode(SyntaxKindExportSpecifier, data) +} + +func (node *ExportSpecifier) ForEachChild(v Visitor) bool { + return visit(v, node.propertyName) || visit(v, node.name) +} + +func (node *ExportSpecifier) Name() *DeclarationName { + return node.name +} + +func isExportSpecifier(node *Node) bool { + return node.kind == SyntaxKindExportSpecifier +} + +// TypeElementBase + +type TypeElementBase struct{} + +// ClassElementBase + +type ClassElementBase struct{} + +// NamedMemberBase + +type NamedMemberBase struct { + DeclarationBase + ModifiersBase + name *PropertyName + postfixToken *TokenNode +} + +func (node *NamedMemberBase) Symbol() *Symbol { return node.symbol } +func (node *NamedMemberBase) DeclarationData() *DeclarationBase { return &node.DeclarationBase } +func (node *NamedMemberBase) Modifiers() *ModifierListNode { return node.modifiers } +func (node *NamedMemberBase) Name() *DeclarationName { return node.name } + +// CallSignatureDeclaration + +type CallSignatureDeclaration struct { + NodeBase + DeclarationBase + FunctionLikeBase + TypeElementBase +} + +func (f *NodeFactory) NewCallSignatureDeclaration(typeParameters *TypeParameterListNode, parameters []*ParameterDeclarationNode, returnType *Node) *Node { + data := &CallSignatureDeclaration{} + data.typeParameters = typeParameters + data.parameters = parameters + data.returnType = returnType + return f.NewNode(SyntaxKindCallSignature, data) +} + +func (node *CallSignatureDeclaration) ForEachChild(v Visitor) bool { + return visit(v, node.typeParameters) || visitNodes(v, node.parameters) || visit(v, node.returnType) +} + +func isCallSignatureDeclaration(node *Node) bool { + return node.kind == SyntaxKindCallSignature +} + +// ConstructSignatureDeclaration + +type ConstructSignatureDeclaration struct { + NodeBase + DeclarationBase + FunctionLikeBase + TypeElementBase +} + +func (f *NodeFactory) NewConstructSignatureDeclaration(typeParameters *TypeParameterListNode, parameters []*ParameterDeclarationNode, returnType *Node) *Node { + data := &ConstructSignatureDeclaration{} + data.typeParameters = typeParameters + data.parameters = parameters + data.returnType = returnType + return f.NewNode(SyntaxKindConstructSignature, data) +} + +func (node *ConstructSignatureDeclaration) ForEachChild(v Visitor) bool { + return visit(v, node.typeParameters) || visitNodes(v, node.parameters) || visit(v, node.returnType) +} + +// ConstructorDeclaration + +type ConstructorDeclaration struct { + NodeBase + DeclarationBase + ModifiersBase + FunctionLikeWithBodyBase + ClassElementBase + returnFlowNode *FlowNode +} + +func (f *NodeFactory) NewConstructorDeclaration(modifiers *ModifierListNode, typeParameters *TypeParameterListNode, parameters []*ParameterDeclarationNode, returnType *Node, body *BlockNode) *Node { + data := &ConstructorDeclaration{} + data.modifiers = modifiers + data.typeParameters = typeParameters + data.parameters = parameters + data.returnType = returnType + data.body = body + return f.NewNode(SyntaxKindConstructor, data) +} + +func (node *ConstructorDeclaration) ForEachChild(v Visitor) bool { + return visit(v, node.modifiers) || visit(v, node.typeParameters) || visitNodes(v, node.parameters) || visit(v, node.returnType) || visit(v, node.body) +} + +func isConstructorDeclaration(node *Node) bool { + return node.kind == SyntaxKindConstructor +} + +// AccessorDeclarationBase + +type AccessorDeclarationBase struct { + NodeBase + NamedMemberBase + FunctionLikeWithBodyBase + FlowNodeBase + TypeElementBase + ClassElementBase + ObjectLiteralElementBase +} + +func (node *AccessorDeclarationBase) ForEachChild(v Visitor) bool { + return visit(v, node.modifiers) || visit(v, node.name) || visit(v, node.typeParameters) || visitNodes(v, node.parameters) || + visit(v, node.returnType) || visit(v, node.body) +} + +func (node *AccessorDeclarationBase) isAccessorDeclaration() {} + +// GetAccessorDeclaration + +type GetAccessorDeclaration struct { + AccessorDeclarationBase +} + +func (f *NodeFactory) NewGetAccessorDeclaration(modifiers *ModifierListNode, name *PropertyName, typeParameters *TypeParameterListNode, parameters []*ParameterDeclarationNode, returnType *Node, body *BlockNode) *Node { + data := &GetAccessorDeclaration{} + data.modifiers = modifiers + data.name = name + data.typeParameters = typeParameters + data.parameters = parameters + data.returnType = returnType + data.body = body + return f.NewNode(SyntaxKindGetAccessor, data) +} + +func isGetAccessorDeclaration(node *Node) bool { + return node.kind == SyntaxKindGetAccessor +} + +// SetAccessorDeclaration + +type SetAccessorDeclaration struct { + AccessorDeclarationBase +} + +func (f *NodeFactory) NewSetAccessorDeclaration(modifiers *ModifierListNode, name *PropertyName, typeParameters *TypeParameterListNode, parameters []*ParameterDeclarationNode, returnType *Node, body *BlockNode) *Node { + data := &SetAccessorDeclaration{} + data.modifiers = modifiers + data.name = name + data.typeParameters = typeParameters + data.parameters = parameters + data.returnType = returnType + data.body = body + return f.NewNode(SyntaxKindSetAccessor, data) +} + +func isSetAccessorDeclaration(node *Node) bool { + return node.kind == SyntaxKindSetAccessor +} + +// IndexSignatureDeclaration + +type IndexSignatureDeclaration struct { + NodeBase + DeclarationBase + ModifiersBase + FunctionLikeBase + TypeElementBase + ClassElementBase +} + +func (f *NodeFactory) NewIndexSignatureDeclaration(modifiers *Node, parameters []*Node, returnType *Node) *Node { + data := &IndexSignatureDeclaration{} + data.modifiers = modifiers + data.parameters = parameters + data.returnType = returnType + return f.NewNode(SyntaxKindIndexSignature, data) +} + +func (node *IndexSignatureDeclaration) ForEachChild(v Visitor) bool { + return visit(v, node.modifiers) || visitNodes(v, node.parameters) || visit(v, node.returnType) +} + +// MethodSignatureDeclaration + +type MethodSignatureDeclaration struct { + NodeBase + NamedMemberBase + FunctionLikeBase + TypeElementBase +} + +func (f *NodeFactory) NewMethodSignatureDeclaration(modifiers *Node, name *Node, postfixToken *Node, typeParameters *Node, parameters []*Node, returnType *Node) *Node { + data := &MethodSignatureDeclaration{} + data.modifiers = modifiers + data.name = name + data.postfixToken = postfixToken + data.typeParameters = typeParameters + data.parameters = parameters + data.returnType = returnType + return f.NewNode(SyntaxKindMethodSignature, data) +} + +func (node *MethodSignatureDeclaration) ForEachChild(v Visitor) bool { + return visit(v, node.modifiers) || visit(v, node.name) || visit(v, node.postfixToken) || visit(v, node.typeParameters) || + visitNodes(v, node.parameters) || visit(v, node.returnType) +} + +func isMethodSignatureDeclaration(node *Node) bool { + return node.kind == SyntaxKindMethodSignature +} + +// MethodSignatureDeclaration + +type MethodDeclaration struct { + NodeBase + NamedMemberBase + FunctionLikeWithBodyBase + FlowNodeBase + ClassElementBase + ObjectLiteralElementBase +} + +func (f *NodeFactory) NewMethodDeclaration(modifiers *Node, asteriskToken *Node, name *Node, postfixToken *Node, typeParameters *Node, parameters []*Node, returnType *Node, body *Node) *Node { + data := &MethodDeclaration{} + data.modifiers = modifiers + data.asteriskToken = asteriskToken + data.name = name + data.postfixToken = postfixToken + data.typeParameters = typeParameters + data.parameters = parameters + data.returnType = returnType + data.body = body + return f.NewNode(SyntaxKindMethodDeclaration, data) +} + +func (node *MethodDeclaration) ForEachChild(v Visitor) bool { + return visit(v, node.modifiers) || visit(v, node.asteriskToken) || visit(v, node.name) || visit(v, node.postfixToken) || + visit(v, node.typeParameters) || visitNodes(v, node.parameters) || visit(v, node.returnType) || visit(v, node.body) +} + +// PropertySignatureDeclaration + +type PropertySignatureDeclaration struct { + NodeBase + NamedMemberBase + TypeElementBase + typeNode *Node + initializer *Node // For error reporting purposes +} + +func (f *NodeFactory) NewPropertySignatureDeclaration(modifiers *Node, name *Node, postfixToken *Node, typeNode *Node, initializer *Node) *Node { + data := &PropertySignatureDeclaration{} + data.modifiers = modifiers + data.name = name + data.postfixToken = postfixToken + data.typeNode = typeNode + data.initializer = initializer + return f.NewNode(SyntaxKindPropertySignature, data) +} + +func (node *PropertySignatureDeclaration) ForEachChild(v Visitor) bool { + return visit(v, node.modifiers) || visit(v, node.name) || visit(v, node.postfixToken) || visit(v, node.typeNode) || visit(v, node.initializer) +} + +func isPropertySignatureDeclaration(node *Node) bool { + return node.kind == SyntaxKindPropertySignature +} + +// PropertyDeclaration + +type PropertyDeclaration struct { + NodeBase + NamedMemberBase + ClassElementBase + typeNode *Node // Optional + initializer *Node // Optional +} + +func (f *NodeFactory) NewPropertyDeclaration(modifiers *Node, name *Node, postfixToken *Node, typeNode *Node, initializer *Node) *Node { + data := &PropertyDeclaration{} + data.modifiers = modifiers + data.name = name + data.postfixToken = postfixToken + data.typeNode = typeNode + data.initializer = initializer + return f.NewNode(SyntaxKindPropertyDeclaration, data) +} + +func (node *PropertyDeclaration) ForEachChild(v Visitor) bool { + return visit(v, node.modifiers) || visit(v, node.name) || visit(v, node.postfixToken) || visit(v, node.typeNode) || visit(v, node.initializer) +} + +func isPropertyDeclaration(node *Node) bool { + return node.kind == SyntaxKindPropertyDeclaration +} + +// SemicolonClassElement + +type SemicolonClassElement struct { + NodeBase + DeclarationBase + ClassElementBase +} + +func (f *NodeFactory) NewSemicolonClassElement() *Node { + return f.NewNode(SyntaxKindSemicolonClassElement, &SemicolonClassElement{}) +} + +// ClassStaticBlockDeclaration + +type ClassStaticBlockDeclaration struct { + NodeBase + DeclarationBase + ModifiersBase + LocalsContainerBase + ClassElementBase + body *Node +} + +func (f *NodeFactory) NewClassStaticBlockDeclaration(modifiers *Node, body *Node) *Node { + data := &ClassStaticBlockDeclaration{} + data.modifiers = modifiers + data.body = body + return f.NewNode(SyntaxKindClassStaticBlockDeclaration, data) +} + +func (node *ClassStaticBlockDeclaration) ForEachChild(v Visitor) bool { + return visit(v, node.modifiers) || visit(v, node.body) +} + +func isClassStaticBlockDeclaration(node *Node) bool { + return node.kind == SyntaxKindClassStaticBlockDeclaration +} + +// TypeParameterList + +type TypeParameterList struct { + NodeBase + parameters []*Node +} + +func (f *NodeFactory) NewTypeParameterList(parameters []*Node) *Node { + data := &TypeParameterList{} + data.parameters = parameters + return f.NewNode(SyntaxKindTypeParameterList, data) +} + +func (node *TypeParameterList) ForEachChild(v Visitor) bool { + return visitNodes(v, node.parameters) +} + +// ExpressionBase + +type ExpressionBase struct { + NodeBase +} + +// OmittedExpression + +type OmittedExpression struct { + ExpressionBase +} + +func (f *NodeFactory) NewOmittedExpression() *Node { + return f.NewNode(SyntaxKindOmittedExpression, &OmittedExpression{}) +} + +// KeywordExpression + +type KeywordExpression struct { + ExpressionBase + FlowNodeBase // For 'this' and 'super' expressions +} + +func (f *NodeFactory) NewKeywordExpression(kind SyntaxKind) *Node { + return f.NewNode(kind, &KeywordExpression{}) +} + +// LiteralLikeBase + +type LiteralLikeBase struct { + text string +} + +// StringLiteral + +type StringLiteral struct { + ExpressionBase + LiteralLikeBase +} + +func (f *NodeFactory) NewStringLiteral(text string) *Node { + data := &StringLiteral{} + data.text = text + return f.NewNode(SyntaxKindStringLiteral, data) +} + +func isStringLiteral(node *Node) bool { + return node.kind == SyntaxKindStringLiteral +} + +// NumericLiteral + +type NumericLiteral struct { + ExpressionBase + LiteralLikeBase +} + +func (f *NodeFactory) NewNumericLiteral(text string) *Node { + data := &NumericLiteral{} + data.text = text + return f.NewNode(SyntaxKindNumericLiteral, data) +} + +// BigintLiteral + +type BigintLiteral struct { + ExpressionBase + LiteralLikeBase +} + +func (f *NodeFactory) NewBigintLiteral(text string) *Node { + data := &BigintLiteral{} + data.text = text + return f.NewNode(SyntaxKindBigintLiteral, data) +} + +// RegularExpressionLiteral + +type RegularExpressionLiteral struct { + ExpressionBase + LiteralLikeBase +} + +func (f *NodeFactory) NewRegularExpressionLiteral(text string) *Node { + data := &RegularExpressionLiteral{} + data.text = text + return f.NewNode(SyntaxKindRegularExpressionLiteral, data) +} + +// NoSubstitutionTemplateLiteral + +type NoSubstitutionTemplateLiteral struct { + ExpressionBase + TemplateLiteralLikeBase +} + +func (f *NodeFactory) NewNoSubstitutionTemplateLiteral(text string) *Node { + data := &NoSubstitutionTemplateLiteral{} + data.text = text + return f.NewNode(SyntaxKindNoSubstitutionTemplateLiteral, data) +} + +// BinaryExpression + +type BinaryExpression struct { + ExpressionBase + DeclarationBase + left *Node + operatorToken *Node + right *Node +} + +func (f *NodeFactory) NewBinaryExpression(left *Node, operatorToken *Node, right *Node) *Node { + data := &BinaryExpression{} + data.left = left + data.operatorToken = operatorToken + data.right = right + return f.NewNode(SyntaxKindBinaryExpression, data) +} + +func (node *BinaryExpression) ForEachChild(v Visitor) bool { + return visit(v, node.left) || visit(v, node.operatorToken) || visit(v, node.right) +} + +// PrefixUnaryExpression + +type PrefixUnaryExpression struct { + ExpressionBase + operator SyntaxKind + operand *Node +} + +func (f *NodeFactory) NewPrefixUnaryExpression(operator SyntaxKind, operand *Node) *Node { + data := &PrefixUnaryExpression{} + data.operator = operator + data.operand = operand + return f.NewNode(SyntaxKindPrefixUnaryExpression, data) +} + +func (node *PrefixUnaryExpression) ForEachChild(v Visitor) bool { + return visit(v, node.operand) +} + +func isPrefixUnaryExpression(node *Node) bool { + return node.kind == SyntaxKindPrefixUnaryExpression +} + +// PostfixUnaryExpression + +type PostfixUnaryExpression struct { + ExpressionBase + operand *Node + operator SyntaxKind +} + +func (f *NodeFactory) NewPostfixUnaryExpression(operand *Node, operator SyntaxKind) *Node { + data := &PostfixUnaryExpression{} + data.operand = operand + data.operator = operator + return f.NewNode(SyntaxKindPostfixUnaryExpression, data) +} + +func (node *PostfixUnaryExpression) ForEachChild(v Visitor) bool { + return visit(v, node.operand) +} + +// YieldExpression + +type YieldExpression struct { + ExpressionBase + asteriskToken *Node + expression *Node // Optional +} + +func (f *NodeFactory) NewYieldExpression(asteriskToken *Node, expression *Node) *Node { + data := &YieldExpression{} + data.asteriskToken = asteriskToken + data.expression = expression + return f.NewNode(SyntaxKindYieldExpression, data) +} + +func (node *YieldExpression) ForEachChild(v Visitor) bool { + return visit(v, node.asteriskToken) || visit(v, node.expression) +} + +// ArrowFunction + +type ArrowFunction struct { + ExpressionBase + DeclarationBase + ModifiersBase + FunctionLikeWithBodyBase + FlowNodeBase + equalsGreaterThanToken *Node +} + +func (f *NodeFactory) NewArrowFunction(modifiers *Node, typeParameters *Node, parameters []*Node, returnType *Node, equalsGreaterThanToken *Node, body *Node) *Node { + data := &ArrowFunction{} + data.modifiers = modifiers + data.typeParameters = typeParameters + data.parameters = parameters + data.returnType = returnType + data.equalsGreaterThanToken = equalsGreaterThanToken + data.body = body + return f.NewNode(SyntaxKindArrowFunction, data) +} + +func (node *ArrowFunction) ForEachChild(v Visitor) bool { + return visit(v, node.modifiers) || visit(v, node.typeParameters) || visitNodes(v, node.parameters) || + visit(v, node.returnType) || visit(v, node.equalsGreaterThanToken) || visit(v, node.body) +} + +func (node *ArrowFunction) Name() *DeclarationName { + return nil +} + +func isArrowFunction(node *Node) bool { + return node.kind == SyntaxKindArrowFunction +} + +// FunctionExpression + +type FunctionExpression struct { + ExpressionBase + DeclarationBase + ModifiersBase + FunctionLikeWithBodyBase + FlowNodeBase + name *Node // Optional + returnFlowNode *FlowNode +} + +func (f *NodeFactory) NewFunctionExpression(modifiers *Node, asteriskToken *Node, name *Node, typeParameters *Node, parameters []*Node, returnType *Node, body *Node) *Node { + data := &FunctionExpression{} + data.modifiers = modifiers + data.asteriskToken = asteriskToken + data.name = name + data.typeParameters = typeParameters + data.parameters = parameters + data.returnType = returnType + data.body = body + return f.NewNode(SyntaxKindFunctionExpression, data) +} + +func (node *FunctionExpression) ForEachChild(v Visitor) bool { + return visit(v, node.modifiers) || visit(v, node.asteriskToken) || visit(v, node.name) || visit(v, node.typeParameters) || + visitNodes(v, node.parameters) || visit(v, node.returnType) || visit(v, node.body) +} + +func (node *FunctionExpression) Name() *DeclarationName { + return node.name +} + +func isFunctionExpression(node *Node) bool { + return node.kind == SyntaxKindFunctionExpression +} + +// AsExpression + +type AsExpression struct { + ExpressionBase + expression *Node + typeNode *Node +} + +func (f *NodeFactory) NewAsExpression(expression *Node, typeNode *Node) *Node { + data := &AsExpression{} + data.expression = expression + data.typeNode = typeNode + return f.NewNode(SyntaxKindAsExpression, data) +} + +func (node *AsExpression) ForEachChild(v Visitor) bool { + return visit(v, node.expression) || visit(v, node.typeNode) +} + +// SatisfiesExpression + +type SatisfiesExpression struct { + ExpressionBase + expression *Node + typeNode *Node +} + +func (f *NodeFactory) NewSatisfiesExpression(expression *Node, typeNode *Node) *Node { + data := &SatisfiesExpression{} + data.expression = expression + data.typeNode = typeNode + return f.NewNode(SyntaxKindSatisfiesExpression, data) +} + +func (node *SatisfiesExpression) ForEachChild(v Visitor) bool { + return visit(v, node.expression) || visit(v, node.typeNode) +} + +// ConditionalExpression + +type ConditionalExpression struct { + ExpressionBase + condition *Node + questionToken *Node + whenTrue *Node + colonToken *Node + whenFalse *Node +} + +func (f *NodeFactory) NewConditionalExpression(condition *Node, questionToken *Node, whenTrue *Node, colonToken *Node, whenFalse *Node) *Node { + data := &ConditionalExpression{} + data.condition = condition + data.questionToken = questionToken + data.whenTrue = whenTrue + data.colonToken = colonToken + data.whenFalse = whenFalse + return f.NewNode(SyntaxKindConditionalExpression, data) +} + +func (node *ConditionalExpression) ForEachChild(v Visitor) bool { + return visit(v, node.condition) || visit(v, node.questionToken) || visit(v, node.whenTrue) || + visit(v, node.colonToken) || visit(v, node.whenFalse) +} + +// PropertyAccessExpression + +type PropertyAccessExpression struct { + ExpressionBase + FlowNodeBase + expression *Node + questionDotToken *Node + name *Node +} + +func (f *NodeFactory) NewPropertyAccessExpression(expression *Node, questionDotToken *Node, name *Node, flags NodeFlags) *Node { + data := &PropertyAccessExpression{} + data.expression = expression + data.questionDotToken = questionDotToken + data.name = name + node := f.NewNode(SyntaxKindPropertyAccessExpression, data) + node.flags |= flags & NodeFlagsOptionalChain + return node +} + +func (node *PropertyAccessExpression) ForEachChild(v Visitor) bool { + return visit(v, node.expression) || visit(v, node.questionDotToken) || visit(v, node.name) +} + +func (node *PropertyAccessExpression) Name() *DeclarationName { return node.name } + +func isPropertyAccessExpression(node *Node) bool { + return node.kind == SyntaxKindPropertyAccessExpression +} + +// ElementAccessExpression + +type ElementAccessExpression struct { + ExpressionBase + FlowNodeBase + expression *Node + questionDotToken *Node + argumentExpression *Node +} + +func (f *NodeFactory) NewElementAccessExpression(expression *Node, questionDotToken *Node, argumentExpression *Node, flags NodeFlags) *Node { + data := &ElementAccessExpression{} + data.expression = expression + data.questionDotToken = questionDotToken + data.argumentExpression = argumentExpression + node := f.NewNode(SyntaxKindElementAccessExpression, data) + node.flags |= flags & NodeFlagsOptionalChain + return node +} + +func (node *ElementAccessExpression) ForEachChild(v Visitor) bool { + return visit(v, node.expression) || visit(v, node.questionDotToken) || visit(v, node.argumentExpression) +} + +func isElementAccessExpression(node *Node) bool { + return node.kind == SyntaxKindElementAccessExpression +} + +// CallExpression + +type CallExpression struct { + ExpressionBase + expression *Node + questionDotToken *Node + typeArguments *Node + arguments []*Node +} + +func (f *NodeFactory) NewCallExpression(expression *Node, questionDotToken *Node, typeArguments *Node, arguments []*Node, flags NodeFlags) *Node { + data := &CallExpression{} + data.expression = expression + data.questionDotToken = questionDotToken + data.typeArguments = typeArguments + data.arguments = arguments + node := f.NewNode(SyntaxKindCallExpression, data) + node.flags |= flags & NodeFlagsOptionalChain + return node +} + +func (node *CallExpression) ForEachChild(v Visitor) bool { + return visit(v, node.expression) || visit(v, node.questionDotToken) || visit(v, node.typeArguments) || visitNodes(v, node.arguments) +} + +func isCallExpression(node *Node) bool { + return node.kind == SyntaxKindCallExpression +} + +// NewExpression + +type NewExpression struct { + ExpressionBase + expression *Node + typeArguments *Node + arguments []*Node +} + +func (f *NodeFactory) NewNewExpression(expression *Node, typeArguments *Node, arguments []*Node) *Node { + data := &NewExpression{} + data.expression = expression + data.typeArguments = typeArguments + data.arguments = arguments + return f.NewNode(SyntaxKindNewExpression, data) +} + +func (node *NewExpression) ForEachChild(v Visitor) bool { + return visit(v, node.expression) || visit(v, node.typeArguments) || visitNodes(v, node.arguments) +} + +// MetaProperty + +type MetaProperty struct { + ExpressionBase + FlowNodeBase + keywordToken SyntaxKind + name *Node +} + +func (f *NodeFactory) NewMetaProperty(keywordToken SyntaxKind, name *Node) *Node { + data := &MetaProperty{} + data.keywordToken = keywordToken + data.name = name + return f.NewNode(SyntaxKindNewExpression, data) +} + +func (node *MetaProperty) ForEachChild(v Visitor) bool { + return visit(v, node.name) +} + +func isMetaProperty(node *Node) bool { + return node.kind == SyntaxKindMetaProperty +} + +// NonNullExpression + +type NonNullExpression struct { + ExpressionBase + expression *Node +} + +func (f *NodeFactory) NewNonNullExpression(expression *Node) *Node { + data := &NonNullExpression{} + data.expression = expression + return f.NewNode(SyntaxKindNewExpression, data) +} + +func (node *NonNullExpression) ForEachChild(v Visitor) bool { + return visit(v, node.expression) +} + +// SpreadElement + +type SpreadElement struct { + ExpressionBase + expression *Node +} + +func (f *NodeFactory) NewSpreadElement(expression *Node) *Node { + data := &SpreadElement{} + data.expression = expression + return f.NewNode(SyntaxKindSpreadElement, data) +} + +func (node *SpreadElement) ForEachChild(v Visitor) bool { + return visit(v, node.expression) +} + +// TemplateExpression + +type TemplateExpression struct { + ExpressionBase + head *Node + templateSpans []*Node +} + +func (f *NodeFactory) NewTemplateExpression(head *Node, templateSpans []*Node) *Node { + data := &TemplateExpression{} + data.head = head + data.templateSpans = templateSpans + return f.NewNode(SyntaxKindTemplateExpression, data) +} + +func (node *TemplateExpression) ForEachChild(v Visitor) bool { + return visit(v, node.head) || visitNodes(v, node.templateSpans) +} + +// TemplateLiteralTypeSpan + +type TemplateSpan struct { + NodeBase + expression *Node + literal *Node +} + +func (f *NodeFactory) NewTemplateSpan(expression *Node, literal *Node) *Node { + data := &TemplateSpan{} + data.expression = expression + data.literal = literal + return f.NewNode(SyntaxKindTemplateSpan, data) +} + +func (node *TemplateSpan) ForEachChild(v Visitor) bool { + return visit(v, node.expression) || visit(v, node.literal) +} + +// TaggedTemplateExpression + +type TaggedTemplateExpression struct { + ExpressionBase + tag *Node + questionDotToken *Node // For error reporting purposes only + typeArguments *Node + template *Node +} + +func (f *NodeFactory) NewTaggedTemplateExpression(tag *Node, questionDotToken *Node, typeArguments *Node, template *Node, flags NodeFlags) *Node { + data := &TaggedTemplateExpression{} + data.tag = tag + data.questionDotToken = questionDotToken + data.typeArguments = typeArguments + data.template = template + node := f.NewNode(SyntaxKindTemplateExpression, data) + node.flags |= flags & NodeFlagsOptionalChain + return node +} + +func (node *TaggedTemplateExpression) ForEachChild(v Visitor) bool { + return visit(v, node.tag) || visit(v, node.questionDotToken) || visit(v, node.typeArguments) || visit(v, node.template) +} + +// ParenthesizedExpression + +type ParenthesizedExpression struct { + ExpressionBase + expression *Node +} + +func (f *NodeFactory) NewParenthesizedExpression(expression *Node) *Node { + data := &ParenthesizedExpression{} + data.expression = expression + return f.NewNode(SyntaxKindParenthesizedExpression, data) +} + +func (node *ParenthesizedExpression) ForEachChild(v Visitor) bool { + return visit(v, node.expression) +} + +func isParenthesizedExpression(node *Node) bool { + return node.kind == SyntaxKindParenthesizedExpression +} + +// ArrayLiteralExpression + +type ArrayLiteralExpression struct { + ExpressionBase + elements []*Node + multiLine bool +} + +func (f *NodeFactory) NewArrayLiteralExpression(elements []*Node, multiLine bool) *Node { + data := &ArrayLiteralExpression{} + data.elements = elements + data.multiLine = multiLine + return f.NewNode(SyntaxKindArrayLiteralExpression, data) +} + +func (node *ArrayLiteralExpression) ForEachChild(v Visitor) bool { + return visitNodes(v, node.elements) +} + +// ObjectLiteralExpression + +type ObjectLiteralExpression struct { + ExpressionBase + DeclarationBase + properties []*Node + multiLine bool +} + +func (f *NodeFactory) NewObjectLiteralExpression(properties []*Node, multiLine bool) *Node { + data := &ObjectLiteralExpression{} + data.properties = properties + data.multiLine = multiLine + return f.NewNode(SyntaxKindObjectLiteralExpression, data) + +} + +func (node *ObjectLiteralExpression) ForEachChild(v Visitor) bool { + return visitNodes(v, node.properties) +} + +func isObjectLiteralExpression(node *Node) bool { + return node.kind == SyntaxKindObjectLiteralExpression +} + +// ObjectLiteralElementBase + +type ObjectLiteralElementBase struct{} + +// SpreadAssignment + +type SpreadAssignment struct { + NodeBase + ObjectLiteralElementBase + expression *Node +} + +func (f *NodeFactory) NewSpreadAssignment(expression *Node) *Node { + data := &SpreadAssignment{} + data.expression = expression + return f.NewNode(SyntaxKindSpreadAssignment, data) +} + +func (node *SpreadAssignment) ForEachChild(v Visitor) bool { + return visit(v, node.expression) +} + +// PropertyAssignment + +type PropertyAssignment struct { + NodeBase + NamedMemberBase + ObjectLiteralElementBase + initializer *Node +} + +func (f *NodeFactory) NewPropertyAssignment(modifiers *Node, name *Node, postfixToken *Node, initializer *Node) *Node { + data := &PropertyAssignment{} + data.modifiers = modifiers + data.name = name + data.postfixToken = postfixToken + data.initializer = initializer + return f.NewNode(SyntaxKindPropertyAssignment, data) +} + +func (node *PropertyAssignment) ForEachChild(v Visitor) bool { + return visit(v, node.modifiers) || visit(v, node.name) || visit(v, node.postfixToken) || visit(v, node.initializer) +} + +func isPropertyAssignment(node *Node) bool { + return node.kind == SyntaxKindPropertyAssignment +} + +// ShorthandPropertyAssignment + +type ShorthandPropertyAssignment struct { + NodeBase + NamedMemberBase + ObjectLiteralElementBase + objectAssignmentInitializer *Node // Optional +} + +func (f *NodeFactory) NewShorthandPropertyAssignment(modifiers *Node, name *Node, postfixToken *Node, objectAssignmentInitializer *Node) *Node { + data := &ShorthandPropertyAssignment{} + data.modifiers = modifiers + data.name = name + data.postfixToken = postfixToken + data.objectAssignmentInitializer = objectAssignmentInitializer + return f.NewNode(SyntaxKindShorthandPropertyAssignment, data) +} + +func (node *ShorthandPropertyAssignment) ForEachChild(v Visitor) bool { + return visit(v, node.modifiers) || visit(v, node.name) || visit(v, node.postfixToken) || visit(v, node.objectAssignmentInitializer) +} + +func isShorthandPropertyAssignment(node *Node) bool { + return node.kind == SyntaxKindShorthandPropertyAssignment +} + +// DeleteExpression + +type DeleteExpression struct { + ExpressionBase + expression *Node +} + +func (f *NodeFactory) NewDeleteExpression(expression *Node) *Node { + data := &DeleteExpression{} + data.expression = expression + return f.NewNode(SyntaxKindDeleteExpression, data) + +} + +func (node *DeleteExpression) ForEachChild(v Visitor) bool { + return visit(v, node.expression) +} + +// TypeOfExpression + +type TypeOfExpression struct { + ExpressionBase + expression *Node +} + +func (f *NodeFactory) NewTypeOfExpression(expression *Node) *Node { + data := &TypeOfExpression{} + data.expression = expression + return f.NewNode(SyntaxKindTypeOfExpression, data) +} + +func (node *TypeOfExpression) ForEachChild(v Visitor) bool { + return visit(v, node.expression) +} + +func isTypeOfExpression(node *Node) bool { + return node.kind == SyntaxKindTypeOfExpression +} + +// VoidExpression + +type VoidExpression struct { + ExpressionBase + expression *Node +} + +func (f *NodeFactory) NewVoidExpression(expression *Node) *Node { + data := &VoidExpression{} + data.expression = expression + return f.NewNode(SyntaxKindVoidExpression, data) +} + +func (node *VoidExpression) ForEachChild(v Visitor) bool { + return visit(v, node.expression) +} + +// AwaitExpression + +type AwaitExpression struct { + ExpressionBase + expression *Node +} + +func (f *NodeFactory) NewAwaitExpression(expression *Node) *Node { + data := &AwaitExpression{} + data.expression = expression + return f.NewNode(SyntaxKindAwaitExpression, data) +} + +func (node *AwaitExpression) ForEachChild(v Visitor) bool { + return visit(v, node.expression) +} + +// TypeAssertion + +type TypeAssertion struct { + ExpressionBase + typeNode *Node + expression *Node +} + +func (f *NodeFactory) NewTypeAssertion(typeNode *Node, expression *Node) *Node { + data := &TypeAssertion{} + data.typeNode = typeNode + data.expression = expression + return f.NewNode(SyntaxKindTypeAssertionExpression, data) +} + +func (node *TypeAssertion) ForEachChild(v Visitor) bool { + return visit(v, node.typeNode) || visit(v, node.expression) +} + +// TypeNodeBase + +type TypeNodeBase struct { + NodeBase +} + +// KeywordTypeNode + +type KeywordTypeNode struct { + TypeNodeBase +} + +func (f *NodeFactory) NewKeywordTypeNode(kind SyntaxKind) *Node { + return f.NewNode(kind, &KeywordTypeNode{}) +} + +// UnionOrIntersectionTypeBase + +type UnionOrIntersectionTypeNodeBase struct { + TypeNodeBase + types []*Node +} + +func (node *UnionOrIntersectionTypeNodeBase) ForEachChild(v Visitor) bool { + return visitNodes(v, node.types) +} + +// UnionTypeNode + +type UnionTypeNode struct { + UnionOrIntersectionTypeNodeBase +} + +func (f *NodeFactory) NewUnionTypeNode(types []*Node) *Node { + data := &UnionTypeNode{} + data.types = types + return f.NewNode(SyntaxKindUnionType, data) +} + +// IntersectionTypeNode + +type IntersectionTypeNode struct { + UnionOrIntersectionTypeNodeBase +} + +func (f *NodeFactory) NewIntersectionTypeNode(types []*Node) *Node { + data := &IntersectionTypeNode{} + data.types = types + return f.NewNode(SyntaxKindIntersectionType, data) +} + +// ConditionalTypeNode + +type ConditionalTypeNode struct { + TypeNodeBase + LocalsContainerBase + checkType *Node + extendsType *Node + trueType *Node + falseType *Node +} + +func (f *NodeFactory) NewConditionalTypeNode(checkType *Node, extendsType *Node, trueType *Node, falseType *Node) *Node { + data := &ConditionalTypeNode{} + data.checkType = checkType + data.extendsType = extendsType + data.trueType = trueType + data.falseType = falseType + return f.NewNode(SyntaxKindConditionalType, data) +} + +func (node *ConditionalTypeNode) ForEachChild(v Visitor) bool { + return visit(v, node.checkType) || visit(v, node.extendsType) || visit(v, node.trueType) || visit(v, node.falseType) +} + +func isConditionalTypeNode(node *Node) bool { + return node.kind == SyntaxKindConditionalType +} + +// TypeOperatorNode + +type TypeOperatorNode struct { + TypeNodeBase + operator SyntaxKind // SyntaxKindKeyOfKeyword | SyntaxKindUniqueKeyword | SyntaxKindReadonlyKeyword + typeNode *Node +} + +func (f *NodeFactory) NewTypeOperatorNode(operator SyntaxKind, typeNode *Node) *Node { + data := &TypeOperatorNode{} + data.operator = operator + data.typeNode = typeNode + return f.NewNode(SyntaxKindTypeOperator, data) +} + +func (node *TypeOperatorNode) ForEachChild(v Visitor) bool { + return visit(v, node.typeNode) +} + +func isTypeOperatorNode(node *Node) bool { + return node.kind == SyntaxKindTypeOperator +} + +// InferTypeNode + +type InferTypeNode struct { + TypeNodeBase + typeParameter *Node +} + +func (f *NodeFactory) NewInferTypeNode(typeParameter *Node) *Node { + data := &InferTypeNode{} + data.typeParameter = typeParameter + return f.NewNode(SyntaxKindInferType, data) +} + +func (node *InferTypeNode) ForEachChild(v Visitor) bool { + return visit(v, node.typeParameter) +} + +// ArrayTypeNode + +type ArrayTypeNode struct { + TypeNodeBase + elementType *Node +} + +func (f *NodeFactory) NewArrayTypeNode(elementType *Node) *Node { + data := &ArrayTypeNode{} + data.elementType = elementType + return f.NewNode(SyntaxKindArrayType, data) +} + +func (node *ArrayTypeNode) ForEachChild(v Visitor) bool { + return visit(v, node.elementType) +} + +// IndexedAccessTypeNode + +type IndexedAccessTypeNode struct { + TypeNodeBase + objectType *Node + indexType *Node +} + +func (f *NodeFactory) NewIndexedAccessTypeNode(objectType *Node, indexType *Node) *Node { + data := &IndexedAccessTypeNode{} + data.objectType = objectType + data.indexType = indexType + return f.NewNode(SyntaxKindIndexedAccessType, data) +} + +func (node *IndexedAccessTypeNode) ForEachChild(v Visitor) bool { + return visit(v, node.objectType) || visit(v, node.indexType) +} + +// TypeArgumentList + +type TypeArgumentList struct { + NodeBase + arguments []*Node +} + +func (f *NodeFactory) NewTypeArgumentList(arguments []*Node) *Node { + data := &TypeArgumentList{} + data.arguments = arguments + return f.NewNode(SyntaxKindTypeArgumentList, data) +} + +func (node *TypeArgumentList) ForEachChild(v Visitor) bool { + return visitNodes(v, node.arguments) +} + +// TypeReferenceNode + +type TypeReferenceNode struct { + TypeNodeBase + typeName *Node + typeArguments *Node +} + +func (f *NodeFactory) NewTypeReferenceNode(typeName *Node, typeArguments *Node) *Node { + data := &TypeReferenceNode{} + data.typeName = typeName + data.typeArguments = typeArguments + return f.NewNode(SyntaxKindTypeReference, data) +} + +func (node *TypeReferenceNode) ForEachChild(v Visitor) bool { + return visit(v, node.typeName) || visit(v, node.typeArguments) +} + +func isTypeReferenceNode(node *Node) bool { + return node.kind == SyntaxKindTypeReference +} + +// ExpressionWithTypeArguments + +type ExpressionWithTypeArguments struct { + ExpressionBase + expression *Node + typeArguments *Node +} + +func (f *NodeFactory) NewExpressionWithTypeArguments(expression *Node, typeArguments *Node) *Node { + data := &ExpressionWithTypeArguments{} + data.expression = expression + data.typeArguments = typeArguments + return f.NewNode(SyntaxKindExpressionWithTypeArguments, data) +} + +func (node *ExpressionWithTypeArguments) ForEachChild(v Visitor) bool { + return visit(v, node.expression) || visit(v, node.typeArguments) +} + +// LiteralTypeNode + +type LiteralTypeNode struct { + TypeNodeBase + literal *Node // KeywordExpression | LiteralExpression | PrefixUnaryExpression +} + +func (f *NodeFactory) NewLiteralTypeNode(literal *Node) *Node { + data := &LiteralTypeNode{} + data.literal = literal + return f.NewNode(SyntaxKindLiteralType, data) +} + +func (node *LiteralTypeNode) ForEachChild(v Visitor) bool { + return visit(v, node.literal) +} + +func isLiteralTypeNode(node *Node) bool { + return node.kind == SyntaxKindLiteralType +} + +// ThisTypeNode + +type ThisTypeNode struct { + TypeNodeBase +} + +func (f *NodeFactory) NewThisTypeNode() *Node { + return f.NewNode(SyntaxKindThisType, &ThisTypeNode{}) +} + +// TypePredicateNode + +type TypePredicateNode struct { + TypeNodeBase + assertsModifier *Node // Optional + parameterName *Node // Identifier | ThisTypeNode + typeNode *Node // Optional +} + +func (f *NodeFactory) NewTypePredicateNode(assertsModifier *Node, parameterName *Node, typeNode *Node) *Node { + data := &TypePredicateNode{} + data.assertsModifier = assertsModifier + data.parameterName = parameterName + data.typeNode = typeNode + return f.NewNode(SyntaxKindTypePredicate, data) +} + +func (node *TypePredicateNode) ForEachChild(v Visitor) bool { + return visit(v, node.assertsModifier) || visit(v, node.parameterName) || visit(v, node.typeNode) +} + +// ImportTypeNode + +type ImportTypeNode struct { + TypeNodeBase + isTypeOf bool + argument *Node + attributes *Node // Optional + qualifier *Node // Optional + typeArguments *Node // Optional +} + +func (f *NodeFactory) NewImportTypeNode(isTypeOf bool, argument *Node, attributes *Node, qualifier *Node, typeArguments *Node) *Node { + data := &ImportTypeNode{} + data.isTypeOf = isTypeOf + data.argument = argument + data.attributes = attributes + data.qualifier = qualifier + data.typeArguments = typeArguments + return f.NewNode(SyntaxKindImportType, data) +} + +func (node *ImportTypeNode) ForEachChild(v Visitor) bool { + return visit(v, node.argument) || visit(v, node.attributes) || visit(v, node.qualifier) || visit(v, node.typeArguments) +} + +func isImportTypeNode(node *Node) bool { + return node.kind == SyntaxKindImportType +} + +// ImportAttribute + +type ImportAttribute struct { + NodeBase + name *Node + value *Node +} + +func (f *NodeFactory) NewImportAttribute(name *Node, value *Node) *Node { + data := &ImportAttribute{} + data.name = name + data.value = value + return f.NewNode(SyntaxKindImportAttribute, data) +} + +func (node *ImportAttribute) ForEachChild(v Visitor) bool { + return visit(v, node.name) || visit(v, node.value) +} + +// ImportAttributes + +type ImportAttributes struct { + NodeBase + token SyntaxKind + attributes []*Node + multiLine bool +} + +func (f *NodeFactory) NewImportAttributes(token SyntaxKind, attributes []*Node, multiLine bool) *Node { + data := &ImportAttributes{} + data.token = token + data.attributes = attributes + data.multiLine = multiLine + return f.NewNode(SyntaxKindImportAttributes, data) +} + +func (node *ImportAttributes) ForEachChild(v Visitor) bool { + return visitNodes(v, node.attributes) +} + +// TypeQueryNode + +type TypeQueryNode struct { + TypeNodeBase + exprName *Node + typeArguments *Node +} + +func (f *NodeFactory) NewTypeQueryNode(exprName *Node, typeArguments *Node) *Node { + data := &TypeQueryNode{} + data.exprName = exprName + data.typeArguments = typeArguments + return f.NewNode(SyntaxKindTypeQuery, data) +} + +func (node *TypeQueryNode) ForEachChild(v Visitor) bool { + return visit(v, node.exprName) || visit(v, node.typeArguments) +} + +func isTypeQueryNode(node *Node) bool { + return node.kind == SyntaxKindTypeQuery +} + +// MappedTypeNode + +type MappedTypeNode struct { + TypeNodeBase + DeclarationBase + LocalsContainerBase + readonlyToken *Node // Optional + typeParameter *Node + nameType *Node // Optional + questionToken *Node // Optional + typeNode *Node // Optional (error if missing) + members []*Node // Used only to produce grammar errors +} + +func (f *NodeFactory) NewMappedTypeNode(readonlyToken *Node, typeParameter *Node, nameType *Node, questionToken *Node, typeNode *Node, members []*Node) *Node { + data := &MappedTypeNode{} + data.readonlyToken = readonlyToken + data.typeParameter = typeParameter + data.nameType = nameType + data.questionToken = questionToken + data.typeNode = typeNode + data.members = members + return f.NewNode(SyntaxKindMappedType, data) +} + +func (node *MappedTypeNode) ForEachChild(v Visitor) bool { + return visit(v, node.readonlyToken) || visit(v, node.typeParameter) || visit(v, node.nameType) || + visit(v, node.questionToken) || visit(v, node.typeNode) || visitNodes(v, node.members) +} + +// TypeLiteralNode + +type TypeLiteralNode struct { + TypeNodeBase + DeclarationBase + members []*TypeElement +} + +func (f *NodeFactory) NewTypeLiteralNode(members []*TypeElement) *Node { + data := &TypeLiteralNode{} + data.members = members + return f.NewNode(SyntaxKindTypeLiteral, data) +} + +func (node *TypeLiteralNode) ForEachChild(v Visitor) bool { + return visitNodes(v, node.members) +} + +// TupleTypeNode + +type TupleTypeNode struct { + TypeNodeBase + elements []*TypeNode +} + +func (f *NodeFactory) NewTupleTypeNode(elements []*TypeNode) *Node { + data := &TupleTypeNode{} + data.elements = elements + return f.NewNode(SyntaxKindTupleType, data) +} + +func (node *TupleTypeNode) Kind() SyntaxKind { + return SyntaxKindTupleType +} + +func (node *TupleTypeNode) ForEachChild(v Visitor) bool { + return visitNodes(v, node.elements) +} + +// NamedTupleTypeMember + +type NamedTupleMember struct { + TypeNodeBase + DeclarationBase + dotDotDotToken *Node + name *Node + questionToken *Node + typeNode *Node +} + +func (f *NodeFactory) NewNamedTupleTypeMember(dotDotDotToken *Node, name *Node, questionToken *Node, typeNode *Node) *Node { + data := &NamedTupleMember{} + data.dotDotDotToken = dotDotDotToken + data.name = name + data.questionToken = questionToken + data.typeNode = typeNode + return f.NewNode(SyntaxKindNamedTupleMember, data) +} + +func (node *NamedTupleMember) ForEachChild(v Visitor) bool { + return visit(v, node.dotDotDotToken) || visit(v, node.name) || visit(v, node.questionToken) || visit(v, node.typeNode) +} + +// OptionalTypeNode + +type OptionalTypeNode struct { + TypeNodeBase + typeNode *TypeNode +} + +func (f *NodeFactory) NewOptionalTypeNode(typeNode *TypeNode) *Node { + data := &OptionalTypeNode{} + data.typeNode = typeNode + return f.NewNode(SyntaxKindOptionalType, data) +} + +func (node *OptionalTypeNode) ForEachChild(v Visitor) bool { + return visit(v, node.typeNode) +} + +// RestTypeNode + +type RestTypeNode struct { + TypeNodeBase + typeNode *TypeNode +} + +func (f *NodeFactory) NewRestTypeNode(typeNode *TypeNode) *Node { + data := &RestTypeNode{} + data.typeNode = typeNode + return f.NewNode(SyntaxKindRestType, data) +} + +func (node *RestTypeNode) ForEachChild(v Visitor) bool { + return visit(v, node.typeNode) +} + +// ParenthesizedTypeNode + +type ParenthesizedTypeNode struct { + TypeNodeBase + typeNode *TypeNode +} + +func (f *NodeFactory) NewParenthesizedTypeNode(typeNode *TypeNode) *Node { + data := &ParenthesizedTypeNode{} + data.typeNode = typeNode + return f.NewNode(SyntaxKindParenthesizedType, data) +} + +func (node *ParenthesizedTypeNode) ForEachChild(v Visitor) bool { + return visit(v, node.typeNode) +} + +func isParenthesizedTypeNode(node *Node) bool { + return node.kind == SyntaxKindParenthesizedType +} + +// FunctionOrConstructorTypeNodeBase + +type FunctionOrConstructorTypeNodeBase struct { + TypeNodeBase + DeclarationBase + ModifiersBase + FunctionLikeBase +} + +func (node *FunctionOrConstructorTypeNodeBase) ForEachChild(v Visitor) bool { + return visit(v, node.modifiers) || visit(v, node.typeParameters) || visitNodes(v, node.parameters) || visit(v, node.returnType) +} + +// FunctionTypeNode + +type FunctionTypeNode struct { + FunctionOrConstructorTypeNodeBase +} + +func (f *NodeFactory) NewFunctionTypeNode(typeParameters *Node, parameters []*Node, returnType *Node) *Node { + data := &FunctionTypeNode{} + data.typeParameters = typeParameters + data.parameters = parameters + data.returnType = returnType + return f.NewNode(SyntaxKindFunctionType, data) +} + +func isFunctionTypeNode(node *Node) bool { + return node.kind == SyntaxKindFunctionType +} + +// ConstructorTypeNode + +type ConstructorTypeNode struct { + FunctionOrConstructorTypeNodeBase +} + +func (f *NodeFactory) NewConstructorTypeNode(modifiers *Node, typeParameters *Node, parameters []*Node, returnType *Node) *Node { + data := &ConstructorTypeNode{} + data.modifiers = modifiers + data.typeParameters = typeParameters + data.parameters = parameters + data.returnType = returnType + return f.NewNode(SyntaxKindConstructorType, data) +} + +// TemplateLiteralLikeBase + +type TemplateLiteralLikeBase struct { + LiteralLikeBase + rawText string + templateFlags TokenFlags +} + +// TemplateHead + +type TemplateHead struct { + NodeBase + TemplateLiteralLikeBase +} + +func (f *NodeFactory) NewTemplateHead(text string, rawText string, templateFlags TokenFlags) *Node { + data := &TemplateHead{} + data.text = text + data.rawText = rawText + data.templateFlags = templateFlags + return f.NewNode(SyntaxKindTemplateHead, data) +} + +// TemplateMiddle + +type TemplateMiddle struct { + NodeBase + TemplateLiteralLikeBase +} + +func (f *NodeFactory) NewTemplateMiddle(text string, rawText string, templateFlags TokenFlags) *Node { + data := &TemplateMiddle{} + data.text = text + data.rawText = rawText + data.templateFlags = templateFlags + return f.NewNode(SyntaxKindTemplateMiddle, data) +} + +// TemplateTail + +type TemplateTail struct { + NodeBase + TemplateLiteralLikeBase +} + +func (f *NodeFactory) NewTemplateTail(text string, rawText string, templateFlags TokenFlags) *Node { + data := &TemplateTail{} + data.text = text + data.rawText = rawText + data.templateFlags = templateFlags + return f.NewNode(SyntaxKindTemplateTail, data) +} + +// TemplateLiteralTypeNode + +type TemplateLiteralTypeNode struct { + TypeNodeBase + head *Node + templateSpans []*Node +} + +func (f *NodeFactory) NewTemplateLiteralTypeNode(head *Node, templateSpans []*Node) *Node { + data := &TemplateLiteralTypeNode{} + data.head = head + data.templateSpans = templateSpans + return f.NewNode(SyntaxKindTemplateLiteralType, data) +} + +func (node *TemplateLiteralTypeNode) ForEachChild(v Visitor) bool { + return visit(v, node.head) || visitNodes(v, node.templateSpans) +} + +// TemplateLiteralTypeSpan + +type TemplateLiteralTypeSpan struct { + NodeBase + typeNode *Node + literal *Node +} + +func (f *NodeFactory) NewTemplateLiteralTypeSpan(typeNode *Node, literal *Node) *Node { + data := &TemplateLiteralTypeSpan{} + data.typeNode = typeNode + data.literal = literal + return f.NewNode(SyntaxKindTemplateLiteralTypeSpan, data) +} + +func (node *TemplateLiteralTypeSpan) ForEachChild(v Visitor) bool { + return visit(v, node.typeNode) || visit(v, node.literal) +} + +/// A JSX expression of the form ... + +type JsxElement struct { + ExpressionBase + openingElement *Node + children []*Node + closingElement *Node +} + +func (f *NodeFactory) NewJsxElement(openingElement *Node, children []*Node, closingElement *Node) *Node { + data := &JsxElement{} + data.openingElement = openingElement + data.children = children + data.closingElement = closingElement + return f.NewNode(SyntaxKindJsxElement, data) +} + +func (node *JsxElement) ForEachChild(v Visitor) bool { + return visit(v, node.openingElement) || visitNodes(v, node.children) || visit(v, node.closingElement) +} + +// JsxAttributes + +type JsxAttributes struct { + ExpressionBase + DeclarationBase + properties []*JsxAttributeLike +} + +func (f *NodeFactory) NewJsxAttributes(properties []*JsxAttributeLike) *Node { + data := &JsxAttributes{} + data.properties = properties + return f.NewNode(SyntaxKindJsxAttributes, data) +} + +func (node *JsxAttributes) ForEachChild(v Visitor) bool { + return visitNodes(v, node.properties) +} + +// JsxNamespacedName + +type JsxNamespacedName struct { + ExpressionBase + name *Node + namespace *Node +} + +func (f *NodeFactory) NewJsxNamespacedName(name *Node, namespace *Node) *Node { + data := &JsxNamespacedName{} + data.name = name + data.namespace = namespace + return f.NewNode(SyntaxKindJsxNamespacedName, data) +} + +func (node *JsxNamespacedName) ForEachChild(v Visitor) bool { + return visit(v, node.name) || visit(v, node.namespace) +} + +func isJsxNamespacedName(node *Node) bool { + return node.kind == SyntaxKindJsxNamespacedName +} + +/// The opening element of a ... JsxElement + +type JsxOpeningElement struct { + ExpressionBase + tagName *Node // Identifier | KeywordExpression | PropertyAccessExpression | JsxNamespacedName + typeArguments *Node + attributes *Node +} + +func (f *NodeFactory) NewJsxOpeningElement(tagName *Node, typeArguments *Node, attributes *Node) *Node { + data := &JsxOpeningElement{} + data.tagName = tagName + data.typeArguments = typeArguments + data.attributes = attributes + return f.NewNode(SyntaxKindJsxOpeningElement, data) +} + +func (node *JsxOpeningElement) ForEachChild(v Visitor) bool { + return visit(v, node.tagName) || visit(v, node.typeArguments) || visit(v, node.attributes) +} + +func isJsxOpeningElement(node *Node) bool { + return node.kind == SyntaxKindJsxOpeningElement +} + +/// A JSX expression of the form + +type JsxSelfClosingElement struct { + ExpressionBase + tagName *Node // Identifier | KeywordExpression | PropertyAccessExpression | JsxNamespacedName + typeArguments *Node + attributes *Node +} + +func (f *NodeFactory) NewJsxSelfClosingElement(tagName *Node, typeArguments *Node, attributes *Node) *Node { + data := &JsxSelfClosingElement{} + data.tagName = tagName + data.typeArguments = typeArguments + data.attributes = attributes + return f.NewNode(SyntaxKindJsxSelfClosingElement, data) +} + +func (node *JsxSelfClosingElement) ForEachChild(v Visitor) bool { + return visit(v, node.tagName) || visit(v, node.typeArguments) || visit(v, node.attributes) +} + +/// A JSX expression of the form <>... + +type JsxFragment struct { + ExpressionBase + openingFragment *Node + children []*Node + closingFragment *Node +} + +func (f *NodeFactory) NewJsxFragment(openingFragment *Node, children []*Node, closingFragment *Node) *Node { + data := &JsxFragment{} + data.openingFragment = openingFragment + data.children = children + data.closingFragment = closingFragment + return f.NewNode(SyntaxKindJsxFragment, data) +} + +func (node *JsxFragment) ForEachChild(v Visitor) bool { + return visit(v, node.openingFragment) || visitNodes(v, node.children) || visit(v, node.closingFragment) +} + +/// The opening element of a <>... JsxFragment + +type JsxOpeningFragment struct { + ExpressionBase +} + +func (f *NodeFactory) NewJsxOpeningFragment() *Node { + return f.NewNode(SyntaxKindJsxOpeningFragment, &JsxOpeningFragment{}) +} + +func isJsxOpeningFragment(node *Node) bool { + return node.kind == SyntaxKindJsxOpeningFragment +} + +/// The closing element of a <>... JsxFragment + +type JsxClosingFragment struct { + ExpressionBase +} + +func (f *NodeFactory) NewJsxClosingFragment() *Node { + return f.NewNode(SyntaxKindJsxClosingFragment, &JsxClosingFragment{}) +} + +// JsxAttribute + +type JsxAttribute struct { + NodeBase + DeclarationBase + name *Node + /// JSX attribute initializers are optional; is sugar for + initializer *Node +} + +func (f *NodeFactory) NewJsxAttribute(name *Node, initializer *Node) *Node { + data := &JsxAttribute{} + data.name = name + data.initializer = initializer + return f.NewNode(SyntaxKindJsxAttribute, data) +} + +func (node *JsxAttribute) ForEachChild(v Visitor) bool { + return visit(v, node.name) || visit(v, node.initializer) +} + +func isJsxAttribute(node *Node) bool { + return node.kind == SyntaxKindJsxAttribute +} + +// JsxSpreadAttribute + +type JsxSpreadAttribute struct { + NodeBase + expression *Node +} + +func (f *NodeFactory) NewJsxSpreadAttribute(expression *Node) *Node { + data := &JsxSpreadAttribute{} + data.expression = expression + return f.NewNode(SyntaxKindJsxAttribute, data) +} + +func (node *JsxSpreadAttribute) ForEachChild(v Visitor) bool { + return visit(v, node.expression) +} + +// JsxClosingElement + +type JsxClosingElement struct { + NodeBase + tagName *Node // Identifier | KeywordExpression | PropertyAccessExpression | JsxNamespacedName +} + +func (f *NodeFactory) NewJsxClosingElement(tagName *Node) *Node { + data := &JsxClosingElement{} + data.tagName = tagName + return f.NewNode(SyntaxKindJsxClosingElement, data) +} + +func (node *JsxClosingElement) ForEachChild(v Visitor) bool { + return visit(v, node.tagName) +} + +// JsxExpression + +type JsxExpression struct { + ExpressionBase + dotDotDotToken *Node + expression *Node +} + +func (f *NodeFactory) NewJsxExpression(dotDotDotToken *Node, expression *Node) *Node { + data := &JsxExpression{} + data.dotDotDotToken = dotDotDotToken + data.expression = expression + return f.NewNode(SyntaxKindJsxExpression, data) +} + +func (node *JsxExpression) ForEachChild(v Visitor) bool { + return visit(v, node.dotDotDotToken) || visit(v, node.expression) +} + +// JsxText + +type JsxText struct { + ExpressionBase + LiteralLikeBase + containsOnlyTriviaWhiteSpaces bool +} + +func (f *NodeFactory) NewJsxText(text string, containsOnlyTriviaWhiteSpace bool) *Node { + data := &JsxText{} + data.text = text + data.containsOnlyTriviaWhiteSpaces = containsOnlyTriviaWhiteSpace + return f.NewNode(SyntaxKindJsxText, data) +} + +// JSDocNonNullableType + +type JSDocNonNullableType struct { + TypeNodeBase + typeNode *Node + postfix bool +} + +func (f *NodeFactory) NewJSDocNonNullableType(typeNode *Node, postfix bool) *Node { + data := &JSDocNonNullableType{} + data.typeNode = typeNode + data.postfix = postfix + return f.NewNode(SyntaxKindJSDocNonNullableType, data) +} + +func (node *JSDocNonNullableType) ForEachChild(v Visitor) bool { + return visit(v, node.typeNode) +} + +// JSDocNullableType + +type JSDocNullableType struct { + TypeNodeBase + typeNode *Node + postfix bool +} + +func (f *NodeFactory) NewJSDocNullableType(typeNode *Node, postfix bool) *Node { + data := &JSDocNullableType{} + data.typeNode = typeNode + data.postfix = postfix + return f.NewNode(SyntaxKindJSDocNullableType, data) +} + +func (node *JSDocNullableType) ForEachChild(v Visitor) bool { + return visit(v, node.typeNode) +} + +// PatternAmbientModule + +type PatternAmbientModule struct { + pattern Pattern + symbol *Symbol +} + +// SourceFile + +type SourceFile struct { + NodeBase + DeclarationBase + LocalsContainerBase + text string + fileName string + path string + statements []*Statement + diagnostics []*Diagnostic + bindDiagnostics []*Diagnostic + bindSuggestionDiagnostics []*Diagnostic + lineMap []TextPos + languageVersion ScriptTarget + languageVariant LanguageVariant + scriptKind ScriptKind + externalModuleIndicator *Node + endFlowNode *FlowNode + jsGlobalAugmentations SymbolTable + isDeclarationFile bool + isBound bool + moduleReferencesProcessed bool + usesUriStyleNodeCoreModules Tristate + symbolCount int + classifiableNames map[string]bool + imports []*LiteralLikeNode + moduleAugmentations []*ModuleName + patternAmbientModules []PatternAmbientModule + ambientModuleNames []string +} + +func (f *NodeFactory) NewSourceFile(text string, fileName string, statements []*Node) *Node { + data := &SourceFile{} + data.text = text + data.fileName = fileName + data.statements = statements + data.languageVersion = ScriptTargetLatest + return f.NewNode(SyntaxKindSourceFile, data) +} + +func (node *SourceFile) FileName() string { + return node.fileName +} + +func (node *SourceFile) Path() string { + return node.path +} + +func (node *SourceFile) Diagnostics() []*Diagnostic { + return node.diagnostics +} + +func (node *SourceFile) BindDiagnostics() []*Diagnostic { + return node.bindDiagnostics +} + +func (node *SourceFile) ForEachChild(v Visitor) bool { + return visitNodes(v, node.statements) +} + +func isSourceFile(node *Node) bool { + return node.kind == SyntaxKindSourceFile +} diff --git a/internal/compiler/binder.go b/internal/compiler/binder.go new file mode 100644 index 0000000000..5f2f909430 --- /dev/null +++ b/internal/compiler/binder.go @@ -0,0 +1,2783 @@ +package compiler + +import ( + "slices" + "strconv" + + "github.com/microsoft/typescript-go/internal/compiler/diagnostics" +) + +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 +) + +type Binder struct { + file *SourceFile + options *CompilerOptions + languageVersion ScriptTarget + parent *Node + container *Node + thisParentContainer *Node + blockScopeContainer *Node + lastContainer *Node + currentFlow *FlowNode + currentBreakTarget *FlowLabel + currentContinueTarget *FlowLabel + currentReturnTarget *FlowLabel + currentTrueTarget *FlowLabel + currentFalseTarget *FlowLabel + currentExceptionTarget *FlowLabel + preSwitchCaseFlow *FlowNode + activeLabelList *ActiveLabel + emitFlags NodeFlags + seenThisKeyword bool + hasExplicitReturn bool + hasFlowEffects bool + inStrictMode bool + inAssignmentPattern bool + symbolCount int + classifiableNames map[string]bool + symbolPool Pool[Symbol] + flowNodePool Pool[FlowNode] + flowListPool Pool[FlowList] + singleDeclarations []*Node +} + +type ModuleInstanceState int32 + +const ( + ModuleInstanceStateUnknown ModuleInstanceState = iota + ModuleInstanceStateNonInstantiated + ModuleInstanceStateInstantiated + ModuleInstanceStateConstEnumOnly +) + +type ActiveLabel struct { + next *ActiveLabel + breakTarget *FlowLabel + continueTarget *FlowLabel + name string + referenced bool +} + +func (label *ActiveLabel) BreakTarget() *FlowNode { return label.breakTarget } +func (label *ActiveLabel) ContinueTarget() *FlowNode { return label.continueTarget } + +func bindSourceFile(file *SourceFile, options *CompilerOptions) { + if !file.isBound { + b := &Binder{} + b.file = file + b.options = options + b.languageVersion = getEmitScriptTarget(options) + b.classifiableNames = make(map[string]bool) + b.bind(file.AsNode()) + file.isBound = true + file.symbolCount = b.symbolCount + file.classifiableNames = b.classifiableNames + } +} + +func (b *Binder) newSymbol(flags SymbolFlags, name string) *Symbol { + b.symbolCount++ + result := b.symbolPool.New() + result.flags = flags + result.name = name + return result +} + +func getMembers(symbol *Symbol) SymbolTable { + if symbol.members == nil { + symbol.members = make(SymbolTable) + } + return symbol.members +} + +func getExports(symbol *Symbol) SymbolTable { + if symbol.exports == nil { + symbol.exports = make(SymbolTable) + } + return symbol.exports +} + +func getLocals(container *Node) SymbolTable { + data := container.LocalsContainerData() + if data.locals == nil { + data.locals = make(SymbolTable) + } + return data.locals +} + +/** + * 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 SymbolTable, parent *Symbol, node *Node, includes SymbolFlags, excludes SymbolFlags) *Symbol { + return b.declareSymbolEx(symbolTable, parent, node, includes, excludes, false /*isReplaceableByMethod*/, false /*isComputedName*/) +} + +func (b *Binder) declareSymbolEx(symbolTable SymbolTable, parent *Symbol, node *Node, includes SymbolFlags, excludes SymbolFlags, isReplaceableByMethod bool, isComputedName bool) *Symbol { + //Debug.assert(isComputedName || !hasDynamicName(node)) + isDefaultExport := hasSyntacticModifier(node, ModifierFlagsDefault) || isExportSpecifier(node) && 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 = InternalSymbolNameComputed + case isDefaultExport && b.parent != nil: + name = InternalSymbolNameDefault + default: + name = b.getDeclarationName(node) + } + var symbol *Symbol + if name == InternalSymbolNameMissing { + symbol = b.newSymbol(SymbolFlagsNone, 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&SymbolFlagsClassifiable != 0 { + b.classifiableNames[name] = true + } + if symbol == nil { + symbol = b.newSymbol(SymbolFlagsNone, name) + symbolTable[name] = symbol + if isReplaceableByMethod { + symbol.isReplaceableByMethod = true + } + } else if isReplaceableByMethod && !symbol.isReplaceableByMethod { + // A symbol already exists, so don't add this as a declaration. + return symbol + } else if symbol.flags&excludes != 0 { + if symbol.isReplaceableByMethod { + // Javascript constructor-declared symbols can be discarded in favor of + // prototype symbols like methods. + symbol = b.newSymbol(SymbolFlagsNone, name) + symbolTable[name] = symbol + } else if includes&SymbolFlagsVariable == 0 || symbol.flags&SymbolFlagsAssignment == 0 { + // Assignment declarations are allowed to merge with variables, no matter what other flags they have. + if node.Name() != nil { + setParent(node.Name(), node) + } + // Report errors every position with duplicate declaration + // Report errors on previous encountered declarations + var message *diagnostics.Message + if symbol.flags&SymbolFlagsBlockScopedVariable != 0 { + message = diagnostics.Cannot_redeclare_block_scoped_variable_0 + } else { + message = diagnostics.Duplicate_identifier_0 + } + messageNeedsName := true + if symbol.flags&SymbolFlagsEnum != 0 || includes&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 && isExportAssignment(node) && !node.AsExportAssignment().isExportEquals { + message = diagnostics.A_module_cannot_have_multiple_default_exports + messageNeedsName = false + multipleDefaultExports = true + } + } + } + var relatedInformation []*Diagnostic + if isTypeAliasDeclaration(node) && nodeIsMissing(node.AsTypeAliasDeclaration().typeNode) && hasSyntacticModifier(node, ModifierFlagsExport) && symbol.flags&(SymbolFlagsAlias|SymbolFlagsType|SymbolFlagsNamespace) != 0 { + // export type T; - may have meant export type { T }? + relatedInformation = append(relatedInformation, b.createDiagnosticForNode(node, diagnostics.Did_you_mean_0, + "export type { "+node.AsTypeAliasDeclaration().name.AsIdentifier().text+" }")) + } + var declarationName *Node = getNameOfDeclaration(node) + if declarationName == nil { + declarationName = node + } + for index, declaration := range symbol.declarations { + var decl *Node = getNameOfDeclaration(declaration) + if decl == nil { + decl = declaration + } + var diag *Diagnostic + if messageNeedsName { + diag = b.createDiagnosticForNode(decl, message, b.getDisplayName(declaration)) + } else { + diag = b.createDiagnosticForNode(decl, message) + } + if multipleDefaultExports { + addRelatedInfo(diag, b.createDiagnosticForNode(declarationName, ifElse(index == 0, diagnostics.Another_export_default_is_here, diagnostics.X_and_here))) + } + b.addDiagnostic(diag) + if multipleDefaultExports { + relatedInformation = append(relatedInformation, b.createDiagnosticForNode(decl, diagnostics.The_first_export_default_is_here)) + } + } + var diag *Diagnostic + if messageNeedsName { + diag = b.createDiagnosticForNode(declarationName, message, b.getDisplayName(node)) + } else { + diag = b.createDiagnosticForNode(declarationName, message) + } + b.addDiagnostic(addRelatedInfo(diag, relatedInformation...)) + symbol = b.newSymbol(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 *Node) string { + if isExportAssignment(node) { + return ifElse(node.AsExportAssignment().isExportEquals, InternalSymbolNameExportEquals, InternalSymbolNameDefault) + } + name := getNameOfDeclaration(node) + if name != nil { + if isAmbientModule(node) { + moduleName := getTextOfIdentifierOrLiteral(name) + if isGlobalScopeAugmentation(node) { + return InternalSymbolNameGlobal + } + return "\"" + moduleName + "\"" + } + if isPrivateIdentifier(name) { + // containingClass exists because private names only allowed inside classes + containingClass := getContainingClass(node) + if containingClass == nil { + // we can get here in cases where there is already a parse error. + return InternalSymbolNameMissing + } + containingClassSymbol := getSymbolFromNode(containingClass) + return getSymbolNameForPrivateIdentifier(containingClassSymbol, getTextOfIdentifierOrLiteral(name)) + } + if isPropertyNameLiteral(name) { + return getTextOfIdentifierOrLiteral(name) + } + if isComputedPropertyName(name) { + nameExpression := name.AsComputedPropertyName().expression + // treat computed property names where expression is string/numeric literal as just string/numeric literal + if isStringOrNumericLiteralLike(nameExpression) { + return getTextOfIdentifierOrLiteral(nameExpression) + } + if isSignedNumericLiteral(nameExpression) { + unaryExpression := nameExpression.AsPrefixUnaryExpression() + return TokenToString(unaryExpression.operator) + getTextOfIdentifierOrLiteral(unaryExpression.operand) + } + panic("Only computed properties with literal names have declaration names") + } + // if isJsxNamespacedName(name) { + // return getEscapedTextOfJsxNamespacedName(name) + // } + return InternalSymbolNameMissing + } + switch node.kind { + case SyntaxKindConstructor: + return InternalSymbolNameConstructor + case SyntaxKindFunctionType, SyntaxKindCallSignature: + return InternalSymbolNameCall + case SyntaxKindConstructorType, SyntaxKindConstructSignature: + return InternalSymbolNameNew + case SyntaxKindIndexSignature: + return InternalSymbolNameIndex + case SyntaxKindExportDeclaration: + return InternalSymbolNameExportStar + case SyntaxKindSourceFile: + return InternalSymbolNameExportEquals + } + return InternalSymbolNameMissing +} + +func (b *Binder) getDisplayName(node *Node) string { + nameNode := node.Name() + if nameNode != nil { + return declarationNameToString(nameNode) + } + name := b.getDeclarationName(node) + if name != InternalSymbolNameMissing { + return name + } + return "(Missing)" +} + +func moduleExportNameIsDefault(node *Node) bool { + return getTextOfIdentifierOrLiteral(node) == InternalSymbolNameDefault +} + +func getSymbolNameForPrivateIdentifier(containingClassSymbol *Symbol, description string) string { + return InternalSymbolNamePrefix + "#" + strconv.Itoa(int(getSymbolId(containingClassSymbol))) + "@" + description +} + +func (b *Binder) declareModuleMember(node *Node, symbolFlags SymbolFlags, symbolExcludes SymbolFlags) *Symbol { + hasExportModifier := getCombinedModifierFlags(node)&ModifierFlagsExport != 0 + if symbolFlags&SymbolFlagsAlias != 0 { + if node.kind == SyntaxKindExportSpecifier || (node.kind == SyntaxKindImportEqualsDeclaration && hasExportModifier) { + return b.declareSymbol(getExports(b.container.Symbol()), b.container.Symbol(), node, symbolFlags, symbolExcludes) + } + return b.declareSymbol(getLocals(b.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 !isAmbientModule(node) && (hasExportModifier || b.container.flags&NodeFlagsExportContext != 0) { + if !isLocalsContainer(b.container) || (hasSyntacticModifier(node, ModifierFlagsDefault) && b.getDeclarationName(node) == InternalSymbolNameMissing) { + return b.declareSymbol(getExports(b.container.Symbol()), b.container.Symbol(), node, symbolFlags, symbolExcludes) + // No local symbol for an unnamed default! + } + exportKind := SymbolFlagsNone + if symbolFlags&SymbolFlagsValue != 0 { + exportKind = SymbolFlagsExportValue + } + local := b.declareSymbol(getLocals(b.container), nil /*parent*/, node, exportKind, symbolExcludes) + local.exportSymbol = b.declareSymbol(getExports(b.container.Symbol()), b.container.Symbol(), node, symbolFlags, symbolExcludes) + node.ExportableData().localSymbol = local + return local + } + return b.declareSymbol(getLocals(b.container), nil /*parent*/, node, symbolFlags, symbolExcludes) +} + +func (b *Binder) declareClassMember(node *Node, symbolFlags SymbolFlags, symbolExcludes SymbolFlags) *Symbol { + if isStatic(node) { + return b.declareSymbol(getExports(b.container.Symbol()), b.container.Symbol(), node, symbolFlags, symbolExcludes) + } + return b.declareSymbol(getMembers(b.container.Symbol()), b.container.Symbol(), node, symbolFlags, symbolExcludes) +} + +func (b *Binder) declareSourceFileMember(node *Node, symbolFlags SymbolFlags, symbolExcludes SymbolFlags) *Symbol { + if isExternalModule(b.file) { + return b.declareModuleMember(node, symbolFlags, symbolExcludes) + } + return b.declareSymbol(getLocals(b.file.AsNode()), nil /*parent*/, node, symbolFlags, symbolExcludes) +} + +func (b *Binder) declareSymbolAndAddToSymbolTable(node *Node, symbolFlags SymbolFlags, symbolExcludes SymbolFlags) *Symbol { + switch b.container.kind { + case SyntaxKindModuleDeclaration: + return b.declareModuleMember(node, symbolFlags, symbolExcludes) + case SyntaxKindSourceFile: + return b.declareSourceFileMember(node, symbolFlags, symbolExcludes) + case SyntaxKindClassExpression, SyntaxKindClassDeclaration: + return b.declareClassMember(node, symbolFlags, symbolExcludes) + case SyntaxKindEnumDeclaration: + return b.declareSymbol(getExports(b.container.Symbol()), b.container.Symbol(), node, symbolFlags, symbolExcludes) + case SyntaxKindTypeLiteral, SyntaxKindJSDocTypeLiteral, SyntaxKindObjectLiteralExpression, SyntaxKindInterfaceDeclaration, SyntaxKindJsxAttributes: + return b.declareSymbol(getMembers(b.container.Symbol()), b.container.Symbol(), node, symbolFlags, symbolExcludes) + case SyntaxKindFunctionType, SyntaxKindConstructorType, SyntaxKindCallSignature, SyntaxKindConstructSignature, SyntaxKindJSDocSignature, + SyntaxKindIndexSignature, SyntaxKindMethodDeclaration, SyntaxKindMethodSignature, SyntaxKindConstructor, SyntaxKindGetAccessor, + SyntaxKindSetAccessor, SyntaxKindFunctionDeclaration, SyntaxKindFunctionExpression, SyntaxKindArrowFunction, SyntaxKindJSDocFunctionType, + SyntaxKindClassStaticBlockDeclaration, SyntaxKindTypeAliasDeclaration, SyntaxKindMappedType: + return b.declareSymbol(getLocals(b.container), nil /*parent*/, node, symbolFlags, symbolExcludes) + } + panic("Unhandled case in declareSymbolAndAddToSymbolTable") +} + +func (b *Binder) newFlowNode(flags FlowFlags) *FlowNode { + result := b.flowNodePool.New() + result.flags = flags + return result +} + +func (b *Binder) newFlowNodeEx(flags FlowFlags, node any, antecedent *FlowNode) *FlowNode { + result := b.newFlowNode(flags) + result.node = node + result.antecedent = antecedent + return result +} + +func (b *Binder) createLoopLabel() *FlowLabel { + return b.newFlowNode(FlowFlagsLoopLabel) +} + +func (b *Binder) createBranchLabel() *FlowLabel { + return b.newFlowNode(FlowFlagsBranchLabel) +} + +func (b *Binder) createReduceLabel(target *FlowLabel, antecedents *FlowList, antecedent *FlowNode) *FlowNode { + return b.newFlowNodeEx(FlowFlagsReduceLabel, &FlowReduceLabelData{target, antecedents}, antecedent) +} + +func (b *Binder) createFlowCondition(flags FlowFlags, antecedent *FlowNode, expression *Node) *FlowNode { + if antecedent.flags&FlowFlagsUnreachable != 0 { + return antecedent + } + if expression == nil { + if flags&FlowFlagsTrueCondition != 0 { + return antecedent + } + return unreachableFlow + } + if (expression.kind == SyntaxKindTrueKeyword && flags&FlowFlagsFalseCondition != 0 || expression.kind == SyntaxKindFalseKeyword && flags&FlowFlagsTrueCondition != 0) && !isExpressionOfOptionalChainRoot(expression) && !isNullishCoalesce(expression.parent) { + return unreachableFlow + } + if !isNarrowingExpression(expression) { + return antecedent + } + setFlowNodeReferenced(antecedent) + return b.newFlowNodeEx(flags, expression, antecedent) +} + +func (b *Binder) createFlowMutation(flags FlowFlags, antecedent *FlowNode, node *Node) *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 *FlowNode, switchStatement *SwitchStatement, clauseStart int32, clauseEnd int32) *FlowNode { + setFlowNodeReferenced(antecedent) + return b.newFlowNodeEx(FlowFlagsSwitchClause, &FlowSwitchClauseData{switchStatement, clauseStart, clauseEnd}, antecedent) +} + +func (b *Binder) createFlowCall(antecedent *FlowNode, node *CallExpression) *FlowNode { + setFlowNodeReferenced(antecedent) + b.hasFlowEffects = true + return b.newFlowNodeEx(FlowFlagsCall, node, antecedent) +} + +func (b *Binder) newFlowList(head *FlowNode, tail *FlowList) *FlowList { + result := b.flowListPool.New() + result.node = head + result.next = tail + return result +} + +func (b *Binder) combineFlowLists(head *FlowList, tail *FlowList) *FlowList { + if head == nil { + return tail + } + return b.newFlowList(head.node, b.combineFlowLists(head.next, tail)) +} + +func (b *Binder) newSingleDeclaration(declaration *Node) []*Node { + if len(b.singleDeclarations) == cap(b.singleDeclarations) { + b.singleDeclarations = make([]*Node, 0, nextPoolSize(len(b.singleDeclarations))) + } + index := len(b.singleDeclarations) + b.singleDeclarations = b.singleDeclarations[:index+1] + b.singleDeclarations[index] = declaration + return b.singleDeclarations[index : index+1 : index+1] +} + +func setFlowNodeReferenced(flow *FlowNode) { + // On first reference we set the Referenced flag, thereafter we set the Shared flag + if flow.flags&FlowFlagsReferenced == 0 { + flow.flags |= FlowFlagsReferenced + } else { + flow.flags |= FlowFlagsShared + } +} + +func hasAntecedent(list *FlowList, antecedent *FlowNode) bool { + for list != nil { + if list.node == antecedent { + return true + } + list = list.next + } + return false +} + +func (b *Binder) addAntecedent(label *FlowLabel, antecedent *FlowNode) { + if antecedent.flags&FlowFlagsUnreachable == 0 && !hasAntecedent(label.antecedents, antecedent) { + label.antecedents = b.newFlowList(antecedent, label.antecedents) + setFlowNodeReferenced(antecedent) + } +} + +func finishFlowLabel(label *FlowLabel) *FlowNode { + if label.antecedents == nil { + return unreachableFlow + } + if label.antecedents.next == nil { + return label.antecedents.node + } + return label +} + +func (b *Binder) bind(node *Node) bool { + if !exists(node) { + return false + } + node.parent = b.parent + 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. + b.bindWorker(node) + // 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. + if node.kind > SyntaxKindLastToken { + saveParent := b.parent + b.parent = node + containerFlags := getContainerFlags(node) + if containerFlags == ContainerFlagsNone { + b.bindChildren(node) + } else { + b.bindContainer(node, containerFlags) + } + b.parent = saveParent + } else { + saveParent := b.parent + if node.kind == SyntaxKindEndOfFile { + b.parent = node + } + b.parent = saveParent + } + b.inStrictMode = saveInStrictMode + return false +} + +func (b *Binder) bindWorker(node *Node) { + switch node.kind { + case SyntaxKindIdentifier: + node.AsIdentifier().flowNode = b.currentFlow + b.checkContextualIdentifier(node) + case SyntaxKindThisKeyword, SyntaxKindSuperKeyword: + node.AsKeywordExpression().flowNode = b.currentFlow + case SyntaxKindQualifiedName: + if b.currentFlow != nil && isPartOfTypeQuery(node) { + node.AsQualifiedName().flowNode = b.currentFlow + } + case SyntaxKindMetaProperty: + node.AsMetaProperty().flowNode = b.currentFlow + case SyntaxKindPrivateIdentifier: + b.checkPrivateIdentifier(node) + case SyntaxKindPropertyAccessExpression, SyntaxKindElementAccessExpression: + if b.currentFlow != nil && isNarrowableReference(node) { + setFlowNode(node, b.currentFlow) + } + case SyntaxKindBinaryExpression: + if isFunctionPropertyAssignment(node) { + b.bindFunctionPropertyAssignment(node) + } + b.checkStrictModeBinaryExpression(node) + case SyntaxKindCatchClause: + b.checkStrictModeCatchClause(node) + case SyntaxKindDeleteExpression: + b.checkStrictModeDeleteExpression(node) + case SyntaxKindPostfixUnaryExpression: + b.checkStrictModePostfixUnaryExpression(node) + case SyntaxKindPrefixUnaryExpression: + b.checkStrictModePrefixUnaryExpression(node) + case SyntaxKindWithStatement: + b.checkStrictModeWithStatement(node) + case SyntaxKindLabeledStatement: + b.checkStrictModeLabeledStatement(node) + case SyntaxKindThisType: + b.seenThisKeyword = true + case SyntaxKindTypeParameter: + b.bindTypeParameter(node) + case SyntaxKindParameter: + b.bindParameter(node) + case SyntaxKindVariableDeclaration: + b.bindVariableDeclarationOrBindingElement(node) + case SyntaxKindBindingElement: + node.AsBindingElement().flowNode = b.currentFlow + b.bindVariableDeclarationOrBindingElement(node) + case SyntaxKindPropertyDeclaration, SyntaxKindPropertySignature: + b.bindPropertyWorker(node) + case SyntaxKindPropertyAssignment, SyntaxKindShorthandPropertyAssignment: + b.bindPropertyOrMethodOrAccessor(node, SymbolFlagsProperty, SymbolFlagsPropertyExcludes) + case SyntaxKindEnumMember: + b.bindPropertyOrMethodOrAccessor(node, SymbolFlagsEnumMember, SymbolFlagsEnumMemberExcludes) + case SyntaxKindCallSignature, SyntaxKindConstructSignature, SyntaxKindIndexSignature: + b.declareSymbolAndAddToSymbolTable(node, SymbolFlagsSignature, SymbolFlagsNone) + case SyntaxKindMethodDeclaration, SyntaxKindMethodSignature: + b.bindPropertyOrMethodOrAccessor(node, SymbolFlagsMethod|ifElse(getPostfixTokenFromNode(node) != nil, SymbolFlagsOptional, SymbolFlagsNone), ifElse(isObjectLiteralMethod(node), SymbolFlagsPropertyExcludes, SymbolFlagsMethodExcludes)) + case SyntaxKindFunctionDeclaration: + b.bindFunctionDeclaration(node) + case SyntaxKindConstructor: + b.declareSymbolAndAddToSymbolTable(node, SymbolFlagsConstructor, SymbolFlagsNone) + case SyntaxKindGetAccessor: + b.bindPropertyOrMethodOrAccessor(node, SymbolFlagsGetAccessor, SymbolFlagsGetAccessorExcludes) + case SyntaxKindSetAccessor: + b.bindPropertyOrMethodOrAccessor(node, SymbolFlagsSetAccessor, SymbolFlagsSetAccessorExcludes) + case SyntaxKindFunctionType, SyntaxKindConstructorType: + // !!! SyntaxKindJSDocFunctionType + // !!! SyntaxKindJSDocSignature + b.bindFunctionOrConstructorType(node) + case SyntaxKindTypeLiteral, SyntaxKindMappedType: + // !!! SyntaxKindJSDocTypeLiteral + b.bindAnonymousDeclaration(node, SymbolFlagsTypeLiteral, InternalSymbolNameType) + case SyntaxKindObjectLiteralExpression: + b.bindAnonymousDeclaration(node, SymbolFlagsObjectLiteral, InternalSymbolNameObject) + case SyntaxKindFunctionExpression, SyntaxKindArrowFunction: + b.bindFunctionExpression(node) + case SyntaxKindClassExpression, SyntaxKindClassDeclaration: + b.inStrictMode = true + b.bindClassLikeDeclaration(node) + case SyntaxKindInterfaceDeclaration: + b.bindBlockScopedDeclaration(node, SymbolFlagsInterface, SymbolFlagsInterfaceExcludes) + case SyntaxKindTypeAliasDeclaration: + b.bindBlockScopedDeclaration(node, SymbolFlagsTypeAlias, SymbolFlagsTypeAliasExcludes) + case SyntaxKindEnumDeclaration: + b.bindEnumDeclaration(node) + case SyntaxKindModuleDeclaration: + b.bindModuleDeclaration(node) + case SyntaxKindImportEqualsDeclaration, SyntaxKindNamespaceImport, SyntaxKindImportSpecifier, SyntaxKindExportSpecifier: + b.declareSymbolAndAddToSymbolTable(node, SymbolFlagsAlias, SymbolFlagsAliasExcludes) + case SyntaxKindNamespaceExportDeclaration: + b.bindNamespaceExportDeclaration(node) + case SyntaxKindImportClause: + b.bindImportClause(node) + case SyntaxKindExportDeclaration: + b.bindExportDeclaration(node) + case SyntaxKindExportAssignment: + b.bindExportAssignment(node) + case SyntaxKindSourceFile: + b.updateStrictModeStatementList(node.AsSourceFile().statements) + b.bindSourceFileIfExternalModule() + case SyntaxKindBlock: + if isFunctionLikeOrClassStaticBlockDeclaration(node.parent) { + b.updateStrictModeStatementList(node.AsBlock().statements) + } + case SyntaxKindModuleBlock: + b.updateStrictModeStatementList(node.AsModuleBlock().statements) + case SyntaxKindJsxAttributes: + b.bindJsxAttributes(node) + case SyntaxKindJsxAttribute: + b.bindJsxAttribute(node, SymbolFlagsProperty, SymbolFlagsPropertyExcludes) + } +} + +func (b *Binder) bindPropertyWorker(node *Node) { + isAutoAccessor := isAutoAccessorPropertyDeclaration(node) + includes := ifElse(isAutoAccessor, SymbolFlagsAccessor, SymbolFlagsProperty) + excludes := ifElse(isAutoAccessor, SymbolFlagsAccessorExcludes, SymbolFlagsPropertyExcludes) + b.bindPropertyOrMethodOrAccessor(node, includes|ifElse(getPostfixTokenFromNode(node) != nil, SymbolFlagsOptional, SymbolFlagsNone), excludes) +} + +func (b *Binder) bindSourceFileIfExternalModule() { + b.setExportContextFlag(b.file.AsNode()) + if isExternalModule(b.file) { + b.bindSourceFileAsExternalModule() + } + // !!! + // else if isJsonSourceFile(b.file) { + // b.bindSourceFileAsExternalModule() + // // Create symbol equivalent for the module.exports = {} + // originalSymbol := b.file.symbol + // b.declareSymbol(b.file.symbol.exports, b.file.symbol, b.file, SymbolFlagsProperty, SymbolFlagsAll) + // b.file.symbol = originalSymbol + // } +} + +func (b *Binder) bindSourceFileAsExternalModule() { + // !!! Remove file extension from module name + b.bindAnonymousDeclaration(b.file.AsNode(), SymbolFlagsValueModule, "\""+b.file.fileName+"\"") +} + +func (b *Binder) bindModuleDeclaration(node *Node) { + b.setExportContextFlag(node) + if isAmbientModule(node) { + if hasSyntacticModifier(node, ModifierFlagsExport) { + b.errorOnFirstToken(node, diagnostics.X_export_modifier_cannot_be_applied_to_ambient_modules_and_module_augmentations_since_they_are_always_visible) + } + if isModuleAugmentationExternal(node) { + b.declareModuleSymbol(node) + } else { + var pattern Pattern + name := node.AsModuleDeclaration().name + if isStringLiteral(name) { + pattern = tryParsePattern(name.AsStringLiteral().text) + if !isValidPattern(pattern) { + b.errorOnFirstToken(name, diagnostics.Pattern_0_can_have_at_most_one_Asterisk_character, name.AsStringLiteral().text) + } + } + symbol := b.declareSymbolAndAddToSymbolTable(node, SymbolFlagsValueModule, SymbolFlagsValueModuleExcludes) + b.file.patternAmbientModules = append(b.file.patternAmbientModules, PatternAmbientModule{pattern, symbol}) + } + } else { + state := b.declareModuleSymbol(node) + if state != ModuleInstanceStateNonInstantiated { + symbol := node.AsModuleDeclaration().symbol + // if module was already merged with some function, class or non-const enum, treat it as non-const-enum-only + symbol.constEnumOnlyModule = symbol.constEnumOnlyModule && (symbol.flags&(SymbolFlagsFunction|SymbolFlagsClass|SymbolFlagsRegularEnum) == 0) && state == ModuleInstanceStateConstEnumOnly + } + } +} + +func (b *Binder) declareModuleSymbol(node *Node) ModuleInstanceState { + state := getModuleInstanceState(node, nil /*visited*/) + instantiated := state != ModuleInstanceStateNonInstantiated + b.declareSymbolAndAddToSymbolTable(node, ifElse(instantiated, SymbolFlagsValueModule, SymbolFlagsNamespaceModule), ifElse(instantiated, SymbolFlagsValueModuleExcludes, SymbolFlagsNamespaceModuleExcludes)) + return state +} + +func (b *Binder) bindNamespaceExportDeclaration(node *Node) { + if node.AsNamespaceExportDeclaration().modifiers != nil { + b.errorOnNode(node, diagnostics.Modifiers_cannot_appear_here) + } + switch { + case !isSourceFile(node.parent): + b.errorOnNode(node, diagnostics.Global_module_exports_may_only_appear_at_top_level) + case !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: + if b.file.symbol.globalExports == nil { + b.file.symbol.globalExports = make(SymbolTable) + } + b.declareSymbol(b.file.symbol.globalExports, b.file.symbol, node, SymbolFlagsAlias, SymbolFlagsAliasExcludes) + } +} + +func (b *Binder) bindImportClause(node *Node) { + if node.AsImportClause().name != nil { + b.declareSymbolAndAddToSymbolTable(node, SymbolFlagsAlias, SymbolFlagsAliasExcludes) + } +} + +func (b *Binder) bindExportDeclaration(node *Node) { + decl := node.AsExportDeclaration() + if b.container.Symbol() == nil { + // Export * in some sort of block construct + b.bindAnonymousDeclaration(node, SymbolFlagsExportStar, b.getDeclarationName(node)) + } else if decl.exportClause == nil { + // All export * declarations are collected in an __export symbol + b.declareSymbol(getExports(b.container.Symbol()), b.container.Symbol(), node, SymbolFlagsExportStar, SymbolFlagsNone) + } else if isNamespaceExport(decl.exportClause) { + // declareSymbol walks up parents to find name text, parent _must_ be set + // but won't be set by the normal binder walk until `bindChildren` later on. + setParent(decl.exportClause, node) + b.declareSymbol(getExports(b.container.Symbol()), b.container.Symbol(), decl.exportClause, SymbolFlagsAlias, SymbolFlagsAliasExcludes) + } +} + +func (b *Binder) bindExportAssignment(node *Node) { + if b.container.Symbol() == nil { + // Incorrect export assignment in some sort of block construct + b.bindAnonymousDeclaration(node, SymbolFlagsValue, b.getDeclarationName(node)) + } else { + flags := SymbolFlagsProperty + if exportAssignmentIsAlias(node) { + flags = 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(getExports(b.container.Symbol()), b.container.Symbol(), node, flags, SymbolFlagsAll) + if 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 *Node) { + b.bindAnonymousDeclaration(node, SymbolFlagsObjectLiteral, InternalSymbolNameJSXAttributes) +} + +func (b *Binder) bindJsxAttribute(node *Node, symbolFlags SymbolFlags, symbolExcludes SymbolFlags) { + b.declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes) +} + +func getModuleInstanceState(node *Node, visited map[NodeId]ModuleInstanceState) ModuleInstanceState { + module := node.AsModuleDeclaration() + if module.body != nil && module.body.parent == nil { + // getModuleInstanceStateForAliasTarget needs to walk up the parent chain, so parent pointers must be set on this tree already + setParent(module.body, node) + setParentInChildren(module.body) + } + if module.body != nil { + return getModuleInstanceStateCached(module.body, visited) + } else { + return ModuleInstanceStateInstantiated + } +} + +func getModuleInstanceStateCached(node *Node, visited map[NodeId]ModuleInstanceState) ModuleInstanceState { + if visited == nil { + visited = make(map[NodeId]ModuleInstanceState) + } + nodeId := getNodeId(node) + if cached, ok := visited[nodeId]; ok { + if cached != ModuleInstanceStateUnknown { + return cached + } + return ModuleInstanceStateNonInstantiated + } + visited[nodeId] = ModuleInstanceStateUnknown + result := getModuleInstanceStateWorker(node, visited) + visited[nodeId] = result + return result +} + +func getModuleInstanceStateWorker(node *Node, visited map[NodeId]ModuleInstanceState) ModuleInstanceState { + // A module is uninstantiated if it contains only + switch node.kind { + case SyntaxKindInterfaceDeclaration, SyntaxKindTypeAliasDeclaration: + return ModuleInstanceStateNonInstantiated + case SyntaxKindEnumDeclaration: + if isEnumConst(node) { + return ModuleInstanceStateConstEnumOnly + } + case SyntaxKindImportDeclaration, SyntaxKindImportEqualsDeclaration: + if !hasSyntacticModifier(node, ModifierFlagsExport) { + return ModuleInstanceStateNonInstantiated + } + case SyntaxKindExportDeclaration: + decl := node.AsExportDeclaration() + if decl.moduleSpecifier == nil && decl.exportClause != nil && decl.exportClause.kind == SyntaxKindNamedExports { + state := ModuleInstanceStateNonInstantiated + for _, specifier := range decl.exportClause.AsNamedExports().elements { + specifierState := getModuleInstanceStateForAliasTarget(specifier, visited) + if specifierState > state { + state = specifierState + } + if state == ModuleInstanceStateInstantiated { + return state + } + } + return state + } + case SyntaxKindModuleBlock: + state := ModuleInstanceStateNonInstantiated + node.ForEachChild(func(n *Node) bool { + childState := getModuleInstanceStateCached(n, visited) + switch childState { + case ModuleInstanceStateNonInstantiated: + return false + case ModuleInstanceStateConstEnumOnly: + state = ModuleInstanceStateConstEnumOnly + return false + case ModuleInstanceStateInstantiated: + state = ModuleInstanceStateInstantiated + return true + } + panic("Unhandled case in getModuleInstanceStateWorker") + }) + return state + case SyntaxKindModuleDeclaration: + return getModuleInstanceState(node, visited) + case SyntaxKindIdentifier: + if node.flags&NodeFlagsIdentifierIsInJSDocNamespace != 0 { + return ModuleInstanceStateNonInstantiated + } + } + return ModuleInstanceStateInstantiated +} + +func getModuleInstanceStateForAliasTarget(node *Node, visited map[NodeId]ModuleInstanceState) ModuleInstanceState { + spec := node.AsExportSpecifier() + name := spec.propertyName + if name == nil { + name = spec.name + } + if name.kind != SyntaxKindIdentifier { + // Skip for invalid syntax like this: export { "x" } + return ModuleInstanceStateInstantiated + } + for p := node.parent; p != nil; p = p.parent { + if isBlock(p) || isModuleBlock(p) || isSourceFile(p) { + statements := getStatementsOfBlock(p) + found := ModuleInstanceStateUnknown + for _, statement := range statements { + if nodeHasName(statement, name) { + if statement.parent == nil { + setParent(statement, p) + setParentInChildren(statement) + } + state := getModuleInstanceStateCached(statement, visited) + if found == ModuleInstanceStateUnknown || state > found { + found = state + } + if found == ModuleInstanceStateInstantiated { + return found + } + if statement.kind == SyntaxKindImportEqualsDeclaration { + // Treat re-exports of import aliases as instantiated since they're ambiguous. This is consistent + // with `export import x = mod.x` being treated as instantiated: + // import x = mod.x; + // export { x }; + found = ModuleInstanceStateInstantiated + } + } + } + if found != ModuleInstanceStateUnknown { + return found + } + } + } + // Couldn't locate, assume could refer to a value + return ModuleInstanceStateInstantiated +} + +func (b *Binder) setExportContextFlag(node *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&NodeFlagsAmbient != 0 && !b.hasExportDeclarations(node) { + node.flags |= NodeFlagsExportContext + } else { + node.flags &= ^NodeFlagsExportContext + } +} + +func (b *Binder) hasExportDeclarations(node *Node) bool { + var statements []*Node + switch node.kind { + case SyntaxKindSourceFile: + statements = node.AsSourceFile().statements + case SyntaxKindModuleDeclaration: + body := node.AsModuleDeclaration().body + if isModuleBlock(body) { + statements = body.AsModuleBlock().statements + } + } + return some(statements, func(s *Node) bool { + return isExportDeclaration(s) || isExportAssignment(s) + }) +} + +func (b *Binder) bindFunctionExpression(node *Node) { + if !b.file.isDeclarationFile && node.flags&NodeFlagsAmbient == 0 && isAsyncFunction(node) { + b.emitFlags |= NodeFlagsHasAsyncFunctions + } + setFlowNode(node, b.currentFlow) + bindingName := InternalSymbolNameFunction + if isFunctionExpression(node) && node.AsFunctionExpression().name != nil { + b.checkStrictModeFunctionName(node) + bindingName = node.AsFunctionExpression().name.AsIdentifier().text + } + b.bindAnonymousDeclaration(node, SymbolFlagsFunction, bindingName) +} + +func (b *Binder) bindClassLikeDeclaration(node *Node) { + name := node.ClassLikeData().name + switch node.kind { + case SyntaxKindClassDeclaration: + b.bindBlockScopedDeclaration(node, SymbolFlagsClass, SymbolFlagsClassExcludes) + case SyntaxKindClassExpression: + nameText := InternalSymbolNameClass + if name != nil { + nameText = name.AsIdentifier().text + b.classifiableNames[nameText] = true + } + b.bindAnonymousDeclaration(node, 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(SymbolFlagsProperty|SymbolFlagsPrototype, "prototype") + symbolExport := getExports(symbol)[prototypeSymbol.name] + if symbolExport != nil { + setParent(name, node) + b.errorOnNode(symbolExport.declarations[0], diagnostics.Duplicate_identifier_0, symbolName(prototypeSymbol)) + } + getExports(symbol)[prototypeSymbol.name] = prototypeSymbol + prototypeSymbol.parent = symbol +} + +func (b *Binder) bindPropertyOrMethodOrAccessor(node *Node, symbolFlags SymbolFlags, symbolExcludes SymbolFlags) { + if !b.file.isDeclarationFile && node.flags&NodeFlagsAmbient == 0 && isAsyncFunction(node) { + b.emitFlags |= NodeFlagsHasAsyncFunctions + } + if b.currentFlow != nil && isObjectLiteralOrClassExpressionMethodOrAccessor(node) { + setFlowNode(node, b.currentFlow) + } + if hasDynamicName(node) { + b.bindAnonymousDeclaration(node, symbolFlags, InternalSymbolNameComputed) + } else { + b.declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes) + } +} + +func (b *Binder) bindFunctionOrConstructorType(node *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(SymbolFlagsSignature, b.getDeclarationName(node)) + b.addDeclarationToSymbol(symbol, node, SymbolFlagsSignature) + typeLiteralSymbol := b.newSymbol(SymbolFlagsTypeLiteral, InternalSymbolNameType) + b.addDeclarationToSymbol(typeLiteralSymbol, node, SymbolFlagsTypeLiteral) + typeLiteralSymbol.members = make(SymbolTable) + typeLiteralSymbol.members[symbol.name] = symbol +} + +func addLateBoundAssignmentDeclarationToSymbol(node *Node, symbol *Symbol) { + if symbol.assignmentDeclarationMembers == nil { + symbol.assignmentDeclarationMembers = make(map[NodeId]*Node) + } + symbol.assignmentDeclarationMembers[getNodeId(node)] = node +} + +func (b *Binder) bindFunctionPropertyAssignment(node *Node) { + expr := node.AsBinaryExpression() + parentName := getAccessedExpression(expr.left).AsIdentifier().text + parentSymbol := b.lookupName(parentName, b.blockScopeContainer) + if parentSymbol == nil { + parentSymbol = b.lookupName(parentName, b.container) + } + if parentSymbol != nil && isFunctionSymbol(parentSymbol) { + // Fix up parent pointers since we're going to use these nodes before we bind into them + setParent(expr.left, node) + setParent(expr.right, node) + if hasDynamicName(node) { + b.bindAnonymousDeclaration(node, SymbolFlagsProperty|SymbolFlagsAssignment, InternalSymbolNameComputed) + addLateBoundAssignmentDeclarationToSymbol(node, parentSymbol) + } else { + b.declareSymbol(getExports(parentSymbol), parentSymbol, node, SymbolFlagsProperty|SymbolFlagsAssignment, SymbolFlagsPropertyExcludes) + } + } +} + +func (b *Binder) bindEnumDeclaration(node *Node) { + if isEnumConst(node) { + b.bindBlockScopedDeclaration(node, SymbolFlagsConstEnum, SymbolFlagsConstEnumExcludes) + } else { + b.bindBlockScopedDeclaration(node, SymbolFlagsRegularEnum, SymbolFlagsRegularEnumExcludes) + } +} + +func (b *Binder) bindVariableDeclarationOrBindingElement(node *Node) { + if b.inStrictMode { + b.checkStrictModeEvalOrArguments(node, node.Name()) + } + if !isBindingPattern(node.Name()) { + switch { + case isBlockOrCatchScoped(node): + b.bindBlockScopedDeclaration(node, SymbolFlagsBlockScopedVariable, SymbolFlagsBlockScopedVariableExcludes) + case 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, SymbolFlagsFunctionScopedVariable, SymbolFlagsParameterExcludes) + default: + b.declareSymbolAndAddToSymbolTable(node, SymbolFlagsFunctionScopedVariable, SymbolFlagsFunctionScopedVariableExcludes) + } + } +} + +func (b *Binder) bindParameter(node *Node) { + // !!! + // if node.kind == SyntaxKindJSDocParameterTag && b.container.kind != SyntaxKindJSDocSignature { + // return + // } + decl := node.AsParameterDeclaration() + if b.inStrictMode && node.flags&NodeFlagsAmbient == 9 { + // 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 isBindingPattern(decl.name) { + index := slices.Index(node.parent.FunctionLikeData().parameters, node) + b.bindAnonymousDeclaration(node, SymbolFlagsFunctionScopedVariable, "__"+strconv.Itoa(index)) + } else { + b.declareSymbolAndAddToSymbolTable(node, SymbolFlagsFunctionScopedVariable, SymbolFlagsParameterExcludes) + } + // If this is a property-parameter, then also declare the property symbol into the + // containing class. + if isParameterPropertyDeclaration(node, node.parent) { + classDeclaration := node.parent.parent + flags := SymbolFlagsProperty | ifElse(decl.questionToken != nil, SymbolFlagsOptional, SymbolFlagsNone) + b.declareSymbol(getMembers(classDeclaration.Symbol()), classDeclaration.Symbol(), node, flags, SymbolFlagsPropertyExcludes) + } +} + +func (b *Binder) bindFunctionDeclaration(node *Node) { + if !b.file.isDeclarationFile && node.flags&NodeFlagsAmbient == 0 && isAsyncFunction(node) { + b.emitFlags |= NodeFlagsHasAsyncFunctions + } + b.checkStrictModeFunctionName(node) + if b.inStrictMode { + b.checkStrictModeFunctionDeclaration(node) + b.bindBlockScopedDeclaration(node, SymbolFlagsFunction, SymbolFlagsFunctionExcludes) + } else { + b.declareSymbolAndAddToSymbolTable(node, SymbolFlagsFunction, SymbolFlagsFunctionExcludes) + } +} + +func (b *Binder) getInferTypeContainer(node *Node) *Node { + extendsType := findAncestor(node, func(n *Node) bool { + parent := n.parent + return parent != nil && isConditionalTypeNode(parent) && parent.AsConditionalTypeNode().extendsType == n + }) + if extendsType != nil { + return extendsType.parent + } + return nil +} + +func (b *Binder) bindAnonymousDeclaration(node *Node, symbolFlags SymbolFlags, name string) { + symbol := b.newSymbol(symbolFlags, name) + if symbolFlags&(SymbolFlagsEnumMember|SymbolFlagsClassMember) != 0 { + symbol.parent = b.container.Symbol() + } + b.addDeclarationToSymbol(symbol, node, symbolFlags) +} + +func (b *Binder) bindBlockScopedDeclaration(node *Node, symbolFlags SymbolFlags, symbolExcludes SymbolFlags) { + switch b.blockScopeContainer.kind { + case SyntaxKindModuleDeclaration: + b.declareModuleMember(node, symbolFlags, symbolExcludes) + case SyntaxKindSourceFile: + if isExternalOrCommonJsModule(b.container.AsSourceFile()) { + b.declareModuleMember(node, symbolFlags, symbolExcludes) + break + } + fallthrough + default: + b.declareSymbol(getLocals(b.blockScopeContainer), nil /*parent*/, node, symbolFlags, symbolExcludes) + } +} + +func (b *Binder) bindTypeParameter(node *Node) { + // !!! + // if isJSDocTemplateTag(node.parent) { + // var container *HasLocals = getEffectiveContainerForJSDocTemplateTag(node.parent) + // if container { + // Debug.assertNode(container, canHaveLocals) + // /* TODO(TS-TO-GO) QuestionQuestionEqualsToken BinaryExpression: container.locals ??= createSymbolTable() */ TODO + // b.declareSymbol(container.locals /*parent*/, nil, node, SymbolFlagsTypeParameter, SymbolFlagsTypeParameterExcludes) + // } else { + // b.declareSymbolAndAddToSymbolTable(node, SymbolFlagsTypeParameter, SymbolFlagsTypeParameterExcludes) + // } + // } + if node.parent.kind == SyntaxKindInferType { + container := b.getInferTypeContainer(node.parent) + if container != nil { + b.declareSymbol(getLocals(container), nil /*parent*/, node, SymbolFlagsTypeParameter, SymbolFlagsTypeParameterExcludes) + } else { + b.bindAnonymousDeclaration(node, SymbolFlagsTypeParameter, b.getDeclarationName(node)) + } + } else { + b.declareSymbolAndAddToSymbolTable(node, SymbolFlagsTypeParameter, SymbolFlagsTypeParameterExcludes) + } +} + +func (b *Binder) lookupName(name string, container *Node) *Symbol { + data := container.LocalsContainerData() + if data != nil { + local := data.locals[name] + if local != nil { + return local + } + } + if isSourceFile(container) { + local := container.AsSourceFile().jsGlobalAugmentations[name] + if local != nil { + return local + } + } + symbol := container.Symbol() + if symbol != nil { + return 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 *Node) { + // Report error only if there are no parse errors in file + if len(b.file.diagnostics) == 0 && node.flags&NodeFlagsAmbient == 0 && node.flags&NodeFlagsJSDoc == 0 && !isIdentifierName(node) { + // strict mode identifiers + originalKeywordKind := getIdentifierToken(node.AsIdentifier().text) + if originalKeywordKind == SyntaxKindIdentifier { + return + } + if b.inStrictMode && originalKeywordKind >= SyntaxKindFirstFutureReservedWord && originalKeywordKind <= SyntaxKindLastFutureReservedWord { + b.errorOnNode(node, b.getStrictModeIdentifierMessage(node), declarationNameToString(node)) + } else if originalKeywordKind == SyntaxKindAwaitKeyword { + if isExternalModule(b.file) && isInTopLevelContext(node) { + b.errorOnNode(node, diagnostics.Identifier_expected_0_is_a_reserved_word_at_the_top_level_of_a_module, declarationNameToString(node)) + } else if node.flags&NodeFlagsAwaitContext != 0 { + b.errorOnNode(node, diagnostics.Identifier_expected_0_is_a_reserved_word_that_cannot_be_used_here, declarationNameToString(node)) + } + } else if originalKeywordKind == SyntaxKindYieldKeyword && node.flags&NodeFlagsYieldContext != 0 { + b.errorOnNode(node, diagnostics.Identifier_expected_0_is_a_reserved_word_that_cannot_be_used_here, declarationNameToString(node)) + } + } +} + +func (b *Binder) checkPrivateIdentifier(node *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, declarationNameToString(node)) + } + } +} + +func (b *Binder) getStrictModeIdentifierMessage(node *Node) *diagnostics.Message { + // Provide specialized messages to help the user understand why we think they're in + // strict mode. + if 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 []*Node) { + if !b.inStrictMode { + for _, statement := range statements { + if !isPrologueDirective(statement) { + return + } + if b.isUseStrictPrologueDirective(statement) { + b.inStrictMode = true + return + } + } + } +} + +// Should be called only on prologue directives (isPrologueDirective(node) should be true) +func (b *Binder) isUseStrictPrologueDirective(node *Node) bool { + nodeText := getSourceTextOfNodeFromSourceFile(b.file, node.AsExpressionStatement().expression) + // 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 (b *Binder) checkStrictModeFunctionName(node *Node) { + if b.inStrictMode && node.flags&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) checkStrictModeFunctionDeclaration(node *Node) { + if b.languageVersion < ScriptTargetES2015 { + // Report error if function is not top level function declaration + if b.blockScopeContainer.kind != SyntaxKindSourceFile && b.blockScopeContainer.kind != SyntaxKindModuleDeclaration && !isFunctionLikeOrClassStaticBlockDeclaration(b.blockScopeContainer) { + // 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(node, b.getStrictModeBlockScopeFunctionDeclarationMessage(node)) + } + } +} + +func (b *Binder) getStrictModeBlockScopeFunctionDeclarationMessage(node *Node) *diagnostics.Message { + // Provide specialized messages to help the user understand why we think they're in strict mode. + if 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 *Node) { + expr := node.AsBinaryExpression() + if b.inStrictMode && isLeftHandSideExpression(expr.left) && 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 *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 *Node) { + // Grammar checking + expr := node.AsDeleteExpression() + if b.inStrictMode && expr.expression.kind == SyntaxKindIdentifier { + // 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 *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 *Node) { + // Grammar checking + if b.inStrictMode { + expr := node.AsPrefixUnaryExpression() + if expr.operator == SyntaxKindPlusPlusToken || expr.operator == SyntaxKindMinusMinusToken { + b.checkStrictModeEvalOrArguments(node, expr.operand) + } + } +} + +func (b *Binder) checkStrictModeWithStatement(node *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 *Node) { + // Grammar checking for labeledStatement + if b.inStrictMode && b.options.Target >= ScriptTargetES2015 { + data := node.AsLabeledStatement() + if isDeclarationStatement(data.statement) || isVariableStatement(data.statement) { + b.errorOnFirstToken(data.label, diagnostics.A_label_is_not_allowed_here) + } + } +} + +func isEvalOrArgumentsIdentifier(node *Node) bool { + if isIdentifier(node) { + text := node.AsIdentifier().text + return text == "eval" || text == "arguments" + } + return false +} + +func (b *Binder) checkStrictModeEvalOrArguments(contextNode *Node, name *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 *Node) *diagnostics.Message { + // Provide specialized messages to help the user understand why we think they're in strict mode + if 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 *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 + saveThisParentContainer := b.thisParentContainer + 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 { + if node.kind != SyntaxKindArrowFunction { + b.thisParentContainer = b.container + } + 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&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 && + !hasSyntacticModifier(node, ModifierFlagsAsync) && + !isGeneratorFunctionExpression(node) && + getImmediatelyInvokedFunctionExpression(node) != nil) || node.kind == SyntaxKindClassStaticBlockDeclaration + // 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(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 == SyntaxKindConstructor { + b.currentReturnTarget = b.newFlowNode(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 &= ^NodeFlagsReachabilityAndEmitFlags + if b.currentFlow.flags&FlowFlagsUnreachable == 0 && containerFlags&ContainerFlagsIsFunctionLike != 0 { + bodyData := node.BodyData() + if bodyData != nil && nodeIsPresent(bodyData.body) { + node.flags |= NodeFlagsHasImplicitReturn + if b.hasExplicitReturn { + node.flags |= NodeFlagsHasExplicitReturn + } + bodyData.endFlowNode = b.currentFlow + } + } + if node.kind == SyntaxKindSourceFile { + node.flags |= b.emitFlags + node.AsSourceFile().endFlowNode = b.currentFlow + } + + if b.currentReturnTarget != nil { + b.addAntecedent(b.currentReturnTarget, b.currentFlow) + b.currentFlow = finishFlowLabel(b.currentReturnTarget) + if node.kind == SyntaxKindConstructor || node.kind == SyntaxKindClassStaticBlockDeclaration { + 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 |= NodeFlagsContainsThis + } else { + node.flags &= ^NodeFlagsContainsThis + } + } else { + b.bindChildren(node) + } + b.container = saveContainer + b.thisParentContainer = saveThisParentContainer + b.blockScopeContainer = savedBlockScopeContainer +} + +func (b *Binder) bindChildren(node *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 >= SyntaxKindFirstStatement && kind <= SyntaxKindLastStatement && (b.options.AllowUnreachableCode != TSTrue || kind == SyntaxKindReturnStatement) { + hasFlowNodeData := node.FlowNodeData() + if hasFlowNodeData != nil { + hasFlowNodeData.flowNode = b.currentFlow + } + } + switch node.kind { + case SyntaxKindWhileStatement: + b.bindWhileStatement(node) + case SyntaxKindDoStatement: + b.bindDoStatement(node) + case SyntaxKindForStatement: + b.bindForStatement(node) + case SyntaxKindForInStatement, SyntaxKindForOfStatement: + b.bindForInOrForOfStatement(node) + case SyntaxKindIfStatement: + b.bindIfStatement(node) + case SyntaxKindReturnStatement: + b.bindReturnStatement(node) + case SyntaxKindThrowStatement: + b.bindThrowStatement(node) + case SyntaxKindBreakStatement: + b.bindBreakStatement(node) + case SyntaxKindContinueStatement: + b.bindContinueStatement(node) + case SyntaxKindTryStatement: + b.bindTryStatement(node) + case SyntaxKindSwitchStatement: + b.bindSwitchStatement(node) + case SyntaxKindCaseBlock: + b.bindCaseBlock(node) + case SyntaxKindCaseClause, SyntaxKindDefaultClause: + b.bindCaseOrDefaultClause(node) + case SyntaxKindExpressionStatement: + b.bindExpressionStatement(node) + case SyntaxKindLabeledStatement: + b.bindLabeledStatement(node) + case SyntaxKindPrefixUnaryExpression: + b.bindPrefixUnaryExpressionFlow(node) + case SyntaxKindPostfixUnaryExpression: + b.bindPostfixUnaryExpressionFlow(node) + case SyntaxKindBinaryExpression: + if 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 SyntaxKindDeleteExpression: + b.bindDeleteExpressionFlow(node) + case SyntaxKindConditionalExpression: + b.bindConditionalExpressionFlow(node) + case SyntaxKindVariableDeclaration: + b.bindVariableDeclarationFlow(node) + case SyntaxKindPropertyAccessExpression, SyntaxKindElementAccessExpression: + b.bindAccessExpressionFlow(node) + case SyntaxKindCallExpression: + b.bindCallExpressionFlow(node) + case SyntaxKindNonNullExpression: + b.bindNonNullExpressionFlow(node) + // case *JSDocTypedefTag, *JSDocCallbackTag, *JSDocEnumTag: + // b.bindJSDocTypeAlias(node) + // case *JSDocImportTag: + // b.bindJSDocImportTag(node) + case SyntaxKindSourceFile: + b.bindEachStatementFunctionsFirst(node.AsSourceFile().statements) + //b.bind(node.endOfFileToken) + case SyntaxKindBlock: + b.bindEachStatementFunctionsFirst(node.AsBlock().statements) + case SyntaxKindModuleBlock: + b.bindEachStatementFunctionsFirst(node.AsModuleBlock().statements) + case SyntaxKindBindingElement: + b.bindBindingElementFlow(node) + case SyntaxKindParameter: + b.bindParameterFlow(node) + case SyntaxKindObjectLiteralExpression, SyntaxKindArrayLiteralExpression, SyntaxKindPropertyAssignment, SyntaxKindSpreadElement: + b.inAssignmentPattern = saveInAssignmentPattern + b.bindEachChild(node) + default: + b.bindEachChild(node) + } + b.inAssignmentPattern = saveInAssignmentPattern +} + +func (b *Binder) bindEachChild(node *Node) { + node.ForEachChild(b.bind) +} + +func (b *Binder) bindEachExpression(nodes []*Node) { + for _, node := range nodes { + b.bind(node) + } +} + +func (b *Binder) bindEachStatement(nodes []*Node) { + for _, node := range nodes { + b.bind(node) + } +} + +func (b *Binder) bindEachStatementFunctionsFirst(statements []*Node) { + for _, node := range statements { + if node.kind == SyntaxKindFunctionDeclaration { + b.bind(node) + } + } + for _, node := range statements { + if node.kind != SyntaxKindFunctionDeclaration { + b.bind(node) + } + } +} + +func (b *Binder) checkUnreachable(node *Node) bool { + if b.currentFlow.flags&FlowFlagsUnreachable == 0 { + return false + } + if b.currentFlow == 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 := isStatementButNotDeclaration(node) && !isEmptyStatement(node) || + isClassDeclaration(node) || + isEnumDeclarationWithPreservedEmit(node, b.options) || + isModuleDeclaration(node) && b.shouldReportErrorOnModuleDeclaration(node) + if reportError { + b.currentFlow = reportedUnreachableFlow + if b.options.AllowUnreachableCode != 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&NodeFlagsAmbient == 0 && (!isVariableStatement(node) || + getCombinedNodeFlags(node.AsVariableStatement().declarationList)&NodeFlagsBlockScoped != 0 || + some(node.AsVariableStatement().declarationList.AsVariableDeclarationList().declarations, func(d *Node) bool { + return d.AsVariableDeclaration().initializer != nil + })) + b.errorOnEachUnreachableRange(node, isError) + } + } + } + return true +} + +func (b *Binder) shouldReportErrorOnModuleDeclaration(node *Node) bool { + instanceState := getModuleInstanceState(node, nil /*visited*/) + return instanceState == ModuleInstanceStateInstantiated || (instanceState == ModuleInstanceStateConstEnumOnly && shouldPreserveConstEnums(b.options)) +} + +func (b *Binder) errorOnEachUnreachableRange(node *Node, isError bool) { + if b.isExecutableStatement(node) && isBlock(node.parent) { + statements := node.parent.AsBlock().statements + index := slices.Index(statements, node) + var first, last *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 *Node) bool { + // Don't remove statements that can validly be used before they appear. + return !isFunctionDeclaration(s) && !b.isPurelyTypeDeclaration(s) && !(isVariableStatement(s) && getCombinedNodeFlags(s)&NodeFlagsBlockScoped == 0 && + some(s.AsVariableStatement().declarationList.AsVariableDeclarationList().declarations, func(d *Node) bool { + return d.AsVariableDeclaration().initializer == nil + })) +} + +func (b *Binder) isPurelyTypeDeclaration(s *Node) bool { + switch s.kind { + case SyntaxKindInterfaceDeclaration, SyntaxKindTypeAliasDeclaration: + return true + case SyntaxKindModuleDeclaration: + return getModuleInstanceState(s, nil /*visited*/) != ModuleInstanceStateInstantiated + case SyntaxKindEnumDeclaration: + return !isEnumDeclarationWithPreservedEmit(s, b.options) + default: + return false + } +} + +func (b *Binder) setContinueTarget(node *Node, target *FlowLabel) *FlowLabel { + label := b.activeLabelList + for label != nil && node.parent.kind == SyntaxKindLabeledStatement { + label.continueTarget = target + label = label.next + node = node.parent + } + return target +} + +func (b *Binder) doWithConditionalBranches(action func(value *Node) bool, value *Node, trueTarget *FlowLabel, falseTarget *FlowLabel) { + savedTrueTarget := b.currentTrueTarget + savedFalseTarget := b.currentFalseTarget + b.currentTrueTarget = trueTarget + b.currentFalseTarget = falseTarget + action(value) + b.currentTrueTarget = savedTrueTarget + b.currentFalseTarget = savedFalseTarget +} + +func (b *Binder) bindCondition(node *Node, trueTarget *FlowLabel, falseTarget *FlowLabel) { + b.doWithConditionalBranches(b.bind, node, trueTarget, falseTarget) + if node == nil || !isLogicalAssignmentExpression(node) && !isLogicalExpression(node) && !(isOptionalChain(node) && isOutermostOptionalChain(node)) { + b.addAntecedent(trueTarget, b.createFlowCondition(FlowFlagsTrueCondition, b.currentFlow, node)) + b.addAntecedent(falseTarget, b.createFlowCondition(FlowFlagsFalseCondition, b.currentFlow, node)) + } +} + +func (b *Binder) bindIterativeStatement(node *Node, breakTarget *FlowLabel, continueTarget *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 *Node) bool { + return isLogicalOrCoalescingAssignmentExpression(skipParentheses(node)) +} + +func (b *Binder) bindAssignmentTargetFlow(node *Node) { + switch node.kind { + case SyntaxKindArrayLiteralExpression: + for _, e := range node.AsArrayLiteralExpression().elements { + if e.kind == SyntaxKindSpreadElement { + b.bindAssignmentTargetFlow(e.AsSpreadElement().expression) + } else { + b.bindDestructuringTargetFlow(e) + } + } + case SyntaxKindObjectLiteralExpression: + for _, p := range node.AsObjectLiteralExpression().properties { + switch p.kind { + case SyntaxKindPropertyAssignment: + b.bindDestructuringTargetFlow(p.AsPropertyAssignment().initializer) + case SyntaxKindShorthandPropertyAssignment: + b.bindAssignmentTargetFlow(p.AsShorthandPropertyAssignment().name) + case SyntaxKindSpreadAssignment: + b.bindAssignmentTargetFlow(p.AsSpreadAssignment().expression) + } + } + default: + if isNarrowableReference(node) { + b.currentFlow = b.createFlowMutation(FlowFlagsAssignment, b.currentFlow, node) + } + } +} + +func (b *Binder) bindDestructuringTargetFlow(node *Node) { + if isBinaryExpression(node) && node.AsBinaryExpression().operatorToken.kind == SyntaxKindEqualsToken { + b.bindAssignmentTargetFlow(node.AsBinaryExpression().left) + } else { + b.bindAssignmentTargetFlow(node) + } +} + +func (b *Binder) bindWhileStatement(node *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 = finishFlowLabel(preBodyLabel) + b.bindIterativeStatement(stmt.statement, postWhileLabel, preWhileLabel) + b.addAntecedent(preWhileLabel, b.currentFlow) + b.currentFlow = finishFlowLabel(postWhileLabel) +} + +func (b *Binder) bindDoStatement(node *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 = finishFlowLabel(preConditionLabel) + b.bindCondition(stmt.expression, preDoLabel, postDoLabel) + b.currentFlow = finishFlowLabel(postDoLabel) +} + +func (b *Binder) bindForStatement(node *Node) { + stmt := node.AsForStatement() + preLoopLabel := b.setContinueTarget(node, b.createLoopLabel()) + preBodyLabel := 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 = finishFlowLabel(preBodyLabel) + b.bindIterativeStatement(stmt.statement, postLoopLabel, preLoopLabel) + b.bind(stmt.incrementor) + b.addAntecedent(preLoopLabel, b.currentFlow) + b.currentFlow = finishFlowLabel(postLoopLabel) +} + +func (b *Binder) bindForInOrForOfStatement(node *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 == SyntaxKindForOfStatement { + b.bind(stmt.awaitModifier) + } + b.addAntecedent(postLoopLabel, b.currentFlow) + b.bind(stmt.initializer) + if stmt.initializer.kind != SyntaxKindVariableDeclarationList { + b.bindAssignmentTargetFlow(stmt.initializer) + } + b.bindIterativeStatement(stmt.statement, postLoopLabel, preLoopLabel) + b.addAntecedent(preLoopLabel, b.currentFlow) + b.currentFlow = finishFlowLabel(postLoopLabel) +} + +func (b *Binder) bindIfStatement(node *Node) { + stmt := node.AsIfStatement() + thenLabel := b.createBranchLabel() + elseLabel := b.createBranchLabel() + postIfLabel := b.createBranchLabel() + b.bindCondition(stmt.expression, thenLabel, elseLabel) + b.currentFlow = finishFlowLabel(thenLabel) + b.bind(stmt.thenStatement) + b.addAntecedent(postIfLabel, b.currentFlow) + b.currentFlow = finishFlowLabel(elseLabel) + b.bind(stmt.elseStatement) + b.addAntecedent(postIfLabel, b.currentFlow) + b.currentFlow = finishFlowLabel(postIfLabel) +} + +func (b *Binder) bindReturnStatement(node *Node) { + b.bind(node.AsReturnStatement().expression) + if b.currentReturnTarget != nil { + b.addAntecedent(b.currentReturnTarget, b.currentFlow) + } + b.currentFlow = unreachableFlow + b.hasExplicitReturn = true + b.hasFlowEffects = true +} + +func (b *Binder) bindThrowStatement(node *Node) { + b.bind(node.AsThrowStatement().expression) + b.currentFlow = unreachableFlow + b.hasFlowEffects = true +} + +func (b *Binder) bindBreakStatement(node *Node) { + b.bindBreakOrContinueStatement(node.AsBreakStatement().label, b.currentBreakTarget, (*ActiveLabel).BreakTarget) +} + +func (b *Binder) bindContinueStatement(node *Node) { + b.bindBreakOrContinueStatement(node.AsContinueStatement().label, b.currentContinueTarget, (*ActiveLabel).ContinueTarget) +} + +func (b *Binder) bindBreakOrContinueStatement(label *Node, currentTarget *FlowNode, getTarget func(*ActiveLabel) *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 *FlowLabel) { + if flowLabel != nil { + b.addAntecedent(flowLabel, b.currentFlow) + b.currentFlow = unreachableFlow + b.hasFlowEffects = true + } +} + +func (b *Binder) bindTryStatement(node *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 = 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&FlowFlagsUnreachable != 0 { + // If the end of the finally block is unreachable, the end of the entire try statement is unreachable. + b.currentFlow = 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.antecedent != 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 blok and back through each possible exception source. + if b.currentExceptionTarget != nil && exceptionLabel.antecedent != 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.antecedent != nil { + b.currentFlow = b.createReduceLabel(finallyLabel, normalExitLabel.antecedents, b.currentFlow) + } else { + b.currentFlow = unreachableFlow + } + } + } else { + b.currentFlow = finishFlowLabel(normalExitLabel) + } +} + +func (b *Binder) bindSwitchStatement(node *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 := some(stmt.caseBlock.AsCaseBlock().clauses, func(c *Node) bool { + return c.kind == SyntaxKindDefaultClause + }) + if !hasDefault { + b.addAntecedent(postSwitchLabel, b.createFlowSwitchClause(b.preSwitchCaseFlow, stmt, 0, 0)) + } + b.currentBreakTarget = saveBreakTarget + b.preSwitchCaseFlow = savePreSwitchCaseFlow + b.currentFlow = finishFlowLabel(postSwitchLabel) +} + +func (b *Binder) bindCaseBlock(node *Node) { + switchStatement := node.parent.AsSwitchStatement() + clauses := node.AsCaseBlock().clauses + isNarrowingSwitch := switchStatement.expression.kind == SyntaxKindTrueKeyword || isNarrowingExpression(switchStatement.expression) + var fallthroughFlow *FlowNode = unreachableFlow + for i := 0; i < len(clauses); i++ { + clauseStart := i + for len(clauses[i].AsCaseOrDefaultClause().statements) == 0 && i+1 < len(clauses) { + if fallthroughFlow == unreachableFlow { + b.currentFlow = b.preSwitchCaseFlow + } + b.bind(clauses[i]) + i++ + } + preCaseLabel := b.createBranchLabel() + preCaseFlow := b.preSwitchCaseFlow + if isNarrowingSwitch { + preCaseFlow = b.createFlowSwitchClause(b.preSwitchCaseFlow, switchStatement, int32(clauseStart), int32(i+1)) + } + b.addAntecedent(preCaseLabel, preCaseFlow) + b.addAntecedent(preCaseLabel, fallthroughFlow) + b.currentFlow = finishFlowLabel(preCaseLabel) + clause := clauses[i] + b.bind(clause) + fallthroughFlow = b.currentFlow + if b.currentFlow.flags&FlowFlagsUnreachable == 0 && i != len(clauses)-1 && b.options.NoFallthroughCasesInSwitch == TSTrue { + clause.AsCaseOrDefaultClause().fallthroughFlowNode = b.currentFlow + } + } +} + +func (b *Binder) bindCaseOrDefaultClause(node *Node) { + clause := node.AsCaseOrDefaultClause() + if clause.expression != nil { + saveCurrentFlow := b.currentFlow + b.currentFlow = b.preSwitchCaseFlow + b.bind(clause.expression) + b.currentFlow = saveCurrentFlow + } + b.bindEachStatement(clause.statements) +} + +func (b *Binder) bindExpressionStatement(node *Node) { + stmt := node.AsExpressionStatement() + b.bind(stmt.expression) + b.maybeBindExpressionFlowIfCall(stmt.expression) +} + +func (b *Binder) maybeBindExpressionFlowIfCall(node *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 isCallExpression(node) { + expr := node.AsCallExpression() + if expr.expression.kind != SyntaxKindSuperKeyword && isDottedName(expr.expression) { + b.currentFlow = b.createFlowCall(b.currentFlow, expr) + } + } +} + +func (b *Binder) bindLabeledStatement(node *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 != TSTrue { + b.errorOrSuggestionOnNode(unusedLabelIsError(b.options), stmt.label, diagnostics.Unused_label) + } + b.activeLabelList = b.activeLabelList.next + b.addAntecedent(postStatementLabel, b.currentFlow) + b.currentFlow = finishFlowLabel(postStatementLabel) +} + +func (b *Binder) bindPrefixUnaryExpressionFlow(node *Node) { + expr := node.AsPrefixUnaryExpression() + if expr.operator == SyntaxKindExclamationToken { + 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 == SyntaxKindPlusPlusToken || expr.operator == SyntaxKindMinusMinusToken { + b.bindAssignmentTargetFlow(expr.operand) + } + } +} + +func (b *Binder) bindPostfixUnaryExpressionFlow(node *Node) { + expr := node.AsPostfixUnaryExpression() + b.bindEachChild(node) + if expr.operator == SyntaxKindPlusPlusToken || expr.operator == SyntaxKindMinusMinusToken { + b.bindAssignmentTargetFlow(expr.operand) + } +} + +func (b *Binder) bindDestructuringAssignmentFlow(node *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) + } else { + b.inAssignmentPattern = true + b.bind(expr.left) + b.inAssignmentPattern = false + b.bind(expr.operatorToken) + b.bind(expr.right) + } + b.bindAssignmentTargetFlow(expr.left) +} + +func (b *Binder) bindBinaryExpressionFlow(node *Node) { + expr := node.AsBinaryExpression() + operator := expr.operatorToken.kind + if isLogicalOrCoalescingBinaryOperator(operator) || 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 = finishFlowLabel(postExpressionLabel) + } else { + b.currentFlow = saveCurrentFlow + } + b.hasFlowEffects = b.hasFlowEffects || saveHasFlowEffects + b.currentFlow = finishFlowLabel(postExpressionLabel) + } else { + b.bindLogicalLikeExpression(node, b.currentTrueTarget, b.currentFalseTarget) + } + } else { + b.bind(expr.left) + if operator == SyntaxKindCommaToken { + b.maybeBindExpressionFlowIfCall(node) + } + b.bind(expr.operatorToken) + b.bind(expr.right) + if operator == SyntaxKindCommaToken { + b.maybeBindExpressionFlowIfCall(node) + } + if isAssignmentOperator(operator) && !isAssignmentTarget(node) { + b.bindAssignmentTargetFlow(expr.left) + if operator == SyntaxKindEqualsToken && expr.left.kind == SyntaxKindElementAccessExpression { + elementAccess := expr.left.AsElementAccessExpression() + if isNarrowableOperand(elementAccess.expression) { + b.currentFlow = b.createFlowMutation(FlowFlagsArrayMutation, b.currentFlow, node) + } + } + } + } +} + +func (b *Binder) bindLogicalLikeExpression(node *Node, trueTarget *FlowLabel, falseTarget *FlowLabel) { + expr := node.AsBinaryExpression() + preRightLabel := b.createBranchLabel() + if expr.operatorToken.kind == SyntaxKindAmpersandAmpersandToken || expr.operatorToken.kind == SyntaxKindAmpersandAmpersandEqualsToken { + b.bindCondition(expr.left, preRightLabel, falseTarget) + } else { + b.bindCondition(expr.left, trueTarget, preRightLabel) + } + b.currentFlow = finishFlowLabel(preRightLabel) + b.bind(expr.operatorToken) + if isLogicalOrCoalescingAssignmentOperator(expr.operatorToken.kind) { + b.doWithConditionalBranches(b.bind, expr.right, trueTarget, falseTarget) + b.bindAssignmentTargetFlow(expr.left) + b.addAntecedent(trueTarget, b.createFlowCondition(FlowFlagsTrueCondition, b.currentFlow, node)) + b.addAntecedent(falseTarget, b.createFlowCondition(FlowFlagsFalseCondition, b.currentFlow, node)) + } else { + b.bindCondition(expr.right, trueTarget, falseTarget) + } +} + +func (b *Binder) bindDeleteExpressionFlow(node *Node) { + expr := node.AsDeleteExpression() + b.bindEachChild(node) + if expr.expression.kind == SyntaxKindPropertyAccessExpression { + b.bindAssignmentTargetFlow(expr.expression) + } +} + +func (b *Binder) bindConditionalExpressionFlow(node *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 = finishFlowLabel(trueLabel) + b.bind(expr.questionToken) + b.bind(expr.whenTrue) + b.addAntecedent(postExpressionLabel, b.currentFlow) + b.currentFlow = finishFlowLabel(falseLabel) + b.bind(expr.colonToken) + b.bind(expr.whenFalse) + b.addAntecedent(postExpressionLabel, b.currentFlow) + if b.hasFlowEffects { + b.currentFlow = finishFlowLabel(postExpressionLabel) + } else { + b.currentFlow = saveCurrentFlow + } + b.hasFlowEffects = b.hasFlowEffects || saveHasFlowEffects +} + +func (b *Binder) bindVariableDeclarationFlow(node *Node) { + b.bindEachChild(node) + if node.AsVariableDeclaration().initializer != nil || isForInOrOfStatement(node.parent.parent) { + b.bindInitializedVariableFlow(node) + } +} + +func (b *Binder) bindInitializedVariableFlow(node *Node) { + var name *Node + switch node.kind { + case SyntaxKindVariableDeclaration: + name = node.AsVariableDeclaration().name + case SyntaxKindBindingElement: + name = node.AsBindingElement().name + } + if isBindingPattern(name) { + for _, child := range name.AsBindingPattern().elements { + b.bindInitializedVariableFlow(child) + } + } else { + b.currentFlow = b.createFlowMutation(FlowFlagsAssignment, b.currentFlow, node) + } +} + +func (b *Binder) bindAccessExpressionFlow(node *Node) { + if isOptionalChain(node) { + b.bindOptionalChainFlow(node) + } else { + b.bindEachChild(node) + } +} + +func (b *Binder) bindOptionalChainFlow(node *Node) { + if isTopLevelLogicalExpression(node) { + postExpressionLabel := b.createBranchLabel() + saveCurrentFlow := b.currentFlow + saveHasFlowEffects := b.hasFlowEffects + b.bindOptionalChain(node, postExpressionLabel, postExpressionLabel) + if b.hasFlowEffects { + b.currentFlow = finishFlowLabel(postExpressionLabel) + } else { + b.currentFlow = saveCurrentFlow + } + b.hasFlowEffects = b.hasFlowEffects || saveHasFlowEffects + } else { + b.bindOptionalChain(node, b.currentTrueTarget, b.currentFalseTarget) + } +} + +func (b *Binder) bindOptionalChain(node *Node, trueTarget *FlowLabel, falseTarget *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 *FlowLabel + if isOptionalChainRoot(node) { + preChainLabel = b.createBranchLabel() + } + b.bindOptionalExpression(getAccessedExpression(node), ifElse(preChainLabel != nil, preChainLabel, trueTarget), falseTarget) + if preChainLabel != nil { + b.currentFlow = finishFlowLabel(preChainLabel) + } + b.doWithConditionalBranches(b.bindOptionalChainRest, node, trueTarget, falseTarget) + if isOutermostOptionalChain(node) { + b.addAntecedent(trueTarget, b.createFlowCondition(FlowFlagsTrueCondition, b.currentFlow, node)) + b.addAntecedent(falseTarget, b.createFlowCondition(FlowFlagsFalseCondition, b.currentFlow, node)) + } +} + +func (b *Binder) bindOptionalExpression(node *Node, trueTarget *FlowLabel, falseTarget *FlowLabel) { + b.doWithConditionalBranches(b.bind, node, trueTarget, falseTarget) + if !isOptionalChain(node) || isOutermostOptionalChain(node) { + b.addAntecedent(trueTarget, b.createFlowCondition(FlowFlagsTrueCondition, b.currentFlow, node)) + b.addAntecedent(falseTarget, b.createFlowCondition(FlowFlagsFalseCondition, b.currentFlow, node)) + } +} + +func (b *Binder) bindOptionalChainRest(node *Node) bool { + switch node.kind { + case SyntaxKindPropertyAccessExpression: + b.bind(node.AsPropertyAccessExpression().questionDotToken) + b.bind(node.AsPropertyAccessExpression().name) + case SyntaxKindElementAccessExpression: + b.bind(node.AsElementAccessExpression().questionDotToken) + b.bind(node.AsElementAccessExpression().argumentExpression) + case SyntaxKindCallExpression: + b.bind(node.AsCallExpression().questionDotToken) + b.bind(node.AsCallExpression().typeArguments) + b.bindEachExpression(node.AsCallExpression().arguments) + } + return false +} + +func (b *Binder) bindCallExpressionFlow(node *Node) { + call := node.AsCallExpression() + if 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 := skipParentheses(call.expression) + if expr.kind == SyntaxKindFunctionExpression || expr.kind == SyntaxKindArrowFunction { + b.bind(call.typeArguments) + b.bindEachExpression(call.arguments) + b.bind(call.expression) + } else { + b.bindEachChild(node) + if call.expression.kind == SyntaxKindSuperKeyword { + b.currentFlow = b.createFlowCall(b.currentFlow, call) + } + } + } + if isPropertyAccessExpression(call.expression) { + access := call.expression.AsPropertyAccessExpression() + if isIdentifier(access.name) && isNarrowableOperand(access.expression) && isPushOrUnshiftIdentifier(access.name) { + b.currentFlow = b.createFlowMutation(FlowFlagsArrayMutation, b.currentFlow, node) + } + } +} + +func (b *Binder) bindNonNullExpressionFlow(node *Node) { + if isOptionalChain(node) { + b.bindOptionalChainFlow(node) + } else { + b.bindEachChild(node) + } +} + +func (b *Binder) bindBindingElementFlow(node *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 *Node) { + param := node.AsParameterDeclaration() + b.bind(param.modifiers) + b.bind(param.dotDotDotToken) + b.bind(param.questionToken) + b.bind(param.typeNode) + 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 *Node) { + if node == nil { + return + } + entryFlow := b.currentFlow + b.bind(node) + if entryFlow == unreachableFlow || entryFlow == b.currentFlow { + return + } + exitFlow := b.createBranchLabel() + b.addAntecedent(exitFlow, entryFlow) + b.addAntecedent(exitFlow, b.currentFlow) + b.currentFlow = finishFlowLabel(exitFlow) +} + +func isEnumDeclarationWithPreservedEmit(node *Node, options *CompilerOptions) bool { + return node.kind == SyntaxKindEnumDeclaration && (!isEnumConst(node) || shouldPreserveConstEnums(options)) +} + +func setFlowNode(node *Node, flowNode *FlowNode) { + data := node.FlowNodeData() + if data != nil { + data.flowNode = flowNode + } +} + +func setReturnFlowNode(node *Node, returnFlowNode *FlowNode) { + switch node.kind { + case SyntaxKindConstructor: + node.AsConstructorDeclaration().returnFlowNode = returnFlowNode + case SyntaxKindFunctionDeclaration: + node.AsFunctionDeclaration().returnFlowNode = returnFlowNode + case SyntaxKindFunctionExpression: + node.AsFunctionExpression().returnFlowNode = returnFlowNode + } +} + +func isGeneratorFunctionExpression(node *Node) bool { + return isFunctionExpression(node) && node.AsFunctionExpression().asteriskToken != nil +} + +func (b *Binder) addToContainerChain(next *Node) { + if b.lastContainer != nil { + next.LocalsContainerData().nextContainer = next + } + b.lastContainer = next +} + +func (b *Binder) addDeclarationToSymbol(symbol *Symbol, node *Node, symbolFlags SymbolFlags) { + symbol.flags |= symbolFlags + node.DeclarationData().symbol = symbol + if symbol.declarations == nil { + symbol.declarations = b.newSingleDeclaration(node) + } else { + symbol.declarations = 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.constEnumOnlyModule && symbol.flags&(SymbolFlagsFunction|SymbolFlagsClass|SymbolFlagsRegularEnum) != 0 { + symbol.constEnumOnlyModule = false + } + if symbolFlags&SymbolFlagsValue != 0 { + setValueDeclaration(symbol, node) + } +} + +func setValueDeclaration(symbol *Symbol, node *Node) { + valueDeclaration := symbol.valueDeclaration + if valueDeclaration == nil || + !(node.flags&NodeFlagsAmbient != 0 && valueDeclaration.flags&NodeFlagsAmbient == 0) && + (isAssignmentDeclaration(valueDeclaration) && !isAssignmentDeclaration(node)) || + (valueDeclaration.kind != node.kind && isEffectiveModuleDeclaration(valueDeclaration)) { + // other kinds of value declarations take precedence over modules and assignment 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 *Node) ContainerFlags { + switch node.kind { + case SyntaxKindClassExpression, SyntaxKindClassDeclaration, SyntaxKindEnumDeclaration, SyntaxKindObjectLiteralExpression, SyntaxKindTypeLiteral, + SyntaxKindJSDocTypeLiteral, SyntaxKindJsxAttributes: + return ContainerFlagsIsContainer + case SyntaxKindInterfaceDeclaration: + return ContainerFlagsIsContainer | ContainerFlagsIsInterface + case SyntaxKindModuleDeclaration, SyntaxKindTypeAliasDeclaration, SyntaxKindMappedType, SyntaxKindIndexSignature: + return ContainerFlagsIsContainer | ContainerFlagsHasLocals + case SyntaxKindSourceFile: + return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals + case SyntaxKindGetAccessor, SyntaxKindSetAccessor, SyntaxKindMethodDeclaration: + if isObjectLiteralOrClassExpressionMethodOrAccessor(node) { + return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals | ContainerFlagsIsFunctionLike | ContainerFlagsIsObjectLiteralOrClassExpressionMethodOrAccessor + } + fallthrough + case SyntaxKindConstructor, SyntaxKindFunctionDeclaration, SyntaxKindMethodSignature, SyntaxKindCallSignature, SyntaxKindJSDocSignature, + SyntaxKindJSDocFunctionType, SyntaxKindFunctionType, SyntaxKindConstructSignature, SyntaxKindConstructorType, SyntaxKindClassStaticBlockDeclaration: + return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals | ContainerFlagsIsFunctionLike + case SyntaxKindFunctionExpression, SyntaxKindArrowFunction: + return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals | ContainerFlagsIsFunctionLike | ContainerFlagsIsFunctionExpression + case SyntaxKindModuleBlock: + return ContainerFlagsIsControlFlowContainer + case SyntaxKindPropertyDeclaration: + if node.AsPropertyDeclaration().initializer != nil { + return ContainerFlagsIsControlFlowContainer + } else { + return ContainerFlagsNone + } + case SyntaxKindCatchClause, SyntaxKindForStatement, SyntaxKindForInStatement, SyntaxKindForOfStatement, SyntaxKindCaseBlock: + return ContainerFlagsIsBlockScopedContainer | ContainerFlagsHasLocals + case SyntaxKindBlock: + if isFunctionLike(node.parent) || isClassStaticBlockDeclaration(node.parent) { + return ContainerFlagsNone + } else { + return ContainerFlagsIsBlockScopedContainer | ContainerFlagsHasLocals + } + } + return ContainerFlagsNone +} + +func isNarrowingExpression(expr *Node) bool { + switch expr.kind { + case SyntaxKindIdentifier, SyntaxKindThisKeyword: + return true + case SyntaxKindPropertyAccessExpression, SyntaxKindElementAccessExpression: + return containsNarrowableReference(expr) + case SyntaxKindCallExpression: + return hasNarrowableArgument(expr) + case SyntaxKindParenthesizedExpression: + // if isJSDocTypeAssertion(expr) { + // return false + // } + return isNarrowingExpression(expr.AsParenthesizedExpression().expression) + case SyntaxKindNonNullExpression: + return isNarrowingExpression(expr.AsNonNullExpression().expression) + case SyntaxKindBinaryExpression: + return isNarrowingBinaryExpression(expr.AsBinaryExpression()) + case SyntaxKindPrefixUnaryExpression: + return expr.AsPrefixUnaryExpression().operator == SyntaxKindExclamationToken && isNarrowingExpression(expr.AsPrefixUnaryExpression().operand) + case SyntaxKindTypeOfExpression: + return isNarrowingExpression(expr.AsTypeOfExpression().expression) + } + return false +} + +func containsNarrowableReference(expr *Node) bool { + if isNarrowableReference(expr) { + return true + } + if expr.flags&NodeFlagsOptionalChain != 0 { + switch expr.kind { + case SyntaxKindPropertyAccessExpression: + return containsNarrowableReference(expr.AsPropertyAccessExpression().expression) + case SyntaxKindElementAccessExpression: + return containsNarrowableReference(expr.AsElementAccessExpression().expression) + case SyntaxKindCallExpression: + return containsNarrowableReference(expr.AsCallExpression().expression) + case SyntaxKindNonNullExpression: + return containsNarrowableReference(expr.AsNonNullExpression().expression) + } + } + return false +} + +func isNarrowableReference(node *Node) bool { + switch node.kind { + case SyntaxKindIdentifier, SyntaxKindThisKeyword, SyntaxKindSuperKeyword, SyntaxKindMetaProperty: + return true + case SyntaxKindPropertyAccessExpression: + return isNarrowableReference(node.AsPropertyAccessExpression().expression) + case SyntaxKindParenthesizedExpression: + return isNarrowableReference(node.AsParenthesizedExpression().expression) + case SyntaxKindNonNullExpression: + return isNarrowableReference(node.AsNonNullExpression().expression) + case SyntaxKindElementAccessExpression: + expr := node.AsElementAccessExpression() + return isStringOrNumericLiteralLike(expr.argumentExpression) || + isEntityNameExpression(expr.argumentExpression) && isNarrowableReference(expr.expression) + case SyntaxKindBinaryExpression: + expr := node.AsBinaryExpression() + return expr.operatorToken.kind == SyntaxKindCommaToken && isNarrowableReference(expr.right) || + isAssignmentOperator(expr.operatorToken.kind) && isLeftHandSideExpression(expr.left) + } + return false +} + +func hasNarrowableArgument(expr *Node) bool { + call := expr.AsCallExpression() + for _, argument := range call.arguments { + if containsNarrowableReference(argument) { + return true + } + } + if isPropertyAccessExpression(call.expression) { + if containsNarrowableReference(call.expression.AsPropertyAccessExpression().expression) { + return true + } + } + return false +} + +func isNarrowingBinaryExpression(expr *BinaryExpression) bool { + switch expr.operatorToken.kind { + case SyntaxKindEqualsToken, SyntaxKindBarBarEqualsToken, SyntaxKindAmpersandAmpersandEqualsToken, SyntaxKindQuestionQuestionEqualsToken: + return containsNarrowableReference(expr.left) + case SyntaxKindEqualsEqualsToken, SyntaxKindExclamationEqualsToken, SyntaxKindEqualsEqualsEqualsToken, SyntaxKindExclamationEqualsEqualsToken: + return isNarrowableOperand(expr.left) || isNarrowableOperand(expr.right) || + isNarrowingTypeOfOperands(expr.right, expr.left) || isNarrowingTypeOfOperands(expr.left, expr.right) || + (isBooleanLiteral(expr.right) && isNarrowingExpression(expr.left) || isBooleanLiteral(expr.left) && isNarrowingExpression(expr.right)) + case SyntaxKindInstanceOfKeyword: + return isNarrowableOperand(expr.left) + case SyntaxKindInKeyword: + return isNarrowingExpression(expr.right) + case SyntaxKindCommaToken: + return isNarrowingExpression(expr.right) + } + return false +} + +func isNarrowableOperand(expr *Node) bool { + switch expr.kind { + case SyntaxKindParenthesizedExpression: + return isNarrowableOperand(expr.AsParenthesizedExpression().expression) + case SyntaxKindBinaryExpression: + binary := expr.AsBinaryExpression() + switch binary.operatorToken.kind { + case SyntaxKindEqualsToken: + return isNarrowableOperand(binary.left) + case SyntaxKindCommaToken: + return isNarrowableOperand(binary.right) + } + } + return containsNarrowableReference(expr) +} + +func isNarrowingTypeOfOperands(expr1 *Node, expr2 *Node) bool { + return isTypeOfExpression(expr1) && isNarrowableOperand(expr1.AsTypeOfExpression().expression) && isStringLiteralLike(expr2) +} + +func (b *Binder) errorOnNode(node *Node, message *diagnostics.Message, args ...any) { + b.addDiagnostic(b.createDiagnosticForNode(node, message, args...)) +} + +func (b *Binder) errorOnFirstToken(node *Node, message *diagnostics.Message, args ...any) { + span := getRangeOfTokenAtPosition(b.file, node.Pos()) + b.addDiagnostic(NewDiagnostic(b.file, span, message, args...)) +} + +func (b *Binder) errorOrSuggestionOnNode(isError bool, node *Node, message *diagnostics.Message) { + b.errorOrSuggestionOnRange(isError, node, node, message) +} + +func (b *Binder) errorOrSuggestionOnRange(isError bool, startNode *Node, endNode *Node, message *diagnostics.Message) { + textRange := NewTextRange(getRangeOfTokenAtPosition(b.file, startNode.Pos()).Pos(), endNode.End()) + diagnostic := 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 *Node, message *diagnostics.Message, args ...any) *Diagnostic { + return NewDiagnostic(b.file, getErrorRangeForNode(b.file, node), message, args...) +} + +func (b *Binder) addDiagnostic(diagnostic *Diagnostic) { + b.file.bindDiagnostics = append(b.file.bindDiagnostics, diagnostic) +} + +func isEnumConst(node *Node) bool { + return getCombinedModifierFlags(node)&ModifierFlagsConst != 0 +} diff --git a/internal/compiler/checker.go b/internal/compiler/checker.go new file mode 100644 index 0000000000..f4e63af5fa --- /dev/null +++ b/internal/compiler/checker.go @@ -0,0 +1,5064 @@ +package compiler + +import ( + "fmt" + "maps" + "slices" + "strconv" + "strings" + + "github.com/microsoft/typescript-go/internal/compiler/diagnostics" +) + +// CheckMode + +type CheckMode uint32 + +const ( + CheckModeNormal CheckMode = 0 // Normal type checking + CheckModeContextual CheckMode = 1 << 0 // Explicitly assigned contextual type, therefore not cacheable + CheckModeInferential CheckMode = 1 << 1 // Inferential typing + CheckModeSkipContextSensitive CheckMode = 1 << 2 // Skip context sensitive function expressions + CheckModeSkipGenericFunctions CheckMode = 1 << 3 // Skip single signature generic functions + CheckModeIsForSignatureHelp CheckMode = 1 << 4 // Call resolution for purposes of signature help + CheckModeRestBindingElement CheckMode = 1 << 5 // Checking a type that is going to be used to determine the type of a rest binding element + // e.g. in `const { a, ...rest } = foo`, when checking the type of `foo` to determine the type of `rest`, + // we need to preserve generic types instead of substituting them for constraints + CheckModeTypeOnly CheckMode = 1 << 6 // Called from getTypeOfExpression, diagnostics may be omitted +) + +type TypeSystemEntity any + +type TypeSystemPropertyName int32 + +const ( + TypeSystemPropertyNameType TypeSystemPropertyName = iota + TypeSystemPropertyNameResolvedBaseConstructorType + TypeSystemPropertyNameDeclaredType + TypeSystemPropertyNameResolvedReturnType + TypeSystemPropertyNameImmediateBaseConstraint + TypeSystemPropertyNameResolvedTypeArguments + TypeSystemPropertyNameResolvedBaseTypes + TypeSystemPropertyNameWriteType + TypeSystemPropertyNameParameterInitializerContainsUndefined +) + +type TypeResolution struct { + target TypeSystemEntity + propertyName TypeSystemPropertyName + result bool +} + +// WideningKind + +type WideningKind int32 + +const ( + WideningKindNormal WideningKind = iota + WideningKindFunctionReturn + WideningKindGeneratorNext + WideningKindGeneratorYield +) + +// EnumLiteralKey + +type EnumLiteralKey struct { + enumSymbol *Symbol + value any +} + +// TypeCacheKind + +type CachedTypeKind int32 + +const ( + CachedTypeKindLiteralUnionBaseType CachedTypeKind = iota +) + +// TypeCacheKey + +type CachedTypeKey struct { + kind CachedTypeKind + typeId TypeId +} + +// UnionOfUnionKey + +type UnionOfUnionKey struct { + id1 TypeId + id2 TypeId + r UnionReduction + a string +} + +// InferenceContext + +type InferenceContext struct{} + +// Checker + +type Checker struct { + program *Program + host CompilerHost + compilerOptions *CompilerOptions + files []*SourceFile + typeCount uint32 + symbolCount uint32 + instantiationCount uint32 + currentNode *Node + emptySymbols SymbolTable + languageVersion ScriptTarget + moduleKind ModuleKind + allowSyntheticDefaultImports bool + strictNullChecks bool + noImplicitAny bool + useUnknownInCatchVariables bool + exactOptionalPropertyTypes bool + globals SymbolTable + stringLiteralTypes map[string]*Type + numberLiteralTypes map[float64]*Type + bigintLiteralTypes map[PseudoBigint]*Type + enumLiteralTypes map[EnumLiteralKey]*Type + cachedTypes map[CachedTypeKey]*Type + undefinedSymbol *Symbol + argumentsSymbol *Symbol + requireSymbol *Symbol + unknownSymbol *Symbol + resolvingSymbol *Symbol + globalThisSymbol *Symbol + resolveName func(location *Node, name string, meaning SymbolFlags, nameNotFoundMessage *diagnostics.Message, isUse bool, excludeGlobals bool) *Symbol + unionTypes map[string]*Type + unionOfUnionTypes map[UnionOfUnionKey]*Type + diagnostics DiagnosticsCollection + suggestionDiagnostics DiagnosticsCollection + symbolPool Pool[Symbol] + mergedSymbols map[MergeId]*Symbol + nodeLinks LinkStore[*Node, NodeLinks] + valueSymbolLinks LinkStore[*Symbol, ValueSymbolLinks] + aliasSymbolLinks LinkStore[*Symbol, AliasSymbolLinks] + moduleSymbolLinks LinkStore[*Symbol, ModuleSymbolLinks] + exportTypeLinks LinkStore[*Symbol, ExportTypeLinks] + membersAndExportsLinks LinkStore[*Symbol, MembersAndExportsLinks] + typeParameterLinks LinkStore[*Symbol, TypeParameterLinks] + interfaceTypeLinks LinkStore[*Symbol, InterfaceTypeLinks] + anyType *Type + autoType *Type + wildcardType *Type + errorType *Type + nonInferrableAnyType *Type + intrinsicMarkerType *Type + unknownType *Type + undefinedType *Type + undefinedWideningType *Type + missingType *Type + undefinedOrMissingType *Type + nullType *Type + nullWideningType *Type + stringType *Type + numberType *Type + bigintType *Type + regularFalseType *Type + falseType *Type + regularTrueType *Type + trueType *Type + booleanType *Type + esSymbolType *Type + voidType *Type + neverType *Type + nonPrimitiveType *Type + emptyObjectType *Type + emptyTypeLiteralType *Type + anyFunctionType *Type + enumNumberIndexInfo *IndexInfo + patternAmbientModules []PatternAmbientModule + patternAmbientModuleAugmentations SymbolTable + globalObjectType *Type + globalFunctionType *Type + globalCallableFunctionType *Type + globalNewableFunctionType *Type + anyArrayType *Type + autoArrayType *Type + anyReadonlyArrayType *Type + contextualBindingPatterns []*Node + typeResolutions []TypeResolution + resolutionStart int + lastGetCombinedNodeFlagsNode *Node + lastGetCombinedNodeFlagsResult NodeFlags + lastGetCombinedModifierFlagsNode *Node + lastGetCombinedModifierFlagsResult ModifierFlags +} + +func NewChecker(program *Program) *Checker { + c := &Checker{} + c.program = program + c.host = program.host + c.compilerOptions = program.options + c.files = program.files + c.emptySymbols = make(SymbolTable) + c.languageVersion = getEmitScriptTarget(c.compilerOptions) + c.moduleKind = getEmitModuleKind(c.compilerOptions) + c.allowSyntheticDefaultImports = getAllowSyntheticDefaultImports(c.compilerOptions) + c.strictNullChecks = c.getStrictOptionValue(c.compilerOptions.StrictNullChecks) + c.noImplicitAny = c.getStrictOptionValue(c.compilerOptions.NoImplicitAny) + c.useUnknownInCatchVariables = c.getStrictOptionValue(c.compilerOptions.UseUnknownInCatchVariables) + c.exactOptionalPropertyTypes = c.compilerOptions.ExactOptionalPropertyTypes == TSTrue + c.globals = make(SymbolTable) + c.stringLiteralTypes = make(map[string]*Type) + c.numberLiteralTypes = make(map[float64]*Type) + c.bigintLiteralTypes = make(map[PseudoBigint]*Type) + c.enumLiteralTypes = make(map[EnumLiteralKey]*Type) + c.undefinedSymbol = c.newSymbol(SymbolFlagsProperty, "undefined") + c.argumentsSymbol = c.newSymbol(SymbolFlagsProperty, "arguments") + c.requireSymbol = c.newSymbol(SymbolFlagsProperty, "require") + c.unknownSymbol = c.newSymbol(SymbolFlagsProperty, "unknown") + c.resolvingSymbol = c.newSymbol(SymbolFlagsNone, InternalSymbolNameResolving) + c.globalThisSymbol = c.newSymbolEx(SymbolFlagsModule, "globalThis", CheckFlagsReadonly) + c.globalThisSymbol.exports = c.globals + c.globals[c.globalThisSymbol.name] = c.globalThisSymbol + c.resolveName = c.createNameResolver().resolve + c.unionTypes = make(map[string]*Type) + c.unionOfUnionTypes = make(map[UnionOfUnionKey]*Type) + c.diagnostics = DiagnosticsCollection{} + c.suggestionDiagnostics = DiagnosticsCollection{} + c.mergedSymbols = make(map[MergeId]*Symbol) + c.anyType = c.newIntrinsicType(TypeFlagsAny, "any") + c.autoType = c.newIntrinsicTypeEx(TypeFlagsAny, "any", ObjectFlagsNonInferrableType) + c.wildcardType = c.newIntrinsicType(TypeFlagsAny, "any") + c.errorType = c.newIntrinsicType(TypeFlagsAny, "error") + c.nonInferrableAnyType = c.newIntrinsicTypeEx(TypeFlagsAny, "any", ObjectFlagsContainsWideningType) + c.intrinsicMarkerType = c.newIntrinsicType(TypeFlagsAny, "intrinsic") + c.unknownType = c.newIntrinsicType(TypeFlagsUnknown, "unknown") + c.undefinedType = c.newIntrinsicType(TypeFlagsUndefined, "undefined") + c.undefinedWideningType = c.createWideningType(c.undefinedType) + c.missingType = c.newIntrinsicType(TypeFlagsUndefined, "undefined") + c.undefinedOrMissingType = ifElse(c.exactOptionalPropertyTypes, c.missingType, c.undefinedType) + c.nullType = c.newIntrinsicType(TypeFlagsNull, "null") + c.nullWideningType = c.createWideningType(c.nullType) + c.stringType = c.newIntrinsicType(TypeFlagsString, "string") + c.numberType = c.newIntrinsicType(TypeFlagsNumber, "number") + c.bigintType = c.newIntrinsicType(TypeFlagsBigint, "bigint") + c.regularFalseType = c.newLiteralType(TypeFlagsBooleanLiteral, false, nil) + c.falseType = c.newLiteralType(TypeFlagsBooleanLiteral, false, c.regularFalseType) + c.regularFalseType.LiteralType().freshType = c.falseType + c.falseType.LiteralType().freshType = c.falseType + c.regularTrueType = c.newLiteralType(TypeFlagsBooleanLiteral, false, nil) + c.trueType = c.newLiteralType(TypeFlagsBooleanLiteral, false, c.regularTrueType) + c.regularTrueType.LiteralType().freshType = c.trueType + c.trueType.LiteralType().freshType = c.trueType + c.booleanType = c.getUnionType([]*Type{c.regularFalseType, c.regularTrueType}) + c.esSymbolType = c.newIntrinsicType(TypeFlagsESSymbol, "symbol") + c.voidType = c.newIntrinsicType(TypeFlagsVoid, "void") + c.neverType = c.newIntrinsicType(TypeFlagsNever, "never") + c.nonPrimitiveType = c.newIntrinsicType(TypeFlagsNonPrimitive, "object") + c.emptyObjectType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil) + c.emptyTypeLiteralType = c.newAnonymousType(c.newSymbol(SymbolFlagsTypeLiteral, InternalSymbolNameType), nil, nil, nil, nil) + c.anyFunctionType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil) + c.anyFunctionType.objectFlags |= ObjectFlagsNonInferrableType + c.enumNumberIndexInfo = &IndexInfo{keyType: c.numberType, valueType: c.stringType, isReadonly: true} + c.globalObjectType = c.emptyObjectType // !!! + c.globalFunctionType = c.emptyObjectType // !!! + c.globalCallableFunctionType = c.emptyObjectType // !!! + c.globalNewableFunctionType = c.emptyObjectType // !!! + c.anyArrayType = c.anyType // !!! + c.autoArrayType = c.anyType // !!! + c.anyReadonlyArrayType = c.anyType // !!! + c.initializeChecker() + return c +} + +func (c *Checker) getStrictOptionValue(value Tristate) bool { + if value != TSUnknown { + return value == TSTrue + } + return c.compilerOptions.Strict == TSTrue +} + +func (c *Checker) initializeChecker() { + c.program.bindSourceFiles() + // Initialize global symbol table + var augmentations [][]*Node + for _, file := range c.files { + if !isExternalOrCommonJsModule(file) { + c.mergeSymbolTable(c.globals, file.locals, false, nil) + } + c.patternAmbientModules = append(c.patternAmbientModules, file.patternAmbientModules...) + augmentations = append(augmentations, file.moduleAugmentations) + if file.symbol != nil { + // Merge in UMD exports with first-in-wins semantics (see #9771) + for name, symbol := range file.symbol.globalExports { + if _, ok := c.globals[name]; !ok { + c.globals[name] = symbol + } + } + } + } + // We do global augmentations separately from module augmentations (and before creating global types) because they + // 1. Affect global types. We won't have the correct global types until global augmentations are merged. Also, + // 2. Module augmentation instantiation requires creating the type of a module, which, in turn, can require + // checking for an export or property on the module (if export=) which, in turn, can fall back to the + // apparent type of the module - either globalObjectType or globalFunctionType - which wouldn't exist if we + // did module augmentations prior to finalizing the global types. + for _, list := range augmentations { + for _, augmentation := range list { + // Merge 'global' module augmentations. This needs to be done after global symbol table is initialized to + // make sure that all ambient modules are indexed + if isGlobalScopeAugmentation(augmentation.parent) { + c.mergeModuleAugmentation(augmentation) + } + } + } + c.addUndefinedToGlobalsOrErrorOnRedeclaration() + c.valueSymbolLinks.get(c.undefinedSymbol).resolvedType = c.undefinedWideningType + c.valueSymbolLinks.get(c.argumentsSymbol).resolvedType = c.errorType // !!! + c.valueSymbolLinks.get(c.unknownSymbol).resolvedType = c.errorType + c.valueSymbolLinks.get(c.globalThisSymbol).resolvedType = c.newObjectType(ObjectFlagsAnonymous, c.globalThisSymbol) + + // merge _nonglobal_ module augmentations. + // this needs to be done after global symbol table is initialized to make sure that all ambient modules are indexed + for _, list := range augmentations { + for _, augmentation := range list { + if !isGlobalScopeAugmentation(augmentation.parent) { + c.mergeModuleAugmentation(augmentation) + } + } + } +} + +func (c *Checker) mergeModuleAugmentation(moduleName *Node) { + moduleNode := moduleName.parent + moduleAugmentation := moduleNode.AsModuleDeclaration() + if moduleAugmentation.symbol.declarations[0] != moduleNode { + // this is a combined symbol for multiple augmentations within the same file. + // its symbol already has accumulated information for all declarations + // so we need to add it just once - do the work only for first declaration + return + } + if isGlobalScopeAugmentation(moduleNode) { + c.mergeSymbolTable(c.globals, moduleAugmentation.symbol.exports, false /*unidirectional*/, nil /*parent*/) + } else { + // find a module that about to be augmented + // do not validate names of augmentations that are defined in ambient context + var moduleNotFoundError *diagnostics.Message + if moduleName.parent.parent.flags&NodeFlagsAmbient == 0 { + moduleNotFoundError = diagnostics.Invalid_module_name_in_augmentation_module_0_cannot_be_found + } + mainModule := c.resolveExternalModuleNameWorker(moduleName, moduleName, moduleNotFoundError /*ignoreErrors*/, false /*isForAugmentation*/, true) + if mainModule == nil { + return + } + // obtain item referenced by 'export=' + mainModule = c.resolveExternalModuleSymbol(mainModule, false /*dontResolveAlias*/) + if mainModule.flags&SymbolFlagsNamespace != 0 { + // If we're merging an augmentation to a pattern ambient module, we want to + // perform the merge unidirectionally from the augmentation ('a.foo') to + // the pattern ('*.foo'), so that 'getMergedSymbol()' on a.foo gives you + // all the exports both from the pattern and from the augmentation, but + // 'getMergedSymbol()' on *.foo only gives you exports from *.foo. + if some(c.patternAmbientModules, func(module PatternAmbientModule) bool { + return mainModule == module.symbol + }) { + merged := c.mergeSymbol(moduleAugmentation.symbol, mainModule, true /*unidirectional*/) + if c.patternAmbientModuleAugmentations == nil { + c.patternAmbientModuleAugmentations = make(SymbolTable) + } + // moduleName will be a StringLiteral since this is not `declare global`. + c.patternAmbientModuleAugmentations[getTextOfIdentifierOrLiteral(moduleName)] = merged + } else { + if mainModule.exports[InternalSymbolNameExportStar] != nil && len(moduleAugmentation.symbol.exports) != 0 { + // We may need to merge the module augmentation's exports into the target symbols of the resolved exports + resolvedExports := c.getResolvedMembersOrExportsOfSymbol(mainModule, MembersOrExportsResolutionKindResolvedExports) + for key, value := range moduleAugmentation.symbol.exports { + if resolvedExports[key] != nil && mainModule.exports[key] == nil { + c.mergeSymbol(resolvedExports[key], value, false /*unidirectional*/) + } + } + } + c.mergeSymbol(mainModule, moduleAugmentation.symbol, false /*unidirectional*/) + } + } else { + // moduleName will be a StringLiteral since this is not `declare global`. + c.error(moduleName, diagnostics.Cannot_augment_module_0_because_it_resolves_to_a_non_module_entity, getTextOfIdentifierOrLiteral(moduleName)) + } + } +} + +func (c *Checker) addUndefinedToGlobalsOrErrorOnRedeclaration() { + name := c.undefinedSymbol.name + targetSymbol := c.globals[name] + if targetSymbol != nil { + for _, declaration := range targetSymbol.declarations { + if !isTypeDeclaration(declaration) { + c.diagnostics.add(createDiagnosticForNode(declaration, diagnostics.Declaration_name_conflicts_with_built_in_global_identifier_0, name)) + } + } + } else { + c.globals[name] = c.undefinedSymbol + } +} + +func (c *Checker) createNameResolver() *NameResolver { + return &NameResolver{ + compilerOptions: c.compilerOptions, + getSymbolOfDeclaration: c.getSymbolOfDeclaration, + error: c.error, + globals: c.globals, + argumentsSymbol: c.argumentsSymbol, + requireSymbol: c.requireSymbol, + lookup: c.getSymbol, + setRequiresScopeChangeCache: c.setRequiresScopeChangeCache, + getRequiresScopeChangeCache: c.getRequiresScopeChangeCache, + onPropertyWithInvalidInitializer: c.checkAndReportErrorForInvalidInitializer, + onFailedToResolveSymbol: c.onFailedToResolveSymbol, + onSuccessfullyResolvedSymbol: c.onSuccessfullyResolvedSymbol, + } +} + +func (c *Checker) getRequiresScopeChangeCache(node *Node) Tristate { + return c.nodeLinks.get(node).declarationRequiresScopeChange +} + +func (c *Checker) setRequiresScopeChangeCache(node *Node, value Tristate) { + c.nodeLinks.get(node).declarationRequiresScopeChange = value +} + +// The invalid initializer error is needed in two situation: +// 1. When result is undefined, after checking for a missing "this." +// 2. When result is defined +func (c *Checker) checkAndReportErrorForInvalidInitializer(errorLocation *Node, name string, propertyWithInvalidInitializer *Node, result *Symbol) bool { + if !getEmitStandardClassFields(c.compilerOptions) { + if errorLocation != nil && result == nil && c.checkAndReportErrorForMissingPrefix(errorLocation, name) { + return true + } + // We have a match, but the reference occurred within a property initializer and the identifier also binds + // to a local variable in the constructor where the code will be emitted. Note that this is actually allowed + // with emitStandardClassFields because the scope semantics are different. + prop := propertyWithInvalidInitializer.AsPropertyDeclaration() + message := ifElse(errorLocation != nil && prop.typeNode != nil && prop.typeNode.loc.ContainsInclusive(errorLocation.Pos()), + diagnostics.Type_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor, + diagnostics.Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor) + c.error(errorLocation, message, declarationNameToString(prop.name), name) + return true + } + return false +} + +func (c *Checker) onFailedToResolveSymbol(errorLocation *Node, name string, meaning SymbolFlags, nameNotFoundMessage *diagnostics.Message) { + // !!! + c.error(errorLocation, nameNotFoundMessage, name, "???") +} + +func (c *Checker) onSuccessfullyResolvedSymbol(errorLocation *Node, result *Symbol, meaning SymbolFlags, lastLocation *Node, associatedDeclarationForContainingInitializerOrBindingName *Node, withinDeferredContext bool) { + name := result.name + isInExternalModule := lastLocation != nil && isSourceFile(lastLocation) && isExternalOrCommonJsModule(lastLocation.AsSourceFile()) + // Only check for block-scoped variable if we have an error location and are looking for the + // name with variable meaning + // For example, + // declare module foo { + // interface bar {} + // } + // const foo/*1*/: foo/*2*/.bar; + // The foo at /*1*/ and /*2*/ will share same symbol with two meanings: + // block-scoped variable and namespace module. However, only when we + // try to resolve name in /*1*/ which is used in variable position, + // we want to check for block-scoped + if errorLocation != nil && (meaning&SymbolFlagsBlockScopedVariable != 0 || meaning&(SymbolFlagsClass|SymbolFlagsEnum) != 0 && meaning&SymbolFlagsValue == SymbolFlagsValue) { + exportOrLocalSymbol := c.getExportSymbolOfValueSymbolIfExported(result) + if exportOrLocalSymbol.flags&(SymbolFlagsBlockScopedVariable|SymbolFlagsClass|SymbolFlagsEnum) != 0 { + c.checkResolvedBlockScopedVariable(exportOrLocalSymbol, errorLocation) + } + } + // If we're in an external module, we can't reference value symbols created from UMD export declarations + if isInExternalModule && (meaning&SymbolFlagsValue) == SymbolFlagsValue && errorLocation.flags&NodeFlagsJSDoc == 0 { + merged := c.getMergedSymbol(result) + if len(merged.declarations) != 0 && every(merged.declarations, func(d *Node) bool { + return isNamespaceExportDeclaration(d) || isSourceFile(d) && d.Symbol().globalExports != nil + }) { + c.errorOrSuggestion(c.compilerOptions.AllowUmdGlobalAccess != TSTrue, errorLocation, diagnostics.X_0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead, name) + } + } + // If we're in a parameter initializer or binding name, we can't reference the values of the parameter whose initializer we're within or parameters to the right + if associatedDeclarationForContainingInitializerOrBindingName != nil && !withinDeferredContext && (meaning&SymbolFlagsValue) == SymbolFlagsValue { + candidate := c.getMergedSymbol(c.getLateBoundSymbol(result)) + root := getRootDeclaration(associatedDeclarationForContainingInitializerOrBindingName) + // A parameter initializer or binding pattern initializer within a parameter cannot refer to itself + if candidate == c.getSymbolOfDeclaration(associatedDeclarationForContainingInitializerOrBindingName) { + c.error(errorLocation, diagnostics.Parameter_0_cannot_reference_itself, declarationNameToString(associatedDeclarationForContainingInitializerOrBindingName.Name())) + } else if candidate.valueDeclaration != nil && candidate.valueDeclaration.Pos() > associatedDeclarationForContainingInitializerOrBindingName.Pos() && root.parent.LocalsContainerData().locals != nil && c.getSymbol(root.parent.LocalsContainerData().locals, candidate.name, meaning) == candidate { + c.error(errorLocation, diagnostics.Parameter_0_cannot_reference_identifier_1_declared_after_it, declarationNameToString(associatedDeclarationForContainingInitializerOrBindingName.Name()), declarationNameToString(errorLocation)) + } + } + if errorLocation != nil && meaning&SymbolFlagsValue != 0 && result.flags&SymbolFlagsAlias != 0 && result.flags&SymbolFlagsValue == 0 && !isValidTypeOnlyAliasUseSite(errorLocation) { + typeOnlyDeclaration := c.getTypeOnlyAliasDeclarationEx(result, SymbolFlagsValue) + if typeOnlyDeclaration != nil { + message := ifElse(nodeKindIs(typeOnlyDeclaration, SyntaxKindExportSpecifier, SyntaxKindExportDeclaration, SyntaxKindNamespaceExport), + diagnostics.X_0_cannot_be_used_as_a_value_because_it_was_exported_using_export_type, + diagnostics.X_0_cannot_be_used_as_a_value_because_it_was_imported_using_import_type) + c.addTypeOnlyDeclarationRelatedInfo(c.error(errorLocation, message, name), typeOnlyDeclaration, name) + } + } + // Look at 'compilerOptions.isolatedModules' and not 'getIsolatedModules(...)' (which considers 'verbatimModuleSyntax') + // here because 'verbatimModuleSyntax' will already have an error for importing a type without 'import type'. + if c.compilerOptions.IsolatedModules == TSTrue && result != nil && isInExternalModule && (meaning&SymbolFlagsValue) == SymbolFlagsValue { + isGlobal := c.getSymbol(c.globals, name, meaning) == result + var nonValueSymbol *Symbol + if isGlobal && isSourceFile(lastLocation) { + nonValueSymbol = c.getSymbol(lastLocation.AsSourceFile().locals, name, ^SymbolFlagsValue) + } + if nonValueSymbol != nil { + importDecl := find(nonValueSymbol.declarations, func(d *Node) bool { + return nodeKindIs(d, SyntaxKindImportSpecifier, SyntaxKindImportClause, SyntaxKindNamespaceImport, SyntaxKindImportEqualsDeclaration) + }) + if importDecl != nil && !isTypeOnlyImportDeclaration(importDecl) { + c.error(importDecl, diagnostics.Import_0_conflicts_with_global_value_used_in_this_file_so_must_be_declared_with_a_type_only_import_when_isolatedModules_is_enabled, name) + } + } + } +} + +func (c *Checker) checkResolvedBlockScopedVariable(result *Symbol, errorLocation *Node) { + //Debug.assert(!!(result.flags&SymbolFlagsBlockScopedVariable || result.flags&SymbolFlagsClass || result.flags&SymbolFlagsEnum)) + if result.flags&(SymbolFlagsFunction|SymbolFlagsFunctionScopedVariable|SymbolFlagsAssignment) != 0 && result.flags&SymbolFlagsClass != 0 { + // constructor functions aren't block scoped + return + } + // Block-scoped variables cannot be used before their definition + declaration := find(result.declarations, func(d *Node) bool { + return isBlockOrCatchScoped(d) || isClassLike(d) || isEnumDeclaration(d) + }) + if declaration == nil { + panic("checkResolvedBlockScopedVariable could not find block-scoped declaration") + } + if declaration.flags&NodeFlagsAmbient == 0 && !c.isBlockScopedNameDeclaredBeforeUse(declaration, errorLocation) { + var diagnostic *Diagnostic + declarationName := declarationNameToString(getNameOfDeclaration(declaration)) + if result.flags&SymbolFlagsBlockScopedVariable != 0 { + diagnostic = c.error(errorLocation, diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationName) + } else if result.flags&SymbolFlagsClass != 0 { + diagnostic = c.error(errorLocation, diagnostics.Class_0_used_before_its_declaration, declarationName) + } else if result.flags&SymbolFlagsRegularEnum != 0 { + diagnostic = c.error(errorLocation, diagnostics.Enum_0_used_before_its_declaration, declarationName) + } else { + //Debug.assert(!!(result.flags & SymbolFlagsConstEnum)) + if getIsolatedModules(c.compilerOptions) { + diagnostic = c.error(errorLocation, diagnostics.Enum_0_used_before_its_declaration, declarationName) + } + } + if diagnostic != nil { + addRelatedInfo(diagnostic, createDiagnosticForNode(declaration, diagnostics.X_0_is_declared_here, declarationName)) + } + } +} + +func (c *Checker) isBlockScopedNameDeclaredBeforeUse(declaration *Node, usage *Node) bool { + return true // !!! +} + +func (c *Checker) checkAndReportErrorForMissingPrefix(errorLocation *Node, name string) bool { + return false // !!! +} + +func (c *Checker) getTypeOnlyAliasDeclaration(symbol *Symbol) *Node { + return c.getTypeOnlyAliasDeclarationEx(symbol, SymbolFlagsNone) +} + +func (c *Checker) getTypeOnlyAliasDeclarationEx(symbol *Symbol, include SymbolFlags) *Node { + if symbol.flags&SymbolFlagsAlias == 0 { + return nil + } + links := c.aliasSymbolLinks.get(symbol) + if !links.typeOnlyDeclarationResolved { + // We need to set a WIP value here to prevent reentrancy during `getImmediateAliasedSymbol` which, paradoxically, can depend on this + links.typeOnlyDeclarationResolved = true + resolved := c.resolveSymbol(symbol) + // While usually the alias will have been marked during the pass by the full typecheck, we may still need to calculate the alias declaration now + var immediateTarget *Symbol + if c.getDeclarationOfAliasSymbol(symbol) != nil { + immediateTarget = c.getImmediateAliasedSymbol(symbol) + } + c.markSymbolOfAliasDeclarationIfTypeOnly(symbol.declarations[0], immediateTarget, resolved, true /*overwriteEmpty*/, nil, "") + } + if include == SymbolFlagsNone { + return links.typeOnlyDeclaration + } + if links.typeOnlyDeclaration != nil { + var resolved *Symbol + if links.typeOnlyDeclaration.kind == SyntaxKindExportDeclaration { + name := links.typeOnlyExportStarName + if name == "" { + name = symbol.name + } + resolved = c.resolveSymbol(c.getExportsOfModule(links.typeOnlyDeclaration.Symbol().parent)[name]) + } else { + resolved = c.resolveAlias(links.typeOnlyDeclaration.Symbol()) + } + if c.getSymbolFlags(resolved)&include != 0 { + return links.typeOnlyDeclaration + } + } + return nil +} + +func (c *Checker) getImmediateAliasedSymbol(symbol *Symbol) *Symbol { + // Debug.assert((symbol.flags&SymbolFlagsAlias) != 0, "Should only get Alias here.") + links := c.aliasSymbolLinks.get(symbol) + if links.immediateTarget == nil { + node := c.getDeclarationOfAliasSymbol(symbol) + if node == nil { + panic("Unexpected nil in getImmediateAliasedSymbol") + } + links.immediateTarget = c.getTargetOfAliasDeclaration(node, true /*dontRecursivelyResolve*/) + } + + return links.immediateTarget +} + +func (c *Checker) addTypeOnlyDeclarationRelatedInfo(diagnostic *Diagnostic, typeOnlyDeclaration *Node, name string) { + // !!! +} + +func (c *Checker) getSymbol(symbols SymbolTable, name string, meaning SymbolFlags) *Symbol { + if meaning != 0 { + symbol := c.getMergedSymbol(symbols[name]) + if symbol != nil { + if symbol.flags&meaning != 0 { + return symbol + } + if symbol.flags&SymbolFlagsAlias != 0 { + targetFlags := c.getSymbolFlags(symbol) + // `targetFlags` will be `SymbolFlags.All` if an error occurred in alias resolution; this avoids cascading errors + if targetFlags&meaning != 0 { + return symbol + } + } + } + } + // return nil if we can't find a symbol + return nil +} + +func (c *Checker) checkSourceFile(sourceFile *SourceFile) { + node := sourceFile.AsNode() + links := c.nodeLinks.get(node) + if links.flags&NodeCheckFlagsTypeChecked == 0 { + c.checkSourceElement(node) + links.flags |= NodeCheckFlagsTypeChecked + } +} + +func (c *Checker) checkSourceElement(node *Node) bool { + if node != nil { + saveCurrentNode := c.currentNode + c.currentNode = node + c.instantiationCount = 0 + c.checkSourceElementWorker(node) + c.currentNode = saveCurrentNode + } + return false +} + +func (c *Checker) checkSourceElementWorker(node *Node) { + // !!! Cancellation + kind := node.kind + if kind >= SyntaxKindFirstStatement && kind <= SyntaxKindLastStatement { + flowNode := node.FlowNodeData().flowNode + if flowNode != nil && !c.isReachableFlowNode(flowNode) { + c.errorOrSuggestion(c.compilerOptions.AllowUnreachableCode == TSFalse, node, diagnostics.Unreachable_code_detected) + } + } + switch node.kind { + case SyntaxKindIdentifier: + if isExpressionNode(node) && + !(isPropertyAccessExpression(node.parent) && node.parent.AsPropertyAccessExpression().name == node) && + !(isQualifiedName(node.parent) && node.parent.AsQualifiedName().right == node) { + c.checkExpression(node) + } + case SyntaxKindStringLiteral, SyntaxKindNumericLiteral, SyntaxKindBigintLiteral: + if isExpressionNode(node) { + c.checkExpression(node) + } + default: + node.ForEachChild(c.checkSourceElement) + } +} + +/** + * Returns the type of an expression. Unlike checkExpression, this function is simply concerned + * with computing the type and may not fully check all contained sub-expressions for errors. + */ + +func (c *Checker) getTypeOfExpression(node *Node) *Type { + // !!! + // // Don't bother caching types that require no flow analysis and are quick to compute. + // quickType := c.getQuickTypeOfExpression(node) + // if quickType != nil { + // return quickType + // } + // // If a type has been cached for the node, return it. + // if node.flags&NodeFlagsTypeCached != 0 { + // cachedType := c.flowTypeCache[getNodeId(node)] + // if cachedType { + // return cachedType + // } + // } + // startInvocationCount := c.flowInvocationCount + // t := c.checkExpressionEx(node, CheckModeTypeOnly) + // // If control flow analysis was required to determine the type, it is worth caching. + // if c.flowInvocationCount != startInvocationCount { + // cache := c.flowTypeCache || ( /* TODO(TS-TO-GO) EqualsToken BinaryExpression: flowTypeCache = [] */ TODO) + // cache[getNodeId(node)] = t + // setNodeFlags(node, node.flags|NodeFlagsTypeCached) + // } + t := c.checkExpressionEx(node, CheckModeTypeOnly) + return t +} + +/** + * Returns the type of an expression. Unlike checkExpression, this function is simply concerned + * with computing the type and may not fully check all contained sub-expressions for errors. + */ +func (c *Checker) getQuickTypeOfExpression(node *Node) *Type { + // !!! + return nil +} + +func (c *Checker) checkExpressionWithContextualType(node *Node, contextualType *Type, inferenceContext *InferenceContext, checkMode CheckMode) *Type { + // !!! + return c.checkExpressionEx(node, checkMode) +} + +func (c *Checker) checkExpressionCached(node *Node) *Type { + return c.checkExpressionCachedEx(node, CheckModeNormal) +} + +func (c *Checker) checkExpressionCachedEx(node *Node, checkMode CheckMode) *Type { + // !!! + return c.checkExpressionEx(node, checkMode) +} + +func (c *Checker) checkExpression(node *Node) *Type { + return c.checkExpressionEx(node, CheckModeNormal) +} + +func (c *Checker) checkExpressionEx(node *Node, checkMode CheckMode) *Type { + saveCurrentNode := c.currentNode + c.currentNode = node + c.instantiationCount = 0 + uninstantiatedType := c.checkExpressionWorker(node, checkMode) + t := c.instantiateTypeWithSingleGenericCallSignature(node, uninstantiatedType, checkMode) + // !!! + // if isConstEnumObjectType(typ) { + // checkConstEnumAccess(node, typ) + // } + c.currentNode = saveCurrentNode + return t +} + +func (c *Checker) instantiateTypeWithSingleGenericCallSignature(node *Node, uninstantiatedType *Type, checkMode CheckMode) *Type { + return uninstantiatedType // !!! +} + +func (c *Checker) checkExpressionWorker(node *Node, checkMode CheckMode) *Type { + switch node.kind { + case SyntaxKindIdentifier: + return c.checkIdentifier(node) + case SyntaxKindStringLiteral: + // !!! Handle blockedStringType + return c.getFreshTypeOfLiteralType(c.getStringLiteralType(node.AsStringLiteral().text)) + case SyntaxKindNoSubstitutionTemplateLiteral: + // !!! Handle blockedStringType + return c.getFreshTypeOfLiteralType(c.getStringLiteralType(node.AsNoSubstitutionTemplateLiteral().text)) + case SyntaxKindNumericLiteral: + // !!! checkGrammarNumericLiteral(node as NumericLiteral) + value, _ := strconv.ParseFloat(node.AsNumericLiteral().text, 64) + return c.getFreshTypeOfLiteralType(c.getNumberLiteralType(value)) + case SyntaxKindBigintLiteral: + // !!! checkGrammarBigIntLiteral(node as BigIntLiteral); + return c.getFreshTypeOfLiteralType(c.getBigintLiteralType(PseudoBigint{ + negative: false, + base10Value: parsePseudoBigint(node.AsBigintLiteral().text), + })) + case SyntaxKindTrueKeyword: + return c.trueType + case SyntaxKindFalseKeyword: + return c.falseType + } + return c.anyType // !!! +} + +func (c *Checker) checkIdentifier(node *Node) *Type { + if isThisInTypeQuery(node) { + return c.checkThisExpression(node) + } + symbol := c.getResolvedSymbol(node) + if symbol == c.unknownSymbol { + return c.errorType + } + // !!! c.checkIdentifierCalculateNodeCheckFlags(node, symbol) + if symbol == c.argumentsSymbol { + if c.isInPropertyInitializerOrClassStaticBlock(node) { + return c.errorType + } + return c.getTypeOfSymbol(symbol) + } + // !!! + // if c.shouldMarkIdentifierAliasReferenced(node) { + // c.markLinkedReferences(node, ReferenceHintIdentifier) + // } + localOrExportSymbol := c.getExportSymbolOfValueSymbolIfExported(symbol) + declaration := localOrExportSymbol.valueDeclaration + // !!! + // immediateDeclaration := declaration + // If the identifier is declared in a binding pattern for which we're currently computing the implied type and the + // reference occurs with the same binding pattern, return the non-inferrable any type. This for example occurs in + // 'const [a, b = a + 1] = [2]' when we're computing the contextual type for the array literal '[2]'. + if declaration != nil && declaration.kind == SyntaxKindBindingElement && slices.Contains(c.contextualBindingPatterns, declaration.parent) && + findAncestor(node, func(parent *Node) bool { return parent == declaration.parent }) != nil { + return c.nonInferrableAnyType + } + t := c.getNarrowedTypeOfSymbol(localOrExportSymbol, node) + assignmentKind := getAssignmentTargetKind(node) + if assignmentKind != AssignmentKindNone { + if localOrExportSymbol.flags&SymbolFlagsVariable == 0 { + var assignmentError *diagnostics.Message + switch { + case localOrExportSymbol.flags&SymbolFlagsEnum != 0: + assignmentError = diagnostics.Cannot_assign_to_0_because_it_is_an_enum + case localOrExportSymbol.flags&SymbolFlagsClass != 0: + assignmentError = diagnostics.Cannot_assign_to_0_because_it_is_a_class + case localOrExportSymbol.flags&SymbolFlagsModule != 0: + assignmentError = diagnostics.Cannot_assign_to_0_because_it_is_a_namespace + case localOrExportSymbol.flags&SymbolFlagsFunction != 0: + assignmentError = diagnostics.Cannot_assign_to_0_because_it_is_a_function + case localOrExportSymbol.flags&SymbolFlagsAlias != 0: + assignmentError = diagnostics.Cannot_assign_to_0_because_it_is_an_import + default: + assignmentError = diagnostics.Cannot_assign_to_0_because_it_is_not_a_variable + } + c.error(node, assignmentError, c.symbolToString(symbol)) + return c.errorType + } + if c.isReadonlySymbol(localOrExportSymbol) { + if localOrExportSymbol.flags&SymbolFlagsVariable != 0 { + c.error(node, diagnostics.Cannot_assign_to_0_because_it_is_a_constant, c.symbolToString(symbol)) + } else { + c.error(node, diagnostics.Cannot_assign_to_0_because_it_is_a_read_only_property, c.symbolToString(symbol)) + } + return c.errorType + } + } + isAlias := localOrExportSymbol.flags&SymbolFlagsAlias != 0 + // We only narrow variables and parameters occurring in a non-assignment position. For all other + // entities we simply return the declared type. + if localOrExportSymbol.flags&SymbolFlagsVariable != 0 { + if assignmentKind == AssignmentKindDefinite { + if isInCompoundLikeAssignment(node) { + return c.getBaseTypeOfLiteralType(t) + } + return t + } + } else if isAlias { + declaration = c.getDeclarationOfAliasSymbol(symbol) + } else { + return t + } + if declaration == nil { + return t + } + // !!! + flowType := t + if assignmentKind != AssignmentKindNone { + // Identifier is target of a compound assignment + return c.getBaseTypeOfLiteralType(flowType) + } + return flowType +} + +func (c *Checker) isInPropertyInitializerOrClassStaticBlock(node *Node) bool { + return findAncestorOrQuit(node, func(node *Node) FindAncestorResult { + switch node.kind { + case SyntaxKindPropertyDeclaration: + return FindAncestorTrue + case SyntaxKindPropertyAssignment, SyntaxKindMethodDeclaration, SyntaxKindGetAccessor, SyntaxKindSetAccessor, SyntaxKindSpreadAssignment, + SyntaxKindComputedPropertyName, SyntaxKindTemplateSpan, SyntaxKindJsxExpression, SyntaxKindJsxAttribute, SyntaxKindJsxAttributes, + SyntaxKindJsxSpreadAttribute, SyntaxKindJsxOpeningElement, SyntaxKindExpressionWithTypeArguments, SyntaxKindHeritageClause: + return FindAncestorFalse + case SyntaxKindArrowFunction, SyntaxKindExpressionStatement: + if isBlock(node.parent) && isClassStaticBlockDeclaration(node.parent.parent) { + return FindAncestorTrue + } + return FindAncestorQuit + default: + if isExpressionNode(node) { + return FindAncestorFalse + } + return FindAncestorQuit + } + }) != nil +} + +func (c *Checker) getNarrowedTypeOfSymbol(symbol *Symbol, location *Node) *Type { + return c.getTypeOfSymbol(symbol) // !!! +} + +func (c *Checker) isReadonlySymbol(symbol *Symbol) bool { + // The following symbols are considered read-only: + // Properties with a 'readonly' modifier + // Variables declared with 'const' + // Get accessors without matching set accessors + // Enum members + // Object.defineProperty assignments with writable false or no setter + // Unions and intersections of the above (unions and intersections eagerly set isReadonly on creation) + return symbol.checkFlags&CheckFlagsReadonly != 0 || + symbol.flags&SymbolFlagsProperty != 0 && getDeclarationModifierFlagsFromSymbol(symbol, false /*isWrite*/)&ModifierFlagsReadonly != 0 || + symbol.flags&SymbolFlagsVariable != 0 && c.getDeclarationNodeFlagsFromSymbol(symbol)&NodeFlagsConstant != 0 || + symbol.flags&SymbolFlagsAccessor != 0 && symbol.flags&SymbolFlagsSetAccessor == 0 || + symbol.flags&SymbolFlagsEnumMember != 0 +} + +func (c *Checker) checkThisExpression(node *Node) *Type { + return c.anyType // !!! +} + +func (c *Checker) checkObjectLiteralMethod(node *MethodDeclaration, checkMode CheckMode) *Type { + return c.anyType // !!! +} + +func (c *Checker) checkPropertyAssignment(node *PropertyAssignment, checkMode CheckMode) *Type { + return c.anyType // !!! +} + +func (c *Checker) checkJsxAttribute(node *JsxAttribute, checkMode CheckMode) *Type { + return c.anyType // !!! +} + +func (c *Checker) checkExpressionForMutableLocation(node *Node, checkMode CheckMode) *Type { + return c.anyType // !!! +} + +func (c *Checker) getResolvedSymbol(node *Node) *Symbol { + links := c.nodeLinks.get(node) + if links.resolvedSymbol == nil { + var symbol *Symbol + if !nodeIsMissing(node) { + symbol = c.resolveName(node, node.AsIdentifier().text, SymbolFlagsValue|SymbolFlagsExportValue, + c.getCannotFindNameDiagnosticForName(node), !isWriteOnlyAccess(node), false /*excludeGlobals*/) + } + if symbol == nil { + symbol = c.unknownSymbol + } + links.resolvedSymbol = symbol + } + return links.resolvedSymbol +} + +func isWriteOnlyAccess(node *Node) bool { + return false // !!! +} + +func (c *Checker) getCannotFindNameDiagnosticForName(node *Node) *diagnostics.Message { + switch node.AsIdentifier().text { + case "document", "console": + return diagnostics.Cannot_find_name_0_Do_you_need_to_change_your_target_library_Try_changing_the_lib_compiler_option_to_include_dom + case "$": + return ifElse(c.compilerOptions.Types != nil, + diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_jQuery_Try_npm_i_save_dev_types_Slashjquery_and_then_add_jquery_to_the_types_field_in_your_tsconfig, + diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_jQuery_Try_npm_i_save_dev_types_Slashjquery) + case "describe", "suite", "it", "test": + return ifElse(c.compilerOptions.Types != nil, + diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_a_test_runner_Try_npm_i_save_dev_types_Slashjest_or_npm_i_save_dev_types_Slashmocha_and_then_add_jest_or_mocha_to_the_types_field_in_your_tsconfig, + diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_a_test_runner_Try_npm_i_save_dev_types_Slashjest_or_npm_i_save_dev_types_Slashmocha) + case "process", "require", "Buffer", "module": + return ifElse(c.compilerOptions.Types != nil, + diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_node_Try_npm_i_save_dev_types_Slashnode_and_then_add_node_to_the_types_field_in_your_tsconfig, + diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_node_Try_npm_i_save_dev_types_Slashnode) + case "Bun": + return ifElse(c.compilerOptions.Types != nil, + diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_Bun_Try_npm_i_save_dev_types_Slashbun_and_then_add_bun_to_the_types_field_in_your_tsconfig, + diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_Bun_Try_npm_i_save_dev_types_Slashbun) + case "Map", "Set", "Promise", "Symbol", "WeakMap", "WeakSet", "Iterator", "AsyncIterator", "SharedArrayBuffer", "Atomics", "AsyncIterable", + "AsyncIterableIterator", "AsyncGenerator", "AsyncGeneratorFunction", "BigInt", "Reflect", "BigInt64Array", "BigUint64Array": + return diagnostics.Cannot_find_name_0_Do_you_need_to_change_your_target_library_Try_changing_the_lib_compiler_option_to_1_or_later + case "await": + if isCallExpression(node.parent) { + return diagnostics.Cannot_find_name_0_Did_you_mean_to_write_this_in_an_async_function + } + fallthrough + default: + if node.parent.kind == SyntaxKindShorthandPropertyAssignment { + return diagnostics.No_value_exists_in_scope_for_the_shorthand_property_0_Either_declare_one_or_provide_an_initializer + } + return diagnostics.Cannot_find_name_0 + } +} + +func (c *Checker) isReachableFlowNode(flowNode *FlowNode) bool { + return true // !!! +} + +func (c *Checker) GetDiagnostics(sourceFile *SourceFile) []*Diagnostic { + if sourceFile != nil { + c.checkSourceFile(sourceFile) + return c.diagnostics.GetDiagnosticsForFile(sourceFile.fileName) + } + for _, file := range c.files { + c.checkSourceFile(file) + } + return c.diagnostics.GetDiagnostics() +} + +func (c *Checker) GetGlobalDiagnostics() []*Diagnostic { + return c.diagnostics.GetGlobalDiagnostics() +} + +func (c *Checker) error(location *Node, message *diagnostics.Message, args ...any) *Diagnostic { + diagnostic := NewDiagnosticForNode(location, message, args...) + c.diagnostics.add(diagnostic) + return diagnostic +} + +func (c *Checker) errorOrSuggestion(isError bool, location *Node, message *diagnostics.Message, args ...any) { + c.addErrorOrSuggestion(isError, NewDiagnosticForNode(location, message, args...)) +} + +func (c *Checker) addErrorOrSuggestion(isError bool, diagnostic *Diagnostic) { + if isError { + c.diagnostics.add(diagnostic) + } else { + suggestion := *diagnostic + suggestion.category = diagnostics.CategorySuggestion + c.suggestionDiagnostics.add(&suggestion) + } +} + +func (c *Checker) newSymbol(flags SymbolFlags, name string) *Symbol { + c.symbolCount++ + result := c.symbolPool.New() + result.flags = flags | SymbolFlagsTransient + result.name = name + return result +} + +func (c *Checker) newSymbolEx(flags SymbolFlags, name string, checkFlags CheckFlags) *Symbol { + result := c.newSymbol(flags, name) + result.checkFlags = checkFlags + return result +} + +func (c *Checker) mergeSymbolTable(target SymbolTable, source SymbolTable, unidirectional bool, mergedParent *Symbol) { + for id, sourceSymbol := range source { + targetSymbol := target[id] + var merged *Symbol + if targetSymbol != nil { + merged = c.mergeSymbol(targetSymbol, sourceSymbol, unidirectional) + } else { + merged = c.getMergedSymbol(sourceSymbol) + } + if mergedParent != nil && targetSymbol != nil { + // If a merge was performed on the target symbol, set its parent to the merged parent that initiated the merge + // of its exports. Otherwise, `merged` came only from `sourceSymbol` and can keep its parent: + // + // // a.ts + // export interface A { x: number; } + // + // // b.ts + // declare module "./a" { + // interface A { y: number; } + // interface B {} + // } + // + // When merging the module augmentation into a.ts, the symbol for `A` will itself be merged, so its parent + // should be the merged module symbol. But the symbol for `B` has only one declaration, so its parent should + // be the module augmentation symbol, which contains its only declaration. + merged.parent = mergedParent + } + target[id] = merged + } +} + +/** + * Note: if target is transient, then it is mutable, and mergeSymbol with both mutate and return it. + * If target is not transient, mergeSymbol will produce a transient clone, mutate that and return it. + */ +func (c *Checker) mergeSymbol(target *Symbol, source *Symbol, unidirectional bool) *Symbol { + if target.flags&getExcludedSymbolFlags(source.flags) == 0 || (source.flags|target.flags)&SymbolFlagsAssignment != 0 { + if source == target { + // This can happen when an export assigned namespace exports something also erroneously exported at the top level + // See `declarationFileNoCrashOnExtraExportModifier` for an example + return target + } + if target.flags&SymbolFlagsTransient == 0 { + resolvedTarget := c.resolveSymbol(target) + if resolvedTarget == c.unknownSymbol { + return source + } + if resolvedTarget.flags&getExcludedSymbolFlags(source.flags) == 0 || (source.flags|resolvedTarget.flags)&SymbolFlagsAssignment != 0 { + target = c.cloneSymbol(resolvedTarget) + } else { + c.reportMergeSymbolError(target, source) + return source + } + } + // Javascript static-property-assignment declarations always merge, even though they are also values + if source.flags&SymbolFlagsValueModule != 0 && target.flags&SymbolFlagsValueModule != 0 && target.constEnumOnlyModule && !source.constEnumOnlyModule { + // reset flag when merging instantiated module into value module that has only const enums + target.constEnumOnlyModule = false + } + target.flags |= source.flags + if source.valueDeclaration != nil { + setValueDeclaration(target, source.valueDeclaration) + } + target.declarations = append(target.declarations, source.declarations...) + if source.members != nil { + if target.members == nil { + target.members = make(SymbolTable) + } + c.mergeSymbolTable(target.members, source.members, unidirectional, nil) + } + if source.exports != nil { + if target.exports == nil { + target.exports = make(SymbolTable) + } + c.mergeSymbolTable(target.exports, source.exports, unidirectional, target) + } + if !unidirectional { + c.recordMergedSymbol(target, source) + } + } else if target.flags&SymbolFlagsNamespaceModule != 0 { + // Do not report an error when merging `var globalThis` with the built-in `globalThis`, + // as we will already report a "Declaration name conflicts..." error, and this error + // won't make much sense. + if target != c.globalThisSymbol { + c.error(getNameOfDeclaration(getFirstDeclaration(source)), diagnostics.Cannot_augment_module_0_with_value_exports_because_it_resolves_to_a_non_module_entity, c.symbolToString(target)) + } + } else { + c.reportMergeSymbolError(target, source) + } + return target +} + +func (c *Checker) reportMergeSymbolError(target *Symbol, source *Symbol) { + isEitherEnum := target.flags&SymbolFlagsEnum != 0 || source.flags&SymbolFlagsEnum != 0 + isEitherBlockScoped := target.flags&SymbolFlagsBlockScopedVariable != 0 || source.flags&SymbolFlagsBlockScopedVariable != 0 + var message *diagnostics.Message + switch { + case isEitherEnum: + message = diagnostics.Enum_declarations_can_only_merge_with_namespace_or_other_enum_declarations + case isEitherBlockScoped: + message = diagnostics.Cannot_redeclare_block_scoped_variable_0 + default: + message = diagnostics.Duplicate_identifier_0 + } + // sourceSymbolFile := getSourceFileOfNode(getFirstDeclaration(source)) + // targetSymbolFile := getSourceFileOfNode(getFirstDeclaration(target)) + symbolName := c.symbolToString(source) + // !!! + // Collect top-level duplicate identifier errors into one mapping, so we can then merge their diagnostics if there are a bunch + // if sourceSymbolFile != nil && targetSymbolFile != nil && c.amalgamatedDuplicates && !isEitherEnum && sourceSymbolFile != targetSymbolFile { + // var firstFile SourceFile + // if comparePaths(sourceSymbolFile.path, targetSymbolFile.path) == ComparisonLessThan { + // firstFile = sourceSymbolFile + // } else { + // firstFile = targetSymbolFile + // } + // var secondFile SourceFile + // if firstFile == sourceSymbolFile { + // secondFile = targetSymbolFile + // } else { + // secondFile = sourceSymbolFile + // } + // filesDuplicates := getOrUpdate(c.amalgamatedDuplicates, __TEMPLATE__(firstFile.path, "|", secondFile.path), func() DuplicateInfoForFiles { + // return (map[any]any{ /* TODO(TS-TO-GO): was object literal */ + // "firstFile": firstFile, + // "secondFile": secondFile, + // "conflictingSymbols": NewMap(), + // }) + // }) + // conflictingSymbolInfo := getOrUpdate(filesDuplicates.conflictingSymbols, symbolName, func() DuplicateInfoForSymbol { + // return (map[any]any{ /* TODO(TS-TO-GO): was object literal */ + // "isBlockScoped": isEitherBlockScoped, + // "firstFileLocations": []never{}, + // "secondFileLocations": []never{}, + // }) + // }) + // if !isSourcePlainJs { + // addDuplicateLocations(conflictingSymbolInfo.firstFileLocations, source) + // } + // if !isTargetPlainJs { + // addDuplicateLocations(conflictingSymbolInfo.secondFileLocations, target) + // } + // } else { + c.addDuplicateDeclarationErrorsForSymbols(source, message, symbolName, target) + c.addDuplicateDeclarationErrorsForSymbols(target, message, symbolName, source) +} + +func (c *Checker) addDuplicateDeclarationErrorsForSymbols(target *Symbol, message *diagnostics.Message, symbolName string, source *Symbol) { + for _, node := range target.declarations { + c.addDuplicateDeclarationError(node, message, symbolName, source.declarations) + } +} + +func (c *Checker) addDuplicateDeclarationError(node *Node, message *diagnostics.Message, symbolName string, relatedNodes []*Node) { + errorNode := getAdjustedNodeForError(node) + if errorNode == nil { + errorNode = node + } + err := c.lookupOrIssueError(errorNode, message, symbolName) + for _, relatedNode := range relatedNodes { + adjustedNode := getAdjustedNodeForError(relatedNode) + if adjustedNode == errorNode { + continue + } + leadingMessage := createDiagnosticForNode(adjustedNode, diagnostics.X_0_was_also_declared_here, symbolName) + followOnMessage := createDiagnosticForNode(adjustedNode, diagnostics.X_and_here) + if len(err.relatedInformation) >= 5 || some(err.relatedInformation, func(d *Diagnostic) bool { + return compareDiagnostics(d, followOnMessage) == 0 || compareDiagnostics(d, leadingMessage) == 0 + }) { + continue + } + if len(err.relatedInformation) == 0 { + addRelatedInfo(err, leadingMessage) + } else { + addRelatedInfo(err, followOnMessage) + } + } +} + +func createDiagnosticForNode(node *Node, message *diagnostics.Message, args ...any) *Diagnostic { + return NewDiagnostic(getSourceFileOfNode(node), node.loc, message, args...) +} + +func getAdjustedNodeForError(node *Node) *Node { + return getNameOfDeclaration(node) +} + +func (c *Checker) lookupOrIssueError(location *Node, message *diagnostics.Message, args ...any) *Diagnostic { + var file *SourceFile + var loc TextRange + if location != nil { + file = getSourceFileOfNode(location) + loc = location.loc + } + diagnostic := NewDiagnostic(file, loc, message, args...) + existing := c.diagnostics.lookup(diagnostic) + if existing != nil { + return existing + } + c.diagnostics.add(diagnostic) + return diagnostic +} + +func getFirstDeclaration(symbol *Symbol) *Node { + if len(symbol.declarations) > 0 { + return symbol.declarations[0] + } + return nil +} + +func getExcludedSymbolFlags(flags SymbolFlags) SymbolFlags { + var result SymbolFlags + if flags&SymbolFlagsBlockScopedVariable != 0 { + result |= SymbolFlagsBlockScopedVariableExcludes + } + if flags&SymbolFlagsFunctionScopedVariable != 0 { + result |= SymbolFlagsFunctionScopedVariableExcludes + } + if flags&SymbolFlagsProperty != 0 { + result |= SymbolFlagsPropertyExcludes + } + if flags&SymbolFlagsEnumMember != 0 { + result |= SymbolFlagsEnumMemberExcludes + } + if flags&SymbolFlagsFunction != 0 { + result |= SymbolFlagsFunctionExcludes + } + if flags&SymbolFlagsClass != 0 { + result |= SymbolFlagsClassExcludes + } + if flags&SymbolFlagsInterface != 0 { + result |= SymbolFlagsInterfaceExcludes + } + if flags&SymbolFlagsRegularEnum != 0 { + result |= SymbolFlagsRegularEnumExcludes + } + if flags&SymbolFlagsConstEnum != 0 { + result |= SymbolFlagsConstEnumExcludes + } + if flags&SymbolFlagsValueModule != 0 { + result |= SymbolFlagsValueModuleExcludes + } + if flags&SymbolFlagsMethod != 0 { + result |= SymbolFlagsMethodExcludes + } + if flags&SymbolFlagsGetAccessor != 0 { + result |= SymbolFlagsGetAccessorExcludes + } + if flags&SymbolFlagsSetAccessor != 0 { + result |= SymbolFlagsSetAccessorExcludes + } + if flags&SymbolFlagsTypeParameter != 0 { + result |= SymbolFlagsTypeParameterExcludes + } + if flags&SymbolFlagsTypeAlias != 0 { + result |= SymbolFlagsTypeAliasExcludes + } + if flags&SymbolFlagsAlias != 0 { + result |= SymbolFlagsAliasExcludes + } + return result +} + +func (c *Checker) cloneSymbol(symbol *Symbol) *Symbol { + result := c.newSymbol(symbol.flags, symbol.name) + // Force reallocation if anything is ever appended to declarations + result.declarations = symbol.declarations[0:len(symbol.declarations):len(symbol.declarations)] + result.parent = symbol.parent + result.valueDeclaration = symbol.valueDeclaration + result.constEnumOnlyModule = symbol.constEnumOnlyModule + result.members = maps.Clone(symbol.members) + result.exports = maps.Clone(symbol.exports) + c.recordMergedSymbol(result, symbol) + return result +} + +func (c *Checker) getMergedSymbol(symbol *Symbol) *Symbol { + // If a symbol was never merged it will have a zero mergeId + if symbol != nil && symbol.mergeId != 0 { + merged := c.mergedSymbols[symbol.mergeId] + if merged != nil { + return merged + } + } + return symbol +} + +func (c *Checker) getParentOfSymbol(symbol *Symbol) *Symbol { + if symbol.parent != nil { + return c.getMergedSymbol(c.getLateBoundSymbol(symbol.parent)) + } + return nil +} + +func (c *Checker) recordMergedSymbol(target *Symbol, source *Symbol) { + c.mergedSymbols[getMergeId(source)] = target +} + +func (c *Checker) getSymbolIfSameReference(s1 *Symbol, s2 *Symbol) *Symbol { + if c.getMergedSymbol(c.resolveSymbol(c.getMergedSymbol(s1))) == c.getMergedSymbol(c.resolveSymbol(c.getMergedSymbol(s2))) { + return s1 + } + return nil +} + +func (c *Checker) getExportSymbolOfValueSymbolIfExported(symbol *Symbol) *Symbol { + if symbol != nil && symbol.flags&SymbolFlagsExportValue != 0 && symbol.exportSymbol != nil { + symbol = symbol.exportSymbol + } + return c.getMergedSymbol(symbol) +} + +func (c *Checker) getSymbolOfDeclaration(node *Node) *Symbol { + symbol := node.Symbol() + if symbol != nil { + return c.getMergedSymbol(c.getLateBoundSymbol(symbol)) + } + return nil +} + +func (c *Checker) getLateBoundSymbol(symbol *Symbol) *Symbol { + return symbol // !!! +} + +func (c *Checker) resolveSymbol(symbol *Symbol) *Symbol { + return c.resolveSymbolEx(symbol, false /*dontResolveAlias*/) +} + +func (c *Checker) resolveSymbolEx(symbol *Symbol, dontResolveAlias bool) *Symbol { + if !dontResolveAlias && isNonLocalAlias(symbol, SymbolFlagsValue|SymbolFlagsType|SymbolFlagsNamespace) { + return c.resolveAlias(symbol) + } + return symbol +} + +func (c *Checker) getTargetOfImportEqualsDeclaration(node *Node, dontResolveAlias bool) *Symbol { + // Node is ImportEqualsDeclaration | VariableDeclaration + commonJSPropertyAccess := c.getCommonJSPropertyAccess(node) + if commonJSPropertyAccess != nil { + access := commonJSPropertyAccess.AsPropertyAccessExpression() + name := getLeftmostAccessExpression(access.expression).AsCallExpression().arguments[0] + if isIdentifier(access.name) { + return c.resolveSymbol(c.getPropertyOfType(c.resolveExternalModuleTypeByLiteral(name), getTextOfIdentifierOrLiteral(access.name))) + } + return nil + } + if isVariableDeclaration(node) || node.AsImportEqualsDeclaration().moduleReference.kind == SyntaxKindExternalModuleReference { + moduleReference := getExternalModuleRequireArgument(node) + if moduleReference == nil { + moduleReference = getExternalModuleImportEqualsDeclarationExpression(node) + } + immediate := c.resolveExternalModuleName(node, moduleReference, false /*ignoreErrors*/) + resolved := c.resolveExternalModuleSymbol(immediate, false /*dontResolveAlias*/) + c.markSymbolOfAliasDeclarationIfTypeOnly(node, immediate, resolved, false /*overwriteEmpty*/, nil, "") + return resolved + } + resolved := c.getSymbolOfPartOfRightHandSideOfImportEquals(node.AsImportEqualsDeclaration().moduleReference, dontResolveAlias) + c.checkAndReportErrorForResolvingImportAliasToTypeOnlySymbol(node, resolved) + return resolved +} + +func (c *Checker) getCommonJSPropertyAccess(node *Node) *Node { + if isVariableDeclaration(node) { + decl := node.AsVariableDeclaration() + if decl.initializer != nil && isPropertyAccessExpression(decl.initializer) { + return decl.initializer + } + } + return nil +} + +func (c *Checker) resolveExternalModuleTypeByLiteral(name *Node) *Type { + moduleSym := c.resolveExternalModuleName(name, name, false /*ignoreErrors*/) + if moduleSym != nil { + resolvedModuleSymbol := c.resolveExternalModuleSymbol(moduleSym, false /*dontResolveAlias*/) + if resolvedModuleSymbol != nil { + return c.getTypeOfSymbol(resolvedModuleSymbol) + } + } + return c.anyType +} + +// This function is only for imports with entity names +func (c *Checker) getSymbolOfPartOfRightHandSideOfImportEquals(entityName *Node, dontResolveAlias bool) *Symbol { + // There are three things we might try to look for. In the following examples, + // the search term is enclosed in |...|: + // + // import a = |b|; // Namespace + // import a = |b.c|; // Value, type, namespace + // import a = |b.c|.d; // Namespace + if entityName.kind == SyntaxKindIdentifier && isRightSideOfQualifiedNameOrPropertyAccess(entityName) { + entityName = entityName.parent // QualifiedName + } + // Check for case 1 and 3 in the above example + if entityName.kind == SyntaxKindIdentifier || entityName.parent.kind == SyntaxKindQualifiedName { + return c.resolveEntityName(entityName, SymbolFlagsNamespace, false /*ignoreErrors*/, dontResolveAlias, nil /*location*/) + } + // Case 2 in above example + // entityName.kind could be a QualifiedName or a Missing identifier + //Debug.assert(entityName.parent.kind == SyntaxKindImportEqualsDeclaration) + return c.resolveEntityName(entityName, SymbolFlagsValue|SymbolFlagsType|SymbolFlagsNamespace, false /*ignoreErrors*/, dontResolveAlias, nil /*location*/) +} + +func (c *Checker) checkAndReportErrorForResolvingImportAliasToTypeOnlySymbol(node *Node, resolved *Symbol) { + decl := node.AsImportEqualsDeclaration() + if c.markSymbolOfAliasDeclarationIfTypeOnly(node, nil /*immediateTarget*/, resolved, false /*overwriteEmpty*/, nil, "") && !decl.isTypeOnly { + typeOnlyDeclaration := c.getTypeOnlyAliasDeclaration(c.getSymbolOfDeclaration(node)) + isExport := nodeKindIs(typeOnlyDeclaration, SyntaxKindExportSpecifier, SyntaxKindExportDeclaration) + message := ifElse(isExport, + diagnostics.An_import_alias_cannot_reference_a_declaration_that_was_exported_using_export_type, + diagnostics.An_import_alias_cannot_reference_a_declaration_that_was_imported_using_import_type) + relatedMessage := ifElse(isExport, + diagnostics.X_0_was_exported_here, + diagnostics.X_0_was_imported_here) + // TODO: how to get name for export *? + name := "*" + if typeOnlyDeclaration.kind == SyntaxKindImportDeclaration { + name = getNameFromImportDeclaration(typeOnlyDeclaration).AsIdentifier().text + } + addRelatedInfo(c.error(decl.moduleReference, message), createDiagnosticForNode(typeOnlyDeclaration, relatedMessage, name)) + } +} + +func (c *Checker) getTargetOfImportClause(node *Node, dontResolveAlias bool) *Symbol { + moduleSymbol := c.resolveExternalModuleName(node, getModuleSpecifierFromNode(node.parent), false /*ignoreErrors*/) + if moduleSymbol != nil { + return c.getTargetOfModuleDefault(moduleSymbol, node, dontResolveAlias) + } + return nil +} + +func (c *Checker) getTargetOfModuleDefault(moduleSymbol *Symbol, node *Node, dontResolveAlias bool) *Symbol { + var exportDefaultSymbol *Symbol + if isShorthandAmbientModuleSymbol(moduleSymbol) { + exportDefaultSymbol = moduleSymbol + } else { + exportDefaultSymbol = c.resolveExportByName(moduleSymbol, InternalSymbolNameDefault, node, dontResolveAlias) + } + // !!! + // file := find(moduleSymbol.declarations, isSourceFile) + // specifier := c.getModuleSpecifierForImportOrExport(node) + // if specifier == nil { + // return exportDefaultSymbol + // } + // hasDefaultOnly := c.isOnlyImportableAsDefault(specifier, moduleSymbol) + // hasSyntheticDefault := c.canHaveSyntheticDefault(file, moduleSymbol, dontResolveAlias, specifier) + // if !exportDefaultSymbol && !hasSyntheticDefault && !hasDefaultOnly { + // if c.hasExportAssignmentSymbol(moduleSymbol) && !c.allowSyntheticDefaultImports { + // var compilerOptionName /* TODO(TS-TO-GO) inferred type "allowSyntheticDefaultImports" | "esModuleInterop" */ any + // if c.moduleKind >= ModuleKindES2015 { + // compilerOptionName = "allowSyntheticDefaultImports" + // } else { + // compilerOptionName = "esModuleInterop" + // } + // exportEqualsSymbol := moduleSymbol.exports.get(InternalSymbolNameExportEquals) + // exportAssignment := exportEqualsSymbol.valueDeclaration + // err := c.error(node.name, Diagnostics.Module_0_can_only_be_default_imported_using_the_1_flag, c.symbolToString(moduleSymbol), compilerOptionName) + + // if exportAssignment { + // addRelatedInfo(err, createDiagnosticForNode(exportAssignment, Diagnostics.This_module_is_declared_with_export_and_can_only_be_used_with_a_default_import_when_using_the_0_flag, compilerOptionName)) + // } + // } else if isImportClause(node) { + // c.reportNonDefaultExport(moduleSymbol, node) + // } else { + // c.errorNoModuleMemberSymbol(moduleSymbol, moduleSymbol, node, isImportOrExportSpecifier(node) && node.propertyName || node.name) + // } + // } else if hasSyntheticDefault || hasDefaultOnly { + // // per emit behavior, a synthetic default overrides a "real" .default member if `__esModule` is not present + // resolved := c.resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias) || c.resolveSymbol(moduleSymbol, dontResolveAlias) + // c.markSymbolOfAliasDeclarationIfTypeOnly(node, moduleSymbol, resolved /*overwriteEmpty*/, false) + // return resolved + // } + // c.markSymbolOfAliasDeclarationIfTypeOnly(node, exportDefaultSymbol /*finalTarget*/, nil /*overwriteEmpty*/, false) + return exportDefaultSymbol +} + +func (c *Checker) resolveExportByName(moduleSymbol *Symbol, name string, sourceNode *Node, dontResolveAlias bool) *Symbol { + exportValue := moduleSymbol.exports[InternalSymbolNameExportEquals] + var exportSymbol *Symbol + if exportValue != nil { + exportSymbol = c.getPropertyOfTypeEx(c.getTypeOfSymbol(exportValue), name, true /*skipObjectFunctionPropertyAugment*/, false /*includeTypeOnlyMembers*/) + } else { + exportSymbol = moduleSymbol.exports[name] + } + resolved := c.resolveSymbolEx(exportSymbol, dontResolveAlias) + c.markSymbolOfAliasDeclarationIfTypeOnly(sourceNode, exportSymbol, resolved, false /*overwriteEmpty*/, nil, "") + return resolved +} + +func (c *Checker) getTargetOfNamespaceImport(node *Node, dontResolveAlias bool) *Symbol { + moduleSpecifier := c.getModuleSpecifierForImportOrExport(node) + immediate := c.resolveExternalModuleName(node, moduleSpecifier, false /*ignoreErrors*/) + resolved := c.resolveESModuleSymbol(immediate, moduleSpecifier, dontResolveAlias /*suppressInteropError*/, false) + c.markSymbolOfAliasDeclarationIfTypeOnly(node, immediate, resolved, false /*overwriteEmpty*/, nil, "") + return resolved +} + +func (c *Checker) getTargetOfNamespaceExport(node *Node, dontResolveAlias bool) *Symbol { + moduleSpecifier := c.getModuleSpecifierForImportOrExport(node) + if moduleSpecifier != nil { + immediate := c.resolveExternalModuleName(node, moduleSpecifier, false /*ignoreErrors*/) + resolved := c.resolveESModuleSymbol(immediate, moduleSpecifier, dontResolveAlias /*suppressInteropError*/, false) + c.markSymbolOfAliasDeclarationIfTypeOnly(node, immediate, resolved, false /*overwriteEmpty*/, nil, "") + return resolved + } + return nil +} + +func (c *Checker) getTargetOfImportSpecifier(node *Node, dontResolveAlias bool) *Symbol { + name := node.AsImportSpecifier().propertyName + if name == nil { + name = node.AsImportSpecifier().name + } + if moduleExportNameIsDefault(name) { + specifier := c.getModuleSpecifierForImportOrExport(node) + if specifier != nil { + moduleSymbol := c.resolveExternalModuleName(node, specifier, false /*ignoreErrors*/) + if moduleSymbol != nil { + return c.getTargetOfModuleDefault(moduleSymbol, node, dontResolveAlias) + } + } + } + root := node.parent.parent.parent // ImportDeclaration + resolved := c.getExternalModuleMember(root, node, dontResolveAlias) + c.markSymbolOfAliasDeclarationIfTypeOnly(node, nil /*immediateTarget*/, resolved, false /*overwriteEmpty*/, nil, "") + return resolved +} + +func (c *Checker) getExternalModuleMember(node *Node, specifier *Node, dontResolveAlias bool) *Symbol { + // node is ImportDeclaration | ExportDeclaration | VariableDeclaration + // specifier is ImportSpecifier | ExportSpecifier | BindingElement | PropertyAccessExpression + moduleSpecifier := getExternalModuleRequireArgument(node) + if moduleSpecifier == nil { + moduleSpecifier = getExternalModuleName(node) + } + moduleSymbol := c.resolveExternalModuleName(node, moduleSpecifier, false /*ignoreErrors*/) + var name *Node + if !isPropertyAccessExpression(specifier) { + name = getPropertyNameFromSpecifier(specifier) + } + if name == nil { + name = getNameFromSpecifier(specifier) + } + if !isIdentifier(name) && !isStringLiteral(name) { + return nil + } + nameText := getTextOfIdentifierOrLiteral(name) + suppressInteropError := nameText == InternalSymbolNameDefault && c.allowSyntheticDefaultImports + targetSymbol := c.resolveESModuleSymbol(moduleSymbol, moduleSpecifier /*dontResolveAlias*/, false, suppressInteropError) + if targetSymbol != nil { + // Note: The empty string is a valid module export name: + // + // import { "" as foo } from "./foo"; + // export { foo as "" }; + // + if nameText != "" || name.kind == SyntaxKindStringLiteral { + if isShorthandAmbientModuleSymbol(moduleSymbol) { + return moduleSymbol + } + var symbolFromVariable *Symbol + // First check if module was specified with "export=". If so, get the member from the resolved type + if moduleSymbol != nil && moduleSymbol.exports[InternalSymbolNameExportEquals] != nil { + symbolFromVariable = c.getPropertyOfTypeEx(c.getTypeOfSymbol(targetSymbol), nameText, true /*skipObjectFunctionPropertyAugment*/, false /*includeTypeOnlyMembers*/) + } else { + symbolFromVariable = c.getPropertyOfVariable(targetSymbol, nameText) + } + // if symbolFromVariable is export - get its final target + symbolFromVariable = c.resolveSymbolEx(symbolFromVariable, dontResolveAlias) + symbolFromModule := c.getExportOfModule(targetSymbol, nameText, specifier, dontResolveAlias) + if symbolFromModule == nil && nameText == InternalSymbolNameDefault { + file := find(moduleSymbol.declarations, isSourceFile) + if c.isOnlyImportableAsDefault(moduleSpecifier, moduleSymbol) || c.canHaveSyntheticDefault(file.AsSourceFile(), moduleSymbol, dontResolveAlias, moduleSpecifier) { + symbolFromModule = c.resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias) + if symbolFromModule == nil { + symbolFromModule = c.resolveSymbolEx(moduleSymbol, dontResolveAlias) + } + } + } + symbol := symbolFromVariable + if symbolFromModule != nil { + symbol = symbolFromModule + if symbolFromVariable != nil { + symbol = c.combineValueAndTypeSymbols(symbolFromVariable, symbolFromModule) + } + } + if isImportOrExportSpecifier(specifier) && c.isOnlyImportableAsDefault(moduleSpecifier, moduleSymbol) && nameText != InternalSymbolNameDefault { + // !!! + // c.error(name, Diagnostics.Named_imports_from_a_JSON_file_into_an_ECMAScript_module_are_not_allowed_when_module_is_set_to_0, ModuleKind[c.moduleKind]) + } else if symbol == nil { + c.errorNoModuleMemberSymbol(moduleSymbol, targetSymbol, node, name) + } + return symbol + } + } + return nil +} + +func (c *Checker) getPropertyOfVariable(symbol *Symbol, name string) *Symbol { + if symbol.flags&SymbolFlagsVariable != 0 { + typeAnnotation := symbol.valueDeclaration.AsVariableDeclaration().typeNode + if typeAnnotation != nil { + return c.resolveSymbol(c.getPropertyOfType(c.getTypeFromTypeNode(typeAnnotation), name)) + } + } + return nil +} + +// This function creates a synthetic symbol that combines the value side of one symbol with the +// type/namespace side of another symbol. Consider this example: +// +// declare module graphics { +// interface Point { +// x: number; +// y: number; +// } +// } +// declare var graphics: { +// Point: new (x: number, y: number) => graphics.Point; +// } +// declare module "graphics" { +// export = graphics; +// } +// +// An 'import { Point } from "graphics"' needs to create a symbol that combines the value side 'Point' +// property with the type/namespace side interface 'Point'. +func (c *Checker) combineValueAndTypeSymbols(valueSymbol *Symbol, typeSymbol *Symbol) *Symbol { + if valueSymbol == c.unknownSymbol && typeSymbol == c.unknownSymbol { + return c.unknownSymbol + } + if valueSymbol.flags&(SymbolFlagsType|SymbolFlagsNamespace) != 0 { + return valueSymbol + } + result := c.newSymbol(valueSymbol.flags|typeSymbol.flags, valueSymbol.name) + //Debug.assert(valueSymbol.declarations || typeSymbol.declarations) + result.declarations = slices.Compact(slices.Concat(valueSymbol.declarations, typeSymbol.declarations)) + result.parent = valueSymbol.parent + if result.parent == nil { + result.parent = typeSymbol.parent + } + result.valueDeclaration = valueSymbol.valueDeclaration + result.members = maps.Clone(typeSymbol.members) + result.exports = maps.Clone(valueSymbol.exports) + return result +} + +func (c *Checker) getExportOfModule(symbol *Symbol, nameText string, specifier *Node, dontResolveAlias bool) *Symbol { + if symbol.flags&SymbolFlagsModule != 0 { + exportSymbol := c.getExportsOfSymbol(symbol)[nameText] + resolved := c.resolveSymbolEx(exportSymbol, dontResolveAlias) + exportStarDeclaration := c.moduleSymbolLinks.get(symbol).typeOnlyExportStarMap[nameText] + c.markSymbolOfAliasDeclarationIfTypeOnly(specifier, exportSymbol, resolved /*overwriteEmpty*/, false, exportStarDeclaration, nameText) + return resolved + } + return nil +} + +func (c *Checker) isOnlyImportableAsDefault(usage *Node, resolvedModule *Symbol) bool { + // In Node.js, JSON modules don't get named exports + if ModuleKindNode16 <= c.moduleKind && c.moduleKind <= ModuleKindNodeNext { + usageMode := c.getEmitSyntaxForModuleSpecifierExpression(usage) + if usageMode == ModuleKindESNext { + if resolvedModule == nil { + resolvedModule = c.resolveExternalModuleName(usage, usage, true /*ignoreErrors*/) + } + var targetFile *SourceFile + if resolvedModule != nil { + targetFile = getSourceFileOfModule(resolvedModule) + } + return targetFile != nil && (isJsonSourceFile(targetFile) || getDeclarationFileExtension(targetFile.fileName) == ".d.json.ts") + } + } + return false +} + +func (c *Checker) canHaveSyntheticDefault(file *SourceFile, moduleSymbol *Symbol, dontResolveAlias bool, usage *Node) bool { + // !!! + // var usageMode ResolutionMode + // if file != nil { + // usageMode = c.getEmitSyntaxForModuleSpecifierExpression(usage) + // } + // if file != nil && usageMode != ModuleKindNone { + // targetMode := host.getImpliedNodeFormatForEmit(file) + // if usageMode == ModuleKindESNext && targetMode == ModuleKindCommonJS && ModuleKindNode16 <= c.moduleKind && c.moduleKind <= ModuleKindNodeNext { + // // In Node.js, CommonJS modules always have a synthetic default when imported into ESM + // return true + // } + // if usageMode == ModuleKindESNext && targetMode == ModuleKindESNext { + // // No matter what the `module` setting is, if we're confident that both files + // // are ESM, there cannot be a synthetic default. + // return false + // } + // } + if !c.allowSyntheticDefaultImports { + return false + } + // Declaration files (and ambient modules) + if file == nil || file.isDeclarationFile { + // Definitely cannot have a synthetic default if they have a syntactic default member specified + defaultExportSymbol := c.resolveExportByName(moduleSymbol, InternalSymbolNameDefault /*sourceNode*/, nil /*dontResolveAlias*/, true) + // Dont resolve alias because we want the immediately exported symbol's declaration + if defaultExportSymbol != nil && some(defaultExportSymbol.declarations, isSyntacticDefault) { + return false + } + // It _might_ still be incorrect to assume there is no __esModule marker on the import at runtime, even if there is no `default` member + // So we check a bit more, + if c.resolveExportByName(moduleSymbol, "__esModule", nil /*sourceNode*/, dontResolveAlias) != nil { + // If there is an `__esModule` specified in the declaration (meaning someone explicitly added it or wrote it in their code), + // it definitely is a module and does not have a synthetic default + return false + } + // There are _many_ declaration files not written with esmodules in mind that still get compiled into a format with __esModule set + // Meaning there may be no default at runtime - however to be on the permissive side, we allow access to a synthetic default member + // as there is no marker to indicate if the accompanying JS has `__esModule` or not, or is even native esm + return true + } + // TypeScript files never have a synthetic default (as they are always emitted with an __esModule marker) _unless_ they contain an export= statement + return hasExportAssignmentSymbol(moduleSymbol) +} + +func (c *Checker) getEmitSyntaxForModuleSpecifierExpression(usage *Node) ResolutionMode { + // !!! + // if isStringLiteralLike(usage) { + // return host.getEmitSyntaxForUsageLocation(getSourceFileOfNode(usage), usage) + // } + return ModuleKindNone +} + +func (c *Checker) errorNoModuleMemberSymbol(moduleSymbol *Symbol, targetSymbol *Symbol, node *Node, name *Node) { + moduleName := c.getFullyQualifiedName(moduleSymbol, node) + declarationName := declarationNameToString(name) + var suggestion *Symbol + if isIdentifier(name) { + suggestion = c.getSuggestedSymbolForNonexistentModule(name, targetSymbol) + } + if suggestion != nil { + suggestionName := c.symbolToString(suggestion) + diagnostic := c.error(name, diagnostics.X_0_has_no_exported_member_named_1_Did_you_mean_2, moduleName, declarationName, suggestionName) + if suggestion.valueDeclaration != nil { + addRelatedInfo(diagnostic, createDiagnosticForNode(suggestion.valueDeclaration, diagnostics.X_0_is_declared_here, suggestionName)) + } + } else { + if moduleSymbol.exports[InternalSymbolNameDefault] != nil { + c.error(name, diagnostics.Module_0_has_no_exported_member_1_Did_you_mean_to_use_import_1_from_0_instead, moduleName, declarationName) + + } else { + c.reportNonExportedMember(node, name, declarationName, moduleSymbol, moduleName) + } + } +} + +func (c *Checker) reportNonExportedMember(node *Node, name *Node, declarationName string, moduleSymbol *Symbol, moduleName string) { + var localSymbol *Symbol + if locals := getLocalsOfNode(moduleSymbol.valueDeclaration); locals != nil { + localSymbol = locals[getTextOfIdentifierOrLiteral(name)] + } + exports := moduleSymbol.exports + if localSymbol != nil { + if exportedEqualsSymbol := exports[InternalSymbolNameExportEquals]; exportedEqualsSymbol != nil { + if c.getSymbolIfSameReference(exportedEqualsSymbol, localSymbol) != nil { + c.reportInvalidImportEqualsExportMember(node, name, declarationName, moduleName) + } else { + c.error(name, diagnostics.Module_0_has_no_exported_member_1, moduleName, declarationName) + } + } else { + exportedSymbol := findInMap(exports, func(symbol *Symbol) bool { + return c.getSymbolIfSameReference(symbol, localSymbol) != nil + }) + var diagnostic *Diagnostic + if exportedSymbol != nil { + diagnostic = c.error(name, diagnostics.Module_0_declares_1_locally_but_it_is_exported_as_2, moduleName, declarationName, c.symbolToString(exportedSymbol)) + } else { + diagnostic = c.error(name, diagnostics.Module_0_declares_1_locally_but_it_is_not_exported, moduleName, declarationName) + } + for i, decl := range localSymbol.declarations { + addRelatedInfo(diagnostic, createDiagnosticForNode(decl, ifElse(i == 0, diagnostics.X_0_is_declared_here, diagnostics.X_and_here), declarationName)) + } + } + } else { + c.error(name, diagnostics.Module_0_has_no_exported_member_1, moduleName, declarationName) + } +} + +func (c *Checker) reportInvalidImportEqualsExportMember(node *Node, name *Node, declarationName string, moduleName string) { + if c.moduleKind >= ModuleKindES2015 { + message := ifElse(getESModuleInterop(c.compilerOptions), + diagnostics.X_0_can_only_be_imported_by_using_a_default_import, + diagnostics.X_0_can_only_be_imported_by_turning_on_the_esModuleInterop_flag_and_using_a_default_import) + c.error(name, message, declarationName) + } else { + message := ifElse(getESModuleInterop(c.compilerOptions), + diagnostics.X_0_can_only_be_imported_by_using_import_1_require_2_or_a_default_import, + diagnostics.X_0_can_only_be_imported_by_using_import_1_require_2_or_by_turning_on_the_esModuleInterop_flag_and_using_a_default_import) + c.error(name, message, declarationName, declarationName, moduleName) + } +} + +func getPropertyNameFromSpecifier(node *Node) *Node { + switch node.kind { + case SyntaxKindImportSpecifier: + return node.AsImportSpecifier().propertyName + case SyntaxKindExportSpecifier: + return node.AsExportSpecifier().propertyName + case SyntaxKindBindingElement: + return node.AsBindingElement().propertyName + } + panic("Unhandled case in getSpecifierPropertyName") +} + +func getNameFromSpecifier(node *Node) *Node { + switch node.kind { + case SyntaxKindImportSpecifier: + return node.AsImportSpecifier().name + case SyntaxKindExportSpecifier: + return node.AsExportSpecifier().name + case SyntaxKindBindingElement: + return node.AsBindingElement().name + case SyntaxKindPropertyAccessExpression: + return node.AsPropertyAccessExpression().name + } + panic("Unhandled case in getSpecifierPropertyName") +} + +func (c *Checker) getTargetOfBindingElement(node *Node, dontResolveAlias bool) *Symbol { + panic("getTargetOfBindingElement") // !!! +} + +func (c *Checker) getTargetOfExportSpecifier(node *Node, meaning SymbolFlags, dontResolveAlias bool) *Symbol { + name := node.AsExportSpecifier().propertyName + if name == nil { + name = node.AsExportSpecifier().name + } + if moduleExportNameIsDefault(name) { + specifier := c.getModuleSpecifierForImportOrExport(node) + if specifier != nil { + moduleSymbol := c.resolveExternalModuleName(node, specifier, false /*ignoreErrors*/) + if moduleSymbol != nil { + return c.getTargetOfModuleDefault(moduleSymbol, node, dontResolveAlias) + } + } + } + exportDeclaration := node.parent.parent + var resolved *Symbol + switch { + case exportDeclaration.AsExportDeclaration().moduleSpecifier != nil: + resolved = c.getExternalModuleMember(exportDeclaration, node, dontResolveAlias) + case isStringLiteral(name): + resolved = nil + default: + resolved = c.resolveEntityName(name, meaning, false /*ignoreErrors*/, dontResolveAlias, nil /*location*/) + } + c.markSymbolOfAliasDeclarationIfTypeOnly(node, nil /*immediateTarget*/, resolved, false /*overwriteEmpty*/, nil, "") + return resolved +} + +func (c *Checker) getTargetOfExportAssignment(node *Node, dontResolveAlias bool) *Symbol { + resolved := c.getTargetOfAliasLikeExpression(node.AsExportAssignment().expression, dontResolveAlias) + c.markSymbolOfAliasDeclarationIfTypeOnly(node, nil /*immediateTarget*/, resolved, false /*overwriteEmpty*/, nil, "") + return resolved +} + +func (c *Checker) getTargetOfBinaryExpression(node *Node, dontResolveAlias bool) *Symbol { + resolved := c.getTargetOfAliasLikeExpression(node.AsBinaryExpression().right, dontResolveAlias) + c.markSymbolOfAliasDeclarationIfTypeOnly(node, nil /*immediateTarget*/, resolved, false /*overwriteEmpty*/, nil, "") + return resolved +} + +func (c *Checker) getTargetOfAliasLikeExpression(expression *Node, dontResolveAlias bool) *Symbol { + if isClassExpression(expression) { + return c.unknownSymbol + // !!! return c.checkExpressionCached(expression).symbol + } + if !isEntityName(expression) && !isEntityNameExpression(expression) { + return nil + } + aliasLike := c.resolveEntityName(expression, SymbolFlagsValue|SymbolFlagsType|SymbolFlagsNamespace, true /*ignoreErrors*/, dontResolveAlias, nil /*location*/) + if aliasLike != nil { + return aliasLike + } + return c.unknownSymbol + // !!! c.checkExpressionCached(expression) + // return c.getNodeLinks(expression).resolvedSymbol +} + +func (c *Checker) getTargetOfNamespaceExportDeclaration(node *Node, dontResolveAlias bool) *Symbol { + if canHaveSymbol(node.parent) { + resolved := c.resolveExternalModuleSymbol(node.parent.Symbol(), dontResolveAlias) + c.markSymbolOfAliasDeclarationIfTypeOnly(node, nil /*immediateTarget*/, resolved, false /*overwriteEmpty*/, nil, "") + return resolved + } + return nil +} + +func (c *Checker) getTargetOfAccessExpression(node *Node, dontRecursivelyResolve bool) *Symbol { + if isBinaryExpression(node.parent) { + expr := node.parent.AsBinaryExpression() + if expr.left == node && expr.operatorToken.kind == SyntaxKindEqualsToken { + return c.getTargetOfAliasLikeExpression(expr.right, dontRecursivelyResolve) + } + } + return nil +} + +func (c *Checker) getModuleSpecifierForImportOrExport(node *Node) *Node { + switch node.kind { + case SyntaxKindImportClause: + return getModuleSpecifierFromNode(node.parent) + case SyntaxKindImportEqualsDeclaration: + if isExternalModuleReference(node.AsImportEqualsDeclaration().moduleReference) { + return node.AsImportEqualsDeclaration().moduleReference.AsExternalModuleReference().expression + } else { + return nil + } + case SyntaxKindNamespaceImport: + return getModuleSpecifierFromNode(node.parent.parent) + case SyntaxKindImportSpecifier: + return getModuleSpecifierFromNode(node.parent.parent.parent) + case SyntaxKindNamespaceExport: + return getModuleSpecifierFromNode(node.parent) + case SyntaxKindExportSpecifier: + return getModuleSpecifierFromNode(node.parent.parent) + } + panic("Unhandled case in getModuleSpecifierForImportOrExport") +} + +func getModuleSpecifierFromNode(node *Node) *Node { + switch node.kind { + case SyntaxKindImportDeclaration: + return node.AsImportDeclaration().moduleSpecifier + case SyntaxKindExportDeclaration: + return node.AsExportDeclaration().moduleSpecifier + } + panic("Unhandled case in getModuleSpecifierFromNode") +} + +/** + * Marks a symbol as type-only if its declaration is syntactically type-only. + * If it is not itself marked type-only, but resolves to a type-only alias + * somewhere in its resolution chain, save a reference to the type-only alias declaration + * so the alias _not_ marked type-only can be identified as _transitively_ type-only. + * + * This function is called on each alias declaration that could be type-only or resolve to + * another type-only alias during `resolveAlias`, so that later, when an alias is used in a + * JS-emitting expression, we can quickly determine if that symbol is effectively type-only + * and issue an error if so. + * + * @param aliasDeclaration The alias declaration not marked as type-only + * @param immediateTarget The symbol to which the alias declaration immediately resolves + * @param finalTarget The symbol to which the alias declaration ultimately resolves + * @param overwriteEmpty Checks `resolvesToSymbol` for type-only declarations even if `aliasDeclaration` + * has already been marked as not resolving to a type-only alias. Used when recursively resolving qualified + * names of import aliases, e.g. `import C = a.b.C`. If namespace `a` is not found to be type-only, the + * import declaration will initially be marked as not resolving to a type-only symbol. But, namespace `b` + * must still be checked for a type-only marker, overwriting the previous negative result if found. + */ + +func (c *Checker) markSymbolOfAliasDeclarationIfTypeOnly(aliasDeclaration *Node, immediateTarget *Symbol, finalTarget *Symbol, overwriteEmpty bool, exportStarDeclaration *Node, exportStarName string) bool { + if aliasDeclaration == nil || isPropertyAccessExpression(aliasDeclaration) { + return false + } + // If the declaration itself is type-only, mark it and return. No need to check what it resolves to. + sourceSymbol := c.getSymbolOfDeclaration(aliasDeclaration) + if isTypeOnlyImportOrExportDeclaration(aliasDeclaration) { + links := c.aliasSymbolLinks.get(sourceSymbol) + links.typeOnlyDeclaration = aliasDeclaration + return true + } + if exportStarDeclaration != nil { + links := c.aliasSymbolLinks.get(sourceSymbol) + links.typeOnlyDeclaration = exportStarDeclaration + if sourceSymbol.name != exportStarName { + links.typeOnlyExportStarName = exportStarName + } + return true + } + links := c.aliasSymbolLinks.get(sourceSymbol) + return c.markSymbolOfAliasDeclarationIfTypeOnlyWorker(links, immediateTarget, overwriteEmpty) || c.markSymbolOfAliasDeclarationIfTypeOnlyWorker(links, finalTarget, overwriteEmpty) +} + +func (c *Checker) markSymbolOfAliasDeclarationIfTypeOnlyWorker(aliasDeclarationLinks *AliasSymbolLinks, target *Symbol, overwriteEmpty bool) bool { + if target != nil && (aliasDeclarationLinks.typeOnlyDeclaration == nil || overwriteEmpty && aliasDeclarationLinks.typeOnlyDeclarationResolved && aliasDeclarationLinks.typeOnlyDeclaration == nil) { + exportSymbol := target.exports[InternalSymbolNameExportEquals] + if exportSymbol == nil { + exportSymbol = target + } + typeOnly := some(exportSymbol.declarations, isTypeOnlyImportOrExportDeclaration) + aliasDeclarationLinks.typeOnlyDeclarationResolved = true + aliasDeclarationLinks.typeOnlyDeclaration = nil + if typeOnly { + aliasDeclarationLinks.typeOnlyDeclaration = c.aliasSymbolLinks.get(exportSymbol).typeOnlyDeclaration + } + } + return aliasDeclarationLinks.typeOnlyDeclaration != nil +} + +func (c *Checker) resolveExternalModuleName(location *Node, moduleReferenceExpression *Node, ignoreErrors bool) *Symbol { + isClassic := getEmitModuleResolutionKind(c.compilerOptions) == ModuleResolutionKindClassic + errorMessage := ifElse(isClassic, + diagnostics.Cannot_find_module_0_Did_you_mean_to_set_the_moduleResolution_option_to_nodenext_or_to_add_aliases_to_the_paths_option, + diagnostics.Cannot_find_module_0_or_its_corresponding_type_declarations) + return c.resolveExternalModuleNameWorker(location, moduleReferenceExpression, ifElse(ignoreErrors, nil, errorMessage), ignoreErrors, false /*isForAugmentation*/) +} + +func (c *Checker) resolveExternalModuleNameWorker(location *Node, moduleReferenceExpression *Node, moduleNotFoundError *diagnostics.Message, ignoreErrors bool, isForAugmentation bool) *Symbol { + if isStringLiteralLike(moduleReferenceExpression) { + return c.resolveExternalModule(location, getTextOfIdentifierOrLiteral(moduleReferenceExpression), moduleNotFoundError, ifElse(!ignoreErrors, moduleReferenceExpression, nil), isForAugmentation) + } + return nil +} + +func (c *Checker) resolveExternalModule(location *Node, moduleReference string, moduleNotFoundError *diagnostics.Message, errorNode *Node, isForAugmentation bool) *Symbol { + if errorNode != nil && strings.HasPrefix(moduleReference, "@types/") { + withoutAtTypePrefix := moduleReference[len("@types/"):] + c.error(errorNode, diagnostics.Cannot_import_type_declaration_files_Consider_importing_0_instead_of_1, withoutAtTypePrefix, moduleReference) + } + ambientModule := c.tryFindAmbientModule(moduleReference, true /*withAugmentations*/) + if ambientModule != nil { + return ambientModule + } + // !!! The following only implements simple module resoltion + sourceFile := c.program.getResolvedModule(getSourceFileOfNode(location), moduleReference) + if sourceFile != nil { + if sourceFile.symbol != nil { + return c.getMergedSymbol(sourceFile.symbol) + } + if errorNode != nil && moduleNotFoundError != nil && !isSideEffectImport(errorNode) { + c.error(errorNode, diagnostics.File_0_is_not_a_module, sourceFile.fileName) + } + return nil + } + if errorNode != nil && moduleNotFoundError != nil { + c.error(errorNode, moduleNotFoundError, moduleReference) + } + return nil +} + +func (c *Checker) tryFindAmbientModule(moduleName string, withAugmentations bool) *Symbol { + if isExternalModuleNameRelative(moduleName) { + return nil + } + symbol := c.getSymbol(c.globals, "\""+moduleName+"\"", SymbolFlagsValueModule) + // merged symbol is module declaration symbol combined with all augmentations + if withAugmentations { + return c.getMergedSymbol(symbol) + } + return symbol +} + +func (c *Checker) resolveExternalModuleSymbol(moduleSymbol *Symbol, dontResolveAlias bool) *Symbol { + if moduleSymbol != nil { + exportEquals := c.resolveSymbolEx(moduleSymbol.exports[InternalSymbolNameExportEquals], dontResolveAlias) + exported := c.getMergedSymbol(c.getCommonJsExportEquals(c.getMergedSymbol(exportEquals), c.getMergedSymbol(moduleSymbol))) + if exported != nil { + return exported + } + } + return moduleSymbol +} + +func (c *Checker) getCommonJsExportEquals(exported *Symbol, moduleSymbol *Symbol) *Symbol { + if exported == nil || exported == c.unknownSymbol || exported == moduleSymbol || len(moduleSymbol.exports) == 1 || exported.flags&SymbolFlagsAlias != 0 { + return exported + } + links := c.moduleSymbolLinks.get(exported) + if links.cjsExportMerged != nil { + return links.cjsExportMerged + } + var merged *Symbol + if exported.flags&SymbolFlagsTransient != 0 { + merged = exported + } else { + merged = c.cloneSymbol(exported) + } + merged.flags |= SymbolFlagsValueModule + mergedExports := getExports(merged) + for name, s := range moduleSymbol.exports { + if name != InternalSymbolNameExportEquals { + if existing, ok := mergedExports[name]; ok { + s = c.mergeSymbol(existing, s /*unidirectional*/, false) + } + mergedExports[name] = s + } + } + if merged == exported { + // We just mutated a symbol, reset any cached links we may have already set + // (Notably required to make late bound members appear) + c.moduleSymbolLinks.get(merged).resolvedExports = nil + // !!! c.moduleSymbolLinks.get(merged).resolvedMembers = nil + } + c.moduleSymbolLinks.get(merged).cjsExportMerged = merged + links.cjsExportMerged = merged + return links.cjsExportMerged +} + +// An external module with an 'export =' declaration may be referenced as an ES6 module provided the 'export =' +// references a symbol that is at least declared as a module or a variable. The target of the 'export =' may +// combine other declarations with the module or variable (e.g. a class/module, function/module, interface/variable). +func (c *Checker) resolveESModuleSymbol(moduleSymbol *Symbol, referencingLocation *Node, dontResolveAlias bool, suppressInteropError bool) *Symbol { + symbol := c.resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias) + if !dontResolveAlias && symbol != nil { + if !suppressInteropError && symbol.flags&(SymbolFlagsModule|SymbolFlagsVariable) == 0 && getDeclarationOfKind(symbol, SyntaxKindSourceFile) == nil { + compilerOptionName := ifElse(c.moduleKind >= ModuleKindES2015, "allowSyntheticDefaultImports", "esModuleInterop") + c.error(referencingLocation, diagnostics.This_module_can_only_be_referenced_with_ECMAScript_imports_Slashexports_by_turning_on_the_0_flag_and_referencing_its_default_export, compilerOptionName) + return symbol + } + referenceParent := referencingLocation.parent + if isImportDeclaration(referenceParent) && getNamespaceDeclarationNode(referenceParent) != nil || isImportCall(referenceParent) { + var reference *Node + if isImportCall(referenceParent) { + reference = referenceParent.AsCallExpression().arguments[0] + } else { + reference = referenceParent.AsImportDeclaration().moduleSpecifier + } + typ := c.getTypeOfSymbol(symbol) + defaultOnlyType := c.getTypeWithSyntheticDefaultOnly(typ, symbol, moduleSymbol, reference) + if defaultOnlyType != nil { + return c.cloneTypeAsModuleType(symbol, defaultOnlyType, referenceParent) + } + // !!! + // targetFile := moduleSymbol. /* ? */ declarations. /* ? */ find(isSourceFile) + // isEsmCjsRef := targetFile && c.isESMFormatImportImportingCommonjsFormatFile(c.getEmitSyntaxForModuleSpecifierExpression(reference), host.getImpliedNodeFormatForEmit(targetFile)) + // if getESModuleInterop(c.compilerOptions) || isEsmCjsRef { + // sigs := c.getSignaturesOfStructuredType(type_, SignatureKindCall) + // if !sigs || !sigs.length { + // sigs = c.getSignaturesOfStructuredType(type_, SignatureKindConstruct) + // } + // if (sigs && sigs.length) || c.getPropertyOfType(type_, InternalSymbolNameDefault /*skipObjectFunctionPropertyAugment*/, true) || isEsmCjsRef { + // var moduleType *Type + // if type_.flags & TypeFlagsStructuredType { + // moduleType = c.getTypeWithSyntheticDefaultImportType(type_, symbol, moduleSymbol, reference) + // } else { + // moduleType = c.createDefaultPropertyWrapperForModule(symbol, symbol.parent) + // } + // return c.cloneTypeAsModuleType(symbol, moduleType, referenceParent) + // } + // } + } + } + return symbol +} + +func (c *Checker) getTypeWithSyntheticDefaultOnly(typ *Type, symbol *Symbol, originalSymbol *Symbol, moduleSpecifier *Node) *Type { + return nil // !!! +} + +func (c *Checker) cloneTypeAsModuleType(symbol *Symbol, moduleType *Type, referenceParent *Node) *Symbol { + result := c.newSymbol(symbol.flags, symbol.name) + result.constEnumOnlyModule = symbol.constEnumOnlyModule + result.declarations = slices.Clone(symbol.declarations) + result.valueDeclaration = symbol.valueDeclaration + result.members = maps.Clone(symbol.members) + result.exports = maps.Clone(symbol.exports) + result.parent = symbol.parent + links := c.exportTypeLinks.get(result) + links.target = symbol + links.originatingImport = referenceParent + resolvedModuleType := c.resolveStructuredTypeMembers(moduleType) + c.valueSymbolLinks.get(result).resolvedType = c.newAnonymousType(result, resolvedModuleType.members, nil, nil, resolvedModuleType.indexInfos) + return result +} + +func (c *Checker) getTargetOfAliasDeclaration(node *Node, dontRecursivelyResolve bool /* = false */) *Symbol { + switch node.kind { + case SyntaxKindImportEqualsDeclaration, SyntaxKindVariableDeclaration: + return c.getTargetOfImportEqualsDeclaration(node, dontRecursivelyResolve) + case SyntaxKindImportClause: + return c.getTargetOfImportClause(node, dontRecursivelyResolve) + case SyntaxKindNamespaceImport: + return c.getTargetOfNamespaceImport(node, dontRecursivelyResolve) + case SyntaxKindNamespaceExport: + return c.getTargetOfNamespaceExport(node, dontRecursivelyResolve) + case SyntaxKindImportSpecifier: + return c.getTargetOfImportSpecifier(node, dontRecursivelyResolve) + case SyntaxKindBindingElement: + return c.getTargetOfBindingElement(node, dontRecursivelyResolve) + case SyntaxKindExportSpecifier: + return c.getTargetOfExportSpecifier(node, SymbolFlagsValue|SymbolFlagsType|SymbolFlagsNamespace, dontRecursivelyResolve) + case SyntaxKindExportAssignment: + return c.getTargetOfExportAssignment(node, dontRecursivelyResolve) + case SyntaxKindBinaryExpression: + return c.getTargetOfBinaryExpression(node, dontRecursivelyResolve) + case SyntaxKindNamespaceExportDeclaration: + return c.getTargetOfNamespaceExportDeclaration(node, dontRecursivelyResolve) + case SyntaxKindShorthandPropertyAssignment: + return c.resolveEntityName(node.AsShorthandPropertyAssignment().name, SymbolFlagsValue|SymbolFlagsType|SymbolFlagsNamespace, true /*ignoreErrors*/, dontRecursivelyResolve, nil /*location*/) + case SyntaxKindPropertyAssignment: + return c.getTargetOfAliasLikeExpression(node.AsPropertyAssignment().initializer, dontRecursivelyResolve) + case SyntaxKindElementAccessExpression, SyntaxKindPropertyAccessExpression: + return c.getTargetOfAccessExpression(node, dontRecursivelyResolve) + } + panic("Unhandled case in getTargetOfAliasDeclaration") +} + +/** + * Resolves a qualified name and any involved aliases. + */ +func (c *Checker) resolveEntityName(name *Node, meaning SymbolFlags, ignoreErrors bool, dontResolveAlias bool, location *Node) *Symbol { + if nodeIsMissing(name) { + return nil + } + var symbol *Symbol + switch name.kind { + case SyntaxKindIdentifier: + var message *diagnostics.Message + if !ignoreErrors { + if meaning == SymbolFlagsNamespace || nodeIsSynthesized(name) { + message = diagnostics.Cannot_find_namespace_0 + } else { + message = c.getCannotFindNameDiagnosticForName(getFirstIdentifier(name)) + } + } + resolveLocation := location + if resolveLocation == nil { + resolveLocation = name + } + symbol = c.getMergedSymbol(c.resolveName(resolveLocation, name.AsIdentifier().text, meaning, message, true /*isUse*/, false /*excludeGlobals*/)) + case SyntaxKindQualifiedName: + qualified := name.AsQualifiedName() + symbol = c.resolveQualifiedName(name, qualified.left, qualified.right, meaning, ignoreErrors, dontResolveAlias, location) + case SyntaxKindPropertyAccessExpression: + access := name.AsPropertyAccessExpression() + symbol = c.resolveQualifiedName(name, access.expression, access.name, meaning, ignoreErrors, dontResolveAlias, location) + default: + panic("Unknown entity name kind") + } + if symbol != nil { + if !nodeIsSynthesized(name) && isEntityName(name) && (symbol.flags&SymbolFlagsAlias != 0 || name.parent.kind == SyntaxKindExportAssignment) { + c.markSymbolOfAliasDeclarationIfTypeOnly(getAliasDeclarationFromName(name), symbol, nil /*finalTarget*/, true /*overwriteEmpty*/, nil, "") + } + if symbol.flags&meaning == 0 && !dontResolveAlias { + return c.resolveAlias(symbol) + } + } + return symbol +} + +func (c *Checker) resolveQualifiedName(name *Node, left *Node, right *Node, meaning SymbolFlags, ignoreErrors bool, dontResolveAlias bool, location *Node) *Symbol { + namespace := c.resolveEntityName(left, SymbolFlagsNamespace, ignoreErrors /*dontResolveAlias*/, false, location) + if namespace == nil || nodeIsMissing(right) { + return nil + } + if namespace == c.unknownSymbol { + return namespace + } + text := right.AsIdentifier().text + symbol := c.getMergedSymbol(c.getSymbol(c.getExportsOfSymbol(namespace), text, meaning)) + if symbol != nil && namespace.flags&SymbolFlagsAlias != 0 { + // `namespace` can be resolved further if there was a symbol merge with a re-export + symbol = c.getMergedSymbol(c.getSymbol(c.getExportsOfSymbol(c.resolveAlias(namespace)), text, meaning)) + } + if symbol == nil { + if !ignoreErrors { + namespaceName := c.getFullyQualifiedName(namespace, nil /*containingLocation*/) + declarationName := declarationNameToString(right) + suggestionForNonexistentModule := c.getSuggestedSymbolForNonexistentModule(right, namespace) + if suggestionForNonexistentModule != nil { + c.error(right, diagnostics.X_0_has_no_exported_member_named_1_Did_you_mean_2, namespaceName, declarationName, c.symbolToString(suggestionForNonexistentModule)) + return nil + } + var containingQualifiedName *Node + if isQualifiedName(name) { + containingQualifiedName = getContainingQualifiedNameNode(name) + } + canSuggestTypeof := c.globalObjectType != nil && meaning&SymbolFlagsType != 0 && containingQualifiedName != nil && !isTypeOfExpression(containingQualifiedName.parent) && c.tryGetQualifiedNameAsValue(containingQualifiedName) != nil + if canSuggestTypeof { + c.error(containingQualifiedName, diagnostics.X_0_refers_to_a_value_but_is_being_used_as_a_type_here_Did_you_mean_typeof_0, entityNameToString(containingQualifiedName)) + return nil + } + if meaning&SymbolFlagsNamespace != 0 { + if isQualifiedName(name.parent) { + exportedTypeSymbol := c.getMergedSymbol(c.getSymbol(c.getExportsOfSymbol(namespace), text, SymbolFlagsType)) + if exportedTypeSymbol != nil { + qualified := name.parent.AsQualifiedName() + c.error(qualified.right, diagnostics.Cannot_access_0_1_because_0_is_a_type_but_not_a_namespace_Did_you_mean_to_retrieve_the_type_of_the_property_1_in_0_with_0_1, c.symbolToString(exportedTypeSymbol), qualified.right.AsIdentifier().text) + return nil + } + } + } + c.error(right, diagnostics.Namespace_0_has_no_exported_member_1, namespaceName, declarationName) + } + } + return symbol +} + +func (c *Checker) tryGetQualifiedNameAsValue(node *Node) *Symbol { + id := getFirstIdentifier(node) + symbol := c.resolveName(id, id.AsIdentifier().text, SymbolFlagsValue, nil /*nameNotFoundMessage*/, true /*isUse*/, false /*excludeGlobals*/) + if symbol == nil { + return nil + } + n := id + for isQualifiedName(n.parent) { + t := c.getTypeOfSymbol(symbol) + symbol = c.getPropertyOfType(t, n.parent.AsQualifiedName().right.AsIdentifier().text) + if symbol == nil { + return nil + } + n = n.parent + } + return symbol +} + +func (c *Checker) getSuggestedSymbolForNonexistentModule(name *Node, targetModule *Symbol) *Symbol { + return nil // !!! +} + +func (c *Checker) getFullyQualifiedName(symbol *Symbol, containingLocation *Node) string { + if symbol.parent != nil { + return c.getFullyQualifiedName(symbol.parent, containingLocation) + "." + c.symbolToString(symbol) + } + return c.symbolToString(symbol) // !!! +} + +func (c *Checker) getExportsOfSymbol(symbol *Symbol) SymbolTable { + if symbol.flags&SymbolFlagsLateBindingContainer != 0 { + return c.getResolvedMembersOrExportsOfSymbol(symbol, MembersOrExportsResolutionKindResolvedExports) + } + if symbol.flags&SymbolFlagsModule != 0 { + return c.getExportsOfModule(symbol) + } + return symbol.exports +} + +func (c *Checker) getResolvedMembersOrExportsOfSymbol(symbol *Symbol, resolutionKind MembersOrExportsResolutionKind) SymbolTable { + links := c.membersAndExportsLinks.get(symbol) + if links[resolutionKind] == nil { + isStatic := resolutionKind == MembersOrExportsResolutionKindResolvedExports + earlySymbols := symbol.exports + switch { + case !isStatic: + earlySymbols = symbol.members + case symbol.flags&SymbolFlagsModule != 0: + earlySymbols, _ = c.getExportsOfModuleWorker(symbol) + } + links[resolutionKind] = earlySymbols + // !!! Resolve late-bound members + } + return links[resolutionKind] +} + +/** + * Gets a SymbolTable containing both the early- and late-bound members of a symbol. + * + * For a description of late-binding, see `lateBindMember`. + */ +func (c *Checker) getMembersOfSymbol(symbol *Symbol) SymbolTable { + if symbol.flags&SymbolFlagsLateBindingContainer != 0 { + return c.getResolvedMembersOrExportsOfSymbol(symbol, MembersOrExportsResolutionKindresolvedMembers) + } + return symbol.members +} + +func (c *Checker) getExportsOfModule(moduleSymbol *Symbol) SymbolTable { + links := c.moduleSymbolLinks.get(moduleSymbol) + if links.resolvedExports == nil { + exports, typeOnlyExportStarMap := c.getExportsOfModuleWorker(moduleSymbol) + links.resolvedExports = exports + links.typeOnlyExportStarMap = typeOnlyExportStarMap + } + return links.resolvedExports +} + +type ExportCollision struct { + specifierText string + exportsWithDuplicate []*Node +} + +type ExportCollisionTable = map[string]*ExportCollision + +func (c *Checker) getExportsOfModuleWorker(moduleSymbol *Symbol) (exports SymbolTable, typeOnlyExportStarMap map[string]*Node) { + var visitedSymbols []*Symbol + nonTypeOnlyNames := make(map[string]bool) + // The ES6 spec permits export * declarations in a module to circularly reference the module itself. For example, + // module 'a' can 'export * from "b"' and 'b' can 'export * from "a"' without error. + var visit func(*Symbol, *Node, bool) SymbolTable + visit = func(symbol *Symbol, exportStar *Node, isTypeOnly bool) SymbolTable { + if !isTypeOnly && symbol != nil { + // Add non-type-only names before checking if we've visited this module, + // because we might have visited it via an 'export type *', and visiting + // again with 'export *' will override the type-onlyness of its exports. + for name := range symbol.exports { + nonTypeOnlyNames[name] = true + } + } + if symbol == nil || symbol.exports == nil || slices.Contains(visitedSymbols, symbol) { + return nil + } + symbols := maps.Clone(symbol.exports) + // All export * declarations are collected in an __export symbol by the binder + exportStars := symbol.exports[InternalSymbolNameExportStar] + if exportStars != nil { + nestedSymbols := make(SymbolTable) + lookupTable := make(ExportCollisionTable) + for _, node := range exportStars.declarations { + resolvedModule := c.resolveExternalModuleName(node, node.AsExportDeclaration().moduleSpecifier, false /*ignoreErrors*/) + exportedSymbols := visit(resolvedModule, node, isTypeOnly || node.AsExportDeclaration().isTypeOnly) + c.extendExportSymbols(nestedSymbols, exportedSymbols, lookupTable, node) + } + for id, s := range lookupTable { + // It's not an error if the file with multiple `export *`s with duplicate names exports a member with that name itself + if id == "export=" || len(s.exportsWithDuplicate) == 0 || symbols[id] != nil { + continue + } + for _, node := range s.exportsWithDuplicate { + c.diagnostics.add(createDiagnosticForNode(node, diagnostics.Module_0_has_already_exported_a_member_named_1_Consider_explicitly_re_exporting_to_resolve_the_ambiguity, s.specifierText, id)) + } + } + c.extendExportSymbols(symbols, nestedSymbols, nil, nil) + } + if exportStar != nil && exportStar.AsExportDeclaration().isTypeOnly { + if typeOnlyExportStarMap == nil { + typeOnlyExportStarMap = make(map[string]*Node) + } + for name := range symbols { + typeOnlyExportStarMap[name] = exportStar + } + } + return symbols + } + // A module defined by an 'export=' consists of one export that needs to be resolved + moduleSymbol = c.resolveExternalModuleSymbol(moduleSymbol, false /*dontResolveAlias*/) + exports = visit(moduleSymbol, nil, false) + if exports == nil { + exports = make(SymbolTable) + } + for name := range nonTypeOnlyNames { + delete(typeOnlyExportStarMap, name) + } + return +} + +/** + * Extends one symbol table with another while collecting information on name collisions for error message generation into the `lookupTable` argument + * Not passing `lookupTable` and `exportNode` disables this collection, and just extends the tables + */ +func (c *Checker) extendExportSymbols(target SymbolTable, source SymbolTable, lookupTable ExportCollisionTable, exportNode *Node) { + for id, sourceSymbol := range source { + if id == InternalSymbolNameDefault { + continue + } + targetSymbol := target[id] + if targetSymbol == nil { + target[id] = sourceSymbol + if lookupTable != nil && exportNode != nil { + lookupTable[id] = &ExportCollision{ + specifierText: getTextOfNode(exportNode.AsExportDeclaration().moduleSpecifier), + } + } + } else if lookupTable != nil && exportNode != nil && c.resolveSymbol(targetSymbol) != c.resolveSymbol(sourceSymbol) { + s := lookupTable[id] + s.exportsWithDuplicate = append(s.exportsWithDuplicate, exportNode) + } + } +} + +/** + * Indicates that a symbol is an alias that does not merge with a local declaration. + * OR Is a JSContainer which may merge an alias with a local declaration + */ +func isNonLocalAlias(symbol *Symbol, excludes SymbolFlags) bool { + if symbol == nil { + return false + } + return symbol.flags&(SymbolFlagsAlias|excludes) == SymbolFlagsAlias || + symbol.flags&SymbolFlagsAlias != 0 && symbol.flags&SymbolFlagsAssignment != 0 +} + +func (c *Checker) resolveAlias(symbol *Symbol) *Symbol { + if symbol == c.unknownSymbol { + return symbol // !!! Remove once all symbols are properly resolved + } + if symbol.flags&SymbolFlagsAlias == 0 { + panic("Should only get alias here") + } + links := c.aliasSymbolLinks.get(symbol) + if links.aliasTarget == nil { + links.aliasTarget = c.resolvingSymbol + node := c.getDeclarationOfAliasSymbol(symbol) + if node == nil { + panic("Unexpected nil in resolveAlias") + } + target := c.getTargetOfAliasDeclaration(node, false /*dontRecursivelyResolve*/) + if links.aliasTarget == c.resolvingSymbol { + if target == nil { + target = c.unknownSymbol + } + links.aliasTarget = target + } else { + c.error(node, diagnostics.Circular_definition_of_import_alias_0, c.symbolToString(symbol)) + } + } else if links.aliasTarget == c.resolvingSymbol { + links.aliasTarget = c.unknownSymbol + } + return links.aliasTarget +} + +/** + * Gets combined flags of a `symbol` and all alias targets it resolves to. `resolveAlias` + * is typically recursive over chains of aliases, but stops mid-chain if an alias is merged + * with another exported symbol, e.g. + * ```ts + * // a.ts + * export const a = 0; + * // b.ts + * export { a } from "./a"; + * export type a = number; + * // c.ts + * import { a } from "./b"; + * ``` + * Calling `resolveAlias` on the `a` in c.ts would stop at the merged symbol exported + * from b.ts, even though there is still more alias to resolve. Consequently, if we were + * trying to determine if the `a` in c.ts has a value meaning, looking at the flags on + * the local symbol and on the symbol returned by `resolveAlias` is not enough. + * @returns SymbolFlags.All if `symbol` is an alias that ultimately resolves to `unknown`; + * combined flags of all alias targets otherwise. + */ +func (c *Checker) getSymbolFlags(symbol *Symbol) SymbolFlags { + return c.getSymbolFlagsEx(symbol, false /*excludeTypeOnlyMeanings*/, false /*excludeLocalMeanings*/) +} + +func (c *Checker) getSymbolFlagsEx(symbol *Symbol, excludeTypeOnlyMeanings bool, excludeLocalMeanings bool) SymbolFlags { + var typeOnlyDeclaration *Node + if excludeTypeOnlyMeanings { + typeOnlyDeclaration = c.getTypeOnlyAliasDeclaration(symbol) + } + typeOnlyDeclarationIsExportStar := typeOnlyDeclaration != nil && isExportDeclaration(typeOnlyDeclaration) + var typeOnlyResolution *Symbol + if typeOnlyDeclaration != nil { + if typeOnlyDeclarationIsExportStar { + moduleSpecifier := typeOnlyDeclaration.AsExportDeclaration().moduleSpecifier + typeOnlyResolution = c.resolveExternalModuleName(moduleSpecifier, moduleSpecifier /*ignoreErrors*/, true) + } else { + typeOnlyResolution = c.resolveAlias(typeOnlyDeclaration.Symbol()) + } + } + var typeOnlyExportStarTargets SymbolTable + if typeOnlyDeclarationIsExportStar && typeOnlyResolution != nil { + typeOnlyExportStarTargets = c.getExportsOfModule(typeOnlyResolution) + } + var flags SymbolFlags + if !excludeLocalMeanings { + flags = symbol.flags + } + var seenSymbols map[*Symbol]bool + for symbol.flags&SymbolFlagsAlias != 0 { + target := c.getExportSymbolOfValueSymbolIfExported(c.resolveAlias(symbol)) + if !typeOnlyDeclarationIsExportStar && target == typeOnlyResolution || typeOnlyExportStarTargets[target.name] == target { + break + } + if target == c.unknownSymbol { + return SymbolFlagsAll + } + // Optimizations - try to avoid creating or adding to + // `seenSymbols` if possible + if target == symbol || seenSymbols[target] { + break + } + if target.flags&SymbolFlagsAlias != 0 { + if seenSymbols == nil { + seenSymbols = make(map[*Symbol]bool) + seenSymbols[symbol] = true + } + seenSymbols[target] = true + } + flags |= target.flags + symbol = target + } + return flags +} + +func (c *Checker) getDeclarationOfAliasSymbol(symbol *Symbol) *Node { + return findLast(symbol.declarations, c.isAliasSymbolDeclaration) +} + +/** + * An alias symbol is created by one of the following declarations: + * import = ... + * import from ... + * import * as from ... + * import { x as } from ... + * export { x as } from ... + * export * as ns from ... + * export = + * export default + */ +func (c *Checker) isAliasSymbolDeclaration(node *Node) bool { + switch node.kind { + case SyntaxKindImportEqualsDeclaration, SyntaxKindNamespaceExportDeclaration, SyntaxKindNamespaceImport, SyntaxKindNamespaceExport, + SyntaxKindImportSpecifier, SyntaxKindExportSpecifier: + return true + case SyntaxKindImportClause: + return node.AsImportClause().name != nil + case SyntaxKindExportAssignment: + return exportAssignmentIsAlias(node) + } + return false +} + +func (c *Checker) getTypeOfSymbol(symbol *Symbol) *Type { + // !!! + // checkFlags := symbol.checkFlags + // if checkFlags&CheckFlagsDeferredType != 0 { + // return c.getTypeOfSymbolWithDeferredType(symbol) + // } + // if checkFlags&CheckFlagsInstantiated != 0 { + // return c.getTypeOfInstantiatedSymbol(symbol) + // } + // if checkFlags&CheckFlagsMapped != 0 { + // return c.getTypeOfMappedSymbol(symbol.(MappedSymbol)) + // } + // if checkFlags&CheckFlagsReverseMapped != 0 { + // return c.getTypeOfReverseMappedSymbol(symbol.(ReverseMappedSymbol)) + // } + if symbol.flags&(SymbolFlagsVariable|SymbolFlagsProperty) != 0 { + return c.getTypeOfVariableOrParameterOrProperty(symbol) + } + if symbol.flags&(SymbolFlagsFunction|SymbolFlagsMethod|SymbolFlagsClass|SymbolFlagsEnum|SymbolFlagsValueModule) != 0 { + return c.getTypeOfFuncClassEnumModule(symbol) + } + // if symbol.flags&SymbolFlagsEnumMember != 0 { + // return c.getTypeOfEnumMember(symbol) + // } + // if symbol.flags&SymbolFlagsAccessor != 0 { + // return c.getTypeOfAccessors(symbol) + // } + // if symbol.flags&SymbolFlagsAlias != 0 { + // return c.getTypeOfAlias(symbol) + // } + return c.errorType +} + +func (c *Checker) getTypeOfVariableOrParameterOrProperty(symbol *Symbol) *Type { + links := c.valueSymbolLinks.get(symbol) + if links.resolvedType == nil { + t := c.getTypeOfVariableOrParameterOrPropertyWorker(symbol) + if t == nil { + panic("Unexpected nil type") + } + // For a contextually typed parameter it is possible that a type has already + // been assigned (in assignTypeToParameterAndFixTypeParameters), and we want + // to preserve this type. In fact, we need to _prefer_ that type, but it won't + // be assigned until contextual typing is complete, so we need to defer in + // cases where contextual typing may take place. + if links.resolvedType == nil && !c.isParameterOfContextSensitiveSignature(symbol) { + links.resolvedType = t + } + return t + } + return links.resolvedType +} + +func (c *Checker) isParameterOfContextSensitiveSignature(symbol *Symbol) bool { + return false // !!! +} + +func (c *Checker) getTypeOfVariableOrParameterOrPropertyWorker(symbol *Symbol) *Type { + // Handle prototype property + if symbol.flags&SymbolFlagsPrototype != 0 { + return c.getTypeOfPrototypeProperty(symbol) + } + // CommonsJS require and module both have type any. + if symbol == c.requireSymbol { + return c.anyType + } + // !!! Debug.assertIsDefined(symbol.valueDeclaration) + declaration := symbol.valueDeclaration + // !!! Handle export default expressions + // if isSourceFile(declaration) && isJsonSourceFile(declaration) { + // if !declaration.statements.length { + // return c.emptyObjectType + // } + // return c.getWidenedType(c.getWidenedLiteralType(c.checkExpression(declaration.statements[0].expression))) + // } + // Handle variable, parameter or property + if !c.pushTypeResolution(symbol, TypeSystemPropertyNameType) { + return c.reportCircularityError(symbol) + } + var result *Type + switch declaration.kind { + case SyntaxKindParameter, SyntaxKindPropertyDeclaration, SyntaxKindPropertySignature, SyntaxKindVariableDeclaration, + SyntaxKindBindingElement: + result = c.getWidenedTypeForVariableLikeDeclaration(declaration, true /*reportErrors*/) + case SyntaxKindExportAssignment: + result = c.widenTypeForVariableLikeDeclaration(c.checkExpressionCached(declaration.AsExportAssignment().expression), declaration, false /*reportErrors*/) + case SyntaxKindBinaryExpression: + result = c.getWidenedTypeForAssignmentDeclaration(symbol, nil) + case SyntaxKindJsxAttribute: + result = c.checkJsxAttribute(declaration.AsJsxAttribute(), CheckModeNormal) + case SyntaxKindEnumMember: + result = c.getTypeOfEnumMember(symbol) + default: + panic("Unhandled case in getTypeOfVariableOrParameterOrPropertyWorker") + } + if !c.popTypeResolution() { + return c.reportCircularityError(symbol) + } + return result +} + +// Return the type associated with a variable, parameter, or property declaration. In the simple case this is the type +// specified in a type annotation or inferred from an initializer. However, in the case of a destructuring declaration it +// is a bit more involved. For example: +// +// var [x, s = ""] = [1, "one"]; +// +// Here, the array literal [1, "one"] is contextually typed by the type [any, string], which is the implied type of the +// binding pattern [x, s = ""]. Because the contextual type is a tuple type, the resulting type of [1, "one"] is the +// tuple type [number, string]. Thus, the type inferred for 'x' is number and the type inferred for 's' is string. +func (c *Checker) getWidenedTypeForVariableLikeDeclaration(declaration *Node, reportErrors bool) *Type { + return c.widenTypeForVariableLikeDeclaration(c.getTypeForVariableLikeDeclaration(declaration /*includeOptionality*/, true, CheckModeNormal), declaration, reportErrors) +} + +// Return the inferred type for a variable, parameter, or property declaration +func (c *Checker) getTypeForVariableLikeDeclaration(declaration *Node, includeOptionality bool, checkMode CheckMode) *Type { + // A variable declared in a for..in statement is of type string, or of type keyof T when the + // right hand expression is of a type parameter type. + if isVariableDeclaration(declaration) { + grandParent := declaration.parent.parent + switch grandParent.kind { + case SyntaxKindForInStatement: + // !!! + // indexType := c.getIndexType(c.getNonNullableTypeIfNeeded(c.checkExpression(declaration.parent.parent.expression /*checkMode*/, checkMode))) + // if indexType.flags & (TypeFlagsTypeParameter | TypeFlagsIndex) { + // return c.getExtractStringType(indexType) + // } else { + // return c.stringType + // } + return c.stringType + case SyntaxKindForOfStatement: + // checkRightHandSideOfForOf will return undefined if the for-of expression type was + // missing properties/signatures required to get its iteratedType (like + // [Symbol.iterator] or next). This may be because we accessed properties from anyType, + // or it may have led to an error inside getElementTypeOfIterable. + return c.checkRightHandSideOfForOf(grandParent) + } + } else if isBindingElement(declaration) { + return c.getTypeForBindingElement(declaration) + } + isProperty := isPropertyDeclaration(declaration) && !hasAccessorModifier(declaration) || isPropertySignatureDeclaration(declaration) + isOptional := includeOptionality && isOptionalDeclaration(declaration) + // Use type from type annotation if one is present + declaredType := c.tryGetTypeFromEffectiveTypeNode(declaration) + if isCatchClauseVariableDeclarationOrBindingElement(declaration) { + if declaredType != nil { + // If the catch clause is explicitly annotated with any or unknown, accept it, otherwise error. + if declaredType.flags&TypeFlagsAnyOrUnknown != 0 { + return declaredType + } + return c.errorType + } + // If the catch clause is not explicitly annotated, treat it as though it were explicitly + // annotated with unknown or any, depending on useUnknownInCatchVariables. + if c.useUnknownInCatchVariables { + return c.unknownType + } else { + return c.anyType + } + } + if declaredType != nil { + return c.addOptionalityEx(declaredType, isProperty, isOptional) + } + if c.noImplicitAny && isVariableDeclaration(declaration) && !isBindingPattern(declaration.Name()) && + c.getCombinedModifierFlagsCached(declaration)&ModifierFlagsExport == 0 && declaration.flags&NodeFlagsAmbient == 0 { + // If --noImplicitAny is on or the declaration is in a Javascript file, + // use control flow tracked 'any' type for non-ambient, non-exported var or let variables with no + // initializer or a 'null' or 'undefined' initializer. + variableDeclaration := declaration.AsVariableDeclaration() + if c.getCombinedNodeFlagsCached(declaration)&NodeFlagsConstant == 0 && (variableDeclaration.initializer == nil || c.isNullOrUndefined(variableDeclaration.initializer)) { + return c.autoType + } + // Use control flow tracked 'any[]' type for non-ambient, non-exported variables with an empty array + // literal initializer. + if variableDeclaration.initializer != nil && isEmptyArrayLiteral(variableDeclaration.initializer) { + return c.anyType // !!! + // return c.autoArrayType + } + } + if isParameter(declaration) { + // !!! + // if declaration.Symbol() == nil { + // // parameters of function types defined in JSDoc in TS files don't have symbols + // return nil + // } + // fn := declaration.parent.(FunctionLikeDeclaration) + // // For a parameter of a set accessor, use the type of the get accessor if one is present + // if fn.kind == SyntaxKindSetAccessor && c.hasBindableName(fn) { + // getter := getDeclarationOfKind(c.getSymbolOfDeclaration(declaration.parent), SyntaxKindGetAccessor) + // if getter != nil { + // getterSignature := c.getSignatureFromDeclaration(getter) + // thisParameter := c.getAccessorThisParameter(fn.(AccessorDeclaration)) + // if thisParameter && declaration == thisParameter { + // // Use the type from the *getter* + // Debug.assert(!thisParameter.type_) + // return c.getTypeOfSymbol(getterSignature.thisParameter) + // } + // return c.getReturnTypeOfSignature(getterSignature) + // } + // } + // parameterTypeOfTypeTag := c.getParameterTypeOfTypeTag(fn, declaration) + // if parameterTypeOfTypeTag { + // return parameterTypeOfTypeTag + // } + // // Use contextual parameter type if one is available + // var type_ *Type + // if declaration.symbol.escapedName == InternalSymbolNameThis { + // type_ = c.getContextualThisParameterType(fn) + // } else { + // type_ = c.getContextuallyTypedParameterType(declaration) + // } + // if type_ { + // return c.addOptionality(type_ /*isProperty*/, false, isOptional) + // } + } + // Use the type of the initializer expression if one is present and the declaration is + // not a parameter of a contextually typed function + if getInitializerFromNode(declaration) != nil { + t := c.widenTypeInferredFromInitializer(declaration, c.checkDeclarationInitializer(declaration, checkMode, nil /*contextualType*/)) + return c.addOptionalityEx(t, isProperty, isOptional) + } + if c.noImplicitAny && isPropertyDeclaration(declaration) { + // We have a property declaration with no type annotation or initializer, in noImplicitAny mode or a .js file. + // Use control flow analysis of this.xxx assignments in the constructor or static block to determine the type of the property. + if !hasStaticModifier(declaration) { + return nil + // !!! + // constructor := findConstructorDeclaration(declaration.parent.(ClassLikeDeclaration)) + // var t *Type + // switch { + // case constructor != nil: + // t = c.getFlowTypeInConstructor(declaration.symbol, constructor) + // case getEffectiveModifierFlags(declaration)&ModifierFlagsAmbient != 0: + // t = c.getTypeOfPropertyInBaseClass(declaration.symbol) + // } + // if t != nil { + // t = c.addOptionalityEx(t, true /*isProperty*/, isOptional) + // } + // return t + } else { + return nil + // !!! + // staticBlocks := filter(declaration.parent.(ClassLikeDeclaration).Members(), isClassStaticBlockDeclaration) + // var t *Type + // switch { + // case len(staticBlocks) != 0: + // t = c.getFlowTypeInStaticBlocks(declaration.symbol, staticBlocks) + // case getEffectiveModifierFlags(declaration)&ModifierFlagsAmbient != 0: + // t = c.getTypeOfPropertyInBaseClass(declaration.symbol) + // } + // if t != nil { + // t = c.addOptionalityEx(t, true /*isProperty*/, isOptional) + // } + // return t + } + } + if isJsxAttribute(declaration) { + // if JSX attribute doesn't have initializer, by default the attribute will have boolean value of true. + // I.e is sugar for + return c.trueType + } + // If the declaration specifies a binding pattern and is not a parameter of a contextually + // typed function, use the type implied by the binding pattern + if isBindingPattern(declaration.Name()) { + return c.getTypeFromBindingPattern(declaration.Name() /*includePatternInType*/, false /*reportErrors*/, true) + } + // No type specified and nothing can be inferred + return nil +} + +func (c *Checker) checkDeclarationInitializer(declaration *Node, checkMode CheckMode, contextualType *Type) *Type { + initializer := c.getEffectiveInitializer(declaration) + t := c.getQuickTypeOfExpression(initializer) + if t == nil { + if contextualType != nil { + t = c.checkExpressionWithContextualType(initializer, contextualType, nil /*inferenceContext*/, checkMode) + } else { + t = c.checkExpressionCachedEx(initializer, checkMode) + } + } + if isParameter(getRootDeclaration(declaration)) { + name := declaration.Name() + switch name.kind { + case SyntaxKindObjectBindingPattern: + if isObjectLiteralType(t) { + return c.padObjectLiteralType(t, name) + } + case SyntaxKindArrayBindingPattern: + if isTupleType(t) { + return c.padTupleType(t, name) + } + } + } + return t +} + +func (c *Checker) padObjectLiteralType(t *Type, pattern *Node) *Type { + return t +} + +func (c *Checker) padTupleType(t *Type, pattern *Node) *Type { + return t +} + +func (c *Checker) getEffectiveInitializer(declaration *Node) *Node { + return getInitializerFromNode(declaration) +} + +func (c *Checker) widenTypeInferredFromInitializer(declaration *Node, t *Type) *Type { + if c.getCombinedNodeFlagsCached(declaration)&NodeFlagsConstant != 0 || isDeclarationReadonly(declaration) { + return t + } + return c.getWidenedLiteralType(t) +} + +func (c *Checker) getTypeOfFuncClassEnumModule(symbol *Symbol) *Type { + links := c.valueSymbolLinks.get(symbol) + if links.resolvedType == nil { + links.resolvedType = c.getTypeOfFuncClassEnumModuleWorker(symbol) + } + return links.resolvedType +} + +func (c *Checker) getTypeOfFuncClassEnumModuleWorker(symbol *Symbol) *Type { + if symbol.flags&SymbolFlagsModule != 0 && isShorthandAmbientModuleSymbol(symbol) { + return c.anyType + } + t := c.newObjectType(ObjectFlagsAnonymous, symbol) + if symbol.flags&SymbolFlagsClass != 0 { + baseTypeVariable := c.getBaseTypeVariableOfClass(symbol) + if baseTypeVariable != nil { + return c.getIntersectionType(t, baseTypeVariable) + } + return t + } + if c.strictNullChecks && symbol.flags&SymbolFlagsOptional != 0 { + return c.getOptionalType(t /*isProperty*/, true) + } + return t +} + +func (c *Checker) getBaseTypeVariableOfClass(symbol *Symbol) *Type { + baseConstructorType := c.getBaseConstructorTypeOfClass(c.getDeclaredTypeOfClassOrInterface(symbol)) + switch { + case baseConstructorType.flags&TypeFlagsTypeVariable != 0: + return baseConstructorType + case baseConstructorType.flags&TypeFlagsIntersection != 0: + return find(baseConstructorType.IntersectionType().types, func(t *Type) bool { + return t.flags&TypeFlagsTypeVariable != 0 + }) + } + return nil +} + +func (c *Checker) getBaseConstructorTypeOfClass(t *Type) *Type { + return c.anyType // !!! +} + +func (c *Checker) getDeclaredTypeOfClassOrInterface(symbol *Symbol) *Type { + links := c.interfaceTypeLinks.get(symbol) + if links.declaredType == nil { + kind := ifElse(symbol.flags&SymbolFlagsClass != 0, ObjectFlagsClass, ObjectFlagsInterface) + t := c.newInterfaceType(kind, symbol) + links.declaredType = t + outerTypeParameters := c.getOuterTypeParametersOfClassOrInterface(symbol) + localTypeParameters := c.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol) + // A class or interface is generic if it has type parameters or a "this" type. We always give classes a "this" type + // because it is not feasible to analyze all members to determine if the "this" type escapes the class (in particular, + // property types inferred from initializers and method return types inferred from return statements are very hard + // to exhaustively analyze). We give interfaces a "this" type if we can't definitely determine that they are free of + // "this" references. + if outerTypeParameters != nil || localTypeParameters != nil || kind == ObjectFlagsClass || !c.isThislessInterface(symbol) { + t.objectFlags |= ObjectFlagsReference + d := t.InterfaceType() + d.thisType = c.newTypeParameter(symbol) + d.thisType.TypeParameter().isThisType = true + d.thisType.TypeParameter().constraint = t + // Allocate room for all type parameters plus the extra thisType + d.typeParameters = append(append(make([]*Type, 0, len(outerTypeParameters)+len(localTypeParameters)+1), outerTypeParameters...), localTypeParameters...) + // Append thisType such that t.typeParameters[:len(t.typeParameters)+1] yields the full list + _ = append(d.typeParameters, d.thisType) + d.resolvedTypeArguments = d.typeParameters + d.outerTypeParameters = outerTypeParameters + d.localTypeParameters = localTypeParameters + d.instantiations = make(map[string]*Type) + d.instantiations[getTypeListId(d.resolvedTypeArguments)] = t + d.target = t + } + } + return links.declaredType +} + +func (c *Checker) isThislessInterface(symbol *Symbol) bool { + return true // !!! +} + +func getTypeListId(types []*Type) string { + var sb strings.Builder + writeTypes(&sb, types) + return sb.String() +} + +func getAliasId(alias *TypeAlias) string { + var sb strings.Builder + writeAlias(&sb, alias) + return sb.String() +} + +func getUnionId(types []*Type, origin *Type, alias *TypeAlias) string { + var sb strings.Builder + switch { + case origin == nil: + writeTypes(&sb, types) + case origin.flags&TypeFlagsUnion != 0: + sb.WriteByte('|') + writeTypes(&sb, origin.UnionType().types) + case origin.flags&TypeFlagsIntersection != 0: + sb.WriteByte('&') + writeTypes(&sb, origin.IntersectionType().types) + case origin.flags&TypeFlagsIndex != 0: + // origin type id alone is insufficient, as `keyof x` may resolve to multiple WIP values while `x` is still resolving + sb.WriteByte('#') + writeUint32(&sb, uint32(origin.id)) + sb.WriteByte('|') + writeTypes(&sb, types) + default: + panic("Unhandled case in getUnionId") + } + if alias != nil { + writeAlias(&sb, alias) + } + return sb.String() +} + +func writeTypes(sb *strings.Builder, types []*Type) { + i := 0 + for i < len(types) { + startId := types[i].id + count := 1 + for i+count < len(types) && types[i+count].id == startId+TypeId(count) { + count++ + } + if sb.Len() != 0 { + sb.WriteByte(',') + } + writeUint32(sb, uint32(startId)) + if count > 1 { + sb.WriteByte(':') + writeUint32(sb, uint32(count)) + } + i += count + } +} + +func writeAlias(sb *strings.Builder, alias *TypeAlias) { + sb.WriteByte('@') + writeUint32(sb, uint32(getSymbolId(alias.symbol))) + if len(alias.typeArguments) != 0 { + sb.WriteByte(':') + writeTypes(sb, alias.typeArguments) + } +} + +var base64chars = []byte{ + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', + 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', + 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '$', '?'} + +func writeUint32(sb *strings.Builder, value uint32) { + for value != 0 { + sb.WriteByte(base64chars[value&0x3F]) + value >>= 6 + } +} + +func (c *Checker) isNullOrUndefined(node *Node) bool { + expr := skipParentheses(node) + switch expr.kind { + case SyntaxKindNullKeyword: + return true + case SyntaxKindIdentifier: + return c.getResolvedSymbol(expr) == c.undefinedSymbol + } + return false +} + +func (c *Checker) checkRightHandSideOfForOf(statement *Node) *Type { + return c.anyType // !!! +} + +func (c *Checker) getTypeForBindingElement(declaration *Node) *Type { + return c.anyType // !!! +} + +// Return the type implied by a binding pattern. This is the type implied purely by the binding pattern itself +// and without regard to its context (i.e. without regard any type annotation or initializer associated with the +// declaration in which the binding pattern is contained). For example, the implied type of [x, y] is [any, any] +// and the implied type of { x, y: z = 1 } is { x: any; y: number; }. The type implied by a binding pattern is +// used as the contextual type of an initializer associated with the binding pattern. Also, for a destructuring +// parameter with no type annotation or initializer, the type implied by the binding pattern becomes the type of +// the parameter. +func (c *Checker) getTypeFromBindingPattern(pattern *Node, includePatternInType bool, reportErrors bool) *Type { + return c.anyType // !!! +} + +func (c *Checker) getTypeOfPrototypeProperty(prototype *Symbol) *Type { + return c.anyType // !!! +} + +func (c *Checker) getWidenedTypeForAssignmentDeclaration(symbol *Symbol, resolvedSymbol *Symbol) *Type { + return c.anyType // !!! +} + +func (c *Checker) widenTypeForVariableLikeDeclaration(t *Type, declaration *Node, reportErrors bool) *Type { + if t != nil { + return t + // !!! + // // TODO: If back compat with pre-3.0/4.0 libs isn't required, remove the following SymbolConstructor special case transforming `symbol` into `unique symbol` + // if t.flags&TypeFlagsESSymbol != 0 && c.isGlobalSymbolConstructor(declaration.parent) { + // t = c.getESSymbolLikeTypeForNode(declaration) + // } + // if reportErrors { + // c.reportErrorsFromWidening(declaration, t) + // } + // // always widen a 'unique symbol' type if the type was created for a different declaration. + // if t.flags&TypeFlagsUniqueESSymbol && (isBindingElement(declaration) || !declaration.type_) && t.symbol != c.getSymbolOfDeclaration(declaration) { + // t = c.esSymbolType + // } + // return c.getWidenedType(t) + } + // Rest parameters default to type any[], other parameters default to type any + if isParameter(declaration) && declaration.AsParameterDeclaration().dotDotDotToken != nil { + t = c.anyArrayType + } else { + t = c.anyType + } + // Report implicit any errors unless this is a private property within an ambient declaration + if reportErrors { + if !declarationBelongsToPrivateAmbientMember(declaration) { + c.reportImplicitAny(declaration, t, WideningKindNormal) + } + } + return t +} + +func (c *Checker) reportImplicitAny(declaration *Node, t *Type, wideningKind WideningKind) { + typeAsString := c.typeToString(c.getWidenedType(t)) + var diagnostic *diagnostics.Message + switch declaration.kind { + case SyntaxKindBinaryExpression, SyntaxKindPropertyDeclaration, SyntaxKindPropertySignature: + diagnostic = ifElse(c.noImplicitAny, + diagnostics.Member_0_implicitly_has_an_1_type, + diagnostics.Member_0_implicitly_has_an_1_type_but_a_better_type_may_be_inferred_from_usage) + case SyntaxKindParameter: + param := declaration.AsParameterDeclaration() + if isIdentifier(param.name) { + name := param.name.AsIdentifier() + originalKeywordKind := identifierToKeywordKind(name) + if (isCallSignatureDeclaration(declaration.parent) || isMethodSignatureDeclaration(declaration.parent) || isFunctionTypeNode(declaration.parent)) && + slices.Contains(declaration.parent.FunctionLikeData().parameters, declaration) && + (isTypeNodeKind(originalKeywordKind) || c.resolveName(declaration, name.text, SymbolFlagsType, nil /*nameNotFoundMessage*/, true /*isUse*/, false /*excludeGlobals*/) != nil) { + newName := fmt.Sprintf("arg%v", slices.Index(declaration.parent.FunctionLikeData().parameters, declaration)) + typeName := declarationNameToString(param.name) + ifElse(param.dotDotDotToken != nil, "[]", "") + c.errorOrSuggestion(c.noImplicitAny, declaration, diagnostics.Parameter_has_a_name_but_no_type_Did_you_mean_0_Colon_1, newName, typeName) + return + } + } + switch { + case param.dotDotDotToken != nil: + if c.noImplicitAny { + diagnostic = diagnostics.Rest_parameter_0_implicitly_has_an_any_type + } else { + diagnostic = diagnostics.Rest_parameter_0_implicitly_has_an_any_type_but_a_better_type_may_be_inferred_from_usage + } + case c.noImplicitAny: + diagnostic = diagnostics.Parameter_0_implicitly_has_an_1_type + default: + diagnostic = diagnostics.Parameter_0_implicitly_has_an_1_type_but_a_better_type_may_be_inferred_from_usage + } + case SyntaxKindBindingElement: + diagnostic = diagnostics.Binding_element_0_implicitly_has_an_1_type + if !c.noImplicitAny { + // Don't issue a suggestion for binding elements since the codefix doesn't yet support them. + return + } + case SyntaxKindFunctionDeclaration, SyntaxKindMethodDeclaration, SyntaxKindMethodSignature, SyntaxKindGetAccessor, + SyntaxKindSetAccessor, SyntaxKindFunctionExpression, SyntaxKindArrowFunction: + if c.noImplicitAny && declaration.Name() == nil { + if wideningKind == WideningKindGeneratorYield { + c.error(declaration, diagnostics.Generator_implicitly_has_yield_type_0_Consider_supplying_a_return_type_annotation, typeAsString) + } else { + c.error(declaration, diagnostics.Function_expression_which_lacks_return_type_annotation_implicitly_has_an_0_return_type, typeAsString) + } + return + } + switch { + case !c.noImplicitAny: + diagnostic = diagnostics.X_0_implicitly_has_an_1_return_type_but_a_better_type_may_be_inferred_from_usage + case wideningKind == WideningKindGeneratorYield: + diagnostic = diagnostics.X_0_which_lacks_return_type_annotation_implicitly_has_an_1_yield_type + default: + diagnostic = diagnostics.X_0_which_lacks_return_type_annotation_implicitly_has_an_1_return_type + } + case SyntaxKindMappedType: + if c.noImplicitAny { + c.error(declaration, diagnostics.Mapped_object_type_implicitly_has_an_any_template_type) + } + return + default: + if c.noImplicitAny { + diagnostic = diagnostics.Variable_0_implicitly_has_an_1_type + } else { + diagnostic = diagnostics.Variable_0_implicitly_has_an_1_type_but_a_better_type_may_be_inferred_from_usage + } + } + c.errorOrSuggestion(c.noImplicitAny, declaration, diagnostic, declarationNameToString(getNameOfDeclaration(declaration)), typeAsString) +} + +func (c *Checker) getWidenedType(t *Type) *Type { + return t // !!! +} + +func (c *Checker) getTypeOfEnumMember(symbol *Symbol) *Type { + return c.anyType // !!! +} + +func (c *Checker) addOptionalityEx(t *Type, isProperty bool, isOptional bool) *Type { + if c.strictNullChecks && isOptional { + return c.getOptionalType(t, isProperty) + } + return t +} + +func (c *Checker) getOptionalType(t *Type, isProperty bool) *Type { + // !!! Debug.assert(c.strictNullChecks) + missingOrUndefined := ifElse(isProperty, c.undefinedOrMissingType, c.undefinedType) + if t == missingOrUndefined || t.flags&TypeFlagsUnion != 0 && t.UnionType().types[0] == missingOrUndefined { + return t + } + return c.getUnionType([]*Type{t, missingOrUndefined}) +} + +func (c *Checker) getDeclarationNodeFlagsFromSymbol(s *Symbol) NodeFlags { + if s.valueDeclaration != nil { + return c.getCombinedNodeFlagsCached(s.valueDeclaration) + } + return NodeFlagsNone +} + +func (c *Checker) getCombinedNodeFlagsCached(node *Node) NodeFlags { + // we hold onto the last node and result to speed up repeated lookups against the same node. + if c.lastGetCombinedNodeFlagsNode == node { + return c.lastGetCombinedNodeFlagsResult + } + c.lastGetCombinedNodeFlagsNode = node + c.lastGetCombinedNodeFlagsResult = getCombinedNodeFlags(node) + return c.lastGetCombinedNodeFlagsResult +} + +func (c *Checker) getCombinedModifierFlagsCached(node *Node) ModifierFlags { + // we hold onto the last node and result to speed up repeated lookups against the same node. + if c.lastGetCombinedModifierFlagsNode == node { + return c.lastGetCombinedModifierFlagsResult + } + c.lastGetCombinedModifierFlagsNode = node + c.lastGetCombinedModifierFlagsResult = getCombinedModifierFlags(node) + return c.lastGetCombinedModifierFlagsResult +} + +/** + * Push an entry on the type resolution stack. If an entry with the given target and the given property name + * is already on the stack, and no entries in between already have a type, then a circularity has occurred. + * In this case, the result values of the existing entry and all entries pushed after it are changed to false, + * and the value false is returned. Otherwise, the new entry is just pushed onto the stack, and true is returned. + * In order to see if the same query has already been done before, the target object and the propertyName both + * must match the one passed in. + * + * @param target The symbol, type, or signature whose type is being queried + * @param propertyName The property name that should be used to query the target for its type + */ +func (c *Checker) pushTypeResolution(target TypeSystemEntity, propertyName TypeSystemPropertyName) bool { + resolutionCycleStartIndex := c.findResolutionCycleStartIndex(target, propertyName) + if resolutionCycleStartIndex >= 0 { + // A cycle was found + for i := resolutionCycleStartIndex; i < len(c.typeResolutions); i++ { + c.typeResolutions[i].result = false + } + return false + } + c.typeResolutions = append(c.typeResolutions, TypeResolution{target: target, propertyName: propertyName, result: true}) + return true +} + +/** + * Pop an entry from the type resolution stack and return its associated result value. The result value will + * be true if no circularities were detected, or false if a circularity was found. + */ +func (c *Checker) popTypeResolution() bool { + lastIndex := len(c.typeResolutions) - 1 + result := c.typeResolutions[lastIndex].result + c.typeResolutions = c.typeResolutions[:lastIndex] + return result +} + +func (c *Checker) findResolutionCycleStartIndex(target TypeSystemEntity, propertyName TypeSystemPropertyName) int { + for i := len(c.typeResolutions) - 1; i >= c.resolutionStart; i-- { + resolution := &c.typeResolutions[i] + if c.typeResolutionHasProperty(resolution) { + return -1 + } + if resolution.target == target && resolution.propertyName == propertyName { + return i + } + } + return -1 +} + +func (c *Checker) typeResolutionHasProperty(r *TypeResolution) bool { + switch r.propertyName { + case TypeSystemPropertyNameType: + return c.valueSymbolLinks.get(r.target.(*Symbol)).resolvedType != nil + // !!! + // case TypeSystemPropertyNameDeclaredType: + // return !!c.getSymbolLinks(target.(Symbol)).declaredType + // case TypeSystemPropertyNameResolvedBaseConstructorType: + // return !!(target.(InterfaceType)).resolvedBaseConstructorType + // case TypeSystemPropertyNameResolvedReturnType: + // return !!(target.(Signature)).resolvedReturnType + // case TypeSystemPropertyNameImmediateBaseConstraint: + // return !!(target.(*Type)).immediateBaseConstraint + // case TypeSystemPropertyNameResolvedTypeArguments: + // return !!(target.(TypeReference)).resolvedTypeArguments + // case TypeSystemPropertyNameResolvedBaseTypes: + // return !!(target.(InterfaceType)).baseTypesResolved + // case TypeSystemPropertyNameWriteType: + // return !!c.getSymbolLinks(target.(Symbol)).writeType + // case TypeSystemPropertyNameParameterInitializerContainsUndefined: + // return c.getNodeLinks(target.(ParameterDeclaration)).parameterInitializerContainsUndefined != nil + } + panic("Unhandled case in typeResolutionHasProperty") +} + +func (c *Checker) reportCircularityError(symbol *Symbol) *Type { + declaration := symbol.valueDeclaration + // Check if variable has type annotation that circularly references the variable itself + if declaration != nil { + if getEffectiveTypeAnnotationNode(declaration) != nil { + c.error(symbol.valueDeclaration, diagnostics.X_0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, c.symbolToString(symbol)) + return c.errorType + } + // Check if variable has initializer that circularly references the variable itself + if c.noImplicitAny && (declaration.kind != SyntaxKindParameter || declaration.AsParameterDeclaration().initializer != nil) { + c.error(symbol.valueDeclaration, diagnostics.X_0_implicitly_has_type_any_because_it_does_not_have_a_type_annotation_and_is_referenced_directly_or_indirectly_in_its_own_initializer, c.symbolToString(symbol)) + } + } else if symbol.flags&SymbolFlagsAlias != 0 { + node := c.getDeclarationOfAliasSymbol(symbol) + if node != nil { + c.error(node, diagnostics.Circular_definition_of_import_alias_0, c.symbolToString(symbol)) + } + } + // Circularities could also result from parameters in function expressions that end up + // having themselves as contextual types following type argument inference. In those cases + // we have already reported an implicit any error so we don't report anything here. + return c.anyType +} + +func (c *Checker) getPropertiesOfType(t *Type) []*Symbol { + t = c.getReducedApparentType(t) + if t.flags&TypeFlagsUnionOrIntersection != 0 { + return c.getPropertiesOfUnionOrIntersectionType(t) + } + return c.getPropertiesOfObjectType(t) +} + +func (c *Checker) getPropertiesOfObjectType(t *Type) []*Symbol { + if t.flags&TypeFlagsObject != 0 { + return c.resolveStructuredTypeMembers(t).properties + } + return nil +} + +func (c *Checker) getPropertiesOfUnionOrIntersectionType(t *Type) []*Symbol { + return nil +} + +func (c *Checker) getPropertyOfType(t *Type, name string) *Symbol { + return c.getPropertyOfTypeEx(t, name, false /*skipObjectFunctionPropertyAugment*/, false /*includeTypeOnlyMembers*/) +} + +/** + * Return the symbol for the property with the given name in the given type. Creates synthetic union properties when + * necessary, maps primitive types and type parameters are to their apparent types, and augments with properties from + * Object and Function as appropriate. + * + * @param type a type to look up property from + * @param name a name of property to look up in a given type + */ +func (c *Checker) getPropertyOfTypeEx(t *Type, name string, skipObjectFunctionPropertyAugment bool, includeTypeOnlyMembers bool) *Symbol { + t = c.getReducedApparentType(t) + switch { + case t.flags&TypeFlagsObject != 0: + resolved := c.resolveStructuredTypeMembers(t) + symbol := resolved.members[name] + if symbol != nil { + if !includeTypeOnlyMembers && t.symbol.flags&SymbolFlagsValueModule != 0 && c.moduleSymbolLinks.get(t.symbol).typeOnlyExportStarMap[name] != nil { + // If this is the type of a module, `resolved.members.get(name)` might have effectively skipped over + // an `export type * from './foo'`, leaving `symbolIsValue` unable to see that the symbol is being + // viewed through a type-only export. + return nil + } + if c.symbolIsValueEx(symbol, includeTypeOnlyMembers) { + return symbol + } + } + if !skipObjectFunctionPropertyAugment { + var functionType *Type + switch { + case t == c.anyFunctionType: + functionType = c.globalFunctionType + case len(resolved.callSignatures) != 0: + functionType = c.globalCallableFunctionType + case len(resolved.constructSignatures) != 0: + functionType = c.globalNewableFunctionType + } + if functionType != nil { + symbol = c.getPropertyOfObjectType(functionType, name) + if symbol != nil { + return symbol + } + } + return c.getPropertyOfObjectType(c.globalObjectType, name) + } + return nil + case t.flags&TypeFlagsIntersection != 0: + prop := c.getPropertyOfUnionOrIntersectionType(t, name, true /*skipObjectFunctionPropertyAugment*/) + if prop != nil { + return prop + } + if !skipObjectFunctionPropertyAugment { + return c.getPropertyOfUnionOrIntersectionType(t, name, skipObjectFunctionPropertyAugment) + } + return nil + case t.flags&TypeFlagsUnion != 0: + return c.getPropertyOfUnionOrIntersectionType(t, name, skipObjectFunctionPropertyAugment) + } + return nil +} + +func (c *Checker) getSignaturesOfType(t *Type, kind SignatureKind) []*Signature { + return nil // !!! +} + +func (c *Checker) getIndexInfosOfType(t *Type) []*IndexInfo { + return nil // !!! +} + +func (c *Checker) resolveStructuredTypeMembers(t *Type) *ObjectTypeBase { + if t.objectFlags&ObjectFlagsMembersResolved == 0 { + switch { + case t.flags&TypeFlagsObject != 0: + switch { + case t.objectFlags&ObjectFlagsReference != 0: + c.resolveTypeReferenceMembers(t) + case t.objectFlags&ObjectFlagsClassOrInterface != 0: + c.resolveClassOrInterfaceMembers(t) + case t.objectFlags&ObjectFlagsReverseMapped != 0: + c.resolveReverseMappedTypeMembers(t) + case t.objectFlags&ObjectFlagsAnonymous != 0: + c.resolveAnonymousTypeMembers(t) + case t.objectFlags&ObjectFlagsMapped != 0: + c.resolveMappedTypeMembers(t) + default: + panic("Unhandled case in resolveStructuredTypeMembers") + } + case t.flags&TypeFlagsUnion != 0: + c.resolveUnionTypeMembers(t) + case t.flags&TypeFlagsIntersection != 0: + c.resolveIntersectionTypeMembers(t) + default: + panic("Unhandled case in resolveStructuredTypeMembers") + } + } + return t.ObjectType() +} + +func (c *Checker) resolveClassOrInterfaceMembers(t *Type) { + c.resolveObjectTypeMembers(t, t, nil, nil) +} + +func (c *Checker) resolveTypeReferenceMembers(t *Type) { + source := t.ParameterizedType().target + // Interface type construction guarantees thisType is stored just past type parameters + typeParameters := source.InterfaceType().typeParameters + typeParameters = typeParameters[:len(typeParameters)+1] + typeArguments := c.getTypeArguments(t) + paddedTypeArguments := typeArguments + if len(typeArguments) == len(typeParameters)-1 { + paddedTypeArguments = concatenate(typeArguments, []*Type{t}) + } + c.resolveObjectTypeMembers(t, source, typeParameters, paddedTypeArguments) +} + +func (c *Checker) resolveObjectTypeMembers(t *Type, source *Type, typeParameters []*Type, typeArguments []*Type) { + var mapper TypeMapper + var members SymbolTable + var callSignatures []*Signature + var constructSignatures []*Signature + var indexInfos []*IndexInfo + var instantiated bool + if slices.Equal(typeParameters, typeArguments) { + members = c.getMembersOfSymbol(source.symbol) + resolved := c.resolveDeclaredMembers(source) + callSignatures = resolved.declaredCallSignatures + constructSignatures = resolved.declaredConstructSignatures + indexInfos = resolved.declaredIndexInfos + } else { + instantiated = true + // !!! + // mapper = c.newTypeMapper(typeParameters, typeArguments) + // members = c.createInstantiatedSymbolTable(source.declaredProperties, mapper /*mappingThisOnly*/, typeParameters.length == 1) + // callSignatures = c.instantiateSignatures(source.declaredCallSignatures, mapper) + // constructSignatures = c.instantiateSignatures(source.declaredConstructSignatures, mapper) + // indexInfos = c.instantiateIndexInfos(source.declaredIndexInfos, mapper) + } + baseTypes := c.getBaseTypes(source) + if len(baseTypes) != 0 { + if !instantiated { + members = maps.Clone(members) + } + c.setStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos) + thisArgument := lastOrNil(typeArguments) + for _, baseType := range baseTypes { + instantiatedBaseType := baseType + if thisArgument != nil { + instantiatedBaseType = c.getTypeWithThisArgument(c.instantiateType(baseType, mapper), thisArgument, false /*needsApparentType*/) + } + c.addInheritedMembers(members, c.getPropertiesOfType(instantiatedBaseType)) + callSignatures = concatenate(callSignatures, c.getSignaturesOfType(instantiatedBaseType, SignatureKindCall)) + constructSignatures = concatenate(constructSignatures, c.getSignaturesOfType(instantiatedBaseType, SignatureKindConstruct)) + var inheritedIndexInfos []*IndexInfo + if instantiatedBaseType != c.anyType { + inheritedIndexInfos = c.getIndexInfosOfType(instantiatedBaseType) + } else { + inheritedIndexInfos = []*IndexInfo{&IndexInfo{keyType: c.stringType, valueType: c.anyType}} + } + indexInfos = concatenate(indexInfos, filter(inheritedIndexInfos, func(info *IndexInfo) bool { + return findIndexInfo(indexInfos, info.keyType) != nil + })) + } + } + c.setStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos) +} + +func findIndexInfo(indexInfos []*IndexInfo, keyType *Type) *IndexInfo { + return find(indexInfos, func(info *IndexInfo) bool { return info.keyType == keyType }) +} + +func (c *Checker) getBaseTypes(t *Type) []*Type { + return nil // !!! +} + +func (c *Checker) getTypeWithThisArgument(t *Type, thisArgument *Type, needApparentType bool) *Type { + if t.objectFlags&ObjectFlagsReference != 0 { + target := t.TypeReference().target + typeArguments := c.getTypeArguments(t) + if len(target.InterfaceType().typeParameters) == len(typeArguments) { + if thisArgument == nil { + thisArgument = target.InterfaceType().thisType + } + return c.createTypeReference(target, concatenate(typeArguments, []*Type{thisArgument})) + } + return t + } else if t.flags&TypeFlagsIntersection != 0 { + types, same := sameMap(t.IntersectionType().types, func(t *Type) *Type { return c.getTypeWithThisArgument(t, thisArgument, needApparentType) }) + if same { + return t + } + return c.getIntersectionType(types...) + } + if needApparentType { + return c.getApparentType(t) + } + return t +} + +func (c *Checker) addInheritedMembers(symbols SymbolTable, baseSymbols []*Symbol) { + for _, base := range baseSymbols { + if !isStaticPrivateIdentifierProperty(base) { + if _, ok := symbols[base.name]; !ok { + symbols[base.name] = base + } + } + } +} + +func (c *Checker) resolveDeclaredMembers(t *Type) *InterfaceTypeData { + d := t.InterfaceType() + if !d.declaredMembersResolved { + d.declaredMembersResolved = true + members := c.getMembersOfSymbol(t.symbol) + d.declaredCallSignatures = c.getSignaturesOfSymbol(members[InternalSymbolNameCall]) + d.declaredConstructSignatures = c.getSignaturesOfSymbol(members[InternalSymbolNameNew]) + d.declaredIndexInfos = c.getIndexInfosOfSymbol(t.symbol) + } + return d +} + +func (c *Checker) getIndexInfosOfSymbol(symbol *Symbol) []*IndexInfo { + indexSymbol := c.getIndexSymbol(symbol) + if indexSymbol != nil { + return c.getIndexInfosOfIndexSymbol(indexSymbol, slices.Collect(maps.Values(c.getMembersOfSymbol(symbol)))) + } + return nil +} + +func (c *Checker) getIndexInfosOfIndexSymbol(indexSymbol *Symbol, siblingSymbols []*Symbol) []*IndexInfo { + return nil // !!! +} + +func (c *Checker) getIndexSymbol(symbol *Symbol) *Symbol { + return c.getMembersOfSymbol(symbol)[InternalSymbolNameIndex] +} + +func (c *Checker) getSignaturesOfSymbol(symbol *Symbol) []*Signature { + return nil +} + +func (c *Checker) resolveAnonymousTypeMembers(t *Type) { + d := t.AnonymousType() + if d.target != nil { + c.setStructuredTypeMembers(t, nil, nil, nil, nil) + // members := c.createInstantiatedSymbolTable(c.getPropertiesOfObjectType(t.target), t.mapper /*mappingThisOnly*/, false) + // callSignatures := c.instantiateSignatures(c.getSignaturesOfType(t.target, SignatureKindCall), t.mapper) + // constructSignatures := c.instantiateSignatures(c.getSignaturesOfType(t.target, SignatureKindConstruct), t.mapper) + // indexInfos := c.instantiateIndexInfos(c.getIndexInfosOfType(t.target), t.mapper) + // c.setStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos) + return + } + symbol := c.getMergedSymbol(t.symbol) + if symbol.flags&SymbolFlagsTypeLiteral != 0 { + c.setStructuredTypeMembers(t, nil, nil, nil, nil) + members := c.getMembersOfSymbol(symbol) + callSignatures := c.getSignaturesOfSymbol(members[InternalSymbolNameCall]) + constructSignatures := c.getSignaturesOfSymbol(members[InternalSymbolNameNew]) + indexInfos := c.getIndexInfosOfSymbol(symbol) + c.setStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos) + return + } + // Combinations of function, class, enum and module + members := c.getExportsOfSymbol(symbol) + var indexInfos []*IndexInfo + if symbol == c.globalThisSymbol { + varsOnly := make(SymbolTable) + for _, p := range members { + if p.flags&SymbolFlagsBlockScoped == 0 && !(p.flags&SymbolFlagsValueModule != 0 && len(p.declarations) != 0 && every(p.declarations, isAmbientModule)) { + varsOnly[p.name] = p + } + } + members = varsOnly + } + var baseConstructorIndexInfo *IndexInfo + c.setStructuredTypeMembers(t, members, nil, nil, nil) + if symbol.flags&SymbolFlagsClass != 0 { + classType := c.getDeclaredTypeOfClassOrInterface(symbol) + baseConstructorType := c.getBaseConstructorTypeOfClass(classType) + if baseConstructorType.flags&(TypeFlagsObject|TypeFlagsIntersection|TypeFlagsTypeVariable) != 0 { + members = maps.Clone(members) + c.addInheritedMembers(members, c.getPropertiesOfType(baseConstructorType)) + c.setStructuredTypeMembers(t, members, nil, nil, nil) + } else if baseConstructorType == c.anyType { + baseConstructorIndexInfo = &IndexInfo{keyType: c.stringType, valueType: c.anyType} + } + } + indexSymbol := members[InternalSymbolNameIndex] + if indexSymbol != nil { + indexInfos = c.getIndexInfosOfIndexSymbol(indexSymbol, slices.Collect(maps.Values(members))) + } else { + if baseConstructorIndexInfo != nil { + indexInfos = append(indexInfos, baseConstructorIndexInfo) + } + if symbol.flags&SymbolFlagsEnum != 0 && (c.getDeclaredTypeOfSymbol(symbol).flags&TypeFlagsEnum != 0 || some(d.properties, func(prop *Symbol) bool { + return c.getTypeOfSymbol(prop).flags&TypeFlagsNumberLike != 0 + })) { + indexInfos = append(indexInfos, c.enumNumberIndexInfo) + } + } + d.indexInfos = indexInfos + // We resolve the members before computing the signatures because a signature may use + // typeof with a qualified name expression that circularly references the type we are + // in the process of resolving (see issue #6072). The temporarily empty signature list + // will never be observed because a qualified name can't reference signatures. + if symbol.flags&(SymbolFlagsFunction|SymbolFlagsMethod) != 0 { + d.callSignatures = c.getSignaturesOfSymbol(symbol) + } + // And likewise for construct signatures for classes + if symbol.flags&SymbolFlagsClass != 0 { + classType := c.getDeclaredTypeOfClassOrInterface(symbol) + constructSignatures := c.getSignaturesOfSymbol(symbol.members[InternalSymbolNameConstructor]) + if len(constructSignatures) == 0 { + constructSignatures = c.getDefaultConstructSignatures(classType) + } + d.constructSignatures = constructSignatures + } +} + +func (c *Checker) getDefaultConstructSignatures(classType *Type) []*Signature { + return nil // !!! + // baseConstructorType := c.getBaseConstructorTypeOfClass(classType) + // baseSignatures := c.getSignaturesOfType(baseConstructorType, SignatureKindConstruct) + // declaration := getClassLikeDeclarationOfSymbol(classType.symbol) + // isAbstract := declaration != nil && hasSyntacticModifier(declaration, ModifierFlagsAbstract) + // if len(baseSignatures) == 0 { + // return []*Signature{c.createSignature(nil, classType.localTypeParameters /*thisParameter*/, nil, emptyArray, classType /*resolvedTypePredicate*/, nil, 0, ifelse(isAbstract, SignatureFlagsAbstract, SignatureFlagsNone))} + // } + // baseTypeNode := c.getBaseTypeNodeOfClass(classType) + // isJavaScript := isInJSFile(baseTypeNode) + // typeArguments := c.typeArgumentsFromTypeReferenceNode(baseTypeNode) + // typeArgCount := length(typeArguments) + // var result []Signature = []never{} + // for _, baseSig := range baseSignatures { + // minTypeArgumentCount := c.getMinTypeArgumentCount(baseSig.typeParameters) + // typeParamCount := length(baseSig.typeParameters) + // if isJavaScript || typeArgCount >= minTypeArgumentCount && typeArgCount <= typeParamCount { + // var sig Signature + // if typeParamCount { + // sig = c.createSignatureInstantiation(baseSig, c.fillMissingTypeArguments(typeArguments, baseSig.typeParameters, minTypeArgumentCount, isJavaScript)) + // } else { + // sig = c.cloneSignature(baseSig) + // } + // sig.typeParameters = classType.localTypeParameters + // sig.resolvedReturnType = classType + // if isAbstract { + // sig.flags = sig.flags | SignatureFlagsAbstract + // } else { + // sig.flags = sig.flags & ~SignatureFlagsAbstract + // } + // result.push(sig) + // } + // } + // return result +} + +func (c *Checker) resolveMappedTypeMembers(t *Type) { + // !!! + c.setStructuredTypeMembers(t, nil, nil, nil, nil) +} + +func (c *Checker) resolveReverseMappedTypeMembers(t *Type) { + // !!! + c.setStructuredTypeMembers(t, nil, nil, nil, nil) +} + +func (c *Checker) resolveUnionTypeMembers(t *Type) { + // !!! + c.setStructuredTypeMembers(t, nil, nil, nil, nil) +} + +func (c *Checker) resolveIntersectionTypeMembers(t *Type) { + // !!! + c.setStructuredTypeMembers(t, nil, nil, nil, nil) +} + +/** + * If the given type is an object type and that type has a property by the given name, + * return the symbol for that property. Otherwise return undefined. + */ +func (c *Checker) getPropertyOfObjectType(t *Type, name string) *Symbol { + if t.flags&TypeFlagsObject != 0 { + resolved := c.resolveStructuredTypeMembers(t) + symbol := resolved.members[name] + if symbol != nil && c.symbolIsValue(symbol) { + return symbol + } + } + return nil +} + +func (c *Checker) getPropertyOfUnionOrIntersectionType(t *Type, name string, skipObjectFunctionPropertyAugment bool) *Symbol { + return nil // !!! +} + +/** + * For a type parameter, return the base constraint of the type parameter. For the string, number, + * boolean, and symbol primitive types, return the corresponding object types. Otherwise return the + * type itself. + */ +func (c *Checker) getApparentType(typ *Type) *Type { + return typ + // !!! + // t := typ + // if t.flags&TypeFlagsInstantiable != 0 { + // t = c.getBaseConstraintOfType(t) + // if t == nil { + // t = c.unknownType + // } + // } + // data := t.TypeData() + // switch { + // case data.objectFlags&ObjectFlagsMapped != 0: + // return c.getApparentTypeOfMappedType(t.(*MappedType)) + // case data.objectFlags&ObjectFlagsReference != 0 && t != typ: + // return c.getTypeWithThisArgument(t, typ) + // case data.flags&TypeFlagsIntersection != 0: + // return c.getApparentTypeOfIntersectionType(t.(*IntersectionType), typ) + // case data.flags&TypeFlagsStringLike != 0: + // return c.globalStringType + // case data.flags&TypeFlagsNumberLike != 0: + // return c.globalNumberType + // case data.flags&TypeFlagsBigIntLike != 0: + // return c.getGlobalBigIntType() + // case data.flags&TypeFlagsBooleanLike != 0: + // return c.globalBooleanType + // case data.flags&TypeFlagsESSymbolLike != 0: + // return c.getGlobalESSymbolType() + // case data.flags&TypeFlagsNonPrimitive != 0: + // return c.emptyObjectType + // case data.flags&TypeFlagsIndex != 0: + // return c.stringNumberSymbolType + // case data.flags&TypeFlagsUnknown != 0 && !c.strictNullChecks: + // return c.emptyObjectType + // default: + // return t + // } +} + +func (c *Checker) getReducedType(t *Type) *Type { + return t // !!! +} + +func (c *Checker) getReducedApparentType(t *Type) *Type { + // Since getApparentType may return a non-reduced union or intersection type, we need to perform + // type reduction both before and after obtaining the apparent type. For example, given a type parameter + // 'T extends A | B', the type 'T & X' becomes 'A & X | B & X' after obtaining the apparent type, and + // that type may need further reduction to remove empty intersections. + return c.getReducedType(c.getApparentType(c.getReducedType(t))) +} + +func (c *Checker) getTypeArguments(t *Type) []*Type { + d := t.ParameterizedType() + if d.resolvedTypeArguments == nil { + dt := d.target.InterfaceType() + if !c.pushTypeResolution(t, TypeSystemPropertyNameResolvedTypeArguments) { + return slices.Repeat([]*Type{c.errorType}, len(dt.outerTypeParameters)+len(dt.localTypeParameters)) + } + var typeArguments []*Type + node := t.TypeReference().node + if node != nil { + switch node.kind { + case SyntaxKindTypeReference: + typeArguments = concatenate(dt.outerTypeParameters, c.getEffectiveTypeArguments(node.AsTypeReference().typeArguments, dt.localTypeParameters)) + case SyntaxKindArrayType: + typeArguments = []*Type{c.getTypeFromTypeNode(node.AsArrayTypeNode().elementType)} + case SyntaxKindTupleType: + typeArguments = mapf(node.AsTupleTypeNode().elements, c.getTypeFromTypeNode) + default: + panic("Unhandled case in getTypeArguments") + } + } + if c.popTypeResolution() { + if d.resolvedTypeArguments == nil { + d.resolvedTypeArguments = c.instantiateTypes(typeArguments, d.mapper) + } + } else { + if d.resolvedTypeArguments == nil { + d.resolvedTypeArguments = slices.Repeat([]*Type{c.errorType}, len(dt.outerTypeParameters)+len(dt.localTypeParameters)) + } + errorNode := ifElse(node != nil, node, c.currentNode) + if d.target.symbol != nil { + c.error(errorNode, diagnostics.Type_arguments_for_0_circularly_reference_themselves, c.symbolToString(d.target.symbol)) + } else { + c.error(errorNode, diagnostics.Tuple_type_arguments_circularly_reference_themselves) + } + } + } + return d.resolvedTypeArguments +} + +func (c *Checker) getEffectiveTypeArguments(node *Node, typeParameters []*Type) []*Type { + return c.fillMissingTypeArguments(mapf(node.AsTypeArgumentList().arguments, c.getTypeFromTypeNode), typeParameters, c.getMinTypeArgumentCount(typeParameters)) +} + +/** + * Gets the minimum number of type arguments needed to satisfy all non-optional type + * parameters. + */ +func (c *Checker) getMinTypeArgumentCount(typeParameters []*Type) int { + minTypeArgumentCount := 0 + for i, typeParameter := range typeParameters { + if !c.hasTypeParameterDefault(typeParameter) { + minTypeArgumentCount = i + 1 + } + } + return minTypeArgumentCount +} + +func (c *Checker) hasTypeParameterDefault(t *Type) bool { + return t.symbol != nil && some(t.symbol.declarations, func(d *Node) bool { + return isTypeParameterDeclaration(d) && d.AsTypeParameter().defaultType != nil + }) +} + +func (c *Checker) fillMissingTypeArguments(typeArguments []*Type, typeParameters []*Type, minTypeArgumentCount int) []*Type { + numTypeParameters := len(typeParameters) + if numTypeParameters == 0 { + return nil + } + numTypeArguments := len(typeArguments) + if numTypeArguments >= minTypeArgumentCount && numTypeArguments < numTypeParameters { + result := make([]*Type, numTypeParameters) + copy(result, typeArguments) + // Map invalid forward references in default types to the error type + for i := numTypeArguments; i < numTypeParameters; i++ { + result[i] = c.errorType + } + for i := numTypeArguments; i < numTypeParameters; i++ { + defaultType := c.getDefaultFromTypeParameter(typeParameters[i]) + if defaultType != nil { + result[i] = c.instantiateType(defaultType, c.newTypeMapper(typeParameters, result)) + } else { + result[i] = c.unknownType + } + } + return result + } + return typeArguments +} + +func (c *Checker) getDefaultFromTypeParameter(t *Type) *Type { + return c.unknownType // !!! +} + +func (c *Checker) getNamedMembers(members SymbolTable) []*Symbol { + var result []*Symbol + for id, symbol := range members { + if c.isNamedMember(symbol, id) { + result = append(result, symbol) + } + } + return result +} + +func (c *Checker) isNamedMember(symbol *Symbol, id string) bool { + return !isReservedMemberName(id) && c.symbolIsValue(symbol) +} + +func isReservedMemberName(name string) bool { + return len(name) >= 1 && name[0] == 0xFE +} + +func (c *Checker) symbolIsValue(symbol *Symbol) bool { + return c.symbolIsValueEx(symbol, false /*includeTyoeOnlyMembers*/) +} + +func (c *Checker) symbolIsValueEx(symbol *Symbol, includeTypeOnlyMembers bool) bool { + return symbol.flags&SymbolFlagsValue != 0 || symbol.flags&SymbolFlagsAlias != 0 && + c.getSymbolFlagsEx(symbol, !includeTypeOnlyMembers, false /*excludeLocalMeanings*/)&SymbolFlagsValue != 0 +} + +func (c *Checker) instantiateType(t *Type, m TypeMapper) *Type { + if m == nil { + return t + } + return t // !!! +} + +func (c *Checker) instantiateTypes(types []*Type, m TypeMapper) []*Type { + if m == nil { + return types + } + return types // !!! +} + +func (c *Checker) tryGetTypeFromEffectiveTypeNode(node *Node) *Type { + typeNode := getEffectiveTypeAnnotationNode(node) + if typeNode != nil { + return c.getTypeFromTypeNode(typeNode) + } + return nil +} + +func (c *Checker) getTypeFromTypeNode(node *Node) *Type { + return c.getConditionalFlowTypeOfType(c.getTypeFromTypeNodeWorker(node), node) +} + +func (c *Checker) getTypeFromTypeNodeWorker(node *Node) *Type { + switch node.kind { + case SyntaxKindAnyKeyword: + return c.anyType + case SyntaxKindUnknownKeyword: + return c.unknownType + case SyntaxKindStringKeyword: + return c.stringType + case SyntaxKindNumberKeyword: + return c.numberType + case SyntaxKindBigIntKeyword: + return c.bigintType + case SyntaxKindBooleanKeyword: + return c.booleanType + case SyntaxKindSymbolKeyword: + return c.esSymbolType + case SyntaxKindVoidKeyword: + return c.voidType + case SyntaxKindUndefinedKeyword: + return c.undefinedType + case SyntaxKindNullKeyword: + return c.nullType + case SyntaxKindNeverKeyword: + return c.neverType + case SyntaxKindObjectKeyword: + if node.flags&NodeFlagsJavaScriptFile != 0 && !c.noImplicitAny { + return c.anyType + } else { + return c.nonPrimitiveType + } + case SyntaxKindIntrinsicKeyword: + return c.intrinsicMarkerType + case SyntaxKindThisType, SyntaxKindThisKeyword: + return c.getTypeFromThisTypeNode(node) + case SyntaxKindLiteralType: + return c.getTypeFromLiteralTypeNode(node) + case SyntaxKindTypeReference: + return c.getTypeFromTypeReference(node) + // case SyntaxKindTypePredicate: + // if (node /* as TypePredicateNode */).assertsModifier { + // return c.voidType + // } else { + // return c.booleanType + // } + // case SyntaxKindExpressionWithTypeArguments: + // return c.getTypeFromTypeReference(node /* as ExpressionWithTypeArguments */) + // case SyntaxKindTypeQuery: + // return c.getTypeFromTypeQueryNode(node /* as TypeQueryNode */) + // case SyntaxKindArrayType, SyntaxKindTupleType: + // return c.getTypeFromArrayOrTupleTypeNode(node /* as ArrayTypeNode | TupleTypeNode */) + // case SyntaxKindOptionalType: + // return c.getTypeFromOptionalTypeNode(node /* as OptionalTypeNode */) + // case SyntaxKindUnionType: + // return c.getTypeFromUnionTypeNode(node /* as UnionTypeNode */) + // case SyntaxKindIntersectionType: + // return c.getTypeFromIntersectionTypeNode(node /* as IntersectionTypeNode */) + // case SyntaxKindNamedTupleMember: + // return c.getTypeFromNamedTupleTypeNode(node /* as NamedTupleMember */) + // case SyntaxKindParenthesizedType: + // return c.getTypeFromTypeNode((node /* as ParenthesizedTypeNode | NamedTupleMember */).type_) + // case SyntaxKindRestType: + // return c.getTypeFromRestTypeNode(node /* as RestTypeNode */) + case SyntaxKindFunctionType, SyntaxKindConstructorType, SyntaxKindTypeLiteral: + return c.getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node) + // case SyntaxKindTypeOperator: + // return c.getTypeFromTypeOperatorNode(node /* as TypeOperatorNode */) + // case SyntaxKindIndexedAccessType: + // return c.getTypeFromIndexedAccessTypeNode(node /* as IndexedAccessTypeNode */) + // case SyntaxKindMappedType: + // return c.getTypeFromMappedTypeNode(node /* as MappedTypeNode */) + // case SyntaxKindConditionalType: + // return c.getTypeFromConditionalTypeNode(node /* as ConditionalTypeNode */) + // case SyntaxKindInferType: + // return c.getTypeFromInferTypeNode(node /* as InferTypeNode */) + // case SyntaxKindTemplateLiteralType: + // return c.getTypeFromTemplateTypeNode(node /* as TemplateLiteralTypeNode */) + // case SyntaxKindImportType: + // return c.getTypeFromImportTypeNode(node /* as ImportTypeNode */) + // case SyntaxKindIdentifier, /* as TypeNodeSyntaxKind */ + // SyntaxKindQualifiedName, /* as TypeNodeSyntaxKind */ + // SyntaxKindPropertyAccessExpression /* as TypeNodeSyntaxKind */ : + // symbol := c.getSymbolAtLocation(node) + // if symbol { + // return c.getDeclaredTypeOfSymbol(symbol) + // } else { + // return c.errorType + // } + default: + return c.errorType + } +} + +func (c *Checker) getTypeFromThisTypeNode(node *Node) *Type { + links := c.nodeLinks.get(node) + if links.resolvedType == nil { + links.resolvedType = c.getThisType(node) + } + return links.resolvedType +} + +func (c *Checker) getThisType(node *Node) *Type { + return c.anyType // !!! +} + +func (c *Checker) getTypeFromLiteralTypeNode(node *Node) *Type { + if node.AsLiteralTypeNode().literal.kind == SyntaxKindNullKeyword { + return c.nullType + } + links := c.nodeLinks.get(node) + if links.resolvedType == nil { + links.resolvedType = c.getRegularTypeOfLiteralType(c.checkExpression(node.AsLiteralTypeNode().literal)) + } + return links.resolvedType +} + +func (c *Checker) getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node *Node) *Type { + links := c.nodeLinks.get(node) + if links.resolvedType == nil { + // Deferred resolution of members is handled by resolveObjectTypeMembers + alias := c.getAliasForTypeNode(node) + if len(c.getMembersOfSymbol(node.Symbol())) == 0 && alias == nil { + links.resolvedType = c.emptyTypeLiteralType + } else { + t := c.newObjectType(ObjectFlagsAnonymous, node.Symbol()) + t.alias = alias + links.resolvedType = t + } + } + return links.resolvedType +} + +func (c *Checker) getTypeFromTypeReference(node *Node) *Type { + links := c.nodeLinks.get(node) + if links.resolvedType == nil { + // handle LS queries on the `const` in `x as const` by resolving to the type of `x` + if isConstTypeReference(node) && isAssertionExpression(node.parent) { + links.resolvedSymbol = c.unknownSymbol + links.resolvedType = c.checkExpressionCached(getAccessedExpression(node.parent)) + return links.resolvedType + } + symbol := c.resolveTypeReferenceName(node, SymbolFlagsType, false /*ignoreErrors*/) + t := c.getTypeReferenceType(node, symbol) + // Cache both the resolved symbol and the resolved type. The resolved symbol is needed when we check the + // type reference in checkTypeReferenceNode. + links.resolvedSymbol = symbol + links.resolvedType = t + } + return links.resolvedType +} + +func (c *Checker) resolveTypeReferenceName(typeReference *Node, meaning SymbolFlags, ignoreErrors bool) *Symbol { + name := getTypeReferenceName(typeReference) + if name == nil { + return c.unknownSymbol + } + symbol := c.resolveEntityName(name, meaning, ignoreErrors, false /*dontResolveAlias*/, nil /*location*/) + if symbol != nil && symbol != c.unknownSymbol { + return symbol + } + if ignoreErrors { + return c.unknownSymbol + } + return c.unknownSymbol // !!! return c.getUnresolvedSymbolForEntityName(name) +} + +func (c *Checker) getTypeReferenceType(node *Node, symbol *Symbol) *Type { + if symbol == c.unknownSymbol { + return c.errorType + } + if symbol.flags&(SymbolFlagsClass|SymbolFlagsInterface) != 0 { + return c.getTypeFromClassOrInterfaceReference(node, symbol) + } + if symbol.flags&SymbolFlagsTypeAlias != 0 { + return c.getTypeFromTypeAliasReference(node, symbol) + } + // Get type from reference to named type that cannot be generic (enum or type parameter) + res := c.tryGetDeclaredTypeOfSymbol(symbol) + if res != nil && c.checkNoTypeArguments(node, symbol) { + return c.getRegularTypeOfLiteralType(res) + } + return c.errorType +} + +/** + * Get type from type-reference that reference to class or interface + */ +func (c *Checker) getTypeFromClassOrInterfaceReference(node *Node, symbol *Symbol) *Type { + t := c.getDeclaredTypeOfClassOrInterface(c.getMergedSymbol(symbol)) + d := t.InterfaceType() + typeParameters := d.localTypeParameters + if len(typeParameters) != 0 { + typeArgumentNodes := c.getTypeArgumentsFromNode(node) + numTypeArguments := len(typeArgumentNodes) + minTypeArgumentCount := c.getMinTypeArgumentCount(typeParameters) + if numTypeArguments < minTypeArgumentCount || numTypeArguments > len(typeParameters) { + message := diagnostics.Generic_type_0_requires_1_type_argument_s + if minTypeArgumentCount < len(typeParameters) { + message = diagnostics.Generic_type_0_requires_between_1_and_2_type_arguments + } + typeStr := c.typeToString(t) // !!! /*enclosingDeclaration*/, nil, TypeFormatFlagsWriteArrayAsGenericType + c.error(node, message, typeStr, minTypeArgumentCount, len(typeParameters)) + // TODO: Adopt same permissive behavior in TS as in JS to reduce follow-on editing experience failures (requires editing fillMissingTypeArguments) + return c.errorType + } + if node.kind == SyntaxKindTypeReference && c.isDeferredTypeReferenceNode(node, numTypeArguments != len(typeParameters)) { + return c.createDeferredTypeReference(t, node, nil /*mapper*/, nil /*alias*/) + } + // In a type reference, the outer type parameters of the referenced class or interface are automatically + // supplied as type arguments and the type reference only specifies arguments for the local type parameters + // of the class or interface. + localTypeArguments := c.fillMissingTypeArguments(mapf(typeArgumentNodes, c.getTypeFromTypeNode), typeParameters, minTypeArgumentCount) + typeArguments := concatenate(d.outerTypeParameters, localTypeArguments) + return c.createTypeReference(t, typeArguments) + } + if c.checkNoTypeArguments(node, symbol) { + return t + } + return c.errorType +} + +func (c *Checker) getTypeArgumentsFromNode(node *Node) []*Node { + typeArgumentList := c.getTypeArgumentListFromNode(node) + if typeArgumentList != nil { + return typeArgumentList.AsTypeArgumentList().arguments + } + return nil +} + +func (c *Checker) getTypeArgumentListFromNode(node *Node) *Node { + switch node.kind { + case SyntaxKindTypeReference: + return node.AsTypeReference().typeArguments + case SyntaxKindExpressionWithTypeArguments: + return node.AsExpressionWithTypeArguments().typeArguments + case SyntaxKindImportType: + return node.AsImportTypeNode().typeArguments + } + panic("Unhandled case in getTypeArgumentListFromNode") +} + +func (c *Checker) checkNoTypeArguments(node *Node, symbol *Symbol) bool { + if len(c.getTypeArgumentsFromNode(node)) != 0 { + c.error(node, diagnostics.Type_0_is_not_generic, c.symbolToString(symbol)) + return false + } + return true +} + +func (c *Checker) isDeferredTypeReferenceNode(node *Node, hasDefaultTypeArguments bool) bool { + return false // !!! +} + +func (c *Checker) getTypeFromTypeAliasReference(node *Node, symbol *Symbol) *Type { + return c.anyType // !!! +} + +func (c *Checker) getDeclaredTypeOfSymbol(symbol *Symbol) *Type { + result := c.tryGetDeclaredTypeOfSymbol(symbol) + if result == nil { + result = c.errorType + } + return result +} + +func (c *Checker) tryGetDeclaredTypeOfSymbol(symbol *Symbol) *Type { + switch { + case symbol.flags&(SymbolFlagsClass|SymbolFlagsInterface) != 0: + return c.getDeclaredTypeOfClassOrInterface(symbol) + case symbol.flags&SymbolFlagsTypeParameter != 0: + return c.getDeclaredTypeOfTypeParameter(symbol) + // !!! + // case symbol.flags&SymbolFlagsTypeAlias != 0: + // return c.getDeclaredTypeOfTypeAlias(symbol) + // case symbol.flags&SymbolFlagsEnum != 0: + // return c.getDeclaredTypeOfEnum(symbol) + // case symbol.flags&SymbolFlagsEnumMember != 0: + // return c.getDeclaredTypeOfEnumMember(symbol) + // case symbol.flags&SymbolFlagsAlias != 0: + // return c.getDeclaredTypeOfAlias(symbol) + } + return nil +} + +func getTypeReferenceName(node *Node) *Node { + switch node.kind { + case SyntaxKindTypeReference: + return node.AsTypeReference().typeName + case SyntaxKindExpressionWithTypeArguments: + // We only support expressions that are simple qualified names. For other + // expressions this produces nil + expr := node.AsExpressionWithTypeArguments().expression + if isEntityNameExpression(expr) { + return expr + } + } + return nil +} + +func (c *Checker) getAliasForTypeNode(node *Node) *TypeAlias { + symbol := c.getAliasSymbolForTypeNode(node) + if symbol != nil { + return &TypeAlias{symbol: symbol, typeArguments: c.getTypeArgumentsForAliasSymbol(symbol)} + } + return nil +} + +func (c *Checker) getAliasSymbolForTypeNode(node *Node) *Symbol { + host := node.parent + for isParenthesizedTypeNode(host) || isTypeOperatorNode(host) && host.AsTypeOperatorNode().operator == SyntaxKindReadonlyKeyword { + host = host.parent + } + if isTypeAlias(host) { + return c.getSymbolOfDeclaration(host) + } + return nil +} + +func (c *Checker) getTypeArgumentsForAliasSymbol(symbol *Symbol) []*Type { + if symbol != nil { + return c.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol) + } + return nil +} + +func (c *Checker) getOuterTypeParametersOfClassOrInterface(symbol *Symbol) []*Type { + return nil // !!! +} + +// The local type parameters are the combined set of type parameters from all declarations of the class, +// interface, or type alias. +func (c *Checker) getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol *Symbol) []*Type { + var result []*Type + for _, node := range symbol.declarations { + if nodeKindIs(node, SyntaxKindInterfaceDeclaration, SyntaxKindClassDeclaration, SyntaxKindClassExpression) || isTypeAlias(node) { + result = c.appendTypeParameters(result, getEffectiveTypeParameterDeclarations(node)) + } + } + return result +} + +// Appends the type parameters given by a list of declarations to a set of type parameters and returns the resulting set. +// The function allocates a new array if the input type parameter set is undefined, but otherwise it modifies the set +// in-place and returns the same array. +func (c *Checker) appendTypeParameters(typeParameters []*Type, declarations []*Node) []*Type { + for _, declaration := range declarations { + typeParameters = appendIfUnique(typeParameters, c.getDeclaredTypeOfTypeParameter(c.getSymbolOfDeclaration(declaration))) + } + return typeParameters +} + +func (c *Checker) getDeclaredTypeOfTypeParameter(symbol *Symbol) *Type { + links := c.typeParameterLinks.get(symbol) + if links.declaredType == nil { + links.declaredType = c.newTypeParameter(symbol) + } + return links.declaredType +} + +func (c *Checker) getConditionalFlowTypeOfType(typ *Type, node *Node) *Type { + return typ // !!! +} + +func (c *Checker) newType(flags TypeFlags, objectFlags ObjectFlags, data TypeData) *Type { + t := &Type{} + c.typeCount++ + t.flags = flags + t.objectFlags = objectFlags + t.id = TypeId(c.typeCount) + t.data = data + return t +} + +func (c *Checker) newIntrinsicType(flags TypeFlags, intrinsicName string) *Type { + return c.newIntrinsicTypeEx(flags, intrinsicName, ObjectFlagsNone) +} + +func (c *Checker) newIntrinsicTypeEx(flags TypeFlags, intrinsicName string, objectFlags ObjectFlags) *Type { + data := &IntrinsicTypeData{} + data.intrinsicName = intrinsicName + return c.newType(flags, objectFlags, data) +} + +func (c *Checker) createWideningType(nonWideningType *Type) *Type { + if c.strictNullChecks { + return nonWideningType + } + return c.newIntrinsicType(nonWideningType.flags, nonWideningType.IntrinsicType().intrinsicName) +} + +func (c *Checker) newLiteralType(flags TypeFlags, value any, regularType *Type) *Type { + data := &LiteralTypeData{} + data.value = value + t := c.newType(flags, ObjectFlagsNone, data) + if regularType != nil { + data.regularType = regularType + } else { + data.regularType = t + } + return t +} + +func (c *Checker) newObjectType(objectFlags ObjectFlags, symbol *Symbol) *Type { + t := c.newType(TypeFlagsObject, objectFlags, &AnonymousTypeData{}) + t.symbol = symbol + return t +} + +func (c *Checker) newAnonymousType(symbol *Symbol, members SymbolTable, callSignatures []*Signature, constructSignatures []*Signature, indexInfos []*IndexInfo) *Type { + t := c.newObjectType(ObjectFlagsAnonymous, symbol) + c.setStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos) + return t +} + +func (c *Checker) setStructuredTypeMembers(t *Type, members SymbolTable, callSignatures []*Signature, constructSignatures []*Signature, indexInfos []*IndexInfo) { + t.objectFlags |= ObjectFlagsMembersResolved + data := t.ObjectType() + data.members = members + data.properties = c.getNamedMembers(members) + data.callSignatures = callSignatures + data.constructSignatures = constructSignatures + data.indexInfos = indexInfos +} + +func (c *Checker) newInterfaceType(objectFlags ObjectFlags, symbol *Symbol) *Type { + t := c.newType(TypeFlagsObject, objectFlags, &InterfaceTypeData{}) + t.symbol = symbol + return t +} + +func (c *Checker) newTypeParameter(symbol *Symbol) *Type { + t := c.newType(TypeFlagsTypeParameter, ObjectFlagsNone, &TypeParameterData{}) + t.symbol = symbol + return t +} + +func (c *Checker) newTypeReference(target *Type) *Type { + data := &TypeReferenceData{} + data.target = target + t := c.newType(TypeFlagsObject, ObjectFlagsReference, data) + t.symbol = target.symbol + return t +} + +func (c *Checker) createTypeReference(target *Type, typeArguments []*Type) *Type { + id := getTypeListId(typeArguments) + dt := target.InterfaceType() + if t, ok := dt.instantiations[id]; ok { + return t + } + t := c.newTypeReference(target) + t.objectFlags |= c.getPropagatingFlagsOfTypes(typeArguments, TypeFlagsNone) + t.TypeReference().resolvedTypeArguments = typeArguments + dt.instantiations[id] = t + return t +} + +func (c *Checker) createDeferredTypeReference(target *Type, node *Node, mapper TypeMapper, alias *TypeAlias) *Type { + if alias == nil { + alias := c.getAliasForTypeNode(node) + if mapper != nil { + alias.typeArguments = c.instantiateTypes(alias.typeArguments, mapper) + } + } + t := c.newTypeReference(target) + t.alias = alias + data := t.TypeReference() + data.mapper = mapper + data.node = node + return t +} + +// This function is used to propagate certain flags when creating new object type references and union types. +// It is only necessary to do so if a constituent type might be the undefined type, the null type, the type +// of an object literal or a non-inferrable type. This is because there are operations in the type checker +// that care about the presence of such types at arbitrary depth in a containing type. +func (c *Checker) getPropagatingFlagsOfTypes(types []*Type, excludeKinds TypeFlags) ObjectFlags { + result := ObjectFlagsNone + for _, t := range types { + if t.flags&excludeKinds == 0 { + result |= t.objectFlags + } + } + return result & ObjectFlagsPropagatingFlags +} + +func (c *Checker) newUnionType(objectFlags ObjectFlags, types []*Type) *Type { + data := &UnionTypeData{} + data.types = types + return c.newType(TypeFlagsUnion, objectFlags, data) +} + +func (c *Checker) getRegularTypeOfLiteralType(t *Type) *Type { + if t.flags&TypeFlagsFreshable != 0 { + return t.LiteralType().regularType + } + if t.flags&TypeFlagsUnion != 0 { + u := t.UnionType() + if u.regularType == nil { + u.regularType = c.mapType(t, c.getRegularTypeOfLiteralType) + } + return u.regularType + } + return t +} + +func (c *Checker) getFreshTypeOfLiteralType(t *Type) *Type { + if data, ok := t.data.(*LiteralTypeData); ok { + if data.freshType == nil { + f := c.newLiteralType(t.flags, data.value, t) + f.LiteralType().freshType = f + data.freshType = f + } + return data.freshType + } + return t +} + +func (c *Checker) getStringLiteralType(value string) *Type { + t := c.stringLiteralTypes[value] + if t == nil { + t = c.newLiteralType(TypeFlagsStringLiteral, value, nil) + c.stringLiteralTypes[value] = t + } + return t +} + +func (c *Checker) getNumberLiteralType(value float64) *Type { + t := c.numberLiteralTypes[value] + if t == nil { + t = c.newLiteralType(TypeFlagsNumberLiteral, value, nil) + c.numberLiteralTypes[value] = t + } + return t +} + +func (c *Checker) getBigintLiteralType(value PseudoBigint) *Type { + t := c.bigintLiteralTypes[value] + if t == nil { + t = c.newLiteralType(TypeFlagsBigintLiteral, value, nil) + c.bigintLiteralTypes[value] = t + } + return t +} + +func (c *Checker) getEnumLiteralType(value any, enumSymbol *Symbol, symbol *Symbol) *Type { + var flags TypeFlags + switch value.(type) { + case string: + flags = TypeFlagsEnumLiteral | TypeFlagsStringLiteral + case float64: + flags = TypeFlagsEnumLiteral | TypeFlagsNumberLiteral + default: + panic("Unhandled case in getEnumLiteralType") + } + key := EnumLiteralKey{enumSymbol: enumSymbol, value: value} + t := c.enumLiteralTypes[key] + if t == nil { + t = c.newLiteralType(flags, value, nil) + t.symbol = symbol + c.enumLiteralTypes[key] = t + } + return t +} + +func (c *Checker) getBaseTypeOfLiteralType(t *Type) *Type { + switch { + case t.flags&TypeFlagsEnumLike != 0: + return c.getBaseTypeOfEnumLikeType(t) + case t.flags&(TypeFlagsStringLiteral|TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0: + return c.stringType + case t.flags&TypeFlagsNumberLiteral != 0: + return c.numberType + case t.flags&TypeFlagsBigintLiteral != 0: + return c.bigintType + case t.flags&TypeFlagsBooleanLiteral != 0: + return c.booleanType + case t.flags&TypeFlagsUnion != 0: + return c.getBaseTypeOfLiteralTypeUnion(t) + } + return t +} + +func (c *Checker) getBaseTypeOfEnumLikeType(t *Type) *Type { + if t.flags&TypeFlagsEnumLike != 0 && t.symbol.flags&SymbolFlagsEnumMember != 0 { + return c.getDeclaredTypeOfSymbol(c.getParentOfSymbol(t.symbol)) + } + return t +} + +func (c *Checker) getBaseTypeOfLiteralTypeUnion(t *Type) *Type { + key := CachedTypeKey{kind: CachedTypeKindLiteralUnionBaseType, typeId: t.id} + if cached, ok := c.cachedTypes[key]; ok { + return cached + } + result := c.mapType(t, c.getBaseTypeOfLiteralType) + c.cachedTypes[key] = result + return result +} + +func (c *Checker) getWidenedLiteralType(t *Type) *Type { + switch { + case t.flags&TypeFlagsEnumLike != 0 && isFreshLiteralType(t): + return c.getBaseTypeOfEnumLikeType(t) + case t.flags&TypeFlagsStringLiteral != 0 && isFreshLiteralType(t): + return c.stringType + case t.flags&TypeFlagsNumberLiteral != 0 && isFreshLiteralType(t): + return c.numberType + case t.flags&TypeFlagsBigintLiteral != 0 && isFreshLiteralType(t): + return c.bigintType + case t.flags&TypeFlagsBooleanLiteral != 0 && isFreshLiteralType(t): + return c.booleanType + case t.flags&TypeFlagsUnion != 0: + return c.mapType(t, c.getWidenedLiteralType) + } + return t +} + +func (c *Checker) getWidenedUniqueESSymbolType(t *Type) *Type { + switch { + case t.flags&TypeFlagsUniqueESSymbol != 0: + return c.esSymbolType + case t.flags&TypeFlagsUnion != 0: + return c.mapType(t, c.getWidenedUniqueESSymbolType) + } + return t +} + +func (c *Checker) mapType(t *Type, f func(*Type) *Type) *Type { + return c.mapTypeEx(t, f, false /*noReductions*/) +} + +func (c *Checker) mapTypeEx(t *Type, f func(*Type) *Type, noReductions bool) *Type { + if t.flags&TypeFlagsNever != 0 { + return t + } + if t.flags&TypeFlagsUnion == 0 { + return f(t) + } + u := t.UnionType() + types := u.types + if u.origin != nil && u.origin.flags&TypeFlagsUnion != 0 { + types = u.origin.UnionType().types + } + var mappedTypes []*Type + var changed bool + for _, s := range types { + var mapped *Type + if s.flags&TypeFlagsUnion != 0 { + mapped = c.mapTypeEx(s, f, noReductions) + } else { + mapped = f(s) + } + if mapped != s { + changed = true + } + if mapped != nil { + mappedTypes = append(mappedTypes, mapped) + } + } + if changed { + unionReduction := UnionReductionLiteral + if noReductions { + unionReduction = UnionReductionNone + } + return c.getUnionTypeEx(mappedTypes, unionReduction, nil /*alias*/, nil /*origin*/) + } + return t +} + +type UnionReduction int32 + +const ( + UnionReductionNone UnionReduction = iota + UnionReductionLiteral + UnionReductionSubtype +) + +func (c *Checker) getUnionType(types []*Type) *Type { + return c.getUnionTypeEx(types, UnionReductionLiteral, nil /*alias*/, nil /*origin*/) +} + +// We sort and deduplicate the constituent types based on object identity. If the subtypeReduction +// flag is specified we also reduce the constituent type set to only include types that aren't subtypes +// of other types. Subtype reduction is expensive for large union types and is possible only when union +// types are known not to circularly reference themselves (as is the case with union types created by +// expression constructs such as array literals and the || and ?: operators). Named types can +// circularly reference themselves and therefore cannot be subtype reduced during their declaration. +// For example, "type Item = string | (() => Item" is a named type that circularly references itself. +func (c *Checker) getUnionTypeEx(types []*Type, unionReduction UnionReduction, alias *TypeAlias, origin *Type) *Type { + if len(types) == 0 { + return c.neverType + } + if len(types) == 1 { + return types[0] + } + // We optimize for the common case of unioning a union type with some other type (such as `undefined`). + if len(types) == 2 && origin == nil && (types[0].flags&TypeFlagsUnion != 0 || types[1].flags&TypeFlagsUnion != 0) { + id1 := types[0].id + id2 := types[1].id + if id1 > id2 { + id1, id2 = id2, id1 + } + key := UnionOfUnionKey{id1: id1, id2: id2, r: unionReduction, a: getAliasId(alias)} + t := c.unionOfUnionTypes[key] + if t == nil { + t = c.getUnionTypeWorker(types, unionReduction, alias, nil /*origin*/) + c.unionOfUnionTypes[key] = t + } + return t + } + return c.getUnionTypeWorker(types, unionReduction, alias, origin) +} + +func (c *Checker) getUnionTypeWorker(types []*Type, unionReduction UnionReduction, alias *TypeAlias, origin *Type) *Type { + typeSet, includes := c.addTypesToUnion(nil, 0, types) + if unionReduction != UnionReductionNone { + if includes&TypeFlagsAnyOrUnknown != 0 { + if includes&TypeFlagsAny != 0 { + switch { + case includes&TypeFlagsIncludesWildcard != 0: + return c.wildcardType + case includes&TypeFlagsIncludesError != 0: + return c.errorType + } + return c.anyType + } + return c.unknownType + } + if includes&TypeFlagsUndefined != 0 { + // If type set contains both undefinedType and missingType, remove missingType + if len(typeSet) >= 2 && typeSet[0] == c.undefinedType && typeSet[1] == c.missingType { + typeSet = slices.Delete(typeSet, 1, 2) + } + } + if includes&(TypeFlagsEnum|TypeFlagsLiteral|TypeFlagsUniqueESSymbol|TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0 || + includes&TypeFlagsVoid != 0 && includes&TypeFlagsUndefined != 0 { + c.removeRedundantLiteralTypes(typeSet, includes, unionReduction&UnionReductionSubtype != 0) + } + if includes&TypeFlagsStringLiteral != 0 && includes&(TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0 { + c.removeStringLiteralsMatchedByTemplateLiterals(typeSet) + } + if includes&TypeFlagsIncludesConstrainedTypeVariable != 0 { + c.removeConstrainedTypeVariables(typeSet) + } + if unionReduction == UnionReductionSubtype { + typeSet = c.removeSubtypes(typeSet, includes&TypeFlagsObject != 0) + if typeSet == nil { + return c.errorType + } + } + if len(typeSet) == 0 { + switch { + case includes&TypeFlagsNull != 0: + if includes&TypeFlagsIncludesNonWideningType != 0 { + return c.nullType + } + return c.nullWideningType + case includes&TypeFlagsUndefined != 0: + if includes&TypeFlagsIncludesNonWideningType != 0 { + return c.undefinedType + } + return c.undefinedWideningType + } + return c.neverType + } + } + if origin == nil && includes&TypeFlagsUnion != 0 { + namedUnions := c.addNamedUnions(nil, types) + var reducedTypes []*Type + for _, t := range typeSet { + if !some(namedUnions, func(u *Type) bool { return containsType(u.UnionType().types, t) }) { + reducedTypes = append(reducedTypes, t) + } + } + if alias == nil && len(namedUnions) == 1 && len(reducedTypes) == 0 { + return namedUnions[0] + } + // We create a denormalized origin type only when the union was created from one or more named unions + // (unions with alias symbols or origins) and when there is no overlap between those named unions. + namedTypesCount := 0 + for _, u := range namedUnions { + namedTypesCount += len(u.UnionType().types) + } + if namedTypesCount+len(reducedTypes) == len(typeSet) { + for _, t := range namedUnions { + reducedTypes = insertType(reducedTypes, t) + } + // !!! Properly handle union vs. intersection origin + origin = c.newUnionType(ObjectFlagsNone, reducedTypes) + } + } + objectFlags := ifElse(includes&TypeFlagsNotPrimitiveUnion != 0, ObjectFlagsNone, ObjectFlagsPrimitiveUnion) | + ifElse(includes&TypeFlagsIntersection != 0, ObjectFlagsContainsIntersections, ObjectFlagsNone) + return c.getUnionTypeFromSortedList(typeSet, objectFlags, alias, origin) +} + +// This function assumes the constituent type list is sorted and deduplicated. +func (c *Checker) getUnionTypeFromSortedList(types []*Type, precomputedObjectFlags ObjectFlags, alias *TypeAlias, origin *Type) *Type { + if len(types) == 0 { + return c.neverType + } + if len(types) == 1 { + return types[0] + } + key := getUnionId(types, origin, alias) + t := c.unionTypes[key] + if t == nil { + t = c.newUnionType(precomputedObjectFlags|c.getPropagatingFlagsOfTypes(types, TypeFlagsNullable), types) + t.UnionType().origin = origin + t.alias = alias + if len(types) == 2 && types[0].flags&TypeFlagsBooleanLiteral != 0 && types[1].flags&TypeFlagsBooleanLiteral != 0 { + t.flags |= TypeFlagsBoolean + } + c.unionTypes[key] = t + } + return t +} + +func (c *Checker) addTypesToUnion(typeSet []*Type, includes TypeFlags, types []*Type) ([]*Type, TypeFlags) { + var lastType *Type + for _, t := range types { + if t != lastType { + if t.flags&TypeFlagsUnion != 0 { + u := t.UnionType() + if t.alias != nil || u.origin != nil { + includes |= TypeFlagsUnion + } + typeSet, includes = c.addTypesToUnion(typeSet, includes, u.types) + } else { + typeSet, includes = c.addTypeToUnion(typeSet, includes, t) + } + lastType = t + } + } + return typeSet, includes +} + +func (c *Checker) addTypeToUnion(typeSet []*Type, includes TypeFlags, t *Type) ([]*Type, TypeFlags) { + flags := t.flags + // We ignore 'never' types in unions + if flags&TypeFlagsNever == 0 { + includes |= flags & TypeFlagsIncludesMask + if flags&TypeFlagsInstantiable != 0 { + includes |= TypeFlagsIncludesInstantiable + } + if flags&TypeFlagsIntersection != 0 && t.objectFlags&ObjectFlagsIsConstrainedTypeVariable != 0 { + includes |= TypeFlagsIncludesConstrainedTypeVariable + } + if t == c.wildcardType { + includes |= TypeFlagsIncludesWildcard + } + if c.isErrorType(t) { + includes |= TypeFlagsIncludesError + } + if !c.strictNullChecks && flags&TypeFlagsNullable != 0 { + if t.objectFlags&ObjectFlagsContainsWideningType == 0 { + includes |= TypeFlagsIncludesNonWideningType + } + } else { + var index int + var ok bool + if len(typeSet) != 0 && t.id > typeSet[len(typeSet)-1].id { + index = len(typeSet) + } else { + index, ok = slices.BinarySearchFunc(typeSet, t, compareTypeIds) + } + if !ok { + typeSet = slices.Insert(typeSet, index, t) + } + } + } + return typeSet, includes +} + +func (c *Checker) addNamedUnions(namedUnions []*Type, types []*Type) []*Type { + for _, t := range types { + if t.flags&TypeFlagsUnion != 0 { + u := t.UnionType() + if t.alias != nil || u.origin != nil && u.origin.flags&TypeFlagsUnion == 0 { + namedUnions = appendIfUnique(namedUnions, t) + } else if u.origin != nil && u.origin.flags&TypeFlagsUnion != 0 { + namedUnions = c.addNamedUnions(namedUnions, u.origin.UnionType().types) + } + } + } + return namedUnions +} + +func (c *Checker) removeRedundantLiteralTypes(types []*Type, includes TypeFlags, reduceVoidUndefined bool) { + // !!! +} + +func (c *Checker) removeStringLiteralsMatchedByTemplateLiterals(types []*Type) { + // !!! +} + +func (c *Checker) removeConstrainedTypeVariables(types []*Type) { + // !!! +} + +func (c *Checker) removeSubtypes(types []*Type, hasObjectTypes bool) []*Type { + // !!! + return types +} + +func containsType(types []*Type, t *Type) bool { + _, ok := slices.BinarySearchFunc(types, t, compareTypeIds) + return ok +} + +func insertType(types []*Type, t *Type) []*Type { + if i, ok := slices.BinarySearchFunc(types, t, compareTypeIds); !ok { + return slices.Insert(types, i, t) + } + return types +} + +func (c *Checker) isErrorType(t *Type) bool { + // The only 'any' types that have alias symbols are those manufactured by getTypeFromTypeAliasReference for + // a reference to an unresolved symbol. We want those to behave like the errorType. + return t == c.errorType || t.flags&TypeFlagsAny != 0 && t.alias != nil +} + +func compareTypeIds(t1, t2 *Type) int { + return int(t1.id) - int(t2.id) +} + +func (c *Checker) getIntersectionType(types ...*Type) *Type { + return c.anyType // !!! +} + +func (c *Checker) newTypeMapper(sources []*Type, targets []*Type) TypeMapper { + if len(sources) == 1 { + return &SimpleTypeMapper{source: sources[0], target: ifElse(targets != nil, targets[0], c.anyType)} + } + return &ArrayTypeMapper{sources: sources, targets: targets} +} + +// SimpleTypeMapper + +type SimpleTypeMapper struct { + source *Type + target *Type +} + +func (m *SimpleTypeMapper) Map(t *Type) *Type { + if t == m.source { + return m.target + } + return t +} + +// ArrayTypeMapper + +type ArrayTypeMapper struct { + sources []*Type + targets []*Type +} + +func (m *ArrayTypeMapper) Map(t *Type) *Type { + for i, s := range m.sources { + if t == s { + return m.targets[i] + } + } + return t +} + +// ArrayToSingleTypeMapper + +type ArrayToSingleTypeMapper struct { + sources []*Type + target *Type +} + +func (m *ArrayToSingleTypeMapper) Map(t *Type) *Type { + for _, s := range m.sources { + if t == s { + return m.target + } + } + return t +} + +// DeferredTypeMapper + +type DeferredTypeMapper struct { + sources []*Type + targets []func() *Type +} + +func (m *DeferredTypeMapper) Map(t *Type) *Type { + for i, s := range m.sources { + if t == s { + return m.targets[i]() + } + } + return t +} + +// FunctionTypeMapper + +type FunctionTypeMapper struct { + fn func(*Type) *Type +} + +func (m *FunctionTypeMapper) Map(t *Type) *Type { + return m.fn(t) +} + +// MergedTypeMapper + +type MergedTypeMapper struct { + m1 TypeMapper + m2 TypeMapper +} + +func (m *MergedTypeMapper) Map(t *Type) *Type { + return m.m2.Map(m.m1.Map(t)) +} + +// CompositeTypeMapper + +type CompositeTypeMapper struct { + c *Checker + m1 TypeMapper + m2 TypeMapper +} + +func (m *CompositeTypeMapper) Map(t *Type) *Type { + t1 := m.m1.Map(t) + if t1 != t { + return m.c.instantiateType(t1, m.m2) + } + return m.m2.Map(t) +} diff --git a/internal/compiler/host.go b/internal/compiler/host.go new file mode 100644 index 0000000000..16eed46ae2 --- /dev/null +++ b/internal/compiler/host.go @@ -0,0 +1,116 @@ +package compiler + +import ( + "bytes" + "encoding/binary" + "fmt" + "io/fs" + "os" + "path/filepath" + "slices" + "strings" + "sync" + "unicode/utf16" +) + +type CompilerHost interface { + ReadFile(fileName string) (text string, ok bool) + ReadDirectory(rootPath string, extensions []string) []FileInfo + AbsFileName(fileName string) string + RunTask(func()) + WaitForTasks() +} + +type FileInfo struct { + Name string + Size int64 +} + +type compilerHost struct { + options *CompilerOptions + singleThreaded bool + wg sync.WaitGroup + readSema chan struct{} +} + +func NewCompilerHost(options *CompilerOptions, singleThreaded bool) CompilerHost { + h := &compilerHost{} + h.options = options + h.singleThreaded = singleThreaded + h.readSema = make(chan struct{}, 128) + return h +} + +func (h *compilerHost) ReadFile(fileName string) (text string, ok bool) { + h.readSema <- struct{}{} + b, err := os.ReadFile(fileName) + <-h.readSema + if err != nil { + return "", false + } + var bom [2]byte + if len(b) >= 2 { + bom = [2]byte{b[0], b[1]} + switch bom { + case [2]byte{0xFF, 0xFE}: + return decodeUtf16(b[2:], binary.LittleEndian), true + case [2]byte{0xFE, 0xFF}: + return decodeUtf16(b[2:], binary.BigEndian), true + } + } + if len(b) >= 3 && b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF { + b = b[3:] + } + return string(b), true +} + +func decodeUtf16(b []byte, order binary.ByteOrder) string { + ints := make([]uint16, len(b)/2) + if err := binary.Read(bytes.NewReader(b), order, &ints); err != nil { + return "" + } + return string(utf16.Decode(ints)) +} + +func (h *compilerHost) ReadDirectory(rootDir string, extensions []string) []FileInfo { + var fileInfos []FileInfo + filepath.Walk(rootDir, func(path string, info fs.FileInfo, err error) error { + if err != nil { + fmt.Println(err) + os.Exit(1) + } + if !info.IsDir() && slices.ContainsFunc(extensions, func(ext string) bool { return strings.HasSuffix(path, ext) }) { + fileInfos = append(fileInfos, FileInfo{Name: path, Size: info.Size()}) + } + return nil + }) + return fileInfos +} + +func (h *compilerHost) AbsFileName(fileName string) string { + absFileName, err := filepath.Abs(fileName) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + return absFileName +} + +func (h *compilerHost) RunTask(task func()) { + if h.singleThreaded { + task() + return + } + h.wg.Add(1) + go func() { + defer h.wg.Done() + task() + }() +} + +func (h *compilerHost) WaitForTasks() { + if h.singleThreaded { + return + } + h.wg.Wait() +} diff --git a/internal/compiler/parser.go b/internal/compiler/parser.go new file mode 100644 index 0000000000..885e5ae7a3 --- /dev/null +++ b/internal/compiler/parser.go @@ -0,0 +1,5916 @@ +package compiler + +import ( + "path" + "strings" + + "github.com/microsoft/typescript-go/internal/compiler/diagnostics" +) + +type ParsingContext int + +const ( + PCSourceElements ParsingContext = iota // Elements in source file + PCBlockStatements // Statements in block + PCSwitchClauses // Clauses in switch statement + PCSwitchClauseStatements // Statements in switch clause + PCTypeMembers // Members in interface or type literal + PCClassMembers // Members in class declaration + PCEnumMembers // Members in enum declaration + PCHeritageClauseElement // Elements in a heritage clause + PCVariableDeclarations // Variable declarations in variable statement + PCObjectBindingElements // Binding elements in object binding list + PCArrayBindingElements // Binding elements in array binding list + PCArgumentExpressions // Expressions in argument list + PCObjectLiteralMembers // Members in object literal + PCJsxAttributes // Attributes in jsx element + PCJsxChildren // Things between opening and closing JSX tags + PCArrayLiteralMembers // Members in array literal + PCParameters // Parameters in parameter list + PCJSDocParameters // JSDoc parameters in parameter list of JSDoc function type + PCRestProperties // Property names in a rest type list + PCTypeParameters // Type parameters in type parameter list + PCTypeArguments // Type arguments in type argument list + PCTupleElementTypes // Element types in tuple element type list + PCHeritageClauses // Heritage clauses for a class or interface declaration. + PCImportOrExportSpecifiers // Named import clause's import specifier list + PCImportAttributes // Import attributes + PCJSDocComment // Parsing via JSDocParser + PCCount // Number of parsing contexts +) + +type ParsingContexts int + +type Parser struct { + scanner *Scanner + factory NodeFactory + fileName string + sourceText string + languageVersion ScriptTarget + scriptKind ScriptKind + languageVariant LanguageVariant + contextFlags NodeFlags + token SyntaxKind + parsingContexts ParsingContexts + diagnostics []*Diagnostic + identifiers map[string]bool + sourceFlags NodeFlags + notParenthesizedArrow map[int]bool + identifierPool Pool[Identifier] +} + +func NewParser() *Parser { + p := &Parser{} + p.scanner = NewScanner() + return p +} + +func ParseSourceFile(fileName string, sourceText string, languageVersion ScriptTarget) *SourceFile { + var p Parser + p.scanner = NewScanner() + p.fileName = path.Clean(fileName) + p.sourceText = sourceText + p.languageVersion = languageVersion + p.scriptKind = ensureScriptKind(fileName, ScriptKindUnknown) + p.languageVariant = getLanguageVariant(p.scriptKind) + p.identifiers = make(map[string]bool) + switch p.scriptKind { + case ScriptKindJS, ScriptKindJSX: + p.contextFlags = NodeFlagsJavaScriptFile + case ScriptKindJSON: + p.contextFlags = NodeFlagsJavaScriptFile | NodeFlagsJsonFile + default: + p.contextFlags = NodeFlagsNone + } + p.scanner.SetText(p.sourceText) + p.scanner.SetOnError(p.scanError) + p.scanner.SetScriptTarget(p.languageVersion) + p.scanner.SetLanguageVariant(p.languageVariant) + p.nextToken() + return p.parseSourceFileWorker() +} + +func (p *Parser) scanError(message *diagnostics.Message, pos int, len int, args ...any) { + p.parseErrorAtRange(NewTextRange(pos, pos+len), message, args...) +} + +func (p *Parser) parseErrorAt(pos int, end int, message *diagnostics.Message, args ...any) *Diagnostic { + return p.parseErrorAtRange(NewTextRange(pos, end), message, args...) +} + +func (p *Parser) parseErrorAtCurrentToken(message *diagnostics.Message, args ...any) *Diagnostic { + return p.parseErrorAtRange(p.scanner.TokenRange(), message, args...) +} + +func (p *Parser) parseErrorAtRange(loc TextRange, message *diagnostics.Message, args ...any) *Diagnostic { + // Don't report another error if it would just be at the same location as the last error + if len(p.diagnostics) == 0 || p.diagnostics[len(p.diagnostics)-1].Loc() != loc { + result := NewDiagnostic(nil, loc, message, args...) + p.diagnostics = append(p.diagnostics, result) + return result + } + return nil +} + +type ParserState struct { + scannerState ScannerState + contextFlags NodeFlags + diagnosticsLen int +} + +func (p *Parser) mark() ParserState { + return ParserState{scannerState: p.scanner.Mark(), contextFlags: p.contextFlags, diagnosticsLen: len(p.diagnostics)} +} + +func (p *Parser) rewind(state ParserState) { + p.scanner.Rewind(state.scannerState) + p.token = p.scanner.token + p.contextFlags = state.contextFlags + p.diagnostics = p.diagnostics[0:state.diagnosticsLen] +} + +func (p *Parser) lookAhead(callback func() bool) bool { + state := p.mark() + result := callback() + p.rewind(state) + return result +} + +func (p *Parser) nextToken() SyntaxKind { + p.token = p.scanner.Scan() + return p.token +} + +func (p *Parser) nodePos() int { + return p.scanner.TokenFullStart() +} + +func (p *Parser) hasPrecedingLineBreak() bool { + return p.scanner.HasPrecedingLineBreak() +} + +func (p *Parser) hasPrecedingJSDocComment() bool { + return false // !!! +} + +func (p *Parser) parseSourceFileWorker() *SourceFile { + isDeclarationFile := isDeclarationFileName(p.fileName) + if isDeclarationFile { + p.contextFlags |= NodeFlagsAmbient + } + pos := p.nodePos() + statements := parseList(p, PCSourceElements, p.parseStatement) + node := p.factory.NewSourceFile(p.sourceText, p.fileName, statements) + p.finishNode(node, pos) + result := node.AsSourceFile() + result.diagnostics = attachFileToDiagnostics(p.diagnostics, result) + result.externalModuleIndicator = isFileProbablyExternalModule(result) + result.isDeclarationFile = isDeclarationFile + return result +} + +func parseList(p *Parser, kind ParsingContext, parseElement func() *Node) []*Node { + saveParsingContexts := p.parsingContexts + p.parsingContexts |= 1 << kind + list := []*Node{} + for !p.isListTerminator(kind) { + if p.isListElement(kind, false /*inErrorRecovery*/) { + list = append(list, parseElement()) + continue + } + if p.abortParsingListOrMoveToNextToken(kind) { + break + } + } + p.parsingContexts = saveParsingContexts + return list +} + +// Return a non-nil (but possibly empty) slice if parsing was successful, or nil if parseElement returned nil +func parseDelimitedList(p *Parser, kind ParsingContext, parseElement func() *Node) []*Node { + saveParsingContexts := p.parsingContexts + p.parsingContexts |= 1 << kind + list := []*Node{} + for { + if p.isListElement(kind, false /*inErrorRecovery*/) { + startPos := p.nodePos() + element := parseElement() + if element == nil { + p.parsingContexts = saveParsingContexts + // Return nil list to indicate parseElement failed + return nil + } + list = append(list, element) + if p.parseOptional(SyntaxKindCommaToken) { + // No need to check for a zero length node since we know we parsed a comma + continue + } + if p.isListTerminator(kind) { + break + } + // We didn't get a comma, and the list wasn't terminated, explicitly parse + // out a comma so we give a good error message. + if p.token != SyntaxKindCommaToken && kind == PCEnumMembers { + p.parseErrorAtCurrentToken(diagnostics.An_enum_member_name_must_be_followed_by_a_or) + } else { + p.parseExpected(SyntaxKindCommaToken) + } + // If the token was a semicolon, and the caller allows that, then skip it and + // continue. This ensures we get back on track and don't result in tons of + // parse errors. For example, this can happen when people do things like use + // a semicolon to delimit object literal members. Note: we'll have already + // reported an error when we called parseExpected above. + if (kind == PCObjectLiteralMembers || kind == PCImportAttributes) && p.token == SyntaxKindSemicolonToken && !p.hasPrecedingLineBreak() { + p.nextToken() + } + if startPos == p.nodePos() { + // What we're parsing isn't actually remotely recognizable as a element and we've consumed no tokens whatsoever + // Consume a token to advance the parser in some way and avoid an infinite loop + // This can happen when we're speculatively parsing parenthesized expressions which we think may be arrow functions, + // or when a modifier keyword which is disallowed as a parameter name (ie, `static` in strict mode) is supplied + p.nextToken() + } + continue + } + if p.isListTerminator(kind) { + break + } + if p.abortParsingListOrMoveToNextToken(kind) { + break + } + } + p.parsingContexts = saveParsingContexts + return list +} + +// Return a non-nil (but possibly empty) slice if parsing was successful, or nil if opening token wasn't found +// or parseElement returned nil +func parseBracketedList(p *Parser, kind ParsingContext, parseElement func() *Node, open SyntaxKind, close SyntaxKind) []*Node { + if p.parseExpected(open) { + result := parseDelimitedList(p, kind, parseElement) + p.parseExpected(close) + return result + } + return nil +} + +// Returns true if we should abort parsing. +func (p *Parser) abortParsingListOrMoveToNextToken(kind ParsingContext) bool { + p.parsingContextErrors(kind) + if p.isInSomeParsingContext() { + return true + } + p.nextToken() + return false +} + +// True if positioned at element or terminator of the current list or any enclosing list +func (p *Parser) isInSomeParsingContext() bool { + // We should be in at least one parsing context, be it SourceElements while parsing + // a SourceFile, or JSDocComment when lazily parsing JSDoc. + // Debug.assert(parsingContext, "Missing parsing context") + for kind := ParsingContext(0); kind < PCCount; kind++ { + if p.parsingContexts&(1<' then we just stop immediately. We've got an + // arrow function here and it's going to be very unlikely that we'll resynchronize and get + // another variable declaration. + return p.canParseSemicolon() || p.token == SyntaxKindInKeyword || p.token == SyntaxKindOfKeyword || p.token == SyntaxKindEqualsGreaterThanToken + case PCTypeParameters: + // Tokens other than '>' are here for better error recovery + return p.token == SyntaxKindGreaterThanToken || p.token == SyntaxKindOpenParenToken || p.token == SyntaxKindOpenBraceToken || p.token == SyntaxKindExtendsKeyword || p.token == SyntaxKindImplementsKeyword + case PCArgumentExpressions: + // Tokens other than ')' are here for better error recovery + return p.token == SyntaxKindCloseParenToken || p.token == SyntaxKindSemicolonToken + case PCArrayLiteralMembers, PCTupleElementTypes, PCArrayBindingElements: + return p.token == SyntaxKindCloseBracketToken + case PCJSDocParameters, PCParameters, PCRestProperties: + // Tokens other than ')' and ']' (the latter for index signatures) are here for better error recovery + return p.token == SyntaxKindCloseParenToken || p.token == SyntaxKindCloseBracketToken /*|| token == SyntaxKindOpenBraceToken*/ + case PCTypeArguments: + // All other tokens should cause the type-argument to terminate except comma token + return p.token != SyntaxKindCommaToken + case PCHeritageClauses: + return p.token == SyntaxKindOpenBraceToken || p.token == SyntaxKindCloseBraceToken + case PCJsxAttributes: + return p.token == SyntaxKindGreaterThanToken || p.token == SyntaxKindSlashToken + case PCJsxChildren: + return p.token == SyntaxKindLessThanToken && p.lookAhead(p.nextTokenIsSlash) + } + return false +} + +func (p *Parser) parseExpectedMatchingBrackets(openKind SyntaxKind, closeKind SyntaxKind, openParsed bool, openPosition int) { + if p.token == closeKind { + p.nextToken() + return + } + lastError := p.parseErrorAtCurrentToken(diagnostics.X_0_expected, TokenToString(closeKind)) + if !openParsed { + return + } + if lastError != nil { + related := NewDiagnostic(nil, NewTextRange(openPosition, openPosition+1), diagnostics.The_parser_expected_to_find_a_1_to_match_the_0_token_here, TokenToString(openKind), TokenToString(closeKind)) + addRelatedInfo(lastError, related) + } +} + +func (p *Parser) parseOptional(token SyntaxKind) bool { + if p.token == token { + p.nextToken() + return true + } + return false +} + +func (p *Parser) parseExpected(kind SyntaxKind) bool { + return p.parseExpectedWithDiagnostic(kind, nil, true) +} + +func (p *Parser) parseExpectedWithoutAdvancing(kind SyntaxKind) bool { + return p.parseExpectedWithDiagnostic(kind, nil, false) +} + +func (p *Parser) parseExpectedWithDiagnostic(kind SyntaxKind, message *diagnostics.Message, shouldAdvance bool) bool { + if p.token == kind { + if shouldAdvance { + p.nextToken() + } + return true + } + // Report specific message if provided with one. Otherwise, report generic fallback message. + if message != nil { + p.parseErrorAtCurrentToken(message) + } else { + p.parseErrorAtCurrentToken(diagnostics.X_0_expected, TokenToString(kind)) + } + return false +} + +func (p *Parser) parseStatement() *Statement { + switch p.token { + case SyntaxKindSemicolonToken: + return p.parseEmptyStatement() + case SyntaxKindOpenBraceToken: + return p.parseBlock(false /*ignoreMissingOpenBrace*/, nil) + case SyntaxKindVarKeyword: + return p.parseVariableStatement(p.nodePos(), p.hasPrecedingJSDocComment(), nil /*modifiers*/) + case SyntaxKindLetKeyword: + if p.isLetDeclaration() { + return p.parseVariableStatement(p.nodePos(), p.hasPrecedingJSDocComment(), nil /*modifiers*/) + } + case SyntaxKindAwaitKeyword: + if p.isAwaitUsingDeclaration() { + return p.parseVariableStatement(p.nodePos(), p.hasPrecedingJSDocComment(), nil /*modifiers*/) + } + case SyntaxKindUsingKeyword: + if p.isUsingDeclaration() { + return p.parseVariableStatement(p.nodePos(), p.hasPrecedingJSDocComment(), nil /*modifiers*/) + } + case SyntaxKindFunctionKeyword: + return p.parseFunctionDeclaration(p.nodePos(), p.hasPrecedingJSDocComment(), nil /*modifiers*/) + case SyntaxKindClassKeyword: + return p.parseClassDeclaration(p.nodePos(), p.hasPrecedingJSDocComment(), nil /*modifiers*/) + case SyntaxKindIfKeyword: + return p.parseIfStatement() + case SyntaxKindDoKeyword: + return p.parseDoStatement() + case SyntaxKindWhileKeyword: + return p.parseWhileStatement() + case SyntaxKindForKeyword: + return p.parseForOrForInOrForOfStatement() + case SyntaxKindContinueKeyword: + return p.parseContinueStatement() + case SyntaxKindBreakKeyword: + return p.parseBreakStatement() + case SyntaxKindReturnKeyword: + return p.parseReturnStatement() + case SyntaxKindWithKeyword: + return p.parseWithStatement() + case SyntaxKindSwitchKeyword: + return p.parseSwitchStatement() + case SyntaxKindThrowKeyword: + return p.parseThrowStatement() + case SyntaxKindTryKeyword, SyntaxKindCatchKeyword, SyntaxKindFinallyKeyword: + return p.parseTryStatement() + case SyntaxKindDebuggerKeyword: + return p.parseDebuggerStatement() + case SyntaxKindAtToken: + return p.parseDeclaration() + case SyntaxKindAsyncKeyword, SyntaxKindInterfaceKeyword, SyntaxKindTypeKeyword, SyntaxKindModuleKeyword, SyntaxKindNamespaceKeyword, + SyntaxKindDeclareKeyword, SyntaxKindConstKeyword, SyntaxKindEnumKeyword, SyntaxKindExportKeyword, SyntaxKindImportKeyword, + SyntaxKindPrivateKeyword, SyntaxKindProtectedKeyword, SyntaxKindPublicKeyword, SyntaxKindAbstractKeyword, SyntaxKindAccessorKeyword, + SyntaxKindStaticKeyword, SyntaxKindReadonlyKeyword, SyntaxKindGlobalKeyword: + if p.isStartOfDeclaration() { + return p.parseDeclaration() + } + } + return p.parseExpressionOrLabeledStatement() +} + +func (p *Parser) parseDeclaration() *Statement { + // `parseListElement` attempted to get the reused node at this position, + // but the ambient context flag was not yet set, so the node appeared + // not reusable in that context. + pos := p.nodePos() + hasJSDoc := p.hasPrecedingJSDocComment() + modifierList := p.parseModifiersWithOptions( /*allowDecorators*/ true, false /*permitConstAsModifier*/, false /*stopOnStartOfClassStaticBlock*/) + isAmbient := modifierList != nil && some(modifierList.AsModifierList().modifiers, isDeclareModifier) + if isAmbient { + // !!! incremental parsing + // node := p.tryReuseAmbientDeclaration(pos) + // if node { + // return node + // } + for _, m := range modifierList.AsModifierList().modifiers { + m.flags |= NodeFlagsAmbient + } + saveContextFlags := p.contextFlags + p.setContextFlags(NodeFlagsAmbient, true) + result := p.parseDeclarationWorker(pos, hasJSDoc, modifierList) + p.contextFlags = saveContextFlags + return result + } else { + return p.parseDeclarationWorker(pos, hasJSDoc, modifierList) + } +} + +func (p *Parser) parseDeclarationWorker(pos int, hasJSDoc bool, modifierList *Node) *Statement { + switch p.token { + case SyntaxKindVarKeyword, SyntaxKindLetKeyword, SyntaxKindConstKeyword, SyntaxKindUsingKeyword, SyntaxKindAwaitKeyword: + return p.parseVariableStatement(pos, hasJSDoc, modifierList) + case SyntaxKindFunctionKeyword: + return p.parseFunctionDeclaration(pos, hasJSDoc, modifierList) + case SyntaxKindClassKeyword: + return p.parseClassDeclaration(pos, hasJSDoc, modifierList) + case SyntaxKindInterfaceKeyword: + return p.parseInterfaceDeclaration(pos, hasJSDoc, modifierList) + case SyntaxKindTypeKeyword: + return p.parseTypeAliasDeclaration(pos, hasJSDoc, modifierList) + case SyntaxKindEnumKeyword: + return p.parseEnumDeclaration(pos, hasJSDoc, modifierList) + case SyntaxKindGlobalKeyword, SyntaxKindModuleKeyword, SyntaxKindNamespaceKeyword: + return p.parseModuleDeclaration(pos, hasJSDoc, modifierList) + case SyntaxKindImportKeyword: + return p.parseImportDeclarationOrImportEqualsDeclaration(pos, hasJSDoc, modifierList) + case SyntaxKindExportKeyword: + p.nextToken() + switch p.token { + case SyntaxKindDefaultKeyword, SyntaxKindEqualsToken: + return p.parseExportAssignment(pos, hasJSDoc, modifierList) + case SyntaxKindAsKeyword: + return p.parseNamespaceExportDeclaration(pos, hasJSDoc, modifierList) + default: + return p.parseExportDeclaration(pos, hasJSDoc, modifierList) + } + } + if modifierList != nil { + // We reached this point because we encountered decorators and/or modifiers and assumed a declaration + // would follow. For recovery and error reporting purposes, return an incomplete declaration. + p.parseErrorAt(p.nodePos(), p.nodePos(), diagnostics.Declaration_expected) + result := p.factory.NewMissingDeclaration(modifierList) + p.finishNode(result, pos) + return result + } + panic("Unhandled case in parseDeclarationWorker") +} + +func isDeclareModifier(modifier *Node) bool { + return modifier.kind == SyntaxKindDeclareKeyword +} + +func (p *Parser) isLetDeclaration() bool { + // In ES6 'let' always starts a lexical declaration if followed by an identifier or { + // or [. + return p.lookAhead(p.nextTokenIsBindingIdentifierOrStartOfDestructuring) +} + +func (p *Parser) nextTokenIsBindingIdentifierOrStartOfDestructuring() bool { + p.nextToken() + return p.isBindingIdentifier() || p.token == SyntaxKindOpenBraceToken || p.token == SyntaxKindOpenBracketToken +} + +func (p *Parser) parseBlock(ignoreMissingOpenBrace bool, diagnosticMessage *diagnostics.Message) *Node { + pos := p.nodePos() + // !!! JSDOC + openBracePosition := p.scanner.TokenStart() + openBraceParsed := p.parseExpectedWithDiagnostic(SyntaxKindOpenBraceToken, diagnosticMessage, true /*shouldAdvance*/) + multiline := false + var statements []*Statement + if openBraceParsed || ignoreMissingOpenBrace { + multiline = p.hasPrecedingLineBreak() + statements = parseList(p, PCBlockStatements, p.parseStatement) + p.parseExpectedMatchingBrackets(SyntaxKindOpenBraceToken, SyntaxKindCloseBraceToken, openBraceParsed, openBracePosition) + if p.token == SyntaxKindEqualsToken { + p.parseErrorAtCurrentToken(diagnostics.Declaration_or_statement_expected_This_follows_a_block_of_statements_so_if_you_intended_to_write_a_destructuring_assignment_you_might_need_to_wrap_the_whole_assignment_in_parentheses) + p.nextToken() + } + } + result := p.factory.NewBlock(statements, multiline) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseEmptyStatement() *Node { + pos := p.nodePos() + //const hasJSDoc = hasPrecedingJSDocComment(); + p.parseExpected(SyntaxKindSemicolonToken) + result := p.factory.NewEmptyStatement() + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseIfStatement() *Node { + pos := p.nodePos() + //const hasJSDoc = hasPrecedingJSDocComment(); + p.parseExpected(SyntaxKindIfKeyword) + openParenPosition := p.scanner.TokenStart() + openParenParsed := p.parseExpected(SyntaxKindOpenParenToken) + expression := p.parseExpressionAllowIn() + p.parseExpectedMatchingBrackets(SyntaxKindOpenParenToken, SyntaxKindCloseParenToken, openParenParsed, openParenPosition) + thenStatement := p.parseStatement() + var elseStatement *Statement + if p.parseOptional(SyntaxKindElseKeyword) { + elseStatement = p.parseStatement() + } + result := p.factory.NewIfStatement(expression, thenStatement, elseStatement) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseDoStatement() *Node { + pos := p.nodePos() + //const hasJSDoc = hasPrecedingJSDocComment(); + p.parseExpected(SyntaxKindDoKeyword) + statement := p.parseStatement() + p.parseExpected(SyntaxKindWhileKeyword) + openParenPosition := p.scanner.TokenStart() + openParenParsed := p.parseExpected(SyntaxKindOpenParenToken) + expression := p.parseExpressionAllowIn() + p.parseExpectedMatchingBrackets(SyntaxKindOpenParenToken, SyntaxKindCloseParenToken, openParenParsed, openParenPosition) + // From: https://mail.mozilla.org/pipermail/es-discuss/2011-August/016188.html + // 157 min --- All allen at wirfs-brock.com CONF --- "do{;}while(false)false" prohibited in + // spec but allowed in consensus reality. Approved -- this is the de-facto standard whereby + // do;while(0)x will have a semicolon inserted before x. + p.parseOptional(SyntaxKindSemicolonToken) + result := p.factory.NewDoStatement(statement, expression) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseWhileStatement() *Node { + pos := p.nodePos() + //const hasJSDoc = hasPrecedingJSDocComment(); + p.parseExpected(SyntaxKindWhileKeyword) + openParenPosition := p.scanner.TokenStart() + openParenParsed := p.parseExpected(SyntaxKindOpenParenToken) + expression := p.parseExpressionAllowIn() + p.parseExpectedMatchingBrackets(SyntaxKindOpenParenToken, SyntaxKindCloseParenToken, openParenParsed, openParenPosition) + statement := p.parseStatement() + result := p.factory.NewWhileStatement(expression, statement) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseForOrForInOrForOfStatement() *Node { + pos := p.nodePos() + //const hasJSDoc = hasPrecedingJSDocComment(); + p.parseExpected(SyntaxKindForKeyword) + awaitToken := p.parseOptionalToken(SyntaxKindAwaitKeyword) + p.parseExpected(SyntaxKindOpenParenToken) + var initializer *ForInitializer + if p.token != SyntaxKindSemicolonToken { + if p.token == SyntaxKindVarKeyword || p.token == SyntaxKindLetKeyword || p.token == SyntaxKindConstKeyword || + p.token == SyntaxKindUsingKeyword && p.lookAhead(p.nextTokenIsBindingIdentifierOrStartOfDestructuringOnSameLineDisallowOf) || + // this one is meant to allow of + p.token == SyntaxKindAwaitKeyword && p.lookAhead(p.nextIsUsingKeywordThenBindingIdentifierOrStartOfObjectDestructuringOnSameLine) { + initializer = p.parseVariableDeclarationList(true /*inForStatementInitializer*/) + } else { + initializer = doInContext(p, NodeFlagsDisallowInContext, true, p.parseExpression) + } + } + var result *Statement + switch { + case awaitToken != nil && p.parseExpected(SyntaxKindOfKeyword) || awaitToken == nil && p.parseOptional(SyntaxKindOfKeyword): + expression := doInContext(p, NodeFlagsDisallowInContext, false, p.parseAssignmentExpressionOrHigher) + p.parseExpected(SyntaxKindCloseParenToken) + result = p.factory.NewForInOrOfStatement(SyntaxKindForOfStatement, awaitToken, initializer, expression, p.parseStatement()) + case p.parseOptional(SyntaxKindInKeyword): + expression := p.parseExpressionAllowIn() + p.parseExpected(SyntaxKindCloseParenToken) + result = p.factory.NewForInOrOfStatement(SyntaxKindForInStatement, nil /*awaitToken*/, initializer, expression, p.parseStatement()) + default: + p.parseExpected(SyntaxKindSemicolonToken) + var condition *Expression + if p.token != SyntaxKindSemicolonToken && p.token != SyntaxKindCloseParenToken { + condition = p.parseExpressionAllowIn() + } + p.parseExpected(SyntaxKindSemicolonToken) + var incrementor *Expression + if p.token != SyntaxKindCloseParenToken { + incrementor = p.parseExpressionAllowIn() + } + p.parseExpected(SyntaxKindCloseParenToken) + result = p.factory.NewForStatement(initializer, condition, incrementor, p.parseStatement()) + } + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseBreakStatement() *Node { + pos := p.nodePos() + //const hasJSDoc = hasPrecedingJSDocComment(); + p.parseExpected(SyntaxKindBreakKeyword) + label := p.parseIdentifierUnlessAtSemicolon() + p.parseSemicolon() + result := p.factory.NewBreakStatement(label) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseContinueStatement() *Node { + pos := p.nodePos() + //const hasJSDoc = hasPrecedingJSDocComment(); + p.parseExpected(SyntaxKindContinueKeyword) + label := p.parseIdentifierUnlessAtSemicolon() + p.parseSemicolon() + result := p.factory.NewContinueStatement(label) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseIdentifierUnlessAtSemicolon() *Node { + if !p.canParseSemicolon() { + return p.parseIdentifier() + } + return nil +} + +func (p *Parser) parseReturnStatement() *Node { + pos := p.nodePos() + //const hasJSDoc = hasPrecedingJSDocComment(); + p.parseExpected(SyntaxKindReturnKeyword) + var expression *Expression + if !p.canParseSemicolon() { + expression = p.parseExpressionAllowIn() + } + p.parseSemicolon() + result := p.factory.NewReturnStatement(expression) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseWithStatement() *Node { + pos := p.nodePos() + //const hasJSDoc = hasPrecedingJSDocComment(); + p.parseExpected(SyntaxKindWithKeyword) + openParenPosition := p.scanner.TokenStart() + openParenParsed := p.parseExpected(SyntaxKindOpenParenToken) + expression := p.parseExpressionAllowIn() + p.parseExpectedMatchingBrackets(SyntaxKindOpenParenToken, SyntaxKindCloseParenToken, openParenParsed, openParenPosition) + statement := doInContext(p, NodeFlagsInWithStatement, true, p.parseStatement) + result := p.factory.NewWithStatement(expression, statement) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseCaseClause() *Node { + pos := p.nodePos() + //const hasJSDoc = hasPrecedingJSDocComment(); + p.parseExpected(SyntaxKindCaseKeyword) + expression := p.parseExpressionAllowIn() + p.parseExpected(SyntaxKindColonToken) + statements := parseList(p, PCSwitchClauseStatements, p.parseStatement) + result := p.factory.NewCaseOrDefaultClause(SyntaxKindCaseClause, expression, statements) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseDefaultClause() *Node { + pos := p.nodePos() + //const hasJSDoc = hasPrecedingJSDocComment(); + p.parseExpected(SyntaxKindDefaultKeyword) + p.parseExpected(SyntaxKindColonToken) + statements := parseList(p, PCSwitchClauseStatements, p.parseStatement) + result := p.factory.NewCaseOrDefaultClause(SyntaxKindDefaultClause, nil /*expression*/, statements) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseCaseOrDefaultClause() *Node { + if p.token == SyntaxKindCaseKeyword { + return p.parseCaseClause() + } + return p.parseDefaultClause() +} + +func (p *Parser) parseCaseBlock() *Node { + pos := p.nodePos() + p.parseExpected(SyntaxKindOpenBraceToken) + clauses := parseList(p, PCSwitchClauses, p.parseCaseOrDefaultClause) + p.parseExpected(SyntaxKindCloseBraceToken) + result := p.factory.NewCaseBlock(clauses) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseSwitchStatement() *Node { + pos := p.nodePos() + //const hasJSDoc = hasPrecedingJSDocComment(); + p.parseExpected(SyntaxKindSwitchKeyword) + p.parseExpected(SyntaxKindOpenParenToken) + expression := p.parseExpressionAllowIn() + p.parseExpected(SyntaxKindCloseParenToken) + caseBlock := p.parseCaseBlock() + result := p.factory.NewSwitchStatement(expression, caseBlock) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseThrowStatement() *Node { + // ThrowStatement[Yield] : + // throw [no LineTerminator here]Expression[In, ?Yield]; + pos := p.nodePos() + //const hasJSDoc = hasPrecedingJSDocComment(); + p.parseExpected(SyntaxKindThrowKeyword) + // Because of automatic semicolon insertion, we need to report error if this + // throw could be terminated with a semicolon. Note: we can't call 'parseExpression' + // directly as that might consume an expression on the following line. + // Instead, we create a "missing" identifier, but don't report an error. The actual error + // will be reported in the grammar walker. + var expression *Expression + if !p.hasPrecedingLineBreak() { + expression = p.parseExpressionAllowIn() + } else { + expression = p.createMissingIdentifier() + } + if !p.tryParseSemicolon() { + p.parseErrorForMissingSemicolonAfter(expression) + } + result := p.factory.NewThrowStatement(expression) + p.finishNode(result, pos) + return result +} + +// TODO: Review for error recovery +func (p *Parser) parseTryStatement() *Node { + pos := p.nodePos() + // const hasJSDoc = hasPrecedingJSDocComment(); + p.parseExpected(SyntaxKindTryKeyword) + tryBlock := p.parseBlock(false /*ignoreMissingOpenBrace*/, nil) + var catchClause *Node + if p.token == SyntaxKindCatchKeyword { + catchClause = p.parseCatchClause() + } + // If we don't have a catch clause, then we must have a finally clause. Try to parse + // one out no matter what. + var finallyBlock *Node + if catchClause == nil || p.token == SyntaxKindFinallyKeyword { + p.parseExpectedWithDiagnostic(SyntaxKindFinallyKeyword, diagnostics.X_catch_or_finally_expected, true /*shouldAdvance*/) + finallyBlock = p.parseBlock(false /*ignoreMissingOpenBrace*/, nil) + } + result := p.factory.NewTryStatement(tryBlock, catchClause, finallyBlock) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseCatchClause() *Node { + pos := p.nodePos() + p.parseExpected(SyntaxKindCatchKeyword) + var variableDeclaration *Node + if p.parseOptional(SyntaxKindOpenParenToken) { + variableDeclaration = p.parseVariableDeclaration() + p.parseExpected(SyntaxKindCloseParenToken) + } + block := p.parseBlock(false /*ignoreMissingOpenBrace*/, nil) + result := p.factory.NewCatchClause(variableDeclaration, block) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseDebuggerStatement() *Node { + pos := p.nodePos() + // const hasJSDoc = hasPrecedingJSDocComment(); + p.parseExpected(SyntaxKindDebuggerKeyword) + p.parseSemicolon() + result := p.factory.NewDebuggerStatement() + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseExpressionOrLabeledStatement() *Statement { + // Avoiding having to do the lookahead for a labeled statement by just trying to parse + // out an expression, seeing if it is identifier and then seeing if it is followed by + // a colon. + pos := p.nodePos() + // !!! JSDoc + expression := p.parseExpression() + if expression.kind == SyntaxKindIdentifier && p.parseOptional(SyntaxKindColonToken) { + result := p.factory.NewLabeledStatement(expression, p.parseStatement()) + p.finishNode(result, pos) + return result + } + // if !p.tryParseSemicolon() { + // p.parseErrorForMissingSemicolonAfter(expression) + // } + p.parseSemicolon() + result := p.factory.NewExpressionStatement(expression) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseVariableStatement(pos int, hasJSDoc bool, modifiers *Node) *Node { + declarationList := p.parseVariableDeclarationList(false /*inForStatementInitializer*/) + p.parseSemicolon() + result := p.factory.NewVariableStatement(modifiers, declarationList) + p.finishNode(result, pos) + _ = hasJSDoc + return result +} + +func (p *Parser) parseVariableDeclarationList(inForStatementInitializer bool) *Node { + pos := p.nodePos() + var flags NodeFlags + switch p.token { + case SyntaxKindVarKeyword: + flags = NodeFlagsNone + case SyntaxKindLetKeyword: + flags = NodeFlagsLet + case SyntaxKindConstKeyword: + flags = NodeFlagsConst + case SyntaxKindUsingKeyword: + flags = NodeFlagsUsing + case SyntaxKindAwaitKeyword: + //Debug.assert(isAwaitUsingDeclaration()); + flags = NodeFlagsAwaitUsing + p.nextToken() + default: + panic("Unhandled case in parseVariableDeclarationList") + } + p.nextToken() + // The user may have written the following: + // + // for (let of X) { } + // + // In this case, we want to parse an empty declaration list, and then parse 'of' + // as a keyword. The reason this is not automatic is that 'of' is a valid identifier. + // So we need to look ahead to determine if 'of' should be treated as a keyword in + // this context. + // The checker will then give an error that there is an empty declaration list. + var declarations []*Node + if p.token == SyntaxKindOfKeyword && p.lookAhead(p.nextIsIdentifierAndCloseParen) { + declarations = []*Node{} + } else { + saveContextFlags := p.contextFlags + p.setContextFlags(NodeFlagsDisallowInContext, inForStatementInitializer) + declarations = parseDelimitedList(p, PCVariableDeclarations, ifElse(inForStatementInitializer, p.parseVariableDeclaration, p.parseVariableDeclarationAllowExclamation)) + p.contextFlags = saveContextFlags + } + result := p.factory.NewVariableDeclarationList(flags, declarations) + p.finishNode(result, pos) + return result +} + +func (p *Parser) nextIsIdentifierAndCloseParen() bool { + return p.nextTokenIsIdentifier() && p.nextToken() == SyntaxKindCloseParenToken +} + +func (p *Parser) nextTokenIsIdentifier() bool { + p.nextToken() + return p.isIdentifier() +} + +func (p *Parser) parseVariableDeclaration() *Node { + return p.parseVariableDeclarationWorker(false /*allowExclamation*/) +} + +func (p *Parser) parseVariableDeclarationAllowExclamation() *Node { + return p.parseVariableDeclarationWorker(true /*allowExclamation*/) +} + +func (p *Parser) parseVariableDeclarationWorker(allowExclamation bool) *Node { + pos := p.nodePos() + // !!! jsDoc + name := p.parseIdentifierOrPatternWithDiagnostic(diagnostics.Private_identifiers_are_not_allowed_in_variable_declarations) + var exclamationToken *Node + if allowExclamation && name.kind == SyntaxKindIdentifier && p.token == SyntaxKindExclamationToken && !p.hasPrecedingLineBreak() { + exclamationToken = p.parseTokenNode() + } + typeNode := p.parseTypeAnnotation() + var initializer *Expression + if p.token != SyntaxKindInKeyword && p.token != SyntaxKindOfKeyword { + initializer = p.parseInitializer() + } + result := p.factory.NewVariableDeclaration(name, exclamationToken, typeNode, initializer) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseIdentifierOrPattern() *Node { + return p.parseIdentifierOrPatternWithDiagnostic(nil) +} + +func (p *Parser) parseIdentifierOrPatternWithDiagnostic(privateIdentifierDiagnosticMessage *diagnostics.Message) *Node { + if p.token == SyntaxKindOpenBracketToken { + return p.parseArrayBindingPattern() + } + if p.token == SyntaxKindOpenBraceToken { + return p.parseObjectBindingPattern() + } + return p.parseBindingIdentifierWithDiagnostic(privateIdentifierDiagnosticMessage) +} + +func (p *Parser) parseArrayBindingPattern() *Node { + pos := p.nodePos() + p.parseExpected(SyntaxKindOpenBracketToken) + saveContextFlags := p.contextFlags + p.setContextFlags(NodeFlagsDisallowInContext, false) + elements := parseDelimitedList(p, PCArrayBindingElements, p.parseArrayBindingElement) + p.contextFlags = saveContextFlags + p.parseExpected(SyntaxKindCloseBracketToken) + result := p.factory.NewBindingPattern(SyntaxKindArrayBindingPattern, elements) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseArrayBindingElement() *Node { + pos := p.nodePos() + var dotDotDotToken *Node + var name *Node + var initializer *Expression + if p.token != SyntaxKindCommaToken { + // These are all nil for a missing element + dotDotDotToken = p.parseOptionalToken(SyntaxKindDotDotDotToken) + name = p.parseIdentifierOrPattern() + initializer = p.parseInitializer() + } + result := p.factory.NewBindingElement(dotDotDotToken, nil /*propertyName*/, name, initializer) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseObjectBindingPattern() *Node { + pos := p.nodePos() + p.parseExpected(SyntaxKindOpenBraceToken) + saveContextFlags := p.contextFlags + p.setContextFlags(NodeFlagsDisallowInContext, false) + elements := parseDelimitedList(p, PCObjectBindingElements, p.parseObjectBindingElement) + p.contextFlags = saveContextFlags + p.parseExpected(SyntaxKindCloseBraceToken) + result := p.factory.NewBindingPattern(SyntaxKindObjectBindingPattern, elements) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseObjectBindingElement() *Node { + pos := p.nodePos() + dotDotDotToken := p.parseOptionalToken(SyntaxKindDotDotDotToken) + tokenIsIdentifier := p.isBindingIdentifier() + propertyName := p.parsePropertyName() + var name *Node + if tokenIsIdentifier && p.token != SyntaxKindColonToken { + name = propertyName + propertyName = nil + } else { + p.parseExpected(SyntaxKindColonToken) + name = p.parseIdentifierOrPattern() + } + initializer := p.parseInitializer() + result := p.factory.NewBindingElement(dotDotDotToken, propertyName, name, initializer) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseTokenNode() *Node { + pos := p.nodePos() + kind := p.token + p.nextToken() + result := p.factory.NewToken(kind) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseExpectedToken(kind SyntaxKind) *Node { + token := p.parseOptionalToken(kind) + if token == nil { + p.parseErrorAtCurrentToken(diagnostics.X_0_expected, TokenToString(kind)) + token = p.factory.NewToken(kind) + p.finishNode(token, p.nodePos()) + } + return token +} + +func (p *Parser) parseOptionalToken(kind SyntaxKind) *Node { + if p.token == kind { + return p.parseTokenNode() + } + return nil +} + +func (p *Parser) parseInitializer() *Expression { + if p.parseOptional(SyntaxKindEqualsToken) { + return p.parseAssignmentExpressionOrHigher() + } + return nil +} + +func (p *Parser) parseTypeAnnotation() *TypeNode { + if p.parseOptional(SyntaxKindColonToken) { + return p.parseType() + } + return nil +} + +func (p *Parser) parseFunctionDeclaration(pos int, hasJSDoc bool, modifiers *Node) *Node { + modifierFlags := modifiersToFlags(modifiers) + p.parseExpected(SyntaxKindFunctionKeyword) + asteriskToken := p.parseOptionalToken(SyntaxKindAsteriskToken) + // We don't parse the name here in await context, instead we will report a grammar error in the checker. + var name *Node + if modifierFlags&ModifierFlagsDefault == 0 || p.isBindingIdentifier() { + name = p.parseBindingIdentifier() + } + signatureFlags := ifElse(asteriskToken != nil, SignatureFlagsYield, SignatureFlagsNone) | ifElse(modifierFlags&ModifierFlagsAsync != 0, SignatureFlagsAwait, SignatureFlagsNone) + typeParameters := p.parseTypeParameters() + saveContextFlags := p.contextFlags + if modifierFlags&ModifierFlagsExport != 0 { + p.setContextFlags(NodeFlagsAwaitContext, true) + } + parameters := p.parseParameters(signatureFlags) + returnType := p.parseReturnType(SyntaxKindColonToken, false /*isType*/) + body := p.parseFunctionBlockOrSemicolon(signatureFlags, diagnostics.X_or_expected) + p.contextFlags = saveContextFlags + result := p.factory.NewFunctionDeclaration(modifiers, asteriskToken, name, typeParameters, parameters, returnType, body) + p.finishNode(result, pos) + _ = hasJSDoc + return result +} + +func (p *Parser) parseClassDeclaration(pos int, hasJSDoc bool, modifiers *Node) *Node { + return p.parseClassDeclarationOrExpression(pos, hasJSDoc, modifiers, SyntaxKindClassDeclaration) +} + +func (p *Parser) parseClassExpression() *Node { + return p.parseClassDeclarationOrExpression(p.nodePos(), p.hasPrecedingJSDocComment(), nil /*modifiers*/, SyntaxKindClassExpression) +} + +func (p *Parser) parseClassDeclarationOrExpression(pos int, hasJSDoc bool, modifiers *Node, kind SyntaxKind) *Node { + saveContextFlags := p.contextFlags + p.parseExpected(SyntaxKindClassKeyword) + // We don't parse the name here in await context, instead we will report a grammar error in the checker. + name := p.parseNameOfClassDeclarationOrExpression() + typeParameters := p.parseTypeParameters() + if modifiers != nil && some(modifiers.AsModifierList().modifiers, isExportModifier) { + p.setContextFlags(NodeFlagsAwaitContext, true /*value*/) + } + heritageClauses := p.parseHeritageClauses() + var members []*Node + if p.parseExpected(SyntaxKindOpenBraceToken) { + // ClassTail[Yield,Await] : (Modified) See 14.5 + // ClassHeritage[?Yield,?Await]opt { ClassBody[?Yield,?Await]opt } + members = parseList(p, PCClassMembers, p.parseClassElement) + p.parseExpected(SyntaxKindCloseBraceToken) + } + p.contextFlags = saveContextFlags + var result *Node + if kind == SyntaxKindClassDeclaration { + result = p.factory.NewClassDeclaration(modifiers, name, typeParameters, heritageClauses, members) + } else { + result = p.factory.NewClassExpression(modifiers, name, typeParameters, heritageClauses, members) + } + p.finishNode(result, pos) + _ = hasJSDoc + return result +} + +func (p *Parser) parseNameOfClassDeclarationOrExpression() *Node { + // implements is a future reserved word so + // 'class implements' might mean either + // - class expression with omitted name, 'implements' starts heritage clause + // - class with name 'implements' + // 'isImplementsClause' helps to disambiguate between these two cases + if p.isBindingIdentifier() && !p.isImplementsClause() { + return p.createIdentifier(p.isBindingIdentifier()) + } + return nil +} + +func (p *Parser) isImplementsClause() bool { + return p.token == SyntaxKindImplementsKeyword && p.lookAhead(p.nextTokenIsIdentifierOrKeyword) +} + +func isExportModifier(modifier *Node) bool { + return modifier.kind == SyntaxKindExportKeyword +} + +func isAsyncModifier(modifier *Node) bool { + return modifier.kind == SyntaxKindAsyncKeyword +} + +func (p *Parser) parseHeritageClauses() []*Node { + // ClassTail[Yield,Await] : (Modified) See 14.5 + // ClassHeritage[?Yield,?Await]opt { ClassBody[?Yield,?Await]opt } + if p.isHeritageClause() { + return parseList(p, PCHeritageClauses, p.parseHeritageClause) + } + return []*Node{} +} + +func (p *Parser) parseHeritageClause() *Node { + pos := p.nodePos() + kind := p.token + p.nextToken() + types := parseDelimitedList(p, PCHeritageClauseElement, p.parseExpressionWithTypeArguments) + result := p.factory.NewHeritageClause(kind, types) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseExpressionWithTypeArguments() *Node { + pos := p.nodePos() + expression := p.parseLeftHandSideExpressionOrHigher() + if isExpressionWithTypeArguments(expression) { + return expression + } + typeArguments := p.parseTypeArguments() + result := p.factory.NewExpressionWithTypeArguments(expression, typeArguments) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseClassElement() *Node { + pos := p.nodePos() + hasJSDoc := p.hasPrecedingJSDocComment() + if p.token == SyntaxKindSemicolonToken { + p.nextToken() + result := p.factory.NewSemicolonClassElement() + p.finishNode(result, pos) + return result + } + modifierList := p.parseModifiersWithOptions(true /*allowDecorators*/, true /*permitConstAsModifier*/, true /*stopOnStartOfClassStaticBlock*/) + if p.token == SyntaxKindStaticKeyword && p.lookAhead(p.nextTokenIsOpenBrace) { + return p.parseClassStaticBlockDeclaration(pos, hasJSDoc, modifierList) + } + if p.parseContextualModifier(SyntaxKindGetKeyword) { + return p.parseAccessorDeclaration(pos, hasJSDoc, modifierList, SyntaxKindGetAccessor, SignatureFlagsNone) + } + if p.parseContextualModifier(SyntaxKindSetKeyword) { + return p.parseAccessorDeclaration(pos, hasJSDoc, modifierList, SyntaxKindSetAccessor, SignatureFlagsNone) + } + if p.token == SyntaxKindConstructorKeyword || p.token == SyntaxKindStringLiteral { + constructorDeclaration := p.tryParseConstructorDeclaration(pos, hasJSDoc, modifierList) + if constructorDeclaration != nil { + return constructorDeclaration + } + } + if p.isIndexSignature() { + return p.parseIndexSignatureDeclaration(pos, hasJSDoc, modifierList) + } + // It is very important that we check this *after* checking indexers because + // the [ token can start an index signature or a computed property name + if tokenIsIdentifierOrKeyword(p.token) || p.token == SyntaxKindStringLiteral || p.token == SyntaxKindNumericLiteral || p.token == SyntaxKindBigintLiteral || p.token == SyntaxKindAsteriskToken || p.token == SyntaxKindOpenBracketToken { + isAmbient := modifierList != nil && some(modifierList.AsModifierList().modifiers, isDeclareModifier) + if isAmbient { + for _, m := range modifierList.AsModifierList().modifiers { + m.flags |= NodeFlagsAmbient + } + saveContextFlags := p.contextFlags + p.setContextFlags(NodeFlagsAmbient, true) + result := p.parsePropertyOrMethodDeclaration(pos, hasJSDoc, modifierList) + p.contextFlags = saveContextFlags + return result + } else { + return p.parsePropertyOrMethodDeclaration(pos, hasJSDoc, modifierList) + } + } + if modifierList != nil { + // treat this as a property declaration with a missing name. + p.parseErrorAt(p.nodePos(), p.nodePos(), diagnostics.Declaration_expected) + name := p.newIdentifier("") + return p.parsePropertyDeclaration(pos, hasJSDoc, modifierList, name, nil /*questionToken*/) + } + // 'isClassMemberStart' should have hinted not to attempt parsing. + panic("Should not have attempted to parse class member declaration.") +} + +func (p *Parser) parseClassStaticBlockDeclaration(pos int, hasJSDoc bool, modifiers *Node) *Node { + p.parseExpectedToken(SyntaxKindStaticKeyword) + body := p.parseClassStaticBlockBody() + result := p.factory.NewClassStaticBlockDeclaration(modifiers, body) + p.finishNode(result, pos) + _ = hasJSDoc + return result +} + +func (p *Parser) parseClassStaticBlockBody() *Node { + saveContextFlags := p.contextFlags + p.setContextFlags(NodeFlagsYieldContext, false) + p.setContextFlags(NodeFlagsAwaitContext, true) + body := p.parseBlock(false /*ignoreMissingOpenBrace*/, nil /*diagnosticMessage*/) + p.contextFlags = saveContextFlags + return body +} + +func (p *Parser) tryParseConstructorDeclaration(pos int, hasJSDoc bool, modifiers *Node) *Node { + state := p.mark() + if p.token == SyntaxKindConstructorKeyword || p.token == SyntaxKindStringLiteral && p.scanner.tokenValue == "constructor" && p.lookAhead(p.nextTokenIsOpenParen) { + p.nextToken() + typeParameters := p.parseTypeParameters() + parameters := p.parseParameters(SignatureFlagsNone) + returnType := p.parseReturnType(SyntaxKindColonToken, false /*isType*/) + body := p.parseFunctionBlockOrSemicolon(SignatureFlagsNone, diagnostics.X_or_expected) + result := p.factory.NewConstructorDeclaration(modifiers, typeParameters, parameters, returnType, body) + p.finishNode(result, pos) + _ = hasJSDoc + return result + } + p.rewind(state) + return nil +} + +func (p *Parser) nextTokenIsOpenParen() bool { + return p.nextToken() == SyntaxKindOpenParenToken +} + +func (p *Parser) parsePropertyOrMethodDeclaration(pos int, hasJSDoc bool, modifiers *Node) *Node { + asteriskToken := p.parseOptionalToken(SyntaxKindAsteriskToken) + name := p.parsePropertyName() + // Note: this is not legal as per the grammar. But we allow it in the parser and + // report an error in the grammar checker. + questionToken := p.parseOptionalToken(SyntaxKindQuestionToken) + if asteriskToken != nil || p.token == SyntaxKindOpenParenToken || p.token == SyntaxKindLessThanToken { + return p.parseMethodDeclaration(pos, hasJSDoc, modifiers, asteriskToken, name, questionToken, diagnostics.X_or_expected) + } + return p.parsePropertyDeclaration(pos, hasJSDoc, modifiers, name, questionToken) +} + +func (p *Parser) parseMethodDeclaration(pos int, hasJSDoc bool, modifiers *Node, asteriskToken *Node, name *Node, questionToken *Node, diagnosticMessage *diagnostics.Message) *Node { + signatureFlags := ifElse(asteriskToken != nil, SignatureFlagsYield, SignatureFlagsNone) | ifElse(hasAsyncModifier(modifiers), SignatureFlagsAwait, SignatureFlagsNone) + typeParameters := p.parseTypeParameters() + parameters := p.parseParameters(signatureFlags) + typeNode := p.parseReturnType(SyntaxKindColonToken, false /*isType*/) + body := p.parseFunctionBlockOrSemicolon(signatureFlags, diagnosticMessage) + result := p.factory.NewMethodDeclaration(modifiers, asteriskToken, name, questionToken, typeParameters, parameters, typeNode, body) + p.finishNode(result, pos) + _ = hasJSDoc + return result +} + +func hasAsyncModifier(modifiers *Node) bool { + return modifiers != nil && some(modifiers.AsModifierList().modifiers, isAsyncModifier) +} + +func (p *Parser) parsePropertyDeclaration(pos int, hasJSDoc bool, modifiers *Node, name *Node, questionToken *Node) *Node { + postfixToken := questionToken + if postfixToken == nil && !p.hasPrecedingLineBreak() { + postfixToken = p.parseOptionalToken(SyntaxKindExclamationToken) + } + typeNode := p.parseTypeAnnotation() + initializer := doInContext(p, NodeFlagsYieldContext|NodeFlagsAwaitContext|NodeFlagsDisallowInContext, false, p.parseInitializer) + p.parseSemicolonAfterPropertyName(name, typeNode, initializer) + result := p.factory.NewPropertyDeclaration(modifiers, name, postfixToken, typeNode, initializer) + p.finishNode(result, pos) + _ = hasJSDoc + return result +} + +func (p *Parser) parseSemicolonAfterPropertyName(name *Node, typeNode *TypeNode, initializer *Expression) { + if p.token == SyntaxKindAtToken && !p.hasPrecedingLineBreak() { + p.parseErrorAtCurrentToken(diagnostics.Decorators_must_precede_the_name_and_all_keywords_of_property_declarations) + return + } + if p.token == SyntaxKindOpenParenToken { + p.parseErrorAtCurrentToken(diagnostics.Cannot_start_a_function_call_in_a_type_annotation) + p.nextToken() + return + } + if typeNode != nil && !p.canParseSemicolon() { + if initializer != nil { + p.parseErrorAtCurrentToken(diagnostics.X_0_expected, TokenToString(SyntaxKindSemicolonToken)) + } else { + p.parseErrorAtCurrentToken(diagnostics.Expected_for_property_initializer) + } + return + } + if p.tryParseSemicolon() { + return + } + if initializer != nil { + p.parseErrorAtCurrentToken(diagnostics.X_0_expected, TokenToString(SyntaxKindSemicolonToken)) + return + } + p.parseErrorForMissingSemicolonAfter(name) +} + +func (p *Parser) parseErrorForMissingSemicolonAfter(node *Node) { + // Tagged template literals are sometimes used in places where only simple strings are allowed, i.e.: + // module `M1` { + // ^^^^^^^^^^^ This block is parsed as a template literal like module`M1`. + if node.kind == SyntaxKindTaggedTemplateExpression { + p.parseErrorAtRange(p.skipRangeTrivia(node.AsTaggedTemplateExpression().template.loc), diagnostics.Module_declaration_names_may_only_use_or_quoted_strings) + return + } + // Otherwise, if this isn't a well-known keyword-like identifier, give the generic fallback message. + var expressionText string + if node.kind == SyntaxKindIdentifier { + expressionText = node.AsIdentifier().text + } + // !!! Also call isIdentifierText(expressionText, languageVersion) + if expressionText == "" { + p.parseErrorAtCurrentToken(diagnostics.X_0_expected, TokenToString(SyntaxKindSemicolonToken)) + return + } + pos := skipTrivia(p.sourceText, node.Pos()) + // Some known keywords are likely signs of syntax being used improperly. + switch expressionText { + case "const", "let", "var": + p.parseErrorAt(pos, node.End(), diagnostics.Variable_declaration_not_allowed_at_this_location) + return + case "declare": + // If a declared node failed to parse, it would have emitted a diagnostic already. + return + case "interface": + p.parseErrorForInvalidName(diagnostics.Interface_name_cannot_be_0, diagnostics.Interface_must_be_given_a_name, SyntaxKindOpenBraceToken) + return + case "is": + p.parseErrorAt(pos, p.scanner.TokenStart(), diagnostics.A_type_predicate_is_only_allowed_in_return_type_position_for_functions_and_methods) + return + case "module", "namespace": + p.parseErrorForInvalidName(diagnostics.Namespace_name_cannot_be_0, diagnostics.Namespace_must_be_given_a_name, SyntaxKindOpenBraceToken) + return + case "type": + p.parseErrorForInvalidName(diagnostics.Type_alias_name_cannot_be_0, diagnostics.Type_alias_must_be_given_a_name, SyntaxKindEqualsToken) + return + } + // !!! The user alternatively might have misspelled or forgotten to add a space after a common keyword. + // const suggestion = getSpellingSuggestion(expressionText, viableKeywordSuggestions, identity) ?? getSpaceSuggestion(expressionText); + // if (suggestion) { + // parseErrorAt(pos, node.end, Diagnostics.Unknown_keyword_or_identifier_Did_you_mean_0, suggestion); + // return; + // } + // Unknown tokens are handled with their own errors in the scanner + if p.token == SyntaxKindUnknown { + return + } + // Otherwise, we know this some kind of unknown word, not just a missing expected semicolon. + p.parseErrorAt(pos, node.End(), diagnostics.Unexpected_keyword_or_identifier) +} + +func (p *Parser) parseErrorForInvalidName(nameDiagnostic *diagnostics.Message, blankDiagnostic *diagnostics.Message, tokenIfBlankName SyntaxKind) { + if p.token == tokenIfBlankName { + p.parseErrorAtCurrentToken(blankDiagnostic) + } else { + p.parseErrorAtCurrentToken(nameDiagnostic, p.scanner.TokenValue()) + } +} + +func (p *Parser) parseInterfaceDeclaration(pos int, hasJSDoc bool, modifiers *Node) *Node { + p.parseExpected(SyntaxKindInterfaceKeyword) + name := p.parseIdentifier() + typeParameters := p.parseTypeParameters() + heritageClauses := p.parseHeritageClauses() + members := p.parseObjectTypeMembers() + result := p.factory.NewInterfaceDeclaration(modifiers, name, typeParameters, heritageClauses, members) + p.finishNode(result, pos) + _ = hasJSDoc + return result +} + +func (p *Parser) parseTypeAliasDeclaration(pos int, hasJSDoc bool, modifiers *Node) *Node { + p.parseExpected(SyntaxKindTypeKeyword) + if p.hasPrecedingLineBreak() { + p.parseErrorAtCurrentToken(diagnostics.Line_break_not_permitted_here) + } + name := p.parseIdentifier() + typeParameters := p.parseTypeParameters() + p.parseExpected(SyntaxKindEqualsToken) + var typeNode *TypeNode + if p.token == SyntaxKindIntrinsicKeyword && p.lookAhead(p.nextIsNotDot) { + typeNode = p.parseKeywordTypeNode() + } else { + typeNode = p.parseType() + } + p.parseSemicolon() + result := p.factory.NewTypeAliasDeclaration(modifiers, name, typeParameters, typeNode) + p.finishNode(result, pos) + _ = hasJSDoc + return result +} + +func (p *Parser) nextIsNotDot() bool { + return p.nextToken() != SyntaxKindDotToken +} + +// In an ambient declaration, the grammar only allows integer literals as initializers. +// In a non-ambient declaration, the grammar allows uninitialized members only in a +// ConstantEnumMemberSection, which starts at the beginning of an enum declaration +// or any time an integer literal initializer is encountered. +func (p *Parser) parseEnumMember() *Node { + pos := p.nodePos() + // hasJSDoc := p.hasPrecedingJSDocComment() + name := p.parsePropertyName() + initializer := doInContext(p, NodeFlagsDisallowInContext, false, p.parseInitializer) + result := p.factory.NewEnumMember(name, initializer) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseEnumDeclaration(pos int, hasJSDoc bool, modifiers *Node) *Node { + p.parseExpected(SyntaxKindEnumKeyword) + name := p.parseIdentifier() + var members []*Node + if p.parseExpected(SyntaxKindOpenBraceToken) { + saveContextFlags := p.contextFlags + p.setContextFlags(NodeFlagsYieldContext|NodeFlagsAwaitContext, false) + members = parseDelimitedList(p, PCEnumMembers, p.parseEnumMember) + p.contextFlags = saveContextFlags + p.parseExpected(SyntaxKindCloseBraceToken) + } + result := p.factory.NewEnumDeclaration(modifiers, name, members) + p.finishNode(result, pos) + _ = hasJSDoc + return result +} + +func (p *Parser) parseModuleDeclaration(pos int, hasJSDoc bool, modifiers *Node) *Statement { + var flags NodeFlags + if p.token == SyntaxKindGlobalKeyword { + // global augmentation + return p.parseAmbientExternalModuleDeclaration(pos, hasJSDoc, modifiers) + } else if p.parseOptional(SyntaxKindNamespaceKeyword) { + flags |= NodeFlagsNamespace + } else { + p.parseExpected(SyntaxKindModuleKeyword) + if p.token == SyntaxKindStringLiteral { + return p.parseAmbientExternalModuleDeclaration(pos, hasJSDoc, modifiers) + } + } + return p.parseModuleOrNamespaceDeclaration(pos, hasJSDoc, modifiers, flags) +} + +func (p *Parser) parseAmbientExternalModuleDeclaration(pos int, hasJSDoc bool, modifiers *Node) *Node { + var flags NodeFlags + var name *Node + if p.token == SyntaxKindGlobalKeyword { + // parse 'global' as name of global scope augmentation + name = p.parseIdentifier() + flags |= NodeFlagsGlobalAugmentation + } else { + // parse string literal + name = p.parseLiteralExpression() + p.internIdentifier(getTextOfIdentifierOrLiteral(name)) + } + var body *Node + if p.token == SyntaxKindOpenBraceToken { + body = p.parseModuleBlock() + } else { + p.parseSemicolon() + } + result := p.factory.NewModuleDeclaration(modifiers, name, body, flags) + p.finishNode(result, pos) + _ = hasJSDoc + return result +} + +func (p *Parser) parseModuleBlock() *Node { + pos := p.nodePos() + var statements []*Statement + if p.parseExpected(SyntaxKindOpenBraceToken) { + statements = parseList(p, PCBlockStatements, p.parseStatement) + p.parseExpected(SyntaxKindCloseBraceToken) + } + result := p.factory.NewModuleBlock(statements) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseModuleOrNamespaceDeclaration(pos int, hasJSDoc bool, modifiers *Node, flags NodeFlags) *Node { + // If we are parsing a dotted namespace name, we want to + // propagate the 'Namespace' flag across the names if set. + namespaceFlag := flags & NodeFlagsNamespace + var name *Node + if flags&NodeFlagsNestedNamespace != 0 { + name = p.parseIdentifierName() + } else { + name = p.parseIdentifier() + } + var body *Node + if p.parseOptional(SyntaxKindDotToken) { + body = p.parseModuleOrNamespaceDeclaration(p.nodePos(), false /*hasJSDoc*/, nil /*modifiers*/, NodeFlagsNestedNamespace|namespaceFlag) + } else { + body = p.parseModuleBlock() + } + result := p.factory.NewModuleDeclaration(modifiers, name, body, flags) + p.finishNode(result, pos) + _ = hasJSDoc + return result +} + +func (p *Parser) parseImportDeclarationOrImportEqualsDeclaration(pos int, hasJSDoc bool, modifiers *Node) *Statement { + p.parseExpected(SyntaxKindImportKeyword) + afterImportPos := p.nodePos() + // We don't parse the identifier here in await context, instead we will report a grammar error in the checker. + var identifier *Node + if p.isIdentifier() { + identifier = p.parseIdentifier() + } + isTypeOnly := false + if identifier != nil && identifier.AsIdentifier().text == "type" && + (p.token != SyntaxKindFromKeyword || p.isIdentifier() && p.lookAhead(p.nextTokenIsFromKeywordOrEqualsToken)) && + (p.isIdentifier() || p.tokenAfterImportDefinitelyProducesImportDeclaration()) { + isTypeOnly = true + identifier = nil + if p.isIdentifier() { + identifier = p.parseIdentifier() + } + } + if identifier != nil && !p.tokenAfterImportedIdentifierDefinitelyProducesImportDeclaration() { + return p.parseImportEqualsDeclaration(pos, hasJSDoc, modifiers, identifier, isTypeOnly) + } + importClause := p.tryParseImportClause(identifier, afterImportPos, isTypeOnly, false /*skipJsDocLeadingAsterisks*/) + moduleSpecifier := p.parseModuleSpecifier() + attributes := p.tryParseImportAttributes() + p.parseSemicolon() + result := p.factory.NewImportDeclaration(modifiers, importClause, moduleSpecifier, attributes) + p.finishNode(result, pos) + return result +} + +func (p *Parser) nextTokenIsFromKeywordOrEqualsToken() bool { + p.nextToken() + return p.token == SyntaxKindFromKeyword || p.token == SyntaxKindEqualsToken +} + +func (p *Parser) tokenAfterImportDefinitelyProducesImportDeclaration() bool { + return p.token == SyntaxKindAsteriskToken || p.token == SyntaxKindOpenBraceToken +} + +func (p *Parser) tokenAfterImportedIdentifierDefinitelyProducesImportDeclaration() bool { + // In `import id ___`, the current token decides whether to produce + // an ImportDeclaration or ImportEqualsDeclaration. + return p.token == SyntaxKindCommaToken || p.token == SyntaxKindFromKeyword +} + +func (p *Parser) parseImportEqualsDeclaration(pos int, hasJSDoc bool, modifiers *Node, identifier *Node, isTypeOnly bool) *Node { + p.parseExpected(SyntaxKindEqualsToken) + moduleReference := p.parseModuleReference() + p.parseSemicolon() + result := p.factory.NewImportEqualsDeclaration(modifiers, isTypeOnly, identifier, moduleReference) + p.finishNode(result, pos) + _ = hasJSDoc + return result +} + +func (p *Parser) parseModuleReference() *Node { + if p.token == SyntaxKindRequireKeyword && p.lookAhead(p.nextTokenIsOpenParen) { + return p.parseExternalModuleReference() + } + return p.parseEntityName(false /*allowReservedWords*/, nil /*diagnosticMessage*/) +} + +func (p *Parser) parseExternalModuleReference() *Node { + pos := p.nodePos() + p.parseExpected(SyntaxKindRequireKeyword) + p.parseExpected(SyntaxKindOpenParenToken) + expression := p.parseModuleSpecifier() + p.parseExpected(SyntaxKindCloseParenToken) + result := p.factory.NewExternalModuleReference(expression) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseModuleSpecifier() *Expression { + if p.token == SyntaxKindStringLiteral { + result := p.parseLiteralExpression() + p.internIdentifier(getTextOfIdentifierOrLiteral(result)) + return result + } + // We allow arbitrary expressions here, even though the grammar only allows string + // literals. We check to ensure that it is only a string literal later in the grammar + // check pass. + return p.parseExpression() +} + +func (p *Parser) tryParseImportClause(identifier *Node, pos int, isTypeOnly bool, skipJsDocLeadingAsterisks bool) *Node { + // ImportDeclaration: + // import ImportClause from ModuleSpecifier ; + // import ModuleSpecifier; + if identifier != nil || p.token == SyntaxKindAsteriskToken || p.token == SyntaxKindOpenBraceToken { + importClause := p.parseImportClause(identifier, pos, isTypeOnly, skipJsDocLeadingAsterisks) + p.parseExpected(SyntaxKindFromKeyword) + return importClause + } + return nil +} + +func (p *Parser) parseImportClause(identifier *Node, pos int, isTypeOnly bool, skipJsDocLeadingAsterisks bool) *Node { + // ImportClause: + // ImportedDefaultBinding + // NameSpaceImport + // NamedImports + // ImportedDefaultBinding, NameSpaceImport + // ImportedDefaultBinding, NamedImports + // If there was no default import or if there is comma token after default import + // parse namespace or named imports + var namedBindings *Node + if identifier == nil || p.parseOptional(SyntaxKindCommaToken) { + _ = skipJsDocLeadingAsterisks + // !!! if (skipJsDocLeadingAsterisks) scanner.setSkipJsDocLeadingAsterisks(true); + if p.token == SyntaxKindAsteriskToken { + namedBindings = p.parseNamespaceImport() + } else { + namedBindings = p.parseNamedImports() + } + // !!! if (skipJsDocLeadingAsterisks) scanner.setSkipJsDocLeadingAsterisks(false); + } + result := p.factory.NewImportClause(isTypeOnly, identifier, namedBindings) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseNamespaceImport() *Node { + // NameSpaceImport: + // * as ImportedBinding + pos := p.nodePos() + p.parseExpected(SyntaxKindAsteriskToken) + p.parseExpected(SyntaxKindAsKeyword) + name := p.parseIdentifier() + result := p.factory.NewNamespaceImport(name) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseNamedImports() *Node { + pos := p.nodePos() + // NamedImports: + // { } + // { ImportsList } + // { ImportsList, } + imports := parseBracketedList(p, PCImportOrExportSpecifiers, p.parseImportSpecifier, SyntaxKindOpenBraceToken, SyntaxKindCloseBraceToken) + result := p.factory.NewNamedImports(imports) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseImportSpecifier() *Node { + pos := p.nodePos() + isTypeOnly, propertyName, name := p.parseImportOrExportSpecifier(SyntaxKindImportSpecifier) + var identifierName *Node + if name.kind == SyntaxKindIdentifier { + identifierName = name + } else { + p.parseErrorAtRange(p.skipRangeTrivia(name.loc), diagnostics.Identifier_expected) + identifierName = p.newIdentifier("") + p.finishNode(identifierName, name.Pos()) + } + result := p.factory.NewImportSpecifier(isTypeOnly, propertyName, identifierName) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseImportOrExportSpecifier(kind SyntaxKind) (isTypeOnly bool, propertyName *Node, name *Node) { + // ImportSpecifier: + // BindingIdentifier + // ModuleExportName as BindingIdentifier + // ExportSpecifier: + // ModuleExportName + // ModuleExportName as ModuleExportName + // let checkIdentifierIsKeyword = isKeyword(token()) && !isIdentifier(); + // let checkIdentifierStart = scanner.getTokenStart(); + // let checkIdentifierEnd = scanner.getTokenEnd(); + canParseAsKeyword := true + name = p.parseModuleExportName(false /*disallowKeywords*/) + if name.kind == SyntaxKindIdentifier && name.AsIdentifier().text == "type" { + // If the first token of an import specifier is 'type', there are a lot of possibilities, + // especially if we see 'as' afterwards: + // + // import { type } from "mod"; - isTypeOnly: false, name: type + // import { type as } from "mod"; - isTypeOnly: true, name: as + // import { type as as } from "mod"; - isTypeOnly: false, name: as, propertyName: type + // import { type as as as } from "mod"; - isTypeOnly: true, name: as, propertyName: as + if p.token == SyntaxKindAsKeyword { + // { type as ...? } + firstAs := p.parseIdentifierName() + if p.token == SyntaxKindAsKeyword { + // { type as as ...? } + secondAs := p.parseIdentifierName() + if p.canParseModuleExportName() { + // { type as as something } + // { type as as "something" } + isTypeOnly = true + propertyName = firstAs + name = p.parseModuleExportName(true /*disallowKeywords*/) + canParseAsKeyword = false + } else { + // { type as as } + propertyName = name + name = secondAs + canParseAsKeyword = false + } + } else if p.canParseModuleExportName() { + // { type as something } + // { type as "something" } + propertyName = name + canParseAsKeyword = false + name = p.parseModuleExportName(true /*disallowKeywords*/) + } else { + // { type as } + isTypeOnly = true + name = firstAs + } + } else if p.canParseModuleExportName() { + // { type something ...? } + // { type "something" ...? } + isTypeOnly = true + name = p.parseModuleExportName(true /*disallowKeywords*/) + } + } + if canParseAsKeyword && p.token == SyntaxKindAsKeyword { + propertyName = name + p.parseExpected(SyntaxKindAsKeyword) + name = p.parseModuleExportName(kind == SyntaxKindImportSpecifier /*disallowKeywords*/) + } + return +} + +func (p *Parser) canParseModuleExportName() bool { + return tokenIsIdentifierOrKeyword(p.token) || p.token == SyntaxKindStringLiteral +} + +func (p *Parser) parseModuleExportName(disallowKeywords bool) *Node { + if p.token == SyntaxKindStringLiteral { + return p.parseLiteralExpression() + } + if disallowKeywords && isKeyword(p.token) && !p.isIdentifier() { + p.parseErrorAtCurrentToken(diagnostics.Identifier_expected) + } + return p.parseIdentifierName() +} + +func (p *Parser) tryParseImportAttributes() *Node { + if (p.token == SyntaxKindWithKeyword || p.token == SyntaxKindAssertKeyword) && !p.hasPrecedingLineBreak() { + return p.parseImportAttributes(p.token, true /*skipKeyword*/) + } + return nil +} + +func (p *Parser) parseExportAssignment(pos int, hasJSDoc bool, modifiers *Node) *Node { + saveContextFlags := p.contextFlags + p.setContextFlags(NodeFlagsAwaitContext, true) + isExportEquals := false + if p.parseOptional(SyntaxKindEqualsToken) { + isExportEquals = true + } else { + p.parseExpected(SyntaxKindDefaultKeyword) + } + expression := p.parseAssignmentExpressionOrHigher() + p.parseSemicolon() + p.contextFlags = saveContextFlags + result := p.factory.NewExportAssignment(modifiers, isExportEquals, expression) + p.finishNode(result, pos) + _ = hasJSDoc + return result +} + +func (p *Parser) parseNamespaceExportDeclaration(pos int, hasJSDoc bool, modifiers *Node) *Node { + p.parseExpected(SyntaxKindAsKeyword) + p.parseExpected(SyntaxKindNamespaceKeyword) + name := p.parseIdentifier() + p.parseSemicolon() + // NamespaceExportDeclaration nodes cannot have decorators or modifiers, we attach them here so we can report them in the grammar checker + result := p.factory.NewNamespaceExportDeclaration(modifiers, name) + p.finishNode(result, pos) + _ = hasJSDoc + return result +} + +func (p *Parser) parseExportDeclaration(pos int, hasJSDoc bool, modifiers *Node) *Node { + saveContextFlags := p.contextFlags + p.setContextFlags(NodeFlagsAwaitContext, true) + var exportClause *Node + var moduleSpecifier *Expression + var attributes *Node + isTypeOnly := p.parseOptional(SyntaxKindTypeKeyword) + namespaceExportPos := p.nodePos() + if p.parseOptional(SyntaxKindAsteriskToken) { + if p.parseOptional(SyntaxKindAsKeyword) { + exportClause = p.parseNamespaceExport(namespaceExportPos) + } + p.parseExpected(SyntaxKindFromKeyword) + moduleSpecifier = p.parseModuleSpecifier() + } else { + exportClause = p.parseNamedExports() + // It is not uncommon to accidentally omit the 'from' keyword. Additionally, in editing scenarios, + // the 'from' keyword can be parsed as a named export when the export clause is unterminated (i.e. `export { from "moduleName";`) + // If we don't have a 'from' keyword, see if we have a string literal such that ASI won't take effect. + if p.token == SyntaxKindFromKeyword || (p.token == SyntaxKindStringLiteral && !p.hasPrecedingLineBreak()) { + p.parseExpected(SyntaxKindFromKeyword) + moduleSpecifier = p.parseModuleSpecifier() + } + } + if moduleSpecifier != nil && (p.token == SyntaxKindWithKeyword || p.token == SyntaxKindAssertKeyword) && !p.hasPrecedingLineBreak() { + attributes = p.parseImportAttributes(p.token, true /*skipKeyword*/) + } + p.parseSemicolon() + p.contextFlags = saveContextFlags + result := p.factory.NewExportDeclaration(modifiers, isTypeOnly, exportClause, moduleSpecifier, attributes) + p.finishNode(result, pos) + _ = hasJSDoc + return result +} + +func (p *Parser) parseNamespaceExport(pos int) *Node { + result := p.factory.NewNamespaceExport(p.parseModuleExportName(false /*disallowKeywords*/)) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseNamedExports() *Node { + pos := p.nodePos() + // NamedImports: + // { } + // { ImportsList } + // { ImportsList, } + exports := parseBracketedList(p, PCImportOrExportSpecifiers, p.parseExportSpecifier, SyntaxKindOpenBraceToken, SyntaxKindCloseBraceToken) + result := p.factory.NewNamedExports(exports) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseExportSpecifier() *Node { + pos := p.nodePos() + isTypeOnly, propertyName, name := p.parseImportOrExportSpecifier(SyntaxKindExportSpecifier) + result := p.factory.NewExportSpecifier(isTypeOnly, propertyName, name) + p.finishNode(result, pos) + return result +} + +// TYPES + +func (p *Parser) parseType() *TypeNode { + saveContextFlags := p.contextFlags + p.setContextFlags(NodeFlagsTypeExcludesFlags, false) + var typeNode *TypeNode + if p.isStartOfFunctionTypeOrConstructorType() { + typeNode = p.parseFunctionOrConstructorType() + } else { + pos := p.nodePos() + typeNode = p.parseUnionTypeOrHigher() + if !p.inDisallowConditionalTypesContext() && !p.hasPrecedingLineBreak() && p.parseOptional(SyntaxKindExtendsKeyword) { + // The type following 'extends' is not permitted to be another conditional type + extendsType := doInContext(p, NodeFlagsDisallowConditionalTypesContext, true, p.parseType) + p.parseExpected(SyntaxKindQuestionToken) + trueType := doInContext(p, NodeFlagsDisallowConditionalTypesContext, false, p.parseType) + p.parseExpected(SyntaxKindColonToken) + falseType := doInContext(p, NodeFlagsDisallowConditionalTypesContext, false, p.parseType) + conditionalType := p.factory.NewConditionalTypeNode(typeNode, extendsType, trueType, falseType) + p.finishNode(conditionalType, pos) + typeNode = conditionalType + } + } + p.contextFlags = saveContextFlags + return typeNode +} + +func (p *Parser) parseUnionTypeOrHigher() *TypeNode { + return p.parseUnionOrIntersectionType(SyntaxKindBarToken, p.parseIntersectionTypeOrHigher) +} + +func (p *Parser) parseIntersectionTypeOrHigher() *TypeNode { + return p.parseUnionOrIntersectionType(SyntaxKindAmpersandToken, p.parseTypeOperatorOrHigher) +} + +func (p *Parser) parseUnionOrIntersectionType(operator SyntaxKind, parseConstituentType func() *TypeNode) *TypeNode { + pos := p.nodePos() + isUnionType := operator == SyntaxKindBarToken + hasLeadingOperator := p.parseOptional(operator) + var typeNode *TypeNode + if hasLeadingOperator { + typeNode = p.parseFunctionOrConstructorTypeToError(isUnionType, parseConstituentType) + } else { + typeNode = parseConstituentType() + } + if p.token == operator || hasLeadingOperator { + types := []*TypeNode{typeNode} + for p.parseOptional(operator) { + types = append(types, p.parseFunctionOrConstructorTypeToError(isUnionType, parseConstituentType)) + } + typeNode = p.createUnionOrIntersectionTypeNode(operator, types) + p.finishNode(typeNode, pos) + } + return typeNode +} + +func (p *Parser) createUnionOrIntersectionTypeNode(operator SyntaxKind, types []*TypeNode) *Node { + switch operator { + case SyntaxKindBarToken: + return p.factory.NewUnionTypeNode(types) + case SyntaxKindAmpersandToken: + return p.factory.NewIntersectionTypeNode(types) + default: + panic("Unhandled case in createUnionOrIntersectionType") + } +} + +func (p *Parser) parseTypeOperatorOrHigher() *TypeNode { + operator := p.token + switch operator { + case SyntaxKindKeyOfKeyword, SyntaxKindUniqueKeyword, SyntaxKindReadonlyKeyword: + return p.parseTypeOperator(operator) + case SyntaxKindInferKeyword: + return p.parseInferType() + } + return doInContext(p, NodeFlagsDisallowConditionalTypesContext, false, p.parsePostfixTypeOrHigher) +} + +func (p *Parser) parseTypeOperator(operator SyntaxKind) *Node { + pos := p.nodePos() + p.parseExpected(operator) + result := p.factory.NewTypeOperatorNode(operator, p.parseTypeOperatorOrHigher()) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseInferType() *Node { + pos := p.nodePos() + p.parseExpected(SyntaxKindInferKeyword) + result := p.factory.NewInferTypeNode(p.parseTypeParameterOfInferType()) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseTypeParameterOfInferType() *Node { + pos := p.nodePos() + name := p.parseIdentifier() + constraint := p.tryParseConstraintOfInferType() + result := p.factory.NewTypeParameterDeclaration(nil /*modifiers*/, name, constraint, nil /*defaultType*/) + p.finishNode(result, pos) + return result +} + +func (p *Parser) tryParseConstraintOfInferType() *Node { + state := p.mark() + if p.parseOptional(SyntaxKindExtendsKeyword) { + constraint := doInContext(p, NodeFlagsDisallowConditionalTypesContext, true, p.parseType) + if p.inDisallowConditionalTypesContext() || p.token != SyntaxKindQuestionToken { + return constraint + } + } + p.rewind(state) + return nil +} + +func (p *Parser) parsePostfixTypeOrHigher() *Node { + pos := p.nodePos() + typeNode := p.parseNonArrayType() + for !p.hasPrecedingLineBreak() { + switch p.token { + case SyntaxKindExclamationToken: + p.nextToken() + typeNode = p.factory.NewJSDocNonNullableType(typeNode, true /*postfix*/) + p.finishNode(typeNode, pos) + case SyntaxKindQuestionToken: + // If next token is start of a type we have a conditional type + if p.lookAhead(p.nextIsStartOfType) { + return typeNode + } + p.nextToken() + typeNode = p.factory.NewJSDocNullableType(typeNode, true /*postfix*/) + p.finishNode(typeNode, pos) + case SyntaxKindOpenBracketToken: + p.parseExpected(SyntaxKindOpenBracketToken) + if p.isStartOfType(false /*isStartOfParameter*/) { + indexType := p.parseType() + p.parseExpected(SyntaxKindCloseBracketToken) + typeNode = p.factory.NewIndexedAccessTypeNode(typeNode, indexType) + p.finishNode(typeNode, pos) + } else { + p.parseExpected(SyntaxKindCloseBracketToken) + typeNode = p.factory.NewArrayTypeNode(typeNode) + p.finishNode(typeNode, pos) + } + default: + return typeNode + } + } + return typeNode +} + +func (p *Parser) nextIsStartOfType() bool { + p.nextToken() + return p.isStartOfType(false /*inStartOfParameter*/) +} + +func (p *Parser) parseNonArrayType() *Node { + switch p.token { + case SyntaxKindAnyKeyword, SyntaxKindUnknownKeyword, SyntaxKindStringKeyword, SyntaxKindNumberKeyword, SyntaxKindBigIntKeyword, + SyntaxKindSymbolKeyword, SyntaxKindBooleanKeyword, SyntaxKindUndefinedKeyword, SyntaxKindNeverKeyword, SyntaxKindObjectKeyword: + state := p.mark() + keywordTypeNode := p.parseKeywordTypeNode() + // If these are followed by a dot then parse these out as a dotted type reference instead + if p.token != SyntaxKindDotToken { + return keywordTypeNode + } + p.rewind(state) + return p.parseTypeReference() + // !!! + // case SyntaxKindAsteriskEqualsToken: + // // If there is '*=', treat it as * followed by postfix = + // p.scanner.reScanAsteriskEqualsToken() + // fallthrough + // case SyntaxKindAsteriskToken: + // return p.parseJSDocAllType() + // case SyntaxKindQuestionQuestionToken: + // // If there is '??', treat it as prefix-'?' in JSDoc type. + // p.scanner.reScanQuestionToken() + // fallthrough + // case SyntaxKindQuestionToken: + // return p.parseJSDocUnknownOrNullableType() + // case SyntaxKindFunctionKeyword: + // return p.parseJSDocFunctionType() + // case SyntaxKindExclamationToken: + // return p.parseJSDocNonNullableType() + case SyntaxKindNoSubstitutionTemplateLiteral, SyntaxKindStringLiteral, SyntaxKindNumericLiteral, SyntaxKindBigintLiteral, SyntaxKindTrueKeyword, + SyntaxKindFalseKeyword, SyntaxKindNullKeyword: + return p.parseLiteralTypeNode(false /*negative*/) + case SyntaxKindMinusToken: + if p.lookAhead(p.nextTokenIsNumericOrBigIntLiteral) { + return p.parseLiteralTypeNode(true /*negative*/) + } + return p.parseTypeReference() + case SyntaxKindVoidKeyword: + return p.parseKeywordTypeNode() + case SyntaxKindThisKeyword: + thisKeyword := p.parseThisTypeNode() + if p.token == SyntaxKindIsKeyword && !p.hasPrecedingLineBreak() { + return p.parseThisTypePredicate(thisKeyword) + } + return thisKeyword + case SyntaxKindTypeOfKeyword: + if p.lookAhead(p.nextIsStartOfTypeOfImportType) { + return p.parseImportType() + } + return p.parseTypeQuery() + case SyntaxKindOpenBraceToken: + if p.lookAhead(p.nextIsStartOfMappedType) { + return p.parseMappedType() + } + return p.parseTypeLiteral() + case SyntaxKindOpenBracketToken: + return p.parseTupleType() + case SyntaxKindOpenParenToken: + return p.parseParenthesizedType() + case SyntaxKindImportKeyword: + return p.parseImportType() + case SyntaxKindAssertsKeyword: + if p.lookAhead(p.nextTokenIsIdentifierOrKeywordOnSameLine) { + return p.parseAssertsTypePredicate() + } + return p.parseTypeReference() + case SyntaxKindTemplateHead: + return p.parseTemplateType() + default: + return p.parseTypeReference() + } +} + +func (p *Parser) parseKeywordTypeNode() *Node { + pos := p.nodePos() + result := p.factory.NewKeywordTypeNode(p.token) + p.nextToken() + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseThisTypeNode() *Node { + pos := p.nodePos() + p.nextToken() + result := p.factory.NewThisTypeNode() + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseThisTypePredicate(lhs *Node) *Node { + p.nextToken() + result := p.factory.NewTypePredicateNode(nil /*assertsModifier*/, lhs, p.parseType()) + p.finishNode(result, lhs.Pos()) + return result +} + +func (p *Parser) parseLiteralTypeNode(negative bool) *Node { + pos := p.nodePos() + if negative { + p.nextToken() + } + var expression *Expression + if p.token == SyntaxKindTrueKeyword || p.token == SyntaxKindFalseKeyword || p.token == SyntaxKindNullKeyword { + expression = p.parseKeywordExpression() + } else { + expression = p.parseLiteralExpression() + } + if negative { + expression = p.factory.NewPrefixUnaryExpression(SyntaxKindMinusToken, expression) + p.finishNode(expression, pos) + } + result := p.factory.NewLiteralTypeNode(expression) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseTypeReference() *Node { + pos := p.nodePos() + result := p.factory.NewTypeReferenceNode(p.parseEntityNameOfTypeReference(), p.parseTypeArgumentsOfTypeReference()) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseEntityNameOfTypeReference() *Node { + return p.parseEntityName(true /*allowReservedWords*/, diagnostics.Type_expected) +} + +func (p *Parser) parseEntityName(allowReservedWords bool, diagnosticMessage *diagnostics.Message) *Node { + pos := p.nodePos() + var entity *Node + if allowReservedWords { + entity = p.parseIdentifierNameWithDiagnostic(diagnosticMessage) + } else { + entity = p.parseIdentifierWithDiagnostic(diagnosticMessage, nil) + } + for p.parseOptional(SyntaxKindDotToken) { + if p.token == SyntaxKindLessThanToken { + // The entity is part of a JSDoc-style generic. We will use the gap between `typeName` and + // `typeArguments` to report it as a grammar error in the checker. + break + } + entity = p.factory.NewQualifiedName(entity, p.parseRightSideOfDot(allowReservedWords, false /*allowPrivateIdentifiers*/, true /*allowUnicodeEscapeSequenceInIdentifierName*/)) + p.finishNode(entity, pos) + } + return entity +} + +func (p *Parser) parseRightSideOfDot(allowIdentifierNames bool, allowPrivateIdentifiers bool, allowUnicodeEscapeSequenceInIdentifierName bool) *Node { + // Technically a keyword is valid here as all identifiers and keywords are identifier names. + // However, often we'll encounter this in error situations when the identifier or keyword + // is actually starting another valid construct. + // + // So, we check for the following specific case: + // + // name. + // identifierOrKeyword identifierNameOrKeyword + // + // Note: the newlines are important here. For example, if that above code + // were rewritten into: + // + // name.identifierOrKeyword + // identifierNameOrKeyword + // + // Then we would consider it valid. That's because ASI would take effect and + // the code would be implicitly: "name.identifierOrKeyword; identifierNameOrKeyword". + // In the first case though, ASI will not take effect because there is not a + // line terminator after the identifier or keyword. + if p.hasPrecedingLineBreak() && tokenIsIdentifierOrKeyword(p.token) && p.lookAhead(p.nextTokenIsIdentifierOrKeywordOnSameLine) { + // Report that we need an identifier. However, report it right after the dot, + // and not on the next token. This is because the next token might actually + // be an identifier and the error would be quite confusing. + p.parseErrorAt(p.nodePos(), p.nodePos(), diagnostics.Identifier_expected) + return p.createMissingIdentifier() + } + if p.token == SyntaxKindPrivateIdentifier { + node := p.parsePrivateIdentifier() + if allowPrivateIdentifiers { + return node + } + p.parseErrorAt(p.nodePos(), p.nodePos(), diagnostics.Identifier_expected) + return p.createMissingIdentifier() + } + if allowIdentifierNames { + if allowUnicodeEscapeSequenceInIdentifierName { + return p.parseIdentifierName() + } + return p.parseIdentifierNameErrorOnUnicodeEscapeSequence() + } + + return p.parseIdentifier() +} + +func (p *Parser) newIdentifier(text string) *Node { + return p.factory.NewIdentifier(text) +} + +func (p *Parser) createMissingIdentifier() *Node { + result := p.newIdentifier("") + p.finishNode(result, p.nodePos()) + return result +} + +func (p *Parser) parsePrivateIdentifier() *Node { + pos := p.nodePos() + text := p.scanner.TokenValue() + p.internIdentifier(text) + p.nextToken() + result := p.factory.NewPrivateIdentifier(text) + p.finishNode(result, pos) + return result +} + +func (p *Parser) reScanLessThanToken() SyntaxKind { + p.token = p.scanner.ReScanLessThanToken() + return p.token +} + +func (p *Parser) reScanGreaterThanToken() SyntaxKind { + p.token = p.scanner.ReScanGreaterThanToken() + return p.token +} + +func (p *Parser) reScanSlashToken() SyntaxKind { + p.token = p.scanner.ReScanSlashToken() + return p.token +} + +func (p *Parser) reScanTemplateToken(isTaggedTemplate bool) SyntaxKind { + p.token = p.scanner.ReScanTemplateToken(isTaggedTemplate) + return p.token +} + +func (p *Parser) parseTypeArgumentsOfTypeReference() *Node { + if !p.hasPrecedingLineBreak() && p.reScanLessThanToken() == SyntaxKindLessThanToken { + return p.parseTypeArguments() + } + return nil +} + +func (p *Parser) parseTypeArguments() *Node { + if p.token == SyntaxKindLessThanToken { + pos := p.nodePos() + typeArguments := parseBracketedList(p, PCTypeArguments, p.parseType, SyntaxKindLessThanToken, SyntaxKindGreaterThanToken) + if typeArguments != nil { + result := p.factory.NewTypeArgumentList(typeArguments) + p.finishNode(result, pos) + return result + } + } + return nil +} + +func (p *Parser) nextIsStartOfTypeOfImportType() bool { + p.nextToken() + return p.token == SyntaxKindImportKeyword +} + +func (p *Parser) parseImportType() *Node { + p.sourceFlags |= NodeFlagsPossiblyContainsDynamicImport + pos := p.nodePos() + isTypeOf := p.parseOptional(SyntaxKindTypeOfKeyword) + p.parseExpected(SyntaxKindImportKeyword) + p.parseExpected(SyntaxKindOpenParenToken) + typeNode := p.parseType() + var attributes *Node + if p.parseOptional(SyntaxKindCommaToken) { + openBracePosition := p.scanner.TokenStart() + p.parseExpected(SyntaxKindOpenBraceToken) + currentToken := p.token + if currentToken == SyntaxKindWithKeyword || currentToken == SyntaxKindAssertKeyword { + p.nextToken() + } else { + p.parseErrorAtCurrentToken(diagnostics.X_0_expected, TokenToString(SyntaxKindWithKeyword)) + } + p.parseExpected(SyntaxKindColonToken) + attributes = p.parseImportAttributes(currentToken, true /*skipKeyword*/) + if !p.parseExpected(SyntaxKindCloseBraceToken) { + if len(p.diagnostics) != 0 { + lastDiagnostic := p.diagnostics[len(p.diagnostics)-1] + if lastDiagnostic.Code() == diagnostics.X_0_expected.Code() { + related := NewDiagnostic(nil, NewTextRange(openBracePosition, openBracePosition+1), diagnostics.The_parser_expected_to_find_a_1_to_match_the_0_token_here, "{", "}") + addRelatedInfo(lastDiagnostic, related) + } + } + } + } + p.parseExpected(SyntaxKindCloseParenToken) + var qualifier *Node + if p.parseOptional(SyntaxKindDotToken) { + qualifier = p.parseEntityNameOfTypeReference() + } + typeArguments := p.parseTypeArgumentsOfTypeReference() + result := p.factory.NewImportTypeNode(isTypeOf, typeNode, attributes, qualifier, typeArguments) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseImportAttribute() *Node { + pos := p.nodePos() + var name *Node + if tokenIsIdentifierOrKeyword(p.token) { + name = p.parseIdentifierName() + } else if p.token == SyntaxKindStringLiteral { + name = p.parseLiteralExpression() + } + if name != nil { + p.parseExpected(SyntaxKindColonToken) + } else { + p.parseErrorAtCurrentToken(diagnostics.Identifier_or_string_literal_expected) + } + value := p.parseAssignmentExpressionOrHigher() + result := p.factory.NewImportAttribute(name, value) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseImportAttributes(token SyntaxKind, skipKeyword bool) *Node { + pos := p.nodePos() + if !skipKeyword { + p.parseExpected(token) + } + var elements []*Node + var multiLine bool + openBracePosition := p.scanner.TokenStart() + if p.parseExpected(SyntaxKindOpenBraceToken) { + multiLine = p.hasPrecedingLineBreak() + elements = parseDelimitedList(p, PCImportAttributes, p.parseImportAttribute) + if !p.parseExpected(SyntaxKindCloseBraceToken) { + if len(p.diagnostics) != 0 { + lastDiagnostic := p.diagnostics[len(p.diagnostics)-1] + if lastDiagnostic.Code() == diagnostics.X_0_expected.Code() { + related := NewDiagnostic(nil, NewTextRange(openBracePosition, openBracePosition+1), diagnostics.The_parser_expected_to_find_a_1_to_match_the_0_token_here, "{", "}") + addRelatedInfo(lastDiagnostic, related) + } + } + } + } + result := p.factory.NewImportAttributes(token, elements, multiLine) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseTypeQuery() *Node { + pos := p.nodePos() + p.parseExpected(SyntaxKindTypeOfKeyword) + entityName := p.parseEntityName(true /*allowReservedWords*/, nil) + // Make sure we perform ASI to prevent parsing the next line's type arguments as part of an instantiation expression + var typeArguments *Node + if !p.hasPrecedingLineBreak() { + typeArguments = p.parseTypeArguments() + } + result := p.factory.NewTypeQueryNode(entityName, typeArguments) + p.finishNode(result, pos) + return result +} + +func (p *Parser) nextIsStartOfMappedType() bool { + p.nextToken() + if p.token == SyntaxKindPlusToken || p.token == SyntaxKindMinusToken { + return p.nextToken() == SyntaxKindReadonlyKeyword + } + if p.token == SyntaxKindReadonlyKeyword { + p.nextToken() + } + return p.token == SyntaxKindOpenBracketToken && p.nextTokenIsIdentifier() && p.nextToken() == SyntaxKindInKeyword +} + +func (p *Parser) parseMappedType() *Node { + pos := p.nodePos() + p.parseExpected(SyntaxKindOpenBraceToken) + var readonlyToken *Node // ReadonlyKeyword | PlusToken | MinusToken + if p.token == SyntaxKindReadonlyKeyword || p.token == SyntaxKindPlusToken || p.token == SyntaxKindMinusToken { + readonlyToken = p.parseTokenNode() + if readonlyToken.kind != SyntaxKindReadonlyKeyword { + p.parseExpected(SyntaxKindReadonlyKeyword) + } + } + p.parseExpected(SyntaxKindOpenBracketToken) + typeParameter := p.parseMappedTypeParameter() + var nameType *TypeNode + if p.parseOptional(SyntaxKindAsKeyword) { + nameType = p.parseType() + } + p.parseExpected(SyntaxKindCloseBracketToken) + var questionToken *Node // QuestionToken | PlusToken | MinusToken + if p.token == SyntaxKindQuestionToken || p.token == SyntaxKindPlusToken || p.token == SyntaxKindMinusToken { + questionToken = p.parseTokenNode() + if questionToken.kind != SyntaxKindQuestionToken { + p.parseExpected(SyntaxKindQuestionToken) + } + } + typeNode := p.parseTypeAnnotation() + p.parseSemicolon() + members := parseList(p, PCTypeMembers, p.parseTypeMember) + p.parseExpected(SyntaxKindCloseBraceToken) + result := p.factory.NewMappedTypeNode(readonlyToken, typeParameter, nameType, questionToken, typeNode, members) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseMappedTypeParameter() *Node { + pos := p.nodePos() + name := p.parseIdentifierName() + p.parseExpected(SyntaxKindInKeyword) + typeNode := p.parseType() + result := p.factory.NewTypeParameterDeclaration(nil /*modifiers*/, name, typeNode, nil /*defaultType*/) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseTypeMember() *Node { + if p.token == SyntaxKindOpenParenToken || p.token == SyntaxKindLessThanToken { + return p.parseSignatureMember(SyntaxKindCallSignature) + } + if p.token == SyntaxKindNewKeyword && p.lookAhead(p.nextTokenIsOpenParenOrLessThan) { + return p.parseSignatureMember(SyntaxKindConstructSignature) + } + pos := p.nodePos() + hasJSDoc := p.hasPrecedingJSDocComment() + modifiers := p.parseModifiers() + if p.parseContextualModifier(SyntaxKindGetKeyword) { + return p.parseAccessorDeclaration(pos, hasJSDoc, modifiers, SyntaxKindGetAccessor, SignatureFlagsType) + } + if p.parseContextualModifier(SyntaxKindSetKeyword) { + return p.parseAccessorDeclaration(pos, hasJSDoc, modifiers, SyntaxKindSetAccessor, SignatureFlagsType) + } + if p.isIndexSignature() { + return p.parseIndexSignatureDeclaration(pos, hasJSDoc, modifiers) + } + return p.parsePropertyOrMethodSignature(pos, hasJSDoc, modifiers) +} + +func (p *Parser) nextTokenIsOpenParenOrLessThan() bool { + p.nextToken() + return p.token == SyntaxKindOpenParenToken || p.token == SyntaxKindLessThanToken +} + +func (p *Parser) parseSignatureMember(kind SyntaxKind) *Node { + pos := p.nodePos() + // hasJSDoc := p.hasPrecedingJSDocComment() + if kind == SyntaxKindConstructSignature { + p.parseExpected(SyntaxKindNewKeyword) + } + typeParameters := p.parseTypeParameters() + parameters := p.parseParameters(SignatureFlagsType) + typeNode := p.parseReturnType(SyntaxKindColonToken /*isType*/, true) + p.parseTypeMemberSemicolon() + var result *Node + if kind == SyntaxKindCallSignature { + result = p.factory.NewCallSignatureDeclaration(typeParameters, parameters, typeNode) + } else { + result = p.factory.NewConstructSignatureDeclaration(typeParameters, parameters, typeNode) + } + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseTypeParameters() *Node { + if p.token == SyntaxKindLessThanToken { + pos := p.nodePos() + typeParameters := parseBracketedList(p, PCTypeParameters, p.parseTypeParameter, SyntaxKindLessThanToken, SyntaxKindGreaterThanToken) + if typeParameters != nil { + result := p.factory.NewTypeParameterList(typeParameters) + p.finishNode(result, pos) + return result + } + } + return nil +} + +func (p *Parser) parseTypeParameter() *Node { + pos := p.nodePos() + modifiers := p.parseModifiersWithOptions(false /*allowDecorators*/, true /*permitConstAsModifier*/, false /*stopOnStartOfClassStaticBlock*/) + name := p.parseIdentifier() + var constraint *TypeNode + var expression *Expression + if p.parseOptional(SyntaxKindExtendsKeyword) { + // It's not uncommon for people to write improper constraints to a generic. If the + // user writes a constraint that is an expression and not an actual type, then parse + // it out as an expression (so we can recover well), but report that a type is needed + // instead. + if p.isStartOfType(false /*inStartOfParameter*/) || !p.isStartOfExpression() { + constraint = p.parseType() + } else { + // It was not a type, and it looked like an expression. Parse out an expression + // here so we recover well. Note: it is important that we call parseUnaryExpression + // and not parseExpression here. If the user has: + // + // + // + // We do *not* want to consume the `>` as we're consuming the expression for "". + expression = p.parseUnaryExpressionOrHigher() + } + } + var defaultType *TypeNode + if p.parseOptional(SyntaxKindEqualsToken) { + defaultType = p.parseType() + } + result := p.factory.NewTypeParameterDeclaration(modifiers, name, constraint, defaultType) + result.AsTypeParameter().expression = expression + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseParameters(flags SignatureFlags) []*Node { + // FormalParameters [Yield,Await]: (modified) + // [empty] + // FormalParameterList[?Yield,Await] + // + // FormalParameter[Yield,Await]: (modified) + // BindingElement[?Yield,Await] + // + // BindingElement [Yield,Await]: (modified) + // SingleNameBinding[?Yield,?Await] + // BindingPattern[?Yield,?Await]Initializer [In, ?Yield,?Await] opt + // + // SingleNameBinding [Yield,Await]: + // BindingIdentifier[?Yield,?Await]Initializer [In, ?Yield,?Await] opt + if p.parseExpected(SyntaxKindOpenParenToken) { + parameters := p.parseParametersWorker(flags, true /*allowAmbiguity*/) + p.parseExpected(SyntaxKindCloseParenToken) + return parameters + } + return nil +} + +func (p *Parser) parseParametersWorker(flags SignatureFlags, allowAmbiguity bool) []*Node { + // FormalParameters [Yield,Await]: (modified) + // [empty] + // FormalParameterList[?Yield,Await] + // + // FormalParameter[Yield,Await]: (modified) + // BindingElement[?Yield,Await] + // + // BindingElement [Yield,Await]: (modified) + // SingleNameBinding[?Yield,?Await] + // BindingPattern[?Yield,?Await]Initializer [In, ?Yield,?Await] opt + // + // SingleNameBinding [Yield,Await]: + // BindingIdentifier[?Yield,?Await]Initializer [In, ?Yield,?Await] opt + inAwaitContext := p.contextFlags&NodeFlagsAwaitContext != 0 + saveContextFlags := p.contextFlags + p.setContextFlags(NodeFlagsYieldContext, flags&SignatureFlagsYield != 0) + p.setContextFlags(NodeFlagsAwaitContext, flags&SignatureFlagsAwait != 0) + // const parameters = flags & SignatureFlags.JSDoc ? + // parseDelimitedList(ParsingContext.JSDocParameters, parseJSDocParameter) : + parameters := parseDelimitedList(p, PCParameters, func() *Node { + return p.parseParameterWithOptions(inAwaitContext, allowAmbiguity) + }) + p.contextFlags = saveContextFlags + return parameters + +} + +func (p *Parser) parseParameter() *Node { + return p.parseParameterWithOptions(false /*inOuterAwaitContext*/, true /*allowAmbiguity*/) +} + +func (p *Parser) parseParameterWithOptions(inOuterAwaitContext bool, allowAmbiguity bool) *Node { + pos := p.nodePos() + // hasJSDoc := p.hasPrecedingJSDocComment() + // FormalParameter [Yield,Await]: + // BindingElement[?Yield,?Await] + // Decorators are parsed in the outer [Await] context, the rest of the parameter is parsed in the function's [Await] context. + saveContextFlags := p.contextFlags + p.setContextFlags(NodeFlagsAwaitContext, inOuterAwaitContext) + modifiers := p.parseModifiersWithOptions(true /*allowDecorators*/, false /*permitConstAsModifier*/, false /*stopOnStartOfClassStaticBlock*/) + p.contextFlags = saveContextFlags + if p.token == SyntaxKindThisKeyword { + result := p.factory.NewParameterDeclaration( + modifiers, + nil, /*dotDotDotToken*/ + p.createIdentifier(true /*isIdentifier*/), + nil, /*questionToken*/ + p.parseTypeAnnotation(), + nil /*initializer*/) + if modifiers != nil { + p.parseErrorAtRange(modifiers.loc, diagnostics.Neither_decorators_nor_modifiers_may_be_applied_to_this_parameters) + } + p.finishNode(result, pos) + return result + } + dotDotDotToken := p.parseOptionalToken(SyntaxKindDotDotDotToken) + if !allowAmbiguity && !p.isParameterNameStart() { + return nil + } + result := p.factory.NewParameterDeclaration( + modifiers, + dotDotDotToken, + p.parseNameOfParameter(modifiers), + p.parseOptionalToken(SyntaxKindQuestionToken), + p.parseTypeAnnotation(), + p.parseInitializer()) + p.finishNode(result, pos) + return result +} + +func (p *Parser) isParameterNameStart() bool { + // Be permissive about await and yield by calling isBindingIdentifier instead of isIdentifier; disallowing + // them during a speculative parse leads to many more follow-on errors than allowing the function to parse then later + // complaining about the use of the keywords. + return p.isBindingIdentifier() || p.token == SyntaxKindOpenBracketToken || p.token == SyntaxKindOpenBraceToken +} + +func (p *Parser) parseNameOfParameter(modifiers *Node) *Node { + // FormalParameter [Yield,Await]: + // BindingElement[?Yield,?Await] + name := p.parseIdentifierOrPatternWithDiagnostic(diagnostics.Private_identifiers_cannot_be_used_as_parameters) + if name.loc.Len() == 0 && modifiers == nil && isModifierKind(p.token) { + // in cases like + // 'use strict' + // function foo(static) + // isParameter('static') == true, because of isModifier('static') + // however 'static' is not a legal identifier in a strict mode. + // so result of this function will be ParameterDeclaration (flags = 0, name = missing, type = undefined, initializer = undefined) + // and current token will not change => parsing of the enclosing parameter list will last till the end of time (or OOM) + // to avoid this we'll advance cursor to the next token. + p.nextToken() + } + return name +} + +func (p *Parser) parseReturnType(returnToken SyntaxKind, isType bool) *TypeNode { + if p.shouldParseReturnType(returnToken, isType) { + return doInContext(p, NodeFlagsDisallowConditionalTypesContext, false, p.parseTypeOrTypePredicate) + } + return nil +} + +func (p *Parser) shouldParseReturnType(returnToken SyntaxKind, isType bool) bool { + if returnToken == SyntaxKindEqualsGreaterThanToken { + p.parseExpected(returnToken) + return true + } else if p.parseOptional(SyntaxKindColonToken) { + return true + } else if isType && p.token == SyntaxKindEqualsGreaterThanToken { + // This is easy to get backward, especially in type contexts, so parse the type anyway + p.parseErrorAtCurrentToken(diagnostics.X_0_expected, TokenToString(SyntaxKindColonToken)) + p.nextToken() + return true + } + return false +} + +func (p *Parser) parseTypeOrTypePredicate() *TypeNode { + if p.isIdentifier() { + state := p.mark() + pos := p.nodePos() + id := p.parseIdentifier() + if p.token == SyntaxKindIsKeyword && !p.hasPrecedingLineBreak() { + p.nextToken() + result := p.factory.NewTypePredicateNode(nil /*assertsModifier*/, id, p.parseType()) + p.finishNode(result, pos) + return result + } + p.rewind(state) + } + return p.parseType() +} + +func (p *Parser) parseTypeMemberSemicolon() { + // We allow type members to be separated by commas or (possibly ASI) semicolons. + // First check if it was a comma. If so, we're done with the member. + if p.parseOptional(SyntaxKindCommaToken) { + return + } + // Didn't have a comma. We must have a (possible ASI) semicolon. + p.parseSemicolon() +} + +func (p *Parser) parseAccessorDeclaration(pos int, hasJSDoc bool, modifiers *Node, kind SyntaxKind, flags SignatureFlags) *Node { + name := p.parsePropertyName() + typeParameters := p.parseTypeParameters() + parameters := p.parseParameters(SignatureFlagsNone) + returnType := p.parseReturnType(SyntaxKindColonToken, false /*isType*/) + body := p.parseFunctionBlockOrSemicolon(flags, nil /*diagnosticMessage*/) + var result *Node + // Keep track of `typeParameters` (for both) and `type` (for setters) if they were parsed those indicate grammar errors + if kind == SyntaxKindGetAccessor { + result = p.factory.NewGetAccessorDeclaration(modifiers, name, typeParameters, parameters, returnType, body) + } else { + result = p.factory.NewSetAccessorDeclaration(modifiers, name, typeParameters, parameters, returnType, body) + } + p.finishNode(result, pos) + _ = hasJSDoc + return result +} + +func (p *Parser) parsePropertyName() *Node { + return p.parsePropertyNameWorker(true /*allowComputedPropertyNames*/) +} + +func (p *Parser) parsePropertyNameWorker(allowComputedPropertyNames bool) *Node { + if p.token == SyntaxKindStringLiteral || p.token == SyntaxKindNumericLiteral || p.token == SyntaxKindBigintLiteral { + literal := p.parseLiteralExpression() + p.internIdentifier(getTextOfIdentifierOrLiteral(literal)) + return literal + } + if allowComputedPropertyNames && p.token == SyntaxKindOpenBracketToken { + return p.parseComputedPropertyName() + } + if p.token == SyntaxKindPrivateIdentifier { + return p.parsePrivateIdentifier() + } + return p.parseIdentifierName() +} + +func (p *Parser) parseComputedPropertyName() *Node { + // PropertyName [Yield]: + // LiteralPropertyName + // ComputedPropertyName[?Yield] + pos := p.nodePos() + p.parseExpected(SyntaxKindOpenBracketToken) + // We parse any expression (including a comma expression). But the grammar + // says that only an assignment expression is allowed, so the grammar checker + // will error if it sees a comma expression. + expression := p.parseExpressionAllowIn() + p.parseExpected(SyntaxKindCloseBracketToken) + result := p.factory.NewComputedPropertyName(expression) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseFunctionBlockOrSemicolon(flags SignatureFlags, diagnosticMessage *diagnostics.Message) *Node { + if p.token != SyntaxKindOpenBraceToken { + if flags&SignatureFlagsType != 0 { + p.parseTypeMemberSemicolon() + return nil + } + if p.canParseSemicolon() { + p.parseSemicolon() + return nil + } + } + return p.parseFunctionBlock(flags, diagnosticMessage) +} + +func (p *Parser) parseFunctionBlock(flags SignatureFlags, diagnosticMessage *diagnostics.Message) *Node { + saveContextFlags := p.contextFlags + p.setContextFlags(NodeFlagsYieldContext, flags&SignatureFlagsYield != 0) + p.setContextFlags(NodeFlagsAwaitContext, flags&SignatureFlagsAwait != 0) + // We may be in a [Decorator] context when parsing a function expression or + // arrow function. The body of the function is not in [Decorator] context. + p.setContextFlags(NodeFlagsDecoratorContext, false) + block := p.parseBlock(flags&SignatureFlagsIgnoreMissingOpenBrace != 0, diagnosticMessage) + p.contextFlags = saveContextFlags + return block +} + +func (p *Parser) isIndexSignature() bool { + return p.token == SyntaxKindOpenBracketToken && p.lookAhead(p.nextIsUnambiguouslyIndexSignature) +} + +func (p *Parser) nextIsUnambiguouslyIndexSignature() bool { + // The only allowed sequence is: + // + // [id: + // + // However, for error recovery, we also check the following cases: + // + // [... + // [id, + // [id?, + // [id?: + // [id?] + // [public id + // [private id + // [protected id + // [] + // + p.nextToken() + if p.token == SyntaxKindDotDotDotToken || p.token == SyntaxKindCloseBracketToken { + return true + } + if isModifierKind(p.token) { + p.nextToken() + if p.isIdentifier() { + return true + } + } else if !p.isIdentifier() { + return false + } else { + // Skip the identifier + p.nextToken() + } + // A colon signifies a well formed indexer + // A comma should be a badly formed indexer because comma expressions are not allowed + // in computed properties. + if p.token == SyntaxKindColonToken || p.token == SyntaxKindCommaToken { + return true + } + // Question mark could be an indexer with an optional property, + // or it could be a conditional expression in a computed property. + if p.token != SyntaxKindQuestionToken { + return false + } + // If any of the following tokens are after the question mark, it cannot + // be a conditional expression, so treat it as an indexer. + p.nextToken() + return p.token == SyntaxKindColonToken || p.token == SyntaxKindCommaToken || p.token == SyntaxKindCloseBracketToken +} + +func (p *Parser) parseIndexSignatureDeclaration(pos int, hasJSDoc bool, modifiers *Node) *Node { + parameters := parseBracketedList(p, PCParameters, p.parseParameter, SyntaxKindOpenBracketToken, SyntaxKindCloseBracketToken) + typeNode := p.parseTypeAnnotation() + p.parseTypeMemberSemicolon() + result := p.factory.NewIndexSignatureDeclaration(modifiers, parameters, typeNode) + p.finishNode(result, pos) + _ = hasJSDoc + return result +} + +func (p *Parser) parsePropertyOrMethodSignature(pos int, hasJSDoc bool, modifiers *Node) *Node { + _ = hasJSDoc + name := p.parsePropertyName() + questionToken := p.parseOptionalToken(SyntaxKindQuestionToken) + var result *Node + if p.token == SyntaxKindOpenParenToken || p.token == SyntaxKindLessThanToken { + // Method signatures don't exist in expression contexts. So they have neither + // [Yield] nor [Await] + typeParameters := p.parseTypeParameters() + parameters := p.parseParameters(SignatureFlagsType) + returnType := p.parseReturnType(SyntaxKindColonToken /*isType*/, true) + result = p.factory.NewMethodSignatureDeclaration(modifiers, name, questionToken, typeParameters, parameters, returnType) + } else { + typeNode := p.parseTypeAnnotation() + // Although type literal properties cannot not have initializers, we attempt + // to parse an initializer so we can report in the checker that an interface + // property or type literal property cannot have an initializer. + var initializer *Expression + if p.token == SyntaxKindEqualsToken { + initializer = p.parseInitializer() + } + result = p.factory.NewPropertySignatureDeclaration(modifiers, name, questionToken, typeNode, initializer) + } + p.parseTypeMemberSemicolon() + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseTypeLiteral() *Node { + pos := p.nodePos() + result := p.factory.NewTypeLiteralNode(p.parseObjectTypeMembers()) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseObjectTypeMembers() []*Node { + var members []*Node + if p.parseExpected(SyntaxKindOpenBraceToken) { + members = parseList(p, PCTypeMembers, p.parseTypeMember) + p.parseExpected(SyntaxKindCloseBraceToken) + } + return members +} + +func (p *Parser) parseTupleType() *Node { + pos := p.nodePos() + result := p.factory.NewTupleTypeNode(parseBracketedList(p, PCTupleElementTypes, p.parseTupleElementNameOrTupleElementType, SyntaxKindOpenBracketToken, SyntaxKindCloseBracketToken)) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseTupleElementNameOrTupleElementType() *Node { + if p.lookAhead(p.scanStartOfNamedTupleElement) { + pos := p.nodePos() + //hasJSDoc := hasPrecedingJSDocComment(); + dotDotDotToken := p.parseOptionalToken(SyntaxKindDotDotDotToken) + name := p.parseIdentifierName() + questionToken := p.parseOptionalToken(SyntaxKindQuestionToken) + p.parseExpected(SyntaxKindColonToken) + typeNode := p.parseTupleElementType() + result := p.factory.NewNamedTupleTypeMember(dotDotDotToken, name, questionToken, typeNode) + p.finishNode(result, pos) + return result + } + return p.parseTupleElementType() +} + +func (p *Parser) scanStartOfNamedTupleElement() bool { + if p.token == SyntaxKindDotDotDotToken { + return tokenIsIdentifierOrKeyword(p.nextToken()) && p.nextTokenIsColonOrQuestionColon() + } + return tokenIsIdentifierOrKeyword(p.token) && p.nextTokenIsColonOrQuestionColon() +} + +func (p *Parser) nextTokenIsColonOrQuestionColon() bool { + return p.nextToken() == SyntaxKindColonToken || p.token == SyntaxKindQuestionToken && p.nextToken() == SyntaxKindColonToken +} + +func (p *Parser) parseTupleElementType() *TypeNode { + if p.parseOptional(SyntaxKindDotDotDotToken) { + pos := p.nodePos() + result := p.factory.NewRestTypeNode(p.parseType()) + p.finishNode(result, pos) + return result + } + typeNode := p.parseType() + if typeNode.kind == SyntaxKindJSDocNonNullableType { + nullableType := typeNode.data.(*JSDocNonNullableType) + if typeNode.loc.pos == nullableType.typeNode.loc.pos { + result := p.factory.NewOptionalTypeNode(nullableType.typeNode) + result.loc = typeNode.loc + result.flags = typeNode.flags + return result + } + } + return typeNode +} + +func (p *Parser) parseParenthesizedType() *Node { + pos := p.nodePos() + p.parseExpected(SyntaxKindOpenParenToken) + typeNode := p.parseType() + p.parseExpected(SyntaxKindCloseParenToken) + result := p.factory.NewParenthesizedTypeNode(typeNode) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseAssertsTypePredicate() *TypeNode { + pos := p.nodePos() + assertsModifier := p.parseExpectedToken(SyntaxKindAssertsKeyword) + var parameterName *Node + if p.token == SyntaxKindThisKeyword { + parameterName = p.parseThisTypeNode() + } else { + parameterName = p.parseIdentifier() + } + var typeNode *TypeNode + if p.parseOptional(SyntaxKindIsKeyword) { + typeNode = p.parseType() + } + result := p.factory.NewTypePredicateNode(assertsModifier, parameterName, typeNode) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseTemplateType() *Node { + pos := p.nodePos() + result := p.factory.NewTemplateLiteralTypeNode(p.parseTemplateHead(false /*isTaggedTemplate*/), p.parseTemplateTypeSpans()) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseTemplateHead(isTaggedTemplate bool) *Node { + if !isTaggedTemplate && p.scanner.tokenFlags&TokenFlagsIsInvalid != 0 { + p.reScanTemplateToken(false /*isTaggedTemplate*/) + } + pos := p.nodePos() + result := p.factory.NewTemplateHead(p.scanner.tokenValue, p.getTemplateLiteralRawText(2 /*endLength*/), p.scanner.tokenFlags&TokenFlagsTemplateLiteralLikeFlags) + p.nextToken() + p.finishNode(result, pos) + return result +} + +func (p *Parser) getTemplateLiteralRawText(endLength int) string { + tokenText := p.scanner.TokenText() + if p.scanner.tokenFlags&TokenFlagsUnterminated != 0 { + endLength = 0 + } + return tokenText[1 : len(tokenText)-endLength] + +} + +func (p *Parser) parseTemplateTypeSpans() []*Node { + list := []*Node{} + for { + span := p.parseTemplateTypeSpan() + list = append(list, span) + if span.AsTemplateLiteralTypeSpan().literal.kind != SyntaxKindTemplateMiddle { + break + } + } + return list +} + +func (p *Parser) parseTemplateTypeSpan() *Node { + pos := p.nodePos() + result := p.factory.NewTemplateLiteralTypeSpan(p.parseType(), p.parseLiteralOfTemplateSpan(false /*isTaggedTemplate*/)) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseLiteralOfTemplateSpan(isTaggedTemplate bool) *Node { + if p.token == SyntaxKindCloseBraceToken { + p.reScanTemplateToken(isTaggedTemplate) + return p.parseTemplateMiddleOrTail() + } + p.parseErrorAtCurrentToken(diagnostics.X_0_expected, TokenToString(SyntaxKindCloseBraceToken)) + result := p.factory.NewTemplateTail("", "", TokenFlagsNone) + p.finishNode(result, p.nodePos()) + return result +} + +func (p *Parser) parseTemplateMiddleOrTail() *Node { + pos := p.nodePos() + var result *Node + if p.token == SyntaxKindTemplateMiddle { + result = p.factory.NewTemplateMiddle(p.scanner.tokenValue, p.getTemplateLiteralRawText(2 /*endLength*/), p.scanner.tokenFlags&TokenFlagsTemplateLiteralLikeFlags) + } else { + result = p.factory.NewTemplateTail(p.scanner.tokenValue, p.getTemplateLiteralRawText(1 /*endLength*/), p.scanner.tokenFlags&TokenFlagsTemplateLiteralLikeFlags) + } + p.nextToken() + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseFunctionOrConstructorTypeToError(isInUnionType bool, parseConstituentType func() *TypeNode) *TypeNode { + // the function type and constructor type shorthand notation + // are not allowed directly in unions and intersections, but we'll + // try to parse them gracefully and issue a helpful message. + if p.isStartOfFunctionTypeOrConstructorType() { + typeNode := p.parseFunctionOrConstructorType() + var diagnostic *diagnostics.Message + if typeNode.kind == SyntaxKindFunctionType { + diagnostic = ifElse(isInUnionType, + diagnostics.Function_type_notation_must_be_parenthesized_when_used_in_a_union_type, + diagnostics.Function_type_notation_must_be_parenthesized_when_used_in_an_intersection_type) + } else { + diagnostic = ifElse(isInUnionType, + diagnostics.Constructor_type_notation_must_be_parenthesized_when_used_in_a_union_type, + diagnostics.Constructor_type_notation_must_be_parenthesized_when_used_in_an_intersection_type) + } + p.parseErrorAtRange(typeNode.loc, diagnostic) + return typeNode + } + return parseConstituentType() +} + +func (p *Parser) isStartOfFunctionTypeOrConstructorType() bool { + return p.token == SyntaxKindLessThanToken || + p.token == SyntaxKindOpenParenToken && p.lookAhead(p.nextIsUnambiguouslyStartOfFunctionType) || + p.token == SyntaxKindNewKeyword || + p.token == SyntaxKindAbstractKeyword && p.lookAhead(p.nextTokenIsNewKeyword) +} + +func (p *Parser) parseFunctionOrConstructorType() *TypeNode { + pos := p.nodePos() + // hasJSDoc := p.hasPrecedingJSDocComment() + modifiers := p.parseModifiersForConstructorType() + isConstructorType := p.parseOptional(SyntaxKindNewKeyword) + // Debug.assert(!modifiers || isConstructorType, "Per isStartOfFunctionOrConstructorType, a function type cannot have modifiers.") + typeParameters := p.parseTypeParameters() + parameters := p.parseParameters(SignatureFlagsType) + returnType := p.parseReturnType(SyntaxKindEqualsGreaterThanToken, false /*isType*/) + var result *TypeNode + if isConstructorType { + result = p.factory.NewConstructorTypeNode(modifiers, typeParameters, parameters, returnType) + } else { + result = p.factory.NewFunctionTypeNode(typeParameters, parameters, returnType) + } + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseModifiersForConstructorType() *Node { + if p.token == SyntaxKindAbstractKeyword { + pos := p.nodePos() + modifier := p.factory.NewModifier(p.token) + p.nextToken() + p.finishNode(modifier, pos) + result := p.factory.NewModifierList([]*Node{modifier}, ModifierFlagsAbstract) + p.finishNode(result, pos) + return result + } + return nil +} + +func (p *Parser) nextTokenIsNewKeyword() bool { + return p.nextToken() == SyntaxKindNewKeyword +} + +func (p *Parser) nextIsUnambiguouslyStartOfFunctionType() bool { + p.nextToken() + if p.token == SyntaxKindCloseParenToken || p.token == SyntaxKindDotDotDotToken { + // ( ) + // ( ... + return true + } + if p.skipParameterStart() { + // We successfully skipped modifiers (if any) and an identifier or binding pattern, + // now see if we have something that indicates a parameter declaration + if p.token == SyntaxKindColonToken || p.token == SyntaxKindCommaToken || p.token == SyntaxKindQuestionToken || p.token == SyntaxKindEqualsToken { + // ( xxx : + // ( xxx , + // ( xxx ? + // ( xxx = + return true + } + if p.token == SyntaxKindCloseParenToken && p.nextToken() == SyntaxKindEqualsGreaterThanToken { + // ( xxx ) => + return true + } + } + return false +} + +func (p *Parser) skipParameterStart() bool { + if isModifierKind(p.token) { + // Skip modifiers + p.parseModifiers() + } + p.parseOptional(SyntaxKindDotDotDotToken) + if p.isIdentifier() || p.token == SyntaxKindThisKeyword { + p.nextToken() + return true + } + if p.token == SyntaxKindOpenBracketToken || p.token == SyntaxKindOpenBraceToken { + // Return true if we can parse an array or object binding pattern with no errors + previousErrorCount := len(p.diagnostics) + p.parseIdentifierOrPattern() + return previousErrorCount == len(p.diagnostics) + } + return false +} + +func (p *Parser) parseModifiers() *Node { + return p.parseModifiersWithOptions(false, false, false) +} + +func (p *Parser) parseModifiersWithOptions(allowDecorators bool, permitConstAsModifier bool, stopOnStartOfClassStaticBlock bool) *Node { + pos := p.nodePos() + list := []*Node{} + preModifierFlags := ModifierFlagsNone + decoratorFlag := ModifierFlagsNone + postModifierFlags := ModifierFlagsNone + // Decorators should be contiguous in a list of modifiers but can potentially appear in two places (i.e., `[...leadingDecorators, ...leadingModifiers, ...trailingDecorators, ...trailingModifiers]`). + // The leading modifiers *should* only contain `export` and `default` when trailingDecorators are present, but we'll handle errors for any other leading modifiers in the checker. + // It is illegal to have both leadingDecorators and trailingDecorators, but we will report that as a grammar check in the checker. + // parse leading decorators + for { + if allowDecorators && p.token == SyntaxKindAtToken && postModifierFlags == ModifierFlagsNone { + decorator := p.parseDecorator() + list = append(list, decorator) + decoratorFlag |= ModifierFlagsDecorator + } else { + modifier := p.tryParseModifier((preModifierFlags|postModifierFlags)&ModifierFlagsStatic != 0, permitConstAsModifier, stopOnStartOfClassStaticBlock) + if modifier == nil { + break + } + list = append(list, modifier) + flag := modifierToFlag(modifier.kind) + if decoratorFlag == ModifierFlagsNone { + preModifierFlags |= flag + } else { + postModifierFlags |= flag + } + } + } + if len(list) > 0 { + result := p.factory.NewModifierList(list, preModifierFlags|decoratorFlag|postModifierFlags) + p.finishNode(result, pos) + return result + } + return nil +} + +func (p *Parser) parseDecorator() *Node { + pos := p.nodePos() + p.parseExpected(SyntaxKindAtToken) + expression := doInContext(p, NodeFlagsDecoratorContext, true, p.parseDecoratorExpression) + result := p.factory.NewDecorator(expression) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseDecoratorExpression() *Expression { + if p.inAwaitContext() && p.token == SyntaxKindAwaitKeyword { + // `@await` is is disallowed in an [Await] context, but can cause parsing to go off the rails + // This simply parses the missing identifier and moves on. + pos := p.nodePos() + awaitExpression := p.parseIdentifierWithDiagnostic(diagnostics.Expression_expected, nil) + p.nextToken() + memberExpression := p.parseMemberExpressionRest(pos, awaitExpression /*allowOptionalChain*/, true) + return p.parseCallExpressionRest(pos, memberExpression) + } + return p.parseLeftHandSideExpressionOrHigher() +} + +func (p *Parser) tryParseModifier(hasSeenStaticModifier bool, permitConstAsModifier bool, stopOnStartOfClassStaticBlock bool) *Node { + pos := p.nodePos() + kind := p.token + if p.token == SyntaxKindConstKeyword && permitConstAsModifier { + // We need to ensure that any subsequent modifiers appear on the same line + // so that when 'const' is a standalone declaration, we don't issue an error. + if !p.lookAhead(p.nextTokenIsOnSameLineAndCanFollowModifier) { + return nil + } else { + p.nextToken() + } + } else if stopOnStartOfClassStaticBlock && p.token == SyntaxKindStaticKeyword && p.lookAhead(p.nextTokenIsOpenBrace) { + return nil + } else if hasSeenStaticModifier && p.token == SyntaxKindStaticKeyword { + return nil + } else { + if !p.parseAnyContextualModifier() { + return nil + } + } + result := p.factory.NewModifier(kind) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseContextualModifier(t SyntaxKind) bool { + state := p.mark() + if p.token == t && p.nextTokenCanFollowModifier() { + return true + } + p.rewind(state) + return false +} + +func (p *Parser) parseAnyContextualModifier() bool { + state := p.mark() + if isModifierKind(p.token) && p.nextTokenCanFollowModifier() { + return true + } + p.rewind(state) + return false +} + +func (p *Parser) nextTokenCanFollowModifier() bool { + switch p.token { + case SyntaxKindConstKeyword: + // 'const' is only a modifier if followed by 'enum'. + return p.nextToken() == SyntaxKindEnumKeyword + case SyntaxKindExportKeyword: + p.nextToken() + if p.token == SyntaxKindDefaultKeyword { + return p.lookAhead(p.nextTokenCanFollowDefaultKeyword) + } + if p.token == SyntaxKindTypeKeyword { + return p.lookAhead(p.nextTokenCanFollowExportModifier) + } + return p.canFollowExportModifier() + case SyntaxKindDefaultKeyword: + return p.nextTokenCanFollowDefaultKeyword() + case SyntaxKindStaticKeyword, SyntaxKindGetKeyword, SyntaxKindSetKeyword: + p.nextToken() + return p.canFollowModifier() + default: + return p.nextTokenIsOnSameLineAndCanFollowModifier() + } +} + +func (p *Parser) nextTokenCanFollowDefaultKeyword() bool { + switch p.nextToken() { + case SyntaxKindClassKeyword, SyntaxKindFunctionKeyword, SyntaxKindInterfaceKeyword, SyntaxKindAtToken: + return true + case SyntaxKindAbstractKeyword: + return p.lookAhead(p.nextTokenIsClassKeywordOnSameLine) + case SyntaxKindAsyncKeyword: + return p.lookAhead(p.nextTokenIsFunctionKeywordOnSameLine) + } + return false +} + +func (p *Parser) nextTokenIsIdentifierOrKeyword() bool { + return tokenIsIdentifierOrKeyword(p.nextToken()) +} + +func (p *Parser) nextTokenIsIdentifierOrKeywordOrGreaterThan() bool { + return tokenIsIdentifierOrKeywordOrGreaterThan(p.nextToken()) +} + +func (p *Parser) nextTokenIsIdentifierOrKeywordOnSameLine() bool { + return p.nextTokenIsIdentifierOrKeyword() && !p.hasPrecedingLineBreak() +} + +func (p *Parser) nextTokenIsIdentifierOrKeywordOrLiteralOnSameLine() bool { + return (p.nextTokenIsIdentifierOrKeyword() || p.token == SyntaxKindNumericLiteral || p.token == SyntaxKindBigintLiteral || p.token == SyntaxKindStringLiteral) && !p.hasPrecedingLineBreak() +} + +func (p *Parser) nextTokenIsClassKeywordOnSameLine() bool { + return p.nextToken() == SyntaxKindClassKeyword && !p.hasPrecedingLineBreak() +} + +func (p *Parser) nextTokenIsFunctionKeywordOnSameLine() bool { + return p.nextToken() == SyntaxKindFunctionKeyword && !p.hasPrecedingLineBreak() +} + +func (p *Parser) nextTokenCanFollowExportModifier() bool { + p.nextToken() + return p.canFollowExportModifier() +} + +func (p *Parser) canFollowExportModifier() bool { + return p.token == SyntaxKindAtToken || p.token != SyntaxKindAsteriskToken && p.token != SyntaxKindAsKeyword && p.token != SyntaxKindOpenBraceToken && p.canFollowModifier() +} + +func (p *Parser) canFollowModifier() bool { + return p.token == SyntaxKindOpenBracketToken || p.token == SyntaxKindOpenBraceToken || p.token == SyntaxKindAsteriskToken || p.token == SyntaxKindDotDotDotToken || p.isLiteralPropertyName() +} + +func (p *Parser) nextTokenIsOnSameLineAndCanFollowModifier() bool { + p.nextToken() + if p.hasPrecedingLineBreak() { + return false + } + return p.canFollowModifier() +} + +func (p *Parser) nextTokenIsOpenBrace() bool { + return p.nextToken() == SyntaxKindOpenBraceToken +} + +func (p *Parser) parseExpression() *Expression { + // Expression[in]: + // AssignmentExpression[in] + // Expression[in] , AssignmentExpression[in] + + // clear the decorator context when parsing Expression, as it should be unambiguous when parsing a decorator + saveContextFlags := p.contextFlags + p.contextFlags &= ^NodeFlagsDecoratorContext + pos := p.nodePos() + expr := p.parseAssignmentExpressionOrHigher() + for { + operatorToken := p.parseOptionalToken(SyntaxKindCommaToken) + if operatorToken == nil { + break + } + expr = p.makeBinaryExpression(expr, operatorToken, p.parseAssignmentExpressionOrHigher(), pos) + } + p.contextFlags = saveContextFlags + return expr +} + +func (p *Parser) parseExpressionAllowIn() *Expression { + return doInContext(p, NodeFlagsDisallowInContext, false, p.parseExpression) +} + +func (p *Parser) parseAssignmentExpressionOrHigher() *Expression { + return p.parseAssignmentExpressionOrHigherWorker(true /*allowReturnTypeInArrowFunction*/) +} + +func (p *Parser) parseAssignmentExpressionOrHigherWorker(allowReturnTypeInArrowFunction bool) *Expression { + // AssignmentExpression[in,yield]: + // 1) ConditionalExpression[?in,?yield] + // 2) LeftHandSideExpression = AssignmentExpression[?in,?yield] + // 3) LeftHandSideExpression AssignmentOperator AssignmentExpression[?in,?yield] + // 4) ArrowFunctionExpression[?in,?yield] + // 5) AsyncArrowFunctionExpression[in,yield,await] + // 6) [+Yield] YieldExpression[?In] + // + // Note: for ease of implementation we treat productions '2' and '3' as the same thing. + // (i.e. they're both BinaryExpressions with an assignment operator in it). + // First, do the simple check if we have a YieldExpression (production '6'). + if p.isYieldExpression() { + return p.parseYieldExpression() + } + // Then, check if we have an arrow function (production '4' and '5') that starts with a parenthesized + // parameter list or is an async arrow function. + // AsyncArrowFunctionExpression: + // 1) async[no LineTerminator here]AsyncArrowBindingIdentifier[?Yield][no LineTerminator here]=>AsyncConciseBody[?In] + // 2) CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await][no LineTerminator here]=>AsyncConciseBody[?In] + // Production (1) of AsyncArrowFunctionExpression is parsed in "tryParseAsyncSimpleArrowFunctionExpression". + // And production (2) is parsed in "tryParseParenthesizedArrowFunctionExpression". + // + // If we do successfully parse arrow-function, we must *not* recurse for productions 1, 2 or 3. An ArrowFunction is + // not a LeftHandSideExpression, nor does it start a ConditionalExpression. So we are done + // with AssignmentExpression if we see one. + arrowExpression := p.tryParseParenthesizedArrowFunctionExpression(allowReturnTypeInArrowFunction) + if arrowExpression != nil { + return arrowExpression + } + arrowExpression = p.tryParseAsyncSimpleArrowFunctionExpression(allowReturnTypeInArrowFunction) + if arrowExpression != nil { + return arrowExpression + } + // arrowExpression2 := p.tryParseAsyncSimpleArrowFunctionExpression(allowReturnTypeInArrowFunction) + // if arrowExpression2 != nil { + // return arrowExpression2 + // } + // Now try to see if we're in production '1', '2' or '3'. A conditional expression can + // start with a LogicalOrExpression, while the assignment productions can only start with + // LeftHandSideExpressions. + // + // So, first, we try to just parse out a BinaryExpression. If we get something that is a + // LeftHandSide or higher, then we can try to parse out the assignment expression part. + // Otherwise, we try to parse out the conditional expression bit. We want to allow any + // binary expression here, so we pass in the 'lowest' precedence here so that it matches + // and consumes anything. + pos := p.nodePos() + hasJSDoc := p.hasPrecedingJSDocComment() + expr := p.parseBinaryExpressionOrHigher(OperatorPrecedenceLowest) + // To avoid a look-ahead, we did not handle the case of an arrow function with a single un-parenthesized + // parameter ('x => ...') above. We handle it here by checking if the parsed expression was a single + // identifier and the current token is an arrow. + if expr.kind == SyntaxKindIdentifier && p.token == SyntaxKindEqualsGreaterThanToken { + return p.parseSimpleArrowFunctionExpression(pos, expr, allowReturnTypeInArrowFunction, hasJSDoc, nil /*asyncModifier*/) + } + // Now see if we might be in cases '2' or '3'. + // If the expression was a LHS expression, and we have an assignment operator, then + // we're in '2' or '3'. Consume the assignment and return. + // + // Note: we call reScanGreaterToken so that we get an appropriately merged token + // for cases like `> > =` becoming `>>=` + if isLeftHandSideExpressionKind(expr.kind) && isAssignmentOperator(p.reScanGreaterThanToken()) { + return p.makeBinaryExpression(expr, p.parseTokenNode(), p.parseAssignmentExpressionOrHigherWorker(allowReturnTypeInArrowFunction), pos) + } + // It wasn't an assignment or a lambda. This is a conditional expression: + return p.parseConditionalExpressionRest(expr, pos, allowReturnTypeInArrowFunction) +} + +func (p *Parser) isYieldExpression() bool { + if p.token == SyntaxKindYieldKeyword { + // If we have a 'yield' keyword, and this is a context where yield expressions are + // allowed, then definitely parse out a yield expression. + if p.inYieldContext() { + return true + } + + // We're in a context where 'yield expr' is not allowed. However, if we can + // definitely tell that the user was trying to parse a 'yield expr' and not + // just a normal expr that start with a 'yield' identifier, then parse out + // a 'yield expr'. We can then report an error later that they are only + // allowed in generator expressions. + // + // for example, if we see 'yield(foo)', then we'll have to treat that as an + // invocation expression of something called 'yield'. However, if we have + // 'yield foo' then that is not legal as a normal expression, so we can + // definitely recognize this as a yield expression. + // + // for now we just check if the next token is an identifier. More heuristics + // can be added here later as necessary. We just need to make sure that we + // don't accidentally consume something legal. + return p.lookAhead(p.nextTokenIsIdentifierOrKeywordOrLiteralOnSameLine) + } + return false +} + +func (p *Parser) parseYieldExpression() *Node { + pos := p.nodePos() + // YieldExpression[In] : + // yield + // yield [no LineTerminator here] [Lexical goal InputElementRegExp]AssignmentExpression[?In, Yield] + // yield [no LineTerminator here] * [Lexical goal InputElementRegExp]AssignmentExpression[?In, Yield] + p.nextToken() + var result *Node + if !p.hasPrecedingLineBreak() && (p.token == SyntaxKindAsteriskToken || p.isStartOfExpression()) { + result = p.factory.NewYieldExpression(p.parseOptionalToken(SyntaxKindAsteriskToken), p.parseAssignmentExpressionOrHigher()) + } else { + // if the next token is not on the same line as yield. or we don't have an '*' or + // the start of an expression, then this is just a simple "yield" expression. + result = p.factory.NewYieldExpression(nil /*asteriskToken*/, nil /*expression*/) + } + p.finishNode(result, pos) + return result +} + +func (p *Parser) isParenthesizedArrowFunctionExpression() Tristate { + if p.token == SyntaxKindOpenParenToken || p.token == SyntaxKindLessThanToken || p.token == SyntaxKindAsyncKeyword { + state := p.mark() + result := p.nextIsParenthesizedArrowFunctionExpression() + p.rewind(state) + return result + } + if p.token == SyntaxKindEqualsGreaterThanToken { + // ERROR RECOVERY TWEAK: + // If we see a standalone => try to parse it as an arrow function expression as that's + // likely what the user intended to write. + return TSTrue + } + // Definitely not a parenthesized arrow function. + return TSFalse +} + +func (p *Parser) nextIsParenthesizedArrowFunctionExpression() Tristate { + if p.token == SyntaxKindAsyncKeyword { + p.nextToken() + if p.hasPrecedingLineBreak() { + return TSFalse + } + if p.token != SyntaxKindOpenParenToken && p.token != SyntaxKindLessThanToken { + return TSFalse + } + } + first := p.token + second := p.nextToken() + if first == SyntaxKindOpenParenToken { + if second == SyntaxKindCloseParenToken { + // Simple cases: "() =>", "(): ", and "() {". + // This is an arrow function with no parameters. + // The last one is not actually an arrow function, + // but this is probably what the user intended. + third := p.nextToken() + switch third { + case SyntaxKindEqualsGreaterThanToken, SyntaxKindColonToken, SyntaxKindOpenBraceToken: + return TSTrue + } + return TSFalse + } + // If encounter "([" or "({", this could be the start of a binding pattern. + // Examples: + // ([ x ]) => { } + // ({ x }) => { } + // ([ x ]) + // ({ x }) + if second == SyntaxKindOpenBracketToken || second == SyntaxKindOpenBraceToken { + return TSUnknown + } + // Simple case: "(..." + // This is an arrow function with a rest parameter. + if second == SyntaxKindDotDotDotToken { + return TSTrue + } + // Check for "(xxx yyy", where xxx is a modifier and yyy is an identifier. This + // isn't actually allowed, but we want to treat it as a lambda so we can provide + // a good error message. + if isModifierKind(second) && second != SyntaxKindAsyncKeyword && p.lookAhead(p.nextTokenIsIdentifier) { + if p.nextToken() == SyntaxKindAsKeyword { + // https://github.com/microsoft/TypeScript/issues/44466 + return TSFalse + } + return TSTrue + } + // If we had "(" followed by something that's not an identifier, + // then this definitely doesn't look like a lambda. "this" is not + // valid, but we want to parse it and then give a semantic error. + if !p.isIdentifier() && second != SyntaxKindThisKeyword { + return TSFalse + } + switch p.nextToken() { + case SyntaxKindColonToken: + // If we have something like "(a:", then we must have a + // type-annotated parameter in an arrow function expression. + return TSTrue + case SyntaxKindQuestionToken: + p.nextToken() + // If we have "(a?:" or "(a?," or "(a?=" or "(a?)" then it is definitely a lambda. + if p.token == SyntaxKindColonToken || p.token == SyntaxKindCommaToken || p.token == SyntaxKindEqualsToken || p.token == SyntaxKindCloseParenToken { + return TSTrue + } + // Otherwise it is definitely not a lambda. + return TSFalse + case SyntaxKindCommaToken, SyntaxKindEqualsToken, SyntaxKindCloseParenToken: + // If we have "(a," or "(a=" or "(a)" this *could* be an arrow function + return TSUnknown + } + // It is definitely not an arrow function + return TSFalse + } else { + // !!! Debug.assert(first == SyntaxKindLessThanToken) + // If we have "<" not followed by an identifier, + // then this definitely is not an arrow function. + if !p.isIdentifier() && p.token != SyntaxKindConstKeyword { + return TSFalse + } + // JSX overrides + if p.languageVariant == LanguageVariantJSX { + isArrowFunctionInJsx := p.lookAhead(func() bool { + p.parseOptional(SyntaxKindConstKeyword) + third := p.nextToken() + if third == SyntaxKindExtendsKeyword { + fourth := p.nextToken() + switch fourth { + case SyntaxKindEqualsToken, SyntaxKindGreaterThanToken, SyntaxKindSlashToken: + return false + } + return true + } else if third == SyntaxKindCommaToken || third == SyntaxKindEqualsToken { + return true + } + return false + }) + if isArrowFunctionInJsx { + return TSTrue + } + return TSFalse + } + // This *could* be a parenthesized arrow function. + return TSUnknown + } +} + +func (p *Parser) tryParseParenthesizedArrowFunctionExpression(allowReturnTypeInArrowFunction bool) *Node { + tristate := p.isParenthesizedArrowFunctionExpression() + if tristate == TSFalse { + // It's definitely not a parenthesized arrow function expression. + return nil + } + // If we definitely have an arrow function, then we can just parse one, not requiring a + // following => or { token. Otherwise, we *might* have an arrow function. Try to parse + // it out, but don't allow any ambiguity, and return 'undefined' if this could be an + // expression instead. + if tristate == TSTrue { + return p.parseParenthesizedArrowFunctionExpression(true /*allowAmbiguity*/, true /*allowReturnTypeInArrowFunction*/) + } + state := p.mark() + result := p.parsePossibleParenthesizedArrowFunctionExpression(allowReturnTypeInArrowFunction) + if result == nil { + p.rewind(state) + } + return result +} + +func (p *Parser) parseParenthesizedArrowFunctionExpression(allowAmbiguity bool, allowReturnTypeInArrowFunction bool) *Node { + pos := p.nodePos() + // hasJSDoc := p.hasPrecedingJSDocComment() + modifiers := p.parseModifiersForArrowFunction() + isAsync := hasAsyncModifier(modifiers) + signatureFlags := ifElse(isAsync, SignatureFlagsAwait, SignatureFlagsNone) + // Arrow functions are never generators. + // + // If we're speculatively parsing a signature for a parenthesized arrow function, then + // we have to have a complete parameter list. Otherwise we might see something like + // a => (b => c) + // And think that "(b =>" was actually a parenthesized arrow function with a missing + // close paren. + typeParameters := p.parseTypeParameters() + var parameters []*Node + if !p.parseExpected(SyntaxKindOpenParenToken) { + if !allowAmbiguity { + return nil + } + } else { + if !allowAmbiguity { + maybeParameters := p.parseParametersWorker(signatureFlags, allowAmbiguity) + if maybeParameters == nil { + return nil + } + parameters = maybeParameters + } else { + parameters = p.parseParametersWorker(signatureFlags, allowAmbiguity) + } + if !p.parseExpected(SyntaxKindCloseParenToken) && !allowAmbiguity { + return nil + } + } + hasReturnColon := p.token == SyntaxKindColonToken + returnType := p.parseReturnType(SyntaxKindColonToken /*isType*/, false) + if returnType != nil && !allowAmbiguity && typeHasArrowFunctionBlockingParseError(returnType) { + return nil + } + // Parsing a signature isn't enough. + // Parenthesized arrow signatures often look like other valid expressions. + // For instance: + // - "(x = 10)" is an assignment expression parsed as a signature with a default parameter value. + // - "(x,y)" is a comma expression parsed as a signature with two parameters. + // - "a ? (b): c" will have "(b):" parsed as a signature with a return type annotation. + // - "a ? (b): function() {}" will too, since function() is a valid JSDoc function type. + // - "a ? (b): (function() {})" as well, but inside of a parenthesized type with an arbitrary amount of nesting. + // + // So we need just a bit of lookahead to ensure that it can only be a signature. + unwrappedType := returnType + for unwrappedType != nil && unwrappedType.kind == SyntaxKindParenthesizedType { + unwrappedType = unwrappedType.AsParenthesizedTypeNode().typeNode // Skip parens if need be + } + hasJSDocFunctionType := unwrappedType != nil && unwrappedType.kind == SyntaxKindJSDocFunctionType + if !allowAmbiguity && p.token != SyntaxKindEqualsGreaterThanToken && (hasJSDocFunctionType || p.token != SyntaxKindOpenBraceToken) { + // Returning undefined here will cause our caller to rewind to where we started from. + return nil + } + // If we have an arrow, then try to parse the body. Even if not, try to parse if we + // have an opening brace, just in case we're in an error state. + lastToken := p.token + equalsGreaterThanToken := p.parseExpectedToken(SyntaxKindEqualsGreaterThanToken) + var body *Node + if lastToken == SyntaxKindEqualsGreaterThanToken || lastToken == SyntaxKindOpenBraceToken { + body = p.parseArrowFunctionExpressionBody(isAsync, allowReturnTypeInArrowFunction) + } else { + body = p.parseIdentifier() + } + // Given: + // x ? y => ({ y }) : z => ({ z }) + // We try to parse the body of the first arrow function by looking at: + // ({ y }) : z => ({ z }) + // This is a valid arrow function with "z" as the return type. + // + // But, if we're in the true side of a conditional expression, this colon + // terminates the expression, so we cannot allow a return type if we aren't + // certain whether or not the preceding text was parsed as a parameter list. + // + // For example, + // a() ? (b: number, c?: string): void => d() : e + // is determined by isParenthesizedArrowFunctionExpression to unambiguously + // be an arrow expression, so we allow a return type. + if !allowReturnTypeInArrowFunction && hasReturnColon { + // However, if the arrow function we were able to parse is followed by another colon + // as in: + // a ? (x): string => x : null + // Then allow the arrow function, and treat the second colon as terminating + // the conditional expression. It's okay to do this because this code would + // be a syntax error in JavaScript (as the second colon shouldn't be there). + if p.token != SyntaxKindColonToken { + return nil + } + } + result := p.factory.NewArrowFunction(modifiers, typeParameters, parameters, returnType, equalsGreaterThanToken, body) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseModifiersForArrowFunction() *Node { + if p.token == SyntaxKindAsyncKeyword { + pos := p.nodePos() + p.nextToken() + modifier := p.factory.NewModifier(SyntaxKindAsyncKeyword) + p.finishNode(modifier, pos) + result := p.factory.NewModifierList([]*Node{modifier}, ModifierFlagsAsync) + p.finishNode(modifier, pos) + return result + } + return nil +} + +// If true, we should abort parsing an error function. +func typeHasArrowFunctionBlockingParseError(node *TypeNode) bool { + switch node.kind { + case SyntaxKindTypeReference: + return nodeIsMissing(node.AsTypeReference().typeName) + case SyntaxKindFunctionType, SyntaxKindConstructorType: + return node.FunctionLikeData().parameters == nil || typeHasArrowFunctionBlockingParseError(node.FunctionLikeData().returnType) + case SyntaxKindParenthesizedType: + return typeHasArrowFunctionBlockingParseError(node.AsParenthesizedTypeNode().typeNode) + } + return false +} + +func (p *Parser) parseArrowFunctionExpressionBody(isAsync bool, allowReturnTypeInArrowFunction bool) *Node { + if p.token == SyntaxKindOpenBraceToken { + return p.parseFunctionBlock(ifElse(isAsync, SignatureFlagsAwait, SignatureFlagsNone), nil /*diagnosticMessage*/) + } + if p.token != SyntaxKindSemicolonToken && p.token != SyntaxKindFunctionKeyword && p.token != SyntaxKindClassKeyword && p.isStartOfStatement() && !p.isStartOfExpressionStatement() { + // Check if we got a plain statement (i.e. no expression-statements, no function/class expressions/declarations) + // + // Here we try to recover from a potential error situation in the case where the + // user meant to supply a block. For example, if the user wrote: + // + // a => + // let v = 0; + // } + // + // they may be missing an open brace. Check to see if that's the case so we can + // try to recover better. If we don't do this, then the next close curly we see may end + // up preemptively closing the containing construct. + // + // Note: even when 'IgnoreMissingOpenBrace' is passed, parseBody will still error. + return p.parseFunctionBlock(SignatureFlagsIgnoreMissingOpenBrace|ifElse(isAsync, SignatureFlagsAwait, SignatureFlagsNone), nil /*diagnosticMessage*/) + } + saveContextFlags := p.contextFlags + p.setContextFlags(NodeFlagsAwaitContext, isAsync) + node := p.parseAssignmentExpressionOrHigherWorker(allowReturnTypeInArrowFunction) + p.contextFlags = saveContextFlags + return node +} + +func (p *Parser) isStartOfExpressionStatement() bool { + // As per the grammar, none of '{' or 'function' or 'class' can start an expression statement. + return p.token != SyntaxKindOpenBraceToken && p.token != SyntaxKindFunctionKeyword && p.token != SyntaxKindClassKeyword && p.token != SyntaxKindAtToken && p.isStartOfExpression() +} + +func (p *Parser) parsePossibleParenthesizedArrowFunctionExpression(allowReturnTypeInArrowFunction bool) *Node { + tokenPos := p.scanner.TokenStart() + if p.notParenthesizedArrow[tokenPos] { + return nil + } + result := p.parseParenthesizedArrowFunctionExpression(false /*allowAmbiguity*/, allowReturnTypeInArrowFunction) + if result == nil { + if p.notParenthesizedArrow == nil { + p.notParenthesizedArrow = make(map[int]bool) + } + p.notParenthesizedArrow[tokenPos] = true + } + return result +} + +func (p *Parser) tryParseAsyncSimpleArrowFunctionExpression(allowReturnTypeInArrowFunction bool) *Node { + // We do a check here so that we won't be doing unnecessarily call to "lookAhead" + if p.token == SyntaxKindAsyncKeyword && p.lookAhead(p.nextIsUnParenthesizedAsyncArrowFunction) { + pos := p.nodePos() + hasJSDoc := p.hasPrecedingJSDocComment() + asyncModifier := p.parseModifiersForArrowFunction() + expr := p.parseBinaryExpressionOrHigher(OperatorPrecedenceLowest) + return p.parseSimpleArrowFunctionExpression(pos, expr, allowReturnTypeInArrowFunction, hasJSDoc, asyncModifier) + } + return nil +} + +func (p *Parser) nextIsUnParenthesizedAsyncArrowFunction() bool { + // AsyncArrowFunctionExpression: + // 1) async[no LineTerminator here]AsyncArrowBindingIdentifier[?Yield][no LineTerminator here]=>AsyncConciseBody[?In] + // 2) CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await][no LineTerminator here]=>AsyncConciseBody[?In] + if p.token == SyntaxKindAsyncKeyword { + p.nextToken() + // If the "async" is followed by "=>" token then it is not a beginning of an async arrow-function + // but instead a simple arrow-function which will be parsed inside "parseAssignmentExpressionOrHigher" + if p.hasPrecedingLineBreak() || p.token == SyntaxKindEqualsGreaterThanToken { + return false + } + // Check for un-parenthesized AsyncArrowFunction + expr := p.parseBinaryExpressionOrHigher(OperatorPrecedenceLowest) + if !p.hasPrecedingLineBreak() && expr.kind == SyntaxKindIdentifier && p.token == SyntaxKindEqualsGreaterThanToken { + return true + } + } + return false +} + +func (p *Parser) parseSimpleArrowFunctionExpression(pos int, identifier *Node, allowReturnTypeInArrowFunction bool, hasJSDoc bool, asyncModifier *Node) *Node { + //Debug.assert(token() == SyntaxKindEqualsGreaterThanToken, "parseSimpleArrowFunctionExpression should only have been called if we had a =>"); + parameter := p.factory.NewParameterDeclaration(nil /*modifiers*/, nil /*dotDotDotToken*/, identifier, nil /*questionToken*/, nil /*typeNode*/, nil /*initializer*/) + p.finishNode(parameter, identifier.Pos()) + parameters := []*Node{parameter} + equalsGreaterThanToken := p.parseExpectedToken(SyntaxKindEqualsGreaterThanToken) + body := p.parseArrowFunctionExpressionBody(asyncModifier != nil /*isAsync*/, allowReturnTypeInArrowFunction) + result := p.factory.NewArrowFunction(asyncModifier, nil /*typeParameters*/, parameters, nil /*returnType*/, equalsGreaterThanToken, body) + p.finishNode(result, pos) + _ = hasJSDoc + return result +} + +func (p *Parser) parseConditionalExpressionRest(leftOperand *Expression, pos int, allowReturnTypeInArrowFunction bool) *Expression { + // Note: we are passed in an expression which was produced from parseBinaryExpressionOrHigher. + questionToken := p.parseOptionalToken(SyntaxKindQuestionToken) + if questionToken == nil { + return leftOperand + } + // Note: we explicitly 'allowIn' in the whenTrue part of the condition expression, and + // we do not that for the 'whenFalse' part. + saveContextFlags := p.contextFlags + p.setContextFlags(NodeFlagsDisallowInContext, false) + trueExpression := p.parseAssignmentExpressionOrHigherWorker(false /*allowReturnTypeInArrowFunction*/) + p.contextFlags = saveContextFlags + colonToken := p.parseExpectedToken(SyntaxKindColonToken) + var falseExpression *Expression + if colonToken != nil { + falseExpression = p.parseAssignmentExpressionOrHigherWorker(allowReturnTypeInArrowFunction) + } else { + p.parseErrorAtCurrentToken(diagnostics.X_0_expected, TokenToString(SyntaxKindColonToken)) + falseExpression = p.createMissingIdentifier() + } + result := p.factory.NewConditionalExpression(leftOperand, questionToken, trueExpression, colonToken, falseExpression) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseBinaryExpressionOrHigher(precedence OperatorPrecedence) *Expression { + pos := p.nodePos() + leftOperand := p.parseUnaryExpressionOrHigher() + return p.parseBinaryExpressionRest(precedence, leftOperand, pos) +} + +func (p *Parser) parseBinaryExpressionRest(precedence OperatorPrecedence, leftOperand *Expression, pos int) *Expression { + for { + // We either have a binary operator here, or we're finished. We call + // reScanGreaterToken so that we merge token sequences like > and = into >= + p.reScanGreaterThanToken() + newPrecedence := getBinaryOperatorPrecedence(p.token) + // Check the precedence to see if we should "take" this operator + // - For left associative operator (all operator but **), consume the operator, + // recursively call the function below, and parse binaryExpression as a rightOperand + // of the caller if the new precedence of the operator is greater then or equal to the current precedence. + // For example: + // a - b - c; + // ^token; leftOperand = b. Return b to the caller as a rightOperand + // a * b - c + // ^token; leftOperand = b. Return b to the caller as a rightOperand + // a - b * c; + // ^token; leftOperand = b. Return b * c to the caller as a rightOperand + // - For right associative operator (**), consume the operator, recursively call the function + // and parse binaryExpression as a rightOperand of the caller if the new precedence of + // the operator is strictly grater than the current precedence + // For example: + // a ** b ** c; + // ^^token; leftOperand = b. Return b ** c to the caller as a rightOperand + // a - b ** c; + // ^^token; leftOperand = b. Return b ** c to the caller as a rightOperand + // a ** b - c + // ^token; leftOperand = b. Return b to the caller as a rightOperand + var consumeCurrentOperator bool + if p.token == SyntaxKindAsteriskAsteriskToken { + consumeCurrentOperator = newPrecedence >= precedence + } else { + consumeCurrentOperator = newPrecedence > precedence + } + if !consumeCurrentOperator { + break + } + if p.token == SyntaxKindInKeyword && p.inDisallowInContext() { + break + } + if p.token == SyntaxKindAsKeyword || p.token == SyntaxKindSatisfiesKeyword { + // Make sure we *do* perform ASI for constructs like this: + // var x = foo + // as (Bar) + // This should be parsed as an initialized variable, followed + // by a function call to 'as' with the argument 'Bar' + if p.hasPrecedingLineBreak() { + break + } else { + keywordKind := p.token + p.nextToken() + if keywordKind == SyntaxKindSatisfiesKeyword { + leftOperand = p.makeSatisfiesExpression(leftOperand, p.parseType()) + } else { + leftOperand = p.makeAsExpression(leftOperand, p.parseType()) + } + } + } else { + leftOperand = p.makeBinaryExpression(leftOperand, p.parseTokenNode(), p.parseBinaryExpressionOrHigher(newPrecedence), pos) + } + } + return leftOperand +} + +func (p *Parser) makeSatisfiesExpression(expression *Expression, typeNode *TypeNode) *Node { + result := p.factory.NewSatisfiesExpression(expression, typeNode) + p.finishNode(result, expression.Pos()) + return result +} + +func (p *Parser) makeAsExpression(left *Expression, right *TypeNode) *Node { + result := p.factory.NewAsExpression(left, right) + p.finishNode(result, left.Pos()) + return result +} + +func (p *Parser) makeBinaryExpression(left *Expression, operatorToken *Node, right *Expression, pos int) *Node { + result := p.factory.NewBinaryExpression(left, operatorToken, right) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseUnaryExpressionOrHigher() *Expression { + // ES7 UpdateExpression: + // 1) LeftHandSideExpression[?Yield] + // 2) LeftHandSideExpression[?Yield][no LineTerminator here]++ + // 3) LeftHandSideExpression[?Yield][no LineTerminator here]-- + // 4) ++UnaryExpression[?Yield] + // 5) --UnaryExpression[?Yield] + if p.isUpdateExpression() { + pos := p.nodePos() + updateExpression := p.parseUpdateExpression() + if p.token == SyntaxKindAsteriskAsteriskToken { + return p.parseBinaryExpressionRest(getBinaryOperatorPrecedence(p.token), updateExpression, pos) + } + return updateExpression + } + // ES7 UnaryExpression: + // 1) UpdateExpression[?yield] + // 2) delete UpdateExpression[?yield] + // 3) void UpdateExpression[?yield] + // 4) typeof UpdateExpression[?yield] + // 5) + UpdateExpression[?yield] + // 6) - UpdateExpression[?yield] + // 7) ~ UpdateExpression[?yield] + // 8) ! UpdateExpression[?yield] + unaryOperator := p.token + simpleUnaryExpression := p.parseSimpleUnaryExpression() + if p.token == SyntaxKindAsteriskAsteriskToken { + pos := skipTrivia(p.sourceText, simpleUnaryExpression.Pos()) + end := simpleUnaryExpression.End() + if simpleUnaryExpression.kind == SyntaxKindTypeAssertionExpression { + p.parseErrorAt(pos, end, diagnostics.A_type_assertion_expression_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses) + } else { + //Debug.assert(isKeywordOrPunctuation(unaryOperator)) + p.parseErrorAt(pos, end, diagnostics.An_unary_expression_with_the_0_operator_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses, TokenToString(unaryOperator)) + } + } + return simpleUnaryExpression +} + +func (p *Parser) isUpdateExpression() bool { + switch p.token { + case SyntaxKindPlusToken, SyntaxKindMinusToken, SyntaxKindTildeToken, SyntaxKindExclamationToken, SyntaxKindDeleteKeyword, SyntaxKindTypeOfKeyword, SyntaxKindVoidKeyword, SyntaxKindAwaitKeyword: + return false + case SyntaxKindLessThanToken: + return p.languageVariant == LanguageVariantJSX + } + return true +} + +func (p *Parser) parseUpdateExpression() *Expression { + pos := p.nodePos() + if p.token == SyntaxKindPlusPlusToken || p.token == SyntaxKindMinusMinusToken { + operator := p.token + p.nextToken() + result := p.factory.NewPrefixUnaryExpression(operator, p.parseLeftHandSideExpressionOrHigher()) + p.finishNode(result, pos) + return result + } else if p.languageVariant == LanguageVariantJSX && p.token == SyntaxKindLessThanToken && p.lookAhead(p.nextTokenIsIdentifierOrKeywordOrGreaterThan) { + // JSXElement is part of primaryExpression + return p.parseJsxElementOrSelfClosingElementOrFragment(true /*inExpressionContext*/, -1 /*topInvalidNodePosition*/, nil /*openingTag*/, false /*mustBeUnary*/) + } + expression := p.parseLeftHandSideExpressionOrHigher() + if (p.token == SyntaxKindPlusPlusToken || p.token == SyntaxKindMinusMinusToken) && !p.hasPrecedingLineBreak() { + operator := p.token + p.nextToken() + result := p.factory.NewPostfixUnaryExpression(expression, operator) + p.finishNode(result, pos) + return result + } + return expression +} + +func (p *Parser) parseJsxElementOrSelfClosingElementOrFragment(inExpressionContext bool, topInvalidNodePosition int, openingTag *Node, mustBeUnary bool) *Expression { + pos := p.nodePos() + opening := p.parseJsxOpeningOrSelfClosingElementOrOpeningFragment(inExpressionContext) + var result *Expression + switch opening.kind { + case SyntaxKindJsxOpeningElement: + children := p.parseJsxChildren(opening) + var closingElement *Node + lastChild := lastElement(children) + if lastChild != nil && lastChild.kind == SyntaxKindJsxElement && + !tagNamesAreEquivalent(lastChild.AsJsxElement().openingElement.AsJsxOpeningElement().tagName, lastChild.AsJsxElement().closingElement.AsJsxClosingElement().tagName) && + tagNamesAreEquivalent(opening.AsJsxOpeningElement().tagName, lastChild.AsJsxElement().closingElement.AsJsxClosingElement().tagName) { + // when an unclosed JsxOpeningElement incorrectly parses its parent's JsxClosingElement, + // restructure (
(......
)) --> (
(......)
) + // (no need to error; the parent will error) + newClosingElement := p.factory.NewJsxClosingElement(p.createMissingIdentifier()) + p.finishNode(newClosingElement, p.nodePos()) + newLast := p.factory.NewJsxElement(lastChild.AsJsxElement().openingElement, lastChild.AsJsxElement().children, newClosingElement) + p.finishNode(newLast, lastChild.AsJsxElement().openingElement.Pos()) + children = append(children[0:len(children)-1], newLast) + closingElement = lastChild.AsJsxElement().closingElement + } else { + closingElement = p.parseJsxClosingElement(opening, inExpressionContext) + if !tagNamesAreEquivalent(opening.AsJsxOpeningElement().tagName, closingElement.AsJsxClosingElement().tagName) { + if openingTag != nil && isJsxOpeningElement(openingTag) && tagNamesAreEquivalent(closingElement.AsJsxClosingElement().tagName, openingTag.AsJsxOpeningElement().tagName) { + // opening incorrectly matched with its parent's closing -- put error on opening + p.parseErrorAtRange(opening.AsJsxOpeningElement().tagName.loc, diagnostics.JSX_element_0_has_no_corresponding_closing_tag, getTextOfNodeFromSourceText(p.sourceText, opening.AsJsxOpeningElement().tagName)) + } else { + // other opening/closing mismatches -- put error on closing + p.parseErrorAtRange(closingElement.AsJsxClosingElement().tagName.loc, diagnostics.Expected_corresponding_JSX_closing_tag_for_0, getTextOfNodeFromSourceText(p.sourceText, opening.AsJsxOpeningElement().tagName)) + } + } + } + result = p.factory.NewJsxElement(opening, children, closingElement) + p.finishNode(result, pos) + case SyntaxKindJsxOpeningFragment: + result = p.factory.NewJsxFragment(opening, p.parseJsxChildren(opening), p.parseJsxClosingFragment(inExpressionContext)) + p.finishNode(result, pos) + case SyntaxKindJsxSelfClosingElement: + // Nothing else to do for self-closing elements + result = opening + default: + panic("Unhandled case in parseJsxElementOrSelfClosingElementOrFragment") + } + // If the user writes the invalid code '
' in an expression context (i.e. not wrapped in + // an enclosing tag), we'll naively try to parse ^ this as a 'less than' operator and the remainder of the tag + // as garbage, which will cause the formatter to badly mangle the JSX. Perform a speculative parse of a JSX + // element if we see a < token so that we can wrap it in a synthetic binary expression so the formatter + // does less damage and we can report a better error. + // Since JSX elements are invalid < operands anyway, this lookahead parse will only occur in error scenarios + // of one sort or another. + // If we are in a unary context, we can't do this recovery; the binary expression we return here is not + // a valid UnaryExpression and will cause problems later. + if !mustBeUnary && inExpressionContext && p.token == SyntaxKindLessThanToken { + topBadPos := topInvalidNodePosition + if topBadPos < 0 { + topBadPos = result.Pos() + } + invalidElement := p.parseJsxElementOrSelfClosingElementOrFragment( /*inExpressionContext*/ true, topBadPos, nil, false) + operatorToken := p.factory.NewToken(SyntaxKindCommaToken) + operatorToken.loc = NewTextRange(invalidElement.Pos(), invalidElement.Pos()) + p.parseErrorAt(skipTrivia(p.sourceText, topBadPos), invalidElement.End(), diagnostics.JSX_expressions_must_have_one_parent_element) + result = p.factory.NewBinaryExpression(result, operatorToken, invalidElement) + p.finishNode(result, pos) + } + return result +} + +func (p *Parser) parseJsxChildren(openingTag *Expression) []*Expression { + saveParsingContexts := p.parsingContexts + p.parsingContexts |= 1 << PCJsxChildren + list := []*Expression{} + for { + currentToken := p.scanner.reScanJsxToken(true /*allowMultilineJsxText*/) + child := p.parseJsxChild(openingTag, currentToken) + if child == nil { + break + } + list = append(list, child) + if isJsxOpeningElement(openingTag) && child.kind == SyntaxKindJsxElement && + !tagNamesAreEquivalent(child.AsJsxElement().openingElement.AsJsxOpeningElement().tagName, child.AsJsxElement().closingElement.AsJsxClosingElement().tagName) && + tagNamesAreEquivalent(openingTag.AsJsxOpeningElement().tagName, child.AsJsxElement().closingElement.AsJsxClosingElement().tagName) { + // stop after parsing a mismatched child like
...(
) in order to reattach the higher + break + } + } + p.parsingContexts = saveParsingContexts + return list +} + +func (p *Parser) parseJsxChild(openingTag *Node, token SyntaxKind) *Expression { + switch token { + case SyntaxKindEndOfFile: + // If we hit EOF, issue the error at the tag that lacks the closing element + // rather than at the end of the file (which is useless) + if isJsxOpeningFragment(openingTag) { + p.parseErrorAtRange(openingTag.loc, diagnostics.JSX_fragment_has_no_corresponding_closing_tag) + } else { + // We want the error span to cover only 'Foo.Bar' in < Foo.Bar > + // or to cover only 'Foo' in < Foo > + tag := openingTag.AsJsxOpeningElement().tagName + start := min(skipTrivia(p.sourceText, tag.Pos()), tag.End()) + p.parseErrorAt(start, tag.End(), diagnostics.JSX_element_0_has_no_corresponding_closing_tag, + getTextOfNodeFromSourceText(p.sourceText, openingTag.AsJsxOpeningElement().tagName)) + } + return nil + case SyntaxKindLessThanSlashToken, SyntaxKindConflictMarkerTrivia: + return nil + case SyntaxKindJsxText, SyntaxKindJsxTextAllWhiteSpaces: + return p.parseJsxText() + case SyntaxKindOpenBraceToken: + return p.parseJsxExpression(false /*inExpressionContext*/) + case SyntaxKindLessThanToken: + return p.parseJsxElementOrSelfClosingElementOrFragment(false /*inExpressionContext*/, -1 /*topInvalidNodePosition*/, openingTag, false) + } + panic("Unhandled case in parseJsxChild") +} + +func (p *Parser) parseJsxText() *Node { + pos := p.nodePos() + result := p.factory.NewJsxText(p.scanner.tokenValue, p.token == SyntaxKindJsxTextAllWhiteSpaces) + p.scanJsxText() + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseJsxExpression(inExpressionContext bool) *Node { + pos := p.nodePos() + if !p.parseExpected(SyntaxKindOpenBraceToken) { + return nil + } + var dotDotDotToken *Node + var expression *Expression + if p.token != SyntaxKindCloseBraceToken { + if !inExpressionContext { + dotDotDotToken = p.parseOptionalToken(SyntaxKindDotDotDotToken) + } + // Only an AssignmentExpression is valid here per the JSX spec, + // but we can unambiguously parse a comma sequence and provide + // a better error message in grammar checking. + expression = p.parseExpression() + } + if inExpressionContext { + p.parseExpected(SyntaxKindCloseBraceToken) + } else if p.parseExpectedWithoutAdvancing(SyntaxKindCloseBraceToken) { + p.scanJsxText() + } + result := p.factory.NewJsxExpression(dotDotDotToken, expression) + p.finishNode(result, pos) + return result +} + +func (p *Parser) scanJsxText() SyntaxKind { + p.token = p.scanner.scanJsxToken() + return p.token +} + +func (p *Parser) scanJsxIdentifier() SyntaxKind { + p.token = p.scanner.scanJsxIdentifier() + return p.token +} + +func (p *Parser) scanJsxAttributeValue() SyntaxKind { + p.token = p.scanner.scanJsxAttributeValue() + return p.token +} + +func (p *Parser) parseJsxClosingElement(open *Node, inExpressionContext bool) *Node { + pos := p.nodePos() + p.parseExpected(SyntaxKindLessThanSlashToken) + tagName := p.parseJsxElementName() + if p.parseExpectedWithDiagnostic(SyntaxKindGreaterThanToken, nil /*diagnosticMessage*/, false /*shouldAdvance*/) { + // manually advance the scanner in order to look for jsx text inside jsx + if inExpressionContext || !tagNamesAreEquivalent(open.AsJsxOpeningElement().tagName, tagName) { + p.nextToken() + } else { + p.scanJsxText() + } + } + result := p.factory.NewJsxClosingElement(tagName) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseJsxOpeningOrSelfClosingElementOrOpeningFragment(inExpressionContext bool) *Expression { + pos := p.nodePos() + p.parseExpected(SyntaxKindLessThanToken) + if p.token == SyntaxKindGreaterThanToken { + // See below for explanation of scanJsxText + p.scanJsxText() + result := p.factory.NewJsxOpeningFragment() + p.finishNode(result, pos) + return result + } + tagName := p.parseJsxElementName() + var typeArguments *Node + if p.contextFlags&NodeFlagsJavaScriptFile == 0 { + typeArguments = p.parseTypeArguments() + } + attributes := p.parseJsxAttributes() + var result *Expression + if p.token == SyntaxKindGreaterThanToken { + // Closing tag, so scan the immediately-following text with the JSX scanning instead + // of regular scanning to avoid treating illegal characters (e.g. '#') as immediate + // scanning errors + p.scanJsxText() + result = p.factory.NewJsxOpeningElement(tagName, typeArguments, attributes) + } else { + p.parseExpected(SyntaxKindSlashToken) + if p.parseExpectedWithoutAdvancing(SyntaxKindGreaterThanToken) { + if inExpressionContext { + p.nextToken() + } else { + p.scanJsxText() + } + } + result = p.factory.NewJsxSelfClosingElement(tagName, typeArguments, attributes) + } + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseJsxElementName() *Expression { + pos := p.nodePos() + // JsxElement can have name in the form of + // propertyAccessExpression + // primaryExpression in the form of an identifier and "this" keyword + // We can't just simply use parseLeftHandSideExpressionOrHigher because then we will start consider class,function etc as a keyword + // We only want to consider "this" as a primaryExpression + initialExpression := p.parseJsxTagName() + if isJsxNamespacedName(initialExpression) { + return initialExpression // `a:b.c` is invalid syntax, don't even look for the `.` if we parse `a:b`, and let `parseAttribute` report "unexpected :" instead. + } + expression := initialExpression + for p.parseOptional(SyntaxKindDotToken) { + expression = p.factory.NewPropertyAccessExpression(expression, nil, p.parseRightSideOfDot(true /*allowIdentifierNames*/, false /*allowPrivateIdentifiers*/, false /*allowUnicodeEscapeSequenceInIdentifierName*/), NodeFlagsNone) + p.finishNode(expression, pos) + } + return expression +} + +func (p *Parser) parseJsxTagName() *Expression { + pos := p.nodePos() + p.scanJsxIdentifier() + isThis := p.token == SyntaxKindThisKeyword + tagName := p.parseIdentifierNameErrorOnUnicodeEscapeSequence() + if p.parseOptional(SyntaxKindColonToken) { + p.scanJsxIdentifier() + result := p.factory.NewJsxNamespacedName(tagName, p.parseIdentifierNameErrorOnUnicodeEscapeSequence()) + p.finishNode(result, pos) + return result + } + if isThis { + result := p.factory.NewKeywordExpression(SyntaxKindThisKeyword) + p.finishNode(result, pos) + return result + } + return tagName +} + +func (p *Parser) parseJsxAttributes() *Node { + pos := p.nodePos() + result := p.factory.NewJsxAttributes(parseList(p, PCJsxAttributes, p.parseJsxAttribute)) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseJsxAttribute() *Node { + if p.token == SyntaxKindOpenBraceToken { + return p.parseJsxSpreadAttribute() + } + pos := p.nodePos() + result := p.factory.NewJsxAttribute(p.parseJsxAttributeName(), p.parseJsxAttributeValue()) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseJsxSpreadAttribute() *Node { + pos := p.nodePos() + p.parseExpected(SyntaxKindOpenBraceToken) + p.parseExpected(SyntaxKindDotDotDotToken) + expression := p.parseExpression() + p.parseExpected(SyntaxKindCloseBraceToken) + result := p.factory.NewJsxSpreadAttribute(expression) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseJsxAttributeName() *Node { + pos := p.nodePos() + p.scanJsxIdentifier() + attrName := p.parseIdentifierNameErrorOnUnicodeEscapeSequence() + if p.parseOptional(SyntaxKindColonToken) { + p.scanJsxIdentifier() + result := p.factory.NewJsxNamespacedName(attrName, p.parseIdentifierNameErrorOnUnicodeEscapeSequence()) + p.finishNode(result, pos) + return result + } + return attrName +} + +func (p *Parser) parseJsxAttributeValue() *Expression { + if p.token == SyntaxKindEqualsToken { + if p.scanJsxAttributeValue() == SyntaxKindStringLiteral { + return p.parseLiteralExpression() + } + if p.token == SyntaxKindOpenBraceToken { + return p.parseJsxExpression( /*inExpressionContext*/ true) + } + if p.token == SyntaxKindLessThanToken { + return p.parseJsxElementOrSelfClosingElementOrFragment(true /*inExpressionContext*/, -1, nil, false) + } + p.parseErrorAtCurrentToken(diagnostics.X_or_JSX_element_expected) + } + return nil +} + +func (p *Parser) parseJsxClosingFragment(inExpressionContext bool) *Node { + pos := p.nodePos() + p.parseExpected(SyntaxKindLessThanSlashToken) + if p.parseExpectedWithDiagnostic(SyntaxKindGreaterThanToken, diagnostics.Expected_corresponding_closing_tag_for_JSX_fragment, false /*shouldAdvance*/) { + // manually advance the scanner in order to look for jsx text inside jsx + if inExpressionContext { + p.nextToken() + } else { + p.scanJsxText() + } + } + result := p.factory.NewJsxClosingFragment() + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseSimpleUnaryExpression() *Expression { + switch p.token { + case SyntaxKindPlusToken, SyntaxKindMinusToken, SyntaxKindTildeToken, SyntaxKindExclamationToken: + return p.parsePrefixUnaryExpression() + case SyntaxKindDeleteKeyword: + return p.parseDeleteExpression() + case SyntaxKindTypeOfKeyword: + return p.parseTypeOfExpression() + case SyntaxKindVoidKeyword: + return p.parseVoidExpression() + case SyntaxKindLessThanToken: + // !!! + // // Just like in parseUpdateExpression, we need to avoid parsing type assertions when + // // in JSX and we see an expression like "+ bar". + // if (languageVariant == LanguageVariant.JSX) { + // return parseJsxElementOrSelfClosingElementOrFragment(/*inExpressionContext*/ true, /*topInvalidNodePosition*/ undefined, /*openingTag*/ undefined, /*mustBeUnary*/ true); + // } + // // This is modified UnaryExpression grammar in TypeScript + // // UnaryExpression (modified): + // // < type > UnaryExpression + return p.parseTypeAssertion() + case SyntaxKindAwaitKeyword: + if p.isAwaitExpression() { + return p.parseAwaitExpression() + } + fallthrough + default: + return p.parseUpdateExpression() + } +} + +func (p *Parser) parsePrefixUnaryExpression() *Node { + pos := p.nodePos() + operator := p.token + p.nextToken() + result := p.factory.NewPrefixUnaryExpression(operator, p.parseSimpleUnaryExpression()) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseDeleteExpression() *Node { + pos := p.nodePos() + p.nextToken() + result := p.factory.NewDeleteExpression(p.parseSimpleUnaryExpression()) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseTypeOfExpression() *Node { + pos := p.nodePos() + p.nextToken() + result := p.factory.NewTypeOfExpression(p.parseSimpleUnaryExpression()) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseVoidExpression() *Node { + pos := p.nodePos() + p.nextToken() + result := p.factory.NewVoidExpression(p.parseSimpleUnaryExpression()) + p.finishNode(result, pos) + return result +} + +func (p *Parser) isAwaitExpression() bool { + if p.token == SyntaxKindAwaitKeyword { + if p.inAwaitContext() { + return true + } + // here we are using similar heuristics as 'isYieldExpression' + return p.lookAhead(p.nextTokenIsIdentifierOrKeywordOrLiteralOnSameLine) + } + return false +} + +func (p *Parser) parseAwaitExpression() *Node { + pos := p.nodePos() + p.nextToken() + result := p.factory.NewAwaitExpression(p.parseSimpleUnaryExpression()) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseTypeAssertion() *Node { + // !!! Debug.assert(languageVariant !== LanguageVariant.JSX, "Type assertions should never be parsed in JSX; they should be parsed as comparisons or JSX elements/fragments."); + pos := p.nodePos() + p.parseExpected(SyntaxKindLessThanToken) + typeNode := p.parseType() + p.parseExpected(SyntaxKindGreaterThanToken) + expression := p.parseSimpleUnaryExpression() + result := p.factory.NewTypeAssertion(typeNode, expression) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseLeftHandSideExpressionOrHigher() *Expression { + // Original Ecma: + // LeftHandSideExpression: See 11.2 + // NewExpression + // CallExpression + // + // Our simplification: + // + // LeftHandSideExpression: See 11.2 + // MemberExpression + // CallExpression + // + // See comment in parseMemberExpressionOrHigher on how we replaced NewExpression with + // MemberExpression to make our lives easier. + // + // to best understand the below code, it's important to see how CallExpression expands + // out into its own productions: + // + // CallExpression: + // MemberExpression Arguments + // CallExpression Arguments + // CallExpression[Expression] + // CallExpression.IdentifierName + // import (AssignmentExpression) + // super Arguments + // super.IdentifierName + // + // Because of the recursion in these calls, we need to bottom out first. There are three + // bottom out states we can run into: 1) We see 'super' which must start either of + // the last two CallExpression productions. 2) We see 'import' which must start import call. + // 3)we have a MemberExpression which either completes the LeftHandSideExpression, + // or starts the beginning of the first four CallExpression productions. + pos := p.nodePos() + var expression *Expression + if p.token == SyntaxKindImportKeyword { + if p.lookAhead(p.nextTokenIsOpenParenOrLessThan) { + // We don't want to eagerly consume all import keyword as import call expression so we look ahead to find "(" + // For example: + // var foo3 = require("subfolder + // import * as foo1 from "module-from-node + // We want this import to be a statement rather than import call expression + p.sourceFlags |= NodeFlagsPossiblyContainsDynamicImport + expression = p.parseKeywordExpression() + } else if p.lookAhead(p.nextTokenIsDot) { + // This is an 'import.*' metaproperty (i.e. 'import.meta') + p.nextToken() // advance past the 'import' + p.nextToken() // advance past the dot + expression = p.factory.NewMetaProperty(SyntaxKindImportKeyword, p.parseIdentifierName()) + p.finishNode(expression, pos) + p.sourceFlags |= NodeFlagsPossiblyContainsImportMeta + } else { + expression = p.parseMemberExpressionOrHigher() + } + } else if p.token == SyntaxKindSuperKeyword { + expression = p.parseSuperExpression() + } else { + expression = p.parseMemberExpressionOrHigher() + } + // Now, we *may* be complete. However, we might have consumed the start of a + // CallExpression or OptionalExpression. As such, we need to consume the rest + // of it here to be complete. + return p.parseCallExpressionRest(pos, expression) +} + +func (p *Parser) nextTokenIsDot() bool { + return p.nextToken() == SyntaxKindDotToken +} + +func (p *Parser) parseSuperExpression() *Expression { + pos := p.nodePos() + expression := p.parseKeywordExpression() + if p.token == SyntaxKindLessThanToken { + startPos := p.nodePos() + typeArguments := p.tryParseTypeArgumentsInExpression() + if typeArguments != nil { + p.parseErrorAt(startPos, p.nodePos(), diagnostics.X_super_may_not_use_type_arguments) + if !p.isTemplateStartOfTaggedTemplate() { + expression := p.factory.NewExpressionWithTypeArguments(expression, typeArguments) + p.finishNode(expression, pos) + } + } + } + if p.token == SyntaxKindOpenParenToken || p.token == SyntaxKindDotToken || p.token == SyntaxKindOpenBracketToken { + return expression + } + // If we have seen "super" it must be followed by '(' or '.'. + // If it wasn't then just try to parse out a '.' and report an error. + p.parseErrorAtCurrentToken(diagnostics.X_super_must_be_followed_by_an_argument_list_or_member_access) + // private names will never work with `super` (`super.#foo`), but that's a semantic error, not syntactic + result := p.factory.NewPropertyAccessExpression(expression, nil /*questionDotToken*/, p.parseRightSideOfDot(true /*allowIdentifierNames*/, true /*allowPrivateIdentifiers*/, true /*allowUnicodeEscapeSequenceInIdentifierName*/), NodeFlagsNone) + p.finishNode(result, pos) + return result +} + +func (p *Parser) isTemplateStartOfTaggedTemplate() bool { + return p.token == SyntaxKindNoSubstitutionTemplateLiteral || p.token == SyntaxKindTemplateHead +} + +func (p *Parser) tryParseTypeArgumentsInExpression() *Node { + // TypeArguments must not be parsed in JavaScript files to avoid ambiguity with binary operators. + state := p.mark() + if p.contextFlags&NodeFlagsJavaScriptFile == 0 && p.reScanLessThanToken() == SyntaxKindLessThanToken { + pos := p.nodePos() + p.nextToken() + typeArguments := parseDelimitedList(p, PCTypeArguments, p.parseType) + // If it doesn't have the closing `>` then it's definitely not an type argument list. + if p.reScanGreaterThanToken() == SyntaxKindGreaterThanToken { + p.nextToken() + // We successfully parsed a type argument list. The next token determines whether we want to + // treat it as such. If the type argument list is followed by `(` or a template literal, as in + // `f(42)`, we favor the type argument interpretation even though JavaScript would view + // it as a relational expression. + if p.canFollowTypeArgumentsInExpression() { + result := p.factory.NewTypeArgumentList(typeArguments) + p.finishNode(result, pos) + return result + } + } + } + p.rewind(state) + return nil +} + +func (p *Parser) canFollowTypeArgumentsInExpression() bool { + switch p.token { + // These tokens can follow a type argument list in a call expression: + // foo( + // foo `...` + // foo `...${100}...` + case SyntaxKindOpenParenToken, SyntaxKindNoSubstitutionTemplateLiteral, SyntaxKindTemplateHead: + return true + // A type argument list followed by `<` never makes sense, and a type argument list followed + // by `>` is ambiguous with a (re-scanned) `>>` operator, so we disqualify both. Also, in + // this context, `+` and `-` are unary operators, not binary operators. + case SyntaxKindLessThanToken, SyntaxKindGreaterThanToken, SyntaxKindPlusToken, SyntaxKindMinusToken: + return false + } + // We favor the type argument list interpretation when it is immediately followed by + // a line break, a binary operator, or something that can't start an expression. + return p.hasPrecedingLineBreak() || p.isBinaryOperator() || !p.isStartOfExpression() +} + +func (p *Parser) parseMemberExpressionOrHigher() *Node { + // Note: to make our lives simpler, we decompose the NewExpression productions and + // place ObjectCreationExpression and FunctionExpression into PrimaryExpression. + // like so: + // + // PrimaryExpression : See 11.1 + // this + // Identifier + // Literal + // ArrayLiteral + // ObjectLiteral + // (Expression) + // FunctionExpression + // new MemberExpression Arguments? + // + // MemberExpression : See 11.2 + // PrimaryExpression + // MemberExpression[Expression] + // MemberExpression.IdentifierName + // + // CallExpression : See 11.2 + // MemberExpression + // CallExpression Arguments + // CallExpression[Expression] + // CallExpression.IdentifierName + // + // Technically this is ambiguous. i.e. CallExpression defines: + // + // CallExpression: + // CallExpression Arguments + // + // If you see: "new Foo()" + // + // Then that could be treated as a single ObjectCreationExpression, or it could be + // treated as the invocation of "new Foo". We disambiguate that in code (to match + // the original grammar) by making sure that if we see an ObjectCreationExpression + // we always consume arguments if they are there. So we treat "new Foo()" as an + // object creation only, and not at all as an invocation. Another way to think + // about this is that for every "new" that we see, we will consume an argument list if + // it is there as part of the *associated* object creation node. Any additional + // argument lists we see, will become invocation expressions. + // + // Because there are no other places in the grammar now that refer to FunctionExpression + // or ObjectCreationExpression, it is safe to push down into the PrimaryExpression + // production. + // + // Because CallExpression and MemberExpression are left recursive, we need to bottom out + // of the recursion immediately. So we parse out a primary expression to start with. + pos := p.nodePos() + expression := p.parsePrimaryExpression() + return p.parseMemberExpressionRest(pos, expression, true /*allowOptionalChain*/) +} + +func (p *Parser) parseMemberExpressionRest(pos int, expression *Expression, allowOptionalChain bool) *Expression { + for { + var questionDotToken *Node + isPropertyAccess := false + if allowOptionalChain && p.isStartOfOptionalPropertyOrElementAccessChain() { + questionDotToken = p.parseExpectedToken(SyntaxKindQuestionDotToken) + isPropertyAccess = tokenIsIdentifierOrKeyword(p.token) + } else { + isPropertyAccess = p.parseOptional(SyntaxKindDotToken) + } + if isPropertyAccess { + expression = p.parsePropertyAccessExpressionRest(pos, expression, questionDotToken) + continue + } + // when in the [Decorator] context, we do not parse ElementAccess as it could be part of a ComputedPropertyName + if (questionDotToken != nil || !p.inDecoratorContext()) && p.parseOptional(SyntaxKindOpenBracketToken) { + expression = p.parseElementAccessExpressionRest(pos, expression, questionDotToken) + continue + } + if p.isTemplateStartOfTaggedTemplate() { + // Absorb type arguments into TemplateExpression when preceding expression is ExpressionWithTypeArguments + if questionDotToken == nil && isExpressionWithTypeArguments(expression) { + expression = p.parseTaggedTemplateRest(pos, expression.AsExpressionWithTypeArguments().expression, questionDotToken, expression.AsExpressionWithTypeArguments().typeArguments) + } else { + expression = p.parseTaggedTemplateRest(pos, expression, questionDotToken, nil /*typeArguments*/) + } + continue + } + if questionDotToken == nil { + if p.token == SyntaxKindExclamationToken && !p.hasPrecedingLineBreak() { + p.nextToken() + expression = p.factory.NewNonNullExpression(expression) + p.finishNode(expression, pos) + continue + } + typeArguments := p.tryParseTypeArgumentsInExpression() + if typeArguments != nil { + expression = p.factory.NewExpressionWithTypeArguments(expression, typeArguments) + p.finishNode(expression, pos) + continue + } + } + return expression + } +} + +func (p *Parser) isStartOfOptionalPropertyOrElementAccessChain() bool { + return p.token == SyntaxKindQuestionDotToken && p.lookAhead(p.nextTokenIsIdentifierOrKeywordOrOpenBracketOrTemplate) +} + +func (p *Parser) nextTokenIsIdentifierOrKeywordOrOpenBracketOrTemplate() bool { + p.nextToken() + return tokenIsIdentifierOrKeyword(p.token) || p.token == SyntaxKindOpenBracketToken || p.isTemplateStartOfTaggedTemplate() +} + +func (p *Parser) parsePropertyAccessExpressionRest(pos int, expression *Expression, questionDotToken *Node) *Node { + name := p.parseRightSideOfDot(true /*allowIdentifierNames*/, true /*allowPrivateIdentifiers*/, true /*allowUnicodeEscapeSequenceInIdentifierName*/) + isOptionalChain := questionDotToken != nil || p.tryReparseOptionalChain(expression) + propertyAccess := p.factory.NewPropertyAccessExpression(expression, questionDotToken, name, ifElse(isOptionalChain, NodeFlagsOptionalChain, NodeFlagsNone)) + if isOptionalChain && isPrivateIdentifier(name) { + p.parseErrorAtRange(p.skipRangeTrivia(name.loc), diagnostics.An_optional_chain_cannot_contain_private_identifiers) + } + if isExpressionWithTypeArguments(expression) && expression.AsExpressionWithTypeArguments().typeArguments != nil { + loc := p.skipRangeTrivia(expression.AsExpressionWithTypeArguments().typeArguments.loc) + p.parseErrorAtRange(loc, diagnostics.An_instantiation_expression_cannot_be_followed_by_a_property_access) + } + p.finishNode(propertyAccess, pos) + return propertyAccess +} + +func (p *Parser) tryReparseOptionalChain(node *Expression) bool { + if node.flags&NodeFlagsOptionalChain != 0 { + return true + } + // check for an optional chain in a non-null expression + if isNonNullExpression(node) { + expr := node.AsNonNullExpression().expression + for isNonNullExpression(expr) && expr.flags&NodeFlagsOptionalChain == 0 { + expr = expr.AsNonNullExpression().expression + } + if expr.flags&NodeFlagsOptionalChain != 0 { + // this is part of an optional chain. Walk down from `node` to `expression` and set the flag. + for isNonNullExpression(node) { + node.flags |= NodeFlagsOptionalChain + node = node.AsNonNullExpression().expression + } + return true + } + } + return false +} + +func (p *Parser) parseElementAccessExpressionRest(pos int, expression *Expression, questionDotToken *Node) *Node { + var argumentExpression *Expression + if p.token == SyntaxKindCloseBracketToken { + p.parseErrorAt(p.nodePos(), p.nodePos(), diagnostics.An_element_access_expression_should_take_an_argument) + argumentExpression = p.createMissingIdentifier() + } else { + argument := p.parseExpressionAllowIn() + if isStringOrNumericLiteralLike(argument) { + p.internIdentifier(getTextOfIdentifierOrLiteral(argument)) + } + argumentExpression = argument + } + p.parseExpected(SyntaxKindCloseBracketToken) + isOptionalChain := questionDotToken != nil || p.tryReparseOptionalChain(expression) + elementAccess := p.factory.NewElementAccessExpression(expression, questionDotToken, argumentExpression, ifElse(isOptionalChain, NodeFlagsOptionalChain, NodeFlagsNone)) + p.finishNode(elementAccess, pos) + return elementAccess +} + +func (p *Parser) parseCallExpressionRest(pos int, expression *Expression) *Expression { + for { + expression = p.parseMemberExpressionRest(pos, expression /*allowOptionalChain*/, true) + var typeArguments *Node + questionDotToken := p.parseOptionalToken(SyntaxKindQuestionDotToken) + if questionDotToken != nil { + typeArguments = p.tryParseTypeArgumentsInExpression() + if p.isTemplateStartOfTaggedTemplate() { + expression = p.parseTaggedTemplateRest(pos, expression, questionDotToken, typeArguments) + continue + } + } + if typeArguments != nil || p.token == SyntaxKindOpenParenToken { + // Absorb type arguments into CallExpression when preceding expression is ExpressionWithTypeArguments + if questionDotToken == nil && expression.kind == SyntaxKindExpressionWithTypeArguments { + typeArguments = expression.AsExpressionWithTypeArguments().typeArguments + expression = expression.AsExpressionWithTypeArguments().expression + } + argumentList := p.parseArgumentList() + isOptionalChain := questionDotToken != nil || p.tryReparseOptionalChain(expression) + expression = p.factory.NewCallExpression(expression, questionDotToken, typeArguments, argumentList, ifElse(isOptionalChain, NodeFlagsOptionalChain, NodeFlagsNone)) + p.finishNode(expression, pos) + continue + } + if questionDotToken != nil { + // We parsed `?.` but then failed to parse anything, so report a missing identifier here. + p.parseErrorAtCurrentToken(diagnostics.Identifier_expected) + expression = p.createMissingIdentifier() + p.finishNode(expression, pos) + } + break + } + return expression +} + +func (p *Parser) parseArgumentList() []*Expression { + p.parseExpected(SyntaxKindOpenParenToken) + result := parseDelimitedList(p, PCArgumentExpressions, p.parseArgumentExpression) + p.parseExpected(SyntaxKindCloseParenToken) + return result +} + +func (p *Parser) parseArgumentExpression() *Expression { + return doInContext(p, NodeFlagsDisallowInContext|NodeFlagsDecoratorContext, false, p.parseArgumentOrArrayLiteralElement) +} + +func (p *Parser) parseArgumentOrArrayLiteralElement() *Expression { + switch p.token { + case SyntaxKindDotDotDotToken: + return p.parseSpreadElement() + case SyntaxKindCommaToken: + result := p.factory.NewOmittedExpression() + p.finishNode(result, p.nodePos()) + return result + } + return p.parseAssignmentExpressionOrHigher() +} + +func (p *Parser) parseSpreadElement() *Node { + pos := p.nodePos() + p.parseExpected(SyntaxKindDotDotDotToken) + expression := p.parseAssignmentExpressionOrHigher() + result := p.factory.NewSpreadElement(expression) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseTaggedTemplateRest(pos int, tag *Expression, questionDotToken *Node, typeArguments *Node) *Node { + var template *Expression + if p.token == SyntaxKindNoSubstitutionTemplateLiteral { + p.reScanTemplateToken(true /*isTaggedTemplate*/) + template = p.parseLiteralExpression() + } else { + template = p.parseTemplateExpression(true /*isTaggedTemplate*/) + } + isOptionalChain := questionDotToken != nil || tag.flags&NodeFlagsOptionalChain != 0 + result := p.factory.NewTaggedTemplateExpression(tag, questionDotToken, typeArguments, template, ifElse(isOptionalChain, NodeFlagsOptionalChain, NodeFlagsNone)) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseTemplateExpression(isTaggedTemplate bool) *Expression { + pos := p.nodePos() + result := p.factory.NewTemplateExpression(p.parseTemplateHead(isTaggedTemplate), p.parseTemplateSpans(isTaggedTemplate)) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseTemplateSpans(isTaggedTemplate bool) []*Node { + list := []*Node{} + for { + span := p.parseTemplateSpan(isTaggedTemplate) + list = append(list, span) + if span.AsTemplateSpan().literal.kind != SyntaxKindTemplateMiddle { + break + } + } + return list +} + +func (p *Parser) parseTemplateSpan(isTaggedTemplate bool) *Node { + pos := p.nodePos() + expression := p.parseExpressionAllowIn() + literal := p.parseLiteralOfTemplateSpan(isTaggedTemplate) + result := p.factory.NewTemplateSpan(expression, literal) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parsePrimaryExpression() *Expression { + switch p.token { + case SyntaxKindNoSubstitutionTemplateLiteral: + if p.scanner.tokenFlags&TokenFlagsIsInvalid != 0 { + p.reScanTemplateToken(false /*isTaggedTemplate*/) + } + fallthrough + case SyntaxKindNumericLiteral, SyntaxKindBigintLiteral, SyntaxKindStringLiteral: + return p.parseLiteralExpression() + case SyntaxKindThisKeyword, SyntaxKindSuperKeyword, SyntaxKindNullKeyword, SyntaxKindTrueKeyword, SyntaxKindFalseKeyword: + return p.parseKeywordExpression() + case SyntaxKindOpenParenToken: + return p.parseParenthesizedExpression() + case SyntaxKindOpenBracketToken: + return p.parseArrayLiteralExpression() + case SyntaxKindOpenBraceToken: + return p.parseObjectLiteralExpression() + case SyntaxKindAsyncKeyword: + // Async arrow functions are parsed earlier in parseAssignmentExpressionOrHigher. + // If we encounter `async [no LineTerminator here] function` then this is an async + // function; otherwise, its an identifier. + if !p.lookAhead(p.nextTokenIsFunctionKeywordOnSameLine) { + break + } + return p.parseFunctionExpression() + case SyntaxKindAtToken: + return p.parseDecoratedExpression() + case SyntaxKindClassKeyword: + return p.parseClassExpression() + case SyntaxKindFunctionKeyword: + return p.parseFunctionExpression() + case SyntaxKindNewKeyword: + return p.parseNewExpressionOrNewDotTarget() + case SyntaxKindSlashToken, SyntaxKindSlashEqualsToken: + if p.reScanSlashToken() == SyntaxKindRegularExpressionLiteral { + return p.parseLiteralExpression() + } + case SyntaxKindTemplateHead: + return p.parseTemplateExpression(false /*isTaggedTemplate*/) + case SyntaxKindPrivateIdentifier: + return p.parsePrivateIdentifier() + } + return p.parseIdentifierWithDiagnostic(diagnostics.Expression_expected, nil) +} + +func (p *Parser) parseParenthesizedExpression() *Expression { + pos := p.nodePos() + // !!! JSDoc + p.parseExpected(SyntaxKindOpenParenToken) + expression := p.parseExpressionAllowIn() + p.parseExpected(SyntaxKindCloseParenToken) + result := p.factory.NewParenthesizedExpression(expression) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseArrayLiteralExpression() *Expression { + pos := p.nodePos() + openBracketPosition := p.scanner.TokenStart() + openBracketParsed := p.parseExpected(SyntaxKindOpenBracketToken) + multiLine := p.hasPrecedingLineBreak() + elements := parseDelimitedList(p, PCArrayLiteralMembers, p.parseArgumentOrArrayLiteralElement) + p.parseExpectedMatchingBrackets(SyntaxKindOpenBracketToken, SyntaxKindCloseBracketToken, openBracketParsed, openBracketPosition) + result := p.factory.NewArrayLiteralExpression(elements, multiLine) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseObjectLiteralExpression() *Expression { + pos := p.nodePos() + openBracePosition := p.scanner.TokenStart() + openBraceParsed := p.parseExpected(SyntaxKindOpenBraceToken) + multiLine := p.hasPrecedingLineBreak() + properties := parseDelimitedList(p, PCObjectLiteralMembers, p.parseObjectLiteralElement) + p.parseExpectedMatchingBrackets(SyntaxKindOpenBraceToken, SyntaxKindCloseBraceToken, openBraceParsed, openBracePosition) + result := p.factory.NewObjectLiteralExpression(properties, multiLine) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseObjectLiteralElement() *Node { + pos := p.nodePos() + hasJSDoc := p.hasPrecedingJSDocComment() + if p.parseOptional(SyntaxKindDotDotDotToken) { + expression := p.parseAssignmentExpressionOrHigher() + result := p.factory.NewSpreadAssignment(expression) + p.finishNode(result, pos) + return result + } + modifiers := p.parseModifiersWithOptions(true /*allowDecorators*/, false /*permitConstAsModifier*/, false /*stopOnStartOfClassStaticBlock*/) + if p.parseContextualModifier(SyntaxKindGetKeyword) { + return p.parseAccessorDeclaration(pos, hasJSDoc, modifiers, SyntaxKindGetAccessor, SignatureFlagsNone) + } + if p.parseContextualModifier(SyntaxKindSetKeyword) { + return p.parseAccessorDeclaration(pos, hasJSDoc, modifiers, SyntaxKindSetAccessor, SignatureFlagsNone) + } + asteriskToken := p.parseOptionalToken(SyntaxKindAsteriskToken) + tokenIsIdentifier := p.isIdentifier() + name := p.parsePropertyName() + // Disallowing of optional property assignments and definite assignment assertion happens in the grammar checker. + postfixToken := p.parseOptionalToken(SyntaxKindQuestionToken) + // Decorators, Modifiers, questionToken, and exclamationToken are not supported by property assignments and are reported in the grammar checker + if postfixToken == nil { + postfixToken = p.parseOptionalToken(SyntaxKindExclamationToken) + } + if asteriskToken != nil || p.token == SyntaxKindOpenParenToken || p.token == SyntaxKindLessThanToken { + return p.parseMethodDeclaration(pos, hasJSDoc, modifiers, asteriskToken, name, postfixToken, nil /*diagnosticMessage*/) + } + // check if it is short-hand property assignment or normal property assignment + // NOTE: if token is EqualsToken it is interpreted as CoverInitializedName production + // CoverInitializedName[Yield] : + // IdentifierReference[?Yield] Initializer[In, ?Yield] + // this is necessary because ObjectLiteral productions are also used to cover grammar for ObjectAssignmentPattern + var node *Node + isShorthandPropertyAssignment := tokenIsIdentifier && p.token != SyntaxKindColonToken + if isShorthandPropertyAssignment { + var initializer *Expression + if p.parseOptional(SyntaxKindEqualsToken) { + initializer = doInContext(p, NodeFlagsDisallowInContext, false, p.parseAssignmentExpressionOrHigher) + } + node = p.factory.NewShorthandPropertyAssignment(modifiers, name, postfixToken, initializer) + } else { + p.parseExpected(SyntaxKindColonToken) + initializer := doInContext(p, NodeFlagsDisallowInContext, false, p.parseAssignmentExpressionOrHigher) + node = p.factory.NewPropertyAssignment(modifiers, name, postfixToken, initializer) + } + p.finishNode(node, pos) + return node +} + +func (p *Parser) parseFunctionExpression() *Expression { + // GeneratorExpression: + // function* BindingIdentifier [Yield][opt](FormalParameters[Yield]){ GeneratorBody } + // + // FunctionExpression: + // function BindingIdentifier[opt](FormalParameters){ FunctionBody } + saveContexFlags := p.contextFlags + p.setContextFlags(NodeFlagsDecoratorContext, false) + pos := p.nodePos() + // !!! JSDoc + modifiers := p.parseModifiers() + p.parseExpected(SyntaxKindFunctionKeyword) + asteriskToken := p.parseOptionalToken(SyntaxKindAsteriskToken) + isGenerator := asteriskToken != nil + isAsync := hasAsyncModifier(modifiers) + signatureFlags := ifElse(isGenerator, SignatureFlagsYield, SignatureFlagsNone) | ifElse(isAsync, SignatureFlagsAwait, SignatureFlagsNone) + var name *Node + switch { + case isGenerator && isAsync: + name = doInContext(p, NodeFlagsYieldContext|NodeFlagsAwaitContext, true, p.parseOptionalBindingIdentifier) + case isGenerator: + name = doInContext(p, NodeFlagsYieldContext, true, p.parseOptionalBindingIdentifier) + case isAsync: + name = doInContext(p, NodeFlagsAwaitContext, true, p.parseOptionalBindingIdentifier) + default: + name = p.parseOptionalBindingIdentifier() + } + typeParameters := p.parseTypeParameters() + parameters := p.parseParameters(signatureFlags) + returnType := p.parseReturnType(SyntaxKindColonToken, false /*isType*/) + body := p.parseFunctionBlock(signatureFlags, nil /*diagnosticMessage*/) + p.contextFlags = saveContexFlags + result := p.factory.NewFunctionExpression(modifiers, asteriskToken, name, typeParameters, parameters, returnType, body) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseOptionalBindingIdentifier() *Node { + if p.isBindingIdentifier() { + return p.parseBindingIdentifier() + } + return nil +} + +func (p *Parser) parseDecoratedExpression() *Expression { + pos := p.nodePos() + hasJSDoc := p.hasPrecedingJSDocComment() + modifiers := p.parseModifiersWithOptions(true /*allowDecorators*/, false /*permitConstAsModifier*/, false /*stopOnStartOfClassStaticBlock*/) + if p.token == SyntaxKindClassKeyword { + return p.parseClassDeclarationOrExpression(pos, hasJSDoc, modifiers, SyntaxKindClassExpression) + } + p.parseErrorAt(p.nodePos(), p.nodePos(), diagnostics.Expression_expected) + result := p.factory.NewMissingDeclaration(modifiers) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseNewExpressionOrNewDotTarget() *Node { + pos := p.nodePos() + p.parseExpected(SyntaxKindNewKeyword) + if p.parseOptional(SyntaxKindDotToken) { + name := p.parseIdentifierName() + result := p.factory.NewMetaProperty(SyntaxKindNewKeyword, name) + p.finishNode(result, pos) + return result + } + expressionPos := p.nodePos() + expression := p.parseMemberExpressionRest(expressionPos, p.parsePrimaryExpression(), false /*allowOptionalChain*/) + var typeArguments *Node + // Absorb type arguments into NewExpression when preceding expression is ExpressionWithTypeArguments + if expression.kind == SyntaxKindExpressionWithTypeArguments { + typeArguments = expression.AsExpressionWithTypeArguments().typeArguments + expression = expression.AsExpressionWithTypeArguments().expression + } + if p.token == SyntaxKindQuestionDotToken { + p.parseErrorAtCurrentToken(diagnostics.Invalid_optional_chain_from_new_expression_Did_you_mean_to_call_0, getTextOfNodeFromSourceText(p.sourceText, expression)) + } + var argumentList []*Expression + if p.token == SyntaxKindOpenParenToken { + argumentList = p.parseArgumentList() + } + result := p.factory.NewNewExpression(expression, typeArguments, argumentList) + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseKeywordExpression() *Node { + pos := p.nodePos() + result := p.factory.NewKeywordExpression(p.token) + p.nextToken() + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseLiteralExpression() *Node { + pos := p.nodePos() + text := p.scanner.TokenValue() + var result *Node + switch p.token { + case SyntaxKindStringLiteral: + result = p.factory.NewStringLiteral(text) + case SyntaxKindNumericLiteral: + result = p.factory.NewNumericLiteral(text) + case SyntaxKindBigintLiteral: + result = p.factory.NewBigintLiteral(text) + case SyntaxKindRegularExpressionLiteral: + result = p.factory.NewRegularExpressionLiteral(text) + case SyntaxKindNoSubstitutionTemplateLiteral: + result = p.factory.NewNoSubstitutionTemplateLiteral(text) + default: + panic("Unhandled case in parseLiteralExpression") + } + p.nextToken() + p.finishNode(result, pos) + return result +} + +func (p *Parser) parseIdentifierNameErrorOnUnicodeEscapeSequence() *Node { + if p.scanner.HasUnicodeEscape() || p.scanner.HasExtendedUnicodeEscape() { + p.parseErrorAtCurrentToken(diagnostics.Unicode_escape_sequence_cannot_appear_here) + } + return p.createIdentifier(tokenIsIdentifierOrKeyword(p.token)) +} + +func (p *Parser) parseBindingIdentifier() *Node { + return p.parseBindingIdentifierWithDiagnostic(nil) +} + +func (p *Parser) parseBindingIdentifierWithDiagnostic(privateIdentifierDiagnosticMessage *diagnostics.Message) *Node { + return p.createIdentifierWithDiagnostic(p.isBindingIdentifier(), nil /*diagnosticMessage*/, privateIdentifierDiagnosticMessage) +} + +func (p *Parser) parseIdentifierName() *Node { + return p.parseIdentifierNameWithDiagnostic(nil) +} + +func (p *Parser) parseIdentifierNameWithDiagnostic(diagnosticMessage *diagnostics.Message) *Node { + return p.createIdentifierWithDiagnostic(tokenIsIdentifierOrKeyword(p.token), diagnosticMessage, nil) +} + +func (p *Parser) parseIdentifier() *Node { + return p.parseIdentifierWithDiagnostic(nil, nil) +} + +func (p *Parser) parseIdentifierWithDiagnostic(diagnosticMessage *diagnostics.Message, privateIdentifierDiagnosticMessage *diagnostics.Message) *Node { + return p.createIdentifierWithDiagnostic(p.isIdentifier(), diagnosticMessage, privateIdentifierDiagnosticMessage) +} + +func (p *Parser) createIdentifier(isIdentifier bool) *Node { + return p.createIdentifierWithDiagnostic(isIdentifier, nil, nil) +} + +func (p *Parser) createIdentifierWithDiagnostic(isIdentifier bool, diagnosticMessage *diagnostics.Message, privateIdentifierDiagnosticMessage *diagnostics.Message) *Node { + if isIdentifier { + pos := p.nodePos() + text := p.scanner.TokenValue() + p.internIdentifier(text) + p.nextToken() + result := p.newIdentifier(text) + p.finishNode(result, pos) + return result + } + if p.token == SyntaxKindPrivateIdentifier { + if privateIdentifierDiagnosticMessage != nil { + p.parseErrorAtCurrentToken(privateIdentifierDiagnosticMessage) + } else { + p.parseErrorAtCurrentToken(diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies) + } + return p.createIdentifier(true /*isIdentifier*/) + } + if diagnosticMessage != nil { + p.parseErrorAtCurrentToken(diagnosticMessage) + } else if isReservedWord(p.token) { + p.parseErrorAtCurrentToken(diagnostics.Identifier_expected_0_is_a_reserved_word_that_cannot_be_used_here, p.scanner.TokenText()) + } else { + p.parseErrorAtCurrentToken(diagnostics.Identifier_expected) + } + result := p.newIdentifier("") + p.finishNode(result, p.nodePos()) + return result +} + +func (p *Parser) internIdentifier(text string) { + p.identifiers[text] = true +} + +func (p *Parser) finishNode(node *Node, pos int) { + node.loc = NewTextRange(pos, p.nodePos()) + node.flags |= p.contextFlags +} + +func (p *Parser) nextTokenIsSlash() bool { + return p.nextToken() == SyntaxKindSlashToken +} + +func (p *Parser) scanTypeMemberStart() bool { + // Return true if we have the start of a signature member + if p.token == SyntaxKindOpenParenToken || p.token == SyntaxKindLessThanToken || p.token == SyntaxKindGetKeyword || p.token == SyntaxKindSetKeyword { + return true + } + idToken := false + // Eat up all modifiers, but hold on to the last one in case it is actually an identifier + for isModifierKind(p.token) { + idToken = true + p.nextToken() + } + // Index signatures and computed property names are type members + if p.token == SyntaxKindOpenBracketToken { + return true + } + // Try to get the first property-like token following all modifiers + if p.isLiteralPropertyName() { + idToken = true + p.nextToken() + } + // If we were able to get any potential identifier, check that it is + // the start of a member declaration + if idToken { + return p.token == SyntaxKindOpenParenToken || p.token == SyntaxKindLessThanToken || p.token == SyntaxKindQuestionToken || p.token == SyntaxKindColonToken || p.token == SyntaxKindCommaToken || p.canParseSemicolon() + } + return false +} + +func (p *Parser) scanClassMemberStart() bool { + idToken := SyntaxKindUnknown + if p.token == SyntaxKindAtToken { + return true + } + // Eat up all modifiers, but hold on to the last one in case it is actually an identifier. + for isModifierKind(p.token) { + idToken = p.token + // If the idToken is a class modifier (protected, private, public, and static), it is + // certain that we are starting to parse class member. This allows better error recovery + // Example: + // public foo() ... // true + // public @dec blah ... // true; we will then report an error later + // export public ... // true; we will then report an error later + if isClassMemberModifier(idToken) { + return true + } + p.nextToken() + } + if p.token == SyntaxKindAsteriskToken { + return true + } + // Try to get the first property-like token following all modifiers. + // This can either be an identifier or the 'get' or 'set' keywords. + if p.isLiteralPropertyName() { + idToken = p.token + p.nextToken() + } + // Index signatures and computed properties are class members; we can parse. + if p.token == SyntaxKindOpenBracketToken { + return true + } + // If we were able to get any potential identifier... + if idToken != SyntaxKindUnknown { + // If we have a non-keyword identifier, or if we have an accessor, then it's safe to parse. + if !isKeyword(idToken) || idToken == SyntaxKindSetKeyword || idToken == SyntaxKindGetKeyword { + return true + } + // If it *is* a keyword, but not an accessor, check a little farther along + // to see if it should actually be parsed as a class member. + switch p.token { + case SyntaxKindOpenParenToken, // Method declaration + SyntaxKindLessThanToken, // Generic Method declaration + SyntaxKindExclamationToken, // Non-null assertion on property name + SyntaxKindColonToken, // Type Annotation for declaration + SyntaxKindEqualsToken, // Initializer for declaration + SyntaxKindQuestionToken: // Not valid, but permitted so that it gets caught later on. + return true + } + // Covers + // - Semicolons (declaration termination) + // - Closing braces (end-of-class, must be declaration) + // - End-of-files (not valid, but permitted so that it gets caught later on) + // - Line-breaks (enabling *automatic semicolon insertion*) + return p.canParseSemicolon() + } + return false +} + +func (p *Parser) canParseSemicolon() bool { + // If there's a real semicolon, then we can always parse it out. + // We can parse out an optional semicolon in ASI cases in the following cases. + return p.token == SyntaxKindSemicolonToken || p.token == SyntaxKindCloseBraceToken || p.token == SyntaxKindEndOfFile || p.hasPrecedingLineBreak() +} + +func (p *Parser) tryParseSemicolon() bool { + if !p.canParseSemicolon() { + return false + } + if p.token == SyntaxKindSemicolonToken { + // consume the semicolon if it was explicitly provided. + p.nextToken() + } + return true +} + +func (p *Parser) parseSemicolon() bool { + return p.tryParseSemicolon() || p.parseExpected(SyntaxKindSemicolonToken) +} + +func (p *Parser) isLiteralPropertyName() bool { + return tokenIsIdentifierOrKeyword(p.token) || p.token == SyntaxKindStringLiteral || p.token == SyntaxKindNumericLiteral || p.token == SyntaxKindBigintLiteral +} + +func (p *Parser) isStartOfStatement() bool { + switch p.token { + // 'catch' and 'finally' do not actually indicate that the code is part of a statement, + // however, we say they are here so that we may gracefully parse them and error later. + case SyntaxKindAtToken, SyntaxKindSemicolonToken, SyntaxKindOpenBraceToken, SyntaxKindVarKeyword, SyntaxKindLetKeyword, + SyntaxKindUsingKeyword, SyntaxKindFunctionKeyword, SyntaxKindClassKeyword, SyntaxKindEnumKeyword, SyntaxKindIfKeyword, + SyntaxKindDoKeyword, SyntaxKindWhileKeyword, SyntaxKindForKeyword, SyntaxKindContinueKeyword, SyntaxKindBreakKeyword, + SyntaxKindReturnKeyword, SyntaxKindWithKeyword, SyntaxKindSwitchKeyword, SyntaxKindThrowKeyword, SyntaxKindTryKeyword, + SyntaxKindDebuggerKeyword, SyntaxKindCatchKeyword, SyntaxKindFinallyKeyword: + return true + case SyntaxKindImportKeyword: + return p.isStartOfDeclaration() || p.isNextTokenOpenParenOrLessThanOrDot() + case SyntaxKindConstKeyword, SyntaxKindExportKeyword: + return p.isStartOfDeclaration() + case SyntaxKindAsyncKeyword, SyntaxKindDeclareKeyword, SyntaxKindInterfaceKeyword, SyntaxKindModuleKeyword, SyntaxKindNamespaceKeyword, + SyntaxKindTypeKeyword, SyntaxKindGlobalKeyword: + // When these don't start a declaration, they're an identifier in an expression statement + return true + case SyntaxKindAccessorKeyword, SyntaxKindPublicKeyword, SyntaxKindPrivateKeyword, SyntaxKindProtectedKeyword, SyntaxKindStaticKeyword, + SyntaxKindReadonlyKeyword: + // When these don't start a declaration, they may be the start of a class member if an identifier + // immediately follows. Otherwise they're an identifier in an expression statement. + return p.isStartOfDeclaration() || !p.lookAhead(p.nextTokenIsIdentifierOrKeywordOnSameLine) + + default: + return p.isStartOfExpression() + } +} + +func (p *Parser) isStartOfDeclaration() bool { + return p.lookAhead(p.scanStartOfDeclaration) +} + +func (p *Parser) scanStartOfDeclaration() bool { + for { + switch p.token { + case SyntaxKindVarKeyword, SyntaxKindLetKeyword, SyntaxKindConstKeyword, SyntaxKindFunctionKeyword, SyntaxKindClassKeyword, + SyntaxKindEnumKeyword: + return true + case SyntaxKindUsingKeyword: + return p.isUsingDeclaration() + case SyntaxKindAwaitKeyword: + return p.isAwaitUsingDeclaration() + // 'declare', 'module', 'namespace', 'interface'* and 'type' are all legal JavaScript identifiers; + // however, an identifier cannot be followed by another identifier on the same line. This is what we + // count on to parse out the respective declarations. For instance, we exploit this to say that + // + // namespace n + // + // can be none other than the beginning of a namespace declaration, but need to respect that JavaScript sees + // + // namespace + // n + // + // as the identifier 'namespace' on one line followed by the identifier 'n' on another. + // We need to look one token ahead to see if it permissible to try parsing a declaration. + // + // *Note*: 'interface' is actually a strict mode reserved word. So while + // + // "use strict" + // interface + // I {} + // + // could be legal, it would add complexity for very little gain. + case SyntaxKindInterfaceKeyword, SyntaxKindTypeKeyword: + return p.nextTokenIsIdentifierOnSameLine() + case SyntaxKindModuleKeyword, SyntaxKindNamespaceKeyword: + return p.nextTokenIsIdentifierOrStringLiteralOnSameLine() + case SyntaxKindAbstractKeyword, SyntaxKindAccessorKeyword, SyntaxKindAsyncKeyword, SyntaxKindDeclareKeyword, SyntaxKindPrivateKeyword, + SyntaxKindProtectedKeyword, SyntaxKindPublicKeyword, SyntaxKindReadonlyKeyword: + previousToken := p.token + p.nextToken() + // ASI takes effect for this modifier. + if p.hasPrecedingLineBreak() { + return false + } + if previousToken == SyntaxKindDeclareKeyword && p.token == SyntaxKindTypeKeyword { + // If we see 'declare type', then commit to parsing a type alias. parseTypeAliasDeclaration will + // report Line_break_not_permitted_here if needed. + return true + } + continue + case SyntaxKindGlobalKeyword: + p.nextToken() + return p.token == SyntaxKindOpenBraceToken || p.token == SyntaxKindIdentifier || p.token == SyntaxKindExportKeyword + case SyntaxKindImportKeyword: + p.nextToken() + return p.token == SyntaxKindStringLiteral || p.token == SyntaxKindAsteriskToken || p.token == SyntaxKindOpenBraceToken || tokenIsIdentifierOrKeyword(p.token) + case SyntaxKindExportKeyword: + p.nextToken() + if p.token == SyntaxKindEqualsToken || p.token == SyntaxKindAsteriskToken || p.token == SyntaxKindOpenBraceToken || + p.token == SyntaxKindDefaultKeyword || p.token == SyntaxKindAsKeyword || p.token == SyntaxKindAtToken { + return true + } + if p.token == SyntaxKindTypeKeyword { + p.nextToken() + return p.token == SyntaxKindAsteriskToken || p.token == SyntaxKindOpenBraceToken || p.isIdentifier() && !p.hasPrecedingLineBreak() + } + continue + case SyntaxKindStaticKeyword: + p.nextToken() + continue + } + return false + } +} + +func (p *Parser) isStartOfExpression() bool { + if p.isStartOfLeftHandSideExpression() { + return true + } + switch p.token { + case SyntaxKindPlusToken, SyntaxKindMinusToken, SyntaxKindTildeToken, SyntaxKindExclamationToken, SyntaxKindDeleteKeyword, + SyntaxKindTypeOfKeyword, SyntaxKindVoidKeyword, SyntaxKindPlusPlusToken, SyntaxKindMinusMinusToken, SyntaxKindLessThanToken, + SyntaxKindAwaitKeyword, SyntaxKindYieldKeyword, SyntaxKindPrivateIdentifier, SyntaxKindAtToken: + // Yield/await always starts an expression. Either it is an identifier (in which case + // it is definitely an expression). Or it's a keyword (either because we're in + // a generator or async function, or in strict mode (or both)) and it started a yield or await expression. + return true + } + // Error tolerance. If we see the start of some binary operator, we consider + // that the start of an expression. That way we'll parse out a missing identifier, + // give a good message about an identifier being missing, and then consume the + // rest of the binary expression. + if p.isBinaryOperator() { + return true + } + return p.isIdentifier() +} + +func (p *Parser) isStartOfLeftHandSideExpression() bool { + switch p.token { + case SyntaxKindThisKeyword, SyntaxKindSuperKeyword, SyntaxKindNullKeyword, SyntaxKindTrueKeyword, SyntaxKindFalseKeyword, + SyntaxKindNumericLiteral, SyntaxKindBigintLiteral, SyntaxKindStringLiteral, SyntaxKindNoSubstitutionTemplateLiteral, SyntaxKindTemplateHead, + SyntaxKindOpenParenToken, SyntaxKindOpenBracketToken, SyntaxKindOpenBraceToken, SyntaxKindFunctionKeyword, SyntaxKindClassKeyword, + SyntaxKindNewKeyword, SyntaxKindSlashToken, SyntaxKindSlashEqualsToken, SyntaxKindIdentifier: + return true + case SyntaxKindImportKeyword: + return p.isNextTokenOpenParenOrLessThanOrDot() + } + return p.isIdentifier() +} + +func (p *Parser) isStartOfType(inStartOfParameter bool) bool { + switch p.token { + case SyntaxKindAnyKeyword, SyntaxKindUnknownKeyword, SyntaxKindStringKeyword, SyntaxKindNumberKeyword, SyntaxKindBigIntKeyword, + SyntaxKindBooleanKeyword, SyntaxKindReadonlyKeyword, SyntaxKindSymbolKeyword, SyntaxKindUniqueKeyword, SyntaxKindVoidKeyword, + SyntaxKindUndefinedKeyword, SyntaxKindNullKeyword, SyntaxKindThisKeyword, SyntaxKindTypeOfKeyword, SyntaxKindNeverKeyword, + SyntaxKindOpenBraceToken, SyntaxKindOpenBracketToken, SyntaxKindLessThanToken, SyntaxKindBarToken, SyntaxKindAmpersandToken, + SyntaxKindNewKeyword, SyntaxKindStringLiteral, SyntaxKindNumericLiteral, SyntaxKindBigintLiteral, SyntaxKindTrueKeyword, + SyntaxKindFalseKeyword, SyntaxKindObjectKeyword, SyntaxKindAsteriskToken, SyntaxKindQuestionToken, SyntaxKindExclamationToken, + SyntaxKindDotDotDotToken, SyntaxKindInferKeyword, SyntaxKindImportKeyword, SyntaxKindAssertsKeyword, SyntaxKindNoSubstitutionTemplateLiteral, + SyntaxKindTemplateHead: + return true + case SyntaxKindFunctionKeyword: + return !inStartOfParameter + case SyntaxKindMinusToken: + return !inStartOfParameter && p.lookAhead(p.nextTokenIsNumericOrBigIntLiteral) + case SyntaxKindOpenParenToken: + // Only consider '(' the start of a type if followed by ')', '...', an identifier, a modifier, + // or something that starts a type. We don't want to consider things like '(1)' a type. + return !inStartOfParameter && p.lookAhead(p.nextIsParenthesizedOrFunctionType) + } + return p.isIdentifier() +} + +func (p *Parser) nextTokenIsNumericOrBigIntLiteral() bool { + p.nextToken() + return p.token == SyntaxKindNumericLiteral || p.token == SyntaxKindBigintLiteral +} + +func (p *Parser) nextIsParenthesizedOrFunctionType() bool { + p.nextToken() + return p.token == SyntaxKindCloseParenToken || p.isStartOfParameter(false /*isJSDocParameter*/) || p.isStartOfType(false /*inStartOfParameter*/) +} + +func (p *Parser) isStartOfParameter(isJSDocParameter bool) bool { + return p.token == SyntaxKindDotDotDotToken || + p.isBindingIdentifierOrPrivateIdentifierOrPattern() || + isModifierKind(p.token) || + p.token == SyntaxKindAtToken || + p.isStartOfType(!isJSDocParameter /*inStartOfParameter*/) +} + +func (p *Parser) isBindingIdentifierOrPrivateIdentifierOrPattern() bool { + return p.token == SyntaxKindOpenBraceToken || p.token == SyntaxKindOpenBracketToken || p.token == SyntaxKindPrivateIdentifier || p.isBindingIdentifier() +} + +func (p *Parser) isNextTokenOpenParenOrLessThanOrDot() bool { + return p.lookAhead(p.nextTokenIsOpenParenOrLessThanOrDot) +} + +func (p *Parser) nextTokenIsOpenParenOrLessThanOrDot() bool { + switch p.nextToken() { + case SyntaxKindOpenParenToken, SyntaxKindLessThanToken, SyntaxKindDotToken: + return true + } + return false +} + +func (p *Parser) nextTokenIsIdentifierOnSameLine() bool { + p.nextToken() + return p.isIdentifier() && !p.hasPrecedingLineBreak() +} + +func (p *Parser) nextTokenIsIdentifierOrStringLiteralOnSameLine() bool { + p.nextToken() + return (p.isIdentifier() || p.token == SyntaxKindStringLiteral) && !p.hasPrecedingLineBreak() +} + +// Ignore strict mode flag because we will report an error in type checker instead. +func (p *Parser) isIdentifier() bool { + if p.token == SyntaxKindIdentifier { + return true + } + // If we have a 'yield' keyword, and we're in the [yield] context, then 'yield' is + // considered a keyword and is not an identifier. + // If we have a 'await' keyword, and we're in the [Await] context, then 'await' is + // considered a keyword and is not an identifier. + if p.token == SyntaxKindYieldKeyword && p.inYieldContext() || p.token == SyntaxKindAwaitKeyword && p.inAwaitContext() { + return false + } + return p.token > SyntaxKindLastReservedWord +} + +func (p *Parser) isBindingIdentifier() bool { + // `let await`/`let yield` in [Yield] or [Await] are allowed here and disallowed in the binder. + return p.token == SyntaxKindIdentifier || p.token > SyntaxKindLastReservedWord +} + +func (p *Parser) isImportAttributeName() bool { + return tokenIsIdentifierOrKeyword(p.token) || p.token == SyntaxKindStringLiteral +} + +func (p *Parser) isBinaryOperator() bool { + if p.inDisallowInContext() && p.token == SyntaxKindInKeyword { + return false + } + return getBinaryOperatorPrecedence(p.token) != OperatorPrecedenceInvalid +} + +func (p *Parser) isValidHeritageClauseObjectLiteral() bool { + return p.lookAhead(p.nextIsValidHeritageClauseObjectLiteral) +} + +func (p *Parser) nextIsValidHeritageClauseObjectLiteral() bool { + if p.nextToken() == SyntaxKindCloseBraceToken { + // if we see "extends {}" then only treat the {} as what we're extending (and not + // the class body) if we have: + // + // extends {} { + // extends {}, + // extends {} extends + // extends {} implements + next := p.nextToken() + return next == SyntaxKindCommaToken || next == SyntaxKindOpenBraceToken || next == SyntaxKindExtendsKeyword || next == SyntaxKindImplementsKeyword + } + return true +} + +func (p *Parser) isHeritageClause() bool { + return p.token == SyntaxKindExtendsKeyword || p.token == SyntaxKindImplementsKeyword +} + +func (p *Parser) isHeritageClauseExtendsOrImplementsKeyword() bool { + return p.isHeritageClause() && p.lookAhead(p.nextIsStartOfExpression) +} + +func (p *Parser) nextIsStartOfExpression() bool { + p.nextToken() + return p.isStartOfExpression() +} + +func (p *Parser) isUsingDeclaration() bool { + // 'using' always starts a lexical declaration if followed by an identifier. We also eagerly parse + // |ObjectBindingPattern| so that we can report a grammar error during check. We don't parse out + // |ArrayBindingPattern| since it potentially conflicts with element access (i.e., `using[x]`). + return p.lookAhead(p.nextTokenIsBindingIdentifierOrStartOfDestructuringOnSameLine) +} + +func (p *Parser) nextTokenIsBindingIdentifierOrStartOfDestructuringOnSameLine() bool { + p.nextToken() + return p.isBindingIdentifier() || p.token == SyntaxKindOpenBraceToken && !p.hasPrecedingLineBreak() +} + +func (p *Parser) nextTokenIsBindingIdentifierOrStartOfDestructuringOnSameLineDisallowOf() bool { + return p.nextTokenIsBindingIdentifierOrStartOfDestructuringOnSameLine() && p.token != SyntaxKindOfKeyword +} + +func (p *Parser) isAwaitUsingDeclaration() bool { + return p.lookAhead(p.nextIsUsingKeywordThenBindingIdentifierOrStartOfObjectDestructuringOnSameLine) +} + +func (p *Parser) nextIsUsingKeywordThenBindingIdentifierOrStartOfObjectDestructuringOnSameLine() bool { + return p.nextToken() == SyntaxKindUsingKeyword && p.nextTokenIsBindingIdentifierOrStartOfDestructuringOnSameLine() +} + +func (p *Parser) nextTokenIsTokenStringLiteral() bool { + return p.nextToken() == SyntaxKindStringLiteral +} + +func (p *Parser) setContextFlags(flags NodeFlags, value bool) { + if value { + p.contextFlags |= flags + } else { + p.contextFlags &= ^flags + } +} + +func doInContext[T any](p *Parser, flags NodeFlags, value bool, f func() T) T { + saveContextFlags := p.contextFlags + p.setContextFlags(flags, value) + result := f() + p.contextFlags = saveContextFlags + return result +} + +func (p *Parser) inYieldContext() bool { + return p.contextFlags&NodeFlagsYieldContext != 0 +} + +func (p *Parser) inDisallowInContext() bool { + return p.contextFlags&NodeFlagsDisallowInContext != 0 +} + +func (p *Parser) inDisallowConditionalTypesContext() bool { + return p.contextFlags&NodeFlagsDisallowConditionalTypesContext != 0 +} + +func (p *Parser) inDecoratorContext() bool { + return p.contextFlags&NodeFlagsDecoratorContext != 0 +} + +func (p *Parser) inAwaitContext() bool { + return p.contextFlags&NodeFlagsAwaitContext != 0 +} + +func (p *Parser) skipRangeTrivia(textRange TextRange) TextRange { + return NewTextRange(skipTrivia(p.sourceText, textRange.Pos()), textRange.End()) +} + +func isModifierKind(token SyntaxKind) bool { + switch token { + case SyntaxKindAbstractKeyword, SyntaxKindAccessorKeyword, SyntaxKindAsyncKeyword, SyntaxKindConstKeyword, SyntaxKindDeclareKeyword, + SyntaxKindDefaultKeyword, SyntaxKindExportKeyword, SyntaxKindImmediateKeyword, SyntaxKindInKeyword, SyntaxKindPublicKeyword, + SyntaxKindPrivateKeyword, SyntaxKindProtectedKeyword, SyntaxKindReadonlyKeyword, SyntaxKindStaticKeyword, SyntaxKindOutKeyword, + SyntaxKindOverrideKeyword: + return true + } + return false +} + +func isClassMemberModifier(token SyntaxKind) bool { + return isParameterPropertyModifier(token) || token == SyntaxKindStaticKeyword || token == SyntaxKindOverrideKeyword || token == SyntaxKindAccessorKeyword +} + +func isParameterPropertyModifier(kind SyntaxKind) bool { + return modifierToFlag(kind)&ModifierFlagsParameterPropertyModifier != 0 +} + +func isKeyword(token SyntaxKind) bool { + return SyntaxKindFirstKeyword <= token && token <= SyntaxKindLastKeyword +} + +func isReservedWord(token SyntaxKind) bool { + return SyntaxKindFirstReservedWord <= token && token <= SyntaxKindLastReservedWord +} + +func isFileProbablyExternalModule(sourceFile *SourceFile) *Node { + for _, statement := range sourceFile.statements { + if isAnExternalModuleIndicatorNode(statement) { + return statement + } + } + return getImportMetaIfNecessary(sourceFile) +} + +func isAnExternalModuleIndicatorNode(node *Statement) bool { + return hasSyntacticModifier(node, ModifierFlagsExport) || + isImportEqualsDeclaration(node) && isExternalModuleReference(node.AsImportEqualsDeclaration().moduleReference) || + isImportDeclaration(node) || isExportAssignment(node) || isExportDeclaration(node) +} + +func getImportMetaIfNecessary(sourceFile *SourceFile) *Node { + if sourceFile.AsNode().flags&NodeFlagsPossiblyContainsImportMeta != 0 { + return findChildNode(sourceFile.AsNode(), isImportMeta) + } + return nil +} + +func findChildNode(root *Node, check func(*Node) bool) *Node { + var result *Node + var visit func(*Node) bool + visit = func(node *Node) bool { + if check(node) { + result = node + return true + } + return node.ForEachChild(visit) + } + visit(root) + return result +} + +func tagNamesAreEquivalent(lhs *Expression, rhs *Expression) bool { + if lhs.kind != rhs.kind { + return false + } + switch lhs.kind { + case SyntaxKindIdentifier: + return lhs.AsIdentifier().text == rhs.AsIdentifier().text + case SyntaxKindThisKeyword: + return true + case SyntaxKindJsxNamespacedName: + return lhs.AsJsxNamespacedName().namespace.AsIdentifier().text == rhs.AsJsxNamespacedName().namespace.AsIdentifier().text && + lhs.AsJsxNamespacedName().name.AsIdentifier().text == rhs.AsJsxNamespacedName().name.AsIdentifier().text + case SyntaxKindPropertyAccessExpression: + return getTextOfIdentifierOrLiteral(lhs.AsPropertyAccessExpression().name) == getTextOfIdentifierOrLiteral(rhs.AsPropertyAccessExpression().name) && + tagNamesAreEquivalent(lhs.AsPropertyAccessExpression().expression, rhs.AsPropertyAccessExpression().expression) + } + panic("Unhandled case in tagNamesAreEquivalent") +} + +func attachFileToDiagnostics(diagnostics []*Diagnostic, file *SourceFile) []*Diagnostic { + for _, d := range diagnostics { + d.file = file + } + return diagnostics +} + +func isDeclarationFileName(fileName string) bool { + return getDeclarationFileExtension(fileName) != "" +} + +func getDeclarationFileExtension(fileName string) string { + _, base := path.Split(fileName) + for _, ext := range supportedDeclarationExtensions { + if strings.HasSuffix(base, ext) { + return ext + } + } + if strings.HasSuffix(base, ExtensionTs) { + index := strings.Index(base, ".d.") + if index >= 0 { + return base[index:] + } + } + return "" +} diff --git a/internal/compiler/parser_test.go b/internal/compiler/parser_test.go new file mode 100644 index 0000000000..14c68b1dfa --- /dev/null +++ b/internal/compiler/parser_test.go @@ -0,0 +1,20 @@ +package compiler + +import ( + "fmt" + "os" + "testing" +) + +func BenchmarkParse(b *testing.B) { + fileName := "../../_submodules/TypeScript/src/compiler/checker.ts" + bytes, err := os.ReadFile(fileName) + if err != nil { + fmt.Println(err) + } + sourceText := string(bytes) + b.ResetTimer() + for i := 0; i < b.N; i++ { + ParseSourceFile(fileName, sourceText, ScriptTargetESNext) + } +} diff --git a/internal/compiler/pkg.go b/internal/compiler/pkg.go index 039ff2b0c4..aa86763b4c 100644 --- a/internal/compiler/pkg.go +++ b/internal/compiler/pkg.go @@ -1,4 +1,2 @@ // Package compiler implements the TypeScript compiler. package compiler - -func CreateSourceFile() {} diff --git a/internal/compiler/printer.go b/internal/compiler/printer.go new file mode 100644 index 0000000000..6783a16739 --- /dev/null +++ b/internal/compiler/printer.go @@ -0,0 +1,116 @@ +package compiler + +import ( + "strconv" + "strings" +) + +func (c *Checker) symbolToString(s *Symbol) string { + return s.name // !!! +} + +func (c *Checker) typeToString(t *Type) string { + p := c.newPrinter() + p.printType(t) + return p.string() +} + +type Printer struct { + c *Checker + sb strings.Builder + depth int +} + +func (c *Checker) newPrinter() *Printer { + return &Printer{c: c} +} + +func (p *Printer) string() string { + return p.sb.String() +} + +func (p *Printer) print(s string) { + p.sb.WriteString(s) +} + +func (p *Printer) printType(t *Type) { + switch { + case t.flags&TypeFlagsIntrinsic != 0: + p.print(t.IntrinsicType().intrinsicName) + case t.flags&TypeFlagsLiteral != 0: + p.printLiteralType(t) + case t.flags&TypeFlagsObject != 0: + p.printObjectType(t) + } +} + +func (p *Printer) printLiteralType(t *Type) { + if t.flags&TypeFlagsEnumLiteral != 0 { + p.printEnumLiteral(t) + } else { + switch value := t.LiteralType().value.(type) { + case string: + p.printStringLiteral(value) + case float64: + p.printNumberLiteral(value) + case bool: + p.printBooleanLiteral(value) + case PseudoBigint: + p.printBigintLiteral(value) + } + } +} + +func (p *Printer) printStringLiteral(s string) { + p.print("\"") + p.print(s) + p.print("\"") +} + +func (p *Printer) printNumberLiteral(f float64) { + p.print(strconv.FormatFloat(f, 'g', -1, 64)) +} + +func (p *Printer) printBooleanLiteral(b bool) { + p.print(ifElse(b, "true", "false")) +} + +func (p *Printer) printBigintLiteral(b PseudoBigint) { + if b.negative { + p.print("-") + } + p.print(b.base10Value) +} + +func (p *Printer) printEnumLiteral(t *Type) { + p.print(p.c.getParentOfSymbol(t.symbol).name) + p.print(".") + p.print(t.symbol.name) +} + +func (p *Printer) printObjectType(t *Type) { + if p.depth != 0 { + p.print("???") + return + } + p.depth++ + props := p.c.getPropertiesOfObjectType(t) + sortSymbols(props) + p.print("{") + var tail bool + for _, prop := range props { + if tail { + p.print(",") + } + p.print(" ") + p.print(prop.name) + p.print(": ") + p.printType(p.c.getTypeOfSymbol(prop)) + tail = true + } + if tail { + p.print(" ") + } + p.print("}") + p.depth-- +} diff --git a/internal/compiler/program.go b/internal/compiler/program.go new file mode 100644 index 0000000000..f5380a5551 --- /dev/null +++ b/internal/compiler/program.go @@ -0,0 +1,375 @@ +package compiler + +import ( + "encoding/json" + "path/filepath" + "slices" + "strings" +) + +type ProgramOptions struct { + RootPath string + Host CompilerHost + Options *CompilerOptions + SingleThreaded bool +} + +type Program struct { + host CompilerHost + options *CompilerOptions + rootPath string + files []*SourceFile + filesByPath map[string]*SourceFile + nodeModules map[string]*SourceFile + checker *Checker + usesUriStyleNodeCoreModules Tristate + currentNodeModulesDepth int +} + +var extensions = []string{".ts", ".tsx"} + +func NewProgram(options ProgramOptions) *Program { + p := &Program{} + p.options = options.Options + if p.options == nil { + p.options = &CompilerOptions{} + } + p.host = options.Host + if p.host == nil { + p.host = NewCompilerHost(p.options, options.SingleThreaded) + } + rootPath := options.RootPath + if rootPath == "" { + rootPath = "." + } + p.rootPath = p.host.AbsFileName(rootPath) + fileInfos := p.host.ReadDirectory(rootPath, extensions) + // Sort files by descending file size + slices.SortFunc(fileInfos, func(a FileInfo, b FileInfo) int { + return int(b.Size) - int(a.Size) + }) + p.parseSourceFiles(fileInfos) + return p +} + +func (p *Program) SourceFiles() []*SourceFile { return p.files } +func (p *Program) Options() *CompilerOptions { return p.options } +func (p *Program) Host() CompilerHost { return p.host } + +func (p *Program) parseSourceFiles(fileInfos []FileInfo) { + p.files = make([]*SourceFile, len(fileInfos))[:len(fileInfos)] + for i := range fileInfos { + p.host.RunTask(func() { + fileName := fileInfos[i].Name + text, _ := p.host.ReadFile(fileName) + sourceFile := ParseSourceFile(fileName, text, getEmitScriptTarget(p.options)) + sourceFile.path, _ = filepath.Abs(fileName) + p.collectExternalModuleReferences(sourceFile) + p.files[i] = sourceFile + }) + } + p.host.WaitForTasks() + p.filesByPath = make(map[string]*SourceFile) + for _, file := range p.files { + p.filesByPath[file.path] = file + } +} + +func (p *Program) bindSourceFiles() { + for _, file := range p.files { + if !file.isBound { + p.host.RunTask(func() { + bindSourceFile(file, p.options) + }) + } + } + p.host.WaitForTasks() +} + +func (p *Program) getResolvedModule(currentSourceFile *SourceFile, moduleReference string) *SourceFile { + directory := filepath.Dir(currentSourceFile.path) + if isExternalModuleNameRelative(moduleReference) { + return p.findSourceFile(filepath.Join(directory, moduleReference)) + } + return p.findNodeModule(moduleReference) +} + +func (p *Program) findSourceFile(candidate string) *SourceFile { + extensionless := removeFileExtension(candidate) + for _, ext := range []string{ExtensionTs, ExtensionTsx, ExtensionDts} { + path := extensionless + ext + if result, ok := p.filesByPath[path]; ok { + return result + } + } + return nil +} + +func (p *Program) findNodeModule(moduleReference string) *SourceFile { + if p.nodeModules == nil { + p.nodeModules = make(map[string]*SourceFile) + } + if sourceFile, ok := p.nodeModules[moduleReference]; ok { + return sourceFile + } + sourceFile := p.tryLoadNodeModule(filepath.Join(p.rootPath, "node_modules", moduleReference)) + if sourceFile == nil { + sourceFile = p.tryLoadNodeModule(filepath.Join(p.rootPath, "node_modules/@types", moduleReference)) + } + p.nodeModules[moduleReference] = sourceFile + return sourceFile +} + +func (p *Program) tryLoadNodeModule(modulePath string) *SourceFile { + if packageJson, ok := p.host.ReadFile(filepath.Join(modulePath, "package.json")); ok { + var jsonMap map[string]any + if json.Unmarshal([]byte(packageJson), &jsonMap) == nil { + typesValue := jsonMap["types"] + if typesValue == nil { + typesValue = jsonMap["typings"] + } + if fileName, ok := typesValue.(string); ok { + path := filepath.Join(modulePath, fileName) + return p.filesByPath[path] + } + } + } + return nil +} + +func (p *Program) GetSyntacticDiagnostics(sourceFile *SourceFile) []*Diagnostic { + return p.getDiagnosticsHelper(sourceFile, p.getSyntaticDiagnosticsForFile) +} + +func (p *Program) GetBindDiagnostics(sourceFile *SourceFile) []*Diagnostic { + p.bindSourceFiles() + return p.getDiagnosticsHelper(sourceFile, p.getBindDiagnosticsForFile) +} + +func (p *Program) GetSemanticDiagnostics(sourceFile *SourceFile) []*Diagnostic { + return p.getDiagnosticsHelper(sourceFile, p.getSemanticDiagnosticsForFile) +} + +func (p *Program) GetGlobalDiagnostics() []*Diagnostic { + return sortAndDeduplicateDiagnostics(p.getTypeChecker().GetGlobalDiagnostics()) +} + +func (p *Program) getTypeChecker() *Checker { + if p.checker == nil { + p.checker = NewChecker(p) + } + return p.checker +} + +func (p *Program) getSyntaticDiagnosticsForFile(sourceFile *SourceFile) []*Diagnostic { + return sourceFile.diagnostics +} + +func (p *Program) getBindDiagnosticsForFile(sourceFile *SourceFile) []*Diagnostic { + return sourceFile.bindDiagnostics +} + +func (p *Program) getSemanticDiagnosticsForFile(sourceFile *SourceFile) []*Diagnostic { + return p.getTypeChecker().GetDiagnostics(sourceFile) +} + +func (p *Program) getDiagnosticsHelper(sourceFile *SourceFile, getDiagnostics func(*SourceFile) []*Diagnostic) []*Diagnostic { + if sourceFile != nil { + return sortAndDeduplicateDiagnostics(getDiagnostics(sourceFile)) + } + var result []*Diagnostic + for _, file := range p.files { + result = append(result, getDiagnostics(file)...) + } + return sortAndDeduplicateDiagnostics(result) +} + +func (p *Program) collectExternalModuleReferences(file *SourceFile) { + if file.moduleReferencesProcessed { + return + } + file.moduleReferencesProcessed = true + // !!! + // If we are importing helpers, we need to add a synthetic reference to resolve the + // helpers library. (A JavaScript file without `externalModuleIndicator` set might be + // a CommonJS module; `commonJsModuleIndicator` doesn't get set until the binder has + // run. We synthesize a helpers import for it just in case; it will never be used if + // the binder doesn't find and set a `commonJsModuleIndicator`.) + // if (isJavaScriptFile || (!file.isDeclarationFile && (getIsolatedModules(options) || isExternalModule(file)))) { + // if (options.importHelpers) { + // // synthesize 'import "tslib"' declaration + // imports = [createSyntheticImport(externalHelpersModuleNameText, file)]; + // } + // const jsxImport = getJSXRuntimeImport(getJSXImplicitImportBase(options, file), options); + // if (jsxImport) { + // // synthesize `import "base/jsx-runtime"` declaration + // (imports ||= []).push(createSyntheticImport(jsxImport, file)); + // } + // } + for _, node := range file.statements { + p.collectModuleReferences(file, node, false /*inAmbientModule*/) + } + // if ((file.flags & NodeFlags.PossiblyContainsDynamicImport) || isJavaScriptFile) { + // collectDynamicImportOrRequireOrJsDocImportCalls(file); + // } + // function collectDynamicImportOrRequireOrJsDocImportCalls(file: SourceFile) { + // const r = /import|require/g; + // while (r.exec(file.text) !== null) { // eslint-disable-line no-restricted-syntax + // const node = getNodeAtPosition(file, r.lastIndex); + // if (isJavaScriptFile && isRequireCall(node, /*requireStringLiteralLikeArgument*/ true)) { + // setParentRecursive(node, /*incremental*/ false); // we need parent data on imports before the program is fully bound, so we ensure it's set here + // imports = append(imports, node.arguments[0]); + // } + // // we have to check the argument list has length of at least 1. We will still have to process these even though we have parsing error. + // else if (isImportCall(node) && node.arguments.length >= 1 && isStringLiteralLike(node.arguments[0])) { + // setParentRecursive(node, /*incremental*/ false); // we need parent data on imports before the program is fully bound, so we ensure it's set here + // imports = append(imports, node.arguments[0]); + // } + // else if (isLiteralImportTypeNode(node)) { + // setParentRecursive(node, /*incremental*/ false); // we need parent data on imports before the program is fully bound, so we ensure it's set here + // imports = append(imports, node.argument.literal); + // } + // else if (isJavaScriptFile && isJSDocImportTag(node)) { + // const moduleNameExpr = getExternalModuleName(node); + // if (moduleNameExpr && isStringLiteral(moduleNameExpr) && moduleNameExpr.text) { + // setParentRecursive(node, /*incremental*/ false); + // imports = append(imports, moduleNameExpr); + // } + // } + // } + // } + // /** Returns a token if position is in [start-of-leading-trivia, end), includes JSDoc only in JS files */ + // function getNodeAtPosition(sourceFile: SourceFile, position: number): Node { + // let current: Node = sourceFile; + // const getContainingChild = (child: Node) => { + // if (child.pos <= position && (position < child.end || (position === child.end && (child.kind === SyntaxKind.EndOfFileToken)))) { + // return child; + // } + // }; + // while (true) { + // const child = isJavaScriptFile && hasJSDocNodes(current) && forEach(current.jsDoc, getContainingChild) || forEachChild(current, getContainingChild); + // if (!child) { + // return current; + // } + // current = child; + // } + // } +} + +var unprefixedNodeCoreModules = map[string]bool{ + "assert": true, + "assert/strict": true, + "async_hooks": true, + "buffer": true, + "child_process": true, + "cluster": true, + "console": true, + "constants": true, + "crypto": true, + "dgram": true, + "diagnostics_channel": true, + "dns": true, + "dns/promises": true, + "domain": true, + "events": true, + "fs": true, + "fs/promises": true, + "http": true, + "http2": true, + "https": true, + "inspector": true, + "inspector/promises": true, + "module": true, + "net": true, + "os": true, + "path": true, + "path/posix": true, + "path/win32": true, + "perf_hooks": true, + "process": true, + "punycode": true, + "querystring": true, + "readline": true, + "readline/promises": true, + "repl": true, + "stream": true, + "stream/consumers": true, + "stream/promises": true, + "stream/web": true, + "string_decoder": true, + "sys": true, + "test/mock_loader": true, + "timers": true, + "timers/promises": true, + "tls": true, + "trace_events": true, + "tty": true, + "url": true, + "util": true, + "util/types": true, + "v8": true, + "vm": true, + "wasi": true, + "worker_threads": true, + "zlib": true, +} + +var exclusivelyPrefixedNodeCoreModules = map[string]bool{ + "node:sea": true, + "node:sqlite": true, + "node:test": true, + "node:test/reporters": true, +} + +func (p *Program) collectModuleReferences(file *SourceFile, node *Statement, inAmbientModule bool) { + if isAnyImportOrReExport(node) { + moduleNameExpr := getExternalModuleName(node) + // TypeScript 1.0 spec (April 2014): 12.1.6 + // An ExternalImportDeclaration in an AmbientExternalModuleDeclaration may reference other external modules + // only through top - level external module names. Relative external module names are not permitted. + if moduleNameExpr != nil && isStringLiteral(moduleNameExpr) { + moduleName := moduleNameExpr.AsStringLiteral().text + if moduleName != "" && (!inAmbientModule || !isExternalModuleNameRelative(moduleName)) { + setParentInChildren(node) // we need parent data on imports before the program is fully bound, so we ensure it's set here + file.imports = append(file.imports, moduleNameExpr) + if file.usesUriStyleNodeCoreModules != TSTrue && p.currentNodeModulesDepth == 0 && !file.isDeclarationFile { + if strings.HasPrefix(moduleName, "node:") && !exclusivelyPrefixedNodeCoreModules[moduleName] { + // Presence of `node:` prefix takes precedence over unprefixed node core modules + file.usesUriStyleNodeCoreModules = TSTrue + } else if file.usesUriStyleNodeCoreModules == TSUnknown && unprefixedNodeCoreModules[moduleName] { + // Avoid `unprefixedNodeCoreModules.has` for every import + file.usesUriStyleNodeCoreModules = TSFalse + } + } + } + } + return + } + if isModuleDeclaration(node) && isAmbientModule(node) && (inAmbientModule || hasSyntacticModifier(node, ModifierFlagsAmbient) || file.isDeclarationFile) { + setParentInChildren(node) + nameText := getTextOfIdentifierOrLiteral(node.AsModuleDeclaration().name) + // Ambient module declarations can be interpreted as augmentations for some existing external modules. + // This will happen in two cases: + // - if current file is external module then module augmentation is a ambient module declaration defined in the top level scope + // - if current file is not external module then module augmentation is an ambient module declaration with non-relative module name + // immediately nested in top level ambient module declaration . + if isExternalModule(file) || (inAmbientModule && !isExternalModuleNameRelative(nameText)) { + file.moduleAugmentations = append(file.moduleAugmentations, node.AsModuleDeclaration().name) + } else if !inAmbientModule { + if file.isDeclarationFile { + // for global .d.ts files record name of ambient module + file.ambientModuleNames = append(file.ambientModuleNames, nameText) + } + // An AmbientExternalModuleDeclaration declares an external module. + // This type of declaration is permitted only in the global module. + // The StringLiteral must specify a top - level external module name. + // Relative external module names are not permitted + // NOTE: body of ambient module is always a module block, if it exists + if node.AsModuleDeclaration().body != nil { + for _, statement := range node.AsModuleDeclaration().body.AsModuleBlock().statements { + p.collectModuleReferences(file, statement, true /*inAmbientModule*/) + } + } + } + } +} diff --git a/internal/compiler/scanner.go b/internal/compiler/scanner.go new file mode 100644 index 0000000000..4effb8240a --- /dev/null +++ b/internal/compiler/scanner.go @@ -0,0 +1,1740 @@ +package compiler + +import ( + "fmt" + "strconv" + "strings" + "unicode/utf8" + + "github.com/microsoft/typescript-go/internal/compiler/diagnostics" +) + +type TokenFlags int32 + +const ( + TokenFlagsNone TokenFlags = 0 + TokenFlagsPrecedingLineBreak TokenFlags = 1 << 0 + TokenFlagsPrecedingJSDocComment TokenFlags = 1 << 1 + TokenFlagsUnterminated TokenFlags = 1 << 2 + TokenFlagsExtendedUnicodeEscape TokenFlags = 1 << 3 // e.g. `\u{10ffff}` + TokenFlagsScientific TokenFlags = 1 << 4 // e.g. `10e2` + TokenFlagsOctal TokenFlags = 1 << 5 // e.g. `0777` + TokenFlagsHexSpecifier TokenFlags = 1 << 6 // e.g. `0x00000000` + TokenFlagsBinarySpecifier TokenFlags = 1 << 7 // e.g. `0b0110010000000000` + TokenFlagsOctalSpecifier TokenFlags = 1 << 8 // e.g. `0o777` + TokenFlagsContainsSeparator TokenFlags = 1 << 9 // e.g. `0b1100_0101` + TokenFlagsUnicodeEscape TokenFlags = 1 << 10 // e.g. `\u00a0` + TokenFlagsContainsInvalidEscape TokenFlags = 1 << 11 // e.g. `\uhello` + TokenFlagsHexEscape TokenFlags = 1 << 12 // e.g. `\xa0` + TokenFlagsContainsLeadingZero TokenFlags = 1 << 13 // e.g. `0888` + TokenFlagsContainsInvalidSeparator TokenFlags = 1 << 14 // e.g. `0_1` + TokenFlagsPrecedingJSDocLeadingAsterisks TokenFlags = 1 << 15 + TokenFlagsBinaryOrOctalSpecifier TokenFlags = TokenFlagsBinarySpecifier | TokenFlagsOctalSpecifier + TokenFlagsWithSpecifier TokenFlags = TokenFlagsHexSpecifier | TokenFlagsBinaryOrOctalSpecifier + TokenFlagsStringLiteralFlags TokenFlags = TokenFlagsHexEscape | TokenFlagsUnicodeEscape | TokenFlagsExtendedUnicodeEscape | TokenFlagsContainsInvalidEscape + TokenFlagsNumericLiteralFlags TokenFlags = TokenFlagsScientific | TokenFlagsOctal | TokenFlagsContainsLeadingZero | TokenFlagsWithSpecifier | TokenFlagsContainsSeparator | TokenFlagsContainsInvalidSeparator + TokenFlagsTemplateLiteralLikeFlags TokenFlags = TokenFlagsHexEscape | TokenFlagsUnicodeEscape | TokenFlagsExtendedUnicodeEscape | TokenFlagsContainsInvalidEscape + TokenFlagsIsInvalid TokenFlags = TokenFlagsOctal | TokenFlagsContainsLeadingZero | TokenFlagsContainsInvalidSeparator | TokenFlagsContainsInvalidEscape +) + +type EscapeSequenceScanningFlags int32 + +const ( + EscapeSequenceScanningFlagsString EscapeSequenceScanningFlags = 1 << 0 + EscapeSequenceScanningFlagsReportErrors EscapeSequenceScanningFlags = 1 << 1 + EscapeSequenceScanningFlagsRegularExpression EscapeSequenceScanningFlags = 1 << 2 + EscapeSequenceScanningFlagsAnnexB EscapeSequenceScanningFlags = 1 << 3 + EscapeSequenceScanningFlagsAnyUnicodeMode EscapeSequenceScanningFlags = 1 << 4 + EscapeSequenceScanningFlagsAtomEscape EscapeSequenceScanningFlags = 1 << 5 + EscapeSequenceScanningFlagsReportInvalidEscapeErrors EscapeSequenceScanningFlags = EscapeSequenceScanningFlagsRegularExpression | EscapeSequenceScanningFlagsReportErrors + EscapeSequenceScanningFlagsAllowExtendedUnicodeEscape EscapeSequenceScanningFlags = EscapeSequenceScanningFlagsString | EscapeSequenceScanningFlagsAnyUnicodeMode +) + +type ErrorCallback func(diagnostic *diagnostics.Message, start, length int, args ...any) + +var textToKeyword = map[string]SyntaxKind{ + "abstract": SyntaxKindAbstractKeyword, + "accessor": SyntaxKindAccessorKeyword, + "any": SyntaxKindAnyKeyword, + "as": SyntaxKindAsKeyword, + "asserts": SyntaxKindAssertsKeyword, + "assert": SyntaxKindAssertKeyword, + "bigint": SyntaxKindBigIntKeyword, + "boolean": SyntaxKindBooleanKeyword, + "break": SyntaxKindBreakKeyword, + "case": SyntaxKindCaseKeyword, + "catch": SyntaxKindCatchKeyword, + "class": SyntaxKindClassKeyword, + "continue": SyntaxKindContinueKeyword, + "const": SyntaxKindConstKeyword, + "constructor": SyntaxKindConstructorKeyword, + "debugger": SyntaxKindDebuggerKeyword, + "declare": SyntaxKindDeclareKeyword, + "default": SyntaxKindDefaultKeyword, + "delete": SyntaxKindDeleteKeyword, + "do": SyntaxKindDoKeyword, + "else": SyntaxKindElseKeyword, + "enum": SyntaxKindEnumKeyword, + "export": SyntaxKindExportKeyword, + "extends": SyntaxKindExtendsKeyword, + "false": SyntaxKindFalseKeyword, + "finally": SyntaxKindFinallyKeyword, + "for": SyntaxKindForKeyword, + "from": SyntaxKindFromKeyword, + "function": SyntaxKindFunctionKeyword, + "get": SyntaxKindGetKeyword, + "if": SyntaxKindIfKeyword, + "immediate": SyntaxKindImmediateKeyword, + "implements": SyntaxKindImplementsKeyword, + "import": SyntaxKindImportKeyword, + "in": SyntaxKindInKeyword, + "infer": SyntaxKindInferKeyword, + "instanceof": SyntaxKindInstanceOfKeyword, + "interface": SyntaxKindInterfaceKeyword, + "intrinsic": SyntaxKindIntrinsicKeyword, + "is": SyntaxKindIsKeyword, + "keyof": SyntaxKindKeyOfKeyword, + "let": SyntaxKindLetKeyword, + "module": SyntaxKindModuleKeyword, + "namespace": SyntaxKindNamespaceKeyword, + "never": SyntaxKindNeverKeyword, + "new": SyntaxKindNewKeyword, + "null": SyntaxKindNullKeyword, + "number": SyntaxKindNumberKeyword, + "object": SyntaxKindObjectKeyword, + "package": SyntaxKindPackageKeyword, + "private": SyntaxKindPrivateKeyword, + "protected": SyntaxKindProtectedKeyword, + "public": SyntaxKindPublicKeyword, + "override": SyntaxKindOverrideKeyword, + "out": SyntaxKindOutKeyword, + "readonly": SyntaxKindReadonlyKeyword, + "require": SyntaxKindRequireKeyword, + "global": SyntaxKindGlobalKeyword, + "return": SyntaxKindReturnKeyword, + "satisfies": SyntaxKindSatisfiesKeyword, + "set": SyntaxKindSetKeyword, + "static": SyntaxKindStaticKeyword, + "string": SyntaxKindStringKeyword, + "super": SyntaxKindSuperKeyword, + "switch": SyntaxKindSwitchKeyword, + "symbol": SyntaxKindSymbolKeyword, + "this": SyntaxKindThisKeyword, + "throw": SyntaxKindThrowKeyword, + "true": SyntaxKindTrueKeyword, + "try": SyntaxKindTryKeyword, + "type": SyntaxKindTypeKeyword, + "typeof": SyntaxKindTypeOfKeyword, + "undefined": SyntaxKindUndefinedKeyword, + "unique": SyntaxKindUniqueKeyword, + "unknown": SyntaxKindUnknownKeyword, + "using": SyntaxKindUsingKeyword, + "var": SyntaxKindVarKeyword, + "void": SyntaxKindVoidKeyword, + "while": SyntaxKindWhileKeyword, + "with": SyntaxKindWithKeyword, + "yield": SyntaxKindYieldKeyword, + "async": SyntaxKindAsyncKeyword, + "await": SyntaxKindAwaitKeyword, + "of": SyntaxKindOfKeyword, +} + +var textToToken = map[string]SyntaxKind{ + "{": SyntaxKindOpenBraceToken, + "}": SyntaxKindCloseBraceToken, + "(": SyntaxKindOpenParenToken, + ")": SyntaxKindCloseParenToken, + "[": SyntaxKindOpenBracketToken, + "]": SyntaxKindCloseBracketToken, + ".": SyntaxKindDotToken, + "...": SyntaxKindDotDotDotToken, + ";": SyntaxKindSemicolonToken, + ",": SyntaxKindCommaToken, + "<": SyntaxKindLessThanToken, + ">": SyntaxKindGreaterThanToken, + "<=": SyntaxKindLessThanEqualsToken, + ">=": SyntaxKindGreaterThanEqualsToken, + "==": SyntaxKindEqualsEqualsToken, + "!=": SyntaxKindExclamationEqualsToken, + "===": SyntaxKindEqualsEqualsEqualsToken, + "!==": SyntaxKindExclamationEqualsEqualsToken, + "=>": SyntaxKindEqualsGreaterThanToken, + "+": SyntaxKindPlusToken, + "-": SyntaxKindMinusToken, + "**": SyntaxKindAsteriskAsteriskToken, + "*": SyntaxKindAsteriskToken, + "/": SyntaxKindSlashToken, + "%": SyntaxKindPercentToken, + "++": SyntaxKindPlusPlusToken, + "--": SyntaxKindMinusMinusToken, + "<<": SyntaxKindLessThanLessThanToken, + ">": SyntaxKindGreaterThanGreaterThanToken, + ">>>": SyntaxKindGreaterThanGreaterThanGreaterThanToken, + "&": SyntaxKindAmpersandToken, + "|": SyntaxKindBarToken, + "^": SyntaxKindCaretToken, + "!": SyntaxKindExclamationToken, + "~": SyntaxKindTildeToken, + "&&": SyntaxKindAmpersandAmpersandToken, + "||": SyntaxKindBarBarToken, + "?": SyntaxKindQuestionToken, + "??": SyntaxKindQuestionQuestionToken, + "?.": SyntaxKindQuestionDotToken, + ":": SyntaxKindColonToken, + "=": SyntaxKindEqualsToken, + "+=": SyntaxKindPlusEqualsToken, + "-=": SyntaxKindMinusEqualsToken, + "*=": SyntaxKindAsteriskEqualsToken, + "**=": SyntaxKindAsteriskAsteriskEqualsToken, + "/=": SyntaxKindSlashEqualsToken, + "%=": SyntaxKindPercentEqualsToken, + "<<=": SyntaxKindLessThanLessThanEqualsToken, + ">>=": SyntaxKindGreaterThanGreaterThanEqualsToken, + ">>>=": SyntaxKindGreaterThanGreaterThanGreaterThanEqualsToken, + "&=": SyntaxKindAmpersandEqualsToken, + "|=": SyntaxKindBarEqualsToken, + "^=": SyntaxKindCaretEqualsToken, + "||=": SyntaxKindBarBarEqualsToken, + "&&=": SyntaxKindAmpersandAmpersandEqualsToken, + "??=": SyntaxKindQuestionQuestionEqualsToken, + "@": SyntaxKindAtToken, + "#": SyntaxKindHashToken, + "`": SyntaxKindBacktickToken, +} + +// As per ECMAScript Language Specification 5th Edition, Section 7.6: ISyntaxToken Names and Identifiers +// IdentifierStart :: +// +// Can contain Unicode 6.2 categories: +// Uppercase letter (Lu), +// Lowercase letter (Ll), +// Titlecase letter (Lt), +// Modifier letter (Lm), +// Other letter (Lo), or +// Letter number (Nl). +// +// IdentifierPart :: +// +// Can contain IdentifierStart + Unicode 6.2 categories: +// Non-spacing mark (Mn), +// Combining spacing mark (Mc), +// Decimal number (Nd), +// Connector punctuation (Pc), +// , or +// . +// +// Codepoint ranges for ES5 Identifiers are extracted from the Unicode 6.2 specification at: +// http://www.unicode.org/Public/6.2.0/ucd/UnicodeData.txt +var unicodeES5IdentifierStart = []rune{170, 170, 181, 181, 186, 186, 192, 214, 216, 246, 248, 705, 710, 721, 736, 740, 748, 748, 750, 750, 880, 884, 886, 887, 890, 893, 902, 902, 904, 906, 908, 908, 910, 929, 931, 1013, 1015, 1153, 1162, 1319, 1329, 1366, 1369, 1369, 1377, 1415, 1488, 1514, 1520, 1522, 1568, 1610, 1646, 1647, 1649, 1747, 1749, 1749, 1765, 1766, 1774, 1775, 1786, 1788, 1791, 1791, 1808, 1808, 1810, 1839, 1869, 1957, 1969, 1969, 1994, 2026, 2036, 2037, 2042, 2042, 2048, 2069, 2074, 2074, 2084, 2084, 2088, 2088, 2112, 2136, 2208, 2208, 2210, 2220, 2308, 2361, 2365, 2365, 2384, 2384, 2392, 2401, 2417, 2423, 2425, 2431, 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, 2486, 2489, 2493, 2493, 2510, 2510, 2524, 2525, 2527, 2529, 2544, 2545, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611, 2613, 2614, 2616, 2617, 2649, 2652, 2654, 2654, 2674, 2676, 2693, 2701, 2703, 2705, 2707, 2728, 2730, 2736, 2738, 2739, 2741, 2745, 2749, 2749, 2768, 2768, 2784, 2785, 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, 2869, 2873, 2877, 2877, 2908, 2909, 2911, 2913, 2929, 2929, 2947, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 3001, 3024, 3024, 3077, 3084, 3086, 3088, 3090, 3112, 3114, 3123, 3125, 3129, 3133, 3133, 3160, 3161, 3168, 3169, 3205, 3212, 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3261, 3261, 3294, 3294, 3296, 3297, 3313, 3314, 3333, 3340, 3342, 3344, 3346, 3386, 3389, 3389, 3406, 3406, 3424, 3425, 3450, 3455, 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, 3585, 3632, 3634, 3635, 3648, 3654, 3713, 3714, 3716, 3716, 3719, 3720, 3722, 3722, 3725, 3725, 3732, 3735, 3737, 3743, 3745, 3747, 3749, 3749, 3751, 3751, 3754, 3755, 3757, 3760, 3762, 3763, 3773, 3773, 3776, 3780, 3782, 3782, 3804, 3807, 3840, 3840, 3904, 3911, 3913, 3948, 3976, 3980, 4096, 4138, 4159, 4159, 4176, 4181, 4186, 4189, 4193, 4193, 4197, 4198, 4206, 4208, 4213, 4225, 4238, 4238, 4256, 4293, 4295, 4295, 4301, 4301, 4304, 4346, 4348, 4680, 4682, 4685, 4688, 4694, 4696, 4696, 4698, 4701, 4704, 4744, 4746, 4749, 4752, 4784, 4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4822, 4824, 4880, 4882, 4885, 4888, 4954, 4992, 5007, 5024, 5108, 5121, 5740, 5743, 5759, 5761, 5786, 5792, 5866, 5870, 5872, 5888, 5900, 5902, 5905, 5920, 5937, 5952, 5969, 5984, 5996, 5998, 6000, 6016, 6067, 6103, 6103, 6108, 6108, 6176, 6263, 6272, 6312, 6314, 6314, 6320, 6389, 6400, 6428, 6480, 6509, 6512, 6516, 6528, 6571, 6593, 6599, 6656, 6678, 6688, 6740, 6823, 6823, 6917, 6963, 6981, 6987, 7043, 7072, 7086, 7087, 7098, 7141, 7168, 7203, 7245, 7247, 7258, 7293, 7401, 7404, 7406, 7409, 7413, 7414, 7424, 7615, 7680, 7957, 7960, 7965, 7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188, 8305, 8305, 8319, 8319, 8336, 8348, 8450, 8450, 8455, 8455, 8458, 8467, 8469, 8469, 8473, 8477, 8484, 8484, 8486, 8486, 8488, 8488, 8490, 8493, 8495, 8505, 8508, 8511, 8517, 8521, 8526, 8526, 8544, 8584, 11264, 11310, 11312, 11358, 11360, 11492, 11499, 11502, 11506, 11507, 11520, 11557, 11559, 11559, 11565, 11565, 11568, 11623, 11631, 11631, 11648, 11670, 11680, 11686, 11688, 11694, 11696, 11702, 11704, 11710, 11712, 11718, 11720, 11726, 11728, 11734, 11736, 11742, 11823, 11823, 12293, 12295, 12321, 12329, 12337, 12341, 12344, 12348, 12353, 12438, 12445, 12447, 12449, 12538, 12540, 12543, 12549, 12589, 12593, 12686, 12704, 12730, 12784, 12799, 13312, 19893, 19968, 40908, 40960, 42124, 42192, 42237, 42240, 42508, 42512, 42527, 42538, 42539, 42560, 42606, 42623, 42647, 42656, 42735, 42775, 42783, 42786, 42888, 42891, 42894, 42896, 42899, 42912, 42922, 43000, 43009, 43011, 43013, 43015, 43018, 43020, 43042, 43072, 43123, 43138, 43187, 43250, 43255, 43259, 43259, 43274, 43301, 43312, 43334, 43360, 43388, 43396, 43442, 43471, 43471, 43520, 43560, 43584, 43586, 43588, 43595, 43616, 43638, 43642, 43642, 43648, 43695, 43697, 43697, 43701, 43702, 43705, 43709, 43712, 43712, 43714, 43714, 43739, 43741, 43744, 43754, 43762, 43764, 43777, 43782, 43785, 43790, 43793, 43798, 43808, 43814, 43816, 43822, 43968, 44002, 44032, 55203, 55216, 55238, 55243, 55291, 63744, 64109, 64112, 64217, 64256, 64262, 64275, 64279, 64285, 64285, 64287, 64296, 64298, 64310, 64312, 64316, 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, 65136, 65140, 65142, 65276, 65313, 65338, 65345, 65370, 65382, 65470, 65474, 65479, 65482, 65487, 65490, 65495, 65498, 65500} +var unicodeES5IdentifierPart = []rune{170, 170, 181, 181, 186, 186, 192, 214, 216, 246, 248, 705, 710, 721, 736, 740, 748, 748, 750, 750, 768, 884, 886, 887, 890, 893, 902, 902, 904, 906, 908, 908, 910, 929, 931, 1013, 1015, 1153, 1155, 1159, 1162, 1319, 1329, 1366, 1369, 1369, 1377, 1415, 1425, 1469, 1471, 1471, 1473, 1474, 1476, 1477, 1479, 1479, 1488, 1514, 1520, 1522, 1552, 1562, 1568, 1641, 1646, 1747, 1749, 1756, 1759, 1768, 1770, 1788, 1791, 1791, 1808, 1866, 1869, 1969, 1984, 2037, 2042, 2042, 2048, 2093, 2112, 2139, 2208, 2208, 2210, 2220, 2276, 2302, 2304, 2403, 2406, 2415, 2417, 2423, 2425, 2431, 2433, 2435, 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, 2486, 2489, 2492, 2500, 2503, 2504, 2507, 2510, 2519, 2519, 2524, 2525, 2527, 2531, 2534, 2545, 2561, 2563, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611, 2613, 2614, 2616, 2617, 2620, 2620, 2622, 2626, 2631, 2632, 2635, 2637, 2641, 2641, 2649, 2652, 2654, 2654, 2662, 2677, 2689, 2691, 2693, 2701, 2703, 2705, 2707, 2728, 2730, 2736, 2738, 2739, 2741, 2745, 2748, 2757, 2759, 2761, 2763, 2765, 2768, 2768, 2784, 2787, 2790, 2799, 2817, 2819, 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, 2869, 2873, 2876, 2884, 2887, 2888, 2891, 2893, 2902, 2903, 2908, 2909, 2911, 2915, 2918, 2927, 2929, 2929, 2946, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 3001, 3006, 3010, 3014, 3016, 3018, 3021, 3024, 3024, 3031, 3031, 3046, 3055, 3073, 3075, 3077, 3084, 3086, 3088, 3090, 3112, 3114, 3123, 3125, 3129, 3133, 3140, 3142, 3144, 3146, 3149, 3157, 3158, 3160, 3161, 3168, 3171, 3174, 3183, 3202, 3203, 3205, 3212, 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3260, 3268, 3270, 3272, 3274, 3277, 3285, 3286, 3294, 3294, 3296, 3299, 3302, 3311, 3313, 3314, 3330, 3331, 3333, 3340, 3342, 3344, 3346, 3386, 3389, 3396, 3398, 3400, 3402, 3406, 3415, 3415, 3424, 3427, 3430, 3439, 3450, 3455, 3458, 3459, 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, 3530, 3530, 3535, 3540, 3542, 3542, 3544, 3551, 3570, 3571, 3585, 3642, 3648, 3662, 3664, 3673, 3713, 3714, 3716, 3716, 3719, 3720, 3722, 3722, 3725, 3725, 3732, 3735, 3737, 3743, 3745, 3747, 3749, 3749, 3751, 3751, 3754, 3755, 3757, 3769, 3771, 3773, 3776, 3780, 3782, 3782, 3784, 3789, 3792, 3801, 3804, 3807, 3840, 3840, 3864, 3865, 3872, 3881, 3893, 3893, 3895, 3895, 3897, 3897, 3902, 3911, 3913, 3948, 3953, 3972, 3974, 3991, 3993, 4028, 4038, 4038, 4096, 4169, 4176, 4253, 4256, 4293, 4295, 4295, 4301, 4301, 4304, 4346, 4348, 4680, 4682, 4685, 4688, 4694, 4696, 4696, 4698, 4701, 4704, 4744, 4746, 4749, 4752, 4784, 4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4822, 4824, 4880, 4882, 4885, 4888, 4954, 4957, 4959, 4992, 5007, 5024, 5108, 5121, 5740, 5743, 5759, 5761, 5786, 5792, 5866, 5870, 5872, 5888, 5900, 5902, 5908, 5920, 5940, 5952, 5971, 5984, 5996, 5998, 6000, 6002, 6003, 6016, 6099, 6103, 6103, 6108, 6109, 6112, 6121, 6155, 6157, 6160, 6169, 6176, 6263, 6272, 6314, 6320, 6389, 6400, 6428, 6432, 6443, 6448, 6459, 6470, 6509, 6512, 6516, 6528, 6571, 6576, 6601, 6608, 6617, 6656, 6683, 6688, 6750, 6752, 6780, 6783, 6793, 6800, 6809, 6823, 6823, 6912, 6987, 6992, 7001, 7019, 7027, 7040, 7155, 7168, 7223, 7232, 7241, 7245, 7293, 7376, 7378, 7380, 7414, 7424, 7654, 7676, 7957, 7960, 7965, 7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188, 8204, 8205, 8255, 8256, 8276, 8276, 8305, 8305, 8319, 8319, 8336, 8348, 8400, 8412, 8417, 8417, 8421, 8432, 8450, 8450, 8455, 8455, 8458, 8467, 8469, 8469, 8473, 8477, 8484, 8484, 8486, 8486, 8488, 8488, 8490, 8493, 8495, 8505, 8508, 8511, 8517, 8521, 8526, 8526, 8544, 8584, 11264, 11310, 11312, 11358, 11360, 11492, 11499, 11507, 11520, 11557, 11559, 11559, 11565, 11565, 11568, 11623, 11631, 11631, 11647, 11670, 11680, 11686, 11688, 11694, 11696, 11702, 11704, 11710, 11712, 11718, 11720, 11726, 11728, 11734, 11736, 11742, 11744, 11775, 11823, 11823, 12293, 12295, 12321, 12335, 12337, 12341, 12344, 12348, 12353, 12438, 12441, 12442, 12445, 12447, 12449, 12538, 12540, 12543, 12549, 12589, 12593, 12686, 12704, 12730, 12784, 12799, 13312, 19893, 19968, 40908, 40960, 42124, 42192, 42237, 42240, 42508, 42512, 42539, 42560, 42607, 42612, 42621, 42623, 42647, 42655, 42737, 42775, 42783, 42786, 42888, 42891, 42894, 42896, 42899, 42912, 42922, 43000, 43047, 43072, 43123, 43136, 43204, 43216, 43225, 43232, 43255, 43259, 43259, 43264, 43309, 43312, 43347, 43360, 43388, 43392, 43456, 43471, 43481, 43520, 43574, 43584, 43597, 43600, 43609, 43616, 43638, 43642, 43643, 43648, 43714, 43739, 43741, 43744, 43759, 43762, 43766, 43777, 43782, 43785, 43790, 43793, 43798, 43808, 43814, 43816, 43822, 43968, 44010, 44012, 44013, 44016, 44025, 44032, 55203, 55216, 55238, 55243, 55291, 63744, 64109, 64112, 64217, 64256, 64262, 64275, 64279, 64285, 64296, 64298, 64310, 64312, 64316, 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, 65024, 65039, 65056, 65062, 65075, 65076, 65101, 65103, 65136, 65140, 65142, 65276, 65296, 65305, 65313, 65338, 65343, 65343, 65345, 65370, 65382, 65470, 65474, 65479, 65482, 65487, 65490, 65495, 65498, 65500} + +// Generated by scripts/regenerate-unicode-identifier-parts.mjs on node v22.1.0 with unicode 15.1 +// based on http://www.unicode.org/reports/tr31/ and https://www.ecma-international.org/ecma-262/6.0/#sec-names-and-keywords +// unicodeESNextIdentifierStart corresponds to the ID_Start and Other_ID_Start property, and +// unicodeESNextIdentifierPart corresponds to ID_Continue, Other_ID_Continue, plus ID_Start and Other_ID_Start +var unicodeESNextIdentifierStart = []rune{65, 90, 97, 122, 170, 170, 181, 181, 186, 186, 192, 214, 216, 246, 248, 705, 710, 721, 736, 740, 748, 748, 750, 750, 880, 884, 886, 887, 890, 893, 895, 895, 902, 902, 904, 906, 908, 908, 910, 929, 931, 1013, 1015, 1153, 1162, 1327, 1329, 1366, 1369, 1369, 1376, 1416, 1488, 1514, 1519, 1522, 1568, 1610, 1646, 1647, 1649, 1747, 1749, 1749, 1765, 1766, 1774, 1775, 1786, 1788, 1791, 1791, 1808, 1808, 1810, 1839, 1869, 1957, 1969, 1969, 1994, 2026, 2036, 2037, 2042, 2042, 2048, 2069, 2074, 2074, 2084, 2084, 2088, 2088, 2112, 2136, 2144, 2154, 2160, 2183, 2185, 2190, 2208, 2249, 2308, 2361, 2365, 2365, 2384, 2384, 2392, 2401, 2417, 2432, 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, 2486, 2489, 2493, 2493, 2510, 2510, 2524, 2525, 2527, 2529, 2544, 2545, 2556, 2556, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611, 2613, 2614, 2616, 2617, 2649, 2652, 2654, 2654, 2674, 2676, 2693, 2701, 2703, 2705, 2707, 2728, 2730, 2736, 2738, 2739, 2741, 2745, 2749, 2749, 2768, 2768, 2784, 2785, 2809, 2809, 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, 2869, 2873, 2877, 2877, 2908, 2909, 2911, 2913, 2929, 2929, 2947, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 3001, 3024, 3024, 3077, 3084, 3086, 3088, 3090, 3112, 3114, 3129, 3133, 3133, 3160, 3162, 3165, 3165, 3168, 3169, 3200, 3200, 3205, 3212, 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3261, 3261, 3293, 3294, 3296, 3297, 3313, 3314, 3332, 3340, 3342, 3344, 3346, 3386, 3389, 3389, 3406, 3406, 3412, 3414, 3423, 3425, 3450, 3455, 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, 3585, 3632, 3634, 3635, 3648, 3654, 3713, 3714, 3716, 3716, 3718, 3722, 3724, 3747, 3749, 3749, 3751, 3760, 3762, 3763, 3773, 3773, 3776, 3780, 3782, 3782, 3804, 3807, 3840, 3840, 3904, 3911, 3913, 3948, 3976, 3980, 4096, 4138, 4159, 4159, 4176, 4181, 4186, 4189, 4193, 4193, 4197, 4198, 4206, 4208, 4213, 4225, 4238, 4238, 4256, 4293, 4295, 4295, 4301, 4301, 4304, 4346, 4348, 4680, 4682, 4685, 4688, 4694, 4696, 4696, 4698, 4701, 4704, 4744, 4746, 4749, 4752, 4784, 4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4822, 4824, 4880, 4882, 4885, 4888, 4954, 4992, 5007, 5024, 5109, 5112, 5117, 5121, 5740, 5743, 5759, 5761, 5786, 5792, 5866, 5870, 5880, 5888, 5905, 5919, 5937, 5952, 5969, 5984, 5996, 5998, 6000, 6016, 6067, 6103, 6103, 6108, 6108, 6176, 6264, 6272, 6312, 6314, 6314, 6320, 6389, 6400, 6430, 6480, 6509, 6512, 6516, 6528, 6571, 6576, 6601, 6656, 6678, 6688, 6740, 6823, 6823, 6917, 6963, 6981, 6988, 7043, 7072, 7086, 7087, 7098, 7141, 7168, 7203, 7245, 7247, 7258, 7293, 7296, 7304, 7312, 7354, 7357, 7359, 7401, 7404, 7406, 7411, 7413, 7414, 7418, 7418, 7424, 7615, 7680, 7957, 7960, 7965, 7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188, 8305, 8305, 8319, 8319, 8336, 8348, 8450, 8450, 8455, 8455, 8458, 8467, 8469, 8469, 8472, 8477, 8484, 8484, 8486, 8486, 8488, 8488, 8490, 8505, 8508, 8511, 8517, 8521, 8526, 8526, 8544, 8584, 11264, 11492, 11499, 11502, 11506, 11507, 11520, 11557, 11559, 11559, 11565, 11565, 11568, 11623, 11631, 11631, 11648, 11670, 11680, 11686, 11688, 11694, 11696, 11702, 11704, 11710, 11712, 11718, 11720, 11726, 11728, 11734, 11736, 11742, 12293, 12295, 12321, 12329, 12337, 12341, 12344, 12348, 12353, 12438, 12443, 12447, 12449, 12538, 12540, 12543, 12549, 12591, 12593, 12686, 12704, 12735, 12784, 12799, 13312, 19903, 19968, 42124, 42192, 42237, 42240, 42508, 42512, 42527, 42538, 42539, 42560, 42606, 42623, 42653, 42656, 42735, 42775, 42783, 42786, 42888, 42891, 42954, 42960, 42961, 42963, 42963, 42965, 42969, 42994, 43009, 43011, 43013, 43015, 43018, 43020, 43042, 43072, 43123, 43138, 43187, 43250, 43255, 43259, 43259, 43261, 43262, 43274, 43301, 43312, 43334, 43360, 43388, 43396, 43442, 43471, 43471, 43488, 43492, 43494, 43503, 43514, 43518, 43520, 43560, 43584, 43586, 43588, 43595, 43616, 43638, 43642, 43642, 43646, 43695, 43697, 43697, 43701, 43702, 43705, 43709, 43712, 43712, 43714, 43714, 43739, 43741, 43744, 43754, 43762, 43764, 43777, 43782, 43785, 43790, 43793, 43798, 43808, 43814, 43816, 43822, 43824, 43866, 43868, 43881, 43888, 44002, 44032, 55203, 55216, 55238, 55243, 55291, 63744, 64109, 64112, 64217, 64256, 64262, 64275, 64279, 64285, 64285, 64287, 64296, 64298, 64310, 64312, 64316, 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, 65136, 65140, 65142, 65276, 65313, 65338, 65345, 65370, 65382, 65470, 65474, 65479, 65482, 65487, 65490, 65495, 65498, 65500, 65536, 65547, 65549, 65574, 65576, 65594, 65596, 65597, 65599, 65613, 65616, 65629, 65664, 65786, 65856, 65908, 66176, 66204, 66208, 66256, 66304, 66335, 66349, 66378, 66384, 66421, 66432, 66461, 66464, 66499, 66504, 66511, 66513, 66517, 66560, 66717, 66736, 66771, 66776, 66811, 66816, 66855, 66864, 66915, 66928, 66938, 66940, 66954, 66956, 66962, 66964, 66965, 66967, 66977, 66979, 66993, 66995, 67001, 67003, 67004, 67072, 67382, 67392, 67413, 67424, 67431, 67456, 67461, 67463, 67504, 67506, 67514, 67584, 67589, 67592, 67592, 67594, 67637, 67639, 67640, 67644, 67644, 67647, 67669, 67680, 67702, 67712, 67742, 67808, 67826, 67828, 67829, 67840, 67861, 67872, 67897, 67968, 68023, 68030, 68031, 68096, 68096, 68112, 68115, 68117, 68119, 68121, 68149, 68192, 68220, 68224, 68252, 68288, 68295, 68297, 68324, 68352, 68405, 68416, 68437, 68448, 68466, 68480, 68497, 68608, 68680, 68736, 68786, 68800, 68850, 68864, 68899, 69248, 69289, 69296, 69297, 69376, 69404, 69415, 69415, 69424, 69445, 69488, 69505, 69552, 69572, 69600, 69622, 69635, 69687, 69745, 69746, 69749, 69749, 69763, 69807, 69840, 69864, 69891, 69926, 69956, 69956, 69959, 69959, 69968, 70002, 70006, 70006, 70019, 70066, 70081, 70084, 70106, 70106, 70108, 70108, 70144, 70161, 70163, 70187, 70207, 70208, 70272, 70278, 70280, 70280, 70282, 70285, 70287, 70301, 70303, 70312, 70320, 70366, 70405, 70412, 70415, 70416, 70419, 70440, 70442, 70448, 70450, 70451, 70453, 70457, 70461, 70461, 70480, 70480, 70493, 70497, 70656, 70708, 70727, 70730, 70751, 70753, 70784, 70831, 70852, 70853, 70855, 70855, 71040, 71086, 71128, 71131, 71168, 71215, 71236, 71236, 71296, 71338, 71352, 71352, 71424, 71450, 71488, 71494, 71680, 71723, 71840, 71903, 71935, 71942, 71945, 71945, 71948, 71955, 71957, 71958, 71960, 71983, 71999, 71999, 72001, 72001, 72096, 72103, 72106, 72144, 72161, 72161, 72163, 72163, 72192, 72192, 72203, 72242, 72250, 72250, 72272, 72272, 72284, 72329, 72349, 72349, 72368, 72440, 72704, 72712, 72714, 72750, 72768, 72768, 72818, 72847, 72960, 72966, 72968, 72969, 72971, 73008, 73030, 73030, 73056, 73061, 73063, 73064, 73066, 73097, 73112, 73112, 73440, 73458, 73474, 73474, 73476, 73488, 73490, 73523, 73648, 73648, 73728, 74649, 74752, 74862, 74880, 75075, 77712, 77808, 77824, 78895, 78913, 78918, 82944, 83526, 92160, 92728, 92736, 92766, 92784, 92862, 92880, 92909, 92928, 92975, 92992, 92995, 93027, 93047, 93053, 93071, 93760, 93823, 93952, 94026, 94032, 94032, 94099, 94111, 94176, 94177, 94179, 94179, 94208, 100343, 100352, 101589, 101632, 101640, 110576, 110579, 110581, 110587, 110589, 110590, 110592, 110882, 110898, 110898, 110928, 110930, 110933, 110933, 110948, 110951, 110960, 111355, 113664, 113770, 113776, 113788, 113792, 113800, 113808, 113817, 119808, 119892, 119894, 119964, 119966, 119967, 119970, 119970, 119973, 119974, 119977, 119980, 119982, 119993, 119995, 119995, 119997, 120003, 120005, 120069, 120071, 120074, 120077, 120084, 120086, 120092, 120094, 120121, 120123, 120126, 120128, 120132, 120134, 120134, 120138, 120144, 120146, 120485, 120488, 120512, 120514, 120538, 120540, 120570, 120572, 120596, 120598, 120628, 120630, 120654, 120656, 120686, 120688, 120712, 120714, 120744, 120746, 120770, 120772, 120779, 122624, 122654, 122661, 122666, 122928, 122989, 123136, 123180, 123191, 123197, 123214, 123214, 123536, 123565, 123584, 123627, 124112, 124139, 124896, 124902, 124904, 124907, 124909, 124910, 124912, 124926, 124928, 125124, 125184, 125251, 125259, 125259, 126464, 126467, 126469, 126495, 126497, 126498, 126500, 126500, 126503, 126503, 126505, 126514, 126516, 126519, 126521, 126521, 126523, 126523, 126530, 126530, 126535, 126535, 126537, 126537, 126539, 126539, 126541, 126543, 126545, 126546, 126548, 126548, 126551, 126551, 126553, 126553, 126555, 126555, 126557, 126557, 126559, 126559, 126561, 126562, 126564, 126564, 126567, 126570, 126572, 126578, 126580, 126583, 126585, 126588, 126590, 126590, 126592, 126601, 126603, 126619, 126625, 126627, 126629, 126633, 126635, 126651, 131072, 173791, 173824, 177977, 177984, 178205, 178208, 183969, 183984, 191456, 191472, 192093, 194560, 195101, 196608, 201546, 201552, 205743} +var unicodeESNextIdentifierPart = []rune{48, 57, 65, 90, 95, 95, 97, 122, 170, 170, 181, 181, 183, 183, 186, 186, 192, 214, 216, 246, 248, 705, 710, 721, 736, 740, 748, 748, 750, 750, 768, 884, 886, 887, 890, 893, 895, 895, 902, 906, 908, 908, 910, 929, 931, 1013, 1015, 1153, 1155, 1159, 1162, 1327, 1329, 1366, 1369, 1369, 1376, 1416, 1425, 1469, 1471, 1471, 1473, 1474, 1476, 1477, 1479, 1479, 1488, 1514, 1519, 1522, 1552, 1562, 1568, 1641, 1646, 1747, 1749, 1756, 1759, 1768, 1770, 1788, 1791, 1791, 1808, 1866, 1869, 1969, 1984, 2037, 2042, 2042, 2045, 2045, 2048, 2093, 2112, 2139, 2144, 2154, 2160, 2183, 2185, 2190, 2200, 2273, 2275, 2403, 2406, 2415, 2417, 2435, 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, 2486, 2489, 2492, 2500, 2503, 2504, 2507, 2510, 2519, 2519, 2524, 2525, 2527, 2531, 2534, 2545, 2556, 2556, 2558, 2558, 2561, 2563, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611, 2613, 2614, 2616, 2617, 2620, 2620, 2622, 2626, 2631, 2632, 2635, 2637, 2641, 2641, 2649, 2652, 2654, 2654, 2662, 2677, 2689, 2691, 2693, 2701, 2703, 2705, 2707, 2728, 2730, 2736, 2738, 2739, 2741, 2745, 2748, 2757, 2759, 2761, 2763, 2765, 2768, 2768, 2784, 2787, 2790, 2799, 2809, 2815, 2817, 2819, 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, 2869, 2873, 2876, 2884, 2887, 2888, 2891, 2893, 2901, 2903, 2908, 2909, 2911, 2915, 2918, 2927, 2929, 2929, 2946, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 3001, 3006, 3010, 3014, 3016, 3018, 3021, 3024, 3024, 3031, 3031, 3046, 3055, 3072, 3084, 3086, 3088, 3090, 3112, 3114, 3129, 3132, 3140, 3142, 3144, 3146, 3149, 3157, 3158, 3160, 3162, 3165, 3165, 3168, 3171, 3174, 3183, 3200, 3203, 3205, 3212, 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3260, 3268, 3270, 3272, 3274, 3277, 3285, 3286, 3293, 3294, 3296, 3299, 3302, 3311, 3313, 3315, 3328, 3340, 3342, 3344, 3346, 3396, 3398, 3400, 3402, 3406, 3412, 3415, 3423, 3427, 3430, 3439, 3450, 3455, 3457, 3459, 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, 3530, 3530, 3535, 3540, 3542, 3542, 3544, 3551, 3558, 3567, 3570, 3571, 3585, 3642, 3648, 3662, 3664, 3673, 3713, 3714, 3716, 3716, 3718, 3722, 3724, 3747, 3749, 3749, 3751, 3773, 3776, 3780, 3782, 3782, 3784, 3790, 3792, 3801, 3804, 3807, 3840, 3840, 3864, 3865, 3872, 3881, 3893, 3893, 3895, 3895, 3897, 3897, 3902, 3911, 3913, 3948, 3953, 3972, 3974, 3991, 3993, 4028, 4038, 4038, 4096, 4169, 4176, 4253, 4256, 4293, 4295, 4295, 4301, 4301, 4304, 4346, 4348, 4680, 4682, 4685, 4688, 4694, 4696, 4696, 4698, 4701, 4704, 4744, 4746, 4749, 4752, 4784, 4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4822, 4824, 4880, 4882, 4885, 4888, 4954, 4957, 4959, 4969, 4977, 4992, 5007, 5024, 5109, 5112, 5117, 5121, 5740, 5743, 5759, 5761, 5786, 5792, 5866, 5870, 5880, 5888, 5909, 5919, 5940, 5952, 5971, 5984, 5996, 5998, 6000, 6002, 6003, 6016, 6099, 6103, 6103, 6108, 6109, 6112, 6121, 6155, 6157, 6159, 6169, 6176, 6264, 6272, 6314, 6320, 6389, 6400, 6430, 6432, 6443, 6448, 6459, 6470, 6509, 6512, 6516, 6528, 6571, 6576, 6601, 6608, 6618, 6656, 6683, 6688, 6750, 6752, 6780, 6783, 6793, 6800, 6809, 6823, 6823, 6832, 6845, 6847, 6862, 6912, 6988, 6992, 7001, 7019, 7027, 7040, 7155, 7168, 7223, 7232, 7241, 7245, 7293, 7296, 7304, 7312, 7354, 7357, 7359, 7376, 7378, 7380, 7418, 7424, 7957, 7960, 7965, 7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188, 8204, 8205, 8255, 8256, 8276, 8276, 8305, 8305, 8319, 8319, 8336, 8348, 8400, 8412, 8417, 8417, 8421, 8432, 8450, 8450, 8455, 8455, 8458, 8467, 8469, 8469, 8472, 8477, 8484, 8484, 8486, 8486, 8488, 8488, 8490, 8505, 8508, 8511, 8517, 8521, 8526, 8526, 8544, 8584, 11264, 11492, 11499, 11507, 11520, 11557, 11559, 11559, 11565, 11565, 11568, 11623, 11631, 11631, 11647, 11670, 11680, 11686, 11688, 11694, 11696, 11702, 11704, 11710, 11712, 11718, 11720, 11726, 11728, 11734, 11736, 11742, 11744, 11775, 12293, 12295, 12321, 12335, 12337, 12341, 12344, 12348, 12353, 12438, 12441, 12447, 12449, 12543, 12549, 12591, 12593, 12686, 12704, 12735, 12784, 12799, 13312, 19903, 19968, 42124, 42192, 42237, 42240, 42508, 42512, 42539, 42560, 42607, 42612, 42621, 42623, 42737, 42775, 42783, 42786, 42888, 42891, 42954, 42960, 42961, 42963, 42963, 42965, 42969, 42994, 43047, 43052, 43052, 43072, 43123, 43136, 43205, 43216, 43225, 43232, 43255, 43259, 43259, 43261, 43309, 43312, 43347, 43360, 43388, 43392, 43456, 43471, 43481, 43488, 43518, 43520, 43574, 43584, 43597, 43600, 43609, 43616, 43638, 43642, 43714, 43739, 43741, 43744, 43759, 43762, 43766, 43777, 43782, 43785, 43790, 43793, 43798, 43808, 43814, 43816, 43822, 43824, 43866, 43868, 43881, 43888, 44010, 44012, 44013, 44016, 44025, 44032, 55203, 55216, 55238, 55243, 55291, 63744, 64109, 64112, 64217, 64256, 64262, 64275, 64279, 64285, 64296, 64298, 64310, 64312, 64316, 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, 65024, 65039, 65056, 65071, 65075, 65076, 65101, 65103, 65136, 65140, 65142, 65276, 65296, 65305, 65313, 65338, 65343, 65343, 65345, 65370, 65381, 65470, 65474, 65479, 65482, 65487, 65490, 65495, 65498, 65500, 65536, 65547, 65549, 65574, 65576, 65594, 65596, 65597, 65599, 65613, 65616, 65629, 65664, 65786, 65856, 65908, 66045, 66045, 66176, 66204, 66208, 66256, 66272, 66272, 66304, 66335, 66349, 66378, 66384, 66426, 66432, 66461, 66464, 66499, 66504, 66511, 66513, 66517, 66560, 66717, 66720, 66729, 66736, 66771, 66776, 66811, 66816, 66855, 66864, 66915, 66928, 66938, 66940, 66954, 66956, 66962, 66964, 66965, 66967, 66977, 66979, 66993, 66995, 67001, 67003, 67004, 67072, 67382, 67392, 67413, 67424, 67431, 67456, 67461, 67463, 67504, 67506, 67514, 67584, 67589, 67592, 67592, 67594, 67637, 67639, 67640, 67644, 67644, 67647, 67669, 67680, 67702, 67712, 67742, 67808, 67826, 67828, 67829, 67840, 67861, 67872, 67897, 67968, 68023, 68030, 68031, 68096, 68099, 68101, 68102, 68108, 68115, 68117, 68119, 68121, 68149, 68152, 68154, 68159, 68159, 68192, 68220, 68224, 68252, 68288, 68295, 68297, 68326, 68352, 68405, 68416, 68437, 68448, 68466, 68480, 68497, 68608, 68680, 68736, 68786, 68800, 68850, 68864, 68903, 68912, 68921, 69248, 69289, 69291, 69292, 69296, 69297, 69373, 69404, 69415, 69415, 69424, 69456, 69488, 69509, 69552, 69572, 69600, 69622, 69632, 69702, 69734, 69749, 69759, 69818, 69826, 69826, 69840, 69864, 69872, 69881, 69888, 69940, 69942, 69951, 69956, 69959, 69968, 70003, 70006, 70006, 70016, 70084, 70089, 70092, 70094, 70106, 70108, 70108, 70144, 70161, 70163, 70199, 70206, 70209, 70272, 70278, 70280, 70280, 70282, 70285, 70287, 70301, 70303, 70312, 70320, 70378, 70384, 70393, 70400, 70403, 70405, 70412, 70415, 70416, 70419, 70440, 70442, 70448, 70450, 70451, 70453, 70457, 70459, 70468, 70471, 70472, 70475, 70477, 70480, 70480, 70487, 70487, 70493, 70499, 70502, 70508, 70512, 70516, 70656, 70730, 70736, 70745, 70750, 70753, 70784, 70853, 70855, 70855, 70864, 70873, 71040, 71093, 71096, 71104, 71128, 71133, 71168, 71232, 71236, 71236, 71248, 71257, 71296, 71352, 71360, 71369, 71424, 71450, 71453, 71467, 71472, 71481, 71488, 71494, 71680, 71738, 71840, 71913, 71935, 71942, 71945, 71945, 71948, 71955, 71957, 71958, 71960, 71989, 71991, 71992, 71995, 72003, 72016, 72025, 72096, 72103, 72106, 72151, 72154, 72161, 72163, 72164, 72192, 72254, 72263, 72263, 72272, 72345, 72349, 72349, 72368, 72440, 72704, 72712, 72714, 72758, 72760, 72768, 72784, 72793, 72818, 72847, 72850, 72871, 72873, 72886, 72960, 72966, 72968, 72969, 72971, 73014, 73018, 73018, 73020, 73021, 73023, 73031, 73040, 73049, 73056, 73061, 73063, 73064, 73066, 73102, 73104, 73105, 73107, 73112, 73120, 73129, 73440, 73462, 73472, 73488, 73490, 73530, 73534, 73538, 73552, 73561, 73648, 73648, 73728, 74649, 74752, 74862, 74880, 75075, 77712, 77808, 77824, 78895, 78912, 78933, 82944, 83526, 92160, 92728, 92736, 92766, 92768, 92777, 92784, 92862, 92864, 92873, 92880, 92909, 92912, 92916, 92928, 92982, 92992, 92995, 93008, 93017, 93027, 93047, 93053, 93071, 93760, 93823, 93952, 94026, 94031, 94087, 94095, 94111, 94176, 94177, 94179, 94180, 94192, 94193, 94208, 100343, 100352, 101589, 101632, 101640, 110576, 110579, 110581, 110587, 110589, 110590, 110592, 110882, 110898, 110898, 110928, 110930, 110933, 110933, 110948, 110951, 110960, 111355, 113664, 113770, 113776, 113788, 113792, 113800, 113808, 113817, 113821, 113822, 118528, 118573, 118576, 118598, 119141, 119145, 119149, 119154, 119163, 119170, 119173, 119179, 119210, 119213, 119362, 119364, 119808, 119892, 119894, 119964, 119966, 119967, 119970, 119970, 119973, 119974, 119977, 119980, 119982, 119993, 119995, 119995, 119997, 120003, 120005, 120069, 120071, 120074, 120077, 120084, 120086, 120092, 120094, 120121, 120123, 120126, 120128, 120132, 120134, 120134, 120138, 120144, 120146, 120485, 120488, 120512, 120514, 120538, 120540, 120570, 120572, 120596, 120598, 120628, 120630, 120654, 120656, 120686, 120688, 120712, 120714, 120744, 120746, 120770, 120772, 120779, 120782, 120831, 121344, 121398, 121403, 121452, 121461, 121461, 121476, 121476, 121499, 121503, 121505, 121519, 122624, 122654, 122661, 122666, 122880, 122886, 122888, 122904, 122907, 122913, 122915, 122916, 122918, 122922, 122928, 122989, 123023, 123023, 123136, 123180, 123184, 123197, 123200, 123209, 123214, 123214, 123536, 123566, 123584, 123641, 124112, 124153, 124896, 124902, 124904, 124907, 124909, 124910, 124912, 124926, 124928, 125124, 125136, 125142, 125184, 125259, 125264, 125273, 126464, 126467, 126469, 126495, 126497, 126498, 126500, 126500, 126503, 126503, 126505, 126514, 126516, 126519, 126521, 126521, 126523, 126523, 126530, 126530, 126535, 126535, 126537, 126537, 126539, 126539, 126541, 126543, 126545, 126546, 126548, 126548, 126551, 126551, 126553, 126553, 126555, 126555, 126557, 126557, 126559, 126559, 126561, 126562, 126564, 126564, 126567, 126570, 126572, 126578, 126580, 126583, 126585, 126588, 126590, 126590, 126592, 126601, 126603, 126619, 126625, 126627, 126629, 126633, 126635, 126651, 130032, 130041, 131072, 173791, 173824, 177977, 177984, 178205, 178208, 183969, 183984, 191456, 191472, 192093, 194560, 195101, 196608, 201546, 201552, 205743, 917760, 917999} + +type ScannerState struct { + pos int // Current position in text (and ending position of current token) + fullStartPos int // Starting position of current token including preceding whitespace + tokenStart int // Starting position of non-whitespace part of current token + token SyntaxKind // SyntaxKind of current token + tokenValue string // Parsed value of current token + tokenFlags TokenFlags // Flags for current token +} + +type Scanner struct { + text string + languageVersion ScriptTarget + languageVariant LanguageVariant + onError ErrorCallback + ScannerState +} + +func NewScanner() *Scanner { + return &Scanner{languageVersion: ScriptTargetLatest} +} + +func (s *Scanner) Text() string { + return s.text +} + +func (s *Scanner) Token() SyntaxKind { + return s.token +} + +func (s *Scanner) TokenFullStart() int { + return s.fullStartPos +} + +func (s *Scanner) TokenStart() int { + return s.tokenStart +} + +func (s *Scanner) TokenEnd() int { + return s.pos +} + +func (s *Scanner) TokenText() string { + return s.text[s.tokenStart:s.pos] +} + +func (s *Scanner) TokenValue() string { + return s.tokenValue +} + +func (s *Scanner) TokenRange() TextRange { + return NewTextRange(s.tokenStart, s.pos) +} + +func (s *Scanner) Mark() ScannerState { + return s.ScannerState +} + +func (s *Scanner) Rewind(state ScannerState) { + s.ScannerState = state +} + +func (s *Scanner) HasUnicodeEscape() bool { + return s.tokenFlags&TokenFlagsUnicodeEscape != 0 +} + +func (s *Scanner) HasExtendedUnicodeEscape() bool { + return s.tokenFlags&TokenFlagsExtendedUnicodeEscape != 0 +} + +func (s *Scanner) HasPrecedingLineBreak() bool { + return s.tokenFlags&TokenFlagsPrecedingLineBreak != 0 +} + +func (s *Scanner) SetText(text string) { + s.text = text + s.ScannerState = ScannerState{} +} + +func (s *Scanner) SetOnError(errorCallback ErrorCallback) { + s.onError = errorCallback +} + +func (s *Scanner) SetScriptTarget(scriptTarget ScriptTarget) { + s.languageVersion = scriptTarget +} + +func (s *Scanner) SetLanguageVariant(languageVariant LanguageVariant) { + s.languageVariant = languageVariant +} + +func (s *Scanner) error(diagnostic *diagnostics.Message) { + s.errorAt(diagnostic, s.pos, 0) +} + +func (s *Scanner) errorAt(diagnostic *diagnostics.Message, pos int, len int, args ...any) { + if s.onError != nil { + s.onError(diagnostic, pos, len, args...) + } +} + +func (s *Scanner) char() rune { + if s.pos < len(s.text) { + return rune(s.text[s.pos]) + } + return -1 +} + +func (s *Scanner) charAt(offset int) rune { + if s.pos+offset < len(s.text) { + return rune(s.text[s.pos+offset]) + } + return -1 +} + +func (s *Scanner) charAndSize() (rune, int) { + return utf8.DecodeRuneInString(s.text[s.pos:]) +} + +func (s *Scanner) shouldParseJSDoc() bool { + return false +} + +func (s *Scanner) Scan() SyntaxKind { + s.fullStartPos = s.pos + s.tokenFlags = TokenFlagsNone + for { + s.tokenStart = s.pos + ch := s.char() + switch ch { + case '\t', '\v', '\f', ' ': + s.pos++ + continue + case '\n', '\r': + s.tokenFlags |= TokenFlagsPrecedingLineBreak + s.pos++ + continue + case '!': + if s.charAt(1) == '=' { + if s.charAt(2) == '=' { + s.pos += 3 + s.token = SyntaxKindExclamationEqualsEqualsToken + } else { + s.pos += 2 + s.token = SyntaxKindExclamationEqualsToken + } + } else { + s.pos++ + s.token = SyntaxKindExclamationToken + } + case '"', '\'': + s.tokenValue = s.scanString(false /*jsxAttributeString*/) + s.token = SyntaxKindStringLiteral + case '`': + s.token = s.scanTemplateAndSetTokenValue(false /*shouldEmitInvalidEscapeError*/) + case '%': + if s.charAt(1) == '=' { + s.pos += 2 + s.token = SyntaxKindPercentEqualsToken + } else { + s.pos++ + s.token = SyntaxKindPercentToken + } + case '&': + if s.charAt(1) == '&' { + if s.charAt(2) == '=' { + s.pos += 3 + s.token = SyntaxKindAmpersandAmpersandEqualsToken + } else { + s.pos += 2 + s.token = SyntaxKindAmpersandAmpersandToken + } + } else if s.charAt(1) == '=' { + s.pos += 2 + s.token = SyntaxKindAmpersandEqualsToken + } else { + s.pos++ + s.token = SyntaxKindAmpersandToken + } + case '(': + s.pos++ + s.token = SyntaxKindOpenParenToken + case ')': + s.pos++ + s.token = SyntaxKindCloseParenToken + case '*': + if s.charAt(1) == '=' { + s.pos += 2 + s.token = SyntaxKindAsteriskEqualsToken + } else if s.charAt(1) == '*' { + s.pos += 2 + s.token = SyntaxKindAsteriskAsteriskToken + } else { + s.pos++ + s.token = SyntaxKindAsteriskToken + } + case '+': + if s.charAt(1) == '=' { + s.pos += 2 + s.token = SyntaxKindPlusEqualsToken + } else if s.charAt(1) == '+' { + s.pos += 2 + s.token = SyntaxKindPlusPlusToken + } else { + s.pos++ + s.token = SyntaxKindPlusToken + } + case ',': + s.pos++ + s.token = SyntaxKindCommaToken + case '-': + if s.charAt(1) == '=' { + s.pos += 2 + s.token = SyntaxKindMinusEqualsToken + } else if s.charAt(1) == '-' { + s.pos += 2 + s.token = SyntaxKindMinusMinusToken + } else { + s.pos++ + s.token = SyntaxKindMinusToken + } + case '.': + if isDigit(s.charAt(1)) { + s.token = s.scanNumber() + } else if s.charAt(1) == '.' && s.charAt(2) == '.' { + s.pos += 3 + s.token = SyntaxKindDotDotDotToken + } else { + s.pos++ + s.token = SyntaxKindDotToken + } + case '/': + if s.charAt(1) == '/' { + s.pos += 2 + for { + ch := s.char() + if ch <= 0x7F { + if ch < 0 || ch == '\r' || ch == '\n' { + break + } + s.pos++ + } else { + ch, size := s.charAndSize() + if isLineBreak(ch) { + break + } + s.pos += size + } + } + // commentDirectives = appendIfCommentDirective(commentDirectives, text.slice(tokenStart, pos), commentDirectiveRegExSingleLine, tokenStart); + continue + } + if s.charAt(1) == '*' { + s.pos += 2 + isJSDoc := s.char() == '*' && s.charAt(1) != '/' + for { + ch = s.char() + if ch <= 0x7F { + if ch < 0 { + s.error(diagnostics.Asterisk_Slash_expected) + break + } + if ch == '*' && s.charAt(1) == '/' { + s.pos += 2 + break + } + if ch == '\r' || ch == '\n' { + s.tokenFlags |= TokenFlagsPrecedingLineBreak + } + s.pos++ + } else { + ch, size := s.charAndSize() + if isLineBreak(ch) { + break + } + s.pos += size + } + } + if isJSDoc && s.shouldParseJSDoc() { + s.tokenFlags |= TokenFlagsPrecedingJSDocComment + } + // commentDirectives = appendIfCommentDirective(commentDirectives, text.slice(lastLineStart, pos), commentDirectiveRegExMultiLine, lastLineStart); + continue + } + if s.charAt(1) == '=' { + s.pos += 2 + s.token = SyntaxKindSlashEqualsToken + } else { + s.pos++ + s.token = SyntaxKindSlashToken + } + case '0': + if s.charAt(1) == 'X' || s.charAt(1) == 'x' { + s.pos += 2 + digits := s.scanHexDigits(1, true, true) + if digits == "" { + s.error(diagnostics.Hexadecimal_digit_expected) + digits = "0" + } + s.tokenValue = "0x" + digits + s.tokenFlags |= TokenFlagsHexSpecifier + s.token = s.scanBigIntSuffix() + break + } + if s.charAt(1) == 'B' || s.charAt(1) == 'b' { + s.pos += 2 + digits := s.scanBinaryOrOctalDigits(2) + if digits == "" { + s.error(diagnostics.Binary_digit_expected) + digits = "0" + } + s.tokenValue = "0b" + digits + s.tokenFlags |= TokenFlagsBinarySpecifier + s.token = s.scanBigIntSuffix() + break + } + if s.charAt(1) == 'O' || s.charAt(1) == 'o' { + s.pos += 2 + digits := s.scanBinaryOrOctalDigits(8) + if digits == "" { + s.error(diagnostics.Octal_digit_expected) + digits = "0" + } + s.tokenValue = "0o" + digits + s.tokenFlags |= TokenFlagsOctalSpecifier + s.token = s.scanBigIntSuffix() + break + } + fallthrough + case '1', '2', '3', '4', '5', '6', '7', '8', '9': + s.token = s.scanNumber() + case ':': + s.pos++ + s.token = SyntaxKindColonToken + case ';': + s.pos++ + s.token = SyntaxKindSemicolonToken + case '<': + // Handle conflict marker trivia !!! + if s.charAt(1) == '<' { + if s.charAt(2) == '=' { + s.pos += 3 + s.token = SyntaxKindLessThanLessThanEqualsToken + } else { + s.pos += 2 + s.token = SyntaxKindLessThanLessThanToken + } + } else if s.charAt(1) == '=' { + s.pos += 2 + s.token = SyntaxKindLessThanEqualsToken + } else if s.languageVariant == LanguageVariantJSX && s.charAt(1) == '/' && s.charAt(2) != '*' { + s.pos += 2 + s.token = SyntaxKindLessThanSlashToken + } else { + s.pos++ + s.token = SyntaxKindLessThanToken + } + case '=': + // Handle conflict marker trivia !!! + if s.charAt(1) == '=' { + if s.charAt(2) == '=' { + s.pos += 3 + s.token = SyntaxKindEqualsEqualsEqualsToken + } else { + s.pos += 2 + s.token = SyntaxKindEqualsEqualsToken + } + } else if s.charAt(1) == '>' { + s.pos += 2 + s.token = SyntaxKindEqualsGreaterThanToken + } else { + s.pos++ + s.token = SyntaxKindEqualsToken + } + case '>': + // Handle conflict marker trivia !!! + s.pos++ + s.token = SyntaxKindGreaterThanToken + case '?': + if s.charAt(1) == '.' && !isDigit(s.charAt(2)) { + s.pos += 2 + s.token = SyntaxKindQuestionDotToken + } else if s.charAt(1) == '?' { + if s.charAt(2) == '=' { + s.pos += 3 + s.token = SyntaxKindQuestionQuestionEqualsToken + } else { + s.pos += 2 + s.token = SyntaxKindQuestionQuestionToken + } + } else { + s.pos++ + s.token = SyntaxKindQuestionToken + } + case '[': + s.pos++ + s.token = SyntaxKindOpenBracketToken + case ']': + s.pos++ + s.token = SyntaxKindCloseBracketToken + case '^': + if s.charAt(1) == '=' { + s.pos += 2 + s.token = SyntaxKindCaretEqualsToken + } else { + s.pos++ + s.token = SyntaxKindCaretToken + } + case '{': + s.pos++ + s.token = SyntaxKindOpenBraceToken + case '|': + // Handle conflict marker trivia !!! + if s.charAt(1) == '|' { + if s.charAt(2) == '=' { + s.pos += 3 + s.token = SyntaxKindBarBarEqualsToken + } else { + s.pos += 2 + s.token = SyntaxKindBarBarToken + } + } else if s.charAt(1) == '=' { + s.pos += 2 + s.token = SyntaxKindBarEqualsToken + } else { + s.pos++ + s.token = SyntaxKindBarToken + } + case '}': + s.pos++ + s.token = SyntaxKindCloseBraceToken + case '~': + s.pos++ + s.token = SyntaxKindTildeToken + case '@': + s.pos++ + s.token = SyntaxKindAtToken + case '\\': + cp := s.peekUnicodeEscape() + if cp >= 0 && isIdentifierStart(cp, s.languageVersion) { + s.tokenValue = string(s.scanUnicodeEscape(true)) + s.scanIdentifierParts() + s.token = getIdentifierToken(s.tokenValue) + } else { + s.scanInvalidCharacter() + } + case '#': + if s.charAt(1) == '!' { + if s.pos == 0 { + s.pos += 2 + for s.char() >= 0 && s.char() != '\n' { + s.pos++ + } + continue + } + s.errorAt(diagnostics.X_can_only_be_used_at_the_start_of_a_file, s.pos, 2) + s.pos += 2 + s.token = SyntaxKindUnknown + break + } + if s.charAt(1) == '\\' { + s.pos++ + cp := s.peekUnicodeEscape() + if cp >= 0 && isIdentifierStart(cp, s.languageVersion) { + s.tokenValue = "#" + string(s.scanUnicodeEscape(true)) + s.scanIdentifierParts() + s.token = SyntaxKindPrivateIdentifier + break + } + s.pos-- + } + if s.scanIdentifier(1) { + s.token = SyntaxKindPrivateIdentifier + } else { + s.errorAt(diagnostics.Invalid_character, s.pos-1, 1) + s.token = SyntaxKindUnknown + } + default: + if ch < 0 { + s.token = SyntaxKindEndOfFile + break + } + if s.scanIdentifier(0) { + s.token = getIdentifierToken(s.tokenValue) + break + } + ch, size := s.charAndSize() + if ch == utf8.RuneError && size == 1 { + s.errorAt(diagnostics.File_appears_to_be_binary, 0, 0) + s.pos = len(s.text) + s.token = SyntaxKindNonTextFileMarkerTrivia + break + } + if isWhiteSpaceSingleLine(ch) { + s.pos += size + continue + } + if isLineBreak(ch) { + s.tokenFlags |= TokenFlagsPrecedingLineBreak + s.pos += size + continue + } + s.scanInvalidCharacter() + } + return s.token + } +} + +func (s *Scanner) ReScanLessThanToken() SyntaxKind { + if s.token == SyntaxKindLessThanLessThanToken { + s.pos = s.tokenStart + 1 + s.token = SyntaxKindLessThanToken + } + return s.token +} + +func (s *Scanner) ReScanGreaterThanToken() SyntaxKind { + if s.token == SyntaxKindGreaterThanToken { + s.pos = s.tokenStart + 1 + if s.char() == '>' { + if s.charAt(1) == '>' { + if s.charAt(2) == '=' { + s.pos += 3 + s.token = SyntaxKindGreaterThanGreaterThanGreaterThanEqualsToken + } else { + s.pos += 2 + s.token = SyntaxKindGreaterThanGreaterThanGreaterThanToken + } + } else if s.charAt(1) == '=' { + s.pos += 2 + s.token = SyntaxKindGreaterThanGreaterThanEqualsToken + } else { + s.pos++ + s.token = SyntaxKindGreaterThanGreaterThanToken + } + } else if s.char() == '=' { + s.pos++ + s.token = SyntaxKindGreaterThanEqualsToken + } + } + return s.token +} + +func (s *Scanner) ReScanTemplateToken(isTaggedTemplate bool) SyntaxKind { + s.pos = s.tokenStart + s.token = s.scanTemplateAndSetTokenValue(!isTaggedTemplate) + return s.token +} + +// !!! https://github.com/microsoft/TypeScript/pull/55600 +func (s *Scanner) ReScanSlashToken() SyntaxKind { + if s.token == SyntaxKindSlashToken || s.token == SyntaxKindSlashEqualsToken { + s.pos = s.tokenStart + 1 + inEscape := false + inCharacterClass := false + loop: + for { + ch, size := s.charAndSize() + // If we reach the end of a file, or hit a newline, then this is an unterminated + // regex. Report error and return what we have so far. + switch { + case size == 0 || isLineBreak(ch): + s.tokenFlags |= TokenFlagsUnterminated + s.error(diagnostics.Unterminated_regular_expression_literal) + break loop + case inEscape: + // Parsing an escape character; + // reset the flag and just advance to the next char. + inEscape = false + case ch == '/' && !inCharacterClass: + // A slash within a character class is permissible, + // but in general it signals the end of the regexp literal. + s.pos++ + break loop + case ch == '[': + inCharacterClass = true + case ch == '\\': + inEscape = true + case ch == ']': + inCharacterClass = false + } + s.pos += size + } + for { + ch, size := s.charAndSize() + if size == 0 || !isIdentifierPart(ch, s.languageVersion, s.languageVariant) { + break + } + s.pos += size + } + s.tokenValue = s.text[s.tokenStart:s.pos] + s.token = SyntaxKindRegularExpressionLiteral + } + return s.token +} + +func (s *Scanner) reScanJsxToken(allowMultilineJsxText bool) SyntaxKind { + s.pos = s.fullStartPos + s.tokenStart = s.fullStartPos + s.token = s.scanJsxTokenEx(allowMultilineJsxText) + return s.token +} + +func (s *Scanner) scanJsxToken() SyntaxKind { + return s.scanJsxTokenEx(true /*allowMultilineJsxText*/) +} + +func (s *Scanner) scanJsxTokenEx(allowMultilineJsxText bool) SyntaxKind { + s.fullStartPos = s.pos + s.tokenStart = s.pos + ch := s.char() + switch { + case ch < 0: + s.token = SyntaxKindEndOfFile + case ch == '<': + if s.charAt(1) == '/' { + s.pos += 2 + s.token = SyntaxKindLessThanSlashToken + } else { + s.pos++ + s.token = SyntaxKindLessThanToken + } + case ch == '{': + s.pos++ + s.token = SyntaxKindOpenBraceToken + default: + // First non-whitespace character on this line. + firstNonWhitespace := 0 + // These initial values are special because the first line is: + // firstNonWhitespace = 0 to indicate that we want leading whitespace + for { + ch, size := s.charAndSize() + if size == 0 || ch == '{' { + break + } + if ch == '<' { + // !!! + // if (isConflictMarkerTrivia(text, pos)) { + // pos = scanConflictMarkerTrivia(text, pos, error); + // return token = SyntaxKind.ConflictMarkerTrivia; + // } + break + } + if ch == '>' { + s.errorAt(diagnostics.Unexpected_token_Did_you_mean_or_gt, s.pos, 1) + } else if ch == '}' { + s.errorAt(diagnostics.Unexpected_token_Did_you_mean_or_rbrace, s.pos, 1) + } + // FirstNonWhitespace is 0, then we only see whitespaces so far. If we see a linebreak, we want to ignore that whitespaces. + // i.e (- : whitespace) + //
---- + //
becomes
+ // + //
----
becomes
----
+ if isLineBreak(ch) && firstNonWhitespace == 0 { + firstNonWhitespace = -1 + } else if !allowMultilineJsxText && isLineBreak(ch) && firstNonWhitespace > 0 { + // Stop JsxText on each line during formatting. This allows the formatter to + // indent each line correctly. + break + } else if !isWhiteSpaceLike(ch) { + firstNonWhitespace = s.pos + } + s.pos += size + } + s.tokenValue = s.text[s.fullStartPos:s.pos] + s.token = SyntaxKindJsxText + if firstNonWhitespace == -1 { + s.token = SyntaxKindJsxTextAllWhiteSpaces + } + } + return s.token +} + +// Scans a JSX identifier; these differ from normal identifiers in that they allow dashes +func (s *Scanner) scanJsxIdentifier() SyntaxKind { + if tokenIsIdentifierOrKeyword(s.token) { + // An identifier or keyword has already been parsed - check for a `-` or a single instance of `:` and then append it and + // everything after it to the token + // Do note that this means that `scanJsxIdentifier` effectively _mutates_ the visible token without advancing to a new token + // Any caller should be expecting this behavior and should only read the pos or token value after calling it. + for { + ch := s.char() + if ch < 0 { + break + } + if ch == '-' { + s.tokenValue += "-" + s.pos++ + continue + } + oldPos := s.pos + s.tokenValue += s.scanIdentifierParts() // reuse `scanIdentifierParts` so unicode escapes are handled + if s.pos == oldPos { + break + } + } + s.token = getIdentifierToken(s.tokenValue) + } + return s.token +} + +func (s *Scanner) scanJsxAttributeValue() SyntaxKind { + s.fullStartPos = s.pos + switch s.char() { + case '"', '\'': + s.tokenValue = s.scanString(true /*jsxAttributeString*/) + s.token = SyntaxKindStringLiteral + return s.token + default: + // If this scans anything other than `{`, it's a parse error. + return s.Scan() + } +} + +func (s *Scanner) reScanJsxAttributeValue() SyntaxKind { + s.pos = s.fullStartPos + s.tokenStart = s.fullStartPos + return s.scanJsxAttributeValue() +} + +func (s *Scanner) scanIdentifier(prefixLength int) bool { + start := s.pos + s.pos += prefixLength + ch := s.char() + // Fast path for simple ASCII identifiers + if isASCIILetter(ch) || ch == '_' || ch == '$' { + for { + s.pos++ + ch = s.char() + if !(isWordCharacter(ch) || ch == '$') { + break + } + } + if ch <= 0x7F || ch != '\\' { + s.tokenValue = s.text[start:s.pos] + return true + } + s.pos = start + } + ch, size := s.charAndSize() + if isIdentifierStart(ch, s.languageVersion) { + for { + s.pos += size + ch, size = s.charAndSize() + if !isIdentifierPart(ch, s.languageVersion, s.languageVariant) { + break + } + } + s.tokenValue = s.text[start:s.pos] + if ch == '\\' { + s.tokenValue += s.scanIdentifierParts() + } + return true + } + return false +} + +func (s *Scanner) scanIdentifierParts() string { + var sb strings.Builder + start := s.pos + for { + ch, size := s.charAndSize() + if isIdentifierPart(ch, s.languageVersion, s.languageVariant) { + s.pos += size + continue + } + if ch == '\\' { + escaped := s.peekUnicodeEscape() + if escaped >= 0 && isIdentifierPart(escaped, s.languageVersion, s.languageVariant) { + sb.WriteString(s.text[start:s.pos]) + sb.WriteString(string(s.scanUnicodeEscape(true))) + start = s.pos + continue + } + } + break + } + sb.WriteString(s.text[start:s.pos]) + return sb.String() +} + +func (s *Scanner) scanString(jsxAttributeString bool) string { + quote := s.char() + s.pos++ + var sb strings.Builder + start := s.pos + for { + ch := s.char() + if ch < 0 { + sb.WriteString(s.text[start:s.pos]) + s.tokenFlags |= TokenFlagsUnterminated + s.error(diagnostics.Unterminated_string_literal) + break + } + if ch == quote { + sb.WriteString(s.text[start:s.pos]) + s.pos++ + break + } + if ch == '\\' && !jsxAttributeString { + sb.WriteString(s.text[start:s.pos]) + sb.WriteString(s.scanEscapeSequence(EscapeSequenceScanningFlagsString | EscapeSequenceScanningFlagsReportErrors)) + start = s.pos + continue + } + if ch == '\n' || ch == '\r' && !jsxAttributeString { + sb.WriteString(s.text[start:s.pos]) + s.tokenFlags |= TokenFlagsUnterminated + s.error(diagnostics.Unterminated_string_literal) + break + } + s.pos++ + } + return sb.String() +} + +func (s *Scanner) scanTemplateAndSetTokenValue(shouldEmitInvalidEscapeError bool) SyntaxKind { + startedWithBacktick := s.char() == '`' + start := s.pos + s.pos++ + contents := "" + var token SyntaxKind + for { + ch := s.char() + if ch < 0 || ch == '`' { + contents += s.text[start:s.pos] + if ch == '`' { + s.pos++ + } else { + s.tokenFlags |= TokenFlagsUnterminated + s.error(diagnostics.Unterminated_template_literal) + } + token = ifElse(startedWithBacktick, SyntaxKindNoSubstitutionTemplateLiteral, SyntaxKindTemplateTail) + break + } + if ch == '$' && s.charAt(1) == '{' { + contents += s.text[start:s.pos] + s.pos += 2 + token = ifElse(startedWithBacktick, SyntaxKindTemplateHead, SyntaxKindTemplateMiddle) + break + } + if ch == '\\' { + contents += s.text[start:s.pos] + contents += s.scanEscapeSequence(EscapeSequenceScanningFlagsString | ifElse(shouldEmitInvalidEscapeError, EscapeSequenceScanningFlagsReportErrors, 0)) + start = s.pos + continue + } + // Speculated ECMAScript 6 Spec 11.8.6.1: + // and LineTerminatorSequences are normalized to for Template Values + if ch == '\r' { + contents += s.text[start:s.pos] + s.pos++ + if ch == '\n' { + s.pos++ + } + contents += "\n" + start = s.pos + continue + } + s.pos++ + } + s.tokenValue = contents + return token +} + +func (s *Scanner) scanEscapeSequence(flags EscapeSequenceScanningFlags) string { + start := s.pos + s.pos++ + ch := s.char() + if ch < 0 { + s.error(diagnostics.Unexpected_end_of_text) + return "" + } + s.pos++ + switch ch { + case '0': + // Although '0' preceding any digit is treated as LegacyOctalEscapeSequence, + // '\08' should separately be interpreted as '\0' + '8'. + if !isDigit(s.char()) { + return "\x00" + } + // '\01', '\011' + fallthrough + case '1', '2', '3': + // '\1', '\17', '\177' + if isOctalDigit(s.char()) { + s.pos++ + } + // '\17', '\177' + fallthrough + case '4', '5', '6', '7': + // '\4', '\47' but not '\477' + if isOctalDigit(s.char()) { + s.pos++ + } + // '\47' + s.tokenFlags |= TokenFlagsContainsInvalidEscape + if flags&EscapeSequenceScanningFlagsReportInvalidEscapeErrors != 0 { + code, _ := strconv.ParseInt(s.text[start+1:s.pos], 8, 32) + if flags&EscapeSequenceScanningFlagsRegularExpression != 0 && flags&EscapeSequenceScanningFlagsAtomEscape == 0 && ch != '0' { + s.errorAt(diagnostics.Octal_escape_sequences_and_backreferences_are_not_allowed_in_a_character_class_If_this_was_intended_as_an_escape_sequence_use_the_syntax_0_instead, start, s.pos-start, fmt.Sprintf("%02x", code)) + } else { + s.errorAt(diagnostics.Octal_escape_sequences_are_not_allowed_Use_the_syntax_0, start, s.pos-start, code) + } + return string(rune(code)) + } + return s.text[start:s.pos] + case '8', '9': + // the invalid '\8' and '\9' + s.tokenFlags |= TokenFlagsContainsInvalidEscape + if flags&EscapeSequenceScanningFlagsReportInvalidEscapeErrors != 0 { + if flags&EscapeSequenceScanningFlagsRegularExpression != 0 && flags&EscapeSequenceScanningFlagsAtomEscape == 0 { + s.errorAt(diagnostics.Decimal_escape_sequences_and_backreferences_are_not_allowed_in_a_character_class, start, s.pos-start) + } else { + s.errorAt(diagnostics.Escape_sequence_0_is_not_allowed, start, s.pos-start, s.text[start:s.pos]) + } + return string(rune(ch)) + } + return s.text[start:s.pos] + case 'b': + return "\b" + case 't': + return "\t" + case 'n': + return "\n" + case 'v': + return "\v" + case 'f': + return "\f" + case 'r': + return "\r" + case '\'': + return "'" + case '"': + return "\"" + case 'u': + // '\uDDDD' and '\U{DDDDDD}' + extended := s.char() == '{' + s.pos -= 2 + codePoint := s.scanUnicodeEscape(flags&EscapeSequenceScanningFlagsReportInvalidEscapeErrors != 0) + if codePoint < 0 { + s.tokenFlags |= TokenFlagsContainsInvalidEscape + return s.text[start:s.pos] + } + if extended { + s.tokenFlags |= TokenFlagsExtendedUnicodeEscape + } else { + s.tokenFlags |= TokenFlagsUnicodeEscape + } + return string(codePoint) + case 'x': + // '\xDD' + for ; s.pos < start+4; s.pos++ { + if !isHexDigit(s.char()) { + s.tokenFlags |= TokenFlagsContainsInvalidEscape + if flags&EscapeSequenceScanningFlagsReportInvalidEscapeErrors != 0 { + s.error(diagnostics.Hexadecimal_digit_expected) + } + return s.text[start:s.pos] + } + } + s.tokenFlags |= TokenFlagsHexEscape + escapedValue, _ := strconv.ParseInt(s.text[start+2:s.pos], 16, 32) + return string(rune(escapedValue)) + case '\r': + // when encountering a LineContinuation (i.e. a backslash and a line terminator sequence), + // the line terminator is interpreted to be "the empty code unit sequence". + if s.char() == '\n' { + s.pos++ + } + fallthrough + case '\n': + // case CharacterCodes.lineSeparator !!! + // case CharacterCodes.paragraphSeparator !!! + return "" + default: + if flags&EscapeSequenceScanningFlagsAnyUnicodeMode != 0 || flags&EscapeSequenceScanningFlagsRegularExpression != 0 && flags&EscapeSequenceScanningFlagsAnnexB == 0 && isIdentifierPart(ch, s.languageVersion, LanguageVariantStandard) { + s.errorAt(diagnostics.This_character_cannot_be_escaped_in_a_regular_expression, s.pos-2, 2) + } + return string(rune(ch)) + } +} + +// Known to be at \u +func (s *Scanner) scanUnicodeEscape(shouldEmitInvalidEscapeError bool) rune { + s.pos += 2 + start := s.pos + extended := s.char() == '{' + var hexDigits string + if extended { + s.pos++ + hexDigits = s.scanHexDigits(1, true, false) + } else { + hexDigits = s.scanHexDigits(4, false, false) + } + if hexDigits == "" { + if shouldEmitInvalidEscapeError { + s.error(diagnostics.Hexadecimal_digit_expected) + } + return -1 + } + hexValue, _ := strconv.ParseInt(hexDigits, 16, 32) + if extended { + if hexValue > 0x10FFFF { + if shouldEmitInvalidEscapeError { + s.errorAt(diagnostics.An_extended_Unicode_escape_value_must_be_between_0x0_and_0x10FFFF_inclusive, start+1, s.pos-start-1) + } + return -1 + } + if s.char() != '}' { + if shouldEmitInvalidEscapeError { + s.error(diagnostics.Unterminated_Unicode_escape_sequence) + } + return -1 + } + s.pos++ + } + return rune(hexValue) +} + +// Current character is known to be a backslash. Check for Unicode escape of the form '\uXXXX' +// or '\u{XXXXXX}' and return code point value if valid Unicode escape is found. Otherwise return -1. +func (s *Scanner) peekUnicodeEscape() rune { + if s.charAt(1) == 'u' { + start := s.pos + codePoint := s.scanUnicodeEscape(false) + s.pos = start + return codePoint + } + return -1 +} + +func (s *Scanner) scanNumber() SyntaxKind { + start := s.pos + var fixedPart string + if s.char() == '0' { + s.pos++ + if s.char() == '_' { + s.tokenFlags |= TokenFlagsContainsSeparator | TokenFlagsContainsInvalidSeparator + s.errorAt(diagnostics.Numeric_separators_are_not_allowed_here, s.pos, 1) + s.pos = start + fixedPart = s.scanNumberFragment() + } else { + digits, isOctal := s.scanDigits() + if digits == "" { + fixedPart = "0" + } else if !isOctal { + s.tokenFlags |= TokenFlagsContainsLeadingZero + fixedPart = digits + } else { + s.tokenFlags |= TokenFlagsOctal + s.tokenValue = "0o" + digits + s.errorAt(diagnostics.Octal_literals_are_not_allowed_Use_the_syntax_0, start, s.pos-start, digits) + return SyntaxKindNumericLiteral + } + } + } else { + fixedPart = s.scanNumberFragment() + } + fixedPartEnd := s.pos + fractionalPart := "" + exponentPreamble := "" + exponentPart := "" + bigIntSuffix := "" + if s.char() == '.' { + s.pos++ + fractionalPart = s.scanNumberFragment() + } + end := s.pos + if s.char() == 'E' || s.char() == 'e' { + s.pos++ + s.tokenFlags |= TokenFlagsScientific + if s.char() == '+' || s.char() == '-' { + s.pos++ + } + startNumericPart := s.pos + exponentPart = s.scanNumberFragment() + if exponentPart == "" { + s.error(diagnostics.Digit_expected) + } else { + exponentPreamble = s.text[end:startNumericPart] + end = s.pos + } + } else if s.char() == 'n' { + bigIntSuffix = "n" + s.pos++ + end = s.pos + } + if s.tokenFlags&TokenFlagsContainsSeparator != 0 { + s.tokenValue = fixedPart + if fractionalPart != "" { + s.tokenValue += "." + fractionalPart + } + if exponentPart != "" { + s.tokenValue += exponentPreamble + exponentPart + } + s.tokenValue += bigIntSuffix + } else { + s.tokenValue = s.text[start:end] + } + if s.tokenFlags&TokenFlagsContainsLeadingZero != 0 { + s.errorAt(diagnostics.Decimals_with_leading_zeros_are_not_allowed, start, s.pos-start) + return SyntaxKindNumericLiteral + } + ch, _ := s.charAndSize() + if isIdentifierStart(ch, s.languageVersion) { + idStart := s.pos + id := s.scanIdentifierParts() + if len(id) == 1 && s.text[idStart] == 'n' { + if s.tokenFlags&TokenFlagsScientific != 0 { + s.errorAt(diagnostics.A_bigint_literal_cannot_use_exponential_notation, start, s.pos-start) + } else if fixedPartEnd < idStart { + s.errorAt(diagnostics.A_bigint_literal_must_be_an_integer, start, s.pos-start) + } else { + s.errorAt(diagnostics.An_identifier_or_keyword_cannot_immediately_follow_a_numeric_literal, idStart, s.pos-idStart) + s.pos = idStart + } + } + } + if bigIntSuffix != "" { + return SyntaxKindBigintLiteral + } + return SyntaxKindNumericLiteral +} + +func (s *Scanner) scanNumberFragment() string { + start := s.pos + allowSeparator := false + isPreviousTokenSeparator := false + result := "" + for { + ch := s.char() + if ch == '_' { + s.tokenFlags |= TokenFlagsContainsSeparator + if allowSeparator { + allowSeparator = false + isPreviousTokenSeparator = true + result += s.text[start:s.pos] + } else { + s.tokenFlags |= TokenFlagsContainsInvalidSeparator + if isPreviousTokenSeparator { + s.errorAt(diagnostics.Multiple_consecutive_numeric_separators_are_not_permitted, s.pos, 1) + } else { + s.errorAt(diagnostics.Numeric_separators_are_not_allowed_here, s.pos, 1) + } + } + s.pos++ + start = s.pos + continue + } + if isDigit(ch) { + allowSeparator = true + isPreviousTokenSeparator = false + s.pos++ + continue + } + break + } + if isPreviousTokenSeparator { + s.tokenFlags |= TokenFlagsContainsInvalidSeparator + s.errorAt(diagnostics.Numeric_separators_are_not_allowed_here, s.pos-1, 1) + } + return result + s.text[start:s.pos] +} + +func (s *Scanner) scanDigits() (string, bool) { + start := s.pos + isOctal := true + for isDigit(s.char()) { + if !isOctalDigit(s.char()) { + isOctal = false + } + s.pos++ + } + return s.text[start:s.pos], isOctal +} + +func (s *Scanner) scanHexDigits(minCount int, scanAsManyAsPossible bool, canHaveSeparators bool) string { + result := "" + allowSeparator := false + isPreviousTokenSeparator := false + for len(result) < minCount || scanAsManyAsPossible { + ch := s.char() + if isHexDigit(ch) { + if ch >= 'A' && ch <= 'F' { + ch += 'a' - 'A' // standardize hex literals to lowercase + } + result += string(ch) + allowSeparator = canHaveSeparators + isPreviousTokenSeparator = false + } else if canHaveSeparators && ch == '_' { + s.tokenFlags |= TokenFlagsContainsSeparator + if allowSeparator { + allowSeparator = false + isPreviousTokenSeparator = true + } else if isPreviousTokenSeparator { + s.errorAt(diagnostics.Multiple_consecutive_numeric_separators_are_not_permitted, s.pos, 1) + } else { + s.errorAt(diagnostics.Numeric_separators_are_not_allowed_here, s.pos, 1) + } + } else { + break + } + s.pos++ + } + if isPreviousTokenSeparator { + s.errorAt(diagnostics.Numeric_separators_are_not_allowed_here, s.pos-1, 1) + } + if len(result) < minCount { + result = "" + } + return result +} + +func (s *Scanner) scanBinaryOrOctalDigits(base int32) string { + result := "" + allowSeparator := false + isPreviousTokenSeparator := false + for { + ch := s.char() + if isDigit(ch) && ch-'0' < base { + result += string(ch) + allowSeparator = true + isPreviousTokenSeparator = false + } else if ch == '_' { + s.tokenFlags |= TokenFlagsContainsSeparator + if allowSeparator { + allowSeparator = false + isPreviousTokenSeparator = true + } else if isPreviousTokenSeparator { + s.errorAt(diagnostics.Multiple_consecutive_numeric_separators_are_not_permitted, s.pos, 1) + } else { + s.errorAt(diagnostics.Numeric_separators_are_not_allowed_here, s.pos, 1) + } + } else { + break + } + s.pos++ + } + if isPreviousTokenSeparator { + s.errorAt(diagnostics.Numeric_separators_are_not_allowed_here, s.pos-1, 1) + } + return result +} + +func (s *Scanner) scanBigIntSuffix() SyntaxKind { + if s.char() == 'n' { + s.pos++ + return SyntaxKindBigintLiteral + } + return SyntaxKindNumericLiteral +} + +func (s *Scanner) scanInvalidCharacter() { + _, size := s.charAndSize() + s.errorAt(diagnostics.Invalid_character, s.pos, size) + s.pos += size + s.token = SyntaxKindUnknown +} + +func getIdentifierToken(str string) SyntaxKind { + if len(str) >= 2 && len(str) <= 12 && str[0] >= 'a' && str[0] <= 'z' { + keyword := textToKeyword[str] + if keyword != SyntaxKindUnknown { + return keyword + } + } + return SyntaxKindIdentifier +} + +func isWhiteSpaceLike(ch rune) bool { + return isWhiteSpaceSingleLine(ch) || isLineBreak(ch) +} + +func isWhiteSpaceSingleLine(ch rune) bool { + // Note: nextLine is in the Zs space, and should be considered to be a whitespace. + // It is explicitly not a line-break as it isn't in the exact set specified by EcmaScript. + switch ch { + case + ' ', // space + '\t', // tab + '\v', // verticalTab + '\f', // formFeed + 0x0085, // nextLine + 0x00A0, // nonBreakingSpace + 0x1680, // ogham + 0x2000, // enQuad + 0x2001, // emQuad + 0x2002, // enSpace + 0x2003, // emSpace + 0x2004, // threePerEmSpace + 0x2005, // fourPerEmSpace + 0x2006, // sixPerEmSpace + 0x2007, // figureSpace + 0x2008, // punctuationEmSpace + 0x2009, // thinSpace + 0x200A, // hairSpace + 0x200B, // zeroWidthSpace + 0x202F, // narrowNoBreakSpace + 0x205F, // mathematicalSpace + 0x3000, // ideographicSpace + 0xFEFF: // byteOrderMark + return true + } + return false +} + +func isLineBreak(ch rune) bool { + // ES5 7.3: + // The ECMAScript line terminator characters are listed in Table 3. + // Table 3: Line Terminator Characters + // Code Unit Value Name Formal Name + // \u000A Line Feed + // \u000D Carriage Return + // \u2028 Line separator + // \u2029 Paragraph separator + // Only the characters in Table 3 are treated as line terminators. Other new line or line + // breaking characters are treated as white space but not as line terminators. + switch ch { + case + '\n', // lineFeed + '\r', // carriageReturn + 0x2028, // lineSeparator + 0x2029: // paragraphSeparator + return true + } + return false +} + +func isDigit(ch rune) bool { + return ch >= '0' && ch <= '9' +} + +func isOctalDigit(ch rune) bool { + return ch >= '0' && ch <= '7' +} + +func isHexDigit(ch rune) bool { + return ch >= '0' && ch <= '9' || ch >= 'A' && ch <= 'F' || ch >= 'a' && ch <= 'f' +} + +func isASCIILetter(ch rune) bool { + return ch >= 'A' && ch <= 'Z' || ch >= 'a' && ch <= 'z' +} + +// Section 6.1.4 +func isWordCharacter(ch rune) bool { + return isASCIILetter(ch) || isDigit(ch) || ch == '_' +} + +func isIdentifierStart(ch rune, languageVersion ScriptTarget) bool { + return isASCIILetter(ch) || ch == '_' || ch == '$' || ch > 0x7F && isUnicodeIdentifierStart(ch, languageVersion) +} + +func isIdentifierPart(ch rune, languageVersion ScriptTarget, identifierVariant LanguageVariant) bool { + return isWordCharacter(ch) || ch == '$' || + // "-" and ":" are valid in JSX Identifiers + identifierVariant == LanguageVariantJSX && (ch == '-' || ch == ':') || + ch > 0x7F && isUnicodeIdentifierPart(ch, languageVersion) +} + +func isUnicodeIdentifierStart(ch rune, languageVersion ScriptTarget) bool { + return isInUnicodeRanges(ch, ifElse(languageVersion >= ScriptTargetES2015, unicodeESNextIdentifierStart, unicodeES5IdentifierStart)) +} + +func isUnicodeIdentifierPart(ch rune, languageVersion ScriptTarget) bool { + return isInUnicodeRanges(ch, ifElse(languageVersion >= ScriptTargetES2015, unicodeESNextIdentifierPart, unicodeES5IdentifierPart)) +} + +func isInUnicodeRanges(cp rune, ranges []rune) bool { + // Bail out quickly if it couldn't possibly be in the map + if cp < ranges[0] { + return false + } + // Perform binary search in one of the Unicode range maps + lo := 0 + hi := len(ranges) + for lo+1 < hi { + mid := lo + (hi-lo)/2 + // mid has to be even to catch beginning of a range + mid -= mid % 2 + if ranges[mid] <= cp && cp <= ranges[mid+1] { + return true + } + if cp < ranges[mid] { + hi = mid + } else { + lo = mid + 2 + } + } + return false +} + +var tokenToText map[SyntaxKind]string + +func init() { + tokenToText = make(map[SyntaxKind]string, len(textToKeyword)+len(textToToken)) + for text, key := range textToKeyword { + tokenToText[key] = text + } + for text, key := range textToToken { + tokenToText[key] = text + } +} + +func TokenToString(token SyntaxKind) string { + return tokenToText[token] +} + +// !!! Should just skip trivia +func skipTrivia(sourceText string, pos int) int { + s := NewScanner() + s.text = sourceText + s.pos = pos + s.Scan() + return s.tokenStart +} + +func getScannerForSourceFile(sourceFile *SourceFile, pos int) *Scanner { + s := NewScanner() + s.text = sourceFile.text + s.pos = pos + s.languageVersion = sourceFile.languageVersion + s.languageVariant = sourceFile.languageVariant + s.Scan() + return s +} + +func getRangeOfTokenAtPosition(sourceFile *SourceFile, pos int) TextRange { + s := getScannerForSourceFile(sourceFile, pos) + return NewTextRange(s.tokenStart, s.pos) +} + +func computeLineStarts(text string) []TextPos { + var result []TextPos + pos := 0 + lineStart := 0 + for pos < len(text) { + b := text[pos] + if b < 0x7F { + pos++ + switch b { + case '\r': + if pos < len(text) && text[pos] == '\n' { + pos++ + } + fallthrough + case '\n': + result = append(result, TextPos(lineStart)) + lineStart = pos + } + } else { + ch, size := utf8.DecodeRuneInString(text[pos:]) + pos += size + if isLineBreak(ch) { + result = append(result, TextPos(lineStart)) + lineStart = pos + } + } + } + result = append(result, TextPos(lineStart)) + return result +} + +func computeLineOfPosition(lineStarts []TextPos, pos TextPos) int { + low := 0 + high := len(lineStarts) - 1 + for low <= high { + middle := low + ((high - low) >> 1) + value := lineStarts[middle] + if value < pos { + low = middle + 1 + } else if value > pos { + high = middle - 1 + } else { + return middle + } + } + return low - 1 +} + +func getLineStarts(sourceFile *SourceFile) []TextPos { + if sourceFile.lineMap == nil { + sourceFile.lineMap = computeLineStarts(sourceFile.text) + } + return sourceFile.lineMap +} + +func GetLineAndCharacterOfPosition(sourceFile *SourceFile, pos int) (line int, character int) { + line = computeLineOfPosition(getLineStarts(sourceFile), TextPos(pos)) + character = utf8.RuneCountInString(sourceFile.text[sourceFile.lineMap[line]:pos]) + return +} + +func getEndLinePosition(sourceFile *SourceFile, line int) int { + pos := int(getLineStarts(sourceFile)[line]) + for { + ch, size := utf8.DecodeRuneInString(sourceFile.text[pos:]) + if size == 0 || isLineBreak(ch) { + return pos + } + pos += size + } +} diff --git a/internal/compiler/types.go b/internal/compiler/types.go new file mode 100644 index 0000000000..9f65e56625 --- /dev/null +++ b/internal/compiler/types.go @@ -0,0 +1,1487 @@ +package compiler + +type SyntaxKind int16 + +const ( + SyntaxKindUnknown SyntaxKind = iota + SyntaxKindEndOfFile + SyntaxKindConflictMarkerTrivia + SyntaxKindNonTextFileMarkerTrivia + SyntaxKindNumericLiteral + SyntaxKindBigintLiteral + SyntaxKindStringLiteral + SyntaxKindJsxText + SyntaxKindJsxTextAllWhiteSpaces + SyntaxKindRegularExpressionLiteral + SyntaxKindNoSubstitutionTemplateLiteral + // Pseudo-literals + SyntaxKindTemplateHead + SyntaxKindTemplateMiddle + SyntaxKindTemplateTail + // Punctuation + SyntaxKindOpenBraceToken + SyntaxKindCloseBraceToken + SyntaxKindOpenParenToken + SyntaxKindCloseParenToken + SyntaxKindOpenBracketToken + SyntaxKindCloseBracketToken + SyntaxKindDotToken + SyntaxKindDotDotDotToken + SyntaxKindSemicolonToken + SyntaxKindCommaToken + SyntaxKindQuestionDotToken + SyntaxKindLessThanToken + SyntaxKindLessThanSlashToken + SyntaxKindGreaterThanToken + SyntaxKindLessThanEqualsToken + SyntaxKindGreaterThanEqualsToken + SyntaxKindEqualsEqualsToken + SyntaxKindExclamationEqualsToken + SyntaxKindEqualsEqualsEqualsToken + SyntaxKindExclamationEqualsEqualsToken + SyntaxKindEqualsGreaterThanToken + SyntaxKindPlusToken + SyntaxKindMinusToken + SyntaxKindAsteriskToken + SyntaxKindAsteriskAsteriskToken + SyntaxKindSlashToken + SyntaxKindPercentToken + SyntaxKindPlusPlusToken + SyntaxKindMinusMinusToken + SyntaxKindLessThanLessThanToken + SyntaxKindGreaterThanGreaterThanToken + SyntaxKindGreaterThanGreaterThanGreaterThanToken + SyntaxKindAmpersandToken + SyntaxKindBarToken + SyntaxKindCaretToken + SyntaxKindExclamationToken + SyntaxKindTildeToken + SyntaxKindAmpersandAmpersandToken + SyntaxKindBarBarToken + SyntaxKindQuestionToken + SyntaxKindColonToken + SyntaxKindAtToken + SyntaxKindQuestionQuestionToken + /** Only the JSDoc scanner produces BacktickToken. The normal scanner produces NoSubstitutionTemplateLiteral and related kinds. */ + SyntaxKindBacktickToken + /** Only the JSDoc scanner produces HashToken. The normal scanner produces PrivateIdentifier. */ + SyntaxKindHashToken + // Assignments + SyntaxKindEqualsToken + SyntaxKindPlusEqualsToken + SyntaxKindMinusEqualsToken + SyntaxKindAsteriskEqualsToken + SyntaxKindAsteriskAsteriskEqualsToken + SyntaxKindSlashEqualsToken + SyntaxKindPercentEqualsToken + SyntaxKindLessThanLessThanEqualsToken + SyntaxKindGreaterThanGreaterThanEqualsToken + SyntaxKindGreaterThanGreaterThanGreaterThanEqualsToken + SyntaxKindAmpersandEqualsToken + SyntaxKindBarEqualsToken + SyntaxKindBarBarEqualsToken + SyntaxKindAmpersandAmpersandEqualsToken + SyntaxKindQuestionQuestionEqualsToken + SyntaxKindCaretEqualsToken + // Identifiers and PrivateIdentifier + SyntaxKindIdentifier + SyntaxKindPrivateIdentifier + SyntaxKindJSDocCommentTextToken + // Reserved words + SyntaxKindBreakKeyword + SyntaxKindCaseKeyword + SyntaxKindCatchKeyword + SyntaxKindClassKeyword + SyntaxKindConstKeyword + SyntaxKindContinueKeyword + SyntaxKindDebuggerKeyword + SyntaxKindDefaultKeyword + SyntaxKindDeleteKeyword + SyntaxKindDoKeyword + SyntaxKindElseKeyword + SyntaxKindEnumKeyword + SyntaxKindExportKeyword + SyntaxKindExtendsKeyword + SyntaxKindFalseKeyword + SyntaxKindFinallyKeyword + SyntaxKindForKeyword + SyntaxKindFunctionKeyword + SyntaxKindIfKeyword + SyntaxKindImportKeyword + SyntaxKindInKeyword + SyntaxKindInstanceOfKeyword + SyntaxKindNewKeyword + SyntaxKindNullKeyword + SyntaxKindReturnKeyword + SyntaxKindSuperKeyword + SyntaxKindSwitchKeyword + SyntaxKindThisKeyword + SyntaxKindThrowKeyword + SyntaxKindTrueKeyword + SyntaxKindTryKeyword + SyntaxKindTypeOfKeyword + SyntaxKindVarKeyword + SyntaxKindVoidKeyword + SyntaxKindWhileKeyword + SyntaxKindWithKeyword + // Strict mode reserved words + SyntaxKindImplementsKeyword + SyntaxKindInterfaceKeyword + SyntaxKindLetKeyword + SyntaxKindPackageKeyword + SyntaxKindPrivateKeyword + SyntaxKindProtectedKeyword + SyntaxKindPublicKeyword + SyntaxKindStaticKeyword + SyntaxKindYieldKeyword + // Contextual keywords + SyntaxKindAbstractKeyword + SyntaxKindAccessorKeyword + SyntaxKindAsKeyword + SyntaxKindAssertsKeyword + SyntaxKindAssertKeyword + SyntaxKindAnyKeyword + SyntaxKindAsyncKeyword + SyntaxKindAwaitKeyword + SyntaxKindBooleanKeyword + SyntaxKindConstructorKeyword + SyntaxKindDeclareKeyword + SyntaxKindGetKeyword + SyntaxKindImmediateKeyword + SyntaxKindInferKeyword + SyntaxKindIntrinsicKeyword + SyntaxKindIsKeyword + SyntaxKindKeyOfKeyword + SyntaxKindModuleKeyword + SyntaxKindNamespaceKeyword + SyntaxKindNeverKeyword + SyntaxKindOutKeyword + SyntaxKindReadonlyKeyword + SyntaxKindRequireKeyword + SyntaxKindNumberKeyword + SyntaxKindObjectKeyword + SyntaxKindSatisfiesKeyword + SyntaxKindSetKeyword + SyntaxKindStringKeyword + SyntaxKindSymbolKeyword + SyntaxKindTypeKeyword + SyntaxKindUndefinedKeyword + SyntaxKindUniqueKeyword + SyntaxKindUnknownKeyword + SyntaxKindUsingKeyword + SyntaxKindFromKeyword + SyntaxKindGlobalKeyword + SyntaxKindBigIntKeyword + SyntaxKindOverrideKeyword + SyntaxKindOfKeyword // LastKeyword and LastToken and LastContextualKeyword + // Parse tree nodes + // Names + SyntaxKindQualifiedName + SyntaxKindComputedPropertyName + // Lists + SyntaxKindModifierList + SyntaxKindTypeParameterList + SyntaxKindTypeArgumentList + // Signature elements + SyntaxKindTypeParameter + SyntaxKindParameter + SyntaxKindDecorator + // TypeMember + SyntaxKindPropertySignature + SyntaxKindPropertyDeclaration + SyntaxKindMethodSignature + SyntaxKindMethodDeclaration + SyntaxKindClassStaticBlockDeclaration + SyntaxKindConstructor + SyntaxKindGetAccessor + SyntaxKindSetAccessor + SyntaxKindCallSignature + SyntaxKindConstructSignature + SyntaxKindIndexSignature + // Type + SyntaxKindTypePredicate + SyntaxKindTypeReference + SyntaxKindFunctionType + SyntaxKindConstructorType + SyntaxKindTypeQuery + SyntaxKindTypeLiteral + SyntaxKindArrayType + SyntaxKindTupleType + SyntaxKindOptionalType + SyntaxKindRestType + SyntaxKindUnionType + SyntaxKindIntersectionType + SyntaxKindConditionalType + SyntaxKindInferType + SyntaxKindParenthesizedType + SyntaxKindThisType + SyntaxKindTypeOperator + SyntaxKindIndexedAccessType + SyntaxKindMappedType + SyntaxKindLiteralType + SyntaxKindNamedTupleMember + SyntaxKindTemplateLiteralType + SyntaxKindTemplateLiteralTypeSpan + SyntaxKindImportType + // Binding patterns + SyntaxKindObjectBindingPattern + SyntaxKindArrayBindingPattern + SyntaxKindBindingElement + // Expression + SyntaxKindArrayLiteralExpression + SyntaxKindObjectLiteralExpression + SyntaxKindPropertyAccessExpression + SyntaxKindElementAccessExpression + SyntaxKindCallExpression + SyntaxKindNewExpression + SyntaxKindTaggedTemplateExpression + SyntaxKindTypeAssertionExpression + SyntaxKindParenthesizedExpression + SyntaxKindFunctionExpression + SyntaxKindArrowFunction + SyntaxKindDeleteExpression + SyntaxKindTypeOfExpression + SyntaxKindVoidExpression + SyntaxKindAwaitExpression + SyntaxKindPrefixUnaryExpression + SyntaxKindPostfixUnaryExpression + SyntaxKindBinaryExpression + SyntaxKindConditionalExpression + SyntaxKindTemplateExpression + SyntaxKindYieldExpression + SyntaxKindSpreadElement + SyntaxKindClassExpression + SyntaxKindOmittedExpression + SyntaxKindExpressionWithTypeArguments + SyntaxKindAsExpression + SyntaxKindNonNullExpression + SyntaxKindMetaProperty + SyntaxKindSyntheticExpression + SyntaxKindSatisfiesExpression + // Misc + SyntaxKindTemplateSpan + SyntaxKindSemicolonClassElement + // Element + SyntaxKindBlock + SyntaxKindEmptyStatement + SyntaxKindVariableStatement + SyntaxKindExpressionStatement + SyntaxKindIfStatement + SyntaxKindDoStatement + SyntaxKindWhileStatement + SyntaxKindForStatement + SyntaxKindForInStatement + SyntaxKindForOfStatement + SyntaxKindContinueStatement + SyntaxKindBreakStatement + SyntaxKindReturnStatement + SyntaxKindWithStatement + SyntaxKindSwitchStatement + SyntaxKindLabeledStatement + SyntaxKindThrowStatement + SyntaxKindTryStatement + SyntaxKindDebuggerStatement + SyntaxKindVariableDeclaration + SyntaxKindVariableDeclarationList + SyntaxKindFunctionDeclaration + SyntaxKindClassDeclaration + SyntaxKindInterfaceDeclaration + SyntaxKindTypeAliasDeclaration + SyntaxKindEnumDeclaration + SyntaxKindModuleDeclaration + SyntaxKindModuleBlock + SyntaxKindCaseBlock + SyntaxKindNamespaceExportDeclaration + SyntaxKindImportEqualsDeclaration + SyntaxKindImportDeclaration + SyntaxKindImportClause + SyntaxKindNamespaceImport + SyntaxKindNamedImports + SyntaxKindImportSpecifier + SyntaxKindExportAssignment + SyntaxKindExportDeclaration + SyntaxKindNamedExports + SyntaxKindNamespaceExport + SyntaxKindExportSpecifier + SyntaxKindMissingDeclaration + // Module references + SyntaxKindExternalModuleReference + // JSX + SyntaxKindJsxElement + SyntaxKindJsxSelfClosingElement + SyntaxKindJsxOpeningElement + SyntaxKindJsxClosingElement + SyntaxKindJsxFragment + SyntaxKindJsxOpeningFragment + SyntaxKindJsxClosingFragment + SyntaxKindJsxAttribute + SyntaxKindJsxAttributes + SyntaxKindJsxSpreadAttribute + SyntaxKindJsxExpression + SyntaxKindJsxNamespacedName + // Clauses + SyntaxKindCaseClause + SyntaxKindDefaultClause + SyntaxKindHeritageClause + SyntaxKindCatchClause + // Import attributes + SyntaxKindImportAttributes + SyntaxKindImportAttribute + // Property assignments + SyntaxKindPropertyAssignment + SyntaxKindShorthandPropertyAssignment + SyntaxKindSpreadAssignment + // Enum + SyntaxKindEnumMember + // Top-level nodes + SyntaxKindSourceFile + SyntaxKindBundle + // JSDoc nodes + SyntaxKindJSDocTypeExpression + SyntaxKindJSDocNameReference + SyntaxKindJSDocMemberName // C#p + SyntaxKindJSDocAllType // The * type + SyntaxKindJSDocUnknownType // The ? type + SyntaxKindJSDocNullableType + SyntaxKindJSDocNonNullableType + SyntaxKindJSDocOptionalType + SyntaxKindJSDocFunctionType + SyntaxKindJSDocVariadicType + SyntaxKindJSDocNamepathType // https://jsdoc.app/about-namepaths.html + SyntaxKindJSDoc + SyntaxKindJSDocText + SyntaxKindJSDocTypeLiteral + SyntaxKindJSDocSignature + SyntaxKindJSDocLink + SyntaxKindJSDocLinkCode + SyntaxKindJSDocLinkPlain + SyntaxKindJSDocTag + SyntaxKindJSDocAugmentsTag + SyntaxKindJSDocImplementsTag + SyntaxKindJSDocAuthorTag + SyntaxKindJSDocDeprecatedTag + SyntaxKindJSDocImmediateTag + SyntaxKindJSDocClassTag + SyntaxKindJSDocPublicTag + SyntaxKindJSDocPrivateTag + SyntaxKindJSDocProtectedTag + SyntaxKindJSDocReadonlyTag + SyntaxKindJSDocOverrideTag + SyntaxKindJSDocCallbackTag + SyntaxKindJSDocOverloadTag + SyntaxKindJSDocEnumTag + SyntaxKindJSDocParameterTag + SyntaxKindJSDocReturnTag + SyntaxKindJSDocThisTag + SyntaxKindJSDocTypeTag + SyntaxKindJSDocTemplateTag + SyntaxKindJSDocTypedefTag + SyntaxKindJSDocSeeTag + SyntaxKindJSDocPropertyTag + SyntaxKindJSDocThrowsTag + SyntaxKindJSDocSatisfiesTag + SyntaxKindJSDocImportTag + // Synthesized list + SyntaxKindSyntaxList + // Transformation nodes + SyntaxKindNotEmittedStatement + SyntaxKindPartiallyEmittedExpression + SyntaxKindCommaListExpression + SyntaxKindSyntheticReferenceExpression + // Enum value count + SyntaxKindCount + // Markers + SyntaxKindFirstAssignment = SyntaxKindEqualsToken + SyntaxKindLastAssignment = SyntaxKindCaretEqualsToken + SyntaxKindFirstCompoundAssignment = SyntaxKindPlusEqualsToken + SyntaxKindLastCompoundAssignment = SyntaxKindCaretEqualsToken + SyntaxKindFirstReservedWord = SyntaxKindBreakKeyword + SyntaxKindLastReservedWord = SyntaxKindWithKeyword + SyntaxKindFirstKeyword = SyntaxKindBreakKeyword + SyntaxKindLastKeyword = SyntaxKindOfKeyword + SyntaxKindFirstFutureReservedWord = SyntaxKindImplementsKeyword + SyntaxKindLastFutureReservedWord = SyntaxKindYieldKeyword + SyntaxKindFirstTypeNode = SyntaxKindTypePredicate + SyntaxKindLastTypeNode = SyntaxKindImportType + SyntaxKindFirstPunctuation = SyntaxKindOpenBraceToken + SyntaxKindLastPunctuation = SyntaxKindCaretEqualsToken + SyntaxKindFirstToken = SyntaxKindUnknown + SyntaxKindLastToken = SyntaxKindLastKeyword + SyntaxKindFirstLiteralToken = SyntaxKindNumericLiteral + SyntaxKindLastLiteralToken = SyntaxKindNoSubstitutionTemplateLiteral + SyntaxKindFirstTemplateToken = SyntaxKindNoSubstitutionTemplateLiteral + SyntaxKindLastTemplateToken = SyntaxKindTemplateTail + SyntaxKindFirstBinaryOperator = SyntaxKindLessThanToken + SyntaxKindLastBinaryOperator = SyntaxKindCaretEqualsToken + SyntaxKindFirstStatement = SyntaxKindVariableStatement + SyntaxKindLastStatement = SyntaxKindDebuggerStatement + SyntaxKindFirstNode = SyntaxKindQualifiedName + SyntaxKindFirstJSDocNode = SyntaxKindJSDocTypeExpression + SyntaxKindLastJSDocNode = SyntaxKindJSDocImportTag + SyntaxKindFirstJSDocTagNode = SyntaxKindJSDocTag + SyntaxKindLastJSDocTagNode = SyntaxKindJSDocImportTag + SyntaxKindFirstContextualKeyword = SyntaxKindAbstractKeyword + SyntaxKindLastContextualKeyword = SyntaxKindOfKeyword +) + +type NodeFlags uint32 + +const ( + NodeFlagsNone NodeFlags = 0 + NodeFlagsLet NodeFlags = 1 << 0 // Variable declaration + NodeFlagsConst NodeFlags = 1 << 1 // Variable declaration + NodeFlagsUsing NodeFlags = 1 << 2 // Variable declaration + NodeFlagsNestedNamespace NodeFlags = 1 << 3 // Namespace declaration + NodeFlagsSynthesized NodeFlags = 1 << 4 // Node was synthesized during transformation + NodeFlagsNamespace NodeFlags = 1 << 5 // Namespace declaration + NodeFlagsOptionalChain NodeFlags = 1 << 6 // Chained MemberExpression rooted to a pseudo-OptionalExpression + NodeFlagsExportContext NodeFlags = 1 << 7 // Export context (initialized by binding) + NodeFlagsContainsThis NodeFlags = 1 << 8 // Interface contains references to "this" + NodeFlagsHasImplicitReturn NodeFlags = 1 << 9 // If function implicitly returns on one of codepaths (initialized by binding) + NodeFlagsHasExplicitReturn NodeFlags = 1 << 10 // If function has explicit reachable return on one of codepaths (initialized by binding) + NodeFlagsGlobalAugmentation NodeFlags = 1 << 11 // Set if module declaration is an augmentation for the global scope + NodeFlagsHasAsyncFunctions NodeFlags = 1 << 12 // If the file has async functions (initialized by binding) + NodeFlagsDisallowInContext NodeFlags = 1 << 13 // If node was parsed in a context where 'in-expressions' are not allowed + NodeFlagsYieldContext NodeFlags = 1 << 14 // If node was parsed in the 'yield' context created when parsing a generator + NodeFlagsDecoratorContext NodeFlags = 1 << 15 // If node was parsed as part of a decorator + NodeFlagsAwaitContext NodeFlags = 1 << 16 // If node was parsed in the 'await' context created when parsing an async function + NodeFlagsDisallowConditionalTypesContext NodeFlags = 1 << 17 // If node was parsed in a context where conditional types are not allowed + NodeFlagsThisNodeHasError NodeFlags = 1 << 18 // If the parser encountered an error when parsing the code that created this node + NodeFlagsJavaScriptFile NodeFlags = 1 << 19 // If node was parsed in a JavaScript + NodeFlagsThisNodeOrAnySubNodesHasError NodeFlags = 1 << 20 // If this node or any of its children had an error + NodeFlagsHasAggregatedChildData NodeFlags = 1 << 21 // If we've computed data from children and cached it in this node + + // These flags will be set when the parser encounters a dynamic import expression or 'import.meta' to avoid + // walking the tree if the flags are not set. However, these flags are just a approximation + // (hence why it's named "PossiblyContainsDynamicImport") because once set, the flags never get cleared. + // During editing, if a dynamic import is removed, incremental parsing will *NOT* clear this flag. + // This means that the tree will always be traversed during module resolution, or when looking for external module indicators. + // However, the removal operation should not occur often and in the case of the + // removal, it is likely that users will add the import anyway. + // The advantage of this approach is its simplicity. For the case of batch compilation, + // we guarantee that users won't have to pay the price of walking the tree if a dynamic import isn't used. + NodeFlagsPossiblyContainsDynamicImport NodeFlags = 1 << 22 + NodeFlagsPossiblyContainsImportMeta NodeFlags = 1 << 23 + + NodeFlagsJSDoc NodeFlags = 1 << 24 // If node was parsed inside jsdoc + NodeFlagsAmbient NodeFlags = 1 << 25 // If node was inside an ambient context -- a declaration file, or inside something with the `declare` modifier. + NodeFlagsInWithStatement NodeFlags = 1 << 26 // If any ancestor of node was the `statement` of a WithStatement (not the `expression`) + NodeFlagsJsonFile NodeFlags = 1 << 27 // If node was parsed in a Json + NodeFlagsTypeCached NodeFlags = 1 << 28 // If a type was cached for node at any point + NodeFlagsDeprecated NodeFlags = 1 << 29 // If has '@deprecated' JSDoc tag + + NodeFlagsBlockScoped = NodeFlagsLet | NodeFlagsConst | NodeFlagsUsing + NodeFlagsConstant = NodeFlagsConst | NodeFlagsUsing + NodeFlagsAwaitUsing = NodeFlagsConst | NodeFlagsUsing // Variable declaration (NOTE: on a single node these flags would otherwise be mutually exclusive) + + NodeFlagsReachabilityCheckFlags = NodeFlagsHasImplicitReturn | NodeFlagsHasExplicitReturn + NodeFlagsReachabilityAndEmitFlags = NodeFlagsReachabilityCheckFlags | NodeFlagsHasAsyncFunctions + + // Parsing context flags + NodeFlagsContextFlags NodeFlags = NodeFlagsDisallowInContext | NodeFlagsDisallowConditionalTypesContext | NodeFlagsYieldContext | NodeFlagsDecoratorContext | NodeFlagsAwaitContext | NodeFlagsJavaScriptFile | NodeFlagsInWithStatement | NodeFlagsAmbient + + // Exclude these flags when parsing a Type + NodeFlagsTypeExcludesFlags NodeFlags = NodeFlagsYieldContext | NodeFlagsAwaitContext + + // Represents all flags that are potentially set once and + // never cleared on SourceFiles which get re-used in between incremental parses. + // See the comment above on `PossiblyContainsDynamicImport` and `PossiblyContainsImportMeta`. + NodeFlagsPermanentlySetIncrementalFlags NodeFlags = NodeFlagsPossiblyContainsDynamicImport | NodeFlagsPossiblyContainsImportMeta + + // The following flags repurpose other NodeFlags as different meanings for Identifier nodes + NodeFlagsIdentifierHasExtendedUnicodeEscape NodeFlags = NodeFlagsContainsThis // Indicates whether the identifier contains an extended unicode escape sequence + NodeFlagsIdentifierIsInJSDocNamespace NodeFlags = NodeFlagsHasAsyncFunctions // Indicates whether the identifier is part of a JSDoc namespace +) + +type ModifierFlags uint32 + +const ( + ModifierFlagsNone ModifierFlags = 0 + // Syntactic/JSDoc modifiers + ModifierFlagsPublic ModifierFlags = 1 << 0 // Property/Method + ModifierFlagsPrivate ModifierFlags = 1 << 1 // Property/Method + ModifierFlagsProtected ModifierFlags = 1 << 2 // Property/Method + ModifierFlagsReadonly ModifierFlags = 1 << 3 // Property/Method + ModifierFlagsOverride ModifierFlags = 1 << 4 // Override method + // Syntactic-only modifiers + ModifierFlagsExport ModifierFlags = 1 << 5 // Declarations + ModifierFlagsAbstract ModifierFlags = 1 << 6 // Class/Method/ConstructSignature + ModifierFlagsAmbient ModifierFlags = 1 << 7 // Declarations + ModifierFlagsStatic ModifierFlags = 1 << 8 // Property/Method + ModifierFlagsAccessor ModifierFlags = 1 << 9 // Property + ModifierFlagsAsync ModifierFlags = 1 << 10 // Property/Method/Function + ModifierFlagsDefault ModifierFlags = 1 << 11 // Function/Class (export default declaration) + ModifierFlagsConst ModifierFlags = 1 << 12 // Const enum + ModifierFlagsIn ModifierFlags = 1 << 13 // Contravariance modifier + ModifierFlagsOut ModifierFlags = 1 << 14 // Covariance modifier + ModifierFlagsDecorator ModifierFlags = 1 << 15 // Contains a decorator. + ModifierFlagsImmediate ModifierFlags = 1 << 16 // Parameter + // JSDoc-only modifiers + ModifierFlagsDeprecated ModifierFlags = 1 << 17 // Deprecated tag. + ModifierFlagsJSDocImmediate ModifierFlags = 1 << 18 // Parameter + // Cache-only JSDoc-modifiers. Should match order of Syntactic/JSDoc modifiers, above. + ModifierFlagsJSDocPublic ModifierFlags = 1 << 23 // if this value changes, `selectEffectiveModifierFlags` must change accordingly + ModifierFlagsJSDocPrivate ModifierFlags = 1 << 24 + ModifierFlagsJSDocProtected ModifierFlags = 1 << 25 + ModifierFlagsJSDocReadonly ModifierFlags = 1 << 26 + ModifierFlagsJSDocOverride ModifierFlags = 1 << 27 + ModifierFlagsHasComputedJSDocModifiers ModifierFlags = 1 << 28 // Indicates the computed modifier flags include modifiers from JSDoc. + ModifierFlagsHasComputedFlags ModifierFlags = 1 << 29 // Modifier flags have been computed + + ModifierFlagsSyntacticOrJSDocModifiers = ModifierFlagsPublic | ModifierFlagsPrivate | ModifierFlagsProtected | ModifierFlagsReadonly | ModifierFlagsOverride + ModifierFlagsSyntacticOnlyModifiers = ModifierFlagsExport | ModifierFlagsAmbient | ModifierFlagsAbstract | ModifierFlagsStatic | ModifierFlagsAccessor | ModifierFlagsAsync | ModifierFlagsDefault | ModifierFlagsConst | ModifierFlagsIn | ModifierFlagsOut | ModifierFlagsDecorator | ModifierFlagsImmediate + ModifierFlagsSyntacticModifiers = ModifierFlagsSyntacticOrJSDocModifiers | ModifierFlagsSyntacticOnlyModifiers + ModifierFlagsJSDocCacheOnlyModifiers = ModifierFlagsJSDocPublic | ModifierFlagsJSDocPrivate | ModifierFlagsJSDocProtected | ModifierFlagsJSDocReadonly | ModifierFlagsJSDocOverride + ModifierFlagsJSDocOnlyModifiers = ModifierFlagsDeprecated | ModifierFlagsJSDocImmediate + ModifierFlagsNonCacheOnlyModifiers = ModifierFlagsSyntacticOrJSDocModifiers | ModifierFlagsSyntacticOnlyModifiers | ModifierFlagsJSDocOnlyModifiers + + ModifierFlagsAccessibilityModifier = ModifierFlagsPublic | ModifierFlagsPrivate | ModifierFlagsProtected + // Accessibility modifiers and 'readonly' can be attached to a parameter in a constructor to make it a property. + ModifierFlagsParameterPropertyModifier = ModifierFlagsAccessibilityModifier | ModifierFlagsReadonly | ModifierFlagsOverride + ModifierFlagsNonPublicAccessibilityModifier = ModifierFlagsPrivate | ModifierFlagsProtected + + ModifierFlagsTypeScriptModifier = ModifierFlagsAmbient | ModifierFlagsPublic | ModifierFlagsPrivate | ModifierFlagsProtected | ModifierFlagsReadonly | ModifierFlagsAbstract | ModifierFlagsConst | ModifierFlagsOverride | ModifierFlagsIn | ModifierFlagsOut | ModifierFlagsImmediate + ModifierFlagsExportDefault = ModifierFlagsExport | ModifierFlagsDefault + ModifierFlagsAll = ModifierFlagsExport | ModifierFlagsAmbient | ModifierFlagsPublic | ModifierFlagsPrivate | ModifierFlagsProtected | ModifierFlagsStatic | ModifierFlagsReadonly | ModifierFlagsAbstract | ModifierFlagsAccessor | ModifierFlagsAsync | ModifierFlagsDefault | ModifierFlagsConst | ModifierFlagsDeprecated | ModifierFlagsOverride | ModifierFlagsIn | ModifierFlagsOut | ModifierFlagsImmediate | ModifierFlagsDecorator + ModifierFlagsModifier = ModifierFlagsAll & ^ModifierFlagsDecorator +) + +// SignatureFlags + +type SignatureFlags uint32 + +const ( + SignatureFlagsNone SignatureFlags = 0 + SignatureFlagsYield SignatureFlags = 1 << 0 + SignatureFlagsAwait SignatureFlags = 1 << 1 + SignatureFlagsType SignatureFlags = 1 << 2 + SignatureFlagsIgnoreMissingOpenBrace SignatureFlags = 1 << 4 + SignatureFlagsJSDoc SignatureFlags = 1 << 5 +) + +// SymbolFlags + +type SymbolFlags uint32 + +const ( + SymbolFlagsNone SymbolFlags = 0 + SymbolFlagsFunctionScopedVariable SymbolFlags = 1 << 0 // Variable (var) or parameter + SymbolFlagsBlockScopedVariable SymbolFlags = 1 << 1 // A block-scoped variable (let or const) + SymbolFlagsProperty SymbolFlags = 1 << 2 // Property or enum member + SymbolFlagsEnumMember SymbolFlags = 1 << 3 // Enum member + SymbolFlagsFunction SymbolFlags = 1 << 4 // Function + SymbolFlagsClass SymbolFlags = 1 << 5 // Class + SymbolFlagsInterface SymbolFlags = 1 << 6 // Interface + SymbolFlagsConstEnum SymbolFlags = 1 << 7 // Const enum + SymbolFlagsRegularEnum SymbolFlags = 1 << 8 // Enum + SymbolFlagsValueModule SymbolFlags = 1 << 9 // Instantiated module + SymbolFlagsNamespaceModule SymbolFlags = 1 << 10 // Uninstantiated module + SymbolFlagsTypeLiteral SymbolFlags = 1 << 11 // Type Literal or mapped type + SymbolFlagsObjectLiteral SymbolFlags = 1 << 12 // Object Literal + SymbolFlagsMethod SymbolFlags = 1 << 13 // Method + SymbolFlagsConstructor SymbolFlags = 1 << 14 // Constructor + SymbolFlagsGetAccessor SymbolFlags = 1 << 15 // Get accessor + SymbolFlagsSetAccessor SymbolFlags = 1 << 16 // Set accessor + SymbolFlagsSignature SymbolFlags = 1 << 17 // Call, construct, or index signature + SymbolFlagsTypeParameter SymbolFlags = 1 << 18 // Type parameter + SymbolFlagsTypeAlias SymbolFlags = 1 << 19 // Type alias + SymbolFlagsExportValue SymbolFlags = 1 << 20 // Exported value marker (see comment in declareModuleMember in binder) + SymbolFlagsAlias SymbolFlags = 1 << 21 // An alias for another symbol (see comment in isAliasSymbolDeclaration in checker) + SymbolFlagsPrototype SymbolFlags = 1 << 22 // Prototype property (no source representation) + SymbolFlagsExportStar SymbolFlags = 1 << 23 // Export * declaration + SymbolFlagsOptional SymbolFlags = 1 << 24 // Optional property + SymbolFlagsTransient SymbolFlags = 1 << 25 // Transient symbol (created during type check) + SymbolFlagsAssignment SymbolFlags = 1 << 26 // Assignment treated as declaration (eg `this.prop = 1`) + SymbolFlagsModuleExports SymbolFlags = 1 << 27 // Symbol for CommonJS `module` of `module.exports` + SymbolFlagsAll SymbolFlags = 0xFFFFFFFF + + SymbolFlagsEnum = SymbolFlagsRegularEnum | SymbolFlagsConstEnum + SymbolFlagsVariable = SymbolFlagsFunctionScopedVariable | SymbolFlagsBlockScopedVariable + SymbolFlagsValue = SymbolFlagsVariable | SymbolFlagsProperty | SymbolFlagsEnumMember | SymbolFlagsObjectLiteral | SymbolFlagsFunction | SymbolFlagsClass | SymbolFlagsEnum | SymbolFlagsValueModule | SymbolFlagsMethod | SymbolFlagsGetAccessor | SymbolFlagsSetAccessor + SymbolFlagsType = SymbolFlagsClass | SymbolFlagsInterface | SymbolFlagsEnum | SymbolFlagsEnumMember | SymbolFlagsTypeLiteral | SymbolFlagsTypeParameter | SymbolFlagsTypeAlias + SymbolFlagsNamespace = SymbolFlagsValueModule | SymbolFlagsNamespaceModule | SymbolFlagsEnum + SymbolFlagsModule = SymbolFlagsValueModule | SymbolFlagsNamespaceModule + SymbolFlagsAccessor = SymbolFlagsGetAccessor | SymbolFlagsSetAccessor + + // Variables can be redeclared, but can not redeclare a block-scoped declaration with the + // same name, or any other value that is not a variable, e.g. ValueModule or Class + SymbolFlagsFunctionScopedVariableExcludes = SymbolFlagsValue & ^SymbolFlagsFunctionScopedVariable + + // Block-scoped declarations are not allowed to be re-declared + // they can not merge with anything in the value space + SymbolFlagsBlockScopedVariableExcludes = SymbolFlagsValue + + SymbolFlagsParameterExcludes = SymbolFlagsValue + SymbolFlagsPropertyExcludes = SymbolFlagsNone + SymbolFlagsEnumMemberExcludes = SymbolFlagsValue | SymbolFlagsType + SymbolFlagsFunctionExcludes = SymbolFlagsValue & ^(SymbolFlagsFunction | SymbolFlagsValueModule | SymbolFlagsClass) + SymbolFlagsClassExcludes = (SymbolFlagsValue | SymbolFlagsType) & ^(SymbolFlagsValueModule | SymbolFlagsInterface | SymbolFlagsFunction) // class-interface mergability done in checker.ts + SymbolFlagsInterfaceExcludes = SymbolFlagsType & ^(SymbolFlagsInterface | SymbolFlagsClass) + SymbolFlagsRegularEnumExcludes = (SymbolFlagsValue | SymbolFlagsType) & ^(SymbolFlagsRegularEnum | SymbolFlagsValueModule) // regular enums merge only with regular enums and modules + SymbolFlagsConstEnumExcludes = (SymbolFlagsValue | SymbolFlagsType) & ^SymbolFlagsConstEnum // const enums merge only with const enums + SymbolFlagsValueModuleExcludes = SymbolFlagsValue & ^(SymbolFlagsFunction | SymbolFlagsClass | SymbolFlagsRegularEnum | SymbolFlagsValueModule) + SymbolFlagsNamespaceModuleExcludes = SymbolFlagsNone + SymbolFlagsMethodExcludes = SymbolFlagsValue & ^SymbolFlagsMethod + SymbolFlagsGetAccessorExcludes = SymbolFlagsValue & ^SymbolFlagsSetAccessor + SymbolFlagsSetAccessorExcludes = SymbolFlagsValue & ^SymbolFlagsGetAccessor + SymbolFlagsAccessorExcludes = SymbolFlagsValue & ^SymbolFlagsAccessor + SymbolFlagsTypeParameterExcludes = SymbolFlagsType & ^SymbolFlagsTypeParameter + SymbolFlagsTypeAliasExcludes = SymbolFlagsType + SymbolFlagsAliasExcludes = SymbolFlagsAlias + SymbolFlagsModuleMember = SymbolFlagsVariable | SymbolFlagsFunction | SymbolFlagsClass | SymbolFlagsInterface | SymbolFlagsEnum | SymbolFlagsModule | SymbolFlagsTypeAlias | SymbolFlagsAlias + SymbolFlagsExportHasLocal = SymbolFlagsFunction | SymbolFlagsClass | SymbolFlagsEnum | SymbolFlagsValueModule + SymbolFlagsBlockScoped = SymbolFlagsBlockScopedVariable | SymbolFlagsClass | SymbolFlagsEnum + SymbolFlagsPropertyOrAccessor = SymbolFlagsProperty | SymbolFlagsAccessor + SymbolFlagsClassMember = SymbolFlagsMethod | SymbolFlagsAccessor | SymbolFlagsProperty + SymbolFlagsExportSupportsDefaultModifier = SymbolFlagsClass | SymbolFlagsFunction | SymbolFlagsInterface + SymbolFlagsExportDoesNotSupportDefaultModifier = ^SymbolFlagsExportSupportsDefaultModifier + // The set of things we consider semantically classifiable. Used to speed up the LS during + // classification. + SymbolFlagsClassifiable = SymbolFlagsClass | SymbolFlagsEnum | SymbolFlagsTypeAlias | SymbolFlagsInterface | SymbolFlagsTypeParameter | SymbolFlagsModule | SymbolFlagsAlias + SymbolFlagsLateBindingContainer = SymbolFlagsClass | SymbolFlagsInterface | SymbolFlagsTypeLiteral | SymbolFlagsObjectLiteral | SymbolFlagsFunction +) + +// CheckFlags + +type CheckFlags = uint32 + +const ( + CheckFlagsNone CheckFlags = 0 + CheckFlagsInstantiated CheckFlags = 1 << 0 // Instantiated symbol + CheckFlagsSyntheticProperty CheckFlags = 1 << 1 // Property in union or intersection type + CheckFlagsSyntheticMethod CheckFlags = 1 << 2 // Method in union or intersection type + CheckFlagsReadonly CheckFlags = 1 << 3 // Readonly transient symbol + CheckFlagsReadPartial CheckFlags = 1 << 4 // Synthetic property present in some but not all constituents + CheckFlagsWritePartial CheckFlags = 1 << 5 // Synthetic property present in some but only satisfied by an index signature in others + CheckFlagsHasNonUniformType CheckFlags = 1 << 6 // Synthetic property with non-uniform type in constituents + CheckFlagsHasLiteralType CheckFlags = 1 << 7 // Synthetic property with at least one literal type in constituents + CheckFlagsContainsPublic CheckFlags = 1 << 8 // Synthetic property with public constituent(s) + CheckFlagsContainsProtected CheckFlags = 1 << 9 // Synthetic property with protected constituent(s) + CheckFlagsContainsPrivate CheckFlags = 1 << 10 // Synthetic property with private constituent(s) + CheckFlagsContainsStatic CheckFlags = 1 << 11 // Synthetic property with static constituent(s) + CheckFlagsLate CheckFlags = 1 << 12 // Late-bound symbol for a computed property with a dynamic name + CheckFlagsReverseMapped CheckFlags = 1 << 13 // Property of reverse-inferred homomorphic mapped type + CheckFlagsOptionalParameter CheckFlags = 1 << 14 // Optional parameter + CheckFlagsRestParameter CheckFlags = 1 << 15 // Rest parameter + CheckFlagsDeferredType CheckFlags = 1 << 16 // Calculation of the type of this symbol is deferred due to processing costs, should be fetched with `getTypeOfSymbolWithDeferredType` + CheckFlagsHasNeverType CheckFlags = 1 << 17 // Synthetic property with at least one never type in constituents + CheckFlagsMapped CheckFlags = 1 << 18 // Property of mapped type + CheckFlagsStripOptional CheckFlags = 1 << 19 // Strip optionality in mapped property + CheckFlagsUnresolved CheckFlags = 1 << 20 // Unresolved type alias symbol + CheckFlagsSynthetic = CheckFlagsSyntheticProperty | CheckFlagsSyntheticMethod + CheckFlagsDiscriminant = CheckFlagsHasNonUniformType | CheckFlagsHasLiteralType + CheckFlagsPartial = CheckFlagsReadPartial | CheckFlagsWritePartial +) + +type SignatureKind int32 + +const ( + SignatureKindCall SignatureKind = iota + SignatureKindConstruct +) + +type ScriptKind int32 + +const ( + ScriptKindUnknown ScriptKind = iota + ScriptKindJS + ScriptKindJSX + ScriptKindTS + ScriptKindTSX + ScriptKindExternal + ScriptKindJSON + /** + * Used on extensions that doesn't define the ScriptKind but the content defines it. + * Deferred extensions are going to be included in all project contexts. + */ + ScriptKindDeferred +) + +type ScriptTarget int32 + +const ( + ScriptTargetNone ScriptTarget = 0 + ScriptTargetES3 ScriptTarget = 0 // Deprecated + ScriptTargetES5 ScriptTarget = 1 + ScriptTargetES2015 ScriptTarget = 2 + ScriptTargetES2016 ScriptTarget = 3 + ScriptTargetES2017 ScriptTarget = 4 + ScriptTargetES2018 ScriptTarget = 5 + ScriptTargetES2019 ScriptTarget = 6 + ScriptTargetES2020 ScriptTarget = 7 + ScriptTargetES2021 ScriptTarget = 8 + ScriptTargetES2022 ScriptTarget = 9 + ScriptTargetES2023 ScriptTarget = 10 + ScriptTargetESNext ScriptTarget = 99 + ScriptTargetJSON ScriptTarget = 100 + ScriptTargetLatest ScriptTarget = ScriptTargetESNext +) + +type LanguageVariant int32 + +const ( + LanguageVariantStandard LanguageVariant = iota + LanguageVariantJSX +) + +// Ids + +type NodeId uint32 +type SymbolId uint32 +type MergeId uint32 +type TypeId uint32 + +// Symbol + +type Symbol struct { + flags SymbolFlags + checkFlags CheckFlags // Non-zero only in transient symbols + constEnumOnlyModule bool // True if module contains only const enums or other modules with only const enums + isReplaceableByMethod bool + name string + declarations []*Node + valueDeclaration *Node + members SymbolTable + exports SymbolTable + id SymbolId + mergeId MergeId // Assigned once symbol is merged somewhere + parent *Symbol + exportSymbol *Symbol + assignmentDeclarationMembers map[NodeId]*Node // Set of detected assignment declarations + globalExports SymbolTable // Conditional global UMD exports +} + +// SymbolTable + +type SymbolTable map[string]*Symbol + +// Links for value symbols + +type ValueSymbolLinks struct { + resolvedType *Type // Type of value symbol +} + +// Links for alias symbols + +type AliasSymbolLinks struct { + immediateTarget *Symbol // Immediate target of an alias. May be another alias. Do not access directly, use `checker.getImmediateAliasedSymbol` instead. + aliasTarget *Symbol // Resolved (non-alias) target of an alias + typeOnlyDeclarationResolved bool // True when typeOnlyDeclaration resolution in process + typeOnlyDeclaration *Node // First resolved alias declaration that makes the symbol only usable in type constructs + typeOnlyExportStarName string // Set to the name of the symbol re-exported by an 'export type *' declaration, when different from the symbol name +} + +// Links for module symbols + +type ModuleSymbolLinks struct { + resolvedExports SymbolTable // Resolved exports of module or combined early- and late-bound static members of a class. + cjsExportMerged *Symbol // Version of the symbol with all non export= exports merged with the export= target + typeOnlyExportStarMap map[string]*Node // Set on a module symbol when some of its exports were resolved through a 'export type * from "mod"' declaration +} + +// Links for export type symbols + +type ExportTypeLinks struct { + target *Symbol // Target symbol + originatingImport *Node // Import declaration which produced the symbol, present if the symbol is marked as uncallable but had call signatures in `resolveESModuleSymbol` +} + +// Links for late-binding containers + +type MembersOrExportsResolutionKind int + +const ( + MembersOrExportsResolutionKindResolvedExports MembersOrExportsResolutionKind = 0 + MembersOrExportsResolutionKindresolvedMembers MembersOrExportsResolutionKind = 1 +) + +type MembersAndExportsLinks [2]SymbolTable // Indexed by MembersOrExportsResolutionKind + +// Links for type parameters + +type TypeParameterLinks struct { + declaredType *Type +} + +// Links for interface types + +type InterfaceTypeLinks struct { + declaredType *Type +} + +// FlowFlags + +type FlowFlags uint32 + +const ( + FlowFlagsUnreachable FlowFlags = 1 << 0 // Unreachable code + FlowFlagsStart FlowFlags = 1 << 1 // Start of flow graph + FlowFlagsBranchLabel FlowFlags = 1 << 2 // Non-looping junction + FlowFlagsLoopLabel FlowFlags = 1 << 3 // Looping junction + FlowFlagsAssignment FlowFlags = 1 << 4 // Assignment + FlowFlagsTrueCondition FlowFlags = 1 << 5 // Condition known to be true + FlowFlagsFalseCondition FlowFlags = 1 << 6 // Condition known to be false + FlowFlagsSwitchClause FlowFlags = 1 << 7 // Switch statement clause + FlowFlagsArrayMutation FlowFlags = 1 << 8 // Potential array mutation + FlowFlagsCall FlowFlags = 1 << 9 // Potential assertion call + FlowFlagsReduceLabel FlowFlags = 1 << 10 // Temporarily reduce antecedents of label + FlowFlagsReferenced FlowFlags = 1 << 11 // Referenced as antecedent once + FlowFlagsShared FlowFlags = 1 << 12 // Referenced as antecedent more than once + FlowFlagsLabel = FlowFlagsBranchLabel | FlowFlagsLoopLabel + FlowFlagsCondition = FlowFlagsTrueCondition | FlowFlagsFalseCondition +) + +// FlowNode + +type FlowNode struct { + flags FlowFlags + node any + antecedent *FlowNode // Antecedent for all but FlowLabel + antecedents *FlowList // Linked list of antecedents for FlowLabel +} + +type FlowList struct { + node *FlowNode + next *FlowList +} + +type FlowLabel = FlowNode + +var unreachableFlow = &FlowNode{flags: FlowFlagsUnreachable} +var reportedUnreachableFlow = &FlowNode{flags: FlowFlagsUnreachable} + +// FlowSwitchClauseData + +type FlowSwitchClauseData struct { + switchStatement *SwitchStatement + clauseStart int32 // Start index of case/default clause range + clauseEnd int32 // End index of case/default clause range +} + +// FlowReduceLabelData + +type FlowReduceLabelData struct { + target *FlowLabel // Target label + antecedents *FlowList // Temporary antecedent list +} + +// Tristate + +type Tristate byte + +const ( + TSUnknown Tristate = iota + TSFalse + TSTrue +) + +type VarianceFlags uint32 + +const ( + VarianceFlagsInvariant VarianceFlags = 0 // Neither covariant nor contravariant + VarianceFlagsCovariant VarianceFlags = 1 << 0 // Covariant + VarianceFlagsContravariant VarianceFlags = 1 << 1 // Contravariant + VarianceFlagsBivariant VarianceFlags = VarianceFlagsCovariant | VarianceFlagsContravariant // Both covariant and contravariant + VarianceFlagsIndependent VarianceFlags = 1 << 2 // Unwitnessed type parameter + VarianceFlagsVarianceMask VarianceFlags = VarianceFlagsInvariant | VarianceFlagsCovariant | VarianceFlagsContravariant | VarianceFlagsIndependent // Mask containing all measured variances without the unmeasurable flag + VarianceFlagsUnmeasurable VarianceFlags = 1 << 3 // Variance result is unusable - relationship relies on structural comparisons which are not reflected in generic relationships + VarianceFlagsUnreliable VarianceFlags = 1 << 4 // Variance result is unreliable - checking may produce false negatives, but not false positives + VarianceFlagsAllowsStructuralFallback = VarianceFlagsUnmeasurable | VarianceFlagsUnreliable +) + +type AccessFlags uint32 + +type AssignmentDeclarationKind = int32 + +const ( + AssignmentDeclarationKindNone = AssignmentDeclarationKind(iota) + /// exports.name = expr + /// module.exports.name = expr + AssignmentDeclarationKindExportsProperty + /// module.exports = expr + AssignmentDeclarationKindModuleExports + /// className.prototype.name = expr + AssignmentDeclarationKindPrototypeProperty + /// this.name = expr + AssignmentDeclarationKindThisProperty + // F.name = expr + AssignmentDeclarationKindProperty + // F.prototype = { ... } + AssignmentDeclarationKindPrototype + // Object.defineProperty(x, 'name', { value: any, writable?: boolean (false by default) }); + // Object.defineProperty(x, 'name', { get: Function, set: Function }); + // Object.defineProperty(x, 'name', { get: Function }); + // Object.defineProperty(x, 'name', { set: Function }); + AssignmentDeclarationKindObjectDefinePropertyValue + // Object.defineProperty(exports || module.exports, 'name', ...); + AssignmentDeclarationKindObjectDefinePropertyExports + // Object.defineProperty(Foo.prototype, 'name', ...); + AssignmentDeclarationKindObjectDefinePrototypeProperty +) + +const InternalSymbolNamePrefix = "\xFE" // Invalid UTF8 sequence, will never occur as IdentifierName + +const ( + InternalSymbolNameCall = InternalSymbolNamePrefix + "call" // Call signatures + InternalSymbolNameConstructor = InternalSymbolNamePrefix + "constructor" // Constructor implementations + InternalSymbolNameNew = InternalSymbolNamePrefix + "new" // Constructor signatures + InternalSymbolNameIndex = InternalSymbolNamePrefix + "index" // Index signatures + InternalSymbolNameExportStar = InternalSymbolNamePrefix + "export" // Module export * declarations + InternalSymbolNameGlobal = InternalSymbolNamePrefix + "global" // Global self-reference + InternalSymbolNameMissing = InternalSymbolNamePrefix + "missing" // Indicates missing symbol + InternalSymbolNameType = InternalSymbolNamePrefix + "type" // Anonymous type literal symbol + InternalSymbolNameObject = InternalSymbolNamePrefix + "object" // Anonymous object literal declaration + InternalSymbolNameJSXAttributes = InternalSymbolNamePrefix + "jsxAttributes" // Anonymous JSX attributes object literal declaration + InternalSymbolNameClass = InternalSymbolNamePrefix + "class" // Unnamed class expression + InternalSymbolNameFunction = InternalSymbolNamePrefix + "function" // Unnamed function expression + InternalSymbolNameComputed = InternalSymbolNamePrefix + "computed" // Computed property name declaration with dynamic name + InternalSymbolNameResolving = InternalSymbolNamePrefix + "resolving" // Indicator symbol used to mark partially resolved type aliases + InternalSymbolNameExportEquals = InternalSymbolNamePrefix + "export=" // Export assignment symbol + InternalSymbolNameInstantiationExpression = InternalSymbolNamePrefix + "instantiationExpression" // Instantiation expressions + InternalSymbolNameImportAttributes = InternalSymbolNamePrefix + "importAttributes" + InternalSymbolNameDefault = "default" // Default export symbol (technically not wholly internal, but included here for usability) + InternalSymbolNameThis = "this" +) + +// CompilerOptions + +type CompilerOptions struct { + AllowSyntheticDefaultImports Tristate + AllowUmdGlobalAccess Tristate + AllowUnreachableCode Tristate + AllowUnusedLabels Tristate + CheckJs Tristate + ESModuleInterop Tristate + ExactOptionalPropertyTypes Tristate + IsolatedModules Tristate + ModuleKind ModuleKind + ModuleResolution ModuleResolutionKind + NoFallthroughCasesInSwitch Tristate + NoImplicitAny Tristate + PreserveConstEnums Tristate + Strict Tristate + StrictNullChecks Tristate + Target ScriptTarget + Types []string + UseDefineForClassFields Tristate + UseUnknownInCatchVariables Tristate + VerbatimModuleSyntax Tristate +} + +type ModuleKind int32 + +const ( + ModuleKindNone ModuleKind = 0 + ModuleKindCommonJS ModuleKind = 1 + ModuleKindAMD ModuleKind = 2 + ModuleKindUMD ModuleKind = 3 + ModuleKindSystem ModuleKind = 4 + // NOTE: ES module kinds should be contiguous to more easily check whether a module kind is *any* ES module kind. + // Non-ES module kinds should not come between ES2015 (the earliest ES module kind) and ESNext (the last ES + // module kind). + ModuleKindES2015 ModuleKind = 5 + ModuleKindES2020 ModuleKind = 6 + ModuleKindES2022 ModuleKind = 7 + ModuleKindESNext ModuleKind = 99 + // Node16+ is an amalgam of commonjs (albeit updated) and es2022+, and represents a distinct module system from es2020/esnext + ModuleKindNode16 ModuleKind = 100 + ModuleKindNodeNext ModuleKind = 199 + // Emit as written + ModuleKindPreserve ModuleKind = 200 +) + +type ResolutionMode = ModuleKind // ModuleKindNone | ModuleKindCommonJS | ModuleKindESNext + +type ModuleResolutionKind int32 + +const ( + ModuleResolutionKindUnknown ModuleResolutionKind = 0 + ModuleResolutionKindClassic ModuleResolutionKind = 1 + ModuleResolutionKindNode10 ModuleResolutionKind = 2 + // Starting with node12, node's module resolver has significant departures from traditional cjs resolution + // to better support ECMAScript modules and their use within node - however more features are still being added. + // TypeScript's Node ESM support was introduced after Node 12 went end-of-life, and Node 14 is the earliest stable + // version that supports both pattern trailers - *but*, Node 16 is the first version that also supports ECMAScript 2022. + // In turn, we offer both a `NodeNext` moving resolution target, and a `Node16` version-anchored resolution target + ModuleResolutionKindNode16 ModuleResolutionKind = 3 + ModuleResolutionKindNodeNext ModuleResolutionKind = 99 // Not simply `Node16` so that compiled code linked against TS can use the `Next` value reliably (same as with `ModuleKind`) + ModuleResolutionKindBundler ModuleResolutionKind = 100 +) + +type NodeCheckFlags uint32 + +const ( + NodeCheckFlagsNone NodeCheckFlags = 0 + NodeCheckFlagsTypeChecked NodeCheckFlags = 1 << 0 // Node has been type checked + NodeCheckFlagsLexicalThis NodeCheckFlags = 1 << 1 // Lexical 'this' reference + NodeCheckFlagsCaptureThis NodeCheckFlags = 1 << 2 // Lexical 'this' used in body + NodeCheckFlagsCaptureNewTarget NodeCheckFlags = 1 << 3 // Lexical 'new.target' used in body + NodeCheckFlagsSuperInstance NodeCheckFlags = 1 << 4 // Instance 'super' reference + NodeCheckFlagsSuperStatic NodeCheckFlags = 1 << 5 // Static 'super' reference + NodeCheckFlagsContextChecked NodeCheckFlags = 1 << 6 // Contextual types have been assigned + NodeCheckFlagsMethodWithSuperPropertyAccessInAsync NodeCheckFlags = 1 << 7 // A method that contains a SuperProperty access in an async context. + NodeCheckFlagsMethodWithSuperPropertyAssignmentInAsync NodeCheckFlags = 1 << 8 // A method that contains a SuperProperty assignment in an async context. + NodeCheckFlagsCaptureArguments NodeCheckFlags = 1 << 9 // Lexical 'arguments' used in body + NodeCheckFlagsEnumValuesComputed NodeCheckFlags = 1 << 10 // Values for enum members have been computed, and any errors have been reported for them. + NodeCheckFlagsLexicalModuleMergesWithClass NodeCheckFlags = 1 << 11 // Instantiated lexical module declaration is merged with a previous class declaration. + NodeCheckFlagsLoopWithCapturedBlockScopedBinding NodeCheckFlags = 1 << 12 // Loop that contains block scoped variable captured in closure + NodeCheckFlagsContainsCapturedBlockScopeBinding NodeCheckFlags = 1 << 13 // Part of a loop that contains block scoped variable captured in closure + NodeCheckFlagsCapturedBlockScopedBinding NodeCheckFlags = 1 << 14 // Block-scoped binding that is captured in some function + NodeCheckFlagsBlockScopedBindingInLoop NodeCheckFlags = 1 << 15 // Block-scoped binding with declaration nested inside iteration statement + NodeCheckFlagsNeedsLoopOutParameter NodeCheckFlags = 1 << 16 // Block scoped binding whose value should be explicitly copied outside of the converted loop + NodeCheckFlagsAssignmentsMarked NodeCheckFlags = 1 << 17 // Parameter assignments have been marked + NodeCheckFlagsContainsConstructorReference NodeCheckFlags = 1 << 18 // Class or class element that contains a binding that references the class constructor. + NodeCheckFlagsConstructorReference NodeCheckFlags = 1 << 29 // Binding to a class constructor inside of the class's body. + NodeCheckFlagsContainsClassWithPrivateIdentifiers NodeCheckFlags = 1 << 20 // Marked on all block-scoped containers containing a class with private identifiers. + NodeCheckFlagsContainsSuperPropertyInStaticInitializer NodeCheckFlags = 1 << 21 // Marked on all block-scoped containers containing a static initializer with 'super.x' or 'super[x]'. + NodeCheckFlagsInCheckIdentifier NodeCheckFlags = 1 << 22 + NodeCheckFlagsPartiallyTypeChecked NodeCheckFlags = 1 << 23 // Node has been partially type checked +) + +// Common links + +type NodeLinks struct { + flags NodeCheckFlags // Set of flags specific to Node + declarationRequiresScopeChange Tristate + resolvedType *Type // Cached type of type node + resolvedSymbol *Symbol // Cached name resolution result +} + +// Signature specific links + +type SignatureLinks struct { + resolvedSignature *Signature + effectsSignature *Signature +} + +// jsxFlag: JsxOpeningElement | JsxClosingElement +// resolvedJsxElementAttributesType: JsxOpeningElement | JsxClosingElement +// resolvedJsxElementAllAttributesType: JsxOpeningElement | JsxClosingElement +// jsxNamespace: Jsx* +// jsxImplicitImportContainer: Jsx* + +// resolvedJSDocType: JSDoc TypeReference | ImportType + +// switchTypes: SwitchStatement + +// contectFreeType: Expression | FunctionExpression | ArrowFunction | MethodDeclaration + +// outerTypeParameters: AnonymousType | MappedType | DeferredTypeReference + +// Only on SourceFile +// deferredNodes []Node // Set of nodes whose checking has been deferred + +// resolvedSignature Signature; // Cached signature of signature node or call expression +// effectsSignature Signature; // Signature with possible control flow effects +// enumMemberValue EvaluatorResult; // Constant value of enum member +// isVisible boolean; // Is this node visible +// containsArgumentsReference boolean; // Whether a function-like declaration contains an 'arguments' reference +// hasReportedStatementInAmbientContext boolean; // Cache boolean if we report statements in ambient context +// jsxFlag JsxFlags; // flags for knowing what kind of element/attributes we're dealing with +// resolvedJsxElementAttributesType Type; // resolved element attributes type of a JSX openinglike element +// resolvedJsxElementAllAttributesType Type; // resolved all element attributes type of a JSX openinglike element +// resolvedJSDocType Type; // Resolved type of a JSDoc type reference +// switchTypes []Type; // Cached array of switch case expression types +// jsxNamespace *Symbol; // Resolved jsx namespace symbol for this node +// jsxImplicitImportContainer *Symbol; // Resolved module symbol the implicit jsx import of this file should refer to +// contextFreeType Type; // Cached context-free type used by the first pass of inference; used when a function's return is partially contextually sensitive +// deferredNodes []Node // Set of nodes whose checking has been deferred +// capturedBlockScopeBindings []*Symbol; // Block-scoped bindings captured beneath this part of an IterationStatement +// outerTypeParameters []*TypeParameter; // Outer type parameters of anonymous object type +// isExhaustive boolean; // Is node an exhaustive switch statement (0 indicates in-process resolution) +// skipDirectInference true; // Flag set by the API `getContextualType` call on a node when `Completions` is passed to force the checker to skip making inferences to a node's type +// declarationRequiresScopeChange boolean; // Set by `useOuterVariableScopeInParameter` in checker when downlevel emit would change the name resolution scope inside of a parameter. +// serializedTypes map[string]SerializedTypeEntry> // Collection of types serialized at this location +// decoratorSignature Signature; // Signature for decorator as if invoked by the runtime. +// spreadIndices { first: number | undefined, last: number | undefined }; // Indices of first and last spread elements in array literal +// parameterInitializerContainsUndefined boolean; // True if this is a parameter declaration whose type annotation contains "undefined". +// fakeScopeForSignatureDeclaration "params" | "typeParams"; // If present, this is a fake scope injected into an enclosing declaration chain. +// assertionExpressionType Type; // Cached type of the expression of a type assertion +// potentialThisCollisions Node[]; +// potentialNewTargetCollisions Node[]; +// potentialWeakMapSetCollisions Node[]; +// potentialReflectCollisions Node[]; +// potentialUnusedRenamedBindingElementsInTypes BindingElement[]; +// externalHelpersModule Symbol; // Resolved symbol for the external helpers module +// instantiationExpressionTypes Map; // Cache of instantiation expression types for the node + +type TypeFlags uint32 + +const ( + TypeFlagsNone TypeFlags = 0 + TypeFlagsAny TypeFlags = 1 << 0 + TypeFlagsUnknown TypeFlags = 1 << 1 + TypeFlagsString TypeFlags = 1 << 2 + TypeFlagsNumber TypeFlags = 1 << 3 + TypeFlagsBoolean TypeFlags = 1 << 4 + TypeFlagsEnum TypeFlags = 1 << 5 // Numeric computed enum member value + TypeFlagsBigint TypeFlags = 1 << 6 + TypeFlagsStringLiteral TypeFlags = 1 << 7 + TypeFlagsNumberLiteral TypeFlags = 1 << 8 + TypeFlagsBooleanLiteral TypeFlags = 1 << 9 + TypeFlagsEnumLiteral TypeFlags = 1 << 10 // Always combined with StringLiteral, NumberLiteral, or Union + TypeFlagsBigintLiteral TypeFlags = 1 << 11 + TypeFlagsESSymbol TypeFlags = 1 << 12 // Type of symbol primitive introduced in ES6 + TypeFlagsUniqueESSymbol TypeFlags = 1 << 13 // unique symbol + TypeFlagsVoid TypeFlags = 1 << 14 + TypeFlagsUndefined TypeFlags = 1 << 15 + TypeFlagsNull TypeFlags = 1 << 16 + TypeFlagsNever TypeFlags = 1 << 17 // Never type + TypeFlagsTypeParameter TypeFlags = 1 << 18 // Type parameter + TypeFlagsObject TypeFlags = 1 << 19 // Object type + TypeFlagsUnion TypeFlags = 1 << 20 // Union (T | U) + TypeFlagsIntersection TypeFlags = 1 << 21 // Intersection (T & U) + TypeFlagsIndex TypeFlags = 1 << 22 // keyof T + TypeFlagsIndexedAccess TypeFlags = 1 << 23 // T[K] + TypeFlagsConditional TypeFlags = 1 << 24 // T extends U ? X : Y + TypeFlagsSubstitution TypeFlags = 1 << 25 // Type parameter substitution + TypeFlagsNonPrimitive TypeFlags = 1 << 26 // intrinsic object type + TypeFlagsTemplateLiteral TypeFlags = 1 << 27 // Template literal type + TypeFlagsStringMapping TypeFlags = 1 << 28 // Uppercase/Lowercase type + TypeFlagsReserved1 TypeFlags = 1 << 29 // Used by union/intersection type construction + TypeFlagsReserved2 TypeFlags = 1 << 30 // Used by union/intersection type construction + TypeFlagsReserved3 TypeFlags = 1 << 31 + + TypeFlagsAnyOrUnknown = TypeFlagsAny | TypeFlagsUnknown + TypeFlagsNullable = TypeFlagsUndefined | TypeFlagsNull + TypeFlagsLiteral = TypeFlagsStringLiteral | TypeFlagsNumberLiteral | TypeFlagsBigintLiteral | TypeFlagsBooleanLiteral + TypeFlagsUnit = TypeFlagsEnum | TypeFlagsLiteral | TypeFlagsUniqueESSymbol | TypeFlagsNullable + TypeFlagsFreshable = TypeFlagsEnum | TypeFlagsLiteral + TypeFlagsStringOrNumberLiteral = TypeFlagsStringLiteral | TypeFlagsNumberLiteral + TypeFlagsStringOrNumberLiteralOrUnique = TypeFlagsStringLiteral | TypeFlagsNumberLiteral | TypeFlagsUniqueESSymbol + TypeFlagsDefinitelyFalsy = TypeFlagsStringLiteral | TypeFlagsNumberLiteral | TypeFlagsBigintLiteral | TypeFlagsBooleanLiteral | TypeFlagsVoid | TypeFlagsUndefined | TypeFlagsNull + TypeFlagsPossiblyFalsy = TypeFlagsDefinitelyFalsy | TypeFlagsString | TypeFlagsNumber | TypeFlagsBigint | TypeFlagsBoolean + TypeFlagsIntrinsic = TypeFlagsAny | TypeFlagsUnknown | TypeFlagsString | TypeFlagsNumber | TypeFlagsBigint | TypeFlagsESSymbol | TypeFlagsVoid | TypeFlagsUndefined | TypeFlagsNull | TypeFlagsNever | TypeFlagsNonPrimitive + TypeFlagsStringLike = TypeFlagsString | TypeFlagsStringLiteral | TypeFlagsTemplateLiteral | TypeFlagsStringMapping + TypeFlagsNumberLike = TypeFlagsNumber | TypeFlagsNumberLiteral | TypeFlagsEnum + TypeFlagsBigIntLike = TypeFlagsBigint | TypeFlagsBigintLiteral + TypeFlagsBooleanLike = TypeFlagsBoolean | TypeFlagsBooleanLiteral + TypeFlagsEnumLike = TypeFlagsEnum | TypeFlagsEnumLiteral + TypeFlagsESSymbolLike = TypeFlagsESSymbol | TypeFlagsUniqueESSymbol + TypeFlagsVoidLike = TypeFlagsVoid | TypeFlagsUndefined + TypeFlagsPrimitive = TypeFlagsStringLike | TypeFlagsNumberLike | TypeFlagsBigIntLike | TypeFlagsBooleanLike | TypeFlagsEnumLike | TypeFlagsESSymbolLike | TypeFlagsVoidLike | TypeFlagsNull + TypeFlagsDefinitelyNonNullable = TypeFlagsStringLike | TypeFlagsNumberLike | TypeFlagsBigIntLike | TypeFlagsBooleanLike | TypeFlagsEnumLike | TypeFlagsESSymbolLike | TypeFlagsObject | TypeFlagsNonPrimitive + TypeFlagsDisjointDomains = TypeFlagsNonPrimitive | TypeFlagsStringLike | TypeFlagsNumberLike | TypeFlagsBigIntLike | TypeFlagsBooleanLike | TypeFlagsESSymbolLike | TypeFlagsVoidLike | TypeFlagsNull + TypeFlagsUnionOrIntersection = TypeFlagsUnion | TypeFlagsIntersection + TypeFlagsStructuredType = TypeFlagsObject | TypeFlagsUnion | TypeFlagsIntersection + TypeFlagsTypeVariable = TypeFlagsTypeParameter | TypeFlagsIndexedAccess + TypeFlagsInstantiableNonPrimitive = TypeFlagsTypeVariable | TypeFlagsConditional | TypeFlagsSubstitution + TypeFlagsInstantiablePrimitive = TypeFlagsIndex | TypeFlagsTemplateLiteral | TypeFlagsStringMapping + TypeFlagsInstantiable = TypeFlagsInstantiableNonPrimitive | TypeFlagsInstantiablePrimitive + TypeFlagsStructuredOrInstantiable = TypeFlagsStructuredType | TypeFlagsInstantiable + TypeFlagsObjectFlagsType = TypeFlagsAny | TypeFlagsNullable | TypeFlagsNever | TypeFlagsObject | TypeFlagsUnion | TypeFlagsIntersection + TypeFlagsSimplifiable = TypeFlagsIndexedAccess | TypeFlagsConditional + TypeFlagsSingleton = TypeFlagsAny | TypeFlagsUnknown | TypeFlagsString | TypeFlagsNumber | TypeFlagsBoolean | TypeFlagsBigint | TypeFlagsESSymbol | TypeFlagsVoid | TypeFlagsUndefined | TypeFlagsNull | TypeFlagsNever | TypeFlagsNonPrimitive + // 'TypeFlagsNarrowable' types are types where narrowing actually narrows. + // This *should* be every type other than null, undefined, void, and never + TypeFlagsNarrowable = TypeFlagsAny | TypeFlagsUnknown | TypeFlagsStructuredOrInstantiable | TypeFlagsStringLike | TypeFlagsNumberLike | TypeFlagsBigIntLike | TypeFlagsBooleanLike | TypeFlagsESSymbol | TypeFlagsUniqueESSymbol | TypeFlagsNonPrimitive + // The following flags are aggregated during union and intersection type construction + TypeFlagsIncludesMask = TypeFlagsAny | TypeFlagsUnknown | TypeFlagsPrimitive | TypeFlagsNever | TypeFlagsObject | TypeFlagsUnion | TypeFlagsIntersection | TypeFlagsNonPrimitive | TypeFlagsTemplateLiteral | TypeFlagsStringMapping + // The following flags are used for different purposes during union and intersection type construction + TypeFlagsIncludesMissingType = TypeFlagsTypeParameter + TypeFlagsIncludesNonWideningType = TypeFlagsIndex + TypeFlagsIncludesWildcard = TypeFlagsIndexedAccess + TypeFlagsIncludesEmptyObject = TypeFlagsConditional + TypeFlagsIncludesInstantiable = TypeFlagsSubstitution + TypeFlagsIncludesConstrainedTypeVariable = TypeFlagsReserved1 + TypeFlagsIncludesError = TypeFlagsReserved2 + TypeFlagsNotPrimitiveUnion = TypeFlagsAny | TypeFlagsUnknown | TypeFlagsVoid | TypeFlagsNever | TypeFlagsObject | TypeFlagsIntersection | TypeFlagsIncludesInstantiable +) + +type ObjectFlags uint32 + +// Types included in TypeFlags.ObjectFlagsType have an objectFlags property. Some ObjectFlags +// are specific to certain types and reuse the same bit position. Those ObjectFlags require a check +// for a certain TypeFlags value to determine their meaning. +// dprint-ignore +const ( + ObjectFlagsNone ObjectFlags = 0 + ObjectFlagsClass ObjectFlags = 1 << 0 // Class + ObjectFlagsInterface ObjectFlags = 1 << 1 // Interface + ObjectFlagsReference ObjectFlags = 1 << 2 // Generic type reference + ObjectFlagsTuple ObjectFlags = 1 << 3 // Synthesized generic tuple type + ObjectFlagsAnonymous ObjectFlags = 1 << 4 // Anonymous + ObjectFlagsMapped ObjectFlags = 1 << 5 // Mapped + ObjectFlagsInstantiated ObjectFlags = 1 << 6 // Instantiated anonymous or mapped type + ObjectFlagsObjectLiteral ObjectFlags = 1 << 7 // Originates in an object literal + ObjectFlagsEvolvingArray ObjectFlags = 1 << 8 // Evolving array type + ObjectFlagsObjectLiteralPatternWithComputedProperties ObjectFlags = 1 << 9 // Object literal pattern with computed properties + ObjectFlagsReverseMapped ObjectFlags = 1 << 10 // Object contains a property from a reverse-mapped type + ObjectFlagsJsxAttributes ObjectFlags = 1 << 11 // Jsx attributes type + ObjectFlagsJSLiteral ObjectFlags = 1 << 12 // Object type declared in JS - disables errors on read/write of nonexisting members + ObjectFlagsFreshLiteral ObjectFlags = 1 << 13 // Fresh object literal + ObjectFlagsArrayLiteral ObjectFlags = 1 << 14 // Originates in an array literal + ObjectFlagsPrimitiveUnion ObjectFlags = 1 << 15 // Union of only primitive types + ObjectFlagsContainsWideningType ObjectFlags = 1 << 16 // Type is or contains undefined or null widening type + ObjectFlagsContainsObjectOrArrayLiteral ObjectFlags = 1 << 17 // Type is or contains object literal type + ObjectFlagsNonInferrableType ObjectFlags = 1 << 18 // Type is or contains anyFunctionType or silentNeverType + ObjectFlagsCouldContainTypeVariablesComputed ObjectFlags = 1 << 19 // CouldContainTypeVariables flag has been computed + ObjectFlagsCouldContainTypeVariables ObjectFlags = 1 << 20 // Type could contain a type variable + ObjectFlagsMembersResolved ObjectFlags = 1 << 21 // Members have been resolved + + ObjectFlagsClassOrInterface = ObjectFlagsClass | ObjectFlagsInterface + ObjectFlagsRequiresWidening = ObjectFlagsContainsWideningType | ObjectFlagsContainsObjectOrArrayLiteral + ObjectFlagsPropagatingFlags = ObjectFlagsContainsWideningType | ObjectFlagsContainsObjectOrArrayLiteral | ObjectFlagsNonInferrableType + ObjectFlagsInstantiatedMapped = ObjectFlagsMapped | ObjectFlagsInstantiated + // Object flags that uniquely identify the kind of ObjectType + ObjectFlagsObjectTypeKindMask = ObjectFlagsClassOrInterface | ObjectFlagsReference | ObjectFlagsTuple | ObjectFlagsAnonymous | ObjectFlagsMapped | ObjectFlagsReverseMapped | ObjectFlagsEvolvingArray + // Flags that require TypeFlags.Object + ObjectFlagsContainsSpread = 1 << 22 // Object literal contains spread operation + ObjectFlagsObjectRestType = 1 << 23 // Originates in object rest declaration + ObjectFlagsInstantiationExpressionType = 1 << 24 // Originates in instantiation expression + ObjectFlagsSingleSignatureType = 1 << 25 // A single signature type extracted from a potentially broader type + ObjectFlagsIsClassInstanceClone = 1 << 26 // Type is a clone of a class instance type + // Flags that require TypeFlags.Object and ObjectFlags.Reference + ObjectFlagsIdenticalBaseTypeCalculated = 1 << 27 // has had `getSingleBaseForNonAugmentingSubtype` invoked on it already + ObjectFlagsIdenticalBaseTypeExists = 1 << 28 // has a defined cachedEquivalentBaseType member + // Flags that require TypeFlags.UnionOrIntersection or TypeFlags.Substitution + ObjectFlagsIsGenericTypeComputed = 1 << 22 // IsGenericObjectType flag has been computed + ObjectFlagsIsGenericObjectType = 1 << 23 // Union or intersection contains generic object type + ObjectFlagsIsGenericIndexType = 1 << 24 // Union or intersection contains generic index type + ObjectFlagsIsGenericType = ObjectFlagsIsGenericObjectType | ObjectFlagsIsGenericIndexType + // Flags that require TypeFlags.Union + ObjectFlagsContainsIntersections = 1 << 25 // Union contains intersections + ObjectFlagsIsUnknownLikeUnionComputed = 1 << 26 // IsUnknownLikeUnion flag has been computed + ObjectFlagsIsUnknownLikeUnion = 1 << 27 // Union of null, undefined, and empty object type + // Flags that require TypeFlags.Intersection + ObjectFlagsIsNeverIntersectionComputed = 1 << 25 // IsNeverLike flag has been computed + ObjectFlagsIsNeverIntersection = 1 << 26 // Intersection reduces to never + ObjectFlagsIsConstrainedTypeVariable = 1 << 27 // T & C, where T's constraint and C are primitives, object, or {} +) + +// TypeAlias + +type TypeAlias struct { + symbol *Symbol + typeArguments []*Type +} + +// Type + +type Type struct { + flags TypeFlags + objectFlags ObjectFlags + id TypeId + symbol *Symbol + alias *TypeAlias + data TypeData // Type specific data +} + +// Casts for concrete struct types + +func (t *Type) IntrinsicType() *IntrinsicTypeData { return t.data.(*IntrinsicTypeData) } +func (t *Type) LiteralType() *LiteralTypeData { return t.data.(*LiteralTypeData) } +func (t *Type) UniqueESSymbolType() *UniqueESSymbolTypeData { return t.data.(*UniqueESSymbolTypeData) } +func (t *Type) InterfaceType() *InterfaceTypeData { return t.data.(*InterfaceTypeData) } +func (t *Type) TypeReference() *TypeReferenceData { return t.data.(*TypeReferenceData) } +func (t *Type) AnonymousType() *AnonymousTypeData { return t.data.(*AnonymousTypeData) } +func (t *Type) TypeParameter() *TypeParameterData { return t.data.(*TypeParameterData) } +func (t *Type) UnionType() *UnionTypeData { return t.data.(*UnionTypeData) } +func (t *Type) IntersectionType() *IntersectionTypeData { return t.data.(*IntersectionTypeData) } +func (t *Type) IndexedAccessType() *IndexedAccessTypeData { return t.data.(*IndexedAccessTypeData) } + +// Casts for embedded struct types + +func (t *Type) ObjectType() *ObjectTypeBase { return t.data.ObjectType() } +func (t *Type) ParameterizedType() *ParameterizedTypeBase { return t.data.ParameterizedType() } + +// TypeData + +type TypeData interface { + ObjectType() *ObjectTypeBase + ParameterizedType() *ParameterizedTypeBase +} + +// TypeBase + +type TypeBase struct{} + +func (data *TypeBase) ObjectType() *ObjectTypeBase { return nil } +func (data *TypeBase) ParameterizedType() *ParameterizedTypeBase { return nil } + +// IntrinsicTypeData + +type IntrinsicTypeData struct { + TypeBase + intrinsicName string +} + +// LiteralTypeData + +type LiteralTypeData struct { + TypeBase + value any // string | float64 | bool | PseudoBigInt | nil (computed enum) + freshType *Type // Fresh version of type + regularType *Type // Regular version of type +} + +type PseudoBigint struct { + negative bool + base10Value string +} + +// UniqueESSymbolTypeData + +type UniqueESSymbolTypeData struct { + TypeBase + name string +} + +// ObjectTypeBase + +type ObjectTypeBase struct { + TypeBase + members SymbolTable + properties []*Symbol + callSignatures []*Signature + constructSignatures []*Signature + indexInfos []*IndexInfo +} + +func (data *ObjectTypeBase) ObjectType() *ObjectTypeBase { return data } + +// InstantiatedTypeData + +type InstantiatedTypeBase struct { + ObjectTypeBase + target *Type // Target of instantiated type + mapper TypeMapper // Type mapper for instantiated type + instantiations map[string]*Type // Map of type instantiations +} + +// ParameterizedTypeData + +type ParameterizedTypeBase struct { + InstantiatedTypeBase + resolvedTypeArguments []*Type +} + +func (data *ParameterizedTypeBase) ParameterizedType() *ParameterizedTypeBase { return data } + +// InterfaceType (when generic, serves as reference to instantiation of itself) + +type InterfaceTypeData struct { + ParameterizedTypeBase + typeParameters []*Type // Type parameters + outerTypeParameters []*Type // Outer type parameters + localTypeParameters []*Type // Local type parameters + thisType *Type // The "this" type (nil if none) + variances []VarianceFlags + baseTypesResolved bool + declaredMembersResolved bool + resolvedBaseConstructorType *Type + resolvedBaseTypes []*Type + declaredCallSignatures []*Signature // Declared call signatures + declaredConstructSignatures []*Signature // Declared construct signatures + declaredIndexInfos []*IndexInfo // Declared index signatures + tupleInfo *TupleInfo // Additional data for tuple types +} + +type TupleInfo struct { +} + +// TypeReference (instantiation of a generic type) + +type TypeReferenceData struct { + ParameterizedTypeBase + node *Node // TypeReferenceNode | ArrayTypeNode | TupleTypeNode | nil +} + +// AnonymousType + +type AnonymousTypeData struct { + InstantiatedTypeBase +} + +// MappedType + +type MappedTypeData struct { + InstantiatedTypeBase + declaration MappedTypeNode + typeParameter *Type + constraintType *Type + nameType *Type + templateType *Type + modifiersType *Type + resolvedApparentType *Type + containsError bool +} + +// ReverseMappedType + +type ReverseMappedTypeData struct { + ObjectTypeBase + source *Type + mappedType *Type + constraintType *Type +} + +// UnionOrIntersectionTypeData + +type UnionOrIntersectionTypeBase struct { + ObjectTypeBase + types []*Type + propertyCache SymbolTable + propertyCacheWithoutFunctionPropertyAugment SymbolTable + resolvedProperties []*Symbol + resolvedBaseConstraint *Type +} + +// UnionType + +type UnionTypeData struct { + UnionOrIntersectionTypeBase + resolvedReducedType *Type + regularType *Type + origin *Type // Denormalized union, intersection, or index type in which union originates + keyPropertyName string // Property with unique unit type that exists in every object/intersection in union type + constituentMap map[TypeId]*Type // Constituents keyed by unit type discriminants +} + +// IntersectionType + +type IntersectionTypeData struct { + UnionOrIntersectionTypeBase + resolvedApparentType *Type +} + +// TypeParameter + +type TypeParameterData struct { + TypeBase + constraint *Type + target *Type + mapper TypeMapper + isThisType bool + resolvedDefaultType *Type +} + +// IndexedAccessType + +type IndexedAccessTypeData struct { + TypeBase + objectType *Type + indexType *Type + accessFlags AccessFlags // Only includes AccessFlags.Persistent +} + +// TypeMapper + +type TypeMapper interface { + Map(t *Type) *Type +} + +// Signature + +type Signature struct { + // !!! +} + +// IndexInfo + +type IndexInfo struct { + keyType *Type + valueType *Type + isReadonly bool + declaration *IndexSignatureDeclaration +} diff --git a/internal/compiler/utilities.go b/internal/compiler/utilities.go new file mode 100644 index 0000000000..5342c06b3d --- /dev/null +++ b/internal/compiler/utilities.go @@ -0,0 +1,3675 @@ +package compiler + +import ( + "fmt" + "maps" + "path/filepath" + "regexp" + "slices" + "strconv" + "strings" + "sync" + "sync/atomic" + + "github.com/microsoft/typescript-go/internal/compiler/diagnostics" +) + +// TextPos + +type TextPos int32 + +// TextRange + +type TextRange struct { + pos TextPos + end TextPos +} + +func NewTextRange(pos int, end int) TextRange { + return TextRange{pos: TextPos(pos), end: TextPos(end)} +} + +func (t TextRange) Pos() int { + return int(t.pos) +} + +func (t TextRange) End() int { + return int(t.end) +} + +func (t TextRange) Len() int { + return int(t.end - t.pos) +} + +func (t TextRange) ContainsInclusive(pos int) bool { + return pos >= int(t.pos) && pos <= int(t.end) +} + +// Pool allocator + +type Pool[T any] struct { + data []T +} + +func (p *Pool[T]) New() *T { + if len(p.data) == cap(p.data) { + p.data = make([]T, 0, nextPoolSize(len(p.data))) + } + index := len(p.data) + p.data = p.data[:index+1] + return &p.data[index] +} + +// Links store + +type LinkStore[K comparable, V any] struct { + entries map[K]*V + pool Pool[V] +} + +func (s *LinkStore[K, V]) get(key K) *V { + value := s.entries[key] + if value != nil { + return value + } + if s.entries == nil { + s.entries = make(map[K]*V) + } + value = s.pool.New() + s.entries[key] = value + return value +} + +// Atomic ids + +var nextNodeId atomic.Uint32 +var nextSymbolId atomic.Uint32 +var nextMergeId atomic.Uint32 + +func getNodeId(node *Node) NodeId { + if node.id == 0 { + node.id = NodeId(nextNodeId.Add(1)) + } + return node.id +} + +func getSymbolId(symbol *Symbol) SymbolId { + if symbol.id == 0 { + symbol.id = SymbolId(nextSymbolId.Add(1)) + } + return symbol.id +} + +func getMergeId(symbol *Symbol) MergeId { + if symbol.mergeId == 0 { + symbol.mergeId = MergeId(nextMergeId.Add(1)) + } + return symbol.mergeId +} + +// Diagnostic + +type Diagnostic struct { + file *SourceFile + loc TextRange + code int32 + category diagnostics.Category + message string + messageChain []*MessageChain + relatedInformation []*Diagnostic +} + +type MessageChain struct { + code int32 + category diagnostics.Category + message string + messageChain []*MessageChain +} + +func (d *Diagnostic) File() *SourceFile { return d.file } +func (d *Diagnostic) Loc() TextRange { return d.loc } +func (d *Diagnostic) Code() int32 { return d.code } +func (d *Diagnostic) Category() diagnostics.Category { return d.category } +func (d *Diagnostic) Message() string { return d.message } +func (d *Diagnostic) RelatedInformation() []*Diagnostic { return d.relatedInformation } + +func (d *Diagnostic) SetCategory(category diagnostics.Category) { d.category = category } + +func NewDiagnostic(file *SourceFile, loc TextRange, message *diagnostics.Message, args ...any) *Diagnostic { + text := message.Message() + if len(args) != 0 { + text = formatStringFromArgs(text, args) + } + return &Diagnostic{ + file: file, + loc: loc, + code: message.Code(), + category: message.Category(), + message: text, + } +} + +func NewDiagnosticForNode(node *Node, message *diagnostics.Message, args ...any) *Diagnostic { + var file *SourceFile + var loc TextRange + if node != nil { + file = getSourceFileOfNode(node) + loc = node.loc + } + return NewDiagnostic(file, loc, message, args...) +} + +func addRelatedInfo(diagnostic *Diagnostic, relatedInformation ...*Diagnostic) *Diagnostic { + diagnostic.relatedInformation = append(diagnostic.relatedInformation, relatedInformation...) + return diagnostic +} + +type OperatorPrecedence int + +const ( + // Expression: + // AssignmentExpression + // Expression `,` AssignmentExpression + OperatorPrecedenceComma OperatorPrecedence = iota + // NOTE: `Spread` is higher than `Comma` due to how it is parsed in |ElementList| + // SpreadElement: + // `...` AssignmentExpression + OperatorPrecedenceSpread + // AssignmentExpression: + // ConditionalExpression + // YieldExpression + // ArrowFunction + // AsyncArrowFunction + // LeftHandSideExpression `=` AssignmentExpression + // LeftHandSideExpression AssignmentOperator AssignmentExpression + // + // NOTE: AssignmentExpression is broken down into several precedences due to the requirements + // of the parenthesizer rules. + // AssignmentExpression: YieldExpression + // YieldExpression: + // `yield` + // `yield` AssignmentExpression + // `yield` `*` AssignmentExpression + OperatorPrecedenceYield + // AssignmentExpression: LeftHandSideExpression `=` AssignmentExpression + // AssignmentExpression: LeftHandSideExpression AssignmentOperator AssignmentExpression + // AssignmentOperator: one of + // `*=` `/=` `%=` `+=` `-=` `<<=` `>>=` `>>>=` `&=` `^=` `|=` `**=` + OperatorPrecedenceAssignment + // NOTE: `Conditional` is considered higher than `Assignment` here, but in reality they have + // the same precedence. + // AssignmentExpression: ConditionalExpression + // ConditionalExpression: + // ShortCircuitExpression + // ShortCircuitExpression `?` AssignmentExpression `:` AssignmentExpression + // ShortCircuitExpression: + // LogicalORExpression + // CoalesceExpression + OperatorPrecedenceConditional + // LogicalORExpression: + // LogicalANDExpression + // LogicalORExpression `||` LogicalANDExpression + OperatorPrecedenceLogicalOR + // LogicalANDExpression: + // BitwiseORExpression + // LogicalANDExprerssion `&&` BitwiseORExpression + OperatorPrecedenceLogicalAND + // BitwiseORExpression: + // BitwiseXORExpression + // BitwiseORExpression `^` BitwiseXORExpression + OperatorPrecedenceBitwiseOR + // BitwiseXORExpression: + // BitwiseANDExpression + // BitwiseXORExpression `^` BitwiseANDExpression + OperatorPrecedenceBitwiseXOR + // BitwiseANDExpression: + // EqualityExpression + // BitwiseANDExpression `^` EqualityExpression + OperatorPrecedenceBitwiseAND + // EqualityExpression: + // RelationalExpression + // EqualityExpression `==` RelationalExpression + // EqualityExpression `!=` RelationalExpression + // EqualityExpression `===` RelationalExpression + // EqualityExpression `!==` RelationalExpression + OperatorPrecedenceEquality + // RelationalExpression: + // ShiftExpression + // RelationalExpression `<` ShiftExpression + // RelationalExpression `>` ShiftExpression + // RelationalExpression `<=` ShiftExpression + // RelationalExpression `>=` ShiftExpression + // RelationalExpression `instanceof` ShiftExpression + // RelationalExpression `in` ShiftExpression + // [+TypeScript] RelationalExpression `as` Type + OperatorPrecedenceRelational + // ShiftExpression: + // AdditiveExpression + // ShiftExpression `<<` AdditiveExpression + // ShiftExpression `>>` AdditiveExpression + // ShiftExpression `>>>` AdditiveExpression + OperatorPrecedenceShift + // AdditiveExpression: + // MultiplicativeExpression + // AdditiveExpression `+` MultiplicativeExpression + // AdditiveExpression `-` MultiplicativeExpression + OperatorPrecedenceAdditive + // MultiplicativeExpression: + // ExponentiationExpression + // MultiplicativeExpression MultiplicativeOperator ExponentiationExpression + // MultiplicativeOperator: one of `*`, `/`, `%` + OperatorPrecedenceMultiplicative + // ExponentiationExpression: + // UnaryExpression + // UpdateExpression `**` ExponentiationExpression + OperatorPrecedenceExponentiation + // UnaryExpression: + // UpdateExpression + // `delete` UnaryExpression + // `void` UnaryExpression + // `typeof` UnaryExpression + // `+` UnaryExpression + // `-` UnaryExpression + // `~` UnaryExpression + // `!` UnaryExpression + // AwaitExpression + // UpdateExpression: // TODO: Do we need to investigate the precedence here? + // `++` UnaryExpression + // `--` UnaryExpression + OperatorPrecedenceUnary + // UpdateExpression: + // LeftHandSideExpression + // LeftHandSideExpression `++` + // LeftHandSideExpression `--` + OperatorPrecedenceUpdate + // LeftHandSideExpression: + // NewExpression + // CallExpression + // NewExpression: + // MemberExpression + // `new` NewExpression + OperatorPrecedenceLeftHandSide + // CallExpression: + // CoverCallExpressionAndAsyncArrowHead + // SuperCall + // ImportCall + // CallExpression Arguments + // CallExpression `[` Expression `]` + // CallExpression `.` IdentifierName + // CallExpression TemplateLiteral + // MemberExpression: + // PrimaryExpression + // MemberExpression `[` Expression `]` + // MemberExpression `.` IdentifierName + // MemberExpression TemplateLiteral + // SuperProperty + // MetaProperty + // `new` MemberExpression Arguments + OperatorPrecedenceMember + // TODO: JSXElement? + // PrimaryExpression: + // `this` + // IdentifierReference + // Literal + // ArrayLiteral + // ObjectLiteral + // FunctionExpression + // ClassExpression + // GeneratorExpression + // AsyncFunctionExpression + // AsyncGeneratorExpression + // RegularExpressionLiteral + // TemplateLiteral + // CoverParenthesizedExpressionAndArrowParameterList + OperatorPrecedencePrimary + // CoalesceExpression: + // CoalesceExpressionHead `??` BitwiseORExpression + // CoalesceExpressionHead: + // CoalesceExpression + // BitwiseORExpression + OperatorPrecedenceCoalesce = OperatorPrecedenceConditional // NOTE: This is wrong + OperatorPrecedenceLowest = OperatorPrecedenceComma + OperatorPrecedenceHighest = OperatorPrecedencePrimary + // -1 is lower than all other precedences. Returning it will cause binary expression + // parsing to stop. + OperatorPrecedenceInvalid OperatorPrecedence = -1 +) + +func getOperatorPrecedence(nodeKind SyntaxKind, operatorKind SyntaxKind, hasArguments bool) OperatorPrecedence { + switch nodeKind { + case SyntaxKindCommaListExpression: + return OperatorPrecedenceComma + case SyntaxKindSpreadElement: + return OperatorPrecedenceSpread + case SyntaxKindYieldExpression: + return OperatorPrecedenceYield + case SyntaxKindConditionalExpression: + return OperatorPrecedenceConditional + case SyntaxKindBinaryExpression: + switch operatorKind { + case SyntaxKindCommaToken: + return OperatorPrecedenceComma + case SyntaxKindEqualsToken, SyntaxKindPlusEqualsToken, SyntaxKindMinusEqualsToken, SyntaxKindAsteriskAsteriskEqualsToken, + SyntaxKindAsteriskEqualsToken, SyntaxKindSlashEqualsToken, SyntaxKindPercentEqualsToken, SyntaxKindLessThanLessThanEqualsToken, + SyntaxKindGreaterThanGreaterThanEqualsToken, SyntaxKindGreaterThanGreaterThanGreaterThanEqualsToken, SyntaxKindAmpersandEqualsToken, + SyntaxKindCaretEqualsToken, SyntaxKindBarEqualsToken, SyntaxKindBarBarEqualsToken, SyntaxKindAmpersandAmpersandEqualsToken, + SyntaxKindQuestionQuestionEqualsToken: + return OperatorPrecedenceAssignment + } + return getBinaryOperatorPrecedence(operatorKind) + // TODO: Should prefix `++` and `--` be moved to the `Update` precedence? + case SyntaxKindTypeAssertionExpression, SyntaxKindNonNullExpression, SyntaxKindPrefixUnaryExpression, SyntaxKindTypeOfExpression, + SyntaxKindVoidExpression, SyntaxKindDeleteExpression, SyntaxKindAwaitExpression: + return OperatorPrecedenceUnary + case SyntaxKindPostfixUnaryExpression: + return OperatorPrecedenceUpdate + case SyntaxKindCallExpression: + return OperatorPrecedenceLeftHandSide + case SyntaxKindNewExpression: + if hasArguments { + return OperatorPrecedenceMember + } + return OperatorPrecedenceLeftHandSide + case SyntaxKindTaggedTemplateExpression, SyntaxKindPropertyAccessExpression, SyntaxKindElementAccessExpression, SyntaxKindMetaProperty: + return OperatorPrecedenceMember + case SyntaxKindAsExpression, SyntaxKindSatisfiesExpression: + return OperatorPrecedenceRelational + case SyntaxKindThisKeyword, SyntaxKindSuperKeyword, SyntaxKindIdentifier, SyntaxKindPrivateIdentifier, SyntaxKindNullKeyword, + SyntaxKindTrueKeyword, SyntaxKindFalseKeyword, SyntaxKindNumericLiteral, SyntaxKindBigintLiteral, SyntaxKindStringLiteral, + SyntaxKindArrayLiteralExpression, SyntaxKindObjectLiteralExpression, SyntaxKindFunctionExpression, SyntaxKindArrowFunction, + SyntaxKindClassExpression, SyntaxKindRegularExpressionLiteral, SyntaxKindNoSubstitutionTemplateLiteral, SyntaxKindTemplateExpression, + SyntaxKindParenthesizedExpression, SyntaxKindOmittedExpression, SyntaxKindJsxElement, SyntaxKindJsxSelfClosingElement, SyntaxKindJsxFragment: + return OperatorPrecedencePrimary + } + return OperatorPrecedenceInvalid +} + +func getBinaryOperatorPrecedence(kind SyntaxKind) OperatorPrecedence { + switch kind { + case SyntaxKindQuestionQuestionToken: + return OperatorPrecedenceCoalesce + case SyntaxKindBarBarToken: + return OperatorPrecedenceLogicalOR + case SyntaxKindAmpersandAmpersandToken: + return OperatorPrecedenceLogicalAND + case SyntaxKindBarToken: + return OperatorPrecedenceBitwiseOR + case SyntaxKindCaretToken: + return OperatorPrecedenceBitwiseXOR + case SyntaxKindAmpersandToken: + return OperatorPrecedenceBitwiseAND + case SyntaxKindEqualsEqualsToken, SyntaxKindExclamationEqualsToken, SyntaxKindEqualsEqualsEqualsToken, SyntaxKindExclamationEqualsEqualsToken: + return OperatorPrecedenceEquality + case SyntaxKindLessThanToken, SyntaxKindGreaterThanToken, SyntaxKindLessThanEqualsToken, SyntaxKindGreaterThanEqualsToken, + SyntaxKindInstanceOfKeyword, SyntaxKindInKeyword, SyntaxKindAsKeyword, SyntaxKindSatisfiesKeyword: + return OperatorPrecedenceRelational + case SyntaxKindLessThanLessThanToken, SyntaxKindGreaterThanGreaterThanToken, SyntaxKindGreaterThanGreaterThanGreaterThanToken: + return OperatorPrecedenceShift + case SyntaxKindPlusToken, SyntaxKindMinusToken: + return OperatorPrecedenceAdditive + case SyntaxKindAsteriskToken, SyntaxKindSlashToken, SyntaxKindPercentToken: + return OperatorPrecedenceMultiplicative + case SyntaxKindAsteriskAsteriskToken: + return OperatorPrecedenceExponentiation + } + // -1 is lower than all other precedences. Returning it will cause binary expression + // parsing to stop. + return OperatorPrecedenceInvalid +} + +var regexps = make(map[string]*regexp.Regexp) +var regexpMutex sync.Mutex + +func makeRegexp(s string) *regexp.Regexp { + regexpMutex.Lock() + rx, ok := regexps[s] + if !ok { + rx = regexp.MustCompile(s) + regexps[s] = rx + } + regexpMutex.Unlock() + return rx +} + +func formatStringFromArgs(text string, args []any) string { + return makeRegexp(`{(\d+)}`).ReplaceAllStringFunc(text, func(match string) string { + index, err := strconv.ParseInt(match[1:len(match)-1], 10, 0) + if err != nil || int(index) >= len(args) { + panic("Invalid formatting placeholder") + } + return fmt.Sprintf("%v", args[int(index)]) + }) +} + +func filter[T any](slice []T, predicate func(T) bool) []T { + var result []T + for _, value := range slice { + if predicate(value) { + result = append(result, value) + } + } + return result +} + +func mapf[T, U any](slice []T, f func(T) U) []U { + if len(slice) == 0 { + return nil + } + result := make([]U, len(slice)) + for i := range slice { + result[i] = f(slice[i]) + } + return result +} + +func mapIndex[T, U any](slice []T, f func(T, int) U) []U { + if len(slice) == 0 { + return nil + } + result := make([]U, len(slice)) + for i := range slice { + result[i] = f(slice[i], i) + } + return result +} + +func sameMap[T comparable](slice []T, f func(T) T) ([]T, bool) { + for i, value := range slice { + mapped := f(value) + if mapped != value { + result := make([]T, len(slice)) + copy(result, slice[:i]) + result[i] = mapped + for i, value := range slice[i+1:] { + result[i] = f(value) + } + return result, false + } + } + return slice, true +} + +func some[T any](array []T, predicate func(T) bool) bool { + for _, value := range array { + if predicate(value) { + return true + } + } + return false +} + +func every[T any](array []T, predicate func(T) bool) bool { + for _, value := range array { + if !predicate(value) { + return false + } + } + return true +} + +func forEach[T any](array []T, action func(T)) { + for _, value := range array { + action(value) + } +} + +func insertSorted[T any](slice []T, element T, cmp func(T, T) int) []T { + i, _ := slices.BinarySearchFunc(slice, element, cmp) + return slices.Insert(slice, i, element) +} + +func firstOrNil[T any](slice []T) T { + if len(slice) != 0 { + return slice[0] + } + return *new(T) +} + +func lastOrNil[T any](slice []T) T { + if len(slice) != 0 { + return slice[len(slice)-1] + } + return *new(T) +} + +func find[T any](slice []T, predicate func(T) bool) T { + for _, value := range slice { + if predicate(value) { + return value + } + } + return *new(T) +} + +func findLast[T any](slice []T, predicate func(T) bool) T { + for i := len(slice) - 1; i >= 0; i-- { + value := slice[i] + if predicate(value) { + return value + } + } + return *new(T) +} + +func findInMap[K comparable, V any](m map[K]V, predicate func(V) bool) V { + for _, value := range m { + if predicate(value) { + return value + } + } + return *new(V) +} + +func concatenate[T any](s1 []T, s2 []T) []T { + if len(s2) == 0 { + return s1 + } + if len(s1) == 0 { + return s2 + } + return slices.Concat(s1, s2) +} + +func boolToTristate(b bool) Tristate { + if b { + return TSTrue + } + return TSFalse +} + +func modifierToFlag(token SyntaxKind) ModifierFlags { + switch token { + case SyntaxKindStaticKeyword: + return ModifierFlagsStatic + case SyntaxKindPublicKeyword: + return ModifierFlagsPublic + case SyntaxKindProtectedKeyword: + return ModifierFlagsProtected + case SyntaxKindPrivateKeyword: + return ModifierFlagsPrivate + case SyntaxKindAbstractKeyword: + return ModifierFlagsAbstract + case SyntaxKindAccessorKeyword: + return ModifierFlagsAccessor + case SyntaxKindExportKeyword: + return ModifierFlagsExport + case SyntaxKindDeclareKeyword: + return ModifierFlagsAmbient + case SyntaxKindConstKeyword: + return ModifierFlagsConst + case SyntaxKindDefaultKeyword: + return ModifierFlagsDefault + case SyntaxKindAsyncKeyword: + return ModifierFlagsAsync + case SyntaxKindReadonlyKeyword: + return ModifierFlagsReadonly + case SyntaxKindOverrideKeyword: + return ModifierFlagsOverride + case SyntaxKindInKeyword: + return ModifierFlagsIn + case SyntaxKindOutKeyword: + return ModifierFlagsOut + case SyntaxKindImmediateKeyword: + return ModifierFlagsImmediate + case SyntaxKindDecorator: + return ModifierFlagsDecorator + } + return ModifierFlagsNone +} + +func modifiersToFlags(modifierList *Node) ModifierFlags { + flags := ModifierFlagsNone + if modifierList != nil { + for _, modifier := range modifierList.AsModifierList().modifiers { + flags |= modifierToFlag(modifier.kind) + } + } + return flags +} + +func nodeIsMissing(node *Node) bool { + return node == nil || node.loc.pos == node.loc.end && node.loc.pos >= 0 && node.kind != SyntaxKindEndOfFile +} + +func nodeIsPresent(node *Node) bool { + return !nodeIsMissing(node) +} + +func isLeftHandSideExpression(node *Node) bool { + return isLeftHandSideExpressionKind(node.kind) +} + +func isLeftHandSideExpressionKind(kind SyntaxKind) bool { + switch kind { + case SyntaxKindPropertyAccessExpression, SyntaxKindElementAccessExpression, SyntaxKindNewExpression, SyntaxKindCallExpression, + SyntaxKindJsxElement, SyntaxKindJsxSelfClosingElement, SyntaxKindJsxFragment, SyntaxKindTaggedTemplateExpression, SyntaxKindArrayLiteralExpression, + SyntaxKindParenthesizedExpression, SyntaxKindObjectLiteralExpression, SyntaxKindClassExpression, SyntaxKindFunctionExpression, SyntaxKindIdentifier, + SyntaxKindPrivateIdentifier, SyntaxKindRegularExpressionLiteral, SyntaxKindNumericLiteral, SyntaxKindBigintLiteral, SyntaxKindStringLiteral, + SyntaxKindNoSubstitutionTemplateLiteral, SyntaxKindTemplateExpression, SyntaxKindFalseKeyword, SyntaxKindNullKeyword, SyntaxKindThisKeyword, + SyntaxKindTrueKeyword, SyntaxKindSuperKeyword, SyntaxKindNonNullExpression, SyntaxKindExpressionWithTypeArguments, SyntaxKindMetaProperty, + SyntaxKindImportKeyword, SyntaxKindMissingDeclaration: + return true + } + return false +} + +func isAssignmentOperator(token SyntaxKind) bool { + return token >= SyntaxKindFirstAssignment && token <= SyntaxKindLastAssignment +} + +func isExpressionWithTypeArguments(node *Node) bool { + return node.kind == SyntaxKindExpressionWithTypeArguments +} + +func isNonNullExpression(node *Node) bool { + return node.kind == SyntaxKindNonNullExpression +} + +func isStringLiteralLike(node *Node) bool { + return node.kind == SyntaxKindStringLiteral || node.kind == SyntaxKindNoSubstitutionTemplateLiteral +} + +func isNumericLiteral(node *Node) bool { + return node.kind == SyntaxKindNumericLiteral +} + +func isStringOrNumericLiteralLike(node *Node) bool { + return isStringLiteralLike(node) || isNumericLiteral(node) +} + +func isSignedNumericLiteral(node *Node) bool { + if node.kind == SyntaxKindPrefixUnaryExpression { + node := node.AsPrefixUnaryExpression() + return (node.operator == SyntaxKindPlusToken || node.operator == SyntaxKindMinusToken) && isNumericLiteral(node.operand) + } + return false +} + +func ifElse[T any](b bool, whenTrue T, whenFalse T) T { + if b { + return whenTrue + } + return whenFalse +} + +func tokenIsIdentifierOrKeyword(token SyntaxKind) bool { + return token >= SyntaxKindIdentifier +} + +func tokenIsIdentifierOrKeywordOrGreaterThan(token SyntaxKind) bool { + return token == SyntaxKindGreaterThanToken || tokenIsIdentifierOrKeyword(token) +} + +func getTextOfNode(node *Node) string { + return getSourceTextOfNodeFromSourceFile(getSourceFileOfNode(node), node) +} + +func getSourceTextOfNodeFromSourceFile(sourceFile *SourceFile, node *Node) string { + return getTextOfNodeFromSourceText(sourceFile.text, node) +} + +func getTextOfNodeFromSourceText(sourceText string, node *Node) string { + if nodeIsMissing(node) { + return "" + } + text := sourceText[skipTrivia(sourceText, node.Pos()):node.End()] + // if (isJSDocTypeExpressionOrChild(node)) { + // // strip space + asterisk at line start + // text = text.split(/\r\n|\n|\r/).map(line => line.replace(/^\s*\*/, "").trimStart()).join("\n"); + // } + return text +} + +func appendIfUnique[T comparable](array []T, element T) []T { + if slices.Contains(array, element) { + return array + } + return append(array, element) +} + +func isAssignmentDeclaration(decl *Node) bool { + return isBinaryExpression(decl) || isAccessExpression(decl) || isIdentifier(decl) || isCallExpression(decl) +} + +func isBinaryExpression(node *Node) bool { + return node.kind == SyntaxKindBinaryExpression +} + +func isAccessExpression(node *Node) bool { + return node.kind == SyntaxKindPropertyAccessExpression || node.kind == SyntaxKindElementAccessExpression +} + +func isInJSFile(node *Node) bool { + return node != nil && node.flags&NodeFlagsJavaScriptFile != 0 +} + +func isEffectiveModuleDeclaration(node *Node) bool { + return isModuleDeclaration(node) || isIdentifier(node) +} + +func isObjectLiteralOrClassExpressionMethodOrAccessor(node *Node) bool { + kind := node.kind + return (kind == SyntaxKindMethodDeclaration || kind == SyntaxKindGetAccessor || kind == SyntaxKindSetAccessor) && + (node.parent.kind == SyntaxKindObjectLiteralExpression || node.parent.kind == SyntaxKindClassExpression) +} + +func isFunctionLike(node *Node) bool { + return node != nil && isFunctionLikeKind(node.kind) +} + +func isFunctionLikeKind(kind SyntaxKind) bool { + switch kind { + case SyntaxKindMethodSignature, SyntaxKindCallSignature, SyntaxKindJSDocSignature, SyntaxKindConstructSignature, SyntaxKindIndexSignature, + SyntaxKindFunctionType, SyntaxKindJSDocFunctionType, SyntaxKindConstructorType: + return true + } + return isFunctionLikeDeclarationKind(kind) +} + +func isFunctionLikeDeclaration(node *Node) bool { + return node != nil && isFunctionLikeDeclarationKind(node.kind) +} + +func isFunctionLikeDeclarationKind(kind SyntaxKind) bool { + switch kind { + case SyntaxKindFunctionDeclaration, SyntaxKindMethodDeclaration, SyntaxKindConstructor, SyntaxKindGetAccessor, SyntaxKindSetAccessor, + SyntaxKindFunctionExpression, SyntaxKindArrowFunction: + return true + } + return false +} + +type OuterExpressionKinds int16 + +const ( + OEKParentheses OuterExpressionKinds = 1 << 0 + OEKTypeAssertions OuterExpressionKinds = 1 << 1 + OEKNonNullAssertions OuterExpressionKinds = 1 << 2 + OEKExpressionsWithTypeArguments OuterExpressionKinds = 1 << 3 + OEKExcludeJSDocTypeAssertion = 1 << 4 + OEKAssertions = OEKTypeAssertions | OEKNonNullAssertions + OEKAll = OEKParentheses | OEKAssertions | OEKExpressionsWithTypeArguments +) + +func isOuterExpression(node *Node, kinds OuterExpressionKinds) bool { + switch node.kind { + case SyntaxKindParenthesizedExpression: + return kinds&OEKParentheses != 0 && !(kinds&OEKExcludeJSDocTypeAssertion != 0 && isJSDocTypeAssertion(node)) + case SyntaxKindTypeAssertionExpression, SyntaxKindAsExpression, SyntaxKindSatisfiesExpression: + return kinds&OEKTypeAssertions != 0 + case SyntaxKindExpressionWithTypeArguments: + return kinds&OEKExpressionsWithTypeArguments != 0 + case SyntaxKindNonNullExpression: + return kinds&OEKNonNullAssertions != 0 + } + return false +} + +func getInnerExpression(node *Node) *Node { + switch node.kind { + case SyntaxKindParenthesizedExpression: + return node.AsParenthesizedExpression().expression + case SyntaxKindTypeAssertionExpression: + return node.AsTypeAssertion().expression + case SyntaxKindAsExpression: + return node.AsAsExpression().expression + case SyntaxKindSatisfiesExpression: + return node.AsSatisfiesExpression().expression + case SyntaxKindExpressionWithTypeArguments: + return node.AsExpressionWithTypeArguments().expression + case SyntaxKindNonNullExpression: + return node.AsNonNullExpression().expression + } + panic("Unhandled case in getInnerExpression") +} + +func getSymbolFromNode(node *Node) *Symbol { + return node.Symbol() +} + +func skipOuterExpressions(node *Node, kinds OuterExpressionKinds) *Node { + for isOuterExpression(node, kinds) { + node = getInnerExpression(node) + } + return node +} + +func skipParentheses(node *Node) *Node { + return skipOuterExpressions(node, OEKParentheses) +} + +func walkUpParenthesizedTypes(node *Node) *Node { + for node != nil && node.kind == SyntaxKindParenthesizedType { + node = node.parent + } + return node +} + +func walkUpParenthesizedExpressions(node *Node) *Node { + for node != nil && node.kind == SyntaxKindParenthesizedExpression { + node = node.parent + } + return node +} + +func isJSDocTypeAssertion(node *Node) bool { + return false // !!! +} + +// Return true if the given identifier is classified as an IdentifierName +func isIdentifierName(node *Node) bool { + parent := node.parent + switch parent.kind { + case SyntaxKindPropertyDeclaration, SyntaxKindPropertySignature, SyntaxKindMethodDeclaration, SyntaxKindMethodSignature, SyntaxKindGetAccessor, + SyntaxKindSetAccessor, SyntaxKindEnumMember, SyntaxKindPropertyAssignment, SyntaxKindPropertyAccessExpression: + return parent.Name() == node + case SyntaxKindQualifiedName: + return parent.AsQualifiedName().right == node + case SyntaxKindBindingElement: + return parent.AsBindingElement().propertyName == node + case SyntaxKindImportSpecifier: + return parent.AsImportSpecifier().propertyName == node + case SyntaxKindExportSpecifier, SyntaxKindJsxAttribute, SyntaxKindJsxSelfClosingElement, SyntaxKindJsxOpeningElement, SyntaxKindJsxClosingElement: + return true + } + return false +} + +func getSourceFileOfNode(node *Node) *SourceFile { + for { + if node == nil { + return nil + } + if node.kind == SyntaxKindSourceFile { + return node.data.(*SourceFile) + } + node = node.parent + } +} + +/** @internal */ +func getErrorRangeForNode(sourceFile *SourceFile, node *Node) TextRange { + errorNode := node + switch node.kind { + case SyntaxKindSourceFile: + pos := skipTrivia(sourceFile.text, 0) + if pos == len(sourceFile.text) { + return NewTextRange(0, 0) + } + return getRangeOfTokenAtPosition(sourceFile, pos) + // This list is a work in progress. Add missing node kinds to improve their error spans + case SyntaxKindVariableDeclaration, SyntaxKindBindingElement, SyntaxKindClassDeclaration, SyntaxKindClassExpression, SyntaxKindInterfaceDeclaration, + SyntaxKindModuleDeclaration, SyntaxKindEnumDeclaration, SyntaxKindEnumMember, SyntaxKindFunctionDeclaration, SyntaxKindFunctionExpression, + SyntaxKindMethodDeclaration, SyntaxKindGetAccessor, SyntaxKindSetAccessor, SyntaxKindTypeAliasDeclaration, SyntaxKindPropertyDeclaration, + SyntaxKindPropertySignature, SyntaxKindNamespaceImport: + errorNode = getNameOfDeclaration(node) + case SyntaxKindArrowFunction: + return getErrorRangeForArrowFunction(sourceFile, node) + case SyntaxKindCaseClause: + case SyntaxKindDefaultClause: + start := skipTrivia(sourceFile.text, node.Pos()) + end := node.End() + statements := node.data.(*CaseOrDefaultClause).statements + if len(statements) != 0 { + end = statements[0].Pos() + } + return NewTextRange(start, end) + case SyntaxKindReturnStatement, SyntaxKindYieldExpression: + pos := skipTrivia(sourceFile.text, node.Pos()) + return getRangeOfTokenAtPosition(sourceFile, pos) + case SyntaxKindSatisfiesExpression: + pos := skipTrivia(sourceFile.text, node.AsSatisfiesExpression().expression.End()) + return getRangeOfTokenAtPosition(sourceFile, pos) + case SyntaxKindConstructor: + scanner := getScannerForSourceFile(sourceFile, node.Pos()) + start := scanner.tokenStart + for scanner.token != SyntaxKindConstructorKeyword && scanner.token != SyntaxKindStringLiteral && scanner.token != SyntaxKindEndOfFile { + scanner.Scan() + } + return NewTextRange(start, scanner.pos) + // !!! + // case SyntaxKindJSDocSatisfiesTag: + // pos := skipTrivia(sourceFile.text, node.tagName.pos) + // return getRangeOfTokenAtPosition(sourceFile, pos) + } + if errorNode == nil { + // If we don't have a better node, then just set the error on the first token of + // construct. + return getRangeOfTokenAtPosition(sourceFile, node.Pos()) + } + pos := errorNode.Pos() + if !nodeIsMissing(errorNode) { + pos = skipTrivia(sourceFile.text, pos) + } + return NewTextRange(pos, errorNode.End()) +} + +func getErrorRangeForArrowFunction(sourceFile *SourceFile, node *Node) TextRange { + pos := skipTrivia(sourceFile.text, node.Pos()) + body := node.AsArrowFunction().body + if body != nil && body.kind == SyntaxKindBlock { + startLine, _ := GetLineAndCharacterOfPosition(sourceFile, body.Pos()) + endLine, _ := GetLineAndCharacterOfPosition(sourceFile, body.End()) + if startLine < endLine { + // The arrow function spans multiple lines, + // make the error span be the first line, inclusive. + return NewTextRange(pos, getEndLinePosition(sourceFile, startLine)) + } + } + return NewTextRange(pos, node.End()) +} + +func getContainingClass(node *Node) *Node { + return findAncestor(node.parent, isClassLike) +} + +func findAncestor(node *Node, callback func(*Node) bool) *Node { + for node != nil { + result := callback(node) + if result { + return node + } + node = node.parent + } + return nil +} + +type FindAncestorResult int32 + +const ( + FindAncestorFalse FindAncestorResult = iota + FindAncestorTrue + FindAncestorQuit +) + +func findAncestorOrQuit(node *Node, callback func(*Node) FindAncestorResult) *Node { + for node != nil { + switch callback(node) { + case FindAncestorQuit: + return nil + case FindAncestorTrue: + return node + } + node = node.parent + } + return nil +} + +func isClassLike(node *Node) bool { + return node != nil && (node.kind == SyntaxKindClassDeclaration || node.kind == SyntaxKindClassExpression) +} + +func declarationNameToString(name *Node) string { + if !exists(name) || name.Pos() == name.End() { + return "(Missing)" + } + return getTextOfNode(name) +} + +func isExternalModule(file *SourceFile) bool { + return file.externalModuleIndicator != nil +} + +func isInTopLevelContext(node *Node) bool { + // The name of a class or function declaration is a BindingIdentifier in its surrounding scope. + if isIdentifier(node) { + parent := node.parent + if (isClassDeclaration(parent) || isFunctionDeclaration(parent)) && parent.Name() == node { + node = parent + } + } + container := getThisContainer(node, true /*includeArrowFunctions*/, false /*includeClassComputedPropertyName*/) + return isSourceFile(container) +} + +func getThisContainer(node *Node, includeArrowFunctions bool, includeClassComputedPropertyName bool) *Node { + for { + node = node.parent + if node == nil { + panic("nil parent in getThisContainer") + } + switch node.kind { + case SyntaxKindComputedPropertyName: + if includeClassComputedPropertyName && isClassLike(node.parent.parent) { + return node + } + node = node.parent.parent + case SyntaxKindDecorator: + if node.parent.kind == SyntaxKindParameter && isClassElement(node.parent.parent) { + // If the decorator's parent is a Parameter, we resolve the this container from + // the grandparent class declaration. + node = node.parent.parent + } else if isClassElement(node.parent) { + // If the decorator's parent is a class element, we resolve the 'this' container + // from the parent class declaration. + node = node.parent + } + case SyntaxKindArrowFunction: + if includeArrowFunctions { + return node + } + case SyntaxKindFunctionDeclaration, SyntaxKindFunctionExpression, SyntaxKindModuleDeclaration, SyntaxKindClassStaticBlockDeclaration, + SyntaxKindPropertyDeclaration, SyntaxKindPropertySignature, SyntaxKindMethodDeclaration, SyntaxKindMethodSignature, SyntaxKindConstructor, + SyntaxKindGetAccessor, SyntaxKindSetAccessor, SyntaxKindCallSignature, SyntaxKindConstructSignature, SyntaxKindIndexSignature, + SyntaxKindEnumDeclaration, SyntaxKindSourceFile: + return node + } + } +} + +func isClassElement(node *Node) bool { + switch node.kind { + case SyntaxKindConstructor, SyntaxKindPropertyDeclaration, SyntaxKindMethodDeclaration, SyntaxKindGetAccessor, SyntaxKindSetAccessor, + SyntaxKindIndexSignature, SyntaxKindClassStaticBlockDeclaration, SyntaxKindSemicolonClassElement: + return true + } + return false +} + +func isPartOfTypeQuery(node *Node) bool { + for node.kind == SyntaxKindQualifiedName || node.kind == SyntaxKindIdentifier { + node = node.parent + } + return node.kind == SyntaxKindTypeQuery +} + +func getModifierFlags(node *Node) ModifierFlags { + modifiers := node.Modifiers() + if modifiers != nil { + return modifiers.AsModifierList().modifierFlags + } + return ModifierFlagsNone +} + +func getNodeFlags(node *Node) NodeFlags { + return node.flags +} + +func hasSyntacticModifier(node *Node, flags ModifierFlags) bool { + return getModifierFlags(node)&flags != 0 +} + +func hasAccessorModifier(node *Node) bool { + return hasSyntacticModifier(node, ModifierFlagsAccessor) +} + +func hasStaticModifier(node *Node) bool { + return hasSyntacticModifier(node, ModifierFlagsStatic) +} + +func getEffectiveModifierFlags(node *Node) ModifierFlags { + return getModifierFlags(node) // !!! Handle JSDoc +} + +func hasEffectiveModifier(node *Node, flags ModifierFlags) bool { + return getEffectiveModifierFlags(node)&flags != 0 +} + +func getImmediatelyInvokedFunctionExpression(fn *Node) *Node { + if fn.kind == SyntaxKindFunctionExpression || fn.kind == SyntaxKindArrowFunction { + prev := fn + parent := fn.parent + for parent.kind == SyntaxKindParenthesizedExpression { + prev = parent + parent = parent.parent + } + if parent.kind == SyntaxKindCallExpression && parent.AsCallExpression().expression == prev { + return parent + } + } + return nil +} + +// Does not handle signed numeric names like `a[+0]` - handling those would require handling prefix unary expressions +// throughout late binding handling as well, which is awkward (but ultimately probably doable if there is demand) +func getElementOrPropertyAccessArgumentExpressionOrName(node *Node) *Node { + switch node.kind { + case SyntaxKindPropertyAccessExpression: + return node.AsPropertyAccessExpression().name + case SyntaxKindElementAccessExpression: + arg := skipParentheses(node.AsElementAccessExpression().argumentExpression) + if isStringOrNumericLiteralLike(arg) { + return arg + } + return node + } + panic("Unhandled case in getElementOrPropertyAccessArgumentExpressionOrName") +} + +func getAccessedExpression(node *Node) *Node { + switch node.kind { + case SyntaxKindPropertyAccessExpression: + return node.AsPropertyAccessExpression().expression + case SyntaxKindElementAccessExpression: + return node.AsElementAccessExpression().expression + case SyntaxKindCallExpression: + return node.AsCallExpression().expression + case SyntaxKindParenthesizedExpression: + return node.AsParenthesizedExpression().expression + case SyntaxKindNonNullExpression: + return node.AsNonNullExpression().expression + case SyntaxKindTypeAssertionExpression: + return node.AsTypeAssertion().expression + case SyntaxKindAsExpression: + return node.AsAsExpression().expression + } + panic("Unhandled case in getAccessedExpression") +} + +func getQuestionDotToken(node *Node) *Node { + switch node.kind { + case SyntaxKindPropertyAccessExpression: + return node.AsPropertyAccessExpression().questionDotToken + case SyntaxKindElementAccessExpression: + return node.AsElementAccessExpression().questionDotToken + case SyntaxKindCallExpression: + return node.AsCallExpression().questionDotToken + } + panic("Unhandled case in getQuestionDotToken") +} + +/** + * A declaration has a dynamic name if all of the following are true: + * 1. The declaration has a computed property name. + * 2. The computed name is *not* expressed as a StringLiteral. + * 3. The computed name is *not* expressed as a NumericLiteral. + * 4. The computed name is *not* expressed as a PlusToken or MinusToken + * immediately followed by a NumericLiteral. + */ +func hasDynamicName(declaration *Node) bool { + name := getNameOfDeclaration(declaration) + return name != nil && isDynamicName(name) +} + +func isDynamicName(name *Node) bool { + var expr *Node + switch name.kind { + case SyntaxKindComputedPropertyName: + expr = name.AsComputedPropertyName().expression + case SyntaxKindElementAccessExpression: + expr = skipParentheses(name.AsElementAccessExpression().argumentExpression) + default: + return false + } + return !isStringOrNumericLiteralLike(expr) && !isSignedNumericLiteral(expr) +} + +func getNameOfDeclaration(declaration *Node) *Node { + if declaration == nil { + return nil + } + nonAssignedName := getNonAssignedNameOfDeclaration(declaration) + if nonAssignedName != nil { + return nonAssignedName + } + if isFunctionExpression(declaration) || isArrowFunction(declaration) || isClassExpression(declaration) { + return getAssignedName(declaration) + } + return nil +} + +func getNonAssignedNameOfDeclaration(declaration *Node) *Node { + switch declaration.kind { + case SyntaxKindBinaryExpression: + if isFunctionPropertyAssignment(declaration) { + return getElementOrPropertyAccessArgumentExpressionOrName(declaration.AsBinaryExpression().left) + } + return nil + case SyntaxKindExportAssignment: + expr := declaration.AsExportAssignment().expression + if isIdentifier(expr) { + return expr + } + return nil + } + return declaration.Name() +} + +func getAssignedName(node *Node) *Node { + parent := node.parent + if parent != nil { + switch parent.kind { + case SyntaxKindPropertyAssignment: + return parent.AsPropertyAssignment().name + case SyntaxKindBindingElement: + return parent.AsBindingElement().name + case SyntaxKindBinaryExpression: + if node == parent.AsBinaryExpression().right { + left := parent.AsBinaryExpression().left + switch left.kind { + case SyntaxKindIdentifier: + return left + case SyntaxKindPropertyAccessExpression: + return left.AsPropertyAccessExpression().name + case SyntaxKindElementAccessExpression: + arg := skipParentheses(left.AsElementAccessExpression().argumentExpression) + if isStringOrNumericLiteralLike(arg) { + return arg + } + } + } + case SyntaxKindVariableDeclaration: + name := parent.AsVariableDeclaration().name + if isIdentifier(name) { + return name + } + } + } + return nil +} + +func isFunctionPropertyAssignment(node *Node) bool { + if node.kind == SyntaxKindBinaryExpression { + expr := node.AsBinaryExpression() + if expr.operatorToken.kind == SyntaxKindEqualsToken { + switch expr.left.kind { + case SyntaxKindPropertyAccessExpression: + // F.id = expr + return isIdentifier(expr.left.AsPropertyAccessExpression().expression) && isIdentifier(expr.left.AsPropertyAccessExpression().name) + case SyntaxKindElementAccessExpression: + // F[xxx] = expr + return isIdentifier(expr.left.AsElementAccessExpression().expression) + } + } + } + return false +} + +func isAssignmentExpression(node *Node, excludeCompoundAssignment bool) bool { + if node.kind == SyntaxKindBinaryExpression { + expr := node.AsBinaryExpression() + return (expr.operatorToken.kind == SyntaxKindEqualsToken || !excludeCompoundAssignment && isAssignmentOperator(expr.operatorToken.kind)) && + isLeftHandSideExpression(expr.left) + } + return false +} + +func isBlockOrCatchScoped(declaration *Node) bool { + return getCombinedNodeFlags(declaration)&NodeFlagsBlockScoped != 0 || isCatchClauseVariableDeclarationOrBindingElement(declaration) +} + +func isCatchClauseVariableDeclarationOrBindingElement(declaration *Node) bool { + node := getRootDeclaration(declaration) + return node.kind == SyntaxKindVariableDeclaration && node.parent.kind == SyntaxKindCatchClause +} + +func isAmbientModule(node *Node) bool { + return isModuleDeclaration(node) && (node.AsModuleDeclaration().name.kind == SyntaxKindStringLiteral || isGlobalScopeAugmentation(node)) +} + +func isGlobalScopeAugmentation(node *Node) bool { + return node.flags&NodeFlagsGlobalAugmentation != 0 +} + +func isPropertyNameLiteral(node *Node) bool { + switch node.kind { + case SyntaxKindIdentifier, SyntaxKindStringLiteral, SyntaxKindNoSubstitutionTemplateLiteral, SyntaxKindNumericLiteral: + return true + } + return false +} + +func getTextOfIdentifierOrLiteral(node *Node) string { + switch node.kind { + case SyntaxKindIdentifier: + return node.AsIdentifier().text + case SyntaxKindPrivateIdentifier: + return node.AsPrivateIdentifier().text + case SyntaxKindStringLiteral: + return node.AsStringLiteral().text + case SyntaxKindNumericLiteral: + return node.AsNumericLiteral().text + case SyntaxKindBigintLiteral: + return node.AsBigintLiteral().text + case SyntaxKindNoSubstitutionTemplateLiteral: + return node.AsNoSubstitutionTemplateLiteral().text + // case isJsxNamespacedName(node): + // return getTextOfJsxNamespacedName(node) + } + panic("Unhandled case in getTextOfIdentifierOrLiteral") +} + +func isMemberName(node *Node) bool { + return node.kind == SyntaxKindIdentifier || node.kind == SyntaxKindPrivateIdentifier +} + +func setParent(child *Node, parent *Node) { + if child != nil { + child.parent = parent + } +} + +func setParentInChildren(node *Node) { + node.ForEachChild(func(child *Node) bool { + child.parent = node + setParentInChildren(child) + return false + }) +} + +func getCombinedFlags[T ~uint32](node *Node, getFlags func(*Node) T) T { + node = getRootDeclaration(node) + flags := getFlags(node) + if node.kind == SyntaxKindVariableDeclaration { + node = node.parent + } + if node != nil && node.kind == SyntaxKindVariableDeclarationList { + flags |= getFlags(node) + node = node.parent + } + if node != nil && node.kind == SyntaxKindVariableStatement { + flags |= getFlags(node) + } + return flags +} + +func getCombinedModifierFlags(node *Node) ModifierFlags { + return getCombinedFlags(node, getModifierFlags) +} + +func getCombinedNodeFlags(node *Node) NodeFlags { + return getCombinedFlags(node, getNodeFlags) +} + +func isBindingPattern(node *Node) bool { + return node != nil && (node.kind == SyntaxKindArrayBindingPattern || node.kind == SyntaxKindObjectBindingPattern) +} + +func isParameterPropertyDeclaration(node *Node, parent *Node) bool { + return isParameter(node) && hasSyntacticModifier(node, ModifierFlagsParameterPropertyModifier) && parent.kind == SyntaxKindConstructor +} + +/** + * Like {@link isVariableDeclarationInitializedToRequire} but allows things like `require("...").foo.bar` or `require("...")["baz"]`. + */ +func isVariableDeclarationInitializedToBareOrAccessedRequire(node *Node) bool { + return isVariableDeclarationInitializedWithRequireHelper(node, true /*allowAccessedRequire*/) +} + +func isVariableDeclarationInitializedWithRequireHelper(node *Node, allowAccessedRequire bool) bool { + if node.kind == SyntaxKindVariableDeclaration && node.AsVariableDeclaration().initializer != nil { + initializer := node.AsVariableDeclaration().initializer + if allowAccessedRequire { + initializer = getLeftmostAccessExpression(initializer) + } + return isRequireCall(initializer, true /*requireStringLiteralLikeArgument*/) + } + return false +} + +func getLeftmostAccessExpression(expr *Node) *Node { + for isAccessExpression(expr) { + expr = getAccessedExpression(expr) + } + return expr +} + +func isRequireCall(node *Node, requireStringLiteralLikeArgument bool) bool { + if isCallExpression(node) { + callExpression := node.AsCallExpression() + if len(callExpression.arguments) == 1 { + if isIdentifier(callExpression.expression) && callExpression.expression.AsIdentifier().text == "require" { + return !requireStringLiteralLikeArgument || isStringLiteralLike(callExpression.arguments[0]) + } + } + } + return false +} + +/** + * This function returns true if the this node's root declaration is a parameter. + * For example, passing a `ParameterDeclaration` will return true, as will passing a + * binding element that is a child of a `ParameterDeclaration`. + * + * If you are looking to test that a `Node` is a `ParameterDeclaration`, use `isParameter`. + */ +func isPartOfParameterDeclaration(node *Node) bool { + return getRootDeclaration(node).kind == SyntaxKindParameter +} + +func getRootDeclaration(node *Node) *Node { + for node.kind == SyntaxKindBindingElement { + node = node.parent.parent + } + return node +} + +func isExternalOrCommonJsModule(file *SourceFile) bool { + return file.externalModuleIndicator != nil +} + +func isAutoAccessorPropertyDeclaration(node *Node) bool { + return isPropertyDeclaration(node) && hasAccessorModifier(node) +} + +func isAsyncFunction(node *Node) bool { + switch node.kind { + case SyntaxKindFunctionDeclaration, SyntaxKindFunctionExpression, SyntaxKindArrowFunction, SyntaxKindMethodDeclaration: + data := node.BodyData() + return data.body != nil && data.asteriskToken == nil && hasSyntacticModifier(node, ModifierFlagsAsync) + } + return false +} + +func isObjectLiteralMethod(node *Node) bool { + return node != nil && node.kind == SyntaxKindMethodDeclaration && node.parent.kind == SyntaxKindObjectLiteralExpression +} + +func symbolName(symbol *Symbol) string { + if symbol.valueDeclaration != nil && isPrivateIdentifierClassElementDeclaration(symbol.valueDeclaration) { + return symbol.valueDeclaration.Name().AsPrivateIdentifier().text + } + return symbol.name +} + +func isStaticPrivateIdentifierProperty(s *Symbol) bool { + return s.valueDeclaration != nil && isPrivateIdentifierClassElementDeclaration(s.valueDeclaration) && isStatic(s.valueDeclaration) +} + +func isPrivateIdentifierClassElementDeclaration(node *Node) bool { + return (isPropertyDeclaration(node) || isMethodOrAccessor(node)) && isPrivateIdentifier(node.Name()) +} + +func isMethodOrAccessor(node *Node) bool { + switch node.kind { + case SyntaxKindMethodDeclaration, SyntaxKindGetAccessor, SyntaxKindSetAccessor: + return true + } + return false +} + +func isFunctionLikeOrClassStaticBlockDeclaration(node *Node) bool { + return node != nil && (isFunctionLikeKind(node.kind) || isClassStaticBlockDeclaration(node)) +} + +func isModuleAugmentationExternal(node *Node) bool { + // external module augmentation is a ambient module declaration that is either: + // - defined in the top level scope and source file is an external module + // - defined inside ambient module declaration located in the top level scope and source file not an external module + switch node.parent.kind { + case SyntaxKindSourceFile: + return isExternalModule(node.parent.AsSourceFile()) + case SyntaxKindModuleBlock: + grandParent := node.parent.parent + return isAmbientModule(grandParent) && isSourceFile(grandParent.parent) && !isExternalModule(grandParent.parent.AsSourceFile()) + } + return false +} + +type Pattern struct { + text string + starIndex int // -1 for exact match +} + +func isValidPattern(pattern Pattern) bool { + return pattern.starIndex == -1 || pattern.starIndex < len(pattern.text) +} + +func tryParsePattern(pattern string) Pattern { + starIndex := strings.Index(pattern, "*") + if starIndex == -1 || !strings.Contains(pattern[starIndex+1:], "*") { + return Pattern{text: pattern, starIndex: starIndex} + } + return Pattern{} +} + +func isDeclarationStatementKind(kind SyntaxKind) bool { + switch kind { + case SyntaxKindFunctionDeclaration, SyntaxKindMissingDeclaration, SyntaxKindClassDeclaration, SyntaxKindInterfaceDeclaration, + SyntaxKindTypeAliasDeclaration, SyntaxKindEnumDeclaration, SyntaxKindModuleDeclaration, SyntaxKindImportDeclaration, + SyntaxKindImportEqualsDeclaration, SyntaxKindExportDeclaration, SyntaxKindExportAssignment, SyntaxKindNamespaceExportDeclaration: + return true + } + return false +} + +func isDeclarationStatement(node *Node) bool { + return isDeclarationStatementKind(node.kind) +} + +func isStatementKindButNotDeclarationKind(kind SyntaxKind) bool { + switch kind { + case SyntaxKindBreakStatement, SyntaxKindContinueStatement, SyntaxKindDebuggerStatement, SyntaxKindDoStatement, SyntaxKindExpressionStatement, + SyntaxKindEmptyStatement, SyntaxKindForInStatement, SyntaxKindForOfStatement, SyntaxKindForStatement, SyntaxKindIfStatement, + SyntaxKindLabeledStatement, SyntaxKindReturnStatement, SyntaxKindSwitchStatement, SyntaxKindThrowStatement, SyntaxKindTryStatement, + SyntaxKindVariableStatement, SyntaxKindWhileStatement, SyntaxKindWithStatement, SyntaxKindNotEmittedStatement: + return true + } + return false +} + +func isStatementButNotDeclaration(node *Node) bool { + return isStatementKindButNotDeclarationKind(node.kind) +} + +func isStatement(node *Node) bool { + kind := node.kind + return isStatementKindButNotDeclarationKind(kind) || isDeclarationStatementKind(kind) || isBlockStatement(node) +} + +func isBlockStatement(node *Node) bool { + if node.kind != SyntaxKindBlock { + return false + } + if node.parent != nil && (node.parent.kind == SyntaxKindTryStatement || node.parent.kind == SyntaxKindCatchClause) { + return false + } + return !isFunctionBlock(node) +} + +func isFunctionBlock(node *Node) bool { + return node != nil && node.kind == SyntaxKindBlock && isFunctionLike(node.parent) +} + +func shouldPreserveConstEnums(options *CompilerOptions) bool { + return options.PreserveConstEnums == TSTrue || options.IsolatedModules == TSTrue +} + +func exportAssignmentIsAlias(node *Node) bool { + return isAliasableExpression(getExportAssignmentExpression(node)) +} + +func getExportAssignmentExpression(node *Node) *Node { + switch node.kind { + case SyntaxKindExportAssignment: + return node.AsExportAssignment().expression + case SyntaxKindBinaryExpression: + return node.AsBinaryExpression().right + } + panic("Unhandled case in getExportAssignmentExpression") +} + +func isAliasableExpression(e *Node) bool { + return isEntityNameExpression(e) || isClassExpression(e) +} + +func isEmptyObjectLiteral(expression *Node) bool { + return expression.kind == SyntaxKindObjectLiteralExpression && len(expression.AsObjectLiteralExpression().properties) == 0 +} + +func isFunctionSymbol(symbol *Symbol) bool { + d := symbol.valueDeclaration + return d != nil && (isFunctionDeclaration(d) || isVariableDeclaration(d) && isFunctionLike(d.AsVariableDeclaration().initializer)) +} + +func isLogicalOrCoalescingAssignmentOperator(token SyntaxKind) bool { + return token == SyntaxKindBarBarEqualsToken || token == SyntaxKindAmpersandAmpersandEqualsToken || token == SyntaxKindQuestionQuestionEqualsToken +} + +func isLogicalOrCoalescingAssignmentExpression(expr *Node) bool { + return isBinaryExpression(expr) && isLogicalOrCoalescingAssignmentOperator(expr.AsBinaryExpression().operatorToken.kind) +} + +func isLogicalOrCoalescingBinaryOperator(token SyntaxKind) bool { + return isBinaryLogicalOperator(token) || token == SyntaxKindQuestionQuestionToken +} + +func isLogicalOrCoalescingBinaryExpression(expr *Node) bool { + return isBinaryExpression(expr) && isLogicalOrCoalescingBinaryOperator(expr.AsBinaryExpression().operatorToken.kind) +} + +func isBinaryLogicalOperator(token SyntaxKind) bool { + return token == SyntaxKindBarBarToken || token == SyntaxKindAmpersandAmpersandToken +} + +/** + * Determines whether a node is the outermost `OptionalChain` in an ECMAScript `OptionalExpression`: + * + * 1. For `a?.b.c`, the outermost chain is `a?.b.c` (`c` is the end of the chain starting at `a?.`) + * 2. For `a?.b!`, the outermost chain is `a?.b` (`b` is the end of the chain starting at `a?.`) + * 3. For `(a?.b.c).d`, the outermost chain is `a?.b.c` (`c` is the end of the chain starting at `a?.` since parens end the chain) + * 4. For `a?.b.c?.d`, both `a?.b.c` and `a?.b.c?.d` are outermost (`c` is the end of the chain starting at `a?.`, and `d` is + * the end of the chain starting at `c?.`) + * 5. For `a?.(b?.c).d`, both `b?.c` and `a?.(b?.c)d` are outermost (`c` is the end of the chain starting at `b`, and `d` is + * the end of the chain starting at `a?.`) + */ +func isOutermostOptionalChain(node *Node) bool { + parent := node.parent + return !isOptionalChain(parent) || // cases 1, 2, and 3 + isOptionalChainRoot(parent) || // case 4 + node != getAccessedExpression(parent) // case 5 +} + +func isNullishCoalesce(node *Node) bool { + return node.kind == SyntaxKindBinaryExpression && node.AsBinaryExpression().operatorToken.kind == SyntaxKindQuestionQuestionToken +} + +func isDottedName(node *Node) bool { + switch node.kind { + case SyntaxKindIdentifier, SyntaxKindThisKeyword, SyntaxKindSuperKeyword, SyntaxKindMetaProperty: + return true + case SyntaxKindPropertyAccessExpression, SyntaxKindParenthesizedExpression: + return isDottedName(getAccessedExpression(node)) + } + return false +} + +func unusedLabelIsError(options *CompilerOptions) bool { + return options.AllowUnusedLabels == TSFalse +} + +func unreachableCodeIsError(options *CompilerOptions) bool { + return options.AllowUnreachableCode == TSFalse +} + +func isDestructuringAssignment(node *Node) bool { + if isAssignmentExpression(node, true /*excludeCompoundAssignment*/) { + kind := node.AsBinaryExpression().left.kind + return kind == SyntaxKindObjectLiteralExpression || kind == SyntaxKindArrayLiteralExpression + } + return false +} + +func isTopLevelLogicalExpression(node *Node) bool { + for isParenthesizedExpression(node.parent) || isPrefixUnaryExpression(node.parent) && node.parent.AsPrefixUnaryExpression().operator == SyntaxKindExclamationToken { + node = node.parent + } + return !isStatementCondition(node) && !isLogicalExpression(node.parent) && !(isOptionalChain(node.parent) && getAccessedExpression(node.parent) == node) +} + +func isStatementCondition(node *Node) bool { + switch node.parent.kind { + case SyntaxKindIfStatement: + return node.parent.AsIfStatement().expression == node + case SyntaxKindWhileStatement: + return node.parent.AsWhileStatement().expression == node + case SyntaxKindDoStatement: + return node.parent.AsDoStatement().expression == node + case SyntaxKindForStatement: + return node.parent.AsForStatement().condition == node + case SyntaxKindConditionalExpression: + return node.parent.AsConditionalExpression().condition == node + } + return false +} + +type AssignmentKind int32 + +const ( + AssignmentKindNone AssignmentKind = iota + AssignmentKindDefinite + AssignmentKindCompound +) + +type AssignmentTarget = Node // BinaryExpression | PrefixUnaryExpression | PostfixUnaryExpression | ForInOrOfStatement + +func getAssignmentTargetKind(node *Node) AssignmentKind { + target := getAssignmentTarget(node) + if target == nil { + return AssignmentKindNone + } + switch target.kind { + case SyntaxKindBinaryExpression: + binaryOperator := target.AsBinaryExpression().operatorToken.kind + if binaryOperator == SyntaxKindEqualsToken || isLogicalOrCoalescingAssignmentOperator(binaryOperator) { + return AssignmentKindDefinite + } + return AssignmentKindCompound + case SyntaxKindPrefixUnaryExpression, SyntaxKindPostfixUnaryExpression: + return AssignmentKindCompound + case SyntaxKindForInStatement, SyntaxKindForOfStatement: + return AssignmentKindDefinite + } + panic("Unhandled case in getAssignmentTargetKind") +} + +// A node is an assignment target if it is on the left hand side of an '=' token, if it is parented by a property +// assignment in an object literal that is an assignment target, or if it is parented by an array literal that is +// an assignment target. Examples include 'a = xxx', '{ p: a } = xxx', '[{ a }] = xxx'. +// (Note that `p` is not a target in the above examples, only `a`.) +func isAssignmentTarget(node *Node) bool { + return getAssignmentTarget(node) != nil +} + +// Returns the BinaryExpression, PrefixUnaryExpression, PostfixUnaryExpression, or ForInOrOfStatement that references +// the given node as an assignment target +func getAssignmentTarget(node *Node) *Node { + for { + parent := node.parent + switch parent.kind { + case SyntaxKindBinaryExpression: + if isAssignmentOperator(parent.AsBinaryExpression().operatorToken.kind) && parent.AsBinaryExpression().left == node { + return parent + } + return nil + case SyntaxKindPrefixUnaryExpression: + if parent.AsPrefixUnaryExpression().operator == SyntaxKindPlusPlusToken || parent.AsPrefixUnaryExpression().operator == SyntaxKindMinusMinusToken { + return parent + } + return nil + case SyntaxKindPostfixUnaryExpression: + if parent.AsPostfixUnaryExpression().operator == SyntaxKindPlusPlusToken || parent.AsPostfixUnaryExpression().operator == SyntaxKindMinusMinusToken { + return parent + } + return nil + case SyntaxKindForInStatement, SyntaxKindForOfStatement: + if parent.AsForInOrOfStatement().initializer == node { + return parent + } + return nil + case SyntaxKindParenthesizedExpression, SyntaxKindArrayLiteralExpression, SyntaxKindSpreadElement, SyntaxKindNonNullExpression: + node = parent + case SyntaxKindSpreadAssignment: + node = parent.parent + case SyntaxKindShorthandPropertyAssignment: + if parent.AsShorthandPropertyAssignment().name != node { + return nil + } + node = parent.parent + case SyntaxKindPropertyAssignment: + if parent.AsPropertyAssignment().name == node { + return nil + } + node = parent.parent + default: + return nil + } + } +} + +func isInCompoundLikeAssignment(node *Node) bool { + target := getAssignmentTarget(node) + return target != nil && isAssignmentExpression(target /*excludeCompoundAssignment*/, true) && isCompoundLikeAssignment(target) +} + +func isCompoundLikeAssignment(assignment *Node) bool { + right := skipParentheses(assignment.AsBinaryExpression().right) + return right.kind == SyntaxKindBinaryExpression && isShiftOperatorOrHigher(right.AsBinaryExpression().operatorToken.kind) +} + +func isPushOrUnshiftIdentifier(node *Node) bool { + text := node.AsIdentifier().text + return text == "push" || text == "unshift" +} + +func isBooleanLiteral(node *Node) bool { + return node.kind == SyntaxKindTrueKeyword || node.kind == SyntaxKindFalseKeyword +} + +func isOptionalChain(node *Node) bool { + kind := node.kind + return node.flags&NodeFlagsOptionalChain != 0 && (kind == SyntaxKindPropertyAccessExpression || + kind == SyntaxKindElementAccessExpression || kind == SyntaxKindCallExpression || kind == SyntaxKindNonNullExpression) +} + +func isOptionalChainRoot(node *Node) bool { + return isOptionalChain(node) && !isNonNullExpression(node) && getQuestionDotToken(node) != nil +} + +/** + * Determines whether a node is the expression preceding an optional chain (i.e. `a` in `a?.b`). + */ +func isExpressionOfOptionalChainRoot(node *Node) bool { + return isOptionalChainRoot(node.parent) && getAccessedExpression(node.parent) == node +} + +func isEntityNameExpression(node *Node) bool { + return node.kind == SyntaxKindIdentifier || isPropertyAccessEntityNameExpression(node) +} + +func isPropertyAccessEntityNameExpression(node *Node) bool { + if node.kind == SyntaxKindPropertyAccessExpression { + expr := node.AsPropertyAccessExpression() + return expr.name.kind == SyntaxKindIdentifier && isEntityNameExpression(expr.expression) + } + return false +} + +func isPrologueDirective(node *Node) bool { + return node.kind == SyntaxKindExpressionStatement && node.AsExpressionStatement().expression.kind == SyntaxKindStringLiteral +} + +func nextPoolSize(size int) int { + switch { + case size < 16: + return 16 + case size < 256: + return size * 2 + } + return size +} + +func getStatementsOfBlock(block *Node) []*Statement { + switch block.kind { + case SyntaxKindBlock: + return block.AsBlock().statements + case SyntaxKindModuleBlock: + return block.AsModuleBlock().statements + case SyntaxKindSourceFile: + return block.AsSourceFile().statements + } + panic("Unhandled case in getStatementsOfBlock") +} + +func nodeHasName(statement *Node, id *Node) bool { + name := statement.Name() + if name != nil { + return isIdentifier(name) && name.AsIdentifier().text == id.AsIdentifier().text + } + if isVariableStatement(statement) { + declarations := statement.AsVariableStatement().declarationList.AsVariableDeclarationList().declarations + return some(declarations, func(d *Node) bool { return nodeHasName(d, id) }) + } + return false +} + +func isImportMeta(node *Node) bool { + if node.kind == SyntaxKindMetaProperty { + return node.AsMetaProperty().keywordToken == SyntaxKindImportKeyword && node.AsMetaProperty().name.AsIdentifier().text == "meta" + } + return false +} + +func lastElement[T any](slice []T) T { + if len(slice) != 0 { + return slice[len(slice)-1] + } + return *new(T) +} + +func ensureScriptKind(fileName string, scriptKind ScriptKind) ScriptKind { + // Using scriptKind as a condition handles both: + // - 'scriptKind' is unspecified and thus it is `undefined` + // - 'scriptKind' is set and it is `Unknown` (0) + // If the 'scriptKind' is 'undefined' or 'Unknown' then we attempt + // to get the ScriptKind from the file name. If it cannot be resolved + // from the file name then the default 'TS' script kind is returned. + if scriptKind == ScriptKindUnknown { + scriptKind = getScriptKindFromFileName(fileName) + } + if scriptKind == ScriptKindUnknown { + scriptKind = ScriptKindTS + } + return scriptKind +} + +const ( + ExtensionTs = ".ts" + ExtensionTsx = ".tsx" + ExtensionDts = ".d.ts" + ExtensionJs = ".js" + ExtensionJsx = ".jsx" + ExtensionJson = ".json" + ExtensionTsBuildInfo = ".tsbuildinfo" + ExtensionMjs = ".mjs" + ExtensionMts = ".mts" + ExtensionDmts = ".d.mts" + ExtensionCjs = ".cjs" + ExtensionCts = ".cts" + ExtensionDcts = ".d.cts" +) + +var supportedDeclarationExtensions = []string{ExtensionDts, ExtensionDcts, ExtensionDmts} + +func getScriptKindFromFileName(fileName string) ScriptKind { + dotPos := strings.LastIndex(fileName, ".") + if dotPos >= 0 { + switch strings.ToLower(fileName[dotPos:]) { + case ExtensionJs, ExtensionCjs, ExtensionMjs: + return ScriptKindJS + case ExtensionJsx: + return ScriptKindJSX + case ExtensionTs, ExtensionCts, ExtensionMts: + return ScriptKindTS + case ExtensionTsx: + return ScriptKindTSX + case ExtensionJson: + return ScriptKindJSON + } + } + return ScriptKindUnknown +} + +func getLanguageVariant(scriptKind ScriptKind) LanguageVariant { + switch scriptKind { + case ScriptKindTSX, ScriptKindJSX, ScriptKindJS, ScriptKindJSON: + // .tsx and .jsx files are treated as jsx language variant. + return LanguageVariantJSX + } + return LanguageVariantStandard +} + +func getEmitScriptTarget(options *CompilerOptions) ScriptTarget { + if options.Target != ScriptTargetNone { + return options.Target + } + return ScriptTargetES5 +} + +func getEmitModuleKind(options *CompilerOptions) ModuleKind { + if options.ModuleKind != ModuleKindNone { + return options.ModuleKind + } + if options.Target >= ScriptTargetES2015 { + return ModuleKindES2015 + } + return ModuleKindCommonJS +} + +func getEmitModuleResolutionKind(options *CompilerOptions) ModuleResolutionKind { + if options.ModuleResolution != ModuleResolutionKindUnknown { + return options.ModuleResolution + } + switch getEmitModuleKind(options) { + case ModuleKindCommonJS: + return ModuleResolutionKindNode10 + case ModuleKindNode16: + return ModuleResolutionKindNode16 + case ModuleKindNodeNext: + return ModuleResolutionKindNodeNext + case ModuleKindPreserve: + return ModuleResolutionKindBundler + } + return ModuleResolutionKindClassic +} + +func getESModuleInterop(options *CompilerOptions) bool { + if options.ESModuleInterop != TSUnknown { + return options.ESModuleInterop == TSTrue + } + switch getEmitModuleKind(options) { + case ModuleKindNode16: + case ModuleKindNodeNext: + case ModuleKindPreserve: + return true + } + return false + +} +func getAllowSyntheticDefaultImports(options *CompilerOptions) bool { + if options.AllowSyntheticDefaultImports != TSUnknown { + return options.AllowSyntheticDefaultImports == TSTrue + } + return getESModuleInterop(options) || + getEmitModuleKind(options) == ModuleKindSystem || + getEmitModuleResolutionKind(options) == ModuleResolutionKindBundler +} + +type DiagnosticsCollection struct { + fileDiagnostics map[string][]*Diagnostic + nonFileDiagnostics []*Diagnostic +} + +func (c *DiagnosticsCollection) add(diagnostic *Diagnostic) { + if diagnostic.file != nil { + fileName := diagnostic.file.fileName + if c.fileDiagnostics == nil { + c.fileDiagnostics = make(map[string][]*Diagnostic) + } + c.fileDiagnostics[fileName] = insertSorted(c.fileDiagnostics[fileName], diagnostic, compareDiagnostics) + } else { + c.nonFileDiagnostics = insertSorted(c.nonFileDiagnostics, diagnostic, compareDiagnostics) + } +} + +func (c *DiagnosticsCollection) lookup(diagnostic *Diagnostic) *Diagnostic { + var diagnostics []*Diagnostic + if diagnostic.file != nil { + diagnostics = c.fileDiagnostics[diagnostic.file.fileName] + } else { + diagnostics = c.nonFileDiagnostics + } + if i, ok := slices.BinarySearchFunc(diagnostics, diagnostic, compareDiagnostics); ok { + return diagnostics[i] + } + return nil +} + +func (c *DiagnosticsCollection) GetGlobalDiagnostics() []*Diagnostic { + return c.nonFileDiagnostics +} + +func (c *DiagnosticsCollection) GetDiagnosticsForFile(fileName string) []*Diagnostic { + return c.fileDiagnostics[fileName] +} + +func (c *DiagnosticsCollection) GetDiagnostics() []*Diagnostic { + fileNames := slices.Collect(maps.Keys(c.fileDiagnostics)) + slices.Sort(fileNames) + diagnostics := c.nonFileDiagnostics + for _, fileName := range fileNames { + diagnostics = append(diagnostics, c.fileDiagnostics[fileName]...) + } + return diagnostics +} + +func sortAndDeduplicateDiagnostics(diagnostics []*Diagnostic) []*Diagnostic { + result := slices.Clone(diagnostics) + slices.SortFunc(result, compareDiagnostics) + return slices.CompactFunc(result, equalDiagnostics) +} + +func equalDiagnostics(d1, d2 *Diagnostic) bool { + return getDiagnosticPath(d1) == getDiagnosticPath(d2) && + d1.loc == d2.loc && + d1.code == d2.code && + d1.message == d2.message && + slices.EqualFunc(d1.messageChain, d2.messageChain, equalMessageChain) && + slices.EqualFunc(d1.relatedInformation, d2.relatedInformation, equalDiagnostics) +} + +func equalMessageChain(c1, c2 *MessageChain) bool { + return c1.code == c2.code && + c1.message == c2.message && + slices.EqualFunc(c1.messageChain, c2.messageChain, equalMessageChain) +} + +func compareDiagnostics(d1, d2 *Diagnostic) int { + c := strings.Compare(getDiagnosticPath(d1), getDiagnosticPath(d2)) + if c != 0 { + return c + } + c = int(d1.loc.pos) - int(d2.loc.pos) + if c != 0 { + return c + } + c = int(d1.loc.end) - int(d2.loc.end) + if c != 0 { + return c + } + c = int(d1.code) - int(d2.code) + if c != 0 { + return c + } + c = strings.Compare(d1.message, d2.message) + if c != 0 { + return c + } + c = compareMessageChainSize(d1.messageChain, d2.messageChain) + if c != 0 { + return c + } + c = compareMessageChainContent(d1.messageChain, d2.messageChain) + if c != 0 { + return c + } + return compareRelatedInfo(d1.relatedInformation, d2.relatedInformation) +} + +func compareMessageChainSize(c1, c2 []*MessageChain) int { + c := len(c2) - len(c1) + if c != 0 { + return c + } + for i := range c1 { + c = compareMessageChainSize(c1[i].messageChain, c2[i].messageChain) + if c != 0 { + return c + } + } + return 0 +} + +func compareMessageChainContent(c1, c2 []*MessageChain) int { + for i := range c1 { + c := strings.Compare(c1[i].message, c2[i].message) + if c != 0 { + return c + } + if c1[i].messageChain != nil { + c = compareMessageChainContent(c1[i].messageChain, c2[i].messageChain) + if c != 0 { + return c + } + } + } + return 0 +} + +func compareRelatedInfo(r1, r2 []*Diagnostic) int { + c := len(r2) - len(r1) + if c != 0 { + return c + } + for i := range r1 { + c = compareDiagnostics(r1[i], r2[i]) + if c != 0 { + return c + } + } + return 0 +} + +func getDiagnosticPath(d *Diagnostic) string { + if d.file != nil { + return d.file.path + } + return "" +} + +func isConstAssertion(location *Node) bool { + switch location.kind { + case SyntaxKindAsExpression: + return isConstTypeReference(location.AsAsExpression().typeNode) + case SyntaxKindTypeAssertionExpression: + return isConstTypeReference(location.AsTypeAssertion().typeNode) + } + return false +} + +func isConstTypeReference(node *Node) bool { + if node.kind == SyntaxKindTypeReference { + ref := node.AsTypeReference() + return ref.typeArguments == nil && isIdentifier(ref.typeName) && ref.typeName.AsIdentifier().text == "const" + } + return false +} + +func isModuleOrEnumDeclaration(node *Node) bool { + return node.kind == SyntaxKindModuleDeclaration || node.kind == SyntaxKindEnumDeclaration +} + +func getLocalsOfNode(node *Node) SymbolTable { + data := node.LocalsContainerData() + if data != nil { + return data.locals + } + return nil +} + +func getBodyOfNode(node *Node) *Node { + bodyData := node.BodyData() + if bodyData != nil { + return bodyData.body + } + return nil +} + +func isGlobalSourceFile(node *Node) bool { + return node.kind == SyntaxKindSourceFile && !isExternalOrCommonJsModule(node.AsSourceFile()) +} + +func isParameterLikeOrReturnTag(node *Node) bool { + switch node.kind { + case SyntaxKindParameter, SyntaxKindTypeParameter, SyntaxKindJSDocParameterTag, SyntaxKindJSDocReturnTag: + return true + } + return false +} + +func getEmitStandardClassFields(options *CompilerOptions) bool { + return options.UseDefineForClassFields != TSFalse && getEmitScriptTarget(options) >= ScriptTargetES2022 +} + +func isTypeNodeKind(kind SyntaxKind) bool { + switch kind { + case SyntaxKindAnyKeyword, SyntaxKindUnknownKeyword, SyntaxKindNumberKeyword, SyntaxKindBigIntKeyword, SyntaxKindObjectKeyword, + SyntaxKindBooleanKeyword, SyntaxKindStringKeyword, SyntaxKindSymbolKeyword, SyntaxKindVoidKeyword, SyntaxKindUndefinedKeyword, + SyntaxKindNeverKeyword, SyntaxKindIntrinsicKeyword, SyntaxKindExpressionWithTypeArguments, SyntaxKindJSDocAllType, SyntaxKindJSDocUnknownType, + SyntaxKindJSDocNullableType, SyntaxKindJSDocNonNullableType, SyntaxKindJSDocOptionalType, SyntaxKindJSDocFunctionType, SyntaxKindJSDocVariadicType: + return true + } + return kind >= SyntaxKindFirstTypeNode && kind <= SyntaxKindLastTypeNode +} + +func isTypeNode(node *Node) bool { + return isTypeNodeKind(node.kind) +} + +func getLocalSymbolForExportDefault(symbol *Symbol) *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 *Symbol) bool { + return symbol != nil && len(symbol.declarations) > 0 && hasSyntacticModifier(symbol.declarations[0], ModifierFlagsDefault) +} + +func getDeclarationOfKind(symbol *Symbol, kind SyntaxKind) *Node { + for _, declaration := range symbol.declarations { + if declaration.kind == kind { + return declaration + } + } + return nil +} + +func getIsolatedModules(options *CompilerOptions) bool { + return options.IsolatedModules == TSTrue || options.VerbatimModuleSyntax == TSTrue +} + +func findConstructorDeclaration(node *Node) *Node { + for _, member := range node.ClassLikeData().members { + if isConstructorDeclaration(member) && nodeIsPresent(member.AsConstructorDeclaration().body) { + return member + } + } + return nil +} + +type NameResolver struct { + compilerOptions *CompilerOptions + getSymbolOfDeclaration func(node *Node) *Symbol + error func(location *Node, message *diagnostics.Message, args ...any) *Diagnostic + globals SymbolTable + argumentsSymbol *Symbol + requireSymbol *Symbol + lookup func(symbols SymbolTable, name string, meaning SymbolFlags) *Symbol + setRequiresScopeChangeCache func(node *Node, value Tristate) + getRequiresScopeChangeCache func(node *Node) Tristate + onPropertyWithInvalidInitializer func(location *Node, name string, declaration *Node, result *Symbol) bool + onFailedToResolveSymbol func(location *Node, name string, meaning SymbolFlags, nameNotFoundMessage *diagnostics.Message) + onSuccessfullyResolvedSymbol func(location *Node, result *Symbol, meaning SymbolFlags, lastLocation *Node, associatedDeclarationForContainingInitializerOrBindingName *Node, withinDeferredContext bool) +} + +func (r *NameResolver) resolve(location *Node, name string, meaning SymbolFlags, nameNotFoundMessage *diagnostics.Message, isUse bool, excludeGlobals bool) *Symbol { + var result *Symbol + var lastLocation *Node + var lastSelfReferenceLocation *Node + var propertyWithInvalidInitializer *Node + var associatedDeclarationForContainingInitializerOrBindingName *Node + var withinDeferredContext bool + var grandparent *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 && 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 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 := getLocalsOfNode(location) + // Locals of a source file are not in scope (because they get merged into the global symbol table) + if locals != nil && !isGlobalSourceFile(location) { + result = r.lookup(locals, name, meaning) + if result != nil { + useResult := true + if isFunctionLike(location) && lastLocation != nil && lastLocation != getBodyOfNode(location) { + // symbol lookup restrictions for function-like declarations + // - Type parameters of a function are in scope in the entire function declaration, including the parameter + // list and return type. However, local types are only in scope in the function body. + // - parameters are only in the scope of function body + // This restriction does not apply to JSDoc comment types because they are parented + // at a higher level than type parameters would normally be + if meaning&result.flags&SymbolFlagsType != 0 && lastLocation.kind != SyntaxKindJSDoc { + useResult = result.flags&SymbolFlagsTypeParameter != 0 && (lastLocation.flags&NodeFlagsSynthesized != 0 || + lastLocation == location.FunctionLikeData().returnType || + isParameterLikeOrReturnTag(lastLocation)) + } + if meaning&result.flags&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&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 == SyntaxKindParameter || + lastLocation.flags&NodeFlagsSynthesized != 0 || + lastLocation == location.FunctionLikeData().returnType && findAncestor(result.valueDeclaration, isParameter) != nil + } + } + } else if location.kind == SyntaxKindConditionalType { + // 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 SyntaxKindSourceFile: + if !isExternalOrCommonJsModule(location.AsSourceFile()) { + break + } + fallthrough + case SyntaxKindModuleDeclaration: + moduleExports := r.getSymbolOfDeclaration(location).exports + if isSourceFile(location) || (isModuleDeclaration(location) && location.flags&NodeFlagsAmbient != 0 && !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[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 == SymbolFlagsAlias && (getDeclarationOfKind(moduleExport, SyntaxKindExportSpecifier) != nil || getDeclarationOfKind(moduleExport, SyntaxKindNamespaceExport) != nil) { + break + } + } + if name != InternalSymbolNameDefault { + result = r.lookup(moduleExports, name, meaning&SymbolFlagsModuleMember) + if result != nil { + break loop + } + } + case SyntaxKindEnumDeclaration: + result = r.lookup(r.getSymbolOfDeclaration(location).exports, name, meaning&SymbolFlagsEnumMember) + if result != nil { + if nameNotFoundMessage != nil && getIsolatedModules(r.compilerOptions) && location.flags&NodeFlagsAmbient == 0 && getSourceFileOfNode(location) != getSourceFileOfNode(result.valueDeclaration) { + isolatedModulesLikeFlagName := ifElse(r.compilerOptions.VerbatimModuleSyntax == 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 SyntaxKindPropertyDeclaration: + if !isStatic(location) { + ctor := findConstructorDeclaration(location.parent) + if ctor != nil && ctor.AsConstructorDeclaration().locals != nil { + if r.lookup(ctor.AsConstructorDeclaration().locals, name, meaning&SymbolFlagsValue) != nil { + // Remember the property node, it will be used later to report appropriate error + propertyWithInvalidInitializer = location + } + } + } + case SyntaxKindClassDeclaration, SyntaxKindClassExpression, SyntaxKindInterfaceDeclaration: + result = r.lookup(r.getSymbolOfDeclaration(location).members, name, meaning&SymbolFlagsType) + if result != nil { + if !isTypeParameterSymbolDeclaredInContainer(result, location) { + // ignore type parameters not declared in this container + result = nil + break + } + if lastLocation != nil && 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 isClassExpression(location) && meaning&SymbolFlagsClass != 0 { + className := location.AsClassExpression().name + if className != nil && name == className.AsIdentifier().text { + result = location.AsClassExpression().symbol + break loop + } + } + case SyntaxKindExpressionWithTypeArguments: + if lastLocation == location.AsExpressionWithTypeArguments().expression && isHeritageClause(location.parent) && location.parent.AsHeritageClause().token == SyntaxKindExtendsKeyword { + container := location.parent.parent + if isClassLike(container) { + result = r.lookup(r.getSymbolOfDeclaration(container).members, name, meaning&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 SyntaxKindComputedPropertyName: + grandparent = location.parent.parent + if isClassLike(grandparent) || isInterfaceDeclaration(grandparent) { + // A reference to this grandparent's type parameters would be an error + result = r.lookup(r.getSymbolOfDeclaration(grandparent).members, name, meaning&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 SyntaxKindArrowFunction: + // when targeting ES6 or higher there is no 'arguments' in an arrow function + // for lower compile targets the resolved symbol is used to emit an error + if getEmitScriptTarget(r.compilerOptions) >= ScriptTargetES2015 { + break + } + fallthrough + case SyntaxKindMethodDeclaration, SyntaxKindConstructor, SyntaxKindGetAccessor, SyntaxKindSetAccessor, SyntaxKindFunctionDeclaration: + if meaning&SymbolFlagsVariable != 0 && name == "arguments" { + result = r.argumentsSymbol + break loop + } + case SyntaxKindFunctionExpression: + if meaning&SymbolFlagsVariable != 0 && name == "arguments" { + result = r.argumentsSymbol + break loop + } + if meaning&SymbolFlagsFunction != 0 { + functionName := location.AsFunctionExpression().name + if functionName != nil && name == functionName.AsIdentifier().text { + result = location.AsFunctionExpression().symbol + break loop + } + } + case SyntaxKindDecorator: + // 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 == SyntaxKindParameter { + 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 && (isClassElement(location.parent) || location.parent.kind == SyntaxKindClassDeclaration) { + location = location.parent + } + case SyntaxKindParameter: + parameterDeclaration := location.AsParameterDeclaration() + if lastLocation != nil && (lastLocation == parameterDeclaration.initializer || + lastLocation == parameterDeclaration.name && isBindingPattern(lastLocation)) { + if associatedDeclarationForContainingInitializerOrBindingName == nil { + associatedDeclarationForContainingInitializerOrBindingName = location + } + } + case SyntaxKindBindingElement: + bindingElement := location.AsBindingElement() + if lastLocation != nil && (lastLocation == bindingElement.initializer || + lastLocation == bindingElement.name && isBindingPattern(lastLocation)) { + if isPartOfParameterDeclaration(location) && associatedDeclarationForContainingInitializerOrBindingName == nil { + associatedDeclarationForContainingInitializerOrBindingName = location + } + } + case SyntaxKindInferType: + if meaning&SymbolFlagsTypeParameter != 0 { + parameterName := location.AsInferTypeNode().typeParameter.AsTypeParameter().name + if parameterName != nil && name == parameterName.AsIdentifier().text { + result = location.AsInferTypeNode().typeParameter.AsTypeParameter().symbol + break loop + } + } + case SyntaxKindExportSpecifier: + 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 + switch { + // case isJSDocTemplateTag(location): + // location = getEffectiveContainerForJSDocTemplateTag(location.(*JSDocTemplateTag)) + // if location == nil { + // location = location.parent + // } + // case isJSDocParameterTag(location) || isJSDocReturnTag(location): + // location = getHostSignatureFromJSDoc(location) + // if location == nil { + // location = location.parent + // } + default: + 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()) { + // !!! result.isReferenced |= meaning + } + if result == nil { + if !excludeGlobals { + result = r.lookup(r.globals, name, meaning) + } + } + if nameNotFoundMessage != nil { + if propertyWithInvalidInitializer != nil && r.onPropertyWithInvalidInitializer(originalLocation, name, propertyWithInvalidInitializer, result) { + return nil + } + if result == nil { + r.onFailedToResolveSymbol(originalLocation, name, meaning, nameNotFoundMessage) + } else { + r.onSuccessfullyResolvedSymbol(originalLocation, result, meaning, lastLocation, associatedDeclarationForContainingInitializerOrBindingName, withinDeferredContext) + } + } + return result +} + +func (r *NameResolver) useOuterVariableScopeInParameter(result *Symbol, location *Node, lastLocation *Node) bool { + if isParameter(lastLocation) { + body := getBodyOfNode(location) + 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 := getEmitScriptTarget(r.compilerOptions) + if target >= ScriptTargetES2015 { + functionLocation := location + declarationRequiresScopeChange := r.getRequiresScopeChangeCache(functionLocation) + if declarationRequiresScopeChange == TSUnknown { + declarationRequiresScopeChange = boolToTristate(some(functionLocation.FunctionLikeData().parameters, r.requiresScopeChange)) + r.setRequiresScopeChangeCache(functionLocation, declarationRequiresScopeChange) + } + return declarationRequiresScopeChange == TSTrue + } + } + } + return false +} + +func (r *NameResolver) requiresScopeChange(node *Node) bool { + d := node.AsParameterDeclaration() + return r.requiresScopeChangeWorker(d.name) || d.initializer != nil && r.requiresScopeChangeWorker(d.initializer) +} + +func (r *NameResolver) requiresScopeChangeWorker(node *Node) bool { + switch node.kind { + case SyntaxKindArrowFunction, SyntaxKindFunctionExpression, SyntaxKindFunctionDeclaration, SyntaxKindConstructor: + return false + case SyntaxKindMethodDeclaration, SyntaxKindGetAccessor, SyntaxKindSetAccessor, SyntaxKindPropertyAssignment: + return r.requiresScopeChangeWorker(node.Name()) + case SyntaxKindPropertyDeclaration: + if hasStaticModifier(node) { + return !getEmitStandardClassFields(r.compilerOptions) + } + return r.requiresScopeChangeWorker(node.AsPropertyDeclaration().name) + default: + if isNullishCoalesce(node) || isOptionalChain(node) { + return getEmitScriptTarget(r.compilerOptions) < ScriptTargetES2020 + } + if isBindingElement(node) && node.AsBindingElement().dotDotDotToken != nil && isObjectBindingPattern(node.parent) { + return getEmitScriptTarget(r.compilerOptions) < ScriptTargetES2017 + } + if isTypeNode(node) { + return false + } + return node.ForEachChild(r.requiresScopeChangeWorker) + } +} + +func getIsDeferredContext(location *Node, lastLocation *Node) bool { + if location.kind != SyntaxKindArrowFunction && location.kind != SyntaxKindFunctionExpression { + // 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 isTypeQueryNode(location) || + (isFunctionLikeDeclaration(location) || location.kind == SyntaxKindPropertyDeclaration && !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 || hasSyntacticModifier(location, ModifierFlagsAsync) { + return true + } + return getImmediatelyInvokedFunctionExpression(location) == nil +} + +func isTypeParameterSymbolDeclaredInContainer(symbol *Symbol, container *Node) bool { + for _, decl := range symbol.declarations { + if decl.kind == SyntaxKindTypeParameter { + parent := decl.parent.parent + if parent == container { + return true + } + } + } + return false +} + +func isSelfReferenceLocation(node *Node, lastLocation *Node) bool { + switch node.kind { + case SyntaxKindParameter: + return lastLocation != nil && lastLocation == node.AsParameterDeclaration().name + case SyntaxKindFunctionDeclaration, SyntaxKindClassDeclaration, SyntaxKindInterfaceDeclaration, SyntaxKindEnumDeclaration, + SyntaxKindTypeAliasDeclaration, SyntaxKindModuleDeclaration: // For `namespace N { N; }` + return true + } + return false +} + +func isTypeReferenceIdentifier(node *Node) bool { + for node.parent.kind == SyntaxKindQualifiedName { + node = node.parent + } + return isTypeReferenceNode(node.parent) +} + +func isInTypeQuery(node *Node) bool { + // TypeScript 1.0 spec (April 2014): 3.6.3 + // A type query consists of the keyword typeof followed by an expression. + // The expression is restricted to a single identifier or a sequence of identifiers separated by periods + return findAncestorOrQuit(node, func(n *Node) FindAncestorResult { + switch n.kind { + case SyntaxKindTypeQuery: + return FindAncestorTrue + case SyntaxKindIdentifier, SyntaxKindQualifiedName: + return FindAncestorFalse + } + return FindAncestorQuit + }) != nil +} + +func nodeKindIs(node *Node, kinds ...SyntaxKind) bool { + return slices.Contains(kinds, node.kind) +} + +func isTypeOnlyImportDeclaration(node *Node) bool { + switch node.kind { + case SyntaxKindImportSpecifier: + return node.AsImportSpecifier().isTypeOnly || node.parent.parent.AsImportClause().isTypeOnly + case SyntaxKindNamespaceImport: + return node.parent.AsImportClause().isTypeOnly + case SyntaxKindImportClause: + return node.AsImportClause().isTypeOnly + case SyntaxKindImportEqualsDeclaration: + return node.AsImportEqualsDeclaration().isTypeOnly + } + return false +} + +func isTypeOnlyExportDeclaration(node *Node) bool { + switch node.kind { + case SyntaxKindExportSpecifier: + return node.AsExportSpecifier().isTypeOnly || node.parent.parent.AsExportDeclaration().isTypeOnly + case SyntaxKindExportDeclaration: + d := node.AsExportDeclaration() + return d.isTypeOnly && d.moduleSpecifier != nil && d.exportClause == nil + case SyntaxKindNamespaceExport: + return node.parent.AsExportDeclaration().isTypeOnly + } + return false +} + +func isTypeOnlyImportOrExportDeclaration(node *Node) bool { + return isTypeOnlyImportDeclaration(node) || isTypeOnlyExportDeclaration(node) +} + +func getNameFromImportDeclaration(node *Node) *Node { + switch node.kind { + case SyntaxKindImportSpecifier: + return node.AsImportSpecifier().name + case SyntaxKindNamespaceImport: + return node.AsNamespaceImport().name + case SyntaxKindImportClause: + return node.AsImportClause().name + case SyntaxKindImportEqualsDeclaration: + return node.AsImportEqualsDeclaration().name + } + return nil +} + +func isValidTypeOnlyAliasUseSite(useSite *Node) bool { + return useSite.flags&NodeFlagsAmbient != 0 || + isPartOfTypeQuery(useSite) || + isIdentifierInNonEmittingHeritageClause(useSite) || + isPartOfPossiblyValidTypeOrAbstractComputedPropertyName(useSite) || + !(isExpressionNode(useSite) || isShorthandPropertyNameUseSite(useSite)) +} + +func isIdentifierInNonEmittingHeritageClause(node *Node) bool { + if node.kind != SyntaxKindIdentifier { + return false + } + heritageClause := findAncestorOrQuit(node.parent, func(parent *Node) FindAncestorResult { + switch parent.kind { + case SyntaxKindHeritageClause: + return FindAncestorTrue + case SyntaxKindPropertyAccessExpression, SyntaxKindExpressionWithTypeArguments: + return FindAncestorFalse + default: + return FindAncestorQuit + } + }) + if heritageClause != nil { + return heritageClause.AsHeritageClause().token == SyntaxKindImmediateKeyword || heritageClause.parent.kind == SyntaxKindInterfaceDeclaration + } + return false +} + +func isPartOfPossiblyValidTypeOrAbstractComputedPropertyName(node *Node) bool { + for nodeKindIs(node, SyntaxKindIdentifier, SyntaxKindPropertyAccessExpression) { + node = node.parent + } + if node.kind != SyntaxKindComputedPropertyName { + return false + } + if hasSyntacticModifier(node.parent, ModifierFlagsAbstract) { + return true + } + return nodeKindIs(node.parent.parent, SyntaxKindInterfaceDeclaration, SyntaxKindTypeLiteral) +} + +func isExpressionNode(node *Node) bool { + switch node.kind { + case SyntaxKindSuperKeyword, SyntaxKindNullKeyword, SyntaxKindTrueKeyword, SyntaxKindFalseKeyword, SyntaxKindRegularExpressionLiteral, + SyntaxKindArrayLiteralExpression, SyntaxKindObjectLiteralExpression, SyntaxKindPropertyAccessExpression, SyntaxKindElementAccessExpression, + SyntaxKindCallExpression, SyntaxKindNewExpression, SyntaxKindTaggedTemplateExpression, SyntaxKindAsExpression, SyntaxKindTypeAssertionExpression, + SyntaxKindSatisfiesExpression, SyntaxKindNonNullExpression, SyntaxKindParenthesizedExpression, SyntaxKindFunctionExpression, + SyntaxKindClassExpression, SyntaxKindArrowFunction, SyntaxKindVoidExpression, SyntaxKindDeleteExpression, SyntaxKindTypeOfExpression, + SyntaxKindPrefixUnaryExpression, SyntaxKindPostfixUnaryExpression, SyntaxKindBinaryExpression, SyntaxKindConditionalExpression, + SyntaxKindSpreadElement, SyntaxKindTemplateExpression, SyntaxKindOmittedExpression, SyntaxKindJsxElement, SyntaxKindJsxSelfClosingElement, + SyntaxKindJsxFragment, SyntaxKindYieldExpression, SyntaxKindAwaitExpression, SyntaxKindMetaProperty: + return true + case SyntaxKindExpressionWithTypeArguments: + return !isHeritageClause(node.parent) + case SyntaxKindQualifiedName: + for node.parent.kind == SyntaxKindQualifiedName { + node = node.parent + } + return isTypeQueryNode(node.parent) || isJSDocLinkLike(node.parent) || isJSXTagName(node) + case SyntaxKindJSDocMemberName: + return isTypeQueryNode(node.parent) || isJSDocLinkLike(node.parent) || isJSXTagName(node) + case SyntaxKindPrivateIdentifier: + return isBinaryExpression(node.parent) && node.parent.AsBinaryExpression().left == node && node.parent.AsBinaryExpression().operatorToken.kind == SyntaxKindInKeyword + case SyntaxKindIdentifier: + if isTypeQueryNode(node.parent) || isJSDocLinkLike(node.parent) || isJSXTagName(node) { + return true + } + fallthrough + case SyntaxKindNumericLiteral, SyntaxKindBigintLiteral, SyntaxKindStringLiteral, SyntaxKindNoSubstitutionTemplateLiteral, SyntaxKindThisKeyword: + return isInExpressionContext(node) + default: + return false + } +} + +func isInExpressionContext(node *Node) bool { + parent := node.parent + switch parent.kind { + case SyntaxKindVariableDeclaration: + return parent.AsVariableDeclaration().initializer == node + case SyntaxKindParameter: + return parent.AsParameterDeclaration().initializer == node + case SyntaxKindPropertyDeclaration: + return parent.AsPropertyDeclaration().initializer == node + case SyntaxKindPropertySignature: + return parent.AsPropertySignatureDeclaration().initializer == node + case SyntaxKindEnumMember: + return parent.AsEnumMember().initializer == node + case SyntaxKindPropertyAssignment: + return parent.AsPropertyAssignment().initializer == node + case SyntaxKindBindingElement: + return parent.AsBindingElement().initializer == node + case SyntaxKindExpressionStatement: + return parent.AsExpressionStatement().expression == node + case SyntaxKindIfStatement: + return parent.AsIfStatement().expression == node + case SyntaxKindDoStatement: + return parent.AsDoStatement().expression == node + case SyntaxKindWhileStatement: + return parent.AsWhileStatement().expression == node + case SyntaxKindReturnStatement: + return parent.AsReturnStatement().expression == node + case SyntaxKindWithStatement: + return parent.AsWithStatement().expression == node + case SyntaxKindSwitchStatement: + return parent.AsSwitchStatement().expression == node + case SyntaxKindCaseClause, SyntaxKindDefaultClause: + return parent.AsCaseOrDefaultClause().expression == node + case SyntaxKindThrowStatement: + return parent.AsThrowStatement().expression == node + case SyntaxKindForStatement: + s := parent.AsForStatement() + return s.initializer == node && s.initializer.kind != SyntaxKindVariableDeclarationList || s.condition == node || s.incrementor == node + case SyntaxKindForInStatement, SyntaxKindForOfStatement: + s := parent.AsForInOrOfStatement() + return s.initializer == node && s.initializer.kind != SyntaxKindVariableDeclarationList || s.expression == node + case SyntaxKindTypeAssertionExpression: + return parent.AsTypeAssertion().expression == node + case SyntaxKindAsExpression: + return parent.AsAsExpression().expression == node + case SyntaxKindTemplateSpan: + return parent.AsTemplateSpan().expression == node + case SyntaxKindComputedPropertyName: + return parent.AsComputedPropertyName().expression == node + case SyntaxKindDecorator, SyntaxKindJsxExpression, SyntaxKindJsxSpreadAttribute, SyntaxKindSpreadAssignment: + return true + case SyntaxKindExpressionWithTypeArguments: + return parent.AsExpressionWithTypeArguments().expression == node && !isPartOfTypeNode(parent) + case SyntaxKindShorthandPropertyAssignment: + return parent.AsShorthandPropertyAssignment().objectAssignmentInitializer == node + case SyntaxKindSatisfiesExpression: + return parent.AsSatisfiesExpression().expression == node + default: + return isExpressionNode(parent) + } +} + +func isPartOfTypeNode(node *Node) bool { + kind := node.kind + if kind >= SyntaxKindFirstTypeNode && kind <= SyntaxKindLastTypeNode { + return true + } + switch node.kind { + case SyntaxKindAnyKeyword, SyntaxKindUnknownKeyword, SyntaxKindNumberKeyword, SyntaxKindBigIntKeyword, SyntaxKindStringKeyword, + SyntaxKindBooleanKeyword, SyntaxKindSymbolKeyword, SyntaxKindObjectKeyword, SyntaxKindUndefinedKeyword, SyntaxKindNullKeyword, + SyntaxKindNeverKeyword: + return true + case SyntaxKindExpressionWithTypeArguments: + return isPartOfTypeExpressionWithTypeArguments(node) + case SyntaxKindTypeParameter: + return node.parent.kind == SyntaxKindMappedType || node.parent.kind == SyntaxKindInferType + case SyntaxKindIdentifier: + parent := node.parent + if isQualifiedName(parent) && parent.AsQualifiedName().right == node { + return isPartOfTypeNodeInParent(parent) + } + if isPropertyAccessExpression(parent) && parent.AsPropertyAccessExpression().name == node { + return isPartOfTypeNodeInParent(parent) + } + return isPartOfTypeNodeInParent(node) + case SyntaxKindQualifiedName, SyntaxKindPropertyAccessExpression, SyntaxKindThisKeyword: + return isPartOfTypeNodeInParent(node) + } + return false +} + +func isPartOfTypeNodeInParent(node *Node) bool { + parent := node.parent + // Do not recursively call isPartOfTypeNode on the parent. In the example: + // + // let a: A.B.C; + // + // Calling isPartOfTypeNode would consider the qualified name A.B a type node. + // Only C and A.B.C are type nodes. + if parent.kind >= SyntaxKindFirstTypeNode && parent.kind <= SyntaxKindLastTypeNode { + return true + } + switch parent.kind { + case SyntaxKindTypeQuery: + return false + case SyntaxKindImportType: + return !parent.AsImportTypeNode().isTypeOf + case SyntaxKindExpressionWithTypeArguments: + return isPartOfTypeExpressionWithTypeArguments(parent) + case SyntaxKindTypeParameter: + return node == parent.AsTypeParameter().constraint + case SyntaxKindPropertyDeclaration: + return node == parent.AsPropertyDeclaration().typeNode + case SyntaxKindPropertySignature: + return node == parent.AsPropertySignatureDeclaration().typeNode + case SyntaxKindParameter: + return node == parent.AsParameterDeclaration().typeNode + case SyntaxKindVariableDeclaration: + return node == parent.AsVariableDeclaration().typeNode + case SyntaxKindFunctionDeclaration, SyntaxKindFunctionExpression, SyntaxKindArrowFunction, SyntaxKindConstructor, SyntaxKindMethodDeclaration, + SyntaxKindMethodSignature, SyntaxKindGetAccessor, SyntaxKindSetAccessor, SyntaxKindCallSignature, SyntaxKindConstructSignature, + SyntaxKindIndexSignature: + return node == parent.FunctionLikeData().returnType + case SyntaxKindTypeAssertionExpression: + return node == parent.AsTypeAssertion().typeNode + case SyntaxKindCallExpression: + return typeArgumentListContains(parent.AsCallExpression().typeArguments, node) + case SyntaxKindNewExpression: + return typeArgumentListContains(parent.AsNewExpression().typeArguments, node) + case SyntaxKindTaggedTemplateExpression: + return typeArgumentListContains(parent.AsTaggedTemplateExpression().typeArguments, node) + } + return false +} + +func isPartOfTypeExpressionWithTypeArguments(node *Node) bool { + parent := node.parent + return isHeritageClause(parent) && (!isClassLike(parent.parent) || parent.AsHeritageClause().token == SyntaxKindImplementsKeyword) +} + +func typeArgumentListContains(list *Node, node *Node) bool { + if list != nil { + return slices.Contains(list.AsTypeArgumentList().arguments, node) + } + return false +} + +func isJSDocLinkLike(node *Node) bool { + return nodeKindIs(node, SyntaxKindJSDocLink, SyntaxKindJSDocLinkCode, SyntaxKindJSDocLinkPlain) +} + +func isJSXTagName(node *Node) bool { + parent := node.parent + switch parent.kind { + case SyntaxKindJsxOpeningElement: + return parent.AsJsxOpeningElement().tagName == node + case SyntaxKindJsxSelfClosingElement: + return parent.AsJsxSelfClosingElement().tagName == node + case SyntaxKindJsxClosingElement: + return parent.AsJsxClosingElement().tagName == node + } + return false +} + +func isShorthandPropertyNameUseSite(useSite *Node) bool { + return isIdentifier(useSite) && isShorthandPropertyAssignment(useSite.parent) && useSite.parent.AsShorthandPropertyAssignment().name == useSite +} + +func isTypeDeclaration(node *Node) bool { + switch node.kind { + case SyntaxKindTypeParameter, SyntaxKindClassDeclaration, SyntaxKindInterfaceDeclaration, SyntaxKindTypeAliasDeclaration, SyntaxKindEnumDeclaration: + return true + case SyntaxKindImportClause: + return node.AsImportClause().isTypeOnly + case SyntaxKindImportSpecifier: + return node.parent.parent.AsImportClause().isTypeOnly + case SyntaxKindExportSpecifier: + return node.parent.parent.AsExportDeclaration().isTypeOnly + default: + return false + } +} + +func canHaveSymbol(node *Node) bool { + switch node.kind { + case SyntaxKindArrowFunction, SyntaxKindBinaryExpression, SyntaxKindBindingElement, SyntaxKindCallExpression, SyntaxKindCallSignature, + SyntaxKindClassDeclaration, SyntaxKindClassExpression, SyntaxKindClassStaticBlockDeclaration, SyntaxKindConstructor, SyntaxKindConstructorType, + SyntaxKindConstructSignature, SyntaxKindElementAccessExpression, SyntaxKindEnumDeclaration, SyntaxKindEnumMember, SyntaxKindExportAssignment, + SyntaxKindExportDeclaration, SyntaxKindExportSpecifier, SyntaxKindFunctionDeclaration, SyntaxKindFunctionExpression, SyntaxKindFunctionType, + SyntaxKindGetAccessor, SyntaxKindIdentifier, SyntaxKindImportClause, SyntaxKindImportEqualsDeclaration, SyntaxKindImportSpecifier, + SyntaxKindIndexSignature, SyntaxKindInterfaceDeclaration, SyntaxKindJSDocCallbackTag, SyntaxKindJSDocEnumTag, SyntaxKindJSDocFunctionType, + SyntaxKindJSDocParameterTag, SyntaxKindJSDocPropertyTag, SyntaxKindJSDocSignature, SyntaxKindJSDocTypedefTag, SyntaxKindJSDocTypeLiteral, + SyntaxKindJsxAttribute, SyntaxKindJsxAttributes, SyntaxKindJsxSpreadAttribute, SyntaxKindMappedType, SyntaxKindMethodDeclaration, + SyntaxKindMethodSignature, SyntaxKindModuleDeclaration, SyntaxKindNamedTupleMember, SyntaxKindNamespaceExport, SyntaxKindNamespaceExportDeclaration, + SyntaxKindNamespaceImport, SyntaxKindNewExpression, SyntaxKindNoSubstitutionTemplateLiteral, SyntaxKindNumericLiteral, SyntaxKindObjectLiteralExpression, + SyntaxKindParameter, SyntaxKindPropertyAccessExpression, SyntaxKindPropertyAssignment, SyntaxKindPropertyDeclaration, SyntaxKindPropertySignature, + SyntaxKindSetAccessor, SyntaxKindShorthandPropertyAssignment, SyntaxKindSourceFile, SyntaxKindSpreadAssignment, SyntaxKindStringLiteral, + SyntaxKindTypeAliasDeclaration, SyntaxKindTypeLiteral, SyntaxKindTypeParameter, SyntaxKindVariableDeclaration: + return true + } + return false +} + +func canHaveLocals(node *Node) bool { + switch node.kind { + case SyntaxKindArrowFunction, SyntaxKindBlock, SyntaxKindCallSignature, SyntaxKindCaseBlock, SyntaxKindCatchClause, + SyntaxKindClassStaticBlockDeclaration, SyntaxKindConditionalType, SyntaxKindConstructor, SyntaxKindConstructorType, + SyntaxKindConstructSignature, SyntaxKindForStatement, SyntaxKindForInStatement, SyntaxKindForOfStatement, SyntaxKindFunctionDeclaration, + SyntaxKindFunctionExpression, SyntaxKindFunctionType, SyntaxKindGetAccessor, SyntaxKindIndexSignature, SyntaxKindJSDocCallbackTag, + SyntaxKindJSDocEnumTag, SyntaxKindJSDocFunctionType, SyntaxKindJSDocSignature, SyntaxKindJSDocTypedefTag, SyntaxKindMappedType, + SyntaxKindMethodDeclaration, SyntaxKindMethodSignature, SyntaxKindModuleDeclaration, SyntaxKindSetAccessor, SyntaxKindSourceFile, + SyntaxKindTypeAliasDeclaration: + return true + } + return false +} + +func isAnyImportOrReExport(node *Node) bool { + return isAnyImportSyntax(node) || isExportDeclaration(node) +} + +func isAnyImportSyntax(node *Node) bool { + return nodeKindIs(node, SyntaxKindImportDeclaration, SyntaxKindImportEqualsDeclaration) +} + +func getExternalModuleName(node *Node) *Node { + switch node.kind { + case SyntaxKindImportDeclaration: + return node.AsImportDeclaration().moduleSpecifier + case SyntaxKindExportDeclaration: + return node.AsExportDeclaration().moduleSpecifier + case SyntaxKindImportEqualsDeclaration: + if node.AsImportEqualsDeclaration().moduleReference.kind == SyntaxKindExternalModuleReference { + return node.AsImportEqualsDeclaration().moduleReference.AsExternalModuleReference().expression + } + return nil + case SyntaxKindImportType: + return getImportTypeNodeLiteral(node) + case SyntaxKindCallExpression: + return node.AsCallExpression().arguments[0] + case SyntaxKindModuleDeclaration: + if isStringLiteral(node.AsModuleDeclaration().name) { + return node.AsModuleDeclaration().name + } + return nil + } + panic("Unhandled case in getExternalModuleName") +} + +func getImportTypeNodeLiteral(node *Node) *Node { + if isImportTypeNode(node) { + importTypeNode := node.AsImportTypeNode() + if isLiteralTypeNode(importTypeNode.argument) { + literalTypeNode := importTypeNode.argument.AsLiteralTypeNode() + if isStringLiteral(literalTypeNode.literal) { + return literalTypeNode.literal + } + } + } + return nil +} + +func isExternalModuleNameRelative(moduleName string) bool { + // TypeScript 1.0 spec (April 2014): 11.2.1 + // An external module name is "relative" if the first term is "." or "..". + // Update: We also consider a path like `C:\foo.ts` "relative" because we do not search for it in `node_modules` or treat it as an ambient module. + return pathIsRelative(moduleName) || isRootedDiskPath(moduleName) +} + +func pathIsRelative(path string) bool { + return makeRegexp(`^\.\.?(?:$|[\\/])`).MatchString(path) +} + +func isRootedDiskPath(path string) bool { + return false // !!! +} + +func isShorthandAmbientModuleSymbol(moduleSymbol *Symbol) bool { + return isShorthandAmbientModule(moduleSymbol.valueDeclaration) +} + +func isShorthandAmbientModule(node *Node) bool { + // The only kind of module that can be missing a body is a shorthand ambient module. + return node != nil && node.kind == SyntaxKindModuleDeclaration && node.AsModuleDeclaration().body == nil +} + +func isEntityName(node *Node) bool { + return node.kind == SyntaxKindIdentifier || node.kind == SyntaxKindQualifiedName +} + +func nodeIsSynthesized(node *Node) bool { + return node.loc.pos < 0 || node.loc.end < 0 +} + +func getFirstIdentifier(node *Node) *Node { + switch node.kind { + case SyntaxKindIdentifier: + return node + case SyntaxKindQualifiedName: + return getFirstIdentifier(node.AsQualifiedName().left) + case SyntaxKindPropertyAccessExpression: + return getFirstIdentifier(node.AsPropertyAccessExpression().expression) + } + panic("Unhandled case in getFirstIdentifier") +} + +func getAliasDeclarationFromName(node *Node) *Node { + switch node.kind { + case SyntaxKindImportClause, SyntaxKindImportSpecifier, SyntaxKindNamespaceImport, SyntaxKindExportSpecifier, SyntaxKindExportAssignment, + SyntaxKindImportEqualsDeclaration, SyntaxKindNamespaceExport: + return node.parent + case SyntaxKindQualifiedName: + return getAliasDeclarationFromName(node.parent) + } + return nil +} + +func entityNameToString(name *Node) string { + switch name.kind { + case SyntaxKindThisKeyword: + return "this" + case SyntaxKindIdentifier, SyntaxKindPrivateIdentifier: + return getTextOfNode(name) + case SyntaxKindQualifiedName: + return entityNameToString(name.AsQualifiedName().left) + "." + entityNameToString(name.AsQualifiedName().right) + case SyntaxKindPropertyAccessExpression: + return entityNameToString(name.AsPropertyAccessExpression().expression) + "." + entityNameToString(name.AsPropertyAccessExpression().name) + case SyntaxKindJsxNamespacedName: + return entityNameToString(name.AsJsxNamespacedName().namespace) + ":" + entityNameToString(name.AsJsxNamespacedName().name) + } + panic("Unhandled case in entityNameToString") +} + +func getContainingQualifiedNameNode(node *Node) *Node { + for isQualifiedName(node.parent) { + node = node.parent + } + return node +} + +var extensionsToRemove = []string{ExtensionDts, ExtensionDmts, ExtensionDcts, ExtensionMjs, ExtensionMts, ExtensionCjs, ExtensionCts, ExtensionTs, ExtensionJs, ExtensionTsx, ExtensionJsx, ExtensionJson} + +func removeFileExtension(path string) string { + // Remove any known extension even if it has more than one dot + for _, ext := range extensionsToRemove { + if strings.HasSuffix(path, ext) { + return path[:len(path)-len(ext)] + } + } + // Otherwise just remove single dot extension, if any + return path[:len(path)-len(filepath.Ext(path))] +} + +func isSideEffectImport(node *Node) bool { + ancestor := findAncestor(node, isImportDeclaration) + return ancestor != nil && ancestor.AsImportDeclaration().importClause == nil +} + +func getExternalModuleRequireArgument(node *Node) *Node { + if isVariableDeclarationInitializedToBareOrAccessedRequire(node) { + return getLeftmostAccessExpression(node.AsVariableDeclaration().initializer).AsCallExpression().arguments[0] + } + return nil +} + +func getExternalModuleImportEqualsDeclarationExpression(node *Node) *Node { + //Debug.assert(isExternalModuleImportEqualsDeclaration(node)) + return node.AsImportEqualsDeclaration().moduleReference.AsExternalModuleReference().expression +} + +func isRightSideOfQualifiedNameOrPropertyAccess(node *Node) bool { + parent := node.parent + switch parent.kind { + case SyntaxKindQualifiedName: + return parent.AsQualifiedName().right == node + case SyntaxKindPropertyAccessExpression: + return parent.AsPropertyAccessExpression().name == node + case SyntaxKindMetaProperty: + return parent.AsMetaProperty().name == node + } + return false +} + +func getNamespaceDeclarationNode(node *Node) *Node { + switch node.kind { + case SyntaxKindImportDeclaration: + importClause := node.AsImportDeclaration().importClause + if importClause != nil && isNamespaceImport(importClause.AsImportClause().namedBindings) { + return importClause.AsImportClause().namedBindings + } + case SyntaxKindImportEqualsDeclaration: + return node + case SyntaxKindExportDeclaration: + exportClause := node.AsExportDeclaration().exportClause + if exportClause != nil && isNamespaceExport(exportClause) { + return exportClause + } + default: + panic("Unhandled case in getNamespaceDeclarationNode") + } + return nil +} + +func isImportCall(node *Node) bool { + return isCallExpression(node) && node.AsCallExpression().expression.kind == SyntaxKindImportKeyword +} + +func getSourceFileOfModule(module *Symbol) *SourceFile { + declaration := module.valueDeclaration + if declaration == nil { + declaration = getNonAugmentationDeclaration(module) + } + return getSourceFileOfNode(declaration) +} + +func getNonAugmentationDeclaration(symbol *Symbol) *Node { + return find(symbol.declarations, func(d *Node) bool { + return !isExternalModuleAugmentation(d) && !(isModuleDeclaration(d) && isGlobalScopeAugmentation(d)) + }) +} + +func isExternalModuleAugmentation(node *Node) bool { + return isAmbientModule(node) && isModuleAugmentationExternal(node) +} + +func isJsonSourceFile(file *SourceFile) bool { + return file.scriptKind == ScriptKindJSON +} + +func isSyntacticDefault(node *Node) bool { + return (isExportAssignment(node) && !node.AsExportAssignment().isExportEquals) || + hasSyntacticModifier(node, ModifierFlagsDefault) || + isExportSpecifier(node) || + isNamespaceExport(node) +} + +func hasExportAssignmentSymbol(moduleSymbol *Symbol) bool { + return moduleSymbol.exports[InternalSymbolNameExportEquals] != nil +} + +func isImportOrExportSpecifier(node *Node) bool { + return isImportSpecifier(node) || isExportSpecifier(node) +} + +func parsePseudoBigint(stringValue string) string { + return stringValue // !!! +} + +func isTypeAlias(node *Node) bool { + return isTypeAliasDeclaration(node) +} + +/** + * Gets the effective type parameters. If the node was parsed in a + * JavaScript file, gets the type parameters from the `@template` tag from JSDoc. + * + * This does *not* return type parameters from a jsdoc reference to a generic type, eg + * + * type Id = (x: T) => T + * /** @type {Id} / + * function id(x) { return x } + */ + +func getEffectiveTypeParameterDeclarations(node *Node) []*Node { + // if isJSDocSignature(node) { + // if isJSDocOverloadTag(node.parent) { + // jsDoc := getJSDocRoot(node.parent) + // if jsDoc && length(jsDoc.tags) { + // return flatMap(jsDoc.tags, func(tag JSDocTag) *NodeArray[TypeParameterDeclaration] { + // if isJSDocTemplateTag(tag) { + // return tag.typeParameters + // } else { + // return nil + // } + // }) + // } + // } + // return emptyArray + // } + // if isJSDocTypeAlias(node) { + // Debug.assert(node.parent.kind == SyntaxKindJSDoc) + // return flatMap(node.parent.tags, func(tag JSDocTag) *NodeArray[TypeParameterDeclaration] { + // if isJSDocTemplateTag(tag) { + // return tag.typeParameters + // } else { + // return nil + // } + // }) + // } + typeParameters := getTypeParameterListFromNode(node) + if typeParameters != nil { + return typeParameters.AsTypeParameterList().parameters + } + // if isInJSFile(node) { + // decls := getJSDocTypeParameterDeclarations(node) + // if decls.length { + // return decls + // } + // typeTag := getJSDocType(node) + // if typeTag && isFunctionTypeNode(typeTag) && typeTag.typeParameters { + // return typeTag.typeParameters + // } + // } + return nil +} + +func getTypeParameterListFromNode(node *Node) *Node { + if isFunctionLike(node) { + return node.FunctionLikeData().typeParameters + } + switch node.kind { + case SyntaxKindClassDeclaration: + return node.AsClassDeclaration().typeParameters + case SyntaxKindClassExpression: + return node.AsClassExpression().typeParameters + case SyntaxKindInterfaceDeclaration: + return node.AsInterfaceDeclaration().typeParameters + case SyntaxKindTypeAliasDeclaration: + return node.AsTypeAliasDeclaration().typeParameters + } + panic("Unhandled case in getTypeParameterListFromNode") +} + +func getInitializerFromNode(node *Node) *Node { + switch node.kind { + case SyntaxKindVariableDeclaration: + return node.AsVariableDeclaration().initializer + case SyntaxKindParameter: + return node.AsParameterDeclaration().initializer + case SyntaxKindBindingElement: + return node.AsBindingElement().initializer + case SyntaxKindPropertyDeclaration: + return node.AsPropertyDeclaration().initializer + case SyntaxKindPropertyAssignment: + return node.AsPropertyAssignment().initializer + case SyntaxKindEnumMember: + return node.AsEnumMember().initializer + case SyntaxKindForStatement: + return node.AsForStatement().initializer + case SyntaxKindForInStatement, SyntaxKindForOfStatement: + return node.AsForInOrOfStatement().initializer + case SyntaxKindJsxAttribute: + return node.AsJsxAttribute().initializer + } + return nil +} + +/** + * Gets the effective type annotation of a variable, parameter, or property. If the node was + * parsed in a JavaScript file, gets the type annotation from JSDoc. Also gets the type of + * functions only the JSDoc case. + */ +func getEffectiveTypeAnnotationNode(node *Node) *Node { + if isTypeAliasDeclaration(node) { + return nil + } + switch node.kind { + case SyntaxKindVariableDeclaration: + return node.AsVariableDeclaration().typeNode + case SyntaxKindParameter: + return node.AsParameterDeclaration().typeNode + case SyntaxKindPropertySignature: + return node.AsPropertySignatureDeclaration().typeNode + case SyntaxKindPropertyDeclaration: + return node.AsPropertyDeclaration().typeNode + case SyntaxKindTypePredicate: + return node.AsTypePredicateNode().typeNode + case SyntaxKindParenthesizedType: + return node.AsParenthesizedTypeNode().typeNode + case SyntaxKindTypeOperator: + return node.AsTypeOperatorNode().typeNode + case SyntaxKindMappedType: + return node.AsMappedTypeNode().typeNode + case SyntaxKindTypeAssertionExpression: + return node.AsTypeAssertion().typeNode + case SyntaxKindAsExpression: + return node.AsAsExpression().typeNode + default: + if isFunctionLike(node) && !isFunctionDeclaration(node) { + return node.FunctionLikeData().returnType + } + } + return nil +} + +func isTypeAny(t *Type) bool { + return t != nil && t.flags&TypeFlagsAny != 0 +} + +func isJSDocOptionalParameter(node *ParameterDeclaration) bool { + return false // !!! +} + +func isQuestionToken(node *Node) bool { + return node != nil && node.kind == SyntaxKindQuestionToken +} + +func isOptionalDeclaration(declaration *Node) bool { + switch declaration.kind { + case SyntaxKindPropertyDeclaration: + return isQuestionToken(declaration.AsPropertyDeclaration().postfixToken) + case SyntaxKindPropertySignature: + return isQuestionToken(declaration.AsPropertySignatureDeclaration().postfixToken) + case SyntaxKindParameter: + return declaration.AsParameterDeclaration().questionToken != nil + } + return false +} + +func isEmptyArrayLiteral(expression *Node) bool { + return expression.kind == SyntaxKindArrayLiteralExpression && len(expression.AsArrayLiteralExpression().elements) == 0 +} + +func declarationBelongsToPrivateAmbientMember(declaration *Node) bool { + root := getRootDeclaration(declaration) + memberDeclaration := root + if root.kind == SyntaxKindParameter { + memberDeclaration = root.parent + } + return isPrivateWithinAmbient(memberDeclaration) +} + +func isPrivateWithinAmbient(node *Node) bool { + return (hasEffectiveModifier(node, ModifierFlagsPrivate) || isPrivateIdentifierClassElementDeclaration(node)) && node.flags&NodeFlagsAmbient != 0 +} + +func identifierToKeywordKind(node *Identifier) SyntaxKind { + return textToKeyword[node.text] +} + +func isAssertionExpression(node *Node) bool { + kind := node.kind + return kind == SyntaxKindTypeAssertionExpression || kind == SyntaxKindAsExpression +} + +func createSymbolTable(symbols []*Symbol) SymbolTable { + if len(symbols) == 0 { + return nil + } + result := make(SymbolTable) + for _, symbol := range symbols { + result[symbol.name] = symbol + } + return result +} + +func sortSymbols(symbols []*Symbol) { + slices.SortFunc(symbols, compareSymbols) +} + +func compareSymbols(s1, s2 *Symbol) int { + if s1 == s2 { + return 0 + } + if s1.valueDeclaration != nil && s2.valueDeclaration != nil { + if s1.parent != nil && s2.parent != nil { + // Symbols with the same unmerged parent are always in the same file + if s1.parent != s2.parent { + f1 := getSourceFileOfNode(s1.valueDeclaration) + f2 := getSourceFileOfNode(s2.valueDeclaration) + if f1 != f2 { + // In different files, first compare base filename + r := strings.Compare(filepath.Base(f1.path), filepath.Base(f2.path)) + if r == 0 { + // Same base filename, compare the full paths (no two files should have the same full path) + r = strings.Compare(f1.path, f2.path) + } + return r + } + } + // In the same file, compare source positions + return s1.valueDeclaration.Pos() - s2.valueDeclaration.Pos() + } + } + // Sort by name + r := strings.Compare(s1.name, s2.name) + if r == 0 { + // Same name, sort by symbol id + r = int(getSymbolId(s1)) - int(getSymbolId(s2)) + } + return r +} + +func getClassLikeDeclarationOfSymbol(symbol *Symbol) *Node { + return find(symbol.declarations, isClassLike) +} + +func isThisInTypeQuery(node *Node) bool { + if !isThisIdentifier(node) { + return false + } + for isQualifiedName(node.parent) && node.parent.AsQualifiedName().left == node { + node = node.parent + } + return node.parent.kind == SyntaxKindTypeQuery +} + +func isThisIdentifier(node *Node) bool { + return node != nil && node.kind == SyntaxKindIdentifier && identifierIsThisKeyword(node) +} + +func identifierIsThisKeyword(id *Node) bool { + return id.AsIdentifier().text == "this" +} + +func getDeclarationModifierFlagsFromSymbol(s *Symbol, isWrite bool) ModifierFlags { + if s.valueDeclaration != nil { + var declaration *Node + if isWrite { + declaration = find(s.declarations, isSetAccessorDeclaration) + } + if declaration == nil && s.flags&SymbolFlagsGetAccessor != 0 { + declaration = find(s.declarations, isGetAccessorDeclaration) + } + if declaration == nil { + declaration = s.valueDeclaration + } + flags := getCombinedModifierFlags(declaration) + if s.parent != nil && s.parent.flags&SymbolFlagsClass != 0 { + return flags + } + return flags & ^ModifierFlagsAccessibilityModifier + } + if s.checkFlags&CheckFlagsSynthetic != 0 { + var accessModifier ModifierFlags + switch { + case s.checkFlags&CheckFlagsContainsPrivate != 0: + accessModifier = ModifierFlagsPrivate + case s.checkFlags&CheckFlagsContainsPublic != 0: + accessModifier = ModifierFlagsPublic + default: + accessModifier = ModifierFlagsProtected + } + var staticModifier ModifierFlags + if s.checkFlags&CheckFlagsContainsStatic != 0 { + staticModifier = ModifierFlagsStatic + } + return accessModifier | staticModifier + } + if s.flags&SymbolFlagsPrototype != 0 { + return ModifierFlagsPublic | ModifierFlagsStatic + } + return ModifierFlagsNone +} + +func isExponentiationOperator(kind SyntaxKind) bool { + return kind == SyntaxKindAsteriskAsteriskToken +} + +func isMultiplicativeOperator(kind SyntaxKind) bool { + return kind == SyntaxKindAsteriskToken || kind == SyntaxKindSlashToken || kind == SyntaxKindPercentToken +} + +func isMultiplicativeOperatorOrHigher(kind SyntaxKind) bool { + return isExponentiationOperator(kind) || isMultiplicativeOperator(kind) +} + +func isAdditiveOperator(kind SyntaxKind) bool { + return kind == SyntaxKindPlusToken || kind == SyntaxKindMinusToken +} + +func isAdditiveOperatorOrHigher(kind SyntaxKind) bool { + return isAdditiveOperator(kind) || isMultiplicativeOperatorOrHigher(kind) +} + +func isShiftOperator(kind SyntaxKind) bool { + return kind == SyntaxKindLessThanLessThanToken || kind == SyntaxKindGreaterThanGreaterThanToken || + kind == SyntaxKindGreaterThanGreaterThanGreaterThanToken +} + +func isShiftOperatorOrHigher(kind SyntaxKind) bool { + return isShiftOperator(kind) || isAdditiveOperatorOrHigher(kind) +} + +func isRelationalOperator(kind SyntaxKind) bool { + return kind == SyntaxKindLessThanToken || kind == SyntaxKindLessThanEqualsToken || kind == SyntaxKindGreaterThanToken || + kind == SyntaxKindGreaterThanEqualsToken || kind == SyntaxKindInstanceOfKeyword || kind == SyntaxKindInKeyword +} + +func isRelationalOperatorOrHigher(kind SyntaxKind) bool { + return isRelationalOperator(kind) || isShiftOperatorOrHigher(kind) +} + +func isEqualityOperator(kind SyntaxKind) bool { + return kind == SyntaxKindEqualsEqualsToken || kind == SyntaxKindEqualsEqualsEqualsToken || + kind == SyntaxKindExclamationEqualsToken || kind == SyntaxKindExclamationEqualsEqualsToken +} + +func isEqualityOperatorOrHigher(kind SyntaxKind) bool { + return isEqualityOperator(kind) || isRelationalOperatorOrHigher(kind) +} + +func isBitwiseOperator(kind SyntaxKind) bool { + return kind == SyntaxKindAmpersandToken || kind == SyntaxKindBarToken || kind == SyntaxKindCaretToken +} + +func isBitwiseOperatorOrHigher(kind SyntaxKind) bool { + return isBitwiseOperator(kind) || isEqualityOperatorOrHigher(kind) +} + +// NOTE: The version in utilities includes ExclamationToken, which is not a binary operator. +func isLogicalOperator(kind SyntaxKind) bool { + return kind == SyntaxKindAmpersandAmpersandToken || kind == SyntaxKindBarBarToken +} + +func isLogicalOperatorOrHigher(kind SyntaxKind) bool { + return isLogicalOperator(kind) || isBitwiseOperatorOrHigher(kind) +} + +func isAssignmentOperatorOrHigher(kind SyntaxKind) bool { + return kind == SyntaxKindQuestionQuestionToken || isLogicalOperatorOrHigher(kind) || isAssignmentOperator(kind) +} + +func isBinaryOperator(kind SyntaxKind) bool { + return isAssignmentOperatorOrHigher(kind) || kind == SyntaxKindCommaToken +} + +func isObjectLiteralType(t *Type) bool { + return t.objectFlags&ObjectFlagsObjectLiteral != 0 +} + +func isTupleType(t *Type) bool { + return t.objectFlags&ObjectFlagsReference != 0 && t.TypeReference().target.objectFlags&ObjectFlagsTuple != 0 +} + +func isDeclarationReadonly(declaration *Node) bool { + return getCombinedModifierFlags(declaration)&ModifierFlagsReadonly != 0 && !isParameterPropertyDeclaration(declaration, declaration.parent) +} + +func isFreshLiteralType(t *Type) bool { + return t.flags&TypeFlagsFreshable != 0 && t.LiteralType().freshType == t +} + +func getPostfixTokenFromNode(node *Node) *Node { + switch node.kind { + case SyntaxKindPropertyDeclaration: + return node.AsPropertyDeclaration().postfixToken + case SyntaxKindPropertySignature: + return node.AsPropertySignatureDeclaration().postfixToken + case SyntaxKindMethodDeclaration: + return node.AsMethodDeclaration().postfixToken + case SyntaxKindMethodSignature: + return node.AsMethodSignatureDeclaration().postfixToken + } + panic("Unhandled case in getPostfixTokenFromNode") +} + +func isStatic(node *Node) bool { + // https://tc39.es/ecma262/#sec-static-semantics-isstatic + return isClassElement(node) && hasStaticModifier(node) || isClassStaticBlockDeclaration(node) +} + +func isLogicalExpression(node *Node) bool { + for { + if node.kind == SyntaxKindParenthesizedExpression { + node = node.AsParenthesizedExpression().expression + } else if node.kind == SyntaxKindPrefixUnaryExpression && node.AsPrefixUnaryExpression().operator == SyntaxKindExclamationToken { + node = node.AsPrefixUnaryExpression().operand + } else { + return isLogicalOrCoalescingBinaryExpression(node) + } + } +}