package checker import ( "cmp" "slices" "strings" "sync" "unicode/utf8" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/binder" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/jsnum" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/module" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/scanner" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" ) func NewDiagnosticForNode(node *ast.Node, message *diagnostics.Message, args ...any) *ast.Diagnostic { var file *ast.SourceFile var loc core.TextRange if node != nil { file = ast.GetSourceFileOfNode(node) loc = scanner.GetErrorRangeForNode(file, node) } return ast.NewDiagnostic(file, loc, message, args...) } func NewDiagnosticChainForNode(chain *ast.Diagnostic, node *ast.Node, message *diagnostics.Message, args ...any) *ast.Diagnostic { if chain != nil { return ast.NewDiagnosticChain(chain, message, args...) } return NewDiagnosticForNode(node, message, args...) } 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 isCompoundAssignment(token ast.Kind) bool { return token >= ast.KindFirstCompoundAssignment && token <= ast.KindLastCompoundAssignment } func tokenIsIdentifierOrKeyword(token ast.Kind) bool { return token >= ast.KindIdentifier } func tokenIsIdentifierOrKeywordOrGreaterThan(token ast.Kind) bool { return token == ast.KindGreaterThanToken || tokenIsIdentifierOrKeyword(token) } func hasOverrideModifier(node *ast.Node) bool { return ast.HasSyntacticModifier(node, ast.ModifierFlagsOverride) } func hasAbstractModifier(node *ast.Node) bool { return ast.HasSyntacticModifier(node, ast.ModifierFlagsAbstract) } func hasAmbientModifier(node *ast.Node) bool { return ast.HasSyntacticModifier(node, ast.ModifierFlagsAmbient) } func hasAsyncModifier(node *ast.Node) bool { return ast.HasSyntacticModifier(node, ast.ModifierFlagsAsync) } func getSelectedModifierFlags(node *ast.Node, flags ast.ModifierFlags) ast.ModifierFlags { return node.ModifierFlags() & flags } func hasReadonlyModifier(node *ast.Node) bool { return ast.HasModifier(node, ast.ModifierFlagsReadonly) } func isStaticPrivateIdentifierProperty(s *ast.Symbol) bool { return s.ValueDeclaration != nil && ast.IsPrivateIdentifierClassElementDeclaration(s.ValueDeclaration) && ast.IsStatic(s.ValueDeclaration) } func isEmptyObjectLiteral(expression *ast.Node) bool { return expression.Kind == ast.KindObjectLiteralExpression && len(expression.AsObjectLiteralExpression().Properties.Nodes) == 0 } type AssignmentKind int32 const ( AssignmentKindNone AssignmentKind = iota AssignmentKindDefinite AssignmentKindCompound ) type AssignmentTarget = ast.Node // BinaryExpression | PrefixUnaryExpression | PostfixUnaryExpression | ForInOrOfStatement func getAssignmentTargetKind(node *ast.Node) AssignmentKind { target := ast.GetAssignmentTarget(node) if target == nil { return AssignmentKindNone } switch target.Kind { case ast.KindBinaryExpression: binaryOperator := target.AsBinaryExpression().OperatorToken.Kind if binaryOperator == ast.KindEqualsToken || ast.IsLogicalOrCoalescingAssignmentOperator(binaryOperator) { return AssignmentKindDefinite } return AssignmentKindCompound case ast.KindPrefixUnaryExpression, ast.KindPostfixUnaryExpression: return AssignmentKindCompound case ast.KindForInStatement, ast.KindForOfStatement: return AssignmentKindDefinite } panic("Unhandled case in getAssignmentTargetKind") } func isDeleteTarget(node *ast.Node) bool { if !ast.IsAccessExpression(node) { return false } node = ast.WalkUpParenthesizedExpressions(node.Parent) return node != nil && node.Kind == ast.KindDeleteExpression } func isInCompoundLikeAssignment(node *ast.Node) bool { target := ast.GetAssignmentTarget(node) return target != nil && ast.IsAssignmentExpression(target /*excludeCompoundAssignment*/, true) && isCompoundLikeAssignment(target) } func isCompoundLikeAssignment(assignment *ast.Node) bool { right := ast.SkipParentheses(assignment.AsBinaryExpression().Right) return right.Kind == ast.KindBinaryExpression && isShiftOperatorOrHigher(right.AsBinaryExpression().OperatorToken.Kind) } func getAssertedTypeNode(node *ast.Node) *ast.Node { switch node.Kind { case ast.KindAsExpression: return node.AsAsExpression().Type case ast.KindSatisfiesExpression: return node.AsSatisfiesExpression().Type case ast.KindTypeAssertionExpression: return node.AsTypeAssertion().Type } panic("Unhandled case in getAssertedTypeNode") } func isConstAssertion(node *ast.Node) bool { switch node.Kind { case ast.KindAsExpression, ast.KindTypeAssertionExpression: return isConstTypeReference(getAssertedTypeNode(node)) } return false } func isConstTypeReference(node *ast.Node) bool { return ast.IsTypeReferenceNode(node) && len(node.TypeArguments()) == 0 && ast.IsIdentifier(node.AsTypeReferenceNode().TypeName) && node.AsTypeReferenceNode().TypeName.Text() == "const" } func GetSingleVariableOfVariableStatement(node *ast.Node) *ast.Node { if !ast.IsVariableStatement(node) { return nil } return core.FirstOrNil(node.AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes) } func isTypeReferenceIdentifier(node *ast.Node) bool { for node.Parent.Kind == ast.KindQualifiedName { node = node.Parent } return ast.IsTypeReferenceNode(node.Parent) } func IsInTypeQuery(node *ast.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 ast.FindAncestorOrQuit(node, func(n *ast.Node) ast.FindAncestorResult { switch n.Kind { case ast.KindTypeQuery: return ast.FindAncestorTrue case ast.KindIdentifier, ast.KindQualifiedName: return ast.FindAncestorFalse } return ast.FindAncestorQuit }) != nil } func getNameFromImportDeclaration(node *ast.Node) *ast.Node { switch node.Kind { case ast.KindImportSpecifier: return node.AsImportSpecifier().Name() case ast.KindNamespaceImport: return node.AsNamespaceImport().Name() case ast.KindImportClause: return node.AsImportClause().Name() case ast.KindImportEqualsDeclaration: return node.AsImportEqualsDeclaration().Name() } return nil } func nodeCanBeDecorated(useLegacyDecorators bool, node *ast.Node, parent *ast.Node, grandparent *ast.Node) bool { // private names cannot be used with decorators yet if useLegacyDecorators && node.Name() != nil && ast.IsPrivateIdentifier(node.Name()) { return false } switch node.Kind { case ast.KindClassDeclaration: // class declarations are valid targets return true case ast.KindClassExpression: // class expressions are valid targets for native decorators return !useLegacyDecorators case ast.KindPropertyDeclaration: // property declarations are valid if their parent is a class declaration. return parent != nil && (useLegacyDecorators && ast.IsClassDeclaration(parent) || !useLegacyDecorators && ast.IsClassLike(parent) && !hasAbstractModifier(node) && !hasAmbientModifier(node)) case ast.KindGetAccessor, ast.KindSetAccessor, ast.KindMethodDeclaration: // if this method has a body and its parent is a class declaration, this is a valid target. return parent != nil && node.Body() != nil && (useLegacyDecorators && ast.IsClassDeclaration(parent) || !useLegacyDecorators && ast.IsClassLike(parent)) case ast.KindParameter: // TODO(rbuckton): Parameter decorator support for ES decorators must wait until it is standardized if !useLegacyDecorators { return false } // if the parameter's parent has a body and its grandparent is a class declaration, this is a valid target. return parent != nil && parent.Body() != nil && (parent.Kind == ast.KindConstructor || parent.Kind == ast.KindMethodDeclaration || parent.Kind == ast.KindSetAccessor) && ast.GetThisParameter(parent) != node && grandparent != nil && grandparent.Kind == ast.KindClassDeclaration } return false } func canHaveLocals(node *ast.Node) bool { switch node.Kind { case ast.KindArrowFunction, ast.KindBlock, ast.KindCallSignature, ast.KindCaseBlock, ast.KindCatchClause, ast.KindClassStaticBlockDeclaration, ast.KindConditionalType, ast.KindConstructor, ast.KindConstructorType, ast.KindConstructSignature, ast.KindForStatement, ast.KindForInStatement, ast.KindForOfStatement, ast.KindFunctionDeclaration, ast.KindFunctionExpression, ast.KindFunctionType, ast.KindGetAccessor, ast.KindIndexSignature, ast.KindJSDocSignature, ast.KindMappedType, ast.KindMethodDeclaration, ast.KindMethodSignature, ast.KindModuleDeclaration, ast.KindSetAccessor, ast.KindSourceFile, ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration: return true } return false } func isShorthandAmbientModuleSymbol(moduleSymbol *ast.Symbol) bool { return isShorthandAmbientModule(moduleSymbol.ValueDeclaration) } func isShorthandAmbientModule(node *ast.Node) bool { // The only kind of module that can be missing a body is a shorthand ambient module. return node != nil && node.Kind == ast.KindModuleDeclaration && node.AsModuleDeclaration().Body == nil } func getAliasDeclarationFromName(node *ast.Node) *ast.Node { switch node.Parent.Kind { case ast.KindImportClause, ast.KindImportSpecifier, ast.KindNamespaceImport, ast.KindExportSpecifier, ast.KindExportAssignment, ast.KindJSExportAssignment, ast.KindImportEqualsDeclaration, ast.KindNamespaceExport: return node.Parent case ast.KindQualifiedName: return getAliasDeclarationFromName(node.Parent) } return nil } func entityNameToString(name *ast.Node) string { switch name.Kind { case ast.KindThisKeyword: return "this" case ast.KindIdentifier, ast.KindPrivateIdentifier: if ast.NodeIsSynthesized(name) { return name.Text() } return scanner.GetTextOfNode(name) case ast.KindQualifiedName: return entityNameToString(name.AsQualifiedName().Left) + "." + entityNameToString(name.AsQualifiedName().Right) case ast.KindPropertyAccessExpression: return entityNameToString(name.AsPropertyAccessExpression().Expression) + "." + entityNameToString(name.AsPropertyAccessExpression().Name()) case ast.KindJsxNamespacedName: return entityNameToString(name.AsJsxNamespacedName().Namespace) + ":" + entityNameToString(name.AsJsxNamespacedName().Name()) } panic("Unhandled case in entityNameToString") } func getContainingQualifiedNameNode(node *ast.Node) *ast.Node { for ast.IsQualifiedName(node.Parent) { node = node.Parent } return node } func isSideEffectImport(node *ast.Node) bool { ancestor := ast.FindAncestor(node, ast.IsImportDeclaration) return ancestor != nil && ancestor.AsImportDeclaration().ImportClause == nil } func getExternalModuleRequireArgument(node *ast.Node) *ast.Node { if ast.IsVariableDeclarationInitializedToRequire(node) { return node.AsVariableDeclaration().Initializer.AsCallExpression().Arguments.Nodes[0] } return nil } func isRightSideOfAccessExpression(node *ast.Node) bool { return node.Parent != nil && (ast.IsPropertyAccessExpression(node.Parent) && node.Parent.Name() == node || ast.IsElementAccessExpression(node.Parent) && node.Parent.AsElementAccessExpression().ArgumentExpression == node) } func isTopLevelInExternalModuleAugmentation(node *ast.Node) bool { return node != nil && node.Parent != nil && ast.IsModuleBlock(node.Parent) && ast.IsExternalModuleAugmentation(node.Parent.Parent) } func isSyntacticDefault(node *ast.Node) bool { return (ast.IsExportAssignment(node) && !node.AsExportAssignment().IsExportEquals) || ast.HasSyntacticModifier(node, ast.ModifierFlagsDefault) || ast.IsExportSpecifier(node) || ast.IsNamespaceExport(node) } func hasExportAssignmentSymbol(moduleSymbol *ast.Symbol) bool { return moduleSymbol.Exports[ast.InternalSymbolNameExportEquals] != nil } func isTypeAlias(node *ast.Node) bool { return ast.IsTypeOrJSTypeAliasDeclaration(node) } func hasOnlyExpressionInitializer(node *ast.Node) bool { switch node.Kind { case ast.KindVariableDeclaration, ast.KindParameter, ast.KindBindingElement, ast.KindPropertyDeclaration, ast.KindPropertyAssignment, ast.KindEnumMember: return true } return false } func hasDotDotDotToken(node *ast.Node) bool { switch node.Kind { case ast.KindParameter: return node.AsParameterDeclaration().DotDotDotToken != nil case ast.KindBindingElement: return node.AsBindingElement().DotDotDotToken != nil case ast.KindNamedTupleMember: return node.AsNamedTupleMember().DotDotDotToken != nil case ast.KindJsxExpression: return node.AsJsxExpression().DotDotDotToken != nil } return false } func IsTypeAny(t *Type) bool { return t != nil && t.flags&TypeFlagsAny != 0 } func isJSDocOptionalParameter(node *ast.ParameterDeclaration) bool { return false // !!! } func isExclamationToken(node *ast.Node) bool { return node != nil && node.Kind == ast.KindExclamationToken } func isOptionalDeclaration(declaration *ast.Node) bool { switch declaration.Kind { case ast.KindParameter: return declaration.AsParameterDeclaration().QuestionToken != nil case ast.KindPropertyDeclaration: return ast.IsQuestionToken(declaration.AsPropertyDeclaration().PostfixToken) case ast.KindPropertySignature: return ast.IsQuestionToken(declaration.AsPropertySignatureDeclaration().PostfixToken) case ast.KindMethodDeclaration: return ast.IsQuestionToken(declaration.AsMethodDeclaration().PostfixToken) case ast.KindMethodSignature: return ast.IsQuestionToken(declaration.AsMethodSignatureDeclaration().PostfixToken) case ast.KindPropertyAssignment: return ast.IsQuestionToken(declaration.AsPropertyAssignment().PostfixToken) case ast.KindShorthandPropertyAssignment: return ast.IsQuestionToken(declaration.AsShorthandPropertyAssignment().PostfixToken) } return false } func isEmptyArrayLiteral(expression *ast.Node) bool { return ast.IsArrayLiteralExpression(expression) && len(expression.AsArrayLiteralExpression().Elements.Nodes) == 0 } func declarationBelongsToPrivateAmbientMember(declaration *ast.Node) bool { root := ast.GetRootDeclaration(declaration) memberDeclaration := root if root.Kind == ast.KindParameter { memberDeclaration = root.Parent } return isPrivateWithinAmbient(memberDeclaration) } func isPrivateWithinAmbient(node *ast.Node) bool { return (ast.HasModifier(node, ast.ModifierFlagsPrivate) || ast.IsPrivateIdentifierClassElementDeclaration(node)) && node.Flags&ast.NodeFlagsAmbient != 0 } func isTypeAssertion(node *ast.Node) bool { return ast.IsAssertionExpression(ast.SkipParentheses(node)) } func createSymbolTable(symbols []*ast.Symbol) ast.SymbolTable { if len(symbols) == 0 { return nil } result := make(ast.SymbolTable) for _, symbol := range symbols { result[symbol.Name] = symbol } return result } func (c *Checker) sortSymbols(symbols []*ast.Symbol) { slices.SortFunc(symbols, c.compareSymbols) } func (c *Checker) compareSymbolsWorker(s1, s2 *ast.Symbol) int { if s1 == s2 { return 0 } if s1 == nil { return 1 } if s2 == nil { return -1 } if len(s1.Declarations) != 0 && len(s2.Declarations) != 0 { if r := c.compareNodes(s1.Declarations[0], s2.Declarations[0]); r != 0 { return r } } else if len(s1.Declarations) != 0 { return -1 } else if len(s2.Declarations) != 0 { return 1 } if r := strings.Compare(s1.Name, s2.Name); r != 0 { return r } // Fall back to symbol IDs. This is a last resort that should happen only when symbols have // no declaration and duplicate names. return int(ast.GetSymbolId(s1)) - int(ast.GetSymbolId(s2)) } func (c *Checker) compareNodes(n1, n2 *ast.Node) int { if n1 == n2 { return 0 } if n1 == nil { return 1 } if n2 == nil { return -1 } s1 := ast.GetSourceFileOfNode(n1) s2 := ast.GetSourceFileOfNode(n2) if s1 != s2 { f1 := c.fileIndexMap[s1] f2 := c.fileIndexMap[s2] // Order by index of file in the containing program return f1 - f2 } // In the same file, order by source position return n1.Pos() - n2.Pos() } func CompareTypes(t1, t2 *Type) int { if t1 == t2 { return 0 } if t1 == nil { return -1 } if t2 == nil { return 1 } if t1.checker != t2.checker { panic("Cannot compare types from different checkers") } // First sort in order of increasing type flags values. if c := getSortOrderFlags(t1) - getSortOrderFlags(t2); c != 0 { return c } // Order named types by name and, in the case of aliased types, by alias type arguments. if c := compareTypeNames(t1, t2); c != 0 { return c } // We have unnamed types or types with identical names. Now sort by data specific to the type. switch { case t1.flags&(TypeFlagsAny|TypeFlagsUnknown|TypeFlagsString|TypeFlagsNumber|TypeFlagsBoolean|TypeFlagsBigInt|TypeFlagsESSymbol|TypeFlagsVoid|TypeFlagsUndefined|TypeFlagsNull|TypeFlagsNever|TypeFlagsNonPrimitive) != 0: // Only distinguished by type IDs, handled below. case t1.flags&TypeFlagsObject != 0: // Order unnamed or identically named object types by symbol. if c := t1.checker.compareSymbols(t1.symbol, t2.symbol); c != 0 { return c } // When object types have the same or no symbol, order by kind. We order type references before other kinds. if t1.objectFlags&ObjectFlagsReference != 0 && t2.objectFlags&ObjectFlagsReference != 0 { r1 := t1.AsTypeReference() r2 := t2.AsTypeReference() if r1.target.objectFlags&ObjectFlagsTuple != 0 && r2.target.objectFlags&ObjectFlagsTuple != 0 { // Tuple types have no associated symbol, instead we order by tuple element information. if c := compareTupleTypes(r1.target.AsTupleType(), r2.target.AsTupleType()); c != 0 { return c } } // Here we know we have references to instantiations of the same type because we have matching targets. if r1.node == nil && r2.node == nil { // Non-deferred type references with the same target are sorted by their type argument lists. if c := compareTypeLists(t1.AsTypeReference().resolvedTypeArguments, t2.AsTypeReference().resolvedTypeArguments); c != 0 { return c } } else { // Deferred type references with the same target are ordered by the source location of the reference. if c := t1.checker.compareNodes(r1.node, r2.node); c != 0 { return c } // Instantiations of the same deferred type reference are ordered by their associated type mappers // (which reflect the mapping of in-scope type parameters to type arguments). if c := compareTypeMappers(t1.AsObjectType().mapper, t2.AsObjectType().mapper); c != 0 { return c } } } else if t1.objectFlags&ObjectFlagsReference != 0 { return -1 } else if t2.objectFlags&ObjectFlagsReference != 0 { return 1 } else { // Order unnamed non-reference object types by kind associated type mappers. Reverse mapped types have // neither symbols nor mappers so they're ultimately ordered by unstable type IDs, but given their rarity // this should be fine. if c := int(t1.objectFlags&ObjectFlagsObjectTypeKindMask) - int(t2.objectFlags&ObjectFlagsObjectTypeKindMask); c != 0 { return c } if c := compareTypeMappers(t1.AsObjectType().mapper, t2.AsObjectType().mapper); c != 0 { return c } } case t1.flags&TypeFlagsUnion != 0: // Unions are ordered by origin and then constituent type lists. o1 := t1.AsUnionType().origin o2 := t2.AsUnionType().origin if o1 == nil && o2 == nil { if c := compareTypeLists(t1.Types(), t2.Types()); c != 0 { return c } } else if o1 == nil { return 1 } else if o2 == nil { return -1 } else { if c := CompareTypes(o1, o2); c != 0 { return c } } case t1.flags&TypeFlagsIntersection != 0: // Intersections are ordered by their constituent type lists. if c := compareTypeLists(t1.Types(), t2.Types()); c != 0 { return c } case t1.flags&(TypeFlagsEnum|TypeFlagsEnumLiteral|TypeFlagsUniqueESSymbol) != 0: // Enum members are ordered by their symbol (and thus their declaration order). if c := t1.checker.compareSymbols(t1.symbol, t2.symbol); c != 0 { return c } case t1.flags&TypeFlagsStringLiteral != 0: // String literal types are ordered by their values. if c := strings.Compare(t1.AsLiteralType().value.(string), t2.AsLiteralType().value.(string)); c != 0 { return c } case t1.flags&TypeFlagsNumberLiteral != 0: // Numeric literal types are ordered by their values. if c := cmp.Compare(t1.AsLiteralType().value.(jsnum.Number), t2.AsLiteralType().value.(jsnum.Number)); c != 0 { return c } case t1.flags&TypeFlagsBooleanLiteral != 0: b1 := t1.AsLiteralType().value.(bool) b2 := t2.AsLiteralType().value.(bool) if b1 != b2 { if b1 { return 1 } return -1 } case t1.flags&TypeFlagsTypeParameter != 0: if c := t1.checker.compareSymbols(t1.symbol, t2.symbol); c != 0 { return c } case t1.flags&TypeFlagsIndex != 0: if c := CompareTypes(t1.AsIndexType().target, t2.AsIndexType().target); c != 0 { return c } if c := int(t1.AsIndexType().flags) - int(t2.AsIndexType().flags); c != 0 { return c } case t1.flags&TypeFlagsIndexedAccess != 0: if c := CompareTypes(t1.AsIndexedAccessType().objectType, t2.AsIndexedAccessType().objectType); c != 0 { return c } if c := CompareTypes(t1.AsIndexedAccessType().indexType, t2.AsIndexedAccessType().indexType); c != 0 { return c } case t1.flags&TypeFlagsConditional != 0: if c := t1.checker.compareNodes(t1.AsConditionalType().root.node.AsNode(), t2.AsConditionalType().root.node.AsNode()); c != 0 { return c } if c := compareTypeMappers(t1.AsConditionalType().mapper, t2.AsConditionalType().mapper); c != 0 { return c } case t1.flags&TypeFlagsSubstitution != 0: if c := CompareTypes(t1.AsSubstitutionType().baseType, t2.AsSubstitutionType().baseType); c != 0 { return c } if c := CompareTypes(t1.AsSubstitutionType().constraint, t2.AsSubstitutionType().constraint); c != 0 { return c } case t1.flags&TypeFlagsTemplateLiteral != 0: if c := slices.Compare(t1.AsTemplateLiteralType().texts, t2.AsTemplateLiteralType().texts); c != 0 { return c } if c := compareTypeLists(t1.AsTemplateLiteralType().types, t2.AsTemplateLiteralType().types); c != 0 { return c } case t1.flags&TypeFlagsStringMapping != 0: if c := CompareTypes(t1.AsStringMappingType().target, t2.AsStringMappingType().target); c != 0 { return c } } // Fall back to type IDs. This results in type creation order for built-in types. return int(t1.id) - int(t2.id) } func getSortOrderFlags(t *Type) int { // Return TypeFlagsEnum for all enum-like unit types (they'll be sorted by their symbols) if t.flags&(TypeFlagsEnumLiteral|TypeFlagsEnum) != 0 && t.flags&TypeFlagsUnion == 0 { return int(TypeFlagsEnum) } return int(t.flags) } func compareTypeNames(t1, t2 *Type) int { s1 := getTypeNameSymbol(t1) s2 := getTypeNameSymbol(t2) if s1 == s2 { if t1.alias != nil { return compareTypeLists(t1.alias.typeArguments, t2.alias.typeArguments) } return 0 } if s1 == nil { return 1 } if s2 == nil { return -1 } return strings.Compare(s1.Name, s2.Name) } func getTypeNameSymbol(t *Type) *ast.Symbol { if t.alias != nil { return t.alias.symbol } if t.flags&(TypeFlagsTypeParameter|TypeFlagsStringMapping) != 0 || t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsReference) != 0 { return t.symbol } return nil } func getObjectTypeName(t *Type) *ast.Symbol { if t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsReference) != 0 { return t.symbol } return nil } func compareTupleTypes(t1, t2 *TupleType) int { if t1 == t2 { return 0 } if t1.readonly != t2.readonly { return core.IfElse(t1.readonly, 1, -1) } if len(t1.elementInfos) != len(t2.elementInfos) { return len(t1.elementInfos) - len(t2.elementInfos) } for i := range t1.elementInfos { if c := int(t1.elementInfos[i].flags) - int(t2.elementInfos[i].flags); c != 0 { return c } } for i := range t1.elementInfos { if c := compareElementLabels(t1.elementInfos[i].labeledDeclaration, t2.elementInfos[i].labeledDeclaration); c != 0 { return c } } return 0 } func compareElementLabels(n1, n2 *ast.Node) int { if n1 == n2 { return 0 } if n1 == nil { return -1 } if n2 == nil { return 1 } return strings.Compare(n1.Name().Text(), n2.Name().Text()) } func compareTypeLists(s1, s2 []*Type) int { if len(s1) != len(s2) { return len(s1) - len(s2) } for i, t1 := range s1 { if c := CompareTypes(t1, s2[i]); c != 0 { return c } } return 0 } func compareTypeMappers(m1, m2 *TypeMapper) int { if m1 == m2 { return 0 } if m1 == nil { return 1 } if m2 == nil { return -1 } kind1 := m1.Kind() kind2 := m2.Kind() if kind1 != kind2 { return int(kind1) - int(kind2) } switch kind1 { case TypeMapperKindSimple: m1 := m1.data.(*SimpleTypeMapper) m2 := m2.data.(*SimpleTypeMapper) if c := CompareTypes(m1.source, m2.source); c != 0 { return c } return CompareTypes(m1.target, m2.target) case TypeMapperKindArray: m1 := m1.data.(*ArrayTypeMapper) m2 := m2.data.(*ArrayTypeMapper) if c := compareTypeLists(m1.sources, m2.sources); c != 0 { return c } return compareTypeLists(m1.targets, m2.targets) case TypeMapperKindMerged: m1 := m1.data.(*MergedTypeMapper) m2 := m2.data.(*MergedTypeMapper) if c := compareTypeMappers(m1.m1, m2.m1); c != 0 { return c } return compareTypeMappers(m1.m2, m2.m2) } return 0 } func getDeclarationModifierFlagsFromSymbol(s *ast.Symbol) ast.ModifierFlags { return getDeclarationModifierFlagsFromSymbolEx(s, false /*isWrite*/) } func getDeclarationModifierFlagsFromSymbolEx(s *ast.Symbol, isWrite bool) ast.ModifierFlags { if s.ValueDeclaration != nil { var declaration *ast.Node if isWrite { declaration = core.Find(s.Declarations, ast.IsSetAccessorDeclaration) } if declaration == nil && s.Flags&ast.SymbolFlagsGetAccessor != 0 { declaration = core.Find(s.Declarations, ast.IsGetAccessorDeclaration) } if declaration == nil { declaration = s.ValueDeclaration } flags := ast.GetCombinedModifierFlags(declaration) if s.Parent != nil && s.Parent.Flags&ast.SymbolFlagsClass != 0 { return flags } return flags & ^ast.ModifierFlagsAccessibilityModifier } if s.CheckFlags&ast.CheckFlagsSynthetic != 0 { var accessModifier ast.ModifierFlags switch { case s.CheckFlags&ast.CheckFlagsContainsPrivate != 0: accessModifier = ast.ModifierFlagsPrivate case s.CheckFlags&ast.CheckFlagsContainsPublic != 0: accessModifier = ast.ModifierFlagsPublic default: accessModifier = ast.ModifierFlagsProtected } var staticModifier ast.ModifierFlags if s.CheckFlags&ast.CheckFlagsContainsStatic != 0 { staticModifier = ast.ModifierFlagsStatic } return accessModifier | staticModifier } if s.Flags&ast.SymbolFlagsPrototype != 0 { return ast.ModifierFlagsPublic | ast.ModifierFlagsStatic } return ast.ModifierFlagsNone } func isExponentiationOperator(kind ast.Kind) bool { return kind == ast.KindAsteriskAsteriskToken } func isMultiplicativeOperator(kind ast.Kind) bool { return kind == ast.KindAsteriskToken || kind == ast.KindSlashToken || kind == ast.KindPercentToken } func isMultiplicativeOperatorOrHigher(kind ast.Kind) bool { return isExponentiationOperator(kind) || isMultiplicativeOperator(kind) } func isAdditiveOperator(kind ast.Kind) bool { return kind == ast.KindPlusToken || kind == ast.KindMinusToken } func isAdditiveOperatorOrHigher(kind ast.Kind) bool { return isAdditiveOperator(kind) || isMultiplicativeOperatorOrHigher(kind) } func isShiftOperator(kind ast.Kind) bool { return kind == ast.KindLessThanLessThanToken || kind == ast.KindGreaterThanGreaterThanToken || kind == ast.KindGreaterThanGreaterThanGreaterThanToken } func isShiftOperatorOrHigher(kind ast.Kind) bool { return isShiftOperator(kind) || isAdditiveOperatorOrHigher(kind) } func isRelationalOperator(kind ast.Kind) bool { return kind == ast.KindLessThanToken || kind == ast.KindLessThanEqualsToken || kind == ast.KindGreaterThanToken || kind == ast.KindGreaterThanEqualsToken || kind == ast.KindInstanceOfKeyword || kind == ast.KindInKeyword } func isRelationalOperatorOrHigher(kind ast.Kind) bool { return isRelationalOperator(kind) || isShiftOperatorOrHigher(kind) } func isEqualityOperator(kind ast.Kind) bool { return kind == ast.KindEqualsEqualsToken || kind == ast.KindEqualsEqualsEqualsToken || kind == ast.KindExclamationEqualsToken || kind == ast.KindExclamationEqualsEqualsToken } func isEqualityOperatorOrHigher(kind ast.Kind) bool { return isEqualityOperator(kind) || isRelationalOperatorOrHigher(kind) } func isBitwiseOperator(kind ast.Kind) bool { return kind == ast.KindAmpersandToken || kind == ast.KindBarToken || kind == ast.KindCaretToken } func isBitwiseOperatorOrHigher(kind ast.Kind) bool { return isBitwiseOperator(kind) || isEqualityOperatorOrHigher(kind) } func isLogicalOperatorOrHigher(kind ast.Kind) bool { return ast.IsLogicalBinaryOperator(kind) || isBitwiseOperatorOrHigher(kind) } func isAssignmentOperatorOrHigher(kind ast.Kind) bool { return kind == ast.KindQuestionQuestionToken || isLogicalOperatorOrHigher(kind) || ast.IsAssignmentOperator(kind) } func isBinaryOperator(kind ast.Kind) bool { return isAssignmentOperatorOrHigher(kind) || kind == ast.KindCommaToken } func isObjectLiteralType(t *Type) bool { return t.objectFlags&ObjectFlagsObjectLiteral != 0 } func isDeclarationReadonly(declaration *ast.Node) bool { return ast.GetCombinedModifierFlags(declaration)&ast.ModifierFlagsReadonly != 0 && !ast.IsParameterPropertyDeclaration(declaration, declaration.Parent) } type orderedSet[T comparable] struct { valuesByKey map[T]struct{} values []T } func (s *orderedSet[T]) contains(value T) bool { _, ok := s.valuesByKey[value] return ok } func (s *orderedSet[T]) add(value T) { if s.valuesByKey == nil { s.valuesByKey = make(map[T]struct{}) } s.valuesByKey[value] = struct{}{} s.values = append(s.values, value) } func getContainingFunctionOrClassStaticBlock(node *ast.Node) *ast.Node { return ast.FindAncestor(node.Parent, ast.IsFunctionLikeOrClassStaticBlockDeclaration) } func isNodeDescendantOf(node *ast.Node, ancestor *ast.Node) bool { for node != nil { if node == ancestor { return true } node = node.Parent } return false } func isTypeUsableAsPropertyName(t *Type) bool { return t.flags&TypeFlagsStringOrNumberLiteralOrUnique != 0 } /** * Gets the symbolic name for a member from its type. */ func getPropertyNameFromType(t *Type) string { switch { case t.flags&TypeFlagsStringLiteral != 0: return t.AsLiteralType().value.(string) case t.flags&TypeFlagsNumberLiteral != 0: return t.AsLiteralType().value.(jsnum.Number).String() case t.flags&TypeFlagsUniqueESSymbol != 0: return t.AsUniqueESSymbolType().name } panic("Unhandled case in getPropertyNameFromType") } func isNumericLiteralName(name string) bool { // The intent of numeric names is that // - they are names with text in a numeric form, and that // - setting properties/indexing with them is always equivalent to doing so with the numeric literal 'numLit', // acquired by applying the abstract 'ToNumber' operation on the name's text. // // The subtlety is in the latter portion, as we cannot reliably say that anything that looks like a numeric literal is a numeric name. // In fact, it is the case that the text of the name must be equal to 'ToString(numLit)' for this to hold. // // Consider the property name '"0xF00D"'. When one indexes with '0xF00D', they are actually indexing with the value of 'ToString(0xF00D)' // according to the ECMAScript specification, so it is actually as if the user indexed with the string '"61453"'. // Thus, the text of all numeric literals equivalent to '61543' such as '0xF00D', '0xf00D', '0170015', etc. are not valid numeric names // because their 'ToString' representation is not equal to their original text. // This is motivated by ECMA-262 sections 9.3.1, 9.8.1, 11.1.5, and 11.2.1. // // Here, we test whether 'ToString(ToNumber(name))' is exactly equal to 'name'. // The '+' prefix operator is equivalent here to applying the abstract ToNumber operation. // Applying the 'toString()' method on a number gives us the abstract ToString operation on a number. // // Note that this accepts the values 'Infinity', '-Infinity', and 'NaN', and that this is intentional. // This is desired behavior, because when indexing with them as numeric entities, you are indexing // with the strings '"Infinity"', '"-Infinity"', and '"NaN"' respectively. return jsnum.FromString(name).String() == name } func isThisProperty(node *ast.Node) bool { return (ast.IsPropertyAccessExpression(node) || ast.IsElementAccessExpression(node)) && node.Expression().Kind == ast.KindThisKeyword } func isValidNumberString(s string, roundTripOnly bool) bool { if s == "" { return false } n := jsnum.FromString(s) return !n.IsNaN() && !n.IsInf() && (!roundTripOnly || n.String() == s) } func isValidBigIntString(s string, roundTripOnly bool) bool { if s == "" { return false } scanner := scanner.NewScanner() scanner.SetSkipTrivia(false) success := true scanner.SetOnError(func(diagnostic *diagnostics.Message, start, length int, args ...any) { success = false }) scanner.SetText(s + "n") result := scanner.Scan() negative := result == ast.KindMinusToken if negative { result = scanner.Scan() } flags := scanner.TokenFlags() // validate that // * scanning proceeded without error // * a bigint can be scanned, and that when it is scanned, it is // * the full length of the input string (so the scanner is one character beyond the augmented input length) // * it does not contain a numeric separator (the `BigInt` constructor does not accept a numeric separator in its input) return success && result == ast.KindBigIntLiteral && scanner.TokenEnd() == len(s)+1 && flags&ast.TokenFlagsContainsSeparator == 0 && (!roundTripOnly || s == pseudoBigIntToString(jsnum.NewPseudoBigInt(jsnum.ParsePseudoBigInt(scanner.TokenValue()), negative))) } func isValidESSymbolDeclaration(node *ast.Node) bool { if ast.IsVariableDeclaration(node) { return ast.IsVarConst(node) && ast.IsIdentifier(node.AsVariableDeclaration().Name()) && isVariableDeclarationInVariableStatement(node) } if ast.IsPropertyDeclaration(node) { return hasReadonlyModifier(node) && ast.HasStaticModifier(node) } return ast.IsPropertySignatureDeclaration(node) && hasReadonlyModifier(node) } func isVariableDeclarationInVariableStatement(node *ast.Node) bool { return ast.IsVariableDeclarationList(node.Parent) && ast.IsVariableStatement(node.Parent.Parent) } func IsKnownSymbol(symbol *ast.Symbol) bool { return isLateBoundName(symbol.Name) } func isLateBoundName(name string) bool { return len(name) >= 2 && name[0] == '\xfe' && name[1] == '@' } func isObjectOrArrayLiteralType(t *Type) bool { return t.objectFlags&(ObjectFlagsObjectLiteral|ObjectFlagsArrayLiteral) != 0 } func getContainingClassExcludingClassDecorators(node *ast.Node) *ast.ClassLikeDeclaration { decorator := ast.FindAncestorOrQuit(node.Parent, func(n *ast.Node) ast.FindAncestorResult { if ast.IsClassLike(n) { return ast.FindAncestorQuit } if ast.IsDecorator(n) { return ast.FindAncestorTrue } return ast.FindAncestorFalse }) if decorator != nil && ast.IsClassLike(decorator.Parent) { return ast.GetContainingClass(decorator.Parent) } if decorator != nil { return ast.GetContainingClass(decorator) } return ast.GetContainingClass(node) } func isThisTypeParameter(t *Type) bool { return t.flags&TypeFlagsTypeParameter != 0 && t.AsTypeParameter().isThisType } func isClassInstanceProperty(node *ast.Node) bool { return node.Parent != nil && ast.IsClassLike(node.Parent) && ast.IsPropertyDeclaration(node) && !ast.HasAccessorModifier(node) } func isThisInitializedObjectBindingExpression(node *ast.Node) bool { return node != nil && (ast.IsShorthandPropertyAssignment(node) || ast.IsPropertyAssignment(node)) && ast.IsBinaryExpression(node.Parent.Parent) && node.Parent.Parent.AsBinaryExpression().OperatorToken.Kind == ast.KindEqualsToken && node.Parent.Parent.AsBinaryExpression().Right.Kind == ast.KindThisKeyword } func isThisInitializedDeclaration(node *ast.Node) bool { return node != nil && ast.IsVariableDeclaration(node) && node.AsVariableDeclaration().Initializer != nil && node.AsVariableDeclaration().Initializer.Kind == ast.KindThisKeyword } func isInfinityOrNaNString(name string) bool { return name == "Infinity" || name == "-Infinity" || name == "NaN" } func (c *Checker) isConstantVariable(symbol *ast.Symbol) bool { return symbol.Flags&ast.SymbolFlagsVariable != 0 && (c.getDeclarationNodeFlagsFromSymbol(symbol)&ast.NodeFlagsConstant) != 0 } func (c *Checker) isParameterOrMutableLocalVariable(symbol *ast.Symbol) bool { // Return true if symbol is a parameter, a catch clause variable, or a mutable local variable if symbol.ValueDeclaration != nil { declaration := ast.GetRootDeclaration(symbol.ValueDeclaration) return declaration != nil && (ast.IsParameter(declaration) || ast.IsVariableDeclaration(declaration) && (ast.IsCatchClause(declaration.Parent) || c.isMutableLocalVariableDeclaration(declaration))) } return false } func (c *Checker) isMutableLocalVariableDeclaration(declaration *ast.Node) bool { // Return true if symbol is a non-exported and non-global `let` variable return declaration.Parent.Flags&ast.NodeFlagsLet != 0 && !(ast.GetCombinedModifierFlags(declaration)&ast.ModifierFlagsExport != 0 || declaration.Parent.Parent.Kind == ast.KindVariableStatement && ast.IsGlobalSourceFile(declaration.Parent.Parent.Parent)) } func isInAmbientOrTypeNode(node *ast.Node) bool { return node.Flags&ast.NodeFlagsAmbient != 0 || ast.FindAncestor(node, func(n *ast.Node) bool { return ast.IsInterfaceDeclaration(n) || ast.IsTypeOrJSTypeAliasDeclaration(n) || ast.IsTypeLiteralNode(n) }) != nil } func isLiteralExpressionOfObject(node *ast.Node) bool { switch node.Kind { case ast.KindObjectLiteralExpression, ast.KindArrayLiteralExpression, ast.KindRegularExpressionLiteral, ast.KindFunctionExpression, ast.KindClassExpression: return true } return false } func canHaveFlowNode(node *ast.Node) bool { return node.FlowNodeData() != nil } func isNonNullAccess(node *ast.Node) bool { return ast.IsAccessExpression(node) && ast.IsNonNullExpression(node.Expression()) } func getTagNameOfNode(node *ast.Node) *ast.Node { switch node.Kind { case ast.KindJsxOpeningElement: return node.AsJsxOpeningElement().TagName case ast.KindJsxClosingElement: return node.AsJsxClosingElement().TagName case ast.KindJsxSelfClosingElement: return node.AsJsxSelfClosingElement().TagName } panic("Unhandled case in getTagNameOfNode") } func getBindingElementPropertyName(node *ast.Node) *ast.Node { name := node.AsBindingElement().PropertyName if name != nil { return name } return node.Name() } func hasContextSensitiveParameters(node *ast.Node) bool { // Functions with type parameters are not context sensitive. if node.TypeParameters() == nil { // Functions with any parameters that lack type annotations are context sensitive. if core.Some(node.Parameters(), func(p *ast.Node) bool { return p.Type() == nil }) { return true } if !ast.IsArrowFunction(node) { // If the first parameter is not an explicit 'this' parameter, then the function has // an implicit 'this' parameter which is subject to contextual typing. parameter := core.FirstOrNil(node.Parameters()) if parameter == nil || !ast.IsThisParameter(parameter) { return true } } } return false } func isCallChain(node *ast.Node) bool { return ast.IsCallExpression(node) && node.Flags&ast.NodeFlagsOptionalChain != 0 } func (c *Checker) callLikeExpressionMayHaveTypeArguments(node *ast.Node) bool { return ast.IsCallOrNewExpression(node) || ast.IsTaggedTemplateExpression(node) || ast.IsJsxOpeningLikeElement(node) } func isSuperCall(n *ast.Node) bool { return ast.IsCallExpression(n) && n.Expression().Kind == ast.KindSuperKeyword } /** * Determines whether a node is a property or element access expression for `super`. * * @internal */ func isSuperProperty(node *ast.Node) bool { return ast.IsAccessExpression(node) && node.Expression().Kind == ast.KindSuperKeyword } func getMembersOfDeclaration(node *ast.Node) []*ast.Node { switch node.Kind { case ast.KindInterfaceDeclaration: return node.AsInterfaceDeclaration().Members.Nodes case ast.KindClassDeclaration: return node.AsClassDeclaration().Members.Nodes case ast.KindClassExpression: return node.AsClassExpression().Members.Nodes case ast.KindTypeLiteral: return node.AsTypeLiteralNode().Members.Nodes case ast.KindObjectLiteralExpression: return node.AsObjectLiteralExpression().Properties.Nodes } return nil } type FunctionFlags uint32 const ( FunctionFlagsNormal FunctionFlags = 0 FunctionFlagsGenerator FunctionFlags = 1 << 0 FunctionFlagsAsync FunctionFlags = 1 << 1 FunctionFlagsInvalid FunctionFlags = 1 << 2 FunctionFlagsAsyncGenerator FunctionFlags = FunctionFlagsAsync | FunctionFlagsGenerator ) func getFunctionFlags(node *ast.Node) FunctionFlags { if node == nil { return FunctionFlagsInvalid } data := node.BodyData() if data == nil { return FunctionFlagsInvalid } flags := FunctionFlagsNormal switch node.Kind { case ast.KindFunctionDeclaration, ast.KindFunctionExpression, ast.KindMethodDeclaration: if data.AsteriskToken != nil { flags |= FunctionFlagsGenerator } fallthrough case ast.KindArrowFunction: if ast.HasSyntacticModifier(node, ast.ModifierFlagsAsync) { flags |= FunctionFlagsAsync } } if data.Body == nil { flags |= FunctionFlagsInvalid } return flags } func isInRightSideOfImportOrExportAssignment(node *ast.EntityName) bool { for node.Parent.Kind == ast.KindQualifiedName { node = node.Parent } return node.Parent.Kind == ast.KindImportEqualsDeclaration && node.Parent.AsImportEqualsDeclaration().ModuleReference == node || (node.Parent.Kind == ast.KindExportAssignment || node.Parent.Kind == ast.KindJSExportAssignment) && node.Parent.AsExportAssignment().Expression == node } func isJsxIntrinsicTagName(tagName *ast.Node) bool { return ast.IsIdentifier(tagName) && scanner.IsIntrinsicJsxName(tagName.Text()) || ast.IsJsxNamespacedName(tagName) } func getContainingObjectLiteral(f *ast.SignatureDeclaration) *ast.Node { if (f.Kind == ast.KindMethodDeclaration || f.Kind == ast.KindGetAccessor || f.Kind == ast.KindSetAccessor) && f.Parent.Kind == ast.KindObjectLiteralExpression { return f.Parent } else if f.Kind == ast.KindFunctionExpression && f.Parent.Kind == ast.KindPropertyAssignment { return f.Parent.Parent } return nil } func isImportTypeQualifierPart(node *ast.Node) *ast.Node { parent := node.Parent for ast.IsQualifiedName(parent) { node = parent parent = parent.Parent } if parent != nil && parent.Kind == ast.KindImportType && parent.AsImportTypeNode().Qualifier == node { return parent } return nil } func isInNameOfExpressionWithTypeArguments(node *ast.Node) bool { for node.Parent.Kind == ast.KindPropertyAccessExpression { node = node.Parent } return node.Parent.Kind == ast.KindExpressionWithTypeArguments } func getIndexSymbolFromSymbolTable(symbolTable ast.SymbolTable) *ast.Symbol { return symbolTable[ast.InternalSymbolNameIndex] } // Indicates whether the result of an `Expression` will be unused. // NOTE: This requires a node with a valid `parent` pointer. func expressionResultIsUnused(node *ast.Node) bool { for { parent := node.Parent // walk up parenthesized expressions, but keep a pointer to the top-most parenthesized expression if ast.IsParenthesizedExpression(parent) { node = parent continue } // result is unused in an expression statement, `void` expression, or the initializer or incrementer of a `for` loop if ast.IsExpressionStatement(parent) || ast.IsVoidExpression(parent) || ast.IsForStatement(parent) && (parent.Initializer() == node || parent.AsForStatement().Incrementor == node) { return true } if ast.IsBinaryExpression(parent) && parent.AsBinaryExpression().OperatorToken.Kind == ast.KindCommaToken { // left side of comma is always unused if node == parent.AsBinaryExpression().Left { return true } // right side of comma is unused if parent is unused node = parent continue } return false } } func pseudoBigIntToString(value jsnum.PseudoBigInt) string { return value.String() } func getSuperContainer(node *ast.Node, stopOnFunctions bool) *ast.Node { for { node = node.Parent if node == nil { return nil } switch node.Kind { case ast.KindComputedPropertyName: node = node.Parent case ast.KindFunctionDeclaration, ast.KindFunctionExpression, ast.KindArrowFunction: if !stopOnFunctions { continue } fallthrough case ast.KindPropertyDeclaration, ast.KindPropertySignature, ast.KindMethodDeclaration, ast.KindMethodSignature, ast.KindConstructor, ast.KindGetAccessor, ast.KindSetAccessor, ast.KindClassStaticBlockDeclaration: return node case ast.KindDecorator: // Decorators are always applied outside of the body of a class or method. if ast.IsParameter(node.Parent) && ast.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 ast.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 } } } } func forEachYieldExpression(body *ast.Node, visitor func(expr *ast.Node)) { var traverse func(*ast.Node) bool traverse = func(node *ast.Node) bool { switch node.Kind { case ast.KindYieldExpression: visitor(node) operand := node.Expression() if operand != nil { traverse(operand) } case ast.KindEnumDeclaration, ast.KindInterfaceDeclaration, ast.KindModuleDeclaration, ast.KindTypeAliasDeclaration: // These are not allowed inside a generator now, but eventually they may be allowed // as local types. Regardless, skip them to avoid the work. default: if ast.IsFunctionLike(node) { if node.Name() != nil && ast.IsComputedPropertyName(node.Name()) { // Note that we will not include methods/accessors of a class because they would require // first descending into the class. This is by design. traverse(node.Name().Expression()) } } else if !ast.IsPartOfTypeNode(node) { // This is the general case, which should include mostly expressions and statements. // Also includes NodeArrays. node.ForEachChild(traverse) } } return false } traverse(body) } func SkipTypeChecking(sourceFile *ast.SourceFile, options *core.CompilerOptions, host Program, ignoreNoCheck bool) bool { return (!ignoreNoCheck && options.NoCheck.IsTrue()) || options.SkipLibCheck.IsTrue() && sourceFile.IsDeclarationFile || options.SkipDefaultLibCheck.IsTrue() && host.IsSourceFileDefaultLibrary(sourceFile.Path()) || host.IsSourceFromProjectReference(sourceFile.Path()) || !canIncludeBindAndCheckDiagnostics(sourceFile, options) } func canIncludeBindAndCheckDiagnostics(sourceFile *ast.SourceFile, options *core.CompilerOptions) bool { if sourceFile.CheckJsDirective != nil && !sourceFile.CheckJsDirective.Enabled { return false } if sourceFile.ScriptKind == core.ScriptKindTS || sourceFile.ScriptKind == core.ScriptKindTSX || sourceFile.ScriptKind == core.ScriptKindExternal { return true } isJS := sourceFile.ScriptKind == core.ScriptKindJS || sourceFile.ScriptKind == core.ScriptKindJSX isCheckJS := isJS && ast.IsCheckJSEnabledForFile(sourceFile, options) isPlainJS := ast.IsPlainJSFile(sourceFile, options.CheckJs) // By default, only type-check .ts, .tsx, Deferred, plain JS, checked JS and External // - plain JS: .js files with no // ts-check and checkJs: undefined // - check JS: .js files with either // ts-check or checkJs: true // - external: files that are added by plugins return isPlainJS || isCheckJS || sourceFile.ScriptKind == core.ScriptKindDeferred } func getEnclosingContainer(node *ast.Node) *ast.Node { return ast.FindAncestor(node.Parent, func(n *ast.Node) bool { return binder.GetContainerFlags(n)&binder.ContainerFlagsIsContainer != 0 }) } func getDeclarationsOfKind(symbol *ast.Symbol, kind ast.Kind) []*ast.Node { return core.Filter(symbol.Declarations, func(d *ast.Node) bool { return d.Kind == kind }) } func hasType(node *ast.Node) bool { return node.Type() != nil } func getNonRestParameterCount(sig *Signature) int { return len(sig.parameters) - core.IfElse(signatureHasRestParameter(sig), 1, 0) } func minAndMax[T any](slice []T, getValue func(value T) int) (int, int) { var minValue, maxValue int for i, element := range slice { value := getValue(element) if i == 0 { minValue = value maxValue = value } else { minValue = min(minValue, value) maxValue = max(maxValue, value) } } return minValue, maxValue } func getNonModifierTokenRangeOfNode(node *ast.Node) core.TextRange { pos := node.Pos() if node.Modifiers() != nil { if last := ast.FindLastVisibleNode(node.Modifiers().Nodes); last != nil { pos = last.Pos() } } return scanner.GetRangeOfTokenAtPosition(ast.GetSourceFileOfNode(node), pos) } type FeatureMapEntry struct { lib string props []string } var getFeatureMap = sync.OnceValue(func() map[string][]FeatureMapEntry { return map[string][]FeatureMapEntry{ "Array": { {lib: "es2015", props: []string{"find", "findIndex", "fill", "copyWithin", "entries", "keys", "values"}}, {lib: "es2016", props: []string{"includes"}}, {lib: "es2019", props: []string{"flat", "flatMap"}}, {lib: "es2022", props: []string{"at"}}, {lib: "es2023", props: []string{"findLastIndex", "findLast", "toReversed", "toSorted", "toSpliced", "with"}}, }, "Iterator": { {lib: "es2015", props: []string{}}, }, "AsyncIterator": { {lib: "es2015", props: []string{}}, }, "ArrayBuffer": { {lib: "es2024", props: []string{ "maxByteLength", "resizable", "resize", "detached", "transfer", "transferToFixedLength", }}, }, "Atomics": { {lib: "es2017", props: []string{ "add", "and", "compareExchange", "exchange", "isLockFree", "load", "or", "store", "sub", "wait", "notify", "xor", }}, {lib: "es2024", props: []string{ "waitAsync", }}, }, "SharedArrayBuffer": { {lib: "es2017", props: []string{ "byteLength", "slice", }}, {lib: "es2024", props: []string{ "growable", "maxByteLength", "grow", }}, }, "AsyncIterable": { {lib: "es2018", props: []string{}}, }, "AsyncIterableIterator": { {lib: "es2018", props: []string{}}, }, "AsyncGenerator": { {lib: "es2018", props: []string{}}, }, "AsyncGeneratorFunction": { {lib: "es2018", props: []string{}}, }, "RegExp": { {lib: "es2015", props: []string{"flags", "sticky", "unicode"}}, {lib: "es2018", props: []string{"dotAll"}}, {lib: "es2024", props: []string{"unicodeSets"}}, }, "Reflect": { {lib: "es2015", props: []string{"apply", "construct", "defineProperty", "deleteProperty", "get", "getOwnPropertyDescriptor", "getPrototypeOf", "has", "isExtensible", "ownKeys", "preventExtensions", "set", "setPrototypeOf"}}, }, "ArrayConstructor": { {lib: "es2015", props: []string{"from", "of"}}, {lib: "esnext", props: []string{"fromAsync"}}, }, "ObjectConstructor": { {lib: "es2015", props: []string{"assign", "getOwnPropertySymbols", "keys", "is", "setPrototypeOf"}}, {lib: "es2017", props: []string{"values", "entries", "getOwnPropertyDescriptors"}}, {lib: "es2019", props: []string{"fromEntries"}}, {lib: "es2022", props: []string{"hasOwn"}}, {lib: "es2024", props: []string{"groupBy"}}, }, "NumberConstructor": { {lib: "es2015", props: []string{"isFinite", "isInteger", "isNaN", "isSafeInteger", "parseFloat", "parseInt"}}, }, "Math": { {lib: "es2015", props: []string{"clz32", "imul", "sign", "log10", "log2", "log1p", "expm1", "cosh", "sinh", "tanh", "acosh", "asinh", "atanh", "hypot", "trunc", "fround", "cbrt"}}, }, "Map": { {lib: "es2015", props: []string{"entries", "keys", "values"}}, }, "MapConstructor": { {lib: "es2024", props: []string{"groupBy"}}, }, "Set": { {lib: "es2015", props: []string{"entries", "keys", "values"}}, {lib: "esnext", props: []string{ "union", "intersection", "difference", "symmetricDifference", "isSubsetOf", "isSupersetOf", "isDisjointFrom", }}, }, "PromiseConstructor": { {lib: "es2015", props: []string{"all", "race", "reject", "resolve"}}, {lib: "es2020", props: []string{"allSettled"}}, {lib: "es2021", props: []string{"any"}}, {lib: "es2024", props: []string{"withResolvers"}}, }, "Symbol": { {lib: "es2015", props: []string{"for", "keyFor"}}, {lib: "es2019", props: []string{"description"}}, }, "WeakMap": { {lib: "es2015", props: []string{"entries", "keys", "values"}}, }, "WeakSet": { {lib: "es2015", props: []string{"entries", "keys", "values"}}, }, "String": { {lib: "es2015", props: []string{"codePointAt", "includes", "endsWith", "normalize", "repeat", "startsWith", "anchor", "big", "blink", "bold", "fixed", "fontcolor", "fontsize", "italics", "link", "small", "strike", "sub", "sup"}}, {lib: "es2017", props: []string{"padStart", "padEnd"}}, {lib: "es2019", props: []string{"trimStart", "trimEnd", "trimLeft", "trimRight"}}, {lib: "es2020", props: []string{"matchAll"}}, {lib: "es2021", props: []string{"replaceAll"}}, {lib: "es2022", props: []string{"at"}}, {lib: "es2024", props: []string{"isWellFormed", "toWellFormed"}}, }, "StringConstructor": { {lib: "es2015", props: []string{"fromCodePoint", "raw"}}, }, "DateTimeFormat": { {lib: "es2017", props: []string{"formatToParts"}}, }, "Promise": { {lib: "es2015", props: []string{}}, {lib: "es2018", props: []string{"finally"}}, }, "RegExpMatchArray": { {lib: "es2018", props: []string{"groups"}}, }, "RegExpExecArray": { {lib: "es2018", props: []string{"groups"}}, }, "Intl": { {lib: "es2018", props: []string{"PluralRules"}}, }, "NumberFormat": { {lib: "es2018", props: []string{"formatToParts"}}, }, "SymbolConstructor": { {lib: "es2020", props: []string{"matchAll"}}, {lib: "esnext", props: []string{ "metadata", "dispose", "asyncDispose", }}, }, "DataView": { {lib: "es2020", props: []string{"setBigInt64", "setBigUint64", "getBigInt64", "getBigUint64"}}, }, "BigInt": { {lib: "es2020", props: []string{}}, }, "RelativeTimeFormat": { {lib: "es2020", props: []string{"format", "formatToParts", "resolvedOptions"}}, }, "Int8Array": { {lib: "es2022", props: []string{"at"}}, {lib: "es2023", props: []string{"findLastIndex", "findLast", "toReversed", "toSorted", "toSpliced", "with"}}, }, "Uint8Array": { {lib: "es2022", props: []string{"at"}}, {lib: "es2023", props: []string{"findLastIndex", "findLast", "toReversed", "toSorted", "toSpliced", "with"}}, }, "Uint8ClampedArray": { {lib: "es2022", props: []string{"at"}}, {lib: "es2023", props: []string{"findLastIndex", "findLast", "toReversed", "toSorted", "toSpliced", "with"}}, }, "Int16Array": { {lib: "es2022", props: []string{"at"}}, {lib: "es2023", props: []string{"findLastIndex", "findLast", "toReversed", "toSorted", "toSpliced", "with"}}, }, "Uint16Array": { {lib: "es2022", props: []string{"at"}}, {lib: "es2023", props: []string{"findLastIndex", "findLast", "toReversed", "toSorted", "toSpliced", "with"}}, }, "Int32Array": { {lib: "es2022", props: []string{"at"}}, {lib: "es2023", props: []string{"findLastIndex", "findLast", "toReversed", "toSorted", "toSpliced", "with"}}, }, "Uint32Array": { {lib: "es2022", props: []string{"at"}}, {lib: "es2023", props: []string{"findLastIndex", "findLast", "toReversed", "toSorted", "toSpliced", "with"}}, }, "Float32Array": { {lib: "es2022", props: []string{"at"}}, {lib: "es2023", props: []string{"findLastIndex", "findLast", "toReversed", "toSorted", "toSpliced", "with"}}, }, "Float64Array": { {lib: "es2022", props: []string{"at"}}, {lib: "es2023", props: []string{"findLastIndex", "findLast", "toReversed", "toSorted", "toSpliced", "with"}}, }, "BigInt64Array": { {lib: "es2020", props: []string{}}, {lib: "es2022", props: []string{"at"}}, {lib: "es2023", props: []string{"findLastIndex", "findLast", "toReversed", "toSorted", "toSpliced", "with"}}, }, "BigUint64Array": { {lib: "es2020", props: []string{}}, {lib: "es2022", props: []string{"at"}}, {lib: "es2023", props: []string{"findLastIndex", "findLast", "toReversed", "toSorted", "toSpliced", "with"}}, }, "Error": { {lib: "es2022", props: []string{"cause"}}, }, } }) func rangeOfTypeParameters(sourceFile *ast.SourceFile, typeParameters *ast.NodeList) core.TextRange { return core.NewTextRange(typeParameters.Pos()-1, min(len(sourceFile.Text()), scanner.SkipTrivia(sourceFile.Text(), typeParameters.End())+1)) } func tryGetPropertyAccessOrIdentifierToString(expr *ast.Node) string { switch { case ast.IsPropertyAccessExpression(expr): baseStr := tryGetPropertyAccessOrIdentifierToString(expr.Expression()) if baseStr != "" { return baseStr + "." + entityNameToString(expr.Name()) } case ast.IsElementAccessExpression(expr): baseStr := tryGetPropertyAccessOrIdentifierToString(expr.Expression()) if baseStr != "" && ast.IsPropertyName(expr.AsElementAccessExpression().ArgumentExpression) { return baseStr + "." + ast.GetPropertyNameForPropertyNameNode(expr.AsElementAccessExpression().ArgumentExpression) } case ast.IsIdentifier(expr): return expr.Text() case ast.IsJsxNamespacedName(expr): return entityNameToString(expr) } return "" } func getFirstJSDocTag(node *ast.Node, f func(*ast.Node) bool) *ast.Node { for _, jsdoc := range node.JSDoc(nil) { tags := jsdoc.AsJSDoc().Tags if tags != nil { for _, tag := range tags.Nodes { if f(tag) { return tag } } } } return nil } func getJSDocDeprecatedTag(node *ast.Node) *ast.Node { return getFirstJSDocTag(node, ast.IsJSDocDeprecatedTag) } func allDeclarationsInSameSourceFile(symbol *ast.Symbol) bool { if len(symbol.Declarations) > 1 { var sourceFile *ast.SourceFile for i, d := range symbol.Declarations { if i == 0 { sourceFile = ast.GetSourceFileOfNode(d) } else if ast.GetSourceFileOfNode(d) != sourceFile { return false } } } return true } func containsNonMissingUndefinedType(c *Checker, t *Type) bool { var candidate *Type if t.flags&TypeFlagsUnion != 0 { candidate = t.AsUnionType().types[0] } else { candidate = t } return candidate.flags&TypeFlagsUndefined != 0 && candidate != c.missingType } func getAnyImportSyntax(node *ast.Node) *ast.Node { var importNode *ast.Node switch node.Kind { case ast.KindImportEqualsDeclaration: importNode = node case ast.KindImportClause: importNode = node.Parent case ast.KindNamespaceImport: importNode = node.Parent.Parent case ast.KindImportSpecifier: importNode = node.Parent.Parent.Parent default: return nil } return importNode } // A reserved member name consists of the byte 0xFE (which is an invalid UTF-8 encoding) followed by one or more // characters where the first character is not '@' or '#'. The '@' character indicates that the name is denoted by // a well known ES Symbol instance and the '#' character indicates that the name is a PrivateIdentifier. func isReservedMemberName(name string) bool { return len(name) >= 2 && name[0] == '\xFE' && name[1] != '@' && name[1] != '#' } func introducesArgumentsExoticObject(node *ast.Node) bool { switch node.Kind { case ast.KindMethodDeclaration, ast.KindMethodSignature, ast.KindConstructor, ast.KindGetAccessor, ast.KindSetAccessor, ast.KindFunctionDeclaration, ast.KindFunctionExpression: return true } return false } func symbolsToArray(symbols ast.SymbolTable) []*ast.Symbol { var result []*ast.Symbol for id, symbol := range symbols { if !isReservedMemberName(id) { result = append(result, symbol) } } return result } // See comment on `declareModuleMember` in `binder.go`. func GetCombinedLocalAndExportSymbolFlags(symbol *ast.Symbol) ast.SymbolFlags { if symbol.ExportSymbol != nil { return symbol.Flags | symbol.ExportSymbol.Flags } return symbol.Flags } func SkipAlias(symbol *ast.Symbol, checker *Checker) *ast.Symbol { if symbol.Flags&ast.SymbolFlagsAlias != 0 { return checker.GetAliasedSymbol(symbol) } return symbol } // True if the symbol is for an external module, as opposed to a namespace. func IsExternalModuleSymbol(moduleSymbol *ast.Symbol) bool { firstRune, _ := utf8.DecodeRuneInString(moduleSymbol.Name) return moduleSymbol.Flags&ast.SymbolFlagsModule != 0 && firstRune == '"' } func (c *Checker) isCanceled() bool { return c.ctx != nil && c.ctx.Err() != nil } func (c *Checker) checkNotCanceled() { if c.wasCanceled { panic("Checker was previously cancelled") } } func (c *Checker) getPackagesMap() map[string]bool { if c.packagesMap == nil { c.packagesMap = make(map[string]bool) resolvedModules := c.program.GetResolvedModules() for _, resolvedModulesInFile := range resolvedModules { for _, module := range resolvedModulesInFile { if module.PackageId.Name != "" { c.packagesMap[module.PackageId.Name] = c.packagesMap[module.PackageId.Name] || module.Extension == tspath.ExtensionDts } } } } return c.packagesMap } func (c *Checker) typesPackageExists(packageName string) bool { packagesMap := c.getPackagesMap() _, ok := packagesMap[module.GetTypesPackageName(packageName)] return ok } func (c *Checker) packageBundlesTypes(packageName string) bool { packagesMap := c.getPackagesMap() hasTypes, _ := packagesMap[packageName] return hasTypes } func ValueToString(value any) string { switch value := value.(type) { case string: return "\"" + printer.EscapeString(value, '"') + "\"" case jsnum.Number: return value.String() case bool: return core.IfElse(value, "true", "false") case jsnum.PseudoBigInt: return value.String() + "n" } panic("unhandled value type in valueToString") } func nodeStartsNewLexicalEnvironment(node *ast.Node) bool { switch node.Kind { case ast.KindConstructor, ast.KindFunctionExpression, ast.KindFunctionDeclaration, ast.KindArrowFunction, ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor, ast.KindModuleDeclaration, ast.KindSourceFile: return true } return false }