From 78201db01254285fbaa57e33aed2396b99c833d6 Mon Sep 17 00:00:00 2001 From: Egor Aristov Date: Wed, 15 Oct 2025 17:28:42 +0300 Subject: [PATCH] remove unused packages --- examples/simple/ts/index.ts | 5 - kitcom/internal/tsgo/checker/checker.go | 30925 ---------------- kitcom/internal/tsgo/checker/checker_test.go | 103 - kitcom/internal/tsgo/checker/emitresolver.go | 1112 - kitcom/internal/tsgo/checker/exports.go | 172 - kitcom/internal/tsgo/checker/flow.go | 2724 -- kitcom/internal/tsgo/checker/grammarchecks.go | 2260 -- kitcom/internal/tsgo/checker/inference.go | 1620 - kitcom/internal/tsgo/checker/jsdoc.go | 97 - kitcom/internal/tsgo/checker/jsx.go | 1467 - kitcom/internal/tsgo/checker/mapper.go | 294 - kitcom/internal/tsgo/checker/nodebuilder.go | 186 - .../internal/tsgo/checker/nodebuilderimpl.go | 3112 -- .../tsgo/checker/nodebuilderscopes.go | 246 - kitcom/internal/tsgo/checker/printer.go | 379 - kitcom/internal/tsgo/checker/relater.go | 4923 --- kitcom/internal/tsgo/checker/services.go | 811 - .../tsgo/checker/stringer_generated.go | 24 - .../tsgo/checker/symbolaccessibility.go | 759 - kitcom/internal/tsgo/checker/symboltracker.go | 138 - kitcom/internal/tsgo/checker/types.go | 1287 - kitcom/internal/tsgo/checker/utilities.go | 1860 - 22 files changed, 54504 deletions(-) delete mode 100644 kitcom/internal/tsgo/checker/checker.go delete mode 100644 kitcom/internal/tsgo/checker/checker_test.go delete mode 100644 kitcom/internal/tsgo/checker/emitresolver.go delete mode 100644 kitcom/internal/tsgo/checker/exports.go delete mode 100644 kitcom/internal/tsgo/checker/flow.go delete mode 100644 kitcom/internal/tsgo/checker/grammarchecks.go delete mode 100644 kitcom/internal/tsgo/checker/inference.go delete mode 100644 kitcom/internal/tsgo/checker/jsdoc.go delete mode 100644 kitcom/internal/tsgo/checker/jsx.go delete mode 100644 kitcom/internal/tsgo/checker/mapper.go delete mode 100644 kitcom/internal/tsgo/checker/nodebuilder.go delete mode 100644 kitcom/internal/tsgo/checker/nodebuilderimpl.go delete mode 100644 kitcom/internal/tsgo/checker/nodebuilderscopes.go delete mode 100644 kitcom/internal/tsgo/checker/printer.go delete mode 100644 kitcom/internal/tsgo/checker/relater.go delete mode 100644 kitcom/internal/tsgo/checker/services.go delete mode 100644 kitcom/internal/tsgo/checker/stringer_generated.go delete mode 100644 kitcom/internal/tsgo/checker/symbolaccessibility.go delete mode 100644 kitcom/internal/tsgo/checker/symboltracker.go delete mode 100644 kitcom/internal/tsgo/checker/types.go delete mode 100644 kitcom/internal/tsgo/checker/utilities.go diff --git a/examples/simple/ts/index.ts b/examples/simple/ts/index.ts index d592f0e..477d85e 100644 --- a/examples/simple/ts/index.ts +++ b/examples/simple/ts/index.ts @@ -1,8 +1,3 @@ - - - - - /** * @kittenipc:api */ diff --git a/kitcom/internal/tsgo/checker/checker.go b/kitcom/internal/tsgo/checker/checker.go deleted file mode 100644 index 21413ea..0000000 --- a/kitcom/internal/tsgo/checker/checker.go +++ /dev/null @@ -1,30925 +0,0 @@ -package checker - -import ( - "context" - "fmt" - "iter" - "maps" - "math" - "slices" - "strconv" - "strings" - "sync" - "sync/atomic" - "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/collections" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/debug" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/evaluator" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/jsnum" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/module" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/modulespecifiers" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/scanner" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/stringutil" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tsoptions" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" -) - -// 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 - CheckModeForceTuple CheckMode = 1 << 7 -) - -type TypeSystemEntity any - -type TypeSystemPropertyName int32 - -const ( - TypeSystemPropertyNameType TypeSystemPropertyName = iota - TypeSystemPropertyNameResolvedBaseConstructorType - TypeSystemPropertyNameDeclaredType - TypeSystemPropertyNameResolvedReturnType - TypeSystemPropertyNameResolvedBaseConstraint - TypeSystemPropertyNameResolvedTypeArguments - TypeSystemPropertyNameResolvedBaseTypes - TypeSystemPropertyNameWriteType - TypeSystemPropertyNameInitializerIsUndefined -) - -type TypeResolution struct { - target TypeSystemEntity - propertyName TypeSystemPropertyName - result bool -} - -// ContextualInfo - -type ContextualInfo struct { - node *ast.Node - t *Type - isCache bool -} - -// InferenceContextInfo - -type InferenceContextInfo struct { - node *ast.Node - context *InferenceContext -} - -// WideningKind - -type WideningKind int32 - -const ( - WideningKindNormal WideningKind = iota - WideningKindFunctionReturn - WideningKindGeneratorNext - WideningKindGeneratorYield -) - -// EnumLiteralKey - -type EnumLiteralKey struct { - enumSymbol *ast.Symbol - value any -} - -// EnumRelationKey - -type EnumRelationKey struct { - sourceId ast.SymbolId - targetId ast.SymbolId -} - -// TypeCacheKind - -type CachedTypeKind int32 - -const ( - CachedTypeKindLiteralUnionBaseType CachedTypeKind = iota - CachedTypeKindIndexType - CachedTypeKindStringIndexType - CachedTypeKindEquivalentBaseType - CachedTypeKindApparentType - CachedTypeKindAwaitedType - CachedTypeKindEvolvingArrayType - CachedTypeKindArrayLiteralType - CachedTypeKindPermissiveInstantiation - CachedTypeKindRestrictiveInstantiation - CachedTypeKindRestrictiveTypeParameter - CachedTypeKindIndexedAccessForReading - CachedTypeKindIndexedAccessForWriting - CachedTypeKindWidened - CachedTypeKindRegularObjectLiteral - CachedTypeKindPromisedTypeOfPromise - CachedTypeKindDefaultOnlyType - CachedTypeKindSyntheticType - CachedTypeKindDecoratorContext - CachedTypeKindDecoratorContextStatic - CachedTypeKindDecoratorContextPrivate - CachedTypeKindDecoratorContextPrivateStatic -) - -// CachedTypeKey - -type CachedTypeKey struct { - kind CachedTypeKind - typeId TypeId -} - -// NarrowedTypeKey - -type NarrowedTypeKey struct { - t *Type - candidate *Type - assumeTrue bool - checkDerived bool -} - -// UnionOfUnionKey - -type UnionOfUnionKey struct { - id1 TypeId - id2 TypeId - r UnionReduction - a string -} - -// CachedSignatureKey - -type CachedSignatureKey struct { - sig *Signature - key string // Type list key or one of the strings below -} - -const ( - SignatureKeyErased string = "-" - SignatureKeyCanonical string = "*" - SignatureKeyBase string = "#" - SignatureKeyInner string = "<" - SignatureKeyOuter string = ">" - SignatureKeyImplementation string = "+" -) - -// StringMappingKey - -type StringMappingKey struct { - s *ast.Symbol - t *Type -} - -// AssignmentReducedKey - -type AssignmentReducedKey struct { - id1 TypeId - id2 TypeId -} - -// DiscriminatedContextualTypeKey - -type DiscriminatedContextualTypeKey struct { - nodeId ast.NodeId - typeId TypeId -} - -// InstantiationExpressionKey - -type InstantiationExpressionKey struct { - nodeId ast.NodeId - typeId TypeId -} - -// SubstitutionTypeKey - -type SubstitutionTypeKey struct { - baseId TypeId - constraintId TypeId -} - -// ReverseMappedTypeKey - -type ReverseMappedTypeKey struct { - sourceId TypeId - targetId TypeId - constraintId TypeId -} - -// IterationTypesKey - -type IterationTypesKey struct { - typeId TypeId - use IterationUse -} - -// FlowLoopKey - -type FlowLoopKey struct { - flowNode *ast.FlowNode - refKey string -} - -type FlowLoopInfo struct { - key FlowLoopKey - types []*Type -} - -// InferenceFlags - -type InferenceFlags uint32 - -const ( - InferenceFlagsNone InferenceFlags = 0 // No special inference behaviors - InferenceFlagsNoDefault InferenceFlags = 1 << 0 // Infer silentNeverType for no inferences (otherwise anyType or unknownType) - InferenceFlagsAnyDefault InferenceFlags = 1 << 1 // Infer anyType (in JS files) for no inferences (otherwise unknownType) - InferenceFlagsSkippedGenericFunction InferenceFlags = 1 << 2 // A generic function was skipped during inference -) - -// InferenceContext - -type InferenceContext struct { - inferences []*InferenceInfo // Inferences made for each type parameter - signature *Signature // Generic signature for which inferences are made (if any) - flags InferenceFlags // Inference flags - compareTypes TypeComparer // Type comparer function - mapper *TypeMapper // Mapper that fixes inferences - nonFixingMapper *TypeMapper // Mapper that doesn't fix inferences - returnMapper *TypeMapper // Type mapper for inferences from return types (if any) - outerReturnMapper *TypeMapper // Type mapper for inferences from return types of outer function (if any) - inferredTypeParameters []*Type // Inferred type parameters for function result - intraExpressionInferenceSites []IntraExpressionInferenceSite -} - -type InferenceInfo struct { - typeParameter *Type // Type parameter for which inferences are being made - candidates []*Type // Candidates in covariant positions - contraCandidates []*Type // Candidates in contravariant positions - inferredType *Type // Cache for resolved inferred type - priority InferencePriority // Priority of current inference set - topLevel bool // True if all inferences are to top level occurrences - isFixed bool // True if inferences are fixed - impliedArity int // Implied arity (or -1) -} - -type InferencePriority int32 - -const ( - InferencePriorityNone InferencePriority = 0 - InferencePriorityNakedTypeVariable InferencePriority = 1 << 0 // Naked type variable in union or intersection type - InferencePrioritySpeculativeTuple InferencePriority = 1 << 1 // Speculative tuple inference - InferencePrioritySubstituteSource InferencePriority = 1 << 2 // Source of inference originated within a substitution type's substitute - InferencePriorityHomomorphicMappedType InferencePriority = 1 << 3 // Reverse inference for homomorphic mapped type - InferencePriorityPartialHomomorphicMappedType InferencePriority = 1 << 4 // Partial reverse inference for homomorphic mapped type - InferencePriorityMappedTypeConstraint InferencePriority = 1 << 5 // Reverse inference for mapped type - InferencePriorityContravariantConditional InferencePriority = 1 << 6 // Conditional type in contravariant position - InferencePriorityReturnType InferencePriority = 1 << 7 // Inference made from return type of generic function - InferencePriorityLiteralKeyof InferencePriority = 1 << 8 // Inference made from a string literal to a keyof T - InferencePriorityNoConstraints InferencePriority = 1 << 9 // Don't infer from constraints of instantiable types - InferencePriorityAlwaysStrict InferencePriority = 1 << 10 // Always use strict rules for contravariant inferences - InferencePriorityMaxValue InferencePriority = 1 << 11 // Seed for inference priority tracking - InferencePriorityCircularity InferencePriority = -1 // Inference circularity (value less than all other priorities) - - InferencePriorityPriorityImpliesCombination = InferencePriorityReturnType | InferencePriorityMappedTypeConstraint | InferencePriorityLiteralKeyof // These priorities imply that the resulting type should be a combination of all candidates -) - -type IntraExpressionInferenceSite struct { - node *ast.Node - t *Type -} - -type DeclarationMeaning uint32 - -const ( - DeclarationMeaningGetAccessor DeclarationMeaning = 1 << iota - DeclarationMeaningSetAccessor - DeclarationMeaningPropertyAssignment - DeclarationMeaningMethod - DeclarationMeaningPrivateStatic - DeclarationMeaningGetOrSetAccessor = DeclarationMeaningGetAccessor | DeclarationMeaningSetAccessor - DeclarationMeaningPropertyAssignmentOrMethod = DeclarationMeaningPropertyAssignment | DeclarationMeaningMethod -) - -type DeclarationSpaces int32 - -const ( - DeclarationSpacesNone DeclarationSpaces = 0 - DeclarationSpacesExportValue DeclarationSpaces = 1 << 0 - DeclarationSpacesExportType DeclarationSpaces = 1 << 1 - DeclarationSpacesExportNamespace DeclarationSpaces = 1 << 2 -) - -// IntrinsicTypeKind - -type IntrinsicTypeKind int32 - -const ( - IntrinsicTypeKindUnknown IntrinsicTypeKind = iota - IntrinsicTypeKindUppercase - IntrinsicTypeKindLowercase - IntrinsicTypeKindCapitalize - IntrinsicTypeKindUncapitalize - IntrinsicTypeKindNoInfer -) - -var intrinsicTypeKinds = map[string]IntrinsicTypeKind{ - "Uppercase": IntrinsicTypeKindUppercase, - "Lowercase": IntrinsicTypeKindLowercase, - "Capitalize": IntrinsicTypeKindCapitalize, - "Uncapitalize": IntrinsicTypeKindUncapitalize, - "NoInfer": IntrinsicTypeKindNoInfer, -} - -type MappedTypeModifiers uint32 - -const ( - MappedTypeModifiersIncludeReadonly MappedTypeModifiers = 1 << 0 - MappedTypeModifiersExcludeReadonly MappedTypeModifiers = 1 << 1 - MappedTypeModifiersIncludeOptional MappedTypeModifiers = 1 << 2 - MappedTypeModifiersExcludeOptional MappedTypeModifiers = 1 << 3 -) - -type MappedTypeNameTypeKind int32 - -const ( - MappedTypeNameTypeKindNone MappedTypeNameTypeKind = iota - MappedTypeNameTypeKindFiltering - MappedTypeNameTypeKindRemapping -) - -type ReferenceHint int32 - -const ( - ReferenceHintUnspecified ReferenceHint = iota - ReferenceHintIdentifier - ReferenceHintProperty - ReferenceHintExportAssignment - ReferenceHintJsx - ReferenceHintExportImportEquals - ReferenceHintExportSpecifier - ReferenceHintDecorator -) - -type TypeFacts uint32 - -const ( - TypeFactsNone TypeFacts = 0 - TypeFactsTypeofEQString TypeFacts = 1 << 0 - TypeFactsTypeofEQNumber TypeFacts = 1 << 1 - TypeFactsTypeofEQBigInt TypeFacts = 1 << 2 - TypeFactsTypeofEQBoolean TypeFacts = 1 << 3 - TypeFactsTypeofEQSymbol TypeFacts = 1 << 4 - TypeFactsTypeofEQObject TypeFacts = 1 << 5 - TypeFactsTypeofEQFunction TypeFacts = 1 << 6 - TypeFactsTypeofEQHostObject TypeFacts = 1 << 7 - TypeFactsTypeofNEString TypeFacts = 1 << 8 - TypeFactsTypeofNENumber TypeFacts = 1 << 9 - TypeFactsTypeofNEBigInt TypeFacts = 1 << 10 - TypeFactsTypeofNEBoolean TypeFacts = 1 << 11 - TypeFactsTypeofNESymbol TypeFacts = 1 << 12 - TypeFactsTypeofNEObject TypeFacts = 1 << 13 - TypeFactsTypeofNEFunction TypeFacts = 1 << 14 - TypeFactsTypeofNEHostObject TypeFacts = 1 << 15 - TypeFactsEQUndefined TypeFacts = 1 << 16 - TypeFactsEQNull TypeFacts = 1 << 17 - TypeFactsEQUndefinedOrNull TypeFacts = 1 << 18 - TypeFactsNEUndefined TypeFacts = 1 << 19 - TypeFactsNENull TypeFacts = 1 << 20 - TypeFactsNEUndefinedOrNull TypeFacts = 1 << 21 - TypeFactsTruthy TypeFacts = 1 << 22 - TypeFactsFalsy TypeFacts = 1 << 23 - TypeFactsIsUndefined TypeFacts = 1 << 24 - TypeFactsIsNull TypeFacts = 1 << 25 - TypeFactsIsUndefinedOrNull TypeFacts = TypeFactsIsUndefined | TypeFactsIsNull - TypeFactsAll TypeFacts = (1 << 27) - 1 - // The following members encode facts about particular kinds of types for use in the getTypeFacts function. - // The presence of a particular fact means that the given test is true for some (and possibly all) values - // of that kind of type. - TypeFactsBaseStringStrictFacts TypeFacts = TypeFactsTypeofEQString | TypeFactsTypeofNENumber | TypeFactsTypeofNEBigInt | TypeFactsTypeofNEBoolean | TypeFactsTypeofNESymbol | TypeFactsTypeofNEObject | TypeFactsTypeofNEFunction | TypeFactsTypeofNEHostObject | TypeFactsNEUndefined | TypeFactsNENull | TypeFactsNEUndefinedOrNull - TypeFactsBaseStringFacts TypeFacts = TypeFactsBaseStringStrictFacts | TypeFactsEQUndefined | TypeFactsEQNull | TypeFactsEQUndefinedOrNull | TypeFactsFalsy - TypeFactsStringStrictFacts TypeFacts = TypeFactsBaseStringStrictFacts | TypeFactsTruthy | TypeFactsFalsy - TypeFactsStringFacts TypeFacts = TypeFactsBaseStringFacts | TypeFactsTruthy - TypeFactsEmptyStringStrictFacts TypeFacts = TypeFactsBaseStringStrictFacts | TypeFactsFalsy - TypeFactsEmptyStringFacts TypeFacts = TypeFactsBaseStringFacts - TypeFactsNonEmptyStringStrictFacts TypeFacts = TypeFactsBaseStringStrictFacts | TypeFactsTruthy - TypeFactsNonEmptyStringFacts TypeFacts = TypeFactsBaseStringFacts | TypeFactsTruthy - TypeFactsBaseNumberStrictFacts TypeFacts = TypeFactsTypeofEQNumber | TypeFactsTypeofNEString | TypeFactsTypeofNEBigInt | TypeFactsTypeofNEBoolean | TypeFactsTypeofNESymbol | TypeFactsTypeofNEObject | TypeFactsTypeofNEFunction | TypeFactsTypeofNEHostObject | TypeFactsNEUndefined | TypeFactsNENull | TypeFactsNEUndefinedOrNull - TypeFactsBaseNumberFacts TypeFacts = TypeFactsBaseNumberStrictFacts | TypeFactsEQUndefined | TypeFactsEQNull | TypeFactsEQUndefinedOrNull | TypeFactsFalsy - TypeFactsNumberStrictFacts TypeFacts = TypeFactsBaseNumberStrictFacts | TypeFactsTruthy | TypeFactsFalsy - TypeFactsNumberFacts TypeFacts = TypeFactsBaseNumberFacts | TypeFactsTruthy - TypeFactsZeroNumberStrictFacts TypeFacts = TypeFactsBaseNumberStrictFacts | TypeFactsFalsy - TypeFactsZeroNumberFacts TypeFacts = TypeFactsBaseNumberFacts - TypeFactsNonZeroNumberStrictFacts TypeFacts = TypeFactsBaseNumberStrictFacts | TypeFactsTruthy - TypeFactsNonZeroNumberFacts TypeFacts = TypeFactsBaseNumberFacts | TypeFactsTruthy - TypeFactsBaseBigIntStrictFacts TypeFacts = TypeFactsTypeofEQBigInt | TypeFactsTypeofNEString | TypeFactsTypeofNENumber | TypeFactsTypeofNEBoolean | TypeFactsTypeofNESymbol | TypeFactsTypeofNEObject | TypeFactsTypeofNEFunction | TypeFactsTypeofNEHostObject | TypeFactsNEUndefined | TypeFactsNENull | TypeFactsNEUndefinedOrNull - TypeFactsBaseBigIntFacts TypeFacts = TypeFactsBaseBigIntStrictFacts | TypeFactsEQUndefined | TypeFactsEQNull | TypeFactsEQUndefinedOrNull | TypeFactsFalsy - TypeFactsBigIntStrictFacts TypeFacts = TypeFactsBaseBigIntStrictFacts | TypeFactsTruthy | TypeFactsFalsy - TypeFactsBigIntFacts TypeFacts = TypeFactsBaseBigIntFacts | TypeFactsTruthy - TypeFactsZeroBigIntStrictFacts TypeFacts = TypeFactsBaseBigIntStrictFacts | TypeFactsFalsy - TypeFactsZeroBigIntFacts TypeFacts = TypeFactsBaseBigIntFacts - TypeFactsNonZeroBigIntStrictFacts TypeFacts = TypeFactsBaseBigIntStrictFacts | TypeFactsTruthy - TypeFactsNonZeroBigIntFacts TypeFacts = TypeFactsBaseBigIntFacts | TypeFactsTruthy - TypeFactsBaseBooleanStrictFacts TypeFacts = TypeFactsTypeofEQBoolean | TypeFactsTypeofNEString | TypeFactsTypeofNENumber | TypeFactsTypeofNEBigInt | TypeFactsTypeofNESymbol | TypeFactsTypeofNEObject | TypeFactsTypeofNEFunction | TypeFactsTypeofNEHostObject | TypeFactsNEUndefined | TypeFactsNENull | TypeFactsNEUndefinedOrNull - TypeFactsBaseBooleanFacts TypeFacts = TypeFactsBaseBooleanStrictFacts | TypeFactsEQUndefined | TypeFactsEQNull | TypeFactsEQUndefinedOrNull | TypeFactsFalsy - TypeFactsBooleanStrictFacts TypeFacts = TypeFactsBaseBooleanStrictFacts | TypeFactsTruthy | TypeFactsFalsy - TypeFactsBooleanFacts TypeFacts = TypeFactsBaseBooleanFacts | TypeFactsTruthy - TypeFactsFalseStrictFacts TypeFacts = TypeFactsBaseBooleanStrictFacts | TypeFactsFalsy - TypeFactsFalseFacts TypeFacts = TypeFactsBaseBooleanFacts - TypeFactsTrueStrictFacts TypeFacts = TypeFactsBaseBooleanStrictFacts | TypeFactsTruthy - TypeFactsTrueFacts TypeFacts = TypeFactsBaseBooleanFacts | TypeFactsTruthy - TypeFactsSymbolStrictFacts TypeFacts = TypeFactsTypeofEQSymbol | TypeFactsTypeofNEString | TypeFactsTypeofNENumber | TypeFactsTypeofNEBigInt | TypeFactsTypeofNEBoolean | TypeFactsTypeofNEObject | TypeFactsTypeofNEFunction | TypeFactsTypeofNEHostObject | TypeFactsNEUndefined | TypeFactsNENull | TypeFactsNEUndefinedOrNull | TypeFactsTruthy - TypeFactsSymbolFacts TypeFacts = TypeFactsSymbolStrictFacts | TypeFactsEQUndefined | TypeFactsEQNull | TypeFactsEQUndefinedOrNull | TypeFactsFalsy - TypeFactsObjectStrictFacts TypeFacts = TypeFactsTypeofEQObject | TypeFactsTypeofEQHostObject | TypeFactsTypeofNEString | TypeFactsTypeofNENumber | TypeFactsTypeofNEBigInt | TypeFactsTypeofNEBoolean | TypeFactsTypeofNESymbol | TypeFactsTypeofNEFunction | TypeFactsNEUndefined | TypeFactsNENull | TypeFactsNEUndefinedOrNull | TypeFactsTruthy - TypeFactsObjectFacts TypeFacts = TypeFactsObjectStrictFacts | TypeFactsEQUndefined | TypeFactsEQNull | TypeFactsEQUndefinedOrNull | TypeFactsFalsy - TypeFactsFunctionStrictFacts TypeFacts = TypeFactsTypeofEQFunction | TypeFactsTypeofEQHostObject | TypeFactsTypeofNEString | TypeFactsTypeofNENumber | TypeFactsTypeofNEBigInt | TypeFactsTypeofNEBoolean | TypeFactsTypeofNESymbol | TypeFactsTypeofNEObject | TypeFactsNEUndefined | TypeFactsNENull | TypeFactsNEUndefinedOrNull | TypeFactsTruthy - TypeFactsFunctionFacts TypeFacts = TypeFactsFunctionStrictFacts | TypeFactsEQUndefined | TypeFactsEQNull | TypeFactsEQUndefinedOrNull | TypeFactsFalsy - TypeFactsVoidFacts TypeFacts = TypeFactsTypeofNEString | TypeFactsTypeofNENumber | TypeFactsTypeofNEBigInt | TypeFactsTypeofNEBoolean | TypeFactsTypeofNESymbol | TypeFactsTypeofNEObject | TypeFactsTypeofNEFunction | TypeFactsTypeofNEHostObject | TypeFactsEQUndefined | TypeFactsEQUndefinedOrNull | TypeFactsNENull | TypeFactsFalsy - TypeFactsUndefinedFacts TypeFacts = TypeFactsTypeofNEString | TypeFactsTypeofNENumber | TypeFactsTypeofNEBigInt | TypeFactsTypeofNEBoolean | TypeFactsTypeofNESymbol | TypeFactsTypeofNEObject | TypeFactsTypeofNEFunction | TypeFactsTypeofNEHostObject | TypeFactsEQUndefined | TypeFactsEQUndefinedOrNull | TypeFactsNENull | TypeFactsFalsy | TypeFactsIsUndefined - TypeFactsNullFacts TypeFacts = TypeFactsTypeofEQObject | TypeFactsTypeofNEString | TypeFactsTypeofNENumber | TypeFactsTypeofNEBigInt | TypeFactsTypeofNEBoolean | TypeFactsTypeofNESymbol | TypeFactsTypeofNEFunction | TypeFactsTypeofNEHostObject | TypeFactsEQNull | TypeFactsEQUndefinedOrNull | TypeFactsNEUndefined | TypeFactsFalsy | TypeFactsIsNull - TypeFactsEmptyObjectStrictFacts TypeFacts = TypeFactsAll & ^(TypeFactsEQUndefined | TypeFactsEQNull | TypeFactsEQUndefinedOrNull | TypeFactsIsUndefinedOrNull) - TypeFactsEmptyObjectFacts TypeFacts = TypeFactsAll & ^TypeFactsIsUndefinedOrNull - TypeFactsUnknownFacts TypeFacts = TypeFactsAll & ^TypeFactsIsUndefinedOrNull - TypeFactsAllTypeofNE TypeFacts = TypeFactsTypeofNEString | TypeFactsTypeofNENumber | TypeFactsTypeofNEBigInt | TypeFactsTypeofNEBoolean | TypeFactsTypeofNESymbol | TypeFactsTypeofNEObject | TypeFactsTypeofNEFunction | TypeFactsNEUndefined - // Masks - TypeFactsOrFactsMask TypeFacts = TypeFactsTypeofEQFunction | TypeFactsTypeofNEObject - TypeFactsAndFactsMask TypeFacts = TypeFactsAll & ^TypeFactsOrFactsMask -) - -type IterationUse uint32 - -const ( - IterationUseAllowsSyncIterablesFlag IterationUse = 1 << 0 - IterationUseAllowsAsyncIterablesFlag IterationUse = 1 << 1 - IterationUseAllowsStringInputFlag IterationUse = 1 << 2 - IterationUseForOfFlag IterationUse = 1 << 3 - IterationUseYieldStarFlag IterationUse = 1 << 4 - IterationUseSpreadFlag IterationUse = 1 << 5 - IterationUseDestructuringFlag IterationUse = 1 << 6 - IterationUsePossiblyOutOfBounds IterationUse = 1 << 7 - // Spread, Destructuring, Array element assignment - IterationUseElement = IterationUseAllowsSyncIterablesFlag - IterationUseSpread = IterationUseAllowsSyncIterablesFlag | IterationUseSpreadFlag - IterationUseDestructuring = IterationUseAllowsSyncIterablesFlag | IterationUseDestructuringFlag - IterationUseForOf = IterationUseAllowsSyncIterablesFlag | IterationUseAllowsStringInputFlag | IterationUseForOfFlag - IterationUseForAwaitOf = IterationUseAllowsSyncIterablesFlag | IterationUseAllowsAsyncIterablesFlag | IterationUseAllowsStringInputFlag | IterationUseForOfFlag - IterationUseYieldStar = IterationUseAllowsSyncIterablesFlag | IterationUseYieldStarFlag - IterationUseAsyncYieldStar = IterationUseAllowsSyncIterablesFlag | IterationUseAllowsAsyncIterablesFlag | IterationUseYieldStarFlag - IterationUseGeneratorReturnType = IterationUseAllowsSyncIterablesFlag - IterationUseAsyncGeneratorReturnType = IterationUseAllowsAsyncIterablesFlag - IterationUseCacheFlags = IterationUseAllowsSyncIterablesFlag | IterationUseAllowsAsyncIterablesFlag | IterationUseForOfFlag -) - -type IterationTypes struct { - yieldType *Type - returnType *Type - nextType *Type -} - -type IterationTypeKind int32 - -const ( - IterationTypeKindYield IterationTypeKind = iota - IterationTypeKindReturn - IterationTypeKindNext -) - -type IterationTypesResolver struct { - iteratorSymbolName string - getGlobalIteratorType func() *Type - getGlobalIterableType func() *Type - getGlobalIterableTypeChecked func() *Type - getGlobalIterableIteratorType func() *Type - getGlobalIterableIteratorTypeChecked func() *Type - getGlobalIteratorObjectType func() *Type - getGlobalGeneratorType func() *Type - getGlobalBuiltinIteratorTypes func() []*Type - resolveIterationType func(t *Type, errorNode *ast.Node) *Type - mustHaveANextMethodDiagnostic *diagnostics.Message - mustBeAMethodDiagnostic *diagnostics.Message - mustHaveAValueDiagnostic *diagnostics.Message -} - -type WideningContext struct { - parent *WideningContext // Parent context - propertyName string // Name of property in parent - siblings []*Type // Types of siblings - resolvedProperties []*ast.Symbol // Properties occurring in sibling object literals -} - -type Program interface { - Host - Options() *core.CompilerOptions - SourceFiles() []*ast.SourceFile - BindSourceFiles() - FileExists(fileName string) bool - GetSourceFile(fileName string) *ast.SourceFile - GetSourceFileForResolvedModule(fileName string) *ast.SourceFile - GetEmitModuleFormatOfFile(sourceFile ast.HasFileName) core.ModuleKind - GetEmitSyntaxForUsageLocation(sourceFile ast.HasFileName, usageLocation *ast.StringLiteralLike) core.ResolutionMode - GetImpliedNodeFormatForEmit(sourceFile ast.HasFileName) core.ModuleKind - GetResolvedModule(currentSourceFile ast.HasFileName, moduleReference string, mode core.ResolutionMode) *module.ResolvedModule - GetResolvedModules() map[tspath.Path]module.ModeAwareCache[*module.ResolvedModule] - GetSourceFileMetaData(path tspath.Path) ast.SourceFileMetaData - GetJSXRuntimeImportSpecifier(path tspath.Path) (moduleReference string, specifier *ast.Node) - GetImportHelpersImportSpecifier(path tspath.Path) *ast.Node - SourceFileMayBeEmitted(sourceFile *ast.SourceFile, forceDtsEmit bool) bool - IsSourceFromProjectReference(path tspath.Path) bool - IsSourceFileDefaultLibrary(path tspath.Path) bool - GetProjectReferenceFromOutputDts(path tspath.Path) *tsoptions.SourceOutputAndProjectReference - GetRedirectForResolution(file ast.HasFileName) *tsoptions.ParsedCommandLine - CommonSourceDirectory() string -} - -type Host interface { - modulespecifiers.ModuleSpecifierGenerationHost -} - -// Checker - -var nextCheckerID atomic.Uint32 - -type Checker struct { - id uint32 - program Program - compilerOptions *core.CompilerOptions - files []*ast.SourceFile - fileIndexMap map[*ast.SourceFile]int - compareSymbols func(*ast.Symbol, *ast.Symbol) int - compareSymbolChains func([]*ast.Symbol, []*ast.Symbol) int - TypeCount uint32 - SymbolCount uint32 - TotalInstantiationCount uint32 - instantiationCount uint32 - instantiationDepth uint32 - inlineLevel int - currentNode *ast.Node - varianceTypeParameter *Type - languageVersion core.ScriptTarget - moduleKind core.ModuleKind - moduleResolutionKind core.ModuleResolutionKind - isInferencePartiallyBlocked bool - legacyDecorators bool - emitStandardClassFields bool - allowSyntheticDefaultImports bool - strictNullChecks bool - strictFunctionTypes bool - strictBindCallApply bool - strictPropertyInitialization bool - strictBuiltinIteratorReturn bool - noImplicitAny bool - noImplicitThis bool - useUnknownInCatchVariables bool - exactOptionalPropertyTypes bool - canCollectSymbolAliasAccessibilityData bool - wasCanceled bool - arrayVariances []VarianceFlags - globals ast.SymbolTable - evaluate evaluator.Evaluator - stringLiteralTypes map[string]*Type - numberLiteralTypes map[jsnum.Number]*Type - bigintLiteralTypes map[jsnum.PseudoBigInt]*Type - enumLiteralTypes map[EnumLiteralKey]*Type - indexedAccessTypes map[string]*Type - templateLiteralTypes map[string]*Type - stringMappingTypes map[StringMappingKey]*Type - uniqueESSymbolTypes map[*ast.Symbol]*Type - thisExpandoKinds map[*ast.Symbol]thisAssignmentDeclarationKind - thisExpandoLocations map[*ast.Symbol]*ast.Node - subtypeReductionCache map[string][]*Type - cachedTypes map[CachedTypeKey]*Type - cachedSignatures map[CachedSignatureKey]*Signature - undefinedProperties map[string]*ast.Symbol - narrowedTypes map[NarrowedTypeKey]*Type - assignmentReducedTypes map[AssignmentReducedKey]*Type - discriminatedContextualTypes map[DiscriminatedContextualTypeKey]*Type - instantiationExpressionTypes map[InstantiationExpressionKey]*Type - substitutionTypes map[SubstitutionTypeKey]*Type - reverseMappedCache map[ReverseMappedTypeKey]*Type - reverseHomomorphicMappedCache map[ReverseMappedTypeKey]*Type - iterationTypesCache map[IterationTypesKey]IterationTypes - markerTypes collections.Set[*Type] - undefinedSymbol *ast.Symbol - argumentsSymbol *ast.Symbol - requireSymbol *ast.Symbol - unknownSymbol *ast.Symbol - resolvingSymbol *ast.Symbol - unresolvedSymbols map[string]*ast.Symbol - errorTypes map[string]*Type - globalThisSymbol *ast.Symbol - resolveName func(location *ast.Node, name string, meaning ast.SymbolFlags, nameNotFoundMessage *diagnostics.Message, isUse bool, excludeGlobals bool) *ast.Symbol - resolveNameForSymbolSuggestion func(location *ast.Node, name string, meaning ast.SymbolFlags, nameNotFoundMessage *diagnostics.Message, isUse bool, excludeGlobals bool) *ast.Symbol - tupleTypes map[string]*Type - unionTypes map[string]*Type - unionOfUnionTypes map[UnionOfUnionKey]*Type - intersectionTypes map[string]*Type - diagnostics ast.DiagnosticsCollection - suggestionDiagnostics ast.DiagnosticsCollection - symbolPool core.Pool[ast.Symbol] - signaturePool core.Pool[Signature] - indexInfoPool core.Pool[IndexInfo] - mergedSymbols map[*ast.Symbol]*ast.Symbol - factory ast.NodeFactory - nodeLinks core.LinkStore[*ast.Node, NodeLinks] - signatureLinks core.LinkStore[*ast.Node, SignatureLinks] - symbolNodeLinks core.LinkStore[*ast.Node, SymbolNodeLinks] - typeNodeLinks core.LinkStore[*ast.Node, TypeNodeLinks] - enumMemberLinks core.LinkStore[*ast.Node, EnumMemberLinks] - assertionLinks core.LinkStore[*ast.Node, AssertionLinks] - arrayLiteralLinks core.LinkStore[*ast.Node, ArrayLiteralLinks] - switchStatementLinks core.LinkStore[*ast.Node, SwitchStatementLinks] - jsxElementLinks core.LinkStore[*ast.Node, JsxElementLinks] - symbolReferenceLinks core.LinkStore[*ast.Symbol, SymbolReferenceLinks] - valueSymbolLinks core.LinkStore[*ast.Symbol, ValueSymbolLinks] - mappedSymbolLinks core.LinkStore[*ast.Symbol, MappedSymbolLinks] - deferredSymbolLinks core.LinkStore[*ast.Symbol, DeferredSymbolLinks] - aliasSymbolLinks core.LinkStore[*ast.Symbol, AliasSymbolLinks] - moduleSymbolLinks core.LinkStore[*ast.Symbol, ModuleSymbolLinks] - lateBoundLinks core.LinkStore[*ast.Symbol, LateBoundLinks] - exportTypeLinks core.LinkStore[*ast.Symbol, ExportTypeLinks] - membersAndExportsLinks core.LinkStore[*ast.Symbol, MembersAndExportsLinks] - typeAliasLinks core.LinkStore[*ast.Symbol, TypeAliasLinks] - declaredTypeLinks core.LinkStore[*ast.Symbol, DeclaredTypeLinks] - spreadLinks core.LinkStore[*ast.Symbol, SpreadLinks] - varianceLinks core.LinkStore[*ast.Symbol, VarianceLinks] - indexSymbolLinks core.LinkStore[*ast.Symbol, IndexSymbolLinks] - ReverseMappedSymbolLinks core.LinkStore[*ast.Symbol, ReverseMappedSymbolLinks] - markedAssignmentSymbolLinks core.LinkStore[*ast.Symbol, MarkedAssignmentSymbolLinks] - symbolContainerLinks core.LinkStore[*ast.Symbol, ContainingSymbolLinks] - sourceFileLinks core.LinkStore[*ast.SourceFile, SourceFileLinks] - patternForType map[*Type]*ast.Node - contextFreeTypes map[*ast.Node]*Type - anyType *Type - autoType *Type - wildcardType *Type - blockedStringType *Type - errorType *Type - unresolvedType *Type - nonInferrableAnyType *Type - intrinsicMarkerType *Type - unknownType *Type - undefinedType *Type - undefinedWideningType *Type - missingType *Type - undefinedOrMissingType *Type - optionalType *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 - silentNeverType *Type - implicitNeverType *Type - unreachableNeverType *Type - nonPrimitiveType *Type - stringOrNumberType *Type - stringNumberSymbolType *Type - numberOrBigIntType *Type - templateConstraintType *Type - numericStringType *Type - uniqueLiteralType *Type - uniqueLiteralMapper *TypeMapper - reliabilityFlags RelationComparisonResult - reportUnreliableMapper *TypeMapper - reportUnmeasurableMapper *TypeMapper - restrictiveMapper *TypeMapper - permissiveMapper *TypeMapper - emptyObjectType *Type - emptyJsxObjectType *Type - emptyFreshJsxObjectType *Type - emptyTypeLiteralType *Type - unknownEmptyObjectType *Type - unknownUnionType *Type - emptyGenericType *Type - anyFunctionType *Type - noConstraintType *Type - circularConstraintType *Type - resolvingDefaultType *Type - markerSuperType *Type - markerSubType *Type - markerOtherType *Type - markerSuperTypeForCheck *Type - markerSubTypeForCheck *Type - noTypePredicate *TypePredicate - anySignature *Signature - unknownSignature *Signature - resolvingSignature *Signature - silentNeverSignature *Signature - cachedArgumentsReferenced map[*ast.Node]bool - enumNumberIndexInfo *IndexInfo - anyBaseTypeIndexInfo *IndexInfo - patternAmbientModules []*ast.PatternAmbientModule - patternAmbientModuleAugmentations ast.SymbolTable - globalObjectType *Type - globalFunctionType *Type - globalCallableFunctionType *Type - globalNewableFunctionType *Type - globalArrayType *Type - globalReadonlyArrayType *Type - globalStringType *Type - globalNumberType *Type - globalBooleanType *Type - globalRegExpType *Type - globalThisType *Type - anyArrayType *Type - autoArrayType *Type - anyReadonlyArrayType *Type - deferredGlobalImportMetaExpressionType *Type - contextualBindingPatterns []*ast.Node - emptyStringType *Type - zeroType *Type - zeroBigIntType *Type - typeofType *Type - typeResolutions []TypeResolution - resolutionStart int - inVarianceComputation bool - apparentArgumentCount *int - lastGetCombinedNodeFlagsNode *ast.Node - lastGetCombinedNodeFlagsResult ast.NodeFlags - lastGetCombinedModifierFlagsNode *ast.Node - lastGetCombinedModifierFlagsResult ast.ModifierFlags - freeinferenceState *InferenceState - freeFlowState *FlowState - flowLoopCache map[FlowLoopKey]*Type - flowLoopStack []FlowLoopInfo - sharedFlows []SharedFlow - antecedentTypes []*Type - flowAnalysisDisabled bool - flowInvocationCount int - flowTypeCache map[*ast.Node]*Type - lastFlowNode *ast.FlowNode - lastFlowNodeReachable bool - flowNodeReachable map[*ast.FlowNode]bool - flowNodePostSuper map[*ast.FlowNode]bool - renamedBindingElementsInTypes []*ast.Node - contextualInfos []ContextualInfo - inferenceContextInfos []InferenceContextInfo - awaitedTypeStack []*Type - reverseMappedSourceStack []*Type - reverseMappedTargetStack []*Type - reverseExpandingFlags ExpandingFlags - freeRelater *Relater - subtypeRelation *Relation - strictSubtypeRelation *Relation - assignableRelation *Relation - comparableRelation *Relation - identityRelation *Relation - enumRelation map[EnumRelationKey]RelationComparisonResult - getGlobalESSymbolType func() *Type - getGlobalBigIntType func() *Type - getGlobalImportMetaType func() *Type - getGlobalImportAttributesType func() *Type - getGlobalImportAttributesTypeChecked func() *Type - getGlobalNonNullableTypeAliasOrNil func() *ast.Symbol - getGlobalExtractSymbol func() *ast.Symbol - getGlobalDisposableType func() *Type - getGlobalAsyncDisposableType func() *Type - getGlobalAwaitedSymbol func() *ast.Symbol - getGlobalAwaitedSymbolOrNil func() *ast.Symbol - getGlobalNaNSymbolOrNil func() *ast.Symbol - getGlobalRecordSymbol func() *ast.Symbol - getGlobalTemplateStringsArrayType func() *Type - getGlobalESSymbolConstructorSymbolOrNil func() *ast.Symbol - getGlobalESSymbolConstructorTypeSymbolOrNil func() *ast.Symbol - getGlobalImportCallOptionsType func() *Type - getGlobalImportCallOptionsTypeChecked func() *Type - getGlobalPromiseType func() *Type - getGlobalPromiseTypeChecked func() *Type - getGlobalPromiseLikeType func() *Type - getGlobalPromiseConstructorSymbol func() *ast.Symbol - getGlobalPromiseConstructorSymbolOrNil func() *ast.Symbol - getGlobalOmitSymbol func() *ast.Symbol - getGlobalNoInferSymbolOrNil func() *ast.Symbol - getGlobalIteratorType func() *Type - getGlobalIterableType func() *Type - getGlobalIterableTypeChecked func() *Type - getGlobalIterableIteratorType func() *Type - getGlobalIterableIteratorTypeChecked func() *Type - getGlobalIteratorObjectType func() *Type - getGlobalGeneratorType func() *Type - getGlobalAsyncIteratorType func() *Type - getGlobalAsyncIterableType func() *Type - getGlobalAsyncIterableTypeChecked func() *Type - getGlobalAsyncIterableIteratorType func() *Type - getGlobalAsyncIterableIteratorTypeChecked func() *Type - getGlobalAsyncIteratorObjectType func() *Type - getGlobalAsyncGeneratorType func() *Type - getGlobalIteratorYieldResultType func() *Type - getGlobalIteratorReturnResultType func() *Type - getGlobalTypedPropertyDescriptorType func() *Type - getGlobalClassDecoratorContextType func() *Type - getGlobalClassMethodDecoratorContextType func() *Type - getGlobalClassGetterDecoratorContextType func() *Type - getGlobalClassSetterDecoratorContextType func() *Type - getGlobalClassAccessorDecoratorContxtType func() *Type - getGlobalClassAccessorDecoratorContextType func() *Type - getGlobalClassAccessorDecoratorTargetType func() *Type - getGlobalClassAccessorDecoratorResultType func() *Type - getGlobalClassFieldDecoratorContextType func() *Type - syncIterationTypesResolver *IterationTypesResolver - asyncIterationTypesResolver *IterationTypesResolver - isPrimitiveOrObjectOrEmptyType func(*Type) bool - containsMissingType func(*Type) bool - couldContainTypeVariables func(*Type) bool - isStringIndexSignatureOnlyType func(*Type) bool - markNodeAssignments func(*ast.Node) bool - emitResolver *emitResolver - emitResolverOnce sync.Once - _jsxNamespace string - _jsxFactoryEntity *ast.Node - skipDirectInferenceNodes collections.Set[*ast.Node] - ctx context.Context - packagesMap map[string]bool - activeMappers []*TypeMapper - activeTypeMappersCaches []map[string]*Type - ambientModulesOnce sync.Once - ambientModules []*ast.Symbol -} - -func NewChecker(program Program) *Checker { - program.BindSourceFiles() - - c := &Checker{} - c.id = nextCheckerID.Add(1) - c.program = program - c.compilerOptions = program.Options() - c.files = program.SourceFiles() - c.fileIndexMap = createFileIndexMap(c.files) - c.compareSymbols = c.compareSymbolsWorker // Closure optimization - c.compareSymbolChains = c.compareSymbolChainsWorker // Closure optimization - c.languageVersion = c.compilerOptions.GetEmitScriptTarget() - c.moduleKind = c.compilerOptions.GetEmitModuleKind() - c.moduleResolutionKind = c.compilerOptions.GetModuleResolutionKind() - c.legacyDecorators = c.compilerOptions.ExperimentalDecorators == core.TSTrue - c.emitStandardClassFields = !c.compilerOptions.UseDefineForClassFields.IsFalse() && c.compilerOptions.GetEmitScriptTarget() >= core.ScriptTargetES2022 - c.allowSyntheticDefaultImports = c.compilerOptions.GetAllowSyntheticDefaultImports() - c.strictNullChecks = c.compilerOptions.GetStrictOptionValue(c.compilerOptions.StrictNullChecks) - c.strictFunctionTypes = c.compilerOptions.GetStrictOptionValue(c.compilerOptions.StrictFunctionTypes) - c.strictBindCallApply = c.compilerOptions.GetStrictOptionValue(c.compilerOptions.StrictBindCallApply) - c.strictPropertyInitialization = c.compilerOptions.GetStrictOptionValue(c.compilerOptions.StrictPropertyInitialization) - c.strictBuiltinIteratorReturn = c.compilerOptions.GetStrictOptionValue(c.compilerOptions.StrictBuiltinIteratorReturn) - c.noImplicitAny = c.compilerOptions.GetStrictOptionValue(c.compilerOptions.NoImplicitAny) - c.noImplicitThis = c.compilerOptions.GetStrictOptionValue(c.compilerOptions.NoImplicitThis) - c.useUnknownInCatchVariables = c.compilerOptions.GetStrictOptionValue(c.compilerOptions.UseUnknownInCatchVariables) - c.exactOptionalPropertyTypes = c.compilerOptions.ExactOptionalPropertyTypes == core.TSTrue - c.canCollectSymbolAliasAccessibilityData = c.compilerOptions.VerbatimModuleSyntax.IsFalseOrUnknown() - c.arrayVariances = []VarianceFlags{VarianceFlagsCovariant} - c.globals = make(ast.SymbolTable, countGlobalSymbols(c.files)) - c.evaluate = evaluator.NewEvaluator(c.evaluateEntity, ast.OEKParentheses) - c.stringLiteralTypes = make(map[string]*Type) - c.numberLiteralTypes = make(map[jsnum.Number]*Type) - c.bigintLiteralTypes = make(map[jsnum.PseudoBigInt]*Type) - c.enumLiteralTypes = make(map[EnumLiteralKey]*Type) - c.indexedAccessTypes = make(map[string]*Type) - c.templateLiteralTypes = make(map[string]*Type) - c.stringMappingTypes = make(map[StringMappingKey]*Type) - c.uniqueESSymbolTypes = make(map[*ast.Symbol]*Type) - c.thisExpandoKinds = make(map[*ast.Symbol]thisAssignmentDeclarationKind) - c.thisExpandoLocations = make(map[*ast.Symbol]*ast.Node) - c.subtypeReductionCache = make(map[string][]*Type) - c.cachedTypes = make(map[CachedTypeKey]*Type) - c.cachedSignatures = make(map[CachedSignatureKey]*Signature) - c.undefinedProperties = make(map[string]*ast.Symbol) - c.narrowedTypes = make(map[NarrowedTypeKey]*Type) - c.assignmentReducedTypes = make(map[AssignmentReducedKey]*Type) - c.discriminatedContextualTypes = make(map[DiscriminatedContextualTypeKey]*Type) - c.instantiationExpressionTypes = make(map[InstantiationExpressionKey]*Type) - c.substitutionTypes = make(map[SubstitutionTypeKey]*Type) - c.reverseMappedCache = make(map[ReverseMappedTypeKey]*Type) - c.reverseHomomorphicMappedCache = make(map[ReverseMappedTypeKey]*Type) - c.iterationTypesCache = make(map[IterationTypesKey]IterationTypes) - c.undefinedSymbol = c.newSymbol(ast.SymbolFlagsProperty, "undefined") - c.argumentsSymbol = c.newSymbol(ast.SymbolFlagsProperty, "arguments") - c.requireSymbol = c.newSymbol(ast.SymbolFlagsProperty, "require") - c.unknownSymbol = c.newSymbol(ast.SymbolFlagsProperty, "unknown") - c.resolvingSymbol = c.newSymbol(ast.SymbolFlagsNone, ast.InternalSymbolNameResolving) - c.unresolvedSymbols = make(map[string]*ast.Symbol) - c.errorTypes = make(map[string]*Type) - c.globalThisSymbol = c.newSymbolEx(ast.SymbolFlagsModule, "globalThis", ast.CheckFlagsReadonly) - c.globalThisSymbol.Exports = c.globals - c.globals[c.globalThisSymbol.Name] = c.globalThisSymbol - c.resolveName = c.createNameResolver().Resolve - c.resolveNameForSymbolSuggestion = c.createNameResolverForSuggestion().Resolve - c.tupleTypes = make(map[string]*Type) - c.unionTypes = make(map[string]*Type) - c.unionOfUnionTypes = make(map[UnionOfUnionKey]*Type) - c.intersectionTypes = make(map[string]*Type) - c.diagnostics = ast.DiagnosticsCollection{} - c.suggestionDiagnostics = ast.DiagnosticsCollection{} - c.mergedSymbols = make(map[*ast.Symbol]*ast.Symbol) - c.patternForType = make(map[*Type]*ast.Node) - c.contextFreeTypes = make(map[*ast.Node]*Type) - c.anyType = c.newIntrinsicType(TypeFlagsAny, "any") - c.autoType = c.newIntrinsicTypeEx(TypeFlagsAny, "any", ObjectFlagsNonInferrableType) - c.wildcardType = c.newIntrinsicType(TypeFlagsAny, "any") - c.blockedStringType = c.newIntrinsicType(TypeFlagsAny, "any") - c.errorType = c.newIntrinsicType(TypeFlagsAny, "any") - c.unresolvedType = c.newIntrinsicType(TypeFlagsAny, "unresolved") - 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 = core.IfElse(c.exactOptionalPropertyTypes, c.missingType, c.undefinedType) - c.optionalType = c.newIntrinsicType(TypeFlagsUndefined, "undefined") - 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.AsLiteralType().freshType = c.falseType - c.falseType.AsLiteralType().freshType = c.falseType - c.regularTrueType = c.newLiteralType(TypeFlagsBooleanLiteral, true, nil) - c.trueType = c.newLiteralType(TypeFlagsBooleanLiteral, true, c.regularTrueType) - c.regularTrueType.AsLiteralType().freshType = c.trueType - c.trueType.AsLiteralType().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.silentNeverType = c.newIntrinsicTypeEx(TypeFlagsNever, "never", ObjectFlagsNonInferrableType) - c.implicitNeverType = c.newIntrinsicType(TypeFlagsNever, "never") - c.unreachableNeverType = c.newIntrinsicType(TypeFlagsNever, "never") - c.nonPrimitiveType = c.newIntrinsicType(TypeFlagsNonPrimitive, "object") - c.stringOrNumberType = c.getUnionType([]*Type{c.stringType, c.numberType}) - c.stringNumberSymbolType = c.getUnionType([]*Type{c.stringType, c.numberType, c.esSymbolType}) - c.numberOrBigIntType = c.getUnionType([]*Type{c.numberType, c.bigintType}) - c.numericStringType = c.getTemplateLiteralType([]string{"", ""}, []*Type{c.numberType}) // The `${number}` type - c.templateConstraintType = c.getUnionType([]*Type{c.stringType, c.numberType, c.booleanType, c.bigintType, c.nullType, c.undefinedType}) - c.uniqueLiteralType = c.newIntrinsicType(TypeFlagsNever, "never") // Special `never` flagged by union reduction to behave as a literal - c.uniqueLiteralMapper = newFunctionTypeMapper(c.getUniqueLiteralTypeForTypeParameter) - c.reportUnreliableMapper = newFunctionTypeMapper(c.reportUnreliableWorker) - c.reportUnmeasurableMapper = newFunctionTypeMapper(c.reportUnmeasurableWorker) - c.restrictiveMapper = newFunctionTypeMapper(c.restrictiveMapperWorker) - c.permissiveMapper = newFunctionTypeMapper(c.permissiveMapperWorker) - c.emptyObjectType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil) - c.emptyJsxObjectType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil) - c.emptyFreshJsxObjectType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil) - c.emptyTypeLiteralType = c.newAnonymousType(c.newSymbol(ast.SymbolFlagsTypeLiteral, ast.InternalSymbolNameType), nil, nil, nil, nil) - c.unknownEmptyObjectType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil) - c.unknownUnionType = c.createUnknownUnionType() - c.emptyGenericType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil) - c.emptyGenericType.AsObjectType().instantiations = make(map[string]*Type) - c.anyFunctionType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil) - c.anyFunctionType.objectFlags |= ObjectFlagsNonInferrableType - c.noConstraintType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil) - c.circularConstraintType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil) - c.resolvingDefaultType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil) - c.markerSuperType = c.newTypeParameter(nil) - c.markerSubType = c.newTypeParameter(nil) - c.markerSubType.AsTypeParameter().constraint = c.markerSuperType - c.markerOtherType = c.newTypeParameter(nil) - c.markerSuperTypeForCheck = c.newTypeParameter(nil) - c.markerSubTypeForCheck = c.newTypeParameter(nil) - c.markerSubTypeForCheck.AsTypeParameter().constraint = c.markerSuperTypeForCheck - c.noTypePredicate = &TypePredicate{kind: TypePredicateKindIdentifier, parameterIndex: 0, parameterName: "<>", t: c.anyType} - c.anySignature = c.newSignature(SignatureFlagsNone, nil, nil, nil, nil, c.anyType, nil, 0) - c.unknownSignature = c.newSignature(SignatureFlagsNone, nil, nil, nil, nil, c.errorType, nil, 0) - c.resolvingSignature = c.newSignature(SignatureFlagsNone, nil, nil, nil, nil, c.anyType, nil, 0) - c.silentNeverSignature = c.newSignature(SignatureFlagsNone, nil, nil, nil, nil, c.silentNeverType, nil, 0) - c.cachedArgumentsReferenced = make(map[*ast.Node]bool) - c.enumNumberIndexInfo = &IndexInfo{keyType: c.numberType, valueType: c.stringType, isReadonly: true} - c.anyBaseTypeIndexInfo = &IndexInfo{keyType: c.stringType, valueType: c.anyType, isReadonly: false} - c.emptyStringType = c.getStringLiteralType("") - c.zeroType = c.getNumberLiteralType(0) - c.zeroBigIntType = c.getBigIntLiteralType(jsnum.PseudoBigInt{}) - c.typeofType = c.getUnionType(core.Map(slices.Sorted(maps.Keys(typeofNEFacts)), c.getStringLiteralType)) - c.flowLoopCache = make(map[FlowLoopKey]*Type) - c.flowNodeReachable = make(map[*ast.FlowNode]bool) - c.flowNodePostSuper = make(map[*ast.FlowNode]bool) - c.subtypeRelation = &Relation{} - c.strictSubtypeRelation = &Relation{} - c.assignableRelation = &Relation{} - c.comparableRelation = &Relation{} - c.identityRelation = &Relation{} - c.enumRelation = make(map[EnumRelationKey]RelationComparisonResult) - c.getGlobalESSymbolType = c.getGlobalTypeResolver("Symbol", 0 /*arity*/, false /*reportErrors*/) - c.getGlobalBigIntType = c.getGlobalTypeResolver("BigInt", 0 /*arity*/, false /*reportErrors*/) - c.getGlobalImportMetaType = c.getGlobalTypeResolver("ImportMeta", 0 /*arity*/, true /*reportErrors*/) - c.getGlobalImportAttributesType = c.getGlobalTypeResolver("ImportAttributes", 0 /*arity*/, false /*reportErrors*/) - c.getGlobalImportAttributesTypeChecked = c.getGlobalTypeResolver("ImportAttributes", 0 /*arity*/, true /*reportErrors*/) - c.getGlobalNonNullableTypeAliasOrNil = c.getGlobalTypeAliasResolver("NonNullable", 1 /*arity*/, false /*reportErrors*/) - c.getGlobalExtractSymbol = c.getGlobalTypeAliasResolver("Extract", 2 /*arity*/, true /*reportErrors*/) - c.getGlobalDisposableType = c.getGlobalTypeResolver("Disposable", 0 /*arity*/, true /*reportErrors*/) - c.getGlobalAsyncDisposableType = c.getGlobalTypeResolver("AsyncDisposable", 0 /*arity*/, true /*reportErrors*/) - c.getGlobalAwaitedSymbol = c.getGlobalTypeAliasResolver("Awaited", 1 /*arity*/, true /*reportErrors*/) - c.getGlobalAwaitedSymbolOrNil = c.getGlobalTypeAliasResolver("Awaited", 1 /*arity*/, false /*reportErrors*/) - c.getGlobalNaNSymbolOrNil = c.getGlobalValueSymbolResolver("NaN", false /*reportErrors*/) - c.getGlobalRecordSymbol = c.getGlobalTypeAliasResolver("Record", 2 /*arity*/, true /*reportErrors*/) - c.getGlobalTemplateStringsArrayType = c.getGlobalTypeResolver("TemplateStringsArray", 0 /*arity*/, true /*reportErrors*/) - c.getGlobalESSymbolConstructorSymbolOrNil = c.getGlobalValueSymbolResolver("Symbol", false /*reportErrors*/) - c.getGlobalESSymbolConstructorTypeSymbolOrNil = c.getGlobalTypeSymbolResolver("SymbolConstructor", false /*reportErrors*/) - c.getGlobalImportCallOptionsType = c.getGlobalTypeResolver("ImportCallOptions", 0 /*arity*/, false /*reportErrors*/) - c.getGlobalImportCallOptionsTypeChecked = c.getGlobalTypeResolver("ImportCallOptions", 0 /*arity*/, true /*reportErrors*/) - c.getGlobalPromiseType = c.getGlobalTypeResolver("Promise", 1 /*arity*/, false /*reportErrors*/) - c.getGlobalPromiseTypeChecked = c.getGlobalTypeResolver("Promise", 1 /*arity*/, true /*reportErrors*/) - c.getGlobalPromiseLikeType = c.getGlobalTypeResolver("PromiseLike", 1 /*arity*/, true /*reportErrors*/) - c.getGlobalPromiseConstructorSymbol = c.getGlobalValueSymbolResolver("Promise", true /*reportErrors*/) - c.getGlobalPromiseConstructorSymbolOrNil = c.getGlobalValueSymbolResolver("Promise", false /*reportErrors*/) - c.getGlobalOmitSymbol = c.getGlobalTypeAliasResolver("Omit", 2 /*arity*/, true /*reportErrors*/) - c.getGlobalNoInferSymbolOrNil = c.getGlobalTypeAliasResolver("NoInfer", 1 /*arity*/, false /*reportErrors*/) - c.getGlobalIteratorType = c.getGlobalTypeResolver("Iterator", 3 /*arity*/, false /*reportErrors*/) - c.getGlobalIterableType = c.getGlobalTypeResolver("Iterable", 3 /*arity*/, false /*reportErrors*/) - c.getGlobalIterableTypeChecked = c.getGlobalTypeResolver("Iterable", 3 /*arity*/, true /*reportErrors*/) - c.getGlobalIterableIteratorType = c.getGlobalTypeResolver("IterableIterator", 3 /*arity*/, false /*reportErrors*/) - c.getGlobalIterableIteratorTypeChecked = c.getGlobalTypeResolver("IterableIterator", 3 /*arity*/, true /*reportErrors*/) - c.getGlobalIteratorObjectType = c.getGlobalTypeResolver("IteratorObject", 3 /*arity*/, false /*reportErrors*/) - c.getGlobalGeneratorType = c.getGlobalTypeResolver("Generator", 3 /*arity*/, false /*reportErrors*/) - c.getGlobalAsyncIteratorType = c.getGlobalTypeResolver("AsyncIterator", 3 /*arity*/, false /*reportErrors*/) - c.getGlobalAsyncIterableType = c.getGlobalTypeResolver("AsyncIterable", 3 /*arity*/, false /*reportErrors*/) - c.getGlobalAsyncIterableTypeChecked = c.getGlobalTypeResolver("AsyncIterable", 3 /*arity*/, true /*reportErrors*/) - c.getGlobalAsyncIterableIteratorType = c.getGlobalTypeResolver("AsyncIterableIterator", 3 /*arity*/, false /*reportErrors*/) - c.getGlobalAsyncIterableIteratorTypeChecked = c.getGlobalTypeResolver("AsyncIterableIterator", 3 /*arity*/, true /*reportErrors*/) - c.getGlobalAsyncIteratorObjectType = c.getGlobalTypeResolver("AsyncIteratorObject", 3 /*arity*/, false /*reportErrors*/) - c.getGlobalAsyncGeneratorType = c.getGlobalTypeResolver("AsyncGenerator", 3 /*arity*/, false /*reportErrors*/) - c.getGlobalIteratorYieldResultType = c.getGlobalTypeResolver("IteratorYieldResult", 1 /*arity*/, false /*reportErrors*/) - c.getGlobalIteratorReturnResultType = c.getGlobalTypeResolver("IteratorReturnResult", 1 /*arity*/, false /*reportErrors*/) - c.getGlobalTypedPropertyDescriptorType = c.getGlobalTypeResolver("TypedPropertyDescriptor", 1 /*arity*/, true /*reportErrors*/) - c.getGlobalClassDecoratorContextType = c.getGlobalTypeResolver("ClassDecoratorContext", 1 /*arity*/, true /*reportErrors*/) - c.getGlobalClassMethodDecoratorContextType = c.getGlobalTypeResolver("ClassMethodDecoratorContext", 2 /*arity*/, true /*reportErrors*/) - c.getGlobalClassGetterDecoratorContextType = c.getGlobalTypeResolver("ClassGetterDecoratorContext", 2 /*arity*/, true /*reportErrors*/) - c.getGlobalClassSetterDecoratorContextType = c.getGlobalTypeResolver("ClassSetterDecoratorContext", 2 /*arity*/, true /*reportErrors*/) - c.getGlobalClassAccessorDecoratorContextType = c.getGlobalTypeResolver("ClassAccessorDecoratorContext", 2 /*arity*/, true /*reportErrors*/) - c.getGlobalClassAccessorDecoratorTargetType = c.getGlobalTypeResolver("ClassAccessorDecoratorTarget", 2 /*arity*/, true /*reportErrors*/) - c.getGlobalClassAccessorDecoratorResultType = c.getGlobalTypeResolver("ClassAccessorDecoratorResult", 2 /*arity*/, true /*reportErrors*/) - c.getGlobalClassFieldDecoratorContextType = c.getGlobalTypeResolver("ClassFieldDecoratorContext", 2 /*arity*/, true /*reportErrors*/) - c.initializeClosures() - c.initializeIterationResolvers() - c.initializeChecker() - return c -} - -func createFileIndexMap(files []*ast.SourceFile) map[*ast.SourceFile]int { - result := make(map[*ast.SourceFile]int, len(files)) - for i, file := range files { - result[file] = i - } - return result -} - -func countGlobalSymbols(files []*ast.SourceFile) int { - count := 0 - for _, file := range files { - if !ast.IsExternalOrCommonJSModule(file) { - count += len(file.Locals) - } - } - return count -} - -func (c *Checker) reportUnreliableWorker(t *Type) *Type { - if t == c.markerSuperType || t == c.markerSubType || t == c.markerOtherType { - c.reliabilityFlags |= RelationComparisonResultReportsUnreliable - } - return t -} - -func (c *Checker) reportUnmeasurableWorker(t *Type) *Type { - if t == c.markerSuperType || t == c.markerSubType || t == c.markerOtherType { - c.reliabilityFlags |= RelationComparisonResultReportsUnmeasurable - } - return t -} - -// Resolve to the global class or interface by the given name and arity, or emptyObjectType/emptyGenericType otherwise -func (c *Checker) getGlobalTypeResolver(name string, arity int, reportErrors bool) func() *Type { - return core.Memoize(func() *Type { - return c.getGlobalType(name, arity, reportErrors) - }) -} - -// Resolve to the global type alias symbol by the given name and arity, or nil otherwise -func (c *Checker) getGlobalTypeAliasResolver(name string, arity int, reportErrors bool) func() *ast.Symbol { - return core.Memoize(func() *ast.Symbol { - return c.getGlobalTypeAliasSymbol(name, arity, reportErrors) - }) -} - -// Resolve to the global value symbol by the given name, or nil otherwise -func (c *Checker) getGlobalValueSymbolResolver(name string, reportErrors bool) func() *ast.Symbol { - return core.Memoize(func() *ast.Symbol { - return c.getGlobalSymbol(name, ast.SymbolFlagsValue, core.IfElse(reportErrors, diagnostics.Cannot_find_global_value_0, nil)) - }) -} - -func (c *Checker) getGlobalTypeSymbolResolver(name string, reportErrors bool) func() *ast.Symbol { - return core.Memoize(func() *ast.Symbol { - return c.getGlobalSymbol(name, ast.SymbolFlagsType, core.IfElse(reportErrors, diagnostics.Cannot_find_global_type_0, nil)) - }) -} - -func (c *Checker) getGlobalTypesResolver(names []string, arity int, reportErrors bool) func() []*Type { - return core.Memoize(func() []*Type { - return core.Map(names, func(name string) *Type { - return c.getGlobalType(name, arity, reportErrors) - }) - }) -} - -func (c *Checker) getGlobalTypeAliasSymbol(name string, arity int, reportErrors bool) *ast.Symbol { - symbol := c.getGlobalSymbol(name, ast.SymbolFlagsTypeAlias, core.IfElse(reportErrors, diagnostics.Cannot_find_global_type_0, nil)) - if symbol == nil { - return nil - } - // Resolve the declared type of the symbol. This resolves type parameters for the type alias so that we can check arity. - c.getDeclaredTypeOfSymbol(symbol) - if len(c.typeAliasLinks.Get(symbol).typeParameters) != arity { - if reportErrors { - decl := core.Find(symbol.Declarations, ast.IsTypeAliasDeclaration) - c.error(decl, diagnostics.Global_type_0_must_have_1_type_parameter_s, ast.SymbolName(symbol), arity) - } - return nil - } - return symbol -} - -func (c *Checker) GetTypeAliasTypeParameters(symbol *ast.Symbol) []*Type { - if symbol.Flags&ast.SymbolFlagsTypeAlias == 0 { - panic("Attempted to fetch type alias parameters for non-type-alias symbol") - } - c.getDeclaredTypeOfSymbol(symbol) - return c.typeAliasLinks.Get(symbol).typeParameters -} - -func (c *Checker) getGlobalType(name string, arity int, reportErrors bool) *Type { - symbol := c.getGlobalSymbol(name, ast.SymbolFlagsType, core.IfElse(reportErrors, diagnostics.Cannot_find_global_type_0, nil)) - if symbol != nil { - if symbol.Flags&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface) != 0 { - t := c.getDeclaredTypeOfSymbol(symbol) - if len(t.AsInterfaceType().TypeParameters()) == arity { - return t - } - if reportErrors { - c.error(getGlobalTypeDeclaration(symbol), diagnostics.Global_type_0_must_have_1_type_parameter_s, ast.SymbolName(symbol), arity) - } - } else if reportErrors { - c.error(getGlobalTypeDeclaration(symbol), diagnostics.Global_type_0_must_be_a_class_or_interface_type, ast.SymbolName(symbol)) - } - } - if arity != 0 { - return c.emptyGenericType - } - return c.emptyObjectType -} - -func getGlobalTypeDeclaration(symbol *ast.Symbol) *ast.Declaration { - for _, declaration := range symbol.Declarations { - switch declaration.Kind { - case ast.KindClassDeclaration, ast.KindInterfaceDeclaration, ast.KindEnumDeclaration, ast.KindTypeAliasDeclaration: - return declaration - } - } - return nil -} - -func (c *Checker) getGlobalSymbol(name string, meaning ast.SymbolFlags, diagnostic *diagnostics.Message) *ast.Symbol { - // Don't track references for global symbols anyway, so value if `isReference` is arbitrary - return c.resolveName(nil, name, meaning, diagnostic, false /*isUse*/, false /*excludeGlobals*/) -} - -func (c *Checker) initializeClosures() { - c.isPrimitiveOrObjectOrEmptyType = func(t *Type) bool { - return t.flags&(TypeFlagsPrimitive|TypeFlagsNonPrimitive) != 0 || c.IsEmptyAnonymousObjectType(t) - } - c.containsMissingType = func(t *Type) bool { - return t == c.missingType || t.flags&TypeFlagsUnion != 0 && t.Types()[0] == c.missingType - } - c.couldContainTypeVariables = c.couldContainTypeVariablesWorker - c.isStringIndexSignatureOnlyType = c.isStringIndexSignatureOnlyTypeWorker - c.markNodeAssignments = c.markNodeAssignmentsWorker -} - -func (c *Checker) initializeIterationResolvers() { - c.syncIterationTypesResolver = &IterationTypesResolver{ - iteratorSymbolName: "iterator", - getGlobalIteratorType: c.getGlobalIteratorType, - getGlobalIterableType: c.getGlobalIterableType, - getGlobalIterableTypeChecked: c.getGlobalIterableTypeChecked, - getGlobalIterableIteratorType: c.getGlobalIterableIteratorType, - getGlobalIterableIteratorTypeChecked: c.getGlobalIterableIteratorTypeChecked, - getGlobalIteratorObjectType: c.getGlobalIteratorObjectType, - getGlobalGeneratorType: c.getGlobalGeneratorType, - getGlobalBuiltinIteratorTypes: c.getGlobalTypesResolver([]string{"ArrayIterator", "MapIterator", "SetIterator", "StringIterator"}, 1, false /*reportErrors*/), - resolveIterationType: func(t *Type, errorNode *ast.Node) *Type { - return t - }, - mustHaveANextMethodDiagnostic: diagnostics.An_iterator_must_have_a_next_method, - mustBeAMethodDiagnostic: diagnostics.The_0_property_of_an_iterator_must_be_a_method, - mustHaveAValueDiagnostic: diagnostics.The_type_returned_by_the_0_method_of_an_iterator_must_have_a_value_property, - } - c.asyncIterationTypesResolver = &IterationTypesResolver{ - iteratorSymbolName: "asyncIterator", - getGlobalIteratorType: c.getGlobalAsyncIteratorType, - getGlobalIterableType: c.getGlobalAsyncIterableType, - getGlobalIterableTypeChecked: c.getGlobalAsyncIterableTypeChecked, - getGlobalIterableIteratorType: c.getGlobalAsyncIterableIteratorType, - getGlobalIterableIteratorTypeChecked: c.getGlobalAsyncIterableIteratorTypeChecked, - getGlobalIteratorObjectType: c.getGlobalAsyncIteratorObjectType, - getGlobalGeneratorType: c.getGlobalAsyncGeneratorType, - getGlobalBuiltinIteratorTypes: c.getGlobalTypesResolver([]string{"ReadableStreamAsyncIterator"}, 1, false /*reportErrors*/), - resolveIterationType: func(t *Type, errorNode *ast.Node) *Type { - return c.getAwaitedTypeEx(t, errorNode, diagnostics.Type_of_await_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member) - }, - mustHaveANextMethodDiagnostic: diagnostics.An_async_iterator_must_have_a_next_method, - mustBeAMethodDiagnostic: diagnostics.The_0_property_of_an_async_iterator_must_be_a_method, - mustHaveAValueDiagnostic: diagnostics.The_type_returned_by_the_0_method_of_an_async_iterator_must_be_a_promise_for_a_type_with_a_value_property, - } -} - -func (c *Checker) initializeChecker() { - // Initialize global symbol table - augmentations := make([][]*ast.Node, 0, len(c.files)) - for _, file := range c.files { - if !ast.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 ast.IsGlobalScopeAugmentation(augmentation.Parent) { - c.mergeModuleAugmentation(augmentation) - } - } - } - c.addUndefinedToGlobalsOrErrorOnRedeclaration() - c.valueSymbolLinks.Get(c.undefinedSymbol).resolvedType = c.undefinedWideningType - c.valueSymbolLinks.Get(c.argumentsSymbol).resolvedType = c.getGlobalType("IArguments", 0 /*arity*/, true /*reportErrors*/) - c.valueSymbolLinks.Get(c.unknownSymbol).resolvedType = c.errorType - c.valueSymbolLinks.Get(c.globalThisSymbol).resolvedType = c.newObjectType(ObjectFlagsAnonymous, c.globalThisSymbol) - // Initialize special types - c.globalArrayType = c.getGlobalType("Array", 1 /*arity*/, true /*reportErrors*/) - c.globalObjectType = c.getGlobalType("Object", 0 /*arity*/, true /*reportErrors*/) - c.globalFunctionType = c.getGlobalType("Function", 0 /*arity*/, true /*reportErrors*/) - c.globalCallableFunctionType = c.getGlobalStrictFunctionType("CallableFunction") - c.globalNewableFunctionType = c.getGlobalStrictFunctionType("NewableFunction") - c.globalStringType = c.getGlobalType("String", 0 /*arity*/, true /*reportErrors*/) - c.globalNumberType = c.getGlobalType("Number", 0 /*arity*/, true /*reportErrors*/) - c.globalBooleanType = c.getGlobalType("Boolean", 0 /*arity*/, true /*reportErrors*/) - c.globalRegExpType = c.getGlobalType("RegExp", 0 /*arity*/, true /*reportErrors*/) - c.anyArrayType = c.createArrayType(c.anyType) - c.autoArrayType = c.createArrayType(c.autoType) - if c.autoArrayType == c.emptyObjectType { - // autoArrayType is used as a marker, so even if global Array type is not defined, it needs to be a unique type - c.autoArrayType = c.newAnonymousType(nil, nil, nil, nil, nil) - } - c.globalReadonlyArrayType = c.getGlobalType("ReadonlyArray", 1 /*arity*/, false /*reportErrors*/) - if c.globalReadonlyArrayType == c.emptyGenericType { - c.globalReadonlyArrayType = c.globalArrayType - } - c.anyReadonlyArrayType = c.createTypeFromGenericGlobalType(c.globalReadonlyArrayType, []*Type{c.anyType}) - c.globalThisType = c.getGlobalType("ThisType", 1 /*arity*/, false /*reportErrors*/) - // 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 !ast.IsGlobalScopeAugmentation(augmentation.Parent) { - c.mergeModuleAugmentation(augmentation) - } - } - } -} - -func (c *Checker) mergeModuleAugmentation(moduleName *ast.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 ast.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&ast.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&ast.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 core.Some(c.patternAmbientModules, func(module *ast.PatternAmbientModule) bool { - return mainModule == module.Symbol - }) { - merged := c.mergeSymbol(moduleAugmentation.Symbol, mainModule, true /*unidirectional*/) - // moduleName will be a StringLiteral since this is not `declare global`. - ast.GetSymbolTable(&c.patternAmbientModuleAugmentations)[moduleName.Text()] = merged - } else { - if mainModule.Exports[ast.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, moduleName.Text()) - } - } -} - -func (c *Checker) addUndefinedToGlobalsOrErrorOnRedeclaration() { - name := c.undefinedSymbol.Name - targetSymbol := c.globals[name] - if targetSymbol != nil { - for _, declaration := range targetSymbol.Declarations { - if !ast.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() *binder.NameResolver { - return &binder.NameResolver{ - CompilerOptions: c.compilerOptions, - GetSymbolOfDeclaration: c.getSymbolOfDeclaration, - Error: c.error, - Globals: c.globals, - ArgumentsSymbol: c.argumentsSymbol, - RequireSymbol: c.requireSymbol, - GetModuleSymbol: c.getModuleSymbol, - Lookup: c.getSymbol, - SymbolReferenced: c.symbolReferenced, - SetRequiresScopeChangeCache: c.setRequiresScopeChangeCache, - GetRequiresScopeChangeCache: c.getRequiresScopeChangeCache, - OnPropertyWithInvalidInitializer: c.checkAndReportErrorForInvalidInitializer, - OnFailedToResolveSymbol: c.onFailedToResolveSymbol, - OnSuccessfullyResolvedSymbol: c.onSuccessfullyResolvedSymbol, - } -} - -func (c *Checker) createNameResolverForSuggestion() *binder.NameResolver { - return &binder.NameResolver{ - CompilerOptions: c.compilerOptions, - GetSymbolOfDeclaration: c.getSymbolOfDeclaration, - Error: c.error, - Globals: c.globals, - ArgumentsSymbol: c.argumentsSymbol, - RequireSymbol: c.requireSymbol, - GetModuleSymbol: c.getModuleSymbol, - Lookup: c.getSuggestionForSymbolNameLookup, - SymbolReferenced: c.symbolReferenced, - SetRequiresScopeChangeCache: c.setRequiresScopeChangeCache, - GetRequiresScopeChangeCache: c.getRequiresScopeChangeCache, - } -} - -func (c *Checker) getModuleSymbol(sourceFile *ast.Node) *ast.Symbol { - result := c.newSymbol(ast.SymbolFlagsModuleExports|ast.SymbolFlagsFunctionScopedVariable, ast.InternalSymbolNameModuleExports) - result.ValueDeclaration = sourceFile - return result -} - -func (c *Checker) symbolReferenced(symbol *ast.Symbol, meaning ast.SymbolFlags) { - c.symbolReferenceLinks.Get(symbol).referenceKinds |= meaning -} - -func (c *Checker) getRequiresScopeChangeCache(node *ast.Node) core.Tristate { - return c.nodeLinks.Get(node).declarationRequiresScopeChange -} - -func (c *Checker) setRequiresScopeChangeCache(node *ast.Node, value core.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 *ast.Node, name string, propertyWithInvalidInitializer *ast.Node, result *ast.Symbol) bool { - if !c.compilerOptions.GetEmitStandardClassFields() { - 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 := core.IfElse(errorLocation != nil && prop.Type != nil && prop.Type.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, scanner.DeclarationNameToString(prop.Name()), name) - return true - } - return false -} - -func (c *Checker) checkAndReportErrorForMissingPrefix(errorLocation *ast.Node, name string) bool { - if !ast.IsIdentifier(errorLocation) || errorLocation.Text() != name || isTypeReferenceIdentifier(errorLocation) || IsInTypeQuery(errorLocation) { - return false - } - container := c.getThisContainer(errorLocation, false /*includeArrowFunctions*/, false /*includeClassComputedPropertyName*/) - for location := container; location.Parent != nil; location = location.Parent { - if ast.IsClassLike(location.Parent) { - classSymbol := c.getSymbolOfDeclaration(location.Parent) - if classSymbol == nil { - break - } - // Check to see if a static member exists. - constructorType := c.getTypeOfSymbol(classSymbol) - if c.getPropertyOfType(constructorType, name) != nil { - c.error(errorLocation, diagnostics.Cannot_find_name_0_Did_you_mean_the_static_member_1_0, name, c.symbolToString(classSymbol)) - return true - } - // No static member is present. - // Check if we're in an instance method and look for a relevant instance member. - if location == container && !ast.IsStatic(location) { - instanceType := c.getDeclaredTypeOfSymbol(classSymbol).AsInterfaceType().thisType - // TODO: GH#18217 - if c.getPropertyOfType(instanceType, name) != nil { - c.error(errorLocation, diagnostics.Cannot_find_name_0_Did_you_mean_the_instance_member_this_0, name) - return true - } - } - } - } - return false -} - -func (c *Checker) onFailedToResolveSymbol(errorLocation *ast.Node, name string, meaning ast.SymbolFlags, nameNotFoundMessage *diagnostics.Message) { - if errorLocation != nil && (errorLocation.Parent.Kind == ast.KindJSDocLink || - c.checkAndReportErrorForMissingPrefix(errorLocation, name) || - c.checkAndReportErrorForExtendingInterface(errorLocation) || - c.checkAndReportErrorForUsingTypeAsNamespace(errorLocation, name, meaning) || - c.checkAndReportErrorForExportingPrimitiveType(errorLocation, name) || - c.checkAndReportErrorForUsingNamespaceAsTypeOrValue(errorLocation, name, meaning) || - c.checkAndReportErrorForUsingTypeAsValue(errorLocation, name, meaning) || - c.checkAndReportErrorForUsingValueAsType(errorLocation, name, meaning)) { - return - } - // Report missing lib first - suggestedLib := c.getSuggestedLibForNonExistentName(name) - if suggestedLib != "" { - c.error(errorLocation, nameNotFoundMessage, name, suggestedLib) - return - } - // Then spelling suggestions - suggestion := c.getSuggestedSymbolForNonexistentSymbol(errorLocation, name, meaning) - if suggestion != nil && !(suggestion.ValueDeclaration != nil && ast.IsAmbientModule(suggestion.ValueDeclaration) && ast.IsGlobalScopeAugmentation(suggestion.ValueDeclaration)) { - suggestionName := c.symbolToString(suggestion) - message := core.IfElse(meaning == ast.SymbolFlagsNamespace, diagnostics.Cannot_find_namespace_0_Did_you_mean_1, diagnostics.Cannot_find_name_0_Did_you_mean_1) - diagnostic := NewDiagnosticForNode(errorLocation, message, name, suggestionName) - if suggestion.ValueDeclaration != nil { - diagnostic.AddRelatedInfo(NewDiagnosticForNode(suggestion.ValueDeclaration, diagnostics.X_0_is_declared_here, suggestionName)) - } - c.diagnostics.Add(diagnostic) - return - } - // And then fall back to unspecified "not found" - c.error(errorLocation, nameNotFoundMessage, name) -} - -func (c *Checker) checkAndReportErrorForUsingTypeAsNamespace(errorLocation *ast.Node, name string, meaning ast.SymbolFlags) bool { - if meaning == ast.SymbolFlagsNamespace { - symbol := c.resolveSymbol(c.resolveName(errorLocation, name, ast.SymbolFlagsType&^ast.SymbolFlagsNamespace, nil /*nameNotFoundMessage*/, false /*isUse*/, false /*excludeGlobals*/)) - if symbol != nil { - parent := errorLocation.Parent - if ast.IsQualifiedName(parent) { - debug.Assert(parent.AsQualifiedName().Left == errorLocation, "Should only be resolving left side of qualified name as a namespace") - propName := parent.AsQualifiedName().Right.Text() - propType := c.getPropertyOfType(c.getDeclaredTypeOfSymbol(symbol), propName) - if propType != nil { - c.error(parent, 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, name, propName) - return true - } - } - c.error(errorLocation, diagnostics.X_0_only_refers_to_a_type_but_is_being_used_as_a_namespace_here, name) - return true - } - } - return false -} - -func (c *Checker) checkAndReportErrorForExportingPrimitiveType(errorLocation *ast.Node, name string) bool { - if isPrimitiveTypeName(name) && errorLocation.Parent.Kind == ast.KindExportSpecifier { - c.error(errorLocation, diagnostics.Cannot_export_0_Only_local_declarations_can_be_exported_from_a_module, name) - return true - } - return false -} - -func isPrimitiveTypeName(s string) bool { - return s == "any" || s == "string" || s == "number" || s == "boolean" || s == "never" || s == "unknown" -} - -func (c *Checker) checkAndReportErrorForUsingNamespaceAsTypeOrValue(errorLocation *ast.Node, name string, meaning ast.SymbolFlags) bool { - if meaning&(ast.SymbolFlagsValue&^ast.SymbolFlagsType) != 0 { - symbol := c.resolveSymbol(c.resolveName(errorLocation, name, ast.SymbolFlagsNamespaceModule, nil /*nameNotFoundMessage*/, false /*isUse*/, false /*excludeGlobals*/)) - if symbol != nil { - c.error(errorLocation, diagnostics.Cannot_use_namespace_0_as_a_value, name) - return true - } - } else if meaning&(ast.SymbolFlagsType&^ast.SymbolFlagsValue) != 0 { - symbol := c.resolveSymbol(c.resolveName(errorLocation, name, ast.SymbolFlagsModule, nil /*nameNotFoundMessage*/, false /*isUse*/, false /*excludeGlobals*/)) - if symbol != nil { - c.error(errorLocation, diagnostics.Cannot_use_namespace_0_as_a_type, name) - return true - } - } - return false -} - -func (c *Checker) checkAndReportErrorForUsingTypeAsValue(errorLocation *ast.Node, name string, meaning ast.SymbolFlags) bool { - if meaning&ast.SymbolFlagsValue != 0 { - if isPrimitiveTypeName(name) { - grandparent := errorLocation.Parent.Parent - if grandparent != nil && grandparent.Parent != nil && ast.IsHeritageClause(grandparent) { - heritageKind := grandparent.AsHeritageClause().Token - containerKind := grandparent.Parent.Kind - if containerKind == ast.KindInterfaceDeclaration && heritageKind == ast.KindExtendsKeyword { - c.error(errorLocation, diagnostics.An_interface_cannot_extend_a_primitive_type_like_0_It_can_only_extend_other_named_object_types, name) - } else if ast.IsClassLike(grandparent.Parent) && heritageKind == ast.KindExtendsKeyword { - c.error(errorLocation, diagnostics.A_class_cannot_extend_a_primitive_type_like_0_Classes_can_only_extend_constructable_values, name) - } else if ast.IsClassLike(grandparent.Parent) && heritageKind == ast.KindImplementsKeyword { - c.error(errorLocation, diagnostics.A_class_cannot_implement_a_primitive_type_like_0_It_can_only_implement_other_named_object_types, name) - } - } else { - c.error(errorLocation, diagnostics.X_0_only_refers_to_a_type_but_is_being_used_as_a_value_here, name) - } - return true - } - symbol := c.resolveSymbol(c.resolveName(errorLocation, name, ast.SymbolFlagsType & ^ast.SymbolFlagsValue, nil /*nameNotFoundMessage*/, false /*isUse*/, false /*excludeGlobals*/)) - if symbol != nil { - allFlags := c.getSymbolFlags(symbol) - if allFlags&ast.SymbolFlagsValue == 0 { - if isES2015OrLaterConstructorName(name) { - c.error(errorLocation, diagnostics.X_0_only_refers_to_a_type_but_is_being_used_as_a_value_here_Do_you_need_to_change_your_target_library_Try_changing_the_lib_compiler_option_to_es2015_or_later, name) - } else if c.maybeMappedType(errorLocation, symbol) { - c.error(errorLocation, diagnostics.X_0_only_refers_to_a_type_but_is_being_used_as_a_value_here_Did_you_mean_to_use_1_in_0, name, core.IfElse(name == "K", "P", "K")) - } else { - c.error(errorLocation, diagnostics.X_0_only_refers_to_a_type_but_is_being_used_as_a_value_here, name) - } - return true - } - } - } - return false -} - -func isES2015OrLaterConstructorName(s string) bool { - return s == "Promise" || s == "Symbol" || s == "Map" || s == "WeakMap" || s == "Set" || s == "WeakSet" -} - -func (c *Checker) maybeMappedType(node *ast.Node, symbol *ast.Symbol) bool { - for ast.IsComputedPropertyName(node) || ast.IsPropertySignatureDeclaration(node) { - node = node.Parent - } - if ast.IsTypeLiteralNode(node) && len(node.AsTypeLiteralNode().Members.Nodes) == 1 { - t := c.getDeclaredTypeOfSymbol(symbol) - return t.flags&TypeFlagsUnion != 0 && c.allTypesAssignableToKind(t, TypeFlagsStringOrNumberLiteral) - } - return false -} - -func (c *Checker) checkAndReportErrorForUsingValueAsType(errorLocation *ast.Node, name string, meaning ast.SymbolFlags) bool { - if meaning&(ast.SymbolFlagsType & ^ast.SymbolFlagsNamespace) != 0 { - symbol := c.resolveSymbol(c.resolveName(errorLocation, name, ^ast.SymbolFlagsType&ast.SymbolFlagsValue, nil /*nameNotFoundMessage*/, false /*isUse*/, false /*excludeGlobals*/)) - if symbol != nil && symbol.Flags&ast.SymbolFlagsNamespace == 0 { - c.error(errorLocation, diagnostics.X_0_refers_to_a_value_but_is_being_used_as_a_type_here_Did_you_mean_typeof_0, name) - return true - } - } - return false -} - -func (c *Checker) getSuggestedLibForNonExistentName(name string) string { - featureMap := getFeatureMap() - if typeFeatures, ok := featureMap[name]; ok { - return typeFeatures[0].lib - } - return "" -} - -func (c *Checker) getPrimitiveAliasSymbols() { - var symbols []*ast.Symbol - for _, name := range []string{"string", "number", "boolean", "object", "bigint", "symbol"} { - symbols = append(symbols, c.newSymbol(ast.SymbolFlagsTypeAlias, name)) - } -} - -func (c *Checker) getSuggestedSymbolForNonexistentSymbol(location *ast.Node, outerName string, meaning ast.SymbolFlags) *ast.Symbol { - return c.resolveNameForSymbolSuggestion(location, outerName, meaning, nil /*nameNotFoundMessage*/, false /*isUse*/, false /*excludeGlobals*/) -} - -func (c *Checker) getSuggestionForSymbolNameLookup(symbols ast.SymbolTable, name string, meaning ast.SymbolFlags) *ast.Symbol { - symbol := c.getSymbol(symbols, name, meaning) - if symbol != nil { - return symbol - } - allSymbols := slices.Collect(maps.Values(symbols)) - if meaning&ast.SymbolFlagsGlobalLookup != 0 { - for _, s := range []string{"stringString", "numberNumber", "booleanBoolean", "objectObject", "bigintBigInt", "symbolSymbol"} { - if _, ok := symbols[s[len(s)/2:]]; ok { - allSymbols = append(allSymbols, c.newSymbol(ast.SymbolFlagsTypeAlias, s[:len(s)/2])) - } - } - } - c.sortSymbols(allSymbols) - return c.getSpellingSuggestionForName(name, allSymbols, meaning) -} - -// Given a name and a list of symbols whose names are *not* equal to the name, return a spelling suggestion if there is -// one that is close enough. Names less than length 3 only check for case-insensitive equality, not levenshtein distance. -// -// If there is a candidate that's the same except for case, return that. -// If there is a candidate that's within one edit of the name, return that. -// Otherwise, return the candidate with the smallest Levenshtein distance, -// -// Except for candidates: -// - With no name -// - Whose meaning doesn't match the `meaning` parameter. -// - Whose length differs from the target name by more than 0.34 of the length of the name. -// - Whose levenshtein distance is more than 0.4 of the length of the name (0.4 allows 1 substitution/transposition -// for every 5 characters, and 1 insertion/deletion at 3 characters) -func (c *Checker) getSpellingSuggestionForName(name string, symbols []*ast.Symbol, meaning ast.SymbolFlags) *ast.Symbol { - getCandidateName := func(candidate *ast.Symbol) string { - candidateName := ast.SymbolName(candidate) - if len(candidateName) == 0 || candidateName[0] == '"' || candidateName[0] == '\xFE' { - return "" - } - if candidate.Flags&meaning != 0 { - return candidateName - } - if candidate.Flags&ast.SymbolFlagsAlias != 0 { - alias := c.tryResolveAlias(candidate) - if alias != nil && alias.Flags&meaning != 0 { - return candidateName - } - } - return "" - } - return core.GetSpellingSuggestion(name, symbols, getCandidateName) -} - -func (c *Checker) onSuccessfullyResolvedSymbol(errorLocation *ast.Node, result *ast.Symbol, meaning ast.SymbolFlags, lastLocation *ast.Node, associatedDeclarationForContainingInitializerOrBindingName *ast.Node, withinDeferredContext bool) { - name := result.Name - isInExternalModule := lastLocation != nil && ast.IsSourceFile(lastLocation) && ast.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&ast.SymbolFlagsBlockScopedVariable != 0 || meaning&(ast.SymbolFlagsClass|ast.SymbolFlagsEnum) != 0 && meaning&ast.SymbolFlagsValue == ast.SymbolFlagsValue) { - exportOrLocalSymbol := c.getExportSymbolOfValueSymbolIfExported(result) - if exportOrLocalSymbol.Flags&(ast.SymbolFlagsBlockScopedVariable|ast.SymbolFlagsClass|ast.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&ast.SymbolFlagsValue) == ast.SymbolFlagsValue && errorLocation.Flags&ast.NodeFlagsJSDoc == 0 { - merged := c.getMergedSymbol(result) - if len(merged.Declarations) != 0 && core.Every(merged.Declarations, func(d *ast.Node) bool { - return ast.IsNamespaceExportDeclaration(d) || ast.IsSourceFile(d) && d.Symbol().GlobalExports != nil - }) { - c.errorOrSuggestion(c.compilerOptions.AllowUmdGlobalAccess != core.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&ast.SymbolFlagsValue) == ast.SymbolFlagsValue { - candidate := c.getMergedSymbol(c.getLateBoundSymbol(result)) - root := ast.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, scanner.DeclarationNameToString(associatedDeclarationForContainingInitializerOrBindingName.Name())) - } else if candidate.ValueDeclaration != nil && candidate.ValueDeclaration.Pos() > associatedDeclarationForContainingInitializerOrBindingName.Pos() && root.Parent.Locals() != nil && c.getSymbol(root.Parent.Locals(), candidate.Name, meaning) == candidate { - c.error(errorLocation, diagnostics.Parameter_0_cannot_reference_identifier_1_declared_after_it, scanner.DeclarationNameToString(associatedDeclarationForContainingInitializerOrBindingName.Name()), scanner.DeclarationNameToString(errorLocation)) - } - } - if errorLocation != nil && meaning&ast.SymbolFlagsValue != 0 && result.Flags&ast.SymbolFlagsAlias != 0 && result.Flags&ast.SymbolFlagsValue == 0 && !ast.IsValidTypeOnlyAliasUseSite(errorLocation) { - typeOnlyDeclaration := c.getTypeOnlyAliasDeclarationEx(result, ast.SymbolFlagsValue) - if typeOnlyDeclaration != nil { - message := core.IfElse(ast.NodeKindIs(typeOnlyDeclaration, ast.KindExportSpecifier, ast.KindExportDeclaration, ast.KindNamespaceExport), - 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 == core.TSTrue && result != nil && isInExternalModule && (meaning&ast.SymbolFlagsValue) == ast.SymbolFlagsValue { - isGlobal := c.getSymbol(c.globals, name, meaning) == result - var nonValueSymbol *ast.Symbol - if isGlobal && ast.IsSourceFile(lastLocation) { - nonValueSymbol = c.getSymbol(lastLocation.Locals(), name, ^ast.SymbolFlagsValue) - } - if nonValueSymbol != nil { - importDecl := core.Find(nonValueSymbol.Declarations, func(d *ast.Node) bool { - return ast.NodeKindIs(d, ast.KindImportSpecifier, ast.KindImportClause, ast.KindNamespaceImport, ast.KindImportEqualsDeclaration) - }) - if importDecl != nil && !ast.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 *ast.Symbol, errorLocation *ast.Node) { - debug.Assert(result.Flags&ast.SymbolFlagsBlockScopedVariable != 0 || result.Flags&ast.SymbolFlagsClass != 0 || result.Flags&ast.SymbolFlagsEnum != 0) - if result.Flags&(ast.SymbolFlagsFunction|ast.SymbolFlagsFunctionScopedVariable|ast.SymbolFlagsAssignment) != 0 && result.Flags&ast.SymbolFlagsClass != 0 { - // constructor functions aren't block scoped - return - } - // Block-scoped variables cannot be used before their definition - declaration := core.Find(result.Declarations, func(d *ast.Node) bool { - return ast.IsBlockOrCatchScoped(d) || ast.IsClassLike(d) || ast.IsEnumDeclaration(d) - }) - if declaration == nil { - panic("checkResolvedBlockScopedVariable could not find block-scoped declaration") - } - if declaration.Flags&ast.NodeFlagsAmbient == 0 && !c.isBlockScopedNameDeclaredBeforeUse(declaration, errorLocation) { - var diagnostic *ast.Diagnostic - declarationName := scanner.DeclarationNameToString(ast.GetNameOfDeclaration(declaration)) - if result.Flags&ast.SymbolFlagsBlockScopedVariable != 0 { - diagnostic = c.error(errorLocation, diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationName) - } else if result.Flags&ast.SymbolFlagsClass != 0 { - diagnostic = c.error(errorLocation, diagnostics.Class_0_used_before_its_declaration, declarationName) - } else if result.Flags&ast.SymbolFlagsRegularEnum != 0 { - diagnostic = c.error(errorLocation, diagnostics.Enum_0_used_before_its_declaration, declarationName) - } else { - debug.Assert(result.Flags&ast.SymbolFlagsConstEnum != 0) - if c.compilerOptions.GetIsolatedModules() { - diagnostic = c.error(errorLocation, diagnostics.Enum_0_used_before_its_declaration, declarationName) - } - } - if diagnostic != nil { - diagnostic.AddRelatedInfo(createDiagnosticForNode(declaration, diagnostics.X_0_is_declared_here, declarationName)) - } - } -} - -func (c *Checker) isBlockScopedNameDeclaredBeforeUse(declaration *ast.Node, usage *ast.Node) bool { - declarationFile := ast.GetSourceFileOfNode(declaration) - useFile := ast.GetSourceFileOfNode(usage) - declContainer := ast.GetEnclosingBlockScopeContainer(declaration) - if declarationFile != useFile { - // nodes are in different files and order cannot be determined - return true - } - // deferred usage in a type context is always OK regardless of the usage position: - if usage.Flags&ast.NodeFlagsJSDoc != 0 || IsInTypeQuery(usage) || c.isInAmbientOrTypeNode(usage) { - return true - } - if declaration.Pos() <= usage.Pos() && !(ast.IsPropertyDeclaration(declaration) && isThisProperty(usage.Parent) && declaration.Initializer() == nil && !isExclamationToken(declaration.AsPropertyDeclaration().PostfixToken)) { - // declaration is before usage - switch { - case declaration.Kind == ast.KindBindingElement: - // still might be illegal if declaration and usage are both binding elements (eg var [a = b, b = b] = [1, 2]) - errorBindingElement := ast.FindAncestorKind(usage, ast.KindBindingElement) - if errorBindingElement != nil { - return ast.FindAncestor(errorBindingElement, ast.IsBindingElement) != ast.FindAncestor(declaration, ast.IsBindingElement) || declaration.Pos() < errorBindingElement.Pos() - } - // or it might be illegal if usage happens before parent variable is declared (eg var [a] = a) - return c.isBlockScopedNameDeclaredBeforeUse(ast.FindAncestorKind(declaration, ast.KindVariableDeclaration), usage) - case declaration.Kind == ast.KindVariableDeclaration: - // still might be illegal if usage is in the initializer of the variable declaration (eg var a = a) - return !isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration, usage, declContainer) - case ast.IsClassLike(declaration): - // still might be illegal if the usage is within a computed property name in the class (eg class A { static p = "a"; [A.p]() {} }) - // or when used within a decorator in the class (e.g. `@dec(A.x) class A { static x = "x" }`), - // except when used in a function that is not an IIFE (e.g., `@dec(() => A.x) class A { ... }`) - container := usage - for container != nil && container != declaration { - if ast.IsComputedPropertyName(container) && container.Parent.Parent == declaration || - !c.legacyDecorators && ast.IsDecorator(container) && (container.Parent == declaration || - ast.IsMethodDeclaration(container.Parent) && container.Parent.Parent == declaration || - ast.IsAccessor(container.Parent) && container.Parent.Parent == declaration || - ast.IsPropertyDeclaration(container.Parent) && container.Parent.Parent == declaration || - ast.IsParameter(container.Parent) && container.Parent.Parent.Parent == declaration) { - break - } - container = container.Parent - } - if container == nil || container == declaration { - return true - } - if !c.legacyDecorators && ast.IsDecorator(container) { - n := usage - for n != nil && n != container { - if ast.IsFunctionLike(n) && ast.GetImmediatelyInvokedFunctionExpression(n) == nil { - break - } - n = n.Parent - } - return n != nil && n != container - } - return false - case ast.IsPropertyDeclaration(declaration): - // still might be illegal if a self-referencing property initializer (eg private x = this.x) - return !isPropertyImmediatelyReferencedWithinDeclaration(declaration, usage, false /*stopAtAnyPropertyDeclaration*/) - case ast.IsParameterPropertyDeclaration(declaration, declaration.Parent): - // foo = this.bar is illegal in emitStandardClassFields when bar is a parameter property - return !(c.emitStandardClassFields && ast.GetContainingClass(declaration) == ast.GetContainingClass(usage) && c.isUsedInFunctionOrInstanceProperty(usage, declaration, declContainer)) - } - return true - } - // declaration is after usage, but it can still be legal if usage is deferred: - // 1. inside an export specifier - // 2. inside a function - // 3. inside an instance property initializer, a reference to a non-instance property - // (except when emitStandardClassFields: true and the reference is to a parameter property) - // 4. inside a static property initializer, a reference to a static method in the same class - // 5. inside a TS export= declaration (since we will move the export statement during emit to avoid TDZ) - if ast.IsExportSpecifier(usage.Parent) || ast.IsExportAssignment(usage.Parent) && usage.Parent.AsExportAssignment().IsExportEquals { - // export specifiers do not use the variable, they only make it available for use - return true - } - // When resolving symbols for exports, the `usage` location passed in can be the export site directly - if ast.IsExportAssignment(usage) && usage.AsExportAssignment().IsExportEquals { - return true - } - if c.isUsedInFunctionOrInstanceProperty(usage, declaration, declContainer) { - if c.emitStandardClassFields && ast.GetContainingClass(declaration) != nil && (ast.IsPropertyDeclaration(declaration) || ast.IsParameterPropertyDeclaration(declaration, declaration.Parent)) { - return !isPropertyImmediatelyReferencedWithinDeclaration(declaration, usage, true /*stopAtAnyPropertyDeclaration*/) - } - return true - } - return false -} - -func (c *Checker) isUsedInFunctionOrInstanceProperty(usage *ast.Node, declaration *ast.Node, declContainer *ast.Node) bool { - return ast.FindAncestorOrQuit(usage, func(current *ast.Node) ast.FindAncestorResult { - if current == declContainer { - return ast.FindAncestorQuit - } - if ast.IsFunctionLike(current) { - return ast.ToFindAncestorResult(ast.GetImmediatelyInvokedFunctionExpression(current) == nil) - } - if ast.IsClassStaticBlockDeclaration(current) { - return ast.ToFindAncestorResult(declaration.Pos() < usage.Pos()) - } - - if current.Parent != nil && ast.IsPropertyDeclaration(current.Parent) { - propertyDeclaration := current.Parent - initializerOfProperty := propertyDeclaration.Initializer() == current - if initializerOfProperty { - if ast.IsStatic(current.Parent) { - if ast.IsMethodDeclaration(declaration) { - return ast.FindAncestorTrue - } - if ast.IsPropertyDeclaration(declaration) && ast.GetContainingClass(usage) == ast.GetContainingClass(declaration) { - propName := declaration.Name() - if ast.IsIdentifier(propName) || ast.IsPrivateIdentifier(propName) { - t := c.getTypeOfSymbol(c.getSymbolOfDeclaration(declaration)) - staticBlocks := core.Filter(declaration.Parent.Members(), ast.IsClassStaticBlockDeclaration) - if c.isPropertyInitializedInStaticBlocks(propName, t, staticBlocks, declaration.Parent.Pos(), current.Pos()) { - return ast.FindAncestorTrue - } - } - } - } else { - isDeclarationInstanceProperty := ast.IsPropertyDeclaration(declaration) && !ast.IsStatic(declaration) - if !isDeclarationInstanceProperty || ast.GetContainingClass(usage) != ast.GetContainingClass(declaration) { - return ast.FindAncestorTrue - } - } - } - } - - if current.Parent != nil && ast.IsDecorator(current.Parent) { - decorator := current.Parent.AsDecorator() - if decorator.Expression == current { - if ast.IsParameter(decorator.Parent) { - if c.isUsedInFunctionOrInstanceProperty(decorator.Parent.Parent.Parent, declaration, declContainer) { - return ast.FindAncestorTrue - } - return ast.FindAncestorQuit - } - if ast.IsMethodDeclaration(decorator.Parent) { - if c.isUsedInFunctionOrInstanceProperty(decorator.Parent.Parent, declaration, declContainer) { - return ast.FindAncestorTrue - } - return ast.FindAncestorQuit - } - } - } - - return ast.FindAncestorFalse - }) != nil -} - -func isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration *ast.Node, usage *ast.Node, declContainer *ast.Node) bool { - switch declaration.Parent.Parent.Kind { - case ast.KindVariableStatement, ast.KindForStatement, ast.KindForOfStatement: - // variable statement/for/for-of statement case, - // use site should not be inside variable declaration (initializer of declaration or binding element) - if isSameScopeDescendentOf(usage, declaration, declContainer) { - return true - } - } - // ForIn/ForOf case - use site should not be used in expression part - grandparent := declaration.Parent.Parent - return ast.IsForInOrOfStatement(grandparent) && isSameScopeDescendentOf(usage, grandparent.Expression(), declContainer) -} - -// Starting from 'initial' node walk up the parent chain until 'stopAt' node is reached. -// If at any point current node is equal to 'parent' node - return true. -// If current node is an IIFE, continue walking up. -// Return false if 'stopAt' node is reached or isFunctionLike(current) === true. -func isSameScopeDescendentOf(initial *ast.Node, parent *ast.Node, stopAt *ast.Node) bool { - if parent == nil { - return false - } - for n := initial; n != nil; n = n.Parent { - if n == parent { - return true - } - if n == stopAt || ast.IsFunctionLike(n) && (ast.GetImmediatelyInvokedFunctionExpression(n) == nil || (getFunctionFlags(n)&FunctionFlagsAsyncGenerator != 0)) { - return false - } - } - return false -} - -// stopAtAnyPropertyDeclaration is used for detecting ES-standard class field use-before-def errors -func isPropertyImmediatelyReferencedWithinDeclaration(declaration *ast.Node, usage *ast.Node, stopAtAnyPropertyDeclaration bool) bool { - // always legal if usage is after declaration - if usage.End() > declaration.End() { - return false - } - // still might be legal if usage is deferred (e.g. x: any = () => this.x) - // otherwise illegal if immediately referenced within the declaration (e.g. x: any = this.x) - for node := usage; node != nil && node != declaration; node = node.Parent { - switch node.Kind { - case ast.KindArrowFunction: - return false - case ast.KindPropertyDeclaration: - // even when stopping at any property declaration, they need to come from the same class - if stopAtAnyPropertyDeclaration && (ast.IsPropertyDeclaration(declaration) && node.Parent == declaration.Parent || ast.IsParameterPropertyDeclaration(declaration, declaration.Parent) && node.Parent == declaration.Parent.Parent) { - return true - } - case ast.KindBlock: - switch node.Parent.Kind { - case ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor: - return false - } - } - } - return true -} - -func (c *Checker) getTypeOnlyAliasDeclaration(symbol *ast.Symbol) *ast.Node { - return c.getTypeOnlyAliasDeclarationEx(symbol, ast.SymbolFlagsNone) -} - -func (c *Checker) getTypeOnlyAliasDeclarationEx(symbol *ast.Symbol, include ast.SymbolFlags) *ast.Node { - if symbol.Flags&ast.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 *ast.Symbol - if c.getDeclarationOfAliasSymbol(symbol) != nil { - immediateTarget = c.getImmediateAliasedSymbol(symbol) - } - c.markSymbolOfAliasDeclarationIfTypeOnly(core.FirstOrNil(symbol.Declarations), immediateTarget, resolved, true /*overwriteEmpty*/, nil, "") - } - if include == ast.SymbolFlagsNone { - return links.typeOnlyDeclaration - } - if links.typeOnlyDeclaration != nil { - var resolved *ast.Symbol - if links.typeOnlyDeclaration.Kind == ast.KindExportDeclaration { - 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 *ast.Symbol) *ast.Symbol { - debug.Assert(symbol.Flags&ast.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 *ast.Diagnostic, typeOnlyDeclaration *ast.Node, name string) *ast.Diagnostic { - if typeOnlyDeclaration == nil { - return diagnostic - } - isExport := ast.IsExportSpecifier(typeOnlyDeclaration) || ast.IsExportDeclaration(typeOnlyDeclaration) || ast.IsNamespaceExport(typeOnlyDeclaration) - return diagnostic.AddRelatedInfo(NewDiagnosticForNode(typeOnlyDeclaration, core.IfElse(isExport, diagnostics.X_0_was_exported_here, diagnostics.X_0_was_imported_here), name)) -} - -func (c *Checker) getSymbol(symbols ast.SymbolTable, name string, meaning ast.SymbolFlags) *ast.Symbol { - if meaning&ast.SymbolFlagsAll != 0 { - symbol := c.getMergedSymbol(symbols[name]) - if symbol != nil { - if symbol.Flags&meaning != 0 { - return symbol - } - if symbol.Flags&ast.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(ctx context.Context, sourceFile *ast.SourceFile) { - if SkipTypeChecking(sourceFile, c.compilerOptions, c.program, false) { - return - } - c.checkSourceFile(ctx, sourceFile) -} - -func (c *Checker) checkSourceFile(ctx context.Context, sourceFile *ast.SourceFile) { - c.checkNotCanceled() - links := c.sourceFileLinks.Get(sourceFile) - if !links.typeChecked { - c.ctx = ctx - // Grammar checking - c.checkGrammarSourceFile(sourceFile) - c.renamedBindingElementsInTypes = nil - c.checkSourceElements(sourceFile.Statements.Nodes) - c.checkDeferredNodes(sourceFile) - if ast.IsExternalOrCommonJSModule(sourceFile) { - c.checkExternalModuleExports(sourceFile.AsNode()) - c.registerForUnusedIdentifiersCheck(sourceFile.AsNode()) - } - if ctx.Err() == nil { - // This relies on the results of other lazy diagnostics, so must be computed after them - if !sourceFile.IsDeclarationFile && (c.compilerOptions.NoUnusedLocals.IsTrue() || c.compilerOptions.NoUnusedParameters.IsTrue()) { - c.checkUnusedIdentifiers(links.identifierCheckNodes) - } - if !sourceFile.IsDeclarationFile { - c.checkUnusedRenamedBindingElements() - } - } else { - c.wasCanceled = true - } - c.ctx = nil - links.typeChecked = true - } -} - -func (c *Checker) checkSourceElements(nodes []*ast.Node) { - for _, node := range nodes { - if c.isCanceled() { - break - } - c.checkSourceElement(node) - } -} - -func (c *Checker) checkSourceElement(node *ast.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 *ast.Node) { - if node.Flags&ast.NodeFlagsHasJSDoc != 0 { - for _, jsdoc := range node.JSDoc(nil) { - c.checkJSDocComments(jsdoc) - if tags := jsdoc.AsJSDoc().Tags; tags != nil { - for _, tag := range tags.Nodes { - c.checkJSDocComments(tag) - } - } - } - } - kind := node.Kind - if kind >= ast.KindFirstStatement && kind <= ast.KindLastStatement { - flowNode := node.FlowNodeData().FlowNode - if flowNode != nil && !c.isReachableFlowNode(flowNode) { - c.errorOrSuggestion(c.compilerOptions.AllowUnreachableCode == core.TSFalse, node, diagnostics.Unreachable_code_detected) - } - } - switch node.Kind { - case ast.KindTypeParameter: - c.checkTypeParameter(node) - case ast.KindParameter: - c.checkParameter(node) - case ast.KindPropertyDeclaration: - c.checkPropertyDeclaration(node) - case ast.KindPropertySignature: - c.checkPropertySignature(node) - case ast.KindConstructorType, ast.KindFunctionType, ast.KindCallSignature, ast.KindConstructSignature, ast.KindIndexSignature: - c.checkSignatureDeclaration(node) - case ast.KindMethodDeclaration, ast.KindMethodSignature: - c.checkMethodDeclaration(node) - case ast.KindClassStaticBlockDeclaration: - c.checkClassStaticBlockDeclaration(node) - case ast.KindConstructor: - c.checkConstructorDeclaration(node) - case ast.KindGetAccessor, ast.KindSetAccessor: - c.checkAccessorDeclaration(node) - case ast.KindTypeReference: - c.checkTypeReferenceNode(node) - case ast.KindTypePredicate: - c.checkTypePredicate(node) - case ast.KindTypeQuery: - c.checkTypeQuery(node) - case ast.KindTypeLiteral: - c.checkTypeLiteral(node) - case ast.KindArrayType: - c.checkArrayType(node) - case ast.KindTupleType: - c.checkTupleType(node) - case ast.KindUnionType, ast.KindIntersectionType: - c.checkUnionOrIntersectionType(node) - case ast.KindParenthesizedType, ast.KindOptionalType, ast.KindRestType: - node.ForEachChild(c.checkSourceElement) - case ast.KindThisType: - c.checkThisType(node) - case ast.KindTypeOperator: - c.checkTypeOperator(node) - case ast.KindConditionalType: - c.checkConditionalType(node) - case ast.KindInferType: - c.checkInferType(node) - case ast.KindTemplateLiteralType: - c.checkTemplateLiteralType(node) - case ast.KindImportType: - c.checkImportType(node) - case ast.KindNamedTupleMember: - c.checkNamedTupleMember(node) - case ast.KindIndexedAccessType: - c.checkIndexedAccessType(node) - case ast.KindMappedType: - c.checkMappedType(node) - case ast.KindFunctionDeclaration: - c.checkFunctionDeclaration(node) - case ast.KindBlock, ast.KindModuleBlock: - c.checkBlock(node) - case ast.KindVariableStatement: - c.checkVariableStatement(node) - case ast.KindExpressionStatement: - c.checkExpressionStatement(node) - case ast.KindIfStatement: - c.checkIfStatement(node) - case ast.KindDoStatement: - c.checkDoStatement(node) - case ast.KindWhileStatement: - c.checkWhileStatement(node) - case ast.KindForStatement: - c.checkForStatement(node) - case ast.KindForInStatement: - c.checkForInStatement(node) - case ast.KindForOfStatement: - c.checkForOfStatement(node) - case ast.KindContinueStatement, ast.KindBreakStatement: - c.checkBreakOrContinueStatement(node) - case ast.KindReturnStatement: - c.checkReturnStatement(node) - case ast.KindWithStatement: - c.checkWithStatement(node) - case ast.KindSwitchStatement: - c.checkSwitchStatement(node) - case ast.KindLabeledStatement: - c.checkLabeledStatement(node) - case ast.KindThrowStatement: - c.checkThrowStatement(node) - case ast.KindTryStatement: - c.checkTryStatement(node) - case ast.KindVariableDeclaration: - c.checkVariableDeclaration(node) - case ast.KindBindingElement: - c.checkBindingElement(node) - case ast.KindClassDeclaration: - c.checkClassDeclaration(node) - case ast.KindInterfaceDeclaration: - c.checkInterfaceDeclaration(node) - case ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration: - c.checkTypeAliasDeclaration(node) - case ast.KindEnumDeclaration: - c.checkEnumDeclaration(node) - case ast.KindEnumMember: - c.checkEnumMember(node) - case ast.KindModuleDeclaration: - c.checkModuleDeclaration(node) - case ast.KindImportDeclaration, ast.KindJSImportDeclaration: - c.checkImportDeclaration(node) - case ast.KindImportEqualsDeclaration: - c.checkImportEqualsDeclaration(node) - case ast.KindExportDeclaration: - c.checkExportDeclaration(node) - case ast.KindExportAssignment, ast.KindJSExportAssignment: - c.checkExportAssignment(node) - case ast.KindEmptyStatement: - c.checkGrammarStatementInAmbientContext(node) - case ast.KindDebuggerStatement: - c.checkGrammarStatementInAmbientContext(node) - case ast.KindMissingDeclaration: - c.checkMissingDeclaration(node) - case ast.KindJSDocNonNullableType, ast.KindJSDocNullableType, ast.KindJSDocAllType, ast.KindJSDocTypeLiteral: - c.checkJSDocType(node) - } -} - -// Function and class expression bodies are checked after all statements in the enclosing body. This is -// to ensure constructs like the following are permitted: -// -// const foo = function () { -// const s = foo(); -// return "hello"; -// } -// -// Here, performing a full type check of the body of the function expression whilst in the process of -// determining the type of foo would cause foo to be given type any because of the recursive reference. -// Delaying the type check of the body ensures foo has been assigned a type. -func (c *Checker) checkNodeDeferred(node *ast.Node) { - enclosingFile := ast.GetSourceFileOfNode(node) - links := c.sourceFileLinks.Get(enclosingFile) - if !links.typeChecked { - links.deferredNodes.Add(node) - } -} - -func (c *Checker) checkDeferredNodes(context *ast.SourceFile) { - links := c.sourceFileLinks.Get(context) - for node := range links.deferredNodes.Values() { - if c.isCanceled() { - break - } - c.checkDeferredNode(node) - } - links.deferredNodes.Clear() -} - -func (c *Checker) checkDeferredNode(node *ast.Node) { - saveCurrentNode := c.currentNode - c.currentNode = node - c.instantiationCount = 0 - switch node.Kind { - case ast.KindCallExpression, ast.KindNewExpression, ast.KindTaggedTemplateExpression, ast.KindDecorator, ast.KindJsxOpeningElement: - // These node kinds are deferred checked when overload resolution fails. To save on work, - // we ensure the arguments are checked just once in a deferred way. - c.resolveUntypedCall(node) - case ast.KindFunctionExpression, ast.KindArrowFunction, ast.KindMethodDeclaration, ast.KindMethodSignature: - c.checkFunctionExpressionOrObjectLiteralMethodDeferred(node) - case ast.KindGetAccessor, ast.KindSetAccessor: - c.checkAccessorDeclaration(node) - case ast.KindClassExpression: - c.checkClassExpressionDeferred(node) - case ast.KindTypeParameter: - c.checkTypeParameterDeferred(node) - case ast.KindJsxSelfClosingElement: - c.checkJsxSelfClosingElementDeferred(node) - case ast.KindJsxElement: - c.checkJsxElementDeferred(node) - case ast.KindTypeAssertionExpression, ast.KindAsExpression: - c.checkAssertionDeferred(node) - case ast.KindVoidExpression: - c.checkExpression(node.AsVoidExpression().Expression) - case ast.KindBinaryExpression: - if ast.IsInstanceOfExpression(node) { - c.resolveUntypedCall(node) - } - } - c.currentNode = saveCurrentNode -} - -func (c *Checker) checkJSDocComments(node *ast.Node) { - for _, comment := range node.Comments() { - c.checkJSDocComment(comment) - } -} - -func (c *Checker) checkJSDocComment(node *ast.Node) { - // This performs minimal checking of JSDoc nodes to ensure that @link references to entities are recorded - // for purposes of checking unused identifiers. - switch node.Kind { - case ast.KindJSDocLink, ast.KindJSDocLinkCode, ast.KindJSDocLinkPlain: - c.resolveJSDocMemberName(node.Name()) - } -} - -func (c *Checker) resolveJSDocMemberName(name *ast.Node) *ast.Symbol { - if name != nil && ast.IsEntityName(name) { - meaning := ast.SymbolFlagsType | ast.SymbolFlagsNamespace | ast.SymbolFlagsValue - if symbol := c.resolveEntityName(name, meaning, true /*ignoreErrors*/, true /*dontResolveAlias*/, nil); symbol != nil { - return symbol - } - if ast.IsQualifiedName(name) { - if symbol := c.resolveJSDocMemberName(name.AsQualifiedName().Left); symbol != nil { - var t *Type - if symbol.Flags&ast.SymbolFlagsValue != 0 { - proto := c.getPropertyOfType(c.getTypeOfSymbol(symbol), "prototype") - if proto != nil { - t = c.getTypeOfSymbol(proto) - } - } - if t == nil { - t = c.getDeclaredTypeOfSymbol(symbol) - } - return c.getPropertyOfType(t, name.AsQualifiedName().Right.Text()) - } - } - } - return nil -} - -func (c *Checker) checkJSDocType(node *ast.Node) { - c.checkJSDocTypeIsInJsFile(node) - node.ForEachChild(c.checkSourceElement) -} - -func (c *Checker) checkJSDocTypeIsInJsFile(node *ast.Node) { - if !ast.IsInJSFile(node) { - if ast.IsJSDocNonNullableType(node) || ast.IsJSDocNullableType(node) { - token := core.IfElse(ast.IsJSDocNonNullableType(node), "!", "?") - postfix := node.Pos() == node.Type().Pos() - message := core.IfElse(postfix, - diagnostics.X_0_at_the_end_of_a_type_is_not_valid_TypeScript_syntax_Did_you_mean_to_write_1, - diagnostics.X_0_at_the_start_of_a_type_is_not_valid_TypeScript_syntax_Did_you_mean_to_write_1) - t := c.getTypeFromTypeNode(node.Type()) - if ast.IsJSDocNullableType(node) && t != c.neverType && t != c.voidType { - t = c.getNullableType(t, core.IfElse(postfix, TypeFlagsUndefined, TypeFlagsNullable)) - } - c.grammarErrorOnNode(node, message, token, c.TypeToString(t)) - } else { - c.grammarErrorOnNode(node, diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments) - } - } -} - -func (c *Checker) checkTypeParameter(node *ast.Node) { - // Grammar Checking - c.checkGrammarModifiers(node) - if expr := node.AsTypeParameter().Expression; expr != nil { - c.grammarErrorOnFirstToken(expr, diagnostics.Type_expected) - } - tpNode := node.AsTypeParameter() - c.checkSourceElement(tpNode.Constraint) - c.checkSourceElement(tpNode.DefaultType) - typeParameter := c.getDeclaredTypeOfTypeParameter(c.getSymbolOfDeclaration(node)) - // Resolve base constraint to reveal circularity errors - c.getBaseConstraintOfType(typeParameter) - if c.getResolvedTypeParameterDefault(typeParameter) == c.circularConstraintType { - c.error(tpNode.DefaultType, diagnostics.Type_parameter_0_has_a_circular_default, c.TypeToString(typeParameter)) - } - constraintType := c.getConstraintOfTypeParameter(typeParameter) - defaultType := c.getDefaultFromTypeParameter(typeParameter) - if constraintType != nil && defaultType != nil { - c.checkTypeAssignableTo(defaultType, c.getTypeWithThisArgument(c.instantiateType(constraintType, newSimpleTypeMapper(typeParameter, defaultType)), defaultType, false), tpNode.DefaultType, diagnostics.Type_0_does_not_satisfy_the_constraint_1) - } - c.checkTypeNameIsReserved(node.Name(), diagnostics.Type_parameter_name_cannot_be_0) - c.checkNodeDeferred(node) -} - -func (c *Checker) checkTypeParameterDeferred(node *ast.Node) { - if ast.IsInterfaceDeclaration(node.Parent) || ast.IsClassLike(node.Parent) || ast.IsTypeOrJSTypeAliasDeclaration(node.Parent) { - typeParameter := c.getDeclaredTypeOfTypeParameter(c.getSymbolOfDeclaration(node)) - modifiers := c.getTypeParameterModifiers(typeParameter) & (ast.ModifierFlagsIn | ast.ModifierFlagsOut) - if modifiers != 0 { - symbol := c.getSymbolOfDeclaration(node.Parent) - if ast.IsTypeOrJSTypeAliasDeclaration(node.Parent) && c.getDeclaredTypeOfSymbol(symbol).objectFlags&(ObjectFlagsAnonymous|ObjectFlagsMapped) == 0 { - c.error(node, diagnostics.Variance_annotations_are_only_supported_in_type_aliases_for_object_function_constructor_and_mapped_types) - } else if modifiers == ast.ModifierFlagsIn || modifiers == ast.ModifierFlagsOut { - source := c.createMarkerType(symbol, typeParameter, core.IfElse(modifiers == ast.ModifierFlagsOut, c.markerSubTypeForCheck, c.markerSuperTypeForCheck)) - target := c.createMarkerType(symbol, typeParameter, core.IfElse(modifiers == ast.ModifierFlagsOut, c.markerSuperTypeForCheck, c.markerSubTypeForCheck)) - saveVarianceTypeParameter := typeParameter - c.varianceTypeParameter = typeParameter - c.checkTypeAssignableTo(source, target, node, diagnostics.Type_0_is_not_assignable_to_type_1_as_implied_by_variance_annotation) - c.varianceTypeParameter = saveVarianceTypeParameter - } - } - } -} - -func (c *Checker) checkParameter(node *ast.Node) { - // Grammar checking - // It is a SyntaxError if the Identifier "eval" or the Identifier "arguments" occurs as the - // Identifier in a PropertySetParameterList of a PropertyAssignment that is contained in strict code - // or if its FunctionBody is strict code(11.1.5). - c.checkGrammarModifiers(node) - c.checkVariableLikeDeclaration(node) - fn := ast.GetContainingFunction(node) - var paramName string - if node.Name() != nil && ast.IsIdentifier(node.Name()) { - paramName = node.Name().Text() - } - if ast.HasSyntacticModifier(node, ast.ModifierFlagsParameterPropertyModifier) { - if c.compilerOptions.ErasableSyntaxOnly.IsTrue() { - c.error(node, diagnostics.This_syntax_is_not_allowed_when_erasableSyntaxOnly_is_enabled) - } - if !(ast.IsConstructorDeclaration(fn) && ast.NodeIsPresent(fn.Body())) { - c.error(node, diagnostics.A_parameter_property_is_only_allowed_in_a_constructor_implementation) - } - if ast.IsConstructorDeclaration(fn) && paramName == "constructor" { - c.error(node.Name(), diagnostics.X_constructor_cannot_be_used_as_a_parameter_property_name) - } - } - if node.Initializer() == nil && isOptionalDeclaration(node) && ast.IsBindingPattern(node.Name()) && fn.Body() != nil { - c.error(node, diagnostics.A_binding_pattern_parameter_cannot_be_optional_in_an_implementation_signature) - } - if paramName == "this" || paramName == "new" { - if slices.Index(fn.Parameters(), node) != 0 { - c.error(node, diagnostics.A_0_parameter_must_be_the_first_parameter, paramName) - } - if ast.IsConstructorDeclaration(fn) || ast.IsConstructSignatureDeclaration(fn) || ast.IsConstructorTypeNode(fn) { - c.error(node, diagnostics.A_constructor_cannot_have_a_this_parameter) - } - if ast.IsArrowFunction(fn) { - c.error(node, diagnostics.An_arrow_function_cannot_have_a_this_parameter) - } - if ast.IsAccessor(fn) { - c.error(node, diagnostics.X_get_and_set_accessors_cannot_declare_this_parameters) - } - } - // Only check rest parameter type if it's not a binding pattern. Since binding patterns are - // not allowed in a rest parameter, we already have an error from checkGrammarParameterList. - if hasDotDotDotToken(node) && !ast.IsBindingPattern(node.Name()) && !c.isTypeAssignableTo(c.getReducedType(c.getTypeOfSymbol(node.Symbol())), c.anyReadonlyArrayType) { - c.error(node, diagnostics.A_rest_parameter_must_be_of_an_array_type) - } -} - -func (c *Checker) checkPropertyDeclaration(node *ast.Node) { - // Grammar checking - if !c.checkGrammarModifiers(node) && !c.checkGrammarProperty(node) { - c.checkGrammarComputedPropertyName(node.Name()) - } - c.checkVariableLikeDeclaration(node) - // property signatures already report "initializer not allowed in ambient context" elsewhere - if ast.HasSyntacticModifier(node, ast.ModifierFlagsAbstract) && ast.IsPropertyDeclaration(node) { - if node.Initializer() != nil { - c.error(node, diagnostics.Property_0_cannot_have_an_initializer_because_it_is_marked_abstract, scanner.DeclarationNameToString(node.Name())) - } - } -} - -func (c *Checker) checkPropertySignature(node *ast.Node) { - if ast.IsPrivateIdentifier(node.AsPropertySignatureDeclaration().Name()) { - c.error(node, diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies) - } - c.checkPropertyDeclaration(node) -} - -func (c *Checker) checkSignatureDeclaration(node *ast.Node) { - // Grammar checking - switch node.Kind { - case ast.KindIndexSignature: - c.checkGrammarIndexSignature(node.AsIndexSignatureDeclaration()) - case ast.KindFunctionType, ast.KindFunctionDeclaration, ast.KindConstructorType, ast.KindCallSignature, ast.KindConstructor, ast.KindConstructSignature: - c.checkGrammarFunctionLikeDeclaration(node) - } - c.checkTypeParameters(node.TypeParameters()) - c.checkUnmatchedJSDocParameters(node) - c.checkSourceElements(node.Parameters()) - returnTypeNode := node.Type() - if returnTypeNode != nil { - c.checkSourceElement(returnTypeNode) - } - if c.noImplicitAny && returnTypeNode == nil { - switch node.Kind { - case ast.KindConstructSignature: - c.error(node, diagnostics.Construct_signature_which_lacks_return_type_annotation_implicitly_has_an_any_return_type) - case ast.KindCallSignature: - c.error(node, diagnostics.Call_signature_which_lacks_return_type_annotation_implicitly_has_an_any_return_type) - } - } - if returnTypeNode != nil { - functionFlags := getFunctionFlags(node) - if (functionFlags & (FunctionFlagsInvalid | FunctionFlagsGenerator)) == FunctionFlagsGenerator { - returnType := c.getTypeFromTypeNode(returnTypeNode) - if returnType == c.voidType { - c.error(returnTypeNode, diagnostics.A_generator_cannot_have_a_void_type_annotation) - } else { - c.checkGeneratorInstantiationAssignabilityToReturnType(returnType, functionFlags, returnTypeNode) - } - } else if (functionFlags & FunctionFlagsAsyncGenerator) == FunctionFlagsAsync { - c.checkAsyncFunctionReturnType(node, returnTypeNode) - } - } - if !ast.IsIndexSignatureDeclaration(node) { - c.registerForUnusedIdentifiersCheck(node) - } -} - -// Checks the return type of an async function to ensure it is a compatible -// Promise implementation. -// -// This checks that an async function has a valid Promise-compatible return type. -// An async function has a valid Promise-compatible return type if the resolved value -// of the return type has a construct signature that takes in an `initializer` function -// that in turn supplies a `resolve` function as one of its arguments and results in an -// object with a callable `then` signature. -func (c *Checker) checkAsyncFunctionReturnType(node *ast.Node, returnTypeNode *ast.Node) { - returnType := c.getTypeFromTypeNode(returnTypeNode) - if c.isErrorType(returnType) { - return - } - globalPromiseType := c.getGlobalPromiseTypeChecked() - if globalPromiseType != c.emptyGenericType && !c.isReferenceToType(returnType, globalPromiseType) { - // The promise type was not a valid type reference to the global promise type, so we - // report an error and return the unknown type. - c.error(returnTypeNode, diagnostics.The_return_type_of_an_async_function_or_method_must_be_the_global_Promise_T_type_Did_you_mean_to_write_Promise_0, c.TypeToString(core.OrElse(c.getAwaitedTypeNoAlias(returnType), c.voidType))) - return - } - c.checkAwaitedType(returnType, false /*withAlias*/, node, diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member) -} - -func (c *Checker) checkMethodDeclaration(node *ast.Node) { - // Grammar checking - if !c.checkGrammarMethod(node) { - c.checkGrammarComputedPropertyName(node.Name()) - if ast.IsMethodDeclaration(node) && node.AsMethodDeclaration().AsteriskToken != nil && ast.IsIdentifier(node.Name()) && node.Name().Text() == "constructor" { - c.error(node.Name(), diagnostics.Class_constructor_may_not_be_a_generator) - } - } - // Grammar checking for modifiers is done inside the function checkGrammarFunctionLikeDeclaration - c.checkFunctionOrMethodDeclaration(node) - // method signatures already report "implementation not allowed in ambient context" elsewhere - if ast.HasSyntacticModifier(node, ast.ModifierFlagsAbstract) && ast.IsMethodDeclaration(node) && node.Body() != nil { - c.error(node, diagnostics.Method_0_cannot_have_an_implementation_because_it_is_marked_abstract, scanner.DeclarationNameToString(node.Name())) - } - // Private named methods are only allowed in class declarations - if ast.IsPrivateIdentifier(node.Name()) && ast.GetContainingClass(node) == nil { - c.error(node, diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies) - } -} - -func (c *Checker) checkClassStaticBlockDeclaration(node *ast.Node) { - // Grammar checking - c.checkGrammarModifiers(node) - node.ForEachChild(c.checkSourceElement) -} - -func (c *Checker) checkConstructorDeclaration(node *ast.Node) { - // Grammar check on signature of constructor and modifier of the constructor is done in checkSignatureDeclaration function. - c.checkSignatureDeclaration(node) - // Grammar check for checking only related to constructorDeclaration - ctor := node.AsConstructorDeclaration() - if !c.checkGrammarConstructorTypeParameters(ctor) { - c.checkGrammarConstructorTypeAnnotation(ctor) - } - c.checkSourceElement(node.Body()) - symbol := c.getSymbolOfDeclaration(node) - firstDeclaration := ast.GetDeclarationOfKind(symbol, node.Kind) - // Only type check the symbol once - if node == firstDeclaration { - c.checkFunctionOrConstructorSymbol(symbol) - } - // exit early in the case of signature - super checks are not relevant to them - if ast.NodeIsMissing(node.Body()) { - return - } - // TS 1.0 spec (April 2014): 8.3.2 - // Constructors of classes with no extends clause may not contain super calls, whereas - // constructors of derived classes must contain at least one super call somewhere in their function body. - containingClassDecl := node.Parent - if ast.GetExtendsHeritageClauseElement(containingClassDecl) == nil { - return - } - classExtendsNull := c.classDeclarationExtendsNull(containingClassDecl) - superCall := c.findFirstSuperCall(node.Body()) - if superCall != nil { - if classExtendsNull { - c.error(superCall, diagnostics.A_constructor_cannot_contain_a_super_call_when_its_class_extends_null) - } - // A super call must be root-level in a constructor if both of the following are true: - // - The containing class is a derived class. - // - The constructor declares parameter properties - // or the containing class declares instance member variables with initializers. - superCallShouldBeRootLevel := !c.emitStandardClassFields && - (core.Some(node.Parent.Members(), isInstancePropertyWithInitializerOrPrivateIdentifierProperty) || - core.Some(node.Parameters(), func(p *ast.Node) bool { - return ast.HasSyntacticModifier(p, ast.ModifierFlagsParameterPropertyModifier) - })) - if superCallShouldBeRootLevel { - // Until we have better flow analysis, it is an error to place the super call within any kind of block or conditional - // See GH #8277 - if !superCallIsRootLevelInConstructor(superCall, node.Body()) { - c.error(superCall, diagnostics.A_super_call_must_be_a_root_level_statement_within_a_constructor_of_a_derived_class_that_contains_initialized_properties_parameter_properties_or_private_identifiers) - } else { - var superCallStatement *ast.Node - for _, statement := range node.Body().AsBlock().Statements.Nodes { - if ast.IsExpressionStatement(statement) && isSuperCall(ast.SkipOuterExpressions(statement.Expression(), ast.OEKAll)) { - superCallStatement = statement - break - } - if nodeImmediatelyReferencesSuperOrThis(statement) { - break - } - } - // Until we have better flow analysis, it is an error to place the super call within any kind of block or conditional - // See GH #8277 - if superCallStatement == nil { - c.error(node, diagnostics.A_super_call_must_be_the_first_statement_in_the_constructor_to_refer_to_super_or_this_when_a_derived_class_contains_initialized_properties_parameter_properties_or_private_identifiers) - } - } - } - } else if !classExtendsNull { - c.error(node, diagnostics.Constructors_for_derived_classes_must_contain_a_super_call) - } -} - -func (c *Checker) findFirstSuperCall(node *ast.Node) *ast.Node { - var superCall *ast.Node - var visit func(node *ast.Node) bool - visit = func(node *ast.Node) bool { - switch { - case isSuperCall(node): - superCall = node - return true - case ast.IsFunctionLike(node): - return false - } - return node.ForEachChild(visit) - } - visit(node) - return superCall -} - -func isInstancePropertyWithInitializerOrPrivateIdentifierProperty(n *ast.Node) bool { - return ast.IsPrivateIdentifierClassElementDeclaration(n) || ast.IsPropertyDeclaration(n) && !ast.IsStatic(n) && n.Initializer() != nil -} - -func superCallIsRootLevelInConstructor(superCall *ast.Node, body *ast.Node) bool { - superCallParent := ast.WalkUpParenthesizedExpressions(superCall.Parent) - return ast.IsExpressionStatement(superCallParent) && superCallParent.Parent == body -} - -func nodeImmediatelyReferencesSuperOrThis(node *ast.Node) bool { - switch node.Kind { - case ast.KindSuperKeyword, ast.KindThisKeyword: - return true - case ast.KindArrowFunction, ast.KindFunctionDeclaration, ast.KindFunctionExpression, ast.KindPropertyDeclaration: - return false - case ast.KindBlock: - switch node.Parent.Kind { - case ast.KindConstructor, ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor: - return false - } - } - return node.ForEachChild(nodeImmediatelyReferencesSuperOrThis) -} - -func (c *Checker) checkAccessorDeclaration(node *ast.Node) { - // Grammar checking accessors - if !c.checkGrammarFunctionLikeDeclaration(node) && !c.checkGrammarAccessor(node) { - c.checkGrammarComputedPropertyName(node.Name()) - } - name := node.Name() - if ast.IsIdentifier(name) && name.Text() == "constructor" && ast.IsClassLike(node.Parent) { - c.error(node.Name(), diagnostics.Class_constructor_may_not_be_an_accessor) - } - c.checkDecorators(node) - c.checkSignatureDeclaration(node) - if ast.IsGetAccessorDeclaration(node) { - if node.Flags&ast.NodeFlagsAmbient == 0 && ast.NodeIsPresent(node.Body()) && node.Flags&ast.NodeFlagsHasImplicitReturn != 0 { - if node.Flags&ast.NodeFlagsHasExplicitReturn == 0 { - c.error(name, diagnostics.A_get_accessor_must_return_a_value) - } - } - } - // Do not use hasDynamicName here, because that returns false for well known symbols. - // We want to perform checkComputedPropertyName for all computed properties, including - // well known symbols. - if ast.IsComputedPropertyName(name) { - c.checkComputedPropertyName(name) - } - if c.hasBindableName(node) { - // TypeScript 1.0 spec (April 2014): 8.4.3 - // Accessors for the same member name must specify the same accessibility. - symbol := c.getSymbolOfDeclaration(node) - getter := ast.GetDeclarationOfKind(symbol, ast.KindGetAccessor) - setter := ast.GetDeclarationOfKind(symbol, ast.KindSetAccessor) - if getter != nil && setter != nil && c.nodeLinks.Get(getter).flags&NodeCheckFlagsTypeChecked == 0 { - c.nodeLinks.Get(getter).flags |= NodeCheckFlagsTypeChecked - getterFlags := getter.ModifierFlags() - setterFlags := setter.ModifierFlags() - if (getterFlags & ast.ModifierFlagsAbstract) != (setterFlags & ast.ModifierFlagsAbstract) { - c.error(getter.Name(), diagnostics.Accessors_must_both_be_abstract_or_non_abstract) - c.error(setter.Name(), diagnostics.Accessors_must_both_be_abstract_or_non_abstract) - } - if ((getterFlags&ast.ModifierFlagsProtected != 0) && setterFlags&(ast.ModifierFlagsProtected|ast.ModifierFlagsPrivate) == 0) || ((getterFlags&ast.ModifierFlagsPrivate != 0) && setterFlags&ast.ModifierFlagsPrivate == 0) { - c.error(getter.Name(), diagnostics.A_get_accessor_must_be_at_least_as_accessible_as_the_setter) - c.error(setter.Name(), diagnostics.A_get_accessor_must_be_at_least_as_accessible_as_the_setter) - } - } - } - returnType := c.getTypeOfAccessors(c.getSymbolOfDeclaration(node)) - if node.Kind == ast.KindGetAccessor { - c.checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, returnType) - } - c.checkSourceElement(node.Body()) -} - -func (c *Checker) checkTypeReferenceNode(node *ast.Node) { - c.checkGrammarTypeArguments(node, node.TypeArgumentList()) - if ast.IsTypeReferenceNode(node) && node.Flags&ast.NodeFlagsJSDoc == 0 { - data := node.AsTypeReferenceNode() - if data.TypeArguments != nil && data.TypeName.End() != data.TypeArguments.Pos() { - // If there was a token between the type name and the type arguments, check if it was a DotToken - sourceFile := ast.GetSourceFileOfNode(node) - if scanner.ScanTokenAtPosition(sourceFile, data.TypeName.End()) == ast.KindDotToken { - c.grammarErrorAtPos(node, scanner.SkipTrivia(sourceFile.Text(), data.TypeName.End()), 1, diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments) - } - } - } - c.checkSourceElements(node.TypeArguments()) - c.checkTypeReferenceOrImport(node) -} - -func (c *Checker) checkTypeReferenceOrImport(node *ast.Node) { - t := c.getTypeFromTypeNode(node) - if !c.isErrorType(t) { - if len(node.TypeArguments()) != 0 { - typeParameters := c.getTypeParametersForTypeReferenceOrImport(node) - if len(typeParameters) != 0 { - c.checkTypeArgumentConstraints(node, typeParameters) - } - } - symbol := c.getResolvedSymbolOrNil(node) - if symbol != nil { - if core.Some(symbol.Declarations, func(d *ast.Node) bool { return ast.IsTypeDeclaration(d) && d.Flags&ast.NodeFlagsDeprecated != 0 }) { - c.addDeprecatedSuggestion(c.getDeprecatedSuggestionNode(node), symbol.Declarations, symbol.Name) - } - } - } -} - -func (c *Checker) checkTypeArgumentConstraints(node *ast.Node, typeParameters []*Type) bool { - var typeArguments []*Type - var mapper *TypeMapper - result := true - for i, typeParameter := range typeParameters { - constraint := c.getConstraintOfTypeParameter(typeParameter) - if constraint != nil { - if typeArguments == nil { - typeArguments = c.getEffectiveTypeArguments(node, typeParameters) - mapper = newTypeMapper(typeParameters, typeArguments) - } - result = result && c.checkTypeAssignableTo(typeArguments[i], c.instantiateType(constraint, mapper), core.ElementOrNil(node.TypeArguments(), i), diagnostics.Type_0_does_not_satisfy_the_constraint_1) - } - } - return result -} - -func (c *Checker) getDeprecatedSuggestionNode(node *ast.Node) *ast.Node { - node = ast.SkipParentheses(node) - switch node.Kind { - case ast.KindCallExpression, ast.KindDecorator, ast.KindNewExpression: - return c.getDeprecatedSuggestionNode(node.Expression()) - case ast.KindTaggedTemplateExpression: - return c.getDeprecatedSuggestionNode(node.AsTaggedTemplateExpression().Tag) - case ast.KindJsxOpeningElement, ast.KindJsxSelfClosingElement: - return c.getDeprecatedSuggestionNode(getTagNameOfNode(node)) - case ast.KindElementAccessExpression: - return node.AsElementAccessExpression().ArgumentExpression - case ast.KindPropertyAccessExpression: - return node.Name() - case ast.KindTypeReference: - typeName := node.AsTypeReferenceNode().TypeName - if ast.IsQualifiedName(typeName) { - return typeName.AsQualifiedName().Right - } - } - return node -} - -func (c *Checker) checkTypePredicate(node *ast.Node) { - parent := c.getTypePredicateParent(node) - if parent == nil { - // The parent must not be valid. - c.error(node, diagnostics.A_type_predicate_is_only_allowed_in_return_type_position_for_functions_and_methods) - return - } - signature := c.getSignatureFromDeclaration(parent) - typePredicate := c.getTypePredicateOfSignature(signature) - if typePredicate == nil { - return - } - c.checkSourceElement(node.Type()) - parameterName := node.AsTypePredicateNode().ParameterName - if typePredicate.kind != TypePredicateKindThis && typePredicate.kind != TypePredicateKindAssertsThis { - if typePredicate.parameterIndex >= 0 { - if signatureHasRestParameter(signature) && int(typePredicate.parameterIndex) == len(signature.parameters)-1 { - c.error(parameterName, diagnostics.A_type_predicate_cannot_reference_a_rest_parameter) - } else { - if typePredicate.t != nil { - var diags []*ast.Diagnostic - if !c.checkTypeAssignableToEx(typePredicate.t, c.getTypeOfSymbol(signature.parameters[typePredicate.parameterIndex]), node.Type(), nil /*headMessage*/, &diags) { - c.diagnostics.Add(ast.NewDiagnosticChain(diags[0], diagnostics.A_type_predicate_s_type_must_be_assignable_to_its_parameter_s_type)) - } - } - } - } else if parameterName != nil { - hasReportedError := false - for _, param := range parent.Parameters() { - name := param.Name() - if ast.IsBindingPattern(name) && c.checkIfTypePredicateVariableIsDeclaredInBindingPattern(name, parameterName, typePredicate.parameterName) { - hasReportedError = true - break - } - } - if !hasReportedError { - c.error(parameterName, diagnostics.Cannot_find_parameter_0, typePredicate.parameterName) - } - } - } -} - -func (c *Checker) getTypePredicateParent(node *ast.Node) *ast.SignatureDeclaration { - parent := node.Parent - switch parent.Kind { - case ast.KindArrowFunction, ast.KindCallSignature, ast.KindFunctionDeclaration, ast.KindFunctionExpression, ast.KindFunctionType, - ast.KindMethodDeclaration, ast.KindMethodSignature: - if node == parent.Type() { - return parent - } - } - return nil -} - -func (c *Checker) checkIfTypePredicateVariableIsDeclaredInBindingPattern(pattern *ast.Node, predicateVariableNode *ast.Node, predicateVariableName string) bool { - for _, element := range pattern.AsBindingPattern().Elements.Nodes { - name := element.Name() - if name == nil { - continue - } - if ast.IsIdentifier(name) && name.Text() == predicateVariableName { - c.error(predicateVariableNode, diagnostics.A_type_predicate_cannot_reference_element_0_in_a_binding_pattern, predicateVariableName) - return true - } - if ast.IsArrayBindingPattern(name) || ast.IsObjectBindingPattern(name) { - if c.checkIfTypePredicateVariableIsDeclaredInBindingPattern(name, predicateVariableNode, predicateVariableName) { - return true - } - } - } - return false -} - -func (c *Checker) checkTypeQuery(node *ast.Node) { - c.getTypeFromTypeQueryNode(node) -} - -func (c *Checker) checkTypeLiteral(node *ast.Node) { - c.checkSourceElements(node.Members()) - t := c.getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node) - c.checkIndexConstraints(t, t.symbol, false /*isStaticIndex*/) - c.checkTypeForDuplicateIndexSignatures(node) - c.checkObjectTypeForDuplicateDeclarations(node, false /*checkPrivateNames*/) -} - -func (c *Checker) checkObjectTypeForDuplicateDeclarations(node *ast.Node, checkPrivateNames bool) { - var instanceNames map[string]int - var staticNames map[string]int - var privateNames map[string]int - nodeInAmbientContext := node.Flags&ast.NodeFlagsAmbient != 0 - checkProperty := func(symbol *ast.Symbol, isStatic bool) { - if len(symbol.Declarations) > 1 { - var names map[string]int - if isStatic { - if staticNames == nil { - staticNames = make(map[string]int) - } - names = staticNames - } else { - if instanceNames == nil { - instanceNames = make(map[string]int) - } - names = instanceNames - } - if state := names[symbol.Name]; state != 2 { - if state == 1 { - c.reportDuplicateMemberErrors(node, symbol.Name, true, isStatic, diagnostics.Duplicate_identifier_0) - } - names[symbol.Name] = state + 1 - } - } - } - for _, member := range node.Members() { - if ast.IsConstructorDeclaration(member) { - for _, param := range member.Parameters() { - if ast.IsParameterPropertyDeclaration(param, member) && !ast.IsBindingPattern(param.Name()) { - checkProperty(c.getSymbolOfDeclaration(param), false /*isStatic*/) - } - } - } else { - symbol := c.getSymbolOfDeclaration(member) - isStatic := ast.HasStaticModifier(member) - // In non-ambient contexts, check that static members are not named 'prototype'. - if !nodeInAmbientContext && isStatic && symbol != nil && symbol.Name == "prototype" { - c.error(member.Name(), diagnostics.Static_property_0_conflicts_with_built_in_property_Function_0_of_constructor_function_1, symbol.Name, c.symbolToString(c.getSymbolOfDeclaration(node))) - } - // When a property has multiple declarations, check that only one of those declarations is in this object - // type declaration (multiple merged object types are permitted to each declare the same property). - if ast.IsPropertyDeclaration(member) && !ast.HasAccessorModifier(member) || ast.IsPropertySignatureDeclaration(member) { - checkProperty(symbol, isStatic) - } - // Check that each private identifier is used only for instance members or only for static members. It is an - // error for an instance and a static member to have the same private identifier. - if checkPrivateNames && member.Name() != nil && ast.IsPrivateIdentifier(member.Name()) { - if flags := privateNames[symbol.Name]; flags != 3 { - flags |= core.IfElse(ast.IsStatic(member), 2, 1) - if privateNames == nil { - privateNames = make(map[string]int) - } - privateNames[symbol.Name] = flags - if flags == 3 { - c.reportDuplicateMemberErrors(node, symbol.Name, false, false, diagnostics.Duplicate_identifier_0_Static_and_instance_elements_cannot_share_the_same_private_name) - } - } - } - } - } -} - -func (c *Checker) reportDuplicateMemberErrors(node *ast.Node, name string, checkStatic bool, isStatic bool, message *diagnostics.Message) { - for _, member := range node.Members() { - if ast.IsConstructorDeclaration(member) { - for _, param := range member.Parameters() { - if ast.IsParameterPropertyDeclaration(param, member) && !ast.IsBindingPattern(param.Name()) { - if symbol := c.getSymbolOfDeclaration(param); symbol.Name == name { - c.error(param.Name(), message, c.symbolToString(symbol)) - } - } - } - } else if symbol := c.getSymbolOfDeclaration(member); symbol != nil && symbol.Name == name && (!checkStatic || isStatic == ast.IsStatic(member)) { - c.error(member.Name(), message, c.symbolToString(symbol)) - } - } -} - -func (c *Checker) checkArrayType(node *ast.Node) { - c.checkSourceElement(node.AsArrayTypeNode().ElementType) -} - -func (c *Checker) checkTupleType(node *ast.Node) { - seenOptionalElement := false - seenRestElement := false - elements := node.AsTupleTypeNode().Elements.Nodes - for _, e := range elements { - flags := c.getTupleElementFlags(e) - if flags&ElementFlagsVariadic != 0 { - t := c.getTypeFromTypeNode(e.Type()) - if !c.isArrayLikeType(t) { - c.error(e, diagnostics.A_rest_element_type_must_be_an_array_type) - break - } - if c.isArrayType(t) || isTupleType(t) && t.TargetTupleType().combinedFlags&ElementFlagsRest != 0 { - flags |= ElementFlagsRest - } - } - if flags&ElementFlagsRest != 0 { - if seenRestElement { - c.grammarErrorOnNode(e, diagnostics.A_rest_element_cannot_follow_another_rest_element) - break - } - seenRestElement = true - } else if flags&ElementFlagsOptional != 0 { - if seenRestElement { - c.grammarErrorOnNode(e, diagnostics.An_optional_element_cannot_follow_a_rest_element) - break - } - seenOptionalElement = true - } else if flags&ElementFlagsRequired != 0 && seenOptionalElement { - c.grammarErrorOnNode(e, diagnostics.A_required_element_cannot_follow_an_optional_element) - break - } - } - c.checkSourceElements(elements) - c.getTypeFromTypeNode(node) -} - -func (c *Checker) checkUnionOrIntersectionType(node *ast.Node) { - node.ForEachChild(c.checkSourceElement) - c.getTypeFromTypeNode(node) -} - -func (c *Checker) checkThisType(node *ast.Node) { - c.getTypeFromThisTypeNode(node) -} - -func (c *Checker) checkTypeOperator(node *ast.Node) { - c.checkGrammarTypeOperatorNode(node.AsTypeOperatorNode()) - c.checkSourceElement(node.Type()) -} - -func (c *Checker) checkConditionalType(node *ast.Node) { - node.ForEachChild(c.checkSourceElement) -} - -func (c *Checker) checkInferType(node *ast.Node) { - if ast.FindAncestor(node, func(n *ast.Node) bool { - return n.Parent != nil && n.Parent.Kind == ast.KindConditionalType && (n.Parent.AsConditionalTypeNode()).ExtendsType == n - }) == nil { - c.grammarErrorOnNode(node, diagnostics.X_infer_declarations_are_only_permitted_in_the_extends_clause_of_a_conditional_type) - } - typeParameterNode := node.AsInferTypeNode().TypeParameter - c.checkSourceElement(typeParameterNode) - symbol := c.getSymbolOfDeclaration(typeParameterNode) - if len(symbol.Declarations) > 1 { - links := c.declaredTypeLinks.Get(symbol) - if !links.typeParametersChecked { - links.typeParametersChecked = true - typeParameter := c.getDeclaredTypeOfTypeParameter(symbol) - declarations := getDeclarationsOfKind(symbol, ast.KindTypeParameter) - if !c.areTypeParametersIdentical(declarations, []*Type{typeParameter}, func(decl *ast.Node) []*ast.Node { return []*ast.Node{decl} }) { - // Report an error on every conflicting declaration. - name := c.symbolToString(symbol) - for _, declaration := range declarations { - c.error(declaration.Name(), diagnostics.All_declarations_of_0_must_have_identical_constraints, name) - } - } - } - } - c.registerForUnusedIdentifiersCheck(node) -} - -func (c *Checker) checkTemplateLiteralType(node *ast.Node) { - for _, span := range node.AsTemplateLiteralTypeNode().TemplateSpans.Nodes { - c.checkSourceElement(span.Type()) - t := c.getTypeFromTypeNode(span.Type()) - c.checkTypeAssignableTo(t, c.templateConstraintType, span.Type(), nil) - } - c.getTypeFromTypeNode(node) -} - -func (c *Checker) checkImportType(node *ast.Node) { - c.checkSourceElement(node.AsImportTypeNode().Argument) - if attributes := node.AsImportTypeNode().Attributes; attributes != nil { - c.getResolutionModeOverride(attributes.AsImportAttributes(), true /*reportErrors*/) - } - c.checkTypeReferenceOrImport(node) -} - -func (c *Checker) getResolutionModeOverride(node *ast.ImportAttributes, reportErrors bool) core.ResolutionMode { - if len(node.Attributes.Nodes) != 1 { - if reportErrors { - c.grammarErrorOnNode(node.AsNode(), core.IfElse(node.Token == ast.KindWithKeyword, - diagnostics.Type_import_attributes_should_have_exactly_one_key_resolution_mode_with_value_import_or_require, - diagnostics.Type_import_assertions_should_have_exactly_one_key_resolution_mode_with_value_import_or_require)) - } - return core.ResolutionModeNone - } - elem := node.Attributes.Nodes[0] - if !ast.IsStringLiteralLike(elem.Name()) { - return core.ResolutionModeNone - } - if elem.Name().Text() != "resolution-mode" { - if reportErrors { - c.grammarErrorOnNode(elem.Name(), core.IfElse(node.Token == ast.KindWithKeyword, - diagnostics.X_resolution_mode_is_the_only_valid_key_for_type_import_attributes, - diagnostics.X_resolution_mode_is_the_only_valid_key_for_type_import_assertions)) - } - return core.ResolutionModeNone - } - value := elem.AsImportAttribute().Value - if !ast.IsStringLiteralLike(value) { - return core.ResolutionModeNone - } - if value.Text() != "import" && value.Text() != "require" { - if reportErrors { - c.grammarErrorOnNode(value, diagnostics.X_resolution_mode_should_be_either_require_or_import) - } - return core.ResolutionModeNone - } - if value.Text() == "import" { - return core.ResolutionModeESM - } - return core.ResolutionModeCommonJS -} - -func (c *Checker) checkNamedTupleMember(node *ast.Node) { - tupleMember := node.AsNamedTupleMember() - if tupleMember.DotDotDotToken != nil && tupleMember.QuestionToken != nil { - c.grammarErrorOnNode(node, diagnostics.A_tuple_member_cannot_be_both_optional_and_rest) - } - if tupleMember.Type.Kind == ast.KindOptionalType { - c.grammarErrorOnNode(tupleMember.Type, diagnostics.A_labeled_tuple_element_is_declared_as_optional_with_a_question_mark_after_the_name_and_before_the_colon_rather_than_after_the_type) - } - if tupleMember.Type.Kind == ast.KindRestType { - c.grammarErrorOnNode(tupleMember.Type, diagnostics.A_labeled_tuple_element_is_declared_as_rest_with_a_before_the_name_rather_than_before_the_type) - } - c.checkSourceElement(node.Type()) - c.getTypeFromTypeNode(node) -} - -func (c *Checker) checkIndexedAccessType(node *ast.Node) { - node.ForEachChild(c.checkSourceElement) - c.checkIndexedAccessIndexType(c.getTypeFromIndexedAccessTypeNode(node), node) -} - -func (c *Checker) checkMappedType(node *ast.Node) { - mappedTypeNode := node.AsMappedTypeNode() - c.checkGrammarMappedType(mappedTypeNode) - c.checkSourceElement(mappedTypeNode.TypeParameter) - c.checkSourceElement(mappedTypeNode.NameType) - c.checkSourceElement(mappedTypeNode.Type) - if mappedTypeNode.Type == nil { - c.reportImplicitAny(node, c.anyType, WideningKindNormal) - } - t := c.getTypeFromMappedTypeNode(node) - nameType := c.getNameTypeFromMappedType(t) - if nameType != nil { - c.checkTypeAssignableTo(nameType, c.stringNumberSymbolType, mappedTypeNode.NameType, nil) - } else { - constraintType := c.getConstraintTypeFromMappedType(t) - c.checkTypeAssignableTo(constraintType, c.stringNumberSymbolType, mappedTypeNode.TypeParameter.AsTypeParameter().Constraint, nil) - } -} - -func (c *Checker) checkFunctionDeclaration(node *ast.Node) { - c.checkFunctionOrMethodDeclaration(node) - c.checkGrammarForGenerator(node) - c.checkCollisionsForDeclarationName(node, node.Name()) -} - -func (c *Checker) checkFunctionOrMethodDeclaration(node *ast.Node) { - c.checkDecorators(node) - c.checkSignatureDeclaration(node) - functionFlags := getFunctionFlags(node) - // Do not use hasDynamicName here, because that returns false for well known symbols. - // We want to perform checkComputedPropertyName for all computed properties, including - // well known symbols. - if node.Name() != nil && ast.IsComputedPropertyName(node.Name()) { - // This check will account for methods in class/interface declarations, - // as well as accessors in classes/object literals - c.checkComputedPropertyName(node.Name()) - } - if c.hasBindableName(node) { - // first we want to check the local symbol that contain this declaration - // - if node.localSymbol !== undefined - this is current declaration is exported and localSymbol points to the local symbol - // - if node.localSymbol === undefined - this node is non-exported so we can just pick the result of getSymbolOfNode - symbol := c.getSymbolOfDeclaration(node) - localSymbol := core.OrElse(node.LocalSymbol(), symbol) - // Since the javascript won't do semantic analysis like typescript, - // if the javascript file comes before the typescript file and both contain same name functions, - // checkFunctionOrConstructorSymbol wouldn't be called if we didnt ignore javascript function. - firstDeclaration := core.Find(localSymbol.Declarations, func(declaration *ast.Node) bool { - return declaration.Kind == node.Kind && declaration.Flags&ast.NodeFlagsJavaScriptFile == 0 - }) - // Only type check the symbol once - if node == firstDeclaration { - c.checkFunctionOrConstructorSymbol(localSymbol) - } - if symbol.Parent != nil { - // run check on export symbol to check that modifiers agree across all exported declarations - c.checkFunctionOrConstructorSymbol(symbol) - } - } - body := node.Body() - c.checkSourceElement(body) - c.checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, c.getReturnTypeFromAnnotation(node)) - if node.FunctionLikeData().FullSignature != nil { - if c.getContextualCallSignature(c.getTypeFromTypeNode(node.FunctionLikeData().FullSignature), node) == nil { - c.error(node.FunctionLikeData().FullSignature, diagnostics.A_JSDoc_type_tag_on_a_function_must_have_a_signature_with_the_correct_number_of_arguments) - } - if node.Type() != nil || core.Some(node.Parameters(), func(p *ast.Node) bool { return p.Type() != nil }) { - c.error(node.FunctionLikeData().FullSignature, diagnostics.A_JSDoc_type_tag_may_not_occur_with_a_param_or_returns_tag) - } - } - if node.Type() == nil { - // Report an implicit any error if there is no body, no explicit return type, and node is not a private method - // in an ambient context - if ast.NodeIsMissing(body) && !isPrivateWithinAmbient(node) { - c.reportImplicitAny(node, c.anyType, WideningKindNormal) - } - if functionFlags&FunctionFlagsGenerator != 0 && ast.NodeIsPresent(body) { - // A generator with a body and no type annotation can still cause errors. It can error if the - // yielded values have no common supertype, or it can give an implicit any error if it has no - // yielded values. The only way to trigger these errors is to try checking its return type. - c.getReturnTypeOfSignature(c.getSignatureFromDeclaration(node)) - } - } -} - -func (c *Checker) checkFunctionOrConstructorSymbol(symbol *ast.Symbol) { - flagsToCheck := ast.ModifierFlagsExport | ast.ModifierFlagsAmbient | ast.ModifierFlagsPrivate | ast.ModifierFlagsProtected | ast.ModifierFlagsAbstract - someNodeFlags := ast.ModifierFlagsNone - allNodeFlags := flagsToCheck - someHaveQuestionToken := false - allHaveQuestionToken := true - hasOverloads := false - var bodyDeclaration *ast.Node - var lastSeenNonAmbientDeclaration *ast.Node - var previousDeclaration *ast.Node - declarations := symbol.Declarations - isConstructor := symbol.Flags&ast.SymbolFlagsConstructor != 0 - duplicateFunctionDeclaration := false - multipleConstructorImplementation := false - hasNonAmbientClass := false - var functionDeclarations []*ast.Node - getCanonicalOverload := func(overloads []*ast.Node, implementation *ast.Node) *ast.Node { - // Consider the canonical set of flags to be the flags of the bodyDeclaration or the first declaration - // Error on all deviations from this canonical set of flags - // The caveat is that if some overloads are defined in lib.d.ts, we don't want to - // report the errors on those. To achieve this, we will say that the implementation is - // the canonical signature only if it is in the same container as the first overload - implementationSharesContainerWithFirstOverload := implementation != nil && implementation.Parent == overloads[0].Parent - if implementationSharesContainerWithFirstOverload { - return implementation - } - return overloads[0] - } - checkFlagAgreementBetweenOverloads := func(overloads []*ast.Node, implementation *ast.Node, flagsToCheck ast.ModifierFlags, someOverloadFlags ast.ModifierFlags, allOverloadFlags ast.ModifierFlags) { - // Error if some overloads have a flag that is not shared by all overloads. To find the - // deviations, we XOR someOverloadFlags with allOverloadFlags - someButNotAllOverloadFlags := someOverloadFlags ^ allOverloadFlags - if someButNotAllOverloadFlags != 0 { - canonicalFlags := c.getEffectiveDeclarationFlags(getCanonicalOverload(overloads, implementation), flagsToCheck) - groups := make(map[*ast.SourceFile][]*ast.Node) - for _, overload := range overloads { - sourceFile := ast.GetSourceFileOfNode(overload) - groups[sourceFile] = append(groups[sourceFile], overload) - } - for _, overloadsInFile := range groups { - canonicalFlagsForFile := c.getEffectiveDeclarationFlags(getCanonicalOverload(overloadsInFile, implementation), flagsToCheck) - for _, overload := range overloadsInFile { - deviation := c.getEffectiveDeclarationFlags(overload, flagsToCheck) ^ canonicalFlags - deviationInFile := c.getEffectiveDeclarationFlags(overload, flagsToCheck) ^ canonicalFlagsForFile - switch { - case deviationInFile&ast.ModifierFlagsExport != 0: - // Overloads in different files need not all have export modifiers. This is ok: - // // lib.d.ts - // declare function foo(s: number): string; - // declare function foo(s: string): number; - // export { foo }; - // - // // app.ts - // declare module "lib" { - // export function foo(s: boolean): boolean; - // } - c.error(ast.GetNameOfDeclaration(overload), diagnostics.Overload_signatures_must_all_be_exported_or_non_exported) - case deviationInFile&ast.ModifierFlagsAmbient != 0: - // Though rare, a module augmentation (necessarily ambient) is allowed to add overloads - // to a non-ambient function in an implementation file. - c.error(ast.GetNameOfDeclaration(overload), diagnostics.Overload_signatures_must_all_be_ambient_or_non_ambient) - case deviation&(ast.ModifierFlagsPrivate|ast.ModifierFlagsProtected) != 0: - c.error(ast.GetNameOfDeclaration(overload), diagnostics.Overload_signatures_must_all_be_public_private_or_protected) - case deviation&ast.ModifierFlagsAbstract != 0: - c.error(ast.GetNameOfDeclaration(overload), diagnostics.Overload_signatures_must_all_be_abstract_or_non_abstract) - } - } - } - } - } - checkQuestionTokenAgreementBetweenOverloads := func(overloads []*ast.Node, implementation *ast.Node, someHaveQuestionToken bool, allHaveQuestionToken bool) { - if someHaveQuestionToken != allHaveQuestionToken { - canonicalHasQuestionToken := isOptionalDeclaration(getCanonicalOverload(overloads, implementation)) - for _, o := range overloads { - if isOptionalDeclaration(o) != canonicalHasQuestionToken { - c.error(ast.GetNameOfDeclaration(o), diagnostics.Overload_signatures_must_all_be_optional_or_required) - } - } - } - } - reportImplementationExpectedError := func(node *ast.Node) { - name := node.Name() - if name != nil && ast.NodeIsMissing(name) { - return - } - seen := false - var subsequentNode *ast.Node - node.Parent.ForEachChild(func(child *ast.Node) bool { - if seen { - subsequentNode = child - return true - } - seen = child == node - return false - }) - // We may be here because of some extra nodes between overloads that could not be parsed into a valid node. - // In this case the subsequent node is not really consecutive (.pos !== node.end), and we must ignore it here. - if subsequentNode != nil && subsequentNode.Pos() == node.End() { - if subsequentNode.Kind == node.Kind { - subsequentName := subsequentNode.Name() - errorNode := core.OrElse(subsequentName, subsequentNode) - if name != nil && subsequentName != nil && - (ast.IsPrivateIdentifier(name) && ast.IsPrivateIdentifier(subsequentName) && name.Text() == subsequentName.Text() || - ast.IsComputedPropertyName(name) && ast.IsComputedPropertyName(subsequentName) && c.isTypeIdenticalTo(c.checkComputedPropertyName(name), c.checkComputedPropertyName(subsequentName)) || - ast.IsPropertyNameLiteral(name) && ast.IsPropertyNameLiteral(subsequentName) && name.Text() == subsequentName.Text()) { - reportError := (ast.IsMethodDeclaration(node) || ast.IsMethodSignatureDeclaration(node)) && ast.IsStatic(node) != ast.IsStatic(subsequentNode) - // we can get here in two cases - // 1. mixed static and instance class members - // 2. something with the same name was defined before the set of overloads that prevents them from merging - // here we'll report error only for the first case since for second we should already report error in binder - if reportError { - diagnostic := core.IfElse(ast.IsStatic(node), diagnostics.Function_overload_must_be_static, diagnostics.Function_overload_must_not_be_static) - c.error(errorNode, diagnostic) - } - return - } - if ast.NodeIsPresent(subsequentNode.Body()) { - c.error(errorNode, diagnostics.Function_implementation_name_must_be_0, scanner.DeclarationNameToString(name)) - return - } - } - } - errorNode := core.OrElse(name, node) - if isConstructor { - c.error(errorNode, diagnostics.Constructor_implementation_is_missing) - } else { - // Report different errors regarding non-consecutive blocks of declarations depending on whether - // the node in question is abstract. - if ast.HasSyntacticModifier(node, ast.ModifierFlagsAbstract) { - c.error(errorNode, diagnostics.All_declarations_of_an_abstract_method_must_be_consecutive) - } else { - c.error(errorNode, diagnostics.Function_implementation_is_missing_or_not_immediately_following_the_declaration) - } - } - } - for _, node := range declarations { - inAmbientContext := node.Flags&ast.NodeFlagsAmbient != 0 - inAmbientContextOrInterface := inAmbientContext || node.Parent != nil && (ast.IsInterfaceDeclaration(node.Parent) || ast.IsTypeLiteralNode(node.Parent)) - if inAmbientContextOrInterface { - // check if declarations are consecutive only if they are non-ambient - // 1. ambient declarations can be interleaved - // i.e. this is legal - // declare function foo(); - // declare function bar(); - // declare function foo(); - // 2. mixing ambient and non-ambient declarations is a separate error that will be reported - do not want to report an extra one - previousDeclaration = nil - } - if ast.IsClassLike(node) && !inAmbientContext { - hasNonAmbientClass = true - } - if ast.IsFunctionDeclaration(node) || ast.IsMethodDeclaration(node) || ast.IsMethodSignatureDeclaration(node) || ast.IsConstructorDeclaration(node) { - functionDeclarations = append(functionDeclarations, node) - currentNodeFlags := c.getEffectiveDeclarationFlags(node, flagsToCheck) - someNodeFlags |= currentNodeFlags - allNodeFlags &= currentNodeFlags - someHaveQuestionToken = someHaveQuestionToken || isOptionalDeclaration(node) - allHaveQuestionToken = allHaveQuestionToken && isOptionalDeclaration(node) - bodyIsPresent := ast.NodeIsPresent(node.Body()) - if bodyIsPresent && bodyDeclaration != nil { - if isConstructor { - multipleConstructorImplementation = true - } else { - duplicateFunctionDeclaration = true - } - } else if previousDeclaration != nil && previousDeclaration.Parent == node.Parent && previousDeclaration.End() != node.Pos() && previousDeclaration.Flags&ast.NodeFlagsReparsed == 0 { - reportImplementationExpectedError(previousDeclaration) - } - if bodyIsPresent { - if bodyDeclaration == nil { - bodyDeclaration = node - } - } else { - hasOverloads = true - } - previousDeclaration = node - if !inAmbientContextOrInterface { - lastSeenNonAmbientDeclaration = node - } - } - } - if multipleConstructorImplementation { - for _, declaration := range functionDeclarations { - c.error(declaration, diagnostics.Multiple_constructor_implementations_are_not_allowed) - } - } - if duplicateFunctionDeclaration { - for _, declaration := range functionDeclarations { - c.error(core.OrElse(ast.GetNameOfDeclaration(declaration), declaration), diagnostics.Duplicate_function_implementation) - } - } - if hasNonAmbientClass && !isConstructor && symbol.Flags&ast.SymbolFlagsFunction != 0 && len(declarations) != 0 { - var relatedDiagnostics []*ast.Diagnostic - for _, declaration := range declarations { - if ast.IsClassDeclaration(declaration) { - relatedDiagnostics = append(relatedDiagnostics, createDiagnosticForNode(declaration, diagnostics.Consider_adding_a_declare_modifier_to_this_class)) - } - } - for _, declaration := range declarations { - var diagnostic *diagnostics.Message - switch declaration.Kind { - case ast.KindClassDeclaration: - diagnostic = diagnostics.Class_declaration_cannot_implement_overload_list_for_0 - case ast.KindFunctionDeclaration: - diagnostic = diagnostics.Function_with_bodies_can_only_merge_with_classes_that_are_ambient - } - if diagnostic != nil { - c.error(core.OrElse(ast.GetNameOfDeclaration(declaration), declaration), diagnostic, symbol.Name).SetRelatedInfo(relatedDiagnostics) - } - } - } - // Abstract methods can't have an implementation -- in particular, they don't need one. - if lastSeenNonAmbientDeclaration != nil && lastSeenNonAmbientDeclaration.Body() == nil && !ast.HasSyntacticModifier(lastSeenNonAmbientDeclaration, ast.ModifierFlagsAbstract) && !isOptionalDeclaration(lastSeenNonAmbientDeclaration) { - reportImplementationExpectedError(lastSeenNonAmbientDeclaration) - } - if hasOverloads { - checkFlagAgreementBetweenOverloads(declarations, bodyDeclaration, flagsToCheck, someNodeFlags, allNodeFlags) - checkQuestionTokenAgreementBetweenOverloads(declarations, bodyDeclaration, someHaveQuestionToken, allHaveQuestionToken) - if bodyDeclaration != nil { - signatures := c.getSignaturesOfSymbol(symbol) - bodySignature := c.getSignatureFromDeclaration(bodyDeclaration) - for _, signature := range signatures { - if !c.isImplementationCompatibleWithOverload(bodySignature, signature) { - errorNode := signature.declaration - c.error(errorNode, diagnostics.This_overload_signature_is_not_compatible_with_its_implementation_signature).AddRelatedInfo(createDiagnosticForNode(bodyDeclaration, diagnostics.The_implementation_signature_is_declared_here)) - break - } - } - } - } -} - -func (c *Checker) getEffectiveDeclarationFlags(n *ast.Node, flagsToCheck ast.ModifierFlags) ast.ModifierFlags { - flags := c.getCombinedModifierFlagsCached(n) - // children of classes (even ambient classes) should not be marked as ambient or export - // because those flags have no useful semantics there. - if !ast.IsInterfaceDeclaration(n.Parent) && !ast.IsClassDeclaration(n.Parent) && !ast.IsClassExpression(n.Parent) && n.Flags&ast.NodeFlagsAmbient != 0 { - container := getEnclosingContainer(n) - if container != nil && container.Flags&ast.NodeFlagsExportContext != 0 && flags&ast.ModifierFlagsAmbient == 0 && !(ast.IsModuleBlock(n.Parent) && ast.IsGlobalScopeAugmentation(n.Parent.Parent)) { - // It is nested in an ambient export context, which means it is automatically exported - flags |= ast.ModifierFlagsExport - } - flags |= ast.ModifierFlagsAmbient - } - return flags & flagsToCheck -} - -func (c *Checker) isImplementationCompatibleWithOverload(implementation *Signature, overload *Signature) bool { - erasedSource := c.getErasedSignature(implementation) - erasedTarget := c.getErasedSignature(overload) - // First see if the return types are compatible in either direction. - sourceReturnType := c.getReturnTypeOfSignature(erasedSource) - targetReturnType := c.getReturnTypeOfSignature(erasedTarget) - if targetReturnType == c.voidType || c.isTypeRelatedTo(targetReturnType, sourceReturnType, c.assignableRelation) || c.isTypeRelatedTo(sourceReturnType, targetReturnType, c.assignableRelation) { - return c.isSignatureAssignableTo(erasedSource, erasedTarget, true /*ignoreReturnTypes*/) - } - return false -} - -func (c *Checker) checkAllCodePathsInNonVoidFunctionReturnOrThrow(fn *ast.Node, returnType *Type) { - functionFlags := getFunctionFlags(fn) - var t *Type - if returnType != nil { - t = c.unwrapReturnType(returnType, functionFlags) - } - // Functions with an explicitly specified return type that includes `void` or is exactly `any` or `undefined` don't - // need any return statements. - if t != nil && (c.maybeTypeOfKind(t, TypeFlagsVoid) || t.flags&(TypeFlagsAny|TypeFlagsUndefined) != 0) { - return - } - // If all we have is a function signature, or an arrow function with an expression body, then there is nothing to check. - // also if HasImplicitReturn flag is not set this means that all codepaths in function body end with return or throw - if ast.IsMethodSignatureDeclaration(fn) || ast.NodeIsMissing(fn.Body()) || !ast.IsBlock(fn.Body()) || !c.functionHasImplicitReturn(fn) { - return - } - hasExplicitReturn := fn.Flags&ast.NodeFlagsHasExplicitReturn != 0 - errorNode := fn.Type() - if errorNode == nil { - if data := fn.FunctionLikeData(); data != nil && data.FullSignature != nil { - errorNode = data.FullSignature - } - } - if errorNode == nil { - errorNode = fn - } - switch { - case t != nil && t.flags&TypeFlagsNever != 0: - c.error(errorNode, diagnostics.A_function_returning_never_cannot_have_a_reachable_end_point) - case t != nil && !hasExplicitReturn: - // minimal check: function has syntactic return type annotation and no explicit return statements in the body - // this function does not conform to the specification. - c.error(errorNode, diagnostics.A_function_whose_declared_type_is_neither_undefined_void_nor_any_must_return_a_value) - case t != nil && c.strictNullChecks && !c.isTypeAssignableTo(c.undefinedType, t): - c.error(errorNode, diagnostics.Function_lacks_ending_return_statement_and_return_type_does_not_include_undefined) - case c.compilerOptions.NoImplicitReturns == core.TSTrue: - if t == nil { - // If return type annotation is omitted check if function has any explicit return statements. - // If it does not have any - its inferred return type is void - don't do any checks. - // Otherwise get inferred return type from function body and report error only if it is not void / anytype - if !hasExplicitReturn { - return - } - inferredReturnType := c.getReturnTypeOfSignature(c.getSignatureFromDeclaration(fn)) - if c.isUnwrappedReturnTypeUndefinedVoidOrAny(fn, inferredReturnType) { - return - } - } - c.error(errorNode, diagnostics.Not_all_code_paths_return_a_value) - } -} - -func (c *Checker) isUnwrappedReturnTypeUndefinedVoidOrAny(fn *ast.Node, returnType *Type) bool { - t := c.unwrapReturnType(returnType, getFunctionFlags(fn)) - return t != nil && (c.maybeTypeOfKind(t, TypeFlagsVoid) || t.flags&(TypeFlagsAny|TypeFlagsUndefined) != 0) -} - -func (c *Checker) checkBlock(node *ast.Node) { - // Grammar checking for SyntaxKind.Block - if node.Kind == ast.KindBlock { - c.checkGrammarStatementInAmbientContext(node) - } - if ast.IsFunctionOrModuleBlock(node) { - saveFlowAnalysisDisabled := c.flowAnalysisDisabled - c.checkSourceElements(node.Statements()) - c.flowAnalysisDisabled = saveFlowAnalysisDisabled - } else { - c.checkSourceElements(node.Statements()) - } - if len(node.Locals()) != 0 { - c.registerForUnusedIdentifiersCheck(node) - } -} - -func (c *Checker) checkIfStatement(node *ast.Node) { - c.checkGrammarStatementInAmbientContext(node) - t := c.checkTruthinessExpression(node.Expression(), CheckModeNormal) - data := node.AsIfStatement() - c.checkTestingKnownTruthyCallableOrAwaitableOrEnumMemberType(node.Expression(), t, data.ThenStatement) - c.checkSourceElement(data.ThenStatement) - if ast.IsEmptyStatement(data.ThenStatement) { - c.error(data.ThenStatement, diagnostics.The_body_of_an_if_statement_cannot_be_the_empty_statement) - } - c.checkSourceElement(data.ElseStatement) -} - -func (c *Checker) checkTestingKnownTruthyCallableOrAwaitableOrEnumMemberType(condExpr *ast.Node, condType *Type, body *ast.Node) { - if !c.strictNullChecks { - return - } - c.checkTestingKnownTruthyTypes(condExpr, condType, body) -} - -func (c *Checker) checkTestingKnownTruthyTypes(condExpr *ast.Node, condType *Type, body *ast.Node) { - condExpr = ast.SkipParentheses(condExpr) - c.checkTestingKnownTruthyType(condExpr, condType, body) - for ast.IsBinaryExpression(condExpr) && (condExpr.AsBinaryExpression().OperatorToken.Kind == ast.KindBarBarToken || condExpr.AsBinaryExpression().OperatorToken.Kind == ast.KindQuestionQuestionToken) { - condExpr = ast.SkipParentheses(condExpr.AsBinaryExpression().Left) - c.checkTestingKnownTruthyType(condExpr, condType, body) - } -} - -func (c *Checker) checkTestingKnownTruthyType(condExpr *ast.Node, condType *Type, body *ast.Node) { - location := condExpr - if ast.IsLogicalOrCoalescingBinaryExpression(condExpr) { - location = ast.SkipParentheses(condExpr.AsBinaryExpression().Right) - } - if ast.IsModuleExportsAccessExpression(location) { - return - } - if ast.IsLogicalOrCoalescingBinaryExpression(location) { - c.checkTestingKnownTruthyTypes(location, condType, body) - return - } - t := condType - if location != condExpr { - t = c.checkExpression(location) - } - if t.flags&TypeFlagsEnumLiteral != 0 && ast.IsPropertyAccessExpression(location) && core.OrElse(c.getResolvedSymbolOrNil(location.Expression()), c.unknownSymbol).Flags&ast.SymbolFlagsEnum != 0 { - // EnumLiteral type at condition with known value is always truthy or always falsy, likely an error - c.error(location, diagnostics.This_condition_will_always_return_0, core.IfElse(evaluator.IsTruthy(t.AsLiteralType().value), "true", "false")) - return - } - isPropertyExpressionCast := ast.IsPropertyAccessExpression(location) && isTypeAssertion(location.Expression()) - if !c.hasTypeFacts(t, TypeFactsTruthy) || isPropertyExpressionCast { - return - } - // While it technically should be invalid for any known-truthy value - // to be tested, we de-scope to functions and Promises unreferenced in - // the block as a heuristic to identify the most common bugs. There - // are too many false positives for values sourced from type - // definitions without strictNullChecks otherwise. - callSignatures := c.getSignaturesOfType(t, SignatureKindCall) - isPromise := c.getAwaitedTypeOfPromise(t) != nil - if len(callSignatures) == 0 && !isPromise { - return - } - var testedNode *ast.Node - switch { - case ast.IsIdentifier(location): - testedNode = location - case ast.IsPropertyAccessExpression(location): - testedNode = location.Name() - } - var testedSymbol *ast.Symbol - if testedNode != nil { - testedSymbol = c.getSymbolAtLocation(testedNode, false) - } - if testedSymbol == nil && !isPromise { - return - } - isUsed := testedSymbol != nil && ast.IsBinaryExpression(condExpr.Parent) && c.isSymbolUsedInBinaryExpressionChain(condExpr.Parent, testedSymbol) || - testedSymbol != nil && body != nil && c.isSymbolUsedInConditionBody(condExpr, body, testedNode, testedSymbol) - if !isUsed { - if isPromise { - c.errorAndMaybeSuggestAwait(location, true, diagnostics.This_condition_will_always_return_true_since_this_0_is_always_defined, c.getTypeNameForErrorDisplay(t)) - } else { - c.error(location, diagnostics.This_condition_will_always_return_true_since_this_function_is_always_defined_Did_you_mean_to_call_it_instead) - } - } -} - -func (c *Checker) isSymbolUsedInBinaryExpressionChain(node *ast.Node, testedSymbol *ast.Symbol) bool { - var visit func(*ast.Node) bool - visit = func(child *ast.Node) bool { - if ast.IsIdentifier(child) { - symbol := c.getSymbolAtLocation(child, false) - if symbol != nil && symbol == testedSymbol { - return true - } - } - return child.ForEachChild(visit) - } - for ast.IsBinaryExpression(node) && node.AsBinaryExpression().OperatorToken.Kind == ast.KindAmpersandAmpersandToken { - isUsed := node.AsBinaryExpression().Right.ForEachChild(visit) - if isUsed { - return true - } - node = node.Parent - } - return false -} - -func (c *Checker) isSymbolUsedInConditionBody(expr *ast.Node, body *ast.Node, testedNode *ast.Node, testedSymbol *ast.Symbol) bool { - var visit func(*ast.Node) bool - visit = func(childNode *ast.Node) bool { - if ast.IsIdentifier(childNode) { - childSymbol := c.getSymbolAtLocation(childNode, false) - if childSymbol != nil && childSymbol == testedSymbol { - // If the test was a simple identifier, the above check is sufficient - if ast.IsIdentifier(expr) || ast.IsIdentifier(testedNode) && ast.IsBinaryExpression(testedNode.Parent) { - return true - } - // Otherwise we need to ensure the symbol is called on the same target - testedExpression := testedNode.Parent - childExpression := childNode.Parent - for testedExpression != nil && childExpression != nil { - if ast.IsIdentifier(testedExpression) && ast.IsIdentifier(childExpression) || testedExpression.Kind == ast.KindThisKeyword && childExpression.Kind == ast.KindThisKeyword { - return c.getSymbolAtLocation(testedExpression, false) == c.getSymbolAtLocation(childExpression, false) - } else if ast.IsPropertyAccessExpression(testedExpression) && ast.IsPropertyAccessExpression(childExpression) { - if c.getSymbolAtLocation(testedExpression.Name(), false) != c.getSymbolAtLocation(childExpression.Name(), false) { - return false - } - childExpression = childExpression.Expression() - testedExpression = testedExpression.Expression() - } else if ast.IsCallExpression(testedExpression) && ast.IsCallExpression(childExpression) { - childExpression = childExpression.Expression() - testedExpression = testedExpression.Expression() - } else { - return false - } - } - } - } - return childNode.ForEachChild(visit) - } - return body.ForEachChild(visit) -} - -func (c *Checker) checkDoStatement(node *ast.Node) { - c.checkGrammarStatementInAmbientContext(node) - c.checkSourceElement(node.AsDoStatement().Statement) - c.checkTruthinessExpression(node.Expression(), CheckModeNormal) -} - -func (c *Checker) checkWhileStatement(node *ast.Node) { - c.checkGrammarStatementInAmbientContext(node) - c.checkTruthinessExpression(node.Expression(), CheckModeNormal) - c.checkSourceElement(node.AsWhileStatement().Statement) -} - -func (c *Checker) checkForStatement(node *ast.Node) { - if !c.checkGrammarStatementInAmbientContext(node) { - if init := node.Initializer(); init != nil && init.Kind == ast.KindVariableDeclarationList { - c.checkGrammarVariableDeclarationList(init.AsVariableDeclarationList()) - } - } - data := node.AsForStatement() - if data.Initializer != nil { - if ast.IsVariableDeclarationList(data.Initializer) { - c.checkVariableDeclarationList(data.Initializer) - } else { - c.checkExpression(data.Initializer) - } - } - if data.Condition != nil { - c.checkTruthinessExpression(data.Condition, CheckModeNormal) - } - if data.Incrementor != nil { - c.checkExpression(data.Incrementor) - } - c.checkSourceElement(data.Statement) - if node.Locals() != nil { - c.registerForUnusedIdentifiersCheck(node) - } -} - -func (c *Checker) checkForInStatement(node *ast.Node) { - data := node.AsForInOrOfStatement() - c.checkGrammarForInOrForOfStatement(data) - rightType := c.getNonNullableTypeIfNeeded(c.checkExpression(data.Expression)) - // TypeScript 1.0 spec (April 2014): 5.4 - // In a 'for-in' statement of the form - // for (let VarDecl in Expr) Statement - // VarDecl must be a variable declaration without a type annotation that declares a variable of type Any, - // and Expr must be an expression of type Any, an object type, or a type parameter type. - if ast.IsVariableDeclarationList(data.Initializer) { - declarations := data.Initializer.AsVariableDeclarationList().Declarations.Nodes - if len(declarations) != 0 && ast.IsBindingPattern(declarations[0].Name()) { - c.error(declarations[0].Name(), diagnostics.The_left_hand_side_of_a_for_in_statement_cannot_be_a_destructuring_pattern) - } - c.checkVariableDeclarationList(data.Initializer) - } else { - // In a 'for-in' statement of the form - // for (Var in Expr) Statement - // Var must be an expression classified as a reference of type Any or the String primitive type, - // and Expr must be an expression of type Any, an object type, or a type parameter type. - varExpr := data.Initializer - leftType := c.checkExpression(varExpr) - if ast.IsArrayLiteralExpression(varExpr) || ast.IsObjectLiteralExpression(varExpr) { - c.error(varExpr, diagnostics.The_left_hand_side_of_a_for_in_statement_cannot_be_a_destructuring_pattern) - } else if !c.isTypeAssignableTo(c.getIndexTypeOrString(rightType), leftType) { - c.error(varExpr, diagnostics.The_left_hand_side_of_a_for_in_statement_must_be_of_type_string_or_any) - } else { - // run check only former check succeeded to avoid cascading errors - c.checkReferenceExpression(varExpr, diagnostics.The_left_hand_side_of_a_for_in_statement_must_be_a_variable_or_a_property_access, diagnostics.The_left_hand_side_of_a_for_in_statement_may_not_be_an_optional_property_access) - } - } - // unknownType is returned i.e. if node.expression is identifier whose name cannot be resolved - // in this case error about missing name is already reported - do not report extra one - if rightType == c.neverType || !c.isTypeAssignableToKind(rightType, TypeFlagsNonPrimitive|TypeFlagsInstantiableNonPrimitive) { - c.error(data.Expression, diagnostics.The_right_hand_side_of_a_for_in_statement_must_be_of_type_any_an_object_type_or_a_type_parameter_but_here_has_type_0, c.TypeToString(rightType)) - } - c.checkSourceElement(data.Statement) - if node.Locals() != nil { - c.registerForUnusedIdentifiersCheck(node) - } -} - -func (c *Checker) getIndexTypeOrString(t *Type) *Type { - indexType := c.getExtractStringType(c.getIndexType(t)) - return core.IfElse(indexType.flags&TypeFlagsNever != 0, c.stringType, indexType) -} - -func (c *Checker) checkForOfStatement(node *ast.Node) { - data := node.AsForInOrOfStatement() - c.checkGrammarForInOrForOfStatement(data) - container := getContainingFunctionOrClassStaticBlock(node) - if data.AwaitModifier != nil { - if container != nil && ast.IsClassStaticBlockDeclaration(container) { - c.grammarErrorOnNode(data.AwaitModifier, diagnostics.X_for_await_loops_cannot_be_used_inside_a_class_static_block) - } - } // Check the LHS and RHS - // If the LHS is a declaration, just check it as a variable declaration, which will in turn check the RHS - // via checkRightHandSideOfForOf. - // If the LHS is an expression, check the LHS, as a destructuring assignment or as a reference. - // Then check that the RHS is assignable to it. - if ast.IsVariableDeclarationList(data.Initializer) { - c.checkVariableDeclarationList(data.Initializer) - } else { - varExpr := data.Initializer - iteratedType := c.checkRightHandSideOfForOf(node) - // There may be a destructuring assignment on the left side - if ast.IsArrayLiteralExpression(varExpr) || ast.IsObjectLiteralExpression(varExpr) { - // iteratedType may be undefined. In this case, we still want to check the structure of - // varExpr, in particular making sure it's a valid LeftHandSideExpression. But we'd like - // to short circuit the type relation checking as much as possible, so we pass the unknownType. - c.checkDestructuringAssignment(varExpr, core.OrElse(iteratedType, c.errorType), CheckModeNormal, false) - } else { - leftType := c.checkExpression(varExpr) - c.checkReferenceExpression(varExpr, diagnostics.The_left_hand_side_of_a_for_of_statement_must_be_a_variable_or_a_property_access, diagnostics.The_left_hand_side_of_a_for_of_statement_may_not_be_an_optional_property_access) - // iteratedType will be undefined if the rightType 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. - if iteratedType != nil { - c.checkTypeAssignableToAndOptionallyElaborate(iteratedType, leftType, varExpr, data.Expression, nil, nil) - } - } - } - c.checkSourceElement(data.Statement) - if node.Locals() != nil { - c.registerForUnusedIdentifiersCheck(node) - } -} - -func (c *Checker) checkBreakOrContinueStatement(node *ast.Node) { - if !c.checkGrammarStatementInAmbientContext(node) { - c.checkGrammarBreakOrContinueStatement(node) - } -} - -func (c *Checker) checkReturnStatement(node *ast.Node) { - if c.checkGrammarStatementInAmbientContext(node) { - return - } - container := getContainingFunctionOrClassStaticBlock(node) - if container != nil && ast.IsClassStaticBlockDeclaration(container) { - c.grammarErrorOnFirstToken(node, diagnostics.A_return_statement_cannot_be_used_inside_a_class_static_block) - return - } - if container == nil { - c.grammarErrorOnFirstToken(node, diagnostics.A_return_statement_can_only_be_used_within_a_function_body) - return - } - signature := c.getSignatureFromDeclaration(container) - returnType := c.getReturnTypeOfSignature(signature) - functionFlags := getFunctionFlags(container) - exprNode := node.Expression() - if c.strictNullChecks || exprNode != nil || returnType.flags&TypeFlagsNever != 0 { - exprType := c.undefinedType - if exprNode != nil { - exprType = c.checkExpressionCached(exprNode) - } - if ast.IsSetAccessorDeclaration(container) { - if exprNode != nil { - c.error(node, diagnostics.Setters_cannot_return_a_value) - } - } else if ast.IsConstructorDeclaration(container) { - if exprNode != nil && !c.checkTypeAssignableToAndOptionallyElaborate(exprType, returnType, node, exprNode, nil, nil) { - c.error(node, diagnostics.Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class) - } - } else if c.getReturnTypeFromAnnotation(container) != nil { - unwrappedReturnType := core.OrElse(c.unwrapReturnType(returnType, functionFlags), returnType) - c.checkReturnExpression(container, unwrappedReturnType, node, node.Expression(), exprType, false) - } - } else if !ast.IsConstructorDeclaration(container) && c.compilerOptions.NoImplicitReturns.IsTrue() && !c.isUnwrappedReturnTypeUndefinedVoidOrAny(container, returnType) { - // The function has a return type, but the return statement doesn't have an expression. - c.error(node, diagnostics.Not_all_code_paths_return_a_value) - } -} - -// When checking an arrow expression such as `(x) => exp`, then `node` is the expression `exp`. -// Otherwise, `node` is a return statement. -func (c *Checker) checkReturnExpression(container *ast.Node, unwrappedReturnType *Type, node *ast.Node, expr *ast.Node, exprType *Type, inConditionalExpression bool) { - unwrappedExprType := exprType - functionFlags := getFunctionFlags(container) - if expr != nil { - unwrappedExpr := ast.SkipParentheses(expr) - if ast.IsConditionalExpression(unwrappedExpr) { - whenTrue := unwrappedExpr.AsConditionalExpression().WhenTrue - whenFalse := unwrappedExpr.AsConditionalExpression().WhenFalse - c.checkReturnExpression(container, unwrappedReturnType, node, whenTrue, c.checkExpression(whenTrue), true /*inConditionalExpression*/) - c.checkReturnExpression(container, unwrappedReturnType, node, whenFalse, c.checkExpression(whenFalse), true /*inConditionalExpression*/) - return - } - } - inReturnStatement := node.Kind == ast.KindReturnStatement - if functionFlags&FunctionFlagsAsync != 0 { - unwrappedExprType = c.checkAwaitedType(exprType, false /*withAlias*/, node, diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member) - } - effectiveExpr := expr // The effective expression for diagnostics purposes. - if expr != nil { - effectiveExpr = c.getEffectiveCheckNode(expr) - } - errorNode := core.IfElse(inReturnStatement && !inConditionalExpression, node, effectiveExpr) - c.checkTypeAssignableToAndOptionallyElaborate(unwrappedExprType, unwrappedReturnType, errorNode, effectiveExpr, nil, nil) -} - -func (c *Checker) checkWithStatement(node *ast.Node) { - if !c.checkGrammarStatementInAmbientContext(node) { - if node.Flags&ast.NodeFlagsAwaitContext != 0 { - c.grammarErrorOnFirstToken(node, diagnostics.X_with_statements_are_not_allowed_in_an_async_function_block) - } - } - c.checkExpression(node.Expression()) - sourceFile := ast.GetSourceFileOfNode(node) - if !c.hasParseDiagnostics(sourceFile) { - start := scanner.GetRangeOfTokenAtPosition(sourceFile, node.Pos()).Pos() - end := node.AsWithStatement().Statement.Pos() - c.grammarErrorAtPos(sourceFile.AsNode(), start, end-start, diagnostics.The_with_statement_is_not_supported_All_symbols_in_a_with_block_will_have_type_any) - } -} - -func (c *Checker) checkSwitchStatement(node *ast.Node) { - // Grammar checking - c.checkGrammarStatementInAmbientContext(node) - var firstDefaultClause *ast.Node - hasDuplicateDefaultClause := false - expressionType := c.checkExpression(node.Expression()) - caseBlock := node.AsSwitchStatement().CaseBlock - for _, clause := range caseBlock.AsCaseBlock().Clauses.Nodes { - // Grammar check for duplicate default clauses, skip if we already report duplicate default clause - if ast.IsDefaultClause(clause) && !hasDuplicateDefaultClause { - if firstDefaultClause == nil { - firstDefaultClause = clause - } else { - c.grammarErrorOnNode(clause, diagnostics.A_default_clause_cannot_appear_more_than_once_in_a_switch_statement) - hasDuplicateDefaultClause = true - } - } - if ast.IsCaseClause(clause) { - caseType := c.checkExpression(clause.Expression()) - if !c.isTypeEqualityComparableTo(expressionType, caseType) { - // expressionType is not comparable to caseType, try the reversed check and report errors if it fails - c.checkTypeComparableTo(caseType, expressionType, clause.Expression(), nil /*headMessage*/) - } - } - c.checkSourceElements(clause.AsCaseOrDefaultClause().Statements.Nodes) - if c.compilerOptions.NoFallthroughCasesInSwitch.IsTrue() { - if flowNode := clause.AsCaseOrDefaultClause().FallthroughFlowNode; flowNode != nil && c.isReachableFlowNode(flowNode) { - c.error(clause, diagnostics.Fallthrough_case_in_switch) - } - } - } - if caseBlock.Locals() != nil { - c.registerForUnusedIdentifiersCheck(caseBlock) - } -} - -func (c *Checker) checkLabeledStatement(node *ast.Node) { - labeledStatement := node.AsLabeledStatement() - labelNode := labeledStatement.Label - labelText := labelNode.Text() - if !c.checkGrammarStatementInAmbientContext(node) { - for current := node.Parent; current != nil && !ast.IsFunctionLike(current); current = current.Parent { - if ast.IsLabeledStatement(current) && current.AsLabeledStatement().Label.Text() == labelText { - c.grammarErrorOnNode(labelNode, diagnostics.Duplicate_label_0, labelText) - break - } - } - } - c.checkSourceElement(labeledStatement.Statement) -} - -func (c *Checker) checkThrowStatement(node *ast.Node) { - throwExpr := node.AsThrowStatement().Expression - if !c.checkGrammarStatementInAmbientContext(node) { - if ast.IsIdentifier(throwExpr) && len(throwExpr.Text()) == 0 { - c.grammarErrorAtPos(node, throwExpr.Pos(), 0 /*length*/, diagnostics.Line_break_not_permitted_here) - } - } - c.checkExpression(throwExpr) -} - -func (c *Checker) checkTryStatement(node *ast.Node) { - c.checkGrammarStatementInAmbientContext(node) - data := node.AsTryStatement() - c.checkBlock(data.TryBlock) - if data.CatchClause != nil { - c.checkCatchClause(data.CatchClause) - } - if data.FinallyBlock != nil { - c.checkBlock(data.FinallyBlock) - } -} - -func (c *Checker) checkCatchClause(node *ast.Node) { - declaration := node.AsCatchClause().VariableDeclaration - if declaration != nil { - c.checkVariableLikeDeclaration(declaration) - typeNode := declaration.Type() - if typeNode != nil { - t := c.getTypeFromTypeNode(typeNode) - if t != nil && t.flags&TypeFlagsAnyOrUnknown == 0 { - c.grammarErrorOnFirstToken(typeNode, diagnostics.Catch_clause_variable_type_annotation_must_be_any_or_unknown_if_specified) - } - } else if declaration.Initializer() != nil { - c.grammarErrorOnFirstToken(declaration.Initializer(), diagnostics.Catch_clause_variable_cannot_have_an_initializer) - } else { - blockLocals := node.AsCatchClause().Block.Locals() - if blockLocals != nil { - for caughtName := range node.Locals() { - if blockLocal := blockLocals[caughtName]; blockLocal != nil && blockLocal.ValueDeclaration != nil && blockLocal.Flags&ast.SymbolFlagsBlockScopedVariable != 0 { - c.grammarErrorOnNode(blockLocal.ValueDeclaration, diagnostics.Cannot_redeclare_identifier_0_in_catch_clause, caughtName) - } - } - } - } - } - c.checkBlock(node.AsCatchClause().Block) -} - -func (c *Checker) checkBindingElement(node *ast.Node) { - c.checkGrammarBindingElement(node.AsBindingElement()) - c.checkVariableLikeDeclaration(node) -} - -func (c *Checker) checkClassDeclaration(node *ast.Node) { - firstDecorator := core.Find(node.ModifierNodes(), ast.IsDecorator) - if c.legacyDecorators && firstDecorator != nil && core.Some(node.Members(), func(p *ast.Node) bool { - return ast.HasStaticModifier(p) && ast.IsPrivateIdentifierClassElementDeclaration(p) - }) { - c.grammarErrorOnNode(firstDecorator, diagnostics.Class_decorators_can_t_be_used_with_static_private_identifier_Consider_removing_the_experimental_decorator) - } - if node.Name() == nil && !ast.HasSyntacticModifier(node, ast.ModifierFlagsDefault) { - c.grammarErrorOnFirstToken(node, diagnostics.A_class_declaration_without_the_default_modifier_must_have_a_name) - } - c.checkClassLikeDeclaration(node) - c.checkSourceElements(node.Members()) - c.registerForUnusedIdentifiersCheck(node) -} - -func (c *Checker) checkClassLikeDeclaration(node *ast.Node) { - c.checkGrammarClassLikeDeclaration(node) - c.checkDecorators(node) - c.checkCollisionsForDeclarationName(node, node.Name()) - c.checkTypeParameters(node.TypeParameters()) - c.checkExportsOnMergedDeclarations(node) - symbol := c.getSymbolOfDeclaration(node) - classType := c.getDeclaredTypeOfSymbol(symbol) - classTypeData := classType.AsInterfaceType() - typeWithThis := c.getTypeWithThisArgument(classType, nil, false) - staticType := c.getTypeOfSymbol(symbol) - c.checkTypeParameterListsIdentical(symbol) - c.checkFunctionOrConstructorSymbol(symbol) - c.checkObjectTypeForDuplicateDeclarations(node, true /*checkPrivateNames*/) - baseTypeNode := ast.GetExtendsHeritageClauseElement(node) - if baseTypeNode != nil { - c.checkSourceElements(baseTypeNode.TypeArguments()) - baseTypes := c.getBaseTypes(classType) - if len(baseTypes) != 0 { - baseType := baseTypes[0] - baseConstructorType := c.getBaseConstructorTypeOfClass(classType) - staticBaseType := c.getApparentType(baseConstructorType) - c.checkBaseTypeAccessibility(staticBaseType, baseTypeNode) - c.checkSourceElement(baseTypeNode.Expression()) - if len(baseTypeNode.TypeArguments()) != 0 { - c.checkSourceElements(baseTypeNode.TypeArguments()) - for _, constructor := range c.getConstructorsForTypeArguments(staticBaseType, baseTypeNode.TypeArguments(), baseTypeNode) { - if !c.checkTypeArgumentConstraints(baseTypeNode, constructor.typeParameters) { - break - } - } - } - baseWithThis := c.getTypeWithThisArgument(baseType, classTypeData.thisType, false) - if !c.checkTypeAssignableTo(typeWithThis, baseWithThis, nil, nil) { - c.issueMemberSpecificError(node, typeWithThis, baseWithThis, diagnostics.Class_0_incorrectly_extends_base_class_1) - } else { - // Report static side error only when instance type is assignable - c.checkTypeAssignableTo(staticType, c.getTypeWithoutSignatures(staticBaseType), core.OrElse(node.Name(), node), diagnostics.Class_static_side_0_incorrectly_extends_base_class_static_side_1) - } - if baseConstructorType.flags&TypeFlagsTypeVariable != 0 { - if !c.isMixinConstructorType(staticType) { - c.error(core.OrElse(node.Name(), node), diagnostics.A_mixin_class_must_have_a_constructor_with_a_single_rest_parameter_of_type_any) - } else { - constructSignatures := c.getSignaturesOfType(baseConstructorType, SignatureKindConstruct) - if core.Some(constructSignatures, func(signature *Signature) bool { - return signature.flags&SignatureFlagsAbstract != 0 - }) && !ast.HasSyntacticModifier(node, ast.ModifierFlagsAbstract) { - c.error(core.OrElse(node.Name(), node), diagnostics.A_mixin_class_that_extends_from_a_type_variable_containing_an_abstract_construct_signature_must_also_be_declared_abstract) - } - } - } - if !(staticBaseType.symbol != nil && staticBaseType.symbol.Flags&ast.SymbolFlagsClass != 0) && baseConstructorType.flags&TypeFlagsTypeVariable == 0 { - // When the static base type is a "class-like" constructor function (but not actually a class), we verify - // that all instantiated base constructor signatures return the same type. - constructors := c.getInstantiatedConstructorsForTypeArguments(staticBaseType, baseTypeNode.TypeArguments(), baseTypeNode) - if !core.Every(constructors, func(sig *Signature) bool { - return c.isTypeIdenticalTo(c.getReturnTypeOfSignature(sig), baseType) - }) { - c.error(baseTypeNode.Expression(), diagnostics.Base_constructors_must_all_have_the_same_return_type) - } - } - c.checkKindsOfPropertyMemberOverrides(classType, baseType) - } - } - c.checkMembersForOverrideModifier(node, classType, typeWithThis, staticType) - implementedTypeNodes := ast.GetImplementsHeritageClauseElements(node) - for _, typeRefNode := range implementedTypeNodes { - expr := typeRefNode.Expression() - if !ast.IsEntityNameExpression(expr) || ast.IsOptionalChain(expr) { - c.error(expr, diagnostics.A_class_can_only_implement_an_identifier_Slashqualified_name_with_optional_type_arguments) - } - c.checkTypeReferenceNode(typeRefNode) - t := c.getReducedType(c.getTypeFromTypeNode(typeRefNode)) - if !c.isErrorType(t) { - if c.isValidBaseType(t) { - genericDiag := core.IfElse(t.symbol != nil && t.symbol.Flags&ast.SymbolFlagsClass != 0, - diagnostics.Class_0_incorrectly_implements_class_1_Did_you_mean_to_extend_1_and_inherit_its_members_as_a_subclass, - diagnostics.Class_0_incorrectly_implements_interface_1) - baseWithThis := c.getTypeWithThisArgument(t, classType.AsInterfaceType().thisType, false) - if !c.checkTypeAssignableTo(typeWithThis, baseWithThis, nil, nil) { - c.issueMemberSpecificError(node, typeWithThis, baseWithThis, genericDiag) - } - } else { - c.error(typeRefNode, diagnostics.A_class_can_only_implement_an_object_type_or_intersection_of_object_types_with_statically_known_members) - } - } - } - c.checkIndexConstraints(classType, symbol, false /*isStaticIndex*/) - c.checkIndexConstraints(staticType, symbol, true /*isStaticIndex*/) - c.checkTypeForDuplicateIndexSignatures(node) - c.checkPropertyInitialization(node) -} - -// Check that type parameter lists are identical across multiple declarations -func (c *Checker) checkTypeParameterListsIdentical(symbol *ast.Symbol) { - if len(symbol.Declarations) == 1 { - return - } - links := c.declaredTypeLinks.Get(symbol) - if !links.typeParametersChecked { - links.typeParametersChecked = true - declarations := c.getClassOrInterfaceDeclarationsOfSymbol(symbol) - if len(declarations) <= 1 { - return - } - t := c.getDeclaredTypeOfSymbol(symbol) - if !c.areTypeParametersIdentical(declarations, t.AsInterfaceType().LocalTypeParameters(), (*ast.Node).TypeParameters) { - // Report an error on every conflicting declaration. - name := c.symbolToString(symbol) - for _, declaration := range declarations { - c.error(declaration.Name(), diagnostics.All_declarations_of_0_must_have_identical_type_parameters, name) - } - } - } -} - -func (c *Checker) getClassOrInterfaceDeclarationsOfSymbol(symbol *ast.Symbol) []*ast.Node { - return core.Filter(symbol.Declarations, func(d *ast.Node) bool { - return ast.IsClassDeclaration(d) || ast.IsInterfaceDeclaration(d) - }) -} - -func (c *Checker) areTypeParametersIdentical(declarations []*ast.Node, targetParameters []*Type, getTypeParameterDeclarations func(node *ast.Node) []*ast.Node) bool { - maxTypeArgumentCount := len(targetParameters) - minTypeArgumentCount := c.getMinTypeArgumentCount(targetParameters) - for _, declaration := range declarations { - // If this declaration has too few or too many type parameters, we report an error - sourceParameters := getTypeParameterDeclarations(declaration) - if len(sourceParameters) < minTypeArgumentCount || len(sourceParameters) > maxTypeArgumentCount { - return false - } - for i, source := range sourceParameters { - target := targetParameters[i] - // If the type parameter node does not have the same name as the resolved type - // parameter at this position, we report an error. - if source.Name().Text() != target.symbol.Name { - return false - } - // If the type parameter node does not have an identical constraintNode as the resolved - // type parameter at this position, we report an error. - constraintNode := source.AsTypeParameter().Constraint - targetConstraint := c.getConstraintOfTypeParameter(target) - // relax check if later interface augmentation has no constraint, it's more broad and is OK to merge with - // a more constrained interface (this could be generalized to a full hierarchy check, but that's maybe overkill) - if constraintNode != nil && targetConstraint != nil && !c.isTypeIdenticalTo(c.getTypeFromTypeNode(constraintNode), targetConstraint) { - return false - } - // If the type parameter node has a default and it is not identical to the default - // for the type parameter at this position, we report an error. - defaultNode := source.AsTypeParameter().DefaultType - targetDefault := c.getDefaultFromTypeParameter(target) - if defaultNode != nil && targetDefault != nil && !c.isTypeIdenticalTo(c.getTypeFromTypeNode(defaultNode), targetDefault) { - return false - } - } - } - return true -} - -func (c *Checker) checkBaseTypeAccessibility(t *Type, node *ast.Node) { - signatures := c.getSignaturesOfType(t, SignatureKindConstruct) - if len(signatures) != 0 { - declaration := signatures[0].declaration - if declaration != nil && ast.HasModifier(declaration, ast.ModifierFlagsPrivate) { - typeClassDeclaration := ast.GetClassLikeDeclarationOfSymbol(t.symbol) - if !c.isNodeWithinClass(node, typeClassDeclaration) { - c.error(node, diagnostics.Cannot_extend_a_class_0_Class_constructor_is_marked_as_private, c.getFullyQualifiedName(t.symbol, nil)) - } - } - } -} - -func (c *Checker) issueMemberSpecificError(node *ast.Node, typeWithThis *Type, baseWithThis *Type, broadDiag *diagnostics.Message) { - // iterate over all implemented properties and issue errors on each one which isn't compatible, rather than the class as a whole, if possible - issuedMemberError := false - for _, member := range node.Members() { - if ast.IsStatic(member) { - continue - } - declaredProp := member.Symbol() - if declaredProp != nil && declaredProp.Name != ast.InternalSymbolNameComputed { - prop := c.getPropertyOfType(typeWithThis, declaredProp.Name) - baseProp := c.getPropertyOfType(baseWithThis, declaredProp.Name) - if prop != nil && baseProp != nil { - var diags []*ast.Diagnostic - if !c.checkTypeAssignableToEx(c.getTypeOfSymbol(prop), c.getTypeOfSymbol(baseProp), core.OrElse(member.Name(), member), nil /*headMessage*/, &diags) { - c.diagnostics.Add(ast.NewDiagnosticChain(diags[0], diagnostics.Property_0_in_type_1_is_not_assignable_to_the_same_property_in_base_type_2, c.symbolToString(declaredProp), c.TypeToString(typeWithThis), c.TypeToString(baseWithThis))) - issuedMemberError = true - } - } - } - } - if !issuedMemberError { - // check again with diagnostics to generate a less-specific error - c.checkTypeAssignableTo(typeWithThis, baseWithThis, core.OrElse(node.Name(), node), broadDiag) - } -} - -func (c *Checker) getTypeWithoutSignatures(t *Type) *Type { - switch { - case t.flags&TypeFlagsObject != 0: - resolved := c.resolveStructuredTypeMembers(t) - if len(resolved.signatures) != 0 { - result := c.newObjectType(ObjectFlagsAnonymous, t.symbol) - result.objectFlags |= ObjectFlagsMembersResolved - result.AsObjectType().members = resolved.members - result.AsObjectType().properties = resolved.properties - return result - } - case t.flags&TypeFlagsIntersection != 0: - return c.getIntersectionType(core.Map(t.AsIntersectionType().types, c.getTypeWithoutSignatures)) - } - return t -} - -func (c *Checker) checkKindsOfPropertyMemberOverrides(t *Type, baseType *Type) { - // TypeScript 1.0 spec (April 2014): 8.2.3 - // A derived class inherits all members from its base class it doesn't override. - // Inheritance means that a derived class implicitly contains all non - overridden members of the base class. - // Both public and private property members are inherited, but only public property members can be overridden. - // A property member in a derived class is said to override a property member in a base class - // when the derived class property member has the same name and kind(instance or static) - // as the base class property member. - // The type of an overriding property member must be assignable(section 3.8.4) - // to the type of the overridden property member, or otherwise a compile - time error occurs. - // Base class instance member functions can be overridden by derived class instance member functions, - // but not by other kinds of members. - // Base class instance member variables and accessors can be overridden by - // derived class instance member variables and accessors, but not by other kinds of members. - // NOTE: assignability is checked in checkClassDeclaration - type MemberInfo struct { - missedProperties []string - baseTypeName string - typeName string - } - var notImplementedInfo map[*ast.Node]MemberInfo -basePropertyCheck: - for _, baseProperty := range c.getPropertiesOfType(baseType) { - base := c.getTargetSymbol(baseProperty) - if base.Flags&ast.SymbolFlagsPrototype != 0 { - continue - } - baseSymbol := c.getPropertyOfObjectType(t, base.Name) - if baseSymbol == nil { - continue - } - derived := c.getTargetSymbol(baseSymbol) - baseDeclarationFlags := getDeclarationModifierFlagsFromSymbol(base) - // In order to resolve whether the inherited method was overridden in the base class or not, - // we compare the Symbols obtained. Since getTargetSymbol returns the symbol on the *uninstantiated* - // type declaration, derived and base resolve to the same symbol even in the case of generic classes. - if derived == base { - // derived class inherits base without override/redeclaration. - if baseDeclarationFlags&ast.ModifierFlagsAbstract != 0 { - // It is an error to inherit an abstract member without implementing it or being declared abstract. - // If there is no declaration for the derived class (as in the case of class expressions), - // then the class cannot be declared abstract. - derivedClassDecl := ast.GetClassLikeDeclarationOfSymbol(t.symbol) - if derivedClassDecl == nil || !ast.HasSyntacticModifier(derivedClassDecl, ast.ModifierFlagsAbstract) { - // Searches other base types for a declaration that would satisfy the inherited abstract member. - // (The class may have more than one base type via declaration merging with an interface with the - // same name.) - for _, otherBaseType := range c.getBaseTypes(t) { - if otherBaseType == baseType { - continue - } - if baseSymbol := c.getPropertyOfObjectType(otherBaseType, base.Name); baseSymbol != nil && base != c.getTargetSymbol(baseSymbol) { - // Derived property exists elsewhere. - continue basePropertyCheck - } - } - baseTypeName := c.TypeToString(baseType) - typeName := c.TypeToString(t) - missedProperties := append(notImplementedInfo[derivedClassDecl].missedProperties, c.symbolToString(baseProperty)) - if notImplementedInfo == nil { - notImplementedInfo = make(map[*ast.Node]MemberInfo) - } - notImplementedInfo[derivedClassDecl] = MemberInfo{ - baseTypeName: baseTypeName, - typeName: typeName, - missedProperties: missedProperties, - } - } - } - } else { - // derived overrides base. - derivedDeclarationFlags := getDeclarationModifierFlagsFromSymbol(derived) - if baseDeclarationFlags&ast.ModifierFlagsPrivate != 0 || derivedDeclarationFlags&ast.ModifierFlagsPrivate != 0 { - // either base or derived property is private - not override, skip it - continue - } - var errorMessage *diagnostics.Message - basePropertyFlags := base.Flags & ast.SymbolFlagsPropertyOrAccessor - derivedPropertyFlags := derived.Flags & ast.SymbolFlagsPropertyOrAccessor - if basePropertyFlags != 0 && derivedPropertyFlags != 0 { - // property/accessor is overridden with property/accessor - if base.CheckFlags&ast.CheckFlagsMapped != 0 || - derived.ValueDeclaration != nil && ast.IsBinaryExpression(derived.ValueDeclaration) || - c.arePropertiesAbstractOrInterface(base, baseDeclarationFlags) { - // when the base property is abstract or from an interface, base/derived flags don't need to match - // for intersection properties, this must be true of *any* of the declarations, for others it must be true of *all* - // same when the derived property is from an assignment - continue - } - overriddenInstanceProperty := basePropertyFlags != ast.SymbolFlagsProperty && derivedPropertyFlags == ast.SymbolFlagsProperty - overriddenInstanceAccessor := basePropertyFlags == ast.SymbolFlagsProperty && derivedPropertyFlags != ast.SymbolFlagsProperty - if overriddenInstanceProperty || overriddenInstanceAccessor { - errorMessage := core.IfElse(overriddenInstanceProperty, - diagnostics.X_0_is_defined_as_an_accessor_in_class_1_but_is_overridden_here_in_2_as_an_instance_property, - diagnostics.X_0_is_defined_as_a_property_in_class_1_but_is_overridden_here_in_2_as_an_accessor) - c.error(core.OrElse(ast.GetNameOfDeclaration(derived.ValueDeclaration), derived.ValueDeclaration), errorMessage, c.symbolToString(base), c.TypeToString(baseType), c.TypeToString(t)) - } else if c.compilerOptions.UseDefineForClassFields.IsTrue() { - uninitialized := core.Find(derived.Declarations, func(d *ast.Node) bool { - return ast.IsPropertyDeclaration(d) && d.Initializer() == nil - }) - if uninitialized != nil && derived.Flags&ast.SymbolFlagsTransient == 0 && baseDeclarationFlags&ast.ModifierFlagsAbstract == 0 && derivedDeclarationFlags&ast.ModifierFlagsAbstract == 0 && !core.Some(derived.Declarations, func(d *ast.Node) bool { - return d.Flags&ast.NodeFlagsAmbient != 0 - }) { - constructor := ast.FindConstructorDeclaration(ast.GetClassLikeDeclarationOfSymbol(t.symbol)) - propName := uninitialized.Name() - if isExclamationToken(uninitialized.AsPropertyDeclaration().PostfixToken) || constructor == nil || !ast.IsIdentifier(propName) || !c.strictNullChecks || !c.isPropertyInitializedInConstructor(propName, t, constructor) { - errorMessage := diagnostics.Property_0_will_overwrite_the_base_property_in_1_If_this_is_intentional_add_an_initializer_Otherwise_add_a_declare_modifier_or_remove_the_redundant_declaration - c.error(core.OrElse(ast.GetNameOfDeclaration(derived.ValueDeclaration), derived.ValueDeclaration), errorMessage, c.symbolToString(base), c.TypeToString(baseType)) - } - } - } - // correct case - continue - } else if isPrototypeProperty(base) { - if isPrototypeProperty(derived) || derived.Flags&ast.SymbolFlagsProperty != 0 { - // method is overridden with method or property -- correct case - continue - } else { - errorMessage = diagnostics.Class_0_defines_instance_member_function_1_but_extended_class_2_defines_it_as_instance_member_accessor - } - } else if base.Flags&ast.SymbolFlagsAccessor != 0 { - errorMessage = diagnostics.Class_0_defines_instance_member_accessor_1_but_extended_class_2_defines_it_as_instance_member_function - } else { - errorMessage = diagnostics.Class_0_defines_instance_member_property_1_but_extended_class_2_defines_it_as_instance_member_function - } - c.error(core.OrElse(ast.GetNameOfDeclaration(derived.ValueDeclaration), derived.ValueDeclaration), errorMessage, c.TypeToString(baseType), c.symbolToString(base), c.TypeToString(t)) - } - } - for errorNode, memberInfo := range notImplementedInfo { - switch { - case len(memberInfo.missedProperties) == 1: - missedProperty := memberInfo.missedProperties[0] - if ast.IsClassExpression(errorNode) { - c.error(errorNode, diagnostics.Non_abstract_class_expression_does_not_implement_inherited_abstract_member_0_from_class_1, missedProperty, memberInfo.baseTypeName) - } else { - c.error(errorNode, diagnostics.Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_from_class_2, memberInfo.typeName, missedProperty, memberInfo.baseTypeName) - } - case len(memberInfo.missedProperties) > 5: - missedProperties := strings.Join(core.Map(memberInfo.missedProperties[:4], func(prop string) string { return "'" + prop + "'" }), ", ") - remainingMissedProperties := len(memberInfo.missedProperties) - 4 - if ast.IsClassExpression(errorNode) { - c.error(errorNode, diagnostics.Non_abstract_class_expression_is_missing_implementations_for_the_following_members_of_0_Colon_1_and_2_more, memberInfo.baseTypeName, missedProperties, remainingMissedProperties) - } else { - c.error(errorNode, diagnostics.Non_abstract_class_0_is_missing_implementations_for_the_following_members_of_1_Colon_2_and_3_more, memberInfo.typeName, memberInfo.baseTypeName, missedProperties, remainingMissedProperties) - } - default: - missedProperties := strings.Join(core.Map(memberInfo.missedProperties, func(prop string) string { return "'" + prop + "'" }), ", ") - if ast.IsClassExpression(errorNode) { - c.error(errorNode, diagnostics.Non_abstract_class_expression_is_missing_implementations_for_the_following_members_of_0_Colon_1, memberInfo.baseTypeName, missedProperties) - } else { - c.error(errorNode, diagnostics.Non_abstract_class_0_is_missing_implementations_for_the_following_members_of_1_Colon_2, memberInfo.typeName, memberInfo.baseTypeName, missedProperties) - } - } - } -} - -func (c *Checker) arePropertiesAbstractOrInterface(base *ast.Symbol, baseDeclarationFlags ast.ModifierFlags) bool { - if base.CheckFlags&ast.CheckFlagsSynthetic != 0 { - return core.Some(base.Declarations, func(d *ast.Node) bool { return c.isPropertyAbstractOrInterface(d, baseDeclarationFlags) }) - } - return core.Every(base.Declarations, func(d *ast.Node) bool { return c.isPropertyAbstractOrInterface(d, baseDeclarationFlags) }) -} - -func (c *Checker) isPropertyAbstractOrInterface(declaration *ast.Node, baseDeclarationFlags ast.ModifierFlags) bool { - return ast.IsInterfaceDeclaration(declaration.Parent) || - baseDeclarationFlags&ast.ModifierFlagsAbstract != 0 && (!ast.IsPropertyDeclaration(declaration) || declaration.Initializer() == nil) -} - -func (c *Checker) checkMembersForOverrideModifier(node *ast.Node, t *Type, typeWithThis *Type, staticType *Type) { - var baseWithThis *Type - baseTypeNode := ast.GetExtendsHeritageClauseElement(node) - if baseTypeNode != nil { - baseTypes := c.getBaseTypes(t) - if len(baseTypes) > 0 { - baseWithThis = c.getTypeWithThisArgument(core.FirstOrNil(baseTypes), t.AsInterfaceType().thisType, false) - } - } - baseStaticType := c.getBaseConstructorTypeOfClass(t) - for _, member := range node.Members() { - if !hasAmbientModifier(member) { - if ast.IsConstructorDeclaration(member) { - for _, param := range member.Parameters() { - if ast.IsParameterPropertyDeclaration(param, member) { - c.checkMemberForOverrideModifier(node, staticType, baseStaticType, baseWithThis, t, typeWithThis, param) - } - } - } else { - c.checkMemberForOverrideModifier(node, staticType, baseStaticType, baseWithThis, t, typeWithThis, member) - } - } - } -} - -func (c *Checker) checkMemberForOverrideModifier(node *ast.Node, staticType *Type, baseStaticType *Type, baseWithThis *Type, t *Type, typeWithThis *Type, member *ast.Node) { - isJs := ast.IsInJSFile(node) - memberHasOverrideModifier := hasOverrideModifier(member) - if baseWithThis == nil { - if memberHasOverrideModifier { - c.error(member, core.IfElse(isJs, diagnostics.This_member_cannot_have_a_JSDoc_comment_with_an_override_tag_because_its_containing_class_0_does_not_extend_another_class, diagnostics.This_member_cannot_have_an_override_modifier_because_its_containing_class_0_does_not_extend_another_class), c.TypeToString(t)) - } - return - } - if sym := member.Symbol(); memberHasOverrideModifier && sym != nil && sym.ValueDeclaration != nil && ast.IsClassElement(member) && member.Name() != nil && c.isNonBindableDynamicName(member.Name()) { - c.error(member, core.IfElse(isJs, diagnostics.This_member_cannot_have_a_JSDoc_comment_with_an_override_tag_because_its_name_is_dynamic, diagnostics.This_member_cannot_have_an_override_modifier_because_its_name_is_dynamic)) - return - } - if !memberHasOverrideModifier && !c.compilerOptions.NoImplicitOverride.IsTrue() { - return - } - // Here we have a base class and also an override modifier or no override modifier in noImplicitOverride mode - symbol := c.getSymbolOfDeclaration(member) - if symbol == nil { - return - } - memberIsStatic := ast.IsStatic(member) - thisType := core.IfElse(memberIsStatic, staticType, typeWithThis) - prop := c.getPropertyOfType(thisType, symbol.Name) - if prop == nil { - return - } - baseType := core.IfElse(memberIsStatic, baseStaticType, baseWithThis) - baseProp := c.getPropertyOfType(baseType, symbol.Name) - if baseProp == nil && memberHasOverrideModifier { - suggestion := c.getSuggestedSymbolForNonexistentClassMember(ast.SymbolName(symbol), baseType) - if suggestion != nil { - c.error(member, core.IfElse(isJs, diagnostics.This_member_cannot_have_a_JSDoc_comment_with_an_override_tag_because_it_is_not_declared_in_the_base_class_0_Did_you_mean_1, diagnostics.This_member_cannot_have_an_override_modifier_because_it_is_not_declared_in_the_base_class_0_Did_you_mean_1), c.TypeToString(baseWithThis), c.symbolToString(suggestion)) - return - } - c.error(member, core.IfElse(isJs, diagnostics.This_member_cannot_have_a_JSDoc_comment_with_an_override_tag_because_it_is_not_declared_in_the_base_class_0, diagnostics.This_member_cannot_have_an_override_modifier_because_it_is_not_declared_in_the_base_class_0), c.TypeToString(baseWithThis)) - return - } - if baseProp != nil && len(baseProp.Declarations) != 0 && !memberHasOverrideModifier && c.compilerOptions.NoImplicitOverride.IsTrue() && node.Flags&ast.NodeFlagsAmbient == 0 { - baseHasAbstract := core.Some(baseProp.Declarations, hasAbstractModifier) - if !baseHasAbstract { - message := core.IfElse(ast.IsParameter(member), - core.IfElse(isJs, diagnostics.This_parameter_property_must_have_a_JSDoc_comment_with_an_override_tag_because_it_overrides_a_member_in_the_base_class_0, diagnostics.This_parameter_property_must_have_an_override_modifier_because_it_overrides_a_member_in_base_class_0), - core.IfElse(isJs, diagnostics.This_member_must_have_a_JSDoc_comment_with_an_override_tag_because_it_overrides_a_member_in_the_base_class_0, diagnostics.This_member_must_have_an_override_modifier_because_it_overrides_a_member_in_the_base_class_0)) - c.error(member, message, c.TypeToString(baseWithThis)) - return - } - if hasAbstractModifier(member) && baseHasAbstract { - c.error(member, diagnostics.This_member_must_have_an_override_modifier_because_it_overrides_an_abstract_method_that_is_declared_in_the_base_class_0, c.TypeToString(baseWithThis)) - } - } -} - -func (c *Checker) getSuggestedSymbolForNonexistentClassMember(name string, baseType *Type) *ast.Symbol { - return c.getSpellingSuggestionForName(name, c.getPropertiesOfType(baseType), ast.SymbolFlagsClassMember) -} - -func (c *Checker) checkIndexConstraints(t *Type, symbol *ast.Symbol, isStaticIndex bool) { - indexInfos := c.getIndexInfosOfType(t) - if len(indexInfos) == 0 { - return - } - for _, prop := range c.getPropertiesOfObjectType(t) { - if !(isStaticIndex && prop.Flags&ast.SymbolFlagsPrototype != 0) { - c.checkIndexConstraintForProperty(t, prop, c.getLiteralTypeFromProperty(prop, TypeFlagsStringOrNumberLiteralOrUnique, true /*includeNonPublic*/), c.getNonMissingTypeOfSymbol(prop)) - } - } - typeDeclaration := symbol.ValueDeclaration - if typeDeclaration != nil && ast.IsClassLike(typeDeclaration) { - for _, member := range typeDeclaration.Members() { - // Only process instance properties with computed names here. Static properties cannot be in conflict with indexers, - // and properties with literal names were already checked. - if !ast.IsStatic(member) && !c.hasBindableName(member) { - symbol := c.getSymbolOfDeclaration(member) - c.checkIndexConstraintForProperty(t, symbol, c.getTypeOfExpression(member.Name().Expression()), c.getNonMissingTypeOfSymbol(symbol)) - } - } - } - if len(indexInfos) > 1 { - for _, info := range indexInfos { - c.checkIndexConstraintForIndexSignature(t, info) - } - } -} - -func (c *Checker) checkIndexConstraintForProperty(t *Type, prop *ast.Symbol, propNameType *Type, propType *Type) { - declaration := prop.ValueDeclaration - name := ast.GetNameOfDeclaration(declaration) - if name != nil && ast.IsPrivateIdentifier(name) { - return - } - indexInfos := c.getApplicableIndexInfos(t, propNameType) - if len(indexInfos) == 0 { - return - } - var interfaceDeclaration *ast.Node - if t.objectFlags&ObjectFlagsInterface != 0 { - interfaceDeclaration = ast.GetDeclarationOfKind(t.symbol, ast.KindInterfaceDeclaration) - } - var propDeclaration *ast.Node - if declaration != nil && ast.IsBinaryExpression(declaration) || name != nil && ast.IsComputedPropertyName(name) { - propDeclaration = declaration - } - var localPropDeclaration *ast.Node - if c.getParentOfSymbol(prop) == t.symbol { - localPropDeclaration = declaration - } - for _, info := range indexInfos { - var localIndexDeclaration *ast.Node - if info.declaration != nil && c.getParentOfSymbol(c.getSymbolOfDeclaration(info.declaration)) == t.symbol { - localIndexDeclaration = info.declaration - } - // We check only when (a) the property is declared in the containing type, or (b) the applicable index signature is declared - // in the containing type, or (c) the containing type is an interface and no base interface contains both the property and - // the index signature (i.e. property and index signature are declared in separate inherited interfaces). - errorNode := core.OrElse(localPropDeclaration, localIndexDeclaration) - if errorNode == nil && interfaceDeclaration != nil && !core.Some(c.getBaseTypes(t), func(base *Type) bool { - return c.getPropertyOfObjectType(base, prop.Name) != nil && c.getIndexTypeOfType(base, info.keyType) != nil - }) { - errorNode = interfaceDeclaration - } - if errorNode != nil && !c.isTypeAssignableTo(propType, info.valueType) { - diagnostic := NewDiagnosticForNode(errorNode, diagnostics.Property_0_of_type_1_is_not_assignable_to_2_index_type_3, c.symbolToString(prop), c.TypeToString(propType), c.TypeToString(info.keyType), c.TypeToString(info.valueType)) - if propDeclaration != nil && errorNode != propDeclaration { - diagnostic.AddRelatedInfo(NewDiagnosticForNode(propDeclaration, diagnostics.X_0_is_declared_here, c.symbolToString(prop))) - } - c.diagnostics.Add(diagnostic) - } - } -} - -func (c *Checker) checkIndexConstraintForIndexSignature(t *Type, checkInfo *IndexInfo) { - declaration := checkInfo.declaration - indexInfos := c.getApplicableIndexInfos(t, checkInfo.keyType) - if len(indexInfos) == 0 { - return - } - var interfaceDeclaration *ast.Node - if t.objectFlags&ObjectFlagsInterface != 0 { - interfaceDeclaration = ast.GetDeclarationOfKind(t.symbol, ast.KindInterfaceDeclaration) - } - var localCheckDeclaration *ast.Node - if declaration != nil && c.getParentOfSymbol(c.getSymbolOfDeclaration(declaration)) == t.symbol { - localCheckDeclaration = declaration - } - for _, info := range indexInfos { - if info == checkInfo { - continue - } - var localIndexDeclaration *ast.Node - if info.declaration != nil && c.getParentOfSymbol(c.getSymbolOfDeclaration(info.declaration)) == t.symbol { - localIndexDeclaration = info.declaration - } - // We check only when (a) the check index signature is declared in the containing type, or (b) the applicable index - // signature is declared in the containing type, or (c) the containing type is an interface and no base interface contains - // both index signatures (i.e. the index signatures are declared in separate inherited interfaces). - errorNode := core.OrElse(localCheckDeclaration, localIndexDeclaration) - if errorNode == nil && interfaceDeclaration != nil && !core.Some(c.getBaseTypes(t), func(base *Type) bool { - return c.getIndexInfoOfType(base, checkInfo.keyType) != nil && c.getIndexTypeOfType(base, info.keyType) != nil - }) { - errorNode = interfaceDeclaration - } - if errorNode != nil && !c.isTypeAssignableTo(checkInfo.valueType, info.valueType) { - c.error(errorNode, diagnostics.X_0_index_type_1_is_not_assignable_to_2_index_type_3, c.TypeToString(checkInfo.keyType), c.TypeToString(checkInfo.valueType), c.TypeToString(info.keyType), c.TypeToString(info.valueType)) - } - } -} - -func (c *Checker) checkTypeForDuplicateIndexSignatures(node *ast.Node) { - if ast.IsInterfaceDeclaration(node) { - // in case of merging interface declaration it is possible that we'll enter this check procedure several times for every declaration - // to prevent this run check only for the first declaration of a given kind - if symbol := c.getSymbolOfDeclaration(node); len(symbol.Declarations) != 0 && symbol.Declarations[0] != node { - return - } - } - // TypeScript 1.0 spec (April 2014) - // 3.7.4: An object type can contain at most one string index signature and one numeric index signature. - // 8.5: A class declaration can have at most one string index member declaration and one numeric index member declaration - indexSymbol := c.getIndexSymbol(c.getSymbolOfDeclaration(node)) - if indexSymbol == nil || len(indexSymbol.Declarations) <= 1 { - return - } - indexSignatureMap := make(map[*Type][]*ast.Node) - for _, declaration := range indexSymbol.Declarations { - if ast.IsIndexSignatureDeclaration(declaration) { - parameters := declaration.Parameters() - if len(parameters) == 1 && parameters[0].Type() != nil { - for _, t := range c.getTypeFromTypeNode(parameters[0].Type()).Distributed() { - indexSignatureMap[t] = append(indexSignatureMap[t], declaration) - } - } - } - // Do nothing for late-bound index signatures: allow these to duplicate one another and explicit indexes - } - for t, declarations := range indexSignatureMap { - if len(declarations) > 1 { - for _, declaration := range declarations { - c.error(declaration, diagnostics.Duplicate_index_signature_for_type_0, c.TypeToString(t)) - } - } - } -} - -func (c *Checker) checkPropertyInitialization(node *ast.Node) { - if !c.strictNullChecks || !c.strictPropertyInitialization || node.Flags&ast.NodeFlagsAmbient != 0 { - return - } - constructor := ast.FindConstructorDeclaration(node) - for _, member := range node.Members() { - if member.ModifierFlags()&ast.ModifierFlagsAmbient != 0 { - continue - } - if !ast.IsStatic(member) && c.isPropertyWithoutInitializer(member) { - propName := member.Name() - if ast.IsIdentifier(propName) || ast.IsPrivateIdentifier(propName) || ast.IsComputedPropertyName(propName) { - t := c.getTypeOfSymbol(c.getSymbolOfDeclaration(member)) - if !(t.flags&TypeFlagsAnyOrUnknown != 0 || c.containsUndefinedType(t)) { - if constructor == nil || !c.isPropertyInitializedInConstructor(propName, t, constructor) { - c.error(member.Name(), diagnostics.Property_0_has_no_initializer_and_is_not_definitely_assigned_in_the_constructor, scanner.DeclarationNameToString(propName)) - } - } - } - } - } -} - -func (c *Checker) isPropertyWithoutInitializer(node *ast.Node) bool { - return ast.IsPropertyDeclaration(node) && !hasAbstractModifier(node) && !isExclamationToken(node.AsPropertyDeclaration().PostfixToken) && node.Initializer() == nil -} - -func (c *Checker) isPropertyInitializedInStaticBlocks(propName *ast.Node, propType *Type, staticBlocks []*ast.Node, startPos int, endPos int) bool { - for _, staticBlock := range staticBlocks { - // static block must be within the provided range as they are evaluated in document order (unlike constructors) - if staticBlock.Pos() >= startPos && staticBlock.Pos() <= endPos { - reference := c.factory.NewPropertyAccessExpression(c.factory.NewKeywordExpression(ast.KindThisKeyword), nil, propName, ast.NodeFlagsNone) - reference.Expression().Parent = reference - reference.Parent = staticBlock - reference.FlowNodeData().FlowNode = staticBlock.AsClassStaticBlockDeclaration().ReturnFlowNode - flowType := c.getFlowTypeOfReferenceEx(reference, propType, c.getOptionalType(propType, false), nil, nil) - if !c.containsUndefinedType(flowType) { - return true - } - } - } - return false -} - -func (c *Checker) isPropertyInitializedInConstructor(propName *ast.Node, propType *Type, constructor *ast.Node) bool { - var reference *ast.Node - if ast.IsComputedPropertyName(propName) { - reference = c.factory.NewElementAccessExpression(c.factory.NewKeywordExpression(ast.KindThisKeyword), nil, propName.Expression(), ast.NodeFlagsNone) - } else { - reference = c.factory.NewPropertyAccessExpression(c.factory.NewKeywordExpression(ast.KindThisKeyword), nil, propName, ast.NodeFlagsNone) - } - reference.Expression().Parent = reference - reference.Parent = constructor - reference.FlowNodeData().FlowNode = constructor.AsConstructorDeclaration().ReturnFlowNode - flowType := c.getFlowTypeOfReferenceEx(reference, propType, c.getOptionalType(propType, false), nil, nil) - return !c.containsUndefinedType(flowType) -} - -func (c *Checker) checkInterfaceDeclaration(node *ast.Node) { - if !c.checkGrammarModifiers(node) { - c.checkGrammarInterfaceDeclaration(node.AsInterfaceDeclaration()) - } - if !c.containerAllowsBlockScopedVariable(node.Parent) { - c.grammarErrorOnNode(node, diagnostics.X_0_declarations_can_only_be_declared_inside_a_block, "interface") - } - c.checkTypeParameters(node.TypeParameters()) - c.checkTypeNameIsReserved(node.Name(), diagnostics.Interface_name_cannot_be_0) - c.checkExportsOnMergedDeclarations(node) - symbol := c.getSymbolOfDeclaration(node) - c.checkTypeParameterListsIdentical(symbol) - // Only check this symbol once - firstInterfaceDecl := ast.GetDeclarationOfKind(symbol, ast.KindInterfaceDeclaration) - if node == firstInterfaceDecl { - t := c.getDeclaredTypeOfSymbol(symbol) - typeWithThis := c.getTypeWithThisArgument(t, nil, false) - // run subsequent checks only if first set succeeded - if c.checkInheritedPropertiesAreIdentical(t, node.Name()) { - for _, baseType := range c.getBaseTypes(t) { - c.checkTypeAssignableTo(typeWithThis, c.getTypeWithThisArgument(baseType, t.AsInterfaceType().thisType, false), node.Name(), diagnostics.Interface_0_incorrectly_extends_interface_1) - } - c.checkIndexConstraints(t, symbol /*isStaticIndex*/, false) - } - } - c.checkObjectTypeForDuplicateDeclarations(node, false /*checkPrivateNames*/) - for _, heritageElement := range ast.GetExtendsHeritageClauseElements(node) { - expr := heritageElement.Expression() - if !ast.IsEntityNameExpression(expr) || ast.IsOptionalChain(expr) { - c.error(expr, diagnostics.An_interface_can_only_extend_an_identifier_Slashqualified_name_with_optional_type_arguments) - } - c.checkTypeReferenceNode(heritageElement) - } - c.checkSourceElements(node.Members()) - c.checkTypeForDuplicateIndexSignatures(node) - c.registerForUnusedIdentifiersCheck(node) -} - -type InheritanceInfo struct { - prop *ast.Symbol - containingType *Type -} - -func (c *Checker) checkInheritedPropertiesAreIdentical(t *Type, typeNode *ast.Node) bool { - baseTypes := c.getBaseTypes(t) - if len(baseTypes) < 2 { - return true - } - seen := make(map[string]InheritanceInfo) - for id, p := range c.resolveDeclaredMembers(t).declaredMembers { - if c.isNamedMember(p, id) { - seen[p.Name] = InheritanceInfo{prop: p, containingType: t} - } - } - identical := true - for _, base := range baseTypes { - properties := c.getPropertiesOfType(c.getTypeWithThisArgument(base, t.AsInterfaceType().thisType, false)) - for _, prop := range properties { - if existing, ok := seen[prop.Name]; !ok { - seen[prop.Name] = InheritanceInfo{prop: prop, containingType: base} - } else { - isInheritedProperty := existing.containingType != t - if isInheritedProperty && !c.isPropertyIdenticalTo(existing.prop, prop) { - identical = false - typeName1 := c.TypeToString(existing.containingType) - typeName2 := c.TypeToString(base) - errorInfo := NewDiagnosticForNode(typeNode, diagnostics.Named_property_0_of_types_1_and_2_are_not_identical, c.symbolToString(prop), typeName1, typeName2) - c.diagnostics.Add(ast.NewDiagnosticChain(errorInfo, diagnostics.Interface_0_cannot_simultaneously_extend_types_1_and_2, c.TypeToString(t), typeName1, typeName2)) - } - } - } - } - return identical -} - -func (c *Checker) isPropertyIdenticalTo(sourceProp *ast.Symbol, targetProp *ast.Symbol) bool { - return c.compareProperties(sourceProp, targetProp, c.compareTypesIdentical) != TernaryFalse -} - -func (c *Checker) checkEnumDeclaration(node *ast.Node) { - c.checkGrammarModifiers(node) - c.checkCollisionsForDeclarationName(node, node.Name()) - c.checkExportsOnMergedDeclarations(node) - c.checkSourceElements(node.Members()) - - if c.compilerOptions.ErasableSyntaxOnly.IsTrue() && node.Flags&ast.NodeFlagsAmbient == 0 { - c.error(node, diagnostics.This_syntax_is_not_allowed_when_erasableSyntaxOnly_is_enabled) - } - - c.computeEnumMemberValues(node) - // Spec 2014 - Section 9.3: - // It isn't possible for one enum declaration to continue the automatic numbering sequence of another, - // and when an enum type has multiple declarations, only one declaration is permitted to omit a value - // for the first member. - // - // Only perform this check once per symbol - enumSymbol := c.getSymbolOfDeclaration(node) - firstDeclaration := ast.GetDeclarationOfKind(enumSymbol, node.Kind) - if node == firstDeclaration { - if len(enumSymbol.Declarations) > 1 { - enumIsConst := ast.IsEnumConst(node) - // check that const is placed\omitted on all enum declarations - for _, decl := range enumSymbol.Declarations { - if ast.IsEnumDeclaration(decl) && ast.IsEnumConst(decl) != enumIsConst { - c.error(ast.GetNameOfDeclaration(decl), diagnostics.Enum_declarations_must_all_be_const_or_non_const) - } - } - } - seenEnumMissingInitialInitializer := false - for _, declaration := range enumSymbol.Declarations { - // return true if we hit a violation of the rule, false otherwise - if declaration.Kind != ast.KindEnumDeclaration { - continue - } - members := declaration.Members() - if len(members) == 0 { - continue - } - firstEnumMember := members[0] - if firstEnumMember.Initializer() == nil { - if seenEnumMissingInitialInitializer { - c.error(firstEnumMember.Name(), diagnostics.In_an_enum_with_multiple_declarations_only_one_declaration_can_omit_an_initializer_for_its_first_enum_element) - } else { - seenEnumMissingInitialInitializer = true - } - } - } - } -} - -func (c *Checker) checkEnumMember(node *ast.Node) { - if ast.IsPrivateIdentifier(node.Name()) { - c.error(node, diagnostics.An_enum_member_cannot_be_named_with_a_private_identifier) - } - if node.Initializer() != nil { - c.checkExpression(node.Initializer()) - } -} - -func (c *Checker) checkModuleDeclaration(node *ast.Node) { - if body := node.Body(); body != nil { - c.checkSourceElement(body) - if !ast.IsGlobalScopeAugmentation(node) { - c.registerForUnusedIdentifiersCheck(node) - } - } - isGlobalAugmentation := ast.IsGlobalScopeAugmentation(node) - inAmbientContext := node.Flags&ast.NodeFlagsAmbient != 0 - if isGlobalAugmentation && !inAmbientContext { - c.error(node.Name(), diagnostics.Augmentations_for_the_global_scope_should_have_declare_modifier_unless_they_appear_in_already_ambient_context) - } - isAmbientExternalModule := ast.IsAmbientModule(node) - contextErrorMessage := core.IfElse(isAmbientExternalModule, - diagnostics.An_ambient_module_declaration_is_only_allowed_at_the_top_level_in_a_file, - diagnostics.A_namespace_declaration_is_only_allowed_at_the_top_level_of_a_namespace_or_module) - if c.checkGrammarModuleElementContext(node, contextErrorMessage) { - // If we hit a module declaration in an illegal context, just bail out to avoid cascading errors. - return - } - if !c.checkGrammarModifiers(node) { - if !inAmbientContext && ast.IsStringLiteral(node.Name()) { - c.grammarErrorOnNode(node.Name(), diagnostics.Only_ambient_modules_can_use_quoted_names) - } - } - if ast.IsIdentifier(node.Name()) { - c.checkCollisionsForDeclarationName(node, node.Name()) - if node.AsModuleDeclaration().Keyword == ast.KindModuleKeyword { - tokenRange := getNonModifierTokenRangeOfNode(node) - c.suggestionDiagnostics.Add(ast.NewDiagnostic(ast.GetSourceFileOfNode(node), tokenRange, diagnostics.A_namespace_declaration_should_not_be_declared_using_the_module_keyword_Please_use_the_namespace_keyword_instead)) - } - } - c.checkExportsOnMergedDeclarations(node) - symbol := c.getSymbolOfDeclaration(node) - // The following checks only apply on a non-ambient instantiated module declaration. - if symbol.Flags&ast.SymbolFlagsValueModule != 0 && !inAmbientContext && isInstantiatedModule(node, c.compilerOptions.ShouldPreserveConstEnums()) { - if c.compilerOptions.ErasableSyntaxOnly.IsTrue() { - c.error(node, diagnostics.This_syntax_is_not_allowed_when_erasableSyntaxOnly_is_enabled) - } - if c.compilerOptions.GetIsolatedModules() && ast.GetSourceFileOfNode(node).ExternalModuleIndicator == nil { - // This could be loosened a little if needed. The only problem we are trying to avoid is unqualified - // references to namespace members declared in other files. But use of namespaces is discouraged anyway, - // so for now we will just not allow them in scripts, which is the only place they can merge cross-file. - c.error(node.Name(), diagnostics.Namespaces_are_not_allowed_in_global_script_files_when_0_is_enabled_If_this_file_is_not_intended_to_be_a_global_script_set_moduleDetection_to_force_or_add_an_empty_export_statement, c.getIsolatedModulesLikeFlagName()) - } - if len(symbol.Declarations) > 1 { - firstNonAmbientClassOrFunc := getFirstNonAmbientClassOrFunctionDeclaration(symbol) - if firstNonAmbientClassOrFunc != nil { - if ast.GetSourceFileOfNode(node) != ast.GetSourceFileOfNode(firstNonAmbientClassOrFunc) { - c.error(node.Name(), diagnostics.A_namespace_declaration_cannot_be_in_a_different_file_from_a_class_or_function_with_which_it_is_merged) - } else if node.Pos() < firstNonAmbientClassOrFunc.Pos() { - c.error(node.Name(), diagnostics.A_namespace_declaration_cannot_be_located_prior_to_a_class_or_function_with_which_it_is_merged) - } - } - } - if c.compilerOptions.VerbatimModuleSyntax.IsTrue() && ast.IsSourceFile(node.Parent) && node.ModifierFlags()&ast.ModifierFlagsExport != 0 && c.program.GetEmitModuleFormatOfFile(node.Parent.AsSourceFile()) == core.ModuleKindCommonJS { - exportModifier := core.Find(node.ModifierNodes(), func(m *ast.Node) bool { return m.Kind == ast.KindExportKeyword }) - c.error(exportModifier, diagnostics.A_top_level_export_modifier_cannot_be_used_on_value_declarations_in_a_CommonJS_module_when_verbatimModuleSyntax_is_enabled) - } - } - if isAmbientExternalModule { - if ast.IsExternalModuleAugmentation(node) { - // body of the augmentation should be checked for consistency only if augmentation was applied to its target (either global scope or module) - // otherwise we'll be swamped in cascading errors. - // We can detect if augmentation was applied using following rules: - // - augmentation for a global scope is always applied - // - augmentation for some external module is applied if symbol for augmentation is merged (it was combined with target module). - checkBody := isGlobalAugmentation || c.getSymbolOfDeclaration(node).Flags&ast.SymbolFlagsTransient != 0 - if checkBody && node.Body() != nil { - for _, statement := range node.Body().AsModuleBlock().Statements.Nodes { - c.checkModuleAugmentationElement(statement) - } - } - } else if ast.IsGlobalSourceFile(node.Parent) { - if isGlobalAugmentation { - c.error(node.Name(), diagnostics.Augmentations_for_the_global_scope_can_only_be_directly_nested_in_external_modules_or_ambient_module_declarations) - } else if tspath.IsExternalModuleNameRelative(node.Name().Text()) { - c.error(node.Name(), diagnostics.Ambient_module_declaration_cannot_specify_relative_module_name) - } - } else { - if isGlobalAugmentation { - c.error(node.Name(), diagnostics.Augmentations_for_the_global_scope_can_only_be_directly_nested_in_external_modules_or_ambient_module_declarations) - } else { - // Node is not an augmentation and is not located on the script level. - // This means that this is declaration of ambient module that is located in other module or namespace which is prohibited. - c.error(node.Name(), diagnostics.Ambient_modules_cannot_be_nested_in_other_modules_or_namespaces) - } - } - } -} - -func isInstantiatedModule(node *ast.Node, preserveConstEnums bool) bool { - moduleState := ast.GetModuleInstanceState(node) - return moduleState == ast.ModuleInstanceStateInstantiated || preserveConstEnums && moduleState == ast.ModuleInstanceStateConstEnumOnly -} - -func getFirstNonAmbientClassOrFunctionDeclaration(symbol *ast.Symbol) *ast.Node { - for _, declaration := range symbol.Declarations { - if (ast.IsClassDeclaration(declaration) || ast.IsFunctionDeclaration(declaration) && ast.NodeIsPresent(declaration.Body())) && declaration.Flags&ast.NodeFlagsAmbient == 0 { - return declaration - } - } - return nil -} - -func (c *Checker) getIsolatedModulesLikeFlagName() string { - return core.IfElse(c.compilerOptions.VerbatimModuleSyntax.IsTrue(), "verbatimModuleSyntax", "isolatedModules") -} - -func (c *Checker) checkModuleAugmentationElement(node *ast.Node) { - switch node.Kind { - case ast.KindVariableStatement: - // error each individual name in variable statement instead of marking the entire variable statement - for _, decl := range node.AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes { - c.checkModuleAugmentationElement(decl) - } - case ast.KindExportAssignment, ast.KindJSExportAssignment, ast.KindExportDeclaration: - c.grammarErrorOnFirstToken(node, diagnostics.Exports_and_export_assignments_are_not_permitted_in_module_augmentations) - case ast.KindImportEqualsDeclaration: - // import a = e.x; in module augmentation is ok, but not import a = require('fs) - if ast.IsInternalModuleImportEqualsDeclaration(node) { - break - } - fallthrough - case ast.KindImportDeclaration, ast.KindJSImportDeclaration: - c.grammarErrorOnFirstToken(node, diagnostics.Imports_are_not_permitted_in_module_augmentations_Consider_moving_them_to_the_enclosing_external_module) - case ast.KindBindingElement, ast.KindVariableDeclaration: - name := node.Name() - if ast.IsBindingPattern(name) { - for _, el := range name.AsBindingPattern().Elements.Nodes { - // mark individual names in binding pattern - c.checkModuleAugmentationElement(el) - } - } - } -} - -func (c *Checker) checkImportDeclaration(node *ast.Node) { - // Grammar checking - var diagnostic *diagnostics.Message - if ast.IsInJSFile(node) { - diagnostic = diagnostics.An_import_declaration_can_only_be_used_at_the_top_level_of_a_module - } else { - diagnostic = diagnostics.An_import_declaration_can_only_be_used_at_the_top_level_of_a_namespace_or_module - } - if c.checkGrammarModuleElementContext(node, diagnostic) { - // If we hit an import declaration in an illegal context, just bail out to avoid cascading errors. - return - } - if !c.checkGrammarModifiers(node) && node.Modifiers() != nil { - c.grammarErrorOnFirstToken(node, diagnostics.An_import_declaration_cannot_have_modifiers) - } - if c.checkExternalImportOrExportDeclaration(node) { - var resolvedModule *ast.Symbol - importClause := node.AsImportDeclaration().ImportClause - moduleSpecifier := node.AsImportDeclaration().ModuleSpecifier - if importClause != nil && !c.checkGrammarImportClause(importClause.AsImportClause()) { - if importClause.Name() != nil { - c.checkImportBinding(importClause) - } - namedBindings := importClause.AsImportClause().NamedBindings - if namedBindings != nil { - if ast.IsNamespaceImport(namedBindings) { - c.checkImportBinding(namedBindings) - } else { - resolvedModule = c.resolveExternalModuleName(node, node.AsImportDeclaration().ModuleSpecifier, false) - if resolvedModule != nil { - for _, binding := range namedBindings.AsNamedImports().Elements.Nodes { - c.checkImportBinding(binding) - } - } - } - } - if !importClause.IsTypeOnly() && - core.ModuleKindNode18 <= c.moduleKind && c.moduleKind <= core.ModuleKindNodeNext && - c.isOnlyImportableAsDefault(moduleSpecifier, resolvedModule) && - !hasTypeJsonImportAttribute(node) { - c.error(moduleSpecifier, diagnostics.Importing_a_JSON_file_into_an_ECMAScript_module_requires_a_type_Colon_json_import_attribute_when_module_is_set_to_0, c.moduleKind.String()) - } - } else if c.compilerOptions.NoUncheckedSideEffectImports.IsTrue() && importClause == nil { - c.resolveExternalModuleName(node, moduleSpecifier, false) - } - } - c.checkImportAttributes(node) -} - -func (c *Checker) checkExternalImportOrExportDeclaration(node *ast.Node) bool { - moduleName := ast.GetExternalModuleName(node) - if moduleName == nil || ast.NodeIsMissing(moduleName) { - // Should be a parse error. - return false - } - if !ast.IsStringLiteral(moduleName) { - c.error(moduleName, diagnostics.String_literal_expected) - return false - } - inAmbientExternalModule := ast.IsModuleBlock(node.Parent) && ast.IsAmbientModule(node.Parent.Parent) - if !ast.IsSourceFile(node.Parent) && !inAmbientExternalModule { - c.error(moduleName, core.IfElse(ast.IsExportDeclaration(node), diagnostics.Export_declarations_are_not_permitted_in_a_namespace, diagnostics.Import_declarations_in_a_namespace_cannot_reference_a_module)) - return false - } - if inAmbientExternalModule && tspath.IsExternalModuleNameRelative(moduleName.Text()) { - // we have already reported errors on top level imports/exports in external module augmentations in checkModuleDeclaration - // no need to do this again. - if !isTopLevelInExternalModuleAugmentation(node) { - // TypeScript 1.0 spec (April 2013): 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. - c.error(node, diagnostics.Import_or_export_declaration_in_an_ambient_module_declaration_cannot_reference_module_through_relative_module_name) - return false - } - } - if !ast.IsImportEqualsDeclaration(node) { - attributes := ast.GetImportAttributes(node) - if attributes != nil { - diagnostic := core.IfElse(attributes.AsImportAttributes().Token == ast.KindWithKeyword, - diagnostics.Import_attribute_values_must_be_string_literal_expressions, - diagnostics.Import_assertion_values_must_be_string_literal_expressions) - hasError := false - for _, attr := range attributes.AsImportAttributes().Attributes.Nodes { - if !ast.IsStringLiteral(attr.AsImportAttribute().Value) { - hasError = true - c.error(attr.AsImportAttribute().Value, diagnostic) - } - } - return !hasError - } - } - return true -} - -func (c *Checker) checkImportBinding(node *ast.Node) { - c.checkCollisionsForDeclarationName(node, node.Name()) - c.checkAliasSymbol(node) - if ast.IsImportSpecifier(node) { - c.checkModuleExportName(node.PropertyName(), true /*allowStringLiteral*/) - } -} - -func (c *Checker) checkModuleExportName(name *ast.Node, allowStringLiteral bool) { - if name == nil || name.Kind != ast.KindStringLiteral { - return - } - if !allowStringLiteral { - c.grammarErrorOnNode(name, diagnostics.Identifier_expected) - } else if c.moduleKind == core.ModuleKindES2015 || c.moduleKind == core.ModuleKindES2020 { - c.grammarErrorOnNode(name, diagnostics.String_literal_import_and_export_names_are_not_supported_when_the_module_flag_is_set_to_es2015_or_es2020) - } -} - -func hasTypeJsonImportAttribute(node *ast.Node) bool { - attributes := node.AsImportDeclaration().Attributes - return attributes != nil && core.Some(attributes.AsImportAttributes().Attributes.Nodes, func(attr *ast.Node) bool { - return attr.Name().Text() == "type" && ast.IsStringLiteralLike(attr.AsImportAttribute().Value) && attr.AsImportAttribute().Value.Text() == "json" - }) -} - -func (c *Checker) checkImportAttributes(declaration *ast.Node) { - node := ast.GetImportAttributes(declaration) - if node == nil { - return - } - importAttributesType := c.getGlobalImportAttributesTypeChecked() - if importAttributesType != c.emptyObjectType { - c.checkTypeAssignableTo(c.getTypeFromImportAttributes(node), c.getNullableType(importAttributesType, TypeFlagsUndefined), node, nil) - } - isTypeOnly := ast.IsExclusivelyTypeOnlyImportOrExport(declaration) - override := c.getResolutionModeOverride(node.AsImportAttributes(), isTypeOnly) - isImportAttributes := node.AsImportAttributes().Token == ast.KindWithKeyword - if isTypeOnly && override != core.ResolutionModeNone { - return // Other grammar checks do not apply to type-only imports with resolution mode assertions - } - - if !c.moduleKind.SupportsImportAttributes() { - if isImportAttributes { - c.grammarErrorOnNode(node, diagnostics.Import_attributes_are_only_supported_when_the_module_option_is_set_to_esnext_node18_node20_nodenext_or_preserve) - } else { - c.grammarErrorOnNode(node, diagnostics.Import_assertions_are_only_supported_when_the_module_option_is_set_to_esnext_node18_node20_nodenext_or_preserve) - } - return - } - - if core.ModuleKindNode20 <= c.moduleKind && c.moduleKind <= core.ModuleKindNodeNext && !isImportAttributes { - c.grammarErrorOnNode(node, diagnostics.Import_assertions_have_been_replaced_by_import_attributes_Use_with_instead_of_assert) - return - } - - if moduleSpecifier := getModuleSpecifierFromNode(declaration); moduleSpecifier != nil { - if c.getEmitSyntaxForModuleSpecifierExpression(moduleSpecifier) == core.ModuleKindCommonJS { - if isImportAttributes { - c.grammarErrorOnNode(node, diagnostics.Import_attributes_are_not_allowed_on_statements_that_compile_to_CommonJS_require_calls) - } else { - c.grammarErrorOnNode(node, diagnostics.Import_assertions_are_not_allowed_on_statements_that_compile_to_CommonJS_require_calls) - } - return - } - } - - if isTypeOnly { - c.grammarErrorOnNode(node, core.IfElse(isImportAttributes, - diagnostics.Import_attributes_cannot_be_used_with_type_only_imports_or_exports, - diagnostics.Import_assertions_cannot_be_used_with_type_only_imports_or_exports)) - return - } - if override != core.ResolutionModeNone { - c.grammarErrorOnNode(node, diagnostics.X_resolution_mode_can_only_be_set_for_type_only_imports) - } -} - -func (c *Checker) getTypeFromImportAttributes(node *ast.Node) *Type { - links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { - symbol := c.newSymbol(ast.SymbolFlagsObjectLiteral, ast.InternalSymbolNameImportAttributes) - members := make(ast.SymbolTable) - for _, attr := range node.AsImportAttributes().Attributes.Nodes { - member := c.newSymbol(ast.SymbolFlagsProperty, attr.Name().Text()) - c.valueSymbolLinks.Get(member).resolvedType = c.getRegularTypeOfLiteralType(c.checkExpression(attr.AsImportAttribute().Value)) - members[member.Name] = member - } - t := c.newAnonymousType(symbol, members, nil, nil, nil) - t.objectFlags |= ObjectFlagsObjectLiteral | ObjectFlagsNonInferrableType - links.resolvedType = t - } - return links.resolvedType -} - -func (c *Checker) checkImportEqualsDeclaration(node *ast.Node) { - diagnostic := core.IfElse(ast.IsInJSFile(node), - diagnostics.An_import_declaration_can_only_be_used_at_the_top_level_of_a_module, - diagnostics.An_import_declaration_can_only_be_used_at_the_top_level_of_a_namespace_or_module) - if c.checkGrammarModuleElementContext(node, diagnostic) { - return // If we hit an import declaration in an illegal context, just bail out to avoid cascading errors. - } - c.checkGrammarModifiers(node) - if c.compilerOptions.ErasableSyntaxOnly.IsTrue() && node.Flags&ast.NodeFlagsAmbient == 0 { - c.error(node, diagnostics.This_syntax_is_not_allowed_when_erasableSyntaxOnly_is_enabled) - } - if ast.IsInternalModuleImportEqualsDeclaration(node) || c.checkExternalImportOrExportDeclaration(node) { - c.checkImportBinding(node) - c.markLinkedReferences(node, ReferenceHintExportImportEquals, nil, nil) - moduleReference := node.AsImportEqualsDeclaration().ModuleReference - if !ast.IsExternalModuleReference(moduleReference) { - target := c.resolveAlias(c.getSymbolOfDeclaration(node)) - if target != c.unknownSymbol { - targetFlags := c.getSymbolFlags(target) - if targetFlags&ast.SymbolFlagsValue != 0 { - // Target is a value symbol, check that it is not hidden by a local declaration with the same name - moduleName := ast.GetFirstIdentifier(moduleReference) - if c.resolveEntityName(moduleName, ast.SymbolFlagsValue|ast.SymbolFlagsNamespace, false, false, nil).Flags&ast.SymbolFlagsNamespace == 0 { - c.error(moduleName, diagnostics.Module_0_is_hidden_by_a_local_declaration_with_the_same_name, scanner.DeclarationNameToString(moduleName)) - } - } - if targetFlags&ast.SymbolFlagsType != 0 { - c.checkTypeNameIsReserved(node.Name(), diagnostics.Import_name_cannot_be_0) - } - } - if node.AsImportEqualsDeclaration().IsTypeOnly { - c.grammarErrorOnNode(node, diagnostics.An_import_alias_cannot_use_import_type) - } - } else { - if core.ModuleKindES2015 <= c.moduleKind && c.moduleKind <= core.ModuleKindESNext && !node.AsImportEqualsDeclaration().IsTypeOnly && node.Flags&ast.NodeFlagsAmbient == 0 { - // Import equals declaration cannot be emitted as ESM - c.grammarErrorOnNode(node, diagnostics.Import_assignment_cannot_be_used_when_targeting_ECMAScript_modules_Consider_using_import_Asterisk_as_ns_from_mod_import_a_from_mod_import_d_from_mod_or_another_module_format_instead) - } - } - } -} - -func (c *Checker) checkExportDeclaration(node *ast.Node) { - diagnostic := core.IfElse(ast.IsInJSFile(node), - diagnostics.An_export_declaration_can_only_be_used_at_the_top_level_of_a_module, - diagnostics.An_export_declaration_can_only_be_used_at_the_top_level_of_a_namespace_or_module) - if c.checkGrammarModuleElementContext(node, diagnostic) { - return // If we hit an export in an illegal context, just bail out to avoid cascading errors. - } - exportDecl := node.AsExportDeclaration() - if !c.checkGrammarModifiers(node) && exportDecl.Modifiers() != nil { - c.grammarErrorOnFirstToken(node, diagnostics.An_export_declaration_cannot_have_modifiers) - } - c.checkGrammarExportDeclaration(exportDecl) - if exportDecl.ModuleSpecifier == nil || c.checkExternalImportOrExportDeclaration(node) { - if exportDecl.ExportClause != nil && !ast.IsNamespaceExport(exportDecl.ExportClause) { - // export { x, y } - // export { x, y } from "foo" - for _, binding := range exportDecl.ExportClause.AsNamedExports().Elements.Nodes { - c.checkExportSpecifier(binding) - } - inAmbientExternalModule := ast.IsModuleBlock(node.Parent) && ast.IsAmbientModule(node.Parent.Parent) - inAmbientNamespaceDeclaration := !inAmbientExternalModule && ast.IsModuleBlock(node.Parent) && exportDecl.ModuleSpecifier == nil && node.Flags&ast.NodeFlagsAmbient != 0 - if !ast.IsSourceFile(node.Parent) && !inAmbientExternalModule && !inAmbientNamespaceDeclaration { - c.error(node, diagnostics.Export_declarations_are_not_permitted_in_a_namespace) - } - } else { - // export * from "foo" - // export * as ns from "foo"; - moduleSymbol := c.resolveExternalModuleName(node, exportDecl.ModuleSpecifier, false) - if moduleSymbol != nil && hasExportAssignmentSymbol(moduleSymbol) { - c.error(exportDecl.ModuleSpecifier, diagnostics.Module_0_uses_export_and_cannot_be_used_with_export_Asterisk, c.symbolToString(moduleSymbol)) - } else if exportDecl.ExportClause != nil { - c.checkAliasSymbol(exportDecl.ExportClause) - c.checkModuleExportName(exportDecl.ExportClause.Name(), true /*allowStringLiteral*/) - } - } - } - c.checkImportAttributes(node) -} - -func (c *Checker) checkExportSpecifier(node *ast.Node) { - c.checkAliasSymbol(node) - hasModuleSpecifier := node.Parent.Parent.AsExportDeclaration().ModuleSpecifier != nil - c.checkModuleExportName(node.AsExportSpecifier().PropertyName, hasModuleSpecifier) - c.checkModuleExportName(node.Name(), true /*allowStringLiteral*/) - - if !hasModuleSpecifier { - exportedName := node.PropertyNameOrName() - if exportedName.Kind == ast.KindStringLiteral { - return // Skip for invalid syntax like this: export { "x" } - } - // find immediate value referenced by exported name (SymbolFlags.Alias is set so we don't chase down aliases) - symbol := c.resolveName(exportedName, exportedName.Text(), ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace|ast.SymbolFlagsAlias, nil /*nameNotFoundMessage*/, true /*isUse*/, false) - if symbol != nil && (symbol == c.undefinedSymbol || symbol == c.globalThisSymbol || symbol.Declarations != nil && ast.IsGlobalSourceFile(ast.GetDeclarationContainer(symbol.Declarations[0]))) { - c.error(exportedName, diagnostics.Cannot_export_0_Only_local_declarations_can_be_exported_from_a_module, exportedName.Text()) - } else { - c.markLinkedReferences(node, ReferenceHintExportSpecifier, nil /*propSymbol*/, nil /*parentType*/) - } - } -} - -func (c *Checker) checkExportAssignment(node *ast.Node) { - isExportEquals := ast.IsJSExportAssignment(node) || node.AsExportAssignment().IsExportEquals - illegalContextMessage := core.IfElse(isExportEquals, - diagnostics.An_export_assignment_must_be_at_the_top_level_of_a_file_or_module_declaration, - diagnostics.A_default_export_must_be_at_the_top_level_of_a_file_or_module_declaration) - if c.checkGrammarModuleElementContext(node, illegalContextMessage) { - return // If we hit an export assignment in an illegal context, just bail out to avoid cascading errors. - } - if c.compilerOptions.ErasableSyntaxOnly.IsTrue() && node.AsExportAssignment().IsExportEquals && node.Flags&ast.NodeFlagsAmbient == 0 { - c.error(node, diagnostics.This_syntax_is_not_allowed_when_erasableSyntaxOnly_is_enabled) - } - container := node.Parent - if !ast.IsSourceFile(container) { - container = container.Parent - } - if ast.IsModuleDeclaration(container) && !ast.IsAmbientModule(container) { - // TODO(danielr): should these be grammar errors? - if isExportEquals { - c.error(node, diagnostics.An_export_assignment_cannot_be_used_in_a_namespace) - } else { - c.error(node, diagnostics.A_default_export_can_only_be_used_in_an_ECMAScript_style_module) - } - return - } - if !c.checkGrammarModifiers(node) && ast.IsExportAssignment(node) && node.AsExportAssignment().Modifiers() != nil { - c.grammarErrorOnFirstToken(node, diagnostics.An_export_assignment_cannot_have_modifiers) - } - isIllegalExportDefaultInCJS := !isExportEquals && node.Flags&ast.NodeFlagsAmbient == 0 && c.compilerOptions.VerbatimModuleSyntax.IsTrue() && c.program.GetEmitModuleFormatOfFile(ast.GetSourceFileOfNode(node)) == core.ModuleKindCommonJS - if ast.IsIdentifier(node.Expression()) { - id := node.Expression() - sym := c.getExportSymbolOfValueSymbolIfExported(c.resolveEntityName(id, ast.SymbolFlagsAll, true /*ignoreErrors*/, true /*dontResolveAlias*/, node)) - if sym != nil { - c.markLinkedReferences(node, ReferenceHintExportAssignment, nil, nil) - typeOnlyDeclaration := c.getTypeOnlyAliasDeclarationEx(sym, ast.SymbolFlagsValue) - // If not a value, we're interpreting the identifier as a type export, along the lines of (`export { Id as default }`) - if c.getSymbolFlags(sym)&ast.SymbolFlagsValue != 0 { - // However if it is a value, we need to check it's being used correctly - c.checkExpressionCached(id) - if !isIllegalExportDefaultInCJS && node.Flags&ast.NodeFlagsAmbient == 0 && c.compilerOptions.VerbatimModuleSyntax.IsTrue() && typeOnlyDeclaration != nil { - message := core.IfElse(isExportEquals, - diagnostics.An_export_declaration_must_reference_a_real_value_when_verbatimModuleSyntax_is_enabled_but_0_resolves_to_a_type_only_declaration, - diagnostics.An_export_default_must_reference_a_real_value_when_verbatimModuleSyntax_is_enabled_but_0_resolves_to_a_type_only_declaration) - c.error(id, message, id.Text()) - } - } else if !isIllegalExportDefaultInCJS && node.Flags&ast.NodeFlagsAmbient == 0 && c.compilerOptions.VerbatimModuleSyntax.IsTrue() { - message := core.IfElse(isExportEquals, - diagnostics.An_export_declaration_must_reference_a_value_when_verbatimModuleSyntax_is_enabled_but_0_only_refers_to_a_type, - diagnostics.An_export_default_must_reference_a_value_when_verbatimModuleSyntax_is_enabled_but_0_only_refers_to_a_type) - c.error(id, message, id.Text()) - } - if !isIllegalExportDefaultInCJS && node.Flags&ast.NodeFlagsAmbient == 0 && c.compilerOptions.GetIsolatedModules() && sym.Flags&ast.SymbolFlagsValue == 0 { - nonLocalMeanings := c.getSymbolFlagsEx(sym, false /*excludeTypeOnlyMeanings*/, true /*excludeLocalMeanings*/) - if sym.Flags&ast.SymbolFlagsAlias != 0 && nonLocalMeanings&ast.SymbolFlagsType != 0 && nonLocalMeanings&ast.SymbolFlagsValue == 0 && (typeOnlyDeclaration == nil || ast.GetSourceFileOfNode(typeOnlyDeclaration) != ast.GetSourceFileOfNode(node)) { - // import { SomeType } from "./someModule"; - // export default SomeType; OR - // export = SomeType; - message := core.IfElse(isExportEquals, - diagnostics.X_0_resolves_to_a_type_and_must_be_marked_type_only_in_this_file_before_re_exporting_when_1_is_enabled_Consider_using_import_type_where_0_is_imported, - diagnostics.X_0_resolves_to_a_type_and_must_be_marked_type_only_in_this_file_before_re_exporting_when_1_is_enabled_Consider_using_export_type_0_as_default) - c.error(id, message, id.Text(), c.getIsolatedModulesLikeFlagName()) - } else if typeOnlyDeclaration != nil && ast.GetSourceFileOfNode(typeOnlyDeclaration) != ast.GetSourceFileOfNode(node) { - // import { SomeTypeOnlyValue } from "./someModule"; - // export default SomeTypeOnlyValue; OR - // export = SomeTypeOnlyValue; - message := core.IfElse(isExportEquals, - diagnostics.X_0_resolves_to_a_type_only_declaration_and_must_be_marked_type_only_in_this_file_before_re_exporting_when_1_is_enabled_Consider_using_import_type_where_0_is_imported, - diagnostics.X_0_resolves_to_a_type_only_declaration_and_must_be_marked_type_only_in_this_file_before_re_exporting_when_1_is_enabled_Consider_using_export_type_0_as_default) - c.addTypeOnlyDeclarationRelatedInfo(c.error(id, message, id.Text(), c.getIsolatedModulesLikeFlagName()), typeOnlyDeclaration, id.Text()) - } - } - } else { - c.checkExpressionCached(id) - // doesn't resolve, check as expression to mark as error - } - - } else { - c.checkExpressionCached(node.Expression()) - } - if isIllegalExportDefaultInCJS { - c.error(node, getVerbatimModuleSyntaxErrorMessage(node)) - } - c.checkExternalModuleExports(container) - if typeNode := node.Type(); typeNode != nil && node.Kind == ast.KindExportAssignment { - t := c.getTypeFromTypeNode(typeNode) - initializerType := c.checkExpressionCached(node.Expression()) - c.checkTypeAssignableToAndOptionallyElaborate(initializerType, t, node.Expression(), node.Expression(), nil /*headMessage*/, nil) - } - if (node.Flags&ast.NodeFlagsAmbient != 0) && !ast.IsEntityNameExpression(node.Expression()) { - c.grammarErrorOnNode(node.Expression(), diagnostics.The_expression_of_an_export_assignment_must_be_an_identifier_or_qualified_name_in_an_ambient_context) - } - if isExportEquals { - // Forbid export= in esm implementation files, and esm mode declaration files - if c.moduleKind >= core.ModuleKindES2015 && c.moduleKind != core.ModuleKindPreserve && ((node.Flags&ast.NodeFlagsAmbient != 0 && c.program.GetImpliedNodeFormatForEmit(ast.GetSourceFileOfNode(node)) == core.ModuleKindESNext) || (node.Flags&ast.NodeFlagsAmbient == 0 && c.program.GetImpliedNodeFormatForEmit(ast.GetSourceFileOfNode(node)) != core.ModuleKindCommonJS)) { - // export assignment is not supported in es6 modules - c.grammarErrorOnNode(node, diagnostics.Export_assignment_cannot_be_used_when_targeting_ECMAScript_modules_Consider_using_export_default_or_another_module_format_instead) - } else if c.moduleKind == core.ModuleKindSystem && node.Flags&ast.NodeFlagsAmbient == 0 { - // system modules does not support export assignment - c.grammarErrorOnNode(node, diagnostics.Export_assignment_is_not_supported_when_module_flag_is_system) - } - } -} - -func getVerbatimModuleSyntaxErrorMessage(node *ast.Node) *diagnostics.Message { - sourceFile := ast.GetSourceFileOfNode(node) - fileName := sourceFile.FileName() - - // Check if the file is .cts or .cjs (CommonJS-specific extensions) - if tspath.FileExtensionIsOneOf(fileName, []string{tspath.ExtensionCts, tspath.ExtensionCjs}) { - return diagnostics.ECMAScript_imports_and_exports_cannot_be_written_in_a_CommonJS_file_under_verbatimModuleSyntax - } - // For .ts, .tsx, .js, etc. - return diagnostics.ECMAScript_imports_and_exports_cannot_be_written_in_a_CommonJS_file_under_verbatimModuleSyntax_Adjust_the_type_field_in_the_nearest_package_json_to_make_this_file_an_ECMAScript_module_or_adjust_your_verbatimModuleSyntax_module_and_moduleResolution_settings_in_TypeScript -} - -func (c *Checker) checkExternalModuleExports(node *ast.Node) { - moduleSymbol := c.getSymbolOfDeclaration(node) - links := c.moduleSymbolLinks.Get(moduleSymbol) - if !links.exportsChecked { - exportEqualsSymbol := moduleSymbol.Exports[ast.InternalSymbolNameExportEquals] - if exportEqualsSymbol != nil && c.hasExportedMembers(moduleSymbol) { - declaration := core.OrElse(c.getDeclarationOfAliasSymbol(exportEqualsSymbol), exportEqualsSymbol.ValueDeclaration) - if declaration != nil && !isTopLevelInExternalModuleAugmentation(declaration) { - c.error(declaration, diagnostics.An_export_assignment_cannot_be_used_in_a_module_with_other_exported_elements) - } - } - // Checks for export * conflicts - for id, symbol := range c.getExportsOfModule(moduleSymbol) { - if id == ast.InternalSymbolNameExportStar { - continue - } - // ECMA262: 15.2.1.1 It is a Syntax Error if the ExportedNames of ModuleItemList contains any duplicate entries. - // (TS Exceptions: namespaces, function overloads, enums, and interfaces) - if symbol.Flags&(ast.SymbolFlagsNamespace|ast.SymbolFlagsEnum) != 0 { - continue - } - exportedDeclarationsCount := core.CountWhere(symbol.Declarations, func(d *ast.Node) bool { - return isNotOverload(d) && !ast.IsAccessor(d) && !ast.IsInterfaceDeclaration(d) - }) - if symbol.Flags&ast.SymbolFlagsTypeAlias != 0 && exportedDeclarationsCount <= 2 { - // it is legal to merge type alias with other values - // so count should be either 1 (just type alias) or 2 (type alias + merged value) - continue - } - if exportedDeclarationsCount > 1 { - for _, declaration := range symbol.Declarations { - if isNotOverload(declaration) { - c.error(declaration, diagnostics.Cannot_redeclare_exported_variable_0, id) - } - } - } - } - links.exportsChecked = true - } -} - -func (c *Checker) hasExportedMembers(moduleSymbol *ast.Symbol) bool { - for id := range moduleSymbol.Exports { - if id != ast.InternalSymbolNameExportEquals { - return true - } - } - return false -} - -func isNotOverload(node *ast.Node) bool { - return !ast.IsFunctionDeclaration(node) && !ast.IsMethodDeclaration(node) || node.Body() != nil -} - -func (c *Checker) checkMissingDeclaration(node *ast.Node) { - c.checkDecorators(node) -} - -func (c *Checker) checkVariableStatement(node *ast.Node) { - varStatement := node.AsVariableStatement() - declarationList := varStatement.DeclarationList - if !c.checkGrammarModifiers(node) && !c.checkGrammarVariableDeclarationList(declarationList.AsVariableDeclarationList()) { - c.checkGrammarForDisallowedBlockScopedVariableStatement(varStatement) - } - c.checkVariableDeclarationList(declarationList) -} - -func (c *Checker) checkVariableDeclarationList(node *ast.Node) { - c.checkSourceElements(node.AsVariableDeclarationList().Declarations.Nodes) -} - -func (c *Checker) checkVariableDeclaration(node *ast.Node) { - c.checkGrammarVariableDeclaration(node.AsVariableDeclaration()) - c.checkVariableLikeDeclaration(node) -} - -// Check variable, parameter, or property declaration -func (c *Checker) checkVariableLikeDeclaration(node *ast.Node) { - c.checkDecorators(node) - name := node.Name() - if name == nil { - return // Missing array binding elements have no name - } - typeNode := node.Type() - initializer := node.Initializer() - if !ast.IsBindingElement(node) { - c.checkSourceElement(typeNode) - } - // For a computed property, just check the initializer and exit - // Do not use hasDynamicName here, because that returns false for well known symbols. - // We want to perform checkComputedPropertyName for all computed properties, including - // well known symbols. - if ast.IsComputedPropertyName(name) { - c.checkComputedPropertyName(name) - if initializer != nil { - c.checkExpressionCached(initializer) - } - } - if ast.IsBindingElement(node) { - propName := node.PropertyName() - if propName != nil && ast.IsIdentifier(node.Name()) && ast.IsPartOfParameterDeclaration(node) && ast.NodeIsMissing(ast.GetContainingFunction(node).Body()) { - // type F = ({a: string}) => void; - // ^^^^^^ - // variable renaming in function type notation is confusing, - // so we forbid it even if noUnusedLocals is not enabled - c.renamedBindingElementsInTypes = append(c.renamedBindingElementsInTypes, node) - return - } - // check computed properties inside property names of binding elements - if propName != nil && ast.IsComputedPropertyName(propName) { - c.checkComputedPropertyName(propName) - } - // check private/protected variable access - parent := node.Parent.Parent - parentCheckMode := core.IfElse(hasDotDotDotToken(node), CheckModeRestBindingElement, CheckModeNormal) - parentType := c.getTypeForBindingElementParent(parent, parentCheckMode) - propNameName := node.PropertyNameOrName() - if parentType != nil && !ast.IsBindingPattern(propNameName) { - exprType := c.getLiteralTypeFromPropertyName(propNameName) - if isTypeUsableAsPropertyName(exprType) { - nameText := getPropertyNameFromType(exprType) - property := c.getPropertyOfType(parentType, nameText) - if property != nil { - c.markPropertyAsReferenced(property, nil /*nodeForCheckWriteOnly*/, false /*isSelfTypeAccess*/) - // A destructuring is never a write-only reference. - c.checkPropertyAccessibility(node, parent.Initializer() != nil && parent.Initializer().Kind == ast.KindSuperKeyword, false /*writing*/, parentType, property) - } - } - } - } - // For a binding pattern, check contained binding elements - if ast.IsBindingPattern(name) { - c.checkSourceElements(name.AsBindingPattern().Elements.Nodes) - } - // For a parameter declaration with an initializer, error and exit if the containing function doesn't have a body - if initializer != nil && ast.IsPartOfParameterDeclaration(node) && ast.NodeIsMissing(ast.GetContainingFunction(node).Body()) { - c.error(node, diagnostics.A_parameter_initializer_is_only_allowed_in_a_function_or_constructor_implementation) - return - } - // For a binding pattern, validate the initializer and exit - if ast.IsBindingPattern(name) { - if isInAmbientOrTypeNode(node) { - return - } - needCheckInitializer := initializer != nil && node.Parent.Parent.Kind != ast.KindForInStatement - needCheckWidenedType := !core.Some(name.AsBindingPattern().Elements.Nodes, func(n *ast.Node) bool { return n.Name() != nil }) - if needCheckInitializer || needCheckWidenedType { - // Don't validate for-in initializer as it is already an error - widenedType := c.getWidenedTypeForVariableLikeDeclaration(node, false /*reportErrors*/) - if needCheckInitializer { - initializerType := c.checkExpressionCached(initializer) - if c.strictNullChecks && needCheckWidenedType { - c.checkNonNullNonVoidType(initializerType, node) - } else { - c.checkTypeAssignableToAndOptionallyElaborate(initializerType, c.getWidenedTypeForVariableLikeDeclaration(node, false), node, initializer, nil, nil) - } - } - // check the binding pattern with empty elements - if needCheckWidenedType { - if ast.IsArrayBindingPattern(name) { - c.checkIteratedTypeOrElementType(IterationUseDestructuring, widenedType, c.undefinedType, node) - } else if c.strictNullChecks { - c.checkNonNullNonVoidType(widenedType, node) - } - } - } - return - } - // For a commonjs `const x = require`, validate the alias and exit - symbol := c.getSymbolOfDeclaration(node) - if symbol.Flags&ast.SymbolFlagsAlias != 0 && ast.IsVariableDeclarationInitializedToRequire(node) { - c.checkAliasSymbol(node) - return - } - if ast.IsBigIntLiteral(name) { - c.error(name, diagnostics.A_bigint_literal_cannot_be_used_as_a_property_name) - } - t := c.convertAutoToAny(c.getTypeOfSymbol(symbol)) - if node == symbol.ValueDeclaration { - // Node is the primary declaration of the symbol, just validate the initializer - // Don't validate for-in initializer as it is already an error - if initializer != nil && !ast.IsForInStatement(node.Parent.Parent) { - initializerType := c.checkExpressionCached(initializer) - c.checkTypeAssignableToAndOptionallyElaborate(initializerType, t, node, initializer, nil /*headMessage*/, nil) - blockScopeKind := c.getCombinedNodeFlagsCached(node) & ast.NodeFlagsBlockScoped - if blockScopeKind == ast.NodeFlagsAwaitUsing { - globalAsyncDisposableType := c.getGlobalAsyncDisposableType() - globalDisposableType := c.getGlobalDisposableType() - if globalAsyncDisposableType != c.emptyObjectType && globalDisposableType != c.emptyObjectType { - optionalDisposableType := c.getUnionType([]*Type{globalAsyncDisposableType, globalDisposableType, c.nullType, c.undefinedType}) - c.checkTypeAssignableTo(c.widenTypeForVariableLikeDeclaration(initializerType, node, false), optionalDisposableType, initializer, - diagnostics.The_initializer_of_an_await_using_declaration_must_be_either_an_object_with_a_Symbol_asyncDispose_or_Symbol_dispose_method_or_be_null_or_undefined) - } - } else if blockScopeKind == ast.NodeFlagsUsing { - globalDisposableType := c.getGlobalDisposableType() - if globalDisposableType != c.emptyObjectType { - optionalDisposableType := c.getUnionType([]*Type{globalDisposableType, c.nullType, c.undefinedType}) - c.checkTypeAssignableTo(c.widenTypeForVariableLikeDeclaration(initializerType, node, false), optionalDisposableType, initializer, - diagnostics.The_initializer_of_a_using_declaration_must_be_either_an_object_with_a_Symbol_dispose_method_or_be_null_or_undefined) - } - } - } - if len(symbol.Declarations) > 1 { - if core.Some(symbol.Declarations, func(d *ast.Declaration) bool { - return d != node && ast.IsVariableLike(d) && !c.areDeclarationFlagsIdentical(d, node) - }) { - c.error(name, diagnostics.All_declarations_of_0_must_have_identical_modifiers, scanner.DeclarationNameToString(name)) - } - } - } else { - // Node is a secondary declaration, check that type is identical to primary declaration and check that - // initializer is consistent with type associated with the node - declarationType := c.convertAutoToAny(c.getWidenedTypeForVariableLikeDeclaration(node, false)) - if !c.isErrorType(t) && !c.isErrorType(declarationType) && !c.isTypeIdenticalTo(t, declarationType) && symbol.Flags&ast.SymbolFlagsAssignment == 0 { - c.errorNextVariableOrPropertyDeclarationMustHaveSameType(symbol.ValueDeclaration, t, node, declarationType) - } - if initializer != nil { - c.checkTypeAssignableToAndOptionallyElaborate(c.checkExpressionCached(initializer), declarationType, node, initializer, nil /*headMessage*/, nil) - } - if symbol.ValueDeclaration != nil && !c.areDeclarationFlagsIdentical(node, symbol.ValueDeclaration) { - c.error(name, diagnostics.All_declarations_of_0_must_have_identical_modifiers, scanner.DeclarationNameToString(name)) - } - } - if !ast.IsPropertyDeclaration(node) && !ast.IsPropertySignatureDeclaration(node) { - // We know we don't have a binding pattern or computed name here - c.checkExportsOnMergedDeclarations(node) - if ast.IsVariableDeclaration(node) || ast.IsBindingElement(node) { - c.checkVarDeclaredNamesNotShadowed(node) - } - c.checkCollisionsForDeclarationName(node, node.Name()) - } -} - -func (c *Checker) errorNextVariableOrPropertyDeclarationMustHaveSameType(firstDeclaration *ast.Declaration, firstType *Type, nextDeclaration *ast.Declaration, nextType *Type) { - nextDeclarationName := ast.GetNameOfDeclaration(nextDeclaration) - message := core.IfElse(ast.IsPropertyDeclaration(nextDeclaration) || ast.IsPropertySignatureDeclaration(nextDeclaration), - diagnostics.Subsequent_property_declarations_must_have_the_same_type_Property_0_must_be_of_type_1_but_here_has_type_2, - diagnostics.Subsequent_variable_declarations_must_have_the_same_type_Variable_0_must_be_of_type_1_but_here_has_type_2) - declName := scanner.DeclarationNameToString(nextDeclarationName) - err := c.error(nextDeclarationName, message, declName, c.TypeToString(firstType), c.TypeToString(nextType)) - if firstDeclaration != nil { - err.AddRelatedInfo(createDiagnosticForNode(firstDeclaration, diagnostics.X_0_was_also_declared_here, declName)) - } -} - -func (c *Checker) checkVarDeclaredNamesNotShadowed(node *ast.Node) { - // - ScriptBody : StatementList - // It is a Syntax Error if any element of the LexicallyDeclaredNames of StatementList - // also occurs in the VarDeclaredNames of StatementList. - - // - Block : { StatementList } - // It is a Syntax Error if any element of the LexicallyDeclaredNames of StatementList - // also occurs in the VarDeclaredNames of StatementList. - - // Variable declarations are hoisted to the top of their function scope. They can shadow - // block scoped declarations, which bind tighter. this will not be flagged as duplicate definition - // by the binder as the declaration scope is different. - // A non-initialized declaration is a no-op as the block declaration will resolve before the var - // declaration. the problem is if the declaration has an initializer. this will act as a write to the - // block declared value. this is fine for let, but not const. - // Only consider declarations with initializers, uninitialized const declarations will not - // step on a let/const variable. - // Do not consider const and const declarations, as duplicate block-scoped declarations - // are handled by the binder. - // We are only looking for const declarations that step on let\const declarations from a - // different scope. e.g.: - // { - // const x = 0; // localDeclarationSymbol obtained after name resolution will correspond to this declaration - // const x = 0; // symbol for this declaration will be 'symbol' - // } - - // skip block-scoped variables and parameters - if (c.getCombinedNodeFlagsCached(node)&ast.NodeFlagsBlockScoped) != 0 || ast.IsPartOfParameterDeclaration(node) { - return - } - // NOTE: in ES6 spec initializer is required in variable declarations where name is binding pattern - // so we'll always treat binding elements as initialized - symbol := c.getSymbolOfDeclaration(node) - name := node.Name() - if symbol.Flags&ast.SymbolFlagsFunctionScopedVariable != 0 { - if !ast.IsIdentifier(name) { - panic("Identifier expected") - } - localDeclarationSymbol := c.resolveName(node, name.Text(), ast.SymbolFlagsVariable, nil /*nameNotFoundMessage*/, false /*isUse*/, false) - if localDeclarationSymbol != nil && localDeclarationSymbol != symbol && localDeclarationSymbol.Flags&ast.SymbolFlagsBlockScopedVariable != 0 { - if c.getDeclarationNodeFlagsFromSymbol(localDeclarationSymbol)&ast.NodeFlagsBlockScoped != 0 { - varDeclList := ast.FindAncestorKind(localDeclarationSymbol.ValueDeclaration, ast.KindVariableDeclarationList) - var container *ast.Node - if ast.IsVariableStatement(varDeclList.Parent) && varDeclList.Parent.Parent != nil { - container = varDeclList.Parent.Parent - } - // names of block-scoped and function scoped variables can collide only - // if block scoped variable is defined in the function\module\source file scope (because of variable hoisting) - namesShareScope := container != nil && (ast.IsBlock(container) && ast.IsFunctionLike(container.Parent) || - ast.IsModuleBlock(container) || ast.IsModuleDeclaration(container) || ast.IsSourceFile(container)) - // here we know that function scoped variable is "shadowed" by block scoped one - // a var declaration can't hoist past a lexical declaration and it results in a SyntaxError at runtime - if !namesShareScope { - name := c.symbolToString(localDeclarationSymbol) - c.error(node, diagnostics.Cannot_initialize_outer_scoped_variable_0_in_the_same_scope_as_block_scoped_declaration_1, name, name) - } - } - } - } -} - -func (c *Checker) checkDecorators(node *ast.Node) { - // skip this check for nodes that cannot have decorators. These should have already had an error reported by - // checkGrammarModifiers. - if !ast.CanHaveDecorators(node) || !ast.HasDecorators(node) || !nodeCanBeDecorated(c.legacyDecorators, node, node.Parent, node.Parent.Parent) { - return - } - firstDecorator := core.Find(node.ModifierNodes(), ast.IsDecorator) - if firstDecorator == nil { - return - } - c.markLinkedReferences(node, ReferenceHintDecorator, nil, nil) - for _, modifier := range node.ModifierNodes() { - if ast.IsDecorator(modifier) { - c.checkDecorator(modifier) - } - } -} - -func (c *Checker) checkDecorator(node *ast.Node) { - c.checkGrammarDecorator(node.AsDecorator()) - signature := c.getResolvedSignature(node, nil, CheckModeNormal) - c.checkDeprecatedSignature(signature, node) - returnType := c.getReturnTypeOfSignature(signature) - if returnType.flags&TypeFlagsAny != 0 { - return - } - // if we fail to get a signature and return type here, we will have already reported a grammar error in `checkDecorators`. - decoratorSignature := c.getDecoratorCallSignature(node) - if decoratorSignature == nil || decoratorSignature.resolvedReturnType == nil { - return - } - var headMessage *diagnostics.Message - expectedReturnType := decoratorSignature.resolvedReturnType - switch node.Parent.Kind { - case ast.KindClassDeclaration, ast.KindClassExpression: - headMessage = diagnostics.Decorator_function_return_type_0_is_not_assignable_to_type_1 - case ast.KindPropertyDeclaration: - if !c.legacyDecorators { - headMessage = diagnostics.Decorator_function_return_type_0_is_not_assignable_to_type_1 - break - } - fallthrough - case ast.KindParameter: - headMessage = diagnostics.Decorator_function_return_type_is_0_but_is_expected_to_be_void_or_any - case ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor: - headMessage = diagnostics.Decorator_function_return_type_0_is_not_assignable_to_type_1 - default: - panic("Unhandled case in checkDecorator") - } - c.checkTypeAssignableTo(returnType, expectedReturnType, node.Expression(), headMessage) -} - -func (c *Checker) checkIteratedTypeOrElementType(use IterationUse, inputType *Type, sentType *Type, errorNode *ast.Node) *Type { - if IsTypeAny(inputType) { - return inputType - } - t := c.getIteratedTypeOrElementType(use, inputType, sentType, errorNode, true /*checkAssignability*/) - if t != nil { - return t - } - return c.anyType -} - -func (c *Checker) getIteratedTypeOrElementType(use IterationUse, inputType *Type, sentType *Type, errorNode *ast.Node, checkAssignability bool) *Type { - allowAsyncIterables := (use & IterationUseAllowsAsyncIterablesFlag) != 0 - if inputType == c.neverType { - if errorNode != nil { - c.reportTypeNotIterableError(errorNode, inputType, allowAsyncIterables) - } - return nil - } - uplevelIteration := c.languageVersion >= core.ScriptTargetES2015 - downlevelIteration := !uplevelIteration && c.compilerOptions.DownlevelIteration == core.TSTrue - possibleOutOfBounds := c.compilerOptions.NoUncheckedIndexedAccess == core.TSTrue && use&IterationUsePossiblyOutOfBounds != 0 - // Get the iterated type of an `Iterable` or `IterableIterator` only in ES2015 - // or higher, when inside of an async generator or for-await-if, or when - // downlevelIteration is requested. - if uplevelIteration || downlevelIteration || allowAsyncIterables { - // We only report errors for an invalid iterable type in ES2015 or higher. - iterationTypes := c.getIterationTypesOfIterable(inputType, use, core.IfElse(uplevelIteration, errorNode, nil)) - if checkAssignability { - if iterationTypes.nextType != nil { - var diagnostic *diagnostics.Message - switch { - case use&IterationUseForOfFlag != 0: - diagnostic = diagnostics.Cannot_iterate_value_because_the_next_method_of_its_iterator_expects_type_1_but_for_of_will_always_send_0 - case use&IterationUseSpreadFlag != 0: - diagnostic = diagnostics.Cannot_iterate_value_because_the_next_method_of_its_iterator_expects_type_1_but_array_spread_will_always_send_0 - case use&IterationUseDestructuringFlag != 0: - diagnostic = diagnostics.Cannot_iterate_value_because_the_next_method_of_its_iterator_expects_type_1_but_array_destructuring_will_always_send_0 - case use&IterationUseYieldStarFlag != 0: - diagnostic = diagnostics.Cannot_delegate_iteration_to_value_because_the_next_method_of_its_iterator_expects_type_1_but_the_containing_generator_will_always_send_0 - } - if diagnostic != nil { - c.checkTypeAssignableTo(sentType, iterationTypes.nextType, errorNode, diagnostic) - } - } - } - if iterationTypes.yieldType != nil || uplevelIteration { - if iterationTypes.yieldType == nil { - return nil - } - if possibleOutOfBounds { - return c.includeUndefinedInIndexSignature(iterationTypes.yieldType) - } - return iterationTypes.yieldType - } - } - arrayType := inputType - hasStringConstituent := false - // If strings are permitted, remove any string-like constituents from the array type. - // This allows us to find other non-string element types from an array unioned with - // a string. - if use&IterationUseAllowsStringInputFlag != 0 { - if arrayType.flags&TypeFlagsUnion != 0 { - // After we remove all types that are StringLike, we will know if there was a string constituent - // based on whether the result of filter is a new array. - arrayTypes := inputType.Types() - filteredTypes := core.Filter(arrayTypes, func(t *Type) bool { - return t.flags&TypeFlagsStringLike == 0 - }) - if !core.Same(filteredTypes, arrayTypes) { - arrayType = c.getUnionTypeEx(filteredTypes, UnionReductionSubtype, nil, nil) - } - } else if arrayType.flags&TypeFlagsStringLike != 0 { - arrayType = c.neverType - } - hasStringConstituent = arrayType != inputType - if hasStringConstituent { - // Now that we've removed all the StringLike types, if no constituents remain, then the entire - // arrayOrStringType was a string. - if arrayType.flags&TypeFlagsNever != 0 { - if possibleOutOfBounds { - return c.includeUndefinedInIndexSignature(c.stringType) - } - return c.stringType - } - } - } - if !c.isArrayLikeType(arrayType) { - if errorNode != nil { - // Which error we report depends on whether we allow strings or if there was a - // string constituent. For example, if the input type is number | string, we - // want to say that number is not an array type. But if the input was just - // number and string input is allowed, we want to say that number is not an - // array type or a string type. - allowsStrings := use&IterationUseAllowsStringInputFlag != 0 && !hasStringConstituent - defaultDiagnostic, maybeMissingAwait := c.getIterationDiagnosticDetails(use, inputType, allowsStrings, downlevelIteration) - c.errorAndMaybeSuggestAwait(errorNode, maybeMissingAwait && c.getAwaitedTypeOfPromise(arrayType) != nil, defaultDiagnostic, c.TypeToString(arrayType)) - } - if hasStringConstituent { - if possibleOutOfBounds { - return c.includeUndefinedInIndexSignature(c.stringType) - } - return c.stringType - } - return nil - } - arrayElementType := c.getIndexTypeOfType(arrayType, c.numberType) - if hasStringConstituent && arrayElementType != nil { - // This is just an optimization for the case where arrayOrStringType is string | string[] - if arrayElementType.flags&TypeFlagsStringLike != 0 && c.compilerOptions.NoUncheckedIndexedAccess != core.TSTrue { - return c.stringType - } - if possibleOutOfBounds { - return c.getUnionTypeEx([]*Type{arrayElementType, c.stringType, c.undefinedType}, UnionReductionSubtype, nil, nil) - } - return c.getUnionTypeEx([]*Type{arrayElementType, c.stringType}, UnionReductionSubtype, nil, nil) - } - if use&IterationUsePossiblyOutOfBounds != 0 { - return c.includeUndefinedInIndexSignature(arrayElementType) - } - return arrayElementType -} - -// Gets the requested "iteration type" from a type that is either `Iterable`-like, `Iterator`-like, -// `IterableIterator`-like, or `Generator`-like (for a non-async generator); or `AsyncIterable`-like, -// `AsyncIterator`-like, `AsyncIterableIterator`-like, or `AsyncGenerator`-like (for an async generator). -func (c *Checker) getIterationTypeOfGeneratorFunctionReturnType(typeKind IterationTypeKind, returnType *Type, isAsyncGenerator bool) *Type { - if IsTypeAny(returnType) { - return nil - } - iterationTypes := c.getIterationTypesOfGeneratorFunctionReturnType(returnType, isAsyncGenerator) - return iterationTypes.getType(typeKind) -} - -func (c *Checker) getIterationTypesOfGeneratorFunctionReturnType(t *Type, isAsyncGenerator bool) IterationTypes { - if IsTypeAny(t) { - return IterationTypes{c.anyType, c.anyType, c.anyType} - } - use := core.IfElse(isAsyncGenerator, IterationUseAsyncGeneratorReturnType, IterationUseGeneratorReturnType) - resolver := core.IfElse(isAsyncGenerator, c.asyncIterationTypesResolver, c.syncIterationTypesResolver) - result := c.getIterationTypesOfIterable(t, use, nil /*errorNode*/) - if result.hasTypes() { - return result - } - return c.getIterationTypesOfIterator(t, resolver, nil /*errorNode*/, nil /*diagnosticOutput*/) -} - -// Gets the requested "iteration type" from an `Iterable`-like or `AsyncIterable`-like type. -func (c *Checker) getIterationTypeOfIterable(use IterationUse, typeKind IterationTypeKind, inputType *Type, errorNode *ast.Node) *Type { - if IsTypeAny(inputType) { - return nil - } - iterationTypes := c.getIterationTypesOfIterable(inputType, use, errorNode) - return iterationTypes.getType(typeKind) -} - -// Gets the *yield*, *return*, and *next* types from an `Iterable`-like or `AsyncIterable`-like type. -// -// At every level that involves analyzing return types of signatures, we union the return types of all the signatures. -// -// Another thing to note is that at any step of this process, we could run into a dead end, -// meaning either the property is missing, or we run into the anyType. If either of these things -// happens, we return a default `IterationTypes{}` to signal that we could not find the iteration type. -// If a property is missing, and the previous step did not result in `any`, then we also give an error -// if the caller requested it. Then the caller can decide what to do in the case where there is no -// iterated type. -// -// For a **for-of** statement, `yield*` (in a normal generator), spread, array -// destructuring, or normal generator we will only ever look for a `[Symbol.iterator]()` -// method. -// -// For an async generator we will only ever look at the `[Symbol.asyncIterator]()` method. -// -// For a **for-await-of** statement or a `yield*` in an async generator we will look for -// the `[Symbol.asyncIterator]()` method first, and then the `[Symbol.iterator]()` method. -func (c *Checker) getIterationTypesOfIterable(t *Type, use IterationUse, errorNode *ast.Node) IterationTypes { - if IsTypeAny(t) { - return IterationTypes{c.anyType, c.anyType, c.anyType} - } - key := IterationTypesKey{typeId: t.id, use: use & IterationUseCacheFlags} - // If we are reporting errors and encounter a cached `noIterationTypes`, we should ignore the cached value and continue as if nothing was cached. - // In addition, we should not cache any new results for this call. - noCache := false - if cached, ok := c.iterationTypesCache[key]; ok { - if errorNode == nil || cached.hasTypes() { - return cached - } - noCache = true - } - result := c.getIterationTypesOfIterableWorker(t, use, errorNode, noCache) - if !noCache { - c.iterationTypesCache[key] = result - } - return result -} - -func (c *Checker) getIterationTypesOfIterableWorker(t *Type, use IterationUse, errorNode *ast.Node, noCache bool) IterationTypes { - if t.flags&TypeFlagsUnion != 0 { - return c.combineIterationTypes(core.Map(t.Types(), func(t *Type) IterationTypes { return c.getIterationTypesOfIterableWorker(t, use, errorNode, noCache) })) - } - if use&IterationUseAllowsAsyncIterablesFlag != 0 { - iterationTypes := c.getIterationTypesOfIterableFast(t, c.asyncIterationTypesResolver) - if iterationTypes.hasTypes() { - if use&IterationUseForOfFlag != 0 { - return c.getAsyncFromSyncIterationTypes(iterationTypes, errorNode) - } - return iterationTypes - } - } - if use&IterationUseAllowsSyncIterablesFlag != 0 { - iterationTypes := c.getIterationTypesOfIterableFast(t, c.syncIterationTypesResolver) - if iterationTypes.hasTypes() { - if use&IterationUseAllowsAsyncIterablesFlag != 0 { - return c.getAsyncFromSyncIterationTypes(iterationTypes, errorNode) - } - return iterationTypes - } - } - var diags []*ast.Diagnostic - if use&IterationUseAllowsAsyncIterablesFlag != 0 { - iterationTypes := c.getIterationTypesOfIterableSlow(t, c.asyncIterationTypesResolver, errorNode, &diags) - if iterationTypes.hasTypes() { - return iterationTypes - } - } - if use&IterationUseAllowsSyncIterablesFlag != 0 { - iterationTypes := c.getIterationTypesOfIterableSlow(t, c.syncIterationTypesResolver, errorNode, &diags) - if iterationTypes.hasTypes() { - if use&IterationUseAllowsAsyncIterablesFlag != 0 { - return c.getAsyncFromSyncIterationTypes(iterationTypes, errorNode) - } - return iterationTypes - } - } - if errorNode != nil { - diagnostic := c.reportTypeNotIterableError(errorNode, t, use&IterationUseAllowsAsyncIterablesFlag != 0) - for _, d := range diags { - diagnostic.AddRelatedInfo(d) - } - } - return IterationTypes{} -} - -func (c *Checker) getIterationTypesOfIterableFast(t *Type, r *IterationTypesResolver) IterationTypes { - // As an optimization, if the type is an instantiation of the following global type, then - // just grab its related type arguments: - // - `Iterable` or `AsyncIterable` - // - `IteratorObject` or `AsyncIteratorObject` - // - `IterableIterator` or `AsyncIterableIterator` - // - `Generator` or `AsyncGenerator` - if c.isReferenceToType(t, r.getGlobalIterableType()) || - c.isReferenceToType(t, r.getGlobalIteratorObjectType()) || - c.isReferenceToType(t, r.getGlobalIterableIteratorType()) || - c.isReferenceToType(t, r.getGlobalGeneratorType()) { - typeArguments := c.getTypeArguments(t) - return r.getResolvedIterationTypes(typeArguments[0], typeArguments[1], typeArguments[2]) - } - // As an optimization, if the type is an instantiation of one of the following global types, then - // just grab the related type argument: - // - `ArrayIterator` - // - `MapIterator` - // - `SetIterator` - // - `StringIterator` - // - `ReadableStreamAsyncIterator` - if c.isReferenceToSomeType(t, r.getGlobalBuiltinIteratorTypes()) { - return r.getResolvedIterationTypes(c.getTypeArguments(t)[0], c.getBuiltinIteratorReturnType(), c.unknownType) - } - return IterationTypes{} -} - -func (r *IterationTypesResolver) getResolvedIterationTypes(yieldType *Type, returnType *Type, nextType *Type) IterationTypes { - return IterationTypes{ - yieldType: core.OrElse(r.resolveIterationType(yieldType, nil /*errorNode*/), yieldType), - returnType: core.OrElse(r.resolveIterationType(returnType, nil /*errorNode*/), returnType), - nextType: nextType, - } -} - -func (c *Checker) isReferenceToType(t *Type, target *Type) bool { - return t != nil && t.objectFlags&ObjectFlagsReference != 0 && t.Target() == target -} - -func (c *Checker) isReferenceToSomeType(t *Type, targets []*Type) bool { - return t != nil && t.objectFlags&ObjectFlagsReference != 0 && slices.Contains(targets, t.Target()) -} - -func (c *Checker) getBuiltinIteratorReturnType() *Type { - return core.IfElse(c.strictBuiltinIteratorReturn, c.undefinedType, c.anyType) -} - -func (iterationTypes *IterationTypes) hasTypes() bool { - return iterationTypes.yieldType != nil || iterationTypes.returnType != nil || iterationTypes.nextType != nil -} - -func (iterationTypes *IterationTypes) getType(typeKind IterationTypeKind) *Type { - switch typeKind { - case IterationTypeKindYield: - return iterationTypes.yieldType - case IterationTypeKindReturn: - return iterationTypes.returnType - case IterationTypeKindNext: - return iterationTypes.nextType - } - panic("Unhandled case in getType(IterationTypeKind)") -} - -func (c *Checker) combineIterationTypes(iterationTypes []IterationTypes) IterationTypes { - return IterationTypes{ - c.getIterationTypeUnion(iterationTypes, func(t IterationTypes) *Type { return t.yieldType }), - c.getIterationTypeUnion(iterationTypes, func(t IterationTypes) *Type { return t.returnType }), - c.getIterationTypeUnion(iterationTypes, func(t IterationTypes) *Type { return t.nextType }), - } -} - -func (c *Checker) getIterationTypeUnion(iterationTypes []IterationTypes, f func(IterationTypes) *Type) *Type { - types := core.MapNonNil(iterationTypes, f) - if len(types) == 0 { - return nil - } - return c.getUnionType(types) -} - -func (c *Checker) getAsyncFromSyncIterationTypes(iterationTypes IterationTypes, errorNode *ast.Node) IterationTypes { - if !iterationTypes.hasTypes() || - iterationTypes.yieldType == c.anyType && iterationTypes.returnType == c.anyType && iterationTypes.nextType == c.anyType { - return iterationTypes - } - // if we're requesting diagnostics, report errors for a missing `Awaited`. - if errorNode != nil { - c.getGlobalAwaitedSymbol() - } - return IterationTypes{ - yieldType: core.OrElse(c.getAwaitedTypeEx(iterationTypes.yieldType, errorNode, nil), c.anyType), - returnType: core.OrElse(c.getAwaitedTypeEx(iterationTypes.returnType, errorNode, nil), c.anyType), - nextType: iterationTypes.nextType, - } -} - -// Gets the *yield*, *return*, and *next* types of an `Iterable`-like or `AsyncIterable`-like -// type from its members. -// -// If we successfully found the *yield*, *return*, and *next* types, an `IterationTypes` with non-nil -// members is returned. Otherwise, a default `IterationTypes{}` is returned. -// -// NOTE: You probably don't want to call this directly and should be calling -// `getIterationTypesOfIterable` instead. -func (c *Checker) getIterationTypesOfIterableSlow(t *Type, r *IterationTypesResolver, errorNode *ast.Node, diagnosticOutput *[]*ast.Diagnostic) IterationTypes { - if method := c.getPropertyOfType(t, c.getPropertyNameForKnownSymbolName(r.iteratorSymbolName)); method != nil && method.Flags&ast.SymbolFlagsOptional == 0 { - methodType := c.getTypeOfSymbol(method) - if IsTypeAny(methodType) { - return IterationTypes{c.anyType, c.anyType, c.anyType} - } - allSignatures := c.getSignaturesOfType(methodType, SignatureKindCall) - validSignatures := core.Filter(allSignatures, func(sig *Signature) bool { - return c.getMinArgumentCount(sig) == 0 - }) - if len(validSignatures) != 0 { - iteratorType := c.getIntersectionType(core.Map(validSignatures, c.getReturnTypeOfSignature)) - return c.getIterationTypesOfIteratorWorker(iteratorType, r, errorNode, diagnosticOutput) - } - if errorNode != nil && len(allSignatures) != 0 { - c.checkTypeAssignableToEx(t, r.getGlobalIterableTypeChecked(), errorNode, nil, diagnosticOutput) - } - } - return IterationTypes{} -} - -// Gets the *yield*, *return*, and *next* types from an `Iterator`-like or `AsyncIterator`-like type. -// -// If we successfully found the *yield*, *return*, and *next* types, an `IterationTypes` with non-nil -// members is returned. Otherwise, a default `IterationTypes{}` is returned. -func (c *Checker) getIterationTypesOfIterator(t *Type, r *IterationTypesResolver, errorNode *ast.Node, diagnosticOutput *[]*ast.Diagnostic) IterationTypes { - return c.getIterationTypesOfIteratorWorker(t, r, errorNode, diagnosticOutput) -} - -// Gets the *yield*, *return*, and *next* types from an `Iterator`-like or `AsyncIterator`-like type. -// -// If we successfully found the *yield*, *return*, and *next* types, an `IterationTypes` with non-nil -// members is returned. Otherwise, a default `IterationTypes{}` is returned. -// -// NOTE: You probably don't want to call this directly and should be calling `getIterationTypesOfIterator` instead. -func (c *Checker) getIterationTypesOfIteratorWorker(t *Type, r *IterationTypesResolver, errorNode *ast.Node, diagnosticOutput *[]*ast.Diagnostic) IterationTypes { - if IsTypeAny(t) { - return IterationTypes{c.anyType, c.anyType, c.anyType} - } - iterationTypes := c.getIterationTypesOfIteratorFast(t, r) - if iterationTypes.hasTypes() { - return iterationTypes - } - return c.getIterationTypesOfIteratorSlow(t, r, errorNode, diagnosticOutput) -} - -func (c *Checker) getIterationTypesOfIteratorFast(t *Type, r *IterationTypesResolver) IterationTypes { - // As an optimization, if the type is an instantiation of the following global type, then - // just grab its related type arguments: - // - `Iterable` or `AsyncIterable` - // - `IteratorObject` or `AsyncIteratorObject` - // - `IterableIterator` or `AsyncIterableIterator` - // - `Generator` or `AsyncGenerator` - if c.isReferenceToType(t, r.getGlobalIteratorType()) || - c.isReferenceToType(t, r.getGlobalIteratorObjectType()) || - c.isReferenceToType(t, r.getGlobalIterableIteratorType()) || - c.isReferenceToType(t, r.getGlobalGeneratorType()) { - typeArguments := c.getTypeArguments(t) - return r.getResolvedIterationTypes(typeArguments[0], typeArguments[1], typeArguments[2]) - } - // As an optimization, if the type is an instantiation of one of the following global types, then - // just grab the related type argument: - // - `ArrayIterator` - // - `MapIterator` - // - `SetIterator` - // - `StringIterator` - // - `ReadableStreamAsyncIterator` - if c.isReferenceToSomeType(t, r.getGlobalBuiltinIteratorTypes()) { - return r.getResolvedIterationTypes(c.getTypeArguments(t)[0], c.getBuiltinIteratorReturnType(), c.unknownType) - } - return IterationTypes{} -} - -func (c *Checker) getIterationTypesOfIteratorSlow(t *Type, r *IterationTypesResolver, errorNode *ast.Node, diagnosticOutput *[]*ast.Diagnostic) IterationTypes { - return c.combineIterationTypes([]IterationTypes{ - c.getIterationTypesOfMethod(t, r, "next", errorNode, diagnosticOutput), - c.getIterationTypesOfMethod(t, r, "return", errorNode, diagnosticOutput), - c.getIterationTypesOfMethod(t, r, "throw", errorNode, diagnosticOutput), - }) -} - -func (c *Checker) getIterationTypesOfMethod(t *Type, resolver *IterationTypesResolver, methodName string, errorNode *ast.Node, diagnosticOutput *[]*ast.Diagnostic) IterationTypes { - method := c.getPropertyOfType(t, methodName) - // Ignore 'return' or 'throw' if they are missing. - if method == nil && methodName != "next" { - return IterationTypes{} - } - var methodType *Type - if method != nil && !(methodName == "next" && method.Flags&ast.SymbolFlagsOptional != 0) { - if methodName == "next" { - methodType = c.getTypeOfSymbol(method) - } else { - methodType = c.getTypeWithFacts(c.getTypeOfSymbol(method), TypeFactsNEUndefinedOrNull) - } - } - if IsTypeAny(methodType) { - return IterationTypes{c.anyType, c.anyType, c.anyType} - } - // Both async and non-async iterators *must* have a `next` method. - var methodSignatures []*Signature - if methodType != nil { - methodSignatures = c.getSignaturesOfType(methodType, SignatureKindCall) - } - if len(methodSignatures) == 0 { - if errorNode != nil { - diagnostic := core.IfElse(methodName == "next", resolver.mustHaveANextMethodDiagnostic, resolver.mustBeAMethodDiagnostic) - c.reportDiagnostic(NewDiagnosticForNode(errorNode, diagnostic, methodName), diagnosticOutput) - } - return IterationTypes{} - } - // If the method signature comes exclusively from the global iterator or generator type, - // create iteration types from its type arguments like `getIterationTypesOfIteratorFast` - // does (so as to remove `undefined` from the next and return types). We arrive here when - // a contextual type for a generator was not a direct reference to one of those global types, - // but looking up `methodType` referred to one of them (and nothing else). E.g., in - // `interface SpecialIterator extends Iterator {}`, `SpecialIterator` is not a - // reference to `Iterator`, but its `next` member derives exclusively from `Iterator`. - if len(methodSignatures) == 1 && methodType.symbol != nil { - globalGeneratorType := resolver.getGlobalGeneratorType() - globalIteratorType := resolver.getGlobalIteratorType() - isGeneratorMethod := globalGeneratorType.symbol != nil && globalGeneratorType.symbol.Members[methodName] == methodType.symbol - isIteratorMethod := !isGeneratorMethod && globalIteratorType.symbol != nil && globalIteratorType.symbol.Members[methodName] == methodType.symbol - if isGeneratorMethod || isIteratorMethod { - typeParameters := core.IfElse(isGeneratorMethod, globalGeneratorType, globalIteratorType).AsInterfaceType().TypeParameters() - mapper := methodType.Mapper() - var nextType *Type - if methodName == "next" { - nextType = mapper.Map(typeParameters[2]) - } - return IterationTypes{mapper.Map(typeParameters[0]), mapper.Map(typeParameters[1]), nextType} - } - } - // Extract the first parameter and return type of each signature. - var methodParameterTypes []*Type - var methodReturnTypes []*Type - for _, signature := range methodSignatures { - if methodName != "throw" && len(signature.parameters) != 0 { - methodParameterTypes = append(methodParameterTypes, c.getTypeAtPosition(signature, 0)) - } - methodReturnTypes = append(methodReturnTypes, c.getReturnTypeOfSignature(signature)) - } - // Resolve the *next* or *return* type from the first parameter of a `next()` or - // `return()` method, respectively. - var returnTypes []*Type - var nextType *Type - if methodName != "throw" { - var methodParameterType *Type - if methodParameterTypes != nil { - methodParameterType = c.getUnionType(methodParameterTypes) - } else { - methodParameterType = c.unknownType - } - if methodName == "next" { - // The value of `next(value)` is *not* awaited by async generators - nextType = methodParameterType - } else if methodName == "return" { - // The value of `return(value)` *is* awaited by async generators - resolvedMethodParameterType := core.OrElse(resolver.resolveIterationType(methodParameterType, errorNode), c.anyType) - returnTypes = append(returnTypes, resolvedMethodParameterType) - } - } - // Resolve the *yield* and *return* types from the return type of the method (i.e. `IteratorResult`) - var yieldType *Type - var methodReturnType *Type - if methodReturnTypes != nil { - methodReturnType = c.getIntersectionType(methodReturnTypes) - } else { - methodReturnType = c.neverType - } - resolvedMethodReturnType := core.OrElse(resolver.resolveIterationType(methodReturnType, errorNode), c.anyType) - iterationTypes := c.getIterationTypesOfIteratorResult(resolvedMethodReturnType) - if !iterationTypes.hasTypes() { - if errorNode != nil { - c.reportDiagnostic(NewDiagnosticForNode(errorNode, resolver.mustHaveAValueDiagnostic, methodName), diagnosticOutput) - } - yieldType = c.anyType - returnTypes = append(returnTypes, c.anyType) - } else { - yieldType = iterationTypes.yieldType - returnTypes = append(returnTypes, iterationTypes.returnType) - } - return IterationTypes{yieldType, c.getUnionType(returnTypes), nextType} -} - -// Gets the *yield* and *return* types of an `IteratorResult`-like type. -// -// If we are unable to determine a *yield* or a *return* type, `noIterationTypes` is -// returned to indicate to the caller that it should handle the error. Otherwise, an -// `IterationTypes` record is returned. -func (c *Checker) getIterationTypesOfIteratorResult(t *Type) IterationTypes { - if IsTypeAny(t) { - return IterationTypes{c.anyType, c.anyType, c.anyType} - } - // As an optimization, if the type is an instantiation of one of the global `IteratorYieldResult` - // or `IteratorReturnResult` types, then just grab its type argument. - if c.isReferenceToType(t, c.getGlobalIteratorYieldResultType()) { - return IterationTypes{c.getTypeArguments(t)[0], nil, nil} - } - if c.isReferenceToType(t, c.getGlobalIteratorReturnResultType()) { - return IterationTypes{nil, c.getTypeArguments(t)[0], nil} - } - // Choose any constituents that can produce the requested iteration type. - yieldIteratorResult := c.filterType(t, c.isYieldIteratorResult) - var yieldType *Type - if yieldIteratorResult != c.neverType { - yieldType = c.getTypeOfPropertyOfType(yieldIteratorResult, "value" /* as __String */) - } - returnIteratorResult := c.filterType(t, c.isReturnIteratorResult) - var returnType *Type - if returnIteratorResult != c.neverType { - returnType = c.getTypeOfPropertyOfType(returnIteratorResult, "value" /* as __String */) - } - if yieldType == nil && returnType == nil { - return IterationTypes{} - } - // From https://tc39.github.io/ecma262/#sec-iteratorresult-interface - // > ... If the iterator does not have a return value, `value` is `undefined`. In that case, the - // > `value` property may be absent from the conforming object if it does not inherit an explicit - // > `value` property. - return IterationTypes{yieldType, core.OrElse(returnType, c.voidType), nil} -} - -func (c *Checker) isYieldIteratorResult(t *Type) bool { - return c.isIteratorResult(t, IterationTypeKindYield) -} - -func (c *Checker) isReturnIteratorResult(t *Type) bool { - return c.isIteratorResult(t, IterationTypeKindReturn) -} - -func (c *Checker) isIteratorResult(t *Type, kind IterationTypeKind) bool { - // From https://tc39.github.io/ecma262/#sec-iteratorresult-interface: - // > [done] is the result status of an iterator `next` method call. If the end of the iterator was reached `done` is `true`. - // > If the end was not reached `done` is `false` and a value is available. - // > If a `done` property (either own or inherited) does not exist, it is consider to have the value `false`. - doneType := core.OrElse(c.getTypeOfPropertyOfType(t, "done"), c.falseType) - return c.isTypeAssignableTo(core.IfElse(kind == IterationTypeKindYield, c.falseType, c.trueType), doneType) -} - -func (c *Checker) reportTypeNotIterableError(errorNode *ast.Node, t *Type, allowAsyncIterables bool) *ast.Diagnostic { - var message *diagnostics.Message - if allowAsyncIterables { - message = diagnostics.Type_0_must_have_a_Symbol_asyncIterator_method_that_returns_an_async_iterator - } else { - message = diagnostics.Type_0_must_have_a_Symbol_iterator_method_that_returns_an_iterator - } - suggestAwait := c.getAwaitedTypeOfPromise(t) != nil || (!allowAsyncIterables && - ast.IsForOfStatement(errorNode.Parent) && - errorNode.Parent.Expression() == errorNode && - c.getGlobalAsyncIterableType() != c.emptyGenericType && - c.isTypeAssignableTo(t, c.createTypeFromGenericGlobalType(c.getGlobalAsyncIterableType(), []*Type{c.anyType, c.anyType, c.anyType}))) - return c.errorAndMaybeSuggestAwait(errorNode, suggestAwait, message, c.TypeToString(t)) -} - -func (c *Checker) getIterationDiagnosticDetails(use IterationUse, inputType *Type, allowsStrings bool, downlevelIteration bool) (*diagnostics.Message, bool) { - if downlevelIteration { - if allowsStrings { - return diagnostics.Type_0_is_not_an_array_type_or_a_string_type_or_does_not_have_a_Symbol_iterator_method_that_returns_an_iterator, true - } - return diagnostics.Type_0_is_not_an_array_type_or_does_not_have_a_Symbol_iterator_method_that_returns_an_iterator, true - } - yieldType := c.getIterationTypeOfIterable(use, IterationTypeKindYield, inputType, nil /*errorNode*/) - if yieldType != nil { - return diagnostics.Type_0_can_only_be_iterated_through_when_using_the_downlevelIteration_flag_or_with_a_target_of_es2015_or_higher, false - } - if inputType.symbol != nil && isES2015OrLaterIterable(inputType.symbol.Name) { - return diagnostics.Type_0_can_only_be_iterated_through_when_using_the_downlevelIteration_flag_or_with_a_target_of_es2015_or_higher, true - } - if allowsStrings { - return diagnostics.Type_0_is_not_an_array_type_or_a_string_type, true - } - return diagnostics.Type_0_is_not_an_array_type, true -} - -func isES2015OrLaterIterable(n string) bool { - switch n { - case "Float32Array", "Float64Array", "Int16Array", "Int32Array", "Int8Array", "NodeList", "Uint16Array", "Uint32Array", "Uint8Array", "Uint8ClampedArray": - return true - } - return false -} - -func (c *Checker) checkAliasSymbol(node *ast.Node) { - symbol := c.getSymbolOfDeclaration(node) - target := c.resolveAlias(symbol) - if target == c.unknownSymbol { - return - } - // For external modules, `symbol` represents the local symbol for an alias. - // This local symbol will merge any other local declarations (excluding other aliases) - // and symbol.flags will contains combined representation for all merged declaration. - // Based on symbol.flags we can compute a set of excluded meanings (meaning that resolved alias should not have, - // otherwise it will conflict with some local declaration). Note that in addition to normal flags we include matching SymbolFlags.Export* - // in order to prevent collisions with declarations that were exported from the current module (they still contribute to local names). - symbol = c.getMergedSymbol(core.OrElse(symbol.ExportSymbol, symbol)) - // A type-only import/export will already have a grammar error in a JS file, so no need to issue more errors within - if ast.IsInJSFile(node) && target.Flags&ast.SymbolFlagsValue == 0 && !ast.IsTypeOnlyImportOrExportDeclaration(node) { - errorNode := core.OrElse(node.PropertyNameOrName(), node) - debug.Assert(node.Kind != ast.KindNamespaceExport) - if ast.IsExportSpecifier(node) { - diag := c.error(errorNode, diagnostics.Types_cannot_appear_in_export_declarations_in_JavaScript_files) - if sourceSymbol := ast.GetSourceFileOfNode(node).AsNode().Symbol(); sourceSymbol != nil { - if alreadyExportedSymbol := sourceSymbol.Exports[node.PropertyNameOrName().Text()]; alreadyExportedSymbol == target { - if exportingDeclaration := core.Find(alreadyExportedSymbol.Declarations, ast.IsJSTypeAliasDeclaration); exportingDeclaration != nil { - diag.AddRelatedInfo(NewDiagnosticForNode(exportingDeclaration, diagnostics.X_0_is_automatically_exported_here, alreadyExportedSymbol.Name)) - } - } - } - } else { - debug.Assert(node.Kind != ast.KindVariableDeclaration) - specifierText := "..." - if importDeclaration := ast.FindAncestor(node, ast.IsImportOrImportEqualsDeclaration); importDeclaration != nil { - if moduleSpecifier := TryGetModuleSpecifierFromDeclaration(importDeclaration); moduleSpecifier != nil { - specifierText = moduleSpecifier.Text() - } - } - identifierText := symbol.Name - if ast.IsIdentifier(errorNode) { - identifierText = errorNode.Text() - } - importText := "import(\"" + specifierText + "\")." + identifierText - c.error(errorNode, diagnostics.X_0_is_a_type_and_cannot_be_imported_in_JavaScript_files_Use_1_in_a_JSDoc_type_annotation, identifierText, importText) - } - return - } - targetFlags := c.getSymbolFlags(target) - excludedMeanings := core.IfElse(symbol.Flags&(ast.SymbolFlagsValue|ast.SymbolFlagsExportValue) != 0, ast.SymbolFlagsValue, 0) | - core.IfElse(symbol.Flags&ast.SymbolFlagsType != 0, ast.SymbolFlagsType, 0) | - core.IfElse(symbol.Flags&ast.SymbolFlagsNamespace != 0, ast.SymbolFlagsNamespace, 0) - if targetFlags&excludedMeanings != 0 { - message := core.IfElse(ast.IsExportSpecifier(node), - diagnostics.Export_declaration_conflicts_with_exported_declaration_of_0, - diagnostics.Import_declaration_conflicts_with_local_declaration_of_0) - c.error(node, message, c.symbolToString(symbol)) - } else if !ast.IsExportSpecifier(node) { - // 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'. - appearsValueyToTranspiler := c.compilerOptions.IsolatedModules.IsTrue() && ast.FindAncestor(node, ast.IsTypeOnlyImportOrExportDeclaration) == nil - if appearsValueyToTranspiler && symbol.Flags&(ast.SymbolFlagsValue|ast.SymbolFlagsExportValue) != 0 { - c.error(node, diagnostics.Import_0_conflicts_with_local_value_so_must_be_declared_with_a_type_only_import_when_isolatedModules_is_enabled, c.symbolToString(symbol), c.getIsolatedModulesLikeFlagName()) - } - } - if c.compilerOptions.GetIsolatedModules() && !ast.IsTypeOnlyImportOrExportDeclaration(node) && node.Flags&ast.NodeFlagsAmbient == 0 { - typeOnlyAlias := c.getTypeOnlyAliasDeclaration(symbol) - isType := targetFlags&ast.SymbolFlagsValue == 0 - if isType || typeOnlyAlias != nil { - switch node.Kind { - case ast.KindImportClause, ast.KindImportSpecifier, ast.KindImportEqualsDeclaration: - if c.compilerOptions.VerbatimModuleSyntax.IsTrue() { - debug.AssertIsDefined(node.Name(), "An ImportClause with a symbol should have a name") - var message *diagnostics.Message - switch { - case c.compilerOptions.VerbatimModuleSyntax.IsTrue() && ast.IsInternalModuleImportEqualsDeclaration(node): - message = diagnostics.An_import_alias_cannot_resolve_to_a_type_or_type_only_declaration_when_verbatimModuleSyntax_is_enabled - case isType: - message = diagnostics.X_0_is_a_type_and_must_be_imported_using_a_type_only_import_when_verbatimModuleSyntax_is_enabled - default: - message = diagnostics.X_0_resolves_to_a_type_only_declaration_and_must_be_imported_using_a_type_only_import_when_verbatimModuleSyntax_is_enabled - } - name := node.PropertyNameOrName().Text() - c.addTypeOnlyDeclarationRelatedInfo(c.error(node, message, name), core.IfElse(isType, nil, typeOnlyAlias), name) - } - if isType && node.Kind == ast.KindImportEqualsDeclaration && ast.HasModifier(node, ast.ModifierFlagsExport) { - c.error(node, diagnostics.Cannot_use_export_import_on_a_type_or_type_only_namespace_when_0_is_enabled, c.getIsolatedModulesLikeFlagName()) - } - case ast.KindExportSpecifier: - // Don't allow re-exporting an export that will be elided when `--isolatedModules` is set. - // The exception is that `import type { A } from './a'; export { A }` is allowed - // because single-file analysis can determine that the export should be dropped. - if c.compilerOptions.VerbatimModuleSyntax.IsTrue() || ast.GetSourceFileOfNode(typeOnlyAlias) != ast.GetSourceFileOfNode(node) { - name := node.PropertyNameOrName().Text() - var diagnostic *ast.Diagnostic - if isType { - diagnostic = c.error(node, diagnostics.Re_exporting_a_type_when_0_is_enabled_requires_using_export_type, c.getIsolatedModulesLikeFlagName()) - } else { - diagnostic = c.error(node, diagnostics.X_0_resolves_to_a_type_only_declaration_and_must_be_re_exported_using_a_type_only_re_export_when_1_is_enabled, name, c.getIsolatedModulesLikeFlagName()) - } - c.addTypeOnlyDeclarationRelatedInfo(diagnostic, core.IfElse(isType, nil, typeOnlyAlias), name) - } - } - } - if c.compilerOptions.VerbatimModuleSyntax.IsTrue() && !ast.IsImportEqualsDeclaration(node) && !ast.IsInJSFile(node) && c.program.GetEmitModuleFormatOfFile(ast.GetSourceFileOfNode(node)) == core.ModuleKindCommonJS { - c.error(node, getVerbatimModuleSyntaxErrorMessage(node)) - } else if c.moduleKind == core.ModuleKindPreserve && !ast.IsImportEqualsDeclaration(node) && !ast.IsVariableDeclaration(node) && c.program.GetEmitModuleFormatOfFile(ast.GetSourceFileOfNode(node)) == core.ModuleKindCommonJS { - // In `--module preserve`, ESM input syntax emits ESM output syntax, but there will be times - // when we look at the `impliedNodeFormat` of this file and decide it's CommonJS (i.e., currently, - // only if the file extension is .cjs/.cts). To avoid that inconsistency, we disallow ESM syntax - // in files that are unambiguously CommonJS in this mode. - c.error(node, diagnostics.ECMAScript_module_syntax_is_not_allowed_in_a_CommonJS_module_when_module_is_set_to_preserve) - } - if c.compilerOptions.VerbatimModuleSyntax.IsTrue() && !ast.IsTypeOnlyImportOrExportDeclaration(node) && node.Flags&ast.NodeFlagsAmbient == 0 && targetFlags&ast.SymbolFlagsConstEnum != 0 { - constEnumDeclaration := target.ValueDeclaration - redirect := c.program.GetProjectReferenceFromOutputDts(ast.GetSourceFileOfNode(constEnumDeclaration).Path()) - if constEnumDeclaration.Flags&ast.NodeFlagsAmbient != 0 && (redirect == nil || !redirect.Resolved.CompilerOptions().ShouldPreserveConstEnums()) { - c.error(node, diagnostics.Cannot_access_ambient_const_enums_when_0_is_enabled, c.getIsolatedModulesLikeFlagName()) - } - } - } - if ast.IsImportSpecifier(node) { - targetSymbol := c.resolveAliasWithDeprecationCheck(symbol, node) - if c.isDeprecatedSymbol(targetSymbol) && targetSymbol.Declarations != nil { - c.addDeprecatedSuggestion(node, targetSymbol.Declarations, targetSymbol.Name) - } - } -} - -func (c *Checker) areDeclarationFlagsIdentical(left *ast.Declaration, right *ast.Declaration) bool { - if ast.IsParameter(left) && ast.IsVariableDeclaration(right) || ast.IsVariableDeclaration(left) && ast.IsParameter(right) { - // Differences in optionality between parameters and variables are allowed. - return true - } - if isOptionalDeclaration(left) != isOptionalDeclaration(right) { - return false - } - interestingFlags := ast.ModifierFlagsPrivate | ast.ModifierFlagsProtected | ast.ModifierFlagsAsync | ast.ModifierFlagsAbstract | ast.ModifierFlagsReadonly | ast.ModifierFlagsStatic - return getSelectedModifierFlags(left, interestingFlags) == getSelectedModifierFlags(right, interestingFlags) -} - -func (c *Checker) checkTypeAliasDeclaration(node *ast.Node) { - // Grammar checking - c.checkGrammarModifiers(node) - c.checkTypeNameIsReserved(node.Name(), diagnostics.Type_alias_name_cannot_be_0) - if !c.containerAllowsBlockScopedVariable(node.Parent) { - c.grammarErrorOnNode(node, diagnostics.X_0_declarations_can_only_be_declared_inside_a_block, "type") - } - c.checkExportsOnMergedDeclarations(node) - - typeNode := node.AsTypeAliasDeclaration().Type - typeParameters := node.TypeParameters() - c.checkTypeParameters(typeParameters) - if typeNode != nil && typeNode.Kind == ast.KindIntrinsicKeyword { - if !(len(typeParameters) == 0 && node.Name().Text() == "BuiltinIteratorReturn" || - len(typeParameters) == 1 && intrinsicTypeKinds[node.Name().Text()] != IntrinsicTypeKindUnknown) { - c.error(typeNode, diagnostics.The_intrinsic_keyword_can_only_be_used_to_declare_compiler_provided_intrinsic_types) - } - return - } - c.checkSourceElement(typeNode) - c.registerForUnusedIdentifiersCheck(node) -} - -func (c *Checker) checkTypeNameIsReserved(name *ast.Node, message *diagnostics.Message) { - // TS 1.0 spec (April 2014): 3.6.1 - // The predefined type keywords are reserved and cannot be used as names of user defined types. - switch name.Text() { - case "any", "unknown", "never", "number", "bigint", "boolean", "string", "symbol", "void", "object", "undefined": - c.error(name, message, name.Text()) - } -} - -func (c *Checker) checkExportsOnMergedDeclarations(node *ast.Node) { - // If localSymbol is defined on node then node itself is exported - check is required. - symbol := node.LocalSymbol() - if symbol == nil { - // Local symbol is undefined => this declaration is non-exported. - // However, symbol might contain other declarations that are exported. - symbol = c.getSymbolOfDeclaration(node) - if symbol.ExportSymbol == nil { - // This is a pure local symbol (all declarations are non-exported) - no need to check anything. - return - } - } - // Run the check only for the first declaration in the list. - if ast.GetDeclarationOfKind(symbol, node.Kind) != node { - return - } - exportedDeclarationSpaces := DeclarationSpacesNone - nonExportedDeclarationSpaces := DeclarationSpacesNone - defaultExportedDeclarationSpaces := DeclarationSpacesNone - for _, d := range symbol.Declarations { - declarationSpaces := c.getDeclarationSpaces(d) - effectiveDeclarationFlags := c.getEffectiveDeclarationFlags(d, ast.ModifierFlagsExport|ast.ModifierFlagsDefault) - if effectiveDeclarationFlags&ast.ModifierFlagsExport != 0 { - if effectiveDeclarationFlags&ast.ModifierFlagsDefault != 0 { - defaultExportedDeclarationSpaces |= declarationSpaces - } else { - exportedDeclarationSpaces |= declarationSpaces - } - } else { - nonExportedDeclarationSpaces |= declarationSpaces - } - } - // Spaces for anything not declared a 'default export'. - nonDefaultExportedDeclarationSpaces := exportedDeclarationSpaces | nonExportedDeclarationSpaces - commonDeclarationSpacesForExportsAndLocals := exportedDeclarationSpaces & nonExportedDeclarationSpaces - commonDeclarationSpacesForDefaultAndNonDefault := defaultExportedDeclarationSpaces & nonDefaultExportedDeclarationSpaces - if commonDeclarationSpacesForExportsAndLocals != 0 || commonDeclarationSpacesForDefaultAndNonDefault != 0 { - // declaration spaces for exported and non-exported declarations intersect - for _, d := range symbol.Declarations { - declarationSpaces := c.getDeclarationSpaces(d) - name := ast.GetNameOfDeclaration(d) - // Only error on the declarations that contributed to the intersecting spaces. - if declarationSpaces&commonDeclarationSpacesForDefaultAndNonDefault != 0 { - c.error(name, diagnostics.Merged_declaration_0_cannot_include_a_default_export_declaration_Consider_adding_a_separate_export_default_0_declaration_instead, scanner.DeclarationNameToString(name)) - } else if declarationSpaces&commonDeclarationSpacesForExportsAndLocals != 0 { - c.error(name, diagnostics.Individual_declarations_in_merged_declaration_0_must_be_all_exported_or_all_local, scanner.DeclarationNameToString(name)) - } - } - } -} - -func (c *Checker) getDeclarationSpaces(node *ast.Declaration) DeclarationSpaces { - switch node.Kind { - case ast.KindInterfaceDeclaration, ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration, ast.KindJSDocTypedefTag, ast.KindJSDocCallbackTag: - return DeclarationSpacesExportType - case ast.KindModuleDeclaration: - if ast.IsAmbientModule(node) || ast.GetModuleInstanceState(node) != ast.ModuleInstanceStateNonInstantiated { - return DeclarationSpacesExportNamespace | DeclarationSpacesExportValue - } - return DeclarationSpacesExportNamespace - case ast.KindClassDeclaration, ast.KindEnumDeclaration, ast.KindEnumMember: - return DeclarationSpacesExportType | DeclarationSpacesExportValue - case ast.KindSourceFile: - return DeclarationSpacesExportType | DeclarationSpacesExportValue | DeclarationSpacesExportNamespace - case ast.KindExportAssignment, ast.KindJSExportAssignment, ast.KindBinaryExpression: - var expression *ast.Node - if ast.IsExportAssignment(node) || ast.IsJSExportAssignment(node) { - expression = node.Expression() - } else { - expression = node.AsBinaryExpression().Right - } - // Export assigned entity name expressions act as aliases and should fall through, otherwise they export values. - if !ast.IsEntityNameExpression(expression) { - return DeclarationSpacesExportValue - } - node = expression - // The below options all declare an Alias, which is allowed to merge with other values within the importing module. - fallthrough - case ast.KindImportEqualsDeclaration, ast.KindNamespaceImport, ast.KindImportClause: - result := DeclarationSpacesNone - target := c.resolveAlias(c.getSymbolOfDeclaration(node)) - for _, d := range target.Declarations { - result |= c.getDeclarationSpaces(d) - } - return result - case ast.KindCommonJSExport: - return DeclarationSpacesExportValue - case ast.KindVariableDeclaration, ast.KindBindingElement, ast.KindFunctionDeclaration, ast.KindImportSpecifier: - return DeclarationSpacesExportValue - case ast.KindMethodSignature, ast.KindPropertySignature: - return DeclarationSpacesExportType - } - panic("Unhandled case in getDeclarationSpaces: " + node.Kind.String()) -} - -func (c *Checker) checkTypeParameters(typeParameterDeclarations []*ast.Node) { - seenDefault := false - for i, node := range typeParameterDeclarations { - c.checkTypeParameter(node) - defaultTypeNode := node.AsTypeParameter().DefaultType - if defaultTypeNode != nil { - seenDefault = true - c.checkTypeParametersNotReferenced(defaultTypeNode, typeParameterDeclarations, i) - } else if seenDefault { - c.error(node, diagnostics.Required_type_parameters_may_not_follow_optional_type_parameters) - } - for j := range i { - if typeParameterDeclarations[j].Symbol() == node.Symbol() { - c.error(node.Name(), diagnostics.Duplicate_identifier_0, scanner.DeclarationNameToString(node.Name())) - } - } - } -} - -// Check that type parameter defaults only reference previously declared type parameters */ -func (c *Checker) checkTypeParametersNotReferenced(root *ast.Node, typeParameters []*ast.Node, index int) { - var visit func(*ast.Node) bool - visit = func(node *ast.Node) bool { - if ast.IsTypeReferenceNode(node) { - t := c.getTypeFromTypeReference(node) - if t.flags&TypeFlagsTypeParameter != 0 { - for i := index; i < len(typeParameters); i++ { - if t.symbol == c.getSymbolOfDeclaration(typeParameters[i]) { - c.error(node, diagnostics.Type_parameter_defaults_can_only_reference_previously_declared_type_parameters) - } - } - } - } - return node.ForEachChild(visit) - } - visit(root) -} - -func (c *Checker) registerForUnusedIdentifiersCheck(node *ast.Node) { - sourceFile := ast.GetSourceFileOfNode(node) - links := c.sourceFileLinks.Get(sourceFile) - links.identifierCheckNodes = append(links.identifierCheckNodes, node) -} - -func (c *Checker) checkUnusedIdentifiers(potentiallyUnusedIdentifiers []*ast.Node) { - for _, node := range potentiallyUnusedIdentifiers { - switch node.Kind { - case ast.KindClassDeclaration, ast.KindClassExpression: - c.checkUnusedClassMembers(node) - c.checkUnusedTypeParameters(node) - case ast.KindSourceFile, ast.KindModuleDeclaration, ast.KindBlock, ast.KindCaseBlock, ast.KindForStatement, ast.KindForInStatement, - ast.KindForOfStatement: - c.checkUnusedLocalsAndParameters(node) - case ast.KindConstructor, ast.KindFunctionExpression, ast.KindFunctionDeclaration, ast.KindArrowFunction, ast.KindMethodDeclaration, - ast.KindGetAccessor, ast.KindSetAccessor: - // Only report unused parameters on the implementation, not overloads. - if node.Body() != nil { - c.checkUnusedLocalsAndParameters(node) - } - c.checkUnusedTypeParameters(node) - case ast.KindMethodSignature, ast.KindCallSignature, ast.KindConstructSignature, ast.KindFunctionType, ast.KindConstructorType, - ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration, ast.KindInterfaceDeclaration: - c.checkUnusedTypeParameters(node) - case ast.KindInferType: - c.checkUnusedInferTypeParameter(node) - default: - panic("Unhandled case in checkUnusedIdentifiers") - } - } -} - -func (c *Checker) isReferenced(symbol *ast.Symbol) bool { - return c.symbolReferenceLinks.Get(symbol).referenceKinds != 0 -} - -type UnusedKind int32 - -const ( - UnusedKindLocal UnusedKind = iota - UnusedKindParameter -) - -func (c *Checker) reportUnusedVariable(location *ast.Node, diagnostic *ast.Diagnostic) { - for ast.IsBindingElement(location) || ast.IsBindingPattern(location) { - location = location.Parent - } - c.reportUnused(location, core.IfElse(ast.IsParameter(location), UnusedKindParameter, UnusedKindLocal), diagnostic) -} - -func (c *Checker) reportUnused(location *ast.Node, kind UnusedKind, diagnostic *ast.Diagnostic) { - if location.Flags&(ast.NodeFlagsAmbient|ast.NodeFlagsThisNodeOrAnySubNodesHasError) == 0 && - (kind == UnusedKindLocal && c.compilerOptions.NoUnusedLocals.IsTrue() || - (kind == UnusedKindParameter && c.compilerOptions.NoUnusedParameters.IsTrue())) { - c.diagnostics.Add(diagnostic) - } -} - -func (c *Checker) checkUnusedClassMembers(node *ast.Node) { - for _, member := range node.Members() { - switch member.Kind { - case ast.KindMethodDeclaration, ast.KindPropertyDeclaration, ast.KindGetAccessor, ast.KindSetAccessor: - if ast.IsSetAccessorDeclaration(member) && member.Symbol().Flags&ast.SymbolFlagsGetAccessor != 0 { - break // Already would have reported an error on the getter. - } - symbol := c.getSymbolOfDeclaration(member) - if !c.isReferenced(symbol) && (ast.HasModifier(member, ast.ModifierFlagsPrivate) || member.Name() != nil && ast.IsPrivateIdentifier(member.Name())) && member.Flags&ast.NodeFlagsAmbient == 0 { - c.reportUnused(member, UnusedKindLocal, NewDiagnosticForNode(member.Name(), diagnostics.X_0_is_declared_but_its_value_is_never_read, c.symbolToString(symbol))) - } - case ast.KindConstructor: - for _, parameter := range member.AsConstructorDeclaration().Parameters.Nodes { - if !c.isReferenced(parameter.Symbol()) && ast.HasSyntacticModifier(parameter, ast.ModifierFlagsPrivate) { - c.reportUnused(parameter, UnusedKindLocal, NewDiagnosticForNode(parameter.Name(), diagnostics.Property_0_is_declared_but_its_value_is_never_read, ast.SymbolName(parameter.Symbol()))) - } - } - case ast.KindIndexSignature, ast.KindSemicolonClassElement, ast.KindClassStaticBlockDeclaration: - // Can't be private - default: - panic("Unhandled case in checkUnusedClassMembers") - } - } -} - -func (c *Checker) checkUnusedLocalsAndParameters(node *ast.Node) { - var variableParents collections.Set[*ast.Node] - var importClauses map[*ast.Node][]*ast.Node - for _, local := range node.Locals() { - referenceKinds := c.symbolReferenceLinks.Get(local).referenceKinds - if local.Flags&ast.SymbolFlagsTypeParameter != 0 && (local.Flags&ast.SymbolFlagsVariable == 0 || referenceKinds&ast.SymbolFlagsVariable != 0) || - local.Flags&ast.SymbolFlagsTypeParameter == 0 && (referenceKinds != 0 || local.ExportSymbol != nil) { - continue - } - for _, declaration := range local.Declarations { - switch { - case ast.IsVariableDeclaration(declaration) || ast.IsParameter(declaration) || ast.IsBindingElement(declaration): - variableParents.Add(ast.GetRootDeclaration(declaration).Parent) - case ast.IsImportClause(declaration) || ast.IsImportSpecifier(declaration) || ast.IsNamespaceImport(declaration): - if !isIdentifierThatStartsWithUnderscore(declaration.Name()) { - if importClauses == nil { - importClauses = make(map[*ast.Node][]*ast.Node) - } - importClause := importClauseFromImported(declaration) - importClauses[importClause] = append(importClauses[importClause], declaration) - } - default: - if !ast.IsTypeParameterDeclaration(declaration) && !ast.IsAmbientModule(declaration) { - c.reportUnusedLocal(declaration, ast.SymbolName(local)) - } - } - } - } - for declaration := range variableParents.Keys() { - if ast.IsVariableDeclarationList(declaration) { - c.reportUnusedVariables(declaration) - } else { - c.reportUnusedParameters(declaration) - } - } - for declaration, unuseds := range importClauses { - c.reportUnusedImports(declaration, unuseds) - } -} - -func (c *Checker) reportUnusedLocal(node *ast.Node, name string) { - message := core.IfElse(ast.IsTypeDeclaration(node), diagnostics.X_0_is_declared_but_never_used, diagnostics.X_0_is_declared_but_its_value_is_never_read) - c.reportUnused(node, UnusedKindLocal, NewDiagnosticForNode(core.OrElse(node.Name(), node), message, name)) -} - -func (c *Checker) reportUnusedVariables(node *ast.Node) { - declarations := node.AsVariableDeclarationList().Declarations.Nodes - if len(declarations) > 1 && core.Every(declarations, c.isUnreferencedVariableDeclaration) { - c.reportUnusedVariable(node, NewDiagnosticForNode(node, diagnostics.All_variables_are_unused)) - } else { - c.reportUnusedVariableDeclarations(declarations) - } -} - -func (c *Checker) reportUnusedParameters(node *ast.Node) { - c.reportUnusedVariableDeclarations(node.Parameters()) -} - -func (c *Checker) reportUnusedBindingElements(node *ast.Node) { - declarations := node.AsBindingPattern().Elements.Nodes - if len(declarations) > 1 && core.Every(declarations, c.isUnreferencedVariableDeclaration) { - c.reportUnusedVariable(node, NewDiagnosticForNode(node, diagnostics.All_destructured_elements_are_unused)) - } else { - c.reportUnusedVariableDeclarations(declarations) - } -} - -func (c *Checker) reportUnusedVariableDeclarations(declarations []*ast.Node) { - for _, declaration := range declarations { - name := declaration.Name() - if name != nil && !ast.IsParameterPropertyDeclaration(declaration, declaration.Parent) && !ast.IsThisParameter(declaration) { - if ast.IsBindingPattern(name) { - c.reportUnusedBindingElements(name) - } else if c.isUnreferencedVariableDeclaration(declaration) { - c.reportUnusedVariable(declaration, NewDiagnosticForNode(name, diagnostics.X_0_is_declared_but_its_value_is_never_read, name.Text())) - } - } - } -} - -func (c *Checker) isUnreferencedVariableDeclaration(node *ast.Node) bool { - name := node.Name() - if name == nil { - return true - } - if ast.IsBindingPattern(name) { - return core.Every(node.Name().AsBindingPattern().Elements.Nodes, c.isUnreferencedVariableDeclaration) - } - if c.symbolReferenceLinks.Get(c.getSymbolOfDeclaration(node)).referenceKinds&ast.SymbolFlagsVariable != 0 { - return false - } - if ast.IsBindingElement(node) && ast.IsObjectBindingPattern(node.Parent) { - // In `{ a, ...b }, `a` is considered used since it removes a property from `b`. `b` may still be unused though. - lastElement := core.LastOrNil(node.Parent.AsBindingPattern().Elements.Nodes) - if node != lastElement && hasDotDotDotToken(lastElement) { - return false - } - } - if (ast.IsParameter(node) || - ast.IsVariableDeclaration(node) && (ast.IsForInOrOfStatement(node.Parent.Parent) || c.getCombinedNodeFlagsCached(node)&ast.NodeFlagsUsing != 0) || - ast.IsBindingElement(node) && !(ast.IsObjectBindingPattern(node.Parent) && node.PropertyName() == nil)) && - isIdentifierThatStartsWithUnderscore(name) { - return false - } - return true -} - -func (c *Checker) reportUnusedImports(node *ast.Node, unuseds []*ast.Node) { - declarationCount := core.IfElse(node.Name() != nil, 1, 0) - namedBindings := node.AsImportClause().NamedBindings - if namedBindings != nil { - if ast.IsNamespaceImport(namedBindings) { - declarationCount++ - } else { - declarationCount += len(namedBindings.AsNamedImports().Elements.Nodes) - } - } - if declarationCount > 1 && declarationCount == len(unuseds) { - c.reportUnused(node, UnusedKindLocal, NewDiagnosticForNode(node.Parent, diagnostics.All_imports_in_import_declaration_are_unused)) - } else { - for _, unused := range unuseds { - c.reportUnusedLocal(unused, unused.Name().Text()) - } - } -} - -func isIdentifierThatStartsWithUnderscore(node *ast.Node) bool { - return ast.IsIdentifier(node) && node.Text() != "" && node.Text()[0] == '_' -} - -func importClauseFromImported(node *ast.Node) *ast.Node { - switch node.Kind { - case ast.KindImportClause: - return node - case ast.KindNamespaceImport: - return node.Parent - default: - return node.Parent.Parent - } -} - -func (c *Checker) checkUnusedInferTypeParameter(node *ast.Node) { - typeParameter := node.AsInferTypeNode().TypeParameter - if c.isUnreferencedTypeParameter(typeParameter) { - c.reportUnused(node, UnusedKindParameter, NewDiagnosticForNode(typeParameter.Name(), diagnostics.X_0_is_declared_but_never_used, typeParameter.Name().Text())) - } -} - -func (c *Checker) checkUnusedTypeParameters(node *ast.Node) { - if !allDeclarationsInSameSourceFile(c.getSymbolOfDeclaration(node)) { - return - } - typeParameterList := node.TypeParameterList() - if typeParameterList == nil { - return - } - if len(typeParameterList.Nodes) > 1 && core.Every(typeParameterList.Nodes, c.isUnreferencedTypeParameter) { - file := ast.GetSourceFileOfNode(node) - loc := rangeOfTypeParameters(file, typeParameterList) - c.reportUnused(node, UnusedKindParameter, ast.NewDiagnostic(file, loc, diagnostics.All_type_parameters_are_unused)) - } else { - for _, typeParameter := range typeParameterList.Nodes { - if c.isUnreferencedTypeParameter(typeParameter) { - c.reportUnused(node, UnusedKindParameter, NewDiagnosticForNode(typeParameter, diagnostics.X_0_is_declared_but_never_used, typeParameter.Name().Text())) - } - } - } -} - -func (c *Checker) isUnreferencedTypeParameter(typeParameter *ast.Node) bool { - return c.symbolReferenceLinks.Get(c.getMergedSymbol(typeParameter.Symbol())).referenceKinds&ast.SymbolFlagsTypeParameter == 0 && !isIdentifierThatStartsWithUnderscore(typeParameter.Name()) -} - -func (c *Checker) checkUnusedRenamedBindingElements() { - for _, node := range c.renamedBindingElementsInTypes { - if c.symbolReferenceLinks.Get(c.getSymbolOfDeclaration(node)).referenceKinds == 0 { - wrappingDeclaration := ast.WalkUpBindingElementsAndPatterns(node) - debug.Assert(ast.IsPartOfParameterDeclaration(wrappingDeclaration), "Only parameter declaration should be checked here") - diagnostic := NewDiagnosticForNode(node.Name(), diagnostics.X_0_is_an_unused_renaming_of_1_Did_you_intend_to_use_it_as_a_type_annotation, scanner.DeclarationNameToString(node.Name()), scanner.DeclarationNameToString(node.PropertyName())) - if wrappingDeclaration.Type() == nil { - // entire parameter does not have type annotation, suggest adding an annotation - diagnostic.AddRelatedInfo(ast.NewDiagnostic(ast.GetSourceFileOfNode(wrappingDeclaration), core.NewTextRange(wrappingDeclaration.End(), wrappingDeclaration.End()), diagnostics.We_can_only_write_a_type_for_0_by_adding_a_type_for_the_entire_parameter_here, scanner.DeclarationNameToString(node.PropertyName()))) - } - c.diagnostics.Add(diagnostic) - } - } -} - -func (c *Checker) checkExpressionStatement(node *ast.Node) { - // Grammar checking - c.checkGrammarStatementInAmbientContext(node) - c.checkExpression(node.AsExpressionStatement().Expression) -} - -// 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 *ast.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 cachedType := c.flowTypeCache[node]; cachedType != nil { - 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 { - if c.flowTypeCache == nil { - c.flowTypeCache = make(map[*ast.Node]*Type) - } - c.flowTypeCache[node] = t - } - 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 *ast.Node) *Type { - expr := ast.SkipParentheses(node) - switch { - case ast.IsAwaitExpression(expr): - t := c.getQuickTypeOfExpression(expr.Expression()) - if t != nil { - return c.getAwaitedType(t) - } - return nil - // Optimize for the common case of a call to a function with a single non-generic call - // signature where we can just fetch the return type without checking the arguments. - case ast.IsCallExpression(expr) && expr.Expression().Kind != ast.KindSuperKeyword && !ast.IsRequireCall(expr, true /*requireStringLiteralLikeArgument*/) && !c.isSymbolOrSymbolForCall(expr) && !ast.IsImportCall(expr): - if isCallChain(expr) { - return c.getReturnTypeOfSingleNonGenericSignatureOfCallChain(expr) - } - return c.getReturnTypeOfSingleNonGenericSignature(c.checkNonNullExpression(expr.Expression()), SignatureKindCall) - case ast.IsNewExpression(expr): - return c.getReturnTypeOfSingleNonGenericSignature(c.checkNonNullExpression(expr.Expression()), SignatureKindConstruct) - case ast.IsAssertionExpression(expr) && !ast.IsConstTypeReference(expr.Type()): - return c.getTypeFromTypeNode(expr.Type()) - case ast.IsLiteralExpression(node) || ast.IsBooleanLiteral(node): - return c.checkExpression(node) - } - return nil -} - -func (c *Checker) getReturnTypeOfSingleNonGenericSignature(funcType *Type, kind SignatureKind) *Type { - signature := c.getSingleSignature(funcType, kind, true /*allowMembers*/) - if signature != nil && len(signature.typeParameters) == 0 { - return c.getReturnTypeOfSignature(signature) - } - return nil -} - -func (c *Checker) getReturnTypeOfSingleNonGenericSignatureOfCallChain(expr *ast.Node) *Type { - funcType := c.checkExpression(expr.Expression()) - nonOptionalType := c.getOptionalExpressionType(funcType, expr.Expression()) - returnType := c.getReturnTypeOfSingleNonGenericSignature(funcType, SignatureKindCall) - if returnType != nil { - return c.propagateOptionalTypeMarker(returnType, expr, nonOptionalType != funcType) - } - return nil -} - -func (c *Checker) checkNonNullExpression(node *ast.Node) *Type { - return c.checkNonNullType(c.checkExpression(node), node) -} - -func (c *Checker) checkNonNullType(t *Type, node *ast.Node) *Type { - return c.checkNonNullTypeWithReporter(t, node, (*Checker).reportObjectPossiblyNullOrUndefinedError) -} - -func (c *Checker) checkNonNullTypeWithReporter(t *Type, node *ast.Node, reportError func(c *Checker, node *ast.Node, facts TypeFacts)) *Type { - if c.strictNullChecks && t.flags&TypeFlagsUnknown != 0 { - if ast.IsEntityNameExpression(node) { - nodeText := entityNameToString(node) - if len(nodeText) < 100 { - c.error(node, diagnostics.X_0_is_of_type_unknown, nodeText) - return c.errorType - } - } - c.error(node, diagnostics.Object_is_of_type_unknown) - return c.errorType - } - facts := c.getTypeFacts(t, TypeFactsIsUndefinedOrNull) - if facts&TypeFactsIsUndefinedOrNull != 0 { - reportError(c, node, facts) - nonNullable := c.GetNonNullableType(t) - if nonNullable.flags&(TypeFlagsNullable|TypeFlagsNever) != 0 { - return c.errorType - } - return nonNullable - } - return t -} - -func (c *Checker) checkNonNullNonVoidType(t *Type, node *ast.Node) *Type { - nonNullType := c.checkNonNullType(t, node) - if nonNullType.flags&TypeFlagsVoid != 0 { - if ast.IsEntityNameExpression(node) { - nodeText := entityNameToString(node) - if ast.IsIdentifier(node) && nodeText == "undefined" { - c.error(node, diagnostics.The_value_0_cannot_be_used_here, nodeText) - return nonNullType - } - if len(nodeText) < 100 { - c.error(node, diagnostics.X_0_is_possibly_undefined, nodeText) - return nonNullType - } - } - c.error(node, diagnostics.Object_is_possibly_undefined) - } - return nonNullType -} - -func (c *Checker) reportObjectPossiblyNullOrUndefinedError(node *ast.Node, facts TypeFacts) { - var nodeText string - if ast.IsEntityNameExpression(node) { - nodeText = entityNameToString(node) - } - if node.Kind == ast.KindNullKeyword { - c.error(node, diagnostics.The_value_0_cannot_be_used_here, "null") - return - } - if nodeText != "" && len(nodeText) < 100 { - if ast.IsIdentifier(node) && nodeText == "undefined" { - c.error(node, diagnostics.The_value_0_cannot_be_used_here, "undefined") - return - } - c.error(node, core.IfElse(facts&TypeFactsIsUndefined != 0, - core.IfElse(facts&TypeFactsIsNull != 0, - diagnostics.X_0_is_possibly_null_or_undefined, - diagnostics.X_0_is_possibly_undefined), - diagnostics.X_0_is_possibly_null), nodeText) - } else { - c.error(node, core.IfElse(facts&TypeFactsIsUndefined != 0, - core.IfElse(facts&TypeFactsIsNull != 0, - diagnostics.Object_is_possibly_null_or_undefined, - diagnostics.Object_is_possibly_undefined), - diagnostics.Object_is_possibly_null)) - } -} - -func (c *Checker) checkExpressionWithContextualType(node *ast.Node, contextualType *Type, inferenceContext *InferenceContext, checkMode CheckMode) *Type { - contextNode := c.getContextNode(node) - c.pushContextualType(contextNode, contextualType, false /*isCache*/) - c.pushInferenceContext(contextNode, inferenceContext) - t := c.checkExpressionEx(node, checkMode|CheckModeContextual|core.IfElse(inferenceContext != nil, CheckModeInferential, 0)) - // In CheckMode.Inferential we collect intra-expression inference sites to process before fixing any type - // parameters. This information is no longer needed after the call to checkExpression. - if inferenceContext != nil && inferenceContext.intraExpressionInferenceSites != nil { - inferenceContext.intraExpressionInferenceSites = nil - } - // We strip literal freshness when an appropriate contextual type is present such that contextually typed - // literals always preserve their literal types (otherwise they might widen during type inference). An alternative - // here would be to not mark contextually typed literals as fresh in the first place. - if c.maybeTypeOfKind(t, TypeFlagsLiteral) && c.isLiteralOfContextualType(t, c.instantiateContextualType(contextualType, node, ContextFlagsNone)) { - t = c.getRegularTypeOfLiteralType(t) - } - c.popInferenceContext() - c.popContextualType() - return t -} - -func (c *Checker) getContextNode(node *ast.Node) *ast.Node { - if ast.IsJsxAttributes(node) && !ast.IsJsxSelfClosingElement(node.Parent) { - // Needs to be the root JsxElement, so it encompasses the attributes _and_ the children (which are essentially part of the attributes) - return node.Parent.Parent - } - return node -} - -func (c *Checker) checkExpressionCached(node *ast.Node) *Type { - return c.checkExpressionCachedEx(node, CheckModeNormal) -} - -func (c *Checker) checkExpressionCachedEx(node *ast.Node, checkMode CheckMode) *Type { - if checkMode != CheckModeNormal { - return c.checkExpressionEx(node, checkMode) - } - links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { - // When computing a type that we're going to cache, we need to ignore any ongoing control flow - // analysis because variables may have transient types in indeterminable states. Moving flowLoopStart - // to the top of the stack ensures all transient types are computed from a known point. - saveFlowLoopStack := c.flowLoopStack - saveFlowTypeCache := c.flowTypeCache - c.flowLoopStack = nil - c.flowTypeCache = nil - links.resolvedType = c.checkExpressionEx(node, checkMode) - c.flowTypeCache = saveFlowTypeCache - c.flowLoopStack = saveFlowLoopStack - } - return links.resolvedType -} - -// 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. -// It is intended for uses where you know there is no contextual type, -// and requesting the contextual type might cause a circularity or other bad behaviour. -// It sets the contextual type of the node to any before calling getTypeOfExpression. -func (c *Checker) getContextFreeTypeOfExpression(node *ast.Node) *Type { - if cached := c.contextFreeTypes[node]; cached != nil { - return cached - } - c.pushContextualType(node, c.anyType, false /*isCache*/) - t := c.checkExpressionEx(node, CheckModeSkipContextSensitive) - c.contextFreeTypes[node] = t - c.popContextualType() - return t -} - -func (c *Checker) checkExpression(node *ast.Node) *Type { - return c.checkExpressionEx(node, CheckModeNormal) -} - -func (c *Checker) checkExpressionEx(node *ast.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(t) { - c.checkConstEnumAccess(node, t) - } - c.currentNode = saveCurrentNode - return t -} - -func (c *Checker) checkConstEnumAccess(node *ast.Node, t *Type) { - // enum object type for const enums are only permitted in: - // - 'left' in property access - // - 'object' in indexed access - // - target in rhs of import statement - ok := ast.IsPropertyAccessExpression(node.Parent) && node.Parent.Expression() == node || - ast.IsElementAccessExpression(node.Parent) && node.Parent.Expression() == node || - ((ast.IsIdentifier(node) || ast.IsQualifiedName(node)) && isInRightSideOfImportOrExportAssignment(node) || - ast.IsTypeQueryNode(node.Parent) && node.Parent.AsTypeQueryNode().ExprName == node) || - ast.IsExportSpecifier(node.Parent) // We allow reexporting const enums - if !ok { - c.error(node, diagnostics.X_const_enums_can_only_be_used_in_property_or_index_access_expressions_or_the_right_hand_side_of_an_import_declaration_or_export_assignment_or_type_query) - } - // --verbatimModuleSyntax only gets checked here when the enum usage does not - // resolve to an import, because imports of ambient const enums get checked - // separately in `checkAliasSymbol`. - if c.compilerOptions.IsolatedModules.IsTrue() || c.compilerOptions.VerbatimModuleSyntax.IsTrue() && ok && c.resolveName(node, ast.GetFirstIdentifier(node).Text(), ast.SymbolFlagsAlias, nil, false, true) == nil { - debug.Assert(t.symbol.Flags&ast.SymbolFlagsConstEnum != 0) - constEnumDeclaration := t.symbol.ValueDeclaration - redirect := c.program.GetProjectReferenceFromOutputDts(ast.GetSourceFileOfNode(constEnumDeclaration).Path()) - if constEnumDeclaration.Flags&ast.NodeFlagsAmbient != 0 && !ast.IsValidTypeOnlyAliasUseSite(node) && (redirect == nil || !redirect.Resolved.CompilerOptions().ShouldPreserveConstEnums()) { - c.error(node, diagnostics.Cannot_access_ambient_const_enums_when_0_is_enabled, c.getIsolatedModulesLikeFlagName()) - } - } -} - -func (c *Checker) instantiateTypeWithSingleGenericCallSignature(node *ast.Node, t *Type, checkMode CheckMode) *Type { - if checkMode&(CheckModeInferential|CheckModeSkipGenericFunctions) == 0 { - return t - } - callSignature := c.getSingleSignature(t, SignatureKindCall, true /*allowMembers*/) - constructSignature := c.getSingleSignature(t, SignatureKindConstruct, true /*allowMembers*/) - signature := core.OrElse(callSignature, constructSignature) - if signature == nil || len(signature.typeParameters) == 0 { - return t - } - contextualType := c.getApparentTypeOfContextualType(node, ContextFlagsNoConstraints) - if contextualType == nil { - return t - } - contextualSignature := c.getSingleSignature(c.GetNonNullableType(contextualType), core.IfElse(callSignature != nil, SignatureKindCall, SignatureKindConstruct), false /*allowMembers*/) - if contextualSignature == nil || len(contextualSignature.typeParameters) != 0 { - return t - } - if checkMode&CheckModeSkipGenericFunctions != 0 { - c.skippedGenericFunction(node, checkMode) - return c.anyFunctionType - } - context := c.getInferenceContext(node) - // We have an expression that is an argument of a generic function for which we are performing - // type argument inference. The expression is of a function type with a single generic call - // signature and a contextual function type with a single non-generic call signature. Now check - // if the outer function returns a function type with a single non-generic call signature and - // if some of the outer function type parameters have no inferences so far. If so, we can - // potentially add inferred type parameters to the outer function return type. - var returnSignature *Signature - if context.signature != nil { - returnType := c.getReturnTypeOfSignature(context.signature) - if returnType != nil { - returnSignature = c.getSingleCallOrConstructSignature(returnType) - } - } - if returnSignature != nil && len(returnSignature.typeParameters) == 0 && !core.Every(context.inferences, hasInferenceCandidates) { - // Instantiate the signature with its own type parameters as type arguments, possibly - // renaming the type parameters to ensure they have unique names. - uniqueTypeParameters := c.getUniqueTypeParameters(context, signature.typeParameters) - instantiatedSignature := c.getSignatureInstantiationWithoutFillingInTypeArguments(signature, uniqueTypeParameters) - // Infer from the parameters of the instantiated signature to the parameters of the - // contextual signature starting with an empty set of inference candidates. - inferences := core.Map(context.inferences, func(info *InferenceInfo) *InferenceInfo { - return newInferenceInfo(info.typeParameter) - }) - c.applyToParameterTypes(instantiatedSignature, contextualSignature, func(source *Type, target *Type) { - c.inferTypes(inferences, source, target, InferencePriorityNone, true /*contravariant*/) - }) - if core.Some(inferences, hasInferenceCandidates) { - // We have inference candidates, indicating that one or more type parameters are referenced - // in the parameter types of the contextual signature. Now also infer from the return type. - c.applyToReturnTypes(instantiatedSignature, contextualSignature, func(source *Type, target *Type) { - c.inferTypes(inferences, source, target, InferencePriorityNone, false) - }) - // If the type parameters for which we produced candidates do not have any inferences yet, - // we adopt the new inference candidates and add the type parameters of the expression type - // to the set of inferred type parameters for the outer function return type. - if !hasOverlappingInferences(context.inferences, inferences) { - c.mergeInferences(context.inferences, inferences) - context.inferredTypeParameters = core.Concatenate(context.inferredTypeParameters, uniqueTypeParameters) - return c.getOrCreateTypeFromSignature(instantiatedSignature) - } - } - } - // TODO: The signature may reference any outer inference contexts, but we map pop off and then apply new inference contexts, - // and thus get different inferred types. That this is cached on the *first* such attempt is not currently an issue, since expression - // types *also* get cached on the first pass. If we ever properly speculate, though, the cached "isolatedSignatureType" signature - // field absolutely needs to be included in the list of speculative caches. - return c.getOrCreateTypeFromSignature(c.instantiateSignatureInContextOf(signature, contextualSignature, context, nil)) -} - -func (c *Checker) getOuterInferenceTypeParameters() []*Type { - var result []*Type - for i := range c.inferenceContextInfos { - context := c.inferenceContextInfos[i].context - if context != nil { - for _, info := range context.inferences { - result = append(result, info.typeParameter) - } - } - } - return result -} - -func (c *Checker) getUniqueTypeParameters(context *InferenceContext, typeParameters []*Type) []*Type { - var oldTypeParameters []*Type - var newTypeParameters []*Type - result := make([]*Type, 0, len(typeParameters)) - for _, tp := range typeParameters { - name := tp.symbol.Name - if hasTypeParameterByName(context.inferredTypeParameters, name) || hasTypeParameterByName(result, name) { - newName := getUniqueTypeParameterName(core.Concatenate(context.inferredTypeParameters, result), name) - symbol := c.newSymbol(ast.SymbolFlagsTypeParameter, newName) - newTypeParameter := c.newTypeParameter(symbol) - newTypeParameter.AsTypeParameter().target = tp - oldTypeParameters = append(oldTypeParameters, tp) - newTypeParameters = append(newTypeParameters, newTypeParameter) - result = append(result, newTypeParameter) - } else { - result = append(result, tp) - } - } - if len(newTypeParameters) != 0 { - mapper := newTypeMapper(oldTypeParameters, newTypeParameters) - for _, tp := range newTypeParameters { - tp.AsTypeParameter().mapper = mapper - } - } - return result -} - -func hasTypeParameterByName(typeParameters []*Type, name string) bool { - return core.Some(typeParameters, func(tp *Type) bool { - return tp.symbol.Name == name - }) -} - -func getUniqueTypeParameterName(typeParameters []*Type, baseName string) string { - for len(baseName) > 1 && baseName[len(baseName)-1] >= '0' && baseName[len(baseName)-1] <= '9' { - baseName = baseName[:len(baseName)-1] - } - index := 1 - for { - augmentedName := baseName + strconv.Itoa(index) - if !hasTypeParameterByName(typeParameters, augmentedName) { - return augmentedName - } - index++ - } -} - -func (c *Checker) checkExpressionWorker(node *ast.Node, checkMode CheckMode) *Type { - switch node.Kind { - case ast.KindIdentifier: - return c.checkIdentifier(node, checkMode) - case ast.KindPrivateIdentifier: - return c.checkPrivateIdentifierExpression(node) - case ast.KindThisKeyword: - return c.checkThisExpression(node) - case ast.KindSuperKeyword: - return c.checkSuperExpression(node) - case ast.KindNullKeyword: - return c.nullWideningType - case ast.KindStringLiteral, ast.KindNoSubstitutionTemplateLiteral: - if c.isSkipDirectInferenceNode(node) { - return c.blockedStringType - } - return c.getFreshTypeOfLiteralType(c.getStringLiteralType(node.Text())) - case ast.KindNumericLiteral: - c.checkGrammarNumericLiteral(node.AsNumericLiteral()) - return c.getFreshTypeOfLiteralType(c.getNumberLiteralType(jsnum.FromString(node.Text()))) - case ast.KindBigIntLiteral: - c.checkGrammarBigIntLiteral(node.AsBigIntLiteral()) - return c.getFreshTypeOfLiteralType(c.getBigIntLiteralType(jsnum.NewPseudoBigInt(jsnum.ParsePseudoBigInt(node.Text()), false /*negative*/))) - case ast.KindTrueKeyword: - return c.trueType - case ast.KindFalseKeyword: - return c.falseType - case ast.KindTemplateExpression: - return c.checkTemplateExpression(node) - case ast.KindRegularExpressionLiteral: - return c.checkRegularExpressionLiteral(node) - case ast.KindArrayLiteralExpression: - return c.checkArrayLiteral(node, checkMode) - case ast.KindObjectLiteralExpression: - return c.checkObjectLiteral(node, checkMode) - case ast.KindPropertyAccessExpression: - return c.checkPropertyAccessExpression(node, checkMode, false /*writeOnly*/) - case ast.KindQualifiedName: - return c.checkQualifiedName(node, checkMode) - case ast.KindElementAccessExpression: - return c.checkIndexedAccess(node, checkMode) - case ast.KindCallExpression: - if ast.IsImportCall(node) { - return c.checkImportCallExpression(node) - } - return c.checkCallExpression(node, checkMode) - case ast.KindNewExpression: - return c.checkCallExpression(node, checkMode) - case ast.KindTaggedTemplateExpression: - return c.checkTaggedTemplateExpression(node) - case ast.KindParenthesizedExpression: - return c.checkParenthesizedExpression(node, checkMode) - case ast.KindClassExpression: - return c.checkClassExpression(node) - case ast.KindFunctionExpression, ast.KindArrowFunction: - return c.checkFunctionExpressionOrObjectLiteralMethod(node, checkMode) - case ast.KindTypeAssertionExpression, ast.KindAsExpression: - return c.checkAssertion(node, checkMode) - case ast.KindTypeOfExpression: - return c.checkTypeOfExpression(node) - case ast.KindNonNullExpression: - return c.checkNonNullAssertion(node) - case ast.KindExpressionWithTypeArguments: - return c.checkExpressionWithTypeArguments(node) - case ast.KindSatisfiesExpression: - return c.checkSatisfiesExpression(node) - case ast.KindMetaProperty: - return c.checkMetaProperty(node) - case ast.KindDeleteExpression: - return c.checkDeleteExpression(node) - case ast.KindVoidExpression: - return c.checkVoidExpression(node) - case ast.KindAwaitExpression: - return c.checkAwaitExpression(node) - case ast.KindPrefixUnaryExpression: - return c.checkPrefixUnaryExpression(node) - case ast.KindPostfixUnaryExpression: - return c.checkPostfixUnaryExpression(node) - case ast.KindBinaryExpression: - return c.checkBinaryExpression(node, checkMode) - case ast.KindConditionalExpression: - return c.checkConditionalExpression(node, checkMode) - case ast.KindSpreadElement: - return c.checkSpreadExpression(node, checkMode) - case ast.KindOmittedExpression: - return c.undefinedWideningType - case ast.KindYieldExpression: - return c.checkYieldExpression(node) - case ast.KindSyntheticExpression: - return c.checkSyntheticExpression(node) - case ast.KindJsxExpression: - return c.checkJsxExpression(node, checkMode) - case ast.KindJsxElement: - return c.checkJsxElement(node, checkMode) - case ast.KindJsxSelfClosingElement: - return c.checkJsxSelfClosingElement(node, checkMode) - case ast.KindJsxFragment: - return c.checkJsxFragment(node) - case ast.KindJsxAttributes: - return c.checkJsxAttributes(node, checkMode) - case ast.KindJsxOpeningElement: - panic("Should never directly check a JsxOpeningElement") - } - return c.errorType -} - -func (c *Checker) checkPrivateIdentifierExpression(node *ast.Node) *Type { - c.checkGrammarPrivateIdentifierExpression(node.AsPrivateIdentifier()) - symbol := c.getSymbolForPrivateIdentifierExpression(node) - if symbol != nil { - c.markPropertyAsReferenced(symbol, nil /*nodeForCheckWriteOnly*/, false /*isSelfTypeAccess*/) - } - return c.anyType -} - -func (c *Checker) getSymbolForPrivateIdentifierExpression(node *ast.Node) *ast.Symbol { - links := c.symbolNodeLinks.Get(node) - if links.resolvedSymbol == nil { - links.resolvedSymbol = c.lookupSymbolForPrivateIdentifierDeclaration(node.Text(), node) - } - return links.resolvedSymbol -} - -func (c *Checker) checkSuperExpression(node *ast.Node) *Type { - isCallExpression := ast.IsCallExpression(node.Parent) && node.Parent.Expression() == node - immediateContainer := getSuperContainer(node, true /*stopOnFunctions*/) - container := immediateContainer - - // adjust the container reference in case if super is used inside arrow functions with arbitrarily deep nesting - if !isCallExpression { - for container != nil && ast.IsArrowFunction(container) { - container = getSuperContainer(container, true /*stopOnFunctions*/) - } - } - - isLegalUsageOfSuperExpression := func() bool { - if isCallExpression { - // TS 1.0 SPEC (April 2014): 4.8.1 - // Super calls are only permitted in constructors of derived classes - return ast.IsConstructorDeclaration(container) - } - // TS 1.0 SPEC (April 2014) - // 'super' property access is allowed - // - In a constructor, instance member function, instance member accessor, or instance member variable initializer where this references a derived class instance - // - In a static member function or static member accessor - - // topmost container must be something that is directly nested in the class declaration\object literal expression - if ast.IsClassLike(container.Parent) || ast.IsObjectLiteralExpression(container.Parent) { - if ast.IsStatic(container) { - return ast.NodeKindIs(container, ast.KindMethodDeclaration, ast.KindMethodSignature, ast.KindGetAccessor, ast.KindSetAccessor, ast.KindPropertyDeclaration, ast.KindClassStaticBlockDeclaration) - } - return ast.NodeKindIs(container, ast.KindMethodDeclaration, ast.KindMethodSignature, ast.KindGetAccessor, ast.KindSetAccessor, ast.KindPropertyDeclaration, ast.KindPropertySignature, ast.KindConstructor) - } - return false - } - - if container == nil || !isLegalUsageOfSuperExpression() { - // issue more specific error if super is used in computed property name - // class A { foo() { return "1" }} - // class B { - // [super.foo()]() {} - // } - current := ast.FindAncestorOrQuit(node, func(n *ast.Node) ast.FindAncestorResult { - if n == container { - return ast.FindAncestorQuit - } - if ast.IsComputedPropertyName(n) { - return ast.FindAncestorTrue - } - return ast.FindAncestorFalse - }) - switch { - case current != nil && ast.IsComputedPropertyName(current): - c.error(node, diagnostics.X_super_cannot_be_referenced_in_a_computed_property_name) - case isCallExpression: - c.error(node, diagnostics.Super_calls_are_not_permitted_outside_constructors_or_in_nested_functions_inside_constructors) - case container == nil || container.Parent == nil || !(ast.IsClassLike(container.Parent) || ast.IsObjectLiteralExpression(container.Parent)): - c.error(node, diagnostics.X_super_can_only_be_referenced_in_members_of_derived_classes_or_object_literal_expressions) - default: - c.error(node, diagnostics.X_super_property_access_is_permitted_only_in_a_constructor_member_function_or_member_accessor_of_a_derived_class) - } - return c.errorType - } - if !isCallExpression && ast.IsConstructorDeclaration(immediateContainer) { - c.checkThisBeforeSuper(node, container, diagnostics.X_super_must_be_called_before_accessing_a_property_of_super_in_the_constructor_of_a_derived_class) - } - // !!! - // nodeCheckFlag := NodeCheckFlagsNone - // if ast.IsStatic(container) || isCallExpression { - // nodeCheckFlag = NodeCheckFlagsSuperStatic - // if !isCallExpression && c.languageVersion >= core.ScriptTargetES2015 && c.languageVersion <= core.ScriptTargetES2021 && (ast.IsPropertyDeclaration(container) || ast.IsClassStaticBlockDeclaration(container)) { - // // for `super.x` or `super[x]` in a static initializer, mark all enclosing - // // block scope containers so that we can report potential collisions with - // // `Reflect`. - // forEachEnclosingBlockScopeContainer(node.Parent, func(current *ast.Node) { - // if !isSourceFile(current) || isExternalOrCommonJSModule(current) { - // c.getNodeLinks(current).flags |= NodeCheckFlagsContainsSuperPropertyInStaticInitializer - // } - // }) - // } - // } else { - // nodeCheckFlag = NodeCheckFlagsSuperInstance - // } - // c.getNodeLinks(node).flags |= nodeCheckFlag - // // Due to how we emit async functions, we need to specialize the emit for an async method that contains a `super` reference. - // // This is due to the fact that we emit the body of an async function inside of a generator function. As generator - // // functions cannot reference `super`, we emit a helper inside of the method body, but outside of the generator. This helper - // // uses an arrow function, which is permitted to reference `super`. - // // - // // There are two primary ways we can access `super` from within an async method. The first is getting the value of a property - // // or indexed access on super, either as part of a right-hand-side expression or call expression. The second is when setting the value - // // of a property or indexed access, either as part of an assignment expression or destructuring assignment. - // // - // // The simplest case is reading a value, in which case we will emit something like the following: - // // - // // // ts - // // ... - // // async asyncMethod() { - // // let x = await super.asyncMethod(); - // // return x; - // // } - // // ... - // // - // // // js - // // ... - // // asyncMethod() { - // // const _super = Object.create(null, { - // // asyncMethod: { get: () => super.asyncMethod }, - // // }); - // // return __awaiter(this, arguments, Promise, function *() { - // // let x = yield _super.asyncMethod.call(this); - // // return x; - // // }); - // // } - // // ... - // // - // // The more complex case is when we wish to assign a value, especially as part of a destructuring assignment. As both cases - // // are legal in ES6, but also likely less frequent, we only emit setters if there is an assignment: - // // - // // // ts - // // ... - // // async asyncMethod(ar: Promise) { - // // [super.a, super.b] = await ar; - // // } - // // ... - // // - // // // js - // // ... - // // asyncMethod(ar) { - // // const _super = Object.create(null, { - // // a: { get: () => super.a, set: (v) => super.a = v }, - // // b: { get: () => super.b, set: (v) => super.b = v } - // // }; - // // return __awaiter(this, arguments, Promise, function *() { - // // [_super.a, _super.b] = yield ar; - // // }); - // // } - // // ... - // // - // // Creating an object that has getter and setters instead of just an accessor function is required for destructuring assignments - // // as a call expression cannot be used as the target of a destructuring assignment while a property access can. - // // - // // For element access expressions (`super[x]`), we emit a generic helper that forwards the element access in both situations. - // if container.Kind == ast.KindMethodDeclaration && inAsyncFunction { - // if isSuperProperty(node.Parent) && isAssignmentTarget(node.Parent) { - // c.getNodeLinks(container).flags |= NodeCheckFlagsMethodWithSuperPropertyAssignmentInAsync - // } else { - // c.getNodeLinks(container).flags |= NodeCheckFlagsMethodWithSuperPropertyAccessInAsync - // } - // } - // if needToCaptureLexicalThis { - // // call expressions are allowed only in constructors so they should always capture correct 'this' - // // super property access expressions can also appear in arrow functions - - // // in this case they should also use correct lexical this - // c.captureLexicalThis(node.Parent, container) - // } - if container.Parent.Kind == ast.KindObjectLiteralExpression { - if c.languageVersion < core.ScriptTargetES2015 { - c.error(node, diagnostics.X_super_is_only_allowed_in_members_of_object_literal_expressions_when_option_target_is_ES2015_or_higher) - return c.errorType - } - // for object literal assume that type of 'super' is 'any' - return c.anyType - } - // at this point the only legal case for parent is ClassLikeDeclaration - classLikeDeclaration := container.Parent - if ast.GetExtendsHeritageClauseElement(classLikeDeclaration) == nil { - c.error(node, diagnostics.X_super_can_only_be_referenced_in_a_derived_class) - return c.errorType - } - if c.classDeclarationExtendsNull(classLikeDeclaration) { - if isCallExpression { - return c.errorType - } - return c.nullWideningType - } - classType := c.getDeclaredTypeOfSymbol(c.getSymbolOfDeclaration(classLikeDeclaration)) - var baseClassType *Type - if classType != nil { - baseClassType = core.FirstOrNil(c.getBaseTypes(classType)) - } - if baseClassType == nil { - return c.errorType - } - if ast.IsConstructorDeclaration(container) && c.isInConstructorArgumentInitializer(node, container) { - // issue custom error message for super property access in constructor arguments (to be aligned with old compiler) - c.error(node, diagnostics.X_super_cannot_be_referenced_in_constructor_arguments) - return c.errorType - } - if ast.IsStatic(container) || isCallExpression { - return c.getBaseConstructorTypeOfClass(classType) - } - return c.getTypeWithThisArgument(baseClassType, classType.AsInterfaceType().thisType, false) -} - -func (c *Checker) isInConstructorArgumentInitializer(node *ast.Node, constructorDecl *ast.Node) bool { - return ast.FindAncestorOrQuit(node, func(n *ast.Node) ast.FindAncestorResult { - if ast.IsFunctionLikeDeclaration(n) { - return ast.FindAncestorQuit - } - if ast.IsParameter(n) && n.Parent == constructorDecl { - return ast.FindAncestorTrue - } - return ast.FindAncestorFalse - }) != nil -} - -func (c *Checker) checkTemplateExpression(node *ast.Node) *Type { - expr := node.AsTemplateExpression() - length := len(expr.TemplateSpans.Nodes) - texts := make([]string, length+1) - types := make([]*Type, length) - texts[0] = expr.Head.Text() - for i, span := range expr.TemplateSpans.Nodes { - t := c.checkExpression(span.Expression()) - if c.maybeTypeOfKindConsideringBaseConstraint(t, TypeFlagsESSymbolLike) { - c.error(span.Expression(), diagnostics.Implicit_conversion_of_a_symbol_to_a_string_will_fail_at_runtime_Consider_wrapping_this_expression_in_String) - } - texts[i+1] = span.AsTemplateSpan().Literal.Text() - types[i] = core.IfElse(c.isTypeAssignableTo(t, c.templateConstraintType), t, c.stringType) - } - var evaluated any - if !ast.IsTaggedTemplateExpression(node.Parent) { - evaluated = c.evaluate(node, node).Value - } - if evaluated != nil { - return c.getFreshTypeOfLiteralType(c.getStringLiteralType(evaluated.(string))) - } - if c.isConstContext(node) || c.isTemplateLiteralContext(node) || someType(core.OrElse(c.getContextualType(node, ContextFlagsNone), c.unknownType), c.isTemplateLiteralContextualType) { - return c.getTemplateLiteralType(texts, types) - } - return c.stringType -} - -func (c *Checker) isTemplateLiteralContext(node *ast.Node) bool { - parent := node.Parent - return ast.IsParenthesizedExpression(parent) && c.isTemplateLiteralContext(parent) || ast.IsElementAccessExpression(parent) && parent.AsElementAccessExpression().ArgumentExpression == node -} - -func (c *Checker) isTemplateLiteralContextualType(t *Type) bool { - return t.flags&(TypeFlagsStringLiteral|TypeFlagsTemplateLiteral) != 0 || t.flags&TypeFlagsInstantiableNonPrimitive != 0 && c.maybeTypeOfKind(core.OrElse(c.getBaseConstraintOfType(t), c.unknownType), TypeFlagsStringLike) -} - -func (c *Checker) checkRegularExpressionLiteral(node *ast.Node) *Type { - nodeLinks := c.nodeLinks.Get(node) - if nodeLinks.flags&NodeCheckFlagsTypeChecked == 0 { - nodeLinks.flags |= NodeCheckFlagsTypeChecked - c.checkGrammarRegularExpressionLiteral(node.AsRegularExpressionLiteral()) - } - return c.globalRegExpType -} - -func (c *Checker) checkArrayLiteral(node *ast.Node, checkMode CheckMode) *Type { - elements := node.AsArrayLiteralExpression().Elements.Nodes - elementTypes := make([]*Type, len(elements)) - elementInfos := make([]TupleElementInfo, len(elements)) - c.pushCachedContextualType(node) - inDestructuringPattern := ast.IsAssignmentTarget(node) - inConstContext := c.isConstContext(node) - contextualType := c.getApparentTypeOfContextualType(node, ContextFlagsNone) - inTupleContext := isSpreadIntoCallOrNew(node) || contextualType != nil && someType(contextualType, func(t *Type) bool { - return c.isTupleLikeType(t) || c.isGenericMappedType(t) && t.AsMappedType().nameType == nil && c.getHomomorphicTypeVariable(core.OrElse(t.AsMappedType().target, t)) != nil - }) - hasOmittedExpression := false - for i, e := range elements { - switch { - case ast.IsSpreadElement(e): - spreadType := c.checkExpressionEx(e.AsSpreadElement().Expression, checkMode) - switch { - case c.isArrayLikeType(spreadType): - elementTypes[i] = spreadType - elementInfos[i] = TupleElementInfo{flags: ElementFlagsVariadic} - case inDestructuringPattern: - // Given the following situation: - // var c: {}; - // [...c] = ["", 0]; - // - // c is represented in the tree as a spread element in an array literal. - // But c really functions as a rest element, and its purpose is to provide - // a contextual type for the right hand side of the assignment. Therefore, - // instead of calling checkExpression on "...c", which will give an error - // if c is not iterable/array-like, we need to act as if we are trying to - // get the contextual element type from it. So we do something similar to - // getContextualTypeForElementExpression, which will crucially not error - // if there is no index type / iterated type. - restElementType := c.getIndexTypeOfType(spreadType, c.numberType) - if restElementType == nil { - restElementType = c.getIteratedTypeOrElementType(IterationUseDestructuring, spreadType, c.undefinedType, nil /*errorNode*/, false /*checkAssignability*/) - if restElementType == nil { - restElementType = c.unknownType - } - } - elementTypes[i] = restElementType - elementInfos[i] = TupleElementInfo{flags: ElementFlagsRest} - default: - elementTypes[i] = c.checkIteratedTypeOrElementType(IterationUseSpread, spreadType, c.undefinedType, e.Expression()) - elementInfos[i] = TupleElementInfo{flags: ElementFlagsRest} - } - case c.exactOptionalPropertyTypes && ast.IsOmittedExpression(e): - hasOmittedExpression = true - elementTypes[i] = c.undefinedOrMissingType - elementInfos[i] = TupleElementInfo{flags: ElementFlagsOptional} - default: - t := c.checkExpressionForMutableLocation(e, checkMode) - elementTypes[i] = c.addOptionalityEx(t, true /*isProperty*/, hasOmittedExpression) - elementInfos[i] = TupleElementInfo{flags: core.IfElse(hasOmittedExpression, ElementFlagsOptional, ElementFlagsRequired)} - if inTupleContext && checkMode&CheckModeInferential != 0 && checkMode&CheckModeSkipContextSensitive == 0 && c.isContextSensitive(e) { - inferenceContext := c.getInferenceContext(node) - // In CheckMode.Inferential we should always have an inference context - c.addIntraExpressionInferenceSite(inferenceContext, e, t) - } - } - } - c.popContextualType() - if inDestructuringPattern { - return c.createTupleTypeEx(elementTypes, elementInfos, false) - } - if checkMode&CheckModeForceTuple != 0 || inConstContext || inTupleContext { - return c.createArrayLiteralType(c.createTupleTypeEx(elementTypes, elementInfos, inConstContext && !(contextualType != nil && someType(contextualType, c.isMutableArrayLikeType)) /*readonly*/)) - } - var elementType *Type - if len(elementTypes) != 0 { - for i, e := range elementTypes { - if elementInfos[i].flags&ElementFlagsVariadic != 0 { - elementTypes[i] = core.OrElse(c.getIndexedAccessTypeOrUndefined(e, c.numberType, AccessFlagsNone, nil, nil), c.anyType) - } - } - elementType = c.getUnionTypeEx(elementTypes, UnionReductionSubtype, nil, nil) - } else { - elementType = core.IfElse(c.strictNullChecks, c.implicitNeverType, c.undefinedWideningType) - } - return c.createArrayLiteralType(c.createArrayTypeEx(elementType, inConstContext)) -} - -func (c *Checker) createArrayLiteralType(t *Type) *Type { - if t.objectFlags&ObjectFlagsReference == 0 { - return t - } - key := CachedTypeKey{kind: CachedTypeKindArrayLiteralType, typeId: t.id} - if cached, ok := c.cachedTypes[key]; ok { - return cached - } - literalType := c.cloneTypeReference(t) - literalType.objectFlags |= ObjectFlagsArrayLiteral | ObjectFlagsContainsObjectOrArrayLiteral - c.cachedTypes[key] = literalType - return literalType -} - -func isSpreadIntoCallOrNew(node *ast.Node) bool { - parent := ast.WalkUpParenthesizedExpressions(node.Parent) - return ast.IsSpreadElement(parent) && ast.IsCallOrNewExpression(parent.Parent) -} - -func (c *Checker) checkQualifiedName(node *ast.Node, checkMode CheckMode) *Type { - left := node.AsQualifiedName().Left - var leftType *Type - if ast.IsPartOfTypeQuery(node) && ast.IsThisIdentifier(left) { - leftType = c.checkNonNullType(c.checkThisExpression(left), left) - } else { - leftType = c.checkNonNullExpression(left) - } - return c.checkPropertyAccessExpressionOrQualifiedName(node, left, leftType, node.AsQualifiedName().Right, checkMode, false) -} - -func (c *Checker) checkIndexedAccess(node *ast.Node, checkMode CheckMode) *Type { - if node.Flags&ast.NodeFlagsOptionalChain != 0 { - return c.checkElementAccessChain(node, checkMode) - } - return c.checkElementAccessExpression(node, c.checkNonNullExpression(node.Expression()), checkMode) -} - -func (c *Checker) checkElementAccessChain(node *ast.Node, checkMode CheckMode) *Type { - exprType := c.checkExpression(node.Expression()) - nonOptionalType := c.getOptionalExpressionType(exprType, node.Expression()) - return c.propagateOptionalTypeMarker(c.checkElementAccessExpression(node, c.checkNonNullType(nonOptionalType, node.Expression()), checkMode), node, nonOptionalType != exprType) -} - -func (c *Checker) checkElementAccessExpression(node *ast.Node, exprType *Type, checkMode CheckMode) *Type { - objectType := exprType - if getAssignmentTargetKind(node) != AssignmentKindNone || c.isMethodAccessForCall(node) { - objectType = c.getWidenedType(objectType) - } - indexExpression := node.AsElementAccessExpression().ArgumentExpression - indexType := c.checkExpression(indexExpression) - if c.isErrorType(objectType) || objectType == c.silentNeverType { - return objectType - } - if isConstEnumObjectType(objectType) && !ast.IsStringLiteralLike(indexExpression) { - c.error(indexExpression, diagnostics.A_const_enum_member_can_only_be_accessed_using_a_string_literal) - return c.errorType - } - effectiveIndexType := indexType - if c.isForInVariableForNumericPropertyNames(indexExpression) { - effectiveIndexType = c.numberType - } - assignmentTargetKind := getAssignmentTargetKind(node) - var accessFlags AccessFlags - if assignmentTargetKind == AssignmentKindNone { - accessFlags = AccessFlagsExpressionPosition - } else { - accessFlags = AccessFlagsWriting | - core.IfElse(assignmentTargetKind == AssignmentKindCompound, AccessFlagsExpressionPosition, 0) | - core.IfElse(c.isGenericObjectType(objectType) && !isThisTypeParameter(objectType), AccessFlagsNoIndexSignatures, 0) - } - indexedAccessType := core.OrElse(c.getIndexedAccessTypeOrUndefined(objectType, effectiveIndexType, accessFlags, node, nil), c.errorType) - return c.checkIndexedAccessIndexType(c.getFlowTypeOfAccessExpression(node, c.getResolvedSymbolOrNil(node), indexedAccessType, indexExpression, checkMode), node) -} - -// Return true if given node is an expression consisting of an identifier (possibly parenthesized) -// that references a for-in variable for an object with numeric property names. -func (c *Checker) isForInVariableForNumericPropertyNames(expr *ast.Node) bool { - e := ast.SkipParentheses(expr) - if ast.IsIdentifier(e) { - symbol := c.getResolvedSymbol(e) - if symbol.Flags&ast.SymbolFlagsVariable != 0 { - child := expr - node := expr.Parent - for node != nil { - if ast.IsForInStatement(node) && child == node.AsForInOrOfStatement().Statement && c.getForInVariableSymbol(node) == symbol && c.hasNumericPropertyNames(c.getTypeOfExpression(node.Expression())) { - return true - } - child = node - node = node.Parent - } - } - } - return false -} - -// Return the symbol of the for-in variable declared or referenced by the given for-in statement. -func (c *Checker) getForInVariableSymbol(node *ast.Node) *ast.Symbol { - initializer := node.Initializer() - if ast.IsVariableDeclarationList(initializer) { - variable := initializer.AsVariableDeclarationList().Declarations.Nodes[0] - if variable != nil && !ast.IsBindingPattern(variable.Name()) { - return c.getSymbolOfDeclaration(variable) - } - } else if ast.IsIdentifier(initializer) { - return c.getResolvedSymbol(initializer) - } - return nil -} - -// Return true if the given type is considered to have numeric property names. -func (c *Checker) hasNumericPropertyNames(t *Type) bool { - return len(c.getIndexInfosOfType(t)) == 1 && c.getIndexInfoOfType(t, c.numberType) != nil -} - -func (c *Checker) checkIndexedAccessIndexType(t *Type, accessNode *ast.Node) *Type { - if t.flags&TypeFlagsIndexedAccess == 0 { - return t - } - // Check if the index type is assignable to 'keyof T' for the object type. - objectType := t.AsIndexedAccessType().objectType - indexType := t.AsIndexedAccessType().indexType - // skip index type deferral on remapping mapped types - var objectIndexType *Type - if c.isGenericMappedType(objectType) && c.getMappedTypeNameTypeKind(objectType) == MappedTypeNameTypeKindRemapping { - objectIndexType = c.getIndexTypeForMappedType(objectType, IndexFlagsNone) - } else { - objectIndexType = c.getIndexTypeEx(objectType, IndexFlagsNone) - } - hasNumberIndexInfo := c.getIndexInfoOfType(objectType, c.numberType) != nil - if everyType(indexType, func(t *Type) bool { - return c.isTypeAssignableTo(t, objectIndexType) || hasNumberIndexInfo && c.isApplicableIndexType(t, c.numberType) - }) { - if accessNode.Kind == ast.KindElementAccessExpression && ast.IsAssignmentTarget(accessNode) && objectType.objectFlags&ObjectFlagsMapped != 0 && getMappedTypeModifiers(objectType)&MappedTypeModifiersIncludeReadonly != 0 { - c.error(accessNode, diagnostics.Index_signature_in_type_0_only_permits_reading, c.TypeToString(objectType)) - } - return t - } - if c.isGenericObjectType(objectType) { - propertyName := c.getPropertyNameFromIndex(indexType, accessNode) - if propertyName != ast.InternalSymbolNameMissing { - propertySymbol := c.getConstituentProperty(objectType, propertyName) - if propertySymbol != nil && getDeclarationModifierFlagsFromSymbol(propertySymbol)&ast.ModifierFlagsNonPublicAccessibilityModifier != 0 { - c.error(accessNode, diagnostics.Private_or_protected_member_0_cannot_be_accessed_on_a_type_parameter, propertyName) - return c.errorType - } - } - } - c.error(accessNode, diagnostics.Type_0_cannot_be_used_to_index_type_1, c.TypeToString(indexType), c.TypeToString(objectType)) - return c.errorType -} - -func (c *Checker) getConstituentProperty(objectType *Type, propertyName string) *ast.Symbol { - for _, t := range c.getApparentType(objectType).Distributed() { - prop := c.getPropertyOfType(t, propertyName) - if prop != nil { - return prop - } - } - return nil -} - -func (c *Checker) checkImportCallExpression(node *ast.Node) *Type { - // Check grammar of dynamic import - c.checkGrammarImportCallExpression(node) - args := node.Arguments() - if len(args) == 0 { - return c.createPromiseReturnType(node, c.anyType) - } - specifier := args[0] - specifierType := c.checkExpressionCached(specifier) - var optionsType *Type - if len(args) > 1 { - optionsType = c.checkExpressionCached(args[1]) - } - // Even though multiple arguments is grammatically incorrect, type-check extra arguments for completion - for i := 2; i < len(args); i++ { - c.checkExpressionCached(args[i]) - } - if specifierType.flags&TypeFlagsNullable != 0 || !c.isTypeAssignableTo(specifierType, c.stringType) { - c.error(specifier, diagnostics.Dynamic_import_s_specifier_must_be_of_type_string_but_here_has_type_0, c.TypeToString(specifierType)) - } - if optionsType != nil { - importCallOptionsType := c.getGlobalImportCallOptionsTypeChecked() - if importCallOptionsType != c.emptyObjectType { - c.checkTypeAssignableTo(optionsType, c.getNullableType(importCallOptionsType, TypeFlagsUndefined), args[1], nil) - } - } - // resolveExternalModuleName will return undefined if the moduleReferenceExpression is not a string literal - moduleSymbol := c.resolveExternalModuleName(node, specifier, false /*ignoreErrors*/) - if moduleSymbol != nil { - esModuleSymbol := c.resolveESModuleSymbol(moduleSymbol, specifier, true /*dontResolveAlias*/, false /*suppressInteropError*/) - if esModuleSymbol != nil { - syntheticType := c.getTypeWithSyntheticDefaultOnly(c.getTypeOfSymbol(esModuleSymbol), esModuleSymbol, moduleSymbol, specifier) - if syntheticType == nil { - syntheticType = c.getTypeWithSyntheticDefaultImportType(c.getTypeOfSymbol(esModuleSymbol), esModuleSymbol, moduleSymbol, specifier) - } - return c.createPromiseReturnType(node, syntheticType) - } - } - return c.createPromiseReturnType(node, c.anyType) -} - -/** - * Syntactically and semantically checks a call or new expression. - * @param node The call/new expression to be checked. - * @returns On success, the expression's signature's return type. On failure, anyType. - */ -func (c *Checker) checkCallExpression(node *ast.Node, checkMode CheckMode) *Type { - c.checkGrammarTypeArguments(node, node.TypeArgumentList()) - signature := c.getResolvedSignature(node, nil /*candidatesOutArray*/, checkMode) - if signature == c.resolvingSignature { - // CheckMode.SkipGenericFunctions is enabled and this is a call to a generic function that - // returns a function type. We defer checking and return silentNeverType. - return c.silentNeverType - } - c.checkDeprecatedSignature(signature, node) - if node.Expression().Kind == ast.KindSuperKeyword { - return c.voidType - } - if ast.IsNewExpression(node) { - declaration := signature.declaration - if declaration != nil && !ast.IsConstructorDeclaration(declaration) && !ast.IsConstructSignatureDeclaration(declaration) && !ast.IsConstructorTypeNode(declaration) { - // When resolved signature is a call signature (and not a construct signature) the result type is any - if c.noImplicitAny { - c.error(node, diagnostics.X_new_expression_whose_target_lacks_a_construct_signature_implicitly_has_an_any_type) - } - return c.anyType - } - } - if ast.IsInJSFile(node) && c.isCommonJSRequire(node) { - return c.resolveExternalModuleTypeByLiteral(node.AsCallExpression().Arguments.Nodes[0]) - } - returnType := c.getReturnTypeOfSignature(signature) - // Treat any call to the global 'Symbol' function that is part of a const variable or readonly property - // as a fresh unique symbol literal type. - if returnType.flags&TypeFlagsESSymbolLike != 0 && c.isSymbolOrSymbolForCall(node) { - return c.getESSymbolLikeTypeForNode(ast.WalkUpParenthesizedExpressions(node.Parent)) - } - if ast.IsCallExpression(node) && node.AsCallExpression().QuestionDotToken == nil && ast.IsExpressionStatement(node.Parent) && returnType.flags&TypeFlagsVoid != 0 && c.getTypePredicateOfSignature(signature) != nil { - if !ast.IsDottedName(node.Expression()) { - c.error(node.Expression(), diagnostics.Assertions_require_the_call_target_to_be_an_identifier_or_qualified_name) - } else if c.getEffectsSignature(node) == nil { - diagnostic := c.error(node.Expression(), diagnostics.Assertions_require_every_name_in_the_call_target_to_be_declared_with_an_explicit_type_annotation) - c.getTypeOfDottedName(node.Expression(), diagnostic) - } - } - return returnType -} - -func (c *Checker) checkDeprecatedSignature(sig *Signature, node *ast.Node) { - if sig.flags&SignatureFlagsIsSignatureCandidateForOverloadFailure != 0 { - return - } - if sig.declaration != nil && sig.declaration.Flags&ast.NodeFlagsDeprecated != 0 { - suggestionNode := c.getDeprecatedSuggestionNode(node) - name := tryGetPropertyAccessOrIdentifierToString(ast.GetInvokedExpression(node)) - c.addDeprecatedSuggestionWithSignature(suggestionNode, sig.declaration, name, c.signatureToString(sig)) - } -} - -func (c *Checker) addDeprecatedSuggestionWithSignature(location *ast.Node, declaration *ast.Node, deprecatedEntity string, signatureString string) *ast.Diagnostic { - message := core.IfElse(deprecatedEntity != "", diagnostics.The_signature_0_of_1_is_deprecated, diagnostics.X_0_is_deprecated) - diagnostic := NewDiagnosticForNode(location, message, signatureString, deprecatedEntity) - return c.addDeprecatedSuggestionWorker([]*ast.Node{declaration}, diagnostic) -} - -func (c *Checker) isSymbolOrSymbolForCall(node *ast.Node) bool { - if !ast.IsCallExpression(node) { - return false - } - left := node.Expression() - if ast.IsPropertyAccessExpression(left) && left.Name().Text() == "for" { - left = left.Expression() - } - if !ast.IsIdentifier(left) || left.Text() != "Symbol" { - return false - } - // make sure `Symbol` is the global symbol - globalESSymbol := c.getGlobalESSymbolConstructorSymbolOrNil() - if globalESSymbol == nil { - return false - } - return globalESSymbol == c.resolveName(left, "Symbol", ast.SymbolFlagsValue, nil /*nameNotFoundMessage*/, false /*isUse*/, false) -} - -/** - * Resolve a signature of a given call-like expression. - * @param node a call-like expression to try resolve a signature for - * @param candidatesOutArray an array of signature to be filled in by the function. It is passed by signature help in the language service; - * the function will fill it up with appropriate candidate signatures - * @return a signature of the call-like expression or undefined if one can't be found - */ -func (c *Checker) getResolvedSignature(node *ast.Node, candidatesOutArray *[]*Signature, checkMode CheckMode) *Signature { - links := c.signatureLinks.Get(node) - // If getResolvedSignature has already been called, we will have cached the resolvedSignature. - // However, it is possible that either candidatesOutArray was not passed in the first time, - // or that a different candidatesOutArray was passed in. Therefore, we need to redo the work - // to correctly fill the candidatesOutArray. - cached := links.resolvedSignature - if cached != nil && cached != c.resolvingSignature && candidatesOutArray == nil { - return cached - } - saveResolutionStart := c.resolutionStart - if cached == nil { - // If we haven't already done so, temporarily reset the resolution stack. This allows us to - // handle "inverted" situations where, for example, an API client asks for the type of a symbol - // containined in a function call argument whose contextual type depends on the symbol itself - // through resolution of the containing function call. By resetting the resolution stack we'll - // retry the symbol type resolution with the resolvingSignature marker in place to suppress - // the contextual type circularity. - c.resolutionStart = len(c.typeResolutions) - } - links.resolvedSignature = c.resolvingSignature - result := c.resolveSignature(node, candidatesOutArray, checkMode) - c.resolutionStart = saveResolutionStart - // When CheckMode.SkipGenericFunctions is set we use resolvingSignature to indicate that call - // resolution should be deferred. - if result != c.resolvingSignature { - // if the signature resolution originated on a node that itself depends on the contextual type - // then it's possible that the resolved signature might not be the same as the one that would be computed in source order - // since resolving such signature leads to resolving the potential outer signature, its arguments and thus the very same signature - // it's possible that this inner resolution sets the resolvedSignature first. - // In such a case we ignore the local result and reuse the correct one that was cached. - if links.resolvedSignature != c.resolvingSignature { - result = links.resolvedSignature - } - // If signature resolution originated in control flow type analysis (for example to compute the - // assigned type in a flow assignment) we don't cache the result as it may be based on temporary - // types from the control flow analysis. - if len(c.flowLoopStack) == 0 { - links.resolvedSignature = result - } else { - links.resolvedSignature = cached - } - } - return result -} - -func (c *Checker) resolveSignature(node *ast.Node, candidatesOutArray *[]*Signature, checkMode CheckMode) *Signature { - switch node.Kind { - case ast.KindCallExpression: - return c.resolveCallExpression(node, candidatesOutArray, checkMode) - case ast.KindNewExpression: - return c.resolveNewExpression(node, candidatesOutArray, checkMode) - case ast.KindTaggedTemplateExpression: - return c.resolveTaggedTemplateExpression(node, candidatesOutArray, checkMode) - case ast.KindDecorator: - return c.resolveDecorator(node, candidatesOutArray, checkMode) - case ast.KindJsxOpeningFragment, ast.KindJsxOpeningElement, ast.KindJsxSelfClosingElement: - return c.resolveJsxOpeningLikeElement(node, candidatesOutArray, checkMode) - case ast.KindBinaryExpression: - return c.resolveInstanceofExpression(node, candidatesOutArray, checkMode) - } - panic("Unhandled case in resolveSignature") -} - -func (c *Checker) resolveCallExpression(node *ast.Node, candidatesOutArray *[]*Signature, checkMode CheckMode) *Signature { - if node.Expression().Kind == ast.KindSuperKeyword { - superType := c.checkSuperExpression(node.Expression()) - if IsTypeAny(superType) { - for _, arg := range node.Arguments() { - // Still visit arguments so they get marked for visibility, etc - c.checkExpression(arg) - } - return c.anySignature - } - if !c.isErrorType(superType) { - // In super call, the candidate signatures are the matching arity signatures of the base constructor function instantiated - // with the type arguments specified in the extends clause. - baseTypeNode := ast.GetExtendsHeritageClauseElement(ast.GetContainingClass(node)) - if baseTypeNode != nil { - baseConstructors := c.getInstantiatedConstructorsForTypeArguments(superType, baseTypeNode.TypeArguments(), baseTypeNode) - return c.resolveCall(node, baseConstructors, candidatesOutArray, checkMode, SignatureFlagsNone, nil) - } - } - return c.resolveUntypedCall(node) - } - var callChainFlags SignatureFlags - funcType := c.checkExpression(node.Expression()) - if isCallChain(node) { - nonOptionalType := c.getOptionalExpressionType(funcType, node.Expression()) - switch { - case nonOptionalType == funcType: - callChainFlags = SignatureFlagsNone - case ast.IsOutermostOptionalChain(node): - callChainFlags = SignatureFlagsIsOuterCallChain - default: - callChainFlags = SignatureFlagsIsInnerCallChain - } - funcType = nonOptionalType - } else { - callChainFlags = SignatureFlagsNone - } - funcType = c.checkNonNullTypeWithReporter(funcType, node.Expression(), (*Checker).reportCannotInvokePossiblyNullOrUndefinedError) - if funcType == c.silentNeverType { - return c.silentNeverSignature - } - apparentType := c.getApparentType(funcType) - if c.isErrorType(apparentType) { - // Another error has already been reported - return c.resolveErrorCall(node) - } - // Technically, this signatures list may be incomplete. We are taking the apparent type, - // but we are not including call signatures that may have been added to the Object or - // Function interface, since they have none by default. This is a bit of a leap of faith - // that the user will not add any. - callSignatures := c.getSignaturesOfType(apparentType, SignatureKindCall) - numConstructSignatures := len(c.getSignaturesOfType(apparentType, SignatureKindConstruct)) - // TS 1.0 Spec: 4.12 - // In an untyped function call no TypeArgs are permitted, Args can be any argument list, no contextual - // types are provided for the argument expressions, and the result is always of type Any. - if c.isUntypedFunctionCall(funcType, apparentType, len(callSignatures), numConstructSignatures) { - // The unknownType indicates that an error already occurred (and was reported). No - // need to report another error in this case. - if !c.isErrorType(funcType) && node.TypeArguments() != nil { - c.error(node, diagnostics.Untyped_function_calls_may_not_accept_type_arguments) - } - return c.resolveUntypedCall(node) - } - // If FuncExpr's apparent type(section 3.8.1) is a function type, the call is a typed function call. - // TypeScript employs overload resolution in typed function calls in order to support functions - // with multiple call signatures. - if len(callSignatures) == 0 { - if numConstructSignatures != 0 { - c.error(node, diagnostics.Value_of_type_0_is_not_callable_Did_you_mean_to_include_new, c.TypeToString(funcType)) - } else { - var relatedInformation *ast.Diagnostic - if len(node.Arguments()) == 1 { - text := ast.GetSourceFileOfNode(node).Text() - options := scanner.SkipTriviaOptions{StopAfterLineBreak: true} - if stringutil.IsLineBreak(rune(text[scanner.SkipTriviaEx(text, node.Expression().End(), &options)-1])) { - relatedInformation = createDiagnosticForNode(node.Expression(), diagnostics.Are_you_missing_a_semicolon) - } - } - c.invocationError(node.Expression(), apparentType, SignatureKindCall, relatedInformation) - } - return c.resolveErrorCall(node) - } - // When a call to a generic function is an argument to an outer call to a generic function for which - // inference is in process, we have a choice to make. If the inner call relies on inferences made from - // its contextual type to its return type, deferring the inner call processing allows the best possible - // contextual type to accumulate. But if the outer call relies on inferences made from the return type of - // the inner call, the inner call should be processed early. There's no sure way to know which choice is - // right (only a full unification algorithm can determine that), so we resort to the following heuristic: - // If no type arguments are specified in the inner call and at least one call signature is generic and - // returns a function type, we choose to defer processing. This narrowly permits function composition - // operators to flow inferences through return types, but otherwise processes calls right away. We - // use the resolvingSignature singleton to indicate that we deferred processing. This result will be - // propagated out and eventually turned into silentNeverType (a type that is assignable to anything and - // from which we never make inferences). - if checkMode&CheckModeSkipGenericFunctions != 0 && len(node.TypeArguments()) == 0 && core.Some(callSignatures, c.isGenericFunctionReturningFunction) { - c.skippedGenericFunction(node, checkMode) - return c.resolvingSignature - } - return c.resolveCall(node, callSignatures, candidatesOutArray, checkMode, callChainFlags, nil) -} - -func (c *Checker) resolveNewExpression(node *ast.Node, candidatesOutArray *[]*Signature, checkMode CheckMode) *Signature { - expressionType := c.checkNonNullExpression(node.Expression()) - if expressionType == c.silentNeverType { - return c.silentNeverSignature - } - // If expressionType's apparent type(section 3.8.1) is an object type with one or - // more construct signatures, the expression is processed in the same manner as a - // function call, but using the construct signatures as the initial set of candidate - // signatures for overload resolution. The result type of the function call becomes - // the result type of the operation. - expressionType = c.getApparentType(expressionType) - if c.isErrorType(expressionType) { - // Another error has already been reported - return c.resolveErrorCall(node) - } - // TS 1.0 spec: 4.11 - // If expressionType is of type Any, Args can be any argument - // list and the result of the operation is of type Any. - if IsTypeAny(expressionType) { - if len(node.TypeArguments()) != 0 { - c.error(node, diagnostics.Untyped_function_calls_may_not_accept_type_arguments) - } - return c.resolveUntypedCall(node) - } - // Technically, this signatures list may be incomplete. We are taking the apparent type, - // but we are not including construct signatures that may have been added to the Object or - // Function interface, since they have none by default. This is a bit of a leap of faith - // that the user will not add any. - constructSignatures := c.getSignaturesOfType(expressionType, SignatureKindConstruct) - if len(constructSignatures) != 0 { - if !c.isConstructorAccessible(node, constructSignatures[0]) { - return c.resolveErrorCall(node) - } - // If the expression is a class of abstract type, or an abstract construct signature, - // then it cannot be instantiated. - // In the case of a merged class-module or class-interface declaration, - // only the class declaration node will have the Abstract flag set. - if someSignature(constructSignatures, func(sig *Signature) bool { - return sig.flags&SignatureFlagsAbstract != 0 - }) { - c.error(node, diagnostics.Cannot_create_an_instance_of_an_abstract_class) - return c.resolveErrorCall(node) - } - if expressionType.symbol != nil { - valueDecl := ast.GetClassLikeDeclarationOfSymbol(expressionType.symbol) - if valueDecl != nil && ast.HasModifier(valueDecl, ast.ModifierFlagsAbstract) { - c.error(node, diagnostics.Cannot_create_an_instance_of_an_abstract_class) - return c.resolveErrorCall(node) - } - } - return c.resolveCall(node, constructSignatures, candidatesOutArray, checkMode, SignatureFlagsNone, nil) - } - // If expressionType's apparent type is an object type with no construct signatures but - // one or more call signatures, the expression is processed as a function call. A compile-time - // error occurs if the result of the function call is not Void. The type of the result of the - // operation is Any. It is an error to have a Void this type. - callSignatures := c.getSignaturesOfType(expressionType, SignatureKindCall) - if len(callSignatures) != 0 { - signature := c.resolveCall(node, callSignatures, candidatesOutArray, checkMode, SignatureFlagsNone, nil) - if !c.noImplicitAny { - if signature.declaration != nil && c.getReturnTypeOfSignature(signature) != c.voidType { - c.error(node, diagnostics.Only_a_void_function_can_be_called_with_the_new_keyword) - } - if c.getThisTypeOfSignature(signature) == c.voidType { - c.error(node, diagnostics.A_function_that_is_called_with_the_new_keyword_cannot_have_a_this_type_that_is_void) - } - } - return signature - } - c.invocationError(node.Expression(), expressionType, SignatureKindConstruct, nil) - return c.resolveErrorCall(node) -} - -func (c *Checker) isConstructorAccessible(node *ast.Node, signature *Signature) bool { - if signature == nil || signature.declaration == nil { - return true - } - declaration := signature.declaration - modifiers := getSelectedModifierFlags(declaration, ast.ModifierFlagsNonPublicAccessibilityModifier) - // (1) Public constructors and (2) constructor functions are always accessible. - if modifiers == 0 || !ast.IsConstructorDeclaration(declaration) { - return true - } - declaringClassDeclaration := ast.GetClassLikeDeclarationOfSymbol(declaration.Parent.Symbol()) - declaringClass := c.getDeclaredTypeOfSymbol(declaration.Parent.Symbol()) - // A private or protected constructor can only be instantiated within its own class (or a subclass, for protected) - if !c.isNodeWithinClass(node, declaringClassDeclaration) { - containingClass := ast.GetContainingClass(node) - if containingClass != nil && modifiers&ast.ModifierFlagsProtected != 0 { - containingType := c.getDeclaredTypeOfSymbol(containingClass.Symbol()) - if c.typeHasProtectedAccessibleBase(declaration.Parent.Symbol(), containingType) { - return true - } - } - if modifiers&ast.ModifierFlagsPrivate != 0 { - c.error(node, diagnostics.Constructor_of_class_0_is_private_and_only_accessible_within_the_class_declaration, c.TypeToString(declaringClass)) - } - if modifiers&ast.ModifierFlagsProtected != 0 { - c.error(node, diagnostics.Constructor_of_class_0_is_protected_and_only_accessible_within_the_class_declaration, c.TypeToString(declaringClass)) - } - return false - } - return true -} - -func (c *Checker) typeHasProtectedAccessibleBase(target *ast.Symbol, t *Type) bool { - baseTypes := c.getBaseTypes(c.getTargetType(t)) - if len(baseTypes) == 0 { - return false - } - firstBase := baseTypes[0] - if firstBase.flags&TypeFlagsIntersection != 0 { - types := firstBase.AsIntersectionType().types - mixinFlags, _ := c.findMixins(types) - for i, intersectionMember := range firstBase.Types() { - // We want to ignore mixin ctors - if !mixinFlags[i] { - if intersectionMember.objectFlags&(ObjectFlagsClass|ObjectFlagsInterface) != 0 { - if intersectionMember.symbol == target { - return true - } - if c.typeHasProtectedAccessibleBase(target, intersectionMember) { - return true - } - } - } - } - return false - } - if firstBase.symbol == target { - return true - } - return c.typeHasProtectedAccessibleBase(target, firstBase) -} - -func someSignature(signatures []*Signature, f func(s *Signature) bool) bool { - for _, sig := range signatures { - if sig.composite != nil && sig.composite.isUnion && core.Some(sig.composite.signatures, f) || sig.composite == nil && f(sig) { - return true - } - } - return false -} - -func (c *Checker) resolveTaggedTemplateExpression(node *ast.Node, candidatesOutArray *[]*Signature, checkMode CheckMode) *Signature { - tag := node.AsTaggedTemplateExpression().Tag - tagType := c.checkExpression(tag) - apparentType := c.getApparentType(tagType) - if c.isErrorType(apparentType) { - // Another error has already been reported - return c.resolveErrorCall(node) - } - callSignatures := c.getSignaturesOfType(apparentType, SignatureKindCall) - numConstructSignatures := len(c.getSignaturesOfType(apparentType, SignatureKindConstruct)) - if c.isUntypedFunctionCall(tagType, apparentType, len(callSignatures), numConstructSignatures) { - return c.resolveUntypedCall(node) - } - if len(callSignatures) == 0 { - if ast.IsArrayLiteralExpression(node.Parent) { - c.error(tag, diagnostics.It_is_likely_that_you_are_missing_a_comma_to_separate_these_two_template_expressions_They_form_a_tagged_template_expression_which_cannot_be_invoked) - return c.resolveErrorCall(node) - } - c.invocationError(tag, apparentType, SignatureKindCall, nil) - return c.resolveErrorCall(node) - } - return c.resolveCall(node, callSignatures, candidatesOutArray, checkMode, SignatureFlagsNone, nil) -} - -func (c *Checker) resolveDecorator(node *ast.Node, candidatesOutArray *[]*Signature, checkMode CheckMode) *Signature { - funcType := c.checkExpression(node.Expression()) - apparentType := c.getApparentType(funcType) - if c.isErrorType(apparentType) { - return c.resolveErrorCall(node) - } - callSignatures := c.getSignaturesOfType(apparentType, SignatureKindCall) - numConstructSignatures := len(c.getSignaturesOfType(apparentType, SignatureKindConstruct)) - if c.isUntypedFunctionCall(funcType, apparentType, len(callSignatures), numConstructSignatures) { - return c.resolveUntypedCall(node) - } - if c.isPotentiallyUncalledDecorator(node, callSignatures) && !ast.IsParenthesizedExpression(node.Expression()) { - nodeStr := scanner.GetTextOfNode(node.Expression()) - c.error(node, diagnostics.X_0_accepts_too_few_arguments_to_be_used_as_a_decorator_here_Did_you_mean_to_call_it_first_and_write_0, nodeStr) - return c.resolveErrorCall(node) - } - headMessage := c.getDiagnosticHeadMessageForDecoratorResolution(node) - if len(callSignatures) == 0 { - diag := ast.NewDiagnosticChain(c.invocationErrorDetails(node.Expression(), apparentType, SignatureKindCall), headMessage) - c.diagnostics.Add(diag) - c.invocationErrorRecovery(apparentType, SignatureKindCall, diag) - return c.resolveErrorCall(node) - } - return c.resolveCall(node, callSignatures, candidatesOutArray, checkMode, SignatureFlagsNone, headMessage) -} - -// Sometimes, we have a decorator that could accept zero arguments, -// but is receiving too many arguments as part of the decorator invocation. -// In those cases, a user may have meant to *call* the expression before using it as a decorator. -func (c *Checker) isPotentiallyUncalledDecorator(decorator *ast.Node, signatures []*Signature) bool { - return len(signatures) != 0 && core.Every(signatures, func(sig *Signature) bool { - return sig.minArgumentCount == 0 && !signatureHasRestParameter(sig) && len(sig.parameters) < c.getDecoratorArgumentCount(decorator, sig) - }) -} - -// Gets the localized diagnostic head message to use for errors when resolving a decorator as a call expression. -func (c *Checker) getDiagnosticHeadMessageForDecoratorResolution(node *ast.Node) *diagnostics.Message { - switch node.Parent.Kind { - case ast.KindClassDeclaration, ast.KindClassExpression: - return diagnostics.Unable_to_resolve_signature_of_class_decorator_when_called_as_an_expression - case ast.KindParameter: - return diagnostics.Unable_to_resolve_signature_of_parameter_decorator_when_called_as_an_expression - case ast.KindPropertyDeclaration: - return diagnostics.Unable_to_resolve_signature_of_property_decorator_when_called_as_an_expression - case ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor: - return diagnostics.Unable_to_resolve_signature_of_method_decorator_when_called_as_an_expression - } - panic("Unhandled case in getDiagnosticHeadMessageForDecoratorResolution") -} - -func (c *Checker) resolveInstanceofExpression(node *ast.Node, candidatesOutArray *[]*Signature, checkMode CheckMode) *Signature { - // if rightType is an object type with a custom `[Symbol.hasInstance]` method, then it is potentially - // valid on the right-hand side of the `instanceof` operator. This allows normal `object` types to - // participate in `instanceof`, as per Step 2 of https://tc39.es/ecma262/#sec-instanceofoperator. - right := node.AsBinaryExpression().Right - rightType := c.checkExpression(right) - if !IsTypeAny(rightType) { - hasInstanceMethodType := c.getSymbolHasInstanceMethodOfObjectType(rightType) - if hasInstanceMethodType != nil { - apparentType := c.getApparentType(hasInstanceMethodType) - if c.isErrorType(apparentType) { - return c.resolveErrorCall(node) - } - callSignatures := c.getSignaturesOfType(apparentType, SignatureKindCall) - constructSignatures := c.getSignaturesOfType(apparentType, SignatureKindConstruct) - if c.isUntypedFunctionCall(hasInstanceMethodType, apparentType, len(callSignatures), len(constructSignatures)) { - return c.resolveUntypedCall(node) - } - if len(callSignatures) != 0 { - return c.resolveCall(node, callSignatures, candidatesOutArray, checkMode, SignatureFlagsNone, nil) - } - } else if !(c.typeHasCallOrConstructSignatures(rightType) || c.isTypeSubtypeOf(rightType, c.globalFunctionType)) { - c.error(right, diagnostics.The_right_hand_side_of_an_instanceof_expression_must_be_either_of_type_any_a_class_function_or_other_type_assignable_to_the_Function_interface_type_or_an_object_type_with_a_Symbol_hasInstance_method) - return c.resolveErrorCall(node) - } - } - // fall back to a default signature - return c.anySignature -} - -type CallState struct { - node *ast.Node - typeArguments []*ast.Node - args []*ast.Node - candidates []*Signature - argCheckMode CheckMode - isSingleNonGenericCandidate bool - signatureHelpTrailingComma bool - candidatesForArgumentError []*Signature - candidateForArgumentArityError *Signature - candidateForTypeArgumentError *Signature -} - -func (c *Checker) resolveCall(node *ast.Node, signatures []*Signature, candidatesOutArray *[]*Signature, checkMode CheckMode, callChainFlags SignatureFlags, headMessage *diagnostics.Message) *Signature { - isTaggedTemplate := node.Kind == ast.KindTaggedTemplateExpression - isDecorator := node.Kind == ast.KindDecorator - isJsxOpeningOrSelfClosingElement := ast.IsJsxOpeningLikeElement(node) - isInstanceof := node.Kind == ast.KindBinaryExpression - reportErrors := !c.isInferencePartiallyBlocked && candidatesOutArray == nil - var s CallState - s.node = node - if !isDecorator && !isInstanceof && !isSuperCall(node) && !ast.IsJsxOpeningFragment(node) { - s.typeArguments = node.TypeArguments() - // We already perform checking on the type arguments on the class declaration itself. - if isTaggedTemplate || isJsxOpeningOrSelfClosingElement || node.Expression().Kind != ast.KindSuperKeyword { - c.checkSourceElements(s.typeArguments) - } - } - s.candidates = c.reorderCandidates(signatures, callChainFlags) - if candidatesOutArray != nil { - *candidatesOutArray = s.candidates - } - - if len(s.candidates) == 0 { - // In Strada we would error here, but no known repro doesn't have at least - // one other error in this codepath. Just return instead. See #54442 - return c.unknownSignature - } - - s.args = c.getEffectiveCallArguments(node) - // The excludeArgument array contains true for each context sensitive argument (an argument - // is context sensitive it is susceptible to a one-time permanent contextual typing). - // - // The idea is that we will perform type argument inference & assignability checking once - // without using the susceptible parameters that are functions, and once more for those - // parameters, contextually typing each as we go along. - // - // For a tagged template, then the first argument be 'undefined' if necessary because it - // represents a TemplateStringsArray. - // - // For a decorator, no arguments are susceptible to contextual typing due to the fact - // decorators are applied to a declaration by the emitter, and not to an expression. - s.isSingleNonGenericCandidate = len(s.candidates) == 1 && len(s.candidates[0].typeParameters) == 0 - if !isDecorator && !s.isSingleNonGenericCandidate && core.Some(s.args, c.isContextSensitive) { - s.argCheckMode = CheckModeSkipContextSensitive - } else { - s.argCheckMode = CheckModeNormal - } - // The following variables are captured and modified by calls to chooseOverload. - // If overload resolution or type argument inference fails, we want to report the - // best error possible. The best error is one which says that an argument was not - // assignable to a parameter. This implies that everything else about the overload - // was fine. So if there is any overload that is only incorrect because of an - // argument, we will report an error on that one. - // - // function foo(s: string): void; - // function foo(n: number): void; // Report argument error on this overload - // function foo(): void; - // foo(true); - // - // If none of the overloads even made it that far, there are two possibilities. - // There was a problem with type arguments for some overload, in which case - // report an error on that. Or none of the overloads even had correct arity, - // in which case give an arity error. - // - // function foo(x: T): void; // Report type argument error - // function foo(): void; - // foo(0); - // - // If we are in signature help, a trailing comma indicates that we intend to provide another argument, - // so we will only accept overloads with arity at least 1 higher than the current number of provided arguments. - s.signatureHelpTrailingComma = checkMode&CheckModeIsForSignatureHelp != 0 && ast.IsCallExpression(node) && node.ArgumentList().HasTrailingComma() - // Section 4.12.1: - // if the candidate list contains one or more signatures for which the type of each argument - // expression is a subtype of each corresponding parameter type, the return type of the first - // of those signatures becomes the return type of the function call. - // Otherwise, the return type of the first signature in the candidate list becomes the return - // type of the function call. - // - // Whether the call is an error is determined by assignability of the arguments. The subtype pass - // is just important for choosing the best signature. So in the case where there is only one - // signature, the subtype pass is useless. So skipping it is an optimization. - var result *Signature - if len(s.candidates) > 1 { - result = c.chooseOverload(&s, c.subtypeRelation) - } - if result == nil { - result = c.chooseOverload(&s, c.assignableRelation) - } - if result != nil { - return result - } - result = c.getCandidateForOverloadFailure(s.node, s.candidates, s.args, candidatesOutArray != nil, checkMode) - // Preemptively cache the result; getResolvedSignature will do this after we return, but - // we need to ensure that the result is present for the error checks below so that if - // this signature is encountered again, we handle the circularity (rather than producing a - // different result which may produce no errors and assert). Callers of getResolvedSignature - // don't hit this issue because they only observe this result after it's had a chance to - // be cached, but the error reporting code below executes before getResolvedSignature sets - // resolvedSignature. - c.signatureLinks.Get(node).resolvedSignature = result - // No signatures were applicable. Now report errors based on the last applicable signature with - // no arguments excluded from assignability checks. - // If candidate is undefined, it means that no candidates had a suitable arity. In that case, - // skip the checkApplicableSignature check. - if reportErrors { - // If the call expression is a synthetic call to a `[Symbol.hasInstance]` method then we will produce a head - // message when reporting diagnostics that explains how we got to `right[Symbol.hasInstance](left)` from - // `left instanceof right`, as it pertains to "Argument" related messages reported for the call. - if headMessage == nil && isInstanceof { - headMessage = diagnostics.The_left_hand_side_of_an_instanceof_expression_must_be_assignable_to_the_first_argument_of_the_right_hand_side_s_Symbol_hasInstance_method - } - c.reportCallResolutionErrors(node, &s, signatures, headMessage) - } - return result -} - -func (c *Checker) reorderCandidates(signatures []*Signature, callChainFlags SignatureFlags) []*Signature { - var lastParent *ast.Node - var lastSymbol *ast.Symbol - var index int - var cutoffIndex int - var spliceIndex int - specializedIndex := -1 - result := make([]*Signature, 0, len(signatures)) - for _, signature := range signatures { - var symbol *ast.Symbol - var parent *ast.Node - if signature.declaration != nil { - symbol = c.getSymbolOfDeclaration(signature.declaration) - parent = signature.declaration.Parent - } - if lastSymbol == nil || symbol == lastSymbol { - if lastParent != nil && parent == lastParent { - index = index + 1 - } else { - lastParent = parent - index = cutoffIndex - } - } else { - // current declaration belongs to a different symbol - // set cutoffIndex so re-orderings in the future won't change result set from 0 to cutoffIndex - index = len(result) - cutoffIndex = len(result) - lastParent = parent - } - lastSymbol = symbol - // specialized signatures always need to be placed before non-specialized signatures regardless - // of the cutoff position; see GH#1133 - if signatureHasLiteralTypes(signature) { - specializedIndex++ - spliceIndex = specializedIndex - // The cutoff index always needs to be greater than or equal to the specialized signature index - // in order to prevent non-specialized signatures from being added before a specialized - // signature. - cutoffIndex++ - } else { - spliceIndex = index - } - if callChainFlags != 0 { - signature = c.getOptionalCallSignature(signature, callChainFlags) - } - result = slices.Insert(result, spliceIndex, signature) - } - return result -} - -func signatureHasLiteralTypes(s *Signature) bool { - return s.flags&SignatureFlagsHasLiteralTypes != 0 -} - -func (c *Checker) getOptionalCallSignature(signature *Signature, callChainFlags SignatureFlags) *Signature { - if signature.flags&SignatureFlagsCallChainFlags == callChainFlags { - return signature - } - key := CachedSignatureKey{sig: signature, key: core.IfElse(callChainFlags == SignatureFlagsIsInnerCallChain, SignatureKeyInner, SignatureKeyOuter)} - if cached := c.cachedSignatures[key]; cached != nil { - return cached - } - result := c.cloneSignature(signature) - result.flags |= callChainFlags - c.cachedSignatures[key] = result - return result -} - -func (c *Checker) chooseOverload(s *CallState, relation *Relation) *Signature { - s.candidatesForArgumentError = nil - s.candidateForArgumentArityError = nil - s.candidateForTypeArgumentError = nil - if s.isSingleNonGenericCandidate { - candidate := s.candidates[0] - if len(s.typeArguments) != 0 || !c.hasCorrectArity(s.node, s.args, candidate, s.signatureHelpTrailingComma) { - return nil - } - if !c.isSignatureApplicable(s.node, s.args, candidate, relation, CheckModeNormal, false /*reportErrors*/, nil /*inferenceContext*/, nil /*diagnosticOutput*/) { - s.candidatesForArgumentError = []*Signature{candidate} - return nil - } - return candidate - } - for candidateIndex, candidate := range s.candidates { - if !c.hasCorrectTypeArgumentArity(candidate, s.typeArguments) || !c.hasCorrectArity(s.node, s.args, candidate, s.signatureHelpTrailingComma) { - continue - } - var checkCandidate *Signature - var inferenceContext *InferenceContext - if len(candidate.typeParameters) != 0 { - // If we are *inside the body of candidate*, we need to create a clone of `candidate` with differing type parameter identities, - // so our inference results for this call doesn't pollute expression types referencing the outer type parameter! - var candidateParameterContext *ast.Node - typeParamDeclaration := core.FirstOrNil(candidate.typeParameters[0].symbol.Declarations) - if typeParamDeclaration != nil { - candidateParameterContext = typeParamDeclaration.Parent - } else if candidate.declaration != nil && ast.IsConstructorDeclaration(candidate.declaration) { - candidateParameterContext = candidate.declaration.Parent - } else { - candidateParameterContext = candidate.declaration - } - if candidateParameterContext != nil && ast.FindAncestor(s.node, func(a *ast.Node) bool { return a == candidateParameterContext }) != nil { - candidate = c.getImplementationSignature(candidate) - } - var typeArgumentTypes []*Type - if len(s.typeArguments) != 0 { - typeArgumentTypes = c.checkTypeArguments(candidate, s.typeArguments, false /*reportErrors*/, nil) - if typeArgumentTypes == nil { - s.candidateForTypeArgumentError = candidate - continue - } - } else { - inferenceContext = c.newInferenceContext(candidate.typeParameters, candidate, InferenceFlagsNone /*flags*/, nil) - // The resulting type arguments are instantiated with the inference context mapper, as the inferred types may still contain references to the inference context's - // type variables via contextual projection. These are kept generic until all inferences are locked in, so the dependencies expressed can pass constraint checks. - typeArgumentTypes = c.instantiateTypes(c.inferTypeArguments(s.node, candidate, s.args, s.argCheckMode|CheckModeSkipGenericFunctions, inferenceContext), inferenceContext.nonFixingMapper) - if inferenceContext.flags&InferenceFlagsSkippedGenericFunction != 0 { - s.argCheckMode |= CheckModeSkipGenericFunctions - } - } - var inferredTypeParameters []*Type - if inferenceContext != nil { - inferredTypeParameters = inferenceContext.inferredTypeParameters - } - checkCandidate = c.getSignatureInstantiation(candidate, typeArgumentTypes, ast.IsInJSFile(candidate.declaration), inferredTypeParameters) - // If the original signature has a generic rest type, instantiation may produce a - // signature with different arity and we need to perform another arity check. - if c.getNonArrayRestType(candidate) != nil && !c.hasCorrectArity(s.node, s.args, checkCandidate, s.signatureHelpTrailingComma) { - s.candidateForArgumentArityError = checkCandidate - continue - } - } else { - checkCandidate = candidate - } - if !c.isSignatureApplicable(s.node, s.args, checkCandidate, relation, s.argCheckMode, false /*reportErrors*/, inferenceContext, nil /*diagnosticOutput*/) { - // Give preference to error candidates that have no rest parameters (as they are more specific) - s.candidatesForArgumentError = append(s.candidatesForArgumentError, checkCandidate) - continue - } - if s.argCheckMode != 0 { - // If one or more context sensitive arguments were excluded, we start including - // them now (and keeping do so for any subsequent candidates) and perform a second - // round of type inference and applicability checking for this particular candidate. - s.argCheckMode = CheckModeNormal - if inferenceContext != nil { - typeArgumentTypes := c.instantiateTypes(c.inferTypeArguments(s.node, candidate, s.args, s.argCheckMode, inferenceContext), inferenceContext.mapper) - checkCandidate = c.getSignatureInstantiation(candidate, typeArgumentTypes, ast.IsInJSFile(candidate.declaration), inferenceContext.inferredTypeParameters) - // If the original signature has a generic rest type, instantiation may produce a - // signature with different arity and we need to perform another arity check. - if c.getNonArrayRestType(candidate) != nil && !c.hasCorrectArity(s.node, s.args, checkCandidate, s.signatureHelpTrailingComma) { - s.candidateForArgumentArityError = checkCandidate - continue - } - } - if !c.isSignatureApplicable(s.node, s.args, checkCandidate, relation, s.argCheckMode, false /*reportErrors*/, inferenceContext, nil /*diagnosticOutput*/) { - // Give preference to error candidates that have no rest parameters (as they are more specific) - s.candidatesForArgumentError = append(s.candidatesForArgumentError, checkCandidate) - continue - } - } - s.candidates[candidateIndex] = checkCandidate - return checkCandidate - } - return nil -} - -func (c *Checker) getImplementationSignature(signature *Signature) *Signature { - key := CachedSignatureKey{sig: signature, key: SignatureKeyImplementation} - if cached := c.cachedSignatures[key]; cached != nil { - return cached - } - result := c.instantiateSignature(signature, newTypeMapper(nil, nil)) - c.cachedSignatures[key] = result - return result -} - -func (c *Checker) hasCorrectArity(node *ast.Node, args []*ast.Node, signature *Signature, signatureHelpTrailingComma bool) bool { - if ast.IsJsxOpeningFragment(node) { - return true - } - var argCount int - callIsIncomplete := false - // In incomplete call we want to be lenient when we have too few arguments - effectiveParameterCount := c.getParameterCount(signature) - effectiveMinimumArguments := c.getMinArgumentCount(signature) - switch { - case ast.IsTaggedTemplateExpression(node): - argCount = len(args) - template := node.AsTaggedTemplateExpression().Template - if ast.IsTemplateExpression(template) { - // If a tagged template expression lacks a tail literal, the call is incomplete. - // Specifically, a template only can end in a TemplateTail or a Missing literal. - lastSpan := core.LastOrNil(template.AsTemplateExpression().TemplateSpans.Nodes) - // we should always have at least one span. - callIsIncomplete = ast.NodeIsMissing(lastSpan.AsTemplateSpan().Literal) || ast.IsUnterminatedLiteral(lastSpan.AsTemplateSpan().Literal) - } else { - // If the template didn't end in a backtick, or its beginning occurred right prior to EOF, - // then this might actually turn out to be a TemplateHead in the future; - // so we consider the call to be incomplete. - callIsIncomplete = ast.IsUnterminatedLiteral(template) - } - case ast.IsDecorator(node): - argCount = c.getDecoratorArgumentCount(node, signature) - case ast.IsBinaryExpression(node): - argCount = 1 - case ast.IsJsxOpeningLikeElement(node): - callIsIncomplete = node.Attributes().End() == node.End() - if callIsIncomplete { - return true - } - argCount = core.IfElse(effectiveMinimumArguments == 0, len(args), 1) - effectiveParameterCount = core.IfElse(len(args) == 0, effectiveParameterCount, 1) // class may have argumentless ctor functions - still resolve ctor and compare vs props member type - effectiveMinimumArguments = min(effectiveMinimumArguments, 1) // sfc may specify context argument - handled by framework and not typechecked - case ast.IsNewExpression(node) && node.ArgumentList() == nil: - // This only happens when we have something of the form: 'new C' - return c.getMinArgumentCount(signature) == 0 - default: - if signatureHelpTrailingComma { - argCount = len(args) + 1 - } else { - argCount = len(args) - } - // If we are missing the close parenthesis, the call is incomplete. - callIsIncomplete = node.ArgumentList().End() == node.End() - // If a spread argument is present, check that it corresponds to a rest parameter or at least that it's in the valid range. - spreadArgIndex := c.getSpreadArgumentIndex(args) - if spreadArgIndex >= 0 { - return spreadArgIndex >= c.getMinArgumentCount(signature) && (c.hasEffectiveRestParameter(signature) || spreadArgIndex < c.getParameterCount(signature)) - } - } - // Too many arguments implies incorrect arity. - if !c.hasEffectiveRestParameter(signature) && argCount > effectiveParameterCount { - return false - } - // If the call is incomplete, we should skip the lower bound check. - // JSX signatures can have extra parameters provided by the library which we don't check - if callIsIncomplete || argCount >= effectiveMinimumArguments { - return true - } - for i := argCount; i < effectiveMinimumArguments; i++ { - t := c.getTypeAtPosition(signature, i) - if c.filterType(t, acceptsVoid).flags&TypeFlagsNever != 0 { - return false - } - } - return true -} - -func acceptsVoid(t *Type) bool { - return t.flags&TypeFlagsVoid != 0 -} - -func (c *Checker) getDecoratorArgumentCount(node *ast.Node, signature *Signature) int { - if c.compilerOptions.ExperimentalDecorators.IsTrue() { - return c.getLegacyDecoratorArgumentCount(node, signature) - } - return min(max(c.getParameterCount(signature), 1), 2) -} - -/** - * Returns the argument count for a decorator node that works like a function invocation. - */ -func (c *Checker) getLegacyDecoratorArgumentCount(node *ast.Node, signature *Signature) int { - switch node.Parent.Kind { - case ast.KindClassDeclaration, ast.KindClassExpression: - return 1 - case ast.KindPropertyDeclaration: - if ast.HasAccessorModifier(node.Parent) { - return 3 - } - return 2 - case ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor: - // For decorators with only two parameters we supply only two arguments - if len(signature.parameters) <= 2 { - return 2 - } - return 3 - case ast.KindParameter: - return 3 - } - panic("Unhandled case in getLegacyDecoratorArgumentCount") -} - -func (c *Checker) hasCorrectTypeArgumentArity(signature *Signature, typeArguments []*ast.Node) bool { - // If the user supplied type arguments, but the number of type arguments does not match - // the declared number of type parameters, the call has an incorrect arity. - numTypeParameters := len(signature.typeParameters) - minTypeArgumentCount := c.getMinTypeArgumentCount(signature.typeParameters) - return len(typeArguments) == 0 || len(typeArguments) >= minTypeArgumentCount && len(typeArguments) <= numTypeParameters -} - -func (c *Checker) checkTypeArguments(signature *Signature, typeArgumentNodes []*ast.Node, reportErrors bool, headMessage *diagnostics.Message) []*Type { - isJavaScript := ast.IsInJSFile(signature.declaration) - typeParameters := signature.typeParameters - typeArgumentTypes := c.fillMissingTypeArguments(core.Map(typeArgumentNodes, c.getTypeFromTypeNode), typeParameters, c.getMinTypeArgumentCount(typeParameters), isJavaScript) - var mapper *TypeMapper - for i := range typeArgumentNodes { - debug.Assert(typeParameters[i] != nil, "Should not call checkTypeArguments with too many type arguments") - constraint := c.getConstraintOfTypeParameter(typeParameters[i]) - if constraint != nil { - typeArgumentHeadMessage := core.OrElse(headMessage, diagnostics.Type_0_does_not_satisfy_the_constraint_1) - if mapper == nil { - mapper = newTypeMapper(typeParameters, typeArgumentTypes) - } - typeArgument := typeArgumentTypes[i] - var errorNode *ast.Node - if reportErrors { - errorNode = typeArgumentNodes[i] - } - var diags []*ast.Diagnostic - if !c.checkTypeAssignableToEx(typeArgument, c.getTypeWithThisArgument(c.instantiateType(constraint, mapper), typeArgument, false), errorNode, typeArgumentHeadMessage, &diags) { - if len(diags) != 0 { - diagnostic := diags[0] - if headMessage != nil { - diagnostic = ast.NewDiagnosticChain(diagnostic, diagnostics.Type_0_does_not_satisfy_the_constraint_1) - } - c.diagnostics.Add(diagnostic) - } - return nil - } - } - } - return typeArgumentTypes -} - -func (c *Checker) isSignatureApplicable(node *ast.Node, args []*ast.Node, signature *Signature, relation *Relation, checkMode CheckMode, reportErrors bool, inferenceContext *InferenceContext, diagnosticOutput *[]*ast.Diagnostic) bool { - if ast.IsJsxCallLike(node) { - return c.checkApplicableSignatureForJsxCallLikeElement(node, signature, relation, checkMode, reportErrors, diagnosticOutput) - } - thisType := c.getThisTypeOfSignature(signature) - if thisType != nil && thisType != c.voidType && !(ast.IsNewExpression(node) || ast.IsCallExpression(node) && isSuperProperty(node.Expression())) { - // If the called expression is not of the form `x.f` or `x["f"]`, then sourceType = voidType - // If the signature's 'this' type is voidType, then the check is skipped -- anything is compatible. - // If the expression is a new expression or super call expression, then the check is skipped. - thisArgumentNode := c.getThisArgumentOfCall(node) - thisArgumentType := c.getThisArgumentType(thisArgumentNode) - var errorNode *ast.Node - if reportErrors { - errorNode = thisArgumentNode - if errorNode == nil { - errorNode = node - } - } - headMessage := diagnostics.The_this_context_of_type_0_is_not_assignable_to_method_s_this_of_type_1 - if !c.checkTypeRelatedToEx(thisArgumentType, thisType, relation, errorNode, headMessage, diagnosticOutput) { - return false - } - } - headMessage := diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1 - restType := c.getNonArrayRestType(signature) - var argCount int - if restType != nil { - argCount = min(c.getParameterCount(signature)-1, len(args)) - } else { - argCount = len(args) - } - for i := range argCount { - arg := args[i] - if !ast.IsOmittedExpression(arg) { - paramType := c.getTypeAtPosition(signature, i) - argType := c.checkExpressionWithContextualType(arg, paramType, nil /*inferenceContext*/, checkMode) - // If one or more arguments are still excluded (as indicated by CheckMode.SkipContextSensitive), - // we obtain the regular type of any object literal arguments because we may not have inferred complete - // parameter types yet and therefore excess property checks may yield false positives (see #17041). - var regularArgType *Type - if checkMode&CheckModeSkipContextSensitive != 0 { - regularArgType = c.getRegularTypeOfObjectLiteral(argType) - } else { - regularArgType = argType - } - // If this was inferred under a given inference context, we may need to instantiate the expression type to finish resolving - // the type variables in the expression. - var checkArgType *Type - if inferenceContext != nil { - checkArgType = c.instantiateType(regularArgType, inferenceContext.nonFixingMapper) - } else { - checkArgType = regularArgType - } - effectiveCheckArgumentNode := c.getEffectiveCheckNode(arg) - if !c.checkTypeRelatedToAndOptionallyElaborate(checkArgType, paramType, relation, core.IfElse(reportErrors, effectiveCheckArgumentNode, nil), effectiveCheckArgumentNode, headMessage, diagnosticOutput) { - c.maybeAddMissingAwaitInfo(arg, checkArgType, paramType, relation, reportErrors, diagnosticOutput) - return false - } - } - } - if restType != nil { - spreadType := c.getSpreadArgumentType(args, argCount, len(args), restType, nil /*context*/, checkMode) - restArgCount := len(args) - argCount - var errorNode *ast.Node - if reportErrors { - switch restArgCount { - case 0: - errorNode = node - case 1: - errorNode = c.getEffectiveCheckNode(args[argCount]) - default: - errorNode = c.createSyntheticExpression(node, spreadType, false, nil) - errorNode.Loc = core.NewTextRange(args[argCount].Pos(), args[len(args)-1].End()) - } - } - if !c.checkTypeRelatedToEx(spreadType, restType, relation, errorNode, headMessage, diagnosticOutput) { - c.maybeAddMissingAwaitInfo(errorNode, spreadType, restType, relation, reportErrors, diagnosticOutput) - return false - } - } - return true -} - -func (c *Checker) maybeAddMissingAwaitInfo(errorNode *ast.Node, source *Type, target *Type, relation *Relation, reportErrors bool, diagnosticOutput *[]*ast.Diagnostic) { - if errorNode != nil && reportErrors && diagnosticOutput != nil && len(*diagnosticOutput) != 0 { - // Bail if target is Promise-like---something else is wrong - if c.getAwaitedTypeOfPromise(target) != nil { - return - } - awaitedTypeOfSource := c.getAwaitedTypeOfPromise(source) - if awaitedTypeOfSource != nil && c.isTypeRelatedTo(awaitedTypeOfSource, target, relation) { - (*diagnosticOutput)[0].AddRelatedInfo(NewDiagnosticForNode(errorNode, diagnostics.Did_you_forget_to_use_await)) - } - } -} - -// Returns the `this` argument node in calls like `x.f(...)` and `x[f](...)`. `nil` otherwise. -func (c *Checker) getThisArgumentOfCall(node *ast.Node) *ast.Node { - if ast.IsBinaryExpression(node) { - return node.AsBinaryExpression().Right - } - var expression *ast.Node - switch { - case ast.IsCallExpression(node): - expression = node.Expression() - case ast.IsTaggedTemplateExpression(node): - expression = node.AsTaggedTemplateExpression().Tag - case ast.IsDecorator(node) && !c.legacyDecorators: - expression = node.Expression() - } - if expression != nil { - callee := ast.SkipOuterExpressions(expression, ast.OEKAll) - if ast.IsAccessExpression(callee) { - return callee.Expression() - } - } - return nil -} - -func (c *Checker) getThisArgumentType(node *ast.Node) *Type { - if node == nil { - return c.voidType - } - thisArgumentType := c.checkExpression(node) - switch { - case ast.IsOptionalChainRoot(node.Parent): - return c.GetNonNullableType(thisArgumentType) - case ast.IsOptionalChain(node.Parent): - return c.removeOptionalTypeMarker(thisArgumentType) - } - return thisArgumentType -} - -func (c *Checker) getEffectiveCheckNode(argument *ast.Node) *ast.Node { - flags := core.IfElse( - ast.IsInJSFile(argument), - ast.OEKParentheses|ast.OEKSatisfies|ast.OEKExcludeJSDocTypeAssertion, - ast.OEKParentheses|ast.OEKSatisfies, - ) - return ast.SkipOuterExpressions(argument, flags) -} - -func (c *Checker) inferTypeArguments(node *ast.Node, signature *Signature, args []*ast.Node, checkMode CheckMode, context *InferenceContext) []*Type { - if ast.IsJsxOpeningLikeElement(node) { - return c.inferJsxTypeArguments(node, signature, checkMode, context) - } - // If a contextual type is available, infer from that type to the return type of the call expression. For - // example, given a 'function wrap(cb: (x: T) => U): (x: T) => U' and a call expression - // 'let f: (x: string) => number = wrap(s => s.length)', we infer from the declared type of 'f' to the - // return type of 'wrap'. - if !ast.IsDecorator(node) && !ast.IsBinaryExpression(node) { - skipBindingPatterns := core.Every(signature.typeParameters, func(p *Type) bool { return c.getDefaultFromTypeParameter(p) != nil }) - contextualType := c.getContextualType(node, core.IfElse(skipBindingPatterns, ContextFlagsSkipBindingPatterns, ContextFlagsNone)) - if contextualType != nil { - inferenceTargetType := c.getReturnTypeOfSignature(signature) - if c.couldContainTypeVariables(inferenceTargetType) { - outerContext := c.getInferenceContext(node) - isFromBindingPattern := !skipBindingPatterns && c.getContextualType(node, ContextFlagsSkipBindingPatterns) != contextualType - // A return type inference from a binding pattern can be used in instantiating the contextual - // type of an argument later in inference, but cannot stand on its own as the final return type. - // It is incorporated into `context.returnMapper` which is used in `instantiateContextualType`, - // but doesn't need to go into `context.inferences`. This allows a an array binding pattern to - // produce a tuple for `T` in - // declare function f(cb: () => T): T; - // const [e1, e2, e3] = f(() => [1, "hi", true]); - // but does not produce any inference for `T` in - // declare function f(): T; - // const [e1, e2, e3] = f(); - if !isFromBindingPattern { - // We clone the inference context to avoid disturbing a resolution in progress for an - // outer call expression. Effectively we just want a snapshot of whatever has been - // inferred for any outer call expression so far. - outerMapper := c.getMapperFromContext(c.cloneInferenceContext(outerContext, InferenceFlagsNoDefault)) - instantiatedType := c.instantiateType(contextualType, outerMapper) - // If the contextual type is a generic function type with a single call signature, we - // instantiate the type with its own type parameters and type arguments. This ensures that - // the type parameters are not erased to type any during type inference such that they can - // be inferred as actual types from the contextual type. For example: - // declare function arrayMap(f: (x: T) => U): (a: T[]) => U[]; - // const boxElements: (a: A[]) => { value: A }[] = arrayMap(value => ({ value })); - // Above, the type of the 'value' parameter is inferred to be 'A'. - contextualSignature := c.getSingleCallSignature(instantiatedType) - var inferenceSourceType *Type - if contextualSignature != nil && len(contextualSignature.typeParameters) != 0 { - inferenceSourceType = c.getOrCreateTypeFromSignature(c.getSignatureInstantiationWithoutFillingInTypeArguments(contextualSignature, contextualSignature.typeParameters)) - } else { - inferenceSourceType = instantiatedType - } - // Inferences made from return types have lower priority than all other inferences. - c.inferTypes(context.inferences, inferenceSourceType, inferenceTargetType, InferencePriorityReturnType, false) - } - // Create a type mapper for instantiating generic contextual types using the inferences made - // from the return type. We need a separate inference pass here because (a) instantiation of - // the source type uses the outer context's return mapper (which excludes inferences made from - // outer arguments), and (b) we don't want any further inferences going into this context. - // We use `createOuterReturnMapper` to ensure that all occurrences of outer type parameters are - // replaced with inferences produced from the outer return type or preceding outer arguments. - // This protects against circular inferences, i.e. avoiding situations where inferences reference - // type parameters for which the inferences are being made. - returnContext := c.newInferenceContext(signature.typeParameters, signature, context.flags, nil) - var outerReturnMapper *TypeMapper - if outerContext != nil { - outerReturnMapper = c.createOuterReturnMapper(outerContext) - } - returnSourceType := c.instantiateType(contextualType, outerReturnMapper) - c.inferTypes(returnContext.inferences, returnSourceType, inferenceTargetType, InferencePriorityNone, false) - if core.Some(returnContext.inferences, hasInferenceCandidates) { - context.returnMapper = c.getMapperFromContext(c.cloneInferredPartOfContext(returnContext)) - } else { - context.returnMapper = nil - } - } - } - } - restType := c.getNonArrayRestType(signature) - argCount := len(args) - if restType != nil { - argCount = min(c.getParameterCount(signature)-1, argCount) - } - if restType != nil && restType.flags&TypeFlagsTypeParameter != 0 { - info := core.Find(context.inferences, func(info *InferenceInfo) bool { return info.typeParameter == restType }) - if info != nil { - if core.FindIndex(args[argCount:], isSpreadArgument) < 0 { - info.impliedArity = len(args) - argCount - } - } - } - thisType := c.getThisTypeOfSignature(signature) - if thisType != nil && c.couldContainTypeVariables(thisType) { - thisArgumentNode := c.getThisArgumentOfCall(node) - c.inferTypes(context.inferences, c.getThisArgumentType(thisArgumentNode), thisType, InferencePriorityNone, false) - } - for i := range argCount { - arg := args[i] - if arg.Kind != ast.KindOmittedExpression { - paramType := c.getTypeAtPosition(signature, i) - if c.couldContainTypeVariables(paramType) { - argType := c.checkExpressionWithContextualType(arg, paramType, context, checkMode) - c.inferTypes(context.inferences, argType, paramType, InferencePriorityNone, false) - } - } - } - if restType != nil && c.couldContainTypeVariables(restType) { - spreadType := c.getSpreadArgumentType(args, argCount, len(args), restType, context, checkMode) - c.inferTypes(context.inferences, spreadType, restType, InferencePriorityNone, false) - } - return c.getInferredTypes(context) -} - -// No signature was applicable. We have already reported the errors for the invalid signature. -func (c *Checker) getCandidateForOverloadFailure(node *ast.Node, candidates []*Signature, args []*ast.Node, hasCandidatesOutArray bool, checkMode CheckMode) *Signature { - // Else should not have called this. - c.checkNodeDeferred(node) - // Normally we will combine overloads. Skip this if they have type parameters since that's hard to combine. - // Don't do this if there is a `candidatesOutArray`, - // because then we want the chosen best candidate to be one of the overloads, not a combination. - if hasCandidatesOutArray || len(candidates) == 1 || core.Some(candidates, func(s *Signature) bool { return len(s.typeParameters) != 0 }) { - return c.pickLongestCandidateSignature(node, candidates, args, checkMode) - } - return c.createUnionOfSignaturesForOverloadFailure(candidates) -} - -func (c *Checker) pickLongestCandidateSignature(node *ast.Node, candidates []*Signature, args []*ast.Node, checkMode CheckMode) *Signature { - // Pick the longest signature. This way we can get a contextual type for cases like: - // declare function f(a: { xa: number; xb: number; }, b: number); - // f({ | - // Also, use explicitly-supplied type arguments if they are provided, so we can get a contextual signature in cases like: - // declare function f(k: keyof T); - // f(" - argCount := len(args) - if c.apparentArgumentCount != nil { - argCount = *c.apparentArgumentCount - } - bestIndex := c.getLongestCandidateIndex(candidates, argCount) - candidate := candidates[bestIndex] - typeParameters := candidate.typeParameters - if len(typeParameters) == 0 { - return candidate - } - var typeArgumentNodes []*ast.Node - if c.callLikeExpressionMayHaveTypeArguments(node) { - typeArgumentNodes = node.TypeArguments() - } - var instantiated *Signature - if len(typeArgumentNodes) != 0 { - instantiated = c.createSignatureInstantiation(candidate, c.getTypeArgumentsFromNodes(typeArgumentNodes, typeParameters)) - } else { - instantiated = c.inferSignatureInstantiationForOverloadFailure(node, typeParameters, candidate, args, checkMode) - } - candidates[bestIndex] = instantiated - return instantiated -} - -func (c *Checker) getLongestCandidateIndex(candidates []*Signature, argsCount int) int { - maxParamsIndex := -1 - maxParams := -1 - for i, candidate := range candidates { - paramCount := c.getParameterCount(candidate) - if c.hasEffectiveRestParameter(candidate) || paramCount >= argsCount { - return i - } - if paramCount > maxParams { - maxParams = paramCount - maxParamsIndex = i - } - } - return maxParamsIndex -} - -func (c *Checker) getTypeArgumentsFromNodes(typeArgumentNodes []*ast.Node, typeParameters []*Type) []*Type { - if len(typeArgumentNodes) > len(typeParameters) { - typeArgumentNodes = typeArgumentNodes[:len(typeParameters)] - } - typeArguments := core.Map(typeArgumentNodes, c.getTypeFromTypeNode) - for len(typeArguments) < len(typeParameters) { - t := c.getDefaultFromTypeParameter(typeParameters[len(typeArguments)]) - if t == nil { - t = c.getConstraintOfTypeParameter(typeParameters[len(typeArguments)]) - if t == nil { - t = c.unknownType - } - } - typeArguments = append(typeArguments, t) - } - return typeArguments -} - -func (c *Checker) inferSignatureInstantiationForOverloadFailure(node *ast.Node, typeParameters []*Type, candidate *Signature, args []*ast.Node, checkMode CheckMode) *Signature { - inferenceContext := c.newInferenceContext(typeParameters, candidate, InferenceFlagsNone, nil) - typeArgumentTypes := c.inferTypeArguments(node, candidate, args, checkMode|CheckModeSkipContextSensitive|CheckModeSkipGenericFunctions, inferenceContext) - return c.createSignatureInstantiation(candidate, typeArgumentTypes) -} - -func (c *Checker) createUnionOfSignaturesForOverloadFailure(candidates []*Signature) *Signature { - thisParameters := core.MapNonNil(candidates, func(c *Signature) *ast.Symbol { return c.thisParameter }) - var thisParameter *ast.Symbol - if len(thisParameters) != 0 { - thisParameter = c.createCombinedSymbolFromTypes(thisParameters, core.Map(thisParameters, c.getTypeOfParameter)) - } - minArgumentCount, maxNonRestParam := minAndMax(candidates, getNonRestParameterCount) - parameters := make([]*ast.Symbol, 0, maxNonRestParam) - for i := range maxNonRestParam { - symbols := core.MapNonNil(candidates, func(s *Signature) *ast.Symbol { - if signatureHasRestParameter(s) { - if i < len(s.parameters)-1 { - return s.parameters[i] - } - return core.LastOrNil(s.parameters) - } - if i < len(s.parameters) { - return s.parameters[i] - } - return nil - }) - parameters = append(parameters, c.createCombinedSymbolFromTypes(symbols, core.MapNonNil(candidates, func(s *Signature) *Type { return c.tryGetTypeAtPosition(s, i) }))) - } - restParameterSymbols := core.MapNonNil(candidates, func(s *Signature) *ast.Symbol { - if signatureHasRestParameter(s) { - return core.LastOrNil(s.parameters) - } - return nil - }) - flags := SignatureFlagsIsSignatureCandidateForOverloadFailure - if len(restParameterSymbols) != 0 { - t := c.createArrayType(c.getUnionTypeEx(core.MapNonNil(candidates, c.tryGetRestTypeOfSignature), UnionReductionSubtype, nil, nil)) - parameters = append(parameters, c.createCombinedSymbolForOverloadFailure(restParameterSymbols, t)) - flags |= SignatureFlagsHasRestParameter - } - if core.Some(candidates, signatureHasLiteralTypes) { - flags |= SignatureFlagsHasLiteralTypes - } - return c.newSignature(flags, candidates[0].declaration, nil, thisParameter, parameters, c.getIntersectionType(core.Map(candidates, c.getReturnTypeOfSignature)), nil, minArgumentCount) -} - -func (c *Checker) createCombinedSymbolFromTypes(sources []*ast.Symbol, types []*Type) *ast.Symbol { - return c.createCombinedSymbolForOverloadFailure(sources, c.getUnionTypeEx(types, UnionReductionSubtype, nil, nil)) -} - -func (c *Checker) createCombinedSymbolForOverloadFailure(sources []*ast.Symbol, t *Type) *ast.Symbol { - // This function is currently only used for erroneous overloads, so it's good enough to just use the first source. - return c.createSymbolWithType(core.FirstOrNil(sources), t) -} - -func (c *Checker) getRestTypeOfSignature(signature *Signature) *Type { - return core.OrElse(c.tryGetRestTypeOfSignature(signature), c.anyType) -} - -func (c *Checker) tryGetRestTypeOfSignature(signature *Signature) *Type { - if !signatureHasRestParameter(signature) { - return nil - } - restType := c.getTypeOfSymbol(signature.parameters[len(signature.parameters)-1]) - if isTupleType(restType) { - restType = c.getRestTypeOfTupleType(restType) - if restType == nil { - return nil - } - } - return c.getIndexTypeOfType(restType, c.numberType) -} - -func (c *Checker) reportCallResolutionErrors(node *ast.Node, s *CallState, signatures []*Signature, headMessage *diagnostics.Message) { - switch { - case len(s.candidatesForArgumentError) != 0: - last := s.candidatesForArgumentError[len(s.candidatesForArgumentError)-1] - var diags []*ast.Diagnostic - c.isSignatureApplicable(s.node, s.args, last, c.assignableRelation, CheckModeNormal, true /*reportErrors*/, nil /*inferenceContext*/, &diags) - for _, diagnostic := range diags { - if len(s.candidatesForArgumentError) > 1 { - diagnostic = ast.NewDiagnosticChain(diagnostic, diagnostics.The_last_overload_gave_the_following_error) - diagnostic = ast.NewDiagnosticChain(diagnostic, diagnostics.No_overload_matches_this_call) - } - if headMessage != nil { - diagnostic = ast.NewDiagnosticChain(diagnostic, headMessage) - } - if last.declaration != nil && len(s.candidatesForArgumentError) > 1 { - diagnostic.AddRelatedInfo(NewDiagnosticForNode(last.declaration, diagnostics.The_last_overload_is_declared_here)) - } - c.addImplementationSuccessElaboration(s, last, diagnostic) - c.diagnostics.Add(diagnostic) - } - case s.candidateForArgumentArityError != nil: - c.diagnostics.Add(c.getArgumentArityError(s.node, []*Signature{s.candidateForArgumentArityError}, s.args, headMessage)) - case s.candidateForTypeArgumentError != nil: - c.checkTypeArguments(s.candidateForTypeArgumentError, s.node.TypeArguments(), true /*reportErrors*/, headMessage) - case !ast.IsJsxOpeningFragment(node): - signaturesWithCorrectTypeArgumentArity := core.Filter(signatures, func(sig *Signature) bool { - return c.hasCorrectTypeArgumentArity(sig, s.typeArguments) - }) - if len(signaturesWithCorrectTypeArgumentArity) == 0 { - c.diagnostics.Add(c.getTypeArgumentArityError(s.node, signatures, s.typeArguments, headMessage)) - } else { - c.diagnostics.Add(c.getArgumentArityError(s.node, signaturesWithCorrectTypeArgumentArity, s.args, headMessage)) - } - } -} - -func (c *Checker) addImplementationSuccessElaboration(s *CallState, failed *Signature, diagnostic *ast.Diagnostic) { - if failed.declaration != nil && failed.declaration.Symbol() != nil { - declarations := failed.declaration.Symbol().Declarations - if len(declarations) > 1 { - implementation := core.Find(declarations, func(d *ast.Declaration) bool { - return ast.IsFunctionLikeDeclaration(d) && ast.NodeIsPresent(d.Body()) - }) - if implementation != nil { - candidate := c.getSignatureFromDeclaration(implementation) - localState := *s - localState.candidates = []*Signature{candidate} - localState.isSingleNonGenericCandidate = len(candidate.typeParameters) == 0 - if c.chooseOverload(&localState, c.assignableRelation) != nil { - diagnostic.AddRelatedInfo(NewDiagnosticForNode(implementation, diagnostics.The_call_would_have_succeeded_against_this_implementation_but_implementation_signatures_of_overloads_are_not_externally_visible)) - } - } - } - } -} - -func (c *Checker) getArgumentArityError(node *ast.Node, signatures []*Signature, args []*ast.Node, headMessage *diagnostics.Message) *ast.Diagnostic { - spreadIndex := c.getSpreadArgumentIndex(args) - if spreadIndex > -1 { - return NewDiagnosticForNode(args[spreadIndex], diagnostics.A_spread_argument_must_either_have_a_tuple_type_or_be_passed_to_a_rest_parameter) - } - minCount := math.MaxInt // smallest parameter count - maxCount := math.MinInt // largest parameter count - maxBelow := math.MinInt // largest parameter count that is smaller than the number of arguments - minAbove := math.MaxInt // smallest parameter count that is larger than the number of arguments - var closestSignature *Signature - for _, sig := range signatures { - minParameter := c.getMinArgumentCount(sig) - maxParameter := c.getParameterCount(sig) - // smallest/largest parameter counts - if minParameter < minCount { - minCount = minParameter - closestSignature = sig - } - maxCount = max(maxCount, maxParameter) - // shortest parameter count *longer than the call*/longest parameter count *shorter than the call* - if minParameter < len(args) && minParameter > maxBelow { - maxBelow = minParameter - } - if len(args) < maxParameter && maxParameter < minAbove { - minAbove = maxParameter - } - } - hasRestParameter := core.Some(signatures, c.hasEffectiveRestParameter) - var parameterRange string - switch { - case hasRestParameter: - parameterRange = strconv.Itoa(minCount) - case minCount < maxCount: - parameterRange = strconv.Itoa(minCount) + "-" + strconv.Itoa(maxCount) - default: - parameterRange = strconv.Itoa(minCount) - } - isVoidPromiseError := !hasRestParameter && parameterRange == "1" && len(args) == 0 && c.isPromiseResolveArityError(node) - var message *diagnostics.Message - switch { - case ast.IsDecorator(node): - if hasRestParameter { - message = diagnostics.The_runtime_will_invoke_the_decorator_with_1_arguments_but_the_decorator_expects_at_least_0 - } else { - message = diagnostics.The_runtime_will_invoke_the_decorator_with_1_arguments_but_the_decorator_expects_0 - } - case hasRestParameter: - message = diagnostics.Expected_at_least_0_arguments_but_got_1 - case isVoidPromiseError: - message = diagnostics.Expected_0_arguments_but_got_1_Did_you_forget_to_include_void_in_your_type_argument_to_Promise - default: - message = diagnostics.Expected_0_arguments_but_got_1 - } - errorNode := getErrorNodeForCallNode(node) - switch { - case minCount < len(args) && len(args) < maxCount: - // between min and max, but with no matching overload - diagnostic := NewDiagnosticForNode(errorNode, diagnostics.No_overload_expects_0_arguments_but_overloads_do_exist_that_expect_either_1_or_2_arguments, len(args), maxBelow, minAbove) - if headMessage != nil { - diagnostic = ast.NewDiagnosticChain(diagnostic, headMessage) - } - return diagnostic - case len(args) < minCount: - // too short: put the error span on the call expression, not any of the args - diagnostic := NewDiagnosticForNode(errorNode, message, parameterRange, len(args)) - if headMessage != nil { - diagnostic = ast.NewDiagnosticChain(diagnostic, headMessage) - } - var parameter *ast.Node - if closestSignature != nil && closestSignature.declaration != nil { - parameter = core.ElementOrNil(closestSignature.declaration.Parameters(), len(args)+core.IfElse(closestSignature.thisParameter != nil, 1, 0)) - } - if parameter != nil { - var related *ast.Diagnostic - switch { - case ast.IsBindingPattern(parameter.Name()): - related = NewDiagnosticForNode(parameter, diagnostics.An_argument_matching_this_binding_pattern_was_not_provided) - case isRestParameter(parameter): - related = NewDiagnosticForNode(parameter, diagnostics.Arguments_for_the_rest_parameter_0_were_not_provided, parameter.Name().Text()) - default: - related = NewDiagnosticForNode(parameter, diagnostics.An_argument_for_0_was_not_provided, parameter.Name().Text()) - } - diagnostic.AddRelatedInfo(related) - } - return diagnostic - default: - sourceFile := ast.GetSourceFileOfNode(node) - pos := scanner.SkipTrivia(sourceFile.Text(), args[maxCount].Pos()) - end := args[len(args)-1].End() - if end == pos { - end++ - } - diagnostic := ast.NewDiagnostic(sourceFile, core.NewTextRange(pos, end), message, parameterRange, len(args)) - if headMessage != nil { - diagnostic = ast.NewDiagnosticChain(diagnostic, headMessage) - } - return diagnostic - } -} - -func (c *Checker) isPromiseResolveArityError(node *ast.Node) bool { - if !ast.IsCallExpression(node) || !ast.IsIdentifier(node.Expression()) { - return false - } - symbol := c.resolveName(node.Expression(), node.Expression().Text(), ast.SymbolFlagsValue, nil /*nameNotFoundMessage*/, false /*isUse*/, false) - if symbol == nil { - return false - } - decl := symbol.ValueDeclaration - if decl == nil || !ast.IsParameter(decl) || !ast.IsFunctionExpressionOrArrowFunction(decl.Parent) || !ast.IsNewExpression(decl.Parent.Parent) || !ast.IsIdentifier(decl.Parent.Parent.Expression()) { - return false - } - globalPromiseSymbol := c.getGlobalPromiseConstructorSymbolOrNil() - if globalPromiseSymbol == nil { - return false - } - constructorSymbol := c.getResolvedSymbol(decl.Parent.Parent.Expression()) - return constructorSymbol == globalPromiseSymbol -} - -func getErrorNodeForCallNode(node *ast.Node) *ast.Node { - if ast.IsCallExpression(node) { - node = node.Expression() - if ast.IsPropertyAccessExpression(node) { - node = node.Name() - } - } - return node -} - -func (c *Checker) getTypeArgumentArityError(node *ast.Node, signatures []*Signature, typeArguments []*ast.Node, headMessage *diagnostics.Message) *ast.Diagnostic { - var diagnostic *ast.Diagnostic - argCount := len(typeArguments) - if len(signatures) == 1 { - // No overloads exist - sig := signatures[0] - minCount := c.getMinTypeArgumentCount(sig.typeParameters) - maxCount := len(sig.typeParameters) - expected := strconv.Itoa(minCount) - if minCount < maxCount { - expected = expected + "-" + strconv.Itoa(maxCount) - } - diagnostic = ast.NewDiagnostic(ast.GetSourceFileOfNode(node), node.TypeArgumentList().Loc, diagnostics.Expected_0_type_arguments_but_got_1, expected, argCount) - } else { - // Overloads exist - belowArgCount := math.MinInt - aboveArgCount := math.MaxInt - for _, sig := range signatures { - minCount := c.getMinTypeArgumentCount(sig.typeParameters) - maxCount := len(sig.typeParameters) - if minCount > argCount { - aboveArgCount = min(aboveArgCount, minCount) - } else if maxCount < argCount { - belowArgCount = max(belowArgCount, maxCount) - } - } - if belowArgCount != math.MinInt && aboveArgCount != math.MaxInt { - diagnostic = ast.NewDiagnostic(ast.GetSourceFileOfNode(node), node.TypeArgumentList().Loc, diagnostics.No_overload_expects_0_type_arguments_but_overloads_do_exist_that_expect_either_1_or_2_type_arguments, argCount, belowArgCount, aboveArgCount) - } else { - diagnostic = ast.NewDiagnostic(ast.GetSourceFileOfNode(node), node.TypeArgumentList().Loc, diagnostics.Expected_0_type_arguments_but_got_1, core.IfElse(belowArgCount == math.MinInt, aboveArgCount, belowArgCount), argCount) - } - } - if headMessage != nil { - diagnostic = ast.NewDiagnosticChain(diagnostic, headMessage) - } - return diagnostic -} - -func (c *Checker) reportCannotInvokePossiblyNullOrUndefinedError(node *ast.Node, facts TypeFacts) { - c.error(node, core.IfElse(facts&TypeFactsIsUndefined != 0, - core.IfElse(facts&TypeFactsIsNull != 0, - diagnostics.Cannot_invoke_an_object_which_is_possibly_null_or_undefined, - diagnostics.Cannot_invoke_an_object_which_is_possibly_undefined), - diagnostics.Cannot_invoke_an_object_which_is_possibly_null)) -} - -func (c *Checker) resolveUntypedCall(node *ast.Node) *Signature { - if c.callLikeExpressionMayHaveTypeArguments(node) { - // Check type arguments even though we will give an error that untyped calls may not accept type arguments. - // This gets us diagnostics for the type arguments and marks them as referenced. - c.checkSourceElements(node.TypeArguments()) - } - switch node.Kind { - case ast.KindTaggedTemplateExpression: - c.checkExpression(node.AsTaggedTemplateExpression().Template) - case ast.KindJsxOpeningElement: - c.checkExpression(node.AsJsxOpeningElement().Attributes) - case ast.KindJsxSelfClosingElement: - c.checkExpression(node.AsJsxSelfClosingElement().Attributes) - case ast.KindBinaryExpression: - c.checkExpression(node.AsBinaryExpression().Left) - case ast.KindCallExpression, ast.KindNewExpression: - for _, argument := range node.Arguments() { - c.checkExpression(argument) - } - } - return c.anySignature -} - -func (c *Checker) resolveErrorCall(node *ast.Node) *Signature { - c.resolveUntypedCall(node) - return c.unknownSignature -} - -/** - * TS 1.0 spec: 4.12 - * If FuncExpr is of type Any, or of an object type that has no call or construct signatures - * but is a subtype of the Function interface, the call is an untyped function call. - */ -func (c *Checker) isUntypedFunctionCall(funcType *Type, apparentFuncType *Type, numCallSignatures int, numConstructSignatures int) bool { - // We exclude union types because we may have a union of function types that happen to have no common signatures. - return IsTypeAny(funcType) || IsTypeAny(apparentFuncType) && funcType.flags&TypeFlagsTypeParameter != 0 || - numCallSignatures == 0 && numConstructSignatures == 0 && apparentFuncType.flags&TypeFlagsUnion == 0 && - c.getReducedType(apparentFuncType).flags&TypeFlagsNever == 0 && c.isTypeAssignableTo(funcType, c.globalFunctionType) -} - -func (c *Checker) invocationErrorDetails(errorTarget *ast.Node, apparentType *Type, kind SignatureKind) *ast.Diagnostic { - var diagnostic *ast.Diagnostic - isCall := kind == SignatureKindCall - awaitedType := c.getAwaitedType(apparentType) - maybeMissingAwait := awaitedType != nil && len(c.getSignaturesOfType(awaitedType, kind)) > 0 - target := errorTarget - if ast.IsPropertyAccessExpression(errorTarget) && ast.IsCallExpression(errorTarget.Parent) { - target = errorTarget.Name() - } - if apparentType.flags&TypeFlagsUnion != 0 { - types := apparentType.Types() - hasSignatures := false - for _, constituent := range types { - signatures := c.getSignaturesOfType(constituent, kind) - if len(signatures) != 0 { - hasSignatures = true - if diagnostic != nil { - // Bail early if we already have an error, no chance of "No constituent of type is callable" - break - } - } else { - // Error on the first non callable constituent only - if diagnostic == nil { - diagnostic = NewDiagnosticForNode(target, core.IfElse(isCall, diagnostics.Type_0_has_no_call_signatures, diagnostics.Type_0_has_no_construct_signatures), c.TypeToString(constituent)) - diagnostic = NewDiagnosticChainForNode(diagnostic, target, core.IfElse(isCall, diagnostics.Not_all_constituents_of_type_0_are_callable, diagnostics.Not_all_constituents_of_type_0_are_constructable), c.TypeToString(apparentType)) - } - if hasSignatures { - // Bail early if we already found a signature, no chance of "No constituent of type is callable" - break - } - } - } - if !hasSignatures { - diagnostic = NewDiagnosticForNode(target, core.IfElse(isCall, diagnostics.No_constituent_of_type_0_is_callable, diagnostics.No_constituent_of_type_0_is_constructable), c.TypeToString(apparentType)) - } - if diagnostic == nil { - diagnostic = NewDiagnosticForNode(target, core.IfElse(isCall, diagnostics.Each_member_of_the_union_type_0_has_signatures_but_none_of_those_signatures_are_compatible_with_each_other, diagnostics.Each_member_of_the_union_type_0_has_construct_signatures_but_none_of_those_signatures_are_compatible_with_each_other), c.TypeToString(apparentType)) - } - } else { - diagnostic = NewDiagnosticChainForNode(diagnostic, target, core.IfElse(isCall, diagnostics.Type_0_has_no_call_signatures, diagnostics.Type_0_has_no_construct_signatures), c.TypeToString(apparentType)) - } - headMessage := core.IfElse(isCall, diagnostics.This_expression_is_not_callable, diagnostics.This_expression_is_not_constructable) - // Diagnose get accessors incorrectly called as functions - if ast.IsCallExpression(errorTarget.Parent) && len(errorTarget.Parent.Arguments()) == 0 { - resolvedSymbol := c.getResolvedSymbolOrNil(errorTarget) - if resolvedSymbol != nil && resolvedSymbol.Flags&ast.SymbolFlagsGetAccessor != 0 { - headMessage = diagnostics.This_expression_is_not_callable_because_it_is_a_get_accessor_Did_you_mean_to_use_it_without - } - } - diagnostic = NewDiagnosticChainForNode(diagnostic, target, headMessage) - if maybeMissingAwait { - diagnostic.AddRelatedInfo(NewDiagnosticForNode(errorTarget, diagnostics.Did_you_forget_to_use_await)) - } - return diagnostic -} - -func (c *Checker) invocationError(errorTarget *ast.Node, apparentType *Type, kind SignatureKind, relatedInformation *ast.Diagnostic) { - diagnostic := c.invocationErrorDetails(errorTarget, apparentType, kind) - if relatedInformation != nil { - diagnostic.AddRelatedInfo(relatedInformation) - } - c.diagnostics.Add(diagnostic) - c.invocationErrorRecovery(apparentType, kind, diagnostic) -} - -func (c *Checker) invocationErrorRecovery(apparentType *Type, kind SignatureKind, diagnostic *ast.Diagnostic) { - if apparentType.symbol == nil { - return - } - importNode := c.exportTypeLinks.Get(apparentType.symbol).originatingImport - // Create a diagnostic on the originating import if possible onto which we can attach a quickfix - // An import call expression cannot be rewritten into another form to correct the error - the only solution is to use `.default` at the use-site - if importNode != nil && !ast.IsImportCall(importNode) { - sigs := c.getSignaturesOfType(c.getTypeOfSymbol(c.valueSymbolLinks.Get(apparentType.symbol).target), kind) - if len(sigs) == 0 { - return - } - diagnostic.AddRelatedInfo(NewDiagnosticForNode(importNode, diagnostics.Type_originates_at_this_import_A_namespace_style_import_cannot_be_called_or_constructed_and_will_cause_a_failure_at_runtime_Consider_using_a_default_import_or_import_require_here_instead)) - } -} - -func (c *Checker) isGenericFunctionReturningFunction(signature *Signature) bool { - return len(signature.typeParameters) != 0 && c.isFunctionType(c.getReturnTypeOfSignature(signature)) -} - -func (c *Checker) skippedGenericFunction(node *ast.Node, checkMode CheckMode) { - if checkMode&CheckModeInferential != 0 { - // We have skipped a generic function during inferential typing. Obtain the inference context and - // indicate this has occurred such that we know a second pass of inference is be needed. - context := c.getInferenceContext(node) - context.flags |= InferenceFlagsSkippedGenericFunction - } -} - -func (c *Checker) checkTaggedTemplateExpression(node *ast.Node) *Type { - if !c.checkGrammarTaggedTemplateChain(node.AsTaggedTemplateExpression()) { - c.checkGrammarTypeArguments(node, node.TypeArgumentList()) - } - signature := c.getResolvedSignature(node, nil, CheckModeNormal) - c.checkDeprecatedSignature(signature, node) - return c.getReturnTypeOfSignature(signature) -} - -func (c *Checker) checkParenthesizedExpression(node *ast.Node, checkMode CheckMode) *Type { - return c.checkExpressionEx(node.Expression(), checkMode) -} - -func (c *Checker) checkClassExpression(node *ast.Node) *Type { - c.checkClassLikeDeclaration(node) - c.checkNodeDeferred(node) - return c.getTypeOfSymbol(c.getSymbolOfDeclaration(node)) -} - -func (c *Checker) checkClassExpressionDeferred(node *ast.Node) { - c.checkSourceElements(node.Members()) - c.registerForUnusedIdentifiersCheck(node) -} - -func (c *Checker) checkFunctionExpressionOrObjectLiteralMethod(node *ast.Node, checkMode CheckMode) *Type { - c.checkNodeDeferred(node) - if ast.IsFunctionExpression(node) { - c.checkCollisionsForDeclarationName(node, node.Name()) - } - if checkMode&CheckModeSkipContextSensitive != 0 && c.isContextSensitive(node) { - // Skip parameters, return signature with return type that retains noncontextual parts so inferences can still be drawn in an early stage - if node.Type() == nil && !hasContextSensitiveParameters(node) { - // Return plain anyFunctionType if there is no possibility we'll make inferences from the return type - contextualSignature := c.getContextualSignature(node) - if contextualSignature != nil && c.couldContainTypeVariables(c.getReturnTypeOfSignature(contextualSignature)) { - if cached, ok := c.contextFreeTypes[node]; ok { - return cached - } - returnType := c.getReturnTypeFromBody(node, checkMode) - returnOnlySignature := c.newSignature(SignatureFlagsIsNonInferrable, nil, nil /*typeParameters*/, nil /*thisParameter*/, nil, returnType, nil /*resolvedTypePredicate*/, 0) - returnOnlyType := c.newAnonymousType(node.Symbol(), nil, []*Signature{returnOnlySignature}, nil, nil) - returnOnlyType.objectFlags |= ObjectFlagsNonInferrableType - c.contextFreeTypes[node] = returnOnlyType - return returnOnlyType - } - } - return c.anyFunctionType - } - // Grammar checking - hasGrammarError := c.checkGrammarFunctionLikeDeclaration(node) - if !hasGrammarError && ast.IsFunctionExpression(node) { - c.checkGrammarForGenerator(node) - } - if node.FunctionLikeData().FullSignature != nil { - if c.getContextualCallSignature(c.getTypeFromTypeNode(node.FunctionLikeData().FullSignature), node) == nil { - c.error(node.FunctionLikeData().FullSignature, diagnostics.A_JSDoc_type_tag_on_a_function_must_have_a_signature_with_the_correct_number_of_arguments) - } - if node.Type() != nil || core.Some(node.Parameters(), func(p *ast.Node) bool { return p.Type() != nil }) { - c.error(node.FunctionLikeData().FullSignature, diagnostics.A_JSDoc_type_tag_may_not_occur_with_a_param_or_returns_tag) - } - } - c.contextuallyCheckFunctionExpressionOrObjectLiteralMethod(node, checkMode) - return c.getTypeOfSymbol(c.getSymbolOfDeclaration(node)) -} - -func (c *Checker) contextuallyCheckFunctionExpressionOrObjectLiteralMethod(node *ast.Node, checkMode CheckMode) { - links := c.nodeLinks.Get(node) - // Check if function expression is contextually typed and assign parameter types if so. - if links.flags&NodeCheckFlagsContextChecked == 0 { - contextualSignature := c.getContextualSignature(node) - // If a type check is started at a function expression that is an argument of a function call, obtaining the - // contextual type may recursively get back to here during overload resolution of the call. If so, we will have - // already assigned contextual types. - if links.flags&NodeCheckFlagsContextChecked == 0 { - links.flags |= NodeCheckFlagsContextChecked - signature := core.FirstOrNil(c.getSignaturesOfType(c.getTypeOfSymbol(c.getSymbolOfDeclaration(node)), SignatureKindCall)) - if signature == nil { - return - } - if c.isContextSensitive(node) { - if contextualSignature != nil { - inferenceContext := c.getInferenceContext(node) - var instantiatedContextualSignature *Signature - if checkMode&CheckModeInferential != 0 { - c.inferFromAnnotatedParametersAndReturn(signature, contextualSignature, inferenceContext) - restType := c.getEffectiveRestType(contextualSignature) - if restType != nil && restType.flags&TypeFlagsTypeParameter != 0 { - instantiatedContextualSignature = c.instantiateSignature(contextualSignature, inferenceContext.nonFixingMapper) - } - } - if instantiatedContextualSignature == nil { - if inferenceContext != nil { - instantiatedContextualSignature = c.instantiateSignature(contextualSignature, inferenceContext.mapper) - } else { - instantiatedContextualSignature = contextualSignature - } - } - c.assignContextualParameterTypes(signature, instantiatedContextualSignature) - } else { - // Force resolution of all parameter types such that the absence of a contextual type is consistently reflected. - c.assignNonContextualParameterTypes(signature) - } - } else if contextualSignature != nil && node.TypeParameters() == nil && len(contextualSignature.parameters) > len(node.Parameters()) { - inferenceContext := c.getInferenceContext(node) - if checkMode&CheckModeInferential != 0 { - c.inferFromAnnotatedParametersAndReturn(signature, contextualSignature, inferenceContext) - } - } - if contextualSignature != nil && c.getReturnTypeFromAnnotation(node) == nil && signature.resolvedReturnType == nil { - returnType := c.getReturnTypeFromBody(node, checkMode) - if signature.resolvedReturnType == nil { - signature.resolvedReturnType = returnType - } - } - c.checkSignatureDeclaration(node) - } - } -} - -func (c *Checker) checkFunctionExpressionOrObjectLiteralMethodDeferred(node *ast.Node) { - functionFlags := getFunctionFlags(node) - returnType := c.getReturnTypeFromAnnotation(node) - c.checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, returnType) - body := node.Body() - if body != nil { - if node.Type() == nil { - // There are some checks that are only performed in getReturnTypeFromBody, that may produce errors - // we need. An example is the noImplicitAny errors resulting from widening the return expression - // of a function. Because checking of function expression bodies is deferred, there was never an - // appropriate time to do this during the main walk of the file (see the comment at the top of - // checkFunctionExpressionBodies). So it must be done now. - c.getReturnTypeOfSignature(c.getSignatureFromDeclaration(node)) - } - if ast.IsBlock(body) { - c.checkSourceElement(body) - } else { - // From within an async function you can return either a non-promise value or a promise. Any - // Promise/A+ compatible implementation will always assimilate any foreign promise, so we - // should not be checking assignability of a promise to the return type. Instead, we need to - // check assignability of the awaited type of the expression body against the promised type of - // its return type annotation. - exprType := c.checkExpression(body) - if returnType != nil { - returnOrPromisedType := c.unwrapReturnType(returnType, functionFlags) - if returnOrPromisedType != nil { - c.checkReturnExpression(node, returnOrPromisedType, body, body, exprType, false) - } - } - } - } -} - -func (c *Checker) inferFromAnnotatedParametersAndReturn(sig *Signature, context *Signature, inferenceContext *InferenceContext) { - length := len(sig.parameters) - core.IfElse(signatureHasRestParameter(sig), 1, 0) - for i := range length { - declaration := sig.parameters[i].ValueDeclaration - typeNode := declaration.Type() - if typeNode != nil { - source := c.addOptionalityEx(c.getTypeFromTypeNode(typeNode), false /*isProperty*/, isOptionalDeclaration(declaration)) - target := c.getTypeAtPosition(context, i) - c.inferTypes(inferenceContext.inferences, source, target, InferencePriorityNone, false) - } - } - if declaration := sig.Declaration(); declaration != nil { - if returnTypeNode := declaration.Type(); returnTypeNode != nil { - source := c.getTypeFromTypeNode(returnTypeNode) - target := c.getReturnTypeOfSignature(context) - c.inferTypes(inferenceContext.inferences, source, target, InferencePriorityNone, false) - } - } -} - -// Return the contextual signature for a given expression node. A contextual type provides a -// contextual signature if it has a single call signature and if that call signature is non-generic. -// If the contextual type is a union type, get the signature from each type possible and if they are -// all identical ignoring their return type, the result is same signature but with return type as -// union type of return types from these signatures -func (c *Checker) getContextualSignature(node *ast.Node) *Signature { - t := c.getApparentTypeOfContextualType(node, ContextFlagsSignature) - if t == nil { - return nil - } - if t.flags&TypeFlagsUnion == 0 { - return c.getContextualCallSignature(t, node) - } - var signatureList []*Signature - types := t.Types() - for _, current := range types { - signature := c.getContextualCallSignature(current, node) - if signature != nil { - if len(signatureList) != 0 && c.compareSignaturesIdentical(signatureList[0], signature, false /*partialMatch*/, true /*ignoreThisTypes*/, true /*ignoreReturnTypes*/, c.compareTypesIdentical) == TernaryFalse { - // Signatures aren't identical, do not use - return nil - } - // Use this signature for contextual union signature - signatureList = append(signatureList, signature) - } - } - switch len(signatureList) { - case 0: - return nil - case 1: - return signatureList[0] - } - // Result is union of signatures collected (return type is union of return types of this signature set) - return c.createUnionSignature(signatureList[0], signatureList) -} - -func (c *Checker) createUnionSignature(sig *Signature, unionSignatures []*Signature) *Signature { - result := c.cloneSignature(sig) - result.composite = &CompositeSignature{isUnion: true, signatures: unionSignatures} - result.target = nil - result.mapper = nil - return result -} - -// If the given type is an object or union type with a single signature, and if that signature has at -// least as many parameters as the given function, return the signature. Otherwise return undefined. -func (c *Checker) getContextualCallSignature(t *Type, node *ast.Node) *Signature { - signatures := c.getSignaturesOfType(t, SignatureKindCall) - applicableByArity := core.Filter(signatures, func(s *Signature) bool { return !c.isAritySmaller(s, node) }) - if len(applicableByArity) == 1 { - return applicableByArity[0] - } - return c.getIntersectedSignatures(applicableByArity) -} - -func (c *Checker) getIntersectedSignatures(signatures []*Signature) *Signature { - if !c.noImplicitAny { - return nil - } - var combined *Signature - for _, sig := range signatures { - switch { - case combined == sig || combined == nil: - combined = sig - case c.compareTypeParametersIdentical(combined.typeParameters, sig.typeParameters): - combined = c.combineUnionOrIntersectionMemberSignatures(combined, sig, false /*isUnion*/) - default: - return nil - } - } - return combined -} - -/** If the contextual signature has fewer parameters than the function expression, do not use it */ -func (c *Checker) isAritySmaller(signature *Signature, target *ast.Node) bool { - parameters := target.Parameters() - targetParameterCount := 0 - for targetParameterCount < len(parameters) { - param := parameters[targetParameterCount] - if param.Initializer() != nil || param.AsParameterDeclaration().QuestionToken != nil || hasDotDotDotToken(param) { - break - } - targetParameterCount++ - } - if len(parameters) != 0 && ast.IsThisParameter(parameters[0]) { - targetParameterCount-- - } - return !c.hasEffectiveRestParameter(signature) && c.getParameterCount(signature) < targetParameterCount -} - -func (c *Checker) assignContextualParameterTypes(sig *Signature, context *Signature) { - if len(context.typeParameters) != 0 { - if len(sig.typeParameters) != 0 { - // This signature has already has a contextual inference performed and cached on it - return - } - sig.typeParameters = context.typeParameters - } - if context.thisParameter != nil { - parameter := sig.thisParameter - if parameter == nil || parameter.ValueDeclaration != nil && parameter.ValueDeclaration.Type() == nil { - if parameter == nil { - sig.thisParameter = c.createSymbolWithType(context.thisParameter, nil /*type*/) - } - c.assignParameterType(sig.thisParameter, c.getTypeOfSymbol(context.thisParameter)) - } - } - length := len(sig.parameters) - core.IfElse(signatureHasRestParameter(sig), 1, 0) - for i := range length { - parameter := sig.parameters[i] - declaration := parameter.ValueDeclaration - if declaration.Type() == nil { - t := c.tryGetTypeAtPosition(context, i) - if t != nil && declaration.Initializer() != nil { - initializerType := c.checkDeclarationInitializer(declaration, CheckModeNormal, nil) - if !c.isTypeAssignableTo(initializerType, t) { - initializerType = c.widenTypeInferredFromInitializer(declaration, initializerType) - if c.isTypeAssignableTo(t, initializerType) { - t = initializerType - } - } - } - c.assignParameterType(parameter, t) - } - } - if signatureHasRestParameter(sig) { - // parameter might be a transient symbol generated by use of `arguments` in the function body. - parameter := core.LastOrNil(sig.parameters) - if parameter.ValueDeclaration != nil && parameter.ValueDeclaration.Type() == nil || - parameter.ValueDeclaration == nil && parameter.CheckFlags&ast.CheckFlagsDeferredType != 0 { - contextualParameterType := c.getRestTypeAtPosition(context, length, false) - c.assignParameterType(parameter, contextualParameterType) - } - } -} - -func (c *Checker) assignNonContextualParameterTypes(signature *Signature) { - if signature.thisParameter != nil { - c.assignParameterType(signature.thisParameter, nil) - } - for _, parameter := range signature.parameters { - c.assignParameterType(parameter, nil) - } -} - -func (c *Checker) assignParameterType(parameter *ast.Symbol, contextualType *Type) { - links := c.valueSymbolLinks.Get(parameter) - if links.resolvedType != nil { - return - } - declaration := parameter.ValueDeclaration - t := contextualType - if t == nil { - if declaration != nil { - t = c.getWidenedTypeForVariableLikeDeclaration(declaration, true /*reportErrors*/) - } else { - t = c.getTypeOfSymbol(parameter) - } - } - links.resolvedType = c.addOptionalityEx(t, false, declaration != nil && declaration.Initializer() == nil && isOptionalDeclaration(declaration)) - if declaration != nil && !ast.IsIdentifier(declaration.Name()) { - // if inference didn't come up with anything but unknown, fall back to the binding pattern if present. - if links.resolvedType == c.unknownType { - links.resolvedType = c.getTypeFromBindingPattern(declaration.Name(), false, false) - } - c.assignBindingElementTypes(declaration.Name(), links.resolvedType) - } -} - -// When contextual typing assigns a type to a parameter that contains a binding pattern, we also need to push -// the destructured type into the contained binding elements. -func (c *Checker) assignBindingElementTypes(pattern *ast.Node, parentType *Type) { - for _, element := range pattern.AsBindingPattern().Elements.Nodes { - name := element.Name() - if name != nil { - t := c.getBindingElementTypeFromParentType(element, parentType, false /*noTupleBoundsCheck*/) - if ast.IsIdentifier(name) { - c.valueSymbolLinks.Get(c.getSymbolOfDeclaration(element)).resolvedType = t - } else { - c.assignBindingElementTypes(name, t) - } - } - } -} - -func (c *Checker) checkCollisionsForDeclarationName(node *ast.Node, name *ast.Node) { - c.checkCollisionWithRequireExportsInGeneratedCode(node, name) - switch { - case name == nil: - return - case ast.IsClassLike(node): - c.checkTypeNameIsReserved(name, diagnostics.Class_name_cannot_be_0) - case ast.IsEnumDeclaration(node): - c.checkTypeNameIsReserved(name, diagnostics.Enum_name_cannot_be_0) - } -} - -func (c *Checker) checkCollisionWithRequireExportsInGeneratedCode(node *ast.Node, name *ast.Node) { - // No need to check for require or exports for ES6 modules and later - if c.program.GetEmitModuleFormatOfFile(ast.GetSourceFileOfNode(node)) >= core.ModuleKindES2015 { - return - } - if name == nil || !c.needCollisionCheckForIdentifier(node, name, "require") && !c.needCollisionCheckForIdentifier(node, name, "exports") { - return - } - // Uninstantiated modules shouldnt do this check - if ast.IsModuleDeclaration(node) && ast.GetModuleInstanceState(node) != ast.ModuleInstanceStateInstantiated { - return - } - // In case of variable declaration, node.parent is variable statement so look at the variable statement's parent - parent := ast.GetDeclarationContainer(node) - if ast.IsSourceFile(parent) && ast.IsExternalOrCommonJSModule(parent.AsSourceFile()) { - // If the declaration happens to be in external module, report error that require and exports are reserved keywords - c.errorSkippedOnNoEmit(name, diagnostics.Duplicate_identifier_0_Compiler_reserves_name_1_in_top_level_scope_of_a_module, scanner.DeclarationNameToString(name), scanner.DeclarationNameToString(name)) - } -} - -func (c *Checker) needCollisionCheckForIdentifier(node *ast.Node, identifier *ast.Node, name string) bool { - if identifier != nil && identifier.Text() != name { - return false - } - switch node.Kind { - case ast.KindPropertyDeclaration, ast.KindPropertySignature, ast.KindMethodDeclaration, ast.KindMethodSignature, - ast.KindGetAccessor, ast.KindSetAccessor, ast.KindPropertyAssignment: - // it is ok to have member named '_super', '_this', `Promise`, etc. - member access is always qualified - return false - } - if node.Flags&ast.NodeFlagsAmbient != 0 { - // ambient context - no codegen impact - return false - } - if ast.IsImportClause(node) || ast.IsImportEqualsDeclaration(node) || ast.IsImportSpecifier(node) { - // type-only imports do not require collision checks against runtime values. - if ast.IsTypeOnlyImportOrExportDeclaration(node) { - return false - } - } - root := ast.GetRootDeclaration(node) - if ast.IsParameter(root) && ast.NodeIsMissing(root.Parent.Body()) { - // just an overload - no codegen impact - return false - } - return true -} - -func (c *Checker) checkTypeOfExpression(node *ast.Node) *Type { - c.checkExpression(node.Expression()) - return c.typeofType -} - -func (c *Checker) checkNonNullAssertion(node *ast.Node) *Type { - if node.Flags&ast.NodeFlagsOptionalChain != 0 { - return c.checkNonNullChain(node) - } - return c.GetNonNullableType(c.checkExpression(node.Expression())) -} - -func (c *Checker) checkNonNullChain(node *ast.Node) *Type { - leftType := c.checkExpression(node.Expression()) - nonOptionalType := c.getOptionalExpressionType(leftType, node.Expression()) - return c.propagateOptionalTypeMarker(c.GetNonNullableType(nonOptionalType), node, nonOptionalType != leftType) -} - -func (c *Checker) checkExpressionWithTypeArguments(node *ast.Node) *Type { - c.checkGrammarExpressionWithTypeArguments(node) - c.checkSourceElements(node.TypeArguments()) - if ast.IsExpressionWithTypeArguments(node) { - parent := ast.WalkUpParenthesizedExpressions(node.Parent) - if ast.IsBinaryExpression(parent) && parent.AsBinaryExpression().OperatorToken.Kind == ast.KindInstanceOfKeyword && isNodeDescendantOf(node, parent.AsBinaryExpression().Right) { - c.error(node, diagnostics.The_right_hand_side_of_an_instanceof_expression_must_not_be_an_instantiation_expression) - } - } - var exprType *Type - if ast.IsExpressionWithTypeArguments(node) { - exprType = c.checkExpression(node.Expression()) - } else { - exprName := node.AsTypeQueryNode().ExprName - if ast.IsThisIdentifier(exprName) { - exprType = c.checkThisExpression(node.AsTypeQueryNode().ExprName) - } else { - exprType = c.checkExpression(node.AsTypeQueryNode().ExprName) - } - } - return c.getInstantiationExpressionType(exprType, node) -} - -func (c *Checker) getInstantiationExpressionType(exprType *Type, node *ast.Node) *Type { - typeArguments := node.TypeArgumentList() - if exprType == c.silentNeverType || c.isErrorType(exprType) || typeArguments == nil { - return exprType - } - key := InstantiationExpressionKey{nodeId: ast.GetNodeId(node), typeId: exprType.id} - if cached := c.instantiationExpressionTypes[key]; cached != nil { - return cached - } - hasSomeApplicableSignature := false - var nonApplicableType *Type - getInstantiatedSignatures := func(signatures []*Signature) []*Signature { - applicableSignatures := core.Filter(signatures, func(sig *Signature) bool { - return len(sig.typeParameters) != 0 && c.hasCorrectTypeArgumentArity(sig, typeArguments.Nodes) - }) - return core.SameMap(applicableSignatures, func(sig *Signature) *Signature { - typeArgumentTypes := c.checkTypeArguments(sig, typeArguments.Nodes, true /*reportErrors*/, nil) - if typeArgumentTypes != nil { - return c.getSignatureInstantiation(sig, typeArgumentTypes, ast.IsInJSFile(sig.declaration), nil) - } - return sig - }) - } - var getInstantiatedType func(*Type) *Type - getInstantiatedType = func(t *Type) *Type { - hasSignatures := false - hasApplicableSignature := false - var getInstantiatedTypePart func(*Type) *Type - getInstantiatedTypePart = func(t *Type) *Type { - if t.flags&TypeFlagsObject != 0 { - resolved := c.resolveStructuredTypeMembers(t) - callSignatures := getInstantiatedSignatures(resolved.CallSignatures()) - constructSignatures := getInstantiatedSignatures(resolved.ConstructSignatures()) - hasSignatures = hasSignatures || len(resolved.CallSignatures()) != 0 || len(resolved.ConstructSignatures()) != 0 - hasApplicableSignature = hasApplicableSignature || len(callSignatures) != 0 || len(constructSignatures) != 0 - if !core.Same(callSignatures, resolved.CallSignatures()) || !core.Same(constructSignatures, resolved.ConstructSignatures()) { - result := c.newObjectType(ObjectFlagsAnonymous|ObjectFlagsInstantiationExpressionType, c.newSymbol(ast.SymbolFlagsNone, ast.InternalSymbolNameInstantiationExpression)) - c.setStructuredTypeMembers(result, resolved.members, callSignatures, constructSignatures, resolved.indexInfos) - result.AsInstantiationExpressionType().node = node - return result - } - } else if t.flags&TypeFlagsInstantiableNonPrimitive != 0 { - constraint := c.getBaseConstraintOfType(t) - if constraint != nil { - instantiated := getInstantiatedTypePart(constraint) - if instantiated != constraint { - return instantiated - } - } - } else if t.flags&TypeFlagsUnion != 0 { - return c.mapType(t, getInstantiatedType) - } else if t.flags&TypeFlagsIntersection != 0 { - return c.getIntersectionType(core.SameMap(t.AsIntersectionType().types, getInstantiatedTypePart)) - } - return t - } - result := getInstantiatedTypePart(t) - hasSomeApplicableSignature = hasSomeApplicableSignature || hasApplicableSignature - if hasSignatures && !hasApplicableSignature { - if nonApplicableType == nil { - nonApplicableType = t - } - } - return result - } - result := getInstantiatedType(exprType) - c.instantiationExpressionTypes[key] = result - var errorType *Type - if hasSomeApplicableSignature { - errorType = nonApplicableType - } else { - errorType = exprType - } - if errorType != nil { - sourceFile := ast.GetSourceFileOfNode(node) - loc := core.NewTextRange(scanner.SkipTrivia(sourceFile.Text(), typeArguments.Pos()), typeArguments.End()) - c.diagnostics.Add(ast.NewDiagnostic(sourceFile, loc, diagnostics.Type_0_has_no_signatures_for_which_the_type_argument_list_is_applicable, c.TypeToString(errorType))) - } - return result -} - -func (c *Checker) checkSatisfiesExpression(node *ast.Node) *Type { - c.checkSourceElement(node.Type()) - return c.checkSatisfiesExpressionWorker(node.Expression(), node.Type(), CheckModeNormal) -} - -func (c *Checker) checkSatisfiesExpressionWorker(expression *ast.Node, target *ast.Node, checkMode CheckMode) *Type { - exprType := c.checkExpressionEx(expression, checkMode) - targetType := c.getTypeFromTypeNode(target) - if c.isErrorType(targetType) { - return targetType - } - errorNode := ast.FindAncestor(target.Parent, func(n *ast.Node) bool { return ast.IsSatisfiesExpression(n) }) - c.checkTypeAssignableToAndOptionallyElaborate(exprType, targetType, errorNode, expression, diagnostics.Type_0_does_not_satisfy_the_expected_type_1, nil) - return exprType -} - -func (c *Checker) checkMetaProperty(node *ast.Node) *Type { - c.checkGrammarMetaProperty(node.AsMetaProperty()) - switch node.AsMetaProperty().KeywordToken { - case ast.KindNewKeyword: - return c.checkNewTargetMetaProperty(node) - case ast.KindImportKeyword: - if node.Name().Text() == "defer" { - debug.Assert(!ast.IsCallExpression(node.Parent) || node.Parent.AsCallExpression().Expression != node, "Trying to get the type of `import.defer` in `import.defer(...)`") - return c.errorType - } - return c.checkImportMetaProperty(node) - } - panic("Unhandled case in checkMetaProperty") -} - -func (c *Checker) checkNewTargetMetaProperty(node *ast.Node) *Type { - container := ast.GetNewTargetContainer(node) - if container == nil { - c.error(node, diagnostics.Meta_property_0_is_only_allowed_in_the_body_of_a_function_declaration_function_expression_or_constructor, "new.target") - return c.errorType - } - if ast.IsConstructorDeclaration(container) { - symbol := c.getSymbolOfDeclaration(container.Parent) - return c.getTypeOfSymbol(symbol) - } - symbol := c.getSymbolOfDeclaration(container) - return c.getTypeOfSymbol(symbol) -} - -func (c *Checker) checkImportMetaProperty(node *ast.Node) *Type { - if core.ModuleKindNode16 <= c.moduleKind && c.moduleKind <= core.ModuleKindNodeNext { - sourceFileMetaData := c.program.GetSourceFileMetaData(ast.GetSourceFileOfNode(node).Path()) - if sourceFileMetaData.ImpliedNodeFormat != core.ModuleKindESNext { - c.error(node, diagnostics.The_import_meta_meta_property_is_not_allowed_in_files_which_will_build_into_CommonJS_output) - } - } else if c.moduleKind < core.ModuleKindES2020 && c.moduleKind != core.ModuleKindSystem { - c.error(node, diagnostics.The_import_meta_meta_property_is_only_allowed_when_the_module_option_is_es2020_es2022_esnext_system_node16_node18_node20_or_nodenext) - } - file := ast.GetSourceFileOfNode(node) - debug.Assert(file.Flags&ast.NodeFlagsPossiblyContainsImportMeta != 0, "Containing file is missing import meta node flag.") - if node.Name().Text() == "meta" { - return c.getGlobalImportMetaType() - } - return c.errorType -} - -func (c *Checker) checkMetaPropertyKeyword(node *ast.Node) *Type { - // !!! This is effectively a helper for GetSymbolAtLocation and GetTypeAtLocation - return c.errorType -} - -func (c *Checker) checkDeleteExpression(node *ast.Node) *Type { - c.checkExpression(node.Expression()) - expr := ast.SkipParentheses(node.Expression()) - if !ast.IsAccessExpression(expr) { - c.error(expr, diagnostics.The_operand_of_a_delete_operator_must_be_a_property_reference) - return c.booleanType - } - if ast.IsPropertyAccessExpression(expr) && ast.IsPrivateIdentifier(expr.Name()) { - c.error(expr, diagnostics.The_operand_of_a_delete_operator_cannot_be_a_private_identifier) - } - symbol := c.getExportSymbolOfValueSymbolIfExported(c.getResolvedSymbolOrNil(expr)) - if symbol != nil { - if c.isReadonlySymbol(symbol) { - c.error(expr, diagnostics.The_operand_of_a_delete_operator_cannot_be_a_read_only_property) - } else { - c.checkDeleteExpressionMustBeOptional(expr, symbol) - } - } - return c.booleanType -} - -func (c *Checker) checkDeleteExpressionMustBeOptional(expr *ast.Node, symbol *ast.Symbol) { - t := c.getTypeOfSymbol(symbol) - if c.strictNullChecks && t.flags&(TypeFlagsAnyOrUnknown|TypeFlagsNever) == 0 { - var isOptional bool - if c.exactOptionalPropertyTypes { - isOptional = symbol.Flags&ast.SymbolFlagsOptional != 0 - } else { - isOptional = c.hasTypeFacts(t, TypeFactsIsUndefined) - } - if !isOptional { - c.error(expr, diagnostics.The_operand_of_a_delete_operator_must_be_optional) - } - } -} - -func (c *Checker) checkVoidExpression(node *ast.Node) *Type { - c.checkNodeDeferred(node) - return c.undefinedWideningType -} - -func (c *Checker) checkAwaitExpression(node *ast.Node) *Type { - c.checkGrammarAwaitOrAwaitUsing(node) - operandType := c.checkExpression(node.Expression()) - awaitedType := c.checkAwaitedType(operandType, true /*withAlias*/, node, diagnostics.Type_of_await_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member) - if awaitedType == operandType && !c.isErrorType(awaitedType) && operandType.flags&TypeFlagsAnyOrUnknown == 0 { - c.addErrorOrSuggestion(false, createDiagnosticForNode(node, diagnostics.X_await_has_no_effect_on_the_type_of_this_expression)) - } - return awaitedType -} - -func (c *Checker) checkPrefixUnaryExpression(node *ast.Node) *Type { - expr := node.AsPrefixUnaryExpression() - operandType := c.checkExpression(expr.Operand) - if operandType == c.silentNeverType { - return c.silentNeverType - } - switch expr.Operand.Kind { - case ast.KindNumericLiteral: - switch expr.Operator { - case ast.KindMinusToken: - return c.getFreshTypeOfLiteralType(c.getNumberLiteralType(-jsnum.FromString(expr.Operand.Text()))) - case ast.KindPlusToken: - return c.getFreshTypeOfLiteralType(c.getNumberLiteralType(+jsnum.FromString(expr.Operand.Text()))) - } - case ast.KindBigIntLiteral: - if expr.Operator == ast.KindMinusToken { - return c.getFreshTypeOfLiteralType(c.getBigIntLiteralType(jsnum.NewPseudoBigInt(jsnum.ParsePseudoBigInt(expr.Operand.Text()), true /*negative*/))) - } - } - switch expr.Operator { - case ast.KindPlusToken, ast.KindMinusToken, ast.KindTildeToken: - c.checkNonNullType(operandType, expr.Operand) - if c.maybeTypeOfKindConsideringBaseConstraint(operandType, TypeFlagsESSymbolLike) { - c.error(expr.Operand, diagnostics.The_0_operator_cannot_be_applied_to_type_symbol, scanner.TokenToString(expr.Operator)) - } - if expr.Operator == ast.KindPlusToken { - if c.maybeTypeOfKindConsideringBaseConstraint(operandType, TypeFlagsBigIntLike) { - c.error(expr.Operand, diagnostics.Operator_0_cannot_be_applied_to_type_1, scanner.TokenToString(expr.Operator), c.TypeToString(c.getBaseTypeOfLiteralType(operandType))) - } - return c.numberType - } - return c.getUnaryResultType(operandType) - case ast.KindExclamationToken: - c.checkTruthinessOfType(operandType, expr.Operand) - facts := c.getTypeFacts(operandType, TypeFactsTruthy|TypeFactsFalsy) - switch { - case facts == TypeFactsTruthy: - return c.falseType - case facts == TypeFactsFalsy: - return c.trueType - default: - return c.booleanType - } - case ast.KindPlusPlusToken, ast.KindMinusMinusToken: - ok := c.checkArithmeticOperandType(expr.Operand, c.checkNonNullType(operandType, expr.Operand), diagnostics.An_arithmetic_operand_must_be_of_type_any_number_bigint_or_an_enum_type, false) - if ok { - // run check only if former checks succeeded to avoid reporting cascading errors - c.checkReferenceExpression(expr.Operand, diagnostics.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access, diagnostics.The_operand_of_an_increment_or_decrement_operator_may_not_be_an_optional_property_access) - } - return c.getUnaryResultType(operandType) - } - return c.errorType -} - -func (c *Checker) checkPostfixUnaryExpression(node *ast.Node) *Type { - expr := node.AsPostfixUnaryExpression() - operandType := c.checkExpression(expr.Operand) - if operandType == c.silentNeverType { - return c.silentNeverType - } - ok := c.checkArithmeticOperandType(expr.Operand, c.checkNonNullType(operandType, expr.Operand), diagnostics.An_arithmetic_operand_must_be_of_type_any_number_bigint_or_an_enum_type, false) - if ok { - // run check only if former checks succeeded to avoid reporting cascading errors - c.checkReferenceExpression(expr.Operand, diagnostics.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access, diagnostics.The_operand_of_an_increment_or_decrement_operator_may_not_be_an_optional_property_access) - } - return c.getUnaryResultType(operandType) -} - -func (c *Checker) getUnaryResultType(operandType *Type) *Type { - if c.maybeTypeOfKind(operandType, TypeFlagsBigIntLike) { - if c.isTypeAssignableToKind(operandType, TypeFlagsAnyOrUnknown) || c.maybeTypeOfKind(operandType, TypeFlagsNumberLike) { - return c.numberOrBigIntType - } - return c.bigintType - } - // If it's not a bigint type, implicit coercion will result in a number - return c.numberType -} - -func (c *Checker) checkConditionalExpression(node *ast.Node, checkMode CheckMode) *Type { - cond := node.AsConditionalExpression() - t := c.checkTruthinessExpression(cond.Condition, checkMode) - c.checkTestingKnownTruthyCallableOrAwaitableOrEnumMemberType(cond.Condition, t, cond.WhenTrue) - type1 := c.checkExpressionEx(cond.WhenTrue, checkMode) - type2 := c.checkExpressionEx(cond.WhenFalse, checkMode) - return c.getUnionTypeEx([]*Type{type1, type2}, UnionReductionSubtype, nil, nil) -} - -func (c *Checker) checkTruthinessExpression(node *ast.Node, checkMode CheckMode) *Type { - return c.checkTruthinessOfType(c.checkExpressionEx(node, checkMode), node) -} - -func (c *Checker) checkSpreadExpression(node *ast.Node, checkMode CheckMode) *Type { - arrayOrIterableType := c.checkExpressionEx(node.Expression(), checkMode) - return c.checkIteratedTypeOrElementType(IterationUseSpread, arrayOrIterableType, c.undefinedType, node.Expression()) -} - -func (c *Checker) checkYieldExpression(node *ast.Node) *Type { - c.checkGrammarYieldExpression(node) - fn := ast.GetContainingFunction(node) - if fn == nil { - return c.anyType - } - functionFlags := getFunctionFlags(fn) - if functionFlags&FunctionFlagsGenerator == 0 { - // If the user's code is syntactically correct, the func should always have a star. After all, we are in a yield context. - return c.anyType - } - isAsync := (functionFlags & FunctionFlagsAsync) != 0 - // There is no point in doing an assignability check if the function - // has no explicit return type because the return type is directly computed - // from the yield expressions. - returnType := c.getReturnTypeFromAnnotation(fn) - if returnType != nil && returnType.flags&TypeFlagsUnion != 0 { - returnType = c.filterType(returnType, func(t *Type) bool { - return c.checkGeneratorInstantiationAssignabilityToReturnType(t, functionFlags, nil /*errorNode*/) - }) - } - var iterationTypes IterationTypes - if returnType != nil { - iterationTypes = c.getIterationTypesOfGeneratorFunctionReturnType(returnType, isAsync) - } - signatureYieldType := core.OrElse(iterationTypes.yieldType, c.anyType) - signatureNextType := core.OrElse(iterationTypes.nextType, c.anyType) - var yieldExpressionType *Type - if node.Expression() != nil { - yieldExpressionType = c.checkExpression(node.Expression()) - } else { - yieldExpressionType = c.undefinedWideningType - } - yieldedType := c.getYieldedTypeOfYieldExpression(node, yieldExpressionType, signatureNextType, isAsync) - if returnType != nil && yieldedType != nil { - c.checkTypeAssignableToAndOptionallyElaborate(yieldedType, signatureYieldType, core.OrElse(node.Expression(), node), node.Expression(), nil, nil) - } - if node.AsYieldExpression().AsteriskToken != nil { - use := core.IfElse(isAsync, IterationUseAsyncYieldStar, IterationUseYieldStar) - return core.OrElse(c.getIterationTypeOfIterable(use, IterationTypeKindReturn, yieldExpressionType, node.Expression()), c.anyType) - } - if returnType != nil { - return core.OrElse(c.getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKindNext, returnType, isAsync), c.anyType) - } - t := c.getContextualIterationType(IterationTypeKindNext, fn) - if t == nil { - t = c.anyType - if c.noImplicitAny && !expressionResultIsUnused(node) { - contextualType := c.getContextualType(node, ContextFlagsNone) - if contextualType == nil || IsTypeAny(contextualType) { - c.error(node, diagnostics.X_yield_expression_implicitly_results_in_an_any_type_because_its_containing_generator_lacks_a_return_type_annotation) - } - } - } - return t -} - -func (c *Checker) getYieldedTypeOfYieldExpression(node *ast.Node, expressionType *Type, sentType *Type, isAsync bool) *Type { - errorNode := core.OrElse(node.Expression(), node) - isYieldStar := node.AsYieldExpression().AsteriskToken != nil - // A `yield*` expression effectively yields everything that its operand yields - yieldedType := expressionType - if isYieldStar { - yieldedType = c.checkIteratedTypeOrElementType(core.IfElse(isAsync, IterationUseAsyncYieldStar, IterationUseYieldStar), expressionType, sentType, errorNode) - } - if !isAsync { - return yieldedType - } - return c.getAwaitedTypeEx(yieldedType, errorNode, core.IfElse(isYieldStar, - diagnostics.Type_of_iterated_elements_of_a_yield_Asterisk_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member, - diagnostics.Type_of_yield_operand_in_an_async_generator_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member)) -} - -func (c *Checker) checkSyntheticExpression(node *ast.Node) *Type { - t := node.AsSyntheticExpression().Type.(*Type) - if node.AsSyntheticExpression().IsSpread { - return c.getIndexedAccessType(t, c.numberType) - } - return t -} - -func (c *Checker) checkIdentifier(node *ast.Node, checkMode CheckMode) *Type { - if ast.IsThisInTypeQuery(node) { - return c.checkThisExpression(node) - } - symbol := c.getResolvedSymbol(node) - if symbol == c.unknownSymbol { - return c.errorType - } - if symbol == c.argumentsSymbol { - if c.isInPropertyInitializerOrClassStaticBlock(node, true /*ignoreArrowFunctions*/) { - c.error(node, diagnostics.X_arguments_cannot_be_referenced_in_property_initializers_or_class_static_initialization_blocks) - return c.errorType - } - return c.getTypeOfSymbol(symbol) - } - if shouldMarkIdentifierAliasReferenced(node) { - c.markLinkedReferences(node, ReferenceHintIdentifier, nil /*propSymbol*/, nil /*parentType*/) - } - localOrExportSymbol := c.getExportSymbolOfValueSymbolIfExported(symbol) - targetSymbol := c.resolveAliasWithDeprecationCheck(localOrExportSymbol, node) - if len(targetSymbol.Declarations) != 0 && c.isDeprecatedSymbol(targetSymbol) && c.isUncalledFunctionReference(node, targetSymbol) { - c.addDeprecatedSuggestion(node, targetSymbol.Declarations, node.Text()) - } - 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 == ast.KindBindingElement && slices.Contains(c.contextualBindingPatterns, declaration.Parent) && - ast.FindAncestor(node, func(parent *ast.Node) bool { return parent == declaration.Parent }) != nil { - return c.nonInferrableAnyType - } - t := c.getNarrowedTypeOfSymbol(localOrExportSymbol, node) - assignmentKind := getAssignmentTargetKind(node) - if assignmentKind != AssignmentKindNone { - if localOrExportSymbol.Flags&ast.SymbolFlagsVariable == 0 { - var assignmentError *diagnostics.Message - switch { - case localOrExportSymbol.Flags&ast.SymbolFlagsEnum != 0: - assignmentError = diagnostics.Cannot_assign_to_0_because_it_is_an_enum - case localOrExportSymbol.Flags&ast.SymbolFlagsClass != 0: - assignmentError = diagnostics.Cannot_assign_to_0_because_it_is_a_class - case localOrExportSymbol.Flags&ast.SymbolFlagsModule != 0: - assignmentError = diagnostics.Cannot_assign_to_0_because_it_is_a_namespace - case localOrExportSymbol.Flags&ast.SymbolFlagsFunction != 0: - assignmentError = diagnostics.Cannot_assign_to_0_because_it_is_a_function - case localOrExportSymbol.Flags&ast.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&ast.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&ast.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&ast.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 - } - t = c.getNarrowableTypeForReference(t, node, checkMode) - // The declaration container is the innermost function that encloses the declaration of the variable - // or parameter. The flow container is the innermost function starting with which we analyze the control - // flow graph to determine the control flow based type. - isParameter := ast.GetRootDeclaration(declaration).Kind == ast.KindParameter - declarationContainer := c.getControlFlowContainer(declaration) - flowContainer := c.getControlFlowContainer(node) - isOuterVariable := flowContainer != declarationContainer - isSpreadDestructuringAssignmentTarget := node.Parent != nil && node.Parent.Parent != nil && ast.IsSpreadAssignment(node.Parent) && c.isDestructuringAssignmentTarget(node.Parent.Parent) - isModuleExports := symbol.Flags&ast.SymbolFlagsModuleExports != 0 - typeIsAutomatic := t == c.autoType || t == c.autoArrayType - isAutomaticTypeInNonNull := typeIsAutomatic && node.Parent.Kind == ast.KindNonNullExpression - // When the control flow originates in a function expression, arrow function, method, or accessor, and - // we are referencing a closed-over const variable or parameter or mutable local variable past its last - // assignment, we extend the origin of the control flow analysis to include the immediately enclosing - // control flow container. - for flowContainer != declarationContainer && - (ast.IsFunctionExpressionOrArrowFunction(flowContainer) || ast.IsObjectLiteralOrClassExpressionMethodOrAccessor(flowContainer)) && - (c.isConstantVariable(localOrExportSymbol) && t != c.autoArrayType || c.isParameterOrMutableLocalVariable(localOrExportSymbol) && c.isPastLastAssignment(localOrExportSymbol, node)) { - flowContainer = c.getControlFlowContainer(flowContainer) - } - // We only look for uninitialized variables in strict null checking mode, and only when we can analyze - // the entire control flow graph from the variable's declaration (i.e. when the flow container and - // declaration container are the same). - isNeverInitialized := immediateDeclaration != nil && ast.IsVariableDeclaration(immediateDeclaration) && immediateDeclaration.Initializer() == nil && - immediateDeclaration.AsVariableDeclaration().ExclamationToken == nil && c.isMutableLocalVariableDeclaration(immediateDeclaration) && - !c.isSymbolAssignedDefinitely(symbol) - assumeInitialized := isParameter || - isAlias || - (isOuterVariable && !isNeverInitialized) || - isSpreadDestructuringAssignmentTarget || - isModuleExports || - c.isSameScopedBindingElement(node, declaration) || - t != c.autoType && t != c.autoArrayType && (!c.strictNullChecks || t.flags&(TypeFlagsAnyOrUnknown|TypeFlagsVoid) != 0 || IsInTypeQuery(node) || c.isInAmbientOrTypeNode(node) || node.Parent.Kind == ast.KindExportSpecifier) || - ast.IsNonNullExpression(node.Parent) || - ast.IsVariableDeclaration(declaration) && declaration.AsVariableDeclaration().ExclamationToken != nil || - declaration.Flags&ast.NodeFlagsAmbient != 0 - var initialType *Type - switch { - case isAutomaticTypeInNonNull: - initialType = c.undefinedType - case assumeInitialized && isParameter: - initialType = c.removeOptionalityFromDeclaredType(t, declaration) - case assumeInitialized: - initialType = t - case typeIsAutomatic: - initialType = c.undefinedType - default: - initialType = c.getOptionalType(t, false /*isProperty*/) - } - var flowType *Type - if isAutomaticTypeInNonNull { - flowType = c.GetNonNullableType(c.getFlowTypeOfReferenceEx(node, t, initialType, flowContainer, nil)) - } else { - flowType = c.getFlowTypeOfReferenceEx(node, t, initialType, flowContainer, nil) - } - // A variable is considered uninitialized when it is possible to analyze the entire control flow graph - // from declaration to use, and when the variable's declared type doesn't include undefined but the - // control flow based type does include undefined. - if !c.isEvolvingArrayOperationTarget(node) && (t == c.autoType || t == c.autoArrayType) { - if flowType == c.autoType || flowType == c.autoArrayType { - if c.noImplicitAny { - c.error(ast.GetNameOfDeclaration(declaration), diagnostics.Variable_0_implicitly_has_type_1_in_some_locations_where_its_type_cannot_be_determined, c.symbolToString(symbol), c.TypeToString(flowType)) - c.error(node, diagnostics.Variable_0_implicitly_has_an_1_type, c.symbolToString(symbol), c.TypeToString(flowType)) - } - return c.convertAutoToAny(flowType) - } - } else if !assumeInitialized && !c.containsUndefinedType(t) && c.containsUndefinedType(flowType) { - c.error(node, diagnostics.Variable_0_is_used_before_being_assigned, c.symbolToString(symbol)) - // Return the declared type to reduce follow-on errors - return t - } - if assignmentKind != AssignmentKindNone { - // Identifier is target of a compound assignment - return c.getBaseTypeOfLiteralType(flowType) - } - return flowType -} - -func (c *Checker) isSameScopedBindingElement(node *ast.Node, declaration *ast.Node) bool { - if ast.IsBindingElement(declaration) { - bindingElement := ast.FindAncestor(node, ast.IsBindingElement) - return bindingElement != nil && ast.GetRootDeclaration(bindingElement) == ast.GetRootDeclaration(declaration) - } - return false -} - -// Remove undefined from the annotated type of a parameter when there is an initializer (that doesn't include undefined) -func (c *Checker) removeOptionalityFromDeclaredType(declaredType *Type, declaration *ast.Node) *Type { - removeUndefined := c.strictNullChecks && ast.IsParameter(declaration) && declaration.Initializer() != nil && c.hasTypeFacts(declaredType, TypeFactsIsUndefined) && !c.parameterInitializerContainsUndefined(declaration) - if removeUndefined { - return c.getTypeWithFacts(declaredType, TypeFactsNEUndefined) - } - return declaredType -} - -func (c *Checker) parameterInitializerContainsUndefined(declaration *ast.Node) bool { - links := c.nodeLinks.Get(declaration) - if links.flags&NodeCheckFlagsInitializerIsUndefinedComputed == 0 { - if !c.pushTypeResolution(declaration, TypeSystemPropertyNameInitializerIsUndefined) { - c.reportCircularityError(declaration.Symbol()) - return true - } - containsUndefined := c.hasTypeFacts(c.checkDeclarationInitializer(declaration, CheckModeNormal, nil), TypeFactsIsUndefined) - if !c.popTypeResolution() { - c.reportCircularityError(declaration.Symbol()) - return true - } - if links.flags&NodeCheckFlagsInitializerIsUndefinedComputed == 0 { - links.flags |= NodeCheckFlagsInitializerIsUndefinedComputed | core.IfElse(containsUndefined, NodeCheckFlagsInitializerIsUndefined, 0) - } - } - return links.flags&NodeCheckFlagsInitializerIsUndefined != 0 -} - -func (c *Checker) isInAmbientOrTypeNode(node *ast.Node) bool { - return node.Flags&ast.NodeFlagsAmbient != 0 || ast.FindAncestor(node, func(n *ast.Node) bool { - return ast.IsInterfaceDeclaration(n) || ast.IsTypeAliasDeclaration(n) || ast.IsJSTypeAliasDeclaration(n) || ast.IsTypeLiteralNode(n) - }) != nil -} - -func (c *Checker) checkPropertyAccessExpression(node *ast.Node, checkMode CheckMode, writeOnly bool) *Type { - if node.Flags&ast.NodeFlagsOptionalChain != 0 { - return c.checkPropertyAccessChain(node, checkMode) - } - expr := node.Expression() - return c.checkPropertyAccessExpressionOrQualifiedName(node, expr, c.checkNonNullExpression(expr), node.AsPropertyAccessExpression().Name(), checkMode, writeOnly) -} - -func (c *Checker) checkPropertyAccessChain(node *ast.Node, checkMode CheckMode) *Type { - leftType := c.checkExpression(node.Expression()) - nonOptionalType := c.getOptionalExpressionType(leftType, node.Expression()) - return c.propagateOptionalTypeMarker(c.checkPropertyAccessExpressionOrQualifiedName(node, node.Expression(), c.checkNonNullType(nonOptionalType, node.Expression()), node.Name(), checkMode, false), node, nonOptionalType != leftType) -} - -func (c *Checker) checkPropertyAccessExpressionOrQualifiedName(node *ast.Node, left *ast.Node, leftType *Type, right *ast.Node, checkMode CheckMode, writeOnly bool) *Type { - parentSymbol := c.getResolvedSymbolOrNil(left) - assignmentKind := getAssignmentTargetKind(node) - widenedType := leftType - if assignmentKind != AssignmentKindNone || c.isMethodAccessForCall(node) { - widenedType = c.getWidenedType(leftType) - } - apparentType := c.getApparentType(widenedType) - isAnyLike := IsTypeAny(apparentType) || apparentType == c.silentNeverType - var prop *ast.Symbol - if ast.IsPrivateIdentifier(right) { - lexicallyScopedSymbol := c.lookupSymbolForPrivateIdentifierDeclaration(right.Text(), right) - if assignmentKind != AssignmentKindNone && lexicallyScopedSymbol != nil && lexicallyScopedSymbol.ValueDeclaration != nil && ast.IsMethodDeclaration(lexicallyScopedSymbol.ValueDeclaration) { - c.grammarErrorOnNode(right, diagnostics.Cannot_assign_to_private_method_0_Private_methods_are_not_writable, right.Text()) - } - if isAnyLike { - if lexicallyScopedSymbol != nil { - if c.isErrorType(apparentType) { - return c.errorType - } - return apparentType - } - if getContainingClassExcludingClassDecorators(right) == nil { - c.grammarErrorOnNode(right, diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies) - return c.anyType - } - } - if lexicallyScopedSymbol != nil { - prop = c.getPrivateIdentifierPropertyOfType(leftType, lexicallyScopedSymbol) - } - if prop == nil { - // Check for private-identifier-specific shadowing and lexical-scoping errors. - if c.checkPrivateIdentifierPropertyAccess(leftType, right, lexicallyScopedSymbol) { - return c.errorType - } - // !!! - // containingClass := getContainingClassExcludingClassDecorators(right) - // if containingClass && isPlainJSFile(ast.GetSourceFileOfNode(containingClass), c.compilerOptions.checkJs) { - // c.grammarErrorOnNode(right, diagnostics.Private_field_0_must_be_declared_in_an_enclosing_class, right.Text()) - // } - } else { - isSetonlyAccessor := prop.Flags&ast.SymbolFlagsSetAccessor != 0 && prop.Flags&ast.SymbolFlagsGetAccessor == 0 - if isSetonlyAccessor && assignmentKind != AssignmentKindDefinite { - c.error(node, diagnostics.Private_accessor_was_defined_without_a_getter) - } - } - } else { - if isAnyLike { - if ast.IsIdentifier(left) && parentSymbol != nil { - c.markLinkedReferences(node, ReferenceHintProperty, nil /*propSymbol*/, leftType) - } - if c.isErrorType(apparentType) { - return c.errorType - } - return apparentType - } - prop = c.getPropertyOfTypeEx(apparentType, right.Text(), isConstEnumObjectType(apparentType) /*skipObjectFunctionPropertyAugment*/, node.Kind == ast.KindQualifiedName /*includeTypeOnlyMembers*/) - } - c.markLinkedReferences(node, ReferenceHintProperty, prop, leftType) - var propType *Type - if prop == nil { - var indexInfo *IndexInfo - if !ast.IsPrivateIdentifier(right) && (assignmentKind == AssignmentKindNone || !c.isGenericObjectType(leftType) || isThisTypeParameter(leftType)) { - indexInfo = c.getApplicableIndexInfoForName(apparentType, right.Text()) - } - if indexInfo == nil { - if leftType.symbol == c.globalThisSymbol { - globalSymbol := c.globalThisSymbol.Exports[right.Text()] - if globalSymbol != nil && globalSymbol.Flags&ast.SymbolFlagsBlockScoped != 0 { - c.error(right, diagnostics.Property_0_does_not_exist_on_type_1, right.Text(), c.TypeToString(leftType)) - } else if c.noImplicitAny { - c.error(right, diagnostics.Element_implicitly_has_an_any_type_because_type_0_has_no_index_signature, c.TypeToString(leftType)) - } - return c.anyType - } - if right.Text() != "" && !c.checkAndReportErrorForExtendingInterface(node) { - c.reportNonexistentProperty(right, core.IfElse(isThisTypeParameter(leftType), apparentType, leftType)) - } - return c.errorType - } - if indexInfo.isReadonly && (ast.IsAssignmentTarget(node) || isDeleteTarget(node)) { - c.error(node, diagnostics.Index_signature_in_type_0_only_permits_reading, c.TypeToString(apparentType)) - } - propType = indexInfo.valueType - if c.compilerOptions.NoUncheckedIndexedAccess == core.TSTrue && getAssignmentTargetKind(node) != AssignmentKindDefinite { - propType = c.getUnionType([]*Type{propType, c.missingType}) - } - if c.compilerOptions.NoPropertyAccessFromIndexSignature == core.TSTrue && ast.IsPropertyAccessExpression(node) { - c.error(right, diagnostics.Property_0_comes_from_an_index_signature_so_it_must_be_accessed_with_0, right.Text()) - } - if indexInfo.declaration != nil && c.IsDeprecatedDeclaration(indexInfo.declaration) { - c.addDeprecatedSuggestion(right, []*ast.Node{indexInfo.declaration}, right.Text()) - } - } else { - targetPropSymbol := c.resolveAliasWithDeprecationCheck(prop, right) - if c.isDeprecatedSymbol(targetPropSymbol) && c.isUncalledFunctionReference(node, targetPropSymbol) && targetPropSymbol.Declarations != nil { - c.addDeprecatedSuggestion(right, targetPropSymbol.Declarations, right.Text()) - } - c.checkPropertyNotUsedBeforeDeclaration(prop, node, right) - c.markPropertyAsReferenced(prop, node, c.isSelfTypeAccess(left, parentSymbol)) - c.symbolNodeLinks.Get(node).resolvedSymbol = prop - c.checkPropertyAccessibility(node, left.Kind == ast.KindSuperKeyword, ast.IsWriteAccess(node), apparentType, prop) - if c.isAssignmentToReadonlyEntity(node, prop, assignmentKind) { - c.error(right, diagnostics.Cannot_assign_to_0_because_it_is_a_read_only_property, right.Text()) - return c.errorType - } - switch { - case c.isThisPropertyAccessInConstructor(node, prop): - propType = c.autoType - case writeOnly || ast.IsWriteOnlyAccess(node): - propType = c.getWriteTypeOfSymbol(prop) - default: - propType = c.getTypeOfSymbol(prop) - } - } - return c.getFlowTypeOfAccessExpression(node, prop, propType, right, checkMode) -} - -func (c *Checker) getFlowTypeOfAccessExpression(node *ast.Node, prop *ast.Symbol, propType *Type, errorNode *ast.Node, checkMode CheckMode) *Type { - // Only compute control flow type if this is a property access expression that isn't an - // assignment target, and the referenced property was declared as a variable, property, - // accessor, or optional method. - assignmentKind := getAssignmentTargetKind(node) - if assignmentKind == AssignmentKindDefinite { - return c.removeMissingType(propType, prop != nil && prop.Flags&ast.SymbolFlagsOptional != 0) - } - if prop != nil && prop.Flags&(ast.SymbolFlagsVariable|ast.SymbolFlagsProperty|ast.SymbolFlagsAccessor) == 0 && !(prop.Flags&ast.SymbolFlagsMethod != 0 && propType.flags&TypeFlagsUnion != 0) { - return propType - } - if propType == c.autoType { - return c.getFlowTypeOfProperty(node, prop) - } - propType = c.getNarrowableTypeForReference(propType, node, checkMode) - // If strict null checks and strict property initialization checks are enabled, if we have - // a this.xxx property access, if the property is an instance property without an initializer, - // and if we are in a constructor of the same class as the property declaration, assume that - // the property is uninitialized at the top of the control flow. - assumeUninitialized := false - initialType := propType - if c.strictNullChecks && prop != nil { - declaration := prop.ValueDeclaration - if declaration != nil && c.strictPropertyInitialization && ast.IsAccessExpression(node) && node.Expression().Kind == ast.KindThisKeyword && c.isPropertyWithoutInitializer(declaration) && !ast.IsStatic(declaration) { - flowContainer := c.getControlFlowContainer(node) - if ast.IsConstructorDeclaration(flowContainer) && flowContainer.Parent == declaration.Parent && declaration.Flags&ast.NodeFlagsAmbient == 0 { - assumeUninitialized = true - initialType = c.getOptionalType(propType, false /*isProperty*/) - } - } - } - flowType := c.getFlowTypeOfReferenceEx(node, propType, initialType, nil, nil) - if assumeUninitialized && !c.containsUndefinedType(propType) && c.containsUndefinedType(flowType) { - c.error(errorNode, diagnostics.Property_0_is_used_before_being_assigned, c.symbolToString(prop)) - // Return the declared type to reduce follow-on errors - return propType - } - if assignmentKind != AssignmentKindNone { - return c.getBaseTypeOfLiteralType(flowType) - } - return flowType -} - -func (c *Checker) getControlFlowContainer(node *ast.Node) *ast.Node { - return ast.FindAncestor(node.Parent, func(node *ast.Node) bool { - return ast.IsFunctionLike(node) && ast.GetImmediatelyInvokedFunctionExpression(node) == nil || ast.IsModuleBlock(node) || ast.IsSourceFile(node) || ast.IsPropertyDeclaration(node) - }) -} - -func (c *Checker) getFlowTypeOfProperty(reference *ast.Node, prop *ast.Symbol) *Type { - initialType := c.undefinedType - if prop != nil && prop.ValueDeclaration != nil && (!c.isAutoTypedProperty(prop) || prop.ValueDeclaration.ModifierFlags()&ast.ModifierFlagsAmbient != 0) { - if baseType := c.getTypeOfPropertyInBaseClass(prop); baseType != nil { - initialType = baseType - } - } - return c.getFlowTypeOfReferenceEx(reference, c.autoType, initialType, nil, nil) -} - -// Return the inherited type of the given property or undefined if property doesn't exist in a base class. -func (c *Checker) getTypeOfPropertyInBaseClass(property *ast.Symbol) *Type { - classType := c.getDeclaringClass(property) - if classType != nil { - baseClassTypes := c.getBaseTypes(classType) - if len(baseClassTypes) > 0 { - return c.getTypeOfPropertyOfType(baseClassTypes[0], property.Name) - } - } - return nil -} - -func (c *Checker) isMethodAccessForCall(node *ast.Node) bool { - for ast.IsParenthesizedExpression(node.Parent) { - node = node.Parent - } - return ast.IsCallOrNewExpression(node.Parent) && node.Parent.Expression() == node -} - -// Lookup the private identifier lexically. -func (c *Checker) lookupSymbolForPrivateIdentifierDeclaration(propName string, location *ast.Node) *ast.Symbol { - for containingClass := getContainingClassExcludingClassDecorators(location); containingClass != nil; containingClass = ast.GetContainingClass(containingClass) { - symbol := containingClass.Symbol() - name := binder.GetSymbolNameForPrivateIdentifier(symbol, propName) - prop := symbol.Members[name] - if prop != nil { - return prop - } - prop = symbol.Exports[name] - if prop != nil { - return prop - } - } - return nil -} - -func (c *Checker) getPrivateIdentifierPropertyOfType(leftType *Type, lexicallyScopedIdentifier *ast.Symbol) *ast.Symbol { - return c.getPropertyOfType(leftType, lexicallyScopedIdentifier.Name) -} - -func (c *Checker) checkPrivateIdentifierPropertyAccess(leftType *Type, right *ast.Node, lexicallyScopedIdentifier *ast.Symbol) bool { - // Either the identifier could not be looked up in the lexical scope OR the lexically scoped identifier did not exist on the type. - // Find a private identifier with the same description on the type. - properties := c.getPropertiesOfType(leftType) - var propertyOnType *ast.Symbol - for _, symbol := range properties { - decl := symbol.ValueDeclaration - if decl != nil && decl.Name() != nil && ast.IsPrivateIdentifier(decl.Name()) && decl.Name().Text() == right.Text() { - propertyOnType = symbol - break - } - } - diagName := scanner.DeclarationNameToString(right) - if propertyOnType != nil { - typeValueDecl := propertyOnType.ValueDeclaration - typeClass := ast.GetContainingClass(typeValueDecl) - // We found a private identifier property with the same description. - // Either: - // - There is a lexically scoped private identifier AND it shadows the one we found on the type. - // - It is an attempt to access the private identifier outside of the class. - if lexicallyScopedIdentifier != nil && lexicallyScopedIdentifier.ValueDeclaration != nil { - lexicalValueDecl := lexicallyScopedIdentifier.ValueDeclaration - lexicalClass := ast.GetContainingClass(lexicalValueDecl) - if ast.FindAncestor(lexicalClass, func(n *ast.Node) bool { return typeClass == n }) != nil { - diagnostic := c.error(right, diagnostics.The_property_0_cannot_be_accessed_on_type_1_within_this_class_because_it_is_shadowed_by_another_private_identifier_with_the_same_spelling, diagName, c.TypeToString(leftType)) - diagnostic.AddRelatedInfo(createDiagnosticForNode(lexicalValueDecl, diagnostics.The_shadowing_declaration_of_0_is_defined_here, diagName)) - diagnostic.AddRelatedInfo(createDiagnosticForNode(typeValueDecl, diagnostics.The_declaration_of_0_that_you_probably_intended_to_use_is_defined_here, diagName)) - return true - } - } - c.error(right, diagnostics.Property_0_is_not_accessible_outside_class_1_because_it_has_a_private_identifier, diagName, scanner.DeclarationNameToString(typeClass.Name())) - return true - } - return false -} - -func (c *Checker) reportNonexistentProperty(propNode *ast.Node, containingType *Type) { - var diagnostic *ast.Diagnostic - if !ast.IsPrivateIdentifier(propNode) && containingType.flags&TypeFlagsUnion != 0 && containingType.flags&TypeFlagsPrimitive == 0 { - for _, subtype := range containingType.Types() { - if c.getPropertyOfType(subtype, propNode.Text()) == nil && c.getApplicableIndexInfoForName(subtype, propNode.Text()) == nil { - diagnostic = NewDiagnosticChainForNode(diagnostic, propNode, diagnostics.Property_0_does_not_exist_on_type_1, scanner.DeclarationNameToString(propNode), c.TypeToString(subtype)) - break - } - } - } - if c.typeHasStaticProperty(propNode.Text(), containingType) { - propName := scanner.DeclarationNameToString(propNode) - typeName := c.TypeToString(containingType) - diagnostic = NewDiagnosticChainForNode(diagnostic, propNode, diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_to_access_the_static_member_2_instead, propName, typeName, typeName+"."+propName) - } else { - promisedType := c.GetPromisedTypeOfPromise(containingType) - if promisedType != nil && c.getPropertyOfType(promisedType, propNode.Text()) != nil { - diagnostic = NewDiagnosticChainForNode(diagnostic, propNode, diagnostics.Property_0_does_not_exist_on_type_1, scanner.DeclarationNameToString(propNode), c.TypeToString(containingType)) - diagnostic.AddRelatedInfo(NewDiagnosticForNode(propNode, diagnostics.Did_you_forget_to_use_await)) - } else { - missingProperty := scanner.DeclarationNameToString(propNode) - container := c.TypeToString(containingType) - libSuggestion := c.getSuggestedLibForNonExistentProperty(missingProperty, containingType) - if libSuggestion != "" { - diagnostic = NewDiagnosticChainForNode(diagnostic, propNode, diagnostics.Property_0_does_not_exist_on_type_1_Do_you_need_to_change_your_target_library_Try_changing_the_lib_compiler_option_to_2_or_later, missingProperty, container, libSuggestion) - } else { - suggestion := c.getSuggestedSymbolForNonexistentProperty(propNode, containingType) - if suggestion != nil { - suggestedName := ast.SymbolName(suggestion) - diagnostic = NewDiagnosticChainForNode(diagnostic, propNode, diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, missingProperty, container, suggestedName) - if suggestion.ValueDeclaration != nil { - diagnostic.AddRelatedInfo(NewDiagnosticForNode(suggestion.ValueDeclaration, diagnostics.X_0_is_declared_here, suggestedName)) - } - } else { - diagnostic = c.elaborateNeverIntersection(diagnostic, propNode, containingType) - var message *diagnostics.Message - if c.containerSeemsToBeEmptyDomElement(containingType) { - message = diagnostics.Property_0_does_not_exist_on_type_1_Try_changing_the_lib_compiler_option_to_include_dom - } else { - message = diagnostics.Property_0_does_not_exist_on_type_1 - } - diagnostic = NewDiagnosticChainForNode(diagnostic, propNode, message, missingProperty, container) - } - } - } - } - c.diagnostics.Add(diagnostic) -} - -func (c *Checker) getSuggestedLibForNonExistentProperty(missingProperty string, containingType *Type) string { - container := c.getApparentType(containingType).symbol - if container != nil { - featureMap := getFeatureMap() - if typeFeatures, ok := featureMap[container.Name]; ok { - for _, entry := range typeFeatures { - if slices.Contains(entry.props, missingProperty) { - return entry.lib - } - } - } - } - return "" -} - -func (c *Checker) getSuggestedSymbolForNonexistentProperty(name *ast.Node, containingType *Type) *ast.Symbol { - props := c.getPropertiesOfType(containingType) - parent := name.Parent - if ast.IsPropertyAccessExpression(parent) { - props = core.Filter(props, func(prop *ast.Symbol) bool { - return c.isValidPropertyAccessForCompletions(parent, containingType, prop) - }) - } - return c.getSpellingSuggestionForName(name.Text(), props, ast.SymbolFlagsValue) -} - -// Checks if an existing property access is valid for completions purposes. -// @param node a property access-like node where we want to check if we can access a property. -// This node does not need to be an access of the property we are checking. -// e.g. in completions, this node will often be an incomplete property access node, as in `foo.`. -// Besides providing a location (i.e. scope) used to check property accessibility, we use this node for -// computing whether this is a `super` property access. -// @param type the type whose property we are checking. -// @param property the accessed property's symbol. -func (c *Checker) isValidPropertyAccessForCompletions(node *ast.Node, t *Type, property *ast.Symbol) bool { - return c.isPropertyAccessible(node, ast.IsPropertyAccessExpression(node) && node.Expression().Kind == ast.KindSuperKeyword, false /*isWrite*/, t, property) - // Previously we validated the 'this' type of methods but this adversely affected performance. See #31377 for more context. -} - -// Checks if a property can be accessed in a location. -// The location is given by the `node` parameter. -// The node does not need to be a property access. -// @param node location where to check property accessibility -// @param isSuper whether to consider this a `super` property access, e.g. `super.foo`. -// @param isWrite whether this is a write access, e.g. `++foo.x`. -// @param containingType type where the property comes from. -// @param property property symbol. -func (c *Checker) isPropertyAccessible(node *ast.Node, isSuper bool, isWrite bool, containingType *Type, property *ast.Symbol) bool { - // Short-circuiting for improved performance. - if IsTypeAny(containingType) { - return true - } - // A #private property access in an optional chain is an error dealt with by the parser. - // The checker does not check for it, so we need to do our own check here. - if property.ValueDeclaration != nil && ast.IsPrivateIdentifierClassElementDeclaration(property.ValueDeclaration) { - declClass := ast.GetContainingClass(property.ValueDeclaration) - return !ast.IsOptionalChain(node) && ast.IsNodeDescendantOf(node, declClass) - } - return c.checkPropertyAccessibilityAtLocation(node, isSuper, isWrite, containingType, property, nil) -} - -func (c *Checker) containerSeemsToBeEmptyDomElement(containingType *Type) bool { - return !slices.Contains(c.compilerOptions.Lib, "lib.dom.d.ts") && everyContainedType(containingType, hasCommonDomTypeName) && c.isEmptyObjectType(containingType) -} - -func hasCommonDomTypeName(t *Type) bool { - if t.symbol == nil { - return false - } - name := t.symbol.Name - return name == "EventTarget" || name == "Node" || name == "Element" || strings.HasPrefix(name, "HTML") && strings.HasSuffix(name, "Element") -} - -func (c *Checker) checkAndReportErrorForExtendingInterface(errorLocation *ast.Node) bool { - expression := c.getEntityNameForExtendingInterface(errorLocation) - if expression != nil && c.resolveEntityName(expression, ast.SymbolFlagsInterface, true /*ignoreErrors*/, false, nil) != nil { - c.error(errorLocation, diagnostics.Cannot_extend_an_interface_0_Did_you_mean_implements, scanner.GetTextOfNode(expression)) - return true - } - return false -} - -/** - * Climbs up parents to an ExpressionWithTypeArguments, and returns its expression, - * but returns undefined if that expression is not an EntityNameExpression. - */ -func (c *Checker) getEntityNameForExtendingInterface(node *ast.Node) *ast.Node { - switch node.Kind { - case ast.KindIdentifier, ast.KindPropertyAccessExpression: - if node.Parent != nil { - return c.getEntityNameForExtendingInterface(node.Parent) - } - case ast.KindExpressionWithTypeArguments: - if ast.IsEntityNameExpression(node.Expression()) { - return node.Expression() - } - } - return nil -} - -func (c *Checker) isUncalledFunctionReference(node *ast.Node, symbol *ast.Symbol) bool { - if symbol.Flags&(ast.SymbolFlagsFunction|ast.SymbolFlagsMethod) != 0 { - parent := ast.FindAncestor(node.Parent, func(n *ast.Node) bool { return !ast.IsAccessExpression(n) }) - if parent == nil { - parent = node.Parent - } - if ast.IsCallLikeExpression(parent) { - return ast.IsCallOrNewExpression(parent) && ast.IsIdentifier(node) && c.hasMatchingArgument(parent, node) - } - return core.Every(symbol.Declarations, func(d *ast.Node) bool { - return !ast.IsFunctionLike(d) || c.IsDeprecatedDeclaration(d) - }) - } - return true -} - -func (c *Checker) checkPropertyNotUsedBeforeDeclaration(prop *ast.Symbol, node *ast.Node, right *ast.Node) { - valueDeclaration := prop.ValueDeclaration - if valueDeclaration == nil || ast.GetSourceFileOfNode(node).IsDeclarationFile { - return - } - var diagnostic *ast.Diagnostic - declarationName := right.Text() - if c.isInPropertyInitializerOrClassStaticBlock(node, false /*ignoreArrowFunctions*/) && - !c.isOptionalPropertyDeclaration(valueDeclaration) && - !(ast.IsAccessExpression(node) && ast.IsAccessExpression(node.Expression())) && - !c.isBlockScopedNameDeclaredBeforeUse(valueDeclaration, right) && - !(ast.IsMethodDeclaration(valueDeclaration) && c.getCombinedModifierFlagsCached(valueDeclaration)&ast.ModifierFlagsStatic != 0) && - (c.compilerOptions.UseDefineForClassFields.IsTrue() || !c.isPropertyDeclaredInAncestorClass(prop)) { - diagnostic = c.error(right, diagnostics.Property_0_is_used_before_its_initialization, declarationName) - } else if ast.IsClassDeclaration(valueDeclaration) && !ast.IsTypeReferenceNode(node.Parent) && valueDeclaration.Flags&ast.NodeFlagsAmbient == 0 && !c.isBlockScopedNameDeclaredBeforeUse(valueDeclaration, right) { - diagnostic = c.error(right, diagnostics.Class_0_used_before_its_declaration, declarationName) - } - if diagnostic != nil { - diagnostic.AddRelatedInfo(NewDiagnosticForNode(valueDeclaration, diagnostics.X_0_is_declared_here, declarationName)) - } -} - -func (c *Checker) isOptionalPropertyDeclaration(node *ast.Node) bool { - return ast.IsPropertyDeclaration(node) && !ast.HasAccessorModifier(node) && ast.IsQuestionToken(node.AsPropertyDeclaration().PostfixToken) -} - -func (c *Checker) isPropertyDeclaredInAncestorClass(prop *ast.Symbol) bool { - if prop.Parent.Flags&ast.SymbolFlagsClass == 0 { - return false - } - classType := c.getDeclaredTypeOfSymbol(prop.Parent) - for { - baseTypes := c.getBaseTypes(classType) - if len(baseTypes) == 0 { - return false - } - classType = baseTypes[0] - superProperty := c.getPropertyOfType(classType, prop.Name) - if superProperty != nil && superProperty.ValueDeclaration != nil { - return true - } - } -} - -/** - * Check whether the requested property access is valid. - * Returns true if node is a valid property access, and false otherwise. - * @param node The node to be checked. - * @param isSuper True if the access is from `super.`. - * @param type The type of the object whose property is being accessed. (Not the type of the property.) - * @param prop The symbol for the property being accessed. - */ -func (c *Checker) checkPropertyAccessibility(node *ast.Node, isSuper bool, writing bool, t *Type, prop *ast.Symbol) bool { - return c.checkPropertyAccessibilityEx(node, isSuper, writing, t, prop, true /*reportError*/) -} - -func (c *Checker) checkPropertyAccessibilityEx(node *ast.Node, isSuper bool, writing bool, t *Type, prop *ast.Symbol, reportError bool /* = true */) bool { - var errorNode *ast.Node - if reportError { - switch node.Kind { - case ast.KindPropertyAccessExpression: - errorNode = node.AsPropertyAccessExpression().Name() - case ast.KindQualifiedName: - errorNode = node.AsQualifiedName().Right - case ast.KindImportType: - errorNode = node - case ast.KindBindingElement: - errorNode = getBindingElementPropertyName(node) - default: - errorNode = node.Name() - } - } - return c.checkPropertyAccessibilityAtLocation(node, isSuper, writing, t, prop, errorNode) -} - -/** - * Check whether the requested property can be accessed at the requested location. - * Returns true if node is a valid property access, and false otherwise. - * @param location The location node where we want to check if the property is accessible. - * @param isSuper True if the access is from `super.`. - * @param writing True if this is a write property access, false if it is a read property access. - * @param containingType The type of the object whose property is being accessed. (Not the type of the property.) - * @param prop The symbol for the property being accessed. - * @param errorNode The node where we should report an invalid property access error, or undefined if we should not report errors. - */ -func (c *Checker) checkPropertyAccessibilityAtLocation(location *ast.Node, isSuper bool, writing bool, containingType *Type, prop *ast.Symbol, errorNode *ast.Node) bool { - flags := getDeclarationModifierFlagsFromSymbolEx(prop, writing) - if isSuper { - // TS 1.0 spec (April 2014): 4.8.2 - // - In a constructor, instance member function, instance member accessor, or - // instance member variable initializer where this references a derived class instance, - // a super property access is permitted and must specify a public instance member function of the base class. - // - In a static member function or static member accessor - // where this references the constructor function object of a derived class, - // a super property access is permitted and must specify a public static member function of the base class. - if c.languageVersion < core.ScriptTargetES2015 { - if c.symbolHasNonMethodDeclaration(prop) { - if errorNode != nil { - c.error(errorNode, diagnostics.Only_public_and_protected_methods_of_the_base_class_are_accessible_via_the_super_keyword) - } - return false - } - } - if flags&ast.ModifierFlagsAbstract != 0 { - // A method cannot be accessed in a super property access if the method is abstract. - // This error could mask a private property access error. But, a member - // cannot simultaneously be private and abstract, so this will trigger an - // additional error elsewhere. - if errorNode != nil { - c.error(errorNode, diagnostics.Abstract_method_0_in_class_1_cannot_be_accessed_via_super_expression, c.symbolToString(prop), c.TypeToString(c.getDeclaringClass(prop))) - } - return false - } - // A class field cannot be accessed via super.* from a derived class. - // This is true for both [[Set]] (old) and [[Define]] (ES spec) semantics. - if flags&ast.ModifierFlagsStatic == 0 && core.Some(prop.Declarations, isClassInstanceProperty) { - if errorNode != nil { - c.error(errorNode, diagnostics.Class_field_0_defined_by_the_parent_class_is_not_accessible_in_the_child_class_via_super, c.symbolToString(prop)) - } - return false - } - } - // Referencing abstract properties within their own constructors is not allowed - if flags&ast.ModifierFlagsAbstract != 0 && c.symbolHasNonMethodDeclaration(prop) && (isThisProperty(location) || - isThisInitializedObjectBindingExpression(location) || - ast.IsObjectBindingPattern(location.Parent) && isThisInitializedDeclaration(location.Parent.Parent)) { - declaringClassDeclaration := ast.GetClassLikeDeclarationOfSymbol(c.getParentOfSymbol(prop)) - if declaringClassDeclaration != nil && c.isNodeUsedDuringClassInitialization(location) { - if errorNode != nil { - c.error(errorNode, diagnostics.Abstract_property_0_in_class_1_cannot_be_accessed_in_the_constructor, c.symbolToString(prop), declaringClassDeclaration.Name().Text()) - } - return false - } - } - // Public properties are otherwise accessible. - if flags&ast.ModifierFlagsNonPublicAccessibilityModifier == 0 { - return true - } - // Property is known to be private or protected at this point - // Private property is accessible if the property is within the declaring class - if flags&ast.ModifierFlagsPrivate != 0 { - declaringClassDeclaration := ast.GetClassLikeDeclarationOfSymbol(c.getParentOfSymbol(prop)) - if !c.isNodeWithinClass(location, declaringClassDeclaration) { - if errorNode != nil { - c.error(errorNode, diagnostics.Property_0_is_private_and_only_accessible_within_class_1, c.symbolToString(prop), c.TypeToString(c.getDeclaringClass(prop))) - } - return false - } - return true - } - // Property is known to be protected at this point - // All protected properties of a supertype are accessible in a super access - if isSuper { - return true - } - // Find the first enclosing class that has the declaring classes of the protected constituents - // of the property as base classes - var enclosingClass *Type - container := ast.GetContainingClass(location) - for container != nil { - class := c.getDeclaredTypeOfSymbol(c.getSymbolOfDeclaration(container)) - if c.isClassDerivedFromDeclaringClasses(class, prop, writing) { - enclosingClass = class - break - } - container = ast.GetContainingClass(container) - } - // A protected property is accessible if the property is within the declaring class or classes derived from it - if enclosingClass == nil { - // allow PropertyAccessibility if context is in function with this parameter - // static member access is disallowed - class := c.getEnclosingClassFromThisParameter(location) - if class != nil && c.isClassDerivedFromDeclaringClasses(class, prop, writing) { - enclosingClass = class - } - if flags&ast.ModifierFlagsStatic != 0 || enclosingClass == nil { - if errorNode != nil { - class := c.getDeclaringClass(prop) - if class == nil { - class = containingType - } - c.error(errorNode, diagnostics.Property_0_is_protected_and_only_accessible_within_class_1_and_its_subclasses, c.symbolToString(prop), c.TypeToString(class)) - } - return false - } - } - // No further restrictions for static properties - if flags&ast.ModifierFlagsStatic != 0 { - return true - } - if containingType.flags&TypeFlagsTypeParameter != 0 { - // get the original type -- represented as the type constraint of the 'this' type - if containingType.AsTypeParameter().isThisType { - containingType = c.getConstraintOfTypeParameter(containingType) - } else { - containingType = c.getBaseConstraintOfType(containingType) - } - } - if containingType == nil || !c.hasBaseType(containingType, enclosingClass) { - if errorNode != nil && containingType != nil { - c.error(errorNode, diagnostics.Property_0_is_protected_and_only_accessible_through_an_instance_of_class_1_This_is_an_instance_of_class_2, c.symbolToString(prop), c.TypeToString(enclosingClass), c.TypeToString(containingType)) - } - return false - } - return true -} - -func (c *Checker) symbolHasNonMethodDeclaration(symbol *ast.Symbol) bool { - return c.forEachProperty(symbol, func(prop *ast.Symbol) bool { return prop.Flags&ast.SymbolFlagsMethod == 0 }) -} - -// Invoke the callback for each underlying property symbol of the given symbol and return the first -// value that isn't undefined. -func (c *Checker) forEachProperty(prop *ast.Symbol, callback func(p *ast.Symbol) bool) bool { - if prop.CheckFlags&ast.CheckFlagsSynthetic == 0 { - return callback(prop) - } - for _, t := range c.valueSymbolLinks.Get(prop).containingType.Types() { - p := c.getPropertyOfType(t, prop.Name) - if p != nil && c.forEachProperty(p, callback) { - return true - } - } - return false -} - -// Return the declaring class type of a property or undefined if property not declared in class -func (c *Checker) getDeclaringClass(prop *ast.Symbol) *Type { - if prop.Parent != nil && prop.Parent.Flags&ast.SymbolFlagsClass != 0 { - return c.getDeclaredTypeOfSymbol(c.getParentOfSymbol(prop)) - } - return nil -} - -// Return true if source property is a valid override of protected parts of target property. -func (c *Checker) isValidOverrideOf(sourceProp *ast.Symbol, targetProp *ast.Symbol) bool { - return !c.forEachProperty(targetProp, func(tp *ast.Symbol) bool { - if getDeclarationModifierFlagsFromSymbol(tp)&ast.ModifierFlagsProtected != 0 { - return !c.isPropertyInClassDerivedFrom(sourceProp, c.getDeclaringClass(tp)) - } - return false - }) -} - -// Return true if some underlying source property is declared in a class that derives -// from the given base class. -func (c *Checker) isPropertyInClassDerivedFrom(prop *ast.Symbol, baseClass *Type) bool { - return c.forEachProperty(prop, func(sp *ast.Symbol) bool { - sourceClass := c.getDeclaringClass(sp) - if sourceClass != nil { - return c.hasBaseType(sourceClass, baseClass) - } - return false - }) -} - -func (c *Checker) isNodeUsedDuringClassInitialization(node *ast.Node) bool { - return ast.FindAncestorOrQuit(node, func(element *ast.Node) ast.FindAncestorResult { - if ast.IsConstructorDeclaration(element) && ast.NodeIsPresent(element.Body()) || ast.IsPropertyDeclaration(element) { - return ast.FindAncestorTrue - } else if ast.IsClassLike(element) || ast.IsFunctionLikeDeclaration(element) { - return ast.FindAncestorQuit - } - return ast.FindAncestorFalse - }) != nil -} - -func (c *Checker) isNodeWithinClass(node *ast.Node, classDeclaration *ast.Node) bool { - return c.forEachEnclosingClass(node, func(n *ast.Node) bool { return n == classDeclaration }) -} - -func (c *Checker) forEachEnclosingClass(node *ast.Node, callback func(node *ast.Node) bool) bool { - containingClass := ast.GetContainingClass(node) - for containingClass != nil { - result := callback(containingClass) - if result { - return true - } - containingClass = ast.GetContainingClass(containingClass) - } - return false -} - -// Return true if the given class derives from each of the declaring classes of the protected -// constituents of the given property. -func (c *Checker) isClassDerivedFromDeclaringClasses(checkClass *Type, prop *ast.Symbol, writing bool) bool { - return !c.forEachProperty(prop, func(p *ast.Symbol) bool { - if getDeclarationModifierFlagsFromSymbolEx(p, writing)&ast.ModifierFlagsProtected != 0 { - return !c.hasBaseType(checkClass, c.getDeclaringClass(p)) - } - return false - }) -} - -func (c *Checker) getEnclosingClassFromThisParameter(node *ast.Node) *Type { - // 'this' type for a node comes from, in priority order... - // 1. The type of a syntactic 'this' parameter in the enclosing function scope - thisParameter := getThisParameterFromNodeContext(node) - var thisType *Type - if thisParameter != nil && thisParameter.AsParameterDeclaration().Type != nil { - thisType = c.getTypeFromTypeNode(thisParameter.AsParameterDeclaration().Type) - } - if thisType != nil { - // 2. The constraint of a type parameter used for an explicit 'this' parameter - if thisType.flags&TypeFlagsTypeParameter != 0 { - thisType = c.getConstraintOfTypeParameter(thisType) - } - } else { - // 3. The 'this' parameter of a contextual type - thisContainer := ast.GetThisContainer(node, false /*includeArrowFunctions*/, false /*includeClassComputedPropertyName*/) - if thisContainer != nil && ast.IsFunctionLike(thisContainer) { - thisType = c.getContextualThisParameterType(thisContainer) - } - } - if thisType != nil && thisType.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsReference) != 0 { - return getTargetType(thisType) - } - return nil -} - -func getThisParameterFromNodeContext(node *ast.Node) *ast.Node { - thisContainer := ast.GetThisContainer(node, false /*includeArrowFunctions*/, false /*includeClassComputedPropertyName*/) - if thisContainer != nil && ast.IsFunctionLike(thisContainer) { - return ast.GetThisParameter(thisContainer) - } - return nil -} - -func (c *Checker) getContextualThisParameterType(fn *ast.Node) *Type { - if ast.IsArrowFunction(fn) { - return nil - } - if c.isContextSensitiveFunctionOrObjectLiteralMethod(fn) { - contextualSignature := c.getContextualSignature(fn) - if contextualSignature != nil { - thisParameter := contextualSignature.thisParameter - if thisParameter != nil { - return c.getTypeOfSymbol(thisParameter) - } - } - } - if c.noImplicitThis { - containingLiteral := getContainingObjectLiteral(fn) - if containingLiteral != nil { - // We have an object literal method. Check if the containing object literal has a contextual type - // that includes a ThisType. If so, T is the contextual type for 'this'. We continue looking in - // any directly enclosing object literals. - contextualType := c.getApparentTypeOfContextualType(containingLiteral, ContextFlagsNone) - thisType := c.getThisTypeOfObjectLiteralFromContextualType(containingLiteral, contextualType) - if thisType != nil { - return c.instantiateType(thisType, c.getMapperFromContext(c.getInferenceContext(containingLiteral))) - } - // There was no contextual ThisType for the containing object literal, so the contextual type - // for 'this' is the non-null form of the contextual type for the containing object literal or - // the type of the object literal itself. - if contextualType != nil { - thisType = c.GetNonNullableType(contextualType) - } else { - thisType = c.checkExpressionCached(containingLiteral) - } - return c.getWidenedType(thisType) - } - // In an assignment of the form 'obj.xxx = function(...)' or 'obj[xxx] = function(...)', the - // contextual type for 'this' is 'obj'. - parent := ast.WalkUpParenthesizedExpressions(fn.Parent) - if ast.IsAssignmentExpression(parent, false) { - target := parent.AsBinaryExpression().Left - if ast.IsAccessExpression(target) { - return c.getWidenedType(c.checkExpressionCached(target.Expression())) - } - } - } - return nil -} - -func (c *Checker) checkThisExpression(node *ast.Node) *Type { - // Stop at the first arrow function so that we can - // tell whether 'this' needs to be captured. - container := ast.GetThisContainer(node, true /*includeArrowFunctions*/, true /*includeClassComputedPropertyName*/) - capturedByArrowFunction := false - thisInComputedPropertyName := false - if ast.IsConstructorDeclaration(container) { - c.checkThisBeforeSuper(node, container, diagnostics.X_super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class) - } - for { - // Now skip arrow functions to get the "real" owner of 'this'. - if ast.IsArrowFunction(container) { - container = ast.GetThisContainer(container, false /*includeArrowFunctions*/, !thisInComputedPropertyName) - capturedByArrowFunction = true - } - if ast.IsComputedPropertyName(container) { - container = ast.GetThisContainer(container, !capturedByArrowFunction, false /*includeClassComputedPropertyName*/) - thisInComputedPropertyName = true - continue - } - break - } - c.checkThisInStaticClassFieldInitializerInDecoratedClass(node, container) - if thisInComputedPropertyName { - c.error(node, diagnostics.X_this_cannot_be_referenced_in_a_computed_property_name) - } else { - switch container.Kind { - case ast.KindModuleDeclaration: - c.error(node, diagnostics.X_this_cannot_be_referenced_in_a_module_or_namespace_body) - // do not return here so in case if lexical this is captured - it will be reflected in flags on NodeLinks - case ast.KindEnumDeclaration: - c.error(node, diagnostics.X_this_cannot_be_referenced_in_current_location) - // do not return here so in case if lexical this is captured - it will be reflected in flags on NodeLinks - } - } - t := c.TryGetThisTypeAtEx(node, true /*includeGlobalThis*/, container) - if c.noImplicitThis { - globalThisType := c.getTypeOfSymbol(c.globalThisSymbol) - if t == globalThisType && capturedByArrowFunction { - c.error(node, diagnostics.The_containing_arrow_function_captures_the_global_value_of_this) - } else if t == nil { - // With noImplicitThis, functions may not reference 'this' if it has type 'any' - diag := c.error(node, diagnostics.X_this_implicitly_has_type_any_because_it_does_not_have_a_type_annotation) - if !ast.IsSourceFile(container) { - outsideThis := c.tryGetThisTypeAt(container) - if outsideThis != nil && outsideThis != globalThisType { - diag.AddRelatedInfo(createDiagnosticForNode(container, diagnostics.An_outer_value_of_this_is_shadowed_by_this_container)) - } - } - } - } - if t == nil { - return c.anyType - } - return t -} - -func (c *Checker) tryGetThisTypeAt(node *ast.Node) *Type { - return c.TryGetThisTypeAtEx(node, true /*includeGlobalThis*/, nil /*container*/) -} - -func (c *Checker) TryGetThisTypeAtEx(node *ast.Node, includeGlobalThis bool, container *ast.Node) *Type { - if container == nil { - container = c.getThisContainer(node, false /*includeArrowFunctions*/, false /*includeClassComputedPropertyName*/) - } - if ast.IsFunctionLike(container) && (!c.isInParameterInitializerBeforeContainingFunction(node) || ast.GetThisParameter(container) != nil) { - thisType := c.getThisTypeOfDeclaration(container) - if thisType == nil && ast.IsInJSFile(container) { - if sig := c.getSignatureOfFullSignatureType(container); sig != nil { - thisType = c.getThisTypeOfSignature(sig) - } - } - // Note: a parameter initializer should refer to class-this unless function-this is explicitly annotated. - // If this is a function in a JS file, it might be a class method. - if thisType == nil { - thisType = c.getContextualThisParameterType(container) - } - if thisType != nil { - return c.getFlowTypeOfReference(node, thisType) - } - } - if container.Parent != nil && ast.IsClassLike(container.Parent) { - symbol := c.getSymbolOfDeclaration(container.Parent) - var t *Type - if ast.IsStatic(container) { - t = c.getTypeOfSymbol(symbol) - } else { - t = c.getDeclaredTypeOfSymbol(symbol).AsInterfaceType().thisType - } - return c.getFlowTypeOfReference(node, t) - } - if ast.IsSourceFile(container) { - // look up in the source file's locals or exports - if container.AsSourceFile().ExternalModuleIndicator != nil { - // TODO: Maybe issue a better error than 'object is possibly undefined' - return c.undefinedType - } - if includeGlobalThis { - return c.getTypeOfSymbol(c.globalThisSymbol) - } - } - return nil -} - -func (c *Checker) getThisContainer(node *ast.Node, includeArrowFunctions bool, includeClassComputedPropertyName bool) *ast.Node { - for { - node = node.Parent - if node == nil { - // If we never pass in a SourceFile, this should be unreachable, since we'll stop when we reach that. - panic("No parent in getThisContainer") - } - switch node.Kind { - case ast.KindComputedPropertyName: - // If the grandparent node is an object literal (as opposed to a class), - // then the computed property is not a 'this' container. - // A computed property name in a class needs to be a this container - // so that we can error on it. - if includeClassComputedPropertyName && ast.IsClassLike(node.Parent.Parent) { - return node - } - // If this is a computed property, then the parent should not - // make it a this container. The parent might be a property - // in an object literal, like a method or accessor. But in order for - // such a parent to be a this container, the reference must be in - // the *body* of the container. - node = node.Parent.Parent - case ast.KindDecorator: - // Decorators are always applied outside of the body of a class or method. - if node.Parent.Kind == ast.KindParameter && 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 - } - case ast.KindArrowFunction: - if !includeArrowFunctions { - continue - } - fallthrough - case ast.KindFunctionDeclaration, ast.KindFunctionExpression, ast.KindModuleDeclaration, ast.KindClassStaticBlockDeclaration, - ast.KindPropertyDeclaration, ast.KindPropertySignature, ast.KindMethodDeclaration, ast.KindMethodSignature, ast.KindConstructor, - ast.KindGetAccessor, ast.KindSetAccessor, ast.KindCallSignature, ast.KindConstructSignature, ast.KindIndexSignature, - ast.KindEnumDeclaration, ast.KindSourceFile: - return node - } - } -} - -func (c *Checker) isInParameterInitializerBeforeContainingFunction(node *ast.Node) bool { - inBindingInitializer := false - for node.Parent != nil && !ast.IsFunctionLike(node.Parent) { - if ast.IsParameter(node.Parent) { - if inBindingInitializer || node.Parent.Initializer() == node { - return true - } - } - - if ast.IsBindingElement(node.Parent) && node.Parent.Initializer() == node { - inBindingInitializer = true - } - - node = node.Parent - } - - return false -} - -func (c *Checker) getThisTypeOfDeclaration(declaration *ast.Node) *Type { - return c.getThisTypeOfSignature(c.getSignatureFromDeclaration(declaration)) -} - -func (c *Checker) checkThisInStaticClassFieldInitializerInDecoratedClass(thisExpression *ast.Node, container *ast.Node) { - if ast.IsPropertyDeclaration(container) && ast.HasStaticModifier(container) && c.legacyDecorators { - initializer := container.Initializer() - if initializer != nil && initializer.Loc.ContainsInclusive(thisExpression.Pos()) && ast.HasDecorators(container.Parent) { - c.error(thisExpression, diagnostics.Cannot_use_this_in_a_static_property_initializer_of_a_decorated_class) - } - } -} - -func (c *Checker) checkThisBeforeSuper(node *ast.Node, container *ast.Node, diagnosticMessage *diagnostics.Message) { - containingClassDecl := container.Parent - baseTypeNode := ast.GetExtendsHeritageClauseElement(containingClassDecl) - // If a containing class does not have extends clause or the class extends null - // skip checking whether super statement is called before "this" accessing. - if baseTypeNode != nil && !c.classDeclarationExtendsNull(containingClassDecl) { - if node.FlowNodeData() != nil && !c.isPostSuperFlowNode(node.FlowNodeData().FlowNode, false /*noCacheCheck*/) { - c.error(node, diagnosticMessage) - } - } -} - -/** - * Check if the given class-declaration extends null then return true. - * Otherwise, return false - * @param classDecl a class declaration to check if it extends null - */ -func (c *Checker) classDeclarationExtendsNull(classDecl *ast.Node) bool { - classSymbol := c.getSymbolOfDeclaration(classDecl) - classInstanceType := c.getDeclaredTypeOfSymbol(classSymbol) - baseConstructorType := c.getBaseConstructorTypeOfClass(classInstanceType) - return baseConstructorType == c.nullWideningType -} - -func (c *Checker) checkAssertion(node *ast.Node, checkMode CheckMode) *Type { - if node.Kind == ast.KindTypeAssertionExpression { - if c.compilerOptions.ErasableSyntaxOnly.IsTrue() { - c.diagnostics.Add(ast.NewDiagnostic(ast.GetSourceFileOfNode(node), core.NewTextRange(scanner.SkipTrivia(ast.GetSourceFileOfNode(node).Text(), node.Pos()), node.Expression().Pos()), diagnostics.This_syntax_is_not_allowed_when_erasableSyntaxOnly_is_enabled)) - } - } - typeNode := node.Type() - exprType := c.checkExpressionEx(node.Expression(), checkMode) - if isConstTypeReference(typeNode) { - if !c.isValidConstAssertionArgument(node.Expression()) { - c.error(node.Expression(), diagnostics.A_const_assertions_can_only_be_applied_to_references_to_enum_members_or_string_number_boolean_array_or_object_literals) - } - return c.getRegularTypeOfLiteralType(exprType) - } - links := c.assertionLinks.Get(node) - links.exprType = exprType - c.checkSourceElement(typeNode) - c.checkNodeDeferred(node) - return c.getTypeFromTypeNode(typeNode) -} - -func (c *Checker) checkAssertionDeferred(node *ast.Node) { - exprType := c.getRegularTypeOfObjectLiteral(c.getBaseTypeOfLiteralType(c.assertionLinks.Get(node).exprType)) - targetType := c.getTypeFromTypeNode(node.Type()) - if !c.isErrorType(targetType) { - widenedType := c.getWidenedType(exprType) - if !c.isTypeComparableTo(targetType, widenedType) { - errNode := node - if node.Flags&ast.NodeFlagsReparsed != 0 { - errNode = node.Type() - } - c.checkTypeComparableTo(exprType, targetType, errNode, diagnostics.Conversion_of_type_0_to_type_1_may_be_a_mistake_because_neither_type_sufficiently_overlaps_with_the_other_If_this_was_intentional_convert_the_expression_to_unknown_first) - } - } -} - -func (c *Checker) checkBinaryExpression(node *ast.Node, checkMode CheckMode) *Type { - binary := node.AsBinaryExpression() - return c.checkBinaryLikeExpression(binary.Left, binary.OperatorToken, binary.Right, checkMode, node) -} - -func (c *Checker) checkBinaryLikeExpression(left *ast.Node, operatorToken *ast.Node, right *ast.Node, checkMode CheckMode, errorNode *ast.Node) *Type { - operator := operatorToken.Kind - if operator == ast.KindEqualsToken && (left.Kind == ast.KindObjectLiteralExpression || left.Kind == ast.KindArrayLiteralExpression) { - return c.checkDestructuringAssignment(left, c.checkExpressionEx(right, checkMode), checkMode, right.Kind == ast.KindThisKeyword) - } - leftType := c.checkExpressionEx(left, checkMode) - rightType := c.checkExpressionEx(right, checkMode) - if ast.IsLogicalOrCoalescingBinaryOperator(operator) { - parent := left.Parent.Parent - for ast.IsParenthesizedExpression(parent) || ast.IsLogicalOrCoalescingBinaryExpression(parent) { - parent = parent.Parent - } - if operator == ast.KindAmpersandAmpersandToken || ast.IsIfStatement(parent) { - var body *ast.Node - if ast.IsIfStatement(parent) { - body = parent.AsIfStatement().ThenStatement - } - c.checkTestingKnownTruthyCallableOrAwaitableOrEnumMemberType(left, leftType, body) - } - if ast.IsLogicalBinaryOperator(operator) { - c.checkTruthinessOfType(leftType, left) - } - } - switch operator { - case ast.KindAsteriskToken, ast.KindAsteriskAsteriskToken, ast.KindAsteriskEqualsToken, ast.KindAsteriskAsteriskEqualsToken, - ast.KindSlashToken, ast.KindSlashEqualsToken, ast.KindPercentToken, ast.KindPercentEqualsToken, ast.KindMinusToken, - ast.KindMinusEqualsToken, ast.KindLessThanLessThanToken, ast.KindLessThanLessThanEqualsToken, ast.KindGreaterThanGreaterThanToken, - ast.KindGreaterThanGreaterThanEqualsToken, ast.KindGreaterThanGreaterThanGreaterThanToken, ast.KindGreaterThanGreaterThanGreaterThanEqualsToken, - ast.KindBarToken, ast.KindBarEqualsToken, ast.KindCaretToken, ast.KindCaretEqualsToken, ast.KindAmpersandToken, ast.KindAmpersandEqualsToken: - if leftType == c.silentNeverType || rightType == c.silentNeverType { - return c.silentNeverType - } - leftType = c.checkNonNullType(leftType, left) - rightType = c.checkNonNullType(rightType, right) - // if a user tries to apply a bitwise operator to 2 boolean operands - // try and return them a helpful suggestion - if leftType.flags&TypeFlagsBooleanLike != 0 && rightType.flags&TypeFlagsBooleanLike != 0 { - suggestedOperator := c.getSuggestedBooleanOperator(operator) - if suggestedOperator != ast.KindUnknown { - c.error(operatorToken, diagnostics.The_0_operator_is_not_allowed_for_boolean_types_Consider_using_1_instead, scanner.TokenToString(operatorToken.Kind), scanner.TokenToString(suggestedOperator)) - return c.numberType - } - } - // otherwise just check each operand separately and report errors as normal - leftOk := c.checkArithmeticOperandType(left, leftType, diagnostics.The_left_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_bigint_or_an_enum_type, true /*isAwaitValid*/) - rightOk := c.checkArithmeticOperandType(right, rightType, diagnostics.The_right_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_bigint_or_an_enum_type, true /*isAwaitValid*/) - var resultType *Type - // If both are any or unknown, allow operation; assume it will resolve to number - if c.isTypeAssignableToKind(leftType, TypeFlagsAnyOrUnknown) && c.isTypeAssignableToKind(rightType, TypeFlagsAnyOrUnknown) || !c.maybeTypeOfKind(leftType, TypeFlagsBigIntLike) && !c.maybeTypeOfKind(rightType, TypeFlagsBigIntLike) { - resultType = c.numberType - } else if c.bothAreBigIntLike(leftType, rightType) { - switch operator { - case ast.KindGreaterThanGreaterThanGreaterThanToken, ast.KindGreaterThanGreaterThanGreaterThanEqualsToken: - c.reportOperatorError(leftType, operator, rightType, errorNode, nil) - case ast.KindAsteriskAsteriskToken, ast.KindAsteriskAsteriskEqualsToken: - if c.languageVersion < core.ScriptTargetES2016 { - c.error(errorNode, diagnostics.Exponentiation_cannot_be_performed_on_bigint_values_unless_the_target_option_is_set_to_es2016_or_later) - } - } - resultType = c.bigintType - } else { - c.reportOperatorError(leftType, operator, rightType, errorNode, c.bothAreBigIntLike) - resultType = c.errorType - } - if leftOk && rightOk { - c.checkAssignmentOperator(left, operator, right, leftType, resultType) - switch operator { - case ast.KindLessThanLessThanToken, ast.KindLessThanLessThanEqualsToken, ast.KindGreaterThanGreaterThanToken, - ast.KindGreaterThanGreaterThanEqualsToken, ast.KindGreaterThanGreaterThanGreaterThanToken, - ast.KindGreaterThanGreaterThanGreaterThanEqualsToken: - rhsEval := c.evaluate(right, right) - if numValue, ok := rhsEval.Value.(jsnum.Number); ok && numValue.Abs() >= 32 { - // Elevate from suggestion to error within an enum member - c.errorOrSuggestion(ast.IsEnumMember(ast.WalkUpParenthesizedExpressions(right.Parent.Parent)), errorNode, diagnostics.This_operation_can_be_simplified_This_shift_is_identical_to_0_1_2, scanner.GetTextOfNode(left), scanner.TokenToString(operator), numValue.Remainder(32)) - } - } - } - return resultType - case ast.KindPlusToken, ast.KindPlusEqualsToken: - if leftType == c.silentNeverType || rightType == c.silentNeverType { - return c.silentNeverType - } - if !c.isTypeAssignableToKind(leftType, TypeFlagsStringLike) && !c.isTypeAssignableToKind(rightType, TypeFlagsStringLike) { - leftType = c.checkNonNullType(leftType, left) - rightType = c.checkNonNullType(rightType, right) - } - var resultType *Type - if c.isTypeAssignableToKindEx(leftType, TypeFlagsNumberLike, true /*strict*/) && c.isTypeAssignableToKindEx(rightType, TypeFlagsNumberLike, true /*strict*/) { - // Operands of an enum type are treated as having the primitive type Number. - // If both operands are of the Number primitive type, the result is of the Number primitive type. - resultType = c.numberType - } else if c.isTypeAssignableToKindEx(leftType, TypeFlagsBigIntLike, true /*strict*/) && c.isTypeAssignableToKindEx(rightType, TypeFlagsBigIntLike, true /*strict*/) { - // If both operands are of the BigInt primitive type, the result is of the BigInt primitive type. - resultType = c.bigintType - } else if c.isTypeAssignableToKindEx(leftType, TypeFlagsStringLike, true /*strict*/) || c.isTypeAssignableToKindEx(rightType, TypeFlagsStringLike, true /*strict*/) { - // If one or both operands are of the String primitive type, the result is of the String primitive type. - resultType = c.stringType - } else if IsTypeAny(leftType) || IsTypeAny(rightType) { - // Otherwise, the result is of type Any. - // NOTE: unknown type here denotes error type. Old compiler treated this case as any type so do we. - if c.isErrorType(leftType) || c.isErrorType(rightType) { - resultType = c.errorType - } else { - resultType = c.anyType - } - } - // Symbols are not allowed at all in arithmetic expressions - if resultType != nil && !c.checkForDisallowedESSymbolOperand(left, right, leftType, rightType, operator) { - return resultType - } - if resultType == nil { - // Types that have a reasonably good chance of being a valid operand type. - // If both types have an awaited type of one of these, we'll assume the user - // might be missing an await without doing an exhaustive check that inserting - // await(s) will actually be a completely valid binary expression. - closeEnoughKind := TypeFlagsNumberLike | TypeFlagsBigIntLike | TypeFlagsStringLike | TypeFlagsAnyOrUnknown - c.reportOperatorError(leftType, operator, rightType, errorNode, func(left *Type, right *Type) bool { - return c.isTypeAssignableToKind(left, closeEnoughKind) && c.isTypeAssignableToKind(right, closeEnoughKind) - }) - return c.anyType - } - if operator == ast.KindPlusEqualsToken { - c.checkAssignmentOperator(left, operator, right, leftType, resultType) - } - return resultType - case ast.KindLessThanToken, ast.KindGreaterThanToken, ast.KindLessThanEqualsToken, ast.KindGreaterThanEqualsToken: - if c.checkForDisallowedESSymbolOperand(left, right, leftType, rightType, operator) { - leftType = c.getBaseTypeOfLiteralTypeForComparison(c.checkNonNullType(leftType, left)) - rightType = c.getBaseTypeOfLiteralTypeForComparison(c.checkNonNullType(rightType, right)) - c.reportOperatorErrorUnless(leftType, operator, rightType, errorNode, func(left *Type, right *Type) bool { - if IsTypeAny(left) || IsTypeAny(right) { - return true - } - leftAssignableToNumber := c.isTypeAssignableTo(left, c.numberOrBigIntType) - rightAssignableToNumber := c.isTypeAssignableTo(right, c.numberOrBigIntType) - return leftAssignableToNumber && rightAssignableToNumber || !leftAssignableToNumber && !rightAssignableToNumber && c.areTypesComparable(left, right) - }) - } - return c.booleanType - case ast.KindEqualsEqualsToken, ast.KindExclamationEqualsToken, ast.KindEqualsEqualsEqualsToken, ast.KindExclamationEqualsEqualsToken: - // We suppress errors in CheckMode.TypeOnly (meaning the invocation came from getTypeOfExpression). During - // control flow analysis it is possible for operands to temporarily have narrower types, and those narrower - // types may cause the operands to not be comparable. We don't want such errors reported (see #46475). - if checkMode&CheckModeTypeOnly == 0 { - if isLiteralExpressionOfObject(left) || isLiteralExpressionOfObject(right) { - eqType := operator == ast.KindEqualsEqualsToken || operator == ast.KindEqualsEqualsEqualsToken - c.error(errorNode, diagnostics.This_condition_will_always_return_0_since_JavaScript_compares_objects_by_reference_not_value, core.IfElse(eqType, "false", "true")) - } - c.checkNaNEquality(errorNode, operator, left, right) - c.reportOperatorErrorUnless(leftType, operator, rightType, errorNode, func(left *Type, right *Type) bool { - return c.isTypeEqualityComparableTo(left, right) || c.isTypeEqualityComparableTo(right, left) - }) - } - return c.booleanType - case ast.KindInstanceOfKeyword: - return c.checkInstanceOfExpression(left, right, leftType, rightType, checkMode) - case ast.KindInKeyword: - return c.checkInExpression(left, right, leftType, rightType) - case ast.KindAmpersandAmpersandToken, ast.KindAmpersandAmpersandEqualsToken: - resultType := leftType - if c.hasTypeFacts(leftType, TypeFactsTruthy) { - t := leftType - if !c.strictNullChecks { - t = c.getBaseTypeOfLiteralType(rightType) - } - resultType = c.getUnionType([]*Type{c.extractDefinitelyFalsyTypes(t), rightType}) - } - if operator == ast.KindAmpersandAmpersandEqualsToken { - c.checkAssignmentOperator(left, operator, right, leftType, rightType) - } - return resultType - case ast.KindBarBarToken, ast.KindBarBarEqualsToken: - resultType := leftType - if c.hasTypeFacts(leftType, TypeFactsFalsy) { - resultType = c.getUnionTypeEx([]*Type{c.GetNonNullableType(c.removeDefinitelyFalsyTypes(leftType)), rightType}, UnionReductionSubtype, nil, nil) - } - if operator == ast.KindBarBarEqualsToken { - c.checkAssignmentOperator(left, operator, right, leftType, rightType) - } - return resultType - case ast.KindQuestionQuestionToken, ast.KindQuestionQuestionEqualsToken: - if operator == ast.KindQuestionQuestionToken { - c.checkNullishCoalesceOperands(left, right) - } - resultType := leftType - if c.hasTypeFacts(leftType, TypeFactsEQUndefinedOrNull) { - resultType = c.getUnionTypeEx([]*Type{c.GetNonNullableType(leftType), rightType}, UnionReductionSubtype, nil, nil) - } - if operator == ast.KindQuestionQuestionEqualsToken { - c.checkAssignmentOperator(left, operator, right, leftType, rightType) - } - return resultType - case ast.KindEqualsToken: - c.checkAssignmentOperator(left, operator, right, leftType, rightType) - return rightType - case ast.KindCommaToken: - if !c.compilerOptions.AllowUnreachableCode.IsTrue() && c.isSideEffectFree(left) && !c.isIndirectCall(left.Parent) { - sf := ast.GetSourceFileOfNode(left) - start := scanner.SkipTrivia(sf.Text(), left.Pos()) - isInDiag2657 := core.Some(sf.Diagnostics(), func(d *ast.Diagnostic) bool { - if d.Code() != diagnostics.JSX_expressions_must_have_one_parent_element.Code() { - return false - } - return d.Loc().Contains(start) - }) - if !isInDiag2657 { - c.error(left, diagnostics.Left_side_of_comma_operator_is_unused_and_has_no_side_effects) - } - } - return rightType - } - panic("Unhandled case in checkBinaryLikeExpression") -} - -func (c *Checker) checkDestructuringAssignment(node *ast.Node, sourceType *Type, checkMode CheckMode, rightIsThis bool) *Type { - var target *ast.Node - if ast.IsShorthandPropertyAssignment(node) { - initializer := node.AsShorthandPropertyAssignment().ObjectAssignmentInitializer - if initializer != nil { - // In strict null checking mode, if a default value of a non-undefined type is specified, remove - // undefined from the final type. - if c.strictNullChecks && !(c.hasTypeFacts(c.checkExpression(initializer), TypeFactsIsUndefined)) { - sourceType = c.getTypeWithFacts(sourceType, TypeFactsNEUndefined) - } - c.checkBinaryLikeExpression(node.Name(), node.AsShorthandPropertyAssignment().EqualsToken, initializer, checkMode, nil) - } - target = node.Name() - } else { - target = node - } - if ast.IsBinaryExpression(target) && target.AsBinaryExpression().OperatorToken.Kind == ast.KindEqualsToken { - c.checkBinaryExpression(target, checkMode) - target = target.AsBinaryExpression().Left - // A default value is specified, so remove undefined from the final type. - if c.strictNullChecks { - sourceType = c.getTypeWithFacts(sourceType, TypeFactsNEUndefined) - } - } - if ast.IsObjectLiteralExpression(target) { - return c.checkObjectLiteralAssignment(target, sourceType, rightIsThis) - } - if ast.IsArrayLiteralExpression(target) { - return c.checkArrayLiteralAssignment(target, sourceType, checkMode) - } - return c.checkReferenceAssignment(target, sourceType, checkMode) -} - -func (c *Checker) checkObjectLiteralAssignment(node *ast.Node, sourceType *Type, rightIsThis bool) *Type { - properties := node.AsObjectLiteralExpression().Properties - if c.strictNullChecks && len(properties.Nodes) == 0 { - return c.checkNonNullType(sourceType, node) - } - for i := range properties.Nodes { - c.checkObjectLiteralDestructuringPropertyAssignment(node, sourceType, i, properties, rightIsThis) - } - return sourceType -} - -// Note: If property cannot be a SpreadAssignment, then allProperties does not need to be provided -func (c *Checker) checkObjectLiteralDestructuringPropertyAssignment(node *ast.Node, objectLiteralType *Type, propertyIndex int, allProperties *ast.NodeList, rightIsThis bool) *Type { - properties := node.AsObjectLiteralExpression().Properties.Nodes - property := properties[propertyIndex] - if ast.IsPropertyAssignment(property) || ast.IsShorthandPropertyAssignment(property) { - name := property.Name() - exprType := c.getLiteralTypeFromPropertyName(name) - if isTypeUsableAsPropertyName(exprType) { - text := getPropertyNameFromType(exprType) - prop := c.getPropertyOfType(objectLiteralType, text) - if prop != nil { - c.markPropertyAsReferenced(prop, property, rightIsThis) - c.checkPropertyAccessibility(property, false /*isSuper*/, true /*writing*/, objectLiteralType, prop) - } - } - elementType := c.getIndexedAccessTypeEx(objectLiteralType, exprType, AccessFlagsExpressionPosition|(core.IfElse(c.hasDefaultValue(property), AccessFlagsAllowMissing, 0)), name, nil) - t := c.getFlowTypeOfDestructuring(property, elementType) - expr := property - if ast.IsPropertyAssignment(property) { - expr = property.Initializer() - } - return c.checkDestructuringAssignment(expr, t, CheckModeNormal, false) - } - if ast.IsSpreadAssignment(property) { - if propertyIndex < len(properties)-1 { - c.error(property, diagnostics.A_rest_element_must_be_last_in_a_destructuring_pattern) - return nil - } - var nonRestNames []*ast.Node - if allProperties != nil { - for _, otherProperty := range allProperties.Nodes { - if !ast.IsSpreadAssignment(otherProperty) { - nonRestNames = append(nonRestNames, otherProperty.Name()) - } - } - } - t := c.getRestType(objectLiteralType, nonRestNames, objectLiteralType.symbol) - c.checkGrammarForDisallowedTrailingComma(allProperties, diagnostics.A_rest_parameter_or_binding_pattern_may_not_have_a_trailing_comma) - return c.checkDestructuringAssignment(property.Expression(), t, CheckModeNormal, false) - } - c.error(property, diagnostics.Property_assignment_expected) - return nil -} - -func (c *Checker) checkArrayLiteralAssignment(node *ast.Node, sourceType *Type, checkMode CheckMode) *Type { - elements := node.AsArrayLiteralExpression().Elements - // This elementType will be used if the specific property corresponding to this index is not - // present (aka the tuple element property). This call also checks that the parentType is in - // fact an iterable or array (depending on target language). - possiblyOutOfBoundsType := core.OrElse(c.checkIteratedTypeOrElementType(IterationUseDestructuring|IterationUsePossiblyOutOfBounds, sourceType, c.undefinedType, node), c.errorType) - inBoundsType := core.IfElse(c.compilerOptions.NoUncheckedIndexedAccess == core.TSTrue, nil, possiblyOutOfBoundsType) - for i := range elements.Nodes { - t := possiblyOutOfBoundsType - if elements.Nodes[i].Kind == ast.KindSpreadElement { - if inBoundsType == nil { - inBoundsType = core.OrElse(c.checkIteratedTypeOrElementType(IterationUseDestructuring, sourceType, c.undefinedType, node), c.errorType) - } - t = inBoundsType - } - c.checkArrayLiteralDestructuringElementAssignment(node, sourceType, i, t, checkMode) - } - return sourceType -} - -func (c *Checker) checkArrayLiteralDestructuringElementAssignment(node *ast.Node, sourceType *Type, elementIndex int, elementType *Type, checkMode CheckMode) *Type { - elements := node.AsArrayLiteralExpression().Elements - element := elements.Nodes[elementIndex] - if !ast.IsOmittedExpression(element) { - if !ast.IsSpreadElement(element) { - indexType := c.getNumberLiteralType(jsnum.Number(elementIndex)) - if c.isArrayLikeType(sourceType) { - // We create a synthetic expression so that getIndexedAccessType doesn't get confused - // when the element is a SyntaxKind.ElementAccessExpression. - accessFlags := AccessFlagsExpressionPosition | core.IfElse(c.hasDefaultValue(element), AccessFlagsAllowMissing, 0) - elementType := core.OrElse(c.getIndexedAccessTypeOrUndefined(sourceType, indexType, accessFlags, c.createSyntheticExpression(element, indexType, false, nil), nil), c.errorType) - assignedType := elementType - if c.hasDefaultValue(element) { - assignedType = c.getTypeWithFacts(elementType, TypeFactsNEUndefined) - } - t := c.getFlowTypeOfDestructuring(element, assignedType) - return c.checkDestructuringAssignment(element, t, checkMode, false) - } - return c.checkDestructuringAssignment(element, elementType, checkMode, false) - } - if elementIndex < len(elements.Nodes)-1 { - c.error(element, diagnostics.A_rest_element_must_be_last_in_a_destructuring_pattern) - } else { - restExpression := element.Expression() - if ast.IsBinaryExpression(restExpression) && restExpression.AsBinaryExpression().OperatorToken.Kind == ast.KindEqualsToken { - c.error(restExpression.AsBinaryExpression().OperatorToken, diagnostics.A_rest_element_cannot_have_an_initializer) - } else { - c.checkGrammarForDisallowedTrailingComma(elements, diagnostics.A_rest_parameter_or_binding_pattern_may_not_have_a_trailing_comma) - var t *Type - if everyType(sourceType, isTupleType) { - t = c.mapType(sourceType, func(t *Type) *Type { return c.sliceTupleType(t, elementIndex, 0) }) - } else { - t = c.createArrayType(elementType) - } - return c.checkDestructuringAssignment(restExpression, t, checkMode, false) - } - } - } - return nil -} - -func (c *Checker) checkReferenceAssignment(target *ast.Node, sourceType *Type, checkMode CheckMode) *Type { - targetType := c.checkExpressionEx(target, checkMode) - message := core.IfElse(ast.IsSpreadAssignment(target.Parent), - diagnostics.The_target_of_an_object_rest_assignment_must_be_a_variable_or_a_property_access, - diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access) - optionalMessage := core.IfElse(ast.IsSpreadAssignment(target.Parent), - diagnostics.The_target_of_an_object_rest_assignment_may_not_be_an_optional_property_access, - diagnostics.The_left_hand_side_of_an_assignment_expression_may_not_be_an_optional_property_access) - if c.checkReferenceExpression(target, message, optionalMessage) { - c.checkTypeAssignableToAndOptionallyElaborate(sourceType, targetType, target, target, nil, nil) - } - return sourceType -} - -func (c *Checker) reportOperatorError(leftType *Type, operator ast.Kind, rightType *Type, errorNode *ast.Node, isRelated func(left *Type, right *Type) bool) { - wouldWorkWithAwait := false - if isRelated != nil { - awaitedLeftType := c.getAwaitedTypeNoAlias(leftType) - awaitedRightType := c.getAwaitedTypeNoAlias(rightType) - wouldWorkWithAwait = !(awaitedLeftType == leftType && awaitedRightType == rightType) && awaitedLeftType != nil && awaitedRightType != nil && isRelated(awaitedLeftType, awaitedRightType) - } - effectiveLeft := leftType - effectiveRight := rightType - if !wouldWorkWithAwait && isRelated != nil { - effectiveLeft, effectiveRight = c.getBaseTypesIfUnrelated(leftType, rightType, isRelated) - } - leftStr, rightStr := c.getTypeNamesForErrorDisplay(effectiveLeft, effectiveRight) - switch operator { - case ast.KindEqualsEqualsEqualsToken, ast.KindEqualsEqualsToken, ast.KindExclamationEqualsEqualsToken, ast.KindExclamationEqualsToken: - c.errorAndMaybeSuggestAwait(errorNode, wouldWorkWithAwait, diagnostics.This_comparison_appears_to_be_unintentional_because_the_types_0_and_1_have_no_overlap, leftStr, rightStr) - default: - c.errorAndMaybeSuggestAwait(errorNode, wouldWorkWithAwait, diagnostics.Operator_0_cannot_be_applied_to_types_1_and_2, scanner.TokenToString(operator), leftStr, rightStr) - } -} - -func (c *Checker) reportOperatorErrorUnless(leftType *Type, operator ast.Kind, rightType *Type, errorNode *ast.Node, typesAreCompatible func(left *Type, right *Type) bool) { - if !typesAreCompatible(leftType, rightType) { - c.reportOperatorError(leftType, operator, rightType, errorNode, typesAreCompatible) - } -} - -func (c *Checker) getBaseTypesIfUnrelated(leftType *Type, rightType *Type, isRelated func(left *Type, right *Type) bool) (*Type, *Type) { - effectiveLeft := leftType - effectiveRight := rightType - leftBase := c.getBaseTypeOfLiteralType(leftType) - rightBase := c.getBaseTypeOfLiteralType(rightType) - if !isRelated(leftBase, rightBase) { - effectiveLeft = leftBase - effectiveRight = rightBase - } - return effectiveLeft, effectiveRight -} - -func (c *Checker) checkAssignmentOperator(left *ast.Node, operator ast.Kind, right *ast.Node, leftType *Type, rightType *Type) { - if ast.IsAssignmentOperator(operator) { - // getters can be a subtype of setters, so to check for assignability we use the setter's type instead - if isCompoundAssignment(operator) && ast.IsPropertyAccessExpression(left) { - leftType = c.checkPropertyAccessExpression(left, CheckModeNormal, true /*writeOnly*/) - } - if c.checkReferenceExpression(left, diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access, diagnostics.The_left_hand_side_of_an_assignment_expression_may_not_be_an_optional_property_access) { - var headMessage *diagnostics.Message - if c.exactOptionalPropertyTypes && ast.IsPropertyAccessExpression(left) && c.maybeTypeOfKind(rightType, TypeFlagsUndefined) { - target := c.getTypeOfPropertyOfType(c.getTypeOfExpression(left.Expression()), left.Name().Text()) - if c.isExactOptionalPropertyMismatch(rightType, target) { - headMessage = diagnostics.Type_0_is_not_assignable_to_type_1_with_exactOptionalPropertyTypes_Colon_true_Consider_adding_undefined_to_the_type_of_the_target - } - } - // to avoid cascading errors check assignability only if 'isReference' check succeeded and no errors were reported - c.checkTypeAssignableToAndOptionallyElaborate(rightType, leftType, left, right, headMessage, nil) - } - } -} - -func (c *Checker) bothAreBigIntLike(left *Type, right *Type) bool { - return c.isTypeAssignableToKind(left, TypeFlagsBigIntLike) && c.isTypeAssignableToKind(right, TypeFlagsBigIntLike) -} - -func (c *Checker) getSuggestedBooleanOperator(operator ast.Kind) ast.Kind { - switch operator { - case ast.KindBarToken, ast.KindBarEqualsToken: - return ast.KindBarBarToken - case ast.KindCaretToken, ast.KindCaretEqualsToken: - return ast.KindExclamationEqualsEqualsToken - case ast.KindAmpersandToken, ast.KindAmpersandEqualsToken: - return ast.KindAmpersandAmpersandToken - } - return ast.KindUnknown -} - -func (c *Checker) checkArithmeticOperandType(operand *ast.Node, t *Type, diagnostic *diagnostics.Message, isAwaitValid bool) bool { - if !c.isTypeAssignableTo(t, c.numberOrBigIntType) { - var awaitedType *Type - if isAwaitValid { - awaitedType = c.getAwaitedTypeOfPromise(t) - } - c.errorAndMaybeSuggestAwait(operand, awaitedType != nil && c.isTypeAssignableTo(awaitedType, c.numberOrBigIntType), diagnostic) - return false - } - return true -} - -// Return true if there was no error, false if there was an error. -func (c *Checker) checkForDisallowedESSymbolOperand(left *ast.Node, right *ast.Node, leftType *Type, rightType *Type, operator ast.Kind) bool { - var offendingSymbolOperand *ast.Node - switch { - case c.maybeTypeOfKindConsideringBaseConstraint(leftType, TypeFlagsESSymbolLike): - offendingSymbolOperand = left - case c.maybeTypeOfKindConsideringBaseConstraint(rightType, TypeFlagsESSymbolLike): - offendingSymbolOperand = right - } - if offendingSymbolOperand != nil { - c.error(offendingSymbolOperand, diagnostics.The_0_operator_cannot_be_applied_to_type_symbol, scanner.TokenToString(operator)) - return false - } - return true -} - -func (c *Checker) checkNaNEquality(errorNode *ast.Node, operator ast.Kind, left *ast.Expression, right *ast.Expression) { - isLeftNaN := c.isGlobalNaN(ast.SkipParentheses(left)) - isRightNaN := c.isGlobalNaN(ast.SkipParentheses(right)) - if isLeftNaN || isRightNaN { - err := c.error(errorNode, diagnostics.This_condition_will_always_return_0, scanner.TokenToString(core.IfElse(operator == ast.KindEqualsEqualsEqualsToken || operator == ast.KindEqualsEqualsToken, ast.KindFalseKeyword, ast.KindTrueKeyword))) - if isLeftNaN && isRightNaN { - return - } - var operatorString string - if operator == ast.KindExclamationEqualsEqualsToken || operator == ast.KindExclamationEqualsToken { - operatorString = scanner.TokenToString(ast.KindExclamationToken) - } - location := left - if isLeftNaN { - location = right - } - expression := ast.SkipParentheses(location) - entityName := "..." - if ast.IsEntityNameExpression(expression) { - entityName = entityNameToString(expression) - } - suggestion := operatorString + "Number.isNaN(" + entityName + ")" - err.AddRelatedInfo(createDiagnosticForNode(location, diagnostics.Did_you_mean_0, suggestion)) - } -} - -func (c *Checker) isGlobalNaN(expr *ast.Expression) bool { - if ast.IsIdentifier(expr) && expr.Text() == "NaN" { - globalNaNSymbol := c.getGlobalNaNSymbolOrNil() - return globalNaNSymbol != nil && globalNaNSymbol == c.getResolvedSymbol(expr) - } - return false -} - -func (c *Checker) isTypeEqualityComparableTo(source *Type, target *Type) bool { - return (target.flags&TypeFlagsNullable) != 0 || c.isTypeComparableTo(source, target) -} - -func (c *Checker) checkTruthinessOfType(t *Type, node *ast.Node) *Type { - if t.flags&TypeFlagsVoid != 0 { - c.error(node, diagnostics.An_expression_of_type_void_cannot_be_tested_for_truthiness) - return t - } - semantics := c.getSyntacticTruthySemantics(node) - if semantics != PredicateSemanticsSometimes { - c.error(node, core.IfElse(semantics == PredicateSemanticsAlways, diagnostics.This_kind_of_expression_is_always_truthy, diagnostics.This_kind_of_expression_is_always_falsy)) - } - return t -} - -type PredicateSemantics uint32 - -const ( - PredicateSemanticsNone PredicateSemantics = 0 - PredicateSemanticsAlways PredicateSemantics = 1 << 0 - PredicateSemanticsNever PredicateSemantics = 1 << 1 - PredicateSemanticsSometimes = PredicateSemanticsAlways | PredicateSemanticsNever -) - -func (c *Checker) getSyntacticTruthySemantics(node *ast.Node) PredicateSemantics { - node = ast.SkipOuterExpressions(node, ast.OEKAll) - switch node.Kind { - case ast.KindNumericLiteral: - // Allow `while(0)` or `while(1)` - if node.Text() == "0" || node.Text() == "1" { - return PredicateSemanticsSometimes - } - return PredicateSemanticsAlways - case ast.KindArrayLiteralExpression, ast.KindArrowFunction, ast.KindBigIntLiteral, ast.KindClassExpression, ast.KindFunctionExpression, - ast.KindJsxElement, ast.KindJsxSelfClosingElement, ast.KindObjectLiteralExpression, ast.KindRegularExpressionLiteral: - return PredicateSemanticsAlways - case ast.KindVoidExpression, ast.KindNullKeyword: - return PredicateSemanticsNever - case ast.KindNoSubstitutionTemplateLiteral, ast.KindStringLiteral: - if node.Text() != "" { - return PredicateSemanticsAlways - } - return PredicateSemanticsNever - case ast.KindConditionalExpression: - return c.getSyntacticTruthySemantics(node.AsConditionalExpression().WhenTrue) | c.getSyntacticTruthySemantics(node.AsConditionalExpression().WhenFalse) - case ast.KindIdentifier: - if c.getResolvedSymbol(node) == c.undefinedSymbol { - return PredicateSemanticsNever - } - } - return PredicateSemanticsSometimes -} - -func (c *Checker) checkNullishCoalesceOperands(left *ast.Node, right *ast.Node) { - if ast.IsBinaryExpression(left.Parent.Parent) { - grandparentLeft := left.Parent.Parent.AsBinaryExpression().Left - grandparentOperatorToken := left.Parent.Parent.AsBinaryExpression().OperatorToken - if ast.IsBinaryExpression(grandparentLeft) && grandparentOperatorToken.Kind == ast.KindBarBarToken { - c.grammarErrorOnNode(grandparentLeft, diagnostics.X_0_and_1_operations_cannot_be_mixed_without_parentheses, scanner.TokenToString(ast.KindQuestionQuestionToken), scanner.TokenToString(grandparentOperatorToken.Kind)) - } - } else if ast.IsBinaryExpression(left) { - operatorToken := left.AsBinaryExpression().OperatorToken - if operatorToken.Kind == ast.KindBarBarToken || operatorToken.Kind == ast.KindAmpersandAmpersandToken { - c.grammarErrorOnNode(left, diagnostics.X_0_and_1_operations_cannot_be_mixed_without_parentheses, scanner.TokenToString(operatorToken.Kind), scanner.TokenToString(ast.KindQuestionQuestionToken)) - } - } else if ast.IsBinaryExpression(right) { - operatorToken := right.AsBinaryExpression().OperatorToken - if operatorToken.Kind == ast.KindAmpersandAmpersandToken { - c.grammarErrorOnNode(right, diagnostics.X_0_and_1_operations_cannot_be_mixed_without_parentheses, scanner.TokenToString(ast.KindQuestionQuestionToken), scanner.TokenToString(operatorToken.Kind)) - } - } - c.checkNullishCoalesceOperandLeft(left) - c.checkNullishCoalesceOperandRight(right) -} - -func (c *Checker) checkNullishCoalesceOperandLeft(left *ast.Node) { - leftTarget := ast.SkipOuterExpressions(left, ast.OEKAll) - nullishSemantics := c.getSyntacticNullishnessSemantics(leftTarget) - if nullishSemantics != PredicateSemanticsSometimes { - if nullishSemantics == PredicateSemanticsAlways { - c.error(leftTarget, diagnostics.This_expression_is_always_nullish) - } else { - c.error(leftTarget, diagnostics.Right_operand_of_is_unreachable_because_the_left_operand_is_never_nullish) - } - } -} - -func (c *Checker) checkNullishCoalesceOperandRight(right *ast.Node) { - binaryExpression := right.Parent - if binaryExpression.Parent != nil && ast.IsBinaryExpression(binaryExpression.Parent) && binaryExpression.Parent.AsBinaryExpression().OperatorToken.Kind == ast.KindQuestionQuestionToken { - rightTarget := ast.SkipOuterExpressions(right, ast.OEKAll) - nullishSemantics := c.getSyntacticNullishnessSemantics(rightTarget) - switch nullishSemantics { - case PredicateSemanticsAlways: - c.error(rightTarget, diagnostics.This_expression_is_always_nullish) - case PredicateSemanticsNever: - c.error(rightTarget, diagnostics.This_expression_is_never_nullish) - } - } -} - -func (c *Checker) getSyntacticNullishnessSemantics(node *ast.Node) PredicateSemantics { - node = ast.SkipOuterExpressions(node, ast.OEKAll) - switch node.Kind { - case ast.KindAwaitExpression, - ast.KindCallExpression, - ast.KindTaggedTemplateExpression, - ast.KindElementAccessExpression, - ast.KindMetaProperty, - ast.KindNewExpression, - ast.KindPropertyAccessExpression, - ast.KindYieldExpression, - ast.KindThisKeyword: - return PredicateSemanticsSometimes - case ast.KindBinaryExpression: - // List of operators that can produce null/undefined: - // = ??= ?? || ||= && &&= - switch node.AsBinaryExpression().OperatorToken.Kind { - case ast.KindEqualsToken, - ast.KindQuestionQuestionToken, - ast.KindQuestionQuestionEqualsToken, - ast.KindBarBarToken, - ast.KindBarBarEqualsToken, - ast.KindAmpersandAmpersandToken, - ast.KindAmpersandAmpersandEqualsToken: - return PredicateSemanticsSometimes - case ast.KindCommaToken: - return c.getSyntacticNullishnessSemantics(node.AsBinaryExpression().Right) - } - return PredicateSemanticsNever - case ast.KindConditionalExpression: - return c.getSyntacticNullishnessSemantics(node.AsConditionalExpression().WhenTrue) | c.getSyntacticNullishnessSemantics(node.AsConditionalExpression().WhenFalse) - case ast.KindNullKeyword: - return PredicateSemanticsAlways - case ast.KindIdentifier: - if c.getResolvedSymbol(node) == c.undefinedSymbol { - return PredicateSemanticsAlways - } - return PredicateSemanticsSometimes - } - return PredicateSemanticsNever -} - -/** - * This is a *shallow* check: An expression is side-effect-free if the - * evaluation of the expression *itself* cannot produce side effects. - * For example, x++ / 3 is side-effect free because the / operator - * does not have side effects. - * The intent is to "smell test" an expression for correctness in positions where - * its value is discarded (e.g. the left side of the comma operator). - */ -func (c *Checker) isSideEffectFree(node *ast.Node) bool { - node = ast.SkipParentheses(node) - switch node.Kind { - case ast.KindIdentifier, ast.KindStringLiteral, ast.KindRegularExpressionLiteral, ast.KindTaggedTemplateExpression, ast.KindTemplateExpression, - ast.KindNoSubstitutionTemplateLiteral, ast.KindNumericLiteral, ast.KindBigIntLiteral, ast.KindTrueKeyword, ast.KindFalseKeyword, - ast.KindNullKeyword, ast.KindUndefinedKeyword, ast.KindFunctionExpression, ast.KindClassExpression, ast.KindArrowFunction, - ast.KindArrayLiteralExpression, ast.KindObjectLiteralExpression, ast.KindTypeOfExpression, ast.KindNonNullExpression, ast.KindJsxSelfClosingElement, - ast.KindJsxElement: - return true - case ast.KindConditionalExpression: - return c.isSideEffectFree(node.AsConditionalExpression().WhenTrue) && c.isSideEffectFree(node.AsConditionalExpression().WhenFalse) - case ast.KindBinaryExpression: - if ast.IsAssignmentOperator(node.AsBinaryExpression().OperatorToken.Kind) { - return false - } - return c.isSideEffectFree(node.AsBinaryExpression().Left) && c.isSideEffectFree(node.AsBinaryExpression().Right) - case ast.KindPrefixUnaryExpression: - // Unary operators ~, !, +, and - have no side effects. - // The rest do. - switch node.AsPrefixUnaryExpression().Operator { - case ast.KindExclamationToken, ast.KindPlusToken, ast.KindMinusToken, ast.KindTildeToken: - return true - } - } - return false -} - -// Return true for "indirect calls", (i.e. `(0, x.f)(...)` or `(0, eval)(...)`), which prevents passing `this`. -func (c *Checker) isIndirectCall(node *ast.Node) bool { - left := node.AsBinaryExpression().Left - right := node.AsBinaryExpression().Right - return ast.IsParenthesizedExpression(node.Parent) && ast.IsNumericLiteral(left) && left.Text() == "0" && - (ast.IsCallExpression(node.Parent.Parent) && node.Parent.Parent.Expression() == node.Parent || - ast.IsTaggedTemplateExpression(node.Parent.Parent) && (ast.IsAccessExpression(right) || ast.IsIdentifier(right) && right.Text() == "eval")) -} - -func (c *Checker) checkInstanceOfExpression(left *ast.Expression, right *ast.Expression, leftType *Type, rightType *Type, checkMode CheckMode) *Type { - if leftType == c.silentNeverType || rightType == c.silentNeverType { - return c.silentNeverType - } - // TypeScript 1.0 spec (April 2014): 4.15.4 - // The instanceof operator requires the left operand to be of type Any, an object type, or a type parameter type, - // and the right operand to be of type Any, a subtype of the 'Function' interface type, or have a call or construct signature. - // The result is always of the Boolean primitive type. - // NOTE: do not raise error if leftType is unknown as related error was already reported - if !IsTypeAny(leftType) && c.allTypesAssignableToKind(leftType, TypeFlagsPrimitive) { - c.error(left, diagnostics.The_left_hand_side_of_an_instanceof_expression_must_be_of_type_any_an_object_type_or_a_type_parameter) - } - signature := c.getResolvedSignature(left.Parent, nil /*candidatesOutArray*/, checkMode) - if signature == c.resolvingSignature { - // CheckMode.SkipGenericFunctions is enabled and this is a call to a generic function that - // returns a function type. We defer checking and return silentNeverType. - return c.silentNeverType - } - // If rightType has a `[Symbol.hasInstance]` method that is not `(value: unknown) => boolean`, we - // must check the expression as if it were a call to `right[Symbol.hasInstance](left)`. The call to - // `getResolvedSignature`, below, will check that leftType is assignable to the type of the first - // parameter. - returnType := c.getReturnTypeOfSignature(signature) - // We also verify that the return type of the `[Symbol.hasInstance]` method is assignable to - // `boolean`. According to the spec, the runtime will actually perform `ToBoolean` on the result, - // but this is more type-safe. - c.checkTypeAssignableTo(returnType, c.booleanType, right, diagnostics.An_object_s_Symbol_hasInstance_method_must_return_a_boolean_value_for_it_to_be_used_on_the_right_hand_side_of_an_instanceof_expression) - return c.booleanType -} - -func (c *Checker) checkInExpression(left *ast.Expression, right *ast.Expression, leftType *Type, rightType *Type) *Type { - if leftType == c.silentNeverType || rightType == c.silentNeverType { - return c.silentNeverType - } - if ast.IsPrivateIdentifier(left) { - // Unlike in 'checkPrivateIdentifierExpression' we now have access to the RHS type - // which provides us with the opportunity to emit more detailed errors - if c.symbolNodeLinks.Get(left).resolvedSymbol == nil && ast.GetContainingClass(left) != nil { - c.reportNonexistentProperty(left, rightType) - } - } else { - // The type of the left operand must be assignable to string, number, or symbol. - c.checkTypeAssignableTo(c.checkNonNullType(leftType, left), c.stringNumberSymbolType, left, nil) - } - // The type of the right operand must be assignable to 'object'. - if c.checkTypeAssignableTo(c.checkNonNullType(rightType, right), c.nonPrimitiveType, right, nil) { - // The {} type is assignable to the object type, yet {} might represent a primitive type. Here we - // detect and error on {} that results from narrowing the unknown type, as well as intersections - // that include {} (we know that the other types in such intersections are assignable to object - // since we already checked for that). - if c.hasEmptyObjectIntersection(rightType) { - c.error(right, diagnostics.Type_0_may_represent_a_primitive_value_which_is_not_permitted_as_the_right_operand_of_the_in_operator, c.TypeToString(rightType)) - } - } - // The result is always of the Boolean primitive type. - return c.booleanType -} - -func (c *Checker) hasEmptyObjectIntersection(t *Type) bool { - return someType(t, func(t *Type) bool { - return t == c.unknownEmptyObjectType || t.flags&TypeFlagsIntersection != 0 && c.IsEmptyAnonymousObjectType(c.getBaseConstraintOrType(t)) - }) -} - -func (c *Checker) getExactOptionalUnassignableProperties(source *Type, target *Type) []*ast.Symbol { - if isTupleType(source) && isTupleType(target) { - return nil - } - return core.Filter(c.getPropertiesOfType(target), func(targetProp *ast.Symbol) bool { - return c.isExactOptionalPropertyMismatch(c.getTypeOfPropertyOfType(source, targetProp.Name), c.getTypeOfSymbol(targetProp)) - }) -} - -func (c *Checker) isExactOptionalPropertyMismatch(source *Type, target *Type) bool { - return source != nil && target != nil && c.maybeTypeOfKind(source, TypeFlagsUndefined) && c.containsMissingType(target) -} - -func (c *Checker) checkReferenceExpression(expr *ast.Node, invalidReferenceMessage *diagnostics.Message, invalidOptionalChainMessage *diagnostics.Message) bool { - // References are combinations of identifiers, parentheses, and property accesses. - node := ast.SkipOuterExpressions(expr, ast.OEKAssertions|ast.OEKParentheses) - if node.Kind != ast.KindIdentifier && !ast.IsAccessExpression(node) { - c.error(expr, invalidReferenceMessage) - return false - } - if node.Flags&ast.NodeFlagsOptionalChain != 0 { - c.error(expr, invalidOptionalChainMessage) - return false - } - return true -} - -func (c *Checker) checkObjectLiteral(node *ast.Node, checkMode CheckMode) *Type { - inDestructuringPattern := ast.IsAssignmentTarget(node) - // Grammar checking - c.checkGrammarObjectLiteralExpression(node.AsObjectLiteralExpression(), inDestructuringPattern) - var allPropertiesTable ast.SymbolTable - if c.strictNullChecks { - allPropertiesTable = make(ast.SymbolTable) - } - propertiesTable := make(ast.SymbolTable) - var propertiesArray []*ast.Symbol - spread := c.emptyObjectType - c.pushCachedContextualType(node) - contextualType := c.getApparentTypeOfContextualType(node, ContextFlagsNone) - var contextualTypeHasPattern bool - if contextualType != nil { - if pattern := c.patternForType[contextualType]; pattern != nil && (ast.IsObjectBindingPattern(pattern) || ast.IsObjectLiteralExpression(pattern)) { - contextualTypeHasPattern = true - } - } - inConstContext := c.isConstContext(node) - var checkFlags ast.CheckFlags - if inConstContext { - checkFlags = ast.CheckFlagsReadonly - } - objectFlags := ObjectFlagsFreshLiteral - patternWithComputedProperties := false - hasComputedStringProperty := false - hasComputedNumberProperty := false - hasComputedSymbolProperty := false - // Spreads may cause an early bail; ensure computed names are always checked (this is cached) - // As otherwise they may not be checked until exports for the type at this position are retrieved, - // which may never occur. - for _, elem := range node.AsObjectLiteralExpression().Properties.Nodes { - if elem.Name() != nil && ast.IsComputedPropertyName(elem.Name()) { - c.checkComputedPropertyName(elem.Name()) - } - } - offset := 0 - createObjectLiteralType := func() *Type { - var indexInfos []*IndexInfo - isReadonly := c.isConstContext(node) - if hasComputedStringProperty { - indexInfos = append(indexInfos, c.getObjectLiteralIndexInfo(isReadonly, propertiesArray[offset:], c.stringType)) - } - if hasComputedNumberProperty { - indexInfos = append(indexInfos, c.getObjectLiteralIndexInfo(isReadonly, propertiesArray[offset:], c.numberType)) - } - if hasComputedSymbolProperty { - indexInfos = append(indexInfos, c.getObjectLiteralIndexInfo(isReadonly, propertiesArray[offset:], c.esSymbolType)) - } - result := c.newAnonymousType(node.Symbol(), propertiesTable, nil, nil, indexInfos) - result.objectFlags |= objectFlags | ObjectFlagsObjectLiteral | ObjectFlagsContainsObjectOrArrayLiteral - if patternWithComputedProperties { - result.objectFlags |= ObjectFlagsObjectLiteralPatternWithComputedProperties - } - if inDestructuringPattern { - c.patternForType[result] = node - } - return result - } - // expando object literals have empty properties but filled exports -- skip straight to type creation - if len(node.AsObjectLiteralExpression().Properties.Nodes) == 0 && node.Symbol() != nil && len(node.Symbol().Exports) > 0 { - propertiesTable = node.Symbol().Exports - return createObjectLiteralType() - } - for _, memberDecl := range node.AsObjectLiteralExpression().Properties.Nodes { - member := c.getSymbolOfDeclaration(memberDecl) - var computedNameType *Type - if memberDecl.Name() != nil && memberDecl.Name().Kind == ast.KindComputedPropertyName { - computedNameType = c.checkComputedPropertyName(memberDecl.Name()) - } - if ast.IsPropertyAssignment(memberDecl) || ast.IsShorthandPropertyAssignment(memberDecl) || ast.IsObjectLiteralMethod(memberDecl) { - var t *Type - switch memberDecl.Kind { - case ast.KindPropertyAssignment: - t = c.checkPropertyAssignment(memberDecl, checkMode) - case ast.KindShorthandPropertyAssignment: - t = c.checkShorthandPropertyAssignment(memberDecl, inDestructuringPattern, checkMode) - default: - t = c.checkObjectLiteralMethod(memberDecl, checkMode) - } - objectFlags |= t.objectFlags & ObjectFlagsPropagatingFlags - var nameType *Type - if computedNameType != nil && isTypeUsableAsPropertyName(computedNameType) { - nameType = computedNameType - } - var prop *ast.Symbol - if nameType != nil { - prop = c.newSymbolEx(ast.SymbolFlagsProperty|member.Flags, getPropertyNameFromType(nameType), checkFlags|ast.CheckFlagsLate) - } else { - prop = c.newSymbolEx(ast.SymbolFlagsProperty|member.Flags, member.Name, checkFlags) - } - links := c.valueSymbolLinks.Get(prop) - if nameType != nil { - links.nameType = nameType - } - if inDestructuringPattern && c.hasDefaultValue(memberDecl) { - // If object literal is an assignment pattern and if the assignment pattern specifies a default value - // for the property, make the property optional. - prop.Flags |= ast.SymbolFlagsOptional - } else if contextualTypeHasPattern && contextualType.objectFlags&ObjectFlagsObjectLiteralPatternWithComputedProperties == 0 { - // If object literal is contextually typed by the implied type of a binding pattern, and if the - // binding pattern specifies a default value for the property, make the property optional. - impliedProp := c.getPropertyOfType(contextualType, member.Name) - if impliedProp != nil { - prop.Flags |= impliedProp.Flags & ast.SymbolFlagsOptional - } else if c.getIndexInfoOfType(contextualType, c.stringType) == nil { - c.error(memberDecl.Name(), diagnostics.Object_literal_may_only_specify_known_properties_and_0_does_not_exist_in_type_1, c.symbolToString(member), c.TypeToString(contextualType)) - } - } - prop.Declarations = member.Declarations - prop.Parent = member.Parent - prop.ValueDeclaration = member.ValueDeclaration - links.resolvedType = t - links.target = member - member = prop - if allPropertiesTable != nil { - allPropertiesTable[prop.Name] = prop - } - if contextualType != nil && checkMode&CheckModeInferential != 0 && checkMode&CheckModeSkipContextSensitive == 0 && (ast.IsPropertyAssignment(memberDecl) || ast.IsMethodDeclaration(memberDecl)) && c.isContextSensitive(memberDecl) { - inferenceContext := c.getInferenceContext(node) - // In CheckMode.Inferential we should always have an inference context - inferenceNode := memberDecl - if ast.IsPropertyAssignment(memberDecl) { - inferenceNode = memberDecl.Initializer() - } - c.addIntraExpressionInferenceSite(inferenceContext, inferenceNode, t) - } - } else if memberDecl.Kind == ast.KindSpreadAssignment { - if len(propertiesArray) > 0 { - spread = c.getSpreadType(spread, createObjectLiteralType(), node.Symbol(), objectFlags, inConstContext) - propertiesArray = nil - propertiesTable = make(ast.SymbolTable) - hasComputedStringProperty = false - hasComputedNumberProperty = false - hasComputedSymbolProperty = false - } - t := c.getReducedType(c.checkExpressionEx(memberDecl.Expression(), checkMode&CheckModeInferential)) - if c.isValidSpreadType(t) { - mergedType := c.tryMergeUnionOfObjectTypeAndEmptyObject(t, inConstContext) - if allPropertiesTable != nil { - c.checkSpreadPropOverrides(mergedType, allPropertiesTable, memberDecl) - } - offset = len(propertiesArray) - if c.isErrorType(spread) { - continue - } - spread = c.getSpreadType(spread, mergedType, node.Symbol(), objectFlags, inConstContext) - } else { - c.error(memberDecl, diagnostics.Spread_types_may_only_be_created_from_object_types) - spread = c.errorType - } - continue - } else { - // TypeScript 1.0 spec (April 2014) - // A get accessor declaration is processed in the same manner as - // an ordinary function declaration(section 6.1) with no parameters. - // A set accessor declaration is processed in the same manner - // as an ordinary function declaration with a single parameter and a Void return type. - debug.Assert(memberDecl.Kind == ast.KindGetAccessor || memberDecl.Kind == ast.KindSetAccessor) - c.checkNodeDeferred(memberDecl) - } - if computedNameType != nil && computedNameType.flags&TypeFlagsStringOrNumberLiteralOrUnique == 0 { - if c.isTypeAssignableTo(computedNameType, c.stringNumberSymbolType) { - if c.isTypeAssignableTo(computedNameType, c.numberType) { - hasComputedNumberProperty = true - } else if c.isTypeAssignableTo(computedNameType, c.esSymbolType) { - hasComputedSymbolProperty = true - } else { - hasComputedStringProperty = true - } - if inDestructuringPattern { - patternWithComputedProperties = true - } - } - } else { - propertiesTable[member.Name] = member - } - propertiesArray = append(propertiesArray, member) - } - c.popContextualType() - if c.isErrorType(spread) { - return c.errorType - } - if spread != c.emptyObjectType { - if len(propertiesArray) > 0 { - spread = c.getSpreadType(spread, createObjectLiteralType(), node.Symbol(), objectFlags, inConstContext) - propertiesArray = nil - propertiesTable = make(ast.SymbolTable) - hasComputedStringProperty = false - hasComputedNumberProperty = false - } - // remap the raw emptyObjectType fed in at the top into a fresh empty object literal type, unique to this use site - return c.mapType(spread, func(t *Type) *Type { - if t == c.emptyObjectType { - return createObjectLiteralType() - } - return t - }) - } - return createObjectLiteralType() -} - -func (c *Checker) checkSpreadPropOverrides(t *Type, props ast.SymbolTable, spread *ast.Node) { - for _, right := range c.getPropertiesOfType(t) { - if right.Flags&ast.SymbolFlagsOptional == 0 { - if left := props[right.Name]; left != nil { - diagnostic := c.error(left.ValueDeclaration, diagnostics.X_0_is_specified_more_than_once_so_this_usage_will_be_overwritten, left.Name) - diagnostic.AddRelatedInfo(NewDiagnosticForNode(spread, diagnostics.This_spread_always_overwrites_this_property)) - } - } - } -} - -/** - * Since the source of spread types are object literals, which are not binary, - * this function should be called in a left folding style, with left = previous result of getSpreadType - * and right = the new element to be spread. - */ -func (c *Checker) getSpreadType(left *Type, right *Type, symbol *ast.Symbol, objectFlags ObjectFlags, readonly bool) *Type { - if left.flags&TypeFlagsAny != 0 || right.flags&TypeFlagsAny != 0 { - return c.anyType - } - if left.flags&TypeFlagsUnknown != 0 || right.flags&TypeFlagsUnknown != 0 { - return c.unknownType - } - if left.flags&TypeFlagsNever != 0 { - return right - } - if right.flags&TypeFlagsNever != 0 { - return left - } - left = c.tryMergeUnionOfObjectTypeAndEmptyObject(left, readonly) - if left.flags&TypeFlagsUnion != 0 { - if c.checkCrossProductUnion([]*Type{left, right}) { - return c.mapType(left, func(t *Type) *Type { - return c.getSpreadType(t, right, symbol, objectFlags, readonly) - }) - } - return c.errorType - } - right = c.tryMergeUnionOfObjectTypeAndEmptyObject(right, readonly) - if right.flags&TypeFlagsUnion != 0 { - if c.checkCrossProductUnion([]*Type{left, right}) { - return c.mapType(right, func(t *Type) *Type { - return c.getSpreadType(left, t, symbol, objectFlags, readonly) - }) - } - return c.errorType - } - if right.flags&(TypeFlagsBooleanLike|TypeFlagsNumberLike|TypeFlagsBigIntLike|TypeFlagsStringLike|TypeFlagsEnumLike|TypeFlagsNonPrimitive|TypeFlagsIndex) != 0 { - return left - } - if c.isGenericObjectType(left) || c.isGenericObjectType(right) { - if c.isEmptyObjectType(left) { - return right - } - // When the left type is an intersection, we may need to merge the last constituent of the - // intersection with the right type. For example when the left type is 'T & { a: string }' - // and the right type is '{ b: string }' we produce 'T & { a: string, b: string }'. - if left.flags&TypeFlagsIntersection != 0 { - types := left.Types() - lastLeft := types[len(types)-1] - if c.isNonGenericObjectType(lastLeft) && c.isNonGenericObjectType(right) { - newTypes := slices.Clone(types) - newTypes[len(newTypes)-1] = c.getSpreadType(lastLeft, right, symbol, objectFlags, readonly) - return c.getIntersectionType(newTypes) - } - } - return c.getIntersectionType([]*Type{left, right}) - } - members := make(ast.SymbolTable) - var skippedPrivateMembers collections.Set[string] - var indexInfos []*IndexInfo - if left == c.emptyObjectType { - indexInfos = c.getIndexInfosOfType(right) - } else { - indexInfos = c.getUnionIndexInfos([]*Type{left, right}) - } - for _, rightProp := range c.getPropertiesOfType(right) { - if getDeclarationModifierFlagsFromSymbol(rightProp)&(ast.ModifierFlagsPrivate|ast.ModifierFlagsProtected) != 0 { - skippedPrivateMembers.Add(rightProp.Name) - } else if c.isSpreadableProperty(rightProp) { - members[rightProp.Name] = c.getSpreadSymbol(rightProp, readonly) - } - } - - for _, leftProp := range c.getPropertiesOfType(left) { - if skippedPrivateMembers.Has(leftProp.Name) || !c.isSpreadableProperty(leftProp) { - continue - } - if members[leftProp.Name] != nil { - rightProp := members[leftProp.Name] - rightType := c.getTypeOfSymbol(rightProp) - if rightProp.Flags&ast.SymbolFlagsOptional != 0 { - declarations := core.Concatenate(leftProp.Declarations, rightProp.Declarations) - flags := ast.SymbolFlagsProperty | (leftProp.Flags & ast.SymbolFlagsOptional) - result := c.newSymbol(flags, leftProp.Name) - links := c.valueSymbolLinks.Get(result) - // Optimization: avoid calculating the union type if spreading into the exact same type. - // This is common, e.g. spreading one options bag into another where the bags have the - // same type, or have properties which overlap. If the unions are large, it may turn out - // to be expensive to perform subtype reduction. - leftType := c.getTypeOfSymbol(leftProp) - leftTypeWithoutUndefined := c.removeMissingOrUndefinedType(leftType) - rightTypeWithoutUndefined := c.removeMissingOrUndefinedType(rightType) - if leftTypeWithoutUndefined == rightTypeWithoutUndefined { - links.resolvedType = leftType - } else { - links.resolvedType = c.getUnionTypeEx([]*Type{leftType, rightTypeWithoutUndefined}, UnionReductionSubtype, nil, nil) - } - c.spreadLinks.Get(result).leftSpread = leftProp - c.spreadLinks.Get(result).rightSpread = rightProp - result.Declarations = declarations - links.nameType = c.valueSymbolLinks.Get(leftProp).nameType - members[leftProp.Name] = result - } - } else { - members[leftProp.Name] = c.getSpreadSymbol(leftProp, readonly) - } - } - spreadIndexInfos := core.SameMap(indexInfos, func(info *IndexInfo) *IndexInfo { - return c.getIndexInfoWithReadonly(info, readonly) - }) - spread := c.newAnonymousType(symbol, members, nil, nil, spreadIndexInfos) - spread.objectFlags |= ObjectFlagsObjectLiteral | ObjectFlagsContainsObjectOrArrayLiteral | ObjectFlagsContainsSpread | objectFlags - return spread -} - -func (c *Checker) getIndexInfoWithReadonly(info *IndexInfo, readonly bool) *IndexInfo { - if info.isReadonly != readonly { - return c.newIndexInfo(info.keyType, info.valueType, readonly, info.declaration, info.components) - } - return info -} - -func (c *Checker) isValidSpreadType(t *Type) bool { - s := c.removeDefinitelyFalsyTypes(c.mapType(t, c.getBaseConstraintOrType)) - return s.flags&(TypeFlagsAny|TypeFlagsNonPrimitive|TypeFlagsObject|TypeFlagsInstantiableNonPrimitive) != 0 || - s.flags&TypeFlagsUnionOrIntersection != 0 && core.Every(s.Types(), c.isValidSpreadType) -} - -func (c *Checker) getUnionIndexInfos(types []*Type) []*IndexInfo { - sourceInfos := c.getIndexInfosOfType(types[0]) - var result []*IndexInfo - for _, info := range sourceInfos { - indexType := info.keyType - if core.Every(types, func(t *Type) bool { return c.getIndexInfoOfType(t, indexType) != nil }) { - valueType := c.getUnionType(core.Map(types, func(t *Type) *Type { - return c.getIndexTypeOfType(t, indexType) - })) - isReadonly := core.Some(types, func(t *Type) bool { return c.getIndexInfoOfType(t, indexType).isReadonly }) - result = append(result, c.newIndexInfo(indexType, valueType, isReadonly, nil, nil)) - } - } - return result -} - -func (c *Checker) isNonGenericObjectType(t *Type) bool { - return t.flags&TypeFlagsObject != 0 && !c.isGenericMappedType(t) -} - -func (c *Checker) tryMergeUnionOfObjectTypeAndEmptyObject(t *Type, readonly bool) *Type { - if t.flags&TypeFlagsUnion == 0 { - return t - } - if core.Every(t.Types(), c.isEmptyObjectTypeOrSpreadsIntoEmptyObject) { - empty := core.Find(t.Types(), c.isEmptyObjectType) - if empty != nil { - return empty - } - return c.emptyObjectType - } - firstType := core.Find(t.Types(), func(t *Type) bool { - return !c.isEmptyObjectTypeOrSpreadsIntoEmptyObject(t) - }) - if firstType == nil { - return t - } - secondType := core.Find(t.Types(), func(t *Type) bool { - return t != firstType && !c.isEmptyObjectTypeOrSpreadsIntoEmptyObject(t) - }) - if secondType != nil { - return t - } - // gets the type as if it had been spread, but where everything in the spread is made optional - members := make(ast.SymbolTable) - for _, prop := range c.getPropertiesOfType(firstType) { - if getDeclarationModifierFlagsFromSymbol(prop)&(ast.ModifierFlagsPrivate|ast.ModifierFlagsProtected) != 0 { - // do nothing, skip privates - } else if c.isSpreadableProperty(prop) { - isSetonlyAccessor := prop.Flags&ast.SymbolFlagsSetAccessor != 0 && prop.Flags&ast.SymbolFlagsGetAccessor == 0 - flags := ast.SymbolFlagsProperty | ast.SymbolFlagsOptional - result := c.newSymbolEx(flags, prop.Name, prop.CheckFlags&ast.CheckFlagsLate|(core.IfElse(readonly, ast.CheckFlagsReadonly, 0))) - links := c.valueSymbolLinks.Get(result) - if isSetonlyAccessor { - links.resolvedType = c.undefinedType - } else { - links.resolvedType = c.addOptionalityEx(c.getTypeOfSymbol(prop), true /*isProperty*/, true /*isOptional*/) - } - result.Declarations = prop.Declarations - links.nameType = c.valueSymbolLinks.Get(prop).nameType - c.mappedSymbolLinks.Get(result).syntheticOrigin = prop - members[prop.Name] = result - } - } - spread := c.newAnonymousType(firstType.symbol, members, nil, nil, c.getIndexInfosOfType(firstType)) - spread.objectFlags |= ObjectFlagsObjectLiteral | ObjectFlagsContainsObjectOrArrayLiteral - return spread -} - -// We approximate own properties as non-methods plus methods that are inside the object literal -func (c *Checker) isSpreadableProperty(prop *ast.Symbol) bool { - return !core.Some(prop.Declarations, ast.IsPrivateIdentifierClassElementDeclaration) && prop.Flags&(ast.SymbolFlagsMethod|ast.SymbolFlagsGetAccessor|ast.SymbolFlagsSetAccessor) == 0 || - !core.Some(prop.Declarations, func(d *ast.Node) bool { return d.Parent != nil && ast.IsClassLike(d.Parent) }) -} - -func (c *Checker) getSpreadSymbol(prop *ast.Symbol, readonly bool) *ast.Symbol { - isSetonlyAccessor := prop.Flags&ast.SymbolFlagsSetAccessor != 0 && prop.Flags&ast.SymbolFlagsGetAccessor == 0 - if !isSetonlyAccessor && readonly == c.isReadonlySymbol(prop) { - return prop - } - flags := ast.SymbolFlagsProperty | (prop.Flags & ast.SymbolFlagsOptional) - result := c.newSymbolEx(flags, prop.Name, prop.CheckFlags&ast.CheckFlagsLate|(core.IfElse(readonly, ast.CheckFlagsReadonly, 0))) - links := c.valueSymbolLinks.Get(result) - if isSetonlyAccessor { - links.resolvedType = c.undefinedType - } else { - links.resolvedType = c.getTypeOfSymbol(prop) - } - result.Declarations = prop.Declarations - links.nameType = c.valueSymbolLinks.Get(prop).nameType - c.mappedSymbolLinks.Get(result).syntheticOrigin = prop - return result -} - -func (c *Checker) isEmptyObjectTypeOrSpreadsIntoEmptyObject(t *Type) bool { - return c.isEmptyObjectType(t) || t.flags&(TypeFlagsNull|TypeFlagsUndefined|TypeFlagsBooleanLike|TypeFlagsNumberLike|TypeFlagsBigIntLike|TypeFlagsStringLike|TypeFlagsEnumLike|TypeFlagsNonPrimitive|TypeFlagsIndex) != 0 -} - -func (c *Checker) hasDefaultValue(node *ast.Node) bool { - return ast.IsBindingElement(node) && node.Initializer() != nil || - ast.IsPropertyAssignment(node) && c.hasDefaultValue(node.Initializer()) || - ast.IsShorthandPropertyAssignment(node) && node.AsShorthandPropertyAssignment().ObjectAssignmentInitializer != nil || - ast.IsBinaryExpression(node) && node.AsBinaryExpression().OperatorToken.Kind == ast.KindEqualsToken -} - -func (c *Checker) isConstContext(node *ast.Node) bool { - parent := node.Parent - return isConstAssertion(parent) || - c.isValidConstAssertionArgument(node) && c.isConstTypeVariable(c.getContextualType(node, ContextFlagsNone), 0) || - (ast.IsParenthesizedExpression(parent) || ast.IsArrayLiteralExpression(parent) || ast.IsSpreadElement(parent)) && c.isConstContext(parent) || - (ast.IsPropertyAssignment(parent) || ast.IsShorthandPropertyAssignment(parent) || ast.IsTemplateSpan(parent)) && c.isConstContext(parent.Parent) -} - -func (c *Checker) isValidConstAssertionArgument(node *ast.Node) bool { - switch node.Kind { - case ast.KindStringLiteral, ast.KindNoSubstitutionTemplateLiteral, ast.KindNumericLiteral, ast.KindBigIntLiteral, ast.KindTrueKeyword, - ast.KindFalseKeyword, ast.KindArrayLiteralExpression, ast.KindObjectLiteralExpression, ast.KindTemplateExpression: - return true - case ast.KindParenthesizedExpression: - return c.isValidConstAssertionArgument(node.Expression()) - case ast.KindPrefixUnaryExpression: - op := node.AsPrefixUnaryExpression().Operator - arg := node.AsPrefixUnaryExpression().Operand - return op == ast.KindMinusToken && (arg.Kind == ast.KindNumericLiteral || arg.Kind == ast.KindBigIntLiteral) || op == ast.KindPlusToken && arg.Kind == ast.KindNumericLiteral - case ast.KindPropertyAccessExpression, ast.KindElementAccessExpression: - expr := ast.SkipParentheses(node.Expression()) - var symbol *ast.Symbol - if ast.IsEntityNameExpression(expr) { - symbol = c.resolveEntityName(expr, ast.SymbolFlagsValue, true /*ignoreErrors*/, false, nil) - } - return symbol != nil && symbol.Flags&ast.SymbolFlagsEnum != 0 - } - return false -} - -func (c *Checker) isConstTypeVariable(t *Type, depth int) bool { - if depth >= 5 || t == nil { - return false - } - switch { - case t.flags&TypeFlagsTypeParameter != 0: - return t.symbol != nil && core.Some(t.symbol.Declarations, func(d *ast.Node) bool { return ast.HasSyntacticModifier(d, ast.ModifierFlagsConst) }) - case t.flags&TypeFlagsUnionOrIntersection != 0: - return core.Some(t.Types(), func(s *Type) bool { return c.isConstTypeVariable(s, depth) }) - case t.flags&TypeFlagsIndexedAccess != 0: - return c.isConstTypeVariable(t.AsIndexedAccessType().objectType, depth+1) - case t.flags&TypeFlagsConditional != 0: - return c.isConstTypeVariable(c.getConstraintOfConditionalType(t), depth+1) - case t.flags&TypeFlagsSubstitution != 0: - return c.isConstTypeVariable(t.AsSubstitutionType().baseType, depth) - case t.objectFlags&ObjectFlagsMapped != 0: - typeVariable := c.getHomomorphicTypeVariable(t) - return typeVariable != nil && c.isConstTypeVariable(typeVariable, depth) - case c.isGenericTupleType(t): - for i, s := range c.getElementTypes(t) { - if t.TargetTupleType().elementInfos[i].flags&ElementFlagsVariadic != 0 && c.isConstTypeVariable(s, depth) { - return true - } - } - } - return false -} - -func (c *Checker) checkPropertyAssignment(node *ast.Node, checkMode CheckMode) *Type { - // Do not use hasDynamicName here, because that returns false for well known symbols. - // We want to perform checkComputedPropertyName for all computed properties, including - // well known symbols. - if ast.IsComputedPropertyName(node.Name()) { - c.checkComputedPropertyName(node.Name()) - } - initializerType := c.checkExpressionForMutableLocation(node.Initializer(), checkMode) - if node.Type() != nil { - t := c.getTypeFromTypeNode(node.Type()) - c.checkTypeAssignableToAndOptionallyElaborate(initializerType, t, node, node.Initializer(), nil /*headMessage*/, nil) - return t - } - return initializerType -} - -func (c *Checker) checkShorthandPropertyAssignment(node *ast.Node, inDestructuringPattern bool, checkMode CheckMode) *Type { - var expr *ast.Node - if !inDestructuringPattern { - expr = node.AsShorthandPropertyAssignment().ObjectAssignmentInitializer - } - if expr == nil { - expr = node.Name() - } - expressionType := c.checkExpressionForMutableLocation(expr, checkMode) - if node.Type() != nil { - t := c.getTypeFromTypeNode(node.Type()) - c.checkTypeAssignableToAndOptionallyElaborate(expressionType, t, node, expr, nil /*headMessage*/, nil) - return t - } - return expressionType -} - -func (c *Checker) isInPropertyInitializerOrClassStaticBlock(node *ast.Node, ignoreArrowFunctions bool) bool { - return ast.FindAncestorOrQuit(node, func(node *ast.Node) ast.FindAncestorResult { - switch node.Kind { - case ast.KindPropertyDeclaration, ast.KindClassStaticBlockDeclaration: - return ast.FindAncestorTrue - case ast.KindTypeQuery, ast.KindJsxClosingElement: - return ast.FindAncestorQuit - case ast.KindArrowFunction: - return core.IfElse(ignoreArrowFunctions, ast.FindAncestorFalse, ast.FindAncestorQuit) - case ast.KindBlock: - return core.IfElse(ast.IsFunctionLikeDeclaration(node.Parent) && node.Parent.Kind != ast.KindArrowFunction, ast.FindAncestorQuit, ast.FindAncestorFalse) - default: - return ast.FindAncestorFalse - } - }) != nil -} - -func (c *Checker) getNarrowedTypeOfSymbol(symbol *ast.Symbol, location *ast.Node) *Type { - t := c.getTypeOfSymbol(symbol) - declaration := symbol.ValueDeclaration - if declaration != nil { - // If we have a non-rest binding element with no initializer declared as a const variable or a const-like - // parameter (a parameter for which there are no assignments in the function body), and if the parent type - // for the destructuring is a union type, one or more of the binding elements may represent discriminant - // properties, and we want the effects of conditional checks on such discriminants to affect the types of - // other binding elements from the same destructuring. Consider: - // - // type Action = - // | { kind: 'A', payload: number } - // | { kind: 'B', payload: string }; - // - // function f({ kind, payload }: Action) { - // if (kind === 'A') { - // payload.toFixed(); - // } - // if (kind === 'B') { - // payload.toUpperCase(); - // } - // } - // - // Above, we want the conditional checks on 'kind' to affect the type of 'payload'. To facilitate this, we use - // the binding pattern AST instance for '{ kind, payload }' as a pseudo-reference and narrow this reference - // as if it occurred in the specified location. We then recompute the narrowed binding element type by - // destructuring from the narrowed parent type. - if ast.IsBindingElement(declaration) && declaration.Initializer() == nil && !hasDotDotDotToken(declaration) && len(declaration.Parent.AsBindingPattern().Elements.Nodes) >= 2 { - parent := declaration.Parent.Parent - rootDeclaration := ast.GetRootDeclaration(parent) - if ast.IsVariableDeclaration(rootDeclaration) && c.getCombinedNodeFlagsCached(rootDeclaration)&ast.NodeFlagsConstant != 0 || ast.IsParameter(rootDeclaration) { - links := c.nodeLinks.Get(parent) - if links.flags&NodeCheckFlagsInCheckIdentifier == 0 { - links.flags |= NodeCheckFlagsInCheckIdentifier - parentType := c.getTypeForBindingElementParent(parent, CheckModeNormal) - var parentTypeConstraint *Type - if parentType != nil { - parentTypeConstraint = c.mapType(parentType, c.getBaseConstraintOrType) - } - links.flags &^= NodeCheckFlagsInCheckIdentifier - if parentTypeConstraint != nil && parentTypeConstraint.flags&TypeFlagsUnion != 0 && !(ast.IsParameter(rootDeclaration) && c.isSomeSymbolAssigned(rootDeclaration)) { - pattern := declaration.Parent - narrowedType := c.getFlowTypeOfReferenceEx(pattern, parentTypeConstraint, parentTypeConstraint, nil /*flowContainer*/, getFlowNodeOfNode(location)) - if narrowedType.flags&TypeFlagsNever != 0 { - return c.neverType - } - // Destructurings are validated against the parent type elsewhere. Here we disable tuple bounds - // checks because the narrowed type may have lower arity than the full parent type. For example, - // for the declaration [x, y]: [1, 2] | [3], we may have narrowed the parent type to just [3]. - return c.getBindingElementTypeFromParentType(declaration, narrowedType, true /*noTupleBoundsCheck*/) - } - } - } - } - // If we have a const-like parameter with no type annotation or initializer, and if the parameter is contextually - // typed by a signature with a single rest parameter of a union of tuple types, one or more of the parameters may - // represent discriminant tuple elements, and we want the effects of conditional checks on such discriminants to - // affect the types of other parameters in the same parameter list. Consider: - // - // type Action = [kind: 'A', payload: number] | [kind: 'B', payload: string]; - // - // const f: (...args: Action) => void = (kind, payload) => { - // if (kind === 'A') { - // payload.toFixed(); - // } - // if (kind === 'B') { - // payload.toUpperCase(); - // } - // } - // - // Above, we want the conditional checks on 'kind' to affect the type of 'payload'. To facilitate this, we use - // the arrow function AST node for '(kind, payload) => ...' as a pseudo-reference and narrow this reference as - // if it occurred in the specified location. We then recompute the narrowed parameter type by indexing into the - // narrowed tuple type. - if ast.IsParameter(declaration) && declaration.Type() == nil && declaration.Initializer() == nil && !hasDotDotDotToken(declaration) { - fn := declaration.Parent - if len(fn.Parameters()) >= 2 && c.isContextSensitiveFunctionOrObjectLiteralMethod(fn) { - contextualSignature := c.getContextualSignature(fn) - if contextualSignature != nil && len(contextualSignature.parameters) == 1 && signatureHasRestParameter(contextualSignature) { - var mapper *TypeMapper - context := c.getInferenceContext(fn) - if context != nil { - mapper = context.nonFixingMapper - } - restType := c.getReducedApparentType(c.instantiateType(c.getTypeOfSymbol(contextualSignature.parameters[0]), mapper)) - if restType.flags&TypeFlagsUnion != 0 && everyType(restType, isTupleType) && !core.Some(fn.Parameters(), c.isSomeSymbolAssigned) { - narrowedType := c.getFlowTypeOfReferenceEx(fn, restType, restType, nil /*flowContainer*/, getFlowNodeOfNode(location)) - index := slices.Index(fn.Parameters(), declaration) - (core.IfElse(ast.GetThisParameter(fn) != nil, 1, 0)) - return c.getIndexedAccessType(narrowedType, c.getNumberLiteralType(jsnum.Number(index))) - } - } - } - } - } - return t -} - -func (c *Checker) isReadonlySymbol(symbol *ast.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&ast.CheckFlagsReadonly != 0 || - symbol.Flags&ast.SymbolFlagsProperty != 0 && getDeclarationModifierFlagsFromSymbol(symbol)&ast.ModifierFlagsReadonly != 0 || - symbol.Flags&ast.SymbolFlagsVariable != 0 && c.getDeclarationNodeFlagsFromSymbol(symbol)&ast.NodeFlagsConstant != 0 || - symbol.Flags&ast.SymbolFlagsAccessor != 0 && symbol.Flags&ast.SymbolFlagsSetAccessor == 0 || - symbol.Flags&ast.SymbolFlagsEnumMember != 0 -} - -func (c *Checker) checkObjectLiteralMethod(node *ast.Node, checkMode CheckMode) *Type { - // Grammar checking - c.checkGrammarMethod(node) - // Do not use hasDynamicName here, because that returns false for well known symbols. - // We want to perform checkComputedPropertyName for all computed properties, including - // well known symbols. - if ast.IsComputedPropertyName(node.Name()) { - c.checkComputedPropertyName(node.Name()) - } - uninstantiatedType := c.checkFunctionExpressionOrObjectLiteralMethod(node, checkMode) - return c.instantiateTypeWithSingleGenericCallSignature(node, uninstantiatedType, checkMode) -} - -func (c *Checker) checkExpressionForMutableLocation(node *ast.Node, checkMode CheckMode) *Type { - t := c.checkExpressionEx(node, checkMode) - switch { - case c.isConstContext(node): - return c.getRegularTypeOfLiteralType(t) - case isTypeAssertion(node): - return t - default: - return c.getWidenedLiteralLikeTypeForContextualType(t, c.instantiateContextualType(c.getContextualType(node, ContextFlagsNone), node, ContextFlagsNone)) - } -} - -func (c *Checker) getResolvedSymbol(node *ast.Node) *ast.Symbol { - links := c.symbolNodeLinks.Get(node) - if links.resolvedSymbol == nil { - var symbol *ast.Symbol - if !ast.NodeIsMissing(node) { - symbol = c.resolveName(node, node.AsIdentifier().Text, ast.SymbolFlagsValue|ast.SymbolFlagsExportValue, - c.getCannotFindNameDiagnosticForName(node), !ast.IsWriteOnlyAccess(node), false /*excludeGlobals*/) - } - links.resolvedSymbol = core.OrElse(symbol, c.unknownSymbol) - } - return links.resolvedSymbol -} - -func (c *Checker) getResolvedSymbolOrNil(node *ast.Node) *ast.Symbol { - return c.symbolNodeLinks.Get(node).resolvedSymbol -} - -func (c *Checker) getCannotFindNameDiagnosticForName(node *ast.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 core.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 core.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 core.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 core.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", "ast.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 ast.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 == ast.KindShorthandPropertyAssignment { - 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) GetDiagnostics(ctx context.Context, sourceFile *ast.SourceFile) []*ast.Diagnostic { - return c.getDiagnostics(ctx, sourceFile, &c.diagnostics) -} - -func (c *Checker) GetSuggestionDiagnostics(ctx context.Context, sourceFile *ast.SourceFile) []*ast.Diagnostic { - return c.getDiagnostics(ctx, sourceFile, &c.suggestionDiagnostics) -} - -func (c *Checker) getDiagnostics(ctx context.Context, sourceFile *ast.SourceFile, collection *ast.DiagnosticsCollection) []*ast.Diagnostic { - c.checkNotCanceled() - if sourceFile != nil { - c.CheckSourceFile(ctx, sourceFile) - if c.wasCanceled { - return nil - } - return collection.GetDiagnosticsForFile(sourceFile.FileName()) - } - for _, file := range c.files { - c.CheckSourceFile(ctx, file) - if c.wasCanceled { - return nil - } - } - return collection.GetDiagnostics() -} - -func (c *Checker) GetDiagnosticsWithoutCheck(sourceFile *ast.SourceFile) []*ast.Diagnostic { - c.checkNotCanceled() - return c.diagnostics.GetDiagnosticsForFile(sourceFile.FileName()) -} - -func (c *Checker) GetGlobalDiagnostics() []*ast.Diagnostic { - c.checkNotCanceled() - return c.diagnostics.GetGlobalDiagnostics() -} - -func (c *Checker) error(location *ast.Node, message *diagnostics.Message, args ...any) *ast.Diagnostic { - diagnostic := NewDiagnosticForNode(location, message, args...) - c.diagnostics.Add(diagnostic) - return diagnostic -} - -func (c *Checker) errorSkippedOnNoEmit(location *ast.Node, message *diagnostics.Message, args ...any) *ast.Diagnostic { - diagnostic := c.error(location, message, args...) - diagnostic.SetSkippedOnNoEmit() - return diagnostic -} - -func (c *Checker) errorOrSuggestion(isError bool, location *ast.Node, message *diagnostics.Message, args ...any) { - c.addErrorOrSuggestion(isError, NewDiagnosticForNode(location, message, args...)) -} - -func (c *Checker) errorAndMaybeSuggestAwait(location *ast.Node, maybeMissingAwait bool, message *diagnostics.Message, args ...any) *ast.Diagnostic { - diagnostic := c.error(location, message, args...) - if maybeMissingAwait { - diagnostic.AddRelatedInfo(createDiagnosticForNode(location, diagnostics.Did_you_forget_to_use_await)) - } - return diagnostic -} - -func (c *Checker) addErrorOrSuggestion(isError bool, diagnostic *ast.Diagnostic) { - if isError { - c.diagnostics.Add(diagnostic) - } else { - suggestion := *diagnostic - suggestion.SetCategory(diagnostics.CategorySuggestion) - c.suggestionDiagnostics.Add(&suggestion) - } -} - -func (c *Checker) IsDeprecatedDeclaration(declaration *ast.Node) bool { - return c.getCombinedNodeFlagsCached(declaration)&ast.NodeFlagsDeprecated != 0 -} - -func (c *Checker) addDeprecatedSuggestion(location *ast.Node, declarations []*ast.Node, deprecatedEntity string) *ast.Diagnostic { - diagnostic := NewDiagnosticForNode(location, diagnostics.X_0_is_deprecated, deprecatedEntity) - return c.addDeprecatedSuggestionWorker(declarations, diagnostic) -} - -func (c *Checker) addDeprecatedSuggestionWorker(declarations []*ast.Node, diagnostic *ast.Diagnostic) *ast.Diagnostic { - for _, declaration := range declarations { - deprecatedTag := getJSDocDeprecatedTag(declaration) - if deprecatedTag != nil { - diagnostic.AddRelatedInfo(NewDiagnosticForNode(deprecatedTag, diagnostics.The_declaration_was_marked_as_deprecated_here)) - break - } - } - c.suggestionDiagnostics.Add(diagnostic) - return diagnostic -} - -func (c *Checker) isDeprecatedSymbol(symbol *ast.Symbol) bool { - parentSymbol := c.getParentOfSymbol(symbol) - if parentSymbol != nil && len(symbol.Declarations) > 1 { - if parentSymbol.Flags&ast.SymbolFlagsInterface != 0 { - return core.Some(symbol.Declarations, c.IsDeprecatedDeclaration) - } else { - return core.Every(symbol.Declarations, c.IsDeprecatedDeclaration) - } - } - return symbol.ValueDeclaration != nil && c.IsDeprecatedDeclaration(symbol.ValueDeclaration) || len(symbol.Declarations) != 0 && core.Every(symbol.Declarations, c.IsDeprecatedDeclaration) -} - -func (c *Checker) hasParseDiagnostics(sourceFile *ast.SourceFile) bool { - return len(sourceFile.Diagnostics()) > 0 -} - -func (c *Checker) newSymbol(flags ast.SymbolFlags, name string) *ast.Symbol { - c.SymbolCount++ - result := c.symbolPool.New() - result.Flags = flags | ast.SymbolFlagsTransient - result.Name = name - return result -} - -func (c *Checker) newSymbolEx(flags ast.SymbolFlags, name string, checkFlags ast.CheckFlags) *ast.Symbol { - result := c.newSymbol(flags, name) - result.CheckFlags = checkFlags - return result -} - -func (c *Checker) newParameter(name string, t *Type) *ast.Symbol { - symbol := c.newSymbol(ast.SymbolFlagsFunctionScopedVariable, name) - c.valueSymbolLinks.Get(symbol).resolvedType = t - return symbol -} - -func (c *Checker) newProperty(name string, t *Type) *ast.Symbol { - symbol := c.newSymbol(ast.SymbolFlagsProperty, name) - c.valueSymbolLinks.Get(symbol).resolvedType = t - return symbol -} - -func (c *Checker) combineSymbolTables(first ast.SymbolTable, second ast.SymbolTable) ast.SymbolTable { - if len(first) == 0 { - return second - } - if len(second) == 0 { - return first - } - combined := make(ast.SymbolTable) - c.mergeSymbolTable(combined, first, false, nil) - c.mergeSymbolTable(combined, second, false, nil) - return combined -} - -func (c *Checker) mergeSymbolTable(target ast.SymbolTable, source ast.SymbolTable, unidirectional bool, mergedParent *ast.Symbol) { - for id, sourceSymbol := range source { - targetSymbol := target[id] - var merged *ast.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. - if merged.Flags&ast.SymbolFlagsTransient != 0 { - 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 *ast.Symbol, source *ast.Symbol, unidirectional bool) *ast.Symbol { - if target.Flags&getExcludedSymbolFlags(source.Flags) == 0 || (source.Flags|target.Flags)&ast.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&ast.SymbolFlagsTransient == 0 { - resolvedTarget := c.resolveSymbol(target) - if resolvedTarget == c.unknownSymbol { - return source - } - if resolvedTarget.Flags&getExcludedSymbolFlags(source.Flags) == 0 || (source.Flags|resolvedTarget.Flags)&ast.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&ast.SymbolFlagsValueModule != 0 && target.Flags&ast.SymbolFlagsValueModule != 0 && target.Flags&ast.SymbolFlagsConstEnumOnlyModule != 0 && source.Flags&ast.SymbolFlagsConstEnumOnlyModule == 0 { - // reset flag when merging instantiated module into value module that has only const enums - target.Flags &^= ast.SymbolFlagsConstEnumOnlyModule - } - target.Flags |= source.Flags - if source.ValueDeclaration != nil { - binder.SetValueDeclaration(target, source.ValueDeclaration) - } - target.Declarations = append(target.Declarations, source.Declarations...) - if source.Members != nil { - c.mergeSymbolTable(ast.GetSymbolTable(&target.Members), source.Members, unidirectional, nil) - } - if source.Exports != nil { - c.mergeSymbolTable(ast.GetSymbolTable(&target.Exports), source.Exports, unidirectional, target) - } - if !unidirectional { - c.recordMergedSymbol(target, source) - } - } else if target.Flags&ast.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(ast.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 *ast.Symbol, source *ast.Symbol) { - isEitherEnum := target.Flags&ast.SymbolFlagsEnum != 0 || source.Flags&ast.SymbolFlagsEnum != 0 - isEitherBlockScoped := target.Flags&ast.SymbolFlagsBlockScopedVariable != 0 || source.Flags&ast.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 := ast.GetSourceFileOfNode(getFirstDeclaration(source)) - // targetSymbolFile := ast.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 *ast.Symbol, message *diagnostics.Message, symbolName string, source *ast.Symbol) { - for _, node := range target.Declarations { - c.addDuplicateDeclarationError(node, message, symbolName, source.Declarations) - } -} - -func (c *Checker) addDuplicateDeclarationError(node *ast.Node, message *diagnostics.Message, symbolName string, relatedNodes []*ast.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 || core.Some(err.RelatedInformation(), func(d *ast.Diagnostic) bool { - return ast.CompareDiagnostics(d, followOnMessage) == 0 || ast.CompareDiagnostics(d, leadingMessage) == 0 - }) { - continue - } - if len(err.RelatedInformation()) == 0 { - err.AddRelatedInfo(leadingMessage) - } else { - err.AddRelatedInfo(followOnMessage) - } - } -} - -func createDiagnosticForNode(node *ast.Node, message *diagnostics.Message, args ...any) *ast.Diagnostic { - return NewDiagnosticForNode(node, message, args...) -} - -func getAdjustedNodeForError(node *ast.Node) *ast.Node { - name := ast.GetNameOfDeclaration(node) - if name != nil { - return name - } - return node -} - -func (c *Checker) lookupOrIssueError(location *ast.Node, message *diagnostics.Message, args ...any) *ast.Diagnostic { - diagnostic := NewDiagnosticForNode(location, message, args...) - existing := c.diagnostics.Lookup(diagnostic) - if existing != nil { - return existing - } - c.diagnostics.Add(diagnostic) - return diagnostic -} - -func getFirstDeclaration(symbol *ast.Symbol) *ast.Node { - if len(symbol.Declarations) > 0 { - return symbol.Declarations[0] - } - return nil -} - -func getExcludedSymbolFlags(flags ast.SymbolFlags) ast.SymbolFlags { - var result ast.SymbolFlags - if flags&ast.SymbolFlagsBlockScopedVariable != 0 { - result |= ast.SymbolFlagsBlockScopedVariableExcludes - } - if flags&ast.SymbolFlagsFunctionScopedVariable != 0 { - result |= ast.SymbolFlagsFunctionScopedVariableExcludes - } - if flags&ast.SymbolFlagsProperty != 0 { - result |= ast.SymbolFlagsPropertyExcludes - } - if flags&ast.SymbolFlagsEnumMember != 0 { - result |= ast.SymbolFlagsEnumMemberExcludes - } - if flags&ast.SymbolFlagsFunction != 0 { - result |= ast.SymbolFlagsFunctionExcludes - } - if flags&ast.SymbolFlagsClass != 0 { - result |= ast.SymbolFlagsClassExcludes - } - if flags&ast.SymbolFlagsInterface != 0 { - result |= ast.SymbolFlagsInterfaceExcludes - } - if flags&ast.SymbolFlagsRegularEnum != 0 { - result |= ast.SymbolFlagsRegularEnumExcludes - } - if flags&ast.SymbolFlagsConstEnum != 0 { - result |= ast.SymbolFlagsConstEnumExcludes - } - if flags&ast.SymbolFlagsValueModule != 0 { - result |= ast.SymbolFlagsValueModuleExcludes - } - if flags&ast.SymbolFlagsMethod != 0 { - result |= ast.SymbolFlagsMethodExcludes - } - if flags&ast.SymbolFlagsGetAccessor != 0 { - result |= ast.SymbolFlagsGetAccessorExcludes - } - if flags&ast.SymbolFlagsSetAccessor != 0 { - result |= ast.SymbolFlagsSetAccessorExcludes - } - if flags&ast.SymbolFlagsTypeParameter != 0 { - result |= ast.SymbolFlagsTypeParameterExcludes - } - if flags&ast.SymbolFlagsTypeAlias != 0 { - result |= ast.SymbolFlagsTypeAliasExcludes - } - if flags&ast.SymbolFlagsAlias != 0 { - result |= ast.SymbolFlagsAliasExcludes - } - return result -} - -func (c *Checker) cloneSymbol(symbol *ast.Symbol) *ast.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.Members = maps.Clone(symbol.Members) - result.Exports = maps.Clone(symbol.Exports) - c.recordMergedSymbol(result, symbol) - return result -} - -func (c *Checker) getMergedSymbol(symbol *ast.Symbol) *ast.Symbol { - if symbol != nil { - merged := c.mergedSymbols[symbol] - if merged != nil { - return merged - } - } - return symbol -} - -func (c *Checker) getParentOfSymbol(symbol *ast.Symbol) *ast.Symbol { - if symbol.Parent != nil { - return c.getMergedSymbol(c.getLateBoundSymbol(symbol.Parent)) - } - return nil -} - -func (c *Checker) recordMergedSymbol(target *ast.Symbol, source *ast.Symbol) { - c.mergedSymbols[source] = target -} - -func (c *Checker) getSymbolIfSameReference(s1 *ast.Symbol, s2 *ast.Symbol) *ast.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 *ast.Symbol) *ast.Symbol { - if symbol != nil && symbol.Flags&ast.SymbolFlagsExportValue != 0 && symbol.ExportSymbol != nil { - symbol = symbol.ExportSymbol - } - return c.getMergedSymbol(symbol) -} - -func (c *Checker) getSymbolOfDeclaration(node *ast.Node) *ast.Symbol { - symbol := node.Symbol() - if symbol != nil { - return c.getMergedSymbol(c.getLateBoundSymbol(symbol)) - } - return nil -} - -// Get the merged symbol for a node. If you know the node is a `Declaration`, it is more type safe to -// use use `getSymbolOfDeclaration` instead. -func (c *Checker) getSymbolOfNode(node *ast.Node) *ast.Symbol { - data := node.DeclarationData() - if data != nil && data.Symbol != nil { - return c.getMergedSymbol(c.getLateBoundSymbol(data.Symbol)) - } - return nil -} - -func (c *Checker) getLateBoundSymbol(symbol *ast.Symbol) *ast.Symbol { - if symbol.Flags&ast.SymbolFlagsClassMember == 0 || symbol.Name != ast.InternalSymbolNameComputed { - return symbol - } - links := c.lateBoundLinks.Get(symbol) - if links.lateSymbol == nil && core.Some(symbol.Declarations, c.hasLateBindableName) { - // force late binding of members/exports. This will set the late-bound symbol - parent := c.getMergedSymbol(symbol.Parent) - if core.Some(symbol.Declarations, ast.HasStaticModifier) { - c.getExportsOfSymbol(parent) - } else { - c.getMembersOfSymbol(parent) - } - } - if links.lateSymbol == nil { - links.lateSymbol = symbol - } - return links.lateSymbol -} - -func (c *Checker) resolveSymbol(symbol *ast.Symbol) *ast.Symbol { - return c.resolveSymbolEx(symbol, false /*dontResolveAlias*/) -} - -func (c *Checker) resolveSymbolEx(symbol *ast.Symbol, dontResolveAlias bool) *ast.Symbol { - if !dontResolveAlias && ast.IsNonLocalAlias(symbol, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace) { - return c.resolveAlias(symbol) - } - return symbol -} - -func (c *Checker) getTargetOfImportEqualsDeclaration(node *ast.Node, dontResolveAlias bool) *ast.Symbol { - // Node is ImportEqualsDeclaration | VariableDeclaration - if ast.IsVariableDeclaration(node) || node.AsImportEqualsDeclaration().ModuleReference.Kind == ast.KindExternalModuleReference { - moduleReference := getExternalModuleRequireArgument(node) - if moduleReference == nil { - moduleReference = ast.GetExternalModuleImportEqualsDeclarationExpression(node) - } - immediate := c.resolveExternalModuleName(node, moduleReference, false /*ignoreErrors*/) - resolved := c.resolveExternalModuleSymbol(immediate, false /*dontResolveAlias*/) - if resolved != nil && core.ModuleKindNode20 <= c.moduleKind && c.moduleKind <= core.ModuleKindNodeNext { - moduleExports := c.getExportOfModule(resolved, ast.InternalSymbolNameModuleExports, node, dontResolveAlias) - if moduleExports != nil { - return moduleExports - } - } - 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) resolveExternalModuleTypeByLiteral(name *ast.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 *ast.Node, dontResolveAlias bool) *ast.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 == ast.KindIdentifier && ast.IsRightSideOfQualifiedNameOrPropertyAccess(entityName) { - entityName = entityName.Parent // QualifiedName - } - // Check for case 1 and 3 in the above example - if entityName.Kind == ast.KindIdentifier || entityName.Parent.Kind == ast.KindQualifiedName { - return c.resolveEntityName(entityName, ast.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 == ast.KindImportEqualsDeclaration) - return c.resolveEntityName(entityName, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace, false /*ignoreErrors*/, dontResolveAlias, nil /*location*/) -} - -func (c *Checker) checkAndReportErrorForResolvingImportAliasToTypeOnlySymbol(node *ast.Node, resolved *ast.Symbol) { - decl := node.AsImportEqualsDeclaration() - if c.markSymbolOfAliasDeclarationIfTypeOnly(node, nil /*immediateTarget*/, resolved, false /*overwriteEmpty*/, nil, "") && !decl.IsTypeOnly { - typeOnlyDeclaration := c.getTypeOnlyAliasDeclaration(c.getSymbolOfDeclaration(node)) - isExport := ast.NodeKindIs(typeOnlyDeclaration, ast.KindExportSpecifier, ast.KindExportDeclaration) - message := core.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 := core.IfElse(isExport, - diagnostics.X_0_was_exported_here, - diagnostics.X_0_was_imported_here) - // TODO: how to get name for export *? - name := "*" - if !ast.IsExportDeclaration(typeOnlyDeclaration) { - name = getNameFromImportDeclaration(typeOnlyDeclaration).Text() - } - c.error(decl.ModuleReference, message).AddRelatedInfo(createDiagnosticForNode(typeOnlyDeclaration, relatedMessage, name)) - } -} - -func (c *Checker) getTargetOfImportClause(node *ast.Node, dontResolveAlias bool) *ast.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 *ast.Symbol, node *ast.Node, dontResolveAlias bool) *ast.Symbol { - file := core.Find(moduleSymbol.Declarations, ast.IsSourceFile) - specifier := c.getModuleSpecifierForImportOrExport(node) - var exportDefaultSymbol *ast.Symbol - var exportModuleDotExportsSymbol *ast.Symbol - if isShorthandAmbientModuleSymbol(moduleSymbol) { - // !!! exportDefaultSymbol = moduleSymbol - // Does nothing? - } else if file != nil && specifier != nil && - core.ModuleKindNode20 <= c.moduleKind && c.moduleKind <= core.ModuleKindNodeNext && - c.getEmitSyntaxForModuleSpecifierExpression(specifier) == core.ModuleKindCommonJS && - c.program.GetImpliedNodeFormatForEmit(file.AsSourceFile()) == core.ModuleKindESNext { - exportModuleDotExportsSymbol = c.resolveExportByName(moduleSymbol, ast.InternalSymbolNameModuleExports, node, dontResolveAlias) - } - if exportModuleDotExportsSymbol != nil { - // We have a transpiled default import where the `require` resolves to an ES module with a `module.exports` named - // export. If `esModuleInterop` is enabled, this will work: - // - // const dep_1 = __importDefault(require("./dep.mjs")); // wraps like { default: require("./dep.mjs") } - // dep_1.default; // require("./dep.mjs") -> the `module.exports` export value - // - // But without `esModuleInterop`, it will be broken: - // - // const dep_1 = require("./dep.mjs"); // the `module.exports` export value (could be primitive) - // dep_1.default; // `default` property access on the `module.exports` export value - // - // We could try to resolve the 'default' property in the latter case, but it's a mistake to run in this - // environment without `esModuleInterop`, so just error. - if !c.compilerOptions.GetESModuleInterop() { - c.error(node.Name(), diagnostics.Module_0_can_only_be_default_imported_using_the_1_flag, c.symbolToString(moduleSymbol), "esModuleInterop") - return nil - } - c.markSymbolOfAliasDeclarationIfTypeOnly(node, exportModuleDotExportsSymbol /*finalTarget*/, nil /*overwriteEmpty*/, false, nil, "") - return exportModuleDotExportsSymbol - } else { - exportDefaultSymbol = c.resolveExportByName(moduleSymbol, ast.InternalSymbolNameDefault, node, dontResolveAlias) - } - if specifier == nil { - return exportDefaultSymbol - } - hasDefaultOnly := c.isOnlyImportableAsDefault(specifier, moduleSymbol) - hasSyntheticDefault := c.canHaveSyntheticDefault(file, moduleSymbol, dontResolveAlias, specifier) - if exportDefaultSymbol == nil && !hasSyntheticDefault && !hasDefaultOnly { - if hasExportAssignmentSymbol(moduleSymbol) && !c.allowSyntheticDefaultImports { - compilerOptionName := core.IfElse(c.moduleKind >= core.ModuleKindES2015, "allowSyntheticDefaultImports", "esModuleInterop") - exportEqualsSymbol := moduleSymbol.Exports[ast.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 != nil { - err.AddRelatedInfo(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 ast.IsImportClause(node) { - c.reportNonDefaultExport(moduleSymbol, node) - } else { - var name *ast.Node - if ast.IsImportOrExportSpecifier(node) { - name = node.PropertyNameOrName() - } else { - name = node.Name() - } - c.errorNoModuleMemberSymbol(moduleSymbol, moduleSymbol, 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) - if resolved == nil { - resolved = c.resolveSymbolEx(moduleSymbol, dontResolveAlias) - } - c.markSymbolOfAliasDeclarationIfTypeOnly(node, moduleSymbol, resolved /*overwriteEmpty*/, false, nil, "") - return resolved - } - c.markSymbolOfAliasDeclarationIfTypeOnly(node, exportDefaultSymbol /*finalTarget*/, nil /*overwriteEmpty*/, false, nil, "") - return exportDefaultSymbol -} - -func (c *Checker) reportNonDefaultExport(moduleSymbol *ast.Symbol, node *ast.Node) { - if moduleSymbol.Exports != nil && moduleSymbol.Exports[node.Symbol().Name] != nil { - c.error(node, diagnostics.Module_0_has_no_default_export_Did_you_mean_to_use_import_1_from_0_instead, c.symbolToString(moduleSymbol), c.symbolToString(node.Symbol())) - } else { - diagnostic := c.error(node.Name(), diagnostics.Module_0_has_no_default_export, c.symbolToString(moduleSymbol)) - var exportStar *ast.Symbol - if moduleSymbol.Exports != nil { - exportStar = moduleSymbol.Exports[ast.InternalSymbolNameExportStar] - } - if exportStar != nil { - defaultExport := core.Find(exportStar.Declarations, func(decl *ast.Declaration) bool { - if !(ast.IsExportDeclaration(decl) && decl.AsExportDeclaration().ModuleSpecifier != nil) { - return false - } - resolvedExternalModuleName := c.resolveExternalModuleName(decl, decl.AsExportDeclaration().ModuleSpecifier, false /*ignoreErrors*/) - return resolvedExternalModuleName != nil && resolvedExternalModuleName.Exports[ast.InternalSymbolNameDefault] != nil - }) - if defaultExport != nil { - diagnostic.AddRelatedInfo(createDiagnosticForNode(defaultExport, diagnostics.X_export_Asterisk_does_not_re_export_a_default)) - } - } - } -} - -func (c *Checker) resolveExportByName(moduleSymbol *ast.Symbol, name string, sourceNode *ast.Node, dontResolveAlias bool) *ast.Symbol { - exportValue := moduleSymbol.Exports[ast.InternalSymbolNameExportEquals] - var exportSymbol *ast.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 *ast.Node, dontResolveAlias bool) *ast.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 *ast.Node, dontResolveAlias bool) *ast.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 *ast.Node, dontResolveAlias bool) *ast.Symbol { - name := node.PropertyNameOrName() - if ast.IsImportSpecifier(node) && ast.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 - if ast.IsBindingElement(node) { - root = ast.GetRootDeclaration(node) - } - resolved := c.getExternalModuleMember(root, node, dontResolveAlias) - c.markSymbolOfAliasDeclarationIfTypeOnly(node, nil /*immediateTarget*/, resolved, false /*overwriteEmpty*/, nil, "") - return resolved -} - -func (c *Checker) getExternalModuleMember(node *ast.Node, specifier *ast.Node, dontResolveAlias bool) *ast.Symbol { - // node is ImportDeclaration | ExportDeclaration | VariableDeclaration - // specifier is ImportSpecifier | ExportSpecifier | BindingElement | PropertyAccessExpression - moduleSpecifier := getExternalModuleRequireArgument(node) - if moduleSpecifier == nil { - moduleSpecifier = ast.GetExternalModuleName(node) - } - moduleSymbol := c.resolveExternalModuleName(node, moduleSpecifier, false /*ignoreErrors*/) - var name *ast.Node - if !ast.IsPropertyAccessExpression(specifier) { - name = specifier.PropertyNameOrName() - } else { - name = specifier.Name() - } - if !ast.IsIdentifier(name) && !ast.IsStringLiteral(name) { - return nil - } - nameText := name.Text() - suppressInteropError := node.Kind == ast.KindVariableDeclaration || nameText == ast.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 == ast.KindStringLiteral { - if isShorthandAmbientModuleSymbol(moduleSymbol) { - return moduleSymbol - } - var symbolFromVariable *ast.Symbol - // First check if module was specified with "export=". If so, get the member from the resolved type - if moduleSymbol != nil && moduleSymbol.Exports[ast.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 == ast.InternalSymbolNameDefault { - file := core.Find(moduleSymbol.Declarations, ast.IsSourceFile) - if c.isOnlyImportableAsDefault(moduleSpecifier, moduleSymbol) || c.canHaveSyntheticDefault(file, 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 ast.IsImportOrExportSpecifier(specifier) && c.isOnlyImportableAsDefault(moduleSpecifier, moduleSymbol) && nameText != ast.InternalSymbolNameDefault { - c.error(name, diagnostics.Named_imports_from_a_JSON_file_into_an_ECMAScript_module_are_not_allowed_when_module_is_set_to_0, c.moduleKind.String()) - } else if symbol == nil { - c.errorNoModuleMemberSymbol(moduleSymbol, targetSymbol, node, name) - } - return symbol - } - } - return nil -} - -func (c *Checker) getPropertyOfVariable(symbol *ast.Symbol, name string) *ast.Symbol { - if symbol.Flags&ast.SymbolFlagsVariable != 0 { - typeAnnotation := symbol.ValueDeclaration.AsVariableDeclaration().Type - 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 *ast.Symbol, typeSymbol *ast.Symbol) *ast.Symbol { - if valueSymbol == c.unknownSymbol && typeSymbol == c.unknownSymbol { - return c.unknownSymbol - } - if valueSymbol.Flags&(ast.SymbolFlagsType|ast.SymbolFlagsNamespace) != 0 { - return valueSymbol - } - result := c.newSymbol(valueSymbol.Flags|typeSymbol.Flags, valueSymbol.Name) - debug.Assert(len(valueSymbol.Declarations) > 0 || len(typeSymbol.Declarations) > 0) - 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 *ast.Symbol, nameText string, specifier *ast.Node, dontResolveAlias bool) *ast.Symbol { - if symbol.Flags&ast.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 *ast.Node, resolvedModule *ast.Symbol) bool { - // In Node.js, JSON modules don't get named exports - if core.ModuleKindNode16 <= c.moduleKind && c.moduleKind <= core.ModuleKindNodeNext { - usageMode := c.getEmitSyntaxForModuleSpecifierExpression(usage) - if usageMode == core.ModuleKindESNext { - if resolvedModule == nil { - resolvedModule = c.resolveExternalModuleName(usage, usage, true /*ignoreErrors*/) - } - var targetFile *ast.SourceFile - if resolvedModule != nil { - targetFile = ast.GetSourceFileOfModule(resolvedModule) - } - return targetFile != nil && (ast.IsJsonSourceFile(targetFile) || tspath.GetDeclarationFileExtension(targetFile.FileName()) == ".d.json.ts") - } - } - return false -} - -func (c *Checker) canHaveSyntheticDefault(file *ast.Node, moduleSymbol *ast.Symbol, dontResolveAlias bool, usage *ast.Node) bool { - var usageMode core.ResolutionMode - if file != nil { - usageMode = c.getEmitSyntaxForModuleSpecifierExpression(usage) - } - if file != nil && usageMode != core.ModuleKindNone { - targetMode := c.program.GetImpliedNodeFormatForEmit(file.AsSourceFile()) - if usageMode == core.ModuleKindESNext && targetMode == core.ModuleKindCommonJS && core.ModuleKindNode16 <= c.moduleKind && c.moduleKind <= core.ModuleKindNodeNext { - // In Node.js, CommonJS modules always have a synthetic default when imported into ESM - return true - } - if usageMode == core.ModuleKindESNext && targetMode == core.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.AsSourceFile().IsDeclarationFile { - // Definitely cannot have a synthetic default if they have a syntactic default member specified - defaultExportSymbol := c.resolveExportByName(moduleSymbol, ast.InternalSymbolNameDefault /*sourceNode*/, nil /*dontResolveAlias*/, true) // Dont resolve alias because we want the immediately exported symbol's declaration - if defaultExportSymbol != nil && core.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 - if !ast.IsInJSFile(file) { - return hasExportAssignmentSymbol(moduleSymbol) - } - - // JS files have a synthetic default if they do not contain ES2015+ module syntax (export = is not valid in js) _and_ do not have an __esModule marker - return !ast.IsExternalModule(file.AsSourceFile()) && c.resolveExportByName(moduleSymbol, "__esModule", nil /*sourceNode*/, dontResolveAlias) == nil -} - -func (c *Checker) getEmitSyntaxForModuleSpecifierExpression(usage *ast.Node) core.ResolutionMode { - if ast.IsStringLiteralLike(usage) { - return c.program.GetEmitSyntaxForUsageLocation(ast.GetSourceFileOfNode(usage), usage) - } - return core.ModuleKindNone -} - -func (c *Checker) errorNoModuleMemberSymbol(moduleSymbol *ast.Symbol, targetSymbol *ast.Symbol, node *ast.Node, name *ast.Node) { - moduleName := c.getFullyQualifiedName(moduleSymbol, node) - declarationName := scanner.DeclarationNameToString(name) - var suggestion *ast.Symbol - if ast.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 { - diagnostic.AddRelatedInfo(createDiagnosticForNode(suggestion.ValueDeclaration, diagnostics.X_0_is_declared_here, suggestionName)) - } - } else { - if moduleSymbol.Exports[ast.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 *ast.Node, name *ast.Node, declarationName string, moduleSymbol *ast.Symbol, moduleName string) { - var localSymbol *ast.Symbol - if locals := moduleSymbol.ValueDeclaration.Locals(); locals != nil { - localSymbol = locals[name.Text()] - } - exports := moduleSymbol.Exports - if localSymbol != nil { - if exportedEqualsSymbol := exports[ast.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 *ast.Symbol) bool { - return c.getSymbolIfSameReference(symbol, localSymbol) != nil - }) - var diagnostic *ast.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 { - diagnostic.AddRelatedInfo(createDiagnosticForNode(decl, core.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 *ast.Node, name *ast.Node, declarationName string, moduleName string) { - if c.moduleKind >= core.ModuleKindES2015 { - message := core.IfElse(c.compilerOptions.GetESModuleInterop(), - 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 := core.IfElse(c.compilerOptions.GetESModuleInterop(), - 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 (c *Checker) getTargetOfExportSpecifier(node *ast.Node, meaning ast.SymbolFlags, dontResolveAlias bool) *ast.Symbol { - name := node.PropertyNameOrName() - if ast.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 *ast.Symbol - switch { - case exportDeclaration.AsExportDeclaration().ModuleSpecifier != nil: - resolved = c.getExternalModuleMember(exportDeclaration, node, dontResolveAlias) - case ast.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 *ast.Node, dontResolveAlias bool) *ast.Symbol { - resolved := c.getTargetOfAliasLikeExpression(node.AsExportAssignment().Expression, dontResolveAlias) - c.markSymbolOfAliasDeclarationIfTypeOnly(node, nil /*immediateTarget*/, resolved, false /*overwriteEmpty*/, nil, "") - return resolved -} - -func (c *Checker) getTargetOfBinaryExpression(node *ast.Node, dontResolveAlias bool) *ast.Symbol { - resolved := c.getTargetOfAliasLikeExpression(node.AsBinaryExpression().Right, dontResolveAlias) - c.markSymbolOfAliasDeclarationIfTypeOnly(node, nil /*immediateTarget*/, resolved, false /*overwriteEmpty*/, nil, "") - return resolved -} - -func (c *Checker) getTargetOfAliasLikeExpression(expression *ast.Node, dontResolveAlias bool) *ast.Symbol { - if ast.IsClassExpression(expression) { - return c.checkExpressionCached(expression).symbol - } - if !ast.IsEntityName(expression) && !ast.IsEntityNameExpression(expression) { - return nil - } - aliasLike := c.resolveEntityName(expression, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace, true /*ignoreErrors*/, dontResolveAlias, nil /*location*/) - if aliasLike != nil { - return aliasLike - } - c.checkExpressionCached(expression) - return c.getResolvedSymbolOrNil(expression) -} - -func (c *Checker) getTargetOfNamespaceExportDeclaration(node *ast.Node, dontResolveAlias bool) *ast.Symbol { - if ast.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 *ast.Node, dontRecursivelyResolve bool) *ast.Symbol { - if ast.IsBinaryExpression(node.Parent) { - expr := node.Parent.AsBinaryExpression() - if expr.Left == node && expr.OperatorToken.Kind == ast.KindEqualsToken { - return c.getTargetOfAliasLikeExpression(expr.Right, dontRecursivelyResolve) - } - } - return nil -} - -func (c *Checker) getModuleSpecifierForImportOrExport(node *ast.Node) *ast.Node { - switch node.Kind { - case ast.KindImportClause: - return getModuleSpecifierFromNode(node.Parent) - case ast.KindImportEqualsDeclaration: - if ast.IsExternalModuleReference(node.AsImportEqualsDeclaration().ModuleReference) { - return node.AsImportEqualsDeclaration().ModuleReference.AsExternalModuleReference().Expression - } else { - return nil - } - case ast.KindNamespaceImport: - return getModuleSpecifierFromNode(node.Parent.Parent) - case ast.KindImportSpecifier: - return getModuleSpecifierFromNode(node.Parent.Parent.Parent) - case ast.KindNamespaceExport: - return getModuleSpecifierFromNode(node.Parent) - case ast.KindExportSpecifier: - return getModuleSpecifierFromNode(node.Parent.Parent) - } - panic("Unhandled case in getModuleSpecifierForImportOrExport") -} - -func getModuleSpecifierFromNode(node *ast.Node) *ast.Node { - switch node.Kind { - case ast.KindImportDeclaration, ast.KindJSImportDeclaration: - return node.AsImportDeclaration().ModuleSpecifier - case ast.KindExportDeclaration: - 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 *ast.Node, immediateTarget *ast.Symbol, finalTarget *ast.Symbol, overwriteEmpty bool, exportStarDeclaration *ast.Node, exportStarName string) bool { - if aliasDeclaration == nil || !ast.IsDeclarationNode(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 ast.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 *ast.Symbol, overwriteEmpty bool) bool { - if target != nil && (!aliasDeclarationLinks.typeOnlyDeclarationResolved || overwriteEmpty && aliasDeclarationLinks.typeOnlyDeclaration == nil) { - exportSymbol := core.OrElse(target.Exports[ast.InternalSymbolNameExportEquals], target) - aliasDeclarationLinks.typeOnlyDeclarationResolved = true - if typeOnly := core.Find(exportSymbol.Declarations, ast.IsTypeOnlyImportOrExportDeclaration); typeOnly != nil { - aliasDeclarationLinks.typeOnlyDeclaration = typeOnly - } else { - aliasDeclarationLinks.typeOnlyDeclaration = c.aliasSymbolLinks.Get(exportSymbol).typeOnlyDeclaration - } - } - return aliasDeclarationLinks.typeOnlyDeclaration != nil -} - -func (c *Checker) resolveExternalModuleName(location *ast.Node, moduleReferenceExpression *ast.Node, ignoreErrors bool) *ast.Symbol { - errorMessage := diagnostics.Cannot_find_module_0_or_its_corresponding_type_declarations - return c.resolveExternalModuleNameWorker(location, moduleReferenceExpression, core.IfElse(ignoreErrors, nil, errorMessage), ignoreErrors, false /*isForAugmentation*/) -} - -func (c *Checker) resolveExternalModuleNameWorker(location *ast.Node, moduleReferenceExpression *ast.Node, moduleNotFoundError *diagnostics.Message, ignoreErrors bool, isForAugmentation bool) *ast.Symbol { - if ast.IsStringLiteralLike(moduleReferenceExpression) { - return c.resolveExternalModule(location, moduleReferenceExpression.Text(), moduleNotFoundError, core.IfElse(!ignoreErrors, moduleReferenceExpression, nil), isForAugmentation) - } - return nil -} - -func (c *Checker) resolveExternalModule(location *ast.Node, moduleReference string, moduleNotFoundError *diagnostics.Message, errorNode *ast.Node, isForAugmentation bool) *ast.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 - } - - importingSourceFile := ast.GetSourceFileOfNode(location) - var ( - contextSpecifier *ast.Node - mode core.ResolutionMode - ) - - if ast.IsStringLiteralLike(location) || location.Parent != nil && ast.IsModuleDeclaration(location.Parent) && location.Parent.AsModuleDeclaration().Name() == location { - contextSpecifier = location - } else if ast.IsModuleDeclaration(location) { - contextSpecifier = location.AsModuleDeclaration().Name() - } else if ast.IsLiteralImportTypeNode(location) { - contextSpecifier = location.AsImportTypeNode().Argument.AsLiteralTypeNode().Literal - } else if ast.IsVariableDeclaration(location) && location.AsVariableDeclaration().Initializer != nil && ast.IsRequireCall(location.AsVariableDeclaration().Initializer, true /*requireStringLiteralLikeArgument*/) { - contextSpecifier = location.AsVariableDeclaration().Initializer.AsCallExpression().Arguments.Nodes[0] - } else { - ancestor := ast.FindAncestor(location, ast.IsImportCall) - if ancestor != nil { - contextSpecifier = ancestor.AsCallExpression().Arguments.Nodes[0] - } - - if ancestor == nil { - ancestor = ast.FindAncestor(location, ast.IsImportDeclarationOrJSImportDeclaration) - if ancestor != nil { - contextSpecifier = ancestor.AsImportDeclaration().ModuleSpecifier - } - } - if ancestor == nil { - ancestor = ast.FindAncestor(location, ast.IsExportDeclaration) - if ancestor != nil { - contextSpecifier = ancestor.AsExportDeclaration().ModuleSpecifier - } - } - if ancestor == nil { - ancestor = ast.FindAncestor(location, ast.IsImportEqualsDeclaration) - if ancestor != nil { - if moduleRefrence := ancestor.AsImportEqualsDeclaration().ModuleReference; moduleRefrence.Kind == ast.KindExternalModuleReference { - contextSpecifier = moduleRefrence.AsExternalModuleReference().Expression - } - } - } - } - - if contextSpecifier != nil && ast.IsStringLiteralLike(contextSpecifier) { - mode = c.program.GetModeForUsageLocation(importingSourceFile, contextSpecifier) - } else { - mode = c.program.GetDefaultResolutionModeForFile(importingSourceFile) - } - - resolvedModule := c.program.GetResolvedModule(importingSourceFile, moduleReference, mode) - - var resolutionDiagnostic *diagnostics.Message - if errorNode != nil && resolvedModule.IsResolved() { - resolutionDiagnostic = module.GetResolutionDiagnostic(c.compilerOptions, resolvedModule, importingSourceFile) - } - - var sourceFile *ast.SourceFile - if resolvedModule.IsResolved() && (resolutionDiagnostic == nil || resolutionDiagnostic == diagnostics.Module_0_was_resolved_to_1_but_jsx_is_not_set) { - sourceFile = c.program.GetSourceFileForResolvedModule(resolvedModule.ResolvedFileName) - } - - if sourceFile != nil { - // If there's a resolutionDiagnostic we need to report it even if a sourceFile is found. - if resolutionDiagnostic != nil { - c.error(errorNode, resolutionDiagnostic, moduleReference, resolvedModule.ResolvedFileName) - } - - if errorNode != nil { - if resolvedModule.ResolvedUsingTsExtension && tspath.IsDeclarationFileName(moduleReference) { - if ast.FindAncestor(location, ast.IsEmittableImport) != nil { - tsExtension := tspath.TryExtractTSExtension(moduleReference) - if tsExtension == "" { - panic("should be able to extract TS extension from string that passes IsDeclarationFileName") - } - c.error( - errorNode, - diagnostics.A_declaration_file_cannot_be_imported_without_import_type_Did_you_mean_to_import_an_implementation_file_0_instead, - c.getSuggestedImportSource(moduleReference, tsExtension, mode), - ) - } - } else if resolvedModule.ResolvedUsingTsExtension && !c.compilerOptions.AllowImportingTsExtensionsFrom(importingSourceFile.FileName()) { - if ast.FindAncestor(location, ast.IsEmittableImport) != nil { - tsExtension := tspath.TryExtractTSExtension(moduleReference) - if tsExtension == "" { - panic("should be able to extract TS extension from string that passes IsDeclarationFileName") - } - c.error( - errorNode, - diagnostics.An_import_path_can_only_end_with_a_0_extension_when_allowImportingTsExtensions_is_enabled, - tsExtension, - ) - } - } else if c.compilerOptions.RewriteRelativeImportExtensions.IsTrue() && - location.Flags&ast.NodeFlagsAmbient == 0 && - !tspath.IsDeclarationFileName(moduleReference) && - !ast.IsLiteralImportTypeNode(location) && - !ast.IsPartOfTypeOnlyImportOrExportDeclaration(location) { - shouldRewrite := core.ShouldRewriteModuleSpecifier(moduleReference, c.compilerOptions) - if !resolvedModule.ResolvedUsingTsExtension && shouldRewrite { - relativeToSourceFile := tspath.GetRelativePathFromFile( - tspath.GetNormalizedAbsolutePath(importingSourceFile.FileName(), c.program.GetCurrentDirectory()), - resolvedModule.ResolvedFileName, - tspath.ComparePathsOptions{ - UseCaseSensitiveFileNames: c.program.UseCaseSensitiveFileNames(), - CurrentDirectory: c.program.GetCurrentDirectory(), - }, - ) - c.error( - errorNode, - diagnostics.This_relative_import_path_is_unsafe_to_rewrite_because_it_looks_like_a_file_name_but_actually_resolves_to_0, - relativeToSourceFile, - ) - } else if resolvedModule.ResolvedUsingTsExtension && !shouldRewrite && c.program.SourceFileMayBeEmitted(sourceFile, false) { - c.error( - errorNode, - diagnostics.This_import_uses_a_0_extension_to_resolve_to_an_input_TypeScript_file_but_will_not_be_rewritten_during_emit_because_it_is_not_a_relative_path, - tspath.GetAnyExtensionFromPath(moduleReference, nil, false), - ) - } else if resolvedModule.ResolvedUsingTsExtension && shouldRewrite { - if redirect := c.program.GetRedirectForResolution(sourceFile); redirect != nil { - ownRootDir := c.program.CommonSourceDirectory() - otherRootDir := redirect.CommonSourceDirectory() - - compareOptions := tspath.ComparePathsOptions{ - UseCaseSensitiveFileNames: c.program.UseCaseSensitiveFileNames(), - CurrentDirectory: c.program.GetCurrentDirectory(), - } - - rootDirPath := tspath.GetRelativePathFromDirectory(ownRootDir, otherRootDir, compareOptions) - - // Get outDir paths, defaulting to root directories if not specified - ownOutDir := c.compilerOptions.OutDir - if ownOutDir == "" { - ownOutDir = ownRootDir - } - otherOutDir := redirect.CompilerOptions().OutDir - if otherOutDir == "" { - otherOutDir = otherRootDir - } - outDirPath := tspath.GetRelativePathFromDirectory(ownOutDir, otherOutDir, compareOptions) - - if rootDirPath != outDirPath { - c.error( - errorNode, - diagnostics.This_import_path_is_unsafe_to_rewrite_because_it_resolves_to_another_project_and_the_relative_path_between_the_projects_output_files_is_not_the_same_as_the_relative_path_between_its_input_files, - ) - } - } - } - } - } - - if sourceFile.Symbol != nil { - if errorNode != nil { - if resolvedModule.IsExternalLibraryImport && !resolutionExtensionIsTSOrJson(resolvedModule.Extension) { - c.errorOnImplicitAnyModule(false /*isError*/, errorNode, mode, resolvedModule, moduleReference) - } - if c.moduleKind == core.ModuleKindNode16 || c.moduleKind == core.ModuleKindNode18 { - isSyncImport := c.program.GetDefaultResolutionModeForFile(importingSourceFile) == core.ModuleKindCommonJS && ast.FindAncestor(location, ast.IsImportCall) == nil || - ast.FindAncestor(location, ast.IsImportEqualsDeclaration) != nil - overrideHost := ast.FindAncestor(location, ast.IsResolutionModeOverrideHost) - if isSyncImport && c.program.GetDefaultResolutionModeForFile(sourceFile) == core.ModuleKindESNext && !ast.HasResolutionModeOverride(overrideHost) { - if ast.FindAncestorKind(location, ast.KindImportEqualsDeclaration) != nil { - // ImportEquals in an ESM file resolving to another ESM file - c.error(errorNode, diagnostics.Module_0_cannot_be_imported_using_this_construct_The_specifier_only_resolves_to_an_ES_module_which_cannot_be_imported_with_require_Use_an_ECMAScript_import_instead, moduleReference) - } else { - // CJS file resolving to an ESM file - var diagnosticDetails *ast.Diagnostic - ext := tspath.TryGetExtensionFromPath(importingSourceFile.FileName()) - if ext == tspath.ExtensionTs || ext == tspath.ExtensionJs || ext == tspath.ExtensionTsx || ext == tspath.ExtensionJsx { - diagnosticDetails = c.createModeMismatchDetails(importingSourceFile, errorNode) - } - - var message *diagnostics.Message - if overrideHost != nil && overrideHost.Kind == ast.KindImportDeclaration && overrideHost.AsImportDeclaration().ImportClause != nil && overrideHost.AsImportDeclaration().ImportClause.IsTypeOnly() { - message = diagnostics.Type_only_import_of_an_ECMAScript_module_from_a_CommonJS_module_must_have_a_resolution_mode_attribute - } else if overrideHost != nil && overrideHost.Kind == ast.KindImportType { - message = diagnostics.Type_import_of_an_ECMAScript_module_from_a_CommonJS_module_must_have_a_resolution_mode_attribute - } else { - message = diagnostics.The_current_file_is_a_CommonJS_module_whose_imports_will_produce_require_calls_however_the_referenced_file_is_an_ECMAScript_module_and_cannot_be_imported_with_require_Consider_writing_a_dynamic_import_0_call_instead - } - - c.diagnostics.Add(NewDiagnosticChainForNode(diagnosticDetails, errorNode, message, moduleReference)) - } - } - } - } - 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 len(c.patternAmbientModules) != 0 { - pattern := core.FindBestPatternMatch(c.patternAmbientModules, func(v *ast.PatternAmbientModule) core.Pattern { return v.Pattern }, moduleReference) - if pattern != nil { - augmentation := c.patternAmbientModuleAugmentations[moduleReference] - if augmentation != nil { - return c.getMergedSymbol(augmentation) - } - return c.getMergedSymbol(pattern.Symbol) - } - } - - if errorNode == nil { - return nil - } - - if resolvedModule.IsResolved() && !resolutionExtensionIsTSOrJson(resolvedModule.Extension) && resolutionDiagnostic == nil || resolutionDiagnostic == diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type { - if isForAugmentation { - c.error( - errorNode, - diagnostics.Invalid_module_name_in_augmentation_Module_0_resolves_to_an_untyped_module_at_1_which_cannot_be_augmented, - moduleReference, - resolvedModule.ResolvedFileName, - ) - } else { - c.errorOnImplicitAnyModule(c.noImplicitAny && moduleNotFoundError != nil, errorNode, mode, resolvedModule, moduleReference) - } - return nil - } - - if moduleNotFoundError != nil { - // See if this was possibly a projectReference redirect - if resolvedModule.IsResolved() { - redirect := c.program.GetProjectReferenceFromSource(tspath.ToPath(resolvedModule.ResolvedFileName, c.program.GetCurrentDirectory(), c.program.UseCaseSensitiveFileNames())) - if redirect != nil && redirect.OutputDts != "" { - c.error( - errorNode, - diagnostics.Output_file_0_has_not_been_built_from_source_file_1, - redirect.OutputDts, - resolvedModule.ResolvedFileName, - ) - return nil - } - } - - if resolutionDiagnostic != nil { - c.error(errorNode, resolutionDiagnostic, moduleReference, resolvedModule.ResolvedFileName) - } else { - isExtensionlessRelativePathImport := tspath.PathIsRelative(moduleReference) && !tspath.HasExtension(moduleReference) - resolutionIsNode16OrNext := c.moduleResolutionKind == core.ModuleResolutionKindNode16 || c.moduleResolutionKind == core.ModuleResolutionKindNodeNext - if !c.compilerOptions.GetResolveJsonModule() && tspath.FileExtensionIs(moduleReference, tspath.ExtensionJson) { - c.error(errorNode, diagnostics.Cannot_find_module_0_Consider_using_resolveJsonModule_to_import_module_with_json_extension, moduleReference) - } else if mode == core.ResolutionModeESM && resolutionIsNode16OrNext && isExtensionlessRelativePathImport { - absoluteRef := tspath.GetNormalizedAbsolutePath(moduleReference, tspath.GetDirectoryPath(importingSourceFile.FileName())) - if suggestedExt := c.getSuggestedImportExtension(absoluteRef); suggestedExt != "" { - c.error(errorNode, diagnostics.Relative_import_paths_need_explicit_file_extensions_in_ECMAScript_imports_when_moduleResolution_is_node16_or_nodenext_Did_you_mean_0, moduleReference+suggestedExt) - } else { - c.error(errorNode, diagnostics.Relative_import_paths_need_explicit_file_extensions_in_ECMAScript_imports_when_moduleResolution_is_node16_or_nodenext_Consider_adding_an_extension_to_the_import_path) - } - } else if resolvedModule != nil && resolvedModule.AlternateResult != "" { - errorInfo := c.createModuleNotFoundChain(resolvedModule, errorNode, moduleReference, mode, moduleReference) - c.diagnostics.Add(NewDiagnosticChainForNode(errorInfo, errorNode, moduleNotFoundError, moduleReference)) - } else { - c.error(errorNode, moduleNotFoundError, moduleReference) - } - } - } - - return nil -} - -func resolutionExtensionIsTSOrJson(ext string) bool { - return tspath.ExtensionIsTs(ext) || ext == tspath.ExtensionJson -} - -func (c *Checker) getSuggestedImportSource(moduleReference string, tsExtension string, mode core.ResolutionMode) string { - importSourceWithoutExtension := tspath.RemoveExtension(moduleReference, tsExtension) - - // Direct users to import source with .js extension if outputting an ES module. - // @see https://github.com/microsoft/TypeScript/issues/42151 - if c.moduleKind.IsNonNodeESM() || mode == core.ModuleKindESNext { - preferTs := tspath.IsDeclarationFileName(moduleReference) && c.compilerOptions.GetAllowImportingTsExtensions() - var ext string - switch { - case tsExtension == tspath.ExtensionMts || tsExtension == tspath.ExtensionDmts: - ext = core.IfElse(preferTs, ".mts", ".mjs") - case tsExtension == tspath.ExtensionCts || tsExtension == tspath.ExtensionDcts: - ext = core.IfElse(preferTs, ".cts", ".cjs") - default: - ext = core.IfElse(preferTs, ".ts", ".js") - } - - return importSourceWithoutExtension + ext - } - - return importSourceWithoutExtension -} - -func (c *Checker) getSuggestedImportExtension(extensionlessImportPath string) string { - switch true { - case c.program.FileExists(extensionlessImportPath + ".mts"): - return ".mjs" - case c.program.FileExists(extensionlessImportPath + ".ts"): - return ".js" - case c.program.FileExists(extensionlessImportPath + ".cts"): - return ".cjs" - case c.program.FileExists(extensionlessImportPath + ".mjs"): - return ".mjs" - case c.program.FileExists(extensionlessImportPath + ".js"): - return ".js" - case c.program.FileExists(extensionlessImportPath + ".cjs"): - return ".cjs" - case c.program.FileExists(extensionlessImportPath + ".tsx"): - return core.IfElse(c.compilerOptions.Jsx == core.JsxEmitPreserve, ".jsx", ".js") - case c.program.FileExists(extensionlessImportPath + ".jsx"): - return ".jsx" - case c.program.FileExists(extensionlessImportPath + ".json"): - return ".json" - } - return "" -} - -func (c *Checker) errorOnImplicitAnyModule(isError bool, errorNode *ast.Node, mode core.ResolutionMode, resolvedModule *module.ResolvedModule, moduleReference string) { - if isSideEffectImport(errorNode) { - return - } - - var errorInfo *ast.Diagnostic - if !tspath.IsExternalModuleNameRelative(moduleReference) && resolvedModule.PackageId.Name != "" { - errorInfo = c.createModuleNotFoundChain(resolvedModule, errorNode, moduleReference, mode, resolvedModule.PackageId.Name) - } - c.addErrorOrSuggestion( - isError, - NewDiagnosticChainForNode( - errorInfo, - errorNode, - diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type, - moduleReference, - resolvedModule.ResolvedFileName, - ), - ) -} - -func (c *Checker) createModuleNotFoundChain(resolvedModule *module.ResolvedModule, errorNode *ast.Node, moduleReference string, mode core.ResolutionMode, packageName string) *ast.Diagnostic { - if resolvedModule.AlternateResult != "" { - if strings.Contains(resolvedModule.AlternateResult, "/node_modules/@types/") { - packageName = "@types/" + module.MangleScopedPackageName(packageName) - } - return NewDiagnosticForNode(errorNode, diagnostics.There_are_types_at_0_but_this_result_could_not_be_resolved_when_respecting_package_json_exports_The_1_library_may_need_to_update_its_package_json_or_typings, resolvedModule.AlternateResult, packageName) - } - if c.typesPackageExists(packageName) { - return NewDiagnosticForNode(errorNode, diagnostics.If_the_0_package_actually_exposes_this_module_consider_sending_a_pull_request_to_amend_https_Colon_Slash_Slashgithub_com_SlashDefinitelyTyped_SlashDefinitelyTyped_Slashtree_Slashmaster_Slashtypes_Slash_1, packageName, module.MangleScopedPackageName(packageName)) - } - if c.packageBundlesTypes(packageName) { - return NewDiagnosticForNode(errorNode, diagnostics.If_the_0_package_actually_exposes_this_module_try_adding_a_new_declaration_d_ts_file_containing_declare_module_1, packageName, moduleReference) - } - return NewDiagnosticForNode(errorNode, diagnostics.Try_npm_i_save_dev_types_Slash_1_if_it_exists_or_add_a_new_declaration_d_ts_file_containing_declare_module_0, moduleReference, module.MangleScopedPackageName(packageName)) -} - -func (c *Checker) createModeMismatchDetails(sourceFile *ast.SourceFile, errorNode *ast.Node) *ast.Diagnostic { - ext := tspath.TryGetExtensionFromPath(sourceFile.FileName()) - targetExt := core.IfElse(ext == tspath.ExtensionTs, tspath.ExtensionMts, core.IfElse(ext == tspath.ExtensionJs, tspath.ExtensionMjs, "")) - meta := c.program.GetSourceFileMetaData(sourceFile.Path()) - packageJsonType := meta.PackageJsonType - packageJsonDirectory := meta.PackageJsonDirectory - var result *ast.Diagnostic - if packageJsonDirectory != "" && packageJsonType == "" { - if targetExt != "" { - result = NewDiagnosticForNode(errorNode, diagnostics.To_convert_this_file_to_an_ECMAScript_module_change_its_file_extension_to_0_or_add_the_field_type_Colon_module_to_1, targetExt, tspath.CombinePaths(packageJsonDirectory, "package.json")) - } else { - result = NewDiagnosticForNode(errorNode, diagnostics.To_convert_this_file_to_an_ECMAScript_module_add_the_field_type_Colon_module_to_0, tspath.CombinePaths(packageJsonDirectory, "package.json")) - } - } else if targetExt != "" { - result = NewDiagnosticForNode(errorNode, diagnostics.To_convert_this_file_to_an_ECMAScript_module_change_its_file_extension_to_0_or_create_a_local_package_json_file_with_type_Colon_module, targetExt) - } else { - result = NewDiagnosticForNode(errorNode, diagnostics.To_convert_this_file_to_an_ECMAScript_module_create_a_local_package_json_file_with_type_Colon_module) - } - return result -} - -func (c *Checker) tryFindAmbientModule(moduleName string, withAugmentations bool) *ast.Symbol { - if tspath.IsExternalModuleNameRelative(moduleName) { - return nil - } - symbol := c.getSymbol(c.globals, "\""+moduleName+"\"", ast.SymbolFlagsValueModule) - // merged symbol is module declaration symbol combined with all augmentations - if withAugmentations { - return c.getMergedSymbol(symbol) - } - return symbol -} - -func (c *Checker) GetAmbientModules() []*ast.Symbol { - c.ambientModulesOnce.Do(func() { - for sym, global := range c.globals { - if strings.HasPrefix(sym, "\"") && strings.HasSuffix(sym, "\"") { - c.ambientModules = append(c.ambientModules, global) - } - } - }) - return c.ambientModules -} - -func (c *Checker) resolveExternalModuleSymbol(moduleSymbol *ast.Symbol, dontResolveAlias bool) *ast.Symbol { - if moduleSymbol != nil { - exportEquals := c.resolveSymbolEx(moduleSymbol.Exports[ast.InternalSymbolNameExportEquals], dontResolveAlias) - if exportEquals != nil { - return c.getMergedSymbol(exportEquals) - } - } - return moduleSymbol -} - -// 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 *ast.Symbol, referencingLocation *ast.Node, dontResolveAlias bool, suppressInteropError bool) *ast.Symbol { - symbol := c.resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias) - if !dontResolveAlias && symbol != nil { - if !suppressInteropError && symbol.Flags&(ast.SymbolFlagsModule|ast.SymbolFlagsVariable) == 0 && ast.GetDeclarationOfKind(symbol, ast.KindSourceFile) == nil { - compilerOptionName := core.IfElse(c.moduleKind >= core.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 - var namespaceImport *ast.Node - if ast.IsImportDeclaration(referenceParent) { - namespaceImport = ast.GetNamespaceDeclarationNode(referenceParent) - } - if namespaceImport != nil || ast.IsImportCall(referenceParent) { - var reference *ast.Node - if ast.IsImportCall(referenceParent) { - reference = referenceParent.AsCallExpression().Arguments.Nodes[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 := core.Find(moduleSymbol.Declarations, ast.IsSourceFile) - usageMode := c.getEmitSyntaxForModuleSpecifierExpression(reference) - var exportModuleDotExportsSymbol *ast.Symbol - if namespaceImport != nil && targetFile != nil && - core.ModuleKindNode20 <= c.moduleKind && c.moduleKind <= core.ModuleKindNodeNext && - usageMode == core.ModuleKindCommonJS && - c.program.GetImpliedNodeFormatForEmit(targetFile.AsSourceFile()) == core.ModuleKindESNext { - exportModuleDotExportsSymbol = c.getExportOfModule(symbol, ast.InternalSymbolNameModuleExports, namespaceImport, dontResolveAlias) - } - if exportModuleDotExportsSymbol != nil { - if !suppressInteropError && symbol.Flags&(ast.SymbolFlagsModule|ast.SymbolFlagsVariable) == 0 { - 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, "esModuleInterop") - } - if c.compilerOptions.GetESModuleInterop() && c.hasSignatures(typ) { - return c.cloneTypeAsModuleType(exportModuleDotExportsSymbol, typ, referenceParent) - } - return exportModuleDotExportsSymbol - } - - isEsmCjsRef := targetFile != nil && isESMFormatImportImportingCommonjsFormatFile(usageMode, c.program.GetImpliedNodeFormatForEmit(targetFile.AsSourceFile())) - if c.compilerOptions.GetESModuleInterop() || isEsmCjsRef { - if c.hasSignatures(typ) || c.getPropertyOfTypeEx(typ, ast.InternalSymbolNameDefault, true /*skipObjectFunctionPropertyAugment*/, false /*includeTypeOnlyMembers*/) != nil || isEsmCjsRef { - var moduleType *Type - if typ.Flags()&TypeFlagsStructuredType != 0 { - moduleType = c.getTypeWithSyntheticDefaultImportType(typ, symbol, moduleSymbol, reference) - } else { - moduleType = c.createDefaultPropertyWrapperForModule(symbol, symbol.Parent, nil) - } - return c.cloneTypeAsModuleType(symbol, moduleType, referenceParent) - } - } - } - } - return symbol -} - -func (c *Checker) hasSignatures(t *Type) bool { - return len(c.getSignaturesOfStructuredType(t, SignatureKindCall)) > 0 || len(c.getSignaturesOfStructuredType(t, SignatureKindConstruct)) > 0 -} - -func isESMFormatImportImportingCommonjsFormatFile(usageMode core.ResolutionMode, targetMode core.ResolutionMode) bool { - return usageMode == core.ModuleKindESNext && targetMode == core.ModuleKindCommonJS -} - -func (c *Checker) getTypeWithSyntheticDefaultOnly(t *Type, symbol *ast.Symbol, originalSymbol *ast.Symbol, moduleSpecifier *ast.Node) *Type { - hasDefaultOnly := c.isOnlyImportableAsDefault(moduleSpecifier, nil) - if hasDefaultOnly && t != nil && !c.isErrorType(t) { - key := CachedTypeKey{kind: CachedTypeKindDefaultOnlyType, typeId: t.id} - if cached := c.cachedTypes[key]; cached != nil { - return cached - } - result := c.createDefaultPropertyWrapperForModule(symbol, originalSymbol, nil) - c.cachedTypes[key] = result - return result - } - return nil -} - -func (c *Checker) getTypeWithSyntheticDefaultImportType(t *Type, symbol *ast.Symbol, originalSymbol *ast.Symbol, moduleSpecifier *ast.Node) *Type { - if c.allowSyntheticDefaultImports && t != nil && !c.isErrorType(t) { - key := CachedTypeKey{kind: CachedTypeKindSyntheticType, typeId: t.id} - if cached := c.cachedTypes[key]; cached != nil { - return cached - } - file := core.Find(originalSymbol.Declarations, ast.IsSourceFile) - hasSyntheticDefault := c.canHaveSyntheticDefault(file, originalSymbol, false /*dontResolveAlias*/, moduleSpecifier) - var syntheticType *Type - if hasSyntheticDefault { - anonymousSymbol := c.newSymbol(ast.SymbolFlagsTypeLiteral, ast.InternalSymbolNameType) - defaultContainingObject := c.createDefaultPropertyWrapperForModule(symbol, originalSymbol, anonymousSymbol) - c.valueSymbolLinks.Get(anonymousSymbol).resolvedType = defaultContainingObject - if c.isValidSpreadType(t) { - syntheticType = c.getSpreadType(t, defaultContainingObject, anonymousSymbol, 0 /*objectFlags*/, false /*readonly*/) - } else { - syntheticType = defaultContainingObject - } - } else { - syntheticType = t - } - c.cachedTypes[key] = syntheticType - return syntheticType - } - return t -} - -func (c *Checker) isCommonJSRequire(node *ast.Node) bool { - if !ast.IsRequireCall(node, true /*requireStringLiteralLikeArgument*/) { - return false - } - if !ast.IsIdentifier(node.Expression()) { - panic("Expected identifier for require call") - } - // Make sure require is not a local function - resolvedRequire := c.resolveName(node.Expression(), node.Expression().Text(), ast.SymbolFlagsValue, nil /*nameNotFoundMessage*/, true /*isUse*/, false /*excludeGlobals*/) - if resolvedRequire == c.requireSymbol { - return true - } - // project includes symbol named 'require' - make sure that it is ambient and local non-alias - if resolvedRequire == nil || resolvedRequire.Flags&ast.SymbolFlagsAlias != 0 { - return false - } - - var targetDeclarationKind ast.Kind - if resolvedRequire.Flags&ast.SymbolFlagsFunction != 0 { - targetDeclarationKind = ast.KindFunctionDeclaration - } else if resolvedRequire.Flags&ast.SymbolFlagsVariable != 0 { - targetDeclarationKind = ast.KindVariableDeclaration - } else { - targetDeclarationKind = ast.KindUnknown - } - if targetDeclarationKind != ast.KindUnknown { - decl := ast.GetDeclarationOfKind(resolvedRequire, targetDeclarationKind) - // function/variable declaration should be ambient - return decl != nil && decl.Flags&ast.NodeFlagsAmbient != 0 - } - return false -} - -func (c *Checker) createDefaultPropertyWrapperForModule(symbol *ast.Symbol, originalSymbol *ast.Symbol, anonymousSymbol *ast.Symbol) *Type { - memberTable := make(ast.SymbolTable) - newSymbol := c.newSymbol(ast.SymbolFlagsAlias, ast.InternalSymbolNameDefault) - newSymbol.Parent = originalSymbol - c.valueSymbolLinks.Get(newSymbol).nameType = c.getStringLiteralType("default") - c.aliasSymbolLinks.Get(newSymbol).aliasTarget = c.resolveSymbol(symbol) - memberTable[ast.InternalSymbolNameDefault] = newSymbol - return c.newAnonymousType(anonymousSymbol, memberTable, nil, nil, nil) -} - -func (c *Checker) cloneTypeAsModuleType(symbol *ast.Symbol, moduleType *Type, referenceParent *ast.Node) *ast.Symbol { - result := c.newSymbol(symbol.Flags, symbol.Name) - 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 *ast.Node, dontRecursivelyResolve bool) *ast.Symbol { - if node == nil { - return nil - } - switch node.Kind { - case ast.KindImportEqualsDeclaration, ast.KindVariableDeclaration: - return c.getTargetOfImportEqualsDeclaration(node, dontRecursivelyResolve) - case ast.KindImportClause: - return c.getTargetOfImportClause(node, dontRecursivelyResolve) - case ast.KindNamespaceImport: - return c.getTargetOfNamespaceImport(node, dontRecursivelyResolve) - case ast.KindNamespaceExport: - return c.getTargetOfNamespaceExport(node, dontRecursivelyResolve) - case ast.KindImportSpecifier, ast.KindBindingElement: - return c.getTargetOfImportSpecifier(node, dontRecursivelyResolve) - case ast.KindExportSpecifier: - return c.getTargetOfExportSpecifier(node, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace, dontRecursivelyResolve) - case ast.KindExportAssignment, ast.KindJSExportAssignment: - return c.getTargetOfExportAssignment(node, dontRecursivelyResolve) - case ast.KindBinaryExpression: - return c.getTargetOfBinaryExpression(node, dontRecursivelyResolve) - case ast.KindNamespaceExportDeclaration: - return c.getTargetOfNamespaceExportDeclaration(node, dontRecursivelyResolve) - case ast.KindShorthandPropertyAssignment: - return c.resolveEntityName(node.AsShorthandPropertyAssignment().Name(), ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace, true /*ignoreErrors*/, dontRecursivelyResolve, nil /*location*/) - case ast.KindPropertyAssignment: - return c.getTargetOfAliasLikeExpression(node.Initializer(), dontRecursivelyResolve) - case ast.KindElementAccessExpression, ast.KindPropertyAccessExpression: - return c.getTargetOfAccessExpression(node, dontRecursivelyResolve) - } - panic("Unhandled case in getTargetOfAliasDeclaration: " + node.Kind.String()) -} - -/** - * Resolves a qualified name and any involved aliases. - */ -func (c *Checker) resolveEntityName(name *ast.Node, meaning ast.SymbolFlags, ignoreErrors bool, dontResolveAlias bool, location *ast.Node) *ast.Symbol { - if ast.NodeIsMissing(name) { - return nil - } - var symbol *ast.Symbol - switch name.Kind { - case ast.KindIdentifier: - var message *diagnostics.Message - if !ignoreErrors { - if meaning == ast.SymbolFlagsNamespace || ast.NodeIsSynthesized(name) { - message = diagnostics.Cannot_find_namespace_0 - } else { - message = c.getCannotFindNameDiagnosticForName(ast.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 ast.KindQualifiedName: - qualified := name.AsQualifiedName() - symbol = c.resolveQualifiedName(name, qualified.Left, qualified.Right, meaning, ignoreErrors, dontResolveAlias, location) - case ast.KindPropertyAccessExpression: - access := name.AsPropertyAccessExpression() - symbol = c.resolveQualifiedName(name, access.Expression, access.Name(), meaning, ignoreErrors, dontResolveAlias, location) - default: - panic("Unknown entity name kind") - } - if symbol != nil && symbol != c.unknownSymbol { - if !ast.NodeIsSynthesized(name) && ast.IsEntityName(name) && - (symbol.Flags&ast.SymbolFlagsAlias != 0 || - name.Parent != nil && name.Parent.Kind == ast.KindExportAssignment || - name.Parent != nil && name.Parent.Kind == ast.KindJSExportAssignment) { - c.markSymbolOfAliasDeclarationIfTypeOnly(getAliasDeclarationFromName(name), symbol, nil /*finalTarget*/, true /*overwriteEmpty*/, nil, "") - } - if symbol.Flags&meaning == 0 && !dontResolveAlias && symbol.Flags&ast.SymbolFlagsAlias != 0 { - return c.resolveAlias(symbol) - } - } - return symbol -} - -func (c *Checker) resolveQualifiedName(name *ast.Node, left *ast.Node, right *ast.Node, meaning ast.SymbolFlags, ignoreErrors bool, dontResolveAlias bool, location *ast.Node) *ast.Symbol { - namespace := c.resolveEntityName(left, ast.SymbolFlagsNamespace, ignoreErrors, false /*dontResolveAlias*/, location) - if namespace == nil || ast.NodeIsMissing(right) { - return nil - } - if namespace == c.unknownSymbol { - return namespace - } - if namespace.ValueDeclaration != nil && - ast.IsInJSFile(namespace.ValueDeclaration) && - c.compilerOptions.GetModuleResolutionKind() != core.ModuleResolutionKindBundler && - ast.IsVariableDeclaration(namespace.ValueDeclaration) && - namespace.ValueDeclaration.AsVariableDeclaration().Initializer != nil && - c.isCommonJSRequire(namespace.ValueDeclaration.AsVariableDeclaration().Initializer) { - moduleName := namespace.ValueDeclaration.AsVariableDeclaration().Initializer.AsCallExpression().Arguments.Nodes[0] - moduleSym := c.resolveExternalModuleName(moduleName, moduleName, false /*ignoreErrors*/) - if moduleSym != nil { - resolvedModuleSymbol := c.resolveExternalModuleSymbol(moduleSym, false /*dontResolveAlias*/) - if resolvedModuleSymbol != nil { - namespace = resolvedModuleSymbol - } - } - } - text := right.AsIdentifier().Text - symbol := c.getMergedSymbol(c.getSymbol(c.getExportsOfSymbol(namespace), text, meaning)) - if symbol == nil && namespace.Flags&ast.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 := scanner.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 *ast.Node - if ast.IsQualifiedName(name) { - containingQualifiedName = getContainingQualifiedNameNode(name) - } - canSuggestTypeof := c.globalObjectType != nil && meaning&ast.SymbolFlagsType != 0 && containingQualifiedName != nil && !ast.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&ast.SymbolFlagsNamespace != 0 { - if ast.IsQualifiedName(name.Parent) { - exportedTypeSymbol := c.getMergedSymbol(c.getSymbol(c.getExportsOfSymbol(namespace), text, ast.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 *ast.Node) *ast.Symbol { - id := ast.GetFirstIdentifier(node) - symbol := c.resolveName(id, id.AsIdentifier().Text, ast.SymbolFlagsValue, nil /*nameNotFoundMessage*/, true /*isUse*/, false /*excludeGlobals*/) - if symbol == nil { - return nil - } - n := id - for ast.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 *ast.Node, targetModule *ast.Symbol) *ast.Symbol { - exports := slices.Collect(maps.Values(c.getExportsOfModule(targetModule))) - c.sortSymbols(exports) - return c.getSpellingSuggestionForName(name.Text(), exports, ast.SymbolFlagsModuleMember) -} - -func (c *Checker) getFullyQualifiedName(symbol *ast.Symbol, containingLocation *ast.Node) string { - if symbol.Parent != nil { - return c.getFullyQualifiedName(symbol.Parent, containingLocation) + "." + c.symbolToString(symbol) - } - return c.symbolToStringEx(symbol, containingLocation, ast.SymbolFlagsAll, SymbolFormatFlagsDoNotIncludeSymbolChain|SymbolFormatFlagsAllowAnyNodeKind) -} - -func (c *Checker) getExportsOfSymbol(symbol *ast.Symbol) ast.SymbolTable { - if symbol.Flags&ast.SymbolFlagsLateBindingContainer != 0 { - return c.getResolvedMembersOrExportsOfSymbol(symbol, MembersOrExportsResolutionKindResolvedExports) - } - if symbol.Flags&ast.SymbolFlagsModule != 0 { - return c.getExportsOfModule(symbol) - } - return symbol.Exports -} - -func (c *Checker) getResolvedMembersOrExportsOfSymbol(symbol *ast.Symbol, resolutionKind MembersOrExportsResolutionKind) ast.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&ast.SymbolFlagsModule != 0: - earlySymbols, _ = c.getExportsOfModuleWorker(symbol) - } - links[resolutionKind] = earlySymbols - // fill in any as-yet-unresolved late-bound members. - var lateSymbols ast.SymbolTable - for _, decl := range symbol.Declarations { - for _, member := range getMembersOfDeclaration(decl) { - if isStatic == ast.HasStaticModifier(member) { - switch { - case c.hasLateBindableName(member): - if lateSymbols == nil { - lateSymbols = make(ast.SymbolTable) - } - c.lateBindMember(symbol, earlySymbols, lateSymbols, member) - case c.hasLateBindableIndexSignature(member): - if lateSymbols == nil { - lateSymbols = make(ast.SymbolTable) - } - c.lateBindIndexSignature(symbol, earlySymbols, lateSymbols, member.AsNode() /* as LateBoundDeclaration | LateBoundBinaryExpressionDeclaration */) - } - } - } - } - if isStatic { - for member := range symbol.AssignmentDeclarationMembers.Keys() { - if c.hasLateBindableName(member) { - if lateSymbols == nil { - lateSymbols = make(ast.SymbolTable) - } - c.lateBindMember(symbol, earlySymbols, lateSymbols, member) - } - } - } - links[resolutionKind] = c.combineSymbolTables(earlySymbols, lateSymbols) - } - return links[resolutionKind] -} - -// Performs late-binding of a dynamic member. This performs the same function for -// late-bound members that `declareSymbol` in binder.ts performs for early-bound -// members. -// -// If a symbol is a dynamic name from a computed property, we perform an additional "late" -// binding phase to attempt to resolve the name for the symbol from the type of the computed -// property's expression. If the type of the expression is a string-literal, numeric-literal, -// or unique symbol type, we can use that type as the name of the symbol. -// -// For example, given: -// -// const x = Symbol(); -// -// interface I { -// [x]: number; -// } -// -// The binder gives the property `[x]: number` a special symbol with the name "__computed". -// In the late-binding phase we can type-check the expression `x` and see that it has a -// unique symbol type which we can then use as the name of the member. This allows users -// to define custom symbols that can be used in the members of an object type. -// -// @param parent The containing symbol for the member. -// @param earlySymbols The early-bound symbols of the parent. -// @param lateSymbols The late-bound symbols of the parent. -// @param decl The member to bind. -func (c *Checker) lateBindMember(parent *ast.Symbol, earlySymbols ast.SymbolTable, lateSymbols ast.SymbolTable, decl *ast.Node) *ast.Symbol { - debug.AssertIsDefined(decl.Symbol(), "The member is expected to have a symbol.") - links := c.symbolNodeLinks.Get(decl) - if links.resolvedSymbol == nil { - // In the event we attempt to resolve the late-bound name of this member recursively, - // fall back to the early-bound name of this member. - links.resolvedSymbol = decl.Symbol() - var declName *ast.Node - if ast.IsBinaryExpression(decl) { - declName = decl.AsBinaryExpression().Left - } else { - declName = decl.Name() - } - var t *Type - if ast.IsElementAccessExpression(declName) { - t = c.checkExpressionCached(declName.AsElementAccessExpression().ArgumentExpression) - } else { - t = c.checkComputedPropertyName(declName) - } - if isTypeUsableAsPropertyName(t) { - memberName := getPropertyNameFromType(t) - symbolFlags := decl.Symbol().Flags - // Get or add a late-bound symbol for the member. This allows us to merge late-bound accessor declarations. - lateSymbol := lateSymbols[memberName] - if lateSymbol == nil { - lateSymbol = c.newSymbolEx(ast.SymbolFlagsNone, memberName, ast.CheckFlagsLate) - lateSymbols[memberName] = lateSymbol - } - // Report an error if there's a symbol declaration with the same name and conflicting flags. - earlySymbol := earlySymbols[memberName] - if lateSymbol.Flags&getExcludedSymbolFlags(symbolFlags) != 0 { - // If we have an existing early-bound member, combine its declarations so that we can - // report an error at each declaration. - var declarations []*ast.Node - if earlySymbol != nil { - declarations = core.Concatenate(earlySymbol.Declarations, lateSymbol.Declarations) - } else { - declarations = lateSymbol.Declarations - } - name := memberName - if t.flags&TypeFlagsUniqueESSymbol != 0 { - name = scanner.DeclarationNameToString(declName) - } - for _, d := range declarations { - c.error(core.OrElse(ast.GetNameOfDeclaration(d), d), diagnostics.Duplicate_identifier_0, name) - } - c.error(core.OrElse(declName, decl), diagnostics.Duplicate_identifier_0, name) - if lateSymbol.Flags&ast.SymbolFlagsAccessor != 0 && lateSymbol.Flags&ast.SymbolFlagsAccessor != symbolFlags&ast.SymbolFlagsAccessor { - lateSymbol.Flags |= ast.SymbolFlagsAccessor - } - lateSymbol = c.newSymbolEx(ast.SymbolFlagsNone, memberName, ast.CheckFlagsLate) - } - c.valueSymbolLinks.Get(lateSymbol).nameType = t - c.addDeclarationToLateBoundSymbol(lateSymbol, decl, symbolFlags) - if lateSymbol.Parent == nil { - lateSymbol.Parent = parent - } - links.resolvedSymbol = lateSymbol - } - } - return links.resolvedSymbol -} - -func (c *Checker) lateBindIndexSignature(parent *ast.Symbol, earlySymbols ast.SymbolTable, lateSymbols ast.SymbolTable, decl *ast.Node) { - // First, late bind the index symbol itself, if needed - indexSymbol := lateSymbols[ast.InternalSymbolNameIndex] - if indexSymbol == nil { - early := earlySymbols[ast.InternalSymbolNameIndex] - if early == nil { - indexSymbol = c.newSymbolEx(ast.SymbolFlagsNone, ast.InternalSymbolNameIndex, ast.CheckFlagsLate) - } else { - indexSymbol = c.cloneSymbol(early) - indexSymbol.CheckFlags |= ast.CheckFlagsLate - } - lateSymbols[ast.InternalSymbolNameIndex] = indexSymbol - } - // Then just add the computed name as a late bound declaration - // (note: unlike `addDeclarationToLateBoundSymbol` we do not set up a `.lateSymbol` on `decl`'s links, - // since that would point at an index symbol and not a single property symbol, like most consumers would expect) - if len(indexSymbol.Declarations) == 0 || decl.Symbol().Flags&ast.SymbolFlagsReplaceableByMethod == 0 { - indexSymbol.Declarations = append(indexSymbol.Declarations, decl) - } -} - -// Adds a declaration to a late-bound dynamic member. This performs the same function for -// late-bound members that `addDeclarationToSymbol` in binder.ts performs for early-bound -// members. -func (c *Checker) addDeclarationToLateBoundSymbol(symbol *ast.Symbol, member *ast.Node, symbolFlags ast.SymbolFlags) { - debug.Assert(symbol.CheckFlags&ast.CheckFlagsLate != 0, "Expected a late-bound symbol.") - symbol.Flags |= symbolFlags - c.lateBoundLinks.Get(member.Symbol()).lateSymbol = symbol - if len(symbol.Declarations) == 0 || member.Symbol().Flags&ast.SymbolFlagsReplaceableByMethod == 0 { - symbol.Declarations = append(symbol.Declarations, member) - } - if symbolFlags&ast.SymbolFlagsValue != 0 { - binder.SetValueDeclaration(symbol, member) - } -} - -/** - * 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 *ast.Symbol) ast.SymbolTable { - if symbol.Flags&ast.SymbolFlagsLateBindingContainer != 0 { - return c.getResolvedMembersOrExportsOfSymbol(symbol, MembersOrExportsResolutionKindResolvedMembers) - } - return symbol.Members -} - -func (c *Checker) getExportsOfModule(moduleSymbol *ast.Symbol) ast.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 []*ast.Node -} - -type ExportCollisionTable = map[string]*ExportCollision - -func (c *Checker) getExportsOfModuleWorker(moduleSymbol *ast.Symbol) (exports ast.SymbolTable, typeOnlyExportStarMap map[string]*ast.Node) { - var visitedSymbols []*ast.Symbol - nonTypeOnlyNames := collections.NewSetWithSizeHint[string](len(moduleSymbol.Exports)) - // 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(*ast.Symbol, *ast.Node, bool) ast.SymbolTable - visit = func(symbol *ast.Symbol, exportStar *ast.Node, isTypeOnly bool) ast.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.Add(name) - } - } - if symbol == nil || symbol.Exports == nil || slices.Contains(visitedSymbols, symbol) { - return nil - } - visitedSymbols = append(visitedSymbols, symbol) - symbols := maps.Clone(symbol.Exports) - // All export * declarations are collected in an __export symbol by the binder - exportStars := symbol.Exports[ast.InternalSymbolNameExportStar] - if exportStars != nil { - nestedSymbols := make(ast.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 == ast.InternalSymbolNameExportEquals || 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]*ast.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(ast.SymbolTable) - } - for name := range nonTypeOnlyNames.Keys() { - delete(typeOnlyExportStarMap, name) - } - return exports, typeOnlyExportStarMap -} - -/** - * 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 ast.SymbolTable, source ast.SymbolTable, lookupTable ExportCollisionTable, exportNode *ast.Node) { - for id, sourceSymbol := range source { - if id == ast.InternalSymbolNameDefault { - continue - } - targetSymbol := target[id] - if targetSymbol == nil { - target[id] = sourceSymbol - if lookupTable != nil && exportNode != nil { - lookupTable[id] = &ExportCollision{ - specifierText: scanner.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) - } - } -} - -func (c *Checker) ResolveAlias(symbol *ast.Symbol) (*ast.Symbol, bool) { - if symbol == nil { - return nil, false - } - resolved := c.resolveAlias(symbol) - return resolved, resolved != c.unknownSymbol -} - -func (c *Checker) resolveAlias(symbol *ast.Symbol) *ast.Symbol { - if symbol.Flags&ast.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 for symbol: " + c.symbolToString(symbol)) - } - 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 -} - -func (c *Checker) tryResolveAlias(symbol *ast.Symbol) *ast.Symbol { - links := c.aliasSymbolLinks.Get(symbol) - if links.aliasTarget != c.resolvingSymbol { - return c.resolveAlias(symbol) - } - return nil -} - -func (c *Checker) resolveAliasWithDeprecationCheck(symbol *ast.Symbol, location *ast.Node) *ast.Symbol { - if symbol.Flags&ast.SymbolFlagsAlias == 0 || c.isDeprecatedSymbol(symbol) || c.getDeclarationOfAliasSymbol(symbol) == nil { - return symbol - } - targetSymbol := c.resolveAlias(symbol) - if targetSymbol == c.unknownSymbol { - return targetSymbol - } - for symbol.Flags&ast.SymbolFlagsAlias != 0 { - target := c.getImmediateAliasedSymbol(symbol) - if target != nil { - if target == targetSymbol { - break - } - if len(target.Declarations) != 0 { - if c.isDeprecatedSymbol(target) { - c.addDeprecatedSuggestion(location, target.Declarations, target.Name) - break - } else { - if symbol == targetSymbol { - break - } - symbol = target - } - } - } else { - break - } - } - return targetSymbol -} - -/** - * 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 *ast.Symbol) ast.SymbolFlags { - return c.getSymbolFlagsEx(symbol, false /*excludeTypeOnlyMeanings*/, false /*excludeLocalMeanings*/) -} - -func (c *Checker) getSymbolFlagsEx(symbol *ast.Symbol, excludeTypeOnlyMeanings bool, excludeLocalMeanings bool) ast.SymbolFlags { - var typeOnlyDeclaration *ast.Node - if excludeTypeOnlyMeanings { - typeOnlyDeclaration = c.getTypeOnlyAliasDeclaration(symbol) - } - typeOnlyDeclarationIsExportStar := typeOnlyDeclaration != nil && ast.IsExportDeclaration(typeOnlyDeclaration) - var typeOnlyResolution *ast.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 ast.SymbolTable - if typeOnlyDeclarationIsExportStar && typeOnlyResolution != nil { - typeOnlyExportStarTargets = c.getExportsOfModule(typeOnlyResolution) - } - var flags ast.SymbolFlags - if !excludeLocalMeanings { - flags = symbol.Flags - } - var seenSymbols collections.Set[*ast.Symbol] - for symbol.Flags&ast.SymbolFlagsAlias != 0 { - target := c.getExportSymbolOfValueSymbolIfExported(c.resolveAlias(symbol)) - if !typeOnlyDeclarationIsExportStar && target == typeOnlyResolution || typeOnlyExportStarTargets[target.Name] == target { - break - } - if target == c.unknownSymbol { - return ast.SymbolFlagsAll - } - // Optimizations - try to avoid creating or adding to - // `seenSymbols` if possible - if target == symbol || seenSymbols.Has(target) { - break - } - if target.Flags&ast.SymbolFlagsAlias != 0 { - if seenSymbols.Len() == 0 { - seenSymbols.Add(symbol) - } - seenSymbols.Add(target) - } - flags |= target.Flags - symbol = target - } - return flags -} - -func (c *Checker) getDeclarationOfAliasSymbol(symbol *ast.Symbol) *ast.Node { - return core.FindLast(symbol.Declarations, ast.IsAliasSymbolDeclaration) -} - -func (c *Checker) getTypeOfSymbolWithDeferredType(symbol *ast.Symbol) *Type { - links := c.valueSymbolLinks.Get(symbol) - if links.resolvedType == nil { - deferred := c.deferredSymbolLinks.Get(symbol) - if deferred.parent.flags&TypeFlagsUnion != 0 { - links.resolvedType = c.getUnionType(deferred.constituents) - } else { - links.resolvedType = c.getIntersectionType(deferred.constituents) - } - } - return links.resolvedType -} - -func (c *Checker) getWriteTypeOfSymbolWithDeferredType(symbol *ast.Symbol) *Type { - links := c.valueSymbolLinks.Get(symbol) - if links.writeType == nil { - deferred := c.deferredSymbolLinks.Get(symbol) - if len(deferred.writeConstituents) != 0 { - if deferred.parent.flags&TypeFlagsUnion != 0 { - links.writeType = c.getUnionType(deferred.writeConstituents) - } else { - links.writeType = c.getIntersectionType(deferred.writeConstituents) - } - } else { - links.writeType = c.getTypeOfSymbolWithDeferredType(symbol) - } - } - return links.writeType -} - -// Distinct write types come only from set accessors, but synthetic union and intersection -// properties deriving from set accessors will either pre-compute or defer the union or -// intersection of the writeTypes of their constituents. -func (c *Checker) getWriteTypeOfSymbol(symbol *ast.Symbol) *Type { - if symbol.CheckFlags&ast.CheckFlagsSyntheticProperty != 0 { - if symbol.CheckFlags&ast.CheckFlagsDeferredType != 0 { - return c.getWriteTypeOfSymbolWithDeferredType(symbol) - } - links := c.valueSymbolLinks.Get(symbol) - return core.OrElse(links.writeType, links.resolvedType) - } - if symbol.Flags&ast.SymbolFlagsProperty != 0 { - return c.removeMissingType(c.getTypeOfSymbol(symbol), symbol.Flags&ast.SymbolFlagsOptional != 0) - } - if symbol.Flags&ast.SymbolFlagsAccessor != 0 { - if symbol.CheckFlags&ast.CheckFlagsInstantiated != 0 { - return c.getWriteTypeOfInstantiatedSymbol(symbol) - } - return c.getWriteTypeOfAccessors(symbol) - } - return c.getTypeOfSymbol(symbol) -} - -func (c *Checker) GetTypeOfSymbolAtLocation(symbol *ast.Symbol, location *ast.Node) *Type { - symbol = c.getExportSymbolOfValueSymbolIfExported(symbol) - if location != nil { - // If we have an identifier or a property access at the given location, if the location is - // an dotted name expression, and if the location is not an assignment target, obtain the type - // of the expression (which will reflect control flow analysis). If the expression indeed - // resolved to the given symbol, return the narrowed type. - if ast.IsIdentifier(location) || ast.IsPrivateIdentifier(location) { - if ast.IsRightSideOfQualifiedNameOrPropertyAccess(location) { - location = location.Parent - } - if ast.IsExpressionNode(location) && (!ast.IsAssignmentTarget(location) || ast.IsWriteAccess(location)) { - var t *Type - if ast.IsWriteAccess(location) && location.Kind == ast.KindPropertyAccessExpression { - t = c.checkPropertyAccessExpression(location, CheckModeNormal, true /*writeOnly*/) - } else { - t = c.getTypeOfExpression(location) - } - if c.getExportSymbolOfValueSymbolIfExported(c.symbolNodeLinks.Get(location).resolvedSymbol) == symbol { - return c.removeOptionalTypeMarker(t) - } - } - } - if ast.IsDeclarationName(location) && ast.IsSetAccessorDeclaration(location.Parent) && c.getAnnotatedAccessorTypeNode(location.Parent) != nil { - return c.getWriteTypeOfAccessors(location.Parent.Symbol()) - } - // The location isn't a reference to the given symbol, meaning we're being asked - // a hypothetical question of what type the symbol would have if there was a reference - // to it at the given location. Since we have no control flow information for the - // hypothetical reference (control flow information is created and attached by the - // binder), we simply return the declared type of the symbol. - if isRightSideOfAccessExpression(location) && ast.IsWriteAccess(location.Parent) { - return c.getWriteTypeOfSymbol(symbol) - } - } - return c.getNonMissingTypeOfSymbol(symbol) -} - -func (c *Checker) getTypeOfSymbol(symbol *ast.Symbol) *Type { - if symbol.CheckFlags&ast.CheckFlagsDeferredType != 0 { - return c.getTypeOfSymbolWithDeferredType(symbol) - } - if symbol.CheckFlags&ast.CheckFlagsInstantiated != 0 { - return c.getTypeOfInstantiatedSymbol(symbol) - } - if symbol.CheckFlags&ast.CheckFlagsMapped != 0 { - return c.getTypeOfMappedSymbol(symbol) - } - if symbol.CheckFlags&ast.CheckFlagsReverseMapped != 0 { - return c.getTypeOfReverseMappedSymbol(symbol) - } - if symbol.Flags&(ast.SymbolFlagsVariable|ast.SymbolFlagsProperty) != 0 { - return c.getTypeOfVariableOrParameterOrProperty(symbol) - } - if symbol.Flags&(ast.SymbolFlagsFunction|ast.SymbolFlagsMethod|ast.SymbolFlagsClass|ast.SymbolFlagsEnum|ast.SymbolFlagsValueModule) != 0 { - return c.getTypeOfFuncClassEnumModule(symbol) - } - if symbol.Flags&ast.SymbolFlagsEnumMember != 0 { - return c.getTypeOfEnumMember(symbol) - } - if symbol.Flags&ast.SymbolFlagsAccessor != 0 { - return c.getTypeOfAccessors(symbol) - } - if symbol.Flags&ast.SymbolFlagsAlias != 0 { - return c.getTypeOfAlias(symbol) - } - return c.errorType -} - -func (c *Checker) getNonMissingTypeOfSymbol(symbol *ast.Symbol) *Type { - return c.removeMissingType(c.getTypeOfSymbol(symbol), symbol.Flags&ast.SymbolFlagsOptional != 0) -} - -func (c *Checker) getTypeOfInstantiatedSymbol(symbol *ast.Symbol) *Type { - links := c.valueSymbolLinks.Get(symbol) - if links.resolvedType == nil { - links.resolvedType = c.instantiateType(c.getTypeOfSymbol(links.target), links.mapper) - } - return links.resolvedType -} - -func (c *Checker) getWriteTypeOfInstantiatedSymbol(symbol *ast.Symbol) *Type { - links := c.valueSymbolLinks.Get(symbol) - if links.writeType == nil { - links.writeType = c.instantiateType(c.getWriteTypeOfSymbol(links.target), links.mapper) - } - return links.writeType -} - -func (c *Checker) getTypeOfVariableOrParameterOrProperty(symbol *ast.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 *ast.Symbol) bool { - decl := symbol.ValueDeclaration - if decl == nil { - return false - } - if ast.IsBindingElement(decl) { - decl = ast.WalkUpBindingElementsAndPatterns(decl) - } - if ast.IsParameter(decl) { - return c.isContextSensitiveFunctionOrObjectLiteralMethod(decl.Parent) - } - return false -} - -func (c *Checker) getTypeOfVariableOrParameterOrPropertyWorker(symbol *ast.Symbol) *Type { - // Handle prototype property - if symbol.Flags&ast.SymbolFlagsPrototype != 0 { - return c.getTypeOfPrototypeProperty(symbol) - } - // CommonsJS require and module both have type any. - if symbol == c.requireSymbol { - return c.anyType - } - if symbol.Flags&ast.SymbolFlagsModuleExports != 0 && symbol.ValueDeclaration != nil { - fileSymbol := c.resolveExternalModuleSymbol(symbol.ValueDeclaration.Symbol(), false /*dontResolveAlias*/) - members := make(ast.SymbolTable, 1) - members["exports"] = fileSymbol - return c.newAnonymousType(symbol, members, nil, nil, nil) - } - debug.AssertIsDefined(symbol.ValueDeclaration) - declaration := symbol.ValueDeclaration - if ast.IsSourceFile(declaration) && ast.IsJsonSourceFile(declaration.AsSourceFile()) { - statements := declaration.AsSourceFile().Statements.Nodes - if len(statements) == 0 { - return c.emptyObjectType - } - return c.getWidenedType(c.getWidenedLiteralType(c.checkExpression(statements[0].Expression()))) - } - // Handle variable, parameter or property - if !c.pushTypeResolution(symbol, TypeSystemPropertyNameType) { - return c.reportCircularityError(symbol) - } - var result *Type - switch declaration.Kind { - case ast.KindParameter, ast.KindPropertyDeclaration, ast.KindPropertySignature, ast.KindVariableDeclaration, - ast.KindBindingElement: - result = c.getWidenedTypeForVariableLikeDeclaration(declaration, true /*reportErrors*/) - case ast.KindPropertyAssignment: - result = c.checkPropertyAssignment(declaration, CheckModeNormal) - case ast.KindShorthandPropertyAssignment: - result = c.checkShorthandPropertyAssignment(declaration, true /*inDestructuringPattern*/, CheckModeNormal) - case ast.KindMethodDeclaration: - result = c.checkObjectLiteralMethod(declaration, CheckModeNormal) - case ast.KindExportAssignment, ast.KindJSExportAssignment: - if declaration.Type() != nil { - result = c.getTypeFromTypeNode(declaration.Type()) - } else { - result = c.widenTypeForVariableLikeDeclaration(c.checkExpressionCached(declaration.AsExportAssignment().Expression), declaration, false /*reportErrors*/) - } - case ast.KindBinaryExpression: - result = c.getWidenedTypeForAssignmentDeclaration(symbol) - case ast.KindJsxAttribute: - result = c.checkJsxAttribute(declaration, CheckModeNormal) - case ast.KindEnumMember: - result = c.getTypeOfEnumMember(symbol) - case ast.KindCommonJSExport: - result = c.checkExpression(declaration.AsCommonJSExport().Initializer) - default: - panic("Unhandled case in getTypeOfVariableOrParameterOrPropertyWorker: " + declaration.Kind.String()) - } - 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 *ast.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 *ast.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 ast.IsVariableDeclaration(declaration) { - grandParent := declaration.Parent.Parent - switch grandParent.Kind { - case ast.KindForInStatement: - indexType := c.getIndexType(c.getNonNullableTypeIfNeeded(c.checkExpressionEx(grandParent.Expression(), checkMode /*checkMode*/))) - if indexType.flags&(TypeFlagsTypeParameter|TypeFlagsIndex) != 0 { - return c.getExtractStringType(indexType) - } - return c.stringType - case ast.KindForOfStatement: - // 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 ast.IsBindingElement(declaration) { - return c.getTypeForBindingElement(declaration) - } - isProperty := ast.IsPropertyDeclaration(declaration) && !ast.HasAccessorModifier(declaration) || ast.IsPropertySignatureDeclaration(declaration) - isOptional := includeOptionality && isOptionalDeclaration(declaration) - // Use type from type annotation if one is present - declaredType := c.tryGetTypeFromTypeNode(declaration) - if ast.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 && ast.IsVariableDeclaration(declaration) && !ast.IsBindingPattern(declaration.Name()) && - c.getCombinedModifierFlagsCached(declaration)&ast.ModifierFlagsExport == 0 && declaration.Flags&ast.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. - initializer := declaration.Initializer() - if c.getCombinedNodeFlagsCached(declaration)&ast.NodeFlagsConstant == 0 && (initializer == nil || c.isNullOrUndefined(initializer)) { - return c.autoType - } - // Use control flow tracked 'any[]' type for non-ambient, non-exported variables with an empty array - // literal initializer. - if initializer != nil && isEmptyArrayLiteral(initializer) { - return c.autoArrayType - } - } - if ast.IsParameter(declaration) { - fn := declaration.Parent - // For a parameter of a set accessor, use the type of the get accessor if one is present - if ast.IsSetAccessorDeclaration(fn) && c.hasBindableName(fn) { - getter := ast.GetDeclarationOfKind(c.getSymbolOfDeclaration(declaration.Parent), ast.KindGetAccessor) - if getter != nil { - getterSignature := c.getSignatureFromDeclaration(getter) - thisParameter := c.getAccessorThisParameter(fn) - if thisParameter != nil && declaration == thisParameter { - // Use the type from the *getter* - debug.AssertNil(thisParameter.Type()) - return c.getTypeOfSymbol(getterSignature.thisParameter) - } - return c.getReturnTypeOfSignature(getterSignature) - } - } - if t := c.getParameterTypeOfFullSignature(fn, declaration); t != nil { - return t - } - // Use contextual parameter type if one is available - var t *Type - if declaration.Symbol().Name == ast.InternalSymbolNameThis { - t = c.getContextualThisParameterType(fn) - } else { - t = c.getContextuallyTypedParameterType(declaration) - } - if t != nil { - return c.addOptionalityEx(t, false /*isProperty*/, 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 declaration.Initializer() != nil { - t := c.widenTypeInferredFromInitializer(declaration, c.checkDeclarationInitializer(declaration, checkMode, nil /*contextualType*/)) - return c.addOptionalityEx(t, isProperty, isOptional) - } - if c.noImplicitAny && ast.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 !ast.HasStaticModifier(declaration) { - constructor := ast.FindConstructorDeclaration(declaration.Parent) - var t *Type - switch { - case constructor != nil: - t = c.getFlowTypeInConstructor(declaration.Symbol(), constructor) - case declaration.ModifierFlags()&ast.ModifierFlagsAmbient != 0: - t = c.getTypeOfPropertyInBaseClass(declaration.Symbol()) - } - if t == nil { - return nil - } - return c.addOptionalityEx(t, true /*isProperty*/, isOptional) - } else { - staticBlocks := core.Filter(declaration.Parent.Members(), ast.IsClassStaticBlockDeclaration) - var t *Type - switch { - case len(staticBlocks) != 0: - t = c.getFlowTypeInStaticBlocks(declaration.Symbol(), staticBlocks) - case declaration.ModifierFlags()&ast.ModifierFlagsAmbient != 0: - t = c.getTypeOfPropertyInBaseClass(declaration.Symbol()) - } - if t == nil { - return nil - } - return c.addOptionalityEx(t, true /*isProperty*/, isOptional) - } - } - if ast.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 ast.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 *ast.Node, checkMode CheckMode, contextualType *Type) *Type { - initializer := declaration.Initializer() - 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 ast.IsParameter(ast.GetRootDeclaration(declaration)) { - name := declaration.Name() - switch name.Kind { - case ast.KindObjectBindingPattern: - if isObjectLiteralType(t) { - return c.padObjectLiteralType(t, name) - } - case ast.KindArrayBindingPattern: - if isTupleType(t) { - return c.padTupleType(t, name) - } - } - } - return t -} - -func (c *Checker) padObjectLiteralType(t *Type, pattern *ast.Node) *Type { - var missingElements []*ast.Node - for _, e := range pattern.AsBindingPattern().Elements.Nodes { - if e.Initializer() != nil { - name := c.getPropertyNameFromBindingElement(e) - if name != ast.InternalSymbolNameMissing && c.getPropertyOfType(t, name) == nil { - missingElements = append(missingElements, e) - } - } - } - if len(missingElements) == 0 { - return t - } - members := make(ast.SymbolTable) - for _, prop := range c.getPropertiesOfObjectType(t) { - members[prop.Name] = prop - } - for _, e := range missingElements { - symbol := c.newSymbol(ast.SymbolFlagsProperty|ast.SymbolFlagsOptional, c.getPropertyNameFromBindingElement(e)) - c.valueSymbolLinks.Get(symbol).resolvedType = c.getTypeFromBindingElement(e, false /*includePatternInType*/, false /*reportErrors*/) - members[symbol.Name] = symbol - } - result := c.newAnonymousType(t.symbol, members, nil, nil, c.getIndexInfosOfType(t)) - result.objectFlags = t.objectFlags - return result -} - -func (c *Checker) getPropertyNameFromBindingElement(e *ast.Node) string { - exprType := c.getLiteralTypeFromPropertyName(e.PropertyNameOrName()) - if isTypeUsableAsPropertyName(exprType) { - return getPropertyNameFromType(exprType) - } - return ast.InternalSymbolNameMissing -} - -func (c *Checker) padTupleType(t *Type, pattern *ast.Node) *Type { - patternElements := pattern.AsBindingPattern().Elements.Nodes - if t.TargetTupleType().combinedFlags&ElementFlagsVariable != 0 || c.getTypeReferenceArity(t) >= len(patternElements) { - return t - } - elementTypes := slices.Clone(c.getElementTypes(t)) - elementInfos := slices.Clone(t.TargetTupleType().elementInfos) - for i := c.getTypeReferenceArity(t); i < len(patternElements); i++ { - e := patternElements[i] - if i < len(patternElements)-1 || !(ast.IsBindingElement(e) && hasDotDotDotToken(e)) { - elementType := c.anyType - if !ast.IsOmittedExpression(e) && c.hasDefaultValue(e) { - elementType = c.getTypeFromBindingElement(e, false /*includePatternInType*/, false /*reportErrors*/) - } - elementTypes = append(elementTypes, elementType) - elementInfos = append(elementInfos, TupleElementInfo{flags: ElementFlagsOptional}) - if !ast.IsOmittedExpression(e) && !c.hasDefaultValue(e) { - c.reportImplicitAny(e, c.anyType, WideningKindNormal) - } - } - } - return c.createTupleTypeEx(elementTypes, elementInfos, t.TargetTupleType().readonly) -} - -func (c *Checker) widenTypeInferredFromInitializer(declaration *ast.Node, t *Type) *Type { - widened := c.getWidenedLiteralTypeForInitializer(declaration, t) - if ast.IsInJSFile(declaration) { - if c.isEmptyLiteralType(widened) { - c.reportImplicitAny(declaration, c.anyType, WideningKindNormal) - return c.anyType - } - if c.isEmptyArrayLiteralType(widened) { - c.reportImplicitAny(declaration, c.anyArrayType, WideningKindNormal) - return c.anyArrayType - } - } - return widened -} - -func (c *Checker) getWidenedLiteralTypeForInitializer(declaration *ast.Node, t *Type) *Type { - if c.getCombinedNodeFlagsCached(declaration)&ast.NodeFlagsConstant != 0 || isDeclarationReadonly(declaration) { - return t - } - return c.getWidenedLiteralType(t) -} - -func (c *Checker) getTypeOfFuncClassEnumModule(symbol *ast.Symbol) *Type { - links := c.valueSymbolLinks.Get(symbol) - if links.resolvedType == nil { - links.resolvedType = c.getTypeOfFuncClassEnumModuleWorker(symbol) - } - return links.resolvedType -} - -func (c *Checker) getTypeOfFuncClassEnumModuleWorker(symbol *ast.Symbol) *Type { - if symbol.Flags&ast.SymbolFlagsModule != 0 && isShorthandAmbientModuleSymbol(symbol) { - return c.anyType - } - t := c.newObjectType(ObjectFlagsAnonymous, symbol) - if symbol.Flags&ast.SymbolFlagsClass != 0 { - baseTypeVariable := c.getBaseTypeVariableOfClass(symbol) - if baseTypeVariable != nil { - return c.getIntersectionType([]*Type{t, baseTypeVariable}) - } - return t - } - if c.strictNullChecks && symbol.Flags&ast.SymbolFlagsOptional != 0 { - return c.getOptionalType(t /*isProperty*/, true) - } - return t -} - -func (c *Checker) getBaseTypeVariableOfClass(symbol *ast.Symbol) *Type { - baseConstructorType := c.getBaseConstructorTypeOfClass(c.getDeclaredTypeOfClassOrInterface(symbol)) - switch { - case baseConstructorType.flags&TypeFlagsTypeVariable != 0: - return baseConstructorType - case baseConstructorType.flags&TypeFlagsIntersection != 0: - return core.Find(baseConstructorType.Types(), func(t *Type) bool { - return t.flags&TypeFlagsTypeVariable != 0 - }) - } - return nil -} - -/** - * The base constructor of a class can resolve to - * * undefinedType if the class has no extends clause, - * * errorType if an error occurred during resolution of the extends expression, - * * nullType if the extends expression is the null value, - * * anyType if the extends expression has type any, or - * * an object type with at least one construct signature. - */ -func (c *Checker) getBaseConstructorTypeOfClass(t *Type) *Type { - data := t.AsInterfaceType() - if data.resolvedBaseConstructorType != nil { - return data.resolvedBaseConstructorType - } - baseTypeNode := getBaseTypeNodeOfClass(t) - if baseTypeNode == nil { - data.resolvedBaseConstructorType = c.undefinedType - return data.resolvedBaseConstructorType - } - if !c.pushTypeResolution(t, TypeSystemPropertyNameResolvedBaseConstructorType) { - return c.errorType - } - baseConstructorType := c.checkExpression(baseTypeNode.Expression()) - if baseConstructorType.flags&(TypeFlagsObject|TypeFlagsIntersection) != 0 { - // Resolving the members of a class requires us to resolve the base class of that class. - // We force resolution here such that we catch circularities now. - c.resolveStructuredTypeMembers(baseConstructorType) - } - if !c.popTypeResolution() { - c.error(t.symbol.ValueDeclaration, diagnostics.X_0_is_referenced_directly_or_indirectly_in_its_own_base_expression, c.symbolToString(t.symbol)) - if data.resolvedBaseConstructorType == nil { - data.resolvedBaseConstructorType = c.errorType - } - return data.resolvedBaseConstructorType - } - if baseConstructorType.flags&TypeFlagsAny == 0 && baseConstructorType != c.nullWideningType && !c.isConstructorType(baseConstructorType) { - err := c.error(baseTypeNode.Expression(), diagnostics.Type_0_is_not_a_constructor_function_type, c.TypeToString(baseConstructorType)) - if baseConstructorType.flags&TypeFlagsTypeParameter != 0 { - constraint := c.getConstraintFromTypeParameter(baseConstructorType) - var ctorReturn *Type = c.unknownType - if constraint != nil { - ctorSigs := c.getSignaturesOfType(constraint, SignatureKindConstruct) - if len(ctorSigs) != 0 { - ctorReturn = c.getReturnTypeOfSignature(ctorSigs[0]) - } - } - if baseConstructorType.symbol.Declarations != nil { - err.AddRelatedInfo(createDiagnosticForNode(baseConstructorType.symbol.Declarations[0], diagnostics.Did_you_mean_for_0_to_be_constrained_to_type_new_args_Colon_any_1, c.symbolToString(baseConstructorType.symbol), c.TypeToString(ctorReturn))) - } - } - if data.resolvedBaseConstructorType == nil { - data.resolvedBaseConstructorType = c.errorType - } - return data.resolvedBaseConstructorType - } - if data.resolvedBaseConstructorType == nil { - data.resolvedBaseConstructorType = baseConstructorType - } - return data.resolvedBaseConstructorType -} - -func (c *Checker) isFunctionType(t *Type) bool { - return t.flags&TypeFlagsObject != 0 && len(c.getSignaturesOfType(t, SignatureKindCall)) > 0 -} - -func (c *Checker) isConstructorType(t *Type) bool { - if len(c.getSignaturesOfType(t, SignatureKindConstruct)) > 0 { - return true - } - if t.flags&TypeFlagsTypeVariable != 0 { - constraint := c.getBaseConstraintOfType(t) - return constraint != nil && c.isMixinConstructorType(constraint) - } - return false -} - -// A type is a mixin constructor if it has a single construct signature taking no type parameters and a single -// rest parameter of type any[]. -func (c *Checker) isMixinConstructorType(t *Type) bool { - signatures := c.getSignaturesOfType(t, SignatureKindConstruct) - if len(signatures) == 1 { - s := signatures[0] - if len(s.typeParameters) == 0 && len(s.parameters) == 1 && signatureHasRestParameter(s) { - paramType := c.getTypeOfParameter(s.parameters[0]) - return IsTypeAny(paramType) || c.getElementTypeOfArrayType(paramType) == c.anyType - } - } - return false -} - -func signatureHasRestParameter(sig *Signature) bool { - return sig.flags&SignatureFlagsHasRestParameter != 0 -} - -func (c *Checker) getTypeOfParameter(symbol *ast.Symbol) *Type { - declaration := symbol.ValueDeclaration - return c.addOptionalityEx(c.getTypeOfSymbol(symbol), false, declaration != nil && (declaration.Initializer() != nil || isOptionalDeclaration(declaration))) -} - -func (c *Checker) getConstraintOfType(t *Type) *Type { - switch { - case t.flags&TypeFlagsTypeParameter != 0: - return c.getConstraintOfTypeParameter(t) - case t.flags&TypeFlagsIndexedAccess != 0: - return c.getConstraintOfIndexedAccess(t) - case t.flags&TypeFlagsConditional != 0: - return c.getConstraintOfConditionalType(t) - } - return c.getBaseConstraintOfType(t) -} - -func (c *Checker) getConstraintOfTypeParameter(typeParameter *Type) *Type { - if c.hasNonCircularBaseConstraint(typeParameter) { - return c.getConstraintFromTypeParameter(typeParameter) - } - return nil -} - -func (c *Checker) hasNonCircularBaseConstraint(t *Type) bool { - return c.getResolvedBaseConstraint(t, nil) != c.circularConstraintType -} - -// This is a worker function. Use getConstraintOfTypeParameter which guards against circular constraints -func (c *Checker) getConstraintFromTypeParameter(t *Type) *Type { - if t.flags&TypeFlagsTypeParameter == 0 { - return nil - } - - tp := t.AsTypeParameter() - if tp.constraint == nil { - var constraint *Type - if tp.target != nil { - constraint = c.instantiateType(c.getConstraintOfTypeParameter(tp.target), tp.mapper) - } else { - constraintDeclaration := c.getConstraintDeclaration(t) - if constraintDeclaration != nil { - constraint = c.getTypeFromTypeNode(constraintDeclaration) - if constraint.flags&TypeFlagsAny != 0 && !c.isErrorType(constraint) { - // use stringNumberSymbolType as the base constraint for mapped type key constraints (unknown isn;t assignable to that, but `any` was), - // use unknown otherwise - if ast.IsMappedTypeNode(constraintDeclaration.Parent.Parent) { - constraint = c.stringNumberSymbolType - } else { - constraint = c.unknownType - } - } - } else { - constraint = c.getInferredTypeParameterConstraint(t, false) - } - } - if constraint == nil { - constraint = c.noConstraintType - } - tp.constraint = constraint - } - if tp.constraint != c.noConstraintType { - return tp.constraint - } - return nil -} - -func (c *Checker) getConstraintOrUnknownFromTypeParameter(t *Type) *Type { - result := c.getConstraintFromTypeParameter(t) - return core.IfElse(result != nil, result, c.unknownType) -} - -func (c *Checker) getInferredTypeParameterConstraint(t *Type, omitTypeReferences bool) *Type { - var inferences []*Type - if t.symbol != nil && len(t.symbol.Declarations) != 0 { - for _, declaration := range t.symbol.Declarations { - if ast.IsInferTypeNode(declaration.Parent) { - // When an 'infer T' declaration is immediately contained in a type reference node - // (such as 'Foo'), T's constraint is inferred from the constraint of the - // corresponding type parameter in 'Foo'. When multiple 'infer T' declarations are - // present, we form an intersection of the inferred constraint types. - child := declaration.Parent - parent := child.Parent - for parent != nil && ast.IsParenthesizedTypeNode(parent) { - child = parent - parent = child.Parent - } - switch { - case ast.IsTypeReferenceNode(parent) && !omitTypeReferences: - typeParameters := c.getTypeParametersForTypeReferenceOrImport(parent) - if typeParameters != nil { - index := slices.Index(parent.TypeArguments(), child) - if index >= 0 && index < len(typeParameters) { - declaredConstraint := c.getConstraintOfTypeParameter(typeParameters[index]) - if declaredConstraint != nil { - // Type parameter constraints can reference other type parameters so - // constraints need to be instantiated. If instantiation produces the - // type parameter itself, we discard that inference. For example, in - // type Foo = [T, U]; - // type Bar = T extends Foo ? Foo : T; - // the instantiated constraint for U is X, so we discard that inference. - mapper := newDeferredTypeMapper(typeParameters, core.MapIndex(typeParameters, func(_ *Type, index int) func() *Type { - return func() *Type { - return c.getEffectiveTypeArgumentAtIndex(parent, typeParameters, index) - } - })) - constraint := c.instantiateType(declaredConstraint, mapper) - if constraint != t { - inferences = append(inferences, constraint) - } - } - } - } - case ast.IsParameter(parent) && parent.AsParameterDeclaration().DotDotDotToken != nil || - ast.IsRestTypeNode(parent) || - ast.IsNamedTupleMember(parent) && parent.AsNamedTupleMember().DotDotDotToken != nil: - inferences = append(inferences, c.createArrayType(c.unknownType)) - case ast.IsTemplateLiteralTypeSpan(parent): - inferences = append(inferences, c.stringType) - case ast.IsTypeParameterDeclaration(parent) && ast.IsMappedTypeNode(parent.Parent): - inferences = append(inferences, c.stringNumberSymbolType) - case ast.IsMappedTypeNode(parent) && parent.AsMappedTypeNode().Type != nil && - ast.SkipParentheses(parent.AsMappedTypeNode().Type) == declaration.Parent && - ast.IsConditionalTypeNode(parent.Parent) && - parent.Parent.AsConditionalTypeNode().ExtendsType == parent && - ast.IsMappedTypeNode(parent.Parent.AsConditionalTypeNode().CheckType) && - parent.Parent.AsConditionalTypeNode().CheckType.AsMappedTypeNode().Type != nil: - checkMappedType := parent.Parent.AsConditionalTypeNode().CheckType - nodeType := c.getTypeFromTypeNode(checkMappedType.AsMappedTypeNode().Type) - checkMappedTypeParameter := checkMappedType.AsMappedTypeNode().TypeParameter - mapper := newSimpleTypeMapper(c.getDeclaredTypeOfTypeParameter(c.getSymbolOfDeclaration(checkMappedTypeParameter)), - core.IfElse(checkMappedTypeParameter.AsTypeParameter().Constraint != nil, - c.getTypeFromTypeNode(checkMappedTypeParameter.AsTypeParameter().Constraint), - c.stringNumberSymbolType)) - inferences = append(inferences, c.instantiateType(nodeType, mapper)) - } - } - } - } - if len(inferences) != 0 { - return c.getIntersectionType(inferences) - } - return nil -} - -func (c *Checker) getTypeParametersForTypeReferenceOrImport(node *ast.Node) []*Type { - t := c.getTypeFromTypeNode(node) - if !c.isErrorType(t) { - symbol := c.getResolvedSymbolOrNil(node) - if symbol != nil { - return c.getTypeParametersForTypeAndSymbol(t, symbol) - } - } - return nil -} - -func (c *Checker) getTypeParametersForTypeAndSymbol(t *Type, symbol *ast.Symbol) []*Type { - if !c.isErrorType(t) { - if symbol.Flags&ast.SymbolFlagsTypeAlias != 0 { - if typeParameters := c.typeAliasLinks.Get(symbol).typeParameters; len(typeParameters) != 0 { - return typeParameters - } - } - if t.objectFlags&ObjectFlagsReference != 0 { - return t.Target().AsInterfaceType().LocalTypeParameters() - } - } - return nil -} - -func (c *Checker) getEffectiveTypeArgumentAtIndex(node *ast.Node, typeParameters []*Type, index int) *Type { - typeArguments := node.TypeArguments() - if index < len(typeArguments) { - return c.getTypeFromTypeNode(typeArguments[index]) - } - return c.getEffectiveTypeArguments(node, typeParameters)[index] -} - -func (c *Checker) getConstraintOfIndexedAccess(t *Type) *Type { - if c.hasNonCircularBaseConstraint(t) { - return c.getConstraintFromIndexedAccess(t) - } - return nil -} - -func (c *Checker) getConstraintFromIndexedAccess(t *Type) *Type { - d := t.AsIndexedAccessType() - if c.isMappedTypeGenericIndexedAccess(t) { - // For indexed access types of the form { [P in K]: E }[X], where K is non-generic and X is generic, - // we substitute an instantiation of E where P is replaced with X. - return c.substituteIndexedMappedType(d.objectType, d.indexType) - } - indexConstraint := c.getSimplifiedTypeOrConstraint(d.indexType) - if indexConstraint != nil && indexConstraint != d.indexType { - indexedAccess := c.getIndexedAccessTypeOrUndefined(d.objectType, indexConstraint, d.accessFlags, nil, nil) - if indexedAccess != nil { - return indexedAccess - } - } - objectConstraint := c.getSimplifiedTypeOrConstraint(d.objectType) - if objectConstraint != nil && objectConstraint != d.objectType { - return c.getIndexedAccessTypeOrUndefined(objectConstraint, d.indexType, d.accessFlags, nil, nil) - } - return nil -} - -func (c *Checker) getConstraintOfConditionalType(t *Type) *Type { - if c.hasNonCircularBaseConstraint(t) { - return c.getConstraintFromConditionalType(t) - } - return nil -} - -func (c *Checker) getConstraintFromConditionalType(t *Type) *Type { - constraint := c.getConstraintOfDistributiveConditionalType(t) - if constraint != nil { - return constraint - } - return c.getDefaultConstraintOfConditionalType(t) -} - -func (c *Checker) getDefaultConstraintOfConditionalType(t *Type) *Type { - d := t.AsConditionalType() - if d.resolvedDefaultConstraint == nil { - // An `any` branch of a conditional type would normally be viral - specifically, without special handling here, - // a conditional type with a single branch of type `any` would be assignable to anything, since it's constraint would simplify to - // just `any`. This result is _usually_ unwanted - so instead here we elide an `any` branch from the constraint type, - // in effect treating `any` like `never` rather than `unknown` in this location. - trueConstraint := c.getInferredTrueTypeFromConditionalType(t) - falseConstraint := c.getFalseTypeFromConditionalType(t) - switch { - case IsTypeAny(trueConstraint): - d.resolvedDefaultConstraint = falseConstraint - case IsTypeAny(falseConstraint): - d.resolvedDefaultConstraint = trueConstraint - default: - d.resolvedDefaultConstraint = c.getUnionType([]*Type{trueConstraint, falseConstraint}) - } - } - return d.resolvedDefaultConstraint -} - -func (c *Checker) getConstraintOfDistributiveConditionalType(t *Type) *Type { - d := t.AsConditionalType() - if d.resolvedConstraintOfDistributive == nil { - // Check if we have a conditional type of the form 'T extends U ? X : Y', where T is a constrained - // type parameter. If so, create an instantiation of the conditional type where T is replaced - // with its constraint. We do this because if the constraint is a union type it will be distributed - // over the conditional type and possibly reduced. For example, 'T extends undefined ? never : T' - // removes 'undefined' from T. - // We skip returning a distributive constraint for a restrictive instantiation of a conditional type - // as the constraint for all type params (check type included) have been replace with `unknown`, which - // is going to produce even more false positive/negative results than the distribute constraint already does. - // Please note: the distributive constraint is a kludge for emulating what a negated type could to do filter - // a union - once negated types exist and are applied to the conditional false branch, this "constraint" - // likely doesn't need to exist. - if d.root.isDistributive && c.cachedTypes[CachedTypeKey{kind: CachedTypeKindRestrictiveInstantiation, typeId: t.id}] != t { - constraint := c.getSimplifiedType(d.checkType, false /*writing*/) - if constraint == d.checkType { - constraint = c.getConstraintOfType(constraint) - } - if constraint != nil && constraint != d.checkType { - instantiated := c.getConditionalTypeInstantiation(t, prependTypeMapping(d.root.checkType, constraint, d.mapper), true /*forConstraint*/, nil) - if instantiated.flags&TypeFlagsNever == 0 { - d.resolvedConstraintOfDistributive = instantiated - return instantiated - } - } - } - d.resolvedConstraintOfDistributive = c.noConstraintType - } - if d.resolvedConstraintOfDistributive != c.noConstraintType { - return d.resolvedConstraintOfDistributive - } - return nil -} - -func (c *Checker) getDeclaredTypeOfClassOrInterface(symbol *ast.Symbol) *Type { - links := c.declaredTypeLinks.Get(symbol) - if links.declaredType == nil { - kind := core.IfElse(symbol.Flags&ast.SymbolFlagsClass != 0, ObjectFlagsClass, ObjectFlagsInterface) - t := c.newObjectType(kind, symbol) - links.declaredType = t - outerTypeParameters := c.getOuterTypeParametersOfClassOrInterface(symbol) - typeParameters := c.appendLocalTypeParametersOfClassOrInterfaceOrTypeAlias(outerTypeParameters, 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 typeParameters != nil || kind == ObjectFlagsClass || !c.isThislessInterface(symbol) { - t.objectFlags |= ObjectFlagsReference - d := t.AsInterfaceType() - d.thisType = c.newTypeParameter(symbol) - d.thisType.AsTypeParameter().isThisType = true - d.thisType.AsTypeParameter().constraint = t - d.allTypeParameters = append(typeParameters, d.thisType) - d.outerTypeParameterCount = len(outerTypeParameters) - d.resolvedTypeArguments = d.TypeParameters() - d.instantiations = make(map[string]*Type) - d.instantiations[getTypeListKey(d.resolvedTypeArguments)] = t - d.target = t - } - } - return links.declaredType -} - -/** - * Returns true if the interface given by the symbol is free of "this" references. - * - * Specifically, the result is true if the interface itself contains no references - * to "this" in its body, if all base types are interfaces, - * and if none of the base interfaces have a "this" type. - */ -func (c *Checker) isThislessInterface(symbol *ast.Symbol) bool { - for _, declaration := range symbol.Declarations { - if ast.IsInterfaceDeclaration(declaration) { - if declaration.Flags&ast.NodeFlagsContainsThis != 0 { - return false - } - baseTypeNodes := ast.GetExtendsHeritageClauseElements(declaration) - for _, node := range baseTypeNodes { - if ast.IsEntityNameExpression(node.Expression()) { - baseSymbol := c.resolveEntityName(node.Expression(), ast.SymbolFlagsType, true /*ignoreErrors*/, false, nil) - if baseSymbol == nil || baseSymbol.Flags&ast.SymbolFlagsInterface == 0 || c.getDeclaredTypeOfClassOrInterface(baseSymbol).AsInterfaceType().thisType != nil { - return false - } - } - } - } - } - return true -} - -type KeyBuilder struct { - strings.Builder -} - -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 (b *KeyBuilder) WriteUint64(value uint64) { - for value != 0 { - b.WriteByte(base64chars[value&0x3F]) - value >>= 6 - } -} - -func (b *KeyBuilder) WriteInt(value int) { - b.WriteUint64(uint64(int64(value))) -} - -func (b *KeyBuilder) WriteSymbolId(id ast.SymbolId) { - b.WriteUint64(uint64(id)) -} - -func (b *KeyBuilder) WriteSymbol(s *ast.Symbol) { - b.WriteSymbolId(ast.GetSymbolId(s)) -} - -func (b *KeyBuilder) WriteTypeId(id TypeId) { - b.WriteUint64(uint64(id)) -} - -func (b *KeyBuilder) WriteType(t *Type) { - b.WriteTypeId(t.id) -} - -func (b *KeyBuilder) WriteTypes(types []*Type) { - i := 0 - var tail bool - for i < len(types) { - startId := types[i].id - count := 1 - for i+count < len(types) && types[i+count].id == startId+TypeId(count) { - count++ - } - if tail { - b.WriteByte(',') - } - b.WriteTypeId(startId) - if count > 1 { - b.WriteByte(':') - b.WriteInt(count) - } - i += count - tail = true - } -} - -func (b *KeyBuilder) WriteAlias(alias *TypeAlias) { - if alias != nil { - b.WriteByte('@') - b.WriteSymbol(alias.symbol) - if len(alias.typeArguments) != 0 { - b.WriteByte(':') - b.WriteTypes(alias.typeArguments) - } - } -} - -func (b *KeyBuilder) WriteGenericTypeReferences(source *Type, target *Type, ignoreConstraints bool) bool { - var constrained bool - typeParameters := make([]*Type, 0, 8) - var writeTypeReference func(*Type, int) - // writeTypeReference(A) writes "111=0-12=1" - // where A.id=111 and number.id=12 - writeTypeReference = func(ref *Type, depth int) { - b.WriteType(ref.Target()) - for _, t := range ref.AsTypeReference().resolvedTypeArguments { - if t.flags&TypeFlagsTypeParameter != 0 { - if ignoreConstraints || t.checker.getConstraintOfTypeParameter(t) == nil { - index := slices.Index(typeParameters, t) - if index < 0 { - index = len(typeParameters) - typeParameters = append(typeParameters, t) - } - b.WriteByte('=') - b.WriteInt(index) - continue - } - constrained = true - } else if depth < 4 && isTypeReferenceWithGenericArguments(t) { - b.WriteByte('<') - writeTypeReference(t, depth+1) - b.WriteByte('>') - continue - } - b.WriteByte('-') - b.WriteType(t) - } - } - writeTypeReference(source, 0) - b.WriteByte(',') - writeTypeReference(target, 0) - return constrained -} - -func (b *KeyBuilder) WriteNodeId(id ast.NodeId) { - b.WriteUint64(uint64(id)) -} - -func (b *KeyBuilder) WriteNode(node *ast.Node) { - if node != nil { - b.WriteNodeId(ast.GetNodeId(node)) - } -} - -func getTypeListKey(types []*Type) string { - var b KeyBuilder - b.WriteTypes(types) - return b.String() -} - -func getAliasKey(alias *TypeAlias) string { - var b KeyBuilder - b.WriteAlias(alias) - return b.String() -} - -func getUnionKey(types []*Type, origin *Type, alias *TypeAlias) string { - var b KeyBuilder - switch { - case origin == nil: - b.WriteTypes(types) - case origin.flags&TypeFlagsUnion != 0: - b.WriteByte('|') - b.WriteTypes(origin.Types()) - case origin.flags&TypeFlagsIntersection != 0: - b.WriteByte('&') - b.WriteTypes(origin.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 - b.WriteByte('#') - b.WriteType(origin) - b.WriteByte('|') - b.WriteTypes(types) - default: - panic("Unhandled case in getUnionKey") - } - b.WriteAlias(alias) - return b.String() -} - -func getIntersectionKey(types []*Type, flags IntersectionFlags, alias *TypeAlias) string { - var b KeyBuilder - b.WriteTypes(types) - if flags&IntersectionFlagsNoConstraintReduction == 0 { - b.WriteAlias(alias) - } else { - b.WriteByte('*') - } - return b.String() -} - -func getTupleKey(elementInfos []TupleElementInfo, readonly bool) string { - var b KeyBuilder - for _, e := range elementInfos { - switch { - case e.flags&ElementFlagsRequired != 0: - b.WriteByte('#') - case e.flags&ElementFlagsOptional != 0: - b.WriteByte('?') - case e.flags&ElementFlagsRest != 0: - b.WriteByte('.') - default: - b.WriteByte('*') - } - if e.labeledDeclaration != nil { - b.WriteNode(e.labeledDeclaration) - } - } - if readonly { - b.WriteByte('!') - } - return b.String() -} - -func getTypeAliasInstantiationKey(typeArguments []*Type, alias *TypeAlias) string { - return getTypeInstantiationKey(typeArguments, alias, false) -} - -func getTypeInstantiationKey(typeArguments []*Type, alias *TypeAlias, singleSignature bool) string { - var b KeyBuilder - b.WriteTypes(typeArguments) - b.WriteAlias(alias) - if singleSignature { - b.WriteByte('!') - } - return b.String() -} - -func getIndexedAccessKey(objectType *Type, indexType *Type, accessFlags AccessFlags, alias *TypeAlias) string { - var b KeyBuilder - b.WriteType(objectType) - b.WriteByte(',') - b.WriteType(indexType) - b.WriteByte(',') - b.WriteUint64(uint64(accessFlags)) - b.WriteAlias(alias) - return b.String() -} - -func getTemplateTypeKey(texts []string, types []*Type) string { - var b KeyBuilder - b.WriteTypes(types) - b.WriteByte('|') - for i, s := range texts { - if i != 0 { - b.WriteByte(',') - } - b.WriteInt(len(s)) - } - b.WriteByte('|') - for _, s := range texts { - b.WriteString(s) - } - return b.String() -} - -func getConditionalTypeKey(typeArguments []*Type, alias *TypeAlias, forConstraint bool) string { - var b KeyBuilder - b.WriteTypes(typeArguments) - b.WriteAlias(alias) - if forConstraint { - b.WriteByte('!') - } - return b.String() -} - -func getRelationKey(source *Type, target *Type, intersectionState IntersectionState, isIdentity bool, ignoreConstraints bool) string { - if isIdentity && source.id > target.id { - source, target = target, source - } - var b KeyBuilder - var constrained bool - if isTypeReferenceWithGenericArguments(source) && isTypeReferenceWithGenericArguments(target) { - constrained = b.WriteGenericTypeReferences(source, target, ignoreConstraints) - } else { - b.WriteType(source) - b.WriteByte(',') - b.WriteType(target) - } - if intersectionState != IntersectionStateNone { - b.WriteByte(':') - b.WriteUint64(uint64(intersectionState)) - } - if constrained { - // We mark keys with type references that reference constrained type parameters such that we know - // to obtain and look for a "broadest equivalent key" in the cache. - b.WriteByte('*') - } - return b.String() -} - -func getNodeListKey(nodes []*ast.Node) string { - var b KeyBuilder - for i, n := range nodes { - if i > 0 { - b.WriteByte(',') - } - b.WriteNode(n) - } - return b.String() -} - -func isTypeReferenceWithGenericArguments(t *Type) bool { - return isNonDeferredTypeReference(t) && core.Some(t.checker.getTypeArguments(t), func(t *Type) bool { - return t.flags&TypeFlagsTypeParameter != 0 || isTypeReferenceWithGenericArguments(t) - }) -} - -func isNonDeferredTypeReference(t *Type) bool { - return t.objectFlags&ObjectFlagsReference != 0 && t.AsTypeReference().node == nil -} - -// Return true if type parameter originates in an unconstrained declaration in a type parameter list -func isUnconstrainedTypeParameter(tp *Type) bool { - target := tp.Target() - if target == nil { - target = tp - } - if target.symbol == nil { - return false - } - for _, d := range target.symbol.Declarations { - if ast.IsTypeParameterDeclaration(d) && (d.AsTypeParameter().Constraint != nil || ast.IsMappedTypeNode(d.Parent) || ast.IsInferTypeNode(d.Parent)) { - return false - } - } - return true -} - -func (c *Checker) isNullOrUndefined(node *ast.Node) bool { - expr := ast.SkipParentheses(node) - switch expr.Kind { - case ast.KindNullKeyword: - return true - case ast.KindIdentifier: - return c.getResolvedSymbol(expr) == c.undefinedSymbol - } - return false -} - -func (c *Checker) checkRightHandSideOfForOf(statement *ast.Node) *Type { - use := core.IfElse(statement.AsForInOrOfStatement().AwaitModifier != nil, IterationUseForAwaitOf, IterationUseForOf) - return c.checkIteratedTypeOrElementType(use, c.checkNonNullExpression(statement.Expression()), c.undefinedType, statement.Expression()) -} - -// Return the inferred type for a binding element -func (c *Checker) getTypeForBindingElement(declaration *ast.Node) *Type { - checkMode := core.IfElse(hasDotDotDotToken(declaration), CheckModeRestBindingElement, CheckModeNormal) - parentType := c.getTypeForBindingElementParent(declaration.Parent.Parent, checkMode) - if parentType != nil { - return c.getBindingElementTypeFromParentType(declaration, parentType, false /*noTupleBoundsCheck*/) - } - return nil -} - -// Return the type of a binding element parent. We check SymbolLinks first to see if a type has been -// assigned by contextual typing. -func (c *Checker) getTypeForBindingElementParent(node *ast.Node, checkMode CheckMode) *Type { - if checkMode != CheckModeNormal { - return c.getTypeForVariableLikeDeclaration(node, false /*includeOptionality*/, checkMode) - } - symbol := c.getSymbolOfDeclaration(node) - if symbol != nil { - resolvedType := c.valueSymbolLinks.Get(symbol).resolvedType - if resolvedType != nil { - return resolvedType - } - } - return c.getTypeForVariableLikeDeclaration(node, false /*includeOptionality*/, checkMode) -} - -func (c *Checker) getBindingElementTypeFromParentType(declaration *ast.Node, parentType *Type, noTupleBoundsCheck bool) *Type { - // If an any type was inferred for parent, infer that for the binding element - if IsTypeAny(parentType) { - return parentType - } - pattern := declaration.Parent - // Relax null check on ambient destructuring parameters, since the parameters have no implementation and are just documentation - if c.strictNullChecks && declaration.Flags&ast.NodeFlagsAmbient != 0 && ast.IsPartOfParameterDeclaration(declaration) { - parentType = c.GetNonNullableType(parentType) - } else if c.strictNullChecks && pattern.Parent.Initializer() != nil && !(c.hasTypeFacts(c.getTypeOfInitializer(pattern.Parent.Initializer()), TypeFactsEQUndefined)) { - parentType = c.getTypeWithFacts(parentType, TypeFactsNEUndefined) - } - accessFlags := AccessFlagsExpressionPosition | core.IfElse(noTupleBoundsCheck || c.hasDefaultValue(declaration), AccessFlagsAllowMissing, 0) - var t *Type - switch pattern.Kind { - case ast.KindObjectBindingPattern: - if hasDotDotDotToken(declaration) { - parentType = c.getReducedType(parentType) - if parentType.flags&TypeFlagsUnknown != 0 || !c.isValidSpreadType(parentType) { - c.error(declaration, diagnostics.Rest_types_may_only_be_created_from_object_types) - return c.errorType - } - elements := pattern.AsBindingPattern().Elements.Nodes - literalMembers := make([]*ast.Node, 0, len(elements)) - for _, element := range elements { - if !hasDotDotDotToken(element) { - name := element.PropertyNameOrName() - literalMembers = append(literalMembers, name) - } - } - t = c.getRestType(parentType, literalMembers, declaration.Symbol()) - } else { - // Use explicitly specified property name ({ p: xxx } form), or otherwise the implied name ({ p } form) - name := declaration.PropertyNameOrName() - indexType := c.getLiteralTypeFromPropertyName(name) - declaredType := c.getIndexedAccessTypeEx(parentType, indexType, accessFlags, name, nil) - t = c.getFlowTypeOfDestructuring(declaration, declaredType) - } - case ast.KindArrayBindingPattern: - // This elementType will be used if the specific property corresponding to this index is not - // present (aka the tuple element property). This call also checks that the parentType is in - // fact an iterable or array (depending on target language). - elementType := c.checkIteratedTypeOrElementType(IterationUseDestructuring|core.IfElse(hasDotDotDotToken(declaration), 0, IterationUsePossiblyOutOfBounds), parentType, c.undefinedType, pattern) - index := slices.Index(pattern.AsBindingPattern().Elements.Nodes, declaration) - if hasDotDotDotToken(declaration) { - // If the parent is a tuple type, the rest element has a tuple type of the - // remaining tuple element types. Otherwise, the rest element has an array type with same - // element type as the parent type. - baseConstraint := c.mapType(parentType, func(t *Type) *Type { - if t.flags&TypeFlagsInstantiableNonPrimitive != 0 { - return c.getBaseConstraintOrType(t) - } - return t - }) - if everyType(baseConstraint, isTupleType) { - t = c.mapType(baseConstraint, func(t *Type) *Type { - return c.sliceTupleType(t, index, 0) - }) - } else { - t = c.createArrayType(elementType) - } - } else if c.isArrayLikeType(parentType) { - indexType := c.getNumberLiteralType(jsnum.Number(index)) - declaredType := core.OrElse(c.getIndexedAccessTypeOrUndefined(parentType, indexType, accessFlags, declaration.Name(), nil), c.errorType) - t = c.getFlowTypeOfDestructuring(declaration, declaredType) - } else { - t = elementType - } - default: - panic("Unhandled case in getBindingElementTypeFromParentType") - } - if declaration.Initializer() == nil { - return t - } - if ast.WalkUpBindingElementsAndPatterns(declaration).Type() != nil { - // In strict null checking mode, if a default value of a non-undefined type is specified, remove - // undefined from the final type. - if c.strictNullChecks && !c.hasTypeFacts(c.checkDeclarationInitializer(declaration, CheckModeNormal, nil), TypeFactsIsUndefined) { - return c.getNonUndefinedType(t) - } - return t - } - return c.widenTypeInferredFromInitializer(declaration, c.getUnionTypeEx([]*Type{c.getNonUndefinedType(t), c.checkDeclarationInitializer(declaration, CheckModeNormal, nil)}, UnionReductionSubtype, nil, nil)) -} - -func (c *Checker) getRestType(source *Type, properties []*ast.Node, symbol *ast.Symbol) *Type { - source = c.filterType(source, func(t *Type) bool { return t.flags&TypeFlagsNullable == 0 }) - if source.flags&TypeFlagsNever != 0 { - return c.emptyObjectType - } - if source.flags&TypeFlagsUnion != 0 { - return c.mapType(source, func(t *Type) *Type { - return c.getRestType(t, properties, symbol) - }) - } - omitKeyType := c.getUnionType(core.Map(properties, c.getLiteralTypeFromPropertyName)) - var spreadableProperties []*ast.Symbol - var unspreadableToRestKeys []*Type - for _, prop := range c.getPropertiesOfType(source) { - literalTypeFromProperty := c.getLiteralTypeFromProperty(prop, TypeFlagsStringOrNumberLiteralOrUnique, false) - if !c.isTypeAssignableTo(literalTypeFromProperty, omitKeyType) && getDeclarationModifierFlagsFromSymbol(prop)&(ast.ModifierFlagsPrivate|ast.ModifierFlagsProtected) == 0 && c.isSpreadableProperty(prop) { - spreadableProperties = append(spreadableProperties, prop) - } else { - unspreadableToRestKeys = append(unspreadableToRestKeys, literalTypeFromProperty) - } - } - if c.isGenericObjectType(source) || c.isGenericIndexType(omitKeyType) { - if len(unspreadableToRestKeys) != 0 { - // If the type we're spreading from has properties that cannot - // be spread into the rest type (e.g. getters, methods), ensure - // they are explicitly omitted, as they would in the non-generic case. - omitKeyType = c.getUnionType(append([]*Type{omitKeyType}, unspreadableToRestKeys...)) - } - if omitKeyType.flags&TypeFlagsNever != 0 { - return source - } - omitTypeAlias := c.getGlobalOmitSymbol() - if omitTypeAlias == nil { - return c.errorType - } - return c.getTypeAliasInstantiation(omitTypeAlias, []*Type{source, omitKeyType}, nil) - } - members := make(ast.SymbolTable) - for _, prop := range spreadableProperties { - members[prop.Name] = c.getSpreadSymbol(prop, false /*readonly*/) - } - result := c.newAnonymousType(symbol, members, nil, nil, c.getIndexInfosOfType(source)) - result.objectFlags |= ObjectFlagsObjectRestType - return result -} - -// Determine the control flow type associated with a destructuring declaration or assignment. The following -// forms of destructuring are possible: -// -// let { x } = obj; // BindingElement -// let [ x ] = obj; // BindingElement -// { x } = obj; // ShorthandPropertyAssignment -// { x: v } = obj; // PropertyAssignment -// [ x ] = obj; // Expression -// -// We construct a synthetic element access expression corresponding to 'obj.x' such that the control -// flow analyzer doesn't have to handle all the different syntactic forms. -func (c *Checker) getFlowTypeOfDestructuring(node *ast.Node, declaredType *Type) *Type { - reference := c.getSyntheticElementAccess(node) - if reference != nil { - return c.getFlowTypeOfReference(reference, declaredType) - } - return declaredType -} - -func (c *Checker) getSyntheticElementAccess(node *ast.Node) *ast.Node { - parentAccess := c.getParentElementAccess(node) - if parentAccess != nil && getFlowNodeOfNode(parentAccess) != nil { - if propName, ok := c.getDestructuringPropertyName(node); ok { - literal := c.factory.NewStringLiteral(propName) - literal.Loc = node.Loc - lhsExpr := parentAccess - if !ast.IsLeftHandSideExpression(parentAccess) { - lhsExpr = c.factory.NewParenthesizedExpression(parentAccess) - lhsExpr.Loc = node.Loc - } - result := c.factory.NewElementAccessExpression(lhsExpr, nil, literal, ast.NodeFlagsNone) - result.Loc = node.Loc - literal.Parent = result - result.Parent = node - if lhsExpr != parentAccess { - lhsExpr.Parent = result - } - result.FlowNodeData().FlowNode = getFlowNodeOfNode(parentAccess) - return result - } - } - return nil -} - -func (c *Checker) getParentElementAccess(node *ast.Node) *ast.Node { - ancestor := node.Parent.Parent - switch ancestor.Kind { - case ast.KindBindingElement, ast.KindPropertyAssignment: - return c.getSyntheticElementAccess(ancestor) - case ast.KindArrayLiteralExpression: - return c.getSyntheticElementAccess(node.Parent) - case ast.KindVariableDeclaration: - return ancestor.Initializer() - case ast.KindBinaryExpression: - return ancestor.AsBinaryExpression().Right - } - return nil -} - -// 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 *ast.Node, includePatternInType bool, reportErrors bool) *Type { - if includePatternInType { - c.contextualBindingPatterns = append(c.contextualBindingPatterns, pattern) - } - var result *Type - if ast.IsObjectBindingPattern(pattern) { - result = c.getTypeFromObjectBindingPattern(pattern, includePatternInType, reportErrors) - } else { - result = c.getTypeFromArrayBindingPattern(pattern, includePatternInType, reportErrors) - } - if includePatternInType { - c.contextualBindingPatterns = c.contextualBindingPatterns[:len(c.contextualBindingPatterns)-1] - } - return result -} - -// Return the type implied by an object binding pattern -func (c *Checker) getTypeFromObjectBindingPattern(pattern *ast.Node, includePatternInType bool, reportErrors bool) *Type { - members := make(ast.SymbolTable) - var stringIndexInfo *IndexInfo - objectFlags := ObjectFlagsObjectLiteral | ObjectFlagsContainsObjectOrArrayLiteral - for _, e := range pattern.AsBindingPattern().Elements.Nodes { - name := e.PropertyNameOrName() - if hasDotDotDotToken(e) { - stringIndexInfo = c.newIndexInfo(c.stringType, c.anyType, false /*isReadonly*/, nil, nil) - continue - } - exprType := c.getLiteralTypeFromPropertyName(name) - if !isTypeUsableAsPropertyName(exprType) { - // do not include computed properties in the implied type - objectFlags |= ObjectFlagsObjectLiteralPatternWithComputedProperties - continue - } - text := getPropertyNameFromType(exprType) - flags := ast.SymbolFlagsProperty | core.IfElse(e.Initializer() != nil, ast.SymbolFlagsOptional, 0) - symbol := c.newSymbol(flags, text) - c.valueSymbolLinks.Get(symbol).resolvedType = c.getTypeFromBindingElement(e, includePatternInType, reportErrors) - members[symbol.Name] = symbol - } - var indexInfos []*IndexInfo - if stringIndexInfo != nil { - indexInfos = []*IndexInfo{stringIndexInfo} - } - result := c.newAnonymousType(nil, members, nil, nil, indexInfos) - result.objectFlags |= objectFlags - if includePatternInType { - c.patternForType[result] = pattern - result.objectFlags |= ObjectFlagsContainsObjectOrArrayLiteral - } - return result -} - -// Return the type implied by an array binding pattern -func (c *Checker) getTypeFromArrayBindingPattern(pattern *ast.Node, includePatternInType bool, reportErrors bool) *Type { - elements := pattern.AsBindingPattern().Elements.Nodes - lastElement := core.LastOrNil(elements) - var restElement *ast.Node - if lastElement != nil && ast.IsBindingElement(lastElement) && hasDotDotDotToken(lastElement) { - restElement = lastElement - } - if len(elements) == 0 || len(elements) == 1 && restElement != nil { - if c.languageVersion >= core.ScriptTargetES2015 { - return c.createIterableType(c.anyType) - } - return c.anyArrayType - } - minLength := core.FindLastIndex(elements, func(e *ast.Node) bool { - return !(e == restElement || e.Name() == nil || c.hasDefaultValue(e)) - }) + 1 - elementTypes := make([]*Type, len(elements)) - elementInfos := make([]TupleElementInfo, len(elements)) - for i, e := range elements { - var t *Type - if e.Name() == nil { - t = c.anyType - } else { - t = c.getTypeFromBindingElement(e, includePatternInType, reportErrors) - } - var flags ElementFlags - if e == restElement { - flags = ElementFlagsRest - } else if i >= minLength { - flags = ElementFlagsOptional - } else { - flags = ElementFlagsRequired - } - elementTypes[i] = t - elementInfos[i] = TupleElementInfo{flags: flags} - } - result := c.createTupleTypeEx(elementTypes, elementInfos, false) - if includePatternInType { - result = c.cloneTypeReference(result) - c.patternForType[result] = pattern - result.objectFlags |= ObjectFlagsContainsObjectOrArrayLiteral - } - return result -} - -// Return the type implied by a binding pattern element. This is the type of the initializer of the element if -// one is present. Otherwise, if the element is itself a binding pattern, it is the type implied by the binding -// pattern. Otherwise, it is the type any. -func (c *Checker) getTypeFromBindingElement(element *ast.Node, includePatternInType bool, reportErrors bool) *Type { - if element.Initializer() != nil { - // The type implied by a binding pattern is independent of context, so we check the initializer with no - // contextual type or, if the element itself is a binding pattern, with the type implied by that binding - // pattern. - contextualType := c.unknownType - if ast.IsBindingPattern(element.Name()) { - contextualType = c.getTypeFromBindingPattern(element.Name(), true /*includePatternInType*/, false /*reportErrors*/) - } - return c.addOptionality(c.getWidenedLiteralTypeForInitializer(element, c.checkDeclarationInitializer(element, CheckModeNormal, contextualType))) - } - if ast.IsBindingPattern(element.Name()) { - return c.getTypeFromBindingPattern(element.Name(), includePatternInType, reportErrors) - } - if reportErrors && !c.declarationBelongsToPrivateAmbientMember(element) { - c.reportImplicitAny(element, c.anyType, WideningKindNormal) - } - // When we're including the pattern in the type (an indication we're obtaining a contextual type), we - // use a non-inferrable any type. Inference will never directly infer this type, but it is possible - // to infer a type that contains it, e.g. for a binding pattern like [foo] or { foo }. In such cases, - // widening of the binding pattern type substitutes a regular any for the non-inferrable any. - if includePatternInType { - return c.nonInferrableAnyType - } - return c.anyType -} - -func (c *Checker) declarationBelongsToPrivateAmbientMember(declaration *ast.Node) bool { - memberDeclaration := ast.GetRootDeclaration(declaration) - if ast.IsParameter(memberDeclaration) { - memberDeclaration = memberDeclaration.Parent - } - return isPrivateWithinAmbient(memberDeclaration) -} - -func (c *Checker) getTypeOfPrototypeProperty(prototype *ast.Symbol) *Type { - // 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'. - classType := c.getDeclaredTypeOfSymbol(c.getParentOfSymbol(prototype)) - typeParameters := classType.AsInterfaceType().TypeParameters() - if len(typeParameters) != 0 { - return c.createTypeReference(classType, core.Map(typeParameters, func(*Type) *Type { return c.anyType })) - } - return classType -} - -type thisAssignmentDeclarationKind int32 - -const ( - thisAssignmentDeclarationNone thisAssignmentDeclarationKind = iota // not (all) this.property assignments - thisAssignmentDeclarationTyped // typed; use the type annotation - thisAssignmentDeclarationConstructor // at least one in the constructor; use control flow - thisAssignmentDeclarationMethod // methods only; look in base first, and if not found, union all declaration types plus undefined -) - -func (c *Checker) getWidenedTypeForAssignmentDeclaration(symbol *ast.Symbol) *Type { - var t *Type - kind, location := c.isConstructorDeclaredThisProperty(symbol) - switch kind { - case thisAssignmentDeclarationTyped: - if location == nil { - panic("location should not be nil when this assignment has a type.") - } - t = c.getTypeFromTypeNode(location) - case thisAssignmentDeclarationConstructor: - if location == nil { - panic("constructor should not be nil when this assignment is in a constructor.") - } - t = c.getFlowTypeInConstructor(symbol, location) - case thisAssignmentDeclarationMethod: - t = c.getTypeOfPropertyInBaseClass(symbol) - } - if t == nil { - var types []*Type - for _, declaration := range symbol.Declarations { - if ast.IsBinaryExpression(declaration) { - if declaration.Type() != nil { - t = c.getTypeFromTypeNode(declaration.Type()) - break - } - types = core.AppendIfUnique(types, c.checkExpressionForMutableLocation(declaration.AsBinaryExpression().Right, CheckModeNormal)) - } - } - if kind == thisAssignmentDeclarationMethod && len(types) > 0 { - if c.strictNullChecks { - types = core.AppendIfUnique(types, c.undefinedOrMissingType) - } - } - if t == nil { - t = c.getWidenedType(c.getUnionType(types)) - } - } - // report an all-nullable or empty union as an implicit any in JS files - if symbol.ValueDeclaration != nil && ast.IsInJSFile(symbol.ValueDeclaration) && - c.filterType(t, func(c *Type) bool { return c.Flags() & ^TypeFlagsNullable != 0 }) == c.neverType { - c.reportImplicitAny(symbol.ValueDeclaration, c.anyType, WideningKindNormal) - return c.anyType - } - return t -} - -// A property is considered a constructor declared property when all declaration sites are this.xxx assignments, -// when no declaration sites have JSDoc type annotations, and when at least one declaration site is in the body of -// a class constructor. -func (c *Checker) isConstructorDeclaredThisProperty(symbol *ast.Symbol) (thisAssignmentDeclarationKind, *ast.Node) { - if symbol.ValueDeclaration == nil || !ast.IsBinaryExpression(symbol.ValueDeclaration) { - return thisAssignmentDeclarationNone, nil - } - if kind, ok := c.thisExpandoKinds[symbol]; ok { - location, ok2 := c.thisExpandoLocations[symbol] - if !ok2 { - panic("location should be cached whenever this expando symbol is cached") - } - return kind, location - } - allThis := true - var typeAnnotation *ast.Node - for _, declaration := range symbol.Declarations { - if !ast.IsBinaryExpression(declaration) { - allThis = false - break - } - bin := declaration.AsBinaryExpression() - if ast.GetAssignmentDeclarationKind(bin) == ast.JSDeclarationKindThisProperty && - (bin.Left.Kind != ast.KindElementAccessExpression || ast.IsStringOrNumericLiteralLike(bin.Left.AsElementAccessExpression().ArgumentExpression)) { - if bin.Type != nil { - typeAnnotation = bin.Type - } - } else { - allThis = false - break - } - } - var location *ast.Node - kind := thisAssignmentDeclarationNone - if allThis { - if typeAnnotation != nil { - location = typeAnnotation - kind = thisAssignmentDeclarationTyped - } else { - location = c.getDeclaringConstructor(symbol) - kind = core.IfElse(location == nil, thisAssignmentDeclarationMethod, thisAssignmentDeclarationConstructor) - } - } - c.thisExpandoKinds[symbol] = kind - c.thisExpandoLocations[symbol] = location - return kind, location -} - -func (c *Checker) isGlobalSymbolConstructor(node *ast.Node) bool { - symbol := c.getSymbolOfNode(node) - globalSymbol := c.getGlobalESSymbolConstructorTypeSymbolOrNil() - return globalSymbol != nil && symbol == globalSymbol -} - -func (c *Checker) widenTypeForVariableLikeDeclaration(t *Type, declaration *ast.Node, reportErrors bool) *Type { - if t != nil { - // This special case is required for backwards compatibility with libraries that merge a `symbol` property into `SymbolConstructor`. - // See https://github.com/microsoft/typescript-go/issues/1212 - if t.flags&TypeFlagsESSymbol != 0 && c.isGlobalSymbolConstructor(declaration.Parent) { - t = c.getESSymbolLikeTypeForNode(declaration) - } - - if reportErrors { - c.reportErrorsFromWidening(declaration, t, WideningKindNormal) - } - - // always widen a 'unique symbol' type if the type was created for a different declaration. - if t.flags&TypeFlagsUniqueESSymbol != 0 && (ast.IsBindingElement(declaration) || declaration.Type() == nil) && 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 ast.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 *ast.Node, t *Type, wideningKind WideningKind) { - typeAsString := c.TypeToString(c.getWidenedType(t)) - var diagnostic *diagnostics.Message - switch declaration.Kind { - case ast.KindBinaryExpression, ast.KindPropertyDeclaration, ast.KindPropertySignature: - diagnostic = core.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 ast.KindParameter: - param := declaration.AsParameterDeclaration() - if ast.IsIdentifier(param.Name()) { - name := param.Name().AsIdentifier() - originalKeywordKind := scanner.IdentifierToKeywordKind(name) - if (ast.IsCallSignatureDeclaration(declaration.Parent) || ast.IsMethodSignatureDeclaration(declaration.Parent) || ast.IsFunctionTypeNode(declaration.Parent)) && - slices.Contains(declaration.Parent.Parameters(), declaration) && - (ast.IsTypeNodeKind(originalKeywordKind) || c.resolveName(declaration, name.Text, ast.SymbolFlagsType, nil /*nameNotFoundMessage*/, true /*isUse*/, false /*excludeGlobals*/) != nil) { - newName := fmt.Sprintf("arg%v", slices.Index(declaration.Parent.Parameters(), declaration)) - typeName := scanner.DeclarationNameToString(param.Name()) + core.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 ast.KindBindingElement: - 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 ast.KindFunctionDeclaration, ast.KindMethodDeclaration, ast.KindMethodSignature, ast.KindGetAccessor, - ast.KindSetAccessor, ast.KindFunctionExpression, ast.KindArrowFunction: - 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 declaration.Flags&ast.NodeFlagsReparsed != 0: - c.error(declaration, diagnostics.This_overload_implicitly_returns_the_type_0_because_it_lacks_a_return_type_annotation, typeAsString) - return - 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 ast.KindMappedType: - 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, scanner.DeclarationNameToString(ast.GetNameOfDeclaration(declaration)), typeAsString) -} - -func (c *Checker) getWidenedType(t *Type) *Type { - return c.getWidenedTypeWithContext(t, nil /*context*/) -} - -func (c *Checker) getWidenedTypeWithContext(t *Type, context *WideningContext) *Type { - if t.objectFlags&ObjectFlagsRequiresWidening != 0 { - if context == nil { - if cached := c.cachedTypes[CachedTypeKey{kind: CachedTypeKindWidened, typeId: t.id}]; cached != nil { - return cached - } - } - var result *Type - switch { - case t.flags&(TypeFlagsAny|TypeFlagsNullable) != 0: - result = c.anyType - case isObjectLiteralType(t): - result = c.getWidenedTypeOfObjectLiteral(t, context) - case t.flags&TypeFlagsUnion != 0: - unionContext := context - if unionContext == nil { - unionContext = &WideningContext{siblings: t.Types()} - } - widenedTypes := core.SameMap(t.Types(), func(t *Type) *Type { - if t.flags&TypeFlagsNullable != 0 { - return t - } - return c.getWidenedTypeWithContext(t, unionContext) - }) - // Widening an empty object literal transitions from a highly restrictive type to - // a highly inclusive one. For that reason we perform subtype reduction here if the - // union includes empty object types (e.g. reducing {} | string to just {}). - result = c.getUnionTypeEx(widenedTypes, core.IfElse(core.Some(widenedTypes, c.isEmptyObjectType), UnionReductionSubtype, UnionReductionLiteral), nil, nil) - case t.flags&TypeFlagsIntersection != 0: - result = c.getIntersectionType(core.SameMap(t.Types(), c.getWidenedType)) - case c.isArrayOrTupleType(t): - result = c.createTypeReference(t.Target(), core.SameMap(c.getTypeArguments(t), c.getWidenedType)) - } - if result != nil && context == nil { - c.cachedTypes[CachedTypeKey{kind: CachedTypeKindWidened, typeId: t.id}] = result - } - return core.OrElse(result, t) - } - return t -} - -func (c *Checker) getWidenedTypeOfObjectLiteral(t *Type, context *WideningContext) *Type { - members := make(ast.SymbolTable) - for _, prop := range c.getPropertiesOfObjectType(t) { - members[prop.Name] = c.getWidenedProperty(prop, context) - } - if context != nil { - for _, prop := range c.getPropertiesOfContext(context) { - if _, ok := members[prop.Name]; !ok { - members[prop.Name] = c.getUndefinedProperty(prop) - } - } - } - result := c.newAnonymousType(t.symbol, members, nil, nil, core.SameMap(c.getIndexInfosOfType(t), func(info *IndexInfo) *IndexInfo { - return c.newIndexInfo(info.keyType, c.getWidenedType(info.valueType), info.isReadonly, info.declaration, info.components) - })) - // Retain js literal flag through widening - result.objectFlags |= t.objectFlags & (ObjectFlagsJSLiteral | ObjectFlagsNonInferrableType) - return result -} - -func (c *Checker) getWidenedProperty(prop *ast.Symbol, context *WideningContext) *ast.Symbol { - if prop.Flags&ast.SymbolFlagsProperty == 0 { - // Since get accessors already widen their return value there is no need to - // widen accessor based properties here. - return prop - } - original := c.getTypeOfSymbol(prop) - var propContext *WideningContext - if context != nil { - propContext = &WideningContext{parent: context, propertyName: prop.Name} - } - widened := c.getWidenedTypeWithContext(original, propContext) - if widened == original { - return prop - } - return c.createSymbolWithType(prop, widened) -} - -func (c *Checker) getPropertiesOfContext(context *WideningContext) []*ast.Symbol { - if context.resolvedProperties == nil { - var names collections.OrderedMap[string, *ast.Symbol] - for _, t := range c.getSiblingsOfContext(context) { - if isObjectLiteralType(t) && t.objectFlags&ObjectFlagsContainsSpread == 0 { - for _, prop := range c.getPropertiesOfType(t) { - names.Set(prop.Name, prop) - } - } - } - context.resolvedProperties = slices.Collect(names.Values()) - } - return context.resolvedProperties -} - -func (c *Checker) getSiblingsOfContext(context *WideningContext) []*Type { - if context.siblings == nil { - siblings := []*Type{} - for _, t := range c.getSiblingsOfContext(context.parent) { - if isObjectLiteralType(t) { - prop := c.getPropertyOfObjectType(t, context.propertyName) - if prop != nil { - siblings = append(siblings, c.getTypeOfSymbol(prop).Distributed()...) - } - } - } - context.siblings = siblings - } - return context.siblings -} - -func (c *Checker) getUndefinedProperty(prop *ast.Symbol) *ast.Symbol { - if cached := c.undefinedProperties[prop.Name]; cached != nil { - return cached - } - result := c.createSymbolWithType(prop, c.undefinedOrMissingType) - result.Flags |= ast.SymbolFlagsOptional - c.undefinedProperties[prop.Name] = result - return result -} - -func (c *Checker) getTypeOfEnumMember(symbol *ast.Symbol) *Type { - links := c.valueSymbolLinks.Get(symbol) - if links.resolvedType == nil { - links.resolvedType = c.getDeclaredTypeOfEnumMember(symbol) - } - return links.resolvedType -} - -func (c *Checker) getTypeOfAccessors(symbol *ast.Symbol) *Type { - links := c.valueSymbolLinks.Get(symbol) - if links.resolvedType == nil { - if !c.pushTypeResolution(symbol, TypeSystemPropertyNameType) { - return c.errorType - } - getter := ast.GetDeclarationOfKind(symbol, ast.KindGetAccessor) - setter := ast.GetDeclarationOfKind(symbol, ast.KindSetAccessor) - property := ast.GetDeclarationOfKind(symbol, ast.KindPropertyDeclaration) - var accessor *ast.Node - if property != nil && ast.IsAutoAccessorPropertyDeclaration(property) { - accessor = property - } - // We try to resolve a getter type annotation, a setter type annotation, or a getter function - // body return type inference, in that order. - t := c.getAnnotatedAccessorType(getter) - if t == nil { - t = c.getAnnotatedAccessorType(setter) - } - if t == nil { - t = c.getAnnotatedAccessorType(accessor) - } - if t == nil && getter != nil { - if body := getter.Body(); body != nil { - t = c.getReturnTypeFromBody(getter, CheckModeNormal) - } - } - if t == nil && accessor != nil { - t = c.getWidenedTypeForVariableLikeDeclaration(accessor, true /*reportErrors*/) - } - if t == nil { - if setter != nil && !isPrivateWithinAmbient(setter) { - c.errorOrSuggestion(c.noImplicitAny, setter, diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation, c.symbolToString(symbol)) - } else if getter != nil && !isPrivateWithinAmbient(getter) { - c.errorOrSuggestion(c.noImplicitAny, getter, diagnostics.Property_0_implicitly_has_type_any_because_its_get_accessor_lacks_a_return_type_annotation, c.symbolToString(symbol)) - } else if accessor != nil && !isPrivateWithinAmbient(accessor) { - c.errorOrSuggestion(c.noImplicitAny, accessor, diagnostics.Member_0_implicitly_has_an_1_type, c.symbolToString(symbol), "any") - } - t = c.anyType - } - if !c.popTypeResolution() { - if c.getAnnotatedAccessorTypeNode(getter) != nil { - c.error(getter, diagnostics.X_0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, c.symbolToString(symbol)) - } else if c.getAnnotatedAccessorTypeNode(setter) != nil { - c.error(setter, diagnostics.X_0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, c.symbolToString(symbol)) - } else if c.getAnnotatedAccessorTypeNode(accessor) != nil { - c.error(setter, diagnostics.X_0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, c.symbolToString(symbol)) - } else if getter != nil && c.noImplicitAny { - c.error(getter, diagnostics.X_0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, c.symbolToString(symbol)) - } - t = c.anyType - } - if links.resolvedType == nil { - links.resolvedType = t - } - } - return links.resolvedType -} - -func (c *Checker) getWriteTypeOfAccessors(symbol *ast.Symbol) *Type { - links := c.valueSymbolLinks.Get(symbol) - if links.writeType == nil { - if !c.pushTypeResolution(symbol, TypeSystemPropertyNameWriteType) { - return c.errorType - } - setter := ast.GetDeclarationOfKind(symbol, ast.KindSetAccessor) - if setter == nil { - propDeclaration := ast.GetDeclarationOfKind(symbol, ast.KindPropertyDeclaration) - if propDeclaration != nil && ast.IsAutoAccessorPropertyDeclaration(propDeclaration) { - setter = propDeclaration - } - } - writeType := c.getAnnotatedAccessorType(setter) - if !c.popTypeResolution() { - if c.getAnnotatedAccessorTypeNode(setter) != nil { - c.error(setter, diagnostics.X_0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, c.symbolToString(symbol)) - } - writeType = c.anyType - } - // Absent an explicit setter type annotation we use the read type of the accessor. - if links.writeType == nil { - if writeType != nil { - links.writeType = writeType - } else { - links.writeType = c.getTypeOfAccessors(symbol) - } - } - } - return links.writeType -} - -func (c *Checker) getTypeOfAlias(symbol *ast.Symbol) *Type { - links := c.valueSymbolLinks.Get(symbol) - if links.resolvedType == nil { - if !c.pushTypeResolution(symbol, TypeSystemPropertyNameType) { - return c.errorType - } - targetSymbol := c.resolveAlias(symbol) - exportSymbol := c.getTargetOfAliasDeclaration(c.getDeclarationOfAliasSymbol(symbol), true /*dontRecursivelyResolve*/) - // It only makes sense to get the type of a value symbol. If the result of resolving - // the alias is not a value, then it has no type. To get the type associated with a - // type symbol, call getDeclaredTypeOfSymbol. - // This check is important because without it, a call to getTypeOfSymbol could end - // up recursively calling getTypeOfAlias, causing a stack overflow. - if links.resolvedType == nil { - if c.getSymbolFlags(targetSymbol)&ast.SymbolFlagsValue != 0 { - links.resolvedType = c.getTypeOfSymbol(targetSymbol) - } else { - links.resolvedType = c.errorType - } - } - if !c.popTypeResolution() { - c.reportCircularityError(core.OrElse(exportSymbol, symbol)) - if links.resolvedType == nil { - links.resolvedType = c.errorType - } - return links.resolvedType - } - } - return links.resolvedType -} - -func (c *Checker) addOptionality(t *Type) *Type { - return c.addOptionalityEx(t, false /*isProperty*/, true /*isOptional*/) -} - -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 := core.IfElse(isProperty, c.undefinedOrMissingType, c.undefinedType) - if t == missingOrUndefined || t.flags&TypeFlagsUnion != 0 && t.Types()[0] == missingOrUndefined { - return t - } - return c.getUnionType([]*Type{t, missingOrUndefined}) -} - -// Add undefined or null or both to a type if they are missing. -func (c *Checker) getNullableType(t *Type, flags TypeFlags) *Type { - missing := (flags & ^t.flags) & (TypeFlagsUndefined | TypeFlagsNull) - switch { - case missing == 0: - return t - case missing == TypeFlagsUndefined: - return c.getUnionType([]*Type{t, c.undefinedType}) - case missing == TypeFlagsNull: - return c.getUnionType([]*Type{t, c.nullType}) - } - return c.getUnionType([]*Type{t, c.undefinedType, c.nullType}) -} - -func (c *Checker) GetNonNullableType(t *Type) *Type { - if c.strictNullChecks { - return c.getAdjustedTypeWithFacts(t, TypeFactsNEUndefinedOrNull) - } - return t -} - -func (c *Checker) IsNullableType(t *Type) bool { - return c.hasTypeFacts(t, TypeFactsIsUndefinedOrNull) -} - -func (c *Checker) getNonNullableTypeIfNeeded(t *Type) *Type { - if c.IsNullableType(t) { - return c.GetNonNullableType(t) - } - return t -} - -func (c *Checker) getDeclarationNodeFlagsFromSymbol(s *ast.Symbol) ast.NodeFlags { - if s.ValueDeclaration != nil { - return c.getCombinedNodeFlagsCached(s.ValueDeclaration) - } - return ast.NodeFlagsNone -} - -func (c *Checker) getCombinedNodeFlagsCached(node *ast.Node) ast.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 = ast.GetCombinedNodeFlags(node) - return c.lastGetCombinedNodeFlagsResult -} - -func (c *Checker) isVarConstLike(node *ast.Node) bool { - blockScopeKind := c.getCombinedNodeFlagsCached(node) & ast.NodeFlagsBlockScoped - return blockScopeKind == ast.NodeFlagsConst || blockScopeKind == ast.NodeFlagsUsing || blockScopeKind == ast.NodeFlagsAwaitUsing -} - -func (c *Checker) getEffectivePropertyNameForPropertyNameNode(node *ast.PropertyName) (string, bool) { - name := ast.GetPropertyNameForPropertyNameNode(node) - switch { - case name != ast.InternalSymbolNameMissing: - return name, true - case ast.IsComputedPropertyName(node): - return c.tryGetNameFromType(c.getTypeOfExpression(node.Expression())) - } - return "", false -} - -func (c *Checker) tryGetNameFromType(t *Type) (name string, ok bool) { - switch { - case t.flags&TypeFlagsUniqueESSymbol != 0: - return t.AsUniqueESSymbolType().name, true - case t.flags&TypeFlagsStringLiteral != 0: - s := getStringLiteralValue(t) - return s, true - case t.flags&TypeFlagsNumberLiteral != 0: - s := getNumberLiteralValue(t).String() - return s, true - default: - return "", false - } -} - -func (c *Checker) getCombinedModifierFlagsCached(node *ast.Node) ast.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 = ast.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[lastIndex] = TypeResolution{} // Clear the last entry to avoid memory leaks - 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.(*ast.Symbol)).resolvedType != nil - case TypeSystemPropertyNameDeclaredType: - return c.typeAliasLinks.Get(r.target.(*ast.Symbol)).declaredType != nil - case TypeSystemPropertyNameResolvedTypeArguments: - return r.target.(*Type).AsTypeReference().resolvedTypeArguments != nil - case TypeSystemPropertyNameResolvedBaseTypes: - return r.target.(*Type).AsInterfaceType().baseTypesResolved - case TypeSystemPropertyNameResolvedBaseConstructorType: - return r.target.(*Type).AsInterfaceType().resolvedBaseConstructorType != nil - case TypeSystemPropertyNameResolvedReturnType: - return r.target.(*Signature).resolvedReturnType != nil - case TypeSystemPropertyNameResolvedBaseConstraint: - return r.target.(*Type).AsConstrainedType().resolvedBaseConstraint != nil - case TypeSystemPropertyNameInitializerIsUndefined: - return c.nodeLinks.Get(r.target.(*ast.Node)).flags&NodeCheckFlagsInitializerIsUndefinedComputed != 0 - case TypeSystemPropertyNameWriteType: - return c.valueSymbolLinks.Get(r.target.(*ast.Symbol)).writeType != nil - } - panic("Unhandled case in typeResolutionHasProperty") -} - -func (c *Checker) reportCircularityError(symbol *ast.Symbol) *Type { - declaration := symbol.ValueDeclaration - // Check if variable has type annotation that circularly references the variable itself - if declaration != nil { - if declaration.Type() != 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 && (!ast.IsParameter(declaration) || declaration.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&ast.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) []*ast.Symbol { - t = c.getReducedApparentType(t) - if t.flags&TypeFlagsUnionOrIntersection != 0 { - return c.getPropertiesOfUnionOrIntersectionType(t) - } - return c.getPropertiesOfObjectType(t) -} - -func (c *Checker) getPropertiesOfObjectType(t *Type) []*ast.Symbol { - if t.flags&TypeFlagsObject != 0 { - return c.resolveStructuredTypeMembers(t).properties - } - return nil -} - -func (c *Checker) getPropertiesOfUnionOrIntersectionType(t *Type) []*ast.Symbol { - d := t.AsUnionOrIntersectionType() - if d.resolvedProperties == nil { - var checked collections.Set[string] - props := []*ast.Symbol{} - for _, current := range d.types { - for _, prop := range c.getPropertiesOfType(current) { - if !checked.Has(prop.Name) { - checked.Add(prop.Name) - combinedProp := c.getPropertyOfUnionOrIntersectionType(t, prop.Name, t.flags&TypeFlagsIntersection != 0 /*skipObjectFunctionPropertyAugment*/) - if combinedProp != nil { - props = append(props, combinedProp) - } - } - } - // The properties of a union type are those that are present in all constituent types, so - // we only need to check the properties of the first type without index signature - if t.flags&TypeFlagsUnion != 0 && len(c.getIndexInfosOfType(current)) == 0 { - break - } - } - d.resolvedProperties = props - } - return d.resolvedProperties -} - -func (c *Checker) getPropertyOfType(t *Type, name string) *ast.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) *ast.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 != nil && t.symbol.Flags&ast.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 { - return nil - } - 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) - 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 -} - -// Return the type of the given property in the given type, or nil if no such property exists -func (c *Checker) getTypeOfPropertyOfType(t *Type, name string) *Type { - prop := c.getPropertyOfType(t, name) - if prop != nil { - return c.getTypeOfSymbol(prop) - } - return nil -} - -func (c *Checker) getSignaturesOfType(t *Type, kind SignatureKind) []*Signature { - return c.getSignaturesOfStructuredType(c.getReducedApparentType(t), kind) -} - -func (c *Checker) getSignaturesOfStructuredType(t *Type, kind SignatureKind) []*Signature { - if t.flags&TypeFlagsStructuredType == 0 { - return nil - } - resolved := c.resolveStructuredTypeMembers(t) - if kind == SignatureKindCall { - return resolved.signatures[:resolved.callSignatureCount] - } - return resolved.signatures[resolved.callSignatureCount:] -} - -func (c *Checker) getIndexInfosOfType(t *Type) []*IndexInfo { - return c.getIndexInfosOfStructuredType(c.getReducedApparentType(t)) -} - -func (c *Checker) getIndexInfosOfStructuredType(t *Type) []*IndexInfo { - if t.flags&TypeFlagsStructuredType != 0 { - return c.resolveStructuredTypeMembers(t).indexInfos - } - return nil -} - -// Return the indexing info of the given kind in the given type. Creates synthetic union index types when necessary and -// maps primitive types and type parameters are to their apparent types. -func (c *Checker) getIndexInfoOfType(t *Type, keyType *Type) *IndexInfo { - return findIndexInfo(c.getIndexInfosOfType(t), keyType) -} - -// Return the index type of the given kind in the given type. Creates synthetic union index types when necessary and -// maps primitive types and type parameters are to their apparent types. -func (c *Checker) getIndexTypeOfType(t *Type, keyType *Type) *Type { - info := c.getIndexInfoOfType(t, keyType) - if info != nil { - return info.valueType - } - return nil -} - -func (c *Checker) getIndexTypeOfTypeEx(t *Type, keyType *Type, defaultType *Type) *Type { - if result := c.getIndexTypeOfType(t, keyType); result != nil { - return result - } - return defaultType -} - -func (c *Checker) getApplicableIndexInfo(t *Type, keyType *Type) *IndexInfo { - return c.findApplicableIndexInfo(c.getIndexInfosOfType(t), keyType) -} - -func (c *Checker) getApplicableIndexInfoForName(t *Type, name string) *IndexInfo { - if isLateBoundName(name) { - return c.getApplicableIndexInfo(t, c.esSymbolType) - } - return c.getApplicableIndexInfo(t, c.getStringLiteralType(name)) -} - -func (c *Checker) findApplicableIndexInfo(indexInfos []*IndexInfo, keyType *Type) *IndexInfo { - // Index signatures for type 'string' are considered only when no other index signatures apply. - var stringIndexInfo *IndexInfo - applicableInfos := make([]*IndexInfo, 0, 8) - for _, info := range indexInfos { - if info.keyType == c.stringType { - stringIndexInfo = info - } else if c.isApplicableIndexType(keyType, info.keyType) { - applicableInfos = append(applicableInfos, info) - } - } - // When more than one index signature is applicable we create a synthetic IndexInfo. Instead of computing - // the intersected key type, we just use unknownType for the key type as nothing actually depends on the - // keyType property of the returned IndexInfo. - switch len(applicableInfos) { - case 0: - if stringIndexInfo != nil && c.isApplicableIndexType(keyType, c.stringType) { - return stringIndexInfo - } - return nil - case 1: - return applicableInfos[0] - default: - isReadonly := true - types := make([]*Type, len(applicableInfos)) - for i, info := range applicableInfos { - types[i] = info.valueType - if !info.isReadonly { - isReadonly = false - } - } - return c.newIndexInfo(c.unknownType, c.getIntersectionType(types), isReadonly, nil, nil) - } -} - -func (c *Checker) isApplicableIndexType(source *Type, target *Type) bool { - // A 'string' index signature applies to types assignable to 'string' or 'number', and a 'number' index - // signature applies to types assignable to 'number', `${number}` and numeric string literal types. - return c.isTypeAssignableTo(source, target) || - target == c.stringType && c.isTypeAssignableTo(source, c.numberType) || - target == c.numberType && (source == c.numericStringType || source.flags&TypeFlagsStringLiteral != 0 && isNumericLiteralName(getStringLiteralValue(source))) -} - -func (c *Checker) resolveStructuredTypeMembers(t *Type) *StructuredType { - 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.AsStructuredType() -} - -func (c *Checker) resolveClassOrInterfaceMembers(t *Type) { - c.resolveObjectTypeMembers(t, t, nil, nil) -} - -func (c *Checker) resolveTypeReferenceMembers(t *Type) { - source := t.Target() - typeParameters := source.AsInterfaceType().allTypeParameters - typeArguments := c.getTypeArguments(t) - paddedTypeArguments := typeArguments - if len(typeArguments) == len(typeParameters)-1 { - paddedTypeArguments = core.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 ast.SymbolTable - var callSignatures []*Signature - var constructSignatures []*Signature - var indexInfos []*IndexInfo - var instantiated bool - resolved := c.resolveDeclaredMembers(source) - if slices.Equal(typeParameters, typeArguments) { - members = resolved.declaredMembers - callSignatures = resolved.declaredCallSignatures - constructSignatures = resolved.declaredConstructSignatures - indexInfos = resolved.declaredIndexInfos - } else { - instantiated = true - mapper = newTypeMapper(typeParameters, typeArguments) - members = c.instantiateSymbolTable(resolved.declaredMembers, mapper, len(typeParameters) == 1 /*mappingThisOnly*/) - callSignatures = c.instantiateSignatures(resolved.declaredCallSignatures, mapper) - constructSignatures = c.instantiateSignatures(resolved.declaredConstructSignatures, mapper) - indexInfos = c.instantiateIndexInfos(resolved.declaredIndexInfos, mapper) - } - baseTypes := c.getBaseTypes(source) - if len(baseTypes) != 0 { - if !instantiated { - members = maps.Clone(members) - } - c.setStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos) - thisArgument := core.LastOrNil(typeArguments) - for _, baseType := range baseTypes { - instantiatedBaseType := baseType - if thisArgument != nil { - instantiatedBaseType = c.getTypeWithThisArgument(c.instantiateType(baseType, mapper), thisArgument, false /*needsApparentType*/) - } - members = c.addInheritedMembers(members, c.getPropertiesOfType(instantiatedBaseType)) - callSignatures = core.Concatenate(callSignatures, c.getSignaturesOfType(instantiatedBaseType, SignatureKindCall)) - constructSignatures = core.Concatenate(constructSignatures, c.getSignaturesOfType(instantiatedBaseType, SignatureKindConstruct)) - var inheritedIndexInfos []*IndexInfo - if instantiatedBaseType != c.anyType { - inheritedIndexInfos = c.getIndexInfosOfType(instantiatedBaseType) - } else { - inheritedIndexInfos = []*IndexInfo{c.anyBaseTypeIndexInfo} - } - indexInfos = core.Concatenate(indexInfos, core.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 { - for _, info := range indexInfos { - if info.keyType == keyType { - return info - } - } - return nil -} - -func (c *Checker) getBaseTypes(t *Type) []*Type { - data := t.AsInterfaceType() - if !data.baseTypesResolved { - if !c.pushTypeResolution(t, TypeSystemPropertyNameResolvedBaseTypes) { - return data.resolvedBaseTypes - } - switch { - case t.objectFlags&ObjectFlagsTuple != 0: - data.resolvedBaseTypes = []*Type{c.getTupleBaseType(t)} - case t.symbol.Flags&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface) != 0: - if t.symbol.Flags&ast.SymbolFlagsClass != 0 { - c.resolveBaseTypesOfClass(t) - } - if t.symbol.Flags&ast.SymbolFlagsInterface != 0 { - c.resolveBaseTypesOfInterface(t) - } - default: - panic("Unhandled case in getBaseTypes") - } - if !c.popTypeResolution() && t.symbol.Declarations != nil { - for _, declaration := range t.symbol.Declarations { - if ast.IsClassDeclaration(declaration) || ast.IsInterfaceDeclaration(declaration) { - c.reportCircularBaseType(declaration, t) - } - } - } - // In general, base type resolution always precedes member resolution. However, it is possible - // for resolution of type parameter defaults to cause circularity errors, possibly leaving - // members partially resolved. Here we ensure any such partial resolution is reset. - // See https://github.com/microsoft/TypeScript/issues/16861 for an example. - t.objectFlags &^= ObjectFlagsMembersResolved - data.baseTypesResolved = true - } - return data.resolvedBaseTypes -} - -func (c *Checker) getTupleBaseType(t *Type) *Type { - typeParameters := t.AsTupleType().TypeParameters() - elementInfos := t.AsTupleType().elementInfos - elementTypes := make([]*Type, len(typeParameters)) - for i, tp := range typeParameters { - if elementInfos[i].flags&ElementFlagsVariadic != 0 { - elementTypes[i] = c.getIndexedAccessType(tp, c.numberType) - } else { - elementTypes[i] = tp - } - } - return c.createArrayTypeEx(c.getUnionType(elementTypes), t.AsTupleType().readonly) -} - -func (c *Checker) resolveBaseTypesOfClass(t *Type) { - baseConstructorType := c.getApparentType(c.getBaseConstructorTypeOfClass(t)) - if baseConstructorType.flags&(TypeFlagsObject|TypeFlagsIntersection|TypeFlagsAny) == 0 { - return - } - baseTypeNode := getBaseTypeNodeOfClass(t) - var baseType *Type - var originalBaseType *Type - if baseConstructorType.symbol != nil { - originalBaseType = c.getDeclaredTypeOfSymbol(baseConstructorType.symbol) - } - if baseConstructorType.symbol != nil && baseConstructorType.symbol.Flags&ast.SymbolFlagsClass != 0 && c.areAllOuterTypeParametersApplied(originalBaseType) { - // When base constructor type is a class with no captured type arguments we know that the constructors all have the same type parameters as the - // class and all return the instance type of the class. There is no need for further checks and we can apply the - // type arguments in the same manner as a type reference to get the same error reporting experience. - baseType = c.getTypeFromClassOrInterfaceReference(baseTypeNode, baseConstructorType.symbol) - } else if baseConstructorType.flags&TypeFlagsAny != 0 { - baseType = baseConstructorType - } else { - // The class derives from a "class-like" constructor function, check that we have at least one construct signature - // with a matching number of type parameters and use the return type of the first instantiated signature. Elsewhere - // we check that all instantiated signatures return the same type. - constructors := c.getInstantiatedConstructorsForTypeArguments(baseConstructorType, baseTypeNode.TypeArguments(), baseTypeNode) - if len(constructors) == 0 { - c.error(baseTypeNode.Expression(), diagnostics.No_base_constructor_has_the_specified_number_of_type_arguments) - return - } - baseType = c.getReturnTypeOfSignature(constructors[0]) - } - if c.isErrorType(baseType) { - return - } - reducedBaseType := c.getReducedType(baseType) - if !c.isValidBaseType(reducedBaseType) { - errorNode := baseTypeNode.Expression() - diagnostic := c.elaborateNeverIntersection(nil, errorNode, baseType) - diagnostic = NewDiagnosticChainForNode(diagnostic, errorNode, diagnostics.Base_constructor_return_type_0_is_not_an_object_type_or_intersection_of_object_types_with_statically_known_members, c.TypeToString(reducedBaseType)) - c.diagnostics.Add(diagnostic) - return - } - if t == reducedBaseType || c.hasBaseType(reducedBaseType, t) { - c.error(t.symbol.ValueDeclaration, diagnostics.Type_0_recursively_references_itself_as_a_base_type, c.TypeToString(t)) - return - } - t.AsInterfaceType().resolvedBaseTypes = []*Type{reducedBaseType} -} - -func getBaseTypeNodeOfClass(t *Type) *ast.Node { - decl := ast.GetClassLikeDeclarationOfSymbol(t.symbol) - if decl != nil { - return ast.GetExtendsHeritageClauseElement(decl) - } - return nil -} - -func (c *Checker) getInstantiatedConstructorsForTypeArguments(t *Type, typeArgumentNodes []*ast.Node, location *ast.Node) []*Signature { - signatures := c.getConstructorsForTypeArguments(t, typeArgumentNodes, location) - typeArguments := core.Map(typeArgumentNodes, c.getTypeFromTypeNode) - return core.SameMap(signatures, func(sig *Signature) *Signature { - if len(sig.typeParameters) != 0 { - return c.getSignatureInstantiation(sig, typeArguments, ast.IsInJSFile(location), nil) - } - return sig - }) -} - -func (c *Checker) getConstructorsForTypeArguments(t *Type, typeArgumentNodes []*ast.Node, location *ast.Node) []*Signature { - typeArgCount := len(typeArgumentNodes) - return core.Filter(c.getSignaturesOfType(t, SignatureKindConstruct), func(sig *Signature) bool { - return typeArgCount >= c.getMinTypeArgumentCount(sig.typeParameters) && typeArgCount <= len(sig.typeParameters) - }) -} - -func (c *Checker) getSignatureInstantiation(sig *Signature, typeArguments []*Type, isJavaScript bool, inferredTypeParameters []*Type) *Signature { - instantiatedSignature := c.getSignatureInstantiationWithoutFillingInTypeArguments(sig, c.fillMissingTypeArguments(typeArguments, sig.typeParameters, c.getMinTypeArgumentCount(sig.typeParameters), isJavaScript)) - if len(inferredTypeParameters) != 0 { - returnSignature := c.getSingleCallOrConstructSignature(c.getReturnTypeOfSignature(instantiatedSignature)) - if returnSignature != nil { - newReturnSignature := c.cloneSignature(returnSignature) - newReturnSignature.typeParameters = inferredTypeParameters - newReturnType := c.getOrCreateTypeFromSignature(newReturnSignature) - newReturnType.AsObjectType().mapper = instantiatedSignature.mapper - newInstantiatedSignature := c.cloneSignature(instantiatedSignature) - newInstantiatedSignature.resolvedReturnType = newReturnType - return newInstantiatedSignature - } - } - return instantiatedSignature -} - -func (c *Checker) cloneSignature(sig *Signature) *Signature { - result := c.newSignature(sig.flags&SignatureFlagsPropagatingFlags, sig.declaration, sig.typeParameters, sig.thisParameter, sig.parameters, nil, nil, int(sig.minArgumentCount)) - result.target = sig.target - result.mapper = sig.mapper - result.composite = sig.composite - return result -} - -func (c *Checker) getSignatureInstantiationWithoutFillingInTypeArguments(sig *Signature, typeArguments []*Type) *Signature { - key := CachedSignatureKey{sig: sig, key: getTypeListKey(typeArguments)} - instantiation := c.cachedSignatures[key] - if instantiation == nil { - instantiation = c.createSignatureInstantiation(sig, typeArguments) - c.cachedSignatures[key] = instantiation - } - return instantiation -} - -func (c *Checker) createSignatureInstantiation(sig *Signature, typeArguments []*Type) *Signature { - return c.instantiateSignatureEx(sig, c.createSignatureTypeMapper(sig, typeArguments), true /*eraseTypeParameters*/) -} - -func (c *Checker) createSignatureTypeMapper(sig *Signature, typeArguments []*Type) *TypeMapper { - return newTypeMapper(c.getTypeParametersForMapper(sig), typeArguments) -} - -func (c *Checker) getTypeParametersForMapper(sig *Signature) []*Type { - return core.SameMap(sig.typeParameters, func(tp *Type) *Type { return c.instantiateType(tp, tp.Mapper()) }) -} - -// If type has a single call signature and no other members, return that signature. Otherwise, return nil. -func (c *Checker) getSingleCallSignature(t *Type) *Signature { - return c.getSingleSignature(t, SignatureKindCall, false /*allowMembers*/) -} - -func (c *Checker) getSingleCallOrConstructSignature(t *Type) *Signature { - callSig := c.getSingleSignature(t, SignatureKindCall, false /*allowMembers*/) - if callSig != nil { - return callSig - } - return c.getSingleSignature(t, SignatureKindConstruct, false /*allowMembers*/) -} - -func (c *Checker) getSingleSignature(t *Type, kind SignatureKind, allowMembers bool) *Signature { - if t.flags&TypeFlagsObject != 0 { - resolved := c.resolveStructuredTypeMembers(t) - if allowMembers || len(resolved.properties) == 0 && len(resolved.indexInfos) == 0 { - if kind == SignatureKindCall && len(resolved.CallSignatures()) == 1 && len(resolved.ConstructSignatures()) == 0 { - return resolved.CallSignatures()[0] - } - if kind == SignatureKindConstruct && len(resolved.ConstructSignatures()) == 1 && len(resolved.CallSignatures()) == 0 { - return resolved.ConstructSignatures()[0] - } - } - } - return nil -} - -func (c *Checker) getOrCreateTypeFromSignature(sig *Signature) *Type { - // There are two ways to declare a construct signature, one is by declaring a class constructor - // using the constructor keyword, and the other is declaring a bare construct signature in an - // object type literal or interface (using the new keyword). Each way of declaring a constructor - // will result in a different declaration kind. - if sig.isolatedSignatureType == nil { - var kind ast.Kind - if sig.declaration != nil { - kind = sig.declaration.Kind - } - // If declaration is undefined, it is likely to be the signature of the default constructor. - isConstructor := kind == ast.KindUnknown || kind == ast.KindConstructor || kind == ast.KindConstructSignature || kind == ast.KindConstructorType - - var symbol *ast.Symbol - if sig.declaration != nil { - symbol = sig.declaration.Symbol() - } - t := c.newObjectType(ObjectFlagsAnonymous|ObjectFlagsSingleSignatureType, symbol) - if isConstructor { - c.setStructuredTypeMembers(t, nil, nil, []*Signature{sig}, nil) - } else { - c.setStructuredTypeMembers(t, nil, []*Signature{sig}, nil, nil) - } - sig.isolatedSignatureType = t - } - return sig.isolatedSignatureType -} - -func (c *Checker) getErasedSignature(signature *Signature) *Signature { - if len(signature.typeParameters) == 0 { - return signature - } - key := CachedSignatureKey{sig: signature, key: SignatureKeyErased} - erased := c.cachedSignatures[key] - if erased == nil { - erased = c.instantiateSignatureEx(signature, newArrayToSingleTypeMapper(signature.typeParameters, c.anyType), true /*eraseTypeParameters*/) - c.cachedSignatures[key] = erased - } - return erased -} - -func (c *Checker) getCanonicalSignature(signature *Signature) *Signature { - if len(signature.typeParameters) == 0 { - return signature - } - key := CachedSignatureKey{sig: signature, key: SignatureKeyCanonical} - canonical := c.cachedSignatures[key] - if canonical == nil { - canonical = c.createCanonicalSignature(signature) - c.cachedSignatures[key] = canonical - } - return canonical -} - -func (c *Checker) createCanonicalSignature(signature *Signature) *Signature { - // Create an instantiation of the signature where each unconstrained type parameter is replaced with - // its original. When a generic class or interface is instantiated, each generic method in the class or - // interface is instantiated with a fresh set of cloned type parameters (which we need to handle scenarios - // where different generations of the same type parameter are in scope). This leads to a lot of new type - // identities, and potentially a lot of work comparing those identities, so here we create an instantiation - // that uses the original type identities for all unconstrained type parameters. - typeArguments := core.Map(signature.typeParameters, func(tp *Type) *Type { - if tp.Target() != nil && c.getConstraintOfTypeParameter(tp.Target()) == nil { - return tp.Target() - } - return tp - }) - return c.getSignatureInstantiation(signature, typeArguments, ast.IsInJSFile(signature.declaration), nil /*inferredTypeParameters*/) -} - -func (c *Checker) getBaseSignature(signature *Signature) *Signature { - typeParameters := signature.typeParameters - if len(typeParameters) == 0 { - return signature - } - key := CachedSignatureKey{sig: signature, key: SignatureKeyBase} - if cached := c.cachedSignatures[key]; cached != nil { - return cached - } - baseConstraintMapper := newTypeMapper(typeParameters, core.Map(typeParameters, func(tp *Type) *Type { - return core.OrElse(c.getConstraintOfTypeParameter(tp), c.unknownType) - })) - baseConstraints := core.Map(typeParameters, func(tp *Type) *Type { - return c.instantiateType(tp, baseConstraintMapper) - }) - // Run N type params thru the immediate constraint mapper up to N times - // This way any noncircular interdependent type parameters are definitely resolved to their external dependencies - for range typeParameters { - baseConstraints = c.instantiateTypes(baseConstraints, baseConstraintMapper) - } - // and then apply a type eraser to remove any remaining circularly dependent type parameters - baseConstraints = c.instantiateTypes(baseConstraints, newArrayToSingleTypeMapper(typeParameters, c.anyType)) - result := c.instantiateSignatureEx(signature, newTypeMapper(typeParameters, baseConstraints), true /*eraseTypeParameters*/) - c.cachedSignatures[key] = result - return result -} - -// Instantiate a generic signature in the context of a non-generic signature (section 3.8.5 in TypeScript spec) -func (c *Checker) instantiateSignatureInContextOf(signature *Signature, contextualSignature *Signature, inferenceContext *InferenceContext, compareTypes TypeComparer) *Signature { - context := c.newInferenceContext(c.getTypeParametersForMapper(signature), signature, InferenceFlagsNone, compareTypes) - // We clone the inferenceContext to avoid fixing. For example, when the source signature is (x: T) => T[] and - // the contextual signature is (...args: A) => B, we want to infer the element type of A's constraint (say 'any') - // for T but leave it possible to later infer '[any]' back to A. - restType := c.getEffectiveRestType(contextualSignature) - var mapper *TypeMapper - if inferenceContext != nil { - if restType != nil && restType.flags&TypeFlagsTypeParameter != 0 { - mapper = inferenceContext.nonFixingMapper - } else { - mapper = inferenceContext.mapper - } - } - var sourceSignature *Signature - if mapper != nil { - sourceSignature = c.instantiateSignature(contextualSignature, mapper) - } else { - sourceSignature = contextualSignature - } - c.applyToParameterTypes(sourceSignature, signature, func(source *Type, target *Type) { - // Type parameters from outer context referenced by source type are fixed by instantiation of the source type - c.inferTypes(context.inferences, source, target, InferencePriorityNone, false) - }) - if inferenceContext == nil { - c.applyToReturnTypes(contextualSignature, signature, func(source *Type, target *Type) { - c.inferTypes(context.inferences, source, target, InferencePriorityReturnType, false) - }) - } - return c.getSignatureInstantiation(signature, c.getInferredTypes(context), ast.IsInJSFile(contextualSignature.declaration), nil /*inferredTypeParameters*/) -} - -func (c *Checker) resolveBaseTypesOfInterface(t *Type) { - data := t.AsInterfaceType() - for _, declaration := range t.symbol.Declarations { - if ast.IsInterfaceDeclaration(declaration) { - for _, node := range ast.GetExtendsHeritageClauseElements(declaration) { - baseType := c.getReducedType(c.getTypeFromTypeNode(node)) - if !c.isErrorType(baseType) { - if c.isValidBaseType(baseType) { - if t != baseType && !c.hasBaseType(baseType, t) { - data.resolvedBaseTypes = append(data.resolvedBaseTypes, baseType) - } else { - c.reportCircularBaseType(declaration, t) - } - } else { - c.error(node, diagnostics.An_interface_can_only_extend_an_object_type_or_intersection_of_object_types_with_statically_known_members) - } - } - } - } - } -} - -func (c *Checker) areAllOuterTypeParametersApplied(t *Type) bool { - // An unapplied type parameter has its symbol still the same as the matching argument symbol. - // Since parameters are applied outer-to-inner, only the last outer parameter needs to be checked. - outerTypeParameters := t.AsInterfaceType().OuterTypeParameters() - if len(outerTypeParameters) != 0 { - last := len(outerTypeParameters) - 1 - typeArguments := c.getTypeArguments(t) - return outerTypeParameters[last].symbol != typeArguments[last].symbol - } - return true -} - -func (c *Checker) reportCircularBaseType(node *ast.Node, t *Type) { - c.error(node, diagnostics.Type_0_recursively_references_itself_as_a_base_type, c.typeToStringEx(t, nil, TypeFormatFlagsWriteArrayAsGenericType)) -} - -// A valid base type is `any`, an object type or intersection of object types. -func (c *Checker) isValidBaseType(t *Type) bool { - if t.flags&TypeFlagsTypeParameter != 0 { - constraint := c.getBaseConstraintOfType(t) - if constraint != nil { - return c.isValidBaseType(constraint) - } - } - // TODO: Given that we allow type parameters here now, is this `!isGenericMappedType(type)` check really needed? - // There's no reason a `T` should be allowed while a `Readonly` should not. - return t.flags&(TypeFlagsObject|TypeFlagsNonPrimitive|TypeFlagsAny) != 0 && !c.isGenericMappedType(t) || - t.flags&TypeFlagsIntersection != 0 && core.Every(t.Types(), c.isValidBaseType) -} - -// TODO: GH#18217 If `checkBase` is undefined, we should not call this because this will always return false. -func (c *Checker) hasBaseType(t *Type, checkBase *Type) bool { - var check func(*Type) bool - check = func(t *Type) bool { - if t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsReference) != 0 { - target := getTargetType(t) - return target == checkBase || core.Some(c.getBaseTypes(target), check) - } - if t.flags&TypeFlagsIntersection != 0 { - return core.Some(t.Types(), check) - } - return false - } - return check(t) -} - -func getTargetType(t *Type) *Type { - if t.objectFlags&ObjectFlagsReference != 0 { - return t.Target() - } - return t -} - -func (c *Checker) getTypeWithThisArgument(t *Type, thisArgument *Type, needApparentType bool) *Type { - if t.objectFlags&ObjectFlagsReference != 0 { - target := t.Target() - typeArguments := c.getTypeArguments(t) - if len(target.AsInterfaceType().TypeParameters()) == len(typeArguments) { - if thisArgument == nil { - thisArgument = target.AsInterfaceType().thisType - } - return c.createTypeReference(target, core.Concatenate(typeArguments, []*Type{thisArgument})) - } - return t - } else if t.flags&TypeFlagsIntersection != 0 { - types := t.Types() - newTypes := core.SameMap(types, func(t *Type) *Type { return c.getTypeWithThisArgument(t, thisArgument, needApparentType) }) - if core.Same(newTypes, types) { - return t - } - return c.getIntersectionType(newTypes) - } - if needApparentType { - return c.getApparentType(t) - } - return t -} - -func (c *Checker) addInheritedMembers(symbols ast.SymbolTable, baseSymbols []*ast.Symbol) ast.SymbolTable { - for _, base := range baseSymbols { - if !isStaticPrivateIdentifierProperty(base) { - if s, ok := symbols[base.Name]; !ok || s.Flags&ast.SymbolFlagsValue == 0 { - if symbols == nil { - symbols = make(ast.SymbolTable) - } - symbols[base.Name] = base - } - } - } - return symbols -} - -func (c *Checker) resolveDeclaredMembers(t *Type) *InterfaceType { - d := t.AsInterfaceType() - if !d.declaredMembersResolved { - members := c.getMembersOfSymbol(t.symbol) - d.declaredMembersResolved = true - d.declaredMembers = members - d.declaredCallSignatures = c.getSignaturesOfSymbol(d.declaredMembers[ast.InternalSymbolNameCall]) - d.declaredConstructSignatures = c.getSignaturesOfSymbol(d.declaredMembers[ast.InternalSymbolNameNew]) - d.declaredIndexInfos = c.getIndexInfosOfSymbol(t.symbol) - } - return d -} - -func (c *Checker) getIndexInfosOfSymbol(symbol *ast.Symbol) []*IndexInfo { - indexSymbol := c.getIndexSymbol(symbol) - if indexSymbol != nil { - return c.getIndexInfosOfIndexSymbol(indexSymbol, slices.Collect(maps.Values(c.getMembersOfSymbol(symbol)))) - } - return nil -} - -// note intentional similarities to index signature building in `checkObjectLiteral` for parity -func (c *Checker) getIndexInfosOfIndexSymbol(indexSymbol *ast.Symbol, siblingSymbols []*ast.Symbol) []*IndexInfo { - var indexInfos []*IndexInfo - hasComputedStringProperty := false - hasComputedNumberProperty := false - hasComputedSymbolProperty := false - readonlyComputedStringProperty := true - readonlyComputedNumberProperty := true - readonlyComputedSymbolProperty := true - var propertySymbols []*ast.Symbol - for _, declaration := range indexSymbol.Declarations { - if ast.IsIndexSignatureDeclaration(declaration) { - parameters := declaration.Parameters() - returnTypeNode := declaration.Type() - if len(parameters) == 1 { - typeNode := parameters[0].AsParameterDeclaration().Type - if typeNode != nil { - valueType := c.anyType - if returnTypeNode != nil { - valueType = c.getTypeFromTypeNode(returnTypeNode) - } - forEachType(c.getTypeFromTypeNode(typeNode), func(keyType *Type) { - if c.isValidIndexKeyType(keyType) && findIndexInfo(indexInfos, keyType) == nil { - indexInfo := c.newIndexInfo(keyType, valueType, ast.HasModifier(declaration, ast.ModifierFlagsReadonly), declaration, nil) - indexInfos = append(indexInfos, indexInfo) - } - }) - } - } - } else if c.hasLateBindableIndexSignature(declaration) { - var declName *ast.Node - if ast.IsBinaryExpression(declaration) { - declName = declaration.AsBinaryExpression().Left - } else { - declName = declaration.Name() - } - var keyType *Type - if ast.IsElementAccessExpression(declName) { - keyType = c.checkExpressionCached(declName.AsElementAccessExpression().ArgumentExpression) - } else { - keyType = c.checkComputedPropertyName(declName) - } - if findIndexInfo(indexInfos, keyType) != nil { - continue - // Explicit index for key type takes priority - } - if c.isTypeAssignableTo(keyType, c.stringNumberSymbolType) { - if c.isTypeAssignableTo(keyType, c.numberType) { - hasComputedNumberProperty = true - if !hasReadonlyModifier(declaration) { - readonlyComputedNumberProperty = false - } - } else if c.isTypeAssignableTo(keyType, c.esSymbolType) { - hasComputedSymbolProperty = true - if !hasReadonlyModifier(declaration) { - readonlyComputedSymbolProperty = false - } - } else { - hasComputedStringProperty = true - if !hasReadonlyModifier(declaration) { - readonlyComputedStringProperty = false - } - } - propertySymbols = append(propertySymbols, declaration.Symbol()) - } - } - } - if hasComputedStringProperty || hasComputedNumberProperty || hasComputedSymbolProperty { - for _, sym := range siblingSymbols { - if sym != indexSymbol { - propertySymbols = append(propertySymbols, sym) - } - } - // aggregate similar index infos implied to be the same key to the same combined index info - if hasComputedStringProperty && findIndexInfo(indexInfos, c.stringType) == nil { - indexInfos = append(indexInfos, c.getObjectLiteralIndexInfo(readonlyComputedStringProperty, propertySymbols, c.stringType)) - } - if hasComputedNumberProperty && findIndexInfo(indexInfos, c.numberType) == nil { - indexInfos = append(indexInfos, c.getObjectLiteralIndexInfo(readonlyComputedNumberProperty, propertySymbols, c.numberType)) - } - if hasComputedSymbolProperty && findIndexInfo(indexInfos, c.esSymbolType) == nil { - indexInfos = append(indexInfos, c.getObjectLiteralIndexInfo(readonlyComputedSymbolProperty, propertySymbols, c.esSymbolType)) - } - } - return indexInfos -} - -// NOTE: currently does not make pattern literal indexers, eg `${number}px` -func (c *Checker) getObjectLiteralIndexInfo(isReadonly bool, properties []*ast.Symbol, keyType *Type) *IndexInfo { - var propTypes []*Type - var components []*ast.Node - for _, prop := range properties { - if keyType == c.stringType && !c.isSymbolWithSymbolName(prop) || - keyType == c.numberType && c.isSymbolWithNumericName(prop) || - keyType == c.esSymbolType && c.isSymbolWithSymbolName(prop) { - propTypes = append(propTypes, c.getTypeOfSymbol(prop)) - if c.isSymbolWithComputedName(prop) { - components = append(components, prop.Declarations[0]) - } - } - } - unionType := c.undefinedType - if len(propTypes) != 0 { - unionType = c.getUnionTypeEx(propTypes, UnionReductionSubtype, nil, nil) - } - return c.newIndexInfo(keyType, unionType, isReadonly, nil /*declaration*/, components) -} - -func (c *Checker) isSymbolWithSymbolName(symbol *ast.Symbol) bool { - if IsKnownSymbol(symbol) { - return true - } - if len(symbol.Declarations) != 0 { - name := symbol.Declarations[0].Name() - return name != nil && ast.IsComputedPropertyName(name) && c.isTypeAssignableToKind(c.checkComputedPropertyName(name), TypeFlagsESSymbol) - } - return false -} - -func (c *Checker) isSymbolWithNumericName(symbol *ast.Symbol) bool { - if isNumericLiteralName(symbol.Name) { - return true - } - if len(symbol.Declarations) != 0 { - name := symbol.Declarations[0].Name() - return name != nil && c.isNumericName(name) - } - return false -} - -func (c *Checker) isSymbolWithComputedName(symbol *ast.Symbol) bool { - if len(symbol.Declarations) != 0 { - name := symbol.Declarations[0].Name() - return name != nil && ast.IsComputedPropertyName(name) - } - return false -} - -func (c *Checker) isNumericName(name *ast.Node) bool { - switch name.Kind { - case ast.KindComputedPropertyName: - return c.isNumericComputedName(name) - case ast.KindIdentifier, ast.KindNumericLiteral, ast.KindStringLiteral: - return isNumericLiteralName(name.Text()) - } - return false -} - -func (c *Checker) isNumericComputedName(name *ast.Node) bool { - // It seems odd to consider an expression of type Any to result in a numeric name, - // but this behavior is consistent with checkIndexedAccess - return c.isTypeAssignableToKind(c.checkComputedPropertyName(name), TypeFlagsNumberLike) -} - -func (c *Checker) isValidIndexKeyType(t *Type) bool { - return t.flags&(TypeFlagsString|TypeFlagsNumber|TypeFlagsESSymbol) != 0 || - c.isPatternLiteralType(t) || - t.flags&TypeFlagsIntersection != 0 && !c.isGenericType(t) && core.Some(t.Types(), c.isValidIndexKeyType) -} - -func (c *Checker) findIndexInfo(indexInfos []*IndexInfo, keyType *Type) *IndexInfo { - for _, info := range indexInfos { - if info.keyType == keyType { - return info - } - } - return nil -} - -func (c *Checker) getIndexSymbol(symbol *ast.Symbol) *ast.Symbol { - return c.getMembersOfSymbol(symbol)[ast.InternalSymbolNameIndex] -} - -func (c *Checker) getSignaturesOfSymbol(symbol *ast.Symbol) []*Signature { - if symbol == nil { - return nil - } - var result []*Signature - for i, decl := range symbol.Declarations { - if !ast.IsFunctionLike(decl) { - continue - } - // Don't include signature if node is the implementation of an overloaded function. A node is considered - // an implementation node if it has a body and the previous node is of the same kind and immediately - // precedes the implementation node (i.e. has the same parent and ends where the implementation starts). - if i > 0 && decl.Body() != nil { - previous := symbol.Declarations[i-1] - if decl.Parent == previous.Parent && decl.Kind == previous.Kind && - (decl.Pos() == previous.End() || previous.Flags&ast.NodeFlagsReparsed != 0) { - continue - } - } - // If this is a function or method declaration, get the signature from the @type tag for the sake of optional parameters. - // Exclude contextually-typed kinds because we already apply the @type tag to the context, plus applying it here to the initializer would suppress checks that the two are compatible. - if ast.IsFunctionExpressionOrArrowFunction(decl) || ast.IsObjectLiteralMethod(decl) { - if sig := c.getSignatureOfFullSignatureType(decl); sig != nil { - result = append(result, sig) - continue - } - } - result = append(result, c.getSignatureFromDeclaration(decl)) - } - return result -} - -func (c *Checker) getSignatureFromDeclaration(declaration *ast.Node) *Signature { - links := c.signatureLinks.Get(declaration) - if links.resolvedSignature != nil { - return links.resolvedSignature - } - var parameters []*ast.Symbol - var flags SignatureFlags - var thisParameter *ast.Symbol - minArgumentCount := 0 - hasThisParameter := false - iife := ast.GetImmediatelyInvokedFunctionExpression(declaration) - for i, param := range declaration.Parameters() { - paramSymbol := param.Symbol() - typeNode := param.Type() - // Include parameter symbol instead of property symbol in the signature - if paramSymbol != nil && paramSymbol.Flags&ast.SymbolFlagsProperty != 0 && !ast.IsBindingPattern(param.Name()) { - resolvedSymbol := c.resolveName(param, paramSymbol.Name, ast.SymbolFlagsValue, nil /*nameNotFoundMessage*/, false /*isUse*/, false /*excludeGlobals*/) - paramSymbol = resolvedSymbol - } - if i == 0 && paramSymbol.Name == ast.InternalSymbolNameThis { - hasThisParameter = true - thisParameter = param.Symbol() - } else { - parameters = append(parameters, paramSymbol) - } - if typeNode != nil && typeNode.Kind == ast.KindLiteralType { - flags |= SignatureFlagsHasLiteralTypes - } - // Record a new minimum argument count if this is not an optional parameter - isOptionalParameter := isOptionalDeclaration(param) || - param.Initializer() != nil || - isRestParameter(param) || - iife != nil && len(parameters) > len(iife.AsCallExpression().Arguments.Nodes) && typeNode == nil - if !isOptionalParameter { - minArgumentCount = len(parameters) - } - } - // If only one accessor includes a this-type annotation, the other behaves as if it had the same type annotation - if (ast.IsGetAccessorDeclaration(declaration) || ast.IsSetAccessorDeclaration(declaration)) && c.hasBindableName(declaration) && (!hasThisParameter || thisParameter == nil) { - otherKind := core.IfElse(ast.IsGetAccessorDeclaration(declaration), ast.KindSetAccessor, ast.KindGetAccessor) - other := ast.GetDeclarationOfKind(c.getSymbolOfDeclaration(declaration), otherKind) - if other != nil { - thisParameter = c.getAnnotatedAccessorThisParameter(other) - } - } - var classType *Type - if ast.IsConstructorDeclaration(declaration) { - classType = c.getDeclaredTypeOfClassOrInterface(c.getMergedSymbol(declaration.Parent.Symbol())) - } - var typeParameters []*Type - if classType != nil { - typeParameters = classType.AsInterfaceType().LocalTypeParameters() - } else { - typeParameters = c.getTypeParametersFromDeclaration(declaration) - } - if hasRestParameter(declaration) { - flags |= SignatureFlagsHasRestParameter - } - if ast.IsConstructorTypeNode(declaration) || ast.IsConstructorDeclaration(declaration) || ast.IsConstructSignatureDeclaration(declaration) { - flags |= SignatureFlagsConstruct - } - if ast.IsConstructorTypeNode(declaration) && ast.HasSyntacticModifier(declaration, ast.ModifierFlagsAbstract) || ast.IsConstructorDeclaration(declaration) && ast.HasSyntacticModifier(declaration.Parent, ast.ModifierFlagsAbstract) { - flags |= SignatureFlagsAbstract - } - links.resolvedSignature = c.newSignature(flags, declaration, typeParameters, thisParameter, parameters, nil /*resolvedReturnType*/, nil /*resolvedTypePredicate*/, minArgumentCount) - return links.resolvedSignature -} - -func (c *Checker) getTypeParametersFromDeclaration(declaration *ast.Node) []*Type { - var result []*Type - for _, node := range declaration.TypeParameters() { - result = core.AppendIfUnique(result, c.getDeclaredTypeOfTypeParameter(node.Symbol())) - } - if len(result) == 0 && ast.IsFunctionDeclaration(declaration) { - if sig := c.getSignatureOfFullSignatureType(declaration); sig != nil { - return sig.TypeParameters() - } - } - return result -} - -func (c *Checker) getAnnotatedAccessorThisParameter(accessor *ast.Node) *ast.Symbol { - parameter := c.getAccessorThisParameter(accessor) - if parameter != nil { - return parameter.Symbol() - } - return nil -} - -func (c *Checker) getAccessorThisParameter(accessor *ast.Node) *ast.Node { - if len(accessor.Parameters()) == core.IfElse(ast.IsGetAccessorDeclaration(accessor), 1, 2) { - return ast.GetThisParameter(accessor) - } - return nil -} - -/** - * Indicates whether a declaration has an early-bound name or a dynamic name that can be late-bound. - */ -func (c *Checker) hasBindableName(node *ast.Node) bool { - return !ast.HasDynamicName(node) || c.hasLateBindableName(node) -} - -/** - * Indicates whether a declaration has a late-bindable dynamic name. - */ -func (c *Checker) hasLateBindableName(node *ast.Node) bool { - name := ast.GetNameOfDeclaration(node) - return name != nil && c.isLateBindableName(name) -} - -/** - * Indicates whether a declaration name is definitely late-bindable. - * A declaration name is only late-bindable if: - * - It is a `ComputedPropertyName`. - * - Its expression is an `Identifier` or either a `PropertyAccessExpression` an - * `ElementAccessExpression` consisting only of these same three types of nodes. - * - The type of its expression is a string or numeric literal type, or is a `unique symbol` type. - */ -func (c *Checker) isLateBindableName(node *ast.Node) bool { - if !isLateBindableAST(node) { - return false - } - if ast.IsComputedPropertyName(node) { - return isTypeUsableAsPropertyName(c.checkComputedPropertyName(node)) - } - return isTypeUsableAsPropertyName(c.checkExpressionCached(node.AsElementAccessExpression().ArgumentExpression)) -} - -func (c *Checker) hasLateBindableIndexSignature(node *ast.Node) bool { - name := ast.GetNameOfDeclaration(node) - return name != nil && c.isLateBindableIndexSignature(name) -} - -func (c *Checker) isLateBindableIndexSignature(node *ast.Node) bool { - if !isLateBindableAST(node) { - return false - } - if ast.IsComputedPropertyName(node) { - return c.isTypeUsableAsIndexSignature(c.checkComputedPropertyName(node)) - } - return c.isTypeUsableAsIndexSignature(c.checkExpressionCached(node.AsElementAccessExpression().ArgumentExpression)) -} - -func (c *Checker) isTypeUsableAsIndexSignature(t *Type) bool { - return c.isTypeAssignableTo(t, c.stringNumberSymbolType) -} - -func isLateBindableAST(node *ast.Node) bool { - var expr *ast.Node - switch { - case ast.IsComputedPropertyName(node): - expr = node.AsComputedPropertyName().Expression - case ast.IsElementAccessExpression(node): - expr = node.AsElementAccessExpression().ArgumentExpression - } - return expr != nil && ast.IsEntityNameExpression(expr) -} - -func (c *Checker) getReturnTypeOfSignature(sig *Signature) *Type { - if sig.resolvedReturnType != nil { - return sig.resolvedReturnType - } - if !c.pushTypeResolution(sig, TypeSystemPropertyNameResolvedReturnType) { - return c.errorType - } - var t *Type - switch { - case sig.target != nil: - t = c.instantiateType(c.getReturnTypeOfSignature(sig.target), sig.mapper) - case sig.composite != nil: - t = c.instantiateType(c.getUnionOrIntersectionType(core.Map(sig.composite.signatures, c.getReturnTypeOfSignature), sig.composite.isUnion, UnionReductionSubtype), sig.mapper) - default: - t = c.getReturnTypeFromAnnotation(sig.declaration) - if t == nil { - if !ast.NodeIsMissing(sig.declaration.Body()) { - t = c.getReturnTypeFromBody(sig.declaration, CheckModeNormal) - } else { - t = c.anyType - } - } - } - if sig.flags&SignatureFlagsIsInnerCallChain != 0 { - t = c.addOptionalTypeMarker(t) - } else if sig.flags&SignatureFlagsIsOuterCallChain != 0 { - t = c.getOptionalType(t, false /*isProperty*/) - } - if !c.popTypeResolution() { - if sig.declaration != nil { - typeNode := sig.declaration.Type() - if typeNode != nil { - c.error(typeNode, diagnostics.Return_type_annotation_circularly_references_itself) - } else if c.noImplicitAny { - name := ast.GetNameOfDeclaration(sig.declaration) - if name != nil { - c.error(name, diagnostics.X_0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, scanner.DeclarationNameToString(name)) - } else { - c.error(sig.declaration, diagnostics.Function_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions) - } - } - } - t = c.anyType - } - if sig.resolvedReturnType == nil { - sig.resolvedReturnType = t - } - return sig.resolvedReturnType -} - -func (c *Checker) getNonCircularReturnTypeOfSignature(sig *Signature) *Type { - if c.isResolvingReturnTypeOfSignature(sig) { - return c.anyType - } - return c.getReturnTypeOfSignature(sig) -} - -func (c *Checker) getReturnTypeFromAnnotation(declaration *ast.Node) *Type { - if ast.IsConstructorDeclaration(declaration) { - return c.getDeclaredTypeOfClassOrInterface(c.getMergedSymbol(declaration.Parent.Symbol())) - } - returnType := declaration.Type() - if returnType != nil { - return c.getTypeFromTypeNode(returnType) - } - if ast.IsGetAccessorDeclaration(declaration) && c.hasBindableName(declaration) { - return c.getAnnotatedAccessorType(ast.GetDeclarationOfKind(c.getSymbolOfDeclaration(declaration), ast.KindSetAccessor)) - } - return c.getReturnTypeOfFullSignature(declaration) -} - -func (c *Checker) getSignatureOfFullSignatureType(node *ast.Node) *Signature { - if ast.IsInJSFile(node) && ast.IsFunctionLike(node) && node.FunctionLikeData().FullSignature != nil { - return c.getSingleCallSignature(c.getTypeFromTypeNode(node.FunctionLikeData().FullSignature)) - } - return nil -} - -func (c *Checker) getParameterTypeOfFullSignature(node *ast.Node, parameter *ast.ParameterDeclarationNode) *Type { - if signature := c.getSignatureOfFullSignatureType(node); signature != nil { - pos := slices.Index(node.Parameters(), parameter) - if parameter.AsParameterDeclaration().DotDotDotToken != nil { - return c.getRestTypeAtPosition(signature, pos, false /*readonly*/) - } else { - return c.getTypeAtPosition(signature, pos) - } - } - return nil -} - -func (c *Checker) getReturnTypeOfFullSignature(node *ast.Node) *Type { - if signature := c.getSignatureOfFullSignatureType(node); signature != nil { - return c.getReturnTypeOfSignature(signature) - } - return nil -} - -func (c *Checker) getAnnotatedAccessorType(accessor *ast.Node) *Type { - node := c.getAnnotatedAccessorTypeNode(accessor) - if node != nil { - return c.getTypeFromTypeNode(node) - } - return nil -} - -func (c *Checker) getAnnotatedAccessorTypeNode(accessor *ast.Node) *ast.Node { - if accessor != nil { - switch accessor.Kind { - case ast.KindGetAccessor, ast.KindPropertyDeclaration: - return accessor.Type() - case ast.KindSetAccessor: - return getEffectiveSetAccessorTypeAnnotationNode(accessor) - } - } - return nil -} - -func getEffectiveSetAccessorTypeAnnotationNode(node *ast.Node) *ast.Node { - param := getSetAccessorValueParameter(node) - if param != nil { - return param.Type() - } - return nil -} - -func getSetAccessorValueParameter(accessor *ast.Node) *ast.Node { - parameters := accessor.Parameters() - if len(parameters) > 0 { - hasThis := len(parameters) == 2 && ast.IsThisParameter(parameters[0]) - return parameters[core.IfElse(hasThis, 1, 0)] - } - return nil -} - -func (c *Checker) getReturnTypeFromBody(fn *ast.Node, checkMode CheckMode) *Type { - body := fn.Body() - if body == nil { - return c.errorType - } - functionFlags := getFunctionFlags(fn) - isAsync := (functionFlags & FunctionFlagsAsync) != 0 - isGenerator := (functionFlags & FunctionFlagsGenerator) != 0 - var returnType *Type - var yieldType *Type - var nextType *Type - var fallbackReturnType *Type = c.voidType - switch { - case !ast.IsBlock(body): - returnType = c.checkExpressionCachedEx(body, checkMode & ^CheckModeSkipGenericFunctions) - if isAsync { - // From within an async function you can return either a non-promise value or a promise. Any - // Promise/A+ compatible implementation will always assimilate any foreign promise, so the - // return type of the body should be unwrapped to its awaited type, which we will wrap in - // the native Promise type later in this function. - returnType = c.unwrapAwaitedType(c.checkAwaitedType(returnType, false /*withAlias*/, fn /*errorNode*/, diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member)) - } - case isGenerator: - returnTypes, isNeverReturning := c.checkAndAggregateReturnExpressionTypes(fn, checkMode) - if isNeverReturning { - fallbackReturnType = c.neverType - } else if len(returnTypes) != 0 { - returnType = c.getUnionTypeEx(returnTypes, UnionReductionSubtype, nil, nil) - } - yieldTypes, nextTypes := c.checkAndAggregateYieldOperandTypes(fn, checkMode) - if len(yieldTypes) != 0 { - yieldType = c.getUnionTypeEx(yieldTypes, UnionReductionSubtype, nil, nil) - } - if len(nextTypes) != 0 { - nextType = c.getIntersectionType(nextTypes) - } - default: - types, isNeverReturning := c.checkAndAggregateReturnExpressionTypes(fn, checkMode) - if isNeverReturning { - // For an async function, the return type will not be never, but rather a Promise for never. - if functionFlags&FunctionFlagsAsync != 0 { - return c.createPromiseReturnType(fn, c.neverType) - } - // Normal function - return c.neverType - } - if len(types) == 0 { - // For an async function, the return type will not be void/undefined, but rather a Promise for void/undefined. - contextualReturnType := c.getContextualReturnType(fn, ContextFlagsNone) - var returnType *Type - if contextualReturnType != nil && core.OrElse(c.unwrapReturnType(contextualReturnType, functionFlags), c.voidType).flags&TypeFlagsUndefined != 0 { - returnType = c.undefinedType - } else { - returnType = c.voidType - } - if functionFlags&FunctionFlagsAsync != 0 { - return c.createPromiseReturnType(fn, returnType) - } - // Normal function - return returnType - } - // Return a union of the return expression types. - returnType = c.getUnionTypeEx(types, UnionReductionSubtype, nil, nil) - } - if returnType != nil || yieldType != nil || nextType != nil { - if yieldType != nil { - c.reportErrorsFromWidening(fn, yieldType, WideningKindGeneratorYield) - } - if returnType != nil { - c.reportErrorsFromWidening(fn, returnType, WideningKindFunctionReturn) - } - if nextType != nil { - c.reportErrorsFromWidening(fn, nextType, WideningKindGeneratorNext) - } - if returnType != nil && isUnitType(returnType) || yieldType != nil && isUnitType(yieldType) || nextType != nil && isUnitType(nextType) { - contextualSignature := c.getContextualSignatureForFunctionLikeDeclaration(fn) - var contextualType *Type - switch { - case contextualSignature == nil: - // No contextual type - case contextualSignature == c.getSignatureFromDeclaration(fn): - if !isGenerator { - contextualType = returnType - } - default: - contextualType = c.instantiateContextualType(c.getReturnTypeOfSignature(contextualSignature), fn, ContextFlagsNone) - } - if isGenerator { - yieldType = c.getWidenedLiteralLikeTypeForContextualIterationTypeIfNeeded(yieldType, contextualType, IterationTypeKindYield, isAsync) - returnType = c.getWidenedLiteralLikeTypeForContextualIterationTypeIfNeeded(returnType, contextualType, IterationTypeKindReturn, isAsync) - nextType = c.getWidenedLiteralLikeTypeForContextualIterationTypeIfNeeded(nextType, contextualType, IterationTypeKindNext, isAsync) - } else { - returnType = c.getWidenedLiteralLikeTypeForContextualReturnTypeIfNeeded(returnType, contextualType, isAsync) - } - } - if yieldType != nil { - yieldType = c.getWidenedType(yieldType) - } - if returnType != nil { - returnType = c.getWidenedType(returnType) - } - if nextType != nil { - nextType = c.getWidenedType(nextType) - } - } - if returnType == nil { - returnType = fallbackReturnType - } - if isGenerator { - if yieldType == nil { - yieldType = c.neverType - } - if nextType == nil { - nextType = c.getContextualIterationType(IterationTypeKindNext, fn) - if nextType == nil { - nextType = c.unknownType - } - } - return c.createGeneratorType(yieldType, returnType, nextType, isAsync) - } - // From within an async function you can return either a non-promise value or a promise. Any - // Promise/A+ compatible implementation will always assimilate any foreign promise, so the - // return type of the body is awaited type of the body, wrapped in a native Promise type. - if isAsync { - return c.createPromiseType(returnType) - } - return returnType -} - -// Returns the aggregated list of return types, plus a bool indicating a never-returning function. -func (c *Checker) checkAndAggregateReturnExpressionTypes(fn *ast.Node, checkMode CheckMode) ([]*Type, bool) { - functionFlags := getFunctionFlags(fn) - var aggregatedTypes []*Type - hasReturnWithNoExpression := c.functionHasImplicitReturn(fn) - hasReturnOfTypeNever := false - ast.ForEachReturnStatement(fn.Body(), func(returnStatement *ast.Node) bool { - expr := returnStatement.Expression() - if expr == nil { - hasReturnWithNoExpression = true - return false - } - expr = ast.SkipParentheses(expr) - // Bare calls to this same function don't contribute to inference - // and `return await` is also safe to unwrap here - if functionFlags&FunctionFlagsAsync != 0 && ast.IsAwaitExpression(expr) { - expr = ast.SkipParentheses(expr.Expression()) - } - if ast.IsCallExpression(expr) && ast.IsIdentifier(expr.Expression()) && c.checkExpressionCached(expr.Expression()).symbol == c.getMergedSymbol(fn.Symbol()) && - (!ast.IsFunctionExpressionOrArrowFunction(fn.Symbol().ValueDeclaration) || c.isConstantReference(expr.Expression())) { - hasReturnOfTypeNever = true - return false - } - t := c.checkExpressionCachedEx(expr, checkMode & ^CheckModeSkipGenericFunctions) - if functionFlags&FunctionFlagsAsync != 0 { - // From within an async function you can return either a non-promise value or a promise. Any - // Promise/A+ compatible implementation will always assimilate any foreign promise, so the - // return type of the body should be unwrapped to its awaited type, which should be wrapped in - // the native Promise type by the caller. - t = c.unwrapAwaitedType(c.checkAwaitedType(t, false /*withAlias*/, fn, diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member)) - } - if t.flags&TypeFlagsNever != 0 { - hasReturnOfTypeNever = true - } - aggregatedTypes = core.AppendIfUnique(aggregatedTypes, t) - return false - }) - if len(aggregatedTypes) == 0 && !hasReturnWithNoExpression && (hasReturnOfTypeNever || mayReturnNever(fn)) { - return nil, true - } - if c.strictNullChecks && len(aggregatedTypes) != 0 && hasReturnWithNoExpression { - aggregatedTypes = core.AppendIfUnique(aggregatedTypes, c.undefinedType) - } - return aggregatedTypes, false -} - -func (c *Checker) functionHasImplicitReturn(fn *ast.Node) bool { - endFlowNode := fn.BodyData().EndFlowNode - return endFlowNode != nil && c.isReachableFlowNode(endFlowNode) -} - -func mayReturnNever(fn *ast.Node) bool { - switch fn.Kind { - case ast.KindFunctionExpression, ast.KindArrowFunction: - return true - case ast.KindMethodDeclaration: - return ast.IsObjectLiteralExpression(fn.Parent) - } - return false -} - -func (c *Checker) checkAndAggregateYieldOperandTypes(fn *ast.Node, checkMode CheckMode) (yieldTypes []*Type, nextTypes []*Type) { - isAsync := (getFunctionFlags(fn) & FunctionFlagsAsync) != 0 - forEachYieldExpression(fn.Body(), func(yieldExpr *ast.Node) { - yieldExprType := c.undefinedWideningType - if yieldExpr.Expression() != nil { - yieldExprType = c.checkExpressionEx(yieldExpr.Expression(), checkMode) - } - yieldTypes = core.AppendIfUnique(yieldTypes, c.getYieldedTypeOfYieldExpression(yieldExpr, yieldExprType, c.anyType, isAsync)) - var nextType *Type - if yieldExpr.AsYieldExpression().AsteriskToken != nil { - iterationTypes := c.getIterationTypesOfIterable(yieldExprType, core.IfElse(isAsync, IterationUseAsyncYieldStar, IterationUseYieldStar), yieldExpr.Expression()) - nextType = iterationTypes.nextType - } else { - nextType = c.getContextualType(yieldExpr, ContextFlagsNone) - } - if nextType != nil { - nextTypes = core.AppendIfUnique(nextTypes, nextType) - } - }) - return yieldTypes, nextTypes -} - -func (c *Checker) createPromiseType(promisedType *Type) *Type { - // creates a `Promise` type where `T` is the promisedType argument - globalPromiseType := c.getGlobalPromiseTypeChecked() - if globalPromiseType != c.emptyGenericType { - // if the promised type is itself a promise, get the underlying type; otherwise, fallback to the promised type - // Unwrap an `Awaited` to `T` to improve inference. - promisedType = core.OrElse(c.getAwaitedTypeNoAlias(c.unwrapAwaitedType(promisedType)), c.unknownType) - return c.createTypeReference(globalPromiseType, []*Type{promisedType}) - } - return c.unknownType -} - -func (c *Checker) createPromiseLikeType(promisedType *Type) *Type { - // creates a `PromiseLike` type where `T` is the promisedType argument - globalPromiseLikeType := c.getGlobalPromiseLikeType() - if globalPromiseLikeType != c.emptyGenericType { - // if the promised type is itself a promise, get the underlying type; otherwise, fallback to the promised type - // Unwrap an `Awaited` to `T` to improve inference. - promisedType = core.OrElse(c.getAwaitedTypeNoAlias(c.unwrapAwaitedType(promisedType)), c.unknownType) - return c.createTypeReference(globalPromiseLikeType, []*Type{promisedType}) - } - return c.unknownType -} - -func (c *Checker) createPromiseReturnType(fn *ast.Node, promisedType *Type) *Type { - promiseType := c.createPromiseType(promisedType) - if promiseType == c.unknownType { - c.error(fn, core.IfElse(ast.IsImportCall(fn), - diagnostics.A_dynamic_import_call_returns_a_Promise_Make_sure_you_have_a_declaration_for_Promise_or_include_ES2015_in_your_lib_option, - diagnostics.An_async_function_or_method_must_return_a_Promise_Make_sure_you_have_a_declaration_for_Promise_or_include_ES2015_in_your_lib_option)) - return c.errorType - } - if c.getGlobalPromiseConstructorSymbol() == nil { - c.error(fn, core.IfElse(ast.IsImportCall(fn), - diagnostics.A_dynamic_import_call_in_ES5_requires_the_Promise_constructor_Make_sure_you_have_a_declaration_for_the_Promise_constructor_or_include_ES2015_in_your_lib_option, - diagnostics.An_async_function_or_method_in_ES5_requires_the_Promise_constructor_Make_sure_you_have_a_declaration_for_the_Promise_constructor_or_include_ES2015_in_your_lib_option)) - } - return promiseType -} - -func (c *Checker) unwrapReturnType(returnType *Type, functionFlags FunctionFlags) *Type { - isGenerator := functionFlags&FunctionFlagsGenerator != 0 - isAsync := functionFlags&FunctionFlagsAsync != 0 - if isGenerator { - returnIterationType := c.getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKindReturn, returnType, isAsync) - if returnIterationType == nil { - return c.errorType - } - if isAsync { - return c.getAwaitedTypeNoAlias(c.unwrapAwaitedType(returnIterationType)) - } - return returnIterationType - } - if isAsync { - return core.OrElse(c.getAwaitedTypeNoAlias(returnType), c.errorType) - } - return returnType -} - -func (c *Checker) getWidenedLiteralLikeTypeForContextualReturnTypeIfNeeded(t *Type, contextualSignatureReturnType *Type, isAsync bool) *Type { - if t != nil && isUnitType(t) { - var contextualType *Type - switch { - case contextualSignatureReturnType == nil: - // No contextual type - case isAsync: - contextualType = c.GetPromisedTypeOfPromise(contextualSignatureReturnType) - default: - contextualType = contextualSignatureReturnType - } - t = c.getWidenedLiteralLikeTypeForContextualType(t, contextualType) - } - return t -} - -func (c *Checker) getWidenedLiteralLikeTypeForContextualIterationTypeIfNeeded(t *Type, contextualSignatureReturnType *Type, kind IterationTypeKind, isAsyncGenerator bool) *Type { - if t != nil && isUnitType(t) { - var contextualType *Type - if contextualSignatureReturnType != nil { - contextualType = c.getIterationTypeOfGeneratorFunctionReturnType(kind, contextualSignatureReturnType, isAsyncGenerator) - } - t = c.getWidenedLiteralLikeTypeForContextualType(t, contextualType) - } - return t -} - -func (c *Checker) createGeneratorType(yieldType *Type, returnType *Type, nextType *Type, isAsyncGenerator bool) *Type { - resolver := core.IfElse(isAsyncGenerator, c.asyncIterationTypesResolver, c.syncIterationTypesResolver) - globalGeneratorType := resolver.getGlobalGeneratorType() - yieldType = core.OrElse(resolver.resolveIterationType(yieldType, nil /*errorNode*/), c.unknownType) - returnType = core.OrElse(resolver.resolveIterationType(returnType, nil /*errorNode*/), c.unknownType) - if globalGeneratorType == c.emptyGenericType { - // Fall back to the global IterableIterator type. - globalIterableIteratorType := resolver.getGlobalIterableIteratorType() - if globalIterableIteratorType != c.emptyGenericType { - return c.createTypeFromGenericGlobalType(globalIterableIteratorType, []*Type{yieldType, returnType, nextType}) - } - // The global Generator type doesn't exist, so report an error - resolver.getGlobalIterableIteratorTypeChecked() - return c.emptyObjectType - } - return c.createTypeFromGenericGlobalType(globalGeneratorType, []*Type{yieldType, returnType, nextType}) -} - -func (c *Checker) reportErrorsFromWidening(declaration *ast.Node, t *Type, wideningKind WideningKind) { - if c.noImplicitAny && t.objectFlags&ObjectFlagsContainsWideningType != 0 { - if wideningKind == WideningKindNormal || ast.IsFunctionLikeDeclaration(declaration) && c.shouldReportErrorsFromWideningWithContextualSignature(declaration, wideningKind) { - // Report implicit any error within type if possible, otherwise report error on declaration - if !c.reportWideningErrorsInType(t) { - c.reportImplicitAny(declaration, t, wideningKind) - } - } - } -} - -func (c *Checker) shouldReportErrorsFromWideningWithContextualSignature(declaration *ast.Node, wideningKind WideningKind) bool { - signature := c.getContextualSignatureForFunctionLikeDeclaration(declaration) - if signature == nil { - return true - } - returnType := c.getReturnTypeOfSignature(signature) - flags := getFunctionFlags(declaration) - switch wideningKind { - case WideningKindFunctionReturn: - if flags&FunctionFlagsGenerator != 0 { - returnType = core.OrElse(c.getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKindReturn, returnType, flags&FunctionFlagsAsync != 0), returnType) - } else if flags&FunctionFlagsAsync != 0 { - returnType = core.OrElse(c.getAwaitedTypeNoAlias(returnType), returnType) - } - return c.isGenericType(returnType) - case WideningKindGeneratorYield: - yieldType := c.getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKindYield, returnType, flags&FunctionFlagsAsync != 0) - return yieldType != nil && c.isGenericType(yieldType) - case WideningKindGeneratorNext: - nextType := c.getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKindNext, returnType, flags&FunctionFlagsAsync != 0) - return nextType != nil && c.isGenericType(nextType) - } - return false -} - -// Reports implicit any errors that occur as a result of widening 'null' and 'undefined' -// to 'any'. A call to reportWideningErrorsInType is normally accompanied by a call to -// getWidenedType. But in some cases getWidenedType is called without reporting errors -// (type argument inference is an example). -// -// The return value indicates whether an error was in fact reported. The particular circumstances -// are on a best effort basis. Currently, if the null or undefined that causes widening is inside -// an object literal property (arbitrarily deeply), this function reports an error. If no error is -// reported, reportImplicitAnyError is a suitable fallback to report a general error. -func (c *Checker) reportWideningErrorsInType(t *Type) bool { - errorReported := false - if t.objectFlags&ObjectFlagsContainsWideningType != 0 { - if t.flags&TypeFlagsUnion != 0 { - if core.Some(t.Types(), c.isEmptyObjectType) { - errorReported = true - } else { - for _, s := range t.Types() { - errorReported = errorReported || c.reportWideningErrorsInType(s) - } - } - } else if c.isArrayOrTupleType(t) { - for _, s := range c.getTypeArguments(t) { - errorReported = errorReported || c.reportWideningErrorsInType(s) - } - } else if isObjectLiteralType(t) { - for _, p := range c.getPropertiesOfObjectType(t) { - s := c.getTypeOfSymbol(p) - if s.objectFlags&ObjectFlagsContainsWideningType != 0 { - errorReported = c.reportWideningErrorsInType(s) - if !errorReported { - // we need to account for property types coming from object literal type normalization in unions - valueDeclaration := core.Find(p.Declarations, func(d *ast.Node) bool { - valueDeclaration := d.Symbol().ValueDeclaration - return valueDeclaration != nil && valueDeclaration.Parent == t.symbol.ValueDeclaration - }) - if valueDeclaration != nil { - c.error(valueDeclaration, diagnostics.Object_literal_s_property_0_implicitly_has_an_1_type, c.symbolToString(p), c.TypeToString(c.getWidenedType(s))) - errorReported = true - } - } - } - } - } - } - return errorReported -} - -func (c *Checker) getTypePredicateFromBody(fn *ast.Node) *TypePredicate { - switch fn.Kind { - case ast.KindConstructor, ast.KindGetAccessor, ast.KindSetAccessor: - return nil - } - functionFlags := getFunctionFlags(fn) - if functionFlags != FunctionFlagsNormal { - return nil - } - // Only attempt to infer a type predicate if there's exactly one return. - var singleReturn *ast.Node - body := fn.Body() - if body != nil && !ast.IsBlock(body) { - // arrow function - singleReturn = body - } else { - bailedEarly := ast.ForEachReturnStatement(body, func(returnStatement *ast.Node) bool { - if singleReturn != nil || returnStatement.Expression() == nil { - return true - } - singleReturn = returnStatement.Expression() - return false - }) - if bailedEarly || singleReturn == nil || c.functionHasImplicitReturn(fn) { - return nil - } - } - return c.checkIfExpressionRefinesAnyParameter(fn, singleReturn) -} - -func (c *Checker) checkIfExpressionRefinesAnyParameter(fn *ast.Node, expr *ast.Node) *TypePredicate { - expr = ast.SkipParentheses(expr) - returnType := c.checkExpressionCached(expr) - if returnType.flags&TypeFlagsBoolean == 0 { - return nil - } - for i, param := range fn.Parameters() { - initType := c.getTypeOfSymbol(param.Symbol()) - if initType == nil || initType.flags&TypeFlagsBoolean != 0 || !ast.IsIdentifier(param.Name()) || c.isSymbolAssigned(param.Symbol()) || isRestParameter(param) { - // Refining "x: boolean" to "x is true" or "x is false" isn't useful. - continue - } - trueType := c.checkIfExpressionRefinesParameter(fn, expr, param, initType) - if trueType != nil { - return c.newTypePredicate(TypePredicateKindIdentifier, param.Name().Text(), int32(i), trueType) - } - } - return nil -} - -func (c *Checker) checkIfExpressionRefinesParameter(fn *ast.Node, expr *ast.Node, param *ast.Node, initType *Type) *Type { - antecedent := getFlowNodeOfNode(expr) - if antecedent == nil && ast.IsReturnStatement(expr.Parent) { - antecedent = getFlowNodeOfNode(expr.Parent) - } - if antecedent == nil { - antecedent = &ast.FlowNode{Flags: ast.FlowFlagsStart} - } - trueCondition := &ast.FlowNode{Flags: ast.FlowFlagsTrueCondition, Node: expr, Antecedent: antecedent} - trueType := c.getFlowTypeOfReferenceEx(param.Name(), initType, initType, fn, trueCondition) - if trueType == initType { - return nil - } - // "x is T" means that x is T if and only if it returns true. If it returns false then x is not T. - // This means that if the function is called with an argument of type trueType, there can't be anything left in the `else` branch. It must reduce to `never`. - falseCondition := &ast.FlowNode{Flags: ast.FlowFlagsFalseCondition, Node: expr, Antecedent: antecedent} - falseSubtype := c.getReducedType(c.getFlowTypeOfReferenceEx(param.Name(), initType, trueType, fn, falseCondition)) - if falseSubtype.flags&TypeFlagsNever != 0 { - return trueType - } - return nil -} - -func (c *Checker) addOptionalTypeMarker(t *Type) *Type { - if c.strictNullChecks { - return c.getUnionType([]*Type{t, c.optionalType}) - } - return t -} - -func (c *Checker) instantiateSignature(sig *Signature, m *TypeMapper) *Signature { - return c.instantiateSignatureEx(sig, m, m == c.permissiveMapper /*eraseTypeParameters*/) -} - -func (c *Checker) instantiateSignatureEx(sig *Signature, m *TypeMapper, eraseTypeParameters bool) *Signature { - var freshTypeParameters []*Type - if len(sig.typeParameters) != 0 && !eraseTypeParameters { - // First create a fresh set of type parameters, then include a mapping from the old to the - // new type parameters in the mapper function. Finally store this mapper in the new type - // parameters such that we can use it when instantiating constraints. - freshTypeParameters = core.Map(sig.typeParameters, c.cloneTypeParameter) - m = c.combineTypeMappers(newTypeMapper(sig.typeParameters, freshTypeParameters), m) - for _, tp := range freshTypeParameters { - tp.AsTypeParameter().mapper = m - } - } - // Don't compute resolvedReturnType and resolvedTypePredicate now, - // because using `mapper` now could trigger inferences to become fixed. (See `createInferenceContext`.) - // See GH#17600. - result := c.newSignature(sig.flags&SignatureFlagsPropagatingFlags, sig.declaration, freshTypeParameters, - c.instantiateSymbol(sig.thisParameter, m), c.instantiateSymbols(sig.parameters, m), - nil /*resolvedReturnType*/, nil /*resolvedTypePredicate*/, int(sig.minArgumentCount)) - result.target = sig - result.mapper = m - return result -} - -func (c *Checker) instantiateIndexInfo(info *IndexInfo, m *TypeMapper) *IndexInfo { - newValueType := c.instantiateType(info.valueType, m) - if newValueType == info.valueType { - return info - } - return c.newIndexInfo(info.keyType, newValueType, info.isReadonly, info.declaration, info.components) -} - -func (c *Checker) resolveAnonymousTypeMembers(t *Type) { - d := t.AsObjectType() - if d.target != nil { - c.setStructuredTypeMembers(t, nil, nil, nil, nil) - members := c.createInstantiatedSymbolTable(c.getPropertiesOfObjectType(d.target), d.mapper) - callSignatures := c.instantiateSignatures(c.getSignaturesOfType(d.target, SignatureKindCall), d.mapper) - constructSignatures := c.instantiateSignatures(c.getSignaturesOfType(d.target, SignatureKindConstruct), d.mapper) - indexInfos := c.instantiateIndexInfos(c.getIndexInfosOfType(d.target), d.mapper) - c.setStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos) - return - } - symbol := c.getMergedSymbol(t.symbol) - if symbol.Flags&ast.SymbolFlagsTypeLiteral != 0 { - c.setStructuredTypeMembers(t, nil, nil, nil, nil) - members := c.getMembersOfSymbol(symbol) - callSignatures := c.getSignaturesOfSymbol(members[ast.InternalSymbolNameCall]) - constructSignatures := c.getSignaturesOfSymbol(members[ast.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(ast.SymbolTable) - for _, p := range members { - if p.Flags&ast.SymbolFlagsBlockScoped == 0 && !(p.Flags&ast.SymbolFlagsValueModule != 0 && len(p.Declarations) != 0 && core.Every(p.Declarations, ast.IsAmbientModule)) { - varsOnly[p.Name] = p - } - } - members = varsOnly - } - var baseConstructorIndexInfo *IndexInfo - c.setStructuredTypeMembers(t, members, nil, nil, nil) - if symbol.Flags&ast.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 = c.anyBaseTypeIndexInfo - } - } - indexSymbol := members[ast.InternalSymbolNameIndex] - if indexSymbol != nil { - indexInfos = c.getIndexInfosOfIndexSymbol(indexSymbol, slices.Collect(maps.Values(members))) - } else { - if baseConstructorIndexInfo != nil { - indexInfos = append(indexInfos, baseConstructorIndexInfo) - } - if symbol.Flags&ast.SymbolFlagsEnum != 0 && (c.getDeclaredTypeOfSymbol(symbol).flags&TypeFlagsEnum != 0 || core.Some(d.properties, func(prop *ast.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&(ast.SymbolFlagsFunction|ast.SymbolFlagsMethod) != 0 { - d.signatures = c.getSignaturesOfSymbol(symbol) - d.callSignatureCount = len(d.signatures) - } - // And likewise for construct signatures for classes - if symbol.Flags&ast.SymbolFlagsClass != 0 { - classType := c.getDeclaredTypeOfClassOrInterface(symbol) - constructSignatures := c.getSignaturesOfSymbol(symbol.Members[ast.InternalSymbolNameConstructor]) - if len(constructSignatures) == 0 { - constructSignatures = c.getDefaultConstructSignatures(classType) - } - d.signatures = append(d.signatures, constructSignatures...) - } -} - -// The mappingThisOnly flag indicates that the only type parameter being mapped is "this". When the flag is true, -// we check symbols to see if we can quickly conclude they are free of "this" references, thus needing no instantiation. -func (c *Checker) createInstantiatedSymbolTable(symbols []*ast.Symbol, m *TypeMapper) ast.SymbolTable { - if len(symbols) == 0 { - return nil - } - result := make(ast.SymbolTable) - for _, symbol := range symbols { - result[symbol.Name] = c.instantiateSymbol(symbol, m) - } - return result -} - -// The mappingThisOnly flag indicates that the only type parameter being mapped is "this". When the flag is true, -// we check symbols to see if we can quickly conclude they are free of "this" references, thus needing no instantiation. -func (c *Checker) instantiateSymbolTable(symbols ast.SymbolTable, m *TypeMapper, mappingThisOnly bool) ast.SymbolTable { - if len(symbols) == 0 { - return nil - } - result := make(ast.SymbolTable, len(symbols)) - for id, symbol := range symbols { - if c.isNamedMember(symbol, id) { - if mappingThisOnly && isThisless(symbol) { - result[id] = symbol - } else { - result[id] = c.instantiateSymbol(symbol, m) - } - } - } - return result -} - -func (c *Checker) instantiateSymbol(symbol *ast.Symbol, m *TypeMapper) *ast.Symbol { - if symbol == nil { - return nil - } - links := c.valueSymbolLinks.Get(symbol) - // If the type of the symbol is already resolved, and if that type could not possibly - // be affected by instantiation, simply return the symbol itself. - if links.resolvedType != nil && !c.couldContainTypeVariables(links.resolvedType) { - if symbol.Flags&ast.SymbolFlagsSetAccessor == 0 { - return symbol - } - // If we're a setter, check writeType. - if links.writeType != nil && !c.couldContainTypeVariables(links.writeType) { - return symbol - } - } - if symbol.CheckFlags&ast.CheckFlagsInstantiated != 0 { - // If symbol being instantiated is itself a instantiation, fetch the original target and combine the - // type mappers. This ensures that original type identities are properly preserved and that aliases - // always reference a non-aliases. - symbol = links.target - m = c.combineTypeMappers(links.mapper, m) - } - // Keep the flags from the symbol we're instantiating. Mark that is instantiated, and - // also transient so that we can just store data on it directly. - result := c.newSymbol(symbol.Flags, symbol.Name) - result.CheckFlags = ast.CheckFlagsInstantiated | symbol.CheckFlags&(ast.CheckFlagsReadonly|ast.CheckFlagsLate|ast.CheckFlagsOptionalParameter|ast.CheckFlagsRestParameter) - result.Declarations = symbol.Declarations - result.Parent = symbol.Parent - result.ValueDeclaration = symbol.ValueDeclaration - resultLinks := c.valueSymbolLinks.Get(result) - resultLinks.target = symbol - resultLinks.mapper = m - resultLinks.nameType = links.nameType - return result -} - -// Returns true if the class or interface member given by the symbol is free of "this" references. The -// function may return false for symbols that are actually free of "this" references because it is not -// feasible to perform a complete analysis in all cases. In particular, property members with types -// inferred from their initializers and function members with inferred return types are conservatively -// assumed not to be free of "this" references. -func isThisless(symbol *ast.Symbol) bool { - if len(symbol.Declarations) == 1 { - declaration := symbol.Declarations[0] - if declaration != nil { - switch declaration.Kind { - case ast.KindPropertyDeclaration, ast.KindPropertySignature: - return isThislessVariableLikeDeclaration(declaration) - case ast.KindMethodDeclaration, ast.KindMethodSignature, ast.KindConstructor, ast.KindGetAccessor, ast.KindSetAccessor: - return isThislessFunctionLikeDeclaration(declaration) - } - } - } - return false -} - -// A variable-like declaration is free of this references if it has a type annotation -// that is thisless, or if it has no type annotation and no initializer (and is thus of type any). -func isThislessVariableLikeDeclaration(node *ast.Node) bool { - typeNode := node.Type() - if typeNode != nil { - return isThislessType(typeNode) - } - return node.Initializer() == nil -} - -// A type is free of this references if it's the any, string, number, boolean, symbol, or void keyword, a string -// literal type, an array with an element type that is free of this references, or a type reference that is -// free of this references. -func isThislessType(node *ast.Node) bool { - switch node.Kind { - case ast.KindAnyKeyword, ast.KindUnknownKeyword, ast.KindStringKeyword, ast.KindNumberKeyword, ast.KindBigIntKeyword, ast.KindBooleanKeyword, - ast.KindSymbolKeyword, ast.KindObjectKeyword, ast.KindVoidKeyword, ast.KindUndefinedKeyword, ast.KindNeverKeyword, ast.KindLiteralType: - return true - case ast.KindArrayType: - return isThislessType(node.AsArrayTypeNode().ElementType) - case ast.KindTypeReference: - return core.Every(node.TypeArguments(), isThislessType) - } - return false -} - -// A function-like declaration is considered free of `this` references if it has a return type -// annotation that is free of this references and if each parameter is thisless and if -// each type parameter (if present) is thisless. -func isThislessFunctionLikeDeclaration(node *ast.Node) bool { - returnType := node.Type() - return (ast.IsConstructorDeclaration(node) || returnType != nil && isThislessType(returnType)) && - core.Every(node.Parameters(), isThislessVariableLikeDeclaration) && - core.Every(node.TypeParameters(), isThislessTypeParameter) -} - -// A type parameter is thisless if its constraint is thisless, or if it has no constraint. */ -func isThislessTypeParameter(node *ast.Node) bool { - constraint := node.AsTypeParameter().Constraint - return constraint == nil || isThislessType(constraint) -} - -func (c *Checker) getDefaultConstructSignatures(classType *Type) []*Signature { - baseConstructorType := c.getBaseConstructorTypeOfClass(classType) - baseSignatures := c.getSignaturesOfType(baseConstructorType, SignatureKindConstruct) - declaration := ast.GetClassLikeDeclarationOfSymbol(classType.symbol) - isAbstract := declaration != nil && ast.HasSyntacticModifier(declaration, ast.ModifierFlagsAbstract) - if len(baseSignatures) == 0 { - flags := core.IfElse(isAbstract, SignatureFlagsConstruct|SignatureFlagsAbstract, SignatureFlagsConstruct) - return []*Signature{c.newSignature(flags, nil, classType.AsInterfaceType().LocalTypeParameters(), nil, nil, classType, nil, 0)} - } - baseTypeNode := getBaseTypeNodeOfClass(classType) - isJavaScript := declaration != nil && ast.IsInJSFile(declaration) - typeArguments := c.getTypeArgumentsFromNode(baseTypeNode) - typeArgCount := len(typeArguments) - var result []*Signature - for _, baseSig := range baseSignatures { - minTypeArgumentCount := c.getMinTypeArgumentCount(baseSig.typeParameters) - typeParamCount := len(baseSig.typeParameters) - if isJavaScript || typeArgCount >= minTypeArgumentCount && typeArgCount <= typeParamCount { - var sig *Signature - if typeParamCount != 0 { - sig = c.createSignatureInstantiation(baseSig, c.fillMissingTypeArguments(typeArguments, baseSig.typeParameters, minTypeArgumentCount, isJavaScript)) - } else { - sig = c.cloneSignature(baseSig) - } - sig.typeParameters = classType.AsInterfaceType().LocalTypeParameters() - sig.resolvedReturnType = classType - if isAbstract { - sig.flags |= SignatureFlagsAbstract - } else { - sig.flags &^= SignatureFlagsAbstract - } - result = append(result, sig) - } - } - return result -} - -func (c *Checker) resolveMappedTypeMembers(t *Type) { - members := make(ast.SymbolTable) - var indexInfos []*IndexInfo - // Resolve upfront such that recursive references see an empty object type. - c.setStructuredTypeMembers(t, nil, nil, nil, nil) - // In { [P in K]: T }, we refer to P as the type parameter type, K as the constraint type, - // and T as the template type. - typeParameter := c.getTypeParameterFromMappedType(t) - constraintType := c.getConstraintTypeFromMappedType(t) - mappedType := core.OrElse(t.AsMappedType().target, t) - nameType := c.getNameTypeFromMappedType(mappedType) - shouldLinkPropDeclarations := c.getMappedTypeNameTypeKind(mappedType) != MappedTypeNameTypeKindRemapping - templateType := c.getTemplateTypeFromMappedType(mappedType) - modifiersType := c.getApparentType(c.getModifiersTypeFromMappedType(t)) - // The 'T' in 'keyof T' - templateModifiers := getMappedTypeModifiers(t) - include := TypeFlagsStringOrNumberLiteralOrUnique - addMemberForKeyTypeWorker := func(keyType *Type, propNameType *Type) { - // If the current iteration type constituent is a string literal type, create a property. - // Otherwise, for type string create a string index signature. - if isTypeUsableAsPropertyName(propNameType) { - propName := getPropertyNameFromType(propNameType) - // String enum members from separate enums with identical values - // are distinct types with the same property name. Make the resulting - // property symbol's name type be the union of those enum member types. - if existingProp := members[propName]; existingProp != nil { - valueLinks := c.valueSymbolLinks.Get(existingProp) - valueLinks.nameType = c.getUnionType([]*Type{valueLinks.nameType, propNameType}) - mappedLinks := c.mappedSymbolLinks.Get(existingProp) - mappedLinks.keyType = c.getUnionType([]*Type{mappedLinks.keyType, keyType}) - } else { - var modifiersProp *ast.Symbol - if isTypeUsableAsPropertyName(keyType) { - modifiersProp = c.getPropertyOfType(modifiersType, getPropertyNameFromType(keyType)) - } - isOptional := templateModifiers&MappedTypeModifiersIncludeOptional != 0 || templateModifiers&MappedTypeModifiersExcludeOptional == 0 && modifiersProp != nil && modifiersProp.Flags&ast.SymbolFlagsOptional != 0 - isReadonly := templateModifiers&MappedTypeModifiersIncludeReadonly != 0 || templateModifiers&MappedTypeModifiersExcludeReadonly == 0 && modifiersProp != nil && c.isReadonlySymbol(modifiersProp) - stripOptional := c.strictNullChecks && !isOptional && modifiersProp != nil && modifiersProp.Flags&ast.SymbolFlagsOptional != 0 - var lateFlag ast.CheckFlags - if modifiersProp != nil { - lateFlag = modifiersProp.CheckFlags & ast.CheckFlagsLate - } - prop := c.newSymbol(ast.SymbolFlagsProperty|core.IfElse(isOptional, ast.SymbolFlagsOptional, 0), propName) - prop.CheckFlags = lateFlag | ast.CheckFlagsMapped | core.IfElse(isReadonly, ast.CheckFlagsReadonly, 0) | core.IfElse(stripOptional, ast.CheckFlagsStripOptional, 0) - valueLinks := c.valueSymbolLinks.Get(prop) - valueLinks.containingType = t - valueLinks.nameType = propNameType - mappedLinks := c.mappedSymbolLinks.Get(prop) - mappedLinks.keyType = keyType - if modifiersProp != nil { - mappedLinks.syntheticOrigin = modifiersProp - if shouldLinkPropDeclarations { - prop.Declarations = modifiersProp.Declarations - } - } - members[propName] = prop - } - } else if c.isValidIndexKeyType(propNameType) || propNameType.flags&(TypeFlagsAny|TypeFlagsEnum) != 0 { - indexKeyType := propNameType - switch { - case propNameType.flags&(TypeFlagsAny|TypeFlagsString) != 0: - indexKeyType = c.stringType - case propNameType.flags&(TypeFlagsNumber|TypeFlagsEnum) != 0: - indexKeyType = c.numberType - } - propType := c.instantiateType(templateType, appendTypeMapping(t.AsMappedType().mapper, typeParameter, keyType)) - modifiersIndexInfo := c.getApplicableIndexInfo(modifiersType, propNameType) - isReadonly := templateModifiers&MappedTypeModifiersIncludeReadonly != 0 || templateModifiers&MappedTypeModifiersExcludeReadonly == 0 && modifiersIndexInfo != nil && modifiersIndexInfo.isReadonly - indexInfo := c.newIndexInfo(indexKeyType, propType, isReadonly, nil, nil) - indexInfos = c.appendIndexInfo(indexInfos, indexInfo, true /*union*/) - } - } - addMemberForKeyType := func(keyType *Type) { - propNameType := keyType - if nameType != nil { - propNameType = c.instantiateType(nameType, appendTypeMapping(t.AsMappedType().mapper, typeParameter, keyType)) - } - forEachType(propNameType, func(t *Type) { - addMemberForKeyTypeWorker(keyType, t) - }) - } - if c.isMappedTypeWithKeyofConstraintDeclaration(t) { - // We have a { [P in keyof T]: X } - c.forEachMappedTypePropertyKeyTypeAndIndexSignatureKeyType(modifiersType, include, false /*stringsOnly*/, addMemberForKeyType) - } else { - forEachType(c.getLowerBoundOfKeyType(constraintType), addMemberForKeyType) - } - c.setStructuredTypeMembers(t, members, nil, nil, indexInfos) -} - -func (c *Checker) getTypeOfMappedSymbol(symbol *ast.Symbol) *Type { - links := c.valueSymbolLinks.Get(symbol) - if links.resolvedType == nil { - mappedType := links.containingType - if !c.pushTypeResolution(symbol, TypeSystemPropertyNameType) { - mappedType.AsMappedType().containsError = true - return c.errorType - } - templateType := c.getTemplateTypeFromMappedType(core.OrElse(mappedType.AsMappedType().target, mappedType)) - mapper := appendTypeMapping(mappedType.AsMappedType().mapper, c.getTypeParameterFromMappedType(mappedType), c.mappedSymbolLinks.Get(symbol).keyType) - propType := c.instantiateType(templateType, mapper) - // When creating an optional property in strictNullChecks mode, if 'undefined' isn't assignable to the - // type, we include 'undefined' in the type. Similarly, when creating a non-optional property in strictNullChecks - // mode, if the underlying property is optional we remove 'undefined' from the type. - switch { - case c.strictNullChecks && symbol.Flags&ast.SymbolFlagsOptional != 0 && !c.maybeTypeOfKind(propType, TypeFlagsUndefined|TypeFlagsVoid): - propType = c.getOptionalType(propType, true /*isProperty*/) - case symbol.CheckFlags&ast.CheckFlagsStripOptional != 0: - propType = c.removeMissingOrUndefinedType(propType) - } - if !c.popTypeResolution() { - c.error(c.currentNode, diagnostics.Type_of_property_0_circularly_references_itself_in_mapped_type_1, c.symbolToString(symbol), c.TypeToString(mappedType)) - propType = c.errorType - } - if links.resolvedType == nil { - links.resolvedType = propType - } - } - return links.resolvedType -} - -// Return the lower bound of the key type in a mapped type. Intuitively, the lower -// bound includes those keys that are known to always be present, for example because -// because of constraints on type parameters (e.g. 'keyof T' for a constrained T). -func (c *Checker) getLowerBoundOfKeyType(t *Type) *Type { - switch { - case t.flags&TypeFlagsIndex != 0: - t := c.getApparentType(t.AsIndexType().target) - if c.isGenericTupleType(t) { - return c.getKnownKeysOfTupleType(t) - } - return c.getIndexType(t) - case t.flags&TypeFlagsConditional != 0: - if t.AsConditionalType().root.isDistributive { - checkType := t.AsConditionalType().checkType - constraint := c.getLowerBoundOfKeyType(checkType) - if constraint != checkType { - return c.getConditionalTypeInstantiation(t, prependTypeMapping(t.AsConditionalType().root.checkType, constraint, t.AsConditionalType().mapper), false /*forConstraint*/, nil) - } - } - return t - case t.flags&TypeFlagsUnion != 0: - return c.mapTypeEx(t, c.getLowerBoundOfKeyType, true /*noReductions*/) - case t.flags&TypeFlagsIntersection != 0: - // Similarly to getTypeFromIntersectionTypeNode, we preserve the special string & {}, number & {}, - // and bigint & {} intersections that are used to prevent subtype reduction in union types. - types := t.Types() - if len(types) == 2 && types[0].flags&(TypeFlagsString|TypeFlagsNumber|TypeFlagsBigInt) != 0 && types[1] == c.emptyTypeLiteralType { - return t - } - return c.getIntersectionType(core.SameMap(t.Types(), c.getLowerBoundOfKeyType)) - } - return t -} - -func (c *Checker) resolveUnionTypeMembers(t *Type) { - // The members and properties collections are empty for union types. To get all properties of a union - // type use getPropertiesOfType (only the language service uses this). - callSignatures := c.getUnionSignatures(core.Map(t.Types(), func(t *Type) []*Signature { - if t == c.globalFunctionType { - return []*Signature{c.unknownSignature} - } - return c.getSignaturesOfType(t, SignatureKindCall) - })) - if len(callSignatures) == 0 { - callSignatures = c.getArrayMemberCallSignatures(t) - } - constructSignatures := c.getUnionSignatures(core.Map(t.Types(), func(t *Type) []*Signature { - return c.getSignaturesOfType(t, SignatureKindConstruct) - })) - indexInfos := c.getUnionIndexInfos(t.Types()) - c.setStructuredTypeMembers(t, nil, callSignatures, constructSignatures, indexInfos) -} - -func (c *Checker) getArrayMemberCallSignatures(t *Type) []*Signature { - // Check if union is exclusively instantiations of a member of the global Array or ReadonlyArray type. - var memberName string - for i, t := range t.Types() { - if t.objectFlags&ObjectFlagsInstantiated == 0 || t.symbol == nil || t.symbol.Parent == nil || !c.isArrayOrTupleSymbol(t.symbol.Parent) { - return nil - } - if i == 0 { - memberName = t.symbol.Name - } else if memberName != t.symbol.Name { - return nil - } - } - // Transform the type from `(A[] | B[])["member"]` to `(A | B)[]["member"]` (since we pretend array is covariant anyway). - arrayArg := c.mapType(t, func(t *Type) *Type { - return t.Mapper().Map(core.IfElse(c.isReadonlyArraySymbol(t.symbol.Parent), c.globalReadonlyArrayType, c.globalArrayType).AsInterfaceType().TypeParameters()[0]) - }) - arrayType := c.createArrayTypeEx(arrayArg, someType(t, func(t *Type) bool { - return c.isReadonlyArraySymbol(t.symbol.Parent) - })) - return c.getSignaturesOfType(c.getTypeOfPropertyOfType(arrayType, memberName), SignatureKindCall) -} - -func (c *Checker) isArrayOrTupleSymbol(symbol *ast.Symbol) bool { - if symbol == nil || c.globalArrayType.symbol == nil || c.globalReadonlyArrayType.symbol == nil { - return false - } - return c.getSymbolIfSameReference(symbol, c.globalArrayType.symbol) != nil || c.getSymbolIfSameReference(symbol, c.globalReadonlyArrayType.symbol) != nil -} - -func (c *Checker) isReadonlyArraySymbol(symbol *ast.Symbol) bool { - if symbol == nil || c.globalReadonlyArrayType.symbol == nil { - return false - } - return c.getSymbolIfSameReference(symbol, c.globalReadonlyArrayType.symbol) != nil -} - -// The signatures of a union type are those signatures that are present in each of the constituent types. -// Generic signatures must match exactly, but non-generic signatures are allowed to have extra optional -// parameters and may differ in return types. When signatures differ in return types, the resulting return -// type is the union of the constituent return types. -func (c *Checker) getUnionSignatures(signatureLists [][]*Signature) []*Signature { - var result []*Signature - var indexWithLengthOverOne int - var countLengthOverOne int - for i := range signatureLists { - if len(signatureLists[i]) == 0 { - return nil - } - if len(signatureLists[i]) > 1 { - indexWithLengthOverOne = i - countLengthOverOne++ - } - for _, signature := range signatureLists[i] { - // Only process signatures with parameter lists that aren't already in the result list - if result == nil || c.findMatchingSignature(result, signature, false /*partialMatch*/, false /*ignoreThisTypes*/, true /*ignoreReturnTypes*/) == nil { - unionSignatures := c.findMatchingSignatures(signatureLists, signature, i) - if unionSignatures != nil { - s := signature - // Union the result types when more than one signature matches - if len(unionSignatures) > 1 { - thisParameter := signature.thisParameter - firstThisParameterOfUnionSignatures := core.FirstNonNil(unionSignatures, func(sig *Signature) *ast.Symbol { - return sig.thisParameter - }) - if firstThisParameterOfUnionSignatures != nil { - thisType := c.getIntersectionType(core.MapNonNil(unionSignatures, func(sig *Signature) *Type { - if sig.thisParameter != nil { - return c.getTypeOfSymbol(sig.thisParameter) - } - return nil - })) - thisParameter = c.createSymbolWithType(firstThisParameterOfUnionSignatures, thisType) - } - s = c.createUnionSignature(signature, unionSignatures) - s.thisParameter = thisParameter - } - result = append(result, s) - } - } - } - } - if len(result) == 0 && countLengthOverOne <= 1 { - // No sufficiently similar signature existed to subsume all the other signatures in the union - time to see if we can make a single - // signature that handles all of them. We only do this when there are overloads in only one constituent. (Overloads are conditional in - // nature and having overloads in multiple constituents would necessitate making a power set of signatures from the type, whose - // ordering would be non-obvious) - masterList := signatureLists[indexWithLengthOverOne] - var results []*Signature = slices.Clone(masterList) - for _, signatures := range signatureLists { - if !core.Same(signatures, masterList) { - signature := signatures[0] - debug.AssertIsDefined(signature, "getUnionSignatures bails early on empty signature lists and should not have empty lists on second pass") - if len(signature.typeParameters) != 0 && core.Some(results, func(s *Signature) bool { - return len(s.typeParameters) != 0 && !c.compareTypeParametersIdentical(signature.typeParameters, s.typeParameters) - }) { - results = nil - } else { - results = core.Map(results, func(sig *Signature) *Signature { - return c.combineUnionOrIntersectionMemberSignatures(sig, signature, true /*isUnion*/) - }) - } - if results == nil { - break - } - } - } - result = results - } - return result -} - -func (c *Checker) combineUnionOrIntersectionMemberSignatures(left *Signature, right *Signature, isUnion bool) *Signature { - typeParams := left.typeParameters - if len(typeParams) == 0 { - typeParams = right.typeParameters - } - var paramMapper *TypeMapper - if len(left.typeParameters) != 0 && len(right.typeParameters) != 0 { - // We just use the type parameter defaults from the first signature - paramMapper = newTypeMapper(right.typeParameters, left.typeParameters) - } - flags := (left.flags | right.flags) & (SignatureFlagsPropagatingFlags & ^SignatureFlagsHasRestParameter) - declaration := left.declaration - params := c.combineUnionOrIntersectionParameters(left, right, paramMapper, isUnion) - lastParam := core.LastOrNil(params) - if lastParam != nil && lastParam.CheckFlags&ast.CheckFlagsRestParameter != 0 { - flags |= SignatureFlagsHasRestParameter - } - thisParam := c.combineUnionOrIntersectionThisParam(left.thisParameter, right.thisParameter, paramMapper, isUnion) - minArgCount := int(max(left.minArgumentCount, right.minArgumentCount)) - result := c.newSignature(flags, declaration, typeParams, thisParam, params, nil, nil, minArgCount) - var leftSignatures []*Signature - if left.composite != nil && left.composite.isUnion { - leftSignatures = left.composite.signatures - } else { - leftSignatures = []*Signature{left} - } - result.composite = &CompositeSignature{isUnion: isUnion, signatures: append(leftSignatures, right)} - if paramMapper != nil { - if left.composite != nil && left.composite.isUnion == isUnion && left.mapper != nil { - result.mapper = c.combineTypeMappers(left.mapper, paramMapper) - } else { - result.mapper = paramMapper - } - } else if left.composite != nil && left.composite.isUnion == isUnion { - result.mapper = left.mapper - } - return result -} - -func (c *Checker) combineUnionOrIntersectionParameters(left *Signature, right *Signature, mapper *TypeMapper, isUnion bool) []*ast.Symbol { - leftCount := c.getParameterCount(left) - rightCount := c.getParameterCount(right) - var longestCount int - var longest, shorter *Signature - if leftCount >= rightCount { - longestCount, longest, shorter = leftCount, left, right - } else { - longestCount, longest, shorter = rightCount, right, left - } - eitherHasEffectiveRest := c.hasEffectiveRestParameter(left) || c.hasEffectiveRestParameter(right) - needsExtraRestElement := eitherHasEffectiveRest && !c.hasEffectiveRestParameter(longest) - params := make([]*ast.Symbol, longestCount+core.IfElse(needsExtraRestElement, 1, 0)) - for i := range longestCount { - longestParamType := c.tryGetTypeAtPosition(longest, i) - if longest == right { - longestParamType = c.instantiateType(longestParamType, mapper) - } - shorterParamType := core.OrElse(c.tryGetTypeAtPosition(shorter, i), c.unknownType) - if shorter == right { - shorterParamType = c.instantiateType(shorterParamType, mapper) - } - combinedParamType := c.getUnionOrIntersectionType([]*Type{longestParamType, shorterParamType}, !isUnion, UnionReductionLiteral) - isRestParam := eitherHasEffectiveRest && !needsExtraRestElement && i == (longestCount-1) - isOptional := i >= c.getMinArgumentCount(longest) && i >= c.getMinArgumentCount(shorter) - var leftName, rightName string - if i < leftCount { - leftName = c.getParameterNameAtPosition(left, i) - } - if i < rightCount { - rightName = c.getParameterNameAtPosition(right, i) - } - var paramName string - switch { - case leftName == rightName: - paramName = leftName - case leftName == "": - paramName = rightName - case rightName == "": - paramName = leftName - } - if paramName == "" { - paramName = "arg" + strconv.Itoa(i) - } - paramSymbol := c.newSymbolEx(ast.SymbolFlagsFunctionScopedVariable|core.IfElse(isOptional && !isRestParam, ast.SymbolFlagsOptional, 0), paramName, - core.IfElse(isRestParam, ast.CheckFlagsRestParameter, core.IfElse(isOptional, ast.CheckFlagsOptionalParameter, 0))) - links := c.valueSymbolLinks.Get(paramSymbol) - if isRestParam { - links.resolvedType = c.createArrayType(combinedParamType) - } else { - links.resolvedType = combinedParamType - } - params[i] = paramSymbol - } - if needsExtraRestElement { - restParamSymbol := c.newSymbolEx(ast.SymbolFlagsFunctionScopedVariable, "args", ast.CheckFlagsRestParameter) - links := c.valueSymbolLinks.Get(restParamSymbol) - links.resolvedType = c.createArrayType(c.getTypeAtPosition(shorter, longestCount)) - if shorter == right { - links.resolvedType = c.instantiateType(links.resolvedType, mapper) - } - params[longestCount] = restParamSymbol - } - return params -} - -func (c *Checker) combineUnionOrIntersectionThisParam(left *ast.Symbol, right *ast.Symbol, mapper *TypeMapper, isUnion bool) *ast.Symbol { - if left == nil { - return right - } - if right == nil { - return left - } - // A signature `this` type might be a read or a write position... It's very possible that it should be invariant - // and we should refuse to merge signatures if there are `this` types and they do not match. However, so as to be - // permissive when calling, for now, we'll intersect the `this` types just like we do for param types in union signatures. - thisType := c.getUnionOrIntersectionType([]*Type{c.getTypeOfSymbol(left), c.instantiateType(c.getTypeOfSymbol(right), mapper)}, !isUnion, UnionReductionLiteral) - return c.createSymbolWithType(left, thisType) -} - -func (c *Checker) resolveIntersectionTypeMembers(t *Type) { - // The members and properties collections are empty for intersection types. To get all properties of an - // intersection type use getPropertiesOfType (only the language service uses this). - var callSignatures []*Signature - var constructSignatures []*Signature - var indexInfos []*IndexInfo - types := t.Types() - mixinFlags, mixinCount := c.findMixins(types) - for i, t := range types { - // When an intersection type contains mixin constructor types, the construct signatures from - // those types are discarded and their return types are mixed into the return types of all - // other construct signatures in the intersection type. For example, the intersection type - // '{ new(...args: any[]) => A } & { new(s: string) => B }' has a single construct signature - // 'new(s: string) => A & B'. - if !mixinFlags[i] { - signatures := c.getSignaturesOfType(t, SignatureKindConstruct) - if len(signatures) != 0 && mixinCount > 0 { - signatures = core.Map(signatures, func(s *Signature) *Signature { - clone := c.cloneSignature(s) - clone.resolvedReturnType = c.includeMixinType(c.getReturnTypeOfSignature(s), types, mixinFlags, i) - return clone - }) - } - constructSignatures = c.appendSignatures(constructSignatures, signatures) - } - callSignatures = c.appendSignatures(callSignatures, c.getSignaturesOfType(t, SignatureKindCall)) - for _, info := range c.getIndexInfosOfType(t) { - indexInfos = c.appendIndexInfo(indexInfos, info, false /*union*/) - } - } - c.setStructuredTypeMembers(t, nil, callSignatures, constructSignatures, indexInfos) -} - -func (c *Checker) appendSignatures(signatures []*Signature, newSignatures []*Signature) []*Signature { - for _, sig := range newSignatures { - if len(signatures) == 0 || core.Every(signatures, func(s *Signature) bool { - return c.compareSignaturesIdentical(s, sig, false /*partialMatch*/, false /*ignoreThisTypes*/, false /*ignoreReturnTypes*/, c.compareTypesIdentical) == TernaryFalse - }) { - signatures = append(signatures, sig) - } - } - return signatures -} - -func (c *Checker) appendIndexInfo(indexInfos []*IndexInfo, newInfo *IndexInfo, union bool) []*IndexInfo { - for i, info := range indexInfos { - if info.keyType == newInfo.keyType { - var valueType *Type - var isReadonly bool - if union { - valueType = c.getUnionType([]*Type{info.valueType, newInfo.valueType}) - isReadonly = info.isReadonly || newInfo.isReadonly - } else { - valueType = c.getIntersectionType([]*Type{info.valueType, newInfo.valueType}) - isReadonly = info.isReadonly && newInfo.isReadonly - } - indexInfos[i] = c.newIndexInfo(info.keyType, valueType, isReadonly, nil, nil) - return indexInfos - } - } - return append(indexInfos, newInfo) -} - -func (c *Checker) findMixins(types []*Type) ([]bool, int) { - mixinFlags := core.Map(types, c.isMixinConstructorType) - var constructorTypeCount, mixinCount int - firstMixinIndex := -1 - for i, t := range types { - if len(c.getSignaturesOfType(t, SignatureKindConstruct)) > 0 { - constructorTypeCount++ - } - if mixinFlags[i] { - if firstMixinIndex < 0 { - firstMixinIndex = i - } - mixinCount++ - } - } - if constructorTypeCount > 0 && constructorTypeCount == mixinCount { - mixinFlags[firstMixinIndex] = false - mixinCount-- - } - return mixinFlags, mixinCount -} - -func (c *Checker) includeMixinType(t *Type, types []*Type, mixinFlags []bool, index int) *Type { - var mixedTypes []*Type - for i := range types { - if i == index { - mixedTypes = append(mixedTypes, t) - } else if mixinFlags[i] { - mixedTypes = append(mixedTypes, c.getReturnTypeOfSignature(c.getSignaturesOfType(types[i], SignatureKindConstruct)[0])) - } - } - return c.getIntersectionType(mixedTypes) -} - -/** - * 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) *ast.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) *ast.Symbol { - prop := c.getUnionOrIntersectionProperty(t, name, skipObjectFunctionPropertyAugment) - // We need to filter out partial properties in union types - if prop != nil && prop.CheckFlags&ast.CheckFlagsReadPartial != 0 { - return nil - } - return prop -} - -// Return the symbol for a given property in a union or intersection type, or undefined if the property -// does not exist in any constituent type. Note that the returned property may only be present in some -// constituents, in which case the isPartial flag is set when the containing type is union type. We need -// these partial properties when identifying discriminant properties, but otherwise they are filtered out -// and do not appear to be present in the union type. -func (c *Checker) getUnionOrIntersectionProperty(t *Type, name string, skipObjectFunctionPropertyAugment bool) *ast.Symbol { - var cache ast.SymbolTable - if skipObjectFunctionPropertyAugment { - cache = ast.GetSymbolTable(&t.AsUnionOrIntersectionType().propertyCacheWithoutFunctionPropertyAugment) - } else { - cache = ast.GetSymbolTable(&t.AsUnionOrIntersectionType().propertyCache) - } - if prop := cache[name]; prop != nil { - return prop - } - prop := c.createUnionOrIntersectionProperty(t, name, skipObjectFunctionPropertyAugment) - if prop != nil { - cache[name] = prop - // Propagate an entry from the non-augmented cache to the augmented cache unless the property is partial. - if skipObjectFunctionPropertyAugment && prop.CheckFlags&ast.CheckFlagsPartial == 0 { - augmentedCache := ast.GetSymbolTable(&t.AsUnionOrIntersectionType().propertyCache) - if augmentedCache[name] == nil { - augmentedCache[name] = prop - } - } - } - return prop -} - -func (c *Checker) createUnionOrIntersectionProperty(containingType *Type, name string, skipObjectFunctionPropertyAugment bool) *ast.Symbol { - propFlags := ast.SymbolFlagsNone - var singleProp *ast.Symbol - var propSet collections.OrderedSet[*ast.Symbol] - var indexTypes []*Type - isUnion := containingType.flags&TypeFlagsUnion != 0 - // Flags we want to propagate to the result if they exist in all source symbols - var checkFlags ast.CheckFlags - var optionalFlag ast.SymbolFlags - if !isUnion { - checkFlags = ast.CheckFlagsReadonly - optionalFlag = ast.SymbolFlagsOptional - } - syntheticFlag := ast.CheckFlagsSyntheticMethod - mergedInstantiations := false - for _, current := range containingType.Types() { - t := c.getApparentType(current) - if !c.isErrorType(t) && t.flags&TypeFlagsNever == 0 { - prop := c.getPropertyOfTypeEx(t, name, skipObjectFunctionPropertyAugment, false) - var modifiers ast.ModifierFlags - if prop != nil { - modifiers = getDeclarationModifierFlagsFromSymbol(prop) - if prop.Flags&ast.SymbolFlagsClassMember != 0 { - if isUnion { - optionalFlag |= prop.Flags & ast.SymbolFlagsOptional - } else { - optionalFlag &= prop.Flags - } - } - if singleProp == nil { - singleProp = prop - propFlags = core.OrElse(prop.Flags&ast.SymbolFlagsAccessor, ast.SymbolFlagsProperty) - } else if prop != singleProp { - isInstantiation := c.getTargetSymbol(prop) == c.getTargetSymbol(singleProp) - // If the symbols are instances of one another with identical types - consider the symbols - // equivalent and just use the first one, which thus allows us to avoid eliding private - // members when intersecting a (this-)instantiations of a class with its raw base or another instance - if isInstantiation && c.compareProperties(singleProp, prop, compareTypesEqual) == TernaryTrue { - // If we merged instantiations of a generic type, we replicate the symbol parent resetting behavior we used - // to do when we recorded multiple distinct symbols so that we still get, eg, `Array.length` printed - // back and not `Array.length` when we're looking at a `.length` access on a `string[] | number[]` - mergedInstantiations = singleProp.Parent != nil && len(c.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(singleProp.Parent)) != 0 - } else { - if propSet.Size() == 0 { - propSet.Add(singleProp) - } - propSet.Add(prop) - } - // classes created by mixins are represented as intersections - // and overriding a property in a derived class redefines it completely at runtime - // so a get accessor can't be merged with a set accessor in a base class, - // for that reason the accessor flags are only used when they are the same in all constituents - if propFlags&ast.SymbolFlagsAccessor != 0 && (prop.Flags&ast.SymbolFlagsAccessor != (propFlags & ast.SymbolFlagsAccessor)) { - propFlags = (propFlags &^ ast.SymbolFlagsAccessor) | ast.SymbolFlagsProperty - } - } - if isUnion && c.isReadonlySymbol(prop) { - checkFlags |= ast.CheckFlagsReadonly - } else if !isUnion && !c.isReadonlySymbol(prop) { - checkFlags &^= ast.CheckFlagsReadonly - } - if modifiers&ast.ModifierFlagsNonPublicAccessibilityModifier == 0 { - checkFlags |= ast.CheckFlagsContainsPublic - } - if modifiers&ast.ModifierFlagsProtected != 0 { - checkFlags |= ast.CheckFlagsContainsProtected - } - if modifiers&ast.ModifierFlagsPrivate != 0 { - checkFlags |= ast.CheckFlagsContainsPrivate - } - if modifiers&ast.ModifierFlagsStatic != 0 { - checkFlags |= ast.CheckFlagsContainsStatic - } - if !isPrototypeProperty(prop) { - syntheticFlag = ast.CheckFlagsSyntheticProperty - } - } else if isUnion { - var indexInfo *IndexInfo - if !isLateBoundName(name) { - indexInfo = c.getApplicableIndexInfoForName(t, name) - } - if indexInfo != nil { - propFlags = propFlags&^ast.SymbolFlagsAccessor | ast.SymbolFlagsProperty - checkFlags |= ast.CheckFlagsWritePartial | (core.IfElse(indexInfo.isReadonly, ast.CheckFlagsReadonly, 0)) - if isTupleType(t) { - indexType := c.getRestTypeOfTupleType(t) - if indexType == nil { - indexType = c.undefinedType - } - indexTypes = append(indexTypes, indexType) - } else { - indexTypes = append(indexTypes, indexInfo.valueType) - } - } else if isObjectLiteralType(t) && t.objectFlags&ObjectFlagsContainsSpread == 0 { - checkFlags |= ast.CheckFlagsWritePartial - indexTypes = append(indexTypes, c.undefinedType) - } else { - checkFlags |= ast.CheckFlagsReadPartial - } - } - } - } - if singleProp == nil || isUnion && - (propSet.Size() != 0 || checkFlags&ast.CheckFlagsPartial != 0) && - checkFlags&(ast.CheckFlagsContainsPrivate|ast.CheckFlagsContainsProtected) != 0 && - !(propSet.Size() != 0 && c.hasCommonDeclaration(&propSet)) { - // No property was found, or, in a union, a property has a private or protected declaration in one - // constituent, but is missing or has a different declaration in another constituent. - return nil - } - if propSet.Size() == 0 && checkFlags&ast.CheckFlagsReadPartial == 0 && len(indexTypes) == 0 { - if !mergedInstantiations { - return singleProp - } - // No symbol from a union/intersection should have a `.parent` set (since unions/intersections don't act as symbol parents) - // Unless that parent is "reconstituted" from the "first value declaration" on the symbol (which is likely different than its instantiated parent!) - // They also have a `.containingType` set, which affects some services endpoints behavior, like `getRootSymbol` - var singlePropType *Type - var singlePropMapper *TypeMapper - if singleProp.Flags&ast.SymbolFlagsTransient != 0 { - links := c.valueSymbolLinks.Get(singleProp) - singlePropType = links.resolvedType - singlePropMapper = links.mapper - } - clone := c.createSymbolWithType(singleProp, singlePropType) - if singleProp.ValueDeclaration != nil { - clone.Parent = singleProp.ValueDeclaration.Symbol().Parent - } - links := c.valueSymbolLinks.Get(clone) - links.containingType = containingType - links.mapper = singlePropMapper - links.writeType = c.getWriteTypeOfSymbol(singleProp) - return clone - } - if propSet.Size() == 0 { - propSet.Add(singleProp) - } - var declarations []*ast.Node - var firstType *Type - var nameType *Type - var propTypes []*Type - var writeTypes []*Type - var firstValueDeclaration *ast.Node - var hasNonUniformValueDeclaration bool - for prop := range propSet.Values() { - if firstValueDeclaration == nil { - firstValueDeclaration = prop.ValueDeclaration - } else if prop.ValueDeclaration != nil && prop.ValueDeclaration != firstValueDeclaration { - hasNonUniformValueDeclaration = true - } - declarations = append(declarations, prop.Declarations...) - t := c.getTypeOfSymbol(prop) - if firstType == nil { - firstType = t - nameType = c.valueSymbolLinks.Get(prop).nameType - } - writeType := c.getWriteTypeOfSymbol(prop) - if writeTypes != nil || writeType != t { - if writeTypes == nil { - writeTypes = slices.Clone(propTypes) - } - writeTypes = append(writeTypes, writeType) - } - if t != firstType { - checkFlags |= ast.CheckFlagsHasNonUniformType - } - if isLiteralType(t) || c.isPatternLiteralType(t) { - checkFlags |= ast.CheckFlagsHasLiteralType - } - if t.flags&TypeFlagsNever != 0 && t != c.uniqueLiteralType { - checkFlags |= ast.CheckFlagsHasNeverType - } - propTypes = append(propTypes, t) - } - propTypes = append(propTypes, indexTypes...) - result := c.newSymbolEx(propFlags|optionalFlag, name, checkFlags|syntheticFlag) - result.Declarations = declarations - if !hasNonUniformValueDeclaration && firstValueDeclaration != nil { - result.ValueDeclaration = firstValueDeclaration - // Inherit information about parent type. - result.Parent = firstValueDeclaration.Symbol().Parent - } - links := c.valueSymbolLinks.Get(result) - links.containingType = containingType - links.nameType = nameType - if len(propTypes) > 2 { - // When `propTypes` has the potential to explode in size when normalized, defer normalization until absolutely needed - result.CheckFlags |= ast.CheckFlagsDeferredType - deferred := c.deferredSymbolLinks.Get(result) - deferred.parent = containingType - deferred.constituents = propTypes - deferred.writeConstituents = writeTypes - return result - } - if isUnion { - links.resolvedType = c.getUnionType(propTypes) - } else { - links.resolvedType = c.getIntersectionType(propTypes) - } - if writeTypes != nil { - if isUnion { - links.writeType = c.getUnionType(writeTypes) - } else { - links.writeType = c.getIntersectionType(writeTypes) - } - } - return result -} - -func (c *Checker) getTargetSymbol(s *ast.Symbol) *ast.Symbol { - // if symbol is instantiated its flags are not copied from the 'target' - // so we'll need to get back original 'target' symbol to work with correct set of flags - // NOTE: cast to TransientSymbol should be safe because only TransientSymbols have CheckFlags.Instantiated - if s.CheckFlags&ast.CheckFlagsInstantiated != 0 { - return c.valueSymbolLinks.Get(s).target - } - return s -} - -/** - * Return whether this symbol is a member of a prototype somewhere - * Note that this is not tracked well within the compiler, so the answer may be incorrect. - */ -func isPrototypeProperty(symbol *ast.Symbol) bool { - return symbol.Flags&ast.SymbolFlagsMethod != 0 || symbol.CheckFlags&ast.CheckFlagsSyntheticMethod != 0 -} - -func (c *Checker) hasCommonDeclaration(symbols *collections.OrderedSet[*ast.Symbol]) bool { - var commonDeclarations collections.Set[*ast.Node] - for symbol := range symbols.Values() { - if len(symbol.Declarations) == 0 { - return false - } - if commonDeclarations.Len() == 0 { - for _, d := range symbol.Declarations { - commonDeclarations.Add(d) - } - continue - } - for d := range commonDeclarations.Keys() { - if !slices.Contains(symbol.Declarations, d) { - commonDeclarations.Delete(d) - } - } - if commonDeclarations.Len() == 0 { - return false - } - } - return commonDeclarations.Len() != 0 -} - -func (c *Checker) createSymbolWithType(source *ast.Symbol, t *Type) *ast.Symbol { - symbol := c.newSymbolEx(source.Flags, source.Name, source.CheckFlags&ast.CheckFlagsReadonly) - symbol.Declarations = source.Declarations - symbol.Parent = source.Parent - symbol.ValueDeclaration = source.ValueDeclaration - links := c.valueSymbolLinks.Get(symbol) - links.resolvedType = t - links.target = source - links.nameType = c.valueSymbolLinks.Get(source).nameType - return symbol -} - -func (c *Checker) isMappedTypeGenericIndexedAccess(t *Type) bool { - if t.flags&TypeFlagsIndexedAccess != 0 { - objectType := t.AsIndexedAccessType().objectType - return objectType.objectFlags&ObjectFlagsMapped != 0 && !c.isGenericMappedType(objectType) && c.isGenericIndexType(t.AsIndexedAccessType().indexType) && - getMappedTypeModifiers(objectType)&MappedTypeModifiersExcludeOptional == 0 && objectType.AsMappedType().declaration.NameType == nil - } - return false -} - -/** - * 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(t *Type) *Type { - originalType := t - if t.flags&TypeFlagsInstantiable != 0 { - t = c.getBaseConstraintOfType(t) - if t == nil { - t = c.unknownType - } - } - switch { - case t.objectFlags&ObjectFlagsMapped != 0: - return c.getApparentTypeOfMappedType(t) - case t.objectFlags&ObjectFlagsReference != 0 && t != originalType: - return c.getTypeWithThisArgument(t, originalType, false /*needsApparentType*/) - case t.flags&TypeFlagsIntersection != 0: - return c.getApparentTypeOfIntersectionType(t, originalType) - case t.flags&TypeFlagsStringLike != 0: - return c.globalStringType - case t.flags&TypeFlagsNumberLike != 0: - return c.globalNumberType - case t.flags&TypeFlagsBigIntLike != 0: - return c.getGlobalBigIntType() - case t.flags&TypeFlagsBooleanLike != 0: - return c.globalBooleanType - case t.flags&TypeFlagsESSymbolLike != 0: - return c.getGlobalESSymbolType() - case t.flags&TypeFlagsNonPrimitive != 0: - return c.emptyObjectType - case t.flags&TypeFlagsIndex != 0: - return c.stringNumberSymbolType - case t.flags&TypeFlagsUnknown != 0 && !c.strictNullChecks: - return c.emptyObjectType - } - return t -} - -func (c *Checker) getApparentTypeOfMappedType(t *Type) *Type { - m := t.AsMappedType() - if m.resolvedApparentType == nil { - m.resolvedApparentType = c.getResolvedApparentTypeOfMappedType(t) - } - return m.resolvedApparentType -} - -func (c *Checker) getResolvedApparentTypeOfMappedType(t *Type) *Type { - target := core.OrElse(t.AsMappedType().target, t) - typeVariable := c.getHomomorphicTypeVariable(target) - if typeVariable != nil && target.AsMappedType().declaration.NameType == nil { - // We have a homomorphic mapped type or an instantiation of a homomorphic mapped type, i.e. a type - // of the form { [P in keyof T]: X }. Obtain the modifiers type (the T of the keyof T), and if it is - // another generic mapped type, recursively obtain its apparent type. Otherwise, obtain its base - // constraint. Then, if every constituent of the base constraint is an array or tuple type, apply - // this mapped type to the base constraint. It is safe to recurse when the modifiers type is a - // mapped type because we protect again circular constraints in getTypeFromMappedTypeNode. - modifiersType := c.getModifiersTypeFromMappedType(t) - var baseConstraint *Type - if c.isGenericMappedType(modifiersType) { - baseConstraint = c.getApparentTypeOfMappedType(modifiersType) - } else { - baseConstraint = c.getBaseConstraintOfType(modifiersType) - } - if baseConstraint != nil && everyType(baseConstraint, func(t *Type) bool { return c.isArrayOrTupleType(t) || c.isArrayOrTupleOrIntersection(t) }) { - return c.instantiateType(target, prependTypeMapping(typeVariable, baseConstraint, t.AsMappedType().mapper)) - } - } - return t -} - -func (c *Checker) getApparentTypeOfIntersectionType(t *Type, thisArgument *Type) *Type { - if t == thisArgument { - d := t.AsIntersectionType() - if d.resolvedApparentType == nil { - d.resolvedApparentType = c.getTypeWithThisArgument(t, thisArgument, true /*needApparentType*/) - } - return d.resolvedApparentType - } - key := CachedTypeKey{kind: CachedTypeKindApparentType, typeId: thisArgument.id} - result := c.cachedTypes[key] - if result == nil { - result = c.getTypeWithThisArgument(t, thisArgument, true /*needApparentType*/) - c.cachedTypes[key] = result - } - return result -} - -/** - * Return the reduced form of the given type. For a union type, it is a union of the normalized constituent types. - * For an intersection of types containing one or more mututally exclusive discriminant properties, it is 'never'. - * For all other types, it is simply the type itself. Discriminant properties are considered mutually exclusive when - * no constituent property has type 'never', but the intersection of the constituent property types is 'never'. - */ -func (c *Checker) getReducedType(t *Type) *Type { - switch { - case t.flags&TypeFlagsUnion != 0: - if t.objectFlags&ObjectFlagsContainsIntersections != 0 { - if reducedType := t.AsUnionType().resolvedReducedType; reducedType != nil { - return reducedType - } - reducedType := c.getReducedUnionType(t) - t.AsUnionType().resolvedReducedType = reducedType - return reducedType - } - case t.flags&TypeFlagsIntersection != 0: - if t.objectFlags&ObjectFlagsIsNeverIntersectionComputed == 0 { - t.objectFlags |= ObjectFlagsIsNeverIntersectionComputed - if core.Some(c.getPropertiesOfUnionOrIntersectionType(t), c.isNeverReducedProperty) { - t.objectFlags |= ObjectFlagsIsNeverIntersection - } - } - if t.objectFlags&ObjectFlagsIsNeverIntersection != 0 { - return c.neverType - } - } - return t -} - -func (c *Checker) getReducedUnionType(unionType *Type) *Type { - reducedTypes := core.SameMap(unionType.Types(), c.getReducedType) - if core.Same(reducedTypes, unionType.Types()) { - return unionType - } - reduced := c.getUnionType(reducedTypes) - if reduced.flags&TypeFlagsUnion != 0 { - reduced.AsUnionType().resolvedReducedType = reduced - } - return reduced -} - -func (c *Checker) isNeverReducedProperty(prop *ast.Symbol) bool { - return c.isDiscriminantWithNeverType(prop) || isConflictingPrivateProperty(prop) -} - -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) elaborateNeverIntersection(chain *ast.Diagnostic, node *ast.Node, t *Type) *ast.Diagnostic { - if t.flags&TypeFlagsIntersection != 0 && t.objectFlags&ObjectFlagsIsNeverIntersection != 0 { - neverProp := core.Find(c.getPropertiesOfUnionOrIntersectionType(t), c.isDiscriminantWithNeverType) - if neverProp != nil { - return NewDiagnosticChainForNode(chain, node, diagnostics.The_intersection_0_was_reduced_to_never_because_property_1_has_conflicting_types_in_some_constituents, c.typeToStringEx(t, nil, TypeFormatFlagsNoTypeReduction), c.symbolToString(neverProp)) - } - privateProp := core.Find(c.getPropertiesOfUnionOrIntersectionType(t), isConflictingPrivateProperty) - if privateProp != nil { - return NewDiagnosticChainForNode(chain, node, diagnostics.The_intersection_0_was_reduced_to_never_because_property_1_exists_in_multiple_constituents_and_is_private_in_some, c.typeToStringEx(t, nil, TypeFormatFlagsNoTypeReduction), c.symbolToString(privateProp)) - } - } - return chain -} - -func (c *Checker) isDiscriminantWithNeverType(prop *ast.Symbol) bool { - // Return true for a synthetic non-optional property with non-uniform types, where at least one is - // a literal type and none is never, that reduces to never. - return prop.Flags&ast.SymbolFlagsOptional == 0 && prop.CheckFlags&(ast.CheckFlagsNonUniformAndLiteral|ast.CheckFlagsHasNeverType) == ast.CheckFlagsNonUniformAndLiteral && c.getTypeOfSymbol(prop).flags&TypeFlagsNever != 0 -} - -func isConflictingPrivateProperty(prop *ast.Symbol) bool { - // Return true for a synthetic property with multiple declarations, at least one of which is private. - return prop.ValueDeclaration == nil && prop.CheckFlags&ast.CheckFlagsContainsPrivate != 0 -} - -type allAccessorDeclarations struct { - firstAccessor *ast.AccessorDeclaration - secondAccessor *ast.AccessorDeclaration - setAccessor *ast.SetAccessorDeclaration - getAccessor *ast.GetAccessorDeclaration -} - -func (c *Checker) getAllAccessorDeclarationsForDeclaration(accessor *ast.AccessorDeclaration) allAccessorDeclarations { - var otherKind ast.Kind - if accessor.Kind == ast.KindSetAccessor { - otherKind = ast.KindGetAccessor - } else if accessor.Kind == ast.KindGetAccessor { - otherKind = ast.KindSetAccessor - } else { - panic(fmt.Sprintf("Unexpected node kind %q", accessor.Kind)) - } - otherAccessor := ast.GetDeclarationOfKind(c.getSymbolOfDeclaration(accessor), otherKind) - - var firstAccessor *ast.AccessorDeclaration - var secondAccessor *ast.AccessorDeclaration - if otherAccessor != nil && (otherAccessor.Pos() < accessor.Pos()) { - firstAccessor = otherAccessor - secondAccessor = accessor - } else { - firstAccessor = accessor - secondAccessor = otherAccessor - } - - var setAccessor *ast.SetAccessorDeclaration - var getAccessor *ast.GetAccessorDeclaration - if accessor.Kind == ast.KindSetAccessor { - setAccessor = accessor.AsSetAccessorDeclaration() - if otherAccessor != nil { - getAccessor = otherAccessor.AsGetAccessorDeclaration() - } - } else { - getAccessor = accessor.AsGetAccessorDeclaration() - if otherAccessor != nil { - setAccessor = otherAccessor.AsSetAccessorDeclaration() - } - } - - return allAccessorDeclarations{ - firstAccessor: firstAccessor, - secondAccessor: secondAccessor, - setAccessor: setAccessor, - getAccessor: getAccessor, - } -} - -func (c *Checker) getTypeArguments(t *Type) []*Type { - d := t.AsTypeReference() - if d.resolvedTypeArguments == nil { - n := d.target.AsInterfaceType() - if !c.pushTypeResolution(t, TypeSystemPropertyNameResolvedTypeArguments) { - return slices.Repeat([]*Type{c.errorType}, len(n.TypeParameters())) - } - var typeArguments []*Type - node := t.AsTypeReference().node - if node != nil { - switch node.Kind { - case ast.KindTypeReference: - typeArguments = append(n.OuterTypeParameters(), c.getEffectiveTypeArguments(node, n.LocalTypeParameters())...) - case ast.KindArrayType: - typeArguments = []*Type{c.getTypeFromTypeNode(node.AsArrayTypeNode().ElementType)} - case ast.KindTupleType: - typeArguments = core.Map(node.AsTupleTypeNode().Elements.Nodes, 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(n.TypeParameters())) - } - errorNode := core.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 *ast.Node, typeParameters []*Type) []*Type { - return c.fillMissingTypeArguments(core.Map(node.TypeArguments(), c.getTypeFromTypeNode), typeParameters, c.getMinTypeArgumentCount(typeParameters), ast.IsInJSFile(node)) -} - -// 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 && core.Some(t.symbol.Declarations, func(d *ast.Node) bool { - return ast.IsTypeParameterDeclaration(d) && d.AsTypeParameter().DefaultType != nil - }) -} - -func (c *Checker) fillMissingTypeArguments(typeArguments []*Type, typeParameters []*Type, minTypeArgumentCount int, isJavaScriptImplicitAny bool) []*Type { - numTypeParameters := len(typeParameters) - if numTypeParameters == 0 { - return nil - } - numTypeArguments := len(typeArguments) - if isJavaScriptImplicitAny || (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 - } - baseDefaultType := c.getDefaultTypeArgumentType(isJavaScriptImplicitAny) - for i := numTypeArguments; i < numTypeParameters; i++ { - defaultType := c.getDefaultFromTypeParameter(typeParameters[i]) - - if isJavaScriptImplicitAny && defaultType != nil && (c.isTypeIdenticalTo(defaultType, c.unknownType) || c.isTypeIdenticalTo(defaultType, c.emptyObjectType)) { - defaultType = c.anyType - } - - if defaultType != nil { - result[i] = c.instantiateType(defaultType, newTypeMapper(typeParameters, result)) - } else { - result[i] = baseDefaultType - } - } - return result - } - return typeArguments -} - -func (c *Checker) getDefaultTypeArgumentType(isInJavaScriptFile bool) *Type { - if isInJavaScriptFile { - return c.anyType - } - return c.unknownType -} - -// Gets the default type for a type parameter. If the type parameter is the result of an instantiation, -// this gets the instantiated default type of its target. If the type parameter has no default type or -// the default is circular, `undefined` is returned. -func (c *Checker) getDefaultFromTypeParameter(t *Type) *Type { - if t.flags&TypeFlagsTypeParameter == 0 { - return nil - } - defaultType := c.getResolvedTypeParameterDefault(t) - if defaultType != c.noConstraintType && defaultType != c.circularConstraintType { - return defaultType - } - return nil -} - -func (c *Checker) getResolvedTypeParameterDefault(t *Type) *Type { - d := t.AsTypeParameter() - if d.resolvedDefaultType == nil { - if d.target != nil { - targetDefault := c.getResolvedTypeParameterDefault(d.target) - if targetDefault != nil { - d.resolvedDefaultType = c.instantiateType(targetDefault, d.mapper) - } else { - d.resolvedDefaultType = c.noConstraintType - } - } else { - // To block recursion, set the initial value to the resolvingDefaultType. - d.resolvedDefaultType = c.resolvingDefaultType - defaultType := c.noConstraintType - if t.symbol != nil { - defaultDeclaration := core.FirstNonNil(t.symbol.Declarations, func(decl *ast.Node) *ast.Node { - if ast.IsTypeParameterDeclaration(decl) { - return decl.AsTypeParameter().DefaultType - } - return nil - }) - if defaultDeclaration != nil { - defaultType = c.getTypeFromTypeNode(defaultDeclaration) - } - } - if d.resolvedDefaultType == c.resolvingDefaultType { - // If we have not been called recursively, set the correct default type. - d.resolvedDefaultType = defaultType - } - } - } else if d.resolvedDefaultType == c.resolvingDefaultType { - // If we are called recursively for this type parameter, mark the default as circular. - d.resolvedDefaultType = c.circularConstraintType - } - return d.resolvedDefaultType -} - -func (c *Checker) getDefaultOrUnknownFromTypeParameter(t *Type) *Type { - result := c.getDefaultFromTypeParameter(t) - return core.IfElse(result != nil, result, c.unknownType) -} - -func (c *Checker) getNamedMembers(members ast.SymbolTable) []*ast.Symbol { - if len(members) == 0 { - return nil - } - result := make([]*ast.Symbol, 0, len(members)) - for id, symbol := range members { - if c.isNamedMember(symbol, id) { - result = append(result, symbol) - } - } - c.sortSymbols(result) - return result -} - -func (c *Checker) isNamedMember(symbol *ast.Symbol, id string) bool { - return !isReservedMemberName(id) && c.symbolIsValue(symbol) -} - -func (c *Checker) symbolIsValue(symbol *ast.Symbol) bool { - return c.symbolIsValueEx(symbol, false /*includeTypeOnlyMembers*/) -} - -func (c *Checker) symbolIsValueEx(symbol *ast.Symbol, includeTypeOnlyMembers bool) bool { - return symbol.Flags&ast.SymbolFlagsValue != 0 || symbol.Flags&ast.SymbolFlagsAlias != 0 && - c.getSymbolFlagsEx(symbol, !includeTypeOnlyMembers, false /*excludeLocalMeanings*/)&ast.SymbolFlagsValue != 0 -} - -func (c *Checker) instantiateType(t *Type, m *TypeMapper) *Type { - return c.instantiateTypeWithAlias(t, m, nil /*alias*/) -} - -func (c *Checker) instantiateTypeWithAlias(t *Type, m *TypeMapper, alias *TypeAlias) *Type { - // Check for type variables in the alias, so things like `type Brand = number & {}` can potentially be copied with new alias type args, despite them being unreferenced. - // This is the behavior most people using aliases expect, and prevents the cache from leaking type parameters outside their scope of validity. - // tests/cases/compiler/declarationEmitArrowFunctionNoRenaming.ts contains an example of this, which previously only worked in strada via some input node reuse logic instead. - if t == nil || m == nil || !(c.couldContainTypeVariables(t) || (t.alias != nil && len(t.alias.typeArguments) > 0 && core.Some(t.alias.typeArguments, c.couldContainTypeVariables))) { - return t - } - if c.instantiationDepth == 100 || c.instantiationCount >= 5_000_000 { - // We have reached 100 recursive type instantiations, or 5M type instantiations caused by the same statement - // or expression. There is a very high likelihood we're dealing with a combination of infinite generic types - // that perpetually generate new type identities, so we stop the recursion here by yielding the error type. - c.error(c.currentNode, diagnostics.Type_instantiation_is_excessively_deep_and_possibly_infinite) - return c.errorType - } - index := c.findActiveMapper(m) - if index == -1 { - c.pushActiveMapper(m) - } - var b KeyBuilder - b.WriteType(t) - b.WriteAlias(alias) - key := b.String() - cache := c.activeTypeMappersCaches[core.IfElse(index != -1, index, len(c.activeTypeMappersCaches)-1)] - if cachedType, ok := cache[key]; ok { - return cachedType - } - c.TotalInstantiationCount++ - c.instantiationCount++ - c.instantiationDepth++ - result := c.instantiateTypeWorker(t, m, alias) - if index == -1 { - c.popActiveMapper() - } else { - cache[key] = result - } - c.instantiationDepth-- - return result -} - -func (c *Checker) pushActiveMapper(mapper *TypeMapper) { - c.activeMappers = append(c.activeMappers, mapper) - - lastIndex := len(c.activeTypeMappersCaches) - if cap(c.activeTypeMappersCaches) > lastIndex { - // The cap may contain an empty map from popActiveMapper; reuse it. - c.activeTypeMappersCaches = c.activeTypeMappersCaches[:lastIndex+1] - if c.activeTypeMappersCaches[lastIndex] == nil { - c.activeTypeMappersCaches[lastIndex] = make(map[string]*Type, 1) - } - } else { - c.activeTypeMappersCaches = append(c.activeTypeMappersCaches, make(map[string]*Type, 1)) - } -} - -func (c *Checker) popActiveMapper() { - c.activeMappers[len(c.activeMappers)-1] = nil - c.activeMappers = c.activeMappers[:len(c.activeMappers)-1] - - // Clear the map, but leave it in the list for later reuse. - lastIndex := len(c.activeTypeMappersCaches) - 1 - clear(c.activeTypeMappersCaches[lastIndex]) - c.activeTypeMappersCaches = c.activeTypeMappersCaches[:lastIndex] -} - -func (c *Checker) findActiveMapper(mapper *TypeMapper) int { - return core.FindLastIndex(c.activeMappers, func(m *TypeMapper) bool { return m == mapper }) -} - -func (c *Checker) clearActiveMapperCaches() { - for _, cache := range c.activeTypeMappersCaches { - clear(cache) - } -} - -// Return true if the given type could possibly reference a type parameter for which -// we perform type inference (i.e. a type parameter of a generic function). We cache -// results for union and intersection types for performance reasons. -func (c *Checker) couldContainTypeVariablesWorker(t *Type) bool { - if t.flags&TypeFlagsStructuredOrInstantiable == 0 { - return false - } - objectFlags := t.objectFlags - if objectFlags&ObjectFlagsCouldContainTypeVariablesComputed != 0 { - return objectFlags&ObjectFlagsCouldContainTypeVariables != 0 - } - result := t.flags&TypeFlagsInstantiable != 0 || - t.flags&TypeFlagsObject != 0 && !c.isNonGenericTopLevelType(t) && (objectFlags&ObjectFlagsReference != 0 && (t.AsTypeReference().node != nil || core.Some(c.getTypeArguments(t), c.couldContainTypeVariables)) || - objectFlags&ObjectFlagsAnonymous != 0 && t.symbol != nil && t.symbol.Flags&(ast.SymbolFlagsFunction|ast.SymbolFlagsMethod|ast.SymbolFlagsClass|ast.SymbolFlagsTypeLiteral|ast.SymbolFlagsObjectLiteral) != 0 && t.symbol.Declarations != nil || - objectFlags&(ObjectFlagsMapped|ObjectFlagsReverseMapped|ObjectFlagsObjectRestType|ObjectFlagsInstantiationExpressionType) != 0) || - t.flags&TypeFlagsUnionOrIntersection != 0 && t.flags&TypeFlagsEnumLiteral == 0 && !c.isNonGenericTopLevelType(t) && core.Some(t.Types(), c.couldContainTypeVariables) - t.objectFlags |= ObjectFlagsCouldContainTypeVariablesComputed | core.IfElse(result, ObjectFlagsCouldContainTypeVariables, 0) - return result -} - -func (c *Checker) isNonGenericTopLevelType(t *Type) bool { - if t.alias != nil && len(t.alias.typeArguments) == 0 { - declaration := ast.GetDeclarationOfKind(t.alias.symbol, ast.KindTypeAliasDeclaration) - if declaration == nil { - declaration = ast.GetDeclarationOfKind(t.alias.symbol, ast.KindJSTypeAliasDeclaration) - } - return declaration != nil && ast.FindAncestorOrQuit(declaration.Parent, func(n *ast.Node) ast.FindAncestorResult { - switch n.Kind { - case ast.KindSourceFile: - return ast.FindAncestorTrue - case ast.KindModuleDeclaration: - return ast.FindAncestorFalse - } - return ast.FindAncestorQuit - }) != nil - } - return false -} - -func (c *Checker) instantiateTypeWorker(t *Type, m *TypeMapper, alias *TypeAlias) *Type { - flags := t.flags - switch { - case flags&TypeFlagsTypeParameter != 0: - return m.Map(t) - case flags&TypeFlagsObject != 0: - objectFlags := t.objectFlags - if objectFlags&(ObjectFlagsReference|ObjectFlagsAnonymous|ObjectFlagsMapped) != 0 { - if objectFlags&ObjectFlagsReference != 0 && t.AsTypeReference().node == nil { - resolvedTypeArguments := t.AsTypeReference().resolvedTypeArguments - newTypeArguments := c.instantiateTypes(resolvedTypeArguments, m) - if core.Same(newTypeArguments, resolvedTypeArguments) { - return t - } - return c.createNormalizedTypeReference(t.Target(), newTypeArguments) - } - if objectFlags&ObjectFlagsReverseMapped != 0 { - return c.instantiateReverseMappedType(t, m) - } - return c.getObjectTypeInstantiation(t, m, alias) - } - return t - case flags&TypeFlagsUnionOrIntersection != 0: - source := t - if t.flags&TypeFlagsUnion != 0 { - origin := t.AsUnionType().origin - if origin != nil && origin.flags&TypeFlagsUnionOrIntersection != 0 { - source = origin - } - } - types := source.Types() - newTypes := c.instantiateTypes(types, m) - if core.Same(newTypes, types) && alias.Symbol() == t.alias.Symbol() { - return t - } - if alias == nil { - alias = c.instantiateTypeAlias(t.alias, m) - } - if source.flags&TypeFlagsIntersection != 0 { - return c.getIntersectionTypeEx(newTypes, IntersectionFlagsNone, alias) - } - return c.getUnionTypeEx(newTypes, UnionReductionLiteral, alias, nil /*origin*/) - case flags&TypeFlagsIndex != 0: - return c.getIndexType(c.instantiateType(t.Target(), m)) - case flags&TypeFlagsIndexedAccess != 0: - if alias == nil { - alias = c.instantiateTypeAlias(t.alias, m) - } - d := t.AsIndexedAccessType() - return c.getIndexedAccessTypeEx(c.instantiateType(d.objectType, m), c.instantiateType(d.indexType, m), d.accessFlags, nil /*accessNode*/, alias) - case flags&TypeFlagsTemplateLiteral != 0: - return c.getTemplateLiteralType(t.AsTemplateLiteralType().texts, c.instantiateTypes(t.AsTemplateLiteralType().types, m)) - case flags&TypeFlagsStringMapping != 0: - return c.getStringMappingType(t.symbol, c.instantiateType(t.AsStringMappingType().target, m)) - case flags&TypeFlagsConditional != 0: - return c.getConditionalTypeInstantiation(t, c.combineTypeMappers(t.AsConditionalType().mapper, m), false /*forConstraint*/, alias) - case flags&TypeFlagsSubstitution != 0: - newBaseType := c.instantiateType(t.AsSubstitutionType().baseType, m) - if c.isNoInferType(t) { - return c.getNoInferType(newBaseType) - } - newConstraint := c.instantiateType(t.AsSubstitutionType().constraint, m) - // A substitution type originates in the true branch of a conditional type and can be resolved - // to just the base type in the same cases as the conditional type resolves to its true branch - // (because the base type is then known to satisfy the constraint). - if newBaseType.flags&TypeFlagsTypeVariable != 0 && c.isGenericType(newConstraint) { - return c.getSubstitutionType(newBaseType, newConstraint) - } - if newConstraint.flags&TypeFlagsAnyOrUnknown != 0 || c.isTypeAssignableTo(c.getRestrictiveInstantiation(newBaseType), c.getRestrictiveInstantiation(newConstraint)) { - return newBaseType - } - if newBaseType.flags&TypeFlagsTypeVariable != 0 { - return c.getSubstitutionType(newBaseType, newConstraint) - } - return c.getIntersectionType([]*Type{newConstraint, newBaseType}) - } - return t -} - -// Handles instantiation of the following object types: -// AnonymousType (ObjectFlagsAnonymous|ObjectFlagsSingleSignatureType) -// TypeReference with node != nil (ObjectFlagsReference) -// InstantiationExpressionType (ObjectFlagsInstantiationExpressionType) -// MappedType (ObjectFlagsMapped) -func (c *Checker) getObjectTypeInstantiation(t *Type, m *TypeMapper, alias *TypeAlias) *Type { - var declaration *ast.Node - var target *Type - var typeParameters []*Type - switch { - case t.objectFlags&ObjectFlagsReference != 0: // Deferred type reference - declaration = t.AsTypeReference().node - case t.objectFlags&ObjectFlagsInstantiationExpressionType != 0: - declaration = t.AsInstantiationExpressionType().node - default: - declaration = t.symbol.Declarations[0] - } - links := c.typeNodeLinks.Get(declaration) - switch { - case t.objectFlags&ObjectFlagsReference != 0: // Deferred type reference - target = links.resolvedType - case t.objectFlags&ObjectFlagsInstantiated != 0: - target = t.Target() - default: - target = t - } - typeParameters = links.outerTypeParameters - if typeParameters == nil { - // The first time an anonymous type is instantiated we compute and store a list of the type - // parameters that are in scope (and therefore potentially referenced). For type literals that - // aren't the right hand side of a generic type alias declaration we optimize by reducing the - // set of type parameters to those that are possibly referenced in the literal. - typeParameters = c.getOuterTypeParameters(declaration, true /*includeThisTypes*/) - if len(target.alias.TypeArguments()) == 0 { - if t.objectFlags&(ObjectFlagsReference|ObjectFlagsInstantiationExpressionType) != 0 { - typeParameters = core.Filter(typeParameters, func(tp *Type) bool { - return c.isTypeParameterPossiblyReferenced(tp, declaration) - }) - } else if target.symbol.Flags&(ast.SymbolFlagsMethod|ast.SymbolFlagsTypeLiteral) != 0 { - typeParameters = core.Filter(typeParameters, func(tp *Type) bool { - return core.Some(t.symbol.Declarations, func(d *ast.Node) bool { - return c.isTypeParameterPossiblyReferenced(tp, d) - }) - }) - } - } - if typeParameters == nil { - typeParameters = []*Type{} - } - links.outerTypeParameters = typeParameters - } - if len(typeParameters) == 0 { - return t - } - // We are instantiating an anonymous type that has one or more type parameters in scope. Apply the - // mapper to the type parameters to produce the effective list of type arguments, and compute the - // instantiation cache key from the type IDs of the type arguments. - combinedMapper := c.combineTypeMappers(t.Mapper(), m) - typeArguments := make([]*Type, len(typeParameters)) - for i, tp := range typeParameters { - typeArguments[i] = combinedMapper.Map(tp) - } - newAlias := alias - if newAlias == nil { - newAlias = c.instantiateTypeAlias(t.alias, m) - } - data := target.AsObjectType() - key := getTypeInstantiationKey(typeArguments, newAlias, t.objectFlags&ObjectFlagsSingleSignatureType != 0) - if data.instantiations == nil { - data.instantiations = make(map[string]*Type) - data.instantiations[getTypeInstantiationKey(typeParameters, target.alias, false)] = target - } - result := data.instantiations[key] - if result == nil { - newMapper := newTypeMapper(typeParameters, typeArguments) - if target.objectFlags&ObjectFlagsSingleSignatureType != 0 && m != nil { - newMapper = c.combineTypeMappers(newMapper, m) - } - switch { - case target.objectFlags&ObjectFlagsReference != 0: - result = c.createDeferredTypeReference(t.Target(), t.AsTypeReference().node, newMapper, newAlias) - case target.objectFlags&ObjectFlagsMapped != 0: - result = c.instantiateMappedType(target, newMapper, newAlias) - default: - result = c.instantiateAnonymousType(target, newMapper, newAlias) - } - data.instantiations[key] = result - if result.flags&TypeFlagsObjectFlagsType != 0 && result.objectFlags&ObjectFlagsCouldContainTypeVariablesComputed == 0 { - // if `result` is one of the object types we tried to make (it may not be, due to how `instantiateMappedType` works), we can carry forward the type variable containment check from the input type arguments - resultCouldContainObjectFlags := core.Some(typeArguments, c.couldContainTypeVariables) - if result.objectFlags&ObjectFlagsCouldContainTypeVariablesComputed == 0 { - if result.objectFlags&(ObjectFlagsMapped|ObjectFlagsAnonymous|ObjectFlagsReference) != 0 { - result.objectFlags |= ObjectFlagsCouldContainTypeVariablesComputed | core.IfElse(resultCouldContainObjectFlags, ObjectFlagsCouldContainTypeVariables, 0) - } else { - // If none of the type arguments for the outer type parameters contain type variables, it follows - // that the instantiated type doesn't reference type variables. - // Intrinsics have `CouldContainTypeVariablesComputed` pre-set, so this should only cover unions and intersections resulting from `instantiateMappedType` - result.objectFlags |= core.IfElse(!resultCouldContainObjectFlags, ObjectFlagsCouldContainTypeVariablesComputed, 0) - } - } - } - } - return result -} - -func (c *Checker) isTypeParameterPossiblyReferenced(tp *Type, node *ast.Node) bool { - var containsReference func(*ast.Node) bool - containsReference = func(node *ast.Node) bool { - switch node.Kind { - case ast.KindThisType: - return tp.AsTypeParameter().isThisType - case ast.KindTypeReference: - // use worker because we're looking for === equality - if !tp.AsTypeParameter().isThisType && len(node.TypeArguments()) == 0 && c.getSymbolFromTypeReference(node) == tp.symbol { - return true - } - case ast.KindTypeQuery: - entityName := node.AsTypeQueryNode().ExprName - firstIdentifier := ast.GetFirstIdentifier(entityName) - if !ast.IsThisIdentifier(firstIdentifier) { - firstIdentifierSymbol := c.getResolvedSymbol(firstIdentifier) - tpDeclaration := tp.symbol.Declarations[0] // There is exactly one declaration, otherwise `containsReference` is not called - var tpScope *ast.Node - switch { - case ast.IsTypeParameterDeclaration(tpDeclaration): - tpScope = tpDeclaration.Parent // Type parameter is a regular type parameter, e.g. foo - case tp.AsTypeParameter().isThisType: - tpScope = tpDeclaration // Type parameter is the this type, and its declaration is the class declaration. - } - if tpScope != nil { - return core.Some(firstIdentifierSymbol.Declarations, func(d *ast.Node) bool { return isNodeDescendantOf(d, tpScope) }) || - core.Some(node.TypeArguments(), containsReference) - } - } - return true - case ast.KindMethodDeclaration, ast.KindMethodSignature: - returnType := node.Type() - return returnType == nil && node.Body() != nil || - core.Some(node.TypeParameters(), containsReference) || - core.Some(node.Parameters(), containsReference) || - returnType != nil && containsReference(returnType) - } - return node.ForEachChild(containsReference) - } - // If the type parameter doesn't have exactly one declaration, if there are intervening statement blocks - // between the node and the type parameter declaration, if the node contains actual references to the - // type parameter, or if the node contains type queries that we can't prove couldn't contain references to the type parameter, - // we consider the type parameter possibly referenced. - if tp.symbol != nil && len(tp.symbol.Declarations) == 1 { - container := tp.symbol.Declarations[0].Parent - for n := node; n != container; n = n.Parent { - if n == nil || ast.IsBlock(n) || ast.IsConditionalTypeNode(n) && containsReference(n.AsConditionalTypeNode().ExtendsType) { - return true - } - } - return containsReference(node) - } - return true -} - -func (c *Checker) instantiateAnonymousType(t *Type, m *TypeMapper, alias *TypeAlias) *Type { - result := c.newObjectType(t.objectFlags&^(ObjectFlagsCouldContainTypeVariablesComputed|ObjectFlagsCouldContainTypeVariables)|ObjectFlagsInstantiated, t.symbol) - switch { - case t.objectFlags&ObjectFlagsMapped != 0: - result.AsMappedType().declaration = t.AsMappedType().declaration - // C.f. instantiateSignature - origTypeParameter := c.getTypeParameterFromMappedType(t) - freshTypeParameter := c.cloneTypeParameter(origTypeParameter) - result.AsMappedType().typeParameter = freshTypeParameter - m = c.combineTypeMappers(newSimpleTypeMapper(origTypeParameter, freshTypeParameter), m) - freshTypeParameter.AsTypeParameter().mapper = m - case t.objectFlags&ObjectFlagsInstantiationExpressionType != 0: - result.AsInstantiationExpressionType().node = t.AsInstantiationExpressionType().node - } - if alias == nil { - alias = c.instantiateTypeAlias(t.alias, m) - } - result.alias = alias - if alias != nil && len(alias.typeArguments) != 0 { - result.objectFlags |= c.getPropagatingFlagsOfTypes(result.alias.typeArguments, TypeFlagsNone) - } - d := result.AsObjectType() - d.target = t - d.mapper = m - return result -} - -func (c *Checker) getConditionalTypeInstantiation(t *Type, mapper *TypeMapper, forConstraint bool, alias *TypeAlias) *Type { - root := t.AsConditionalType().root - if len(root.outerTypeParameters) != 0 { - // We are instantiating a conditional type that has one or more type parameters in scope. Apply the - // mapper to the type parameters to produce the effective list of type arguments, and compute the - // instantiation cache key from the type IDs of the type arguments. - typeArguments := core.Map(root.outerTypeParameters, func(t *Type) *Type { return mapper.Map(t) }) - key := getConditionalTypeKey(typeArguments, alias, forConstraint) - result := root.instantiations[key] - if result == nil { - newMapper := newTypeMapper(root.outerTypeParameters, typeArguments) - checkType := root.checkType - var distributionType *Type - if root.isDistributive { - distributionType = c.getReducedType(newMapper.Map(checkType)) - } - // Distributive conditional types are distributed over union types. For example, when the - // distributive conditional type T extends U ? X : Y is instantiated with A | B for T, the - // result is (A extends U ? X : Y) | (B extends U ? X : Y). - if distributionType != nil && checkType != distributionType && distributionType.flags&(TypeFlagsUnion|TypeFlagsNever) != 0 { - result = c.mapTypeWithAlias(distributionType, func(t *Type) *Type { - return c.getConditionalType(root, prependTypeMapping(checkType, t, newMapper), forConstraint, nil) - }, alias) - } else { - result = c.getConditionalType(root, newMapper, forConstraint, alias) - } - root.instantiations[key] = result - } - return result - } - return t -} - -func (c *Checker) cloneTypeParameter(tp *Type) *Type { - result := c.newTypeParameter(tp.symbol) - result.AsTypeParameter().target = tp - return result -} - -func (c *Checker) getHomomorphicTypeVariable(t *Type) *Type { - constraintType := c.getConstraintTypeFromMappedType(t) - if constraintType.flags&TypeFlagsIndex != 0 { - typeVariable := c.getActualTypeVariable(constraintType.AsIndexType().target) - if typeVariable.flags&TypeFlagsTypeParameter != 0 { - return typeVariable - } - } - return nil -} - -func (c *Checker) instantiateMappedType(t *Type, m *TypeMapper, alias *TypeAlias) *Type { - // For a homomorphic mapped type { [P in keyof T]: X }, where T is some type variable, the mapping - // operation depends on T as follows: - // * If T is a primitive type no mapping is performed and the result is simply T. - // * If T is a union type we distribute the mapped type over the union. - // * If T is an array we map to an array where the element type has been transformed. - // * If T is a tuple we map to a tuple where the element types have been transformed. - // * If T is an intersection of array or tuple types we map to an intersection of transformed array or tuple types. - // * Otherwise we map to an object type where the type of each property has been transformed. - // For example, when T is instantiated to a union type A | B, we produce { [P in keyof A]: X } | - // { [P in keyof B]: X }, and when when T is instantiated to a union type A | undefined, we produce - // { [P in keyof A]: X } | undefined. - d := t.AsMappedType() - typeVariable := c.getHomomorphicTypeVariable(t) - var instantiateConstituent func(*Type) *Type - instantiateConstituent = func(s *Type) *Type { - if s.flags&(TypeFlagsAnyOrUnknown|TypeFlagsInstantiableNonPrimitive|TypeFlagsObject|TypeFlagsIntersection) == 0 || s == c.wildcardType || c.isErrorType(s) { - return s - } - if d.declaration.NameType == nil { - if c.isArrayType(s) || s.flags&TypeFlagsAny != 0 && c.findResolutionCycleStartIndex(typeVariable, TypeSystemPropertyNameResolvedBaseConstraint) < 0 && c.hasArrayOrTypeTypeConstraint(typeVariable) { - return c.instantiateMappedArrayType(s, t, prependTypeMapping(typeVariable, s, m)) - } - if isTupleType(s) { - return c.instantiateMappedTupleType(s, t, typeVariable, m) - } - if c.isArrayOrTupleOrIntersection(s) { - return c.getIntersectionType(core.Map(s.Types(), instantiateConstituent)) - } - } - return c.instantiateAnonymousType(t, prependTypeMapping(typeVariable, s, m), nil) - } - if typeVariable != nil { - mappedTypeVariable := c.instantiateType(typeVariable, m) - if typeVariable != mappedTypeVariable { - return c.mapTypeWithAlias(c.getReducedType(mappedTypeVariable), instantiateConstituent, alias) - } - } - // If the constraint type of the instantiation is the wildcard type, return the wildcard type. - if c.instantiateType(c.getConstraintTypeFromMappedType(t), m) == c.wildcardType { - return c.wildcardType - } - return c.instantiateAnonymousType(t, m, alias) -} - -func (c *Checker) hasArrayOrTypeTypeConstraint(typeVariable *Type) bool { - constraint := c.getConstraintOfTypeParameter(typeVariable) - return constraint != nil && everyType(constraint, c.isArrayOrTupleType) -} - -func (c *Checker) instantiateMappedArrayType(arrayType *Type, mappedType *Type, m *TypeMapper) *Type { - elementType := c.instantiateMappedTypeTemplate(mappedType, c.numberType, true /*isOptional*/, m) - if c.isErrorType(elementType) { - return c.errorType - } - return c.createArrayTypeEx(elementType, getModifiedReadonlyState(c.isReadonlyArrayType(arrayType), getMappedTypeModifiers(mappedType))) -} - -func (c *Checker) instantiateMappedTupleType(tupleType *Type, mappedType *Type, typeVariable *Type, m *TypeMapper) *Type { - // We apply the mapped type's template type to each of the fixed part elements. For variadic elements, we - // apply the mapped type itself to the variadic element type. For other elements in the variable part of the - // tuple, we surround the element type with an array type and apply the mapped type to that. This ensures - // that we get sequential property key types for the fixed part of the tuple, and property key type number - // for the remaining elements. For example - // - // type Keys = { [K in keyof T]: K }; - // type Foo = Keys<[string, string, ...T, string]>; // ["0", "1", ...Keys, number] - // - elementInfos := tupleType.TargetTupleType().elementInfos - fixedLength := tupleType.TargetTupleType().fixedLength - fixedMapper := m - if fixedLength != 0 { - fixedMapper = prependTypeMapping(typeVariable, tupleType, m) - } - modifiers := getMappedTypeModifiers(mappedType) - elementTypes := c.getElementTypes(tupleType) - newElementTypes := make([]*Type, len(elementTypes)) - newElementInfos := slices.Clone(elementInfos) - for i, e := range elementTypes { - flags := elementInfos[i].flags - var mapped *Type - switch { - case i < fixedLength: - mapped = c.instantiateMappedTypeTemplate(mappedType, c.getStringLiteralType(strconv.Itoa(i)), flags&ElementFlagsOptional != 0, fixedMapper) - case flags&ElementFlagsVariadic != 0: - mapped = c.instantiateType(mappedType, prependTypeMapping(typeVariable, e, m)) - default: - mapped = c.getElementTypeOfArrayType(c.instantiateType(mappedType, prependTypeMapping(typeVariable, c.createArrayType(e), m))) - if mapped == nil { - mapped = c.unknownType - } - } - switch { - case modifiers&MappedTypeModifiersIncludeOptional != 0: - if flags&ElementFlagsRequired != 0 { - newElementInfos[i].flags = ElementFlagsOptional - } - case modifiers&MappedTypeModifiersExcludeOptional != 0: - if flags&ElementFlagsOptional != 0 { - newElementInfos[i].flags = ElementFlagsRequired - } - } - newElementTypes[i] = mapped - } - newReadonly := getModifiedReadonlyState(tupleType.TargetTupleType().readonly, getMappedTypeModifiers(mappedType)) - if slices.Contains(newElementTypes, c.errorType) { - return c.errorType - } - return c.createTupleTypeEx(newElementTypes, newElementInfos, newReadonly) -} - -func (c *Checker) instantiateMappedTypeTemplate(t *Type, key *Type, isOptional bool, m *TypeMapper) *Type { - templateMapper := appendTypeMapping(m, c.getTypeParameterFromMappedType(t), key) - propType := c.instantiateType(c.getTemplateTypeFromMappedType(core.OrElse(t.AsMappedType().target, t)), templateMapper) - modifiers := getMappedTypeModifiers(t) - switch { - case c.strictNullChecks && modifiers&MappedTypeModifiersIncludeOptional != 0 && !c.maybeTypeOfKind(propType, TypeFlagsUndefined|TypeFlagsVoid): - return c.getOptionalType(propType, true /*isProperty*/) - case c.strictNullChecks && modifiers&MappedTypeModifiersExcludeOptional != 0 && isOptional: - return c.getTypeWithFacts(propType, TypeFactsNEUndefined) - default: - return propType - } -} - -func getModifiedReadonlyState(state bool, modifiers MappedTypeModifiers) bool { - switch { - case modifiers&MappedTypeModifiersIncludeReadonly != 0: - return true - case modifiers&MappedTypeModifiersExcludeReadonly != 0: - return false - } - return state -} - -func (c *Checker) getTypeParameterFromMappedType(t *Type) *Type { - m := t.AsMappedType() - if m.typeParameter == nil { - m.typeParameter = c.getDeclaredTypeOfTypeParameter(c.getSymbolOfDeclaration(m.declaration.TypeParameter)) - } - return m.typeParameter -} - -func (c *Checker) getConstraintTypeFromMappedType(t *Type) *Type { - m := t.AsMappedType() - if m.constraintType == nil { - m.constraintType = core.OrElse(c.getConstraintOfTypeParameter(c.getTypeParameterFromMappedType(t)), c.errorType) - } - return m.constraintType -} - -func (c *Checker) getNameTypeFromMappedType(t *Type) *Type { - m := t.AsMappedType() - if m.declaration.NameType == nil { - return nil - } - if m.nameType == nil { - m.nameType = c.instantiateType(c.getTypeFromTypeNode(m.declaration.NameType), m.mapper) - } - return m.nameType -} - -func (c *Checker) getTemplateTypeFromMappedType(t *Type) *Type { - m := t.AsMappedType() - if m.templateType == nil { - if m.declaration.Type != nil { - m.templateType = c.instantiateType(c.addOptionalityEx(c.getTypeFromTypeNode(m.declaration.Type) /*isProperty*/, true, getMappedTypeModifiers(t)&MappedTypeModifiersIncludeOptional != 0), m.mapper) - } else { - m.templateType = c.errorType - } - } - return m.templateType -} - -func (c *Checker) isMappedTypeWithKeyofConstraintDeclaration(t *Type) bool { - constraintDeclaration := c.getConstraintDeclarationForMappedType(t) - return ast.IsTypeOperatorNode(constraintDeclaration) && constraintDeclaration.AsTypeOperatorNode().Operator == ast.KindKeyOfKeyword -} - -func (c *Checker) getConstraintDeclarationForMappedType(t *Type) *ast.Node { - return t.AsMappedType().declaration.TypeParameter.AsTypeParameter().Constraint -} - -func (c *Checker) getApparentMappedTypeKeys(nameType *Type, targetType *Type) *Type { - modifiersType := c.getApparentType(c.getModifiersTypeFromMappedType(targetType)) - var mappedKeys []*Type - c.forEachMappedTypePropertyKeyTypeAndIndexSignatureKeyType(modifiersType, TypeFlagsStringOrNumberLiteralOrUnique, false, func(t *Type) { - mappedKeys = append(mappedKeys, c.instantiateType(nameType, appendTypeMapping(targetType.Mapper(), c.getTypeParameterFromMappedType(targetType), t))) - }) - return c.getUnionType(mappedKeys) -} - -func (c *Checker) forEachMappedTypePropertyKeyTypeAndIndexSignatureKeyType(t *Type, include TypeFlags, stringsOnly bool, cb func(keyType *Type)) { - for _, prop := range c.getPropertiesOfType(t) { - cb(c.getLiteralTypeFromProperty(prop, include, false)) - } - if t.flags&TypeFlagsAny != 0 { - cb(c.stringType) - } else { - for _, info := range c.getIndexInfosOfType(t) { - if !stringsOnly || info.keyType.flags&(TypeFlagsString|TypeFlagsTemplateLiteral) != 0 { - cb(info.keyType) - } - } - } -} - -func (c *Checker) instantiateReverseMappedType(t *Type, m *TypeMapper) *Type { - r := t.AsReverseMappedType() - innerMappedType := c.instantiateType(r.mappedType, m) - if innerMappedType.objectFlags&ObjectFlagsMapped == 0 { - return t - } - innerIndexType := c.instantiateType(r.constraintType, m) - if innerIndexType.flags&TypeFlagsIndex == 0 { - return t - } - instantiated := c.inferTypeForHomomorphicMappedType(c.instantiateType(r.source, m), innerMappedType, innerIndexType) - if instantiated != nil { - return instantiated - } - return t - // Nested invocation of `inferTypeForHomomorphicMappedType` or the `source` instantiated into something unmappable -} - -func (c *Checker) instantiateTypeAlias(alias *TypeAlias, m *TypeMapper) *TypeAlias { - if alias == nil { - return nil - } - return &TypeAlias{symbol: alias.symbol, typeArguments: c.instantiateTypes(alias.typeArguments, m)} -} - -func (c *Checker) instantiateTypes(types []*Type, m *TypeMapper) []*Type { - return instantiateList(c, types, m, (*Checker).instantiateType) -} - -func (c *Checker) instantiateSymbols(symbols []*ast.Symbol, m *TypeMapper) []*ast.Symbol { - return instantiateList(c, symbols, m, (*Checker).instantiateSymbol) -} - -func (c *Checker) instantiateSignatures(signatures []*Signature, m *TypeMapper) []*Signature { - return instantiateList(c, signatures, m, (*Checker).instantiateSignature) -} - -func (c *Checker) instantiateIndexInfos(indexInfos []*IndexInfo, m *TypeMapper) []*IndexInfo { - return instantiateList(c, indexInfos, m, (*Checker).instantiateIndexInfo) -} - -func instantiateList[T comparable](c *Checker, values []T, m *TypeMapper, instantiator func(c *Checker, value T, m *TypeMapper) T) []T { - for i, value := range values { - mapped := instantiator(c, value, m) - if mapped != value { - result := make([]T, len(values)) - copy(result, values[:i]) - result[i] = mapped - for j := i + 1; j < len(values); j++ { - result[j] = instantiator(c, values[j], m) - } - return result - } - } - return values -} - -func (c *Checker) tryGetTypeFromTypeNode(node *ast.Node) *Type { - typeNode := node.Type() - if typeNode != nil { - return c.getTypeFromTypeNode(typeNode) - } - return nil -} - -func (c *Checker) getTypeFromTypeNode(node *ast.Node) *Type { - return c.getConditionalFlowTypeOfType(c.getTypeFromTypeNodeWorker(node), node) -} - -func (c *Checker) getTypeFromTypeNodeWorker(node *ast.Node) *Type { - switch node.Kind { - case ast.KindAnyKeyword, ast.KindJSDocAllType: - return c.anyType - case ast.KindJSDocNonNullableType: - return c.getTypeFromTypeNode(node.AsJSDocNonNullableType().Type) - case ast.KindJSDocNullableType: - t := c.getTypeFromTypeNode(node.AsJSDocNullableType().Type) - if c.strictNullChecks { - return c.getNullableType(t, TypeFlagsNull) - } else { - return t - } - case ast.KindJSDocVariadicType: - return c.createArrayType(c.getTypeFromTypeNode(node.AsJSDocVariadicType().Type)) - case ast.KindJSDocOptionalType: - return c.addOptionality(c.getTypeFromTypeNode(node.AsJSDocOptionalType().Type)) - case ast.KindUnknownKeyword: - return c.unknownType - case ast.KindStringKeyword: - return c.stringType - case ast.KindNumberKeyword: - return c.numberType - case ast.KindBigIntKeyword: - return c.bigintType - case ast.KindBooleanKeyword: - return c.booleanType - case ast.KindSymbolKeyword: - return c.esSymbolType - case ast.KindVoidKeyword: - return c.voidType - case ast.KindUndefinedKeyword: - return c.undefinedType - case ast.KindNullKeyword: - return c.nullType - case ast.KindNeverKeyword: - return c.neverType - case ast.KindObjectKeyword: - return c.nonPrimitiveType - case ast.KindIntrinsicKeyword: - return c.intrinsicMarkerType - case ast.KindThisType, ast.KindThisKeyword: - return c.getTypeFromThisTypeNode(node) - case ast.KindLiteralType: - return c.getTypeFromLiteralTypeNode(node) - case ast.KindTypeReference, ast.KindExpressionWithTypeArguments: - return c.getTypeFromTypeReference(node) - case ast.KindTypePredicate: - if node.AsTypePredicateNode().AssertsModifier != nil { - return c.voidType - } - return c.booleanType - case ast.KindTypeQuery: - return c.getTypeFromTypeQueryNode(node) - case ast.KindArrayType, ast.KindTupleType: - return c.getTypeFromArrayOrTupleTypeNode(node) - case ast.KindOptionalType: - return c.getTypeFromOptionalTypeNode(node) - case ast.KindUnionType: - return c.getTypeFromUnionTypeNode(node) - case ast.KindIntersectionType: - return c.getTypeFromIntersectionTypeNode(node) - case ast.KindNamedTupleMember: - return c.getTypeFromNamedTupleTypeNode(node) - case ast.KindParenthesizedType: - return c.getTypeFromTypeNode(node.AsParenthesizedTypeNode().Type) - case ast.KindRestType: - return c.getTypeFromRestTypeNode(node) - case ast.KindFunctionType, ast.KindConstructorType, ast.KindTypeLiteral: - return c.getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node) - case ast.KindTypeOperator: - return c.getTypeFromTypeOperatorNode(node) - case ast.KindIndexedAccessType: - return c.getTypeFromIndexedAccessTypeNode(node) - case ast.KindTemplateLiteralType: - return c.getTypeFromTemplateTypeNode(node) - case ast.KindMappedType: - return c.getTypeFromMappedTypeNode(node) - case ast.KindConditionalType: - return c.getTypeFromConditionalTypeNode(node) - case ast.KindInferType: - return c.getTypeFromInferTypeNode(node) - case ast.KindImportType: - return c.getTypeFromImportTypeNode(node) - default: - return c.errorType - } -} - -func (c *Checker) getTypeFromThisTypeNode(node *ast.Node) *Type { - links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { - links.resolvedType = c.getThisType(node) - } - return links.resolvedType -} - -func (c *Checker) getThisType(node *ast.Node) *Type { - container := ast.GetThisContainer(node /*includeArrowFunctions*/, false /*includeClassComputedPropertyName*/, false) - if container != nil { - parent := container.Parent - if parent != nil && (ast.IsClassLike(parent) || ast.IsInterfaceDeclaration(parent)) { - if !ast.IsStatic(container) && (!ast.IsConstructorDeclaration(container) || isNodeDescendantOf(node, container.Body())) { - return c.getDeclaredTypeOfClassOrInterface(c.getSymbolOfDeclaration(parent)).AsInterfaceType().thisType - } - } - } - c.error(node, diagnostics.A_this_type_is_available_only_in_a_non_static_member_of_a_class_or_interface) - return c.errorType -} - -func (c *Checker) getTypeFromLiteralTypeNode(node *ast.Node) *Type { - if node.AsLiteralTypeNode().Literal.Kind == ast.KindNullKeyword { - return c.nullType - } - links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { - links.resolvedType = c.getRegularTypeOfLiteralType(c.checkExpression(node.AsLiteralTypeNode().Literal)) - } - return links.resolvedType -} - -func (c *Checker) getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node *ast.Node) *Type { - links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { - // Deferred resolution of members is handled by resolveObjectTypeMembers - alias := c.getAliasForTypeNode(node) - if sym := node.Symbol(); sym == nil || len(c.getMembersOfSymbol(sym)) == 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) getTypeFromIndexedAccessTypeNode(node *ast.Node) *Type { - links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { - objectType := c.getTypeFromTypeNode(node.AsIndexedAccessTypeNode().ObjectType) - indexType := c.getTypeFromTypeNode(node.AsIndexedAccessTypeNode().IndexType) - potentialAlias := c.getAliasForTypeNode(node) - links.resolvedType = c.getIndexedAccessTypeEx(objectType, indexType, AccessFlagsNone, node, potentialAlias) - } - return links.resolvedType -} - -func (c *Checker) getTypeFromTypeOperatorNode(node *ast.Node) *Type { - links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { - argType := node.AsTypeOperatorNode().Type - switch node.AsTypeOperatorNode().Operator { - case ast.KindKeyOfKeyword: - links.resolvedType = c.getIndexType(c.getTypeFromTypeNode(argType)) - case ast.KindUniqueKeyword: - if argType.Kind == ast.KindSymbolKeyword { - links.resolvedType = c.getESSymbolLikeTypeForNode(ast.WalkUpParenthesizedTypes(node.Parent)) - } else { - links.resolvedType = c.errorType - } - case ast.KindReadonlyKeyword: - links.resolvedType = c.getTypeFromTypeNode(argType) - default: - panic("Unhandled case in getTypeFromTypeOperatorNode") - } - } - return links.resolvedType -} - -func (c *Checker) getESSymbolLikeTypeForNode(node *ast.Node) *Type { - if isValidESSymbolDeclaration(node) { - symbol := c.getSymbolOfNode(node) - if symbol != nil { - uniqueType := c.uniqueESSymbolTypes[symbol] - if uniqueType == nil { - var b KeyBuilder - b.WriteString(ast.InternalSymbolNamePrefix) - b.WriteByte('@') - b.WriteString(symbol.Name) - b.WriteByte('@') - b.WriteSymbol(symbol) - uniqueType = c.newUniqueESSymbolType(symbol, b.String()) - c.uniqueESSymbolTypes[symbol] = uniqueType - } - return uniqueType - } - } - return c.esSymbolType -} - -func (c *Checker) getTypeFromTypeReference(node *ast.Node) *Type { - links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { - // Cache both the resolved symbol and the resolved type. The resolved symbol is needed when we check the - // type reference in checkTypeReferenceNode. - symbol := c.getSymbolFromTypeReference(node) - // handle LS queries on the `const` in `x as const` by resolving to the type of `x` - if isConstTypeReference(node) && ast.IsAssertionExpression(node.Parent) { - links.resolvedType = c.checkExpressionCached(node.Parent.Expression()) - } else { - links.resolvedType = c.getTypeReferenceType(node, symbol) - } - } - return links.resolvedType -} - -func (c *Checker) getSymbolFromTypeReference(node *ast.Node) *ast.Symbol { - links := c.symbolNodeLinks.Get(node) - if links.resolvedSymbol == nil { - if isConstTypeReference(node) && ast.IsAssertionExpression(node.Parent) { - links.resolvedSymbol = c.unknownSymbol - } else { - links.resolvedSymbol = c.resolveTypeReferenceName(node, ast.SymbolFlagsType, false /*ignoreErrors*/) - } - } - return links.resolvedSymbol -} - -func (c *Checker) resolveTypeReferenceName(typeReference *ast.Node, meaning ast.SymbolFlags, ignoreErrors bool) *ast.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.getUnresolvedSymbolForEntityName(name) -} - -func (c *Checker) getUnresolvedSymbolForEntityName(name *ast.Node) *ast.Symbol { - var identifier *ast.Node - switch name.Kind { - case ast.KindQualifiedName: - identifier = name.AsQualifiedName().Right - case ast.KindPropertyAccessExpression: - identifier = name.Name() - default: - identifier = name - } - text := identifier.Text() - if text != "" { - var parentSymbol *ast.Symbol - switch name.Kind { - case ast.KindQualifiedName: - parentSymbol = c.getUnresolvedSymbolForEntityName(name.AsQualifiedName().Left) - case ast.KindPropertyAccessExpression: - parentSymbol = c.getUnresolvedSymbolForEntityName(name.Expression()) - } - var path string - if parentSymbol != nil { - path = getSymbolPath(parentSymbol) + "." + text - } else { - path = text - } - result := c.unresolvedSymbols[path] - if result == nil { - result = c.newSymbolEx(ast.SymbolFlagsTypeAlias, text, ast.CheckFlagsUnresolved) - c.unresolvedSymbols[path] = result - result.Parent = parentSymbol - c.typeAliasLinks.Get(result).declaredType = c.unresolvedType - } - return result - } - return c.unknownSymbol -} - -func getSymbolPath(symbol *ast.Symbol) string { - if symbol.Parent != nil { - return getSymbolPath(symbol.Parent) + "." + symbol.Name - } - return symbol.Name -} - -func (c *Checker) getTypeReferenceType(node *ast.Node, symbol *ast.Symbol) *Type { - if symbol == c.unknownSymbol { - return c.errorType - } - if symbol.Flags&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface) != 0 { - return c.getTypeFromClassOrInterfaceReference(node, symbol) - } - if symbol.Flags&ast.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) - } - - // !!! Resolving values as types for JS - return c.errorType -} - -/** - * Get type from type-reference that reference to class or interface - */ -func (c *Checker) getTypeFromClassOrInterfaceReference(node *ast.Node, symbol *ast.Symbol) *Type { - t := c.getDeclaredTypeOfClassOrInterface(c.getMergedSymbol(symbol)) - d := t.AsInterfaceType() - typeParameters := d.LocalTypeParameters() - if len(typeParameters) != 0 { - numTypeArguments := len(node.TypeArguments()) - minTypeArgumentCount := c.getMinTypeArgumentCount(typeParameters) - isJs := ast.IsInJSFile(node) - isJsImplicitAny := !c.noImplicitAny && isJs - if !isJsImplicitAny && (numTypeArguments < minTypeArgumentCount || numTypeArguments > len(typeParameters)) { - var message *diagnostics.Message - - missingAugmentsTag := isJs && ast.IsExpressionWithTypeArguments(node) && !ast.IsJSDocAugmentsTag(node.Parent) - if missingAugmentsTag { - message = diagnostics.Expected_0_type_arguments_provide_these_with_an_extends_tag - if minTypeArgumentCount < len(typeParameters) { - message = diagnostics.Expected_0_1_type_arguments_provide_these_with_an_extends_tag - } - } else { - 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.TypeToStringEx(t, nil /*enclosingDeclaration*/, TypeFormatFlagsWriteArrayAsGenericType) - c.error(node, message, typeStr, minTypeArgumentCount, len(typeParameters)) - if !isJs { - // 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 == ast.KindTypeReference && 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(c.getTypeArgumentsFromNode(node), typeParameters, minTypeArgumentCount, isJs) - typeArguments := append(d.OuterTypeParameters(), localTypeArguments...) - return c.createTypeReference(t, typeArguments) - } - if c.checkNoTypeArguments(node, symbol) { - return t - } - return c.errorType -} - -func (c *Checker) getTypeArgumentsFromNode(node *ast.Node) []*Type { - return core.Map(node.TypeArguments(), c.getTypeFromTypeNode) -} - -func (c *Checker) checkNoTypeArguments(node *ast.Node, symbol *ast.Symbol) bool { - if len(node.TypeArguments()) != 0 { - c.error(node, diagnostics.Type_0_is_not_generic, c.symbolToString(symbol)) - return false - } - return true -} - -// Return true if the given type reference node is directly aliased or if it needs to be deferred -// because it is possibly contained in a circular chain of eagerly resolved types. -func (c *Checker) isDeferredTypeReferenceNode(node *ast.Node, hasDefaultTypeArguments bool) bool { - if c.getAliasSymbolForTypeNode(node) != nil { - return true - } - if c.isResolvedByTypeAlias(node) { - switch node.Kind { - case ast.KindArrayType: - return c.mayResolveTypeAlias(node.AsArrayTypeNode().ElementType) - case ast.KindTupleType: - return core.Some(node.AsTupleTypeNode().Elements.Nodes, c.mayResolveTypeAlias) - case ast.KindTypeReference: - return hasDefaultTypeArguments || core.Some(node.TypeArguments(), c.mayResolveTypeAlias) - } - panic("Unhandled case in isDeferredTypeReferenceNode") - } - return false -} - -// Return true when the given node is transitively contained in type constructs that eagerly -// resolve their constituent types. We include SyntaxKind.TypeReference because type arguments -// of type aliases are eagerly resolved. -func (c *Checker) isResolvedByTypeAlias(node *ast.Node) bool { - parent := node.Parent - switch parent.Kind { - case ast.KindParenthesizedType, ast.KindNamedTupleMember, ast.KindTypeReference, ast.KindUnionType, ast.KindIntersectionType, - ast.KindIndexedAccessType, ast.KindConditionalType, ast.KindTypeOperator, ast.KindArrayType, ast.KindTupleType: - return c.isResolvedByTypeAlias(parent) - case ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration: - return true - } - return false -} - -// Return true if resolving the given node (i.e. getTypeFromTypeNode) possibly causes resolution -// of a type alias. -func (c *Checker) mayResolveTypeAlias(node *ast.Node) bool { - switch node.Kind { - case ast.KindTypeReference: - return c.resolveTypeReferenceName(node, ast.SymbolFlagsType, false).Flags&ast.SymbolFlagsTypeAlias != 0 - case ast.KindTypeQuery: - return true - case ast.KindTypeOperator: - return node.AsTypeOperatorNode().Operator != ast.KindUniqueKeyword && c.mayResolveTypeAlias(node.AsTypeOperatorNode().Type) - case ast.KindParenthesizedType: - return c.mayResolveTypeAlias(node.AsParenthesizedTypeNode().Type) - case ast.KindOptionalType: - return c.mayResolveTypeAlias(node.AsOptionalTypeNode().Type) - case ast.KindNamedTupleMember: - return c.mayResolveTypeAlias(node.AsNamedTupleMember().Type) - case ast.KindRestType: - return node.AsRestTypeNode().Type.Kind != ast.KindArrayType || c.mayResolveTypeAlias(node.AsRestTypeNode().Type.AsArrayTypeNode().ElementType) - case ast.KindUnionType: - return core.Some(node.AsUnionTypeNode().Types.Nodes, c.mayResolveTypeAlias) - case ast.KindIntersectionType: - return core.Some(node.AsIntersectionTypeNode().Types.Nodes, c.mayResolveTypeAlias) - case ast.KindIndexedAccessType: - return c.mayResolveTypeAlias(node.AsIndexedAccessTypeNode().ObjectType) || c.mayResolveTypeAlias(node.AsIndexedAccessTypeNode().IndexType) - case ast.KindConditionalType: - return c.mayResolveTypeAlias(node.AsConditionalTypeNode().CheckType) || c.mayResolveTypeAlias(node.AsConditionalTypeNode().ExtendsType) || - c.mayResolveTypeAlias(node.AsConditionalTypeNode().TrueType) || c.mayResolveTypeAlias(node.AsConditionalTypeNode().FalseType) - } - return false -} - -func (c *Checker) createNormalizedTypeReference(target *Type, typeArguments []*Type) *Type { - if target.objectFlags&ObjectFlagsTuple != 0 { - return c.createNormalizedTupleType(target, typeArguments) - } - return c.createTypeReference(target, typeArguments) -} - -func (c *Checker) createNormalizedTupleType(target *Type, elementTypes []*Type) *Type { - d := target.AsTupleType() - if d.combinedFlags&ElementFlagsNonRequired == 0 { - // No need to normalize when we only have regular required elements - return c.createTypeReference(target, elementTypes) - } - if d.combinedFlags&ElementFlagsVariadic != 0 { - for i, e := range elementTypes { - if d.elementInfos[i].flags&ElementFlagsVariadic != 0 && e.flags&(TypeFlagsNever|TypeFlagsUnion) != 0 { - // Transform [A, ...(X | Y | Z)] into [A, ...X] | [A, ...Y] | [A, ...Z] - checkTypes := core.MapIndex(elementTypes, func(t *Type, i int) *Type { - if d.elementInfos[i].flags&ElementFlagsVariadic != 0 { - return t - } - return c.unknownType - }) - if c.checkCrossProductUnion(checkTypes) { - return c.mapType(e, func(t *Type) *Type { - return c.createNormalizedTupleType(target, core.ReplaceElement(elementTypes, i, t)) - }) - } - } - } - } - // We have optional, rest, or variadic n that may need normalizing. Normalization ensures that all variadic - // n are generic and that the tuple type has one of the following layouts, disregarding variadic n: - // (1) Zero or more required n, followed by zero or more optional n, followed by zero or one rest element. - // (2) Zero or more required n, followed by a rest element, followed by zero or more required n. - // In either layout, zero or more generic variadic n may be present at any location. - n := &TupleNormalizer{} - if !n.normalize(c, elementTypes, d.elementInfos) { - return c.errorType - } - tupleTarget := c.getTupleTargetType(n.infos, d.readonly) - switch { - case tupleTarget == c.emptyGenericType: - return c.emptyObjectType - case len(n.types) != 0: - return c.createTypeReference(tupleTarget, n.types) - } - return tupleTarget -} - -type TupleNormalizer struct { - c *Checker - types []*Type - infos []TupleElementInfo - lastRequiredIndex int - firstRestIndex int - lastOptionalOrRestIndex int -} - -func (n *TupleNormalizer) normalize(c *Checker, elementTypes []*Type, elementInfos []TupleElementInfo) bool { - n.c = c - n.lastRequiredIndex = -1 - n.firstRestIndex = -1 - n.lastOptionalOrRestIndex = -1 - for i, t := range elementTypes { - info := elementInfos[i] - if info.flags&ElementFlagsVariadic != 0 { - if t.flags&TypeFlagsAny != 0 { - n.add(t, TupleElementInfo{flags: ElementFlagsRest, labeledDeclaration: info.labeledDeclaration}) - } else if t.flags&TypeFlagsInstantiableNonPrimitive != 0 || c.isGenericMappedType(t) { - // Generic variadic elements stay as they are. - n.add(t, info) - } else if isTupleType(t) { - spreadTypes := c.getElementTypes(t) - if len(spreadTypes)+len(n.types) >= 10_000 { - message := core.IfElse(ast.IsPartOfTypeNode(c.currentNode), - diagnostics.Type_produces_a_tuple_type_that_is_too_large_to_represent, - diagnostics.Expression_produces_a_tuple_type_that_is_too_large_to_represent) - c.error(c.currentNode, message) - return false - } - // Spread variadic elements with tuple types into the resulting tuple. - spreadInfos := t.TargetTupleType().elementInfos - for j, s := range spreadTypes { - n.add(s, spreadInfos[j]) - } - } else { - // Treat everything else as an array type and create a rest element. - var s *Type - if c.isArrayLikeType(t) { - s = c.getIndexTypeOfType(t, c.numberType) - } - if s == nil { - s = c.errorType - } - n.add(s, TupleElementInfo{flags: ElementFlagsRest, labeledDeclaration: info.labeledDeclaration}) - } - } else { - // Copy other element kinds with no change. - n.add(t, info) - } - } - // Turn optional elements preceding the last required element into required elements - for i := range n.lastRequiredIndex { - if n.infos[i].flags&ElementFlagsOptional != 0 { - n.infos[i].flags = ElementFlagsRequired - } - } - if n.firstRestIndex >= 0 && n.firstRestIndex < n.lastOptionalOrRestIndex { - // Turn elements between first rest and last optional/rest into a single rest element - var types []*Type - for i := n.firstRestIndex; i <= n.lastOptionalOrRestIndex; i++ { - t := n.types[i] - if n.infos[i].flags&ElementFlagsVariadic != 0 { - t = c.getIndexedAccessType(t, c.numberType) - } - types = append(types, t) - } - n.types[n.firstRestIndex] = c.getUnionType(types) - n.types = slices.Delete(n.types, n.firstRestIndex+1, n.lastOptionalOrRestIndex+1) - n.infos = slices.Delete(n.infos, n.firstRestIndex+1, n.lastOptionalOrRestIndex+1) - } - return true -} - -func (n *TupleNormalizer) add(t *Type, info TupleElementInfo) { - if info.flags&ElementFlagsRequired != 0 { - n.lastRequiredIndex = len(n.types) - } - if info.flags&ElementFlagsRest != 0 && n.firstRestIndex < 0 { - n.firstRestIndex = len(n.types) - } - if info.flags&(ElementFlagsOptional|ElementFlagsRest) != 0 { - n.lastOptionalOrRestIndex = len(n.types) - } - n.types = append(n.types, n.c.addOptionalityEx(t, true /*isProperty*/, info.flags&ElementFlagsOptional != 0)) - n.infos = append(n.infos, info) -} - -// Return count of starting consecutive tuple elements of the given kind(s) -func getStartElementCount(t *TupleType, flags ElementFlags) int { - for i, info := range t.elementInfos { - if info.flags&flags == 0 { - return i - } - } - return len(t.elementInfos) -} - -// Return count of ending consecutive tuple elements of the given kind(s) -func getEndElementCount(t *TupleType, flags ElementFlags) int { - for i := len(t.elementInfos); i > 0; i-- { - if t.elementInfos[i-1].flags&flags == 0 { - return len(t.elementInfos) - i - } - } - return len(t.elementInfos) -} - -func getTotalFixedElementCount(t *TupleType) int { - return t.fixedLength + getEndElementCount(t, ElementFlagsFixed) -} - -func (c *Checker) getElementTypes(t *Type) []*Type { - typeArguments := c.getTypeArguments(t) - arity := c.getTypeReferenceArity(t) - if len(typeArguments) == arity { - return typeArguments - } - return typeArguments[0:arity] -} - -func (c *Checker) getTypeReferenceArity(t *Type) int { - return len(t.TargetInterfaceType().TypeParameters()) -} - -func (c *Checker) isArrayType(t *Type) bool { - return t.objectFlags&ObjectFlagsReference != 0 && (t.Target() == c.globalArrayType || t.Target() == c.globalReadonlyArrayType) -} - -func (c *Checker) isReadonlyArrayType(t *Type) bool { - return t.objectFlags&ObjectFlagsReference != 0 && t.Target() == c.globalReadonlyArrayType -} - -func isTupleType(t *Type) bool { - return t.objectFlags&ObjectFlagsReference != 0 && t.Target().objectFlags&ObjectFlagsTuple != 0 -} - -func isMutableTupleType(t *Type) bool { - return isTupleType(t) && !t.TargetTupleType().readonly -} - -func isGenericTupleType(t *Type) bool { - return isTupleType(t) && t.TargetTupleType().combinedFlags&ElementFlagsVariadic != 0 -} - -func isSingleElementGenericTupleType(t *Type) bool { - return isGenericTupleType(t) && len(t.TargetTupleType().elementInfos) == 1 -} - -func (c *Checker) isArrayOrTupleType(t *Type) bool { - return c.isArrayType(t) || isTupleType(t) -} - -func (c *Checker) isMutableArrayOrTuple(t *Type) bool { - return c.isArrayType(t) && !c.isReadonlyArrayType(t) || isTupleType(t) && !t.TargetTupleType().readonly -} - -func (c *Checker) getElementTypeOfArrayType(t *Type) *Type { - if c.isArrayType(t) { - return c.getTypeArguments(t)[0] - } - return nil -} - -func (c *Checker) isArrayLikeType(t *Type) bool { - // A type is array-like if it is a reference to the global Array or global ReadonlyArray type, - // or if it is not the undefined or null type and if it is assignable to ReadonlyArray - return c.isArrayType(t) || t.flags&TypeFlagsNullable == 0 && c.isTypeAssignableTo(t, c.anyReadonlyArrayType) -} - -func (c *Checker) isMutableArrayLikeType(t *Type) bool { - // A type is mutable-array-like if it is a reference to the global Array type, or if it is not the - // any, undefined or null type and if it is assignable to Array - return c.isMutableArrayOrTuple(t) || t.flags&(TypeFlagsAny|TypeFlagsNullable) == 0 && c.isTypeAssignableTo(t, c.anyArrayType) -} - -func (c *Checker) isEmptyArrayLiteralType(t *Type) bool { - elementType := c.getElementTypeOfArrayType(t) - return elementType != nil && c.isEmptyLiteralType(elementType) -} - -func (c *Checker) isEmptyLiteralType(t *Type) bool { - if c.strictNullChecks { - return t == c.implicitNeverType - } - return t == c.undefinedWideningType -} - -func (c *Checker) isTupleLikeType(t *Type) bool { - if isTupleType(t) || c.getPropertyOfType(t, "0") != nil { - return true - } - if c.isArrayLikeType(t) { - if lengthType := c.getTypeOfPropertyOfType(t, "length"); lengthType != nil { - return everyType(lengthType, func(t *Type) bool { return t.flags&TypeFlagsNumberLiteral != 0 }) - } - } - return false -} - -func (c *Checker) isArrayOrTupleLikeType(t *Type) bool { - return c.isArrayLikeType(t) || c.isTupleLikeType(t) -} - -func (c *Checker) isArrayOrTupleOrIntersection(t *Type) bool { - return t.flags&TypeFlagsIntersection != 0 && core.Every(t.Types(), c.isArrayOrTupleType) -} - -func (c *Checker) getTupleElementType(t *Type, index int) *Type { - propType := c.getTypeOfPropertyOfType(t, strconv.Itoa(index)) - if propType != nil { - return propType - } - if everyType(t, isTupleType) { - return c.getTupleElementTypeOutOfStartCount(t, jsnum.Number(index), core.IfElse(c.compilerOptions.NoUncheckedIndexedAccess == core.TSTrue, c.undefinedType, nil)) - } - return nil -} - -/** - * Get type from reference to type alias. When a type alias is generic, the declared type of the type alias may include - * references to the type parameters of the alias. We replace those with the actual type arguments by instantiating the - * declared type. Instantiations are cached using the type identities of the type arguments as the key. - */ -func (c *Checker) getTypeFromTypeAliasReference(node *ast.Node, symbol *ast.Symbol) *Type { - typeArguments := node.TypeArguments() - if symbol.CheckFlags&ast.CheckFlagsUnresolved != 0 { - alias := &TypeAlias{symbol: symbol, typeArguments: core.Map(typeArguments, c.getTypeFromTypeNode)} - key := getAliasKey(alias) - errorType := c.errorTypes[key] - if errorType == nil { - errorType = c.newIntrinsicType(TypeFlagsAny, "error") - errorType.alias = alias - c.errorTypes[key] = errorType - } - return errorType - } - t := c.getDeclaredTypeOfSymbol(symbol) - typeParameters := c.typeAliasLinks.Get(symbol).typeParameters - if len(typeParameters) != 0 { - numTypeArguments := len(typeArguments) - minTypeArgumentCount := c.getMinTypeArgumentCount(typeParameters) - if numTypeArguments < minTypeArgumentCount || numTypeArguments > len(typeParameters) { - message := core.IfElse(minTypeArgumentCount == len(typeParameters), - diagnostics.Generic_type_0_requires_1_type_argument_s, - diagnostics.Generic_type_0_requires_between_1_and_2_type_arguments) - c.error(node, message, c.symbolToString(symbol), minTypeArgumentCount, len(typeParameters)) - return c.errorType - } - // We refrain from associating a local type alias with an instantiation of a top-level type alias - // because the local alias may end up being referenced in an inferred return type where it is not - // accessible--which in turn may lead to a large structural expansion of the type when generating - // a .d.ts file. See #43622 for an example. - aliasSymbol := c.getAliasSymbolForTypeNode(node) - var newAliasSymbol *ast.Symbol - if aliasSymbol != nil && (isLocalTypeAlias(symbol) || !isLocalTypeAlias(aliasSymbol)) { - newAliasSymbol = aliasSymbol - } - var aliasTypeArguments []*Type - if newAliasSymbol != nil { - aliasTypeArguments = c.getTypeArgumentsForAliasSymbol(newAliasSymbol) - } else if ast.IsTypeReferenceType(node) { - aliasSymbol := c.resolveTypeReferenceName(node, ast.SymbolFlagsAlias, true /*ignoreErrors*/) - // refers to an alias import/export/reexport - by making sure we use the target as an aliasSymbol, - // we ensure the exported symbol is used to refer to the type when it is reserialized later - if aliasSymbol != nil && aliasSymbol != c.unknownSymbol { - resolved := c.resolveAlias(aliasSymbol) - if resolved != nil && resolved.Flags&ast.SymbolFlagsTypeAlias != 0 { - newAliasSymbol = resolved - aliasTypeArguments = c.getTypeArgumentsFromNode(node) - } - } - } - var newAlias *TypeAlias - if newAliasSymbol != nil { - newAlias = &TypeAlias{symbol: newAliasSymbol, typeArguments: aliasTypeArguments} - } - return c.getTypeAliasInstantiation(symbol, c.getTypeArgumentsFromNode(node), newAlias) - } - if c.checkNoTypeArguments(node, symbol) { - return t - } - return c.errorType -} - -func (c *Checker) getTypeAliasInstantiation(symbol *ast.Symbol, typeArguments []*Type, alias *TypeAlias) *Type { - t := c.getDeclaredTypeOfSymbol(symbol) - if t == c.intrinsicMarkerType { - if typeKind, ok := intrinsicTypeKinds[symbol.Name]; ok && len(typeArguments) == 1 { - switch typeKind { - case IntrinsicTypeKindNoInfer: - return c.getNoInferType(typeArguments[0]) - default: - return c.getStringMappingType(symbol, typeArguments[0]) - } - } - } - links := c.typeAliasLinks.Get(symbol) - typeParameters := links.typeParameters - key := getTypeAliasInstantiationKey(typeArguments, alias) - instantiation := links.instantiations[key] - if instantiation == nil { - mapper := newTypeMapper(typeParameters, c.fillMissingTypeArguments(typeArguments, typeParameters, c.getMinTypeArgumentCount(typeParameters), ast.IsInJSFile(symbol.ValueDeclaration))) - instantiation = c.instantiateTypeWithAlias(t, mapper, alias) - links.instantiations[key] = instantiation - } - return instantiation -} - -func isLocalTypeAlias(symbol *ast.Symbol) bool { - declaration := core.Find(symbol.Declarations, isTypeAlias) - return declaration != nil && ast.GetContainingFunction(declaration) != nil -} - -func (c *Checker) getDeclaredTypeOfSymbol(symbol *ast.Symbol) *Type { - result := c.tryGetDeclaredTypeOfSymbol(symbol) - if result == nil { - result = c.errorType - } - return result -} - -func (c *Checker) tryGetDeclaredTypeOfSymbol(symbol *ast.Symbol) *Type { - switch { - case symbol.Flags&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface) != 0: - return c.getDeclaredTypeOfClassOrInterface(symbol) - case symbol.Flags&ast.SymbolFlagsTypeParameter != 0: - return c.getDeclaredTypeOfTypeParameter(symbol) - case symbol.Flags&ast.SymbolFlagsTypeAlias != 0: - return c.getDeclaredTypeOfTypeAlias(symbol) - case symbol.Flags&ast.SymbolFlagsEnum != 0: - return c.getDeclaredTypeOfEnum(symbol) - case symbol.Flags&ast.SymbolFlagsEnumMember != 0: - return c.getDeclaredTypeOfEnumMember(symbol) - case symbol.Flags&ast.SymbolFlagsAlias != 0: - return c.getDeclaredTypeOfAlias(symbol) - } - return nil -} - -func getTypeReferenceName(node *ast.Node) *ast.Node { - switch node.Kind { - case ast.KindTypeReference: - return node.AsTypeReference().TypeName - case ast.KindExpressionWithTypeArguments: - // We only support expressions that are simple qualified names. For other - // expressions this produces nil - expr := node.AsExpressionWithTypeArguments().Expression - if ast.IsEntityNameExpression(expr) { - return expr - } - } - return nil -} - -func (c *Checker) getAliasForTypeNode(node *ast.Node) *TypeAlias { - symbol := c.getAliasSymbolForTypeNode(node) - if symbol != nil { - return &TypeAlias{symbol: symbol, typeArguments: c.getTypeArgumentsForAliasSymbol(symbol)} - } - return nil -} - -func (c *Checker) getAliasSymbolForTypeNode(node *ast.Node) *ast.Symbol { - host := node.Parent - for ast.IsParenthesizedTypeNode(host) || ast.IsTypeOperatorNode(host) && host.AsTypeOperatorNode().Operator == ast.KindReadonlyKeyword { - host = host.Parent - } - if isTypeAlias(host) { - return c.getSymbolOfDeclaration(host) - } - return nil -} - -func (c *Checker) getTypeArgumentsForAliasSymbol(symbol *ast.Symbol) []*Type { - if symbol != nil { - return c.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol) - } - return nil -} - -func (c *Checker) getOuterTypeParametersOfClassOrInterface(symbol *ast.Symbol) []*Type { - declaration := symbol.ValueDeclaration - if symbol.Flags&(ast.SymbolFlagsClass|ast.SymbolFlagsFunction) == 0 { - declaration = core.Find(symbol.Declarations, func(d *ast.Node) bool { - if ast.IsInterfaceDeclaration(d) { - return true - } - if !ast.IsVariableDeclaration(d) { - return false - } - initializer := d.Initializer() - return initializer != nil && ast.IsFunctionExpressionOrArrowFunction(initializer) - }) - } - debug.AssertIsDefined(declaration, "Class was missing valueDeclaration -OR- non-class had no interface declarations") - return c.getOuterTypeParameters(declaration, false /*includeThisTypes*/) -} - -// Return the outer type parameters of a node or undefined if the node has no outer type parameters. -func (c *Checker) getOuterTypeParameters(node *ast.Node, includeThisTypes bool) []*Type { - for { - node = node.Parent - if node == nil { - return nil - } - kind := node.Kind - switch kind { - case ast.KindClassDeclaration, ast.KindClassExpression, ast.KindInterfaceDeclaration, ast.KindCallSignature, ast.KindConstructSignature, - ast.KindMethodSignature, ast.KindFunctionType, ast.KindConstructorType, ast.KindFunctionDeclaration, - ast.KindMethodDeclaration, ast.KindFunctionExpression, ast.KindArrowFunction, ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration, ast.KindMappedType, - ast.KindConditionalType: - outerTypeParameters := c.getOuterTypeParameters(node, includeThisTypes) - if (kind == ast.KindFunctionExpression || kind == ast.KindArrowFunction || ast.IsObjectLiteralMethod(node)) && c.isContextSensitive(node) { - signature := core.FirstOrNil(c.getSignaturesOfType(c.getTypeOfSymbol(c.getSymbolOfDeclaration(node)), SignatureKindCall)) - if signature != nil && len(signature.typeParameters) != 0 { - return append(outerTypeParameters, signature.typeParameters...) - } - } - if kind == ast.KindMappedType { - return append(outerTypeParameters, c.getDeclaredTypeOfTypeParameter(c.getSymbolOfDeclaration((node.AsMappedTypeNode().TypeParameter)))) - } - if kind == ast.KindConditionalType { - return append(outerTypeParameters, c.getInferTypeParameters(node)...) - } - outerAndOwnTypeParameters := c.appendTypeParameters(outerTypeParameters, node.TypeParameters()) - var thisType *Type - if includeThisTypes && (kind == ast.KindClassDeclaration || kind == ast.KindClassExpression || kind == ast.KindInterfaceDeclaration) { - thisType = c.getDeclaredTypeOfClassOrInterface(c.getSymbolOfDeclaration(node)).AsInterfaceType().thisType - } - if thisType != nil { - return append(outerAndOwnTypeParameters, thisType) - } - return outerAndOwnTypeParameters - } - } -} - -func (c *Checker) getInferTypeParameters(node *ast.Node) []*Type { - var result []*Type - for _, symbol := range node.Locals() { - if symbol.Flags&ast.SymbolFlagsTypeParameter != 0 { - result = append(result, c.getDeclaredTypeOfSymbol(symbol)) - } - } - return result -} - -// 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 *ast.Symbol) []*Type { - return c.appendLocalTypeParametersOfClassOrInterfaceOrTypeAlias(nil, symbol) -} - -func (c *Checker) appendLocalTypeParametersOfClassOrInterfaceOrTypeAlias(types []*Type, symbol *ast.Symbol) []*Type { - for _, node := range symbol.Declarations { - if ast.NodeKindIs(node, ast.KindInterfaceDeclaration, ast.KindClassDeclaration, ast.KindClassExpression) || isTypeAlias(node) { - types = c.appendTypeParameters(types, node.TypeParameters()) - } - } - return types -} - -// 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 []*ast.Node) []*Type { - for _, declaration := range declarations { - typeParameters = core.AppendIfUnique(typeParameters, c.getDeclaredTypeOfTypeParameter(c.getSymbolOfDeclaration(declaration))) - } - return typeParameters -} - -func (c *Checker) getDeclaredTypeOfTypeParameter(symbol *ast.Symbol) *Type { - links := c.declaredTypeLinks.Get(symbol) - if links.declaredType == nil { - links.declaredType = c.newTypeParameter(symbol) - } - return links.declaredType -} - -func (c *Checker) getDeclaredTypeOfTypeAlias(symbol *ast.Symbol) *Type { - links := c.typeAliasLinks.Get(symbol) - if links.declaredType == nil { - // Note that we use the links object as the target here because the symbol object is used as the unique - // identity for resolution of the 'type' property in SymbolLinks. - if !c.pushTypeResolution(symbol, TypeSystemPropertyNameDeclaredType) { - return c.errorType - } - declaration := core.Find(symbol.Declarations, ast.IsTypeOrJSTypeAliasDeclaration) - typeNode := declaration.AsTypeAliasDeclaration().Type - t := c.getTypeFromTypeNode(typeNode) - if c.popTypeResolution() { - typeParameters := c.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol) - if len(typeParameters) != 0 { - // Initialize the instantiation cache for generic type aliases. The declared type corresponds to - // an instantiation of the type alias with the type parameters supplied as type arguments. - links.typeParameters = typeParameters - links.instantiations = make(map[string]*Type) - links.instantiations[getTypeListKey(typeParameters)] = t - } - if t == c.intrinsicMarkerType && symbol.Name == "BuiltinIteratorReturn" { - t = c.getBuiltinIteratorReturnType() - } - } else { - errorNode := declaration.Name() - if errorNode == nil { - errorNode = declaration - } - c.error(errorNode, diagnostics.Type_alias_0_circularly_references_itself, c.symbolToString(symbol)) - t = c.errorType - } - if links.declaredType == nil { - links.declaredType = t - } - } - return links.declaredType -} - -func (c *Checker) getDeclaredTypeOfEnum(symbol *ast.Symbol) *Type { - links := c.declaredTypeLinks.Get(symbol) - if !(links.declaredType != nil) { - var memberTypeList []*Type - for _, declaration := range symbol.Declarations { - if declaration.Kind == ast.KindEnumDeclaration { - for _, member := range declaration.Members() { - if c.hasBindableName(member) { - memberSymbol := c.getSymbolOfDeclaration(member) - value := c.getEnumMemberValue(member).Value - var memberType *Type - if value != nil { - memberType = c.getEnumLiteralType(value, symbol, memberSymbol) - } else { - memberType = c.createComputedEnumType(memberSymbol) - } - c.declaredTypeLinks.Get(memberSymbol).declaredType = c.getFreshTypeOfLiteralType(memberType) - memberTypeList = append(memberTypeList, memberType) - } - } - } - } - var enumType *Type - if len(memberTypeList) != 0 { - enumType = c.getUnionTypeEx(memberTypeList, UnionReductionLiteral, &TypeAlias{symbol: symbol}, nil /*origin*/) - } else { - enumType = c.createComputedEnumType(symbol) - } - if enumType.flags&TypeFlagsUnion != 0 { - enumType.flags |= TypeFlagsEnumLiteral - enumType.symbol = symbol - } - links.declaredType = enumType - } - return links.declaredType -} - -func (c *Checker) getEnumMemberValue(node *ast.Node) evaluator.Result { - c.computeEnumMemberValues(node.Parent) - return c.enumMemberLinks.Get(node).value -} - -func (c *Checker) createComputedEnumType(symbol *ast.Symbol) *Type { - regularType := c.newLiteralType(TypeFlagsEnum, nil, nil) - regularType.symbol = symbol - freshType := c.newLiteralType(TypeFlagsEnum, nil, regularType) - freshType.symbol = symbol - regularType.AsLiteralType().freshType = freshType - freshType.AsLiteralType().freshType = freshType - return regularType -} - -func (c *Checker) getDeclaredTypeOfEnumMember(symbol *ast.Symbol) *Type { - links := c.declaredTypeLinks.Get(symbol) - if !(links.declaredType != nil) { - enumType := c.getDeclaredTypeOfEnum(c.getParentOfSymbol(symbol)) - if links.declaredType == nil { - links.declaredType = enumType - } - } - return links.declaredType -} - -func (c *Checker) computeEnumMemberValues(node *ast.Node) { - nodeLinks := c.nodeLinks.Get(node) - if !(nodeLinks.flags&NodeCheckFlagsEnumValuesComputed != 0) { - nodeLinks.flags |= NodeCheckFlagsEnumValuesComputed - var autoValue jsnum.Number - var previous *ast.Node - for _, member := range node.Members() { - result := c.computeEnumMemberValue(member, autoValue, previous) - c.enumMemberLinks.Get(member).value = result - if value, isNumber := result.Value.(jsnum.Number); isNumber { - autoValue = value + 1 - } else { - autoValue = jsnum.NaN() - } - previous = member - } - } -} - -func (c *Checker) computeEnumMemberValue(member *ast.Node, autoValue jsnum.Number, previous *ast.Node) evaluator.Result { - if ast.IsComputedNonLiteralName(member.Name()) { - c.error(member.Name(), diagnostics.Computed_property_names_are_not_allowed_in_enums) - } else if ast.IsBigIntLiteral(member.Name()) { - c.error(member.Name(), diagnostics.An_enum_member_cannot_have_a_numeric_name) - } else { - text := ast.GetTextOfPropertyName(member.Name()) - if isNumericLiteralName(text) && !isInfinityOrNaNString(text) { - c.error(member.Name(), diagnostics.An_enum_member_cannot_have_a_numeric_name) - } - } - if member.Initializer() != nil { - return c.computeConstantEnumMemberValue(member) - } - // In ambient non-const numeric enum declarations, enum members without initializers are - // considered computed members (as opposed to having auto-incremented values). - if member.Parent.Flags&ast.NodeFlagsAmbient != 0 && !ast.IsEnumConst(member.Parent) { - return evaluator.NewResult(nil, false, false, false) - } - // If the member declaration specifies no value, the member is considered a constant enum member. - // If the member is the first member in the enum declaration, it is assigned the value zero. - // Otherwise, it is assigned the value of the immediately preceding member plus one, and an error - // occurs if the immediately preceding member is not a constant enum member. - if autoValue.IsNaN() { - c.error(member.Name(), diagnostics.Enum_member_must_have_initializer) - return evaluator.NewResult(nil, false, false, false) - } - if c.compilerOptions.GetIsolatedModules() && previous != nil && previous.AsEnumMember().Initializer != nil { - prevValue := c.getEnumMemberValue(previous) - _, prevIsNum := prevValue.Value.(jsnum.Number) - if !prevIsNum || prevValue.ResolvedOtherFiles { - c.error(member.Name(), diagnostics.Enum_member_following_a_non_literal_numeric_member_must_have_an_initializer_when_isolatedModules_is_enabled) - } - } - return evaluator.NewResult(autoValue, false, false, false) -} - -func (c *Checker) computeConstantEnumMemberValue(member *ast.Node) evaluator.Result { - isConstEnum := ast.IsEnumConst(member.Parent) - initializer := member.Initializer() - result := c.evaluate(initializer, member) - switch { - case result.Value != nil: - if isConstEnum { - if numValue, isNumber := result.Value.(jsnum.Number); isNumber && (numValue.IsInf() || numValue.IsNaN()) { - c.error(initializer, core.IfElse(numValue.IsNaN(), - diagnostics.X_const_enum_member_initializer_was_evaluated_to_disallowed_value_NaN, - diagnostics.X_const_enum_member_initializer_was_evaluated_to_a_non_finite_value)) - } - } - if c.compilerOptions.GetIsolatedModules() { - if _, isString := result.Value.(string); isString && !result.IsSyntacticallyString { - memberName := member.Parent.Name().Text() + "." + member.Name().Text() - c.error(initializer, diagnostics.X_0_has_a_string_type_but_must_have_syntactically_recognizable_string_syntax_when_isolatedModules_is_enabled, memberName) - } - } - case isConstEnum: - c.error(initializer, diagnostics.X_const_enum_member_initializers_must_be_constant_expressions) - case member.Parent.Flags&ast.NodeFlagsAmbient != 0: - c.error(initializer, diagnostics.In_ambient_enum_declarations_member_initializer_must_be_constant_expression) - default: - c.checkTypeAssignableTo(c.checkExpression(initializer), c.numberType, initializer, diagnostics.Type_0_is_not_assignable_to_type_1_as_required_for_computed_enum_member_values) - } - return result -} - -func (c *Checker) evaluateEntity(expr *ast.Node, location *ast.Node) evaluator.Result { - switch expr.Kind { - case ast.KindIdentifier, ast.KindPropertyAccessExpression: - symbol := c.resolveEntityName(expr, ast.SymbolFlagsValue, true /*ignoreErrors*/, false, nil) - if symbol == nil { - return evaluator.NewResult(nil, false, false, false) - } - if expr.Kind == ast.KindIdentifier { - if isInfinityOrNaNString(expr.Text()) && (symbol == c.getGlobalSymbol(expr.Text(), ast.SymbolFlagsValue, nil /*diagnostic*/)) { - // Technically we resolved a global lib file here, but the decision to treat this as numeric - // is more predicated on the fact that the single-file resolution *didn't* resolve to a - // different meaning of `Infinity` or `NaN`. Transpilers handle this no problem. - return evaluator.NewResult(jsnum.FromString(expr.Text()), false, false, false) - } - } - if symbol.Flags&ast.SymbolFlagsEnumMember != 0 { - if location != nil { - return c.evaluateEnumMember(expr, symbol, location) - } - return c.getEnumMemberValue(symbol.ValueDeclaration) - } - if c.isConstantVariable(symbol) { - declaration := symbol.ValueDeclaration - if declaration != nil && ast.IsVariableDeclaration(declaration) && declaration.Type() == nil && declaration.Initializer() != nil && - (location == nil || declaration != location && c.isBlockScopedNameDeclaredBeforeUse(declaration, location)) { - result := c.evaluate(declaration.Initializer(), declaration) - if location != nil && ast.GetSourceFileOfNode(location) != ast.GetSourceFileOfNode(declaration) { - return evaluator.NewResult(result.Value, false, true, true) - } - return evaluator.NewResult(result.Value, result.IsSyntacticallyString, result.ResolvedOtherFiles, true /*hasExternalReferences*/) - } - } - return evaluator.NewResult(nil, false, false, false) - case ast.KindElementAccessExpression: - root := expr.Expression() - if ast.IsEntityNameExpression(root) && ast.IsStringLiteralLike(expr.AsElementAccessExpression().ArgumentExpression) { - rootSymbol := c.resolveEntityName(root, ast.SymbolFlagsValue, true /*ignoreErrors*/, false, nil) - if rootSymbol != nil && rootSymbol.Flags&ast.SymbolFlagsEnum != 0 { - name := expr.AsElementAccessExpression().ArgumentExpression.Text() - member := rootSymbol.Exports[name] - if member != nil { - debug.Assert(ast.GetSourceFileOfNode(member.ValueDeclaration) == ast.GetSourceFileOfNode(rootSymbol.ValueDeclaration)) - if location != nil { - return c.evaluateEnumMember(expr, member, location) - } - return c.getEnumMemberValue(member.ValueDeclaration) - } - } - } - return evaluator.NewResult(nil, false, false, false) - } - panic("Unhandled case in evaluateEntity") -} - -func (c *Checker) evaluateEnumMember(expr *ast.Node, symbol *ast.Symbol, location *ast.Node) evaluator.Result { - declaration := symbol.ValueDeclaration - if declaration == nil || declaration == location { - c.error(expr, diagnostics.Property_0_is_used_before_being_assigned, c.symbolToString(symbol)) - return evaluator.NewResult(nil, false, false, false) - } - if !c.isBlockScopedNameDeclaredBeforeUse(declaration, location) { - c.error(expr, diagnostics.A_member_initializer_in_a_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_enums) - return evaluator.NewResult(jsnum.Number(0), false, false, false) - } - value := c.getEnumMemberValue(declaration) - if location.Parent != declaration.Parent { - return evaluator.NewResult(value.Value, value.IsSyntacticallyString, value.ResolvedOtherFiles, true /*hasExternalReferences*/) - } - return value -} - -func (c *Checker) getDeclaredTypeOfAlias(symbol *ast.Symbol) *Type { - links := c.declaredTypeLinks.Get(symbol) - if links.declaredType == nil { - links.declaredType = c.getDeclaredTypeOfSymbol(c.resolveAlias(symbol)) - } - return links.declaredType -} - -func (c *Checker) getTypeFromTypeQueryNode(node *ast.Node) *Type { - links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { - // TypeScript 1.0 spec (April 2014): 3.6.3 - // The expression is processed as an identifier expression (section 4.3) - // or property access expression(section 4.10), - // the widened type(section 3.9) of which becomes the result. - t := c.checkExpressionWithTypeArguments(node) - links.resolvedType = c.getRegularTypeOfLiteralType(c.getWidenedType(t)) - } - return links.resolvedType -} - -func (c *Checker) getTypeFromArrayOrTupleTypeNode(node *ast.Node) *Type { - links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { - target := c.getArrayOrTupleTargetType(node) - if target == c.emptyGenericType { - links.resolvedType = c.emptyObjectType - } else if !(node.Kind == ast.KindTupleType && core.Some(node.AsTupleTypeNode().Elements.Nodes, c.isVariadicTupleElement)) && c.isDeferredTypeReferenceNode(node, false) { - if node.Kind == ast.KindTupleType && len(node.AsTupleTypeNode().Elements.Nodes) == 0 { - links.resolvedType = target - } else { - links.resolvedType = c.createDeferredTypeReference(target, node, nil /*mapper*/, nil /*alias*/) - } - } else { - var elementTypes []*Type - if node.Kind == ast.KindArrayType { - elementTypes = []*Type{c.getTypeFromTypeNode(node.AsArrayTypeNode().ElementType)} - } else { - elementTypes = core.Map(node.AsTupleTypeNode().Elements.Nodes, c.getTypeFromTypeNode) - } - links.resolvedType = c.createNormalizedTypeReference(target, elementTypes) - } - } - return links.resolvedType -} - -func (c *Checker) isVariadicTupleElement(node *ast.Node) bool { - return c.getTupleElementFlags(node)&ElementFlagsVariadic != 0 -} - -func (c *Checker) getArrayOrTupleTargetType(node *ast.Node) *Type { - readonly := c.isReadonlyTypeOperator(node.Parent) - elementType := c.getArrayElementTypeNode(node) - if elementType != nil { - if readonly { - return c.globalReadonlyArrayType - } - return c.globalArrayType - } - return c.getTupleTargetType(core.Map(node.AsTupleTypeNode().Elements.Nodes, c.getTupleElementInfo), readonly) -} - -func (c *Checker) isReadonlyTypeOperator(node *ast.Node) bool { - return ast.IsTypeOperatorNode(node) && node.AsTypeOperatorNode().Operator == ast.KindReadonlyKeyword -} - -func (c *Checker) getTypeFromNamedTupleTypeNode(node *ast.Node) *Type { - links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { - if node.AsNamedTupleMember().DotDotDotToken != nil { - links.resolvedType = c.getTypeFromRestTypeNode(node) - } else { - links.resolvedType = c.addOptionalityEx(c.getTypeFromTypeNode(node.Type()), true /*isProperty*/, node.AsNamedTupleMember().QuestionToken != nil) - } - } - return links.resolvedType -} - -func (c *Checker) getTypeFromRestTypeNode(node *ast.Node) *Type { - typeNode := node.Type() - elementTypeNode := c.getArrayElementTypeNode(typeNode) - if elementTypeNode != nil { - typeNode = elementTypeNode - } - return c.getTypeFromTypeNode(typeNode) -} - -func (c *Checker) getArrayElementTypeNode(node *ast.Node) *ast.Node { - switch node.Kind { - case ast.KindParenthesizedType: - return c.getArrayElementTypeNode(node.AsParenthesizedTypeNode().Type) - case ast.KindTupleType: - if len(node.AsTupleTypeNode().Elements.Nodes) == 1 { - node = node.AsTupleTypeNode().Elements.Nodes[0] - if node.Kind == ast.KindRestType { - return c.getArrayElementTypeNode(node.AsRestTypeNode().Type) - } - if node.Kind == ast.KindNamedTupleMember && node.AsNamedTupleMember().DotDotDotToken != nil { - return c.getArrayElementTypeNode(node.AsNamedTupleMember().Type) - } - } - case ast.KindArrayType: - return node.AsArrayTypeNode().ElementType - } - return nil -} - -func (c *Checker) getTypeFromOptionalTypeNode(node *ast.Node) *Type { - return c.addOptionalityEx(c.getTypeFromTypeNode(node.AsOptionalTypeNode().Type), true /*isProperty*/, true /*isOptional*/) -} - -func (c *Checker) getTypeFromUnionTypeNode(node *ast.Node) *Type { - links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { - alias := c.getAliasForTypeNode(node) - links.resolvedType = c.getUnionTypeEx(core.Map(node.AsUnionTypeNode().Types.Nodes, c.getTypeFromTypeNode), UnionReductionLiteral, alias, nil /*origin*/) - } - return links.resolvedType -} - -func (c *Checker) getTypeFromIntersectionTypeNode(node *ast.Node) *Type { - links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { - alias := c.getAliasForTypeNode(node) - types := core.Map(node.AsIntersectionTypeNode().Types.Nodes, c.getTypeFromTypeNode) - // We perform no supertype reduction for X & {} or {} & X, where X is one of string, number, bigint, - // or a pattern literal template type. This enables union types like "a" | "b" | string & {} or - // "aa" | "ab" | `a${string}` which preserve the literal types for purposes of statement completion. - noSupertypeReduction := false - if len(types) == 2 { - emptyIndex := slices.Index(types, c.emptyTypeLiteralType) - if emptyIndex >= 0 { - t := types[1-emptyIndex] - noSupertypeReduction = t.flags&(TypeFlagsString|TypeFlagsNumber|TypeFlagsBigInt) != 0 || t.flags&TypeFlagsTemplateLiteral != 0 && c.isPatternLiteralType(t) - } - } - links.resolvedType = c.getIntersectionTypeEx(types, core.IfElse(noSupertypeReduction, IntersectionFlagsNoSupertypeReduction, 0), alias) - } - return links.resolvedType -} - -func (c *Checker) getTypeFromTemplateTypeNode(node *ast.Node) *Type { - links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { - spans := node.AsTemplateLiteralTypeNode().TemplateSpans - texts := make([]string, len(spans.Nodes)+1) - types := make([]*Type, len(spans.Nodes)) - texts[0] = node.AsTemplateLiteralTypeNode().Head.Text() - for i, span := range spans.Nodes { - texts[i+1] = span.AsTemplateLiteralTypeSpan().Literal.Text() - types[i] = c.getTypeFromTypeNode(span.AsTemplateLiteralTypeSpan().Type) - } - links.resolvedType = c.getTemplateLiteralType(texts, types) - } - return links.resolvedType -} - -func (c *Checker) getTypeFromMappedTypeNode(node *ast.Node) *Type { - links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { - t := c.newObjectType(ObjectFlagsMapped, node.Symbol()) - t.AsMappedType().declaration = node.AsMappedTypeNode() - t.alias = c.getAliasForTypeNode(node) - links.resolvedType = t - // Eagerly resolve the constraint type which forces an error if the constraint type circularly - // references itself through one or more type aliases. - c.getConstraintTypeFromMappedType(t) - } - return links.resolvedType -} - -func (c *Checker) getTypeFromConditionalTypeNode(node *ast.Node) *Type { - links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { - checkType := c.getTypeFromTypeNode(node.AsConditionalTypeNode().CheckType) - alias := c.getAliasForTypeNode(node) - allOuterTypeParameters := c.getOuterTypeParameters(node, true /*includeThisTypes*/) - var outerTypeParameters []*Type - if alias != nil && len(alias.typeArguments) != 0 { - outerTypeParameters = allOuterTypeParameters - } else { - outerTypeParameters = core.Filter(allOuterTypeParameters, func(tp *Type) bool { return c.isTypeParameterPossiblyReferenced(tp, node) }) - } - root := &ConditionalRoot{ - node: node.AsConditionalTypeNode(), - checkType: checkType, - extendsType: c.getTypeFromTypeNode(node.AsConditionalTypeNode().ExtendsType), - isDistributive: checkType.flags&TypeFlagsTypeParameter != 0, - inferTypeParameters: c.getInferTypeParameters(node), - outerTypeParameters: outerTypeParameters, - instantiations: nil, - alias: alias, - } - links.resolvedType = c.getConditionalType(root, nil /*mapper*/, false /*forConstraint*/, nil) - if outerTypeParameters != nil { - root.instantiations = make(map[string]*Type) - root.instantiations[getTypeListKey(outerTypeParameters)] = links.resolvedType - } - } - return links.resolvedType -} - -func (c *Checker) getConditionalType(root *ConditionalRoot, mapper *TypeMapper, forConstraint bool, alias *TypeAlias) *Type { - var result *Type - var extraTypes []*Type - tailCount := 0 - // We loop here for an immediately nested conditional type in the false position, effectively treating - // types of the form 'A extends B ? X : C extends D ? Y : E extends F ? Z : ...' as a single construct for - // purposes of resolution. We also loop here when resolution of a conditional type ends in resolution of - // another (or, through recursion, possibly the same) conditional type. In the potentially tail-recursive - // cases we increment the tail recursion counter and stop after 1000 iterations. - for { - if tailCount == 1000 { - c.error(c.currentNode, diagnostics.Type_instantiation_is_excessively_deep_and_possibly_infinite) - return c.errorType - } - checkType := c.instantiateType(c.getActualTypeVariable(root.checkType), mapper) - extendsType := c.instantiateType(root.extendsType, mapper) - if checkType == c.errorType || extendsType == c.errorType { - return c.errorType - } - if checkType == c.wildcardType || extendsType == c.wildcardType { - return c.wildcardType - } - checkTypeNode := ast.SkipTypeParentheses(root.node.CheckType) - extendsTypeNode := ast.SkipTypeParentheses(root.node.ExtendsType) - // When the check and extends types are simple tuple types of the same arity, we defer resolution of the - // conditional type when any tuple elements are generic. This is such that non-distributable conditional - // types can be written `[X] extends [Y] ? ...` and be deferred similarly to `X extends Y ? ...`. - checkTuples := c.isSimpleTupleType(checkTypeNode) && c.isSimpleTupleType(extendsTypeNode) && len(checkTypeNode.AsTupleTypeNode().Elements.Nodes) == len(extendsTypeNode.AsTupleTypeNode().Elements.Nodes) - checkTypeDeferred := c.isDeferredType(checkType, checkTuples) - var combinedMapper *TypeMapper - if len(root.inferTypeParameters) != 0 { - // When we're looking at making an inference for an infer type, when we get its constraint, it'll automagically be - // instantiated with the context, so it doesn't need the mapper for the inference context - however the constraint - // may refer to another _root_, _uncloned_ `infer` type parameter [1], or to something mapped by `mapper` [2]. - // [1] Eg, if we have `Foo` and `Foo` - `B` is constrained to `T`, which, in turn, has been instantiated - // as `number` - // Conversely, if we have `Foo`, `B` is still constrained to `T` and `T` is instantiated as `A` - // [2] Eg, if we have `Foo` and `Foo` where `Q` is mapped by `mapper` into `number` - `B` is constrained to `T` - // which is in turn instantiated as `Q`, which is in turn instantiated as `number`. - // So we need to: - // * combine `context.nonFixingMapper` with `mapper` so their constraints can be instantiated in the context of `mapper` (otherwise they'd only get inference context information) - // * incorporate all of the component mappers into the combined mapper for the true and false members - // This means we have two mappers that need applying: - // * The original `mapper` used to create this conditional - // * The mapper that maps the infer type parameter to its inference result (`context.mapper`) - context := c.newInferenceContext(root.inferTypeParameters, nil /*signature*/, InferenceFlagsNone, nil) - if mapper != nil { - context.nonFixingMapper = c.combineTypeMappers(context.nonFixingMapper, mapper) - } - if !checkTypeDeferred { - // We don't want inferences from constraints as they may cause us to eagerly resolve the - // conditional type instead of deferring resolution. Also, we always want strict function - // types rules (i.e. proper contravariance) for inferences. - c.inferTypes(context.inferences, checkType, extendsType, InferencePriorityNoConstraints|InferencePriorityAlwaysStrict, false) - } - // It's possible for 'infer T' type parameters to be given uninstantiated constraints when the - // those type parameters are used in type references (see getInferredTypeParameterConstraint). For - // that reason we need context.mapper to be first in the combined mapper. See #42636 for examples. - if mapper != nil { - combinedMapper = c.combineTypeMappers(context.mapper, mapper) - } else { - combinedMapper = context.mapper - } - } - // Instantiate the extends type including inferences for 'infer T' type parameters - var inferredExtendsType *Type - if combinedMapper != nil { - inferredExtendsType = c.instantiateType(root.extendsType, combinedMapper) - } else { - inferredExtendsType = extendsType - } - // We attempt to resolve the conditional type only when the check and extends types are non-generic - if !checkTypeDeferred && !c.isDeferredType(inferredExtendsType, checkTuples) { - // Return falseType for a definitely false extends check. We check an instantiations of the two - // types with type parameters mapped to the wildcard type, the most permissive instantiations - // possible (the wildcard type is assignable to and from all types). If those are not related, - // then no instantiations will be and we can just return the false branch type. - if inferredExtendsType.flags&TypeFlagsAnyOrUnknown == 0 && (checkType.flags&TypeFlagsAny != 0 || !c.isTypeAssignableTo(c.getPermissiveInstantiation(checkType), c.getPermissiveInstantiation(inferredExtendsType))) { - // Return union of trueType and falseType for 'any' since it matches anything. Furthermore, for a - // distributive conditional type applied to the constraint of a type variable, include trueType if - // there are possible values of the check type that are also possible values of the extends type. - // We use a reverse assignability check as it is less expensive than the comparable relationship - // and avoids false positives of a non-empty intersection check. - if checkType.flags&TypeFlagsAny != 0 || forConstraint && inferredExtendsType.flags&TypeFlagsNever == 0 && someType(c.getPermissiveInstantiation(inferredExtendsType), func(t *Type) bool { - return c.isTypeAssignableTo(t, c.getPermissiveInstantiation(checkType)) - }) { - extraTypes = append(extraTypes, c.instantiateType(c.getTypeFromTypeNode(root.node.TrueType), core.OrElse(combinedMapper, mapper))) - } - // If falseType is an immediately nested conditional type that isn't distributive or has an - // identical checkType, switch to that type and loop. - falseType := c.getTypeFromTypeNode(root.node.FalseType) - if falseType.flags&TypeFlagsConditional != 0 { - newRoot := falseType.AsConditionalType().root - if newRoot.node.Parent == root.node.AsNode() && (!newRoot.isDistributive || newRoot.checkType == root.checkType) { - root = newRoot - continue - } - if newRoot, newRootMapper := c.getTailRecursionRoot(falseType, mapper); newRoot != nil { - root = newRoot - mapper = newRootMapper - alias = nil - if newRoot.alias != nil { - tailCount++ - } - continue - } - } - result = c.instantiateType(falseType, mapper) - break - } - // Return trueType for a definitely true extends check. We check instantiations of the two - // types with type parameters mapped to their restrictive form, i.e. a form of the type parameter - // that has no constraint. This ensures that, for example, the type - // type Foo = T extends { x: string } ? string : number - // doesn't immediately resolve to 'string' instead of being deferred. - if inferredExtendsType.flags&TypeFlagsAnyOrUnknown != 0 || c.isTypeAssignableTo(c.getRestrictiveInstantiation(checkType), c.getRestrictiveInstantiation(inferredExtendsType)) { - trueType := c.getTypeFromTypeNode(root.node.TrueType) - trueMapper := core.OrElse(combinedMapper, mapper) - if newRoot, newRootMapper := c.getTailRecursionRoot(trueType, trueMapper); newRoot != nil { - root = newRoot - mapper = newRootMapper - alias = nil - if newRoot.alias != nil { - tailCount++ - } - continue - } - result = c.instantiateType(trueType, trueMapper) - break - } - } - // Return a deferred type for a check that is neither definitely true nor definitely false - result = c.newConditionalType(root, mapper, combinedMapper) - if alias != nil { - result.alias = alias - } else { - result.alias = c.instantiateTypeAlias(root.alias, mapper) - } - break - } - if extraTypes != nil { - return c.getUnionType(append(extraTypes, result)) - } - return result -} - -// We tail-recurse for generic conditional types that (a) have not already been evaluated and cached, and -// (b) are non distributive, have a check type that is unaffected by instantiation, or have a non-union check -// type. Note that recursion is possible only through aliased conditional types, so we only increment the tail -// recursion counter for those. -func (c *Checker) getTailRecursionRoot(newType *Type, newMapper *TypeMapper) (*ConditionalRoot, *TypeMapper) { - if newType.flags&TypeFlagsConditional != 0 && newMapper != nil { - newRoot := newType.AsConditionalType().root - if len(newRoot.outerTypeParameters) != 0 { - typeParamMapper := c.combineTypeMappers(newType.AsConditionalType().mapper, newMapper) - typeArguments := core.Map(newRoot.outerTypeParameters, func(t *Type) *Type { return typeParamMapper.Map(t) }) - newRootMapper := newTypeMapper(newRoot.outerTypeParameters, typeArguments) - var newCheckType *Type - if newRoot.isDistributive { - newCheckType = newRootMapper.Map(newRoot.checkType) - } - if newCheckType == nil || newCheckType == newRoot.checkType || newCheckType.flags&(TypeFlagsUnion|TypeFlagsNever) == 0 { - return newRoot, newRootMapper - } - } - } - return nil, nil -} - -func (c *Checker) isSimpleTupleType(node *ast.Node) bool { - return ast.IsTupleTypeNode(node) && len(node.AsTupleTypeNode().Elements.Nodes) > 0 && !core.Some(node.AsTupleTypeNode().Elements.Nodes, func(e *ast.Node) bool { - return ast.IsOptionalTypeNode(e) || ast.IsRestTypeNode(e) || ast.IsNamedTupleMember(e) && (e.AsNamedTupleMember().QuestionToken != nil || e.AsNamedTupleMember().DotDotDotToken != nil) - }) -} - -func (c *Checker) isDeferredType(t *Type, checkTuples bool) bool { - return c.isGenericType(t) || checkTuples && isTupleType(t) && core.Some(c.getElementTypes(t), c.isGenericType) -} - -func (c *Checker) getPermissiveInstantiation(t *Type) *Type { - if t.flags&(TypeFlagsPrimitive|TypeFlagsAnyOrUnknown|TypeFlagsNever) != 0 { - return t - } - key := CachedTypeKey{kind: CachedTypeKindPermissiveInstantiation, typeId: t.id} - if cached := c.cachedTypes[key]; cached != nil { - return cached - } - result := c.instantiateType(t, c.permissiveMapper) - c.cachedTypes[key] = result - return result -} - -func (c *Checker) getRestrictiveInstantiation(t *Type) *Type { - if t.flags&(TypeFlagsPrimitive|TypeFlagsAnyOrUnknown|TypeFlagsNever) != 0 { - return t - } - key := CachedTypeKey{kind: CachedTypeKindRestrictiveInstantiation, typeId: t.id} - if cached := c.cachedTypes[key]; cached != nil { - return cached - } - result := c.instantiateType(t, c.restrictiveMapper) - c.cachedTypes[key] = result - // We set the following so we don't attempt to set the restrictive instance of a restrictive instance - // which is redundant - we'll produce new type identities, but all type params have already been mapped. - // This also gives us a way to detect restrictive instances upon comparisons and _disable_ the "distributeive constraint" - // assignability check for them, which is distinctly unsafe, as once you have a restrctive instance, all the type parameters - // are constrained to `unknown` and produce tons of false positives/negatives! - c.cachedTypes[CachedTypeKey{kind: CachedTypeKindRestrictiveInstantiation, typeId: result.id}] = result - return result -} - -func (c *Checker) getRestrictiveTypeParameter(t *Type) *Type { - if t.AsTypeParameter().constraint == nil && c.getConstraintDeclaration(t) == nil || t.AsTypeParameter().constraint == c.noConstraintType { - return t - } - key := CachedTypeKey{kind: CachedTypeKindRestrictiveTypeParameter, typeId: t.id} - if cached := c.cachedTypes[key]; cached != nil { - return cached - } - result := c.newTypeParameter(t.symbol) - result.AsTypeParameter().constraint = c.noConstraintType - c.cachedTypes[key] = result - return result -} - -func (c *Checker) restrictiveMapperWorker(t *Type) *Type { - if t.flags&TypeFlagsTypeParameter != 0 { - return c.getRestrictiveTypeParameter(t) - } - return t -} - -func (c *Checker) permissiveMapperWorker(t *Type) *Type { - if t.flags&TypeFlagsTypeParameter != 0 { - return c.wildcardType - } - return t -} - -func (c *Checker) getTrueTypeFromConditionalType(t *Type) *Type { - d := t.AsConditionalType() - if d.resolvedTrueType == nil { - d.resolvedTrueType = c.instantiateType(c.getTypeFromTypeNode(d.root.node.TrueType), d.mapper) - } - return d.resolvedTrueType -} - -func (c *Checker) getFalseTypeFromConditionalType(t *Type) *Type { - d := t.AsConditionalType() - if d.resolvedFalseType == nil { - d.resolvedFalseType = c.instantiateType(c.getTypeFromTypeNode(d.root.node.FalseType), d.mapper) - } - return d.resolvedFalseType -} - -func (c *Checker) getInferredTrueTypeFromConditionalType(t *Type) *Type { - d := t.AsConditionalType() - if d.resolvedInferredTrueType == nil { - if d.combinedMapper != nil { - d.resolvedInferredTrueType = c.instantiateType(c.getTypeFromTypeNode(d.root.node.TrueType), d.combinedMapper) - } else { - d.resolvedInferredTrueType = c.getTrueTypeFromConditionalType(t) - } - } - return d.resolvedInferredTrueType -} - -func (c *Checker) getTypeFromInferTypeNode(node *ast.Node) *Type { - links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { - links.resolvedType = c.getDeclaredTypeOfTypeParameter(c.getSymbolOfDeclaration(node.AsInferTypeNode().TypeParameter)) - } - return links.resolvedType -} - -func (c *Checker) getTypeFromImportTypeNode(node *ast.Node) *Type { - links := c.typeNodeLinks.Get(node) - if links.resolvedType == nil { - n := node.AsImportTypeNode() - if !ast.IsLiteralImportTypeNode(node) { - c.error(n.Argument, diagnostics.String_literal_expected) - c.symbolNodeLinks.Get(node).resolvedSymbol = c.unknownSymbol - links.resolvedType = c.errorType - return links.resolvedType - } - targetMeaning := core.IfElse(n.IsTypeOf, ast.SymbolFlagsValue, ast.SymbolFlagsType) - // TODO: Future work: support unions/generics/whatever via a deferred import-type - innerModuleSymbol := c.resolveExternalModuleName(node, n.Argument.AsLiteralTypeNode().Literal, false /*ignoreErrors*/) - if innerModuleSymbol == nil { - c.symbolNodeLinks.Get(node).resolvedSymbol = c.unknownSymbol - links.resolvedType = c.errorType - return links.resolvedType - } - moduleSymbol := c.resolveExternalModuleSymbol(innerModuleSymbol, false /*dontResolveAlias*/) - if !ast.NodeIsMissing(n.Qualifier) { - nameChain := c.getIdentifierChain(n.Qualifier) - currentNamespace := moduleSymbol - for i, current := range nameChain { - meaning := ast.SymbolFlagsNamespace - if i == len(nameChain)-1 { - meaning = targetMeaning - } - // typeof a.b.c is normally resolved using `checkExpression` which in turn defers to `checkQualifiedName` - // That, in turn, ultimately uses `getPropertyOfType` on the type of the symbol, which differs slightly from - // the `exports` lookup process that only looks up namespace members which is used for most type references - mergedResolvedSymbol := c.getMergedSymbol(c.resolveSymbol(currentNamespace)) - var symbolFromVariable *ast.Symbol - var symbolFromModule *ast.Symbol - if n.IsTypeOf { - symbolFromVariable = c.getPropertyOfTypeEx(c.getTypeOfSymbol(mergedResolvedSymbol), current.Text(), false /*skipObjectFunctionPropertyAugment*/, true /*includeTypeOnlyMembers*/) - } else { - symbolFromModule = c.getSymbol(c.getExportsOfSymbol(mergedResolvedSymbol), current.Text(), meaning) - } - next := core.OrElse(symbolFromModule, symbolFromVariable) - if next == nil { - c.error(current, diagnostics.Namespace_0_has_no_exported_member_1, c.getFullyQualifiedName(currentNamespace, nil), scanner.DeclarationNameToString(current)) - links.resolvedType = c.errorType - return links.resolvedType - } - c.symbolNodeLinks.Get(current).resolvedSymbol = next - c.symbolNodeLinks.Get(current.Parent).resolvedSymbol = next - currentNamespace = next - } - links.resolvedType = c.resolveImportSymbolType(node, currentNamespace, targetMeaning) - } else { - if moduleSymbol.Flags&targetMeaning != 0 { - links.resolvedType = c.resolveImportSymbolType(node, moduleSymbol, targetMeaning) - } else { - message := core.IfElse(targetMeaning == ast.SymbolFlagsValue, - diagnostics.Module_0_does_not_refer_to_a_value_but_is_used_as_a_value_here, - diagnostics.Module_0_does_not_refer_to_a_type_but_is_used_as_a_type_here_Did_you_mean_typeof_import_0) - c.error(node, message, n.Argument.AsLiteralTypeNode().Literal.Text()) - c.symbolNodeLinks.Get(node).resolvedSymbol = c.unknownSymbol - links.resolvedType = c.errorType - } - } - } - return links.resolvedType -} - -func (c *Checker) getIdentifierChain(node *ast.Node) []*ast.Node { - if ast.IsIdentifier(node) { - return []*ast.Node{node} - } - return append(c.getIdentifierChain(node.AsQualifiedName().Left), node.AsQualifiedName().Right) -} - -func (c *Checker) resolveImportSymbolType(node *ast.Node, symbol *ast.Symbol, meaning ast.SymbolFlags) *Type { - resolvedSymbol := c.resolveSymbol(symbol) - c.symbolNodeLinks.Get(node).resolvedSymbol = resolvedSymbol - if meaning == ast.SymbolFlagsValue { - // intentionally doesn't use resolved symbol so type is cached as expected on the alias - return c.getInstantiationExpressionType(c.getTypeOfSymbol(symbol), node) - } - // getTypeReferenceType doesn't handle aliases - it must get the resolved symbol - return c.getTypeReferenceType(node, resolvedSymbol) -} - -func (c *Checker) createTypeFromGenericGlobalType(genericGlobalType *Type, typeArguments []*Type) *Type { - if genericGlobalType != c.emptyGenericType { - return c.createTypeReference(genericGlobalType, typeArguments) - } - return c.emptyObjectType -} - -func (c *Checker) getGlobalStrictFunctionType(name string) *Type { - if c.strictBindCallApply { - return c.getGlobalType(name, 0 /*arity*/, true /*reportErrors*/) - } - return c.globalFunctionType -} - -func (c *Checker) getGlobalImportMetaExpressionType() *Type { - if c.deferredGlobalImportMetaExpressionType == nil { - // Create a synthetic type `ImportMetaExpression { meta: MetaProperty }` - symbol := c.newSymbol(ast.SymbolFlagsNone, "ImportMetaExpression") - importMetaType := c.getGlobalImportMetaType() - metaPropertySymbol := c.newSymbolEx(ast.SymbolFlagsProperty, "meta", ast.CheckFlagsReadonly) - metaPropertySymbol.Parent = symbol - c.valueSymbolLinks.Get(metaPropertySymbol).resolvedType = importMetaType - members := createSymbolTable([]*ast.Symbol{metaPropertySymbol}) - symbol.Members = members - c.deferredGlobalImportMetaExpressionType = c.newAnonymousType(symbol, members, nil, nil, nil) - } - return c.deferredGlobalImportMetaExpressionType -} - -func (c *Checker) createIterableType(iteratedType *Type) *Type { - return c.createTypeFromGenericGlobalType(c.getGlobalIterableTypeChecked(), []*Type{iteratedType, c.voidType, c.undefinedType}) -} - -func (c *Checker) createArrayType(elementType *Type) *Type { - return c.createArrayTypeEx(elementType, false /*readonly*/) -} - -func (c *Checker) createArrayTypeEx(elementType *Type, readonly bool) *Type { - return c.createTypeFromGenericGlobalType(core.IfElse(readonly, c.globalReadonlyArrayType, c.globalArrayType), []*Type{elementType}) -} - -func (c *Checker) getTupleElementFlags(node *ast.Node) ElementFlags { - switch node.Kind { - case ast.KindOptionalType: - return ElementFlagsOptional - case ast.KindRestType: - return core.IfElse(c.getArrayElementTypeNode(node.AsRestTypeNode().Type) != nil, ElementFlagsRest, ElementFlagsVariadic) - case ast.KindNamedTupleMember: - named := node.AsNamedTupleMember() - switch { - case named.QuestionToken != nil: - return ElementFlagsOptional - case named.DotDotDotToken != nil: - return core.IfElse(c.getArrayElementTypeNode(named.Type) != nil, ElementFlagsRest, ElementFlagsVariadic) - } - return ElementFlagsRequired - } - return ElementFlagsRequired -} - -func (c *Checker) getTupleElementInfo(node *ast.Node) TupleElementInfo { - return TupleElementInfo{ - flags: c.getTupleElementFlags(node), - labeledDeclaration: core.IfElse(ast.IsNamedTupleMember(node) || ast.IsParameter(node), node, nil), - } -} - -func (c *Checker) createTupleType(elementTypes []*Type) *Type { - elementInfos := core.Map(elementTypes, func(_ *Type) TupleElementInfo { return TupleElementInfo{flags: ElementFlagsRequired} }) - return c.createTupleTypeEx(elementTypes, elementInfos, false /*readonly*/) -} - -func (c *Checker) createTupleTypeEx(elementTypes []*Type, elementInfos []TupleElementInfo, readonly bool) *Type { - tupleTarget := c.getTupleTargetType(elementInfos, readonly) - switch { - case tupleTarget == c.emptyGenericType: - return c.emptyObjectType - case len(elementTypes) != 0: - return c.createNormalizedTypeReference(tupleTarget, elementTypes) - } - return tupleTarget -} - -func (c *Checker) getTupleTargetType(elementInfos []TupleElementInfo, readonly bool) *Type { - if len(elementInfos) == 1 && elementInfos[0].flags&ElementFlagsRest != 0 { - // [...X[]] is equivalent to just X[] - if readonly { - return c.globalReadonlyArrayType - } - return c.globalArrayType - } - key := getTupleKey(elementInfos, readonly) - t := c.tupleTypes[key] - if t == nil { - t = c.createTupleTargetType(elementInfos, readonly) - c.tupleTypes[key] = t - } - return t -} - -// We represent tuple types as type references to synthesized generic interface types created by -// this function. The types are of the form: -// -// interface Tuple extends Array { 0: T0, 1: T1, 2: T2, ... } -// -// Note that the generic type created by this function has no symbol associated with it. The same -// is true for each of the synthesized type parameters. -func (c *Checker) createTupleTargetType(elementInfos []TupleElementInfo, readonly bool) *Type { - arity := len(elementInfos) - minLength := core.CountWhere(elementInfos, func(e TupleElementInfo) bool { - return e.flags&(ElementFlagsRequired|ElementFlagsVariadic) != 0 - }) - var typeParameters []*Type - members := make(ast.SymbolTable) - combinedFlags := ElementFlagsNone - if arity != 0 { - typeParameters = make([]*Type, 0, arity) - for i := range arity { - typeParameter := c.newTypeParameter(nil) - typeParameters = append(typeParameters, typeParameter) - flags := elementInfos[i].flags - combinedFlags |= flags - if combinedFlags&ElementFlagsVariable == 0 { - property := c.newSymbolEx(ast.SymbolFlagsProperty|(core.IfElse(flags&ElementFlagsOptional != 0, ast.SymbolFlagsOptional, 0)), strconv.Itoa(i), core.IfElse(readonly, ast.CheckFlagsReadonly, 0)) - c.valueSymbolLinks.Get(property).resolvedType = typeParameter - // c.valueSymbolLinks.get(property).tupleLabelDeclaration = elementInfos[i].labeledDeclaration - members[property.Name] = property - } - } - } - fixedLength := len(members) - lengthSymbol := c.newSymbolEx(ast.SymbolFlagsProperty, "length", core.IfElse(readonly, ast.CheckFlagsReadonly, 0)) - if combinedFlags&ElementFlagsVariable != 0 { - c.valueSymbolLinks.Get(lengthSymbol).resolvedType = c.numberType - } else { - var literalTypes []*Type - for i := minLength; i <= arity; i++ { - literalTypes = append(literalTypes, c.getNumberLiteralType(jsnum.Number(i))) - } - c.valueSymbolLinks.Get(lengthSymbol).resolvedType = c.getUnionType(literalTypes) - } - members[lengthSymbol.Name] = lengthSymbol - t := c.newObjectType(ObjectFlagsTuple|ObjectFlagsReference, nil) - d := t.AsTupleType() - d.thisType = c.newTypeParameter(nil) - d.thisType.AsTypeParameter().isThisType = true - d.thisType.AsTypeParameter().constraint = t - d.allTypeParameters = append(typeParameters, d.thisType) - d.instantiations = make(map[string]*Type) - d.instantiations[getTypeListKey(d.TypeParameters())] = t - d.target = t - d.resolvedTypeArguments = d.TypeParameters() - d.declaredMembersResolved = true - d.declaredMembers = members - d.elementInfos = elementInfos - d.minLength = minLength - d.fixedLength = fixedLength - d.combinedFlags = combinedFlags - d.readonly = readonly - return t -} - -func (c *Checker) getElementTypeOfSliceOfTupleType(t *Type, index int, endSkipCount int, writing bool, noReductions bool) *Type { - length := c.getTypeReferenceArity(t) - endSkipCount - elementInfos := t.TargetTupleType().elementInfos - if index < length { - typeArguments := c.getTypeArguments(t) - var elementTypes []*Type - for i := index; i < length; i++ { - e := typeArguments[i] - if elementInfos[i].flags&ElementFlagsVariadic != 0 { - e = c.getIndexedAccessType(e, c.numberType) - } - elementTypes = append(elementTypes, e) - } - if writing { - return c.getIntersectionType(elementTypes) - } - return c.getUnionTypeEx(elementTypes, core.IfElse(noReductions, UnionReductionNone, UnionReductionLiteral), nil, nil) - } - return nil -} - -func (c *Checker) getRestTypeOfTupleType(t *Type) *Type { - return c.getElementTypeOfSliceOfTupleType(t, t.TargetTupleType().fixedLength, 0, false, false) -} - -func (c *Checker) getTupleElementTypeOutOfStartCount(t *Type, index jsnum.Number, undefinedLikeType *Type) *Type { - return c.mapType(t, func(t *Type) *Type { - restType := c.getRestTypeOfTupleType(t) - if restType == nil { - return c.undefinedType - } - if undefinedLikeType != nil && index >= jsnum.Number(getTotalFixedElementCount(t.TargetTupleType())) { - return c.getUnionType([]*Type{restType, undefinedLikeType}) - } - return restType - }) -} - -func (c *Checker) isGenericType(t *Type) bool { - return c.getGenericObjectFlags(t) != 0 -} - -func (c *Checker) isGenericObjectType(t *Type) bool { - return c.getGenericObjectFlags(t)&ObjectFlagsIsGenericObjectType != 0 -} - -func (c *Checker) isGenericIndexType(t *Type) bool { - return c.getGenericObjectFlags(t)&ObjectFlagsIsGenericIndexType != 0 -} - -func (c *Checker) getGenericObjectFlags(t *Type) ObjectFlags { - var combinedFlags ObjectFlags - if t.flags&(TypeFlagsUnionOrIntersection|TypeFlagsSubstitution) != 0 { - if t.objectFlags&ObjectFlagsIsGenericTypeComputed == 0 { - if t.flags&TypeFlagsUnionOrIntersection != 0 { - for _, u := range t.Types() { - combinedFlags |= c.getGenericObjectFlags(u) - } - } else { - combinedFlags = c.getGenericObjectFlags(t.AsSubstitutionType().baseType) | c.getGenericObjectFlags(t.AsSubstitutionType().constraint) - } - t.objectFlags |= ObjectFlagsIsGenericTypeComputed | combinedFlags - } - return t.objectFlags & ObjectFlagsIsGenericType - } - if t.flags&TypeFlagsInstantiableNonPrimitive != 0 || c.isGenericMappedType(t) || c.isGenericTupleType(t) { - combinedFlags |= ObjectFlagsIsGenericObjectType - } - if t.flags&(TypeFlagsInstantiableNonPrimitive|TypeFlagsIndex) != 0 || c.isGenericStringLikeType(t) { - combinedFlags |= ObjectFlagsIsGenericIndexType - } - return combinedFlags -} - -func (c *Checker) isGenericTupleType(t *Type) bool { - return isTupleType(t) && t.TargetTupleType().combinedFlags&ElementFlagsVariadic != 0 -} - -func (c *Checker) isGenericMappedType(t *Type) bool { - if t.objectFlags&ObjectFlagsMapped != 0 { - constraint := c.getConstraintTypeFromMappedType(t) - if c.isGenericIndexType(constraint) { - return true - } - // A mapped type is generic if the 'as' clause references generic types other than the iteration type. - // To determine this, we substitute the constraint type (that we now know isn't generic) for the iteration - // type and check whether the resulting type is generic. - nameType := c.getNameTypeFromMappedType(t) - if nameType != nil && c.isGenericIndexType(c.instantiateType(nameType, newSimpleTypeMapper(c.getTypeParameterFromMappedType(t), constraint))) { - return true - } - } - return false -} - -/** - * A union type which is reducible upon instantiation (meaning some members are removed under certain instantiations) - * must be kept generic, as that instantiation information needs to flow through the type system. By replacing all - * type parameters in the union with a special never type that is treated as a literal in `getReducedType`, we can cause - * the `getReducedType` logic to reduce the resulting type if possible (since only intersections with conflicting - * literal-typed properties are reducible). - */ -func (c *Checker) isGenericReducibleType(t *Type) bool { - return t.flags&TypeFlagsUnion != 0 && t.objectFlags&ObjectFlagsContainsIntersections != 0 && core.Some(t.Types(), c.isGenericReducibleType) || - t.flags&TypeFlagsIntersection != 0 && c.isReducibleIntersection(t) -} - -func (c *Checker) isReducibleIntersection(t *Type) bool { - d := t.AsIntersectionType() - if d.uniqueLiteralFilledInstantiation == nil { - d.uniqueLiteralFilledInstantiation = c.instantiateType(t, c.uniqueLiteralMapper) - } - return c.getReducedType(d.uniqueLiteralFilledInstantiation) != d.uniqueLiteralFilledInstantiation -} - -func (c *Checker) getUniqueLiteralTypeForTypeParameter(t *Type) *Type { - if t.flags&TypeFlagsTypeParameter != 0 { - return c.uniqueLiteralType - } - return t -} - -func (c *Checker) getConditionalFlowTypeOfType(t *Type, node *ast.Node) *Type { - var constraints []*Type - covariant := true - for node != nil && !ast.IsStatement(node) && node.Kind != ast.KindJSDoc { - parent := node.Parent - // only consider variance flipped by parameter locations - `keyof` types would usually be considered variance inverting, but - // often get used in indexed accesses where they behave sortof invariantly, but our checking is lax - if ast.IsParameter(parent) { - covariant = !covariant - } - // Always substitute on type parameters, regardless of variance, since even - // in contravariant positions, they may rely on substituted constraints to be valid - if (covariant || t.flags&TypeFlagsTypeVariable != 0) && ast.IsConditionalTypeNode(parent) && node == parent.AsConditionalTypeNode().TrueType { - constraint := c.getImpliedConstraint(t, parent.AsConditionalTypeNode().CheckType, parent.AsConditionalTypeNode().ExtendsType) - if constraint != nil { - constraints = append(constraints, constraint) - } - } else if t.flags&TypeFlagsTypeParameter != 0 && ast.IsMappedTypeNode(parent) && parent.AsMappedTypeNode().NameType == nil && node == parent.AsMappedTypeNode().Type { - mappedType := c.getTypeFromTypeNode(parent) - if c.getTypeParameterFromMappedType(mappedType) == c.getActualTypeVariable(t) { - typeParameter := c.getHomomorphicTypeVariable(mappedType) - if typeParameter != nil { - constraint := c.getConstraintOfTypeParameter(typeParameter) - if constraint != nil && everyType(constraint, c.isArrayOrTupleType) { - constraints = append(constraints, c.getUnionType([]*Type{c.numberType, c.numericStringType})) - } - } - } - } - node = parent - } - if len(constraints) != 0 { - return c.getSubstitutionType(t, c.getIntersectionType(constraints)) - } - return t -} - -func (c *Checker) getImpliedConstraint(t *Type, checkNode *ast.Node, extendsNode *ast.Node) *Type { - switch { - case isUnaryTupleTypeNode(checkNode) && isUnaryTupleTypeNode(extendsNode): - return c.getImpliedConstraint(t, checkNode.AsTupleTypeNode().Elements.Nodes[0], extendsNode.AsTupleTypeNode().Elements.Nodes[0]) - case c.getActualTypeVariable(c.getTypeFromTypeNode(checkNode)) == c.getActualTypeVariable(t): - return c.getTypeFromTypeNode(extendsNode) - } - return nil -} - -func isUnaryTupleTypeNode(node *ast.Node) bool { - return ast.IsTupleTypeNode(node) && len(node.AsTupleTypeNode().Elements.Nodes) == 1 -} - -func (c *Checker) newType(flags TypeFlags, objectFlags ObjectFlags, data TypeData) *Type { - c.TypeCount++ - t := data.AsType() - t.flags = flags - t.objectFlags = objectFlags &^ (ObjectFlagsCouldContainTypeVariablesComputed | ObjectFlagsCouldContainTypeVariables | ObjectFlagsMembersResolved) - t.id = TypeId(c.TypeCount) - t.checker = c - 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 := &IntrinsicType{} - data.intrinsicName = intrinsicName - return c.newType(flags, objectFlags, data) -} - -func (c *Checker) createWideningType(nonWideningType *Type) *Type { - if c.strictNullChecks { - return nonWideningType - } - t := c.newIntrinsicType(nonWideningType.flags, nonWideningType.AsIntrinsicType().intrinsicName) - t.objectFlags |= ObjectFlagsContainsWideningType - return t -} - -func (c *Checker) createUnknownUnionType() *Type { - if c.strictNullChecks { - return c.getUnionType([]*Type{c.undefinedType, c.nullType, c.unknownEmptyObjectType}) - } - return c.unknownType -} - -func (c *Checker) newLiteralType(flags TypeFlags, value any, regularType *Type) *Type { - data := &LiteralType{} - data.value = value - t := c.newType(flags, ObjectFlagsNone, data) - if regularType != nil { - data.regularType = regularType - } else { - data.regularType = t - } - return t -} - -func (c *Checker) newUniqueESSymbolType(symbol *ast.Symbol, name string) *Type { - data := &UniqueESSymbolType{} - data.name = name - t := c.newType(TypeFlagsUniqueESSymbol, ObjectFlagsNone, data) - t.symbol = symbol - return t -} - -func (c *Checker) newObjectType(objectFlags ObjectFlags, symbol *ast.Symbol) *Type { - var data TypeData - switch { - case objectFlags&ObjectFlagsClassOrInterface != 0: - data = &InterfaceType{} - case objectFlags&ObjectFlagsTuple != 0: - data = &TupleType{} - case objectFlags&ObjectFlagsReference != 0: - data = &TypeReference{} - case objectFlags&ObjectFlagsMapped != 0: - data = &MappedType{} - case objectFlags&ObjectFlagsReverseMapped != 0: - data = &ReverseMappedType{} - case objectFlags&ObjectFlagsEvolvingArray != 0: - data = &EvolvingArrayType{} - case objectFlags&ObjectFlagsInstantiationExpressionType != 0: - data = &InstantiationExpressionType{} - case objectFlags&ObjectFlagsAnonymous != 0: - data = &ObjectType{} - default: - panic("Unhandled case in newObjectType") - } - t := c.newType(TypeFlagsObject, objectFlags, data) - t.symbol = symbol - return t -} - -func (c *Checker) newAnonymousType(symbol *ast.Symbol, members ast.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) tryCreateTypeReference(target *Type, typeArguments []*Type) *Type { - if len(typeArguments) != 0 && target == c.emptyGenericType { - return c.unknownType - } - return c.createTypeReference(target, typeArguments) -} - -func (c *Checker) createTypeReference(target *Type, typeArguments []*Type) *Type { - id := getTypeListKey(typeArguments) - intf := target.AsInterfaceType() - if t, ok := intf.instantiations[id]; ok { - return t - } - t := c.newObjectType(ObjectFlagsReference, target.symbol) - t.objectFlags |= c.getPropagatingFlagsOfTypes(typeArguments, TypeFlagsNone) - d := t.AsTypeReference() - d.target = target - d.resolvedTypeArguments = typeArguments - intf.instantiations[id] = t - return t -} - -func (c *Checker) createDeferredTypeReference(target *Type, node *ast.Node, mapper *TypeMapper, alias *TypeAlias) *Type { - if alias == nil { - alias = c.getAliasForTypeNode(node) - if alias != nil && mapper != nil { - alias.typeArguments = c.instantiateTypes(alias.typeArguments, mapper) - } - } - t := c.newObjectType(ObjectFlagsReference, target.symbol) - t.alias = alias - d := t.AsTypeReference() - d.target = target - d.mapper = mapper - d.node = node - return t -} - -func (c *Checker) cloneTypeReference(source *Type) *Type { - t := c.newObjectType(ObjectFlagsReference, source.symbol) - t.objectFlags = source.objectFlags &^ ObjectFlagsMembersResolved - t.AsTypeReference().target = source.AsTypeReference().target - t.AsTypeReference().resolvedTypeArguments = source.AsTypeReference().resolvedTypeArguments - return t -} - -func (c *Checker) setStructuredTypeMembers(t *Type, members ast.SymbolTable, callSignatures []*Signature, constructSignatures []*Signature, indexInfos []*IndexInfo) { - t.objectFlags |= ObjectFlagsMembersResolved - data := t.AsStructuredType() - data.members = members - data.properties = c.getNamedMembers(members) - if len(callSignatures) != 0 { - if len(constructSignatures) != 0 { - data.signatures = core.Concatenate(callSignatures, constructSignatures) - } else { - data.signatures = slices.Clip(callSignatures) - } - data.callSignatureCount = len(callSignatures) - } else { - if len(constructSignatures) != 0 { - data.signatures = slices.Clip(constructSignatures) - } else { - data.signatures = nil - } - data.callSignatureCount = 0 - } - data.indexInfos = slices.Clip(indexInfos) -} - -func (c *Checker) newTypeParameter(symbol *ast.Symbol) *Type { - t := c.newType(TypeFlagsTypeParameter, ObjectFlagsNone, &TypeParameter{}) - t.symbol = symbol - 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 := &UnionType{} - data.types = types - return c.newType(TypeFlagsUnion, objectFlags, data) -} - -func (c *Checker) newIntersectionType(objectFlags ObjectFlags, types []*Type) *Type { - data := &IntersectionType{} - data.types = types - return c.newType(TypeFlagsIntersection, objectFlags, data) -} - -func (c *Checker) newIndexedAccessType(objectType *Type, indexType *Type, accessFlags AccessFlags) *Type { - data := &IndexedAccessType{} - data.objectType = objectType - data.indexType = indexType - data.accessFlags = accessFlags - return c.newType(TypeFlagsIndexedAccess, ObjectFlagsNone, data) -} - -func (c *Checker) newIndexType(target *Type, indexFlags IndexFlags) *Type { - data := &IndexType{} - data.target = target - data.indexFlags = indexFlags - return c.newType(TypeFlagsIndex, ObjectFlagsNone, data) -} - -func (c *Checker) newTemplateLiteralType(texts []string, types []*Type) *Type { - data := &TemplateLiteralType{} - data.texts = texts - data.types = types - return c.newType(TypeFlagsTemplateLiteral, ObjectFlagsNone, data) -} - -func (c *Checker) newStringMappingType(symbol *ast.Symbol, target *Type) *Type { - data := &StringMappingType{} - data.target = target - t := c.newType(TypeFlagsStringMapping, ObjectFlagsNone, data) - t.symbol = symbol - return t -} - -func (c *Checker) newConditionalType(root *ConditionalRoot, mapper *TypeMapper, combinedMapper *TypeMapper) *Type { - data := &ConditionalType{} - data.root = root - data.checkType = c.instantiateType(root.checkType, mapper) - data.extendsType = c.instantiateType(root.extendsType, mapper) - data.mapper = mapper - data.combinedMapper = combinedMapper - return c.newType(TypeFlagsConditional, ObjectFlagsNone, data) -} - -func (c *Checker) newSubstitutionType(baseType *Type, constraint *Type) *Type { - data := &SubstitutionType{} - data.baseType = baseType - data.constraint = constraint - return c.newType(TypeFlagsSubstitution, ObjectFlagsNone, data) -} - -func (c *Checker) newSignature(flags SignatureFlags, declaration *ast.Node, typeParameters []*Type, thisParameter *ast.Symbol, parameters []*ast.Symbol, resolvedReturnType *Type, resolvedTypePredicate *TypePredicate, minArgumentCount int) *Signature { - sig := c.signaturePool.New() - sig.flags = flags - sig.declaration = declaration - sig.typeParameters = typeParameters - sig.parameters = parameters - sig.thisParameter = thisParameter - sig.resolvedReturnType = resolvedReturnType - sig.resolvedTypePredicate = resolvedTypePredicate - sig.minArgumentCount = int32(minArgumentCount) - sig.resolvedMinArgumentCount = -1 - return sig -} - -func (c *Checker) newIndexInfo(keyType *Type, valueType *Type, isReadonly bool, declaration *ast.Node, components []*ast.Node) *IndexInfo { - info := c.indexInfoPool.New() - info.keyType = keyType - info.valueType = valueType - info.isReadonly = isReadonly - info.declaration = declaration - info.components = components - return info -} - -func (c *Checker) getRegularTypeOfLiteralType(t *Type) *Type { - if t.flags&TypeFlagsFreshable != 0 { - return t.AsLiteralType().regularType - } - if t.flags&TypeFlagsUnion != 0 { - u := t.AsUnionType() - if u.regularType == nil { - u.regularType = c.mapType(t, c.getRegularTypeOfLiteralType) - } - return u.regularType - } - return t -} - -func (c *Checker) getFreshTypeOfLiteralType(t *Type) *Type { - if t.flags&TypeFlagsFreshable != 0 { - d := t.AsLiteralType() - if d.freshType == nil { - f := c.newLiteralType(t.flags, d.value, t) - f.symbol = t.symbol - f.AsLiteralType().freshType = f - d.freshType = f - } - return d.freshType - } - return t -} - -func isFreshLiteralType(t *Type) bool { - return t.flags&TypeFlagsFreshable != 0 && t.AsLiteralType().freshType == 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 jsnum.Number) *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 jsnum.PseudoBigInt) *Type { - t := c.bigintLiteralTypes[value] - if t == nil { - t = c.newLiteralType(TypeFlagsBigIntLiteral, value, nil) - c.bigintLiteralTypes[value] = t - } - return t -} - -// text is a valid bigint string excluding a trailing `n`, but including a possible prefix `-`. -// Use `isValidBigIntString(text, roundTripOnly)` before calling this function. -func (c *Checker) parseBigIntLiteralType(text string) *Type { - return c.getBigIntLiteralType(jsnum.ParseValidBigInt(text)) -} - -func getStringLiteralValue(t *Type) string { - return t.AsLiteralType().value.(string) -} - -func getNumberLiteralValue(t *Type) jsnum.Number { - return t.AsLiteralType().value.(jsnum.Number) -} - -func getBigIntLiteralValue(t *Type) jsnum.PseudoBigInt { - return t.AsLiteralType().value.(jsnum.PseudoBigInt) -} - -func getBooleanLiteralValue(t *Type) bool { - return t.AsLiteralType().value.(bool) -} - -func (c *Checker) getEnumLiteralType(value any, enumSymbol *ast.Symbol, symbol *ast.Symbol) *Type { - var flags TypeFlags - switch value.(type) { - case string: - flags = TypeFlagsEnumLiteral | TypeFlagsStringLiteral - case jsnum.Number: - 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 isLiteralType(t *Type) bool { - if t.flags&TypeFlagsBoolean != 0 { - return true - } - if t.flags&TypeFlagsUnion != 0 { - if t.flags&TypeFlagsEnumLiteral != 0 { - return true - } - return core.Every(t.Types(), isUnitType) - } - return isUnitType(t) -} - -func isNeitherUnitTypeNorNever(t *Type) bool { - return t.flags&(TypeFlagsUnit|TypeFlagsNever) == 0 -} - -func isUnitType(t *Type) bool { - return t.flags&TypeFlagsUnit != 0 -} - -func (c *Checker) isUnitLikeType(t *Type) bool { - // Intersections that reduce to 'never' (e.g. 'T & null' where 'T extends {}') are not unit types. - t = c.getBaseConstraintOrType(t) - // Scan intersections such that tagged literal types are considered unit types. - if t.flags&TypeFlagsIntersection != 0 { - return core.Some(t.AsIntersectionType().types, isUnitType) - } - return isUnitType(t) -} - -func (c *Checker) extractUnitType(t *Type) *Type { - if t.flags&TypeFlagsIntersection != 0 { - u := core.Find(t.AsIntersectionType().types, isUnitType) - if u != nil { - return u - } - } - 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 -} - -// This like getBaseTypeOfLiteralType, but instead treats enum literals as strings/numbers instead -// of returning their enum base type (which depends on the types of other literals in the enum). -func (c *Checker) getBaseTypeOfLiteralTypeForComparison(t *Type) *Type { - switch { - case t.flags&(TypeFlagsStringLiteral|TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0: - return c.stringType - case t.flags&(TypeFlagsNumberLiteral|TypeFlagsEnum) != 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.mapType(t, c.getBaseTypeOfLiteralTypeForComparison) - } - return t -} - -func (c *Checker) getBaseTypeOfEnumLikeType(t *Type) *Type { - if t.flags&TypeFlagsEnumLike != 0 && t.symbol.Flags&ast.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) getWidenedLiteralLikeTypeForContextualType(t *Type, contextualType *Type) *Type { - if !c.isLiteralOfContextualType(t, contextualType) { - t = c.getWidenedUniqueESSymbolType(c.getWidenedLiteralType(t)) - } - return c.getRegularTypeOfLiteralType(t) -} - -func (c *Checker) isLiteralOfContextualType(candidateType *Type, contextualType *Type) bool { - if contextualType != nil { - if contextualType.flags&TypeFlagsUnionOrIntersection != 0 { - return core.Some(contextualType.Types(), func(t *Type) bool { - return c.isLiteralOfContextualType(candidateType, t) - }) - } - if contextualType.flags&TypeFlagsInstantiableNonPrimitive != 0 { - // If the contextual type is a type variable constrained to a primitive type, consider - // this a literal context for literals of that primitive type. For example, given a - // type parameter 'T extends string', infer string literal types for T. - constraint := c.getBaseConstraintOfType(contextualType) - if constraint == nil { - constraint = c.unknownType - } - return c.maybeTypeOfKind(constraint, TypeFlagsString) && c.maybeTypeOfKind(candidateType, TypeFlagsStringLiteral) || - c.maybeTypeOfKind(constraint, TypeFlagsNumber) && c.maybeTypeOfKind(candidateType, TypeFlagsNumberLiteral) || - c.maybeTypeOfKind(constraint, TypeFlagsBigInt) && c.maybeTypeOfKind(candidateType, TypeFlagsBigIntLiteral) || - c.maybeTypeOfKind(constraint, TypeFlagsESSymbol) && c.maybeTypeOfKind(candidateType, TypeFlagsUniqueESSymbol) || - c.isLiteralOfContextualType(candidateType, constraint) - } - // If the contextual type is a literal of a particular primitive type, we consider this a - // literal context for all literals of that primitive type. - return contextualType.flags&(TypeFlagsStringLiteral|TypeFlagsIndex|TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0 && c.maybeTypeOfKind(candidateType, TypeFlagsStringLiteral) || - contextualType.flags&TypeFlagsNumberLiteral != 0 && c.maybeTypeOfKind(candidateType, TypeFlagsNumberLiteral) || - contextualType.flags&TypeFlagsBigIntLiteral != 0 && c.maybeTypeOfKind(candidateType, TypeFlagsBigIntLiteral) || - contextualType.flags&TypeFlagsBooleanLiteral != 0 && c.maybeTypeOfKind(candidateType, TypeFlagsBooleanLiteral) || - contextualType.flags&TypeFlagsUniqueESSymbol != 0 && c.maybeTypeOfKind(candidateType, TypeFlagsUniqueESSymbol) - } - return false -} - -func (c *Checker) mapTypeWithAlias(t *Type, f func(t *Type) *Type, alias *TypeAlias) *Type { - if t.flags&TypeFlagsUnion != 0 && alias != nil { - return c.getUnionTypeEx(core.Map(t.Types(), f), UnionReductionLiteral, alias, nil) - } - return c.mapType(t, f) -} - -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.AsUnionType() - types := u.types - if u.origin != nil && u.origin.flags&TypeFlagsUnion != 0 { - types = u.origin.Types() - } - mappedTypes := make([]*Type, 0, 16) - 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 { - if len(mappedTypes) == 0 { - return nil - } - return c.getUnionTypeEx(slices.Clone(mappedTypes), core.IfElse(noReductions, UnionReductionNone, UnionReductionLiteral), nil /*alias*/, nil /*origin*/) - } - return t -} - -type UnionReduction int32 - -const ( - UnionReductionNone UnionReduction = iota - UnionReductionLiteral - UnionReductionSubtype -) - -func (c *Checker) getUnionOrIntersectionType(types []*Type, isUnion bool, unionReduction UnionReduction) *Type { - if isUnion { - return c.getUnionTypeEx(types, unionReduction, nil, nil) - } - return c.getIntersectionType(types) -} - -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: getAliasKey(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(make([]*Type, 0, len(types)), 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 { - typeSet = c.removeRedundantLiteralTypes(typeSet, includes, unionReduction&UnionReductionSubtype != 0) - } - if includes&TypeFlagsStringLiteral != 0 && includes&(TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0 { - typeSet = c.removeStringLiteralsMatchedByTemplateLiterals(typeSet) - } - if includes&TypeFlagsIncludesConstrainedTypeVariable != 0 { - typeSet = 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 !core.Some(namedUnions, func(u *Type) bool { return containsType(u.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.Types()) - } - if namedTypesCount+len(reducedTypes) == len(typeSet) { - for _, t := range namedUnions { - reducedTypes, _ = insertType(reducedTypes, t) - } - origin = c.newUnionType(ObjectFlagsNone, reducedTypes) - } - } - objectFlags := core.IfElse(includes&TypeFlagsNotPrimitiveUnion != 0, ObjectFlagsNone, ObjectFlagsPrimitiveUnion) | - core.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 := getUnionKey(types, origin, alias) - t := c.unionTypes[key] - if t == nil { - t = c.newUnionType(precomputedObjectFlags|c.getPropagatingFlagsOfTypes(types, TypeFlagsNullable), types) - t.AsUnionType().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) UnionTypes() iter.Seq[*Type] { - return maps.Values(c.unionTypes) -} - -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.AsUnionType() - 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 { - if index, ok := slices.BinarySearchFunc(typeSet, t, CompareTypes); !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.AsUnionType() - if t.alias != nil || u.origin != nil && u.origin.flags&TypeFlagsUnion == 0 { - namedUnions = core.AppendIfUnique(namedUnions, t) - } else if u.origin != nil && u.origin.flags&TypeFlagsUnion != 0 { - namedUnions = c.addNamedUnions(namedUnions, u.origin.Types()) - } - } - } - return namedUnions -} - -func (c *Checker) removeRedundantLiteralTypes(types []*Type, includes TypeFlags, reduceVoidUndefined bool) []*Type { - i := len(types) - for i > 0 { - i-- - t := types[i] - flags := t.flags - remove := flags&(TypeFlagsStringLiteral|TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0 && includes&TypeFlagsString != 0 || - flags&TypeFlagsNumberLiteral != 0 && includes&TypeFlagsNumber != 0 || - flags&TypeFlagsBigIntLiteral != 0 && includes&TypeFlagsBigInt != 0 || - flags&TypeFlagsUniqueESSymbol != 0 && includes&TypeFlagsESSymbol != 0 || - reduceVoidUndefined && flags&TypeFlagsUndefined != 0 && includes&TypeFlagsVoid != 0 || - isFreshLiteralType(t) && containsType(types, t.AsLiteralType().regularType) - if remove { - types = slices.Delete(types, i, i+1) - } - } - return types -} - -func (c *Checker) removeStringLiteralsMatchedByTemplateLiterals(types []*Type) []*Type { - templates := core.Filter(types, c.isPatternLiteralType) - if len(templates) != 0 { - i := len(types) - for i > 0 { - i-- - t := types[i] - if t.flags&TypeFlagsStringLiteral != 0 && core.Some(templates, func(template *Type) bool { - return c.isTypeMatchedByTemplateLiteralOrStringMapping(t, template) - }) { - types = slices.Delete(types, i, i+1) - } - } - } - return types -} - -func (c *Checker) isTypeMatchedByTemplateLiteralOrStringMapping(t *Type, template *Type) bool { - if template.flags&TypeFlagsTemplateLiteral != 0 { - return c.isTypeMatchedByTemplateLiteralType(t, template.AsTemplateLiteralType()) - } - return c.isMemberOfStringMapping(t, template) -} - -func (c *Checker) removeConstrainedTypeVariables(types []*Type) []*Type { - var typeVariables []*Type - // First collect a list of the type variables occurring in constraining intersections. - for _, t := range types { - if t.flags&TypeFlagsIntersection != 0 && t.objectFlags&ObjectFlagsIsConstrainedTypeVariable != 0 { - index := 0 - if t.AsIntersectionType().types[0].flags&TypeFlagsTypeVariable == 0 { - index = 1 - } - typeVariables = core.AppendIfUnique(typeVariables, t.AsIntersectionType().types[index]) - } - } - // For each type variable, check if the constraining intersections for that type variable fully - // cover the constraint of the type variable; if so, remove the constraining intersections and - // substitute the type variable. - for _, typeVariable := range typeVariables { - var primitives []*Type - // First collect the primitive types from the constraining intersections. - for _, t := range types { - if t.flags&TypeFlagsIntersection != 0 && t.objectFlags&ObjectFlagsIsConstrainedTypeVariable != 0 { - index := 0 - if t.AsIntersectionType().types[0].flags&TypeFlagsTypeVariable == 0 { - index = 1 - } - if t.AsIntersectionType().types[index] == typeVariable { - primitives, _ = insertType(primitives, t.AsIntersectionType().types[1-index]) - } - } - } - // If every constituent in the type variable's constraint is covered by an intersection of the type - // variable and that constituent, remove those intersections and substitute the type variable. - constraint := c.getBaseConstraintOfType(typeVariable) - if everyType(constraint, func(t *Type) bool { return containsType(primitives, t) }) { - i := len(types) - for i > 0 { - i-- - t := types[i] - if t.flags&TypeFlagsIntersection != 0 && t.objectFlags&ObjectFlagsIsConstrainedTypeVariable != 0 { - index := 0 - if t.AsIntersectionType().types[0].flags&TypeFlagsTypeVariable == 0 { - index = 1 - } - if t.AsIntersectionType().types[index] == typeVariable && containsType(primitives, t.AsIntersectionType().types[1-index]) { - types = slices.Delete(types, i, i+1) - } - } - } - types, _ = insertType(types, typeVariable) - } - } - return types -} - -func (c *Checker) removeSubtypes(types []*Type, hasObjectTypes bool) []*Type { - // [] and [T] immediately reduce to [] and [T] respectively - if len(types) < 2 { - return types - } - key := getTypeListKey(types) - if cached := c.subtypeReductionCache[key]; cached != nil { - return cached - } - // We assume that redundant primitive types have already been removed from the types array and that there - // are no any and unknown types in the array. Thus, the only possible supertypes for primitive types are empty - // object types, and if none of those are present we can exclude primitive types from the subtype check. - hasEmptyObject := hasObjectTypes && core.Some(types, func(t *Type) bool { - return t.flags&TypeFlagsObject != 0 && !c.isGenericMappedType(t) && c.isEmptyResolvedType(c.resolveStructuredTypeMembers(t)) - }) - length := len(types) - i := length - count := 0 - for i > 0 { - i-- - source := types[i] - if hasEmptyObject || source.flags&TypeFlagsStructuredOrInstantiable != 0 { - // A type parameter with a union constraint may be a subtype of some union, but not a subtype of the - // individual constituents of that union. For example, `T extends A | B` is a subtype of `A | B`, but not - // a subtype of just `A` or just `B`. When we encounter such a type parameter, we therefore check if the - // type parameter is a subtype of a union of all the other types. - if source.flags&TypeFlagsTypeParameter != 0 && c.getBaseConstraintOrType(source).flags&TypeFlagsUnion != 0 { - if c.isTypeRelatedTo(source, c.getUnionType(core.Map(types, func(t *Type) *Type { - if t == source { - return c.neverType - } - return t - })), c.strictSubtypeRelation) { - types = slices.Delete(types, i, i+1) - } - continue - } - // Find the first property with a unit type, if any. When constituents have a property by the same name - // but of a different unit type, we can quickly disqualify them from subtype checks. This helps subtype - // reduction of large discriminated union types. - var keyProperty *ast.Symbol - var keyPropertyType *Type - if source.flags&(TypeFlagsObject|TypeFlagsIntersection|TypeFlagsInstantiableNonPrimitive) != 0 { - keyProperty = core.Find(c.getPropertiesOfType(source), func(p *ast.Symbol) bool { - return isUnitType(c.getTypeOfSymbol(p)) - }) - } - if keyProperty != nil { - keyPropertyType = c.getRegularTypeOfLiteralType(c.getTypeOfSymbol(keyProperty)) - } - for _, target := range types { - if source != target { - if count == 100000 { - // After 100000 subtype checks we estimate the remaining amount of work by assuming the - // same ratio of checks per element. If the estimated number of remaining type checks is - // greater than 1M we deem the union type too complex to represent. This for example - // caps union types at 1000 unique object types. - estimatedCount := (count / (length - i)) * length - if estimatedCount > 1000000 { - c.error(c.currentNode, diagnostics.Expression_produces_a_union_type_that_is_too_complex_to_represent) - return nil - } - } - count++ - if keyProperty != nil && target.flags&(TypeFlagsObject|TypeFlagsIntersection|TypeFlagsInstantiableNonPrimitive) != 0 { - t := c.getTypeOfPropertyOfType(target, keyProperty.Name) - if t != nil && isUnitType(t) && c.getRegularTypeOfLiteralType(t) != keyPropertyType { - continue - } - } - if (source == c.emptyObjectType || source == c.unknownEmptyObjectType) && target.symbol != nil && c.IsEmptyAnonymousObjectType(target) { - continue - } - if c.isTypeRelatedTo(source, target, c.strictSubtypeRelation) && (c.getTargetType(source).objectFlags&ObjectFlagsClass == 0 || - c.getTargetType(target).objectFlags&ObjectFlagsClass == 0 || - c.isTypeDerivedFrom(source, target)) { - types = slices.Delete(types, i, i+1) - break - } - } - } - } - } - c.subtypeReductionCache[key] = types - return types -} - -func (c *Checker) intersectTypes(type1 *Type, type2 *Type) *Type { - switch { - case type1 == nil: - return type2 - case type2 == nil: - return type1 - } - return c.getIntersectionType([]*Type{type1, type2}) -} - -type IntersectionFlags uint32 - -const ( - IntersectionFlagsNone IntersectionFlags = 0 - IntersectionFlagsNoSupertypeReduction IntersectionFlags = 1 << 0 - IntersectionFlagsNoConstraintReduction IntersectionFlags = 1 << 1 -) - -// We normalize combinations of intersection and union types based on the distributive property of the '&' -// operator. Specifically, because X & (A | B) is equivalent to X & A | X & B, we can transform intersection -// types with union type constituents into equivalent union types with intersection type constituents and -// effectively ensure that union types are always at the top level in type representations. -// -// We do not perform structural deduplication on intersection types. Intersection types are created only by the & -// type operator and we can't reduce those because we want to support recursive intersection types. For example, -// a type alias of the form "type List = T & { next: List }" cannot be reduced during its declaration. -// Also, unlike union types, the order of the constituent types is preserved in order that overload resolution -// for intersections of types with signatures can be deterministic. -func (c *Checker) getIntersectionType(types []*Type) *Type { - return c.getIntersectionTypeEx(types, IntersectionFlagsNone, nil /*alias*/) -} - -func (c *Checker) getIntersectionTypeEx(types []*Type, flags IntersectionFlags, alias *TypeAlias) *Type { - var orderedTypes orderedSet[*Type] - orderedTypes.values = make([]*Type, 0, len(types)) - orderedTypes.valuesByKey = make(map[*Type]struct{}, len(types)) - includes := c.addTypesToIntersection(&orderedTypes, 0, types) - typeSet := orderedTypes.values - objectFlags := ObjectFlagsNone - // An intersection type is considered empty if it contains - // the type never, or - // more than one unit type or, - // an object type and a nullable type (null or undefined), or - // a string-like type and a type known to be non-string-like, or - // a number-like type and a type known to be non-number-like, or - // a symbol-like type and a type known to be non-symbol-like, or - // a void-like type and a type known to be non-void-like, or - // a non-primitive type and a type known to be primitive. - if includes&TypeFlagsNever != 0 { - if slices.Contains(typeSet, c.silentNeverType) { - return c.silentNeverType - } - return c.neverType - } - if c.strictNullChecks && includes&TypeFlagsNullable != 0 && includes&(TypeFlagsObject|TypeFlagsNonPrimitive|TypeFlagsIncludesEmptyObject) != 0 || - includes&TypeFlagsNonPrimitive != 0 && includes&(TypeFlagsDisjointDomains&^TypeFlagsNonPrimitive) != 0 || - includes&TypeFlagsStringLike != 0 && includes&(TypeFlagsDisjointDomains&^TypeFlagsStringLike) != 0 || - includes&TypeFlagsNumberLike != 0 && includes&(TypeFlagsDisjointDomains&^TypeFlagsNumberLike) != 0 || - includes&TypeFlagsBigIntLike != 0 && includes&(TypeFlagsDisjointDomains&^TypeFlagsBigIntLike) != 0 || - includes&TypeFlagsESSymbolLike != 0 && includes&(TypeFlagsDisjointDomains&^TypeFlagsESSymbolLike) != 0 || - includes&TypeFlagsVoidLike != 0 && includes&(TypeFlagsDisjointDomains&^TypeFlagsVoidLike) != 0 { - return c.neverType - } - if includes&(TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0 && includes&TypeFlagsStringLiteral != 0 { - var isEmptySet bool - typeSet, isEmptySet = c.extractRedundantTemplateLiterals(typeSet) - if isEmptySet { - return c.neverType - } - } - if includes&TypeFlagsAny != 0 { - switch { - case includes&TypeFlagsIncludesWildcard != 0: - return c.wildcardType - case includes&TypeFlagsIncludesError != 0: - return c.errorType - } - return c.anyType - } - if !c.strictNullChecks && includes&TypeFlagsNullable != 0 { - switch { - case includes&TypeFlagsIncludesEmptyObject != 0: - return c.neverType - case includes&TypeFlagsUndefined != 0: - return c.undefinedType - } - return c.nullType - } - if includes&TypeFlagsString != 0 && includes&(TypeFlagsStringLiteral|TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0 || - includes&TypeFlagsNumber != 0 && includes&TypeFlagsNumberLiteral != 0 || - includes&TypeFlagsBigInt != 0 && includes&TypeFlagsBigIntLiteral != 0 || - includes&TypeFlagsESSymbol != 0 && includes&TypeFlagsUniqueESSymbol != 0 || - includes&TypeFlagsVoid != 0 && includes&TypeFlagsUndefined != 0 || - includes&TypeFlagsIncludesEmptyObject != 0 && includes&TypeFlagsDefinitelyNonNullable != 0 { - if flags&IntersectionFlagsNoSupertypeReduction == 0 { - typeSet = c.removeRedundantSupertypes(typeSet, includes) - } - } - if includes&TypeFlagsIncludesMissingType != 0 { - typeSet[slices.Index(typeSet, c.undefinedType)] = c.missingType - } - if len(typeSet) == 0 { - return c.unknownType - } - if len(typeSet) == 1 { - return typeSet[0] - } - if len(typeSet) == 2 && flags&IntersectionFlagsNoConstraintReduction == 0 { - typeVarIndex := 0 - if typeSet[0].flags&TypeFlagsTypeVariable == 0 { - typeVarIndex = 1 - } - typeVariable := typeSet[typeVarIndex] - primitiveType := typeSet[1-typeVarIndex] - if typeVariable.flags&TypeFlagsTypeVariable != 0 && (primitiveType.flags&(TypeFlagsPrimitive|TypeFlagsNonPrimitive) != 0 && !c.isGenericStringLikeType(primitiveType) || - includes&TypeFlagsIncludesEmptyObject != 0) { - // We have an intersection T & P or P & T, where T is a type variable and P is a primitive type, the object type, or {}. - constraint := c.getBaseConstraintOfType(typeVariable) - // Check that T's constraint is similarly composed of primitive types, the object type, or {}. - if constraint != nil && everyType(constraint, c.isPrimitiveOrObjectOrEmptyType) { - // If T's constraint is a subtype of P, simply return T. For example, given `T extends "a" | "b"`, - // the intersection `T & string` reduces to just T. - if c.isTypeStrictSubtypeOf(constraint, primitiveType) { - return typeVariable - } - if !(constraint.flags&TypeFlagsUnion != 0 && someType(constraint, func(n *Type) bool { - return c.isTypeStrictSubtypeOf(n, primitiveType) - })) { - // No constituent of T's constraint is a subtype of P. If P is also not a subtype of T's constraint, - // then the constraint and P are unrelated, and the intersection reduces to never. For example, given - // `T extends "a" | "b"`, the intersection `T & number` reduces to never. - if !c.isTypeStrictSubtypeOf(primitiveType, constraint) { - return c.neverType - } - } - // Some constituent of T's constraint is a subtype of P, or P is a subtype of T's constraint. Thus, - // the intersection further constrains the type variable. For example, given `T extends string | number`, - // the intersection `T & "a"` is marked as a constrained type variable. Likewise, given `T extends "a" | 1`, - // the intersection `T & number` is marked as a constrained type variable. - objectFlags = ObjectFlagsIsConstrainedTypeVariable - } - } - } - key := getIntersectionKey(typeSet, flags, alias) - result := c.intersectionTypes[key] - if result == nil { - if includes&TypeFlagsUnion != 0 { - var reduced bool - typeSet, reduced = c.intersectUnionsOfPrimitiveTypes(typeSet) - switch { - case reduced: - // When the intersection creates a reduced set (which might mean that *all* union types have - // disappeared), we restart the operation to get a new set of combined flags. Once we have - // reduced we'll never reduce again, so this occurs at most once. - result = c.getIntersectionTypeEx(typeSet, flags, alias) - case core.Every(typeSet, isUnionWithUndefined): - containedUndefinedType := c.undefinedType - if core.Some(typeSet, c.containsMissingType) { - containedUndefinedType = c.missingType - } - c.filterTypes(typeSet, isNotUndefinedType) - result = c.getUnionTypeEx([]*Type{c.getIntersectionTypeEx(typeSet, flags, nil /*alias*/), containedUndefinedType}, UnionReductionLiteral, alias, nil /*origin*/) - case core.Every(typeSet, isUnionWithNull): - c.filterTypes(typeSet, isNotNullType) - result = c.getUnionTypeEx([]*Type{c.getIntersectionTypeEx(typeSet, flags, nil /*alias*/), c.nullType}, UnionReductionLiteral, alias, nil /*origin*/) - case len(typeSet) >= 3 && len(types) > 2: - // When we have three or more constituents, more than two inputs (to head off infinite reexpansion), some of which are unions, we employ a "divide and conquer" strategy - // where A & B & C & D is processed as (A & B) & (C & D). Since intersections of unions often produce far smaller - // unions of intersections than the full cartesian product (due to some intersections becoming `never`), this can - // dramatically reduce the overall work. - middle := len(typeSet) / 2 - result = c.getIntersectionTypeEx([]*Type{ - c.getIntersectionTypeEx(typeSet[:middle], flags, nil /*alias*/), - c.getIntersectionTypeEx(typeSet[middle:], flags, nil /*alias*/), - }, - flags, alias) - default: - // We are attempting to construct a type of the form X & (A | B) & (C | D). Transform this into a type of - // the form X & A & C | X & A & D | X & B & C | X & B & D. If the estimated size of the resulting union type - // exceeds 100000 constituents, report an error. - if !c.checkCrossProductUnion(typeSet) { - return c.errorType - } - constituents := c.getCrossProductIntersections(typeSet, flags) - // We attach a denormalized origin type when at least one constituent of the cross-product union is an - // intersection (i.e. when the intersection didn't just reduce one or more unions to smaller unions) and - // the denormalized origin has fewer constituents than the union itself. - var origin *Type - if core.Some(constituents, isIntersectionType) && getConstituentCountOfTypes(constituents) > getConstituentCountOfTypes(typeSet) { - origin = c.newIntersectionType(ObjectFlagsNone, typeSet) - } - result = c.getUnionTypeEx(constituents, UnionReductionLiteral, alias, origin) - } - } else { - result = c.newIntersectionType(objectFlags|c.getPropagatingFlagsOfTypes(types /*excludeKinds*/, TypeFlagsNullable), typeSet) - result.alias = alias - } - c.intersectionTypes[key] = result - } - return result -} - -func isUnionWithUndefined(t *Type) bool { - return t.flags&TypeFlagsUnion != 0 && t.Types()[0].flags&TypeFlagsUndefined != 0 -} - -func isUnionWithNull(t *Type) bool { - return t.flags&TypeFlagsUnion != 0 && (t.Types()[0].flags&TypeFlagsNull != 0 || t.Types()[1].flags&TypeFlagsNull != 0) -} - -func isIntersectionType(t *Type) bool { - return t.flags&TypeFlagsIntersection != 0 -} - -func isPrimitiveUnion(t *Type) bool { - return t.objectFlags&ObjectFlagsPrimitiveUnion != 0 -} - -func isNotUndefinedType(t *Type) bool { - return t.flags&TypeFlagsUndefined == 0 -} - -func isNotNullType(t *Type) bool { - return t.flags&TypeFlagsNull == 0 -} - -// Add the given types to the given type set. Order is preserved, freshness is removed from literal -// types, duplicates are removed, and nested types of the given kind are flattened into the set. -func (c *Checker) addTypesToIntersection(typeSet *orderedSet[*Type], includes TypeFlags, types []*Type) TypeFlags { - for _, t := range types { - includes = c.addTypeToIntersection(typeSet, includes, c.getRegularTypeOfLiteralType(t)) - } - return includes -} - -func (c *Checker) addTypeToIntersection(typeSet *orderedSet[*Type], includes TypeFlags, t *Type) TypeFlags { - flags := t.flags - if flags&TypeFlagsIntersection != 0 { - return c.addTypesToIntersection(typeSet, includes, t.Types()) - } - if c.IsEmptyAnonymousObjectType(t) { - if includes&TypeFlagsIncludesEmptyObject == 0 { - includes |= TypeFlagsIncludesEmptyObject - typeSet.add(t) - } - } else { - if flags&TypeFlagsAnyOrUnknown != 0 { - if t == c.wildcardType { - includes |= TypeFlagsIncludesWildcard - } - if c.isErrorType(t) { - includes |= TypeFlagsIncludesError - } - } else if c.strictNullChecks || flags&TypeFlagsNullable == 0 { - if t == c.missingType { - includes |= TypeFlagsIncludesMissingType - t = c.undefinedType - } - if !typeSet.contains(t) { - if t.flags&TypeFlagsUnit != 0 && includes&TypeFlagsUnit != 0 { - // We have seen two distinct unit types which means we should reduce to an - // empty intersection. Adding TypeFlags.NonPrimitive causes that to happen. - includes |= TypeFlagsNonPrimitive - } - typeSet.add(t) - } - } - includes |= flags & TypeFlagsIncludesMask - } - return includes -} - -func (c *Checker) removeRedundantSupertypes(types []*Type, includes TypeFlags) []*Type { - i := len(types) - for i > 0 { - i-- - t := types[i] - remove := t.flags&TypeFlagsString != 0 && includes&(TypeFlagsStringLiteral|TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0 || - t.flags&TypeFlagsNumber != 0 && includes&TypeFlagsNumberLiteral != 0 || - t.flags&TypeFlagsBigInt != 0 && includes&TypeFlagsBigIntLiteral != 0 || - t.flags&TypeFlagsESSymbol != 0 && includes&TypeFlagsUniqueESSymbol != 0 || - t.flags&TypeFlagsVoid != 0 && includes&TypeFlagsUndefined != 0 || - c.IsEmptyAnonymousObjectType(t) && includes&TypeFlagsDefinitelyNonNullable != 0 - if remove { - types = slices.Delete(types, i, i+1) - } - } - return types -} - -/** - * Returns true if the intersection of the template literals and string literals is the empty set, - * for example `get${string}` & "setX", and should reduce to never. - */ -func (c *Checker) extractRedundantTemplateLiterals(types []*Type) ([]*Type, bool) { - literals := core.Filter(types, func(t *Type) bool { return t.flags&TypeFlagsStringLiteral != 0 }) - i := len(types) - for i > 0 { - i-- - t := types[i] - if t.flags&(TypeFlagsTemplateLiteral|TypeFlagsStringMapping) == 0 { - continue - } - for _, t2 := range literals { - if c.isTypeSubtypeOf(t2, t) { - // For example, `get${T}` & "getX" is just "getX", and Lowercase & "foo" is just "foo" - types = slices.Delete(types, i, i+1) - break - } - if c.isPatternLiteralType(t) { - return types, true - } - } - } - return types, false -} - -// If the given list of types contains more than one union of primitive types, replace the -// first with a union containing an intersection of those primitive types, then remove the -// other unions and return true. Otherwise, do nothing and return false. -func (c *Checker) intersectUnionsOfPrimitiveTypes(types []*Type) ([]*Type, bool) { - index := slices.IndexFunc(types, isPrimitiveUnion) - if index < 0 { - return types, false - } - // Remove all but the first union of primitive types and collect them in - // the unionTypes array. - i := index + 1 - unionTypes := types[index:i:i] - for i < len(types) { - t := types[i] - if t.objectFlags&ObjectFlagsPrimitiveUnion != 0 { - unionTypes = append(unionTypes, t) - types = slices.Delete(types, i, i+1) - } else { - i++ - } - } - // Return false if there was only one union of primitive types - if len(unionTypes) == 1 { - return types, false - } - // We have more than one union of primitive types, now intersect them. For each - // type in each union we check if the type is matched in every union and if so - // we include it in the result. - var checked []*Type - var result []*Type - for _, u := range unionTypes { - for _, t := range u.Types() { - var inserted bool - if checked, inserted = insertType(checked, t); inserted { - if c.eachUnionContains(unionTypes, t) { - // undefinedType/missingType are always sorted first so we leverage that here - if t == c.undefinedType && len(result) != 0 && result[0] == c.missingType { - continue - } - if t == c.missingType && len(result) != 0 && result[0] == c.undefinedType { - result[0] = c.missingType - continue - } - result, _ = insertType(result, t) - } - } - } - } - // Finally replace the first union with the result - types[index] = c.getUnionTypeFromSortedList(result, ObjectFlagsPrimitiveUnion, nil /*alias*/, nil /*origin*/) - return types, true -} - -// Check that the given type has a match in every union. A given type is matched by -// an identical type, and a literal type is additionally matched by its corresponding -// primitive type, and missingType is matched by undefinedType (and vice versa). -func (c *Checker) eachUnionContains(unionTypes []*Type, t *Type) bool { - for _, u := range unionTypes { - types := u.Types() - if !containsType(types, t) { - if t == c.missingType { - return containsType(types, c.undefinedType) - } - if t == c.undefinedType { - return containsType(types, c.missingType) - } - var primitive *Type - switch { - case t.flags&TypeFlagsStringLiteral != 0: - primitive = c.stringType - case t.flags&(TypeFlagsEnum|TypeFlagsNumberLiteral) != 0: - primitive = c.numberType - case t.flags&TypeFlagsBigIntLiteral != 0: - primitive = c.bigintType - case t.flags&TypeFlagsUniqueESSymbol != 0: - primitive = c.esSymbolType - } - if primitive == nil || !containsType(types, primitive) { - return false - } - } - } - return true -} - -func (c *Checker) getCrossProductIntersections(types []*Type, flags IntersectionFlags) []*Type { - count := c.getCrossProductUnionSize(types) - var intersections []*Type - for i := range count { - constituents := slices.Clone(types) - n := i - for j := len(types) - 1; j >= 0; j-- { - if types[j].flags&TypeFlagsUnion != 0 { - sourceTypes := types[j].Types() - length := len(sourceTypes) - constituents[j] = sourceTypes[n%length] - n = n / length - } - } - t := c.getIntersectionTypeEx(constituents, flags, nil /*alias*/) - if t.flags&TypeFlagsNever == 0 { - intersections = append(intersections, t) - } - } - return intersections -} - -func getConstituentCount(t *Type) int { - switch { - case t.flags&TypeFlagsUnionOrIntersection == 0 || t.alias != nil: - return 1 - case t.flags&TypeFlagsUnion != 0 && t.AsUnionType().origin != nil: - return getConstituentCount(t.AsUnionType().origin) - } - return getConstituentCountOfTypes(t.Types()) -} - -func getConstituentCountOfTypes(types []*Type) int { - n := 0 - for _, t := range types { - n += getConstituentCount(t) - } - return n -} - -func (c *Checker) filterTypes(types []*Type, predicate func(*Type) bool) { - for i, t := range types { - types[i] = c.filterType(t, predicate) - } -} - -func (c *Checker) IsEmptyAnonymousObjectType(t *Type) bool { - return t.objectFlags&ObjectFlagsAnonymous != 0 && (t.objectFlags&ObjectFlagsMembersResolved != 0 && c.isEmptyResolvedType(t.AsStructuredType()) || - t.symbol != nil && t.symbol.Flags&ast.SymbolFlagsTypeLiteral != 0 && len(c.getMembersOfSymbol(t.symbol)) == 0) -} - -func (c *Checker) isEmptyResolvedType(t *StructuredType) bool { - return t.AsType() != c.anyFunctionType && len(t.properties) == 0 && len(t.signatures) == 0 && len(t.indexInfos) == 0 -} - -func (c *Checker) isEmptyObjectType(t *Type) bool { - switch { - case t.flags&TypeFlagsObject != 0: - return !c.isGenericMappedType(t) && c.isEmptyResolvedType(c.resolveStructuredTypeMembers(t)) - case t.flags&TypeFlagsNonPrimitive != 0: - return true - case t.flags&TypeFlagsUnion != 0: - return core.Some(t.Types(), c.isEmptyObjectType) - case t.flags&TypeFlagsIntersection != 0: - return core.Every(t.Types(), c.isEmptyObjectType) - } - return false -} - -func (c *Checker) isPatternLiteralPlaceholderType(t *Type) bool { - if t.flags&TypeFlagsIntersection != 0 { - // Return true if the intersection consists of one or more placeholders and zero or - // more object type tags. - seenPlaceholder := false - for _, s := range t.Types() { - if s.flags&(TypeFlagsLiteral|TypeFlagsNullable) != 0 || c.isPatternLiteralPlaceholderType(s) { - seenPlaceholder = true - } else if s.flags&TypeFlagsObject == 0 { - return false - } - } - return seenPlaceholder - } - return t.flags&(TypeFlagsAny|TypeFlagsString|TypeFlagsNumber|TypeFlagsBigInt) != 0 || c.isPatternLiteralType(t) -} - -func (c *Checker) isPatternLiteralType(t *Type) bool { - // A pattern literal type is a template literal or a string mapping type that contains only - // non-generic pattern literal placeholders. - return t.flags&TypeFlagsTemplateLiteral != 0 && core.Every(t.AsTemplateLiteralType().types, c.isPatternLiteralPlaceholderType) || - t.flags&TypeFlagsStringMapping != 0 && c.isPatternLiteralPlaceholderType(t.Target()) -} - -func (c *Checker) isGenericStringLikeType(t *Type) bool { - return t.flags&(TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0 && !c.isPatternLiteralType(t) -} - -func forEachType(t *Type, f func(t *Type)) { - if t.flags&TypeFlagsUnion != 0 { - for _, u := range t.Types() { - f(u) - } - } else { - f(t) - } -} - -func someType(t *Type, f func(*Type) bool) bool { - if t.flags&TypeFlagsUnion != 0 { - return core.Some(t.Types(), f) - } - return f(t) -} - -func everyType(t *Type, f func(*Type) bool) bool { - if t.flags&TypeFlagsUnion != 0 { - return core.Every(t.Types(), f) - } - return f(t) -} - -func everyContainedType(t *Type, f func(*Type) bool) bool { - if t.flags&TypeFlagsUnionOrIntersection != 0 { - return core.Every(t.Types(), f) - } - return f(t) -} - -func (c *Checker) filterType(t *Type, f func(*Type) bool) *Type { - if t.flags&TypeFlagsUnion != 0 { - types := t.Types() - filtered := core.Filter(types, f) - if core.Same(types, filtered) { - return t - } - origin := t.AsUnionType().origin - var newOrigin *Type - if origin != nil && origin.flags&TypeFlagsUnion != 0 { - // If the origin type is a (denormalized) union type, filter its non-union constituents. If that ends - // up removing a smaller number of types than in the normalized constituent set (meaning some of the - // filtered types are within nested unions in the origin), then we can't construct a new origin type. - // Otherwise, if we have exactly one type left in the origin set, return that as the filtered type. - // Otherwise, construct a new filtered origin type. - originTypes := origin.Types() - originFiltered := core.Filter(originTypes, func(u *Type) bool { - return u.flags&TypeFlagsUnion != 0 || f(u) - }) - if len(originTypes)-len(originFiltered) == len(types)-len(filtered) { - if len(originFiltered) == 1 { - return originFiltered[0] - } - newOrigin = c.newUnionType(ObjectFlagsNone, originFiltered) - } - } - // filtering could remove intersections so `ContainsIntersections` might be forwarded "incorrectly" - // it is purely an optimization hint so there is no harm in accidentally forwarding it - return c.getUnionTypeFromSortedList(filtered, t.AsUnionType().objectFlags&(ObjectFlagsPrimitiveUnion|ObjectFlagsContainsIntersections), nil /*alias*/, newOrigin) - } - if t.flags&TypeFlagsNever != 0 || f(t) { - return t - } - return c.neverType -} - -func (c *Checker) removeType(t *Type, targetType *Type) *Type { - return c.filterType(t, func(t *Type) bool { return t != targetType }) -} - -func containsType(types []*Type, t *Type) bool { - _, ok := slices.BinarySearchFunc(types, t, CompareTypes) - return ok -} - -func insertType(types []*Type, t *Type) ([]*Type, bool) { - if i, ok := slices.BinarySearchFunc(types, t, CompareTypes); !ok { - return slices.Insert(types, i, t), true - } - return types, false -} - -func countTypes(t *Type) int { - switch { - case t.flags&TypeFlagsUnion != 0: - return len(t.Types()) - case t.flags&TypeFlagsNever != 0: - return 0 - } - return 1 -} - -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) checkCrossProductUnion(types []*Type) bool { - size := c.getCrossProductUnionSize(types) - if size >= 100_000 { - c.error(c.currentNode, diagnostics.Expression_produces_a_union_type_that_is_too_complex_to_represent) - return false - } - return true -} - -func (c *Checker) getCrossProductUnionSize(types []*Type) int { - size := 1 - for _, t := range types { - switch { - case t.flags&TypeFlagsUnion != 0: - size *= len(t.Types()) - case t.flags&TypeFlagsNever != 0: - return 0 - } - } - return size -} - -func (c *Checker) getIndexType(t *Type) *Type { - return c.getIndexTypeEx(t, IndexFlagsNone) -} - -func (c *Checker) getIndexTypeEx(t *Type, indexFlags IndexFlags) *Type { - t = c.getReducedType(t) - switch { - case c.isNoInferType(t): - return c.getNoInferType(c.getIndexTypeEx(t.AsSubstitutionType().baseType, indexFlags)) - case c.shouldDeferIndexType(t, indexFlags): - return c.getIndexTypeForGenericType(t, indexFlags) - case t.flags&TypeFlagsUnion != 0: - return c.getIntersectionType(core.Map(t.Types(), func(t *Type) *Type { return c.getIndexTypeEx(t, indexFlags) })) - case t.flags&TypeFlagsIntersection != 0: - return c.getUnionType(core.Map(t.Types(), func(t *Type) *Type { return c.getIndexTypeEx(t, indexFlags) })) - case t.objectFlags&ObjectFlagsMapped != 0: - return c.getIndexTypeForMappedType(t, indexFlags) - case t == c.wildcardType: - return c.wildcardType - case t.flags&TypeFlagsUnknown != 0: - return c.neverType - case t.flags&(TypeFlagsAny|TypeFlagsNever) != 0: - return c.stringNumberSymbolType - } - include := core.IfElse(indexFlags&IndexFlagsNoIndexSignatures != 0, TypeFlagsStringLiteral, TypeFlagsStringLike) | - core.IfElse(indexFlags&IndexFlagsStringsOnly != 0, TypeFlagsNone, TypeFlagsNumberLike|TypeFlagsESSymbolLike) - return c.getLiteralTypeFromProperties(t, include, indexFlags == IndexFlagsNone) -} - -func (c *Checker) getExtractStringType(t *Type) *Type { - extractTypeAlias := c.getGlobalExtractSymbol() - if extractTypeAlias != nil { - return c.getTypeAliasInstantiation(extractTypeAlias, []*Type{t, c.stringType}, nil) - } - return c.stringType -} - -func (c *Checker) getLiteralTypeFromProperties(t *Type, include TypeFlags, includeOrigin bool) *Type { - var origin *Type - if includeOrigin && t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsReference) != 0 || t.alias != nil { - origin = c.newIndexType(t, IndexFlagsNone) - } - var types []*Type - for _, prop := range c.getPropertiesOfType(t) { - types = append(types, c.getLiteralTypeFromProperty(prop, include, false)) - } - for _, info := range c.getIndexInfosOfType(t) { - if info != c.enumNumberIndexInfo && c.isKeyTypeIncluded(info.keyType, include) { - if info.keyType == c.stringType && include&TypeFlagsNumber != 0 { - types = append(types, c.stringOrNumberType) - } else { - types = append(types, info.keyType) - } - } - } - return c.getUnionTypeEx(types, UnionReductionLiteral, nil, origin) -} - -func (c *Checker) getLiteralTypeFromProperty(prop *ast.Symbol, include TypeFlags, includeNonPublic bool) *Type { - if includeNonPublic || getDeclarationModifierFlagsFromSymbol(prop)&ast.ModifierFlagsNonPublicAccessibilityModifier == 0 { - t := c.valueSymbolLinks.Get(c.getLateBoundSymbol(prop)).nameType - if t == nil { - if prop.Name == ast.InternalSymbolNameDefault { - t = c.getStringLiteralType("default") - } else { - name := ast.GetNameOfDeclaration(prop.ValueDeclaration) - if name != nil { - t = c.getLiteralTypeFromPropertyName(name) - } - if t == nil && !IsKnownSymbol(prop) { - t = c.getStringLiteralType(ast.SymbolName(prop)) - } - } - } - if t != nil && t.flags&include != 0 { - return t - } - } - return c.neverType -} - -func (c *Checker) getLiteralTypeFromPropertyName(name *ast.Node) *Type { - if ast.IsPrivateIdentifier(name) { - return c.neverType - } - if ast.IsNumericLiteral(name) { - return c.getRegularTypeOfLiteralType(c.checkExpression(name)) - } - if ast.IsComputedPropertyName(name) { - return c.getRegularTypeOfLiteralType(c.checkComputedPropertyName(name)) - } - propertyName := ast.GetPropertyNameForPropertyNameNode(name) - if propertyName != ast.InternalSymbolNameMissing { - return c.getStringLiteralType(propertyName) - } - if ast.IsExpression(name) { - return c.getRegularTypeOfLiteralType(c.checkExpression(name)) - } - return c.neverType -} - -func (c *Checker) isKeyTypeIncluded(keyType *Type, include TypeFlags) bool { - return keyType.flags&include != 0 || - keyType.flags&TypeFlagsIntersection != 0 && core.Some(keyType.Types(), func(t *Type) bool { - return c.isKeyTypeIncluded(t, include) - }) -} - -func (c *Checker) checkComputedPropertyName(node *ast.Node) *Type { - links := c.typeNodeLinks.Get(node.Expression()) - if links.resolvedType == nil { - if (ast.IsTypeLiteralNode(node.Parent.Parent) || ast.IsClassLike(node.Parent.Parent) || ast.IsInterfaceDeclaration(node.Parent.Parent)) && - ast.IsBinaryExpression(node.Expression()) && node.Expression().AsBinaryExpression().OperatorToken.Kind == ast.KindInKeyword && - !ast.IsAccessor(node.Parent) { - links.resolvedType = c.errorType - return links.resolvedType - } - links.resolvedType = c.checkExpression(node.Expression()) - // This will allow types number, string, symbol or any. It will also allow enums, the unknown - // type, and any union of these types (like string | number). - if links.resolvedType.flags&TypeFlagsNullable != 0 || - !c.isTypeAssignableToKind(links.resolvedType, TypeFlagsStringLike|TypeFlagsNumberLike|TypeFlagsESSymbolLike) && - !c.isTypeAssignableTo(links.resolvedType, c.stringNumberSymbolType) { - c.error(node, diagnostics.A_computed_property_name_must_be_of_type_string_number_symbol_or_any) - } - } - return links.resolvedType -} - -func (c *Checker) isNoInferType(t *Type) bool { - // A NoInfer type is represented as a substitution type with a TypeFlags.Unknown constraint. - return t.flags&TypeFlagsSubstitution != 0 && t.AsSubstitutionType().constraint.flags&TypeFlagsUnknown != 0 -} - -func (c *Checker) getSubstitutionIntersection(t *Type) *Type { - if c.isNoInferType(t) { - return t.AsSubstitutionType().baseType - } - return c.getIntersectionType([]*Type{t.AsSubstitutionType().constraint, t.AsSubstitutionType().baseType}) -} - -func (c *Checker) shouldDeferIndexType(t *Type, indexFlags IndexFlags) bool { - return t.flags&TypeFlagsInstantiableNonPrimitive != 0 || - c.isGenericTupleType(t) || - c.isGenericMappedType(t) && (!c.hasDistributiveNameType(t) || c.getMappedTypeNameTypeKind(t) == MappedTypeNameTypeKindRemapping) || - t.flags&TypeFlagsUnion != 0 && indexFlags&IndexFlagsNoReducibleCheck == 0 && c.isGenericReducibleType(t) || - t.flags&TypeFlagsIntersection != 0 && c.maybeTypeOfKind(t, TypeFlagsInstantiable) && core.Some(t.Types(), c.IsEmptyAnonymousObjectType) -} - -// Ordinarily we reduce a keyof M, where M is a mapped type { [P in K as N

]: X }, to simply N. This however presumes -// that N distributes over union types, i.e. that N is equivalent to N | N | N. Specifically, we only -// want to perform the reduction when the name type of a mapped type is distributive with respect to the type variable -// introduced by the 'in' clause of the mapped type. Note that non-generic types are considered to be distributive because -// they're the same type regardless of what's being distributed over. -func (c *Checker) hasDistributiveNameType(mappedType *Type) bool { - typeVariable := c.getTypeParameterFromMappedType(mappedType) - var isDistributive func(*Type) bool - isDistributive = func(t *Type) bool { - switch { - case t.flags&(TypeFlagsAnyOrUnknown|TypeFlagsPrimitive|TypeFlagsNever|TypeFlagsTypeParameter|TypeFlagsObject|TypeFlagsNonPrimitive) != 0: - return true - case t.flags&TypeFlagsConditional != 0: - return t.AsConditionalType().root.isDistributive && t.AsConditionalType().checkType == typeVariable - case t.flags&TypeFlagsUnionOrIntersection != 0: - return core.Every(t.Types(), isDistributive) - case t.flags&TypeFlagsTemplateLiteral != 0: - return core.Every(t.AsTemplateLiteralType().types, isDistributive) - case t.flags&TypeFlagsIndexedAccess != 0: - return isDistributive(t.AsIndexedAccessType().objectType) && isDistributive(t.AsIndexedAccessType().indexType) - case t.flags&TypeFlagsSubstitution != 0: - return isDistributive(t.AsSubstitutionType().baseType) && isDistributive(t.AsSubstitutionType().constraint) - case t.flags&TypeFlagsStringMapping != 0: - return isDistributive(t.Target()) - default: - return false - } - } - nameType := c.getNameTypeFromMappedType(mappedType) - if nameType == nil { - nameType = typeVariable - } - return isDistributive(nameType) -} - -func (c *Checker) getMappedTypeNameTypeKind(t *Type) MappedTypeNameTypeKind { - nameType := c.getNameTypeFromMappedType(t) - if nameType == nil { - return MappedTypeNameTypeKindNone - } - if c.isTypeAssignableTo(nameType, c.getTypeParameterFromMappedType(t)) { - return MappedTypeNameTypeKindFiltering - } - return MappedTypeNameTypeKindRemapping -} - -func (c *Checker) getIndexTypeForGenericType(t *Type, indexFlags IndexFlags) *Type { - key := CachedTypeKey{ - kind: core.IfElse(indexFlags&IndexFlagsStringsOnly != 0, CachedTypeKindStringIndexType, CachedTypeKindIndexType), - typeId: t.id, - } - if indexType := c.cachedTypes[key]; indexType != nil { - return indexType - } - indexType := c.newIndexType(t, indexFlags&IndexFlagsStringsOnly) - c.cachedTypes[key] = indexType - return indexType -} - -// This roughly mirrors `resolveMappedTypeMembers` in the nongeneric case, except only reports a union of the keys calculated, -// rather than manufacturing the properties. We can't just fetch the `constraintType` since that would ignore mappings -// and mapping the `constraintType` directly ignores how mapped types map _properties_ and not keys (thus ignoring subtype -// reduction in the constraintType) when possible. -// @param noIndexSignatures Indicates if _string_ index signatures should be elided. (other index signatures are always reported) -func (c *Checker) getIndexTypeForMappedType(t *Type, indexFlags IndexFlags) *Type { - typeParameter := c.getTypeParameterFromMappedType(t) - constraintType := c.getConstraintTypeFromMappedType(t) - nameType := c.getNameTypeFromMappedType(core.OrElse(t.AsMappedType().target, t)) - if nameType == nil && indexFlags&IndexFlagsNoIndexSignatures == 0 { - // no mapping and no filtering required, just quickly bail to returning the constraint in the common case - return constraintType - } - var keyTypes []*Type - addMemberForKeyType := func(keyType *Type) { - propNameType := keyType - if nameType != nil { - propNameType = c.instantiateType(nameType, appendTypeMapping(t.AsMappedType().mapper, typeParameter, keyType)) - } - // `keyof` currently always returns `string | number` for concrete `string` index signatures - the below ternary keeps that behavior for mapped types - // See `getLiteralTypeFromProperties` where there's a similar ternary to cause the same behavior. - keyTypes = append(keyTypes, core.IfElse(propNameType == c.stringType, c.stringOrNumberType, propNameType)) - } - // Calling getApparentType on the `T` of a `keyof T` in the constraint type of a generic mapped type can - // trigger a circularity. For example, `T extends { [P in keyof T & string as Captitalize

]: any }` is - // a circular definition. For this reason, we only eagerly manifest the keys if the constraint is non-generic. - if c.isGenericIndexType(constraintType) { - if c.isMappedTypeWithKeyofConstraintDeclaration(t) { - // We have a generic index and a homomorphic mapping (but a distributive key remapping) - we need to defer - // the whole `keyof whatever` for later since it's not safe to resolve the shape of modifier type. - return c.getIndexTypeForGenericType(t, indexFlags) - } - // Include the generic component in the resulting type. - forEachType(constraintType, addMemberForKeyType) - } else if c.isMappedTypeWithKeyofConstraintDeclaration(t) { - modifiersType := c.getApparentType(c.getModifiersTypeFromMappedType(t)) - // The 'T' in 'keyof T' - c.forEachMappedTypePropertyKeyTypeAndIndexSignatureKeyType(modifiersType, TypeFlagsStringOrNumberLiteralOrUnique, indexFlags&IndexFlagsStringsOnly != 0, addMemberForKeyType) - } else { - forEachType(c.getLowerBoundOfKeyType(constraintType), addMemberForKeyType) - } - // We had to pick apart the constraintType to potentially map/filter it - compare the final resulting list with the - // original constraintType, so we can return the union that preserves aliases/origin data if possible. - var result *Type - if indexFlags&IndexFlagsNoIndexSignatures != 0 { - result = c.filterType(c.getUnionType(keyTypes), func(t *Type) bool { - return t.flags&(TypeFlagsAny|TypeFlagsString) == 0 - }) - } else { - result = c.getUnionType(keyTypes) - } - if result.flags&TypeFlagsUnion != 0 && constraintType.flags&TypeFlagsUnion != 0 && getTypeListKey(result.Types()) == getTypeListKey(constraintType.Types()) { - return constraintType - } - return result -} - -func (c *Checker) getIndexedAccessType(objectType *Type, indexType *Type) *Type { - return c.getIndexedAccessTypeEx(objectType, indexType, AccessFlagsNone, nil, nil) -} - -func (c *Checker) getIndexedAccessTypeEx(objectType *Type, indexType *Type, accessFlags AccessFlags, accessNode *ast.Node, alias *TypeAlias) *Type { - result := c.getIndexedAccessTypeOrUndefined(objectType, indexType, accessFlags, accessNode, alias) - if result == nil { - result = core.IfElse(accessNode != nil, c.errorType, c.unknownType) - } - return result -} - -func (c *Checker) getIndexedAccessTypeOrUndefined(objectType *Type, indexType *Type, accessFlags AccessFlags, accessNode *ast.Node, alias *TypeAlias) *Type { - if objectType == c.wildcardType || indexType == c.wildcardType { - return c.wildcardType - } - objectType = c.getReducedType(objectType) - // If the object type has a string index signature and no other members we know that the result will - // always be the type of that index signature and we can simplify accordingly. - if c.isStringIndexSignatureOnlyType(objectType) && indexType.flags&TypeFlagsNullable == 0 && c.isTypeAssignableToKind(indexType, TypeFlagsString|TypeFlagsNumber) { - indexType = c.stringType - } - // In noUncheckedIndexedAccess mode, indexed access operations that occur in an expression in a read position and resolve to - // an index signature have 'undefined' included in their type. - if c.compilerOptions.NoUncheckedIndexedAccess == core.TSTrue && accessFlags&AccessFlagsExpressionPosition != 0 { - accessFlags |= AccessFlagsIncludeUndefined - } - // If the index type is generic, or if the object type is generic and doesn't originate in an expression and - // the operation isn't exclusively indexing the fixed (non-variadic) portion of a tuple type, we are performing - // a higher-order index access where we cannot meaningfully access the properties of the object type. Note that - // for a generic T and a non-generic K, we eagerly resolve T[K] if it originates in an expression. This is to - // preserve backwards compatibility. For example, an element access 'this["foo"]' has always been resolved - // eagerly using the constraint type of 'this' at the given location. - if c.shouldDeferIndexedAccessType(objectType, indexType, accessNode) { - if objectType.flags&TypeFlagsAnyOrUnknown != 0 { - return objectType - } - // Defer the operation by creating an indexed access type. - persistentAccessFlags := accessFlags & AccessFlagsPersistent - key := getIndexedAccessKey(objectType, indexType, accessFlags, alias) - t := c.indexedAccessTypes[key] - if t == nil { - t = c.newIndexedAccessType(objectType, indexType, persistentAccessFlags) - t.alias = alias - c.indexedAccessTypes[key] = t - } - return t - } - // In the following we resolve T[K] to the type of the property in T selected by K. - // We treat boolean as different from other unions to improve errors; - // skipping straight to getPropertyTypeForIndexType gives errors with 'boolean' instead of 'true'. - apparentObjectType := c.getReducedApparentType(objectType) - if indexType.flags&TypeFlagsUnion != 0 && indexType.flags&TypeFlagsBoolean == 0 { - var propTypes []*Type - wasMissingProp := false - for _, t := range indexType.Types() { - propType := c.getPropertyTypeForIndexType(objectType, apparentObjectType, t, indexType, accessNode, accessFlags|core.IfElse(wasMissingProp, AccessFlagsSuppressNoImplicitAnyError, 0)) - if propType != nil { - propTypes = append(propTypes, propType) - } else if accessNode == nil { - // If there's no error node, we can immediately stop, since error reporting is off - return nil - } else { - // Otherwise we set a flag and return at the end of the loop so we still mark all errors - wasMissingProp = true - } - } - if wasMissingProp { - return nil - } - if accessFlags&AccessFlagsWriting != 0 { - return c.getIntersectionTypeEx(propTypes, IntersectionFlagsNone, alias) - } - return c.getUnionTypeEx(propTypes, UnionReductionLiteral, alias, nil) - } - return c.getPropertyTypeForIndexType(objectType, apparentObjectType, indexType, indexType, accessNode, accessFlags|AccessFlagsCacheSymbol|AccessFlagsReportDeprecated) -} - -func (c *Checker) getPropertyTypeForIndexType(originalObjectType *Type, objectType *Type, indexType *Type, fullIndexType *Type, accessNode *ast.Node, accessFlags AccessFlags) *Type { - var accessExpression *ast.Node - if accessNode != nil && ast.IsElementAccessExpression(accessNode) { - accessExpression = accessNode - } - var propName string - var hasPropName bool - if !(accessNode != nil && ast.IsPrivateIdentifier(accessNode)) { - propName = c.getPropertyNameFromIndex(indexType, accessNode) - hasPropName = propName != ast.InternalSymbolNameMissing - } - if hasPropName { - if accessFlags&AccessFlagsContextual != 0 { - t := c.getTypeOfPropertyOfContextualType(objectType, propName) - if t == nil { - t = c.anyType - } - return t - } - prop := c.getPropertyOfType(objectType, propName) - if prop != nil { - // !!! - // if accessFlags&AccessFlagsReportDeprecated != 0 && accessNode != nil && len(prop.declarations) != 0 && c.isDeprecatedSymbol(prop) && c.isUncalledFunctionReference(accessNode, prop) { - // deprecatedNode := /* TODO(TS-TO-GO) QuestionQuestionToken BinaryExpression: accessExpression?.argumentExpression ?? (isIndexedAccessTypeNode(accessNode) ? accessNode.indexType : accessNode) */ TODO - // c.addDeprecatedSuggestion(deprecatedNode, prop.declarations, propName /* as string */) - // } - if accessExpression != nil { - c.markPropertyAsReferenced(prop, accessExpression, c.isSelfTypeAccess(accessExpression.Expression(), objectType.symbol)) - if c.isAssignmentToReadonlyEntity(accessExpression, prop, getAssignmentTargetKind(accessExpression)) { - c.error(accessExpression.AsElementAccessExpression().ArgumentExpression, diagnostics.Cannot_assign_to_0_because_it_is_a_read_only_property, c.symbolToString(prop)) - return nil - } - if accessFlags&AccessFlagsCacheSymbol != 0 { - c.symbolNodeLinks.Get(accessNode).resolvedSymbol = prop - } - if c.isThisPropertyAccessInConstructor(accessExpression, prop) { - return c.autoType - } - } - var propType *Type - if accessFlags&AccessFlagsWriting != 0 { - propType = c.getWriteTypeOfSymbol(prop) - } else { - propType = c.getTypeOfSymbol(prop) - } - switch { - case accessExpression != nil && getAssignmentTargetKind(accessExpression) != AssignmentKindDefinite: - return c.getFlowTypeOfReference(accessExpression, propType) - case accessNode != nil && ast.IsIndexedAccessTypeNode(accessNode) && c.containsMissingType(propType): - return c.getUnionType([]*Type{propType, c.undefinedType}) - default: - return propType - } - } - if everyType(objectType, isTupleType) && isNumericLiteralName(propName) { - index := jsnum.FromString(propName) - if accessNode != nil && everyType(objectType, func(t *Type) bool { - return t.TargetTupleType().combinedFlags&ElementFlagsVariable == 0 - }) && accessFlags&AccessFlagsAllowMissing == 0 { - indexNode := getIndexNodeForAccessExpression(accessNode) - if isTupleType(objectType) { - if index < 0 { - c.error(indexNode, diagnostics.A_tuple_type_cannot_be_indexed_with_a_negative_value) - return c.undefinedType - } - c.error(indexNode, diagnostics.Tuple_type_0_of_length_1_has_no_element_at_index_2, c.TypeToString(objectType), c.getTypeReferenceArity(objectType), propName) - } else { - c.error(indexNode, diagnostics.Property_0_does_not_exist_on_type_1, propName, c.TypeToString(objectType)) - } - } - if index >= 0 { - c.errorIfWritingToReadonlyIndex(c.getIndexInfoOfType(objectType, c.numberType), objectType, accessExpression) - return c.getTupleElementTypeOutOfStartCount(objectType, index, core.IfElse(accessFlags&AccessFlagsIncludeUndefined != 0, c.missingType, nil)) - } - } - } - if indexType.flags&TypeFlagsNullable == 0 && c.isTypeAssignableToKind(indexType, TypeFlagsStringLike|TypeFlagsNumberLike|TypeFlagsESSymbolLike) { - if objectType.flags&(TypeFlagsAny|TypeFlagsNever) != 0 { - return objectType - } - // If no index signature is applicable, we default to the string index signature. In effect, this means the string - // index signature applies even when accessing with a symbol-like type. - indexInfo := c.getApplicableIndexInfo(objectType, indexType) - if indexInfo == nil { - indexInfo = c.getIndexInfoOfType(objectType, c.stringType) - } - if indexInfo != nil { - if accessFlags&AccessFlagsNoIndexSignatures != 0 && indexInfo.keyType != c.numberType { - if accessExpression != nil { - if accessFlags&AccessFlagsWriting != 0 { - c.error(accessExpression, diagnostics.Type_0_is_generic_and_can_only_be_indexed_for_reading, c.TypeToString(originalObjectType)) - } else { - c.error(accessExpression, diagnostics.Type_0_cannot_be_used_to_index_type_1, c.TypeToString(indexType), c.TypeToString(originalObjectType)) - } - } - return nil - } - if accessNode != nil && indexInfo.keyType == c.stringType && !c.isTypeAssignableToKind(indexType, TypeFlagsString|TypeFlagsNumber) { - indexNode := getIndexNodeForAccessExpression(accessNode) - c.error(indexNode, diagnostics.Type_0_cannot_be_used_as_an_index_type, c.TypeToString(indexType)) - if accessFlags&AccessFlagsIncludeUndefined != 0 { - return c.getUnionType([]*Type{indexInfo.valueType, c.missingType}) - } else { - return indexInfo.valueType - } - } - c.errorIfWritingToReadonlyIndex(indexInfo, objectType, accessExpression) - // When accessing an enum object with its own type, - // e.g. E[E.A] for enum E { A }, undefined shouldn't - // be included in the result type - if accessFlags&AccessFlagsIncludeUndefined != 0 && - !(objectType.symbol != nil && - objectType.symbol.Flags&(ast.SymbolFlagsRegularEnum|ast.SymbolFlagsConstEnum) != 0 && - (indexType.symbol != nil && - indexType.flags&TypeFlagsEnumLiteral != 0 && - c.getParentOfSymbol(indexType.symbol) == objectType.symbol)) { - return c.getUnionType([]*Type{indexInfo.valueType, c.missingType}) - } - return indexInfo.valueType - } - if indexType.flags&TypeFlagsNever != 0 { - return c.neverType - } - if accessExpression != nil && !isConstEnumObjectType(objectType) { - if isObjectLiteralType(objectType) { - if c.noImplicitAny && indexType.flags&(TypeFlagsStringLiteral|TypeFlagsNumberLiteral) != 0 { - c.diagnostics.Add(createDiagnosticForNode(accessExpression, diagnostics.Property_0_does_not_exist_on_type_1, indexType.AsLiteralType().value, c.TypeToString(objectType))) - return c.undefinedType - } else if indexType.flags&(TypeFlagsNumber|TypeFlagsString) != 0 { - types := core.Map(objectType.AsStructuredType().properties, func(prop *ast.Symbol) *Type { - return c.getTypeOfSymbol(prop) - }) - return c.getUnionType(append(types, c.undefinedType)) - } - } - if objectType.symbol == c.globalThisSymbol && hasPropName && c.globalThisSymbol.Exports[propName] != nil && c.globalThisSymbol.Exports[propName].Flags&ast.SymbolFlagsBlockScoped != 0 { - c.error(accessExpression, diagnostics.Property_0_does_not_exist_on_type_1, propName, c.TypeToString(objectType)) - } else if c.noImplicitAny && accessFlags&AccessFlagsSuppressNoImplicitAnyError == 0 { - if hasPropName && c.typeHasStaticProperty(propName, objectType) { - typeName := c.TypeToString(objectType) - c.error(accessExpression, diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_to_access_the_static_member_2_instead, propName /* as string */, typeName, typeName+"["+scanner.GetTextOfNode(accessExpression.AsElementAccessExpression().ArgumentExpression)+"]") - } else if c.getIndexTypeOfType(objectType, c.numberType) != nil { - c.error(accessExpression.AsElementAccessExpression().ArgumentExpression, diagnostics.Element_implicitly_has_an_any_type_because_index_expression_is_not_of_type_number) - } else { - var suggestion string - if hasPropName { - suggestion = c.getSuggestionForNonexistentProperty(propName, objectType) - } - if suggestion != "" { - c.error(accessExpression.AsElementAccessExpression().ArgumentExpression, diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, propName /* as string */, c.TypeToString(objectType), suggestion) - } else { - suggestion = c.getSuggestionForNonexistentIndexSignature(objectType, accessExpression, indexType) - if suggestion != "" { - c.error(accessExpression, diagnostics.Element_implicitly_has_an_any_type_because_type_0_has_no_index_signature_Did_you_mean_to_call_1, c.TypeToString(objectType), suggestion) - } else { - var diagnostic *ast.Diagnostic - switch { - case indexType.flags&TypeFlagsEnumLiteral != 0: - diagnostic = NewDiagnosticForNode(accessExpression, diagnostics.Property_0_does_not_exist_on_type_1, "["+c.TypeToString(indexType)+"]", c.TypeToString(objectType)) - case indexType.flags&TypeFlagsUniqueESSymbol != 0: - symbolName := c.getFullyQualifiedName(indexType.symbol, accessExpression) - diagnostic = NewDiagnosticForNode(accessExpression, diagnostics.Property_0_does_not_exist_on_type_1, "["+symbolName+"]", c.TypeToString(objectType)) - case indexType.flags&TypeFlagsStringLiteral != 0: - diagnostic = NewDiagnosticForNode(accessExpression, diagnostics.Property_0_does_not_exist_on_type_1, indexType.AsLiteralType().value, c.TypeToString(objectType)) - case indexType.flags&TypeFlagsNumberLiteral != 0: - diagnostic = NewDiagnosticForNode(accessExpression, diagnostics.Property_0_does_not_exist_on_type_1, indexType.AsLiteralType().value, c.TypeToString(objectType)) - case indexType.flags&(TypeFlagsNumber|TypeFlagsString) != 0: - diagnostic = NewDiagnosticForNode(accessExpression, diagnostics.No_index_signature_with_a_parameter_of_type_0_was_found_on_type_1, c.TypeToString(indexType), c.TypeToString(objectType)) - } - c.diagnostics.Add(NewDiagnosticChainForNode(diagnostic, accessExpression, diagnostics.Element_implicitly_has_an_any_type_because_expression_of_type_0_can_t_be_used_to_index_type_1, c.TypeToString(fullIndexType), c.TypeToString(objectType))) - } - } - } - } - return nil - } - } - if accessFlags&AccessFlagsAllowMissing != 0 && isObjectLiteralType(objectType) { - return c.undefinedType - } - if accessNode != nil { - indexNode := getIndexNodeForAccessExpression(accessNode) - if indexNode.Kind != ast.KindBigIntLiteral && indexType.flags&(TypeFlagsStringLiteral|TypeFlagsNumberLiteral) != 0 { - c.error(indexNode, diagnostics.Property_0_does_not_exist_on_type_1, indexType.AsLiteralType().value, c.TypeToString(objectType)) - } else if indexType.flags&(TypeFlagsString|TypeFlagsNumber) != 0 { - c.error(indexNode, diagnostics.Type_0_has_no_matching_index_signature_for_type_1, c.TypeToString(objectType), c.TypeToString(indexType)) - } else { - var typeString string - if indexNode.Kind == ast.KindBigIntLiteral { - typeString = "bigint" - } else { - typeString = c.TypeToString(indexType) - } - c.error(indexNode, diagnostics.Type_0_cannot_be_used_as_an_index_type, typeString) - } - } - if IsTypeAny(indexType) { - return indexType - } - return nil -} - -func (c *Checker) typeHasStaticProperty(propName string, containingType *Type) bool { - if containingType.symbol != nil { - prop := c.getPropertyOfType(c.getTypeOfSymbol(containingType.symbol), propName) - return prop != nil && prop.ValueDeclaration != nil && ast.IsStatic(prop.ValueDeclaration) - } - return false -} - -func (c *Checker) getSuggestionForNonexistentProperty(name string, containingType *Type) string { - symbol := c.getSpellingSuggestionForName(name, c.getPropertiesOfType(containingType), ast.SymbolFlagsValue) - if symbol != nil { - return symbol.Name - } - return "" -} - -func (c *Checker) getSuggestionForNonexistentIndexSignature(objectType *Type, expr *ast.Node, keyedType *Type) string { - // check if object type has setter or getter - hasProp := func(name string) bool { - prop := c.getPropertyOfObjectType(objectType, name) - if prop != nil { - s := c.getSingleCallSignature(c.getTypeOfSymbol(prop)) - return s != nil && c.getMinArgumentCount(s) >= 1 && c.isTypeAssignableTo(keyedType, c.getTypeAtPosition(s, 0)) - } - return false - } - suggestedMethod := core.IfElse(ast.IsAssignmentTarget(expr), "set", "get") - if !hasProp(suggestedMethod) { - return "" - } - suggestion := tryGetPropertyAccessOrIdentifierToString(expr.Expression()) - if suggestion == "" { - return suggestedMethod - } - return suggestion + "." + suggestedMethod -} - -func (c *Checker) getSuggestedTypeForNonexistentStringLiteralType(source *Type, target *Type) *Type { - candidates := core.Filter(target.Types(), func(t *Type) bool { return t.flags&TypeFlagsStringLiteral != 0 }) - return core.GetSpellingSuggestion(getStringLiteralValue(source), candidates, getStringLiteralValue) -} - -func getIndexNodeForAccessExpression(accessNode *ast.Node) *ast.Node { - switch accessNode.Kind { - case ast.KindElementAccessExpression: - return accessNode.AsElementAccessExpression().ArgumentExpression - case ast.KindIndexedAccessType: - return accessNode.AsIndexedAccessTypeNode().IndexType - case ast.KindComputedPropertyName: - return accessNode.AsComputedPropertyName().Expression - } - return accessNode -} - -func (c *Checker) errorIfWritingToReadonlyIndex(indexInfo *IndexInfo, objectType *Type, accessExpression *ast.Node) { - if indexInfo != nil && indexInfo.isReadonly && accessExpression != nil && (ast.IsAssignmentTarget(accessExpression) || isDeleteTarget(accessExpression)) { - c.error(accessExpression, diagnostics.Index_signature_in_type_0_only_permits_reading, c.TypeToString(objectType)) - } -} - -func (c *Checker) isSelfTypeAccess(name *ast.Node, parent *ast.Symbol) bool { - return name.Kind == ast.KindThisKeyword || parent != nil && ast.IsEntityNameExpression(name) && parent == c.getResolvedSymbol(ast.GetFirstIdentifier(name)) -} - -func (c *Checker) isAssignmentToReadonlyEntity(expr *ast.Node, symbol *ast.Symbol, assignmentKind AssignmentKind) bool { - if assignmentKind == AssignmentKindNone { - // no assignment means it doesn't matter whether the entity is readonly - return false - } - if ast.IsAccessExpression(expr) { - node := ast.SkipParentheses(expr.Expression()) - if ast.IsIdentifier(node) { - expressionSymbol := c.getResolvedSymbol(node) - // CommonJS module.exports is never readonly - if expressionSymbol.Flags&ast.SymbolFlagsModuleExports != 0 { - return false - } - // references through namespace import should be readonly - if expressionSymbol.Flags&ast.SymbolFlagsAlias != 0 { - declaration := c.getDeclarationOfAliasSymbol(expressionSymbol) - return declaration != nil && ast.IsNamespaceImport(declaration) - } - } - } - if c.isReadonlySymbol(symbol) { - // Allow assignments to readonly properties within constructors of the same class declaration. - if symbol.Flags&ast.SymbolFlagsProperty != 0 && ast.IsAccessExpression(expr) && expr.Expression().Kind == ast.KindThisKeyword { - // Look for if this is the constructor for the class that `symbol` is a property of. - ctor := c.getControlFlowContainer(expr) - if ctor == nil || !ast.IsConstructorDeclaration(ctor) { - return true - } - if symbol.ValueDeclaration != nil { - isAssignmentDeclaration := ast.IsBinaryExpression(symbol.ValueDeclaration) - isLocalPropertyDeclaration := ctor.Parent == symbol.ValueDeclaration.Parent - isLocalParameterProperty := ctor == symbol.ValueDeclaration.Parent - isLocalThisPropertyAssignment := isAssignmentDeclaration && symbol.Parent.ValueDeclaration == ctor.Parent - isLocalThisPropertyAssignmentConstructorFunction := isAssignmentDeclaration && symbol.Parent.ValueDeclaration == ctor - isWriteableSymbol := isLocalPropertyDeclaration || isLocalParameterProperty || isLocalThisPropertyAssignment || isLocalThisPropertyAssignmentConstructorFunction - return !isWriteableSymbol - } - } - return true - } - return false -} - -func (c *Checker) isThisPropertyAccessInConstructor(node *ast.Node, prop *ast.Symbol) bool { - var constructor *ast.Node - if kind, location := c.isConstructorDeclaredThisProperty(prop); kind == thisAssignmentDeclarationConstructor { - constructor = location - } else if isThisProperty(node) && c.isAutoTypedProperty(prop) { - constructor = c.getDeclaringConstructor(prop) - } - return ast.GetThisContainer(node, true /*includeArrowFunctions*/, false /*includeClassComputedPropertyName*/) == constructor -} - -func (c *Checker) isAutoTypedProperty(symbol *ast.Symbol) bool { - // A property is auto-typed when its declaration has no type annotation or initializer and we're in - // noImplicitAny mode or a .js file. - declaration := symbol.ValueDeclaration - return declaration != nil && ast.IsPropertyDeclaration(declaration) && declaration.Type() == nil && declaration.Initializer() == nil && c.noImplicitAny -} - -func (c *Checker) getDeclaringConstructor(symbol *ast.Symbol) *ast.Node { - for _, declaration := range symbol.Declarations { - container := ast.GetThisContainer(declaration, false /*includeArrowFunctions*/, false /*includeClassComputedPropertyName*/) - if container != nil && ast.IsConstructorDeclaration(container) { - return container - } - } - return nil -} - -func (c *Checker) getPropertyNameFromIndex(indexType *Type, accessNode *ast.Node) string { - if isTypeUsableAsPropertyName(indexType) { - return getPropertyNameFromType(indexType) - } - if accessNode != nil && ast.IsPropertyName(accessNode) { - return ast.GetPropertyNameForPropertyNameNode(accessNode) - } - return ast.InternalSymbolNameMissing -} - -func (c *Checker) isStringIndexSignatureOnlyTypeWorker(t *Type) bool { - return t.flags&TypeFlagsObject != 0 && !c.isGenericMappedType(t) && len(c.getPropertiesOfType(t)) == 0 && len(c.getIndexInfosOfType(t)) == 1 && c.getIndexInfoOfType(t, c.stringType) != nil || - t.flags&TypeFlagsUnionOrIntersection != 0 && core.Every(t.Types(), c.isStringIndexSignatureOnlyType) -} - -func (c *Checker) shouldDeferIndexedAccessType(objectType *Type, indexType *Type, accessNode *ast.Node) bool { - if c.isGenericIndexType(indexType) { - return true - } - if accessNode != nil && !ast.IsIndexedAccessTypeNode(accessNode) { - return c.isGenericTupleType(objectType) && !indexTypeLessThan(indexType, getTotalFixedElementCount(objectType.TargetTupleType())) - } - return c.isGenericObjectType(objectType) && !(isTupleType(objectType) && indexTypeLessThan(indexType, getTotalFixedElementCount(objectType.TargetTupleType()))) || - c.isGenericReducibleType(objectType) -} - -func indexTypeLessThan(indexType *Type, limit int) bool { - return everyType(indexType, func(t *Type) bool { - if t.flags&TypeFlagsStringOrNumberLiteral != 0 { - propName := getPropertyNameFromType(t) - if isNumericLiteralName(propName) { - index := jsnum.FromString(propName) - return index >= 0 && index < jsnum.Number(limit) - } - } - return false - }) -} - -func (c *Checker) getNoInferType(t *Type) *Type { - if c.isNoInferTargetType(t) { - return c.getOrCreateSubstitutionType(t, c.unknownType) - } - return t -} - -func (c *Checker) isNoInferTargetType(t *Type) bool { - // This is effectively a more conservative and predictable form of couldContainTypeVariables. We want to - // preserve NoInfer only for types that could contain type variables, but we don't want to exhaustively - // examine all object type members. - return t.flags&TypeFlagsUnionOrIntersection != 0 && core.Some(t.AsUnionOrIntersectionType().types, c.isNoInferTargetType) || - t.flags&TypeFlagsSubstitution != 0 && !c.isNoInferType(t) && c.isNoInferTargetType(t.AsSubstitutionType().baseType) || - t.flags&TypeFlagsObject != 0 && !c.IsEmptyAnonymousObjectType(t) || - t.flags&(TypeFlagsInstantiable & ^TypeFlagsSubstitution) != 0 && !c.isPatternLiteralType(t) -} - -func (c *Checker) getSubstitutionType(baseType *Type, constraint *Type) *Type { - if constraint.flags&TypeFlagsAnyOrUnknown != 0 || constraint == baseType || baseType.flags&TypeFlagsAny != 0 { - return baseType - } - return c.getOrCreateSubstitutionType(baseType, constraint) -} - -func (c *Checker) getOrCreateSubstitutionType(baseType *Type, constraint *Type) *Type { - key := SubstitutionTypeKey{baseId: baseType.id, constraintId: constraint.id} - if cached := c.substitutionTypes[key]; cached != nil { - return cached - } - result := c.newSubstitutionType(baseType, constraint) - c.substitutionTypes[key] = result - return result -} - -func (c *Checker) getBaseConstraintOrType(t *Type) *Type { - constraint := c.getBaseConstraintOfType(t) - if constraint != nil { - return constraint - } - return t -} - -func (c *Checker) getBaseConstraintOfType(t *Type) *Type { - if t.flags&(TypeFlagsInstantiableNonPrimitive|TypeFlagsUnionOrIntersection|TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0 || c.isGenericTupleType(t) { - constraint := c.getResolvedBaseConstraint(t, nil) - if constraint != c.noConstraintType && constraint != c.circularConstraintType { - return constraint - } - return nil - } - if t.flags&TypeFlagsIndex != 0 { - return c.stringNumberSymbolType - } - return nil -} - -func (c *Checker) getResolvedBaseConstraint(t *Type, stack []RecursionId) *Type { - constrained := t.AsConstrainedType() - if constrained == nil { - return t - } - if constrained.resolvedBaseConstraint != nil { - return constrained.resolvedBaseConstraint - } - if !c.pushTypeResolution(t, TypeSystemPropertyNameResolvedBaseConstraint) { - return c.circularConstraintType - } - var constraint *Type - // We always explore at least 10 levels of nested constraints. Thereafter, we continue to explore - // up to 50 levels of nested constraints provided there are no "deeply nested" types on the stack - // (i.e. no types for which five instantiations have been recorded on the stack). If we reach 50 - // levels of nesting, we are presumably exploring a repeating pattern with a long cycle that hasn't - // yet triggered the deeply nested limiter. We have no test cases that actually get to 50 levels of - // nesting, so it is effectively just a safety stop. - identity := getRecursionIdentity(t) - if len(stack) < 10 || len(stack) < 50 && !slices.Contains(stack, identity) { - constraint = c.computeBaseConstraint(c.getSimplifiedType(t /*writing*/, false), append(stack, identity)) - } - if !c.popTypeResolution() { - if t.flags&TypeFlagsTypeParameter != 0 { - errorNode := c.getConstraintDeclaration(t) - if errorNode != nil { - diagnostic := c.error(errorNode, diagnostics.Type_parameter_0_has_a_circular_constraint, c.TypeToString(t)) - if c.currentNode != nil && !isNodeDescendantOf(errorNode, c.currentNode) && !isNodeDescendantOf(c.currentNode, errorNode) { - diagnostic.AddRelatedInfo(NewDiagnosticForNode(c.currentNode, diagnostics.Circularity_originates_in_type_at_this_location)) - } - } - } - constraint = c.circularConstraintType - } - if constraint == nil { - constraint = c.noConstraintType - } - if constrained.resolvedBaseConstraint == nil { - constrained.resolvedBaseConstraint = constraint - } - return constraint -} - -func (c *Checker) computeBaseConstraint(t *Type, stack []RecursionId) *Type { - switch { - case t.flags&TypeFlagsTypeParameter != 0: - constraint := c.getConstraintFromTypeParameter(t) - if t.AsTypeParameter().isThisType { - return constraint - } - return c.getNextBaseConstraint(constraint, stack) - case t.flags&TypeFlagsUnionOrIntersection != 0: - types := t.Types() - constraints := make([]*Type, 0, len(types)) - different := false - for _, s := range types { - constraint := c.getNextBaseConstraint(s, stack) - if constraint != nil { - if constraint != s { - different = true - } - constraints = append(constraints, constraint) - } else { - different = true - } - } - if !different { - return t - } - switch { - case t.flags&TypeFlagsUnion != 0 && len(constraints) == len(types): - return c.getUnionType(constraints) - case t.flags&TypeFlagsIntersection != 0 && len(constraints) != 0: - return c.getIntersectionType(constraints) - } - return nil - case t.flags&TypeFlagsIndex != 0: - return c.stringNumberSymbolType - case t.flags&TypeFlagsTemplateLiteral != 0: - types := t.Types() - constraints := make([]*Type, 0, len(types)) - for _, s := range types { - constraint := c.getNextBaseConstraint(s, stack) - if constraint != nil { - constraints = append(constraints, constraint) - } - } - if len(constraints) == len(types) { - return c.getTemplateLiteralType(t.AsTemplateLiteralType().texts, constraints) - } - return c.stringType - case t.flags&TypeFlagsStringMapping != 0: - constraint := c.getNextBaseConstraint(t.Target(), stack) - if constraint != nil && constraint != t.Target() { - return c.getStringMappingType(t.symbol, constraint) - } - return c.stringType - case t.flags&TypeFlagsIndexedAccess != 0: - if c.isMappedTypeGenericIndexedAccess(t) { - // For indexed access types of the form { [P in K]: E }[X], where K is non-generic and X is generic, - // we substitute an instantiation of E where P is replaced with X. - return c.getNextBaseConstraint(c.substituteIndexedMappedType(t.AsIndexedAccessType().objectType, t.AsIndexedAccessType().indexType), stack) - } - baseObjectType := c.getNextBaseConstraint(t.AsIndexedAccessType().objectType, stack) - baseIndexType := c.getNextBaseConstraint(t.AsIndexedAccessType().indexType, stack) - if baseObjectType == nil || baseIndexType == nil { - return nil - } - return c.getNextBaseConstraint(c.getIndexedAccessTypeOrUndefined(baseObjectType, baseIndexType, t.AsIndexedAccessType().accessFlags, nil, nil), stack) - case t.flags&TypeFlagsConditional != 0: - return c.getNextBaseConstraint(c.getConstraintFromConditionalType(t), stack) - case t.flags&TypeFlagsSubstitution != 0: - return c.getNextBaseConstraint(c.getSubstitutionIntersection(t), stack) - case c.isGenericTupleType(t): - // We substitute constraints for variadic elements only when the constraints are array types or - // non-variadic tuple types as we want to avoid further (possibly unbounded) recursion. - elementTypes := c.getElementTypes(t) - elementInfos := t.TargetTupleType().elementInfos - newElements := make([]*Type, 0, len(elementTypes)) - for i, v := range elementTypes { - newElement := v - if v.flags&TypeFlagsTypeParameter != 0 && elementInfos[i].flags&ElementFlagsVariadic != 0 { - constraint := c.getNextBaseConstraint(v, stack) - if constraint != nil && constraint != v && everyType(constraint, func(n *Type) bool { return c.isArrayOrTupleType(n) && !c.isGenericTupleType(n) }) { - newElement = constraint - } - } - newElements = append(newElements, newElement) - } - return c.createTupleTypeEx(newElements, elementInfos, t.TargetTupleType().readonly) - } - return t -} - -func (c *Checker) getNextBaseConstraint(t *Type, stack []RecursionId) *Type { - if t == nil { - return nil - } - constraint := c.getResolvedBaseConstraint(t, stack) - if constraint == c.noConstraintType || constraint == c.circularConstraintType { - return nil - } - return constraint -} - -// Return true if type might be of the given kind. A union or intersection type might be of a given -// kind if at least one constituent type is of the given kind. -func (c *Checker) maybeTypeOfKind(t *Type, kind TypeFlags) bool { - if t.flags&kind != 0 { - return true - } - if t.flags&TypeFlagsUnionOrIntersection != 0 { - for _, t := range t.Types() { - if c.maybeTypeOfKind(t, kind) { - return true - } - } - } - return false -} - -func (c *Checker) maybeTypeOfKindConsideringBaseConstraint(t *Type, kind TypeFlags) bool { - if c.maybeTypeOfKind(t, kind) { - return true - } - baseConstraint := c.getBaseConstraintOrType(t) - return baseConstraint != nil && c.maybeTypeOfKind(baseConstraint, kind) -} - -func (c *Checker) allTypesAssignableToKind(source *Type, kind TypeFlags) bool { - return c.allTypesAssignableToKindEx(source, kind, false) -} - -func (c *Checker) allTypesAssignableToKindEx(source *Type, kind TypeFlags, strict bool) bool { - if source.flags&TypeFlagsUnion != 0 { - return core.Every(source.Types(), func(subType *Type) bool { - return c.allTypesAssignableToKindEx(subType, kind, strict) - }) - } - return c.isTypeAssignableToKindEx(source, kind, strict) -} - -func (c *Checker) isTypeAssignableToKind(source *Type, kind TypeFlags) bool { - return c.isTypeAssignableToKindEx(source, kind, false) -} - -func (c *Checker) isTypeAssignableToKindEx(source *Type, kind TypeFlags, strict bool) bool { - if source.flags&kind != 0 { - return true - } - if strict && source.flags&(TypeFlagsAnyOrUnknown|TypeFlagsVoid|TypeFlagsUndefined|TypeFlagsNull) != 0 { - return false - } - return kind&TypeFlagsNumberLike != 0 && c.isTypeAssignableTo(source, c.numberType) || - kind&TypeFlagsBigIntLike != 0 && c.isTypeAssignableTo(source, c.bigintType) || - kind&TypeFlagsStringLike != 0 && c.isTypeAssignableTo(source, c.stringType) || - kind&TypeFlagsBooleanLike != 0 && c.isTypeAssignableTo(source, c.booleanType) || - kind&TypeFlagsVoid != 0 && c.isTypeAssignableTo(source, c.voidType) || - kind&TypeFlagsNever != 0 && c.isTypeAssignableTo(source, c.neverType) || - kind&TypeFlagsNull != 0 && c.isTypeAssignableTo(source, c.nullType) || - kind&TypeFlagsUndefined != 0 && c.isTypeAssignableTo(source, c.undefinedType) || - kind&TypeFlagsESSymbol != 0 && c.isTypeAssignableTo(source, c.esSymbolType) || - kind&TypeFlagsNonPrimitive != 0 && c.isTypeAssignableTo(source, c.nonPrimitiveType) -} - -func isConstEnumObjectType(t *Type) bool { - return t.objectFlags&ObjectFlagsAnonymous != 0 && t.symbol != nil && isConstEnumSymbol(t.symbol) -} - -func isConstEnumSymbol(symbol *ast.Symbol) bool { - return symbol.Flags&ast.SymbolFlagsConstEnum != 0 -} - -func (c *Checker) compareProperties(sourceProp *ast.Symbol, targetProp *ast.Symbol, compareTypes func(source *Type, target *Type) Ternary) Ternary { - // Two members are considered identical when - // - they are public properties with identical names, optionality, and types, - // - they are private or protected properties originating in the same declaration and having identical types - if sourceProp == targetProp { - return TernaryTrue - } - sourcePropAccessibility := getDeclarationModifierFlagsFromSymbol(sourceProp) & ast.ModifierFlagsNonPublicAccessibilityModifier - targetPropAccessibility := getDeclarationModifierFlagsFromSymbol(targetProp) & ast.ModifierFlagsNonPublicAccessibilityModifier - if sourcePropAccessibility != targetPropAccessibility { - return TernaryFalse - } - if sourcePropAccessibility != ast.ModifierFlagsNone { - if c.getTargetSymbol(sourceProp) != c.getTargetSymbol(targetProp) { - return TernaryFalse - } - } else { - if (sourceProp.Flags & ast.SymbolFlagsOptional) != (targetProp.Flags & ast.SymbolFlagsOptional) { - return TernaryFalse - } - } - if c.isReadonlySymbol(sourceProp) != c.isReadonlySymbol(targetProp) { - return TernaryFalse - } - return compareTypes(c.getTypeOfSymbol(sourceProp), c.getTypeOfSymbol(targetProp)) -} - -func compareTypesEqual(s *Type, t *Type) Ternary { - if s == t { - return TernaryTrue - } - return TernaryFalse -} - -func (c *Checker) markPropertyAsReferenced(prop *ast.Symbol, nodeForCheckWriteOnly *ast.Node, isSelfTypeAccess bool) { - if prop.Flags&ast.SymbolFlagsClassMember == 0 || prop.ValueDeclaration == nil { - return - } - hasPrivateModifier := ast.HasModifier(prop.ValueDeclaration, ast.ModifierFlagsPrivate) - hasPrivateIdentifier := prop.ValueDeclaration.Name() != nil && ast.IsPrivateIdentifier(prop.ValueDeclaration.Name()) - if !hasPrivateModifier && !hasPrivateIdentifier { - return - } - if nodeForCheckWriteOnly != nil && ast.IsWriteOnlyAccess(nodeForCheckWriteOnly) && prop.Flags&ast.SymbolFlagsSetAccessor == 0 { - return - } - if isSelfTypeAccess { - // Find any FunctionLikeDeclaration because those create a new 'this' binding. But this should only matter for methods (or getters/setters). - containingMethod := ast.FindAncestor(nodeForCheckWriteOnly, ast.IsFunctionLikeDeclaration) - if containingMethod != nil && containingMethod.Symbol() == prop { - return - } - } - target := prop - if prop.CheckFlags&ast.CheckFlagsInstantiated != 0 { - target = c.valueSymbolLinks.Get(prop).target - } - c.symbolReferenceLinks.Get(target).referenceKinds |= ast.SymbolFlagsAll -} - -func (c *Checker) expandSignatureParametersWithTupleMembers(signature *Signature, restType *TypeReference, restIndex int, restSymbol *ast.Symbol) []*ast.Symbol { - elementTypes := c.getTypeArguments(restType.AsType()) - elementInfos := restType.TargetTupleType().elementInfos - associatedNames := c.getUniqAssociatedNamesFromTupleType(restType, restSymbol) - expanded := append(make([]*ast.Symbol, 0, restIndex+len(elementTypes)), signature.parameters[:restIndex]...) - for i, t := range elementTypes { - flags := elementInfos[i].flags - checkFlags := ast.CheckFlagsNone - switch { - case flags&ElementFlagsVariable != 0: - checkFlags = ast.CheckFlagsRestParameter - case flags&ElementFlagsOptional != 0: - checkFlags = ast.CheckFlagsOptionalParameter - } - symbol := c.newSymbolEx(ast.SymbolFlagsFunctionScopedVariable, associatedNames[i], checkFlags) - links := c.valueSymbolLinks.Get(symbol) - if flags&ElementFlagsRest != 0 { - links.resolvedType = c.createArrayType(t) - } else { - links.resolvedType = t - } - expanded = append(expanded, symbol) - } - return expanded -} - -func (c *Checker) getUniqAssociatedNamesFromTupleType(t *TypeReference, restSymbol *ast.Symbol) []string { - elementInfos := t.TargetTupleType().elementInfos - names := make([]string, len(elementInfos)) - counters := make(map[string]int) - for i, info := range elementInfos { - names[i] = c.getTupleElementLabel(info, restSymbol, i) - // count duplicates using negative values - counters[names[i]]-- - } - for i, name := range names { - if counters[name] == -1 { - continue - } - for { - if counters[name] < 0 { - // switch to a positive suffix counter - counters[name] = 0 - } - counters[name]++ - candidateName := name + "_" + strconv.Itoa(counters[name]) - if counters[candidateName] == 0 { - names[i] = candidateName - break - } - } - } - return names -} - -func hasRestParameter(signature *ast.Node) bool { - last := core.LastOrNil(signature.Parameters()) - return last != nil && isRestParameter(last) -} - -func isRestParameter(param *ast.Node) bool { - return param.AsParameterDeclaration().DotDotDotToken != nil -} - -func getNameFromIndexInfo(info *IndexInfo) string { - if info.declaration != nil { - return scanner.DeclarationNameToString(info.declaration.Parameters()[0].Name()) - } - return "x" -} - -func (c *Checker) isUnknownLikeUnionType(t *Type) bool { - if c.strictNullChecks && t.flags&TypeFlagsUnion != 0 { - if t.objectFlags&ObjectFlagsIsUnknownLikeUnionComputed == 0 { - t.objectFlags |= ObjectFlagsIsUnknownLikeUnionComputed - types := t.Types() - if len(types) >= 3 && types[0].flags&TypeFlagsUndefined != 0 && types[1].flags&TypeFlagsNull != 0 && core.Some(types, c.IsEmptyAnonymousObjectType) { - t.objectFlags |= ObjectFlagsIsUnknownLikeUnion - } - } - return t.objectFlags&ObjectFlagsIsUnknownLikeUnion != 0 - } - return false -} - -func (c *Checker) containsUndefinedType(t *Type) bool { - if t.flags&TypeFlagsUnion != 0 { - t = t.Types()[0] - } - return t.flags&TypeFlagsUndefined != 0 -} - -func (c *Checker) typeHasCallOrConstructSignatures(t *Type) bool { - return t.flags&TypeFlagsStructuredType != 0 && len(c.resolveStructuredTypeMembers(t).signatures) != 0 -} - -func (c *Checker) getNormalizedType(t *Type, writing bool) *Type { - for { - var n *Type - switch { - case isFreshLiteralType(t): - n = t.AsLiteralType().regularType - case c.isGenericTupleType(t): - n = c.getNormalizedTupleType(t, writing) - case t.objectFlags&ObjectFlagsReference != 0: - if t.AsTypeReference().node != nil { - n = c.createTypeReference(t.Target(), c.getTypeArguments(t)) - } else { - n = c.getSingleBaseForNonAugmentingSubtype(t) - if n == nil { - n = t - } - } - case t.flags&TypeFlagsUnionOrIntersection != 0: - n = c.getNormalizedUnionOrIntersectionType(t, writing) - case t.flags&TypeFlagsSubstitution != 0: - if writing { - n = t.AsSubstitutionType().baseType - } else { - n = c.getSubstitutionIntersection(t) - } - case t.flags&TypeFlagsSimplifiable != 0: - n = c.getSimplifiedType(t, writing) - default: - return t - } - if n == t { - return n - } - t = n - } -} - -func (c *Checker) getSimplifiedType(t *Type, writing bool) *Type { - switch { - case t.flags&TypeFlagsIndexedAccess != 0: - return c.getSimplifiedIndexedAccessType(t, writing) - case t.flags&TypeFlagsConditional != 0: - return c.getSimplifiedConditionalType(t, writing) - } - return t -} - -// Transform an indexed access to a simpler form, if possible. Return the simpler form, or return -// the type itself if no transformation is possible. The writing flag indicates that the type is -// the target of an assignment. -func (c *Checker) getSimplifiedIndexedAccessType(t *Type, writing bool) *Type { - key := CachedTypeKey{kind: core.IfElse(writing, CachedTypeKindIndexedAccessForWriting, CachedTypeKindIndexedAccessForReading), typeId: t.id} - if cached := c.cachedTypes[key]; cached != nil { - return core.IfElse(cached == c.circularConstraintType, t, cached) - } - c.cachedTypes[key] = t - // We recursively simplify the object type as it may in turn be an indexed access type. For example, with - // '{ [P in T]: { [Q in U]: number } }[T][U]' we want to first simplify the inner indexed access type. - objectType := c.getSimplifiedType(t.AsIndexedAccessType().objectType, writing) - indexType := c.getSimplifiedType(t.AsIndexedAccessType().indexType, writing) - // T[A | B] -> T[A] | T[B] (reading) - // T[A | B] -> T[A] & T[B] (writing) - distributedOverIndex := c.distributeObjectOverIndexType(objectType, indexType, writing) - if distributedOverIndex != nil { - c.cachedTypes[key] = distributedOverIndex - return distributedOverIndex - } - // Only do the inner distributions if the index can no longer be instantiated to cause index distribution again - if indexType.flags&TypeFlagsInstantiable == 0 { - // (T | U)[K] -> T[K] | U[K] (reading) - // (T | U)[K] -> T[K] & U[K] (writing) - // (T & U)[K] -> T[K] & U[K] - distributedOverObject := c.distributeIndexOverObjectType(objectType, indexType, writing) - if distributedOverObject != nil { - c.cachedTypes[key] = distributedOverObject - return distributedOverObject - } - } - // So ultimately (reading): - // ((A & B) | C)[K1 | K2] -> ((A & B) | C)[K1] | ((A & B) | C)[K2] -> (A & B)[K1] | C[K1] | (A & B)[K2] | C[K2] -> (A[K1] & B[K1]) | C[K1] | (A[K2] & B[K2]) | C[K2] - // A generic tuple type indexed by a number exists only when the index type doesn't select a - // fixed element. We simplify to either the combined type of all elements (when the index type - // the actual number type) or to the combined type of all non-fixed elements. - if c.isGenericTupleType(objectType) && indexType.flags&TypeFlagsNumberLike != 0 { - elementType := c.getElementTypeOfSliceOfTupleType(objectType, core.IfElse(indexType.flags&TypeFlagsNumber != 0, 0, objectType.TargetTupleType().fixedLength), 0 /*endSkipCount*/, writing, false) - if elementType != nil { - c.cachedTypes[key] = elementType - return elementType - } - } - // If the object type is a mapped type { [P in K]: E }, where K is generic, or { [P in K as N]: E }, where - // K is generic and N is assignable to P, instantiate E using a mapper that substitutes the index type for P. - // For example, for an index access { [P in K]: Box }[X], we construct the type Box. - if c.isGenericMappedType(objectType) { - if c.getMappedTypeNameTypeKind(objectType) != MappedTypeNameTypeKindRemapping { - result := c.mapType(c.substituteIndexedMappedType(objectType, t.AsIndexedAccessType().indexType), func(t *Type) *Type { - return c.getSimplifiedType(t, writing) - }) - c.cachedTypes[key] = result - return result - } - } - return t -} - -func (c *Checker) distributeObjectOverIndexType(objectType *Type, indexType *Type, writing bool) *Type { - // T[A | B] -> T[A] | T[B] (reading) - // T[A | B] -> T[A] & T[B] (writing) - if indexType.flags&TypeFlagsUnion != 0 { - types := core.Map(indexType.Types(), func(t *Type) *Type { - return c.getSimplifiedType(c.getIndexedAccessType(objectType, t), writing) - }) - if writing { - return c.getIntersectionType(types) - } - return c.getUnionType(types) - } - return nil -} - -func (c *Checker) distributeIndexOverObjectType(objectType *Type, indexType *Type, writing bool) *Type { - // (T | U)[K] -> T[K] | U[K] (reading) - // (T | U)[K] -> T[K] & U[K] (writing) - // (T & U)[K] -> T[K] & U[K] - if objectType.flags&TypeFlagsUnion != 0 || objectType.flags&TypeFlagsIntersection != 0 && !c.shouldDeferIndexType(objectType, IndexFlagsNone) { - types := core.Map(objectType.Types(), func(t *Type) *Type { - return c.getSimplifiedType(c.getIndexedAccessType(t, indexType), writing) - }) - if objectType.flags&TypeFlagsIntersection != 0 || writing { - return c.getIntersectionType(types) - } - return c.getUnionType(types) - } - return nil -} - -func (c *Checker) getSimplifiedConditionalType(t *Type, writing bool) *Type { - checkType := t.AsConditionalType().checkType - extendsType := t.AsConditionalType().extendsType - trueType := c.getTrueTypeFromConditionalType(t) - falseType := c.getFalseTypeFromConditionalType(t) - // Simplifications for types of the form `T extends U ? T : never` and `T extends U ? never : T`. - if falseType.flags&TypeFlagsNever != 0 && c.getActualTypeVariable(trueType) == c.getActualTypeVariable(checkType) { - if checkType.flags&TypeFlagsAny != 0 || c.isTypeAssignableTo(c.getRestrictiveInstantiation(checkType), c.getRestrictiveInstantiation(extendsType)) { - return c.getSimplifiedType(trueType, writing) - } else if c.isIntersectionEmpty(checkType, extendsType) { - return c.neverType - } - } else if trueType.flags&TypeFlagsNever != 0 && c.getActualTypeVariable(falseType) == c.getActualTypeVariable(checkType) { - if checkType.flags&TypeFlagsAny == 0 && c.isTypeAssignableTo(c.getRestrictiveInstantiation(checkType), c.getRestrictiveInstantiation(extendsType)) { - return c.neverType - } else if checkType.flags&TypeFlagsAny != 0 || c.isIntersectionEmpty(checkType, extendsType) { - return c.getSimplifiedType(falseType, writing) - } - } - return t -} - -// Invokes union simplification logic to determine if an intersection is considered empty as a union constituent -func (c *Checker) isIntersectionEmpty(type1 *Type, type2 *Type) bool { - return c.getUnionType([]*Type{c.intersectTypes(type1, type2), c.neverType}).flags&TypeFlagsNever != 0 -} - -func (c *Checker) getSimplifiedTypeOrConstraint(t *Type) *Type { - if simplified := c.getSimplifiedType(t, false /*writing*/); simplified != t { - return simplified - } - return c.getConstraintOfType(t) -} - -func (c *Checker) getNormalizedUnionOrIntersectionType(t *Type, writing bool) *Type { - if reduced := c.getReducedType(t); reduced != t { - return reduced - } - if t.flags&TypeFlagsIntersection != 0 && c.shouldNormalizeIntersection(t) { - // Normalization handles cases like - // Partial[K] & ({} | null) ==> - // Partial[K] & {} | Partial[K} & null ==> - // (T[K] | undefined) & {} | (T[K] | undefined) & null ==> - // T[K] & {} | undefined & {} | T[K] & null | undefined & null ==> - // T[K] & {} | T[K] & null - types := t.Types() - normalizedTypes := core.SameMap(types, func(u *Type) *Type { return c.getNormalizedType(u, writing) }) - if !core.Same(normalizedTypes, types) { - return c.getIntersectionType(normalizedTypes) - } - } - return t -} - -func (c *Checker) shouldNormalizeIntersection(t *Type) bool { - hasInstantiable := false - hasNullableOrEmpty := false - for _, t := range t.Types() { - hasInstantiable = hasInstantiable || t.flags&TypeFlagsInstantiable != 0 - hasNullableOrEmpty = hasNullableOrEmpty || t.flags&TypeFlagsNullable != 0 || c.IsEmptyAnonymousObjectType(t) - if hasInstantiable && hasNullableOrEmpty { - return true - } - } - return false -} - -func (c *Checker) getNormalizedTupleType(t *Type, writing bool) *Type { - elements := c.getElementTypes(t) - normalizedElements := core.SameMap(elements, func(t *Type) *Type { - if t.flags&TypeFlagsSimplifiable != 0 { - return c.getSimplifiedType(t, writing) - } - return t - }) - if !core.Same(elements, normalizedElements) { - return c.createNormalizedTupleType(t.Target(), normalizedElements) - } - return t -} - -func (c *Checker) getSingleBaseForNonAugmentingSubtype(t *Type) *Type { - if t.objectFlags&ObjectFlagsReference == 0 || t.Target().objectFlags&ObjectFlagsClassOrInterface == 0 { - return nil - } - key := CachedTypeKey{kind: CachedTypeKindEquivalentBaseType, typeId: t.id} - if t.objectFlags&ObjectFlagsIdenticalBaseTypeCalculated != 0 { - return c.cachedTypes[key] - } - t.objectFlags |= ObjectFlagsIdenticalBaseTypeCalculated - target := t.Target() - if target.objectFlags&ObjectFlagsClass != 0 { - baseTypeNode := getBaseTypeNodeOfClass(target) - // A base type expression may circularly reference the class itself (e.g. as an argument to function call), so we only - // check for base types specified as simple qualified names. - if baseTypeNode != nil && !ast.IsIdentifier(baseTypeNode.Expression()) && !ast.IsPropertyAccessExpression(baseTypeNode.Expression()) { - return nil - } - } - bases := c.getBaseTypes(target) - if len(bases) != 1 { - return nil - } - if len(c.getMembersOfSymbol(t.symbol)) != 0 { - // If the interface has any members, they may subtype members in the base, so we should do a full structural comparison - return nil - } - var instantiatedBase *Type - typeParameters := target.AsInterfaceType().TypeParameters() - if len(typeParameters) == 0 { - instantiatedBase = bases[0] - } else { - instantiatedBase = c.instantiateType(bases[0], newTypeMapper(typeParameters, c.getTypeArguments(t)[:len(typeParameters)])) - } - if len(c.getTypeArguments(t)) > len(typeParameters) { - instantiatedBase = c.getTypeWithThisArgument(instantiatedBase, core.LastOrNil(c.getTypeArguments(t)), false) - } - c.cachedTypes[key] = instantiatedBase - return instantiatedBase -} - -func (c *Checker) getModifiersTypeFromMappedType(t *Type) *Type { - m := t.AsMappedType() - if m.modifiersType == nil { - if c.isMappedTypeWithKeyofConstraintDeclaration(t) { - // If the constraint declaration is a 'keyof T' node, the modifiers type is T. We check - // AST nodes here because, when T is a non-generic type, the logic below eagerly resolves - // 'keyof T' to a literal union type and we can't recover T from that type. - m.modifiersType = c.instantiateType(c.getTypeFromTypeNode(c.getConstraintDeclarationForMappedType(t).AsTypeOperatorNode().Type), m.mapper) - } else { - // Otherwise, get the declared constraint type, and if the constraint type is a type parameter, - // get the constraint of that type parameter. If the resulting type is an indexed type 'keyof T', - // the modifiers type is T. Otherwise, the modifiers type is unknown. - declaredType := c.getTypeFromMappedTypeNode(m.declaration.AsNode()) - constraint := c.getConstraintTypeFromMappedType(declaredType) - extendedConstraint := constraint - if constraint != nil && constraint.flags&TypeFlagsTypeParameter != 0 { - extendedConstraint = c.getConstraintOfTypeParameter(constraint) - } - if extendedConstraint != nil && extendedConstraint.flags&TypeFlagsIndex != 0 { - m.modifiersType = c.instantiateType(extendedConstraint.AsIndexType().target, m.mapper) - } else { - m.modifiersType = c.unknownType - } - } - } - return m.modifiersType -} - -func (c *Checker) extractTypesOfKind(t *Type, kind TypeFlags) *Type { - return c.filterType(t, func(t *Type) bool { return t.flags&kind != 0 }) -} - -func (c *Checker) getRegularTypeOfObjectLiteral(t *Type) *Type { - if !(isObjectLiteralType(t) && t.objectFlags&ObjectFlagsFreshLiteral != 0) { - return t - } - key := CachedTypeKey{kind: CachedTypeKindRegularObjectLiteral, typeId: t.id} - if cached := c.cachedTypes[key]; cached != nil { - return cached - } - resolved := c.resolveStructuredTypeMembers(t) - members := c.transformTypeOfMembers(t, c.getRegularTypeOfObjectLiteral) - regular := c.newAnonymousType(t.symbol, members, resolved.CallSignatures(), resolved.ConstructSignatures(), resolved.indexInfos) - regular.flags = resolved.flags - regular.objectFlags |= resolved.objectFlags & ^ObjectFlagsFreshLiteral - c.cachedTypes[key] = regular - return regular -} - -func (c *Checker) transformTypeOfMembers(t *Type, f func(propertyType *Type) *Type) ast.SymbolTable { - members := make(ast.SymbolTable) - for _, property := range c.getPropertiesOfObjectType(t) { - original := c.getTypeOfSymbol(property) - updated := f(original) - if updated != original { - property = c.createSymbolWithType(property, updated) - } - members[property.Name] = property - } - return members -} - -func (c *Checker) markLinkedReferences(location *ast.Node, hint ReferenceHint, propSymbol *ast.Symbol, parentType *Type) { - if !c.canCollectSymbolAliasAccessibilityData { - return - } - if location.Flags&ast.NodeFlagsAmbient != 0 && !ast.IsPropertySignatureDeclaration(location) && !ast.IsPropertyDeclaration(location) { - // References within types and declaration files are never going to contribute to retaining a JS import, - // except for properties (which can be decorated). - return - } - switch hint { - case ReferenceHintIdentifier: - c.markIdentifierAliasReferenced(location) - case ReferenceHintProperty: - c.markPropertyAliasReferenced(location, propSymbol, parentType) - case ReferenceHintExportAssignment: - c.markExportAssignmentAliasReferenced(location) - case ReferenceHintJsx: - c.markJsxAliasReferenced(location) - case ReferenceHintExportImportEquals: - c.markImportEqualsAliasReferenced(location) - case ReferenceHintExportSpecifier: - c.markExportSpecifierAliasReferenced(location) - case ReferenceHintDecorator: - c.markDecoratorAliasReferenced(location) - case ReferenceHintUnspecified: - // Identifiers in expression contexts are emitted, so we need to follow their referenced aliases and mark them as used - // Some non-expression identifiers are also treated as expression identifiers for this purpose, eg, `a` in `b = {a}` or `q` in `import r = q` - // This is the exception, rather than the rule - most non-expression identifiers are declaration names. - if ast.IsIdentifier(location) && - (ast.IsExpressionNode(location) || - ast.IsShorthandPropertyAssignment(location.Parent) || - (ast.IsImportEqualsDeclaration(location.Parent) && - location.Parent.AsImportEqualsDeclaration().ModuleReference == location)) && - shouldMarkIdentifierAliasReferenced(location) { - if ast.IsPropertyAccessOrQualifiedName(location.Parent) { - var left *ast.Node - if ast.IsPropertyAccessExpression(location.Parent) { - left = location.Parent.Expression() - } else { - left = location.Parent.AsQualifiedName().Left - } - if left != location { - return // Only mark the LHS (the RHS is a property lookup) - } - } - c.markIdentifierAliasReferenced(location) - return - } - if ast.IsPropertyAccessOrQualifiedName(location) { - topProp := location - for ast.IsPropertyAccessOrQualifiedName(topProp) { - if ast.IsPartOfTypeNode(topProp) { - return - } - topProp = topProp.Parent - } - c.markPropertyAliasReferenced(location, nil /*propSymbol*/, nil /*parentType*/) - return - } - if ast.IsExportAssignment(location) { - c.markExportAssignmentAliasReferenced(location) - return - } - if ast.IsJsxOpeningLikeElement(location) || ast.IsJsxOpeningFragment(location) { - c.markJsxAliasReferenced(location) - return - } - if ast.IsImportEqualsDeclaration(location) { - if isInternalModuleImportEqualsDeclaration(location) || c.checkExternalImportOrExportDeclaration(location) { - c.markImportEqualsAliasReferenced(location) - return - } - return - } - if ast.IsExportSpecifier(location) { - c.markExportSpecifierAliasReferenced(location) - return - } - if !c.compilerOptions.EmitDecoratorMetadata.IsTrue() { - return - } - if !ast.CanHaveDecorators(location) || !ast.HasDecorators(location) || location.Modifiers() == nil || !nodeCanBeDecorated(c.legacyDecorators, location, location.Parent, location.Parent.Parent) { - return - } - - c.markDecoratorAliasReferenced(location) - return - default: - panic("Unhandled reference hint") - } -} - -func isExportOrExportExpression(location *ast.Node) bool { - return ast.FindAncestor(location, func(n *ast.Node) bool { - parent := n.Parent - if parent != nil { - if ast.IsAnyExportAssignment(parent) { - return parent.AsExportAssignment().Expression == n && ast.IsEntityNameExpression(n) - } - if ast.IsExportSpecifier(parent) { - return parent.AsExportSpecifier().Name() == n || parent.AsExportSpecifier().PropertyName == n - } - } - return false - }) != nil -} - -func shouldMarkIdentifierAliasReferenced(node *ast.IdentifierNode) bool { - parent := node.Parent - if parent != nil { - // A property access expression LHS? checkPropertyAccessExpression will handle that. - if ast.IsPropertyAccessExpression(parent) && parent.Expression() == node { - return false - } - // Next two check for an identifier inside a type only export. - if ast.IsExportSpecifier(parent) && parent.AsExportSpecifier().IsTypeOnly { - return false - } - if parent.Parent != nil { - greatGrandparent := parent.Parent.Parent - if greatGrandparent != nil && ast.IsExportDeclaration(greatGrandparent) && greatGrandparent.AsExportDeclaration().IsTypeOnly { - return false - } - } - } - return true -} - -func isInternalModuleImportEqualsDeclaration(node *ast.Node) bool { - return node.Kind == ast.KindImportEqualsDeclaration && - node.AsImportEqualsDeclaration().ModuleReference.Kind != ast.KindExternalModuleReference -} - -func (c *Checker) markIdentifierAliasReferenced(location *ast.IdentifierNode) { - symbol := c.getResolvedSymbol(location) - if symbol != nil && symbol != c.argumentsSymbol && symbol != c.unknownSymbol && !ast.IsThisInTypeQuery(location) { - c.markAliasReferenced(symbol, location) - } -} - -func (c *Checker) markPropertyAliasReferenced(location *ast.Node /*PropertyAccessExpression | QualifiedName*/, propSymbol *ast.Symbol, parentType *Type) { - var left *ast.Node - if ast.IsPropertyAccessExpression(location) { - left = location.AsPropertyAccessExpression().Expression - } else { - left = location.AsQualifiedName().Left - } - if ast.IsThisIdentifier(left) || !ast.IsIdentifier(left) { - return - } - parentSymbol := c.getResolvedSymbol(left) - if parentSymbol == nil || parentSymbol == c.unknownSymbol { - return - } - // In `Foo.Bar.Baz`, 'Foo' is not referenced if 'Bar' is a const enum or a module containing only const enums. - // `Foo` is also not referenced in `enum FooCopy { Bar = Foo.Bar }`, because the enum member value gets inlined - // here even if `Foo` is not a const enum. - // - // The exceptions are: - // 1. if 'isolatedModules' is enabled, because the const enum value will not be inlined, and - // 2. if 'preserveConstEnums' is enabled and the expression is itself an export, e.g. `export = Foo.Bar.Baz`. - // - // The property lookup is deferred as much as possible, in as many situations as possible, to avoid alias marking - // pulling on types/symbols it doesn't strictly need to. - if c.compilerOptions.GetIsolatedModules() || (c.compilerOptions.ShouldPreserveConstEnums() && isExportOrExportExpression(location)) { - c.markAliasReferenced(parentSymbol, location) - return - } - // Hereafter, this relies on type checking - but every check prior to this only used symbol information - leftType := parentType - if leftType == nil { - leftType = c.checkExpressionCached(left) - } - if IsTypeAny(leftType) || leftType == c.silentNeverType { - c.markAliasReferenced(parentSymbol, location) - return - } - prop := propSymbol - if prop == nil && parentType == nil { - var right *ast.Node - if ast.IsPropertyAccessExpression(location) { - right = location.AsPropertyAccessExpression().Name() - } else { - right = location.AsQualifiedName().Right - } - var lexicallyScopedSymbol *ast.Symbol - if ast.IsPrivateIdentifier(right) { - lexicallyScopedSymbol = c.lookupSymbolForPrivateIdentifierDeclaration(right.Text(), right) - } - assignmentKind := getAssignmentTargetKind(location) - var apparentType *Type - if assignmentKind != AssignmentKindNone || c.isMethodAccessForCall(location) { - apparentType = c.getApparentType(c.getWidenedType(leftType)) - } else { - apparentType = c.getApparentType(leftType) - } - if ast.IsPrivateIdentifier(right) { - if lexicallyScopedSymbol != nil { - prop = c.getPrivateIdentifierPropertyOfType(apparentType, lexicallyScopedSymbol) - } - } else { - prop = c.getPropertyOfType(apparentType, right.Text()) - } - } - if !(prop != nil && (isConstEnumOrConstEnumOnlyModule(prop) || prop.Flags&ast.SymbolFlagsEnumMember != 0 && location.Parent.Kind == ast.KindEnumMember)) { - c.markAliasReferenced(parentSymbol, location) - } -} - -func (c *Checker) markExportAssignmentAliasReferenced(location *ast.Node /*ExportAssignment*/) { - id := location.Expression() - if ast.IsIdentifier(id) { - sym := c.getExportSymbolOfValueSymbolIfExported(c.resolveEntityName(id, ast.SymbolFlagsAll, true /*ignoreErrors*/, true /*dontResolveAlias*/, location)) - if sym != nil { - c.markAliasReferenced(sym, id) - } - } -} - -func (c *Checker) markJsxAliasReferenced(node *ast.Node /*JsxOpeningLikeElement | JsxOpeningFragment*/) { - if c.getJsxNamespaceContainerForImplicitImport(node) != nil { - return - } - // The reactNamespace/jsxFactory's root symbol should be marked as 'used' so we don't incorrectly elide its import. - // And if there is no reactNamespace/jsxFactory's symbol in scope when targeting React emit, we should issue an error. - jsxFactoryRefErr := core.IfElse(c.compilerOptions.Jsx == core.JsxEmitReact, diagnostics.This_JSX_tag_requires_0_to_be_in_scope_but_it_could_not_be_found, nil) - jsxFactoryNamespace := c.getJsxNamespace(node) - jsxFactoryLocation := node - if ast.IsJsxOpeningLikeElement(node) { - jsxFactoryLocation = node.TagName() - } - shouldFactoryRefErr := c.compilerOptions.Jsx != core.JsxEmitPreserve && c.compilerOptions.Jsx != core.JsxEmitReactNative - // #38720/60122, allow null as jsxFragmentFactory - var jsxFactorySym *ast.Symbol - if !(ast.IsJsxOpeningFragment(node) && jsxFactoryNamespace == "null") { - flags := ast.SymbolFlagsValue - if !shouldFactoryRefErr { - flags &= ^ast.SymbolFlagsEnum - } - jsxFactorySym = c.resolveName(jsxFactoryLocation, jsxFactoryNamespace, flags, jsxFactoryRefErr, true /*isUse*/, false /*excludeGlobals*/) - } - if jsxFactorySym != nil { - // Mark local symbol as referenced here because it might not have been marked - // if jsx emit was not jsxFactory as there wont be error being emitted - c.symbolReferenced(jsxFactorySym, ast.SymbolFlagsAll) - // If react/jsxFactory symbol is alias, mark it as refereced - if c.canCollectSymbolAliasAccessibilityData && jsxFactorySym.Flags&ast.SymbolFlagsAlias != 0 && c.getTypeOnlyAliasDeclaration(jsxFactorySym) == nil { - c.markAliasSymbolAsReferenced(jsxFactorySym) - } - } - // if JsxFragment, additionally mark jsx pragma as referenced, since `getJsxNamespace` above would have resolved to only the fragment factory if they are distinct - if ast.IsJsxOpeningFragment(node) { - file := ast.GetSourceFileOfNode(node) - entity := c.getJsxFactoryEntity(file.AsNode()) - if entity != nil { - localJsxNamespace := ast.GetFirstIdentifier(entity).Text() - flags := ast.SymbolFlagsValue - if !shouldFactoryRefErr { - flags &= ^ast.SymbolFlagsEnum - } - c.resolveName(jsxFactoryLocation, localJsxNamespace, flags, jsxFactoryRefErr, true /*isUse*/, false /*excludeGlobals*/) - } - } -} - -func (c *Checker) markImportEqualsAliasReferenced(location *ast.Node /*ImportEqualsDeclaration*/) { - if ast.HasSyntacticModifier(location, ast.ModifierFlagsExport) { - c.markExportAsReferenced(location) - } -} - -func (c *Checker) markExportSpecifierAliasReferenced(location *ast.ExportSpecifierNode) { - if location.Parent.Parent.AsExportDeclaration().ModuleSpecifier == nil && !location.AsExportSpecifier().IsTypeOnly && !location.Parent.Parent.AsExportDeclaration().IsTypeOnly { - exportedName := location.PropertyName() - if exportedName == nil { - exportedName = location.Name() - } - if exportedName.Kind == ast.KindStringLiteral { - return // Skip for invalid syntax like this: export { "x" } - } - symbol := c.resolveName(exportedName, exportedName.Text(), ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace|ast.SymbolFlagsAlias, nil /*nameNotFoundMessage*/, true /*isUse*/, false /*excludeGlobals*/) - if symbol != nil && (symbol == c.undefinedSymbol || symbol == c.globalThisSymbol || symbol.Declarations != nil && ast.IsGlobalSourceFile(ast.GetDeclarationContainer(symbol.Declarations[0]))) { - // Do nothing, non-local symbol - } else { - target := symbol - if target != nil && target.Flags&ast.SymbolFlagsAlias != 0 { - target = c.resolveAlias(target) - } - if target == nil || c.getSymbolFlags(target)&ast.SymbolFlagsValue != 0 { - c.markExportAsReferenced(location) // marks export as used - c.markIdentifierAliasReferenced(exportedName) // marks target of export as used - } - } - } -} - -func (c *Checker) markDecoratorAliasReferenced(node *ast.Node /*HasDecorators*/) { - // !!! Implement if/when we support emitDecoratorMetadata -} - -func (c *Checker) markAliasReferenced(symbol *ast.Symbol, location *ast.Node) { - if !c.canCollectSymbolAliasAccessibilityData { - return - } - if ast.IsNonLocalAlias(symbol, ast.SymbolFlagsValue /*excludes*/) && !IsInTypeQuery(location) { - target := c.resolveAlias(symbol) - if c.getSymbolFlagsEx(symbol, true /*excludeTypeOnlyMeanings*/, false /*excludeLocalMeanings*/)&(ast.SymbolFlagsValue|ast.SymbolFlagsExportValue) != 0 { - // An alias resolving to a const enum cannot be elided if (1) 'isolatedModules' is enabled - // (because the const enum value will not be inlined), or if (2) the alias is an export - // of a const enum declaration that will be preserved. - if c.compilerOptions.GetIsolatedModules() || - c.compilerOptions.ShouldPreserveConstEnums() && isExportOrExportExpression(location) || - !isConstEnumOrConstEnumOnlyModule(c.getExportSymbolOfValueSymbolIfExported(target)) { - c.markAliasSymbolAsReferenced(symbol) - } - } - } -} - -// When an alias symbol is referenced, we need to mark the entity it references as referenced and in turn repeat that until -// we reach a non-alias or an exported entity (which is always considered referenced). We do this by checking the target of -// the alias as an expression (which recursively takes us back here if the target references another alias). -func (c *Checker) markAliasSymbolAsReferenced(symbol *ast.Symbol) { - links := c.aliasSymbolLinks.Get(symbol) - if !links.referenced { - links.referenced = true - node := c.getDeclarationOfAliasSymbol(symbol) - if node == nil { - panic("Unexpected nil in markAliasSymbolAsReferenced") - } - // We defer checking of the reference of an `import =` until the import itself is referenced, - // This way a chain of imports can be elided if ultimately the final input is only used in a type - // position. - if ast.IsImportEqualsDeclaration(node) && node.AsImportEqualsDeclaration().ModuleReference.Kind != ast.KindExternalModuleReference { - if c.getSymbolFlags(c.resolveSymbol(symbol))&ast.SymbolFlagsValue != 0 { - // import foo = - left := ast.GetFirstIdentifier(node.AsImportEqualsDeclaration().ModuleReference) - c.markIdentifierAliasReferenced(left) - } - } - } -} - -func (c *Checker) markExportAsReferenced(node *ast.Node /*ImportEqualsDeclaration | ExportSpecifier*/) { - symbol := c.getSymbolOfDeclaration(node) - target := c.resolveAlias(symbol) - if target != nil { - markAlias := target == c.unknownSymbol || - ((c.getSymbolFlagsEx(symbol, true /*excludeTypeOnlyMeanings*/, false /*excludeLocalMeanings*/)&ast.SymbolFlagsValue != 0) && !isConstEnumOrConstEnumOnlyModule(target)) - if markAlias { - c.markAliasSymbolAsReferenced(symbol) - } - } -} - -func (c *Checker) markEntityNameOrEntityExpressionAsReference(typeName *ast.Node /*EntityNameOrEntityNameExpression | nil*/, forDecoratorMetadata bool) { - if typeName == nil { - return - } - - rootName := ast.GetFirstIdentifier(typeName) - meaning := core.IfElse(typeName.Kind == ast.KindIdentifier, ast.SymbolFlagsType, ast.SymbolFlagsNamespace) | ast.SymbolFlagsAlias - rootSymbol := c.resolveName(rootName, rootName.Text(), meaning, nil /*nameNotFoundMessage*/, true /*isUse*/, false /*excludeGlobals*/) - - if rootSymbol != nil && rootSymbol.Flags&ast.SymbolFlagsAlias != 0 { - if c.canCollectSymbolAliasAccessibilityData && - c.symbolIsValue(rootSymbol) && - !isConstEnumOrConstEnumOnlyModule(c.resolveAlias(rootSymbol)) && - c.getTypeOnlyAliasDeclaration(rootSymbol) == nil { - c.markAliasSymbolAsReferenced(rootSymbol) - } else if forDecoratorMetadata && - c.compilerOptions.GetIsolatedModules() && - c.compilerOptions.GetEmitModuleKind() >= core.ModuleKindES2015 && - !c.symbolIsValue(rootSymbol) && - !core.Some(rootSymbol.Declarations, ast.IsTypeOnlyImportOrExportDeclaration) { - diag := c.error(typeName, diagnostics.A_type_referenced_in_a_decorated_signature_must_be_imported_with_import_type_or_a_namespace_import_when_isolatedModules_and_emitDecoratorMetadata_are_enabled) - var aliasDeclaration *ast.Node - for _, decl := range rootSymbol.Declarations { - if ast.IsAliasSymbolDeclaration(decl) { - aliasDeclaration = decl - break - } - } - if aliasDeclaration != nil { - diag.SetRelatedInfo([]*ast.Diagnostic{createDiagnosticForNode(aliasDeclaration, diagnostics.X_0_was_imported_here, rootName.Text())}) - } - } - } -} - -func getEntityNameFromTypeNode(node *ast.TypeNode) *ast.Node { - switch node.Kind { - case ast.KindTypeReference: - return node.AsTypeReferenceNode().TypeName - - case ast.KindExpressionWithTypeArguments: - if ast.IsEntityNameExpression(node.Expression()) { - return node.Expression() - } - return nil - - // These aren't valid TypeNodes, but we treat them as such because of `isPartOfTypeNode`, which returns `true` for things that aren't `TypeNode`s. - case ast.KindIdentifier, ast.KindQualifiedName: - return node - } - - return nil -} - -// If a TypeNode can be resolved to a value symbol imported from an external module, it is -// marked as referenced to prevent import elision. -func (c *Checker) markTypeNodeAsReferenced(node *ast.TypeNode) { - if node != nil { - c.markEntityNameOrEntityExpressionAsReference(getEntityNameFromTypeNode(node), false /*forDecoratorMetadata*/) - } -} - -func (c *Checker) GetPromisedTypeOfPromise(t *Type) *Type { - return c.getPromisedTypeOfPromiseEx(t, nil, nil) -} - -// Gets the "promised type" of a promise. -// @param type The type of the promise. -// @remarks The "promised type" of a type is the type of the "value" parameter of the "onfulfilled" callback. -func (c *Checker) getPromisedTypeOfPromiseEx(t *Type, errorNode *ast.Node, thisTypeForErrorOut **Type) *Type { - // { // type - // then( // thenFunction - // onfulfilled: ( // onfulfilledParameterType - // value: T // valueParameterType - // ) => any - // ): any; - // } - if IsTypeAny(t) { - return nil - } - key := CachedTypeKey{kind: CachedTypeKindPromisedTypeOfPromise, typeId: t.id} - if cached := c.cachedTypes[key]; cached != nil { - return cached - } - if c.isReferenceToType(t, c.getGlobalPromiseType()) { - result := c.getTypeArguments(t)[0] - c.cachedTypes[key] = result - return result - } - // primitives with a `{ then() }` won't be unwrapped/adopted. - if c.allTypesAssignableToKind(c.getBaseConstraintOrType(t), TypeFlagsPrimitive|TypeFlagsNever) { - return nil - } - thenFunction := c.getTypeOfPropertyOfType(t, "then") - // TODO: GH#18217 - if IsTypeAny(thenFunction) { - return nil - } - var thenSignatures []*Signature - if thenFunction != nil { - thenSignatures = c.getSignaturesOfType(thenFunction, SignatureKindCall) - } - if len(thenSignatures) == 0 { - if errorNode != nil { - c.error(errorNode, diagnostics.A_promise_must_have_a_then_method) - } - return nil - } - var thisTypeForError *Type - var candidates []*Signature - for _, thenSignature := range thenSignatures { - thisType := c.getThisTypeOfSignature(thenSignature) - if thisType != nil && thisType != c.voidType && !c.isTypeRelatedTo(t, thisType, c.subtypeRelation) { - thisTypeForError = thisType - } else { - candidates = append(candidates, thenSignature) - } - } - if len(candidates) == 0 { - debug.AssertIsDefined(thisTypeForError) - if thisTypeForErrorOut != nil { - *thisTypeForErrorOut = thisTypeForError - } - if errorNode != nil { - c.error(errorNode, diagnostics.The_this_context_of_type_0_is_not_assignable_to_method_s_this_of_type_1, c.TypeToString(t), c.TypeToString(thisTypeForError)) - } - return nil - } - onfulfilledParameterType := c.getTypeWithFacts(c.getUnionType(core.Map(candidates, c.getTypeOfFirstParameterOfSignature)), TypeFactsNEUndefinedOrNull) - if IsTypeAny(onfulfilledParameterType) { - return nil - } - onfulfilledParameterSignatures := c.getSignaturesOfType(onfulfilledParameterType, SignatureKindCall) - if len(onfulfilledParameterSignatures) == 0 { - if errorNode != nil { - c.error(errorNode, diagnostics.The_first_parameter_of_the_then_method_of_a_promise_must_be_a_callback) - } - return nil - } - result := c.getUnionTypeEx(core.Map(onfulfilledParameterSignatures, c.getTypeOfFirstParameterOfSignature), UnionReductionSubtype, nil, nil) - c.cachedTypes[key] = result - return result -} - -func (c *Checker) getTypeOfFirstParameterOfSignature(signature *Signature) *Type { - return c.getTypeOfFirstParameterOfSignatureWithFallback(signature, c.neverType) -} - -func (c *Checker) getTypeOfFirstParameterOfSignatureWithFallback(signature *Signature, fallbackType *Type) *Type { - if len(signature.parameters) > 0 { - return c.getTypeAtPosition(signature, 0) - } - return fallbackType -} - -func getMappedTypeModifiers(t *Type) MappedTypeModifiers { - declaration := t.AsMappedType().declaration - var modifiers MappedTypeModifiers - if declaration.ReadonlyToken != nil { - modifiers |= core.IfElse(declaration.ReadonlyToken.Kind == ast.KindMinusToken, MappedTypeModifiersExcludeReadonly, MappedTypeModifiersIncludeReadonly) - } - if declaration.QuestionToken != nil { - modifiers |= core.IfElse(declaration.QuestionToken.Kind == ast.KindMinusToken, MappedTypeModifiersExcludeOptional, MappedTypeModifiersIncludeOptional) - } - return modifiers -} - -// Return -1, 0, or 1, where -1 means optionality is stripped (i.e. -?), 0 means optionality is unchanged, and 1 means -// optionality is added (i.e. +?). -func getMappedTypeOptionality(t *Type) int { - modifiers := getMappedTypeModifiers(t) - switch { - case modifiers&MappedTypeModifiersExcludeOptional != 0: - return -1 - case modifiers&MappedTypeModifiersIncludeOptional != 0: - return 1 - } - return 0 -} - -// Return -1, 0, or 1, for stripped, unchanged, or added optionality respectively. When a homomorphic mapped type doesn't -// modify optionality, recursively consult the optionality of the type being mapped over to see if it strips or adds optionality. -// For intersections, return -1 or 1 when all constituents strip or add optionality, otherwise return 0. -func (c *Checker) getCombinedMappedTypeOptionality(t *Type) int { - if t.objectFlags&ObjectFlagsMapped != 0 { - optionality := getMappedTypeOptionality(t) - if optionality != 0 { - return optionality - } - return c.getCombinedMappedTypeOptionality(c.getModifiersTypeFromMappedType(t)) - } - if t.flags&TypeFlagsIntersection != 0 { - optionality := c.getCombinedMappedTypeOptionality(t.Types()[0]) - for _, t := range t.Types()[1:] { - if c.getCombinedMappedTypeOptionality(t) != optionality { - return 0 - } - } - return optionality - } - return 0 -} - -func isPartialMappedType(t *Type) bool { - return t.objectFlags&ObjectFlagsMapped != 0 && getMappedTypeModifiers(t)&MappedTypeModifiersIncludeOptional != 0 -} - -func (c *Checker) getOptionalExpressionType(exprType *Type, expression *ast.Node) *Type { - switch { - case ast.IsExpressionOfOptionalChainRoot(expression): - return c.GetNonNullableType(exprType) - case ast.IsOptionalChain(expression): - return c.removeOptionalTypeMarker(exprType) - default: - return exprType - } -} - -func (c *Checker) removeOptionalTypeMarker(t *Type) *Type { - if c.strictNullChecks { - return c.removeType(t, c.optionalType) - } - return t -} - -func (c *Checker) propagateOptionalTypeMarker(t *Type, node *ast.Node, wasOptional bool) *Type { - if wasOptional { - if ast.IsOutermostOptionalChain(node) { - return c.getOptionalType(t, false) - } - return c.addOptionalTypeMarker(t) - } - return t -} - -func (c *Checker) removeMissingType(t *Type, isOptional bool) *Type { - if c.exactOptionalPropertyTypes && isOptional { - return c.removeType(t, c.missingType) - } - return t -} - -func (c *Checker) removeMissingOrUndefinedType(t *Type) *Type { - if c.exactOptionalPropertyTypes { - return c.removeType(t, c.missingType) - } - return c.getTypeWithFacts(t, TypeFactsNEUndefined) -} - -func (c *Checker) removeDefinitelyFalsyTypes(t *Type) *Type { - return c.filterType(t, func(t *Type) bool { return c.hasTypeFacts(t, TypeFactsTruthy) }) -} - -func (c *Checker) extractDefinitelyFalsyTypes(t *Type) *Type { - return c.mapType(t, c.getDefinitelyFalsyPartOfType) -} - -func (c *Checker) getDefinitelyFalsyPartOfType(t *Type) *Type { - switch { - case t.flags&TypeFlagsString != 0: - return c.emptyStringType - case t.flags&TypeFlagsNumber != 0: - return c.zeroType - case t.flags&TypeFlagsBigInt != 0: - return c.zeroBigIntType - case t == c.regularFalseType || t == c.falseType || - t.flags&(TypeFlagsVoid|TypeFlagsUndefined|TypeFlagsNull|TypeFlagsAnyOrUnknown) != 0 || - t.flags&TypeFlagsStringLiteral != 0 && getStringLiteralValue(t) == "" || - t.flags&TypeFlagsNumberLiteral != 0 && getNumberLiteralValue(t) == 0 || - t.flags&TypeFlagsBigIntLiteral != 0 && isZeroBigInt(t): - return t - } - return c.neverType -} - -func (c *Checker) getConstraintDeclaration(t *Type) *ast.Node { - if t.symbol != nil { - for _, d := range t.symbol.Declarations { - if ast.IsTypeParameterDeclaration(d) { - if constraint := d.AsTypeParameter().Constraint; constraint != nil { - return constraint - } - } - } - } - return nil -} - -func (c *Checker) getTemplateLiteralType(texts []string, types []*Type) *Type { - unionIndex := core.FindIndex(types, func(t *Type) bool { - return t.flags&(TypeFlagsNever|TypeFlagsUnion) != 0 - }) - if unionIndex >= 0 { - if !c.checkCrossProductUnion(types) { - return c.errorType - } - return c.mapType(types[unionIndex], func(t *Type) *Type { - return c.getTemplateLiteralType(texts, core.ReplaceElement(types, unionIndex, t)) - }) - } - if slices.Contains(types, c.wildcardType) { - return c.wildcardType - } - var newTypes []*Type - var newTexts []string - var sb strings.Builder - sb.WriteString(texts[0]) - var addSpans func([]string, []*Type) bool - addSpans = func(texts []string, types []*Type) bool { - for i, t := range types { - switch { - case t.flags&(TypeFlagsLiteral|TypeFlagsNull|TypeFlagsUndefined) != 0: - sb.WriteString(c.getTemplateStringForType(t)) - sb.WriteString(texts[i+1]) - case t.flags&TypeFlagsTemplateLiteral != 0: - sb.WriteString(t.AsTemplateLiteralType().texts[0]) - if !addSpans(t.AsTemplateLiteralType().texts, t.AsTemplateLiteralType().types) { - return false - } - sb.WriteString(texts[i+1]) - case c.isGenericIndexType(t) || c.isPatternLiteralPlaceholderType(t): - newTypes = append(newTypes, t) - newTexts = append(newTexts, sb.String()) - sb.Reset() - sb.WriteString(texts[i+1]) - default: - return false - } - } - return true - } - if !addSpans(texts, types) { - return c.stringType - } - if len(newTypes) == 0 { - return c.getStringLiteralType(sb.String()) - } - newTexts = append(newTexts, sb.String()) - if core.Every(newTexts, func(t string) bool { return t == "" }) { - if core.Every(newTypes, func(t *Type) bool { return t.flags&TypeFlagsString != 0 }) { - return c.stringType - } - // Normalize `${Mapping}` into Mapping - if len(newTypes) == 1 && c.isPatternLiteralType(newTypes[0]) { - return newTypes[0] - } - } - key := getTemplateTypeKey(newTexts, newTypes) - t := c.templateLiteralTypes[key] - if t == nil { - t = c.newTemplateLiteralType(newTexts, newTypes) - c.templateLiteralTypes[key] = t - } - return t -} - -func (c *Checker) getTemplateStringForType(t *Type) string { - switch { - case t.flags&(TypeFlagsStringLiteral|TypeFlagsNumberLiteral|TypeFlagsBooleanLiteral|TypeFlagsBigIntLiteral) != 0: - return evaluator.AnyToString(t.AsLiteralType().value) - case t.flags&TypeFlagsNullable != 0: - return t.AsIntrinsicType().intrinsicName - } - return "" -} - -func (c *Checker) getStringMappingType(symbol *ast.Symbol, t *Type) *Type { - switch { - case t.flags&(TypeFlagsUnion|TypeFlagsNever) != 0: - return c.mapType(t, func(t *Type) *Type { return c.getStringMappingType(symbol, t) }) - case t.flags&TypeFlagsStringLiteral != 0: - return c.getStringLiteralType(applyStringMapping(symbol, getStringLiteralValue(t))) - case t.flags&TypeFlagsTemplateLiteral != 0: - return c.getTemplateLiteralType(c.applyTemplateStringMapping(symbol, t.AsTemplateLiteralType().texts, t.AsTemplateLiteralType().types)) - case t.flags&TypeFlagsStringMapping != 0 && symbol == t.symbol: - return t - case t.flags&(TypeFlagsAny|TypeFlagsString|TypeFlagsStringMapping) != 0 || c.isGenericIndexType(t): - return c.getStringMappingTypeForGenericType(symbol, t) - case c.isPatternLiteralPlaceholderType(t): - return c.getStringMappingTypeForGenericType(symbol, c.getTemplateLiteralType([]string{"", ""}, []*Type{t})) - default: - return t - } -} - -func applyStringMapping(symbol *ast.Symbol, str string) string { - switch intrinsicTypeKinds[symbol.Name] { - case IntrinsicTypeKindUppercase: - return strings.ToUpper(str) - case IntrinsicTypeKindLowercase: - return strings.ToLower(str) - case IntrinsicTypeKindCapitalize: - _, size := utf8.DecodeRuneInString(str) - return strings.ToUpper(str[:size]) + str[size:] - case IntrinsicTypeKindUncapitalize: - _, size := utf8.DecodeRuneInString(str) - return strings.ToLower(str[:size]) + str[size:] - } - return str -} - -func (c *Checker) applyTemplateStringMapping(symbol *ast.Symbol, texts []string, types []*Type) ([]string, []*Type) { - switch intrinsicTypeKinds[symbol.Name] { - case IntrinsicTypeKindUppercase, IntrinsicTypeKindLowercase: - return core.Map(texts, func(t string) string { return applyStringMapping(symbol, t) }), - core.Map(types, func(t *Type) *Type { return c.getStringMappingType(symbol, t) }) - case IntrinsicTypeKindCapitalize, IntrinsicTypeKindUncapitalize: - if texts[0] != "" { - newTexts := slices.Clone(texts) - newTexts[0] = applyStringMapping(symbol, newTexts[0]) - return newTexts, types - } - newTypes := slices.Clone(types) - newTypes[0] = c.getStringMappingType(symbol, newTypes[0]) - return texts, newTypes - } - return texts, types -} - -func (c *Checker) getStringMappingTypeForGenericType(symbol *ast.Symbol, t *Type) *Type { - key := StringMappingKey{s: symbol, t: t} - result := c.stringMappingTypes[key] - if result == nil { - result = c.newStringMappingType(symbol, t) - c.stringMappingTypes[key] = result - } - return result -} - -// Given an indexed access on a mapped type of the form { [P in K]: E }[X], return an instantiation of E where P is -// replaced with X. Since this simplification doesn't account for mapped type modifiers, add 'undefined' to the -// resulting type if the mapped type includes a '?' modifier or if the modifiers type indicates that some properties -// are optional. If the modifiers type is generic, conservatively estimate optionality by recursively looking for -// mapped types that include '?' modifiers. -func (c *Checker) substituteIndexedMappedType(objectType *Type, index *Type) *Type { - mapper := newSimpleTypeMapper(c.getTypeParameterFromMappedType(objectType), index) - templateMapper := c.combineTypeMappers(objectType.AsMappedType().mapper, mapper) - instantiatedTemplateType := c.instantiateType(c.getTemplateTypeFromMappedType(core.OrElse(objectType.AsMappedType().target, objectType)), templateMapper) - isOptional := getMappedTypeOptionality(objectType) > 0 - if !isOptional { - if c.isGenericType(objectType) { - isOptional = c.getCombinedMappedTypeOptionality(c.getModifiersTypeFromMappedType(objectType)) > 0 - } else { - isOptional = c.couldAccessOptionalProperty(objectType, index) - } - } - return c.addOptionalityEx(instantiatedTemplateType, true /*isProperty*/, isOptional) -} - -// Return true if an indexed access with the given object and index types could access an optional property. -func (c *Checker) couldAccessOptionalProperty(objectType *Type, indexType *Type) bool { - indexConstraint := c.getBaseConstraintOfType(indexType) - return indexConstraint != nil && core.Some(c.getPropertiesOfType(objectType), func(p *ast.Symbol) bool { - return p.Flags&ast.SymbolFlagsOptional != 0 && c.isTypeAssignableTo(c.getLiteralTypeFromProperty(p, TypeFlagsStringOrNumberLiteralOrUnique, false), indexConstraint) - }) -} - -func (c *Checker) getTypeOfPropertyOrIndexSignatureOfType(t *Type, name string) *Type { - propType := c.getTypeOfPropertyOfType(t, name) - if propType != nil { - return propType - } - indexInfo := c.getApplicableIndexInfoForName(t, name) - if indexInfo != nil { - return c.addOptionalityEx(indexInfo.valueType, true /*isProperty*/, true /*isOptional*/) - } - return nil -} - -/** - * Whoa! Do you really want to use this function? - * - * Unless you're trying to get the *non-apparent* type for a - * value-literal type or you're authoring relevant portions of this algorithm, - * you probably meant to use 'getApparentTypeOfContextualType'. - * Otherwise this may not be very useful. - * - * In cases where you *are* working on this function, you should understand - * when it is appropriate to use 'getContextualType' and 'getApparentTypeOfContextualType'. - * - * - Use 'getContextualType' when you are simply going to propagate the result to the expression. - * - Use 'getApparentTypeOfContextualType' when you're going to need the members of the type. - * - * @param node the expression whose contextual type will be returned. - * @returns the contextual type of an expression. - */ -func (c *Checker) getContextualType(node *ast.Node, contextFlags ContextFlags) *Type { - if node.Flags&ast.NodeFlagsInWithStatement != 0 { - // We cannot answer semantic questions within a with block, do not proceed any further - return nil - } - // Cached contextual types are obtained with no ContextFlags, so we can only consult them for - // requests with no ContextFlags. - index := c.findContextualNode(node, contextFlags == ContextFlagsNone /*includeCaches*/) - if index >= 0 { - return c.contextualInfos[index].t - } - parent := node.Parent - switch parent.Kind { - case ast.KindVariableDeclaration, ast.KindParameter, ast.KindPropertyDeclaration, ast.KindPropertySignature, ast.KindBindingElement: - return c.getContextualTypeForInitializerExpression(node, contextFlags) - case ast.KindArrowFunction, ast.KindReturnStatement: - return c.getContextualTypeForReturnExpression(node, contextFlags) - case ast.KindYieldExpression: - return c.getContextualTypeForYieldOperand(parent, contextFlags) - case ast.KindAwaitExpression: - return c.getContextualTypeForAwaitOperand(parent, contextFlags) - case ast.KindCallExpression, ast.KindNewExpression: - return c.getContextualTypeForArgument(parent, node) - case ast.KindDecorator: - return c.getContextualTypeForDecorator(parent) - case ast.KindTypeAssertionExpression, ast.KindAsExpression: - if isConstAssertion(parent) { - return c.getContextualType(parent, contextFlags) - } - return c.getTypeFromTypeNode(getAssertedTypeNode(parent)) - case ast.KindBinaryExpression: - return c.getContextualTypeForBinaryOperand(node, contextFlags) - case ast.KindPropertyAssignment, - ast.KindShorthandPropertyAssignment: - return c.getContextualTypeForObjectLiteralElement(parent, contextFlags) - case ast.KindSpreadAssignment: - return c.getContextualType(parent.Parent, contextFlags) - case ast.KindArrayLiteralExpression: - t := c.getApparentTypeOfContextualType(parent, contextFlags) - elementIndex := ast.IndexOfNode(parent.AsArrayLiteralExpression().Elements.Nodes, node) - firstSpreadIndex, lastSpreadIndex := c.getSpreadIndices(parent) - return c.getContextualTypeForElementExpression(t, elementIndex, len(parent.AsArrayLiteralExpression().Elements.Nodes), firstSpreadIndex, lastSpreadIndex) - case ast.KindConditionalExpression: - return c.getContextualTypeForConditionalOperand(node, contextFlags) - case ast.KindTemplateSpan: - return c.getContextualTypeForSubstitutionExpression(parent.Parent, node) - case ast.KindParenthesizedExpression: - return c.getContextualType(parent, contextFlags) - case ast.KindNonNullExpression: - return c.getContextualType(parent, contextFlags) - case ast.KindSatisfiesExpression: - return c.getTypeFromTypeNode(parent.AsSatisfiesExpression().Type) - case ast.KindExportAssignment, ast.KindJSExportAssignment, ast.KindCommonJSExport: - return c.tryGetTypeFromTypeNode(parent) - case ast.KindJsxExpression: - return c.getContextualTypeForJsxExpression(parent, contextFlags) - case ast.KindJsxAttribute, ast.KindJsxSpreadAttribute: - return c.getContextualTypeForJsxAttribute(parent, contextFlags) - case ast.KindJsxOpeningElement, ast.KindJsxSelfClosingElement: - return c.getContextualJsxElementAttributesType(parent, contextFlags) - case ast.KindImportAttribute: - return c.getContextualImportAttributeType(parent) - } - return nil -} - -// In a variable, parameter or property declaration with a type annotation, -// the contextual type of an initializer expression is the type of the variable, parameter or property. -// -// Otherwise, in a parameter declaration of a contextually typed function expression, -// the contextual type of an initializer expression is the contextual type of the parameter. -// -// Otherwise, in a variable or parameter declaration with a binding pattern name, -// the contextual type of an initializer expression is the type implied by the binding pattern. -// -// Otherwise, in a binding pattern inside a variable or parameter declaration, -// the contextual type of an initializer expression is the type annotation of the containing declaration, if present. -func (c *Checker) getContextualTypeForInitializerExpression(node *ast.Node, contextFlags ContextFlags) *Type { - declaration := node.Parent - initializer := declaration.Initializer() - if node == initializer { - result := c.getContextualTypeForVariableLikeDeclaration(declaration, contextFlags) - if result != nil { - return result - } - if contextFlags&ContextFlagsSkipBindingPatterns == 0 && ast.IsBindingPattern(declaration.Name()) && len(declaration.Name().AsBindingPattern().Elements.Nodes) > 0 { - return c.getTypeFromBindingPattern(declaration.Name(), true /*includePatternInType*/, false /*reportErrors*/) - } - } - return nil -} - -func (c *Checker) getContextualTypeForVariableLikeDeclaration(declaration *ast.Node, contextFlags ContextFlags) *Type { - typeNode := declaration.Type() - if typeNode != nil { - return c.getTypeFromTypeNode(typeNode) - } - switch declaration.Kind { - case ast.KindParameter: - return c.getContextuallyTypedParameterType(declaration) - case ast.KindBindingElement: - return c.getContextualTypeForBindingElement(declaration, contextFlags) - case ast.KindPropertyDeclaration: - if ast.IsStatic(declaration) { - return c.getContextualTypeForStaticPropertyDeclaration(declaration, contextFlags) - } - } - // By default, do nothing and return nil - only the above cases have context implied by a parent - return nil -} - -// Return contextual type of parameter or undefined if no contextual type is available -func (c *Checker) getContextuallyTypedParameterType(parameter *ast.Node) *Type { - fn := parameter.Parent - if !c.isContextSensitiveFunctionOrObjectLiteralMethod(fn) { - return nil - } - iife := ast.GetImmediatelyInvokedFunctionExpression(fn) - if iife != nil { - args := c.getEffectiveCallArguments(iife) - indexOfParameter := slices.Index(fn.Parameters(), parameter) - if hasDotDotDotToken(parameter) { - return c.getSpreadArgumentType(args, indexOfParameter, len(args), c.anyType, nil /*context*/, CheckModeNormal) - } - links := c.signatureLinks.Get(iife) - cached := links.resolvedSignature - links.resolvedSignature = c.anySignature - var t *Type - switch { - case indexOfParameter < len(args): - t = c.getWidenedLiteralType(c.checkExpression(args[indexOfParameter])) - case parameter.Initializer() != nil: - t = nil - default: - t = c.undefinedWideningType - } - links.resolvedSignature = cached - return t - } - contextualSignature := c.getContextualSignature(fn) - if contextualSignature != nil { - index := slices.Index(fn.Parameters(), parameter) - core.IfElse(ast.GetThisParameter(fn) != nil, 1, 0) - if hasDotDotDotToken(parameter) && core.LastOrNil(fn.Parameters()) == parameter { - return c.getRestTypeAtPosition(contextualSignature, index, false) - } - return c.tryGetTypeAtPosition(contextualSignature, index) - } - return nil -} - -func (c *Checker) isContextSensitiveFunctionOrObjectLiteralMethod(fn *ast.Node) bool { - return (ast.IsFunctionExpressionOrArrowFunction(fn) || ast.IsObjectLiteralMethod(fn)) && c.isContextSensitiveFunctionLikeDeclaration(fn) -} - -func (c *Checker) getSpreadArgumentType(args []*ast.Node, index int, argCount int, restType *Type, context *InferenceContext, checkMode CheckMode) *Type { - inConstContext := c.isConstTypeVariable(restType, 0) - if argCount > 0 && index >= argCount-1 { - arg := args[argCount-1] - if isSpreadArgument(arg) { - // We are inferring from a spread expression in the last argument position, i.e. both the parameter - // and the argument are ...x forms. - var spreadType *Type - if ast.IsSyntheticExpression(arg) { - spreadType = arg.AsSyntheticExpression().Type.(*Type) - } else { - spreadType = c.checkExpressionWithContextualType(arg.Expression(), restType, context, checkMode) - } - if c.isArrayLikeType(spreadType) { - return c.getMutableArrayOrTupleType(spreadType) - } - if ast.IsSpreadElement(arg) { - arg = arg.Expression() - } - return c.createArrayTypeEx(c.checkIteratedTypeOrElementType(IterationUseSpread, spreadType, c.undefinedType, arg), inConstContext) - } - } - var types []*Type - var infos []TupleElementInfo - for i := index; i < argCount; i++ { - arg := args[i] - var t *Type - var info TupleElementInfo - if isSpreadArgument(arg) { - var spreadType *Type - if ast.IsSyntheticExpression(arg) { - spreadType = arg.AsSyntheticExpression().Type.(*Type) - } else { - spreadType = c.checkExpression(arg.Expression()) - } - if c.isArrayLikeType(spreadType) { - t = spreadType - info.flags = ElementFlagsVariadic - } else { - if ast.IsSpreadElement(arg) { - t = c.checkIteratedTypeOrElementType(IterationUseSpread, spreadType, c.undefinedType, arg.Expression()) - } else { - t = c.checkIteratedTypeOrElementType(IterationUseSpread, spreadType, c.undefinedType, arg) - } - info.flags = ElementFlagsRest - } - } else { - var contextualType *Type - if isTupleType(restType) { - contextualType = core.OrElse(c.getContextualTypeForElementExpression(restType, i-index, argCount-index, -1, -1), c.unknownType) - } else { - contextualType = c.getIndexedAccessTypeEx(restType, c.getNumberLiteralType(jsnum.Number(i-index)), AccessFlagsContextual, nil, nil) - } - argType := c.checkExpressionWithContextualType(arg, contextualType, context, checkMode) - hasPrimitiveContextualType := inConstContext || c.maybeTypeOfKind(contextualType, TypeFlagsPrimitive|TypeFlagsIndex|TypeFlagsTemplateLiteral|TypeFlagsStringMapping) - if hasPrimitiveContextualType { - t = c.getRegularTypeOfLiteralType(argType) - } else { - t = c.getWidenedLiteralType(argType) - } - info.flags = ElementFlagsRequired - } - if ast.IsSyntheticExpression(arg) && arg.AsSyntheticExpression().TupleNameSource != nil { - info.labeledDeclaration = arg.AsSyntheticExpression().TupleNameSource - } - types = append(types, t) - infos = append(infos, info) - } - return c.createTupleTypeEx(types, infos, inConstContext && !someType(restType, c.isMutableArrayLikeType)) -} - -func (c *Checker) getMutableArrayOrTupleType(t *Type) *Type { - switch { - case t.flags&TypeFlagsUnion != 0: - return c.mapType(t, c.getMutableArrayOrTupleType) - case t.flags&TypeFlagsAny != 0 || c.isMutableArrayOrTuple(c.getBaseConstraintOrType(t)): - return t - case isTupleType(t): - return c.createTupleTypeEx(c.getElementTypes(t), t.TargetTupleType().elementInfos, false /*readonly*/) - } - return c.createTupleTypeEx([]*Type{t}, []TupleElementInfo{{flags: ElementFlagsVariadic}}, false) -} - -func (c *Checker) getContextualTypeForBindingElement(declaration *ast.Node, contextFlags ContextFlags) *Type { - name := declaration.PropertyNameOrName() - if ast.IsBindingPattern(name) || ast.IsComputedNonLiteralName(name) { - return nil - } - parent := declaration.Parent.Parent - parentType := c.getContextualTypeForVariableLikeDeclaration(parent, contextFlags) - if parentType == nil { - if !ast.IsBindingElement(parent) && parent.Initializer() != nil { - parentType = c.checkDeclarationInitializer(parent, core.IfElse(hasDotDotDotToken(declaration), CheckModeRestBindingElement, CheckModeNormal), nil) - } - } - if parentType == nil { - return nil - } - if ast.IsArrayBindingPattern(parent.Name()) { - index := slices.Index(declaration.Parent.AsBindingPattern().Elements.Nodes, declaration) - if index < 0 { - return nil - } - return c.getContextualTypeForElementExpression(parentType, index, -1, -1, -1) - } - nameType := c.getLiteralTypeFromPropertyName(name) - if isTypeUsableAsPropertyName(nameType) { - return c.getTypeOfPropertyOfType(parentType, getPropertyNameFromType(nameType)) - } - return nil -} - -func (c *Checker) getContextualTypeForStaticPropertyDeclaration(declaration *ast.Node, contextFlags ContextFlags) *Type { - if ast.IsExpression(declaration.Parent) { - if parentType := c.getContextualType(declaration.Parent, contextFlags); parentType != nil { - return c.getTypeOfPropertyOfContextualType(parentType, c.getSymbolOfDeclaration(declaration).Name) - } - } - return nil -} - -func (c *Checker) getContextualTypeForReturnExpression(node *ast.Node, contextFlags ContextFlags) *Type { - fn := ast.GetContainingFunction(node) - if fn != nil { - contextualReturnType := c.getContextualReturnType(fn, contextFlags) - if contextualReturnType != nil { - functionFlags := getFunctionFlags(fn) - if functionFlags&FunctionFlagsGenerator != 0 { - isAsyncGenerator := (functionFlags & FunctionFlagsAsync) != 0 - if contextualReturnType.flags&TypeFlagsUnion != 0 { - contextualReturnType = c.filterType(contextualReturnType, func(t *Type) bool { - return c.getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKindReturn, t, isAsyncGenerator) != nil - }) - } - iterationReturnType := c.getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKindReturn, contextualReturnType, (functionFlags&FunctionFlagsAsync) != 0) - if iterationReturnType == nil { - return nil - } - contextualReturnType = iterationReturnType - // falls through to unwrap Promise for AsyncGenerators - } - if functionFlags&FunctionFlagsAsync != 0 { - // Get the awaited type without the `Awaited` alias - contextualAwaitedType := c.mapType(contextualReturnType, c.getAwaitedTypeNoAlias) - return c.getUnionType([]*Type{contextualAwaitedType, c.createPromiseLikeType(contextualAwaitedType)}) - } - // Regular function or Generator function - return contextualReturnType - } - } - return nil -} - -func (c *Checker) getContextualIterationType(kind IterationTypeKind, functionDecl *ast.Node) *Type { - isAsync := getFunctionFlags(functionDecl)&FunctionFlagsAsync != 0 - contextualReturnType := c.getContextualReturnType(functionDecl, ContextFlagsNone) - if contextualReturnType != nil { - return c.getIterationTypeOfGeneratorFunctionReturnType(kind, contextualReturnType, isAsync) - } - return nil -} - -func (c *Checker) getContextualReturnType(functionDecl *ast.Node, contextFlags ContextFlags) *Type { - // If the containing function has a return type annotation, is a constructor, or is a get accessor whose - // corresponding set accessor has a type annotation, return statements in the function are contextually typed - returnType := c.getReturnTypeFromAnnotation(functionDecl) - if returnType != nil { - return returnType - } - // Otherwise, if the containing function is contextually typed by a function type with exactly one call signature - // and that call signature is non-generic, return statements are contextually typed by the return type of the signature - signature := c.getContextualSignatureForFunctionLikeDeclaration(functionDecl) - if signature != nil && !c.isResolvingReturnTypeOfSignature(signature) { - returnType := c.getReturnTypeOfSignature(signature) - functionFlags := getFunctionFlags(functionDecl) - if functionFlags&FunctionFlagsGenerator != 0 { - return c.filterType(returnType, func(t *Type) bool { - return t.flags&(TypeFlagsAnyOrUnknown|TypeFlagsVoid|TypeFlagsInstantiableNonPrimitive) != 0 || c.checkGeneratorInstantiationAssignabilityToReturnType(t, functionFlags, nil /*errorNode*/) - }) - } - if functionFlags&FunctionFlagsAsync != 0 { - return c.filterType(returnType, func(t *Type) bool { - return t.flags&(TypeFlagsAnyOrUnknown|TypeFlagsVoid|TypeFlagsInstantiableNonPrimitive) != 0 || c.getAwaitedTypeOfPromise(t) != nil - }) - } - return returnType - } - iife := ast.GetImmediatelyInvokedFunctionExpression(functionDecl) - if iife != nil { - return c.getContextualType(iife, contextFlags) - } - return nil -} - -func (c *Checker) checkGeneratorInstantiationAssignabilityToReturnType(returnType *Type, functionFlags FunctionFlags, errorNode *ast.Node) bool { - // Naively, one could check that Generator is assignable to the return type annotation. - // However, that would not catch the error in the following case. - // - // interface BadGenerator extends Iterable, Iterator { } - // function* g(): BadGenerator { } // Iterable and Iterator have different types! - // - generatorYieldType := core.OrElse(c.getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKindYield, returnType, (functionFlags&FunctionFlagsAsync) != 0), c.anyType) - generatorReturnType := core.OrElse(c.getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKindReturn, returnType, (functionFlags&FunctionFlagsAsync) != 0), generatorYieldType) - generatorNextType := core.OrElse(c.getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKindNext, returnType, (functionFlags&FunctionFlagsAsync) != 0), c.unknownType) - generatorInstantiation := c.createGeneratorType(generatorYieldType, generatorReturnType, generatorNextType, functionFlags&FunctionFlagsAsync != 0) - return c.checkTypeAssignableTo(generatorInstantiation, returnType, errorNode, nil) -} - -func (c *Checker) getContextualSignatureForFunctionLikeDeclaration(node *ast.Node) *Signature { - // Only function expressions, arrow functions, and object literal methods are contextually typed. - if ast.IsFunctionExpressionOrArrowFunction(node) || ast.IsObjectLiteralMethod(node) { - return c.getContextualSignature(node) - } - return nil -} - -func (c *Checker) getContextualTypeForYieldOperand(node *ast.Node, contextFlags ContextFlags) *Type { - fn := ast.GetContainingFunction(node) - if fn != nil { - functionFlags := getFunctionFlags(fn) - contextualReturnType := c.getContextualReturnType(fn, contextFlags) - if contextualReturnType != nil { - isAsyncGenerator := functionFlags&FunctionFlagsAsync != 0 - isYieldStar := node.AsYieldExpression().AsteriskToken != nil - if !isYieldStar && contextualReturnType.flags&TypeFlagsUnion != 0 { - contextualReturnType = c.filterType(contextualReturnType, func(t *Type) bool { - return c.getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKindReturn, t, isAsyncGenerator) != nil - }) - } - if isYieldStar { - iterationTypes := c.getIterationTypesOfGeneratorFunctionReturnType(contextualReturnType, isAsyncGenerator) - yieldType := core.OrElse(iterationTypes.yieldType, c.silentNeverType) - returnType := core.OrElse(c.getContextualType(node, contextFlags), c.silentNeverType) - nextType := core.OrElse(iterationTypes.nextType, c.unknownType) - generatorType := c.createGeneratorType(yieldType, returnType, nextType, false /*isAsyncGenerator*/) - if isAsyncGenerator { - asyncGeneratorType := c.createGeneratorType(yieldType, returnType, nextType, true /*isAsyncGenerator*/) - return c.getUnionType([]*Type{generatorType, asyncGeneratorType}) - } - return generatorType - } - return c.getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKindYield, contextualReturnType, isAsyncGenerator) - } - } - return nil -} - -func (c *Checker) getContextualTypeForAwaitOperand(node *ast.Node, contextFlags ContextFlags) *Type { - contextualType := c.getContextualType(node, contextFlags) - if contextualType != nil { - contextualAwaitedType := c.getAwaitedTypeNoAlias(contextualType) - if contextualAwaitedType != nil { - return c.getUnionType([]*Type{contextualAwaitedType, c.createPromiseLikeType(contextualAwaitedType)}) - } - } - return nil -} - -// In a typed function call, an argument or substitution expression is contextually typed by the type of the corresponding parameter. -func (c *Checker) getContextualTypeForArgument(callTarget *ast.Node, arg *ast.Node) *Type { - args := c.getEffectiveCallArguments(callTarget) - argIndex := slices.Index(args, arg) - // -1 for e.g. the expression of a CallExpression, or the tag of a TaggedTemplateExpression - if argIndex == -1 { - return nil - } - return c.getContextualTypeForArgumentAtIndex(callTarget, argIndex) -} - -func (c *Checker) getContextualTypeForArgumentAtIndex(callTarget *ast.Node, argIndex int) *Type { - if ast.IsImportCall(callTarget) { - switch { - case argIndex == 0: - return c.stringType - case argIndex == 1: - return c.getGlobalImportCallOptionsType() - default: - return c.anyType - } - } - // If we're already in the process of resolving the given signature, don't resolve again as - // that could cause infinite recursion. Instead, return anySignature. - var signature *Signature - if c.signatureLinks.Get(callTarget).resolvedSignature == c.resolvingSignature { - signature = c.resolvingSignature - } else { - signature = c.getResolvedSignature(callTarget, nil, CheckModeNormal) - } - if ast.IsJsxOpeningLikeElement(callTarget) && argIndex == 0 { - return c.getEffectiveFirstArgumentForJsxSignature(signature, callTarget) - } - restIndex := len(signature.parameters) - 1 - if signatureHasRestParameter(signature) && argIndex >= restIndex { - return c.getIndexedAccessTypeEx(c.getTypeOfSymbol(signature.parameters[restIndex]), c.getNumberLiteralType(jsnum.Number(argIndex-restIndex)), AccessFlagsContextual, nil, nil) - } - return c.getTypeAtPosition(signature, argIndex) -} - -func (c *Checker) getContextualTypeForDecorator(decorator *ast.Node) *Type { - signature := c.getDecoratorCallSignature(decorator) - if signature != nil { - return c.getOrCreateTypeFromSignature(signature) - } - return nil -} - -func (c *Checker) getContextualTypeForBinaryOperand(node *ast.Node, contextFlags ContextFlags) *Type { - binary := node.Parent.AsBinaryExpression() - if t := binary.Type; t != nil { - return c.getTypeFromTypeNode(t) - } - switch binary.OperatorToken.Kind { - case ast.KindEqualsToken, ast.KindAmpersandAmpersandEqualsToken, ast.KindBarBarEqualsToken, ast.KindQuestionQuestionEqualsToken: - // In an assignment expression, the right operand is contextually typed by the type of the left operand - // unless it's an assignment declaration. - kind := ast.GetAssignmentDeclarationKind(binary) - if node == binary.Right && kind != ast.JSDeclarationKindModuleExports && kind != ast.JSDeclarationKindExportsProperty { - return c.getContextualTypeForAssignmentExpression(binary) - } - case ast.KindBarBarToken, ast.KindQuestionQuestionToken: - // When an || expression has a contextual type, the operands are contextually typed by that type, except - // when that type originates in a binding pattern, the right operand is contextually typed by the type of - // the left operand. When an || expression has no contextual type, the right operand is contextually typed - // by the type of the left operand, except for the special case of Javascript declarations of the form - // `namespace.prop = namespace.prop || {}`. - t := c.getContextualType(binary.AsNode(), contextFlags) - if node == binary.Right && (t == nil || c.patternForType[t] != nil) { - return c.getTypeOfExpression(binary.Left) - } - return t - case ast.KindAmpersandAmpersandToken, ast.KindCommaToken: - if node == binary.Right { - return c.getContextualType(binary.AsNode(), contextFlags) - } - } - return nil -} - -func (c *Checker) getContextualTypeForAssignmentExpression(binary *ast.BinaryExpression) *Type { - left := binary.Left - if ast.IsAccessExpression(left) { - expr := left.Expression() - switch expr.Kind { - case ast.KindIdentifier: - symbol := c.getExportSymbolOfValueSymbolIfExported(c.getResolvedSymbol(expr)) - if symbol.Flags&ast.SymbolFlagsModuleExports != 0 { - // No contextual type for an expression of the form 'module.exports = expr'. - return nil - } - if binary.Symbol != nil { - // We have an assignment declaration (a binary expression with a symbol assigned by the binder) of the form - // 'F.id = expr' or 'F[xxx] = expr'. If 'F' is declared as a variable with a type annotation, we can obtain a - // contextual type from the annotated type without triggering a circularity. Otherwise, the assignment - // declaration has no contextual type. - if symbol.ValueDeclaration != nil && ast.IsVariableDeclaration(symbol.ValueDeclaration) { - if typeNode := symbol.ValueDeclaration.Type(); typeNode != nil { - if ast.IsPropertyAccessExpression(left) { - return c.getTypeOfPropertyOfContextualType(c.getTypeFromTypeNode(typeNode), left.Name().Text()) - } - nameType := c.checkExpressionCached(left.AsElementAccessExpression().ArgumentExpression) - if isTypeUsableAsPropertyName(nameType) { - return c.getTypeOfPropertyOfContextualTypeEx(c.getTypeFromTypeNode(typeNode), getPropertyNameFromType(nameType), nameType) - } - return c.getTypeOfExpression(left) - } - } - return nil - } - case ast.KindPropertyAccessExpression, ast.KindElementAccessExpression: - if binary.Symbol != nil { - return nil - } - case ast.KindThisKeyword: - var symbol *ast.Symbol - thisType := c.getTypeOfExpression(expr) - if ast.IsPropertyAccessExpression(left) { - name := left.Name() - if ast.IsPrivateIdentifier(name) { - symbol = c.getPropertyOfType(thisType, binder.GetSymbolNameForPrivateIdentifier(thisType.symbol, name.Text())) - } else { - symbol = c.getPropertyOfType(thisType, name.Text()) - } - } else { - propType := c.checkExpressionCached(left.AsElementAccessExpression().ArgumentExpression) - if isTypeUsableAsPropertyName(propType) { - symbol = c.getPropertyOfType(thisType, getPropertyNameFromType(propType)) - } - } - if symbol != nil { - if d := symbol.ValueDeclaration; d != nil && (ast.IsPropertyDeclaration(d) || ast.IsPropertySignatureDeclaration(d)) && d.Type() == nil && d.Initializer() == nil { - // No contextual type for 'this.xxx = expr', where xxx is declared as a property with no type annotation or initializer. - return nil - } - } - if binary.Symbol != nil && binary.Symbol.ValueDeclaration != nil && binary.Symbol.ValueDeclaration.Type() == nil { - // We have an assignment declaration 'this.xxx = expr' with no (synthetic) type annotation - if !ast.IsObjectLiteralMethod(c.getThisContainer(expr, false, false)) { - return nil - } - // and now for one single case of object literal methods - name := ast.GetElementOrPropertyAccessName(left) - if name == nil { - return nil - } else { - // !!! contextual typing for `this` in object literals - return nil - } - } - } - } - return c.getTypeOfExpression(left) -} - -func (c *Checker) getContextualTypeForObjectLiteralElement(element *ast.Node, contextFlags ContextFlags) *Type { - if t := element.Type(); t != nil && !ast.IsObjectLiteralMethod(element) { - return c.getTypeFromTypeNode(t) - } - objectLiteral := element.Parent - t := c.getApparentTypeOfContextualType(objectLiteral, contextFlags) - if t != nil { - if c.hasBindableName(element) { - // For a (non-symbol) computed property, there is no reason to look up the name - // in the type. It will just be "__computed", which does not appear in any - // SymbolTable. - symbol := c.getSymbolOfDeclaration(element) - return c.getTypeOfPropertyOfContextualTypeEx(t, symbol.Name, c.valueSymbolLinks.Get(symbol).nameType) - } - if ast.HasDynamicName(element) { - name := ast.GetNameOfDeclaration(element) - if name != nil && ast.IsComputedPropertyName(name) { - exprType := c.checkExpression(name.Expression()) - if isTypeUsableAsPropertyName(exprType) { - propType := c.getTypeOfPropertyOfContextualType(t, getPropertyNameFromType(exprType)) - if propType != nil { - return propType - } - } - } - } - if element.Name() != nil { - nameType := c.getLiteralTypeFromPropertyName(element.Name()) - // We avoid calling getApplicableIndexInfo here because it performs potentially expensive intersection reduction. - return c.mapTypeEx(t, func(t *Type) *Type { - indexInfo := c.findApplicableIndexInfo(c.getIndexInfosOfStructuredType(t), nameType) - if indexInfo == nil { - return nil - } - return indexInfo.valueType - }, true /*noReductions*/) - } - } - return nil -} - -// In an object literal contextually typed by a type T, the contextual type of a property assignment is the type of -// the matching property in T, if one exists. Otherwise, it is the type of the numeric index signature in T, if one -// exists. Otherwise, it is the type of the string index signature in T, if one exists. -func (c *Checker) getContextualTypeForObjectLiteralMethod(node *ast.Node, contextFlags ContextFlags) *Type { - if node.Flags&ast.NodeFlagsInWithStatement != 0 { - // We cannot answer semantic questions within a with block, do not proceed any further - return nil - } - return c.getContextualTypeForObjectLiteralElement(node, contextFlags) -} - -func (c *Checker) getContextualTypeForElementExpression(t *Type, index int, length int, firstSpreadIndex int, lastSpreadIndex int) *Type { - if t == nil { - return nil - } - return c.mapTypeEx(t, func(t *Type) *Type { - if isTupleType(t) { - // If index is before any spread element and within the fixed part of the contextual tuple type, return - // the type of the contextual tuple element. - if (firstSpreadIndex < 0 || index < firstSpreadIndex) && index < t.TargetTupleType().fixedLength { - return c.removeMissingType(c.getTypeArguments(t)[index], t.TargetTupleType().elementInfos[index].flags&ElementFlagsOptional != 0) - } - // When the length is known and the index is after all spread elements we compute the offset from the element - // to the end and the number of ending fixed elements in the contextual tuple type. - offset := 0 - if length >= 0 && (lastSpreadIndex < 0 || index > lastSpreadIndex) { - offset = length - index - } - fixedEndLength := 0 - if offset > 0 && t.TargetTupleType().combinedFlags&ElementFlagsVariable != 0 { - fixedEndLength = getEndElementCount(t.TargetTupleType(), ElementFlagsFixed) - } - // If the offset is within the ending fixed part of the contextual tuple type, return the type of the contextual - // tuple element. - if offset > 0 && offset <= fixedEndLength { - return c.getTypeArguments(t)[c.getTypeReferenceArity(t)-offset] - } - // Return a union of the possible contextual element types with no subtype reduction. - tupleIndex := t.TargetTupleType().fixedLength - if firstSpreadIndex >= 0 { - tupleIndex = min(tupleIndex, firstSpreadIndex) - } - endSkipCount := fixedEndLength - if length >= 0 && lastSpreadIndex >= 0 { - endSkipCount = min(fixedEndLength, length-lastSpreadIndex) - } - return c.getElementTypeOfSliceOfTupleType(t, tupleIndex, endSkipCount, false /*writing*/, true /*noReductions*/) - } - // If element index is known and a contextual property with that name exists, return it. Otherwise return the - // iterated or element type of the contextual type. - if firstSpreadIndex < 0 || index < firstSpreadIndex { - propType := c.getTypeOfPropertyOfContextualType(t, strconv.Itoa(index)) - if propType != nil { - return propType - } - } - return c.getIteratedTypeOrElementType(IterationUseElement, t, c.undefinedType, nil /*errorNode*/, false /*checkAssignability*/) - }, true /*noReductions*/) -} - -// In a contextually typed conditional expression, the true/false expressions are contextually typed by the same type. -func (c *Checker) getContextualTypeForConditionalOperand(node *ast.Node, contextFlags ContextFlags) *Type { - conditional := node.Parent.AsConditionalExpression() - if node == conditional.WhenTrue || node == conditional.WhenFalse { - return c.getContextualType(node.Parent, contextFlags) - } - return nil -} - -func (c *Checker) getContextualTypeForSubstitutionExpression(template *ast.Node, substitutionExpression *ast.Node) *Type { - if ast.IsTaggedTemplateExpression(template.Parent) { - return c.getContextualTypeForArgument(template.Parent, substitutionExpression) - } - return nil -} - -func (c *Checker) getContextualImportAttributeType(node *ast.Node) *Type { - return c.getTypeOfPropertyOfContextualType(c.getGlobalImportAttributesType(), node.Name().Text()) -} - -// Returns the effective arguments for an expression that works like a function invocation. -func (c *Checker) getEffectiveCallArguments(node *ast.Node) []*ast.Node { - switch { - case ast.IsJsxOpeningFragment(node): - // This attributes Type does not include a children property yet, the same way a fragment created with does not at this stage - return []*ast.Node{c.createSyntheticExpression(node, c.emptyFreshJsxObjectType, false, nil)} - case ast.IsTaggedTemplateExpression(node): - template := node.AsTaggedTemplateExpression().Template - firstArg := c.createSyntheticExpression(template, c.getGlobalTemplateStringsArrayType(), false, nil) - if !ast.IsTemplateExpression(template) { - return []*ast.Node{firstArg} - } - spans := template.AsTemplateExpression().TemplateSpans.Nodes - args := make([]*ast.Node, len(spans)+1) - args[0] = firstArg - for i, span := range spans { - args[i+1] = span.Expression() - } - return args - case ast.IsDecorator(node): - return c.getEffectiveDecoratorArguments(node) - case ast.IsBinaryExpression(node): - // Handles instanceof operator - return []*ast.Node{node.AsBinaryExpression().Left} - case ast.IsJsxOpeningLikeElement(node): - if len(node.Attributes().AsJsxAttributes().Properties.Nodes) != 0 || (ast.IsJsxOpeningElement(node) && len(node.Parent.Children().Nodes) != 0) { - return []*ast.Node{node.Attributes()} - } - return nil - default: - args := node.Arguments() - spreadIndex := c.getSpreadArgumentIndex(args) - if spreadIndex >= 0 { - // Create synthetic arguments from spreads of tuple types. - effectiveArgs := slices.Clip(args[:spreadIndex]) - for i := spreadIndex; i < len(args); i++ { - arg := args[i] - var spreadType *Type - // We can call checkExpressionCached because spread expressions never have a contextual type. - if ast.IsSpreadElement(arg) { - if len(c.flowLoopStack) != 0 { - spreadType = c.checkExpression(arg.Expression()) - } else { - spreadType = c.checkExpressionCached(arg.Expression()) - } - } - if spreadType != nil && isTupleType(spreadType) { - for i, t := range c.getElementTypes(spreadType) { - elementInfos := spreadType.TargetTupleType().elementInfos - flags := elementInfos[i].flags - syntheticType := t - if flags&ElementFlagsRest != 0 { - syntheticType = c.createArrayType(t) - } - syntheticArg := c.createSyntheticExpression(arg, syntheticType, flags&ElementFlagsVariable != 0, elementInfos[i].labeledDeclaration) - effectiveArgs = append(effectiveArgs, syntheticArg) - } - } else { - effectiveArgs = append(effectiveArgs, arg) - } - } - return effectiveArgs - } - return args - } -} - -func (c *Checker) getSpreadArgumentIndex(args []*ast.Node) int { - return core.FindIndex(args, isSpreadArgument) -} - -func isSpreadArgument(arg *ast.Node) bool { - return ast.IsSpreadElement(arg) || ast.IsSyntheticExpression(arg) && arg.AsSyntheticExpression().IsSpread -} - -func (c *Checker) createSyntheticExpression(parent *ast.Node, t *Type, isSpread bool, tupleNameSource *ast.Node) *ast.Node { - result := c.factory.NewSyntheticExpression(t, isSpread, tupleNameSource) - result.Loc = parent.Loc - result.Parent = parent - return result -} - -func (c *Checker) getSpreadIndices(node *ast.Node) (int, int) { - links := c.arrayLiteralLinks.Get(node) - if !links.indicesComputed { - first, last := -1, -1 - for i, element := range node.AsArrayLiteralExpression().Elements.Nodes { - if ast.IsSpreadElement(element) { - if first < 0 { - first = i - } - last = i - } - } - links.firstSpreadIndex, links.lastSpreadIndex = first, last - links.indicesComputed = true - } - return links.firstSpreadIndex, links.lastSpreadIndex -} - -// Returns the synthetic argument list for a decorator invocation. -func (c *Checker) getEffectiveDecoratorArguments(node *ast.Node) []*ast.Node { - expr := node.Expression() - signature := c.getDecoratorCallSignature(node) - if signature != nil { - args := make([]*ast.Node, len(signature.parameters)) - for i, param := range signature.parameters { - args[i] = c.createSyntheticExpression(expr, c.getTypeOfSymbol(param), false, nil) - } - return args - } - panic("Decorator signature not found") -} - -func (c *Checker) getDecoratorCallSignature(decorator *ast.Node) *Signature { - if c.legacyDecorators { - return c.getLegacyDecoratorCallSignature(decorator) - } - return c.getESDecoratorCallSignature(decorator) -} - -func (c *Checker) getLegacyDecoratorCallSignature(decorator *ast.Node) *Signature { - node := decorator.Parent - links := c.signatureLinks.Get(node) - if links.decoratorSignature == nil { - links.decoratorSignature = c.anySignature - switch node.Kind { - case ast.KindClassDeclaration, ast.KindClassExpression: - // For a class decorator, the `target` is the type of the class (e.g. the - // "static" or "constructor" side of the class). - targetType := c.getTypeOfSymbol(c.getSymbolOfDeclaration(node)) - targetParam := c.newParameter("target", targetType) - links.decoratorSignature = c.newCallSignature(nil, nil, []*ast.Symbol{targetParam}, c.getUnionType([]*Type{targetType, c.voidType})) - case ast.KindParameter: - if !ast.IsConstructorDeclaration(node.Parent) && !(ast.IsMethodDeclaration(node.Parent) || ast.IsSetAccessorDeclaration(node.Parent) && ast.IsClassLike(node.Parent.Parent)) { - break - } - if ast.GetThisParameter(node.Parent) == node { - break - } - index := slices.Index(node.Parent.Parameters(), node) - core.IfElse(ast.GetThisParameter(node.Parent) != nil, 1, 0) - debug.Assert(index >= 0) - // A parameter declaration decorator will have three arguments (see `ParameterDecorator` in - // core.d.ts). - var targetType *Type - var keyType *Type - if ast.IsConstructorDeclaration(node.Parent) { - targetType = c.getTypeOfSymbol(c.getSymbolOfDeclaration(node.Parent.Parent)) - keyType = c.undefinedType - } else { - targetType = c.getParentTypeOfClassElement(node.Parent) - keyType = c.getClassElementPropertyKeyType(node.Parent) - } - indexType := c.getNumberLiteralType(jsnum.Number(index)) - targetParam := c.newParameter("target", targetType) - keyParam := c.newParameter("propertyKey", keyType) - indexParam := c.newParameter("parameterIndex", indexType) - links.decoratorSignature = c.newCallSignature(nil, nil, []*ast.Symbol{targetParam, keyParam, indexParam}, c.voidType) - case ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor, ast.KindPropertyDeclaration: - if !ast.IsClassLike(node.Parent) { - break - } - // A method or accessor declaration decorator will have either two or three arguments (see - // `PropertyDecorator` and `MethodDecorator` in core.d.ts). - targetType := c.getParentTypeOfClassElement(node) - targetParam := c.newParameter("target", targetType) - keyType := c.getClassElementPropertyKeyType(node) - keyParam := c.newParameter("propertyKey", keyType) - returnType := c.voidType - if !ast.IsPropertyDeclaration(node) { - returnType = c.newTypedPropertyDescriptorType(c.getTypeOfNode(node)) - } - hasPropDesc := !ast.IsPropertyDeclaration(node) || ast.HasAccessorModifier(node) - if hasPropDesc { - descriptorType := c.newTypedPropertyDescriptorType(c.getTypeOfNode(node)) - descriptorParam := c.newParameter("descriptor", descriptorType) - links.decoratorSignature = c.newCallSignature(nil, nil, []*ast.Symbol{targetParam, keyParam, descriptorParam}, c.getUnionType([]*Type{returnType, c.voidType})) - } else { - links.decoratorSignature = c.newCallSignature(nil, nil, []*ast.Symbol{targetParam, keyParam}, c.getUnionType([]*Type{returnType, c.voidType})) - } - } - } - if links.decoratorSignature == c.anySignature { - return nil - } - return links.decoratorSignature -} - -func (c *Checker) getESDecoratorCallSignature(decorator *ast.Node) *Signature { - // We are considering a future change that would allow the type of a decorator to affect the type of the - // class and its members, such as a `@Stringify` decorator changing the type of a `number` field to `string`, or - // a `@Callable` decorator adding a call signature to a `class`. The type arguments for the various context - // types may eventually change to reflect such mutations. - // - // In some cases we describe such potential mutations as coming from a "prior decorator application". It is - // important to note that, while decorators are *evaluated* left to right, they are *applied* right to left - // to preserve f à§¹ g -> f(g(x)) application order. In these cases, a "prior" decorator usually means the - // next decorator following this one in document order. - // - // The "original type" of a class or member is the type it was declared as, or the type we infer from - // initializers, before _any_ decorators are applied. - // - // The type of a class or member that is a result of a prior decorator application represents the - // "current type", i.e., the type for the declaration at the time the decorator is _applied_. - // - // The type of a class or member that is the result of the application of *all* relevant decorators is the - // "final type". - // - // Any decorator that allows mutation or replacement will also refer to an "input type" and an - // "output type". The "input type" corresponds to the "current type" of the declaration, while the - // "output type" will become either the "input type/current type" for a subsequent decorator application, - // or the "final type" for the decorated declaration. - // - // It is important to understand decorator application order as it relates to how the "current", "input", - // "output", and "final" types will be determined: - // - // @E2 @E1 class SomeClass { - // @A2 @A1 static f() {} - // @B2 @B1 g() {} - // @C2 @C1 static x; - // @D2 @D1 y; - // } - // - // Per [the specification][1], decorators are applied in the following order: - // - // 1. For each static method (incl. get/set methods and `accessor` fields), in document order: - // a. Apply each decorator for that method, in reverse order (`A1`, `A2`). - // 2. For each instance method (incl. get/set methods and `accessor` fields), in document order: - // a. Apply each decorator for that method, in reverse order (`B1`, `B2`). - // 3. For each static field (excl. auto-accessors), in document order: - // a. Apply each decorator for that field, in reverse order (`C1`, `C2`). - // 4. For each instance field (excl. auto-accessors), in document order: - // a. Apply each decorator for that field, in reverse order (`D1`, `D2`). - // 5. Apply each decorator for the class, in reverse order (`E1`, `E2`). - // - // As a result, "current" types at each decorator application are as follows: - // - For `A1`, the "current" types of the class and method are their "original" types. - // - For `A2`, the "current type" of the method is the "output type" of `A1`, and the "current type" of the - // class is the type of `SomeClass` where `f` is the "output type" of `A1`. This becomes the "final type" - // of `f`. - // - For `B1`, the "current type" of the method is its "original type", and the "current type" of the class - // is the type of `SomeClass` where `f` now has its "final type". - // - etc. - // - // [1]: https://arai-a.github.io/ecma262-compare/?pr=2417&id=sec-runtime-semantics-classdefinitionevaluation - // - // This seems complicated at first glance, but is not unlike our existing inference for functions: - // - // declare function pipe( - // original: Original, - // a1: (input: Original, context: Context) => A1, - // a2: (input: A1, context: Context) => A2, - // b1: (input: A2, context: Context) => B1, - // b2: (input: B1, context: Context) => B2, - // c1: (input: B2, context: Context) => C1, - // c2: (input: C1, context: Context) => C2, - // d1: (input: C2, context: Context) => D1, - // d2: (input: D1, context: Context) => D2, - // e1: (input: D2, context: Context) => E1, - // e2: (input: E1, context: Context) => E2, - // ): E2; - - // When a decorator is applied, it is passed two arguments: "target", which is a value representing the - // thing being decorated (constructors for classes, functions for methods/accessors, `undefined` for fields, - // and a `{ get, set }` object for auto-accessors), and "context", which is an object that provides - // reflection information about the decorated element, as well as the ability to add additional "extra" - // initializers. In most cases, the "target" argument corresponds to the "input type" in some way, and the - // return value similarly corresponds to the "output type" (though if the "output type" is `void` or - // `undefined` then the "output type" is the "input type"). - node := decorator.Parent - links := c.signatureLinks.Get(node) - if links.decoratorSignature == nil { - links.decoratorSignature = c.anySignature - switch node.Kind { - case ast.KindClassDeclaration, ast.KindClassExpression: - // Class decorators have a `context` of `ClassDecoratorContext`, where the `Class` type - // argument will be the "final type" of the class after all decorators are applied. - targetType := c.getTypeOfSymbol(c.getSymbolOfDeclaration(node)) - contextType := c.newClassDecoratorContextType(targetType) - links.decoratorSignature = c.newESDecoratorCallSignature(targetType, contextType, targetType) - case ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor: - if !ast.IsClassLike(node.Parent) { - break - } - // Method decorators have a `context` of `ClassMethodDecoratorContext`, where the - // `Value` type argument corresponds to the "final type" of the method. - // - // Getter decorators have a `context` of `ClassGetterDecoratorContext`, where the - // `Value` type argument corresponds to the "final type" of the value returned by the getter. - // - // Setter decorators have a `context` of `ClassSetterDecoratorContext`, where the - // `Value` type argument corresponds to the "final type" of the parameter of the setter. - // - // In all three cases, the `This` type argument is the "final type" of either the class or - // instance, depending on whether the member was `static`. - var valueType *Type - if ast.IsMethodDeclaration(node) { - valueType = c.getOrCreateTypeFromSignature(c.getSignatureFromDeclaration(node)) - } else { - valueType = c.getTypeOfNode(node) - } - var thisType *Type - if ast.HasStaticModifier(node) { - thisType = c.getTypeOfSymbol(c.getSymbolOfDeclaration(node.Parent)) - } else { - thisType = c.getDeclaredTypeOfClassOrInterface(c.getSymbolOfDeclaration(node.Parent)) - } - // We wrap the "input type", if necessary, to match the decoration target. For getters this is - // something like `() => inputType`, for setters it's `(value: inputType) => void` and for - // methods it is just the input type. - var targetType *Type - switch { - case ast.IsGetAccessorDeclaration(node): - targetType = c.newGetterFunctionType(valueType) - case ast.IsSetAccessorDeclaration(node): - targetType = c.newSetterFunctionType(valueType) - default: - targetType = valueType - } - contextType := c.newClassMemberDecoratorContextTypeForNode(node, thisType, valueType) - links.decoratorSignature = c.newESDecoratorCallSignature(targetType, contextType, targetType) - case ast.KindPropertyDeclaration: - if !ast.IsClassLike(node.Parent) { - break - } - // Field decorators have a `context` of `ClassFieldDecoratorContext` and - // auto-accessor decorators have a `context` of `ClassAccessorDecoratorContext. In - // both cases, the `This` type argument is the "final type" of either the class or instance, - // depending on whether the member was `static`, and the `Value` type argument corresponds to - // the "final type" of the value stored in the field. - valueType := c.getTypeOfNode(node) - var thisType *Type - if ast.HasStaticModifier(node) { - thisType = c.getTypeOfSymbol(c.getSymbolOfDeclaration(node.Parent)) - } else { - thisType = c.getDeclaredTypeOfClassOrInterface(c.getSymbolOfDeclaration(node.Parent)) - } - // The `target` of an auto-accessor decorator is a `{ get, set }` object, representing the - // runtime-generated getter and setter that are added to the class/prototype. The `target` of a - // regular field decorator is always `undefined` as it isn't installed until it is initialized. - var targetType *Type - if ast.HasAccessorModifier(node) { - targetType = c.newClassAccessorDecoratorTargetType(thisType, valueType) - } else { - targetType = c.undefinedType - } - // We wrap the "output type" depending on the declaration. For auto-accessors, we wrap the - // "output type" in a `ClassAccessorDecoratorResult` type, which allows for - // mutation of the runtime-generated getter and setter, as well as the injection of an - // initializer mutator. For regular fields, we wrap the "output type" in an initializer mutator. - var returnType *Type - if ast.HasAccessorModifier(node) { - returnType = c.newClassAccessorDecoratorResultType(thisType, valueType) - } else { - returnType = c.newClassFieldDecoratorInitializerMutatorType(thisType, valueType) - } - contextType := c.newClassMemberDecoratorContextTypeForNode(node, thisType, valueType) - links.decoratorSignature = c.newESDecoratorCallSignature(targetType, contextType, returnType) - } - } - if links.decoratorSignature == c.anySignature { - return nil - } - return links.decoratorSignature -} - -func (c *Checker) newClassDecoratorContextType(classType *Type) *Type { - return c.tryCreateTypeReference(c.getGlobalClassDecoratorContextType(), []*Type{classType}) -} - -func (c *Checker) newClassMethodDecoratorContextType(classType *Type, valueType *Type) *Type { - return c.tryCreateTypeReference(c.getGlobalClassMethodDecoratorContextType(), []*Type{classType, valueType}) -} - -func (c *Checker) newClassGetterDecoratorContextType(classType *Type, valueType *Type) *Type { - return c.tryCreateTypeReference(c.getGlobalClassGetterDecoratorContextType(), []*Type{classType, valueType}) -} - -func (c *Checker) newClassSetterDecoratorContextType(classType *Type, valueType *Type) *Type { - return c.tryCreateTypeReference(c.getGlobalClassSetterDecoratorContextType(), []*Type{classType, valueType}) -} - -func (c *Checker) newClassAccessorDecoratorContextType(thisType *Type, valueType *Type) *Type { - return c.tryCreateTypeReference(c.getGlobalClassAccessorDecoratorContextType(), []*Type{thisType, valueType}) -} - -func (c *Checker) newClassFieldDecoratorContextType(thisType *Type, valueType *Type) *Type { - return c.tryCreateTypeReference(c.getGlobalClassFieldDecoratorContextType(), []*Type{thisType, valueType}) -} - -// Gets a type like `{ name: "foo", private: false, static: true }` that is used to provided member-specific -// details that will be intersected with a decorator context type. -func (c *Checker) getClassMemberDecoratorContextOverrideType(nameType *Type, isPrivate bool, isStatic bool) *Type { - kind := core.IfElse(isPrivate, - core.IfElse(isStatic, CachedTypeKindDecoratorContextPrivateStatic, CachedTypeKindDecoratorContextPrivate), - core.IfElse(isStatic, CachedTypeKindDecoratorContextStatic, CachedTypeKindDecoratorContext), - ) - key := CachedTypeKey{kind: kind, typeId: nameType.id} - if overrideType := c.cachedTypes[key]; overrideType != nil { - return overrideType - } - members := make(ast.SymbolTable) - members["name"] = c.newProperty("name", nameType) - members["private"] = c.newProperty("private", core.IfElse(isPrivate, c.trueType, c.falseType)) - members["static"] = c.newProperty("static", core.IfElse(isStatic, c.trueType, c.falseType)) - overrideType := c.newAnonymousType(nil, members, nil, nil, nil) - c.cachedTypes[key] = overrideType - return overrideType -} - -func (c *Checker) newClassMemberDecoratorContextTypeForNode(node *ast.Node, thisType *Type, valueType *Type) *Type { - isStatic := ast.HasStaticModifier(node) - isPrivate := ast.IsPrivateIdentifier(node.Name()) - var nameType *Type - if isPrivate { - nameType = c.getStringLiteralType(node.Name().Text()) - } else { - nameType = c.getLiteralTypeFromPropertyName(node.Name()) - } - var contextType *Type - switch { - case ast.IsMethodDeclaration(node): - contextType = c.newClassMethodDecoratorContextType(thisType, valueType) - case ast.IsGetAccessorDeclaration(node): - contextType = c.newClassGetterDecoratorContextType(thisType, valueType) - case ast.IsSetAccessorDeclaration(node): - contextType = c.newClassSetterDecoratorContextType(thisType, valueType) - case ast.IsAutoAccessorPropertyDeclaration(node): - contextType = c.newClassAccessorDecoratorContextType(thisType, valueType) - case ast.IsPropertyDeclaration(node): - contextType = c.newClassFieldDecoratorContextType(thisType, valueType) - default: - panic("Unhandled case in createClassMemberDecoratorContextTypeForNode") - } - overrideType := c.getClassMemberDecoratorContextOverrideType(nameType, isPrivate, isStatic) - return c.getIntersectionType([]*Type{contextType, overrideType}) -} - -func (c *Checker) newClassAccessorDecoratorTargetType(thisType *Type, valueType *Type) *Type { - return c.tryCreateTypeReference(c.getGlobalClassAccessorDecoratorTargetType(), []*Type{thisType, valueType}) -} - -func (c *Checker) newClassAccessorDecoratorResultType(thisType *Type, valueType *Type) *Type { - return c.tryCreateTypeReference(c.getGlobalClassAccessorDecoratorResultType(), []*Type{thisType, valueType}) -} - -func (c *Checker) newClassFieldDecoratorInitializerMutatorType(thisType *Type, valueType *Type) *Type { - thisParam := c.newParameter("this", thisType) - valueParam := c.newParameter("value", valueType) - return c.newFunctionType(nil, thisParam, []*ast.Symbol{valueParam}, valueType) -} - -// Creates a call signature for an ES Decorator. This method is used by the semantics of -// `getESDecoratorCallSignature`, which you should probably be using instead. -func (c *Checker) newESDecoratorCallSignature(targetType *Type, contextType *Type, nonOptionalReturnType *Type) *Signature { - targetParam := c.newParameter("target", targetType) - contextParam := c.newParameter("context", contextType) - returnType := c.getUnionType([]*Type{nonOptionalReturnType, c.voidType}) - return c.newCallSignature(nil, nil /*thisParameter*/, []*ast.Symbol{targetParam, contextParam}, returnType) -} - -// Creates a synthetic `FunctionType` -func (c *Checker) newFunctionType(typeParameters []*Type, thisParameter *ast.Symbol, parameters []*ast.Symbol, returnType *Type) *Type { - signature := c.newCallSignature(typeParameters, thisParameter, parameters, returnType) - return c.getOrCreateTypeFromSignature(signature) -} - -func (c *Checker) newGetterFunctionType(t *Type) *Type { - return c.newFunctionType(nil, nil /*thisParameter*/, nil, t) -} - -func (c *Checker) newSetterFunctionType(t *Type) *Type { - valueParam := c.newParameter("value", t) - return c.newFunctionType(nil, nil /*thisParameter*/, []*ast.Symbol{valueParam}, c.voidType) -} - -// Creates a synthetic `Signature` corresponding to a call signature. -func (c *Checker) newCallSignature(typeParameters []*Type, thisParameter *ast.Symbol, parameters []*ast.Symbol, returnType *Type) *Signature { - decl := c.factory.NewFunctionTypeNode(nil, nil, c.factory.NewKeywordTypeNode(ast.KindAnyKeyword)) - return c.newSignature(SignatureFlagsNone, decl, typeParameters, thisParameter, parameters, returnType, nil, len(parameters)) -} - -func (c *Checker) newTypedPropertyDescriptorType(propertyType *Type) *Type { - return c.createTypeFromGenericGlobalType(c.getGlobalTypedPropertyDescriptorType(), []*Type{propertyType}) -} - -func (c *Checker) getParentTypeOfClassElement(node *ast.Node) *Type { - classSymbol := c.getSymbolOfNode(node.Parent) - if ast.IsStatic(node) { - return c.getTypeOfSymbol(classSymbol) - } - return c.getDeclaredTypeOfSymbol(classSymbol) -} - -func (c *Checker) getClassElementPropertyKeyType(element *ast.Node) *Type { - name := element.Name() - switch name.Kind { - case ast.KindIdentifier, ast.KindNumericLiteral, ast.KindStringLiteral: - return c.getStringLiteralType(name.Text()) - case ast.KindComputedPropertyName: - nameType := c.checkComputedPropertyName(name) - if c.isTypeAssignableToKind(nameType, TypeFlagsESSymbolLike) { - return nameType - } - return c.stringType - } - panic("Unhandled case in getClassElementPropertyKeyType") -} - -func (c *Checker) getTypeOfPropertyOfContextualType(t *Type, name string) *Type { - return c.getTypeOfPropertyOfContextualTypeEx(t, name, nil) -} - -func (c *Checker) getTypeOfPropertyOfContextualTypeEx(t *Type, name string, nameType *Type) *Type { - return c.mapTypeEx(t, func(t *Type) *Type { - if t.flags&TypeFlagsIntersection != 0 { - var types []*Type - var indexInfoCandidates []*Type - ignoreIndexInfos := false - for _, constituentType := range t.Types() { - if constituentType.flags&TypeFlagsObject == 0 { - continue - } - if c.isGenericMappedType(constituentType) && c.getMappedTypeNameTypeKind(constituentType) != MappedTypeNameTypeKindRemapping { - substitutedType := c.getIndexedMappedTypeSubstitutedTypeOfContextualType(constituentType, name, nameType) - types = c.appendContextualPropertyTypeConstituent(types, substitutedType) - continue - } - propertyType := c.getTypeOfConcretePropertyOfContextualType(constituentType, name) - if propertyType == nil { - if !ignoreIndexInfos { - indexInfoCandidates = append(indexInfoCandidates, constituentType) - } - continue - } - ignoreIndexInfos = true - indexInfoCandidates = nil - types = c.appendContextualPropertyTypeConstituent(types, propertyType) - } - for _, candidate := range indexInfoCandidates { - indexInfoType := c.getTypeFromIndexInfosOfContextualType(candidate, name, nameType) - types = c.appendContextualPropertyTypeConstituent(types, indexInfoType) - } - if len(types) == 0 { - return nil - } - if len(types) == 1 { - return types[0] - } - return c.getIntersectionType(types) - } - if t.flags&TypeFlagsObject == 0 { - return nil - } - if c.isGenericMappedType(t) && c.getMappedTypeNameTypeKind(t) != MappedTypeNameTypeKindRemapping { - return c.getIndexedMappedTypeSubstitutedTypeOfContextualType(t, name, nameType) - } - result := c.getTypeOfConcretePropertyOfContextualType(t, name) - if result != nil { - return result - } - return c.getTypeFromIndexInfosOfContextualType(t, name, nameType) - }, true /*noReductions*/) -} - -func (c *Checker) getIndexedMappedTypeSubstitutedTypeOfContextualType(t *Type, name string, nameType *Type) *Type { - propertyNameType := nameType - if propertyNameType == nil { - propertyNameType = c.getStringLiteralType(name) - } - constraint := c.getConstraintTypeFromMappedType(t) - // special case for conditional types pretending to be negated types - if t.AsMappedType().nameType != nil && c.isExcludedMappedPropertyName(t.AsMappedType().nameType, propertyNameType) || c.isExcludedMappedPropertyName(constraint, propertyNameType) { - return nil - } - constraintOfConstraint := c.getBaseConstraintOrType(constraint) - if !c.isTypeAssignableTo(propertyNameType, constraintOfConstraint) { - return nil - } - return c.substituteIndexedMappedType(t, propertyNameType) -} - -func (c *Checker) isExcludedMappedPropertyName(t *Type, propertyNameType *Type) bool { - if t.flags&TypeFlagsConditional != 0 { - return c.getReducedType(c.getTrueTypeFromConditionalType(t)).flags&TypeFlagsNever != 0 && - c.getActualTypeVariable(c.getFalseTypeFromConditionalType(t)) == c.getActualTypeVariable(t.AsConditionalType().checkType) && - c.isTypeAssignableTo(propertyNameType, t.AsConditionalType().extendsType) - } - if t.flags&TypeFlagsIntersection != 0 { - return core.Some(t.Types(), func(t *Type) bool { - return c.isExcludedMappedPropertyName(t, propertyNameType) - }) - } - return false -} - -func (c *Checker) getTypeOfConcretePropertyOfContextualType(t *Type, name string) *Type { - prop := c.getPropertyOfType(t, name) - if prop == nil || c.isCircularMappedProperty(prop) { - return nil - } - return c.removeMissingType(c.getTypeOfSymbol(prop), prop.Flags&ast.SymbolFlagsOptional != 0) -} - -func (c *Checker) getTypeFromIndexInfosOfContextualType(t *Type, name string, nameType *Type) *Type { - if isTupleType(t) && isNumericLiteralName(name) && jsnum.FromString(name) >= 0 { - restType := c.getElementTypeOfSliceOfTupleType(t, t.TargetTupleType().fixedLength, 0 /*endSkipCount*/, false /*writing*/, true /*noReductions*/) - if restType != nil { - return restType - } - } - if nameType == nil { - nameType = c.getStringLiteralType(name) - } - indexInfo := c.findApplicableIndexInfo(c.getIndexInfosOfStructuredType(t), nameType) - if indexInfo == nil { - return nil - } - return indexInfo.valueType -} - -func (c *Checker) isCircularMappedProperty(symbol *ast.Symbol) bool { - if symbol.CheckFlags&ast.CheckFlagsMapped != 0 { - links := c.valueSymbolLinks.Get(symbol) - return links.resolvedType == nil && c.findResolutionCycleStartIndex(symbol, TypeSystemPropertyNameType) >= 0 - } - return false -} - -func (c *Checker) appendContextualPropertyTypeConstituent(types []*Type, t *Type) []*Type { - // any doesn't provide any contextual information but could spoil the overall result by nullifying contextual information - // provided by other intersection constituents so it gets replaced with `unknown` as `T & unknown` is just `T` and all - // types computed based on the contextual information provided by other constituens are still assignable to any - if t == nil { - return types - } - if t.flags&TypeFlagsAny != 0 { - return append(types, c.unknownType) - } - return append(types, t) -} - -// Return the contextual type for a given expression node. During overload resolution, a contextual type may temporarily -// be "pushed" onto a node using the contextualType property. -func (c *Checker) getApparentTypeOfContextualType(node *ast.Node, contextFlags ContextFlags) *Type { - var contextualType *Type - if ast.IsObjectLiteralMethod(node) { - contextualType = c.getContextualTypeForObjectLiteralMethod(node, contextFlags) - } else { - contextualType = c.getContextualType(node, contextFlags) - } - instantiatedType := c.instantiateContextualType(contextualType, node, contextFlags) - if instantiatedType != nil && !(contextFlags&ContextFlagsNoConstraints != 0 && instantiatedType.flags&TypeFlagsTypeVariable != 0) { - apparentType := c.mapTypeEx(instantiatedType, func(t *Type) *Type { - if t.objectFlags&ObjectFlagsMapped != 0 { - return t - } - return c.getApparentType(t) - }, true) - switch { - case apparentType.flags&TypeFlagsUnion != 0 && ast.IsObjectLiteralExpression(node): - return c.discriminateContextualTypeByObjectMembers(node, apparentType) - case apparentType.flags&TypeFlagsUnion != 0 && ast.IsJsxAttributes(node): - return c.discriminateContextualTypeByJSXAttributes(node, apparentType) - default: - return apparentType - } - } - return nil -} - -type ObjectLiteralDiscriminator struct { - c *Checker - props []*ast.Node - members []*ast.Symbol -} - -func (d *ObjectLiteralDiscriminator) len() int { - return len(d.props) + len(d.members) -} - -func (d *ObjectLiteralDiscriminator) name(index int) string { - if index < len(d.props) { - return d.props[index].Symbol().Name - } - return d.members[index-len(d.props)].Name -} - -func (d *ObjectLiteralDiscriminator) matches(index int, t *Type) bool { - var propType *Type - if index < len(d.props) { - prop := d.props[index] - if ast.IsPropertyAssignment(prop) || ast.IsJsxAttribute(prop) { - initializer := prop.Initializer() - if initializer != nil { - propType = d.c.getContextFreeTypeOfExpression(prop.Initializer()) - } else { - propType = d.c.trueType // JsxAttribute without initializer is always true - } - } else { - propType = d.c.getContextFreeTypeOfExpression(prop.Name()) - } - } else { - propType = d.c.undefinedType - } - for _, s := range propType.Distributed() { - if d.c.isTypeAssignableTo(s, t) { - return true - } - } - return false -} - -func (c *Checker) discriminateContextualTypeByObjectMembers(node *ast.Node, contextualType *Type) *Type { - key := DiscriminatedContextualTypeKey{nodeId: ast.GetNodeId(node), typeId: contextualType.id} - if discriminated := c.discriminatedContextualTypes[key]; discriminated != nil { - return discriminated - } - discriminated := c.getMatchingUnionConstituentForObjectLiteral(contextualType, node) - if discriminated == nil { - discriminantProperties := core.Filter(node.AsObjectLiteralExpression().Properties.Nodes, func(p *ast.Node) bool { - symbol := p.Symbol() - if symbol == nil { - return false - } - if ast.IsPropertyAssignment(p) { - return c.isPossiblyDiscriminantValue(p.Initializer()) && c.isDiscriminantProperty(contextualType, symbol.Name) - } - if ast.IsShorthandPropertyAssignment(p) { - return c.isDiscriminantProperty(contextualType, symbol.Name) - } - return false - }) - discriminantMembers := core.Filter(c.getPropertiesOfType(contextualType), func(s *ast.Symbol) bool { - return s.Flags&ast.SymbolFlagsOptional != 0 && node.Symbol().Members[s.Name] == nil && c.isDiscriminantProperty(contextualType, s.Name) - }) - discriminator := &ObjectLiteralDiscriminator{c: c, props: discriminantProperties, members: discriminantMembers} - discriminated = c.discriminateTypeByDiscriminableItems(contextualType, discriminator) - } - c.discriminatedContextualTypes[key] = discriminated - return discriminated -} - -func (c *Checker) getMatchingUnionConstituentForObjectLiteral(unionType *Type, node *ast.Node) *Type { - keyPropertyName := c.getKeyPropertyName(unionType) - if keyPropertyName != "" { - propNode := core.Find(node.AsObjectLiteralExpression().Properties.Nodes, func(p *ast.Node) bool { - return p.Symbol() != nil && ast.IsPropertyAssignment(p) && p.Symbol().Name == keyPropertyName && c.isPossiblyDiscriminantValue(p.Initializer()) - }) - if propNode != nil { - propType := c.getContextFreeTypeOfExpression(propNode.Initializer()) - return c.getConstituentTypeForKeyType(unionType, propType) - } - } - return nil -} - -// Return true if the given expression is possibly a discriminant value. We limit the kinds of -// expressions we check to those that don't depend on their contextual type in order not to cause -// recursive (and possibly infinite) invocations of getContextualType. -func (c *Checker) isPossiblyDiscriminantValue(node *ast.Node) bool { - switch node.Kind { - case ast.KindStringLiteral, ast.KindNumericLiteral, ast.KindBigIntLiteral, ast.KindNoSubstitutionTemplateLiteral, ast.KindTemplateExpression, - ast.KindTrueKeyword, ast.KindFalseKeyword, ast.KindNullKeyword, ast.KindIdentifier, ast.KindUndefinedKeyword: - return true - case ast.KindPropertyAccessExpression, ast.KindParenthesizedExpression: - return c.isPossiblyDiscriminantValue(node.Expression()) - case ast.KindJsxExpression: - return node.AsJsxExpression().Expression == nil || c.isPossiblyDiscriminantValue(node.AsJsxExpression().Expression) - } - return false -} - -// If the given contextual type contains instantiable types and if a mapper representing -// return type inferences is available, instantiate those types using that mapper. -func (c *Checker) instantiateContextualType(contextualType *Type, node *ast.Node, contextFlags ContextFlags) *Type { - if contextualType != nil && c.maybeTypeOfKind(contextualType, TypeFlagsInstantiable) { - inferenceContext := c.getInferenceContext(node) - // If no inferences have been made, and none of the type parameters for which we are inferring - // specify default types, nothing is gained from instantiating as type parameters would just be - // replaced with their constraints similar to the apparent type. - if inferenceContext != nil { - if contextFlags&ContextFlagsSignature != 0 && core.Some(inferenceContext.inferences, hasInferenceCandidatesOrDefault) { - // For contextual signatures we incorporate all inferences made so far, e.g. from return - // types as well as arguments to the left in a function call. - return c.instantiateInstantiableTypes(contextualType, inferenceContext.nonFixingMapper) - } - if inferenceContext.returnMapper != nil { - // For other purposes (e.g. determining whether to produce literal types) we only - // incorporate inferences made from the return type in a function call. We remove - // the 'boolean' type from the contextual type such that contextually typed boolean - // literals actually end up widening to 'boolean' (see #48363). - t := c.instantiateInstantiableTypes(contextualType, inferenceContext.returnMapper) - if t.flags&TypeFlagsUnion != 0 && containsType(t.Types(), c.regularFalseType) && containsType(t.Types(), c.regularTrueType) { - return c.filterType(t, func(t *Type) bool { - return t != c.regularFalseType && t != c.regularTrueType - }) - } - return t - } - } - } - return contextualType -} - -// This function is similar to instantiateType, except that (a) it only instantiates types that -// are classified as instantiable (i.e. it doesn't instantiate object types), and (b) it performs -// no reductions on instantiated union types. -func (c *Checker) instantiateInstantiableTypes(t *Type, mapper *TypeMapper) *Type { - if t.flags&TypeFlagsInstantiable != 0 { - return c.instantiateType(t, mapper) - } - if t.flags&TypeFlagsUnion != 0 { - return c.getUnionTypeEx(core.Map(t.Types(), func(t *Type) *Type { - return c.instantiateInstantiableTypes(t, mapper) - }), UnionReductionNone, nil, nil) - } - if t.flags&TypeFlagsIntersection != 0 { - return c.getIntersectionType(core.Map(t.Types(), func(t *Type) *Type { - return c.instantiateInstantiableTypes(t, mapper) - })) - } - return t -} - -func (c *Checker) pushCachedContextualType(node *ast.Node) { - c.pushContextualType(node, c.getContextualType(node, ContextFlagsNone), true /*isCache*/) -} - -func (c *Checker) pushContextualType(node *ast.Node, t *Type, isCache bool) { - c.contextualInfos = append(c.contextualInfos, ContextualInfo{node, t, isCache}) -} - -func (c *Checker) popContextualType() { - lastIndex := len(c.contextualInfos) - 1 - c.contextualInfos[lastIndex] = ContextualInfo{} - c.contextualInfos = c.contextualInfos[:lastIndex] -} - -func (c *Checker) findContextualNode(node *ast.Node, includeCaches bool) int { - for i, info := range c.contextualInfos { - if node == info.node && (includeCaches || !info.isCache) { - return i - } - } - return -1 -} - -// Returns true if the given expression contains (at any level of nesting) a function or arrow expression -// that is subject to contextual typing. -func (c *Checker) isContextSensitive(node *ast.Node) bool { - switch node.Kind { - case ast.KindFunctionExpression, ast.KindArrowFunction, ast.KindMethodDeclaration, ast.KindFunctionDeclaration: - return c.isContextSensitiveFunctionLikeDeclaration(node) - case ast.KindObjectLiteralExpression: - return core.Some(node.AsObjectLiteralExpression().Properties.Nodes, c.isContextSensitive) - case ast.KindArrayLiteralExpression: - return core.Some(node.AsArrayLiteralExpression().Elements.Nodes, c.isContextSensitive) - case ast.KindConditionalExpression: - return c.isContextSensitive(node.AsConditionalExpression().WhenTrue) || c.isContextSensitive(node.AsConditionalExpression().WhenFalse) - case ast.KindBinaryExpression: - binary := node.AsBinaryExpression() - return ast.NodeKindIs(binary.OperatorToken, ast.KindBarBarToken, ast.KindQuestionQuestionToken) && (c.isContextSensitive(binary.Left) || c.isContextSensitive(binary.Right)) - case ast.KindPropertyAssignment: - return c.isContextSensitive(node.Initializer()) - case ast.KindParenthesizedExpression: - return c.isContextSensitive(node.Expression()) - case ast.KindJsxAttributes: - return core.Some(node.AsJsxAttributes().Properties.Nodes, c.isContextSensitive) || ast.IsJsxOpeningElement(node.Parent) && core.Some(node.Parent.Parent.Children().Nodes, c.isContextSensitive) - case ast.KindJsxAttribute: - // If there is no initializer, JSX attribute has a boolean value of true which is not context sensitive. - initializer := node.Initializer() - return initializer != nil && c.isContextSensitive(initializer) - case ast.KindJsxExpression: - // It is possible to that node.expression is undefined (e.g

) - expression := node.Expression() - return expression != nil && c.isContextSensitive(expression) - } - return false -} - -func (c *Checker) isContextSensitiveFunctionLikeDeclaration(node *ast.Node) bool { - return hasContextSensitiveParameters(node) || c.hasContextSensitiveReturnExpression(node) -} - -func (c *Checker) hasContextSensitiveReturnExpression(node *ast.Node) bool { - if node.TypeParameters() != nil || node.Type() != nil { - return false - } - body := node.Body() - if body == nil { - return false - } - if !ast.IsBlock(body) { - return c.isContextSensitive(body) - } - return ast.ForEachReturnStatement(body, func(statement *ast.Node) bool { - return statement.Expression() != nil && c.isContextSensitive(statement.Expression()) - }) -} - -func (c *Checker) pushInferenceContext(node *ast.Node, context *InferenceContext) { - c.inferenceContextInfos = append(c.inferenceContextInfos, InferenceContextInfo{node, context}) -} - -func (c *Checker) popInferenceContext() { - lastIndex := len(c.inferenceContextInfos) - 1 - c.inferenceContextInfos[lastIndex] = InferenceContextInfo{} - c.inferenceContextInfos = c.inferenceContextInfos[:lastIndex] -} - -func (c *Checker) getInferenceContext(node *ast.Node) *InferenceContext { - for i := len(c.inferenceContextInfos) - 1; i >= 0; i-- { - if isNodeDescendantOf(node, c.inferenceContextInfos[i].node) { - return c.inferenceContextInfos[i].context - } - } - return nil -} - -func (c *Checker) getTypeFacts(t *Type, mask TypeFacts) TypeFacts { - return c.getTypeFactsWorker(t, mask) & mask -} - -func (c *Checker) hasTypeFacts(t *Type, mask TypeFacts) bool { - return c.getTypeFacts(t, mask) != 0 -} - -func (c *Checker) getTypeFactsWorker(t *Type, callerOnlyNeeds TypeFacts) TypeFacts { - if t.flags&(TypeFlagsIntersection|TypeFlagsInstantiable) != 0 { - t = c.getBaseConstraintOfType(t) - if t == nil { - t = c.unknownType - } - } - flags := t.flags - switch { - case flags&(TypeFlagsString|TypeFlagsStringMapping) != 0: - if c.strictNullChecks { - return TypeFactsStringStrictFacts - } - return TypeFactsStringFacts - case flags&(TypeFlagsStringLiteral|TypeFlagsTemplateLiteral) != 0: - isEmpty := flags&TypeFlagsStringLiteral != 0 && getStringLiteralValue(t) == "" - if c.strictNullChecks { - if isEmpty { - return TypeFactsEmptyStringStrictFacts - } - return TypeFactsNonEmptyStringStrictFacts - } - if isEmpty { - return TypeFactsEmptyStringFacts - } - return TypeFactsNonEmptyStringFacts - case flags&(TypeFlagsNumber|TypeFlagsEnum) != 0: - if c.strictNullChecks { - return TypeFactsNumberStrictFacts - } - return TypeFactsNumberFacts - case flags&TypeFlagsNumberLiteral != 0: - isZero := getNumberLiteralValue(t) == 0 - if c.strictNullChecks { - if isZero { - return TypeFactsZeroNumberStrictFacts - } - return TypeFactsNonZeroNumberStrictFacts - } - if isZero { - return TypeFactsZeroNumberFacts - } - return TypeFactsNonZeroNumberFacts - case flags&TypeFlagsBigInt != 0: - if c.strictNullChecks { - return TypeFactsBigIntStrictFacts - } - return TypeFactsBigIntFacts - case flags&TypeFlagsBigIntLiteral != 0: - isZero := isZeroBigInt(t) - if c.strictNullChecks { - if isZero { - return TypeFactsZeroBigIntStrictFacts - } - return TypeFactsNonZeroBigIntStrictFacts - } - if isZero { - return TypeFactsZeroBigIntFacts - } - return TypeFactsNonZeroBigIntFacts - case flags&TypeFlagsBoolean != 0: - if c.strictNullChecks { - return TypeFactsBooleanStrictFacts - } - return TypeFactsBooleanFacts - case flags&TypeFlagsBooleanLike != 0: - isFalse := t == c.falseType || t == c.regularFalseType - if c.strictNullChecks { - if isFalse { - return TypeFactsFalseStrictFacts - } - return TypeFactsTrueStrictFacts - } - if isFalse { - return TypeFactsFalseFacts - } - return TypeFactsTrueFacts - case flags&TypeFlagsObject != 0: - var possibleFacts TypeFacts - if c.strictNullChecks { - possibleFacts = TypeFactsEmptyObjectStrictFacts | TypeFactsFunctionStrictFacts | TypeFactsObjectStrictFacts - } else { - possibleFacts = TypeFactsEmptyObjectFacts | TypeFactsFunctionFacts | TypeFactsObjectFacts - } - if (callerOnlyNeeds & possibleFacts) == 0 { - // If the caller doesn't care about any of the facts that we could possibly produce, - // return zero so we can skip resolving members. - return TypeFactsNone - } - switch { - case t.objectFlags&ObjectFlagsAnonymous != 0 && c.isEmptyObjectType(t): - if c.strictNullChecks { - return TypeFactsEmptyObjectStrictFacts - } - return TypeFactsEmptyObjectFacts - case c.isFunctionObjectType(t): - if c.strictNullChecks { - return TypeFactsFunctionStrictFacts - } - return TypeFactsFunctionFacts - case c.strictNullChecks: - return TypeFactsObjectStrictFacts - } - return TypeFactsObjectFacts - case flags&TypeFlagsVoid != 0: - return TypeFactsVoidFacts - case flags&TypeFlagsUndefined != 0: - return TypeFactsUndefinedFacts - case flags&TypeFlagsNull != 0: - return TypeFactsNullFacts - case flags&TypeFlagsESSymbolLike != 0: - if c.strictNullChecks { - return TypeFactsSymbolStrictFacts - } else { - return TypeFactsSymbolFacts - } - case flags&TypeFlagsNonPrimitive != 0: - if c.strictNullChecks { - return TypeFactsObjectStrictFacts - } else { - return TypeFactsObjectFacts - } - case flags&TypeFlagsNever != 0: - return TypeFactsNone - case flags&TypeFlagsUnion != 0: - var facts TypeFacts - for _, t := range t.Types() { - facts |= c.getTypeFactsWorker(t, callerOnlyNeeds) - } - return facts - case flags&TypeFlagsIntersection != 0: - return c.getIntersectionTypeFacts(t, callerOnlyNeeds) - } - return TypeFactsUnknownFacts -} - -func (c *Checker) getIntersectionTypeFacts(t *Type, callerOnlyNeeds TypeFacts) TypeFacts { - // When an intersection contains a primitive type we ignore object type constituents as they are - // presumably type tags. For example, in string & { __kind__: "name" } we ignore the object type. - ignoreObjects := c.maybeTypeOfKind(t, TypeFlagsPrimitive) - // When computing the type facts of an intersection type, certain type facts are computed as `and` - // and others are computed as `or`. - oredFacts := TypeFactsNone - andedFacts := TypeFactsAll - for _, t := range t.Types() { - if !(ignoreObjects && t.flags&TypeFlagsObject != 0) { - f := c.getTypeFactsWorker(t, callerOnlyNeeds) - oredFacts |= f - andedFacts &= f - } - } - return oredFacts&TypeFactsOrFactsMask | andedFacts&TypeFactsAndFactsMask -} - -func isZeroBigInt(t *Type) bool { - return getBigIntLiteralValue(t) == jsnum.PseudoBigInt{} -} - -func (c *Checker) isFunctionObjectType(t *Type) bool { - if t.objectFlags&ObjectFlagsEvolvingArray != 0 { - return false - } - // We do a quick check for a "bind" property before performing the more expensive subtype - // check. This gives us a quicker out in the common case where an object type is not a function. - resolved := c.resolveStructuredTypeMembers(t) - return len(resolved.signatures) != 0 || resolved.members["bind"] != nil && c.isTypeSubtypeOf(t, c.globalFunctionType) -} - -func (c *Checker) getTypeWithFacts(t *Type, include TypeFacts) *Type { - return c.filterType(t, func(t *Type) bool { - return c.hasTypeFacts(t, include) - }) -} - -// This function is similar to getTypeWithFacts, except that in strictNullChecks mode it replaces type -// unknown with the union {} | null | undefined (and reduces that accordingly), and it intersects remaining -// instantiable types with {}, {} | null, or {} | undefined in order to remove null and/or undefined. -func (c *Checker) getAdjustedTypeWithFacts(t *Type, facts TypeFacts) *Type { - reduced := c.recombineUnknownType(c.getTypeWithFacts(core.IfElse(c.strictNullChecks && t.flags&TypeFlagsUnknown != 0, c.unknownUnionType, t), facts)) - if c.strictNullChecks { - switch facts { - case TypeFactsNEUndefined: - return c.removeNullableByIntersection(reduced, TypeFactsEQUndefined, TypeFactsEQNull, TypeFactsIsNull, c.nullType) - case TypeFactsNENull: - return c.removeNullableByIntersection(reduced, TypeFactsEQNull, TypeFactsEQUndefined, TypeFactsIsUndefined, c.undefinedType) - case TypeFactsNEUndefinedOrNull, TypeFactsTruthy: - return c.mapType(reduced, func(t *Type) *Type { - if c.hasTypeFacts(t, TypeFactsEQUndefinedOrNull) { - return c.getGlobalNonNullableTypeInstantiation(t) - } - return t - }) - } - } - return reduced -} - -func (c *Checker) removeNullableByIntersection(t *Type, targetFacts TypeFacts, otherFacts TypeFacts, otherIncludesFacts TypeFacts, otherType *Type) *Type { - facts := c.getTypeFacts(t, TypeFactsEQUndefined|TypeFactsEQNull|TypeFactsIsUndefined|TypeFactsIsNull) - // Simply return the type if it never compares equal to the target nullable. - if facts&targetFacts == 0 { - return t - } - // By default we intersect with a union of {} and the opposite nullable. - emptyAndOtherUnion := c.getUnionType([]*Type{c.emptyObjectType, otherType}) - // For each constituent type that can compare equal to the target nullable, intersect with the above union - // if the type doesn't already include the opposite nullable and the constituent can compare equal to the - // opposite nullable; otherwise, just intersect with {}. - return c.mapType(t, func(t *Type) *Type { - if c.hasTypeFacts(t, targetFacts) { - if facts&otherIncludesFacts == 0 && c.hasTypeFacts(t, otherFacts) { - return c.getIntersectionType([]*Type{t, emptyAndOtherUnion}) - } - return c.getIntersectionType([]*Type{t, c.emptyObjectType}) - } - return t - }) -} - -func (c *Checker) recombineUnknownType(t *Type) *Type { - if t == c.unknownUnionType { - return c.unknownType - } - return t -} - -func (c *Checker) getGlobalNonNullableTypeInstantiation(t *Type) *Type { - alias := c.getGlobalNonNullableTypeAliasOrNil() - if alias != nil { - return c.getTypeAliasInstantiation(alias, []*Type{t}, nil) - } - return c.getIntersectionType([]*Type{t, c.emptyObjectType}) -} - -func (c *Checker) convertAutoToAny(t *Type) *Type { - switch { - case t == c.autoType: - return c.anyType - case t == c.autoArrayType: - return c.anyArrayType - } - return t -} - -// Gets the "awaited type" of a type. -// @param type The type to await. -// @param withAlias When `true`, wraps the "awaited type" in `Awaited` if needed. -// @remarks The "awaited type" of an expression is its "promised type" if the expression is a -// Promise-like type; otherwise, it is the type of the expression. This is used to reflect -// The runtime behavior of the `await` keyword. -func (c *Checker) checkAwaitedType(t *Type, withAlias bool, errorNode *ast.Node, diagnosticMessage *diagnostics.Message) *Type { - var awaitedType *Type - if withAlias { - awaitedType = c.getAwaitedTypeEx(t, errorNode, diagnosticMessage) - } else { - awaitedType = c.getAwaitedTypeNoAliasEx(t, errorNode, diagnosticMessage) - } - if awaitedType != nil { - return awaitedType - } - return c.errorType -} - -// Gets the "awaited type" of a type. -// -// The "awaited type" of an expression is its "promised type" if the expression is a -// Promise-like type; otherwise, it is the type of the expression. If the "promised -// type" is itself a Promise-like, the "promised type" is recursively unwrapped until a -// non-promise type is found. -// -// This is used to reflect the runtime behavior of the `await` keyword. -func (c *Checker) getAwaitedType(t *Type) *Type { - return c.getAwaitedTypeEx(t, nil, nil) -} - -func (c *Checker) getAwaitedTypeEx(t *Type, errorNode *ast.Node, diagnosticMessage *diagnostics.Message, args ...any) *Type { - awaitedType := c.getAwaitedTypeNoAliasEx(t, errorNode, diagnosticMessage, args...) - if awaitedType != nil { - return c.createAwaitedTypeIfNeeded(awaitedType) - } - return nil -} - -// Gets the "awaited type" of a type without introducing an `Awaited` wrapper. -func (c *Checker) getAwaitedTypeNoAlias(t *Type) *Type { - return c.getAwaitedTypeNoAliasEx(t, nil, nil) -} - -func (c *Checker) getAwaitedTypeNoAliasEx(t *Type, errorNode *ast.Node, diagnosticMessage *diagnostics.Message, args ...any) *Type { - if IsTypeAny(t) { - return t - } - // If this is already an `Awaited`, just return it. This avoids `Awaited>` in higher-order - if c.isAwaitedTypeInstantiation(t) { - return t - } - // If we've already cached an awaited type, return a possible `Awaited` for it. - key := CachedTypeKey{kind: CachedTypeKindAwaitedType, typeId: t.id} - if awaitedType := c.cachedTypes[key]; awaitedType != nil { - return awaitedType - } - // For a union, get a union of the awaited types of each constituent. - if t.flags&TypeFlagsUnion != 0 { - if slices.Contains(c.awaitedTypeStack, t) { - if errorNode != nil { - c.error(errorNode, diagnostics.Type_is_referenced_directly_or_indirectly_in_the_fulfillment_callback_of_its_own_then_method) - } - return nil - } - c.awaitedTypeStack = append(c.awaitedTypeStack, t) - mapped := c.mapType(t, func(t *Type) *Type { return c.getAwaitedTypeNoAliasEx(t, errorNode, diagnosticMessage, args...) }) - c.awaitedTypeStack = c.awaitedTypeStack[:len(c.awaitedTypeStack)-1] - c.cachedTypes[key] = mapped - return mapped - } - // If `type` is generic and should be wrapped in `Awaited`, return it. - if c.isAwaitedTypeNeeded(t) { - c.cachedTypes[key] = t - return t - } - var thisTypeForError *Type - promisedType := c.getPromisedTypeOfPromiseEx(t, nil /*errorNode*/, &thisTypeForError) - if promisedType != nil { - if t == promisedType || slices.Contains(c.awaitedTypeStack, promisedType) { - // Verify that we don't have a bad actor in the form of a promise whose - // promised type is the same as the promise type, or a mutually recursive - // promise. If so, we return undefined as we cannot guess the shape. If this - // were the actual case in the JavaScript, this Promise would never resolve. - // - // An example of a bad actor with a singly-recursive promise type might - // be: - // - // interface BadPromise { - // then( - // onfulfilled: (value: BadPromise) => any, - // onrejected: (error: any) => any): BadPromise; - // } - // - // The above interface will pass the PromiseLike check, and return a - // promised type of `BadPromise`. Since this is a self reference, we - // don't want to keep recursing ad infinitum. - // - // An example of a bad actor in the form of a mutually-recursive - // promise type might be: - // - // interface BadPromiseA { - // then( - // onfulfilled: (value: BadPromiseB) => any, - // onrejected: (error: any) => any): BadPromiseB; - // } - // - // interface BadPromiseB { - // then( - // onfulfilled: (value: BadPromiseA) => any, - // onrejected: (error: any) => any): BadPromiseA; - // } - // - if errorNode != nil { - c.error(errorNode, diagnostics.Type_is_referenced_directly_or_indirectly_in_the_fulfillment_callback_of_its_own_then_method) - } - return nil - } - // Keep track of the type we're about to unwrap to avoid bad recursive promise types. - // See the comments above for more information. - c.awaitedTypeStack = append(c.awaitedTypeStack, t) - awaitedType := c.getAwaitedTypeNoAliasEx(promisedType, errorNode, diagnosticMessage, args...) - c.awaitedTypeStack = c.awaitedTypeStack[:len(c.awaitedTypeStack)-1] - if awaitedType == nil { - return nil - } - c.cachedTypes[key] = awaitedType - return awaitedType - } - // The type was not a promise, so it could not be unwrapped any further. - // As long as the type does not have a callable "then" property, it is - // safe to return the type; otherwise, an error is reported and we return - // undefined. - // - // An example of a non-promise "thenable" might be: - // - // await { then(): void {} } - // - // The "thenable" does not match the minimal definition for a promise. When - // a Promise/A+-compatible or ES6 promise tries to adopt this value, the promise - // will never settle. We treat this as an error to help flag an early indicator - // of a runtime problem. If the user wants to return this value from an async - // function, they would need to wrap it in some other value. If they want it to - // be treated as a promise, they can cast to . - if c.isThenableType(t) { - if errorNode != nil { - var diagnostic *ast.Diagnostic - if thisTypeForError != nil { - diagnostic = NewDiagnosticForNode(errorNode, diagnostics.The_this_context_of_type_0_is_not_assignable_to_method_s_this_of_type_1, c.TypeToString(t), c.TypeToString(thisTypeForError)) - } - c.diagnostics.Add(NewDiagnosticChainForNode(diagnostic, errorNode, diagnosticMessage, args...)) - } - return nil - } - c.cachedTypes[key] = t - return t -} - -func (c *Checker) isAwaitedTypeInstantiation(t *Type) bool { - if t.flags&TypeFlagsConditional != 0 { - awaitedSymbol := c.getGlobalAwaitedSymbolOrNil() - return awaitedSymbol != nil && t.alias != nil && t.alias.symbol == awaitedSymbol && len(t.alias.typeArguments) == 1 - } - return false -} - -func (c *Checker) isAwaitedTypeNeeded(t *Type) bool { - // If this is already an `Awaited`, we shouldn't wrap it. This helps to avoid `Awaited>` in higher-order. - if IsTypeAny(t) || c.isAwaitedTypeInstantiation(t) { - return false - } - // We only need `Awaited` if `T` contains possibly non-primitive types. - if c.isGenericObjectType(t) { - baseConstraint := c.getBaseConstraintOfType(t) - // We only need `Awaited` if `T` is a type variable that has no base constraint, or the base constraint of `T` is `any`, `unknown`, `{}`, `object`, - // or is promise-like. - if baseConstraint != nil { - return baseConstraint.flags&TypeFlagsAnyOrUnknown != 0 || c.isEmptyObjectType(baseConstraint) || someType(baseConstraint, c.isThenableType) - } - return c.maybeTypeOfKind(t, TypeFlagsTypeVariable) - } - return false -} - -func (c *Checker) createAwaitedTypeIfNeeded(t *Type) *Type { - // We wrap type `T` in `Awaited` based on the following conditions: - // - `T` is not already an `Awaited`, and - // - `T` is generic, and - // - One of the following applies: - // - `T` has no base constraint, or - // - The base constraint of `T` is `any`, `unknown`, `object`, or `{}`, or - // - The base constraint of `T` is an object type with a callable `then` method. - if c.isAwaitedTypeNeeded(t) { - awaitedType := c.tryCreateAwaitedType(t) - if awaitedType != nil { - return awaitedType - } - } - return t -} - -func (c *Checker) tryCreateAwaitedType(t *Type) *Type { - // Nothing to do if `Awaited` doesn't exist - awaitedSymbol := c.getGlobalAwaitedSymbol() - if awaitedSymbol != nil { - // Unwrap unions that may contain `Awaited`, otherwise its possible to manufacture an `Awaited | U>` where - // an `Awaited` would suffice. - return c.getTypeAliasInstantiation(awaitedSymbol, []*Type{c.unwrapAwaitedType(t)}, nil) - } - return nil -} - -// For a generic `Awaited`, gets `T`. -func (c *Checker) unwrapAwaitedType(t *Type) *Type { - switch { - case t.flags&TypeFlagsUnion != 0: - return c.mapType(t, c.unwrapAwaitedType) - case c.isAwaitedTypeInstantiation(t): - return t.alias.typeArguments[0] - } - return t -} - -func (c *Checker) isThenableType(t *Type) bool { - if c.allTypesAssignableToKind(c.getBaseConstraintOrType(t), TypeFlagsPrimitive|TypeFlagsNever) { - // primitive types cannot be considered "thenable" since they are not objects. - return false - } - thenFunction := c.getTypeOfPropertyOfType(t, "then") - return thenFunction != nil && len(c.getSignaturesOfType(c.getTypeWithFacts(thenFunction, TypeFactsNEUndefinedOrNull), SignatureKindCall)) != 0 -} - -func (c *Checker) getAwaitedTypeOfPromise(t *Type) *Type { - return c.getAwaitedTypeOfPromiseEx(t, nil, nil) -} - -func (c *Checker) getAwaitedTypeOfPromiseEx(t *Type, errorNode *ast.Node, diagnosticMessage *diagnostics.Message, args ...any) *Type { - promisedType := c.getPromisedTypeOfPromiseEx(t, errorNode, nil) - if promisedType != nil { - return c.getAwaitedTypeEx(promisedType, errorNode, diagnosticMessage, args...) - } - return nil -} - -// Check if a parameter or catch variable (or their bindings elements) is assigned anywhere -func (c *Checker) isSomeSymbolAssigned(rootDeclaration *ast.Node) bool { - return c.isSomeSymbolAssignedWorker(rootDeclaration.Name()) -} - -func (c *Checker) isSomeSymbolAssignedWorker(node *ast.Node) bool { - if node.Kind == ast.KindIdentifier { - return c.isSymbolAssigned(c.getSymbolOfDeclaration(node.Parent)) - } - return core.Some(node.AsBindingPattern().Elements.Nodes, func(e *ast.Node) bool { - return e.Name() != nil && c.isSomeSymbolAssignedWorker(e.Name()) - }) -} - -func (c *Checker) getTargetType(t *Type) *Type { - if t.objectFlags&ObjectFlagsReference != 0 { - return t.AsTypeReference().target - } - return t -} - -func (c *Checker) getNarrowableTypeForReference(t *Type, reference *ast.Node, checkMode CheckMode) *Type { - if c.isNoInferType(t) { - t = t.AsSubstitutionType().baseType - } - // When the type of a reference is or contains an instantiable type with a union type constraint, and - // when the reference is in a constraint position (where it is known we'll obtain the apparent type) or - // has a contextual type containing no top-level instantiables (meaning constraints will determine - // assignability), we substitute constraints for all instantiables in the type of the reference to give - // control flow analysis an opportunity to narrow it further. For example, for a reference of a type - // parameter type 'T extends string | undefined' with a contextual type 'string', we substitute - // 'string | undefined' to give control flow analysis the opportunity to narrow to type 'string'. - substituteConstraints := checkMode&CheckModeInferential == 0 && someType(t, c.isGenericTypeWithUnionConstraint) && (c.isConstraintPosition(t, reference) || c.hasContextualTypeWithNoGenericTypes(reference, checkMode)) - if substituteConstraints { - return c.mapType(t, c.getBaseConstraintOrType) - } - return t -} - -func (c *Checker) isConstraintPosition(t *Type, node *ast.Node) bool { - parent := node.Parent - // In an element access obj[x], we consider obj to be in a constraint position, except when obj is of - // a generic type without a nullable constraint and x is a generic type. This is because when both obj - // and x are of generic types T and K, we want the resulting type to be T[K]. - return ast.IsPropertyAccessExpression(parent) || ast.IsQualifiedName(parent) || - (ast.IsCallExpression(parent) || ast.IsNewExpression(parent)) && parent.Expression() == node || - ast.IsElementAccessExpression(parent) && parent.Expression() == node && !(someType(t, c.isGenericTypeWithoutNullableConstraint) && c.isGenericIndexType(c.getTypeOfExpression(parent.AsElementAccessExpression().ArgumentExpression))) -} - -func (c *Checker) isGenericTypeWithUnionConstraint(t *Type) bool { - if t.flags&TypeFlagsIntersection != 0 { - return core.Some(t.AsIntersectionType().types, c.isGenericTypeWithUnionConstraint) - } - return t.flags&TypeFlagsInstantiable != 0 && c.getBaseConstraintOrType(t).flags&(TypeFlagsNullable|TypeFlagsUnion) != 0 -} - -func (c *Checker) isGenericTypeWithoutNullableConstraint(t *Type) bool { - if t.flags&TypeFlagsIntersection != 0 { - return core.Some(t.AsIntersectionType().types, c.isGenericTypeWithoutNullableConstraint) - } - return t.flags&TypeFlagsInstantiable != 0 && !c.maybeTypeOfKind(c.getBaseConstraintOrType(t), TypeFlagsNullable) -} - -func (c *Checker) hasContextualTypeWithNoGenericTypes(node *ast.Node, checkMode CheckMode) bool { - // Computing the contextual type for a child of a JSX element involves resolving the type of the - // element's tag name, so we exclude that here to avoid circularities. - // If check mode has `CheckMode.RestBindingElement`, we skip binding pattern contextual types, - // as we want the type of a rest element to be generic when possible. - if (ast.IsIdentifier(node) || ast.IsPropertyAccessExpression(node) || ast.IsElementAccessExpression(node)) && - !((ast.IsJsxOpeningElement(node.Parent) || ast.IsJsxSelfClosingElement(node.Parent)) && getTagNameOfNode(node.Parent) == node) { - contextualType := c.getContextualType(node, core.IfElse(checkMode&CheckModeRestBindingElement != 0, ContextFlagsSkipBindingPatterns, ContextFlagsNone)) - if contextualType != nil { - return !c.isGenericType(contextualType) - } - } - return false -} - -func (c *Checker) getNonUndefinedType(t *Type) *Type { - typeOrConstraint := t - if someType(t, c.isGenericTypeWithUndefinedConstraint) { - typeOrConstraint = c.mapType(t, func(t *Type) *Type { - if t.flags&TypeFlagsInstantiable != 0 { - return c.getBaseConstraintOrType(t) - } - return t - }) - } - return c.getTypeWithFacts(typeOrConstraint, TypeFactsNEUndefined) -} - -func (c *Checker) isGenericTypeWithUndefinedConstraint(t *Type) bool { - if t.flags&TypeFlagsInstantiable != 0 { - constraint := c.getBaseConstraintOfType(t) - if constraint != nil { - return c.maybeTypeOfKind(constraint, TypeFlagsUndefined) - } - } - return false -} - -func (c *Checker) getActualTypeVariable(t *Type) *Type { - if t.flags&TypeFlagsSubstitution != 0 { - return c.getActualTypeVariable(t.AsSubstitutionType().baseType) - } - if t.flags&TypeFlagsIndexedAccess != 0 && (t.AsIndexedAccessType().objectType.flags&TypeFlagsSubstitution != 0 || t.AsIndexedAccessType().indexType.flags&TypeFlagsSubstitution != 0) { - return c.getIndexedAccessType(c.getActualTypeVariable(t.AsIndexedAccessType().objectType), c.getActualTypeVariable(t.AsIndexedAccessType().indexType)) - } - return t -} - -func (c *Checker) GetSymbolAtLocation(node *ast.Node) *ast.Symbol { - // !!! - // const node = getParseTreeNode(nodeIn); - - // set ignoreErrors: true because any lookups invoked by the API shouldn't cause any new errors - if node.Parent == nil || node.Parent.Parent == nil { - return nil - } - return c.getSymbolAtLocation(node, true /*ignoreErrors*/) -} - -// Returns the symbol associated with a given AST node. Do *not* use this function in the checker itself! It should -// be used only by the language service and external tools. The semantics of the function are deliberately "fuzzy" -// and aim to just return *some* symbol for the node. To obtain the symbol associated with a node for type checking -// purposes, use appropriate function for the context, e.g. `getResolvedSymbol` for an expression identifier, -// `getSymbolOfDeclaration` for a declaration, etc. -func (c *Checker) getSymbolAtLocation(node *ast.Node, ignoreErrors bool) *ast.Symbol { - if ast.IsSourceFile(node) { - if ast.IsExternalOrCommonJSModule(node.AsSourceFile()) { - return c.getMergedSymbol(node.Symbol()) - } - return nil - } - parent := node.Parent - grandParent := parent.Parent - - if node.Flags&ast.NodeFlagsInWithStatement != 0 { - // We cannot answer semantic questions within a with block, do not proceed any further - return nil - } - - if ast.IsDeclarationNameOrImportPropertyName(node) { - // This is a declaration, call getSymbolOfNode - parentSymbol := c.getSymbolOfDeclaration(parent) - if ast.IsImportOrExportSpecifier(parent) && parent.PropertyName() == node { - return c.getImmediateAliasedSymbol(parentSymbol) - } - return parentSymbol - } else if ast.IsLiteralComputedPropertyDeclarationName(node) { - return c.getSymbolOfDeclaration(grandParent) - } - - if ast.IsIdentifier(node) { - if isInRightSideOfImportOrExportAssignment(node) { - return c.getSymbolOfNameOrPropertyAccessExpression(node) - } else if ast.IsBindingElement(parent) && ast.IsObjectBindingPattern(grandParent) && node == parent.PropertyName() { - typeOfPattern := c.getTypeOfNode(grandParent) - if propertyDeclaration := c.getPropertyOfType(typeOfPattern, node.Text()); propertyDeclaration != nil { - return propertyDeclaration - } - } else if ast.IsMetaProperty(parent) && parent.Name() == node { - metaProp := parent.AsMetaProperty() - if metaProp.KeywordToken == ast.KindNewKeyword && node.Text() == "target" { - // `target` in `new.target` - return c.checkNewTargetMetaProperty(parent).symbol - } - // The `meta` in `import.meta` could be given `getTypeOfNode(parent).symbol` (the `ImportMeta` interface symbol), but - // we have a fake expression type made for other reasons already, whose transient `meta` - // member should more exactly be the kind of (declarationless) symbol we want. - // (See #44364 and #45031 for relevant implementation PRs) - if metaProp.KeywordToken == ast.KindImportKeyword && node.Text() == "meta" { - return c.getGlobalImportMetaExpressionType().AsObjectType().members["meta"] - } - // no other meta properties are valid syntax, thus no others should have symbols - return nil - } else if ast.IsJSDocParameterTag(parent) && parent.Name() == node { - if fn := ast.GetNodeAtPosition(ast.GetSourceFileOfNode(node), node.Pos(), false); fn != nil && ast.IsFunctionLike(fn) { - for _, param := range fn.Parameters() { - if param.Name().Text() == node.Text() { - return c.getSymbolOfNode(param) - } - } - } - } - } - - switch node.Kind { - case ast.KindIdentifier, ast.KindPrivateIdentifier, ast.KindPropertyAccessExpression, ast.KindQualifiedName: - if !ast.IsThisInTypeQuery(node) { - return c.getSymbolOfNameOrPropertyAccessExpression(node) - } - fallthrough - case ast.KindThisKeyword: - container := c.getThisContainer(node, false /*includeArrowFunctions*/, false /*includeClassComputedPropertyName*/) - if ast.IsFunctionLike(container) { - sig := c.getSignatureFromDeclaration(container) - if sig.thisParameter != nil { - return sig.thisParameter - } - } - if ast.IsInExpressionContext(node) { - return c.checkExpression(node).symbol - } - fallthrough - case ast.KindThisType: - return c.getTypeFromThisTypeNode(node).symbol - case ast.KindSuperKeyword: - return c.checkExpression(node).symbol - case ast.KindConstructorKeyword: - // constructor keyword for an overload, should take us to the definition if it exist - constructorDeclaration := parent - if constructorDeclaration != nil && constructorDeclaration.Kind == ast.KindConstructor { - return constructorDeclaration.Parent.Symbol() - } - return nil - case ast.KindStringLiteral, ast.KindNoSubstitutionTemplateLiteral: - // 1). import x = require("./mo/*gotToDefinitionHere*/d") - // 2). External module name in an import declaration - // 3). Require in Javascript - // 4). type A = import("./f/*gotToDefinitionHere*/oo") - if (ast.IsExternalModuleImportEqualsDeclaration(grandParent) && ast.GetExternalModuleImportEqualsDeclarationExpression(grandParent) == node) || - ((parent.Kind == ast.KindImportDeclaration || parent.Kind == ast.KindJSImportDeclaration || parent.Kind == ast.KindExportDeclaration) && ast.GetExternalModuleName(parent) == node) || - ast.IsVariableDeclarationInitializedToRequire(grandParent) || ast.IsImportCall(parent) || - (ast.IsLiteralTypeNode(parent) && ast.IsLiteralImportTypeNode(grandParent) && grandParent.AsImportTypeNode().Argument == parent) { - return c.resolveExternalModuleName(node, node, ignoreErrors) - } - - fallthrough - case ast.KindNumericLiteral: - // index access - var objectType *Type - if ast.IsElementAccessExpression(parent) { - if parent.AsElementAccessExpression().ArgumentExpression == node { - objectType = c.getTypeOfExpression(parent.Expression()) - } - } else if ast.IsLiteralTypeNode(parent) && ast.IsIndexedAccessTypeNode(grandParent) { - objectType = c.getTypeFromTypeNode(grandParent.AsIndexedAccessTypeNode().ObjectType) - } - - if objectType != nil { - return c.getPropertyOfType(objectType, node.Text()) - } - return nil - case ast.KindDefaultKeyword, ast.KindFunctionKeyword, ast.KindEqualsGreaterThanToken, ast.KindClassKeyword: - return c.getSymbolOfNode(node) - case ast.KindImportType: - if ast.IsLiteralImportTypeNode(node) { - return c.getSymbolAtLocation(node.AsImportTypeNode().Argument.AsLiteralTypeNode().Literal, ignoreErrors) - } - return nil - case ast.KindExportKeyword: - if ast.IsExportAssignment(parent) { - if parent.Symbol() == nil { - panic("Symbol should be defined") - } - return parent.Symbol() - } - return nil - case ast.KindImportKeyword: - if ast.IsMetaProperty(node.Parent) && node.Parent.Text() == "defer" { - return nil - } - fallthrough - case ast.KindNewKeyword: - if ast.IsMetaProperty(parent) { - return c.checkMetaPropertyKeyword(parent).symbol - } - return nil - case ast.KindInstanceOfKeyword: - if ast.IsBinaryExpression(parent) { - t := c.getTypeOfExpression(parent.AsBinaryExpression().Right) - hasInstanceMethodType := c.getSymbolHasInstanceMethodOfObjectType(t) - if hasInstanceMethodType != nil && hasInstanceMethodType.symbol != nil { - return hasInstanceMethodType.symbol - } - return t.symbol - } - return nil - case ast.KindMetaProperty: - return c.checkExpression(node).symbol - case ast.KindJsxNamespacedName: - if ast.IsJsxTagName(node) && isJsxIntrinsicTagName(node) { - symbol := c.getIntrinsicTagSymbol(node.Parent) - if symbol == c.unknownSymbol { - return nil - } - return symbol - } - fallthrough - default: - return nil - } -} - -func (c *Checker) getIndexSignaturesAtLocation(node *ast.Node) []*ast.Node { - var signatures []*ast.Node - if ast.IsIdentifier(node) && ast.IsPropertyAccessExpression(node.Parent) && node.Parent.Name() == node { - keyType := c.getLiteralTypeFromPropertyName(node) - objectType := c.getTypeOfExpression(node.Parent.Expression()) - for _, t := range objectType.Distributed() { - for _, info := range c.getApplicableIndexInfos(t, keyType) { - if info.declaration != nil { - signatures = core.AppendIfUnique(signatures, info.declaration) - } - } - } - } - return signatures -} - -func (c *Checker) getSymbolOfNameOrPropertyAccessExpression(name *ast.Node) *ast.Symbol { - if ast.IsDeclarationName(name) { - return c.getSymbolOfNode(name.Parent) - } - - if (name.Parent.Kind == ast.KindExportAssignment || name.Parent.Kind == ast.KindJSExportAssignment) && ast.IsEntityNameExpression(name) { - // Even an entity name expression that doesn't resolve as an entityname may still typecheck as a property access expression - success := c.resolveEntityName( - name, - /*all meanings*/ ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace|ast.SymbolFlagsAlias, - true /*ignoreErrors*/, false /*dontResolveAlias*/, nil /*location*/) - if success != nil && success != c.unknownSymbol { - return success - } - } else if ast.IsEntityName(name) && isInRightSideOfImportOrExportAssignment(name) { - // Since we already checked for ExportAssignment, this really could only be an Import - importEqualsDeclaration := ast.FindAncestorKind(name, ast.KindImportEqualsDeclaration) - if importEqualsDeclaration == nil { - panic("ImportEqualsDeclaration should be defined") - } - return c.getSymbolOfPartOfRightHandSideOfImportEquals(name, true /*dontResolveAlias*/) - } - - if ast.IsEntityName(name) { - possibleImportNode := isImportTypeQualifierPart(name) - if possibleImportNode != nil { - c.getTypeFromTypeNode(possibleImportNode) - sym := c.getResolvedSymbolOrNil(name) - return core.IfElse(sym == c.unknownSymbol, nil, sym) - } - } - - for ast.IsRightSideOfQualifiedNameOrPropertyAccess(name) { - name = name.Parent - } - - if isInNameOfExpressionWithTypeArguments(name) { - var meaning ast.SymbolFlags - if name.Parent.Kind == ast.KindExpressionWithTypeArguments { - // An 'ExpressionWithTypeArguments' may appear in type space (interface Foo extends Bar), - // value space (return foo), or both(class Foo extends Bar); ensure the meaning matches. - meaning = core.IfElse(ast.IsPartOfTypeNode(name), ast.SymbolFlagsType, ast.SymbolFlagsValue) - - // In a class 'extends' clause we are also looking for a value. - if ast.IsExpressionWithTypeArgumentsInClassExtendsClause(name.Parent) { - meaning = meaning | ast.SymbolFlagsValue - } - } else { - meaning = ast.SymbolFlagsNamespace - } - - meaning = meaning | ast.SymbolFlagsAlias - var entityNameSymbol *ast.Symbol - if ast.IsEntityNameExpression(name) { - entityNameSymbol = c.resolveEntityName(name, meaning, true /*ignoreErrors*/, false /*dontResolveAlias*/, nil /*location*/) - } - if entityNameSymbol != nil { - return entityNameSymbol - } - } - - if ast.IsExpressionNode(name) { - if ast.NodeIsMissing(name) { - // Missing entity name. - return nil - } - isJSDoc := ast.IsJSDocNameReferenceContext(name) - if ast.IsIdentifier(name) { - if ast.IsJsxTagName(name) && isJsxIntrinsicTagName(name) { - symbol := c.getIntrinsicTagSymbol(name.Parent) - return core.IfElse(symbol == c.unknownSymbol, nil, symbol) - } - meaning := core.IfElse(isJSDoc, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace, ast.SymbolFlagsValue) - result := c.resolveEntityName(name, meaning, true /*ignoreErrors*/, true /*dontResolveAlias*/, nil /*location*/) - if result == nil && isJSDoc { - if container := ast.FindAncestor(name, ast.IsClassOrInterfaceLike); container != nil { - symbol := c.getSymbolOfDeclaration(container) - // Handle unqualified references to class static members and class or interface instance members - if result = c.getMergedSymbol(c.getSymbol(c.getExportsOfSymbol(symbol), name.Text(), meaning)); result == nil { - result = c.getPropertyOfType(c.getDeclaredTypeOfSymbol(symbol), name.Text()) - } - } - } - return result - } else if ast.IsPrivateIdentifier(name) { - return c.getSymbolForPrivateIdentifierExpression(name) - } else if ast.IsPropertyAccessExpression(name) || ast.IsQualifiedName(name) { - links := c.symbolNodeLinks.Get(name) - if links.resolvedSymbol != nil { - return links.resolvedSymbol - } - if ast.IsPropertyAccessExpression(name) { - c.checkPropertyAccessExpression(name, CheckModeNormal, false /*writeOnly*/) - if links.resolvedSymbol == nil { - links.resolvedSymbol = c.getApplicableIndexSymbol( - c.checkExpressionCached(name.Expression()), - c.getLiteralTypeFromPropertyName(name.Name()), - ) - } - } else { - c.checkQualifiedName(name, CheckModeNormal) - } - if links.resolvedSymbol == nil && isJSDoc && ast.IsQualifiedName(name) { - return c.resolveJSDocMemberName(name) - } - return links.resolvedSymbol - } - } else if ast.IsEntityName(name) && isTypeReferenceIdentifier(name) { - meaning := core.IfElse(name.Parent.Kind == ast.KindTypeReference, ast.SymbolFlagsType, ast.SymbolFlagsNamespace) - symbol := c.resolveEntityName(name, meaning, true /*ignoreErrors*/, true /*dontResolveAlias*/, nil /*location*/) - if symbol != nil && symbol != c.unknownSymbol { - return symbol - } - return c.getUnresolvedSymbolForEntityName(name) - } - - if name.Parent.Kind == ast.KindTypePredicate { - return c.resolveEntityName( - name, - ast.SymbolFlagsFunctionScopedVariable, /*meaning*/ - true, /*ignoreErrors*/ - false, /*dontResolveAlias*/ - nil, /*location*/ - ) - } - return nil -} - -func (c *Checker) isThisPropertyAndThisTyped(node *ast.Node) bool { - if node.AsPropertyAccessExpression().Expression.Kind == ast.KindThisKeyword { - container := c.getThisContainer(node, false /*includeArrowFunctions*/, false /*includeClassComputedPropertyName*/) - if ast.IsFunctionLike(container) { - containingLiteral := getContainingObjectLiteral(container) - if containingLiteral != nil { - contextualType := c.getApparentTypeOfContextualType(containingLiteral, ContextFlagsNone) - t := c.getThisTypeOfObjectLiteralFromContextualType(containingLiteral, contextualType) - return t != nil && !IsTypeAny(t) - } - } - } - return false -} - -func (c *Checker) getTypeOfNode(node *ast.Node) *Type { - if ast.IsSourceFile(node) && !ast.IsExternalOrCommonJSModule(node.AsSourceFile()) { - return c.errorType - } - - if node.Flags&ast.NodeFlagsInWithStatement != 0 { - // We cannot answer semantic questions within a with block, do not proceed any further - return c.errorType - } - - classDecl, isImplements := ast.TryGetClassImplementingOrExtendingExpressionWithTypeArguments(node) - var classType *Type - if classDecl != nil { - classType = c.getDeclaredTypeOfClassOrInterface(c.getSymbolOfDeclaration(classDecl)) - } - - if ast.IsPartOfTypeNode(node) { - typeFromTypeNode := c.getTypeFromTypeNode(node) - if classType != nil { - return c.getTypeWithThisArgument( - typeFromTypeNode, - classType.AsInterfaceType().thisType, - false /*needApparentType*/) - } - return typeFromTypeNode - } - - if ast.IsExpressionNode(node) { - return c.getRegularTypeOfExpression(node) - } - - if classType != nil && !isImplements { - // A SyntaxKind.ExpressionWithTypeArguments is considered a type node, except when it occurs in the - // extends clause of a class. We handle that case here. - baseType := core.FirstOrNil(c.getBaseTypes(classType)) - if baseType != nil { - return c.getTypeWithThisArgument(baseType, classType.AsInterfaceType().thisType, false /*needApparentType*/) - } - return c.errorType - } - - if ast.IsTypeDeclaration(node) { - // In this case, we call getSymbolOfDeclaration instead of getSymbolAtLocation because it is a declaration - symbol := c.getSymbolOfDeclaration(node) - return c.getDeclaredTypeOfSymbol(symbol) - } - - if ast.IsTypeDeclarationName(node) { - symbol := c.getSymbolAtLocation(node, false /*ignoreErrors*/) - if symbol != nil { - return c.getDeclaredTypeOfSymbol(symbol) - } - return c.errorType - } - - if ast.IsBindingElement(node) { - t := c.getTypeForVariableLikeDeclaration(node, true /*includeOptionality*/, CheckModeNormal) - if t != nil { - return t - } - return c.errorType - } - - if ast.IsDeclaration(node) { - // In this case, we call getSymbolOfDeclaration instead of getSymbolLAtocation because it is a declaration - symbol := c.getSymbolOfDeclaration(node) - if symbol != nil { - return c.getTypeOfSymbol(symbol) - } - return c.errorType - } - - if ast.IsDeclarationNameOrImportPropertyName(node) { - symbol := c.getSymbolAtLocation(node, false /*ignoreErrors*/) - if symbol != nil { - return c.getTypeOfSymbol(symbol) - } - return c.errorType - } - - if ast.IsBindingPattern(node) { - t := c.getTypeForVariableLikeDeclaration(node.Parent, true /*includeOptionality*/, CheckModeNormal) - if t != nil { - return t - } - return c.errorType - } - - if isInRightSideOfImportOrExportAssignment(node) { - symbol := c.getSymbolAtLocation(node, false /*ignoreErrors*/) - if symbol != nil { - declaredType := c.getDeclaredTypeOfSymbol(symbol) - if !c.isErrorType(declaredType) { - return declaredType - } - return c.getTypeOfSymbol(symbol) - } - } - - if ast.IsMetaProperty(node.Parent) && node.Parent.AsMetaProperty().KeywordToken == node.Kind { - return c.checkMetaPropertyKeyword(node.Parent) - } - - if ast.IsImportAttributes(node) { - return c.getGlobalImportAttributesType() - } - - return c.errorType -} - -func (c *Checker) getThisTypeOfObjectLiteralFromContextualType(containingLiteral *ast.Node, contextualType *Type) *Type { - literal := containingLiteral - t := contextualType - for t != nil { - thisType := c.getThisTypeFromContextualType(t) - if thisType != nil { - return thisType - } - if literal.Parent.Kind != ast.KindPropertyAssignment { - break - } - literal = literal.Parent.Parent - t = c.getApparentTypeOfContextualType(literal, ContextFlagsNone) - } - return nil -} - -func (c *Checker) getThisTypeFromContextualType(t *Type) *Type { - return c.mapType(t, func(t *Type) *Type { - if t.flags&TypeFlagsIntersection != 0 { - for _, t := range t.AsIntersectionType().types { - typeArg := c.getThisTypeArgument(t) - if typeArg != nil { - return typeArg - } - } - return nil - } else { - return c.getThisTypeArgument(t) - } - }) -} - -func (c *Checker) getThisTypeArgument(t *Type) *Type { - if t.objectFlags&ObjectFlagsReference != 0 && t.AsTypeReference().target == c.globalThisType { - return c.getTypeArguments(t)[0] - } - return nil -} - -func (c *Checker) getApplicableIndexInfos(t *Type, keyType *Type) []*IndexInfo { - return core.Filter(c.getIndexInfosOfType(t), func(info *IndexInfo) bool { return c.isApplicableIndexType(keyType, info.keyType) }) -} - -func (c *Checker) getApplicableIndexSymbol(t *Type, keyType *Type) *ast.Symbol { - infos := c.getApplicableIndexInfos(t, keyType) - if len(infos) > 0 && t.flags&TypeFlagsObject != 0 && t.AsObjectType().members != nil { - symbol := getIndexSymbolFromSymbolTable(c.resolveStructuredTypeMembers(t).members) - if core.Same(infos, c.getIndexInfosOfType(t)) { - return symbol - } else if symbol != nil { - indexSymbolLinks := c.indexSymbolLinks.Get(symbol) - declarationList := core.MapNonNil(infos, func(info *IndexInfo) *ast.Node { return info.declaration }) - nodeListId := getNodeListKey(declarationList) - if indexSymbolLinks.filteredIndexSymbolCache == nil { - indexSymbolLinks.filteredIndexSymbolCache = make(map[string]*ast.Symbol) - } - if result, ok := indexSymbolLinks.filteredIndexSymbolCache[nodeListId]; ok { - return result - } else { - symbolCopy := c.newSymbol(ast.SymbolFlagsSignature, ast.InternalSymbolNameIndex) - symbolCopy.Declarations = declarationList - if t.alias != nil && t.alias.symbol != nil { - symbolCopy.Parent = t.alias.symbol - } else if t.symbol != nil { - symbolCopy.Parent = t.symbol - } else { - symbolCopy.Parent = c.getSymbolAtLocation(symbolCopy.Declarations[0].Parent, false /*ignoreErrors*/) - } - indexSymbolLinks.filteredIndexSymbolCache[nodeListId] = symbolCopy - return symbolCopy - } - } - } - return nil -} - -func (c *Checker) getRegularTypeOfExpression(expr *ast.Node) *Type { - if ast.IsRightSideOfQualifiedNameOrPropertyAccess(expr) { - expr = expr.Parent - } - return c.getRegularTypeOfLiteralType(c.getTypeOfExpression(expr)) -} - -func (c *Checker) containsArgumentsReference(node *ast.Node) bool { - if node.Body() == nil { - return false - } - - if containsArguments, ok := c.cachedArgumentsReferenced[node]; ok { - return containsArguments - } - - var visit func(node *ast.Node) bool - visit = func(node *ast.Node) bool { - if node == nil { - return false - } - switch node.Kind { - case ast.KindIdentifier: - return node.Text() == c.argumentsSymbol.Name && c.IsArgumentsSymbol(c.getResolvedSymbol(node)) - case ast.KindPropertyDeclaration, ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor: - if ast.IsComputedPropertyName(node.Name()) { - return visit(node.Name()) - } - case ast.KindPropertyAccessExpression, ast.KindElementAccessExpression: - return visit(node.Expression()) - case ast.KindPropertyAssignment: - return visit(node.AsPropertyAssignment().Initializer) - } - if nodeStartsNewLexicalEnvironment(node) || ast.IsPartOfTypeNode(node) { - return false - } - return node.ForEachChild(visit) - } - - containsArguments := visit(node.Body()) - c.cachedArgumentsReferenced[node] = containsArguments - return containsArguments -} - -func (c *Checker) GetTypeAtLocation(node *ast.Node) *Type { - return c.getTypeOfNode(node) -} - -func (c *Checker) GetEmitResolver() *emitResolver { - c.emitResolverOnce.Do(func() { - c.emitResolver = newEmitResolver(c) - }) - - return c.emitResolver -} - -func (c *Checker) GetAliasedSymbol(symbol *ast.Symbol) *ast.Symbol { - return c.resolveAlias(symbol) -} diff --git a/kitcom/internal/tsgo/checker/checker_test.go b/kitcom/internal/tsgo/checker/checker_test.go deleted file mode 100644 index 699c0ad..0000000 --- a/kitcom/internal/tsgo/checker/checker_test.go +++ /dev/null @@ -1,103 +0,0 @@ -package checker_test - -import ( - "testing" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/bundled" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/checker" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/compiler" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/repo" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tsoptions" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs/osvfs" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs/vfstest" - "gotest.tools/v3/assert" -) - -func TestGetSymbolAtLocation(t *testing.T) { - t.Parallel() - - content := `interface Foo { - bar: string; -} -declare const foo: Foo; -foo.bar;` - fs := vfstest.FromMap(map[string]string{ - "/foo.ts": content, - "/tsconfig.json": ` - { - "compilerOptions": {}, - "files": ["foo.ts"] - } - `, - }, false /*useCaseSensitiveFileNames*/) - fs = bundled.WrapFS(fs) - - cd := "/" - host := compiler.NewCompilerHost(cd, fs, bundled.LibPath(), nil, nil) - - parsed, errors := tsoptions.GetParsedCommandLineOfConfigFile("/tsconfig.json", &core.CompilerOptions{}, host, nil) - assert.Equal(t, len(errors), 0, "Expected no errors in parsed command line") - - p := compiler.NewProgram(compiler.ProgramOptions{ - Config: parsed, - Host: host, - }) - p.BindSourceFiles() - c, done := p.GetTypeChecker(t.Context()) - defer done() - file := p.GetSourceFile("/foo.ts") - interfaceId := file.Statements.Nodes[0].Name() - varId := file.Statements.Nodes[1].AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes[0].Name() - propAccess := file.Statements.Nodes[2].AsExpressionStatement().Expression - nodes := []*ast.Node{interfaceId, varId, propAccess} - for _, node := range nodes { - symbol := c.GetSymbolAtLocation(node) - if symbol == nil { - t.Fatalf("Expected symbol to be non-nil") - } - } -} - -func TestCheckSrcCompiler(t *testing.T) { - t.Parallel() - - repo.SkipIfNoTypeScriptSubmodule(t) - fs := osvfs.FS() - fs = bundled.WrapFS(fs) - - rootPath := tspath.CombinePaths(tspath.NormalizeSlashes(repo.TypeScriptSubmodulePath), "src", "compiler") - - host := compiler.NewCompilerHost(rootPath, fs, bundled.LibPath(), nil, nil) - parsed, errors := tsoptions.GetParsedCommandLineOfConfigFile(tspath.CombinePaths(rootPath, "tsconfig.json"), &core.CompilerOptions{}, host, nil) - assert.Equal(t, len(errors), 0, "Expected no errors in parsed command line") - p := compiler.NewProgram(compiler.ProgramOptions{ - Config: parsed, - Host: host, - }) - p.CheckSourceFiles(t.Context(), nil) -} - -func BenchmarkNewChecker(b *testing.B) { - repo.SkipIfNoTypeScriptSubmodule(b) - fs := osvfs.FS() - fs = bundled.WrapFS(fs) - - rootPath := tspath.CombinePaths(tspath.NormalizeSlashes(repo.TypeScriptSubmodulePath), "src", "compiler") - - host := compiler.NewCompilerHost(rootPath, fs, bundled.LibPath(), nil, nil) - parsed, errors := tsoptions.GetParsedCommandLineOfConfigFile(tspath.CombinePaths(rootPath, "tsconfig.json"), &core.CompilerOptions{}, host, nil) - assert.Equal(b, len(errors), 0, "Expected no errors in parsed command line") - p := compiler.NewProgram(compiler.ProgramOptions{ - Config: parsed, - Host: host, - }) - - b.ReportAllocs() - - for b.Loop() { - checker.NewChecker(p) - } -} diff --git a/kitcom/internal/tsgo/checker/emitresolver.go b/kitcom/internal/tsgo/checker/emitresolver.go deleted file mode 100644 index a094d39..0000000 --- a/kitcom/internal/tsgo/checker/emitresolver.go +++ /dev/null @@ -1,1112 +0,0 @@ -package checker - -import ( - "maps" - "slices" - "sync" - - "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/debug" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/evaluator" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/jsnum" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/nodebuilder" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" -) - -var _ printer.EmitResolver = (*emitResolver)(nil) - -// Links for jsx -type JSXLinks struct { - importRef *ast.Node -} - -// Links for declarations - -type DeclarationLinks struct { - isVisible core.Tristate // if declaration is depended upon by exported declarations -} - -type DeclarationFileLinks struct { - aliasesMarked bool // if file has had alias visibility marked -} - -type emitResolver struct { - checker *Checker - checkerMu sync.Mutex - isValueAliasDeclaration func(node *ast.Node) bool - aliasMarkingVisitor func(node *ast.Node) bool - referenceResolver binder.ReferenceResolver - jsxLinks core.LinkStore[*ast.Node, JSXLinks] - declarationLinks core.LinkStore[*ast.Node, DeclarationLinks] - declarationFileLinks core.LinkStore[*ast.Node, DeclarationFileLinks] -} - -func newEmitResolver(checker *Checker) *emitResolver { - e := &emitResolver{checker: checker} - e.isValueAliasDeclaration = e.isValueAliasDeclarationWorker - e.aliasMarkingVisitor = e.aliasMarkingVisitorWorker - return e -} - -func (r *emitResolver) GetJsxFactoryEntity(location *ast.Node) *ast.Node { - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - return r.checker.getJsxFactoryEntity(location) -} - -func (r *emitResolver) GetJsxFragmentFactoryEntity(location *ast.Node) *ast.Node { - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - return r.checker.getJsxFragmentFactoryEntity(location) -} - -func (r *emitResolver) IsOptionalParameter(node *ast.Node) bool { - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - return r.isOptionalParameter(node) -} - -func (r *emitResolver) IsLateBound(node *ast.Node) bool { - // TODO: Require an emitContext to construct an EmitResolver, remove all emitContext arguments - // node = r.emitContext.ParseNode(node) - if node == nil { - return false - } - if !ast.IsParseTreeNode(node) { - return false - } - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - symbol := r.checker.getSymbolOfDeclaration(node) - if symbol == nil { - return false - } - return symbol.CheckFlags&ast.CheckFlagsLate != 0 -} - -func (r *emitResolver) GetEnumMemberValue(node *ast.Node) evaluator.Result { - // node = r.emitContext.ParseNode(node) - if !ast.IsParseTreeNode(node) { - return evaluator.NewResult(nil, false, false, false) - } - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - - r.checker.computeEnumMemberValues(node.Parent) - if !r.checker.enumMemberLinks.Has(node) { - return evaluator.NewResult(nil, false, false, false) - } - return r.checker.enumMemberLinks.Get(node).value -} - -func (r *emitResolver) IsDeclarationVisible(node *ast.Node) bool { - // Only lock on external API func to prevent deadlocks - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - return r.isDeclarationVisible(node) -} - -func (r *emitResolver) isDeclarationVisible(node *ast.Node) bool { - // node = r.emitContext.ParseNode(node) - if !ast.IsParseTreeNode(node) { - return false - } - if node == nil { - return false - } - - links := r.declarationLinks.Get(node) - if links.isVisible == core.TSUnknown { - if r.determineIfDeclarationIsVisible(node) { - links.isVisible = core.TSTrue - } else { - links.isVisible = core.TSFalse - } - } - return links.isVisible == core.TSTrue -} - -func (r *emitResolver) determineIfDeclarationIsVisible(node *ast.Node) bool { - switch node.Kind { - case ast.KindJSDocCallbackTag, - // ast.KindJSDocEnumTag, // !!! TODO: JSDoc @enum support? - ast.KindJSDocTypedefTag: - // Top-level jsdoc type aliases are considered exported - // First parent is comment node, second is hosting declaration or token; we only care about those tokens or declarations whose parent is a source file - return node.Parent != nil && node.Parent.Parent != nil && node.Parent.Parent.Parent != nil && ast.IsSourceFile(node.Parent.Parent.Parent) - case ast.KindBindingElement: - return r.isDeclarationVisible(node.Parent.Parent) - case ast.KindVariableDeclaration, - ast.KindModuleDeclaration, - ast.KindClassDeclaration, - ast.KindInterfaceDeclaration, - ast.KindTypeAliasDeclaration, - ast.KindJSTypeAliasDeclaration, - ast.KindFunctionDeclaration, - ast.KindEnumDeclaration, - ast.KindImportEqualsDeclaration: - if ast.IsVariableDeclaration(node) { - if ast.IsBindingPattern(node.Name()) && - len(node.Name().AsBindingPattern().Elements.Nodes) == 0 { - // If the binding pattern is empty, this variable declaration is not visible - return false - } - // falls through - } - // external module augmentation is always visible - if ast.IsExternalModuleAugmentation(node) { - return true - } - parent := ast.GetDeclarationContainer(node) - // If the node is not exported or it is not ambient module element (except import declaration) - if r.checker.getCombinedModifierFlagsCached(node)&ast.ModifierFlagsExport == 0 && - !(node.Kind != ast.KindImportEqualsDeclaration && parent.Kind != ast.KindSourceFile && parent.Flags&ast.NodeFlagsAmbient != 0) { - return ast.IsGlobalSourceFile(parent) - } - // Exported members/ambient module elements (exception import declaration) are visible if parent is visible - return r.isDeclarationVisible(parent) - - case ast.KindPropertyDeclaration, - ast.KindPropertySignature, - ast.KindGetAccessor, - ast.KindSetAccessor, - ast.KindMethodDeclaration, - ast.KindMethodSignature: - if r.checker.GetEffectiveDeclarationFlags(node, ast.ModifierFlagsPrivate|ast.ModifierFlagsProtected) != 0 { - // Private/protected properties/methods are not visible - return false - } - // Public properties/methods are visible if its parents are visible, so: - return r.isDeclarationVisible(node.Parent) - - case ast.KindConstructor, - ast.KindConstructSignature, - ast.KindCallSignature, - ast.KindIndexSignature, - ast.KindParameter, - ast.KindModuleBlock, - ast.KindFunctionType, - ast.KindConstructorType, - ast.KindTypeLiteral, - ast.KindTypeReference, - ast.KindArrayType, - ast.KindTupleType, - ast.KindUnionType, - ast.KindIntersectionType, - ast.KindParenthesizedType, - ast.KindNamedTupleMember: - return r.isDeclarationVisible(node.Parent) - - // Default binding, import specifier and namespace import is visible - // only on demand so by default it is not visible - case ast.KindImportClause, - ast.KindNamespaceImport, - ast.KindImportSpecifier: - return false - - // Type parameters are always visible - case ast.KindTypeParameter: - return true - // Source file and namespace export are always visible - case ast.KindSourceFile, - ast.KindNamespaceExportDeclaration: - return true - - // Export assignments do not create name bindings outside the module - case ast.KindExportAssignment, ast.KindJSExportAssignment: - return false - - default: - return false - } -} - -func (r *emitResolver) PrecalculateDeclarationEmitVisibility(file *ast.SourceFile) { - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - if r.declarationFileLinks.Get(file.AsNode()).aliasesMarked { - return - } - r.declarationFileLinks.Get(file.AsNode()).aliasesMarked = true - // TODO: Does this even *have* to be an upfront walk? If it's not possible for a - // import a = a.b.c statement to chain into exposing a statement in a sibling scope, - // it could at least be pushed into scope entry - then it wouldn't need to be recursive. - file.AsNode().ForEachChild(r.aliasMarkingVisitor) -} - -func (r *emitResolver) aliasMarkingVisitorWorker(node *ast.Node) bool { - switch node.Kind { - case ast.KindExportAssignment, ast.KindJSExportAssignment: - if node.AsExportAssignment().Expression.Kind == ast.KindIdentifier { - r.markLinkedAliases(node.Expression()) - } - case ast.KindExportSpecifier: - r.markLinkedAliases(node.PropertyNameOrName()) - } - return node.ForEachChild(r.aliasMarkingVisitor) -} - -// Sets the isVisible link on statements the Identifier or ExportName node points at -// Follows chains of import d = a.b.c -func (r *emitResolver) markLinkedAliases(node *ast.Node) { - var exportSymbol *ast.Symbol - if node.Kind != ast.KindStringLiteral && node.Parent != nil && node.Parent.Kind == ast.KindExportAssignment { - exportSymbol = r.checker.resolveName(node, node.AsIdentifier().Text, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace|ast.SymbolFlagsAlias /*nameNotFoundMessage*/, nil /*isUse*/, false, false) - } else if node.Parent.Kind == ast.KindExportSpecifier { - exportSymbol = r.checker.getTargetOfExportSpecifier(node.Parent, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace|ast.SymbolFlagsAlias, false) - } - - visited := make(map[ast.SymbolId]struct{}, 2) // guard against circular imports - for exportSymbol != nil { - _, seen := visited[ast.GetSymbolId(exportSymbol)] - if seen { - break - } - visited[ast.GetSymbolId(exportSymbol)] = struct{}{} - - var nextSymbol *ast.Symbol - for _, declaration := range exportSymbol.Declarations { - r.declarationLinks.Get(declaration).isVisible = core.TSTrue - - if ast.IsInternalModuleImportEqualsDeclaration(declaration) { - // Add the referenced top container visible - internalModuleReference := declaration.AsImportEqualsDeclaration().ModuleReference - firstIdentifier := ast.GetFirstIdentifier(internalModuleReference) - importSymbol := r.checker.resolveName(declaration, firstIdentifier.AsIdentifier().Text, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace|ast.SymbolFlagsAlias /*nameNotFoundMessage*/, nil /*isUse*/, false, false) - nextSymbol = importSymbol - } - } - - exportSymbol = nextSymbol - } -} - -func getMeaningOfEntityNameReference(entityName *ast.Node) ast.SymbolFlags { - // get symbol of the first identifier of the entityName - if entityName.Parent.Kind == ast.KindTypeQuery || - entityName.Parent.Kind == ast.KindExpressionWithTypeArguments && !ast.IsPartOfTypeNode(entityName.Parent) || - entityName.Parent.Kind == ast.KindComputedPropertyName || - entityName.Parent.Kind == ast.KindTypePredicate && entityName.Parent.AsTypePredicateNode().ParameterName == entityName { - // Typeof value - return ast.SymbolFlagsValue | ast.SymbolFlagsExportValue - } - if entityName.Kind == ast.KindQualifiedName || entityName.Kind == ast.KindPropertyAccessExpression || - entityName.Parent.Kind == ast.KindImportEqualsDeclaration || - (entityName.Parent.Kind == ast.KindQualifiedName && entityName.Parent.AsQualifiedName().Left == entityName) || - (entityName.Parent.Kind == ast.KindPropertyAccessExpression && (entityName.Parent.AsPropertyAccessExpression()).Expression == entityName) || - (entityName.Parent.Kind == ast.KindElementAccessExpression && (entityName.Parent.AsElementAccessExpression()).Expression == entityName) { - // Left identifier from type reference or TypeAlias - // Entity name of the import declaration - return ast.SymbolFlagsNamespace - } - // Type Reference or TypeAlias entity = Identifier - return ast.SymbolFlagsType -} - -func (r *emitResolver) IsEntityNameVisible(entityName *ast.Node, enclosingDeclaration *ast.Node) printer.SymbolAccessibilityResult { - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - return r.isEntityNameVisible(entityName, enclosingDeclaration, true) -} - -func (r *emitResolver) isEntityNameVisible(entityName *ast.Node, enclosingDeclaration *ast.Node, shouldComputeAliasToMakeVisible bool) printer.SymbolAccessibilityResult { - // node = r.emitContext.ParseNode(entityName) - if !ast.IsParseTreeNode(entityName) { - return printer.SymbolAccessibilityResult{Accessibility: printer.SymbolAccessibilityNotAccessible} - } - - meaning := getMeaningOfEntityNameReference(entityName) - firstIdentifier := ast.GetFirstIdentifier(entityName) - - symbol := r.checker.resolveName(enclosingDeclaration, firstIdentifier.Text(), meaning, nil, false, false) - - if symbol != nil && symbol.Flags&ast.SymbolFlagsTypeParameter != 0 && meaning&ast.SymbolFlagsType != 0 { - return printer.SymbolAccessibilityResult{Accessibility: printer.SymbolAccessibilityAccessible} - } - - if symbol == nil && ast.IsThisIdentifier(firstIdentifier) { - sym := r.checker.getSymbolOfDeclaration(r.checker.getThisContainer(firstIdentifier, false, false)) - if r.isSymbolAccessible(sym, enclosingDeclaration, meaning, false).Accessibility == printer.SymbolAccessibilityAccessible { - return printer.SymbolAccessibilityResult{Accessibility: printer.SymbolAccessibilityAccessible} - } - } - - if symbol == nil { - return printer.SymbolAccessibilityResult{ - Accessibility: printer.SymbolAccessibilityNotResolved, - ErrorSymbolName: firstIdentifier.Text(), - ErrorNode: firstIdentifier, - } - } - - visible := r.hasVisibleDeclarations(symbol, shouldComputeAliasToMakeVisible) - if visible != nil { - return *visible - } - - return printer.SymbolAccessibilityResult{ - Accessibility: printer.SymbolAccessibilityNotAccessible, - ErrorSymbolName: firstIdentifier.Text(), - ErrorNode: firstIdentifier, - } -} - -func noopAddVisibleAlias(declaration *ast.Node, aliasingStatement *ast.Node) {} - -func (r *emitResolver) hasVisibleDeclarations(symbol *ast.Symbol, shouldComputeAliasToMakeVisible bool) *printer.SymbolAccessibilityResult { - var aliasesToMakeVisibleSet map[ast.NodeId]*ast.Node - - var addVisibleAlias func(declaration *ast.Node, aliasingStatement *ast.Node) - if shouldComputeAliasToMakeVisible { - addVisibleAlias = func(declaration *ast.Node, aliasingStatement *ast.Node) { - r.declarationLinks.Get(declaration).isVisible = core.TSTrue - if aliasesToMakeVisibleSet == nil { - aliasesToMakeVisibleSet = make(map[ast.NodeId]*ast.Node) - } - aliasesToMakeVisibleSet[ast.GetNodeId(declaration)] = aliasingStatement - } - } else { - addVisibleAlias = noopAddVisibleAlias - } - - for _, declaration := range symbol.Declarations { - if ast.IsIdentifier(declaration) { - continue - } - - if !r.isDeclarationVisible(declaration) { - // Mark the unexported alias as visible if its parent is visible - // because these kind of aliases can be used to name types in declaration file - anyImportSyntax := getAnyImportSyntax(declaration) - if anyImportSyntax != nil && - !ast.HasSyntacticModifier(anyImportSyntax, ast.ModifierFlagsExport) && // import clause without export - r.isDeclarationVisible(anyImportSyntax.Parent) { - addVisibleAlias(declaration, anyImportSyntax) - continue - } - if ast.IsVariableDeclaration(declaration) && ast.IsVariableStatement(declaration.Parent.Parent) && - !ast.HasSyntacticModifier(declaration.Parent.Parent, ast.ModifierFlagsExport) && // unexported variable statement - r.isDeclarationVisible(declaration.Parent.Parent.Parent) { - addVisibleAlias(declaration, declaration.Parent.Parent) - continue - } - if ast.IsLateVisibilityPaintedStatement(declaration) && // unexported top-level statement - !ast.HasSyntacticModifier(declaration, ast.ModifierFlagsExport) && - r.isDeclarationVisible(declaration.Parent) { - addVisibleAlias(declaration, declaration) - continue - } - if ast.IsBindingElement(declaration) { - if symbol.Flags&ast.SymbolFlagsAlias != 0 && ast.IsInJSFile(declaration) && declaration.Parent != nil && declaration.Parent.Parent != nil && // exported import-like top-level JS require statement - ast.IsVariableDeclaration(declaration.Parent.Parent) && - declaration.Parent.Parent.Parent.Parent != nil && ast.IsVariableStatement(declaration.Parent.Parent.Parent.Parent) && - !ast.HasSyntacticModifier(declaration.Parent.Parent.Parent.Parent, ast.ModifierFlagsExport) && - declaration.Parent.Parent.Parent.Parent.Parent != nil && // check if the thing containing the variable statement is visible (ie, the file) - r.isDeclarationVisible(declaration.Parent.Parent.Parent.Parent.Parent) { - addVisibleAlias(declaration, declaration.Parent.Parent.Parent.Parent) - continue - } - if symbol.Flags&ast.SymbolFlagsBlockScopedVariable != 0 { - rootDeclaration := ast.WalkUpBindingElementsAndPatterns(declaration) - if ast.IsParameter(rootDeclaration) { - return nil - } - variableStatement := rootDeclaration.Parent.Parent - if !ast.IsVariableStatement(variableStatement) { - return nil - } - if ast.HasSyntacticModifier(variableStatement, ast.ModifierFlagsExport) { - continue // no alias to add, already exported - } - if !r.isDeclarationVisible(variableStatement.Parent) { - return nil // not visible - } - addVisibleAlias(declaration, variableStatement) - continue - } - } - - // Declaration is not visible - return nil - } - } - - return &printer.SymbolAccessibilityResult{ - Accessibility: printer.SymbolAccessibilityAccessible, - AliasesToMakeVisible: slices.Collect(maps.Values(aliasesToMakeVisibleSet)), - } -} - -func (r *emitResolver) IsImplementationOfOverload(node *ast.SignatureDeclaration) bool { - // node = r.emitContext.ParseNode(node) - if !ast.IsParseTreeNode(node) { - return false - } - if ast.NodeIsPresent(node.Body()) { - if ast.IsGetAccessorDeclaration(node) || ast.IsSetAccessorDeclaration(node) { - return false // Get or set accessors can never be overload implementations, but can have up to 2 signatures - } - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - symbol := r.checker.getSymbolOfDeclaration(node) - signaturesOfSymbol := r.checker.getSignaturesOfSymbol(symbol) - // If this function body corresponds to function with multiple signature, it is implementation of overload - // e.g.: function foo(a: string): string; - // function foo(a: number): number; - // function foo(a: any) { // This is implementation of the overloads - // return a; - // } - return len(signaturesOfSymbol) > 1 || - // If there is single signature for the symbol, it is overload if that signature isn't coming from the node - // e.g.: function foo(a: string): string; - // function foo(a: any) { // This is implementation of the overloads - // return a; - // } - (len(signaturesOfSymbol) == 1 && signaturesOfSymbol[0].declaration != node) - } - return false -} - -func (r *emitResolver) IsImportRequiredByAugmentation(decl *ast.ImportDeclaration) bool { - // node = r.emitContext.ParseNode(node) - if !ast.IsParseTreeNode(decl.AsNode()) { - return false - } - file := ast.GetSourceFileOfNode(decl.AsNode()) - if file.Symbol == nil { - // script file - return false - } - importTarget := r.GetExternalModuleFileFromDeclaration(decl.AsNode()) - if importTarget == nil { - return false - } - if importTarget == file { - return false - } - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - exports := r.checker.getExportsOfModule(file.Symbol) - for s := range maps.Values(exports) { - merged := r.checker.getMergedSymbol(s) - if merged != s { - if len(merged.Declarations) > 0 { - for _, d := range merged.Declarations { - declFile := ast.GetSourceFileOfNode(d) - if declFile == importTarget { - return true - } - } - } - } - } - return false -} - -func (r *emitResolver) IsDefinitelyReferenceToGlobalSymbolObject(node *ast.Node) bool { - if !ast.IsPropertyAccessExpression(node) || - !ast.IsIdentifier(node.Name()) || - !ast.IsPropertyAccessExpression(node.Expression()) && !ast.IsIdentifier(node.Expression()) { - return false - } - if node.Expression().Kind == ast.KindIdentifier { - if node.Expression().AsIdentifier().Text != "Symbol" { - return false - } - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - // Exactly `Symbol.something` and `Symbol` either does not resolve or definitely resolves to the global Symbol - return r.checker.getResolvedSymbol(node.Expression()) == r.checker.getGlobalSymbol("Symbol", ast.SymbolFlagsValue|ast.SymbolFlagsExportValue, nil /*diagnostic*/) - } - if node.Expression().Expression().Kind != ast.KindIdentifier || node.Expression().Expression().AsIdentifier().Text != "globalThis" || node.Expression().Name().Text() != "Symbol" { - return false - } - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - // Exactly `globalThis.Symbol.something` and `globalThis` resolves to the global `globalThis` - return r.checker.getResolvedSymbol(node.Expression().Expression()) == r.checker.globalThisSymbol -} - -func (r *emitResolver) RequiresAddingImplicitUndefined(declaration *ast.Node, symbol *ast.Symbol, enclosingDeclaration *ast.Node) bool { - if !ast.IsParseTreeNode(declaration) { - return false - } - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - return r.requiresAddingImplicitUndefined(declaration, symbol, enclosingDeclaration) -} - -func (r *emitResolver) requiresAddingImplicitUndefined(declaration *ast.Node, symbol *ast.Symbol, enclosingDeclaration *ast.Node) bool { - // node = r.emitContext.ParseNode(node) - if !ast.IsParseTreeNode(declaration) { - return false - } - switch declaration.Kind { - case ast.KindPropertyDeclaration, ast.KindPropertySignature, ast.KindJSDocPropertyTag: - if symbol == nil { - symbol = r.checker.getSymbolOfDeclaration(declaration) - } - t := r.checker.getTypeOfSymbol(symbol) - r.checker.mappedSymbolLinks.Has(symbol) - return (symbol.Flags&ast.SymbolFlagsProperty != 0) && (symbol.Flags&ast.SymbolFlagsOptional != 0) && isOptionalDeclaration(declaration) && r.checker.ReverseMappedSymbolLinks.Has(symbol) && r.checker.ReverseMappedSymbolLinks.Get(symbol).mappedType != nil && containsNonMissingUndefinedType(r.checker, t) - case ast.KindParameter, ast.KindJSDocParameterTag: - return r.requiresAddingImplicitUndefinedWorker(declaration, enclosingDeclaration) - default: - panic("Node cannot possibly require adding undefined") - } -} - -func (r *emitResolver) requiresAddingImplicitUndefinedWorker(parameter *ast.Node, enclosingDeclaration *ast.Node) bool { - return (r.isRequiredInitializedParameter(parameter, enclosingDeclaration) || r.isOptionalUninitializedParameterProperty(parameter)) && !r.declaredParameterTypeContainsUndefined(parameter) -} - -func (r *emitResolver) declaredParameterTypeContainsUndefined(parameter *ast.Node) bool { - // typeNode := getNonlocalEffectiveTypeAnnotationNode(parameter); // !!! JSDoc Support - typeNode := parameter.Type() - if typeNode == nil { - return false - } - t := r.checker.getTypeFromTypeNode(typeNode) - // allow error type here to avoid confusing errors that the annotation has to contain undefined when it does in cases like this: - // - // export function fn(x?: Unresolved | undefined): void {} - return r.checker.isErrorType(t) || r.checker.containsUndefinedType(t) -} - -func (r *emitResolver) isOptionalUninitializedParameterProperty(parameter *ast.Node) bool { - return r.checker.strictNullChecks && - r.isOptionalParameter(parameter) && - ( /*isJSDocParameterTag(parameter) ||*/ parameter.Initializer() == nil) && // !!! TODO: JSDoc support - ast.HasSyntacticModifier(parameter, ast.ModifierFlagsParameterPropertyModifier) -} - -func (r *emitResolver) isRequiredInitializedParameter(parameter *ast.Node, enclosingDeclaration *ast.Node) bool { - if !r.checker.strictNullChecks || r.isOptionalParameter(parameter) || /*isJSDocParameterTag(parameter) ||*/ parameter.Initializer() == nil { // !!! TODO: JSDoc Support - return false - } - if ast.HasSyntacticModifier(parameter, ast.ModifierFlagsParameterPropertyModifier) { - return enclosingDeclaration != nil && ast.IsFunctionLikeDeclaration(enclosingDeclaration) - } - return true -} - -func (r *emitResolver) isOptionalParameter(node *ast.Node) bool { - // !!! TODO: JSDoc support - // if (hasEffectiveQuestionToken(node)) { - // return true; - // } - if ast.IsParameter(node) && node.AsParameterDeclaration().QuestionToken != nil { - return true - } - if !ast.IsParameter(node) { - return false - } - if node.Initializer() != nil { - signature := r.checker.getSignatureFromDeclaration(node.Parent) - parameterIndex := core.FindIndex(node.Parent.Parameters(), func(p *ast.ParameterDeclarationNode) bool { return p == node }) - debug.Assert(parameterIndex >= 0) - // Only consider syntactic or instantiated parameters as optional, not `void` parameters as this function is used - // in grammar checks and checking for `void` too early results in parameter types widening too early - // and causes some noImplicitAny errors to be lost. - return parameterIndex >= r.checker.getMinArgumentCountEx(signature, MinArgumentCountFlagsStrongArityForUntypedJS|MinArgumentCountFlagsVoidIsNonOptional) - } - iife := ast.GetImmediatelyInvokedFunctionExpression(node.Parent) - if iife != nil { - parameterIndex := core.FindIndex(node.Parent.Parameters(), func(p *ast.ParameterDeclarationNode) bool { return p == node }) - return node.Type() == nil && - node.AsParameterDeclaration().DotDotDotToken == nil && - parameterIndex >= len(r.checker.getEffectiveCallArguments(iife)) - } - - return false -} - -func (r *emitResolver) IsLiteralConstDeclaration(node *ast.Node) bool { - // node = r.emitContext.ParseNode(node) - if !ast.IsParseTreeNode(node) { - return false - } - if isDeclarationReadonly(node) || ast.IsVariableDeclaration(node) && ast.IsVarConst(node) { - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - return isFreshLiteralType(r.checker.getTypeOfSymbol(r.checker.getSymbolOfDeclaration(node))) - } - return false -} - -func (r *emitResolver) IsExpandoFunctionDeclaration(node *ast.Node) bool { - // node = r.emitContext.ParseNode(node) - // !!! TODO: expando function support - return false -} - -func (r *emitResolver) isSymbolAccessible(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags, shouldComputeAliasToMarkVisible bool) printer.SymbolAccessibilityResult { - return r.checker.IsSymbolAccessible(symbol, enclosingDeclaration, meaning, shouldComputeAliasToMarkVisible) -} - -func (r *emitResolver) IsSymbolAccessible(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags, shouldComputeAliasToMarkVisible bool) printer.SymbolAccessibilityResult { - // TODO: Split into locking and non-locking API methods - only current usage is the symbol tracker, which is non-locking, - // as all tracker calls happen within a CreateX call below, which already holds a lock - // r.checkerMu.Lock() - // defer r.checkerMu.Unlock() - return r.isSymbolAccessible(symbol, enclosingDeclaration, meaning, shouldComputeAliasToMarkVisible) -} - -func isConstEnumOrConstEnumOnlyModule(s *ast.Symbol) bool { - return isConstEnumSymbol(s) || s.Flags&ast.SymbolFlagsConstEnumOnlyModule != 0 -} - -func (r *emitResolver) IsReferencedAliasDeclaration(node *ast.Node) bool { - c := r.checker - if !c.canCollectSymbolAliasAccessibilityData || !ast.IsParseTreeNode(node) { - return true - } - - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - - if ast.IsAliasSymbolDeclaration(node) { - if symbol := c.getSymbolOfDeclaration(node); symbol != nil { - aliasLinks := c.aliasSymbolLinks.Get(symbol) - if aliasLinks.referenced { - return true - } - target := aliasLinks.aliasTarget - if target != nil && node.ModifierFlags()&ast.ModifierFlagsExport != 0 && - c.getSymbolFlags(target)&ast.SymbolFlagsValue != 0 && - (c.compilerOptions.ShouldPreserveConstEnums() || !isConstEnumOrConstEnumOnlyModule(target)) { - return true - } - } - } - return false -} - -func (r *emitResolver) IsValueAliasDeclaration(node *ast.Node) bool { - c := r.checker - if !c.canCollectSymbolAliasAccessibilityData || !ast.IsParseTreeNode(node) { - return true - } - - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - - return r.isValueAliasDeclarationWorker(node) -} - -func (r *emitResolver) isValueAliasDeclarationWorker(node *ast.Node) bool { - c := r.checker - - switch node.Kind { - case ast.KindImportEqualsDeclaration: - return r.isAliasResolvedToValue(c.getSymbolOfDeclaration(node), false /*excludeTypeOnlyValues*/) - case ast.KindImportClause, - ast.KindNamespaceImport, - ast.KindImportSpecifier, - ast.KindExportSpecifier: - symbol := c.getSymbolOfDeclaration(node) - return symbol != nil && r.isAliasResolvedToValue(symbol, true /*excludeTypeOnlyValues*/) - case ast.KindExportDeclaration: - exportClause := node.AsExportDeclaration().ExportClause - return exportClause != nil && (ast.IsNamespaceExport(exportClause) || - core.Some(exportClause.AsNamedExports().Elements.Nodes, r.isValueAliasDeclaration)) - case ast.KindExportAssignment, ast.KindJSExportAssignment: - if node.AsExportAssignment().Expression != nil && node.AsExportAssignment().Expression.Kind == ast.KindIdentifier { - return r.isAliasResolvedToValue(c.getSymbolOfDeclaration(node), true /*excludeTypeOnlyValues*/) - } - return true - } - return false -} - -func (r *emitResolver) isAliasResolvedToValue(symbol *ast.Symbol, excludeTypeOnlyValues bool) bool { - c := r.checker - if symbol == nil { - return false - } - if symbol.ValueDeclaration != nil { - if container := ast.GetSourceFileOfNode(symbol.ValueDeclaration); container != nil { - fileSymbol := c.getSymbolOfDeclaration(container.AsNode()) - // Ensures cjs export assignment is setup, since this symbol may point at, and merge with, the file itself. - // If we don't, the merge may not have yet occurred, and the flags check below will be missing flags that - // are added as a result of the merge. - c.resolveExternalModuleSymbol(fileSymbol, false /*dontResolveAlias*/) - } - } - target := c.getExportSymbolOfValueSymbolIfExported(c.resolveAlias(symbol)) - if target == c.unknownSymbol { - return !excludeTypeOnlyValues || c.getTypeOnlyAliasDeclaration(symbol) == nil - } - // const enums and modules that contain only const enums are not considered values from the emit perspective - // unless 'preserveConstEnums' option is set to true - return c.getSymbolFlagsEx(symbol, excludeTypeOnlyValues, true /*excludeLocalMeanings*/)&ast.SymbolFlagsValue != 0 && - (c.compilerOptions.ShouldPreserveConstEnums() || - !isConstEnumOrConstEnumOnlyModule(target)) -} - -func (r *emitResolver) IsTopLevelValueImportEqualsWithEntityName(node *ast.Node) bool { - c := r.checker - if !c.canCollectSymbolAliasAccessibilityData { - return true - } - if !ast.IsParseTreeNode(node) || node.Kind != ast.KindImportEqualsDeclaration || node.Parent.Kind != ast.KindSourceFile { - return false - } - if ast.IsImportEqualsDeclaration(node) && - (ast.NodeIsMissing(node.AsImportEqualsDeclaration().ModuleReference) || node.AsImportEqualsDeclaration().ModuleReference.Kind != ast.KindExternalModuleReference) { - return false - } - - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - - return r.isAliasResolvedToValue(c.getSymbolOfDeclaration(node), false /*excludeTypeOnlyValues*/) -} - -func (r *emitResolver) MarkLinkedReferencesRecursively(file *ast.SourceFile) { - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - - if file != nil { - var visit ast.Visitor - visit = func(n *ast.Node) bool { - if ast.IsImportEqualsDeclaration(n) && n.ModifierFlags()&ast.ModifierFlagsExport == 0 { - return false // These are deferred and marked in a chain when referenced - } - if ast.IsJSExportAssignment(n) { - return false - } - if ast.IsImportDeclaration(n) { - return false // likewise, these are ultimately what get marked by calls on other nodes - we want to skip them - } - r.checker.markLinkedReferences(n, ReferenceHintUnspecified, nil /*propSymbol*/, nil /*parentType*/) - n.ForEachChild(visit) - return false - } - file.ForEachChild(visit) - } -} - -func (r *emitResolver) GetExternalModuleFileFromDeclaration(declaration *ast.Node) *ast.SourceFile { - if !ast.IsParseTreeNode(declaration) { - return nil - } - - var specifier *ast.Node - if declaration.Kind == ast.KindModuleDeclaration { - if ast.IsStringLiteral(declaration.Name()) { - specifier = declaration.Name() - } - } else { - specifier = ast.GetExternalModuleName(declaration) - } - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - moduleSymbol := r.checker.resolveExternalModuleNameWorker(specifier, specifier /*moduleNotFoundError*/, nil, false, false) // TODO: GH#18217 - if moduleSymbol == nil { - return nil - } - decl := ast.GetDeclarationOfKind(moduleSymbol, ast.KindSourceFile) - if decl == nil { - return nil - } - return decl.AsSourceFile() -} - -func (r *emitResolver) getReferenceResolver() binder.ReferenceResolver { - if r.referenceResolver == nil { - r.referenceResolver = binder.NewReferenceResolver(r.checker.compilerOptions, binder.ReferenceResolverHooks{ - ResolveName: r.checker.resolveName, - GetResolvedSymbol: r.checker.getResolvedSymbol, - GetMergedSymbol: r.checker.getMergedSymbol, - GetParentOfSymbol: r.checker.getParentOfSymbol, - GetSymbolOfDeclaration: r.checker.getSymbolOfDeclaration, - GetTypeOnlyAliasDeclaration: r.checker.getTypeOnlyAliasDeclarationEx, - GetExportSymbolOfValueSymbolIfExported: r.checker.getExportSymbolOfValueSymbolIfExported, - GetElementAccessExpressionName: r.checker.tryGetElementAccessExpressionName, - }) - } - return r.referenceResolver -} - -func (r *emitResolver) GetReferencedExportContainer(node *ast.IdentifierNode, prefixLocals bool) *ast.Node /*SourceFile|ModuleDeclaration|EnumDeclaration*/ { - if !ast.IsParseTreeNode(node) { - return nil - } - - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - - return r.getReferenceResolver().GetReferencedExportContainer(node, prefixLocals) -} - -func (r *emitResolver) SetReferencedImportDeclaration(node *ast.IdentifierNode, ref *ast.Declaration) { - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - r.jsxLinks.Get(node).importRef = ref -} - -func (r *emitResolver) GetReferencedImportDeclaration(node *ast.IdentifierNode) *ast.Declaration { - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - if !ast.IsParseTreeNode(node) { - return r.jsxLinks.Get(node).importRef - } - - return r.getReferenceResolver().GetReferencedImportDeclaration(node) -} - -func (r *emitResolver) GetReferencedValueDeclaration(node *ast.IdentifierNode) *ast.Declaration { - if !ast.IsParseTreeNode(node) { - return nil - } - - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - - return r.getReferenceResolver().GetReferencedValueDeclaration(node) -} - -func (r *emitResolver) GetReferencedValueDeclarations(node *ast.IdentifierNode) []*ast.Declaration { - if !ast.IsParseTreeNode(node) { - return nil - } - - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - - return r.getReferenceResolver().GetReferencedValueDeclarations(node) -} - -func (r *emitResolver) GetElementAccessExpressionName(expression *ast.ElementAccessExpression) string { - if !ast.IsParseTreeNode(expression.AsNode()) { - return "" - } - - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - - return r.getReferenceResolver().GetElementAccessExpressionName(expression) -} - -// TODO: the emit resolver being responsible for some amount of node construction is a very leaky abstraction, -// and requires giving it access to a lot of context it's otherwise not required to have, which also further complicates the API -// and likely reduces performance. There's probably some refactoring that could be done here to simplify this. - -func (r *emitResolver) CreateReturnTypeOfSignatureDeclaration(emitContext *printer.EmitContext, signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { - original := emitContext.ParseNode(signatureDeclaration) - if original == nil { - return emitContext.Factory.NewKeywordTypeNode(ast.KindAnyKeyword) - } - - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - requestNodeBuilder := NewNodeBuilder(r.checker, emitContext) // TODO: cache per-context - return requestNodeBuilder.SerializeReturnTypeForSignature(original, enclosingDeclaration, flags, internalFlags, tracker) -} - -func (r *emitResolver) CreateTypeParametersOfSignatureDeclaration(emitContext *printer.EmitContext, signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node { - original := emitContext.ParseNode(signatureDeclaration) - if original == nil { - return nil - } - - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - requestNodeBuilder := NewNodeBuilder(r.checker, emitContext) // TODO: cache per-context - return requestNodeBuilder.SerializeTypeParametersForSignature(original, enclosingDeclaration, flags, internalFlags, tracker) -} - -func (r *emitResolver) CreateTypeOfDeclaration(emitContext *printer.EmitContext, declaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { - original := emitContext.ParseNode(declaration) - if original == nil { - return emitContext.Factory.NewKeywordTypeNode(ast.KindAnyKeyword) - } - - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - requestNodeBuilder := NewNodeBuilder(r.checker, emitContext) // TODO: cache per-context - // // Get type of the symbol if this is the valid symbol otherwise get type at location - symbol := r.checker.getSymbolOfDeclaration(declaration) - return requestNodeBuilder.SerializeTypeForDeclaration(declaration, symbol, enclosingDeclaration, flags|nodebuilder.FlagsMultilineObjectLiterals, internalFlags, tracker) -} - -func (r *emitResolver) CreateLiteralConstValue(emitContext *printer.EmitContext, node *ast.Node, tracker nodebuilder.SymbolTracker) *ast.Node { - node = emitContext.ParseNode(node) - r.checkerMu.Lock() - t := r.checker.getTypeOfSymbol(r.checker.getSymbolOfDeclaration(node)) - r.checkerMu.Unlock() - if t == nil { - return nil // TODO: How!? Maybe this should be a panic. All symbols should have a type. - } - - var enumResult *ast.Node - if t.flags&TypeFlagsEnumLike != 0 { - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - requestNodeBuilder := NewNodeBuilder(r.checker, emitContext) // TODO: cache per-context - enumResult = requestNodeBuilder.SymbolToExpression(t.symbol, ast.SymbolFlagsValue, node, nodebuilder.FlagsNone, nodebuilder.InternalFlagsNone, tracker) - // What about regularTrueType/regularFalseType - since those aren't fresh, we never make initializers from them - // TODO: handle those if this function is ever used for more than initializers in declaration emit - } else if t == r.checker.trueType { - enumResult = emitContext.Factory.NewKeywordExpression(ast.KindTrueKeyword) - } else if t == r.checker.falseType { - enumResult = emitContext.Factory.NewKeywordExpression(ast.KindFalseKeyword) - } - if enumResult != nil { - return enumResult - } - if t.flags&TypeFlagsLiteral == 0 { - return nil // non-literal type - } - switch value := t.AsLiteralType().value.(type) { - case string: - return emitContext.Factory.NewStringLiteral(value) - case jsnum.Number: - if value.Abs() != value { - // negative - return emitContext.Factory.NewPrefixUnaryExpression( - ast.KindMinusToken, - emitContext.Factory.NewNumericLiteral(value.String()[1:]), - ) - } - return emitContext.Factory.NewNumericLiteral(value.String()) - case jsnum.PseudoBigInt: - return emitContext.Factory.NewBigIntLiteral(pseudoBigIntToString(value) + "n") - case bool: - kind := ast.KindFalseKeyword - if value { - kind = ast.KindTrueKeyword - } - return emitContext.Factory.NewKeywordExpression(kind) - } - panic("unhandled literal const value kind") -} - -func (r *emitResolver) CreateTypeOfExpression(emitContext *printer.EmitContext, expression *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { - expression = emitContext.ParseNode(expression) - if expression == nil { - return emitContext.Factory.NewKeywordTypeNode(ast.KindAnyKeyword) - } - - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - requestNodeBuilder := NewNodeBuilder(r.checker, emitContext) // TODO: cache per-context - return requestNodeBuilder.SerializeTypeForExpression(expression, enclosingDeclaration, flags|nodebuilder.FlagsMultilineObjectLiterals, internalFlags, tracker) -} - -func (r *emitResolver) CreateLateBoundIndexSignatures(emitContext *printer.EmitContext, container *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node { - container = emitContext.ParseNode(container) - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - - sym := container.Symbol() - staticInfos := r.checker.getIndexInfosOfType(r.checker.getTypeOfSymbol(sym)) - instanceIndexSymbol := r.checker.getIndexSymbol(sym) - var instanceInfos []*IndexInfo - if instanceIndexSymbol != nil { - siblingSymbols := slices.Collect(maps.Values(r.checker.getMembersOfSymbol(sym))) - instanceInfos = r.checker.getIndexInfosOfIndexSymbol(instanceIndexSymbol, siblingSymbols) - } - - requestNodeBuilder := NewNodeBuilder(r.checker, emitContext) // TODO: cache per-context - - var result []*ast.Node - for i, infoList := range [][]*IndexInfo{staticInfos, instanceInfos} { - isStatic := true - if i > 0 { - isStatic = false - } - if len(infoList) == 0 { - continue - } - for _, info := range infoList { - if info.declaration != nil { - continue - } - if info == r.checker.anyBaseTypeIndexInfo { - continue // inherited, but looks like a late-bound signature because it has no declarations - } - if len(info.components) != 0 { - // !!! TODO: Complete late-bound index info support - getObjectLiteralIndexInfo does not yet add late bound components to index signatures - allComponentComputedNamesSerializable := enclosingDeclaration != nil && core.Every(info.components, func(c *ast.Node) bool { - return c.Name() != nil && - ast.IsComputedPropertyName(c.Name()) && - ast.IsEntityNameExpression(c.Name().AsComputedPropertyName().Expression) && - r.isEntityNameVisible(c.Name().AsComputedPropertyName().Expression, enclosingDeclaration, false).Accessibility == printer.SymbolAccessibilityAccessible - }) - if allComponentComputedNamesSerializable { - for _, c := range info.components { - if r.checker.hasLateBindableName(c) { - // skip late bound props that contribute to the index signature - they'll be preserved via other means - continue - } - - firstIdentifier := ast.GetFirstIdentifier(c.Name().Expression()) - name := r.checker.resolveName(firstIdentifier, firstIdentifier.Text(), ast.SymbolFlagsValue|ast.SymbolFlagsExportValue, nil /*nameNotFoundMessage*/, true /*isUse*/, false /*excludeGlobals*/) - if name != nil { - tracker.TrackSymbol(name, enclosingDeclaration, ast.SymbolFlagsValue) - } - - mods := core.IfElse(isStatic, []*ast.Node{emitContext.Factory.NewModifier(ast.KindStaticKeyword)}, nil) - if info.isReadonly { - mods = append(mods, emitContext.Factory.NewModifier(ast.KindReadonlyKeyword)) - } - - decl := emitContext.Factory.NewPropertyDeclaration( - core.IfElse(mods != nil, emitContext.Factory.NewModifierList(mods), nil), - c.Name(), - c.QuestionToken(), - requestNodeBuilder.TypeToTypeNode(r.checker.getTypeOfSymbol(c.Symbol()), enclosingDeclaration, flags, internalFlags, tracker), - nil, - ) - result = append(result, decl) - } - continue - } - } - node := requestNodeBuilder.IndexInfoToIndexSignatureDeclaration(info, enclosingDeclaration, flags, internalFlags, tracker) - if node != nil && isStatic { - modNodes := []*ast.Node{emitContext.Factory.NewModifier(ast.KindStaticKeyword)} - mods := node.Modifiers() - if mods != nil { - modNodes = append(modNodes, mods.Nodes...) - } - mods = emitContext.Factory.NewModifierList(modNodes) - node = emitContext.Factory.UpdateIndexSignatureDeclaration( - node.AsIndexSignatureDeclaration(), - mods, - node.ParameterList(), - node.Type(), - ) - } - if node != nil { - result = append(result, node) - } - } - } - return result -} - -func (r *emitResolver) GetEffectiveDeclarationFlags(node *ast.Node, flags ast.ModifierFlags) ast.ModifierFlags { - // node = emitContext.ParseNode(node) - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - return r.checker.GetEffectiveDeclarationFlags(node, flags) -} - -func (r *emitResolver) GetResolutionModeOverride(node *ast.Node) core.ResolutionMode { - // node = emitContext.ParseNode(node) - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - return r.checker.GetResolutionModeOverride(node.AsImportAttributes(), false) -} - -func (r *emitResolver) GetConstantValue(node *ast.Node) any { - // node = emitContext.ParseNode(node) - r.checkerMu.Lock() - defer r.checkerMu.Unlock() - return r.checker.GetConstantValue(node) -} diff --git a/kitcom/internal/tsgo/checker/exports.go b/kitcom/internal/tsgo/checker/exports.go deleted file mode 100644 index 6019466..0000000 --- a/kitcom/internal/tsgo/checker/exports.go +++ /dev/null @@ -1,172 +0,0 @@ -package checker - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics" -) - -func (c *Checker) GetStringType() *Type { - return c.stringType -} - -func (c *Checker) GetUnknownSymbol() *ast.Symbol { - return c.unknownSymbol -} - -func (c *Checker) GetUnionType(types []*Type) *Type { - return c.getUnionType(types) -} - -func (c *Checker) GetGlobalSymbol(name string, meaning ast.SymbolFlags, diagnostic *diagnostics.Message) *ast.Symbol { - return c.getGlobalSymbol(name, meaning, diagnostic) -} - -func (c *Checker) GetMergedSymbol(symbol *ast.Symbol) *ast.Symbol { - return c.getMergedSymbol(symbol) -} - -func (c *Checker) TryFindAmbientModule(moduleName string) *ast.Symbol { - return c.tryFindAmbientModule(moduleName, true /* withAugmentations */) -} - -func (c *Checker) GetImmediateAliasedSymbol(symbol *ast.Symbol) *ast.Symbol { - return c.getImmediateAliasedSymbol(symbol) -} - -func (c *Checker) GetTypeOnlyAliasDeclaration(symbol *ast.Symbol) *ast.Node { - return c.getTypeOnlyAliasDeclaration(symbol) -} - -func (c *Checker) ResolveExternalModuleName(moduleSpecifier *ast.Node) *ast.Symbol { - return c.resolveExternalModuleName(moduleSpecifier, moduleSpecifier, true /*ignoreErrors*/) -} - -func (c *Checker) ResolveExternalModuleSymbol(moduleSymbol *ast.Symbol) *ast.Symbol { - return c.resolveExternalModuleSymbol(moduleSymbol, false /*dontResolveAlias*/) -} - -func (c *Checker) GetTypeFromTypeNode(node *ast.Node) *Type { - return c.getTypeFromTypeNode(node) -} - -func (c *Checker) IsArrayLikeType(t *Type) bool { - return c.isArrayLikeType(t) -} - -func (c *Checker) GetPropertiesOfType(t *Type) []*ast.Symbol { - return c.getPropertiesOfType(t) -} - -func (c *Checker) GetPropertyOfType(t *Type, name string) *ast.Symbol { - return c.getPropertyOfType(t, name) -} - -func (c *Checker) TypeHasCallOrConstructSignatures(t *Type) bool { - return c.typeHasCallOrConstructSignatures(t) -} - -// Checks if a property can be accessed in a location. -// The location is given by the `node` parameter. -// The node does not need to be a property access. -// @param node location where to check property accessibility -// @param isSuper whether to consider this a `super` property access, e.g. `super.foo`. -// @param isWrite whether this is a write access, e.g. `++foo.x`. -// @param containingType type where the property comes from. -// @param property property symbol. -func (c *Checker) IsPropertyAccessible(node *ast.Node, isSuper bool, isWrite bool, containingType *Type, property *ast.Symbol) bool { - return c.isPropertyAccessible(node, isSuper, isWrite, containingType, property) -} - -func (c *Checker) GetTypeOfPropertyOfContextualType(t *Type, name string) *Type { - return c.getTypeOfPropertyOfContextualType(t, name) -} - -func GetDeclarationModifierFlagsFromSymbol(s *ast.Symbol) ast.ModifierFlags { - return getDeclarationModifierFlagsFromSymbol(s) -} - -func (c *Checker) WasCanceled() bool { - return c.wasCanceled -} - -func (c *Checker) GetSignaturesOfType(t *Type, kind SignatureKind) []*Signature { - return c.getSignaturesOfType(t, kind) -} - -func (c *Checker) GetDeclaredTypeOfSymbol(symbol *ast.Symbol) *Type { - return c.getDeclaredTypeOfSymbol(symbol) -} - -func (c *Checker) GetTypeOfSymbol(symbol *ast.Symbol) *Type { - return c.getTypeOfSymbol(symbol) -} - -func (c *Checker) GetConstraintOfTypeParameter(typeParameter *Type) *Type { - return c.getConstraintOfTypeParameter(typeParameter) -} - -func (c *Checker) GetResolutionModeOverride(node *ast.ImportAttributes, reportErrors bool) core.ResolutionMode { - return c.getResolutionModeOverride(node, reportErrors) -} - -func (c *Checker) GetEffectiveDeclarationFlags(n *ast.Node, flagsToCheck ast.ModifierFlags) ast.ModifierFlags { - return c.getEffectiveDeclarationFlags(n, flagsToCheck) -} - -func (c *Checker) GetBaseConstraintOfType(t *Type) *Type { - return c.getBaseConstraintOfType(t) -} - -func (c *Checker) GetTypePredicateOfSignature(sig *Signature) *TypePredicate { - return c.getTypePredicateOfSignature(sig) -} - -func IsTupleType(t *Type) bool { - return isTupleType(t) -} - -func (c *Checker) GetReturnTypeOfSignature(sig *Signature) *Type { - return c.getReturnTypeOfSignature(sig) -} - -func (c *Checker) HasEffectiveRestParameter(signature *Signature) bool { - return c.hasEffectiveRestParameter(signature) -} - -func (c *Checker) GetLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol *ast.Symbol) []*Type { - return c.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol) -} - -func (c *Checker) GetContextualTypeForObjectLiteralElement(element *ast.Node, contextFlags ContextFlags) *Type { - return c.getContextualTypeForObjectLiteralElement(element, contextFlags) -} - -func (c *Checker) TypePredicateToString(t *TypePredicate) string { - return c.typePredicateToString(t) -} - -func (c *Checker) GetExpandedParameters(signature *Signature, skipUnionExpanding bool) [][]*ast.Symbol { - return c.getExpandedParameters(signature, skipUnionExpanding) -} - -func (c *Checker) GetResolvedSignature(node *ast.Node) *Signature { - return c.getResolvedSignature(node, nil, CheckModeNormal) -} - -// Return the type of the given property in the given type, or nil if no such property exists -func (c *Checker) GetTypeOfPropertyOfType(t *Type, name string) *Type { - return c.getTypeOfPropertyOfType(t, name) -} - -func (c *Checker) GetContextualTypeForArgumentAtIndex(node *ast.Node, argIndex int) *Type { - return c.getContextualTypeForArgumentAtIndex(node, argIndex) -} - -func (c *Checker) GetIndexSignaturesAtLocation(node *ast.Node) []*ast.Node { - return c.getIndexSignaturesAtLocation(node) -} - -func (c *Checker) GetResolvedSymbol(node *ast.Node) *ast.Symbol { - return c.getResolvedSymbol(node) -} diff --git a/kitcom/internal/tsgo/checker/flow.go b/kitcom/internal/tsgo/checker/flow.go deleted file mode 100644 index b6b67d6..0000000 --- a/kitcom/internal/tsgo/checker/flow.go +++ /dev/null @@ -1,2724 +0,0 @@ -package checker - -import ( - "math" - "slices" - "strconv" - "strings" - - "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/evaluator" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/scanner" -) - -type FlowType struct { - t *Type - incomplete bool -} - -func (ft *FlowType) isNil() bool { - return ft.t == nil -} - -func (c *Checker) newFlowType(t *Type, incomplete bool) FlowType { - if incomplete && t.flags&TypeFlagsNever != 0 { - t = c.silentNeverType - } - return FlowType{t: t, incomplete: incomplete} -} - -type SharedFlow struct { - flow *ast.FlowNode - flowType FlowType -} - -type FlowState struct { - reference *ast.Node - declaredType *Type - initialType *Type - flowContainer *ast.Node - refKey string - depth int - sharedFlowStart int - reduceLabels []*ast.FlowReduceLabelData - next *FlowState -} - -func (c *Checker) getFlowState() *FlowState { - f := c.freeFlowState - if f == nil { - f = &FlowState{} - } - c.freeFlowState = f.next - return f -} - -func (c *Checker) putFlowState(f *FlowState) { - *f = FlowState{ - reduceLabels: f.reduceLabels[:0], - next: c.freeFlowState, - } - c.freeFlowState = f -} - -func getFlowNodeOfNode(node *ast.Node) *ast.FlowNode { - flowNodeData := node.FlowNodeData() - if flowNodeData != nil { - return flowNodeData.FlowNode - } - return nil -} - -func (c *Checker) getFlowTypeOfReference(reference *ast.Node, declaredType *Type) *Type { - return c.getFlowTypeOfReferenceEx(reference, declaredType, declaredType, nil, nil) -} - -func (c *Checker) getFlowTypeOfReferenceEx(reference *ast.Node, declaredType *Type, initialType *Type, flowContainer *ast.Node, flowNode *ast.FlowNode) *Type { - if c.flowAnalysisDisabled { - return c.errorType - } - if flowNode == nil { - flowNode = getFlowNodeOfNode(reference) - if flowNode == nil { - return declaredType - } - } - f := c.getFlowState() - f.reference = reference - f.declaredType = declaredType - f.initialType = core.Coalesce(initialType, declaredType) - f.flowContainer = flowContainer - f.sharedFlowStart = len(c.sharedFlows) - c.flowInvocationCount++ - evolvedType := c.getTypeAtFlowNode(f, flowNode).t - c.sharedFlows = c.sharedFlows[:f.sharedFlowStart] - c.putFlowState(f) - // When the reference is 'x' in an 'x.length', 'x.push(value)', 'x.unshift(value)' or x[n] = value' operation, - // we give type 'any[]' to 'x' instead of using the type determined by control flow analysis such that operations - // on empty arrays are possible without implicit any errors and new element types can be inferred without - // type mismatch errors. - var resultType *Type - if evolvedType.objectFlags&ObjectFlagsEvolvingArray != 0 && c.isEvolvingArrayOperationTarget(reference) { - resultType = c.autoArrayType - } else { - resultType = c.finalizeEvolvingArrayType(evolvedType) - } - if resultType == c.unreachableNeverType || reference.Parent != nil && ast.IsNonNullExpression(reference.Parent) && resultType.flags&TypeFlagsNever == 0 && c.getTypeWithFacts(resultType, TypeFactsNEUndefinedOrNull).flags&TypeFlagsNever != 0 { - return declaredType - } - return resultType -} - -func (c *Checker) getTypeAtFlowNode(f *FlowState, flow *ast.FlowNode) FlowType { - if f.depth == 2000 { - // We have made 2000 recursive invocations. To avoid overflowing the call stack we report an error - // and disable further control flow analysis in the containing function or module body. - c.flowAnalysisDisabled = true - c.reportFlowControlError(f.reference) - return FlowType{t: c.errorType} - } - f.depth++ - var sharedFlow *ast.FlowNode - for { - flags := flow.Flags - if flags&ast.FlowFlagsShared != 0 { - // We cache results of flow type resolution for shared nodes that were previously visited in - // the same getFlowTypeOfReference invocation. A node is considered shared when it is the - // antecedent of more than one node. - for i := f.sharedFlowStart; i < len(c.sharedFlows); i++ { - if c.sharedFlows[i].flow == flow { - f.depth-- - return c.sharedFlows[i].flowType - } - } - sharedFlow = flow - } - var t FlowType - switch { - case flags&ast.FlowFlagsAssignment != 0: - t = c.getTypeAtFlowAssignment(f, flow) - if t.isNil() { - flow = flow.Antecedent - continue - } - case flags&ast.FlowFlagsCall != 0: - t = c.getTypeAtFlowCall(f, flow) - if t.isNil() { - flow = flow.Antecedent - continue - } - case flags&ast.FlowFlagsCondition != 0: - t = c.getTypeAtFlowCondition(f, flow) - case flags&ast.FlowFlagsSwitchClause != 0: - t = c.getTypeAtSwitchClause(f, flow) - case flags&ast.FlowFlagsBranchLabel != 0: - antecedents := getBranchLabelAntecedents(flow, f.reduceLabels) - if antecedents.Next == nil { - flow = antecedents.Flow - continue - } - t = c.getTypeAtFlowBranchLabel(f, flow, antecedents) - case flags&ast.FlowFlagsLoopLabel != 0: - if flow.Antecedents.Next == nil { - flow = flow.Antecedents.Flow - continue - } - t = c.getTypeAtFlowLoopLabel(f, flow) - case flags&ast.FlowFlagsArrayMutation != 0: - t = c.getTypeAtFlowArrayMutation(f, flow) - if t.isNil() { - flow = flow.Antecedent - continue - } - case flags&ast.FlowFlagsReduceLabel != 0: - f.reduceLabels = append(f.reduceLabels, flow.Node.AsFlowReduceLabelData()) - t = c.getTypeAtFlowNode(f, flow.Antecedent) - f.reduceLabels = f.reduceLabels[:len(f.reduceLabels)-1] - case flags&ast.FlowFlagsStart != 0: - // Check if we should continue with the control flow of the containing function. - container := flow.Node - if container != nil && container != f.flowContainer && !ast.IsPropertyAccessExpression(f.reference) && !ast.IsElementAccessExpression(f.reference) && !(f.reference.Kind == ast.KindThisKeyword && !ast.IsArrowFunction(container)) { - flow = container.FlowNodeData().FlowNode - continue - } - // At the top of the flow we have the initial type. - t = FlowType{t: f.initialType} - default: - // Unreachable code errors are reported in the binding phase. Here we - // simply return the non-auto declared type to reduce follow-on errors. - t = FlowType{t: c.convertAutoToAny(f.declaredType)} - } - if sharedFlow != nil { - // Record visited node and the associated type in the cache. - c.sharedFlows = append(c.sharedFlows, SharedFlow{flow: sharedFlow, flowType: t}) - } - f.depth-- - return t - } -} - -func getBranchLabelAntecedents(flow *ast.FlowNode, reduceLabels []*ast.FlowReduceLabelData) *ast.FlowList { - i := len(reduceLabels) - for i != 0 { - i-- - data := reduceLabels[i] - if data.Target == flow { - return data.Antecedents - } - } - return flow.Antecedents -} - -func (c *Checker) getTypeAtFlowAssignment(f *FlowState, flow *ast.FlowNode) FlowType { - node := flow.Node - // Assignments only narrow the computed type if the declared type is a union type. Thus, we - // only need to evaluate the assigned type if the declared type is a union type. - if c.isMatchingReference(f.reference, node) { - if !c.isReachableFlowNode(flow) { - return FlowType{t: c.unreachableNeverType} - } - if getAssignmentTargetKind(node) == AssignmentKindCompound { - flowType := c.getTypeAtFlowNode(f, flow.Antecedent) - return c.newFlowType(c.getBaseTypeOfLiteralType(flowType.t), flowType.incomplete) - } - if f.declaredType == c.autoType || f.declaredType == c.autoArrayType { - if c.isEmptyArrayAssignment(node) { - return FlowType{t: c.getEvolvingArrayType(c.neverType)} - } - assignedType := c.getWidenedLiteralType(c.getInitialOrAssignedType(f, flow)) - if c.isTypeAssignableTo(assignedType, f.declaredType) { - return FlowType{t: assignedType} - } - return FlowType{t: c.anyArrayType} - } - t := f.declaredType - if isInCompoundLikeAssignment(node) { - t = c.getBaseTypeOfLiteralType(t) - } - if t.flags&TypeFlagsUnion != 0 { - return FlowType{t: c.getAssignmentReducedType(t, c.getInitialOrAssignedType(f, flow))} - } - return FlowType{t: t} - } - // We didn't have a direct match. However, if the reference is a dotted name, this - // may be an assignment to a left hand part of the reference. For example, for a - // reference 'x.y.z', we may be at an assignment to 'x.y' or 'x'. In that case, - // return the declared type. - if c.containsMatchingReference(f.reference, node) { - if !c.isReachableFlowNode(flow) { - return FlowType{t: c.unreachableNeverType} - } - return FlowType{t: f.declaredType} - } - // for (const _ in ref) acts as a nonnull on ref - if ast.IsVariableDeclaration(node) && ast.IsForInStatement(node.Parent.Parent) && (c.isMatchingReference(f.reference, node.Parent.Parent.Expression()) || c.optionalChainContainsReference(node.Parent.Parent.Expression(), f.reference)) { - return FlowType{t: c.getNonNullableTypeIfNeeded(c.finalizeEvolvingArrayType(c.getTypeAtFlowNode(f, flow.Antecedent).t))} - } - // Assignment doesn't affect reference - return FlowType{} -} - -func (c *Checker) getInitialOrAssignedType(f *FlowState, flow *ast.FlowNode) *Type { - if ast.IsVariableDeclaration(flow.Node) || ast.IsBindingElement(flow.Node) { - return c.getNarrowableTypeForReference(c.getInitialType(flow.Node), f.reference, CheckModeNormal) - } - return c.getNarrowableTypeForReference(c.getAssignedType(flow.Node), f.reference, CheckModeNormal) -} - -func (c *Checker) isEmptyArrayAssignment(node *ast.Node) bool { - return ast.IsVariableDeclaration(node) && node.Initializer() != nil && isEmptyArrayLiteral(node.Initializer()) || - !ast.IsBindingElement(node) && ast.IsBinaryExpression(node.Parent) && isEmptyArrayLiteral(node.Parent.AsBinaryExpression().Right) -} - -func (c *Checker) getTypeAtFlowCall(f *FlowState, flow *ast.FlowNode) FlowType { - signature := c.getEffectsSignature(flow.Node) - if signature != nil { - predicate := c.getTypePredicateOfSignature(signature) - if predicate != nil && (predicate.kind == TypePredicateKindAssertsThis || predicate.kind == TypePredicateKindAssertsIdentifier) { - flowType := c.getTypeAtFlowNode(f, flow.Antecedent) - t := c.finalizeEvolvingArrayType(flowType.t) - var narrowedType *Type - switch { - case predicate.t != nil: - narrowedType = c.narrowTypeByTypePredicate(f, t, predicate, flow.Node, true /*assumeTrue*/) - case predicate.kind == TypePredicateKindAssertsIdentifier && predicate.parameterIndex >= 0 && int(predicate.parameterIndex) < len(flow.Node.Arguments()): - narrowedType = c.narrowTypeByAssertion(f, t, flow.Node.Arguments()[predicate.parameterIndex]) - default: - narrowedType = t - } - if narrowedType == t { - return flowType - } - return c.newFlowType(narrowedType, flowType.incomplete) - } - if c.getReturnTypeOfSignature(signature).flags&TypeFlagsNever != 0 { - return FlowType{t: c.unreachableNeverType} - } - } - return FlowType{} -} - -func (c *Checker) narrowTypeByTypePredicate(f *FlowState, t *Type, predicate *TypePredicate, callExpression *ast.Node, assumeTrue bool) *Type { - // Don't narrow from 'any' if the predicate type is exactly 'Object' or 'Function' - if predicate.t != nil && !(IsTypeAny(t) && (predicate.t == c.globalObjectType || predicate.t == c.globalFunctionType)) { - predicateArgument := c.getTypePredicateArgument(predicate, callExpression) - if predicateArgument != nil { - if c.isMatchingReference(f.reference, predicateArgument) { - return c.getNarrowedType(t, predicate.t, assumeTrue, false /*checkDerived*/) - } - if c.strictNullChecks && c.optionalChainContainsReference(predicateArgument, f.reference) && (assumeTrue && !(c.hasTypeFacts(predicate.t, TypeFactsEQUndefined)) || !assumeTrue && everyType(predicate.t, c.IsNullableType)) { - t = c.getAdjustedTypeWithFacts(t, TypeFactsNEUndefinedOrNull) - } - access := c.getDiscriminantPropertyAccess(f, predicateArgument, t) - if access != nil { - return c.narrowTypeByDiscriminant(t, access, func(t *Type) *Type { - return c.getNarrowedType(t, predicate.t, assumeTrue, false /*checkDerived*/) - }) - } - } - } - return t -} - -func (c *Checker) narrowTypeByAssertion(f *FlowState, t *Type, expr *ast.Node) *Type { - node := ast.SkipParentheses(expr) - if node.Kind == ast.KindFalseKeyword { - return c.unreachableNeverType - } - if node.Kind == ast.KindBinaryExpression { - if node.AsBinaryExpression().OperatorToken.Kind == ast.KindAmpersandAmpersandToken { - return c.narrowTypeByAssertion(f, c.narrowTypeByAssertion(f, t, node.AsBinaryExpression().Left), node.AsBinaryExpression().Right) - } - if node.AsBinaryExpression().OperatorToken.Kind == ast.KindBarBarToken { - return c.getUnionType([]*Type{c.narrowTypeByAssertion(f, t, node.AsBinaryExpression().Left), c.narrowTypeByAssertion(f, t, node.AsBinaryExpression().Right)}) - } - } - return c.narrowType(f, t, node, true /*assumeTrue*/) -} - -func (c *Checker) getTypeAtFlowCondition(f *FlowState, flow *ast.FlowNode) FlowType { - flowType := c.getTypeAtFlowNode(f, flow.Antecedent) - if flowType.t.flags&TypeFlagsNever != 0 { - return flowType - } - // If we have an antecedent type (meaning we're reachable in some way), we first - // attempt to narrow the antecedent type. If that produces the never type, and if - // the antecedent type is incomplete (i.e. a transient type in a loop), then we - // take the type guard as an indication that control *could* reach here once we - // have the complete type. We proceed by switching to the silent never type which - // doesn't report errors when operators are applied to it. Note that this is the - // *only* place a silent never type is ever generated. - assumeTrue := flow.Flags&ast.FlowFlagsTrueCondition != 0 - nonEvolvingType := c.finalizeEvolvingArrayType(flowType.t) - narrowedType := c.narrowType(f, nonEvolvingType, flow.Node, assumeTrue) - if narrowedType == nonEvolvingType { - return flowType - } - return c.newFlowType(narrowedType, flowType.incomplete) -} - -// Narrow the given type based on the given expression having the assumed boolean value. The returned type -// will be a subtype or the same type as the argument. -func (c *Checker) narrowType(f *FlowState, t *Type, expr *ast.Node, assumeTrue bool) *Type { - // for `a?.b`, we emulate a synthetic `a !== null && a !== undefined` condition for `a` - if ast.IsExpressionOfOptionalChainRoot(expr) || ast.IsBinaryExpression(expr.Parent) && (expr.Parent.AsBinaryExpression().OperatorToken.Kind == ast.KindQuestionQuestionToken || expr.Parent.AsBinaryExpression().OperatorToken.Kind == ast.KindQuestionQuestionEqualsToken) && expr.Parent.AsBinaryExpression().Left == expr { - return c.narrowTypeByOptionality(f, t, expr, assumeTrue) - } - switch expr.Kind { - case ast.KindIdentifier: - // When narrowing a reference to a const variable, non-assigned parameter, or readonly property, we inline - // up to five levels of aliased conditional expressions that are themselves declared as const variables. - if !c.isMatchingReference(f.reference, expr) && c.inlineLevel < 5 { - symbol := c.getResolvedSymbol(expr) - if c.isConstantVariable(symbol) { - declaration := symbol.ValueDeclaration - if declaration != nil && ast.IsVariableDeclaration(declaration) && declaration.Type() == nil && declaration.Initializer() != nil && c.isConstantReference(f.reference) { - c.inlineLevel++ - result := c.narrowType(f, t, declaration.Initializer(), assumeTrue) - c.inlineLevel-- - return result - } - } - } - fallthrough - case ast.KindThisKeyword, ast.KindSuperKeyword, ast.KindPropertyAccessExpression, ast.KindElementAccessExpression: - return c.narrowTypeByTruthiness(f, t, expr, assumeTrue) - case ast.KindCallExpression: - return c.narrowTypeByCallExpression(f, t, expr, assumeTrue) - case ast.KindParenthesizedExpression, ast.KindNonNullExpression, ast.KindSatisfiesExpression: - return c.narrowType(f, t, expr.Expression(), assumeTrue) - case ast.KindBinaryExpression: - return c.narrowTypeByBinaryExpression(f, t, expr.AsBinaryExpression(), assumeTrue) - case ast.KindPrefixUnaryExpression: - if expr.AsPrefixUnaryExpression().Operator == ast.KindExclamationToken { - return c.narrowType(f, t, expr.AsPrefixUnaryExpression().Operand, !assumeTrue) - } - } - return t -} - -func (c *Checker) narrowTypeByOptionality(f *FlowState, t *Type, expr *ast.Node, assumePresent bool) *Type { - if c.isMatchingReference(f.reference, expr) { - return c.getAdjustedTypeWithFacts(t, core.IfElse(assumePresent, TypeFactsNEUndefinedOrNull, TypeFactsEQUndefinedOrNull)) - } - access := c.getDiscriminantPropertyAccess(f, expr, t) - if access != nil { - return c.narrowTypeByDiscriminant(t, access, func(t *Type) *Type { - return c.getTypeWithFacts(t, core.IfElse(assumePresent, TypeFactsNEUndefinedOrNull, TypeFactsEQUndefinedOrNull)) - }) - } - return t -} - -func (c *Checker) narrowTypeByTruthiness(f *FlowState, t *Type, expr *ast.Node, assumeTrue bool) *Type { - if c.isMatchingReference(f.reference, expr) { - return c.getAdjustedTypeWithFacts(t, core.IfElse(assumeTrue, TypeFactsTruthy, TypeFactsFalsy)) - } - if c.strictNullChecks && assumeTrue && c.optionalChainContainsReference(expr, f.reference) { - t = c.getAdjustedTypeWithFacts(t, TypeFactsNEUndefinedOrNull) - } - access := c.getDiscriminantPropertyAccess(f, expr, t) - if access != nil { - return c.narrowTypeByDiscriminant(t, access, func(t *Type) *Type { - return c.getTypeWithFacts(t, core.IfElse(assumeTrue, TypeFactsTruthy, TypeFactsFalsy)) - }) - } - return t -} - -func (c *Checker) narrowTypeByCallExpression(f *FlowState, t *Type, callExpression *ast.Node, assumeTrue bool) *Type { - if c.hasMatchingArgument(callExpression, f.reference) { - var predicate *TypePredicate - if assumeTrue || !isCallChain(callExpression) { - signature := c.getEffectsSignature(callExpression) - if signature != nil { - predicate = c.getTypePredicateOfSignature(signature) - } - } - if predicate != nil && (predicate.kind == TypePredicateKindThis || predicate.kind == TypePredicateKindIdentifier) { - return c.narrowTypeByTypePredicate(f, t, predicate, callExpression, assumeTrue) - } - } - if c.containsMissingType(t) && ast.IsAccessExpression(f.reference) && ast.IsPropertyAccessExpression(callExpression.Expression()) { - callAccess := callExpression.Expression() - if c.isMatchingReference(f.reference.Expression(), c.getReferenceCandidate(callAccess.Expression())) && ast.IsIdentifier(callAccess.Name()) && callAccess.Name().Text() == "hasOwnProperty" && len(callExpression.Arguments()) == 1 { - argument := callExpression.Arguments()[0] - if accessedName, ok := c.getAccessedPropertyName(f.reference); ok && ast.IsStringLiteralLike(argument) && accessedName == argument.Text() { - return c.getTypeWithFacts(t, core.IfElse(assumeTrue, TypeFactsNEUndefined, TypeFactsEQUndefined)) - } - } - } - return t -} - -func (c *Checker) narrowTypeByBinaryExpression(f *FlowState, t *Type, expr *ast.BinaryExpression, assumeTrue bool) *Type { - switch expr.OperatorToken.Kind { - case ast.KindEqualsToken, ast.KindBarBarEqualsToken, ast.KindAmpersandAmpersandEqualsToken, ast.KindQuestionQuestionEqualsToken: - return c.narrowTypeByTruthiness(f, c.narrowType(f, t, expr.Right, assumeTrue), expr.Left, assumeTrue) - case ast.KindEqualsEqualsToken, ast.KindExclamationEqualsToken, ast.KindEqualsEqualsEqualsToken, ast.KindExclamationEqualsEqualsToken: - operator := expr.OperatorToken.Kind - left := c.getReferenceCandidate(expr.Left) - right := c.getReferenceCandidate(expr.Right) - if left.Kind == ast.KindTypeOfExpression && ast.IsStringLiteralLike(right) { - return c.narrowTypeByTypeof(f, t, left.AsTypeOfExpression(), operator, right, assumeTrue) - } - if right.Kind == ast.KindTypeOfExpression && ast.IsStringLiteralLike(left) { - return c.narrowTypeByTypeof(f, t, right.AsTypeOfExpression(), operator, left, assumeTrue) - } - if c.isMatchingReference(f.reference, left) { - return c.narrowTypeByEquality(t, operator, right, assumeTrue) - } - if c.isMatchingReference(f.reference, right) { - return c.narrowTypeByEquality(t, operator, left, assumeTrue) - } - if c.strictNullChecks { - if c.optionalChainContainsReference(left, f.reference) { - t = c.narrowTypeByOptionalChainContainment(f, t, operator, right, assumeTrue) - } else if c.optionalChainContainsReference(right, f.reference) { - t = c.narrowTypeByOptionalChainContainment(f, t, operator, left, assumeTrue) - } - } - leftAccess := c.getDiscriminantPropertyAccess(f, left, t) - if leftAccess != nil { - return c.narrowTypeByDiscriminantProperty(t, leftAccess, operator, right, assumeTrue) - } - rightAccess := c.getDiscriminantPropertyAccess(f, right, t) - if rightAccess != nil { - return c.narrowTypeByDiscriminantProperty(t, rightAccess, operator, left, assumeTrue) - } - if c.isMatchingConstructorReference(f, left) { - return c.narrowTypeByConstructor(t, operator, right, assumeTrue) - } - if c.isMatchingConstructorReference(f, right) { - return c.narrowTypeByConstructor(t, operator, left, assumeTrue) - } - if ast.IsBooleanLiteral(right) && !ast.IsAccessExpression(left) { - return c.narrowTypeByBooleanComparison(f, t, left, right, operator, assumeTrue) - } - if ast.IsBooleanLiteral(left) && !ast.IsAccessExpression(right) { - return c.narrowTypeByBooleanComparison(f, t, right, left, operator, assumeTrue) - } - case ast.KindInstanceOfKeyword: - return c.narrowTypeByInstanceof(f, t, expr, assumeTrue) - case ast.KindInKeyword: - if ast.IsPrivateIdentifier(expr.Left) { - return c.narrowTypeByPrivateIdentifierInInExpression(f, t, expr, assumeTrue) - } - target := c.getReferenceCandidate(expr.Right) - if c.containsMissingType(t) && ast.IsAccessExpression(f.reference) && c.isMatchingReference(f.reference.Expression(), target) { - leftType := c.getTypeOfExpression(expr.Left) - if isTypeUsableAsPropertyName(leftType) { - if accessedName, ok := c.getAccessedPropertyName(f.reference); ok && accessedName == getPropertyNameFromType(leftType) { - return c.getTypeWithFacts(t, core.IfElse(assumeTrue, TypeFactsNEUndefined, TypeFactsEQUndefined)) - } - } - } - if c.isMatchingReference(f.reference, target) { - leftType := c.getTypeOfExpression(expr.Left) - if isTypeUsableAsPropertyName(leftType) { - return c.narrowTypeByInKeyword(f, t, leftType, assumeTrue) - } - } - case ast.KindCommaToken: - return c.narrowType(f, t, expr.Right, assumeTrue) - case ast.KindAmpersandAmpersandToken: - // Ordinarily we won't see && and || expressions in control flow analysis because the Binder breaks those - // expressions down to individual conditional control flows. However, we may encounter them when analyzing - // aliased conditional expressions. - if assumeTrue { - return c.narrowType(f, c.narrowType(f, t, expr.Left, true /*assumeTrue*/), expr.Right, true /*assumeTrue*/) - } - return c.getUnionType([]*Type{c.narrowType(f, t, expr.Left, false /*assumeTrue*/), c.narrowType(f, t, expr.Right, false /*assumeTrue*/)}) - case ast.KindBarBarToken: - if assumeTrue { - return c.getUnionType([]*Type{c.narrowType(f, t, expr.Left, true /*assumeTrue*/), c.narrowType(f, t, expr.Right, true /*assumeTrue*/)}) - } - return c.narrowType(f, c.narrowType(f, t, expr.Left, false /*assumeTrue*/), expr.Right, false /*assumeTrue*/) - } - return t -} - -func (c *Checker) narrowTypeByEquality(t *Type, operator ast.Kind, value *ast.Node, assumeTrue bool) *Type { - if t.flags&TypeFlagsAny != 0 { - return t - } - if operator == ast.KindExclamationEqualsToken || operator == ast.KindExclamationEqualsEqualsToken { - assumeTrue = !assumeTrue - } - valueType := c.getTypeOfExpression(value) - doubleEquals := operator == ast.KindEqualsEqualsToken || operator == ast.KindExclamationEqualsToken - if valueType.flags&TypeFlagsNullable != 0 { - if !c.strictNullChecks { - return t - } - var facts TypeFacts - switch { - case doubleEquals: - facts = core.IfElse(assumeTrue, TypeFactsEQUndefinedOrNull, TypeFactsNEUndefinedOrNull) - case valueType.flags&TypeFlagsNull != 0: - facts = core.IfElse(assumeTrue, TypeFactsEQNull, TypeFactsNENull) - default: - facts = core.IfElse(assumeTrue, TypeFactsEQUndefined, TypeFactsNEUndefined) - } - return c.getAdjustedTypeWithFacts(t, facts) - } - if assumeTrue { - if !doubleEquals && (t.flags&TypeFlagsUnknown != 0 || someType(t, c.IsEmptyAnonymousObjectType)) { - if valueType.flags&(TypeFlagsPrimitive|TypeFlagsNonPrimitive) != 0 || c.IsEmptyAnonymousObjectType(valueType) { - return valueType - } - if valueType.flags&TypeFlagsObject != 0 { - return c.nonPrimitiveType - } - } - filteredType := c.filterType(t, func(t *Type) bool { - return c.areTypesComparable(t, valueType) || doubleEquals && isCoercibleUnderDoubleEquals(t, valueType) - }) - return c.replacePrimitivesWithLiterals(filteredType, valueType) - } - if isUnitType(valueType) { - return c.filterType(t, func(t *Type) bool { - return !(c.isUnitLikeType(t) && c.areTypesComparable(t, valueType)) - }) - } - return t -} - -func (c *Checker) narrowTypeByTypeof(f *FlowState, t *Type, typeOfExpr *ast.TypeOfExpression, operator ast.Kind, literal *ast.Node, assumeTrue bool) *Type { - // We have '==', '!=', '===', or !==' operator with 'typeof xxx' and string literal operands - if operator == ast.KindExclamationEqualsToken || operator == ast.KindExclamationEqualsEqualsToken { - assumeTrue = !assumeTrue - } - target := c.getReferenceCandidate(typeOfExpr.Expression) - if !c.isMatchingReference(f.reference, target) { - if c.strictNullChecks && c.optionalChainContainsReference(target, f.reference) && assumeTrue == (literal.Text() != "undefined") { - t = c.getAdjustedTypeWithFacts(t, TypeFactsNEUndefinedOrNull) - } - propertyAccess := c.getDiscriminantPropertyAccess(f, target, t) - if propertyAccess != nil { - return c.narrowTypeByDiscriminant(t, propertyAccess, func(t *Type) *Type { - return c.narrowTypeByLiteralExpression(t, literal, assumeTrue) - }) - } - return t - } - return c.narrowTypeByLiteralExpression(t, literal, assumeTrue) -} - -var typeofNEFacts = map[string]TypeFacts{ - "string": TypeFactsTypeofNEString, - "number": TypeFactsTypeofNENumber, - "bigint": TypeFactsTypeofNEBigInt, - "boolean": TypeFactsTypeofNEBoolean, - "symbol": TypeFactsTypeofNESymbol, - "undefined": TypeFactsNEUndefined, - "object": TypeFactsTypeofNEObject, - "function": TypeFactsTypeofNEFunction, -} - -func (c *Checker) narrowTypeByLiteralExpression(t *Type, literal *ast.LiteralExpression, assumeTrue bool) *Type { - if assumeTrue { - return c.narrowTypeByTypeName(t, literal.Text()) - } - facts, ok := typeofNEFacts[literal.Text()] - if !ok { - facts = TypeFactsTypeofNEHostObject - } - return c.getAdjustedTypeWithFacts(t, facts) -} - -func (c *Checker) narrowTypeByTypeName(t *Type, typeName string) *Type { - switch typeName { - case "string": - return c.narrowTypeByTypeFacts(t, c.stringType, TypeFactsTypeofEQString) - case "number": - return c.narrowTypeByTypeFacts(t, c.numberType, TypeFactsTypeofEQNumber) - case "bigint": - return c.narrowTypeByTypeFacts(t, c.bigintType, TypeFactsTypeofEQBigInt) - case "boolean": - return c.narrowTypeByTypeFacts(t, c.booleanType, TypeFactsTypeofEQBoolean) - case "symbol": - return c.narrowTypeByTypeFacts(t, c.esSymbolType, TypeFactsTypeofEQSymbol) - case "object": - if t.flags&TypeFlagsAny != 0 { - return t - } - return c.getUnionType([]*Type{c.narrowTypeByTypeFacts(t, c.nonPrimitiveType, TypeFactsTypeofEQObject), c.narrowTypeByTypeFacts(t, c.nullType, TypeFactsEQNull)}) - case "function": - if t.flags&TypeFlagsAny != 0 { - return t - } - return c.narrowTypeByTypeFacts(t, c.globalFunctionType, TypeFactsTypeofEQFunction) - case "undefined": - return c.narrowTypeByTypeFacts(t, c.undefinedType, TypeFactsEQUndefined) - } - return c.narrowTypeByTypeFacts(t, c.nonPrimitiveType, TypeFactsTypeofEQHostObject) -} - -func (c *Checker) narrowTypeByTypeFacts(t *Type, impliedType *Type, facts TypeFacts) *Type { - return c.mapType(t, func(t *Type) *Type { - switch { - case c.isTypeRelatedTo(t, impliedType, c.strictSubtypeRelation): - if c.hasTypeFacts(t, facts) { - return t - } - return c.neverType - case c.isTypeSubtypeOf(impliedType, t): - return impliedType - case c.hasTypeFacts(t, facts): - return c.getIntersectionType([]*Type{t, impliedType}) - } - return c.neverType - }) -} - -func (c *Checker) narrowTypeByDiscriminantProperty(t *Type, access *ast.Node, operator ast.Kind, value *ast.Node, assumeTrue bool) *Type { - if (operator == ast.KindEqualsEqualsEqualsToken || operator == ast.KindExclamationEqualsEqualsToken) && t.flags&TypeFlagsUnion != 0 { - keyPropertyName := c.getKeyPropertyName(t) - if keyPropertyName != "" { - if accessedName, ok := c.getAccessedPropertyName(access); ok && keyPropertyName == accessedName { - candidate := c.getConstituentTypeForKeyType(t, c.getTypeOfExpression(value)) - if candidate != nil { - if assumeTrue && operator == ast.KindEqualsEqualsEqualsToken || !assumeTrue && operator == ast.KindExclamationEqualsEqualsToken { - return candidate - } - if propType := c.getTypeOfPropertyOfType(candidate, keyPropertyName); propType != nil && isUnitType(propType) { - return c.removeType(t, candidate) - } - return t - } - } - } - } - return c.narrowTypeByDiscriminant(t, access, func(t *Type) *Type { - return c.narrowTypeByEquality(t, operator, value, assumeTrue) - }) -} - -func (c *Checker) narrowTypeByDiscriminant(t *Type, access *ast.Node, narrowType func(t *Type) *Type) *Type { - propName, ok := c.getAccessedPropertyName(access) - if !ok { - return t - } - optionalChain := ast.IsOptionalChain(access) - removeNullable := c.strictNullChecks && (optionalChain || isNonNullAccess(access)) && c.maybeTypeOfKind(t, TypeFlagsNullable) - nonNullType := t - if removeNullable { - nonNullType = c.getTypeWithFacts(t, TypeFactsNEUndefinedOrNull) - } - propType := c.getTypeOfPropertyOfType(nonNullType, propName) - if propType == nil { - return t - } - if removeNullable && optionalChain { - propType = c.getOptionalType(propType, false) - } - narrowedPropType := narrowType(propType) - return c.filterType(t, func(t *Type) bool { - discriminantType := core.OrElse(c.getTypeOfPropertyOrIndexSignatureOfType(t, propName), c.unknownType) - return discriminantType.flags&TypeFlagsNever == 0 && narrowedPropType.flags&TypeFlagsNever == 0 && c.areTypesComparable(narrowedPropType, discriminantType) - }) -} - -func (c *Checker) isMatchingConstructorReference(f *FlowState, expr *ast.Node) bool { - if ast.IsAccessExpression(expr) { - if accessedName, ok := c.getAccessedPropertyName(expr); ok && accessedName == "constructor" && c.isMatchingReference(f.reference, expr.Expression()) { - return true - } - } - return false -} - -func (c *Checker) narrowTypeByConstructor(t *Type, operator ast.Kind, identifier *ast.Node, assumeTrue bool) *Type { - // Do not narrow when checking inequality. - if assumeTrue && operator != ast.KindEqualsEqualsToken && operator != ast.KindEqualsEqualsEqualsToken || !assumeTrue && operator != ast.KindExclamationEqualsToken && operator != ast.KindExclamationEqualsEqualsToken { - return t - } - // Get the type of the constructor identifier expression, if it is not a function then do not narrow. - identifierType := c.getTypeOfExpression(identifier) - if !c.isFunctionType(identifierType) && !c.isConstructorType(identifierType) { - return t - } - // Get the prototype property of the type identifier so we can find out its type. - prototypeProperty := c.getPropertyOfType(identifierType, "prototype") - if prototypeProperty == nil { - return t - } - // Get the type of the prototype, if it is undefined, or the global `Object` or `Function` types then do not narrow. - prototypeType := c.getTypeOfSymbol(prototypeProperty) - var candidate *Type - if !IsTypeAny(prototypeType) { - candidate = prototypeType - } - if candidate == nil || candidate == c.globalObjectType || candidate == c.globalFunctionType { - return t - } - // If the type that is being narrowed is `any` then just return the `candidate` type since every type is a subtype of `any`. - if IsTypeAny(t) { - return candidate - } - // Filter out types that are not considered to be "constructed by" the `candidate` type. - return c.filterType(t, func(t *Type) bool { - return c.isConstructedBy(t, candidate) - }) -} - -func (c *Checker) isConstructedBy(source *Type, target *Type) bool { - // If either the source or target type are a class type then we need to check that they are the same exact type. - // This is because you may have a class `A` that defines some set of properties, and another class `B` - // that defines the same set of properties as class `A`, in that case they are structurally the same - // type, but when you do something like `instanceOfA.constructor === B` it will return false. - if source.flags&TypeFlagsObject != 0 && source.objectFlags&ObjectFlagsClass != 0 || target.flags&TypeFlagsObject != 0 && target.objectFlags&ObjectFlagsClass != 0 { - return source.symbol == target.symbol - } - // For all other types just check that the `source` type is a subtype of the `target` type. - return c.isTypeSubtypeOf(source, target) -} - -func (c *Checker) narrowTypeByBooleanComparison(f *FlowState, t *Type, expr *ast.Node, boolValue *ast.Node, operator ast.Kind, assumeTrue bool) *Type { - assumeTrue = (assumeTrue != (boolValue.Kind == ast.KindTrueKeyword)) != (operator != ast.KindExclamationEqualsEqualsToken && operator != ast.KindExclamationEqualsToken) - return c.narrowType(f, t, expr, assumeTrue) -} - -func (c *Checker) narrowTypeByInstanceof(f *FlowState, t *Type, expr *ast.BinaryExpression, assumeTrue bool) *Type { - left := c.getReferenceCandidate(expr.Left) - if !c.isMatchingReference(f.reference, left) { - if assumeTrue && c.strictNullChecks && c.optionalChainContainsReference(left, f.reference) { - return c.getAdjustedTypeWithFacts(t, TypeFactsNEUndefinedOrNull) - } - return t - } - right := expr.Right - rightType := c.getTypeOfExpression(right) - if !c.isTypeDerivedFrom(rightType, c.globalObjectType) { - return t - } - // if the right-hand side has an object type with a custom `[Symbol.hasInstance]` method, and that method - // has a type predicate, use the type predicate to perform narrowing. This allows normal `object` types to - // participate in `instanceof`, as per Step 2 of https://tc39.es/ecma262/#sec-instanceofoperator. - var predicate *TypePredicate - if signature := c.getEffectsSignature(expr.AsNode()); signature != nil { - predicate = c.getTypePredicateOfSignature(signature) - } - if predicate != nil && predicate.kind == TypePredicateKindIdentifier && predicate.parameterIndex == 0 { - return c.getNarrowedType(t, predicate.t, assumeTrue, true /*checkDerived*/) - } - if !c.isTypeDerivedFrom(rightType, c.globalFunctionType) { - return t - } - instanceType := c.mapType(rightType, c.getInstanceType) - // Don't narrow from `any` if the target type is exactly `Object` or `Function`, and narrow - // in the false branch only if the target is a non-empty object type. - if IsTypeAny(t) && (instanceType == c.globalObjectType || instanceType == c.globalFunctionType) || !assumeTrue && !(instanceType.flags&TypeFlagsObject != 0 && !c.IsEmptyAnonymousObjectType(instanceType)) { - return t - } - return c.getNarrowedType(t, instanceType, assumeTrue, true /*checkDerived*/) -} - -func (c *Checker) getNarrowedType(t *Type, candidate *Type, assumeTrue bool, checkDerived bool) *Type { - if t.flags&TypeFlagsUnion == 0 { - return c.getNarrowedTypeWorker(t, candidate, assumeTrue, checkDerived) - } - key := NarrowedTypeKey{t, candidate, assumeTrue, checkDerived} - if narrowedType, ok := c.narrowedTypes[key]; ok { - return narrowedType - } - narrowedType := c.getNarrowedTypeWorker(t, candidate, assumeTrue, checkDerived) - c.narrowedTypes[key] = narrowedType - return narrowedType -} - -func (c *Checker) getNarrowedTypeWorker(t *Type, candidate *Type, assumeTrue bool, checkDerived bool) *Type { - if !assumeTrue { - if t == candidate { - return c.neverType - } - if checkDerived { - return c.filterType(t, func(t *Type) bool { - return !c.isTypeDerivedFrom(t, candidate) - }) - } - if t.flags&TypeFlagsUnknown != 0 { - t = c.unknownUnionType - } - trueType := c.getNarrowedType(t, candidate, true /*assumeTrue*/, false /*checkDerived*/) - return c.recombineUnknownType(c.filterType(t, func(t *Type) bool { - return !c.isTypeSubsetOf(t, trueType) - })) - } - if t.flags&TypeFlagsAnyOrUnknown != 0 { - return candidate - } - if t == candidate { - return candidate - } - // We first attempt to filter the current type, narrowing constituents as appropriate and removing - // constituents that are unrelated to the candidate. - var keyPropertyName string - if t.flags&TypeFlagsUnion != 0 { - keyPropertyName = c.getKeyPropertyName(t) - } - narrowedType := c.mapType(candidate, func(n *Type) *Type { - // If a discriminant property is available, use that to reduce the type. - matching := t - if keyPropertyName != "" { - if discriminant := c.getTypeOfPropertyOfType(n, keyPropertyName); discriminant != nil { - if constituent := c.getConstituentTypeForKeyType(t, discriminant); constituent != nil { - matching = constituent - } - } - } - // For each constituent t in the current type, if t and c are directly related, pick the most - // specific of the two. When t and c are related in both directions, we prefer c for type predicates - // because that is the asserted type, but t for `instanceof` because generics aren't reflected in - // prototype object types. - var mapType func(*Type) *Type - if checkDerived { - mapType = func(t *Type) *Type { - switch { - case c.isTypeDerivedFrom(t, n): - return t - case c.isTypeDerivedFrom(n, t): - return n - } - return c.neverType - } - } else { - mapType = func(t *Type) *Type { - switch { - case c.isTypeStrictSubtypeOf(t, n): - return t - case c.isTypeStrictSubtypeOf(n, t): - return n - case c.isTypeSubtypeOf(t, n): - return t - case c.isTypeSubtypeOf(n, t): - return n - } - return c.neverType - } - } - directlyRelated := c.mapType(matching, mapType) - if directlyRelated.flags&TypeFlagsNever == 0 { - return directlyRelated - } - // If no constituents are directly related, create intersections for any generic constituents that - // are related by constraint. - var isRelated func(*Type, *Type) bool - if checkDerived { - isRelated = c.isTypeDerivedFrom - } else { - isRelated = c.isTypeSubtypeOf - } - return c.mapType(t, func(t *Type) *Type { - if c.maybeTypeOfKind(t, TypeFlagsInstantiable) { - constraint := c.getBaseConstraintOfType(t) - if constraint == nil || isRelated(n, constraint) { - return c.getIntersectionType([]*Type{t, n}) - } - } - return c.neverType - }) - }) - // If filtering produced a non-empty type, return that. Otherwise, pick the most specific of the two - // based on assignability, or as a last resort produce an intersection. - switch { - case narrowedType.flags&TypeFlagsNever == 0: - return narrowedType - case c.isTypeSubtypeOf(candidate, t): - return candidate - case c.isTypeAssignableTo(t, candidate): - return t - case c.isTypeAssignableTo(candidate, t): - return candidate - } - return c.getIntersectionType([]*Type{t, candidate}) -} - -func (c *Checker) getInstanceType(constructorType *Type) *Type { - prototypePropertyType := c.getTypeOfPropertyOfType(constructorType, "prototype") - if prototypePropertyType != nil && !IsTypeAny(prototypePropertyType) { - return prototypePropertyType - } - constructSignatures := c.getSignaturesOfType(constructorType, SignatureKindConstruct) - if len(constructSignatures) != 0 { - return c.getUnionType(core.Map(constructSignatures, func(signature *Signature) *Type { - return c.getReturnTypeOfSignature(c.getErasedSignature(signature)) - })) - } - // We use the empty object type to indicate we don't know the type of objects created by - // this constructor function. - return c.emptyObjectType -} - -func (c *Checker) narrowTypeByPrivateIdentifierInInExpression(f *FlowState, t *Type, expr *ast.BinaryExpression, assumeTrue bool) *Type { - target := c.getReferenceCandidate(expr.Right) - if !c.isMatchingReference(f.reference, target) { - return t - } - symbol := c.getSymbolForPrivateIdentifierExpression(expr.Left) - if symbol == nil { - return t - } - classSymbol := symbol.Parent - var targetType *Type - if ast.HasStaticModifier(symbol.ValueDeclaration) { - targetType = c.getTypeOfSymbol(classSymbol) - } else { - targetType = c.getDeclaredTypeOfSymbol(classSymbol) - } - return c.getNarrowedType(t, targetType, assumeTrue, true /*checkDerived*/) -} - -func (c *Checker) narrowTypeByInKeyword(f *FlowState, t *Type, nameType *Type, assumeTrue bool) *Type { - name := getPropertyNameFromType(nameType) - isKnownProperty := someType(t, func(t *Type) bool { - return c.isTypePresencePossible(t, name, true /*assumeTrue*/) - }) - if isKnownProperty { - // If the check is for a known property (i.e. a property declared in some constituent of - // the target type), we filter the target type by presence of absence of the property. - return c.filterType(t, func(t *Type) bool { - return c.isTypePresencePossible(t, name, assumeTrue) - }) - } - if assumeTrue { - // If the check is for an unknown property, we intersect the target type with `Record`, - // where X is the name of the property. - recordSymbol := c.getGlobalRecordSymbol() - if recordSymbol != nil { - return c.getIntersectionType([]*Type{t, c.getTypeAliasInstantiation(recordSymbol, []*Type{nameType, c.unknownType}, nil)}) - } - } - return t -} - -func (c *Checker) isTypePresencePossible(t *Type, propName string, assumeTrue bool) bool { - prop := c.getPropertyOfType(t, propName) - if prop != nil { - return prop.Flags&ast.SymbolFlagsOptional != 0 || prop.CheckFlags&ast.CheckFlagsPartial != 0 || assumeTrue - } - return c.getApplicableIndexInfoForName(t, propName) != nil || !assumeTrue -} - -func (c *Checker) narrowTypeByOptionalChainContainment(f *FlowState, t *Type, operator ast.Kind, value *ast.Node, assumeTrue bool) *Type { - // We are in a branch of obj?.foo === value (or any one of the other equality operators). We narrow obj as follows: - // When operator is === and type of value excludes undefined, null and undefined is removed from type of obj in true branch. - // When operator is !== and type of value excludes undefined, null and undefined is removed from type of obj in false branch. - // When operator is == and type of value excludes null and undefined, null and undefined is removed from type of obj in true branch. - // When operator is != and type of value excludes null and undefined, null and undefined is removed from type of obj in false branch. - // When operator is === and type of value is undefined, null and undefined is removed from type of obj in false branch. - // When operator is !== and type of value is undefined, null and undefined is removed from type of obj in true branch. - // When operator is == and type of value is null or undefined, null and undefined is removed from type of obj in false branch. - // When operator is != and type of value is null or undefined, null and undefined is removed from type of obj in true branch. - equalsOperator := operator == ast.KindEqualsEqualsToken || operator == ast.KindEqualsEqualsEqualsToken - var nullableFlags TypeFlags - if operator == ast.KindEqualsEqualsToken || operator == ast.KindExclamationEqualsToken { - nullableFlags = TypeFlagsNullable - } else { - nullableFlags = TypeFlagsUndefined - } - valueType := c.getTypeOfExpression(value) - // Note that we include any and unknown in the exclusion test because their domain includes null and undefined. - removeNullable := equalsOperator != assumeTrue && everyType(valueType, func(t *Type) bool { return t.flags&nullableFlags != 0 }) || - equalsOperator == assumeTrue && everyType(valueType, func(t *Type) bool { return t.flags&(TypeFlagsAnyOrUnknown|nullableFlags) == 0 }) - if removeNullable { - return c.getAdjustedTypeWithFacts(t, TypeFactsNEUndefinedOrNull) - } - return t -} - -func (c *Checker) getTypeAtSwitchClause(f *FlowState, flow *ast.FlowNode) FlowType { - data := flow.Node.AsFlowSwitchClauseData() - expr := ast.SkipParentheses(data.SwitchStatement.Expression()) - flowType := c.getTypeAtFlowNode(f, flow.Antecedent) - t := flowType.t - switch { - case c.isMatchingReference(f.reference, expr): - t = c.narrowTypeBySwitchOnDiscriminant(t, data) - case expr.Kind == ast.KindTypeOfExpression && c.isMatchingReference(f.reference, expr.Expression()): - t = c.narrowTypeBySwitchOnTypeOf(t, data) - case expr.Kind == ast.KindTrueKeyword: - t = c.narrowTypeBySwitchOnTrue(f, t, data) - default: - if c.strictNullChecks { - if c.optionalChainContainsReference(expr, f.reference) { - t = c.narrowTypeBySwitchOptionalChainContainment(t, data, func(t *Type) bool { - return t.flags&(TypeFlagsUndefined|TypeFlagsNever) == 0 - }) - } else if ast.IsTypeOfExpression(expr) && c.optionalChainContainsReference(expr.Expression(), f.reference) { - t = c.narrowTypeBySwitchOptionalChainContainment(t, data, func(t *Type) bool { - return !(t.flags&TypeFlagsNever != 0 || t.flags&TypeFlagsStringLiteral != 0 && getStringLiteralValue(t) == "undefined") - }) - } - } - access := c.getDiscriminantPropertyAccess(f, expr, t) - if access != nil { - t = c.narrowTypeBySwitchOnDiscriminantProperty(t, access, data) - } - } - return c.newFlowType(t, flowType.incomplete) -} - -func (c *Checker) narrowTypeBySwitchOnDiscriminant(t *Type, data *ast.FlowSwitchClauseData) *Type { - // We only narrow if all case expressions specify - // values with unit types, except for the case where - // `type` is unknown. In this instance we map object - // types to the nonPrimitive type and narrow with that. - switchTypes := c.getSwitchClauseTypes(data.SwitchStatement) - if len(switchTypes) == 0 { - return t - } - clauseTypes := switchTypes[data.ClauseStart:data.ClauseEnd] - hasDefaultClause := data.ClauseStart == data.ClauseEnd || slices.Contains(clauseTypes, c.neverType) - if (t.flags&TypeFlagsUnknown != 0) && !hasDefaultClause { - var groundClauseTypes []*Type - for i, s := range clauseTypes { - if s.flags&(TypeFlagsPrimitive|TypeFlagsNonPrimitive) != 0 { - if groundClauseTypes != nil { - groundClauseTypes = append(groundClauseTypes, s) - } - } else if s.flags&TypeFlagsObject != 0 { - if groundClauseTypes == nil { - groundClauseTypes = clauseTypes[:i:i] - } - groundClauseTypes = append(groundClauseTypes, c.nonPrimitiveType) - } else { - return t - } - } - return c.getUnionType(core.IfElse(groundClauseTypes == nil, clauseTypes, groundClauseTypes)) - } - discriminantType := c.getUnionType(clauseTypes) - var caseType *Type - if discriminantType.flags&TypeFlagsNever != 0 { - caseType = c.neverType - } else { - filtered := c.filterType(t, func(t *Type) bool { return c.areTypesComparable(discriminantType, t) }) - caseType = c.replacePrimitivesWithLiterals(filtered, discriminantType) - } - if !hasDefaultClause { - return caseType - } - defaultType := c.filterType(t, func(t *Type) bool { - if !c.isUnitLikeType(t) { - return true - } - u := c.undefinedType - if t.flags&TypeFlagsUndefined == 0 { - u = c.getRegularTypeOfLiteralType(c.extractUnitType(t)) - } - return !slices.Contains(switchTypes, u) - }) - if caseType.flags&TypeFlagsNever != 0 { - return defaultType - } - return c.getUnionType([]*Type{caseType, defaultType}) -} - -func (c *Checker) narrowTypeBySwitchOnTypeOf(t *Type, data *ast.FlowSwitchClauseData) *Type { - witnesses := c.getSwitchClauseTypeOfWitnesses(data.SwitchStatement) - if witnesses == nil { - return t - } - clauses := data.SwitchStatement.AsSwitchStatement().CaseBlock.AsCaseBlock().Clauses.Nodes - // Equal start and end denotes implicit fallthrough; undefined marks explicit default clause. - defaultIndex := core.FindIndex(clauses, func(clause *ast.Node) bool { - return clause.Kind == ast.KindDefaultClause - }) - clauseStart := int(data.ClauseStart) - clauseEnd := int(data.ClauseEnd) - hasDefaultClause := clauseStart == clauseEnd || (defaultIndex >= clauseStart && defaultIndex < clauseEnd) - if hasDefaultClause { - // In the default clause we filter constituents down to those that are not-equal to all handled cases. - notEqualFacts := c.getNotEqualFactsFromTypeofSwitch(clauseStart, clauseEnd, witnesses) - return c.filterType(t, func(t *Type) bool { - return c.getTypeFacts(t, notEqualFacts) == notEqualFacts - }) - } - // In the non-default cause we create a union of the type narrowed by each of the listed cases. - clauseWitnesses := witnesses[clauseStart:clauseEnd] - return c.getUnionType(core.Map(clauseWitnesses, func(text string) *Type { - if text != "" { - return c.narrowTypeByTypeName(t, text) - } - return c.neverType - })) -} - -func (c *Checker) narrowTypeBySwitchOnTrue(f *FlowState, t *Type, data *ast.FlowSwitchClauseData) *Type { - clauses := data.SwitchStatement.AsSwitchStatement().CaseBlock.AsCaseBlock().Clauses.Nodes - defaultIndex := core.FindIndex(clauses, func(clause *ast.Node) bool { - return clause.Kind == ast.KindDefaultClause - }) - clauseStart := int(data.ClauseStart) - clauseEnd := int(data.ClauseEnd) - hasDefaultClause := clauseStart == clauseEnd || (defaultIndex >= clauseStart && defaultIndex < clauseEnd) - // First, narrow away all of the cases that preceded this set of cases. - for i := range clauseStart { - clause := clauses[i] - if clause.Kind == ast.KindCaseClause { - t = c.narrowType(f, t, clause.Expression(), false /*assumeTrue*/) - } - } - // If our current set has a default, then none the other cases were hit either. - // There's no point in narrowing by the other cases in the set, since we can - // get here through other paths. - if hasDefaultClause { - for i := clauseEnd; i < len(clauses); i++ { - clause := clauses[i] - if clause.Kind == ast.KindCaseClause { - t = c.narrowType(f, t, clause.Expression(), false /*assumeTrue*/) - } - } - return t - } - // Now, narrow based on the cases in this set. - return c.getUnionType(core.Map(clauses[clauseStart:clauseEnd], func(clause *ast.Node) *Type { - if clause.Kind == ast.KindCaseClause { - return c.narrowType(f, t, clause.Expression(), true /*assumeTrue*/) - } - return c.neverType - })) -} - -func (c *Checker) narrowTypeBySwitchOptionalChainContainment(t *Type, data *ast.FlowSwitchClauseData, clauseCheck func(t *Type) bool) *Type { - everyClauseChecks := data.ClauseStart != data.ClauseEnd && core.Every(c.getSwitchClauseTypes(data.SwitchStatement)[data.ClauseStart:data.ClauseEnd], clauseCheck) - if everyClauseChecks { - return c.getTypeWithFacts(t, TypeFactsNEUndefinedOrNull) - } - return t -} - -func (c *Checker) narrowTypeBySwitchOnDiscriminantProperty(t *Type, access *ast.Node, data *ast.FlowSwitchClauseData) *Type { - if data.ClauseStart < data.ClauseEnd && t.flags&TypeFlagsUnion != 0 { - accessedName, _ := c.getAccessedPropertyName(access) - if accessedName != "" && c.getKeyPropertyName(t) == accessedName { - clauseTypes := c.getSwitchClauseTypes(data.SwitchStatement)[data.ClauseStart:data.ClauseEnd] - candidate := c.getUnionType(core.Map(clauseTypes, func(s *Type) *Type { - result := c.getConstituentTypeForKeyType(t, s) - if result != nil { - return result - } - return c.unknownType - })) - if candidate != c.unknownType { - return candidate - } - } - } - return c.narrowTypeByDiscriminant(t, access, func(t *Type) *Type { - return c.narrowTypeBySwitchOnDiscriminant(t, data) - }) -} - -func (c *Checker) getTypeAtFlowBranchLabel(f *FlowState, flow *ast.FlowNode, antecedents *ast.FlowList) FlowType { - antecedentStart := len(c.antecedentTypes) - subtypeReduction := false - seenIncomplete := false - var bypassFlow *ast.FlowNode - for list := antecedents; list != nil; list = list.Next { - antecedent := list.Flow - if bypassFlow == nil && antecedent.Flags&ast.FlowFlagsSwitchClause != 0 && antecedent.Node.AsFlowSwitchClauseData().IsEmpty() { - // The antecedent is the bypass branch of a potentially exhaustive switch statement. - bypassFlow = antecedent - continue - } - flowType := c.getTypeAtFlowNode(f, antecedent) - // If the type at a particular antecedent path is the declared type and the - // reference is known to always be assigned (i.e. when declared and initial types - // are the same), there is no reason to process more antecedents since the only - // possible outcome is subtypes that will be removed in the final union type anyway. - if flowType.t == f.declaredType && f.declaredType == f.initialType { - c.antecedentTypes = c.antecedentTypes[:antecedentStart] - return FlowType{t: flowType.t} - } - if !slices.Contains(c.antecedentTypes[antecedentStart:], flowType.t) { - c.antecedentTypes = append(c.antecedentTypes, flowType.t) - } - // If an antecedent type is not a subset of the declared type, we need to perform - // subtype reduction. This happens when a "foreign" type is injected into the control - // flow using the instanceof operator or a user defined type predicate. - if !c.isTypeSubsetOf(flowType.t, f.initialType) { - subtypeReduction = true - } - if flowType.incomplete { - seenIncomplete = true - } - } - if bypassFlow != nil { - flowType := c.getTypeAtFlowNode(f, bypassFlow) - // If the bypass flow contributes a type we haven't seen yet and the switch statement - // isn't exhaustive, process the bypass flow type. Since exhaustiveness checks increase - // the risk of circularities, we only want to perform them when they make a difference. - if flowType.t.flags&TypeFlagsNever == 0 && !slices.Contains(c.antecedentTypes[antecedentStart:], flowType.t) && !c.isExhaustiveSwitchStatement(bypassFlow.Node.AsFlowSwitchClauseData().SwitchStatement) { - if flowType.t == f.declaredType && f.declaredType == f.initialType { - c.antecedentTypes = c.antecedentTypes[:antecedentStart] - return FlowType{t: flowType.t} - } - c.antecedentTypes = append(c.antecedentTypes, flowType.t) - if !c.isTypeSubsetOf(flowType.t, f.initialType) { - subtypeReduction = true - } - if flowType.incomplete { - seenIncomplete = true - } - } - } - result := c.newFlowType(c.getUnionOrEvolvingArrayType(f, c.antecedentTypes[antecedentStart:], core.IfElse(subtypeReduction, UnionReductionSubtype, UnionReductionLiteral)), seenIncomplete) - c.antecedentTypes = c.antecedentTypes[:antecedentStart] - return result -} - -// At flow control branch or loop junctions, if the type along every antecedent code path -// is an evolving array type, we construct a combined evolving array type. Otherwise we -// finalize all evolving array types. -func (c *Checker) getUnionOrEvolvingArrayType(f *FlowState, types []*Type, subtypeReduction UnionReduction) *Type { - if isEvolvingArrayTypeList(types) { - return c.getEvolvingArrayType(c.getUnionType(core.Map(types, c.getElementTypeOfEvolvingArrayType))) - } - result := c.recombineUnknownType(c.getUnionTypeEx(core.SameMap(types, c.finalizeEvolvingArrayType), subtypeReduction, nil, nil)) - if result != f.declaredType && result.flags&f.declaredType.flags&TypeFlagsUnion != 0 && slices.Equal(result.AsUnionType().types, f.declaredType.AsUnionType().types) { - return f.declaredType - } - return result -} - -func (c *Checker) getTypeAtFlowLoopLabel(f *FlowState, flow *ast.FlowNode) FlowType { - if f.refKey == "" { - f.refKey = c.getFlowReferenceKey(f) - } - if f.refKey == "?" { - // No cache key is generated when binding patterns are in unnarrowable situations - return FlowType{t: f.declaredType} - } - key := FlowLoopKey{flowNode: flow, refKey: f.refKey} - // If we have previously computed the control flow type for the reference at - // this flow loop junction, return the cached type. - if cached := c.flowLoopCache[key]; cached != nil { - return FlowType{t: cached} - } - // If this flow loop junction and reference are already being processed, return - // the union of the types computed for each branch so far, marked as incomplete. - // It is possible to see an empty array in cases where loops are nested and the - // back edge of the outer loop reaches an inner loop that is already being analyzed. - // In such cases we restart the analysis of the inner loop, which will then see - // a non-empty in-process array for the outer loop and eventually terminate because - // the first antecedent of a loop junction is always the non-looping control flow - // path that leads to the top. - for _, loopInfo := range c.flowLoopStack { - if loopInfo.key == key && len(loopInfo.types) != 0 { - return c.newFlowType(c.getUnionOrEvolvingArrayType(f, loopInfo.types, UnionReductionLiteral), true /*incomplete*/) - } - } - // Add the flow loop junction and reference to the in-process stack and analyze - // each antecedent code path. - antecedentTypes := make([]*Type, 0, 4) - subtypeReduction := false - var firstAntecedentType FlowType - for list := flow.Antecedents; list != nil; list = list.Next { - var flowType FlowType - if firstAntecedentType.isNil() { - // The first antecedent of a loop junction is always the non-looping control - // flow path that leads to the top. - firstAntecedentType = c.getTypeAtFlowNode(f, list.Flow) - flowType = firstAntecedentType - } else { - // All but the first antecedent are the looping control flow paths that lead - // back to the loop junction. We track these on the flow loop stack. - c.flowLoopStack = append(c.flowLoopStack, FlowLoopInfo{key: key, types: antecedentTypes}) - saveFlowTypeCache := c.flowTypeCache - c.flowTypeCache = nil - flowType = c.getTypeAtFlowNode(f, list.Flow) - c.flowTypeCache = saveFlowTypeCache - c.flowLoopStack = c.flowLoopStack[:len(c.flowLoopStack)-1] - // If we see a value appear in the cache it is a sign that control flow analysis - // was restarted and completed by checkExpressionCached. We can simply pick up - // the resulting type and bail out. - if cached := c.flowLoopCache[key]; cached != nil { - return FlowType{t: cached} - } - } - antecedentTypes = core.AppendIfUnique(antecedentTypes, flowType.t) - // If an antecedent type is not a subset of the declared type, we need to perform - // subtype reduction. This happens when a "foreign" type is injected into the control - // flow using the instanceof operator or a user defined type predicate. - if !c.isTypeSubsetOf(flowType.t, f.initialType) { - subtypeReduction = true - } - // If the type at a particular antecedent path is the declared type there is no - // reason to process more antecedents since the only possible outcome is subtypes - // that will be removed in the final union type anyway. - if flowType.t == f.declaredType { - break - } - } - // The result is incomplete if the first antecedent (the non-looping control flow path) - // is incomplete. - result := c.getUnionOrEvolvingArrayType(f, antecedentTypes, core.IfElse(subtypeReduction, UnionReductionSubtype, UnionReductionLiteral)) - if firstAntecedentType.incomplete { - return c.newFlowType(result, true /*incomplete*/) - } - c.flowLoopCache[key] = result - return FlowType{t: result} -} - -func (c *Checker) getTypeAtFlowArrayMutation(f *FlowState, flow *ast.FlowNode) FlowType { - if f.declaredType == c.autoType || f.declaredType == c.autoArrayType { - node := flow.Node - var expr *ast.Node - if ast.IsCallExpression(node) { - expr = node.Expression().Expression() - } else { - expr = node.AsBinaryExpression().Left.Expression() - } - if c.isMatchingReference(f.reference, c.getReferenceCandidate(expr)) { - flowType := c.getTypeAtFlowNode(f, flow.Antecedent) - if flowType.t.objectFlags&ObjectFlagsEvolvingArray != 0 { - evolvedType := flowType.t - if ast.IsCallExpression(node) { - for _, arg := range node.Arguments() { - evolvedType = c.addEvolvingArrayElementType(evolvedType, arg) - } - } else { - // We must get the context free expression type so as to not recur in an uncached fashion on the LHS (which causes exponential blowup in compile time) - indexType := c.getContextFreeTypeOfExpression(node.AsBinaryExpression().Left.AsElementAccessExpression().ArgumentExpression) - if c.isTypeAssignableToKind(indexType, TypeFlagsNumberLike) { - evolvedType = c.addEvolvingArrayElementType(evolvedType, node.AsBinaryExpression().Right) - } - } - return c.newFlowType(evolvedType, flowType.incomplete) - } - return flowType - } - } - return FlowType{} -} - -func (c *Checker) getDiscriminantPropertyAccess(f *FlowState, expr *ast.Node, computedType *Type) *ast.Node { - // As long as the computed type is a subset of the declared type, we use the full declared type to detect - // a discriminant property. In cases where the computed type isn't a subset, e.g because of a preceding type - // predicate narrowing, we use the actual computed type. - if f.declaredType.flags&TypeFlagsUnion != 0 || computedType.flags&TypeFlagsUnion != 0 { - access := c.getCandidateDiscriminantPropertyAccess(f, expr) - if access != nil { - if name, ok := c.getAccessedPropertyName(access); ok { - t := computedType - if f.declaredType.flags&TypeFlagsUnion != 0 && c.isTypeSubsetOf(computedType, f.declaredType) { - t = f.declaredType - } - if c.isDiscriminantProperty(t, name) { - return access - } - } - } - } - return nil -} - -func (c *Checker) getCandidateDiscriminantPropertyAccess(f *FlowState, expr *ast.Node) *ast.Node { - switch { - case ast.IsBindingPattern(f.reference) || ast.IsFunctionExpressionOrArrowFunction(f.reference) || ast.IsObjectLiteralMethod(f.reference): - // When the reference is a binding pattern or function or arrow expression, we are narrowing a pseudo-reference in - // getNarrowedTypeOfSymbol. An identifier for a destructuring variable declared in the same binding pattern or - // parameter declared in the same parameter list is a candidate. - if ast.IsIdentifier(expr) { - symbol := c.getResolvedSymbol(expr) - declaration := c.getExportSymbolOfValueSymbolIfExported(symbol).ValueDeclaration - if declaration != nil && (ast.IsBindingElement(declaration) || ast.IsParameter(declaration)) && f.reference == declaration.Parent && declaration.Initializer() == nil && !hasDotDotDotToken(declaration) { - return declaration - } - } - case ast.IsAccessExpression(expr): - // An access expression is a candidate if the reference matches the left hand expression. - if c.isMatchingReference(f.reference, expr.Expression()) { - return expr - } - case ast.IsIdentifier(expr): - symbol := c.getResolvedSymbol(expr) - if c.isConstantVariable(symbol) { - declaration := symbol.ValueDeclaration - // Given 'const x = obj.kind', allow 'x' as an alias for 'obj.kind' - if ast.IsVariableDeclaration(declaration) && declaration.Type() == nil { - if initializer := declaration.Initializer(); initializer != nil && ast.IsAccessExpression(initializer) && c.isMatchingReference(f.reference, initializer.Expression()) { - return initializer - } - } - // Given 'const { kind: x } = obj', allow 'x' as an alias for 'obj.kind' - if ast.IsBindingElement(declaration) && declaration.Initializer() == nil { - parent := declaration.Parent.Parent - if ast.IsVariableDeclaration(parent) && parent.Type() == nil { - if initializer := parent.Initializer(); initializer != nil && (ast.IsIdentifier(initializer) || ast.IsAccessExpression(initializer)) && c.isMatchingReference(f.reference, initializer) { - return declaration - } - } - } - } - } - return nil -} - -// An evolving array type tracks the element types that have so far been seen in an -// 'x.push(value)' or 'x[n] = value' operation along the control flow graph. Evolving -// array types are ultimately converted into manifest array types (using getFinalArrayType) -// and never escape the getFlowTypeOfReference function. -func (c *Checker) getEvolvingArrayType(elementType *Type) *Type { - key := CachedTypeKey{kind: CachedTypeKindEvolvingArrayType, typeId: elementType.id} - result := c.cachedTypes[key] - if result == nil { - result = c.newObjectType(ObjectFlagsEvolvingArray, nil) - result.AsEvolvingArrayType().elementType = elementType - c.cachedTypes[key] = result - } - return result -} - -func (c *Checker) getElementTypeOfEvolvingArrayType(t *Type) *Type { - if t.objectFlags&ObjectFlagsEvolvingArray != 0 { - return t.AsEvolvingArrayType().elementType - } - return c.neverType -} - -func isEvolvingArrayTypeList(types []*Type) bool { - hasEvolvingArrayType := false - for _, t := range types { - if t.flags&TypeFlagsNever == 0 { - if t.objectFlags&ObjectFlagsEvolvingArray == 0 { - return false - } - hasEvolvingArrayType = true - } - } - return hasEvolvingArrayType -} - -// Return true if the given node is 'x' in an 'x.length', x.push(value)', 'x.unshift(value)' or -// 'x[n] = value' operation, where 'n' is an expression of type any, undefined, or a number-like type. -func (c *Checker) isEvolvingArrayOperationTarget(node *ast.Node) bool { - root := c.getReferenceRoot(node) - parent := root.Parent - isLengthPushOrUnshift := ast.IsPropertyAccessExpression(parent) && (parent.Name().Text() == "length" || - ast.IsCallExpression(parent.Parent) && ast.IsIdentifier(parent.Name()) && ast.IsPushOrUnshiftIdentifier(parent.Name())) - isElementAssignment := ast.IsElementAccessExpression(parent) && parent.Expression() == root && - ast.IsBinaryExpression(parent.Parent) && parent.Parent.AsBinaryExpression().OperatorToken.Kind == ast.KindEqualsToken && - parent.Parent.AsBinaryExpression().Left == parent && !ast.IsAssignmentTarget(parent.Parent) && - c.isTypeAssignableToKind(c.getTypeOfExpression(parent.AsElementAccessExpression().ArgumentExpression), TypeFlagsNumberLike) - return isLengthPushOrUnshift || isElementAssignment -} - -// When adding evolving array element types we do not perform subtype reduction. Instead, -// we defer subtype reduction until the evolving array type is finalized into a manifest -// array type. -func (c *Checker) addEvolvingArrayElementType(evolvingArrayType *Type, node *ast.Node) *Type { - newElementType := c.getRegularTypeOfObjectLiteral(c.getBaseTypeOfLiteralType(c.getContextFreeTypeOfExpression(node))) - elementType := evolvingArrayType.AsEvolvingArrayType().elementType - if c.isTypeSubsetOf(newElementType, elementType) { - return evolvingArrayType - } - return c.getEvolvingArrayType(c.getUnionType([]*Type{elementType, newElementType})) -} - -func (c *Checker) finalizeEvolvingArrayType(t *Type) *Type { - if t.objectFlags&ObjectFlagsEvolvingArray != 0 { - return c.getFinalArrayType(t.AsEvolvingArrayType()) - } - return t -} - -func (c *Checker) getFinalArrayType(t *EvolvingArrayType) *Type { - if t.finalArrayType == nil { - t.finalArrayType = c.createFinalArrayType(t.elementType) - } - return t.finalArrayType -} - -func (c *Checker) createFinalArrayType(elementType *Type) *Type { - switch { - case elementType.flags&TypeFlagsNever != 0: - return c.autoArrayType - case elementType.flags&TypeFlagsUnion != 0: - return c.createArrayType(c.getUnionTypeEx(elementType.Types(), UnionReductionSubtype, nil, nil)) - } - return c.createArrayType(elementType) -} - -func (c *Checker) reportFlowControlError(node *ast.Node) { - block := ast.FindAncestor(node, ast.IsFunctionOrModuleBlock) - sourceFile := ast.GetSourceFileOfNode(node) - span := scanner.GetRangeOfTokenAtPosition(sourceFile, ast.GetStatementsOfBlock(block).Pos()) - c.diagnostics.Add(ast.NewDiagnostic(sourceFile, span, diagnostics.The_containing_function_or_module_body_is_too_large_for_control_flow_analysis)) -} - -func (c *Checker) isMatchingReference(source *ast.Node, target *ast.Node) bool { - switch target.Kind { - case ast.KindParenthesizedExpression, ast.KindNonNullExpression: - return c.isMatchingReference(source, target.Expression()) - case ast.KindBinaryExpression: - return ast.IsAssignmentExpression(target, false) && c.isMatchingReference(source, target.AsBinaryExpression().Left) || - ast.IsBinaryExpression(target) && target.AsBinaryExpression().OperatorToken.Kind == ast.KindCommaToken && - c.isMatchingReference(source, target.AsBinaryExpression().Right) - } - switch source.Kind { - case ast.KindMetaProperty: - return ast.IsMetaProperty(target) && source.AsMetaProperty().KeywordToken == target.AsMetaProperty().KeywordToken && source.Name().Text() == target.Name().Text() - case ast.KindIdentifier, ast.KindPrivateIdentifier: - if ast.IsThisInTypeQuery(source) { - return target.Kind == ast.KindThisKeyword - } - return ast.IsIdentifier(target) && c.getResolvedSymbol(source) == c.getResolvedSymbol(target) || - (ast.IsVariableDeclaration(target) || ast.IsBindingElement(target)) && c.getExportSymbolOfValueSymbolIfExported(c.getResolvedSymbol(source)) == c.getSymbolOfDeclaration(target) - case ast.KindThisKeyword: - return target.Kind == ast.KindThisKeyword - case ast.KindSuperKeyword: - return target.Kind == ast.KindSuperKeyword - case ast.KindNonNullExpression, ast.KindParenthesizedExpression, ast.KindSatisfiesExpression: - return c.isMatchingReference(source.Expression(), target) - case ast.KindPropertyAccessExpression, ast.KindElementAccessExpression: - if sourcePropertyName, ok := c.getAccessedPropertyName(source); ok { - if ast.IsAccessExpression(target) { - if targetPropertyName, ok := c.getAccessedPropertyName(target); ok { - return targetPropertyName == sourcePropertyName && c.isMatchingReference(source.Expression(), target.Expression()) - } - } - } - if ast.IsElementAccessExpression(source) && ast.IsElementAccessExpression(target) { - sourceArg := source.AsElementAccessExpression().ArgumentExpression - targetArg := target.AsElementAccessExpression().ArgumentExpression - if ast.IsIdentifier(sourceArg) && ast.IsIdentifier(targetArg) { - symbol := c.getResolvedSymbol(sourceArg) - if symbol == c.getResolvedSymbol(targetArg) && (c.isConstantVariable(symbol) || c.isParameterOrMutableLocalVariable(symbol) && !c.isSymbolAssigned(symbol)) { - return c.isMatchingReference(source.Expression(), target.Expression()) - } - } - } - case ast.KindQualifiedName: - if ast.IsAccessExpression(target) { - if targetPropertyName, ok := c.getAccessedPropertyName(target); ok { - return source.AsQualifiedName().Right.Text() == targetPropertyName && c.isMatchingReference(source.AsQualifiedName().Left, target.Expression()) - } - } - case ast.KindBinaryExpression: - return ast.IsBinaryExpression(source) && source.AsBinaryExpression().OperatorToken.Kind == ast.KindCommaToken && c.isMatchingReference(source.AsBinaryExpression().Right, target) - } - return false -} - -// Return the flow cache key for a "dotted name" (i.e. a sequence of identifiers -// separated by dots). The key consists of the id of the symbol referenced by the -// leftmost identifier followed by zero or more property names separated by dots. -// The result is an empty string if the reference isn't a dotted name. -func (c *Checker) getFlowReferenceKey(f *FlowState) string { - var b KeyBuilder - if c.writeFlowCacheKey(&b, f.reference, f.declaredType, f.initialType, f.flowContainer) { - return b.String() - } - return "?" // Reference isn't a dotted name -} - -func (c *Checker) writeFlowCacheKey(b *KeyBuilder, node *ast.Node, declaredType *Type, initialType *Type, flowContainer *ast.Node) bool { - switch node.Kind { - case ast.KindIdentifier: - if !ast.IsThisInTypeQuery(node) { - symbol := c.getResolvedSymbol(node) - if symbol == c.unknownSymbol { - return false - } - b.WriteSymbol(symbol) - } - fallthrough - case ast.KindThisKeyword: - b.WriteByte(':') - b.WriteType(declaredType) - if initialType != declaredType { - b.WriteByte('=') - b.WriteType(initialType) - } - if flowContainer != nil { - b.WriteByte('@') - b.WriteNode(flowContainer) - } - return true - case ast.KindNonNullExpression, ast.KindParenthesizedExpression: - return c.writeFlowCacheKey(b, node.Expression(), declaredType, initialType, flowContainer) - case ast.KindQualifiedName: - if !c.writeFlowCacheKey(b, node.AsQualifiedName().Left, declaredType, initialType, flowContainer) { - return false - } - b.WriteByte('.') - b.WriteString(node.AsQualifiedName().Right.Text()) - return true - case ast.KindPropertyAccessExpression, ast.KindElementAccessExpression: - if propName, ok := c.getAccessedPropertyName(node); ok { - if !c.writeFlowCacheKey(b, node.Expression(), declaredType, initialType, flowContainer) { - return false - } - b.WriteByte('.') - b.WriteString(propName) - return true - } - if ast.IsElementAccessExpression(node) && ast.IsIdentifier(node.AsElementAccessExpression().ArgumentExpression) { - symbol := c.getResolvedSymbol(node.AsElementAccessExpression().ArgumentExpression) - if c.isConstantVariable(symbol) || c.isParameterOrMutableLocalVariable(symbol) && !c.isSymbolAssigned(symbol) { - if !c.writeFlowCacheKey(b, node.Expression(), declaredType, initialType, flowContainer) { - return false - } - b.WriteString(".@") - b.WriteSymbol(symbol) - return true - } - } - case ast.KindObjectBindingPattern, ast.KindArrayBindingPattern, ast.KindFunctionDeclaration, - ast.KindFunctionExpression, ast.KindArrowFunction, ast.KindMethodDeclaration: - b.WriteNode(node) - b.WriteByte('#') - b.WriteType(declaredType) - return true - } - return false -} - -func (c *Checker) getAccessedPropertyName(access *ast.Node) (string, bool) { - if ast.IsPropertyAccessExpression(access) { - return access.Name().Text(), true - } - if ast.IsElementAccessExpression(access) { - return c.tryGetElementAccessExpressionName(access.AsElementAccessExpression()) - } - if ast.IsBindingElement(access) { - return c.getDestructuringPropertyName(access) - } - if ast.IsParameter(access) { - return strconv.Itoa(slices.Index(access.Parent.Parameters(), access)), true - } - return "", false -} - -func (c *Checker) tryGetElementAccessExpressionName(node *ast.ElementAccessExpression) (string, bool) { - switch { - case ast.IsStringOrNumericLiteralLike(node.ArgumentExpression): - return node.ArgumentExpression.Text(), true - case ast.IsEntityNameExpression(node.ArgumentExpression): - return c.tryGetNameFromEntityNameExpression(node.ArgumentExpression) - } - return "", false -} - -func (c *Checker) tryGetNameFromEntityNameExpression(node *ast.Node) (string, bool) { - symbol := c.resolveEntityName(node, ast.SymbolFlagsValue, true /*ignoreErrors*/, false, nil) - if symbol == nil || !(c.isConstantVariable(symbol) || (symbol.Flags&ast.SymbolFlagsEnumMember != 0)) { - return "", false - } - declaration := symbol.ValueDeclaration - if declaration == nil { - return "", false - } - t := c.tryGetTypeFromTypeNode(declaration) - if t != nil { - if name, ok := tryGetNameFromType(t); ok { - return name, true - } - } - if hasOnlyExpressionInitializer(declaration) && c.isBlockScopedNameDeclaredBeforeUse(declaration, node) { - initializer := declaration.Initializer() - if initializer != nil { - var initializerType *Type - if ast.IsBindingPattern(declaration.Parent) { - initializerType = c.getTypeForBindingElement(declaration) - } else { - initializerType = c.getTypeOfExpression(initializer) - } - if initializerType != nil { - return tryGetNameFromType(initializerType) - } - } else if ast.IsEnumMember(declaration) { - return ast.TryGetTextOfPropertyName(declaration.Name()) - } - } - return "", false -} - -func tryGetNameFromType(t *Type) (string, bool) { - switch { - case t.flags&TypeFlagsUniqueESSymbol != 0: - return t.AsUniqueESSymbolType().name, true - case t.flags&TypeFlagsStringOrNumberLiteral != 0: - return evaluator.AnyToString(t.AsLiteralType().value), true - } - return "", false -} - -func (c *Checker) getDestructuringPropertyName(node *ast.Node) (string, bool) { - parent := node.Parent - if ast.IsBindingElement(node) && ast.IsObjectBindingPattern(parent) { - return c.getLiteralPropertyNameText(getBindingElementPropertyName(node)) - } - if ast.IsPropertyAssignment(node) || ast.IsShorthandPropertyAssignment(node) { - return c.getLiteralPropertyNameText(node.Name()) - } - if ast.IsArrayBindingPattern(parent) { - return strconv.Itoa(slices.Index(parent.AsBindingPattern().Elements.Nodes, node)), true - } - if ast.IsArrayLiteralExpression(parent) { - return strconv.Itoa(slices.Index(parent.AsArrayLiteralExpression().Elements.Nodes, node)), true - } - return "", false -} - -func (c *Checker) getLiteralPropertyNameText(name *ast.Node) (string, bool) { - t := c.getLiteralTypeFromPropertyName(name) - if t.flags&(TypeFlagsStringLiteral|TypeFlagsNumberLiteral) != 0 { - return evaluator.AnyToString(t.AsLiteralType().value), true - } - return "", false -} - -func (c *Checker) isConstantReference(node *ast.Node) bool { - switch node.Kind { - case ast.KindThisKeyword: - return true - case ast.KindIdentifier: - if !ast.IsThisInTypeQuery(node) { - symbol := c.getResolvedSymbol(node) - return c.isConstantVariable(symbol) || c.isParameterOrMutableLocalVariable(symbol) && !c.isSymbolAssigned(symbol) || symbol.ValueDeclaration != nil && ast.IsFunctionExpression(symbol.ValueDeclaration) - } - case ast.KindPropertyAccessExpression, ast.KindElementAccessExpression: - // The resolvedSymbol property is initialized by checkPropertyAccess or checkElementAccess before we get here. - if c.isConstantReference(node.Expression()) { - symbol := c.getResolvedSymbolOrNil(node) - if symbol != nil { - return c.isReadonlySymbol(symbol) - } - } - case ast.KindObjectBindingPattern, ast.KindArrayBindingPattern: - rootDeclaration := ast.GetRootDeclaration(node.Parent) - if ast.IsParameter(rootDeclaration) || ast.IsVariableDeclaration(rootDeclaration) && ast.IsCatchClause(rootDeclaration.Parent) { - return !c.isSomeSymbolAssigned(rootDeclaration) - } - return ast.IsVariableDeclaration(rootDeclaration) && c.isVarConstLike(rootDeclaration) - } - return false -} - -func (c *Checker) containsMatchingReference(source *ast.Node, target *ast.Node) bool { - for ast.IsAccessExpression(source) { - source = source.Expression() - if c.isMatchingReference(source, target) { - return true - } - } - return false -} - -func (c *Checker) optionalChainContainsReference(source *ast.Node, target *ast.Node) bool { - for ast.IsOptionalChain(source) { - source = source.Expression() - if c.isMatchingReference(source, target) { - return true - } - } - return false -} - -func (c *Checker) getReferenceCandidate(node *ast.Node) *ast.Node { - switch node.Kind { - case ast.KindParenthesizedExpression: - return c.getReferenceCandidate(node.Expression()) - case ast.KindBinaryExpression: - switch node.AsBinaryExpression().OperatorToken.Kind { - case ast.KindEqualsToken, ast.KindBarBarEqualsToken, ast.KindAmpersandAmpersandEqualsToken, ast.KindQuestionQuestionEqualsToken: - return c.getReferenceCandidate(node.AsBinaryExpression().Left) - case ast.KindCommaToken: - return c.getReferenceCandidate(node.AsBinaryExpression().Right) - } - } - return node -} - -func (c *Checker) getReferenceRoot(node *ast.Node) *ast.Node { - parent := node.Parent - if ast.IsParenthesizedExpression(parent) || - ast.IsBinaryExpression(parent) && parent.AsBinaryExpression().OperatorToken.Kind == ast.KindEqualsToken && parent.AsBinaryExpression().Left == node || - ast.IsBinaryExpression(parent) && parent.AsBinaryExpression().OperatorToken.Kind == ast.KindCommaToken && parent.AsBinaryExpression().Right == node { - return c.getReferenceRoot(parent) - } - return node -} - -func (c *Checker) hasMatchingArgument(expression *ast.Node, reference *ast.Node) bool { - for _, argument := range expression.Arguments() { - if c.isOrContainsMatchingReference(reference, argument) || c.optionalChainContainsReference(argument, reference) { - return true - } - } - if ast.IsPropertyAccessExpression(expression.Expression()) && c.isOrContainsMatchingReference(reference, expression.Expression().Expression()) { - return true - } - return false -} - -func (c *Checker) isOrContainsMatchingReference(source *ast.Node, target *ast.Node) bool { - return c.isMatchingReference(source, target) || c.containsMatchingReference(source, target) -} - -// Return a new type in which occurrences of the string, number and bigint primitives and placeholder template -// literal types in typeWithPrimitives have been replaced with occurrences of compatible and more specific types -// from typeWithLiterals. This is essentially a limited form of intersection between the two types. We avoid a -// true intersection because it is more costly and, when applied to union types, generates a large number of -// types we don't actually care about. -func (c *Checker) replacePrimitivesWithLiterals(typeWithPrimitives *Type, typeWithLiterals *Type) *Type { - if c.maybeTypeOfKind(typeWithPrimitives, TypeFlagsString|TypeFlagsTemplateLiteral|TypeFlagsNumber|TypeFlagsBigInt) && - c.maybeTypeOfKind(typeWithLiterals, TypeFlagsStringLiteral|TypeFlagsTemplateLiteral|TypeFlagsStringMapping|TypeFlagsNumberLiteral|TypeFlagsBigIntLiteral) { - return c.mapType(typeWithPrimitives, func(t *Type) *Type { - switch { - case t.flags&TypeFlagsString != 0: - return c.extractTypesOfKind(typeWithLiterals, TypeFlagsString|TypeFlagsStringLiteral|TypeFlagsTemplateLiteral|TypeFlagsStringMapping) - case c.isPatternLiteralType(t) && !c.maybeTypeOfKind(typeWithLiterals, TypeFlagsString|TypeFlagsTemplateLiteral|TypeFlagsStringMapping): - return c.extractTypesOfKind(typeWithLiterals, TypeFlagsStringLiteral) - case t.flags&TypeFlagsNumber != 0: - return c.extractTypesOfKind(typeWithLiterals, TypeFlagsNumber|TypeFlagsNumberLiteral) - case t.flags&TypeFlagsBigInt != 0: - return c.extractTypesOfKind(typeWithLiterals, TypeFlagsBigInt|TypeFlagsBigIntLiteral) - default: - return t - } - }) - } - return typeWithPrimitives -} - -func isCoercibleUnderDoubleEquals(source *Type, target *Type) bool { - return source.flags&(TypeFlagsNumber|TypeFlagsString|TypeFlagsBooleanLiteral) != 0 && - target.flags&(TypeFlagsNumber|TypeFlagsString|TypeFlagsBoolean) != 0 -} - -func (c *Checker) isExhaustiveSwitchStatement(node *ast.Node) bool { - links := c.switchStatementLinks.Get(node) - if links.exhaustiveState == ExhaustiveStateUnknown { - // Indicate resolution is in process - links.exhaustiveState = ExhaustiveStateComputing - isExhaustive := c.computeExhaustiveSwitchStatement(node) - if links.exhaustiveState == ExhaustiveStateComputing { - links.exhaustiveState = core.IfElse(isExhaustive, ExhaustiveStateTrue, ExhaustiveStateFalse) - } - } else if links.exhaustiveState == ExhaustiveStateComputing { - // Resolve circularity to false - links.exhaustiveState = ExhaustiveStateFalse - } - return links.exhaustiveState == ExhaustiveStateTrue -} - -func (c *Checker) computeExhaustiveSwitchStatement(node *ast.Node) bool { - if ast.IsTypeOfExpression(node.Expression()) { - witnesses := c.getSwitchClauseTypeOfWitnesses(node) - if witnesses == nil { - return false - } - operandConstraint := c.getBaseConstraintOrType(c.checkExpressionCached(node.Expression().Expression())) - // Get the not-equal flags for all handled cases. - notEqualFacts := c.getNotEqualFactsFromTypeofSwitch(0, 0, witnesses) - if operandConstraint.flags&TypeFlagsAnyOrUnknown != 0 { - // We special case the top types to be exhaustive when all cases are handled. - return TypeFactsAllTypeofNE¬EqualFacts == TypeFactsAllTypeofNE - } - // A missing not-equal flag indicates that the type wasn't handled by some case. - return !someType(operandConstraint, func(t *Type) bool { - return c.getTypeFacts(t, notEqualFacts) == notEqualFacts - }) - } - t := c.getBaseConstraintOrType(c.checkExpressionCached(node.Expression())) - if !isLiteralType(t) { - return false - } - switchTypes := c.getSwitchClauseTypes(node) - if len(switchTypes) == 0 || core.Some(switchTypes, isNeitherUnitTypeNorNever) { - return false - } - return c.eachTypeContainedIn(c.mapType(t, c.getRegularTypeOfLiteralType), switchTypes) -} - -func (c *Checker) eachTypeContainedIn(source *Type, types []*Type) bool { - if source.flags&TypeFlagsUnion != 0 { - return !core.Some(source.AsUnionType().types, func(t *Type) bool { - return !slices.Contains(types, t) - }) - } - return slices.Contains(types, source) -} - -// Get the type names from all cases in a switch on `typeof`. The default clause and/or duplicate type names are -// represented as empty strings. Return nil if one or more case clause expressions are not string literals. -func (c *Checker) getSwitchClauseTypeOfWitnesses(node *ast.Node) []string { - links := c.switchStatementLinks.Get(node) - if !links.witnessesComputed { - clauses := node.AsSwitchStatement().CaseBlock.AsCaseBlock().Clauses.Nodes - witnesses := make([]string, len(clauses)) - for i, clause := range clauses { - if clause.Kind == ast.KindCaseClause { - var text string - if ast.IsStringLiteralLike(clause.Expression()) { - text = clause.Expression().Text() - } - if text == "" { - witnesses = nil - break - } - if !slices.Contains(witnesses, text) { - witnesses[i] = text - } - } - } - links.witnesses = witnesses - links.witnessesComputed = true - } - return links.witnesses -} - -// Return the combined not-equal type facts for all cases except those between the start and end indices. -func (c *Checker) getNotEqualFactsFromTypeofSwitch(start int, end int, witnesses []string) TypeFacts { - var facts TypeFacts = TypeFactsNone - for i, witness := range witnesses { - if (i < start || i >= end) && witness != "" { - f, ok := typeofNEFacts[witness] - if !ok { - f = TypeFactsTypeofNEHostObject - } - facts |= f - } - } - return facts -} - -func (c *Checker) getSwitchClauseTypes(node *ast.Node) []*Type { - links := c.switchStatementLinks.Get(node) - if !links.switchTypesComputed { - clauses := node.AsSwitchStatement().CaseBlock.AsCaseBlock().Clauses.Nodes - types := make([]*Type, len(clauses)) - for i, clause := range clauses { - types[i] = c.getTypeOfSwitchClause(clause) - } - links.switchTypes = types - links.switchTypesComputed = true - } - return links.switchTypes -} - -func (c *Checker) getTypeOfSwitchClause(clause *ast.Node) *Type { - if clause.Kind == ast.KindCaseClause { - return c.getRegularTypeOfLiteralType(c.getTypeOfExpression(clause.Expression())) - } - return c.neverType -} - -func (c *Checker) getEffectsSignature(node *ast.Node) *Signature { - links := c.signatureLinks.Get(node) - signature := links.effectsSignature - if signature == nil { - // A call expression parented by an expression statement is a potential assertion. Other call - // expressions are potential type predicate function calls. In order to avoid triggering - // circularities in control flow analysis, we use getTypeOfDottedName when resolving the call - // target expression of an assertion. - var funcType *Type - if ast.IsBinaryExpression(node) { - rightType := c.checkNonNullExpression(node.AsBinaryExpression().Right) - funcType = c.getSymbolHasInstanceMethodOfObjectType(rightType) - } else if ast.IsExpressionStatement(node.Parent) { - funcType = c.getTypeOfDottedName(node.Expression(), nil /*diagnostic*/) - } else if node.Expression().Kind != ast.KindSuperKeyword { - if ast.IsOptionalChain(node) { - funcType = c.checkNonNullType(c.getOptionalExpressionType(c.checkExpression(node.Expression()), node.Expression()), node.Expression()) - } else { - funcType = c.checkNonNullExpression(node.Expression()) - } - } - var apparentType *Type - if funcType != nil { - apparentType = c.getApparentType(funcType) - } - signatures := c.getSignaturesOfType(core.OrElse(apparentType, c.unknownType), SignatureKindCall) - switch { - case len(signatures) == 1 && len(signatures[0].typeParameters) == 0: - signature = signatures[0] - case core.Some(signatures, c.hasTypePredicateOrNeverReturnType): - signature = c.getResolvedSignature(node, nil, CheckModeNormal) - } - if !(signature != nil && c.hasTypePredicateOrNeverReturnType(signature)) { - signature = c.unknownSignature - } - links.effectsSignature = signature - } - if signature == c.unknownSignature { - return nil - } - return signature -} - -/** - * Get the type of the `[Symbol.hasInstance]` method of an object type. - */ -func (c *Checker) getSymbolHasInstanceMethodOfObjectType(t *Type) *Type { - hasInstancePropertyName := c.getPropertyNameForKnownSymbolName("hasInstance") - if c.allTypesAssignableToKind(t, TypeFlagsNonPrimitive) { - hasInstanceProperty := c.getPropertyOfType(t, hasInstancePropertyName) - if hasInstanceProperty != nil { - hasInstancePropertyType := c.getTypeOfSymbol(hasInstanceProperty) - if hasInstancePropertyType != nil && len(c.getSignaturesOfType(hasInstancePropertyType, SignatureKindCall)) != 0 { - return hasInstancePropertyType - } - } - } - return nil -} - -func (c *Checker) getPropertyNameForKnownSymbolName(symbolName string) string { - ctorType := c.getGlobalESSymbolConstructorSymbolOrNil() - if ctorType != nil { - uniqueType := c.getTypeOfPropertyOfType(c.getTypeOfSymbol(ctorType), symbolName) - if uniqueType != nil && isTypeUsableAsPropertyName(uniqueType) { - return getPropertyNameFromType(uniqueType) - } - } - return ast.InternalSymbolNamePrefix + "@" + symbolName -} - -// We require the dotted function name in an assertion expression to be comprised of identifiers -// that reference function, method, class or value module symbols; or variable, property or -// parameter symbols with declarations that have explicit type annotations. Such references are -// resolvable with no possibility of triggering circularities in control flow analysis. -func (c *Checker) getTypeOfDottedName(node *ast.Node, diagnostic *ast.Diagnostic) *Type { - if node.Flags&ast.NodeFlagsInWithStatement == 0 { - switch node.Kind { - case ast.KindIdentifier: - symbol := c.getExportSymbolOfValueSymbolIfExported(c.getResolvedSymbol(node)) - return c.getExplicitTypeOfSymbol(symbol, diagnostic) - case ast.KindThisKeyword: - return c.getExplicitThisType(node) - case ast.KindSuperKeyword: - return c.checkSuperExpression(node) - case ast.KindPropertyAccessExpression: - t := c.getTypeOfDottedName(node.AsPropertyAccessExpression().Expression, diagnostic) - if t != nil { - name := node.Name() - var prop *ast.Symbol - if ast.IsPrivateIdentifier(name) { - if t.symbol != nil { - prop = c.getPropertyOfType(t, binder.GetSymbolNameForPrivateIdentifier(t.symbol, name.Text())) - } - } else { - prop = c.getPropertyOfType(t, name.Text()) - } - if prop != nil { - return c.getExplicitTypeOfSymbol(prop, diagnostic) - } - } - case ast.KindParenthesizedExpression: - return c.getTypeOfDottedName(node.AsParenthesizedExpression().Expression, diagnostic) - } - } - return nil -} - -func (c *Checker) getExplicitTypeOfSymbol(symbol *ast.Symbol, diagnostic *ast.Diagnostic) *Type { - symbol = c.resolveSymbol(symbol) - if symbol.Flags&(ast.SymbolFlagsFunction|ast.SymbolFlagsMethod|ast.SymbolFlagsClass|ast.SymbolFlagsValueModule) != 0 { - return c.getTypeOfSymbol(symbol) - } - if symbol.Flags&(ast.SymbolFlagsVariable|ast.SymbolFlagsProperty) != 0 { - if symbol.CheckFlags&ast.CheckFlagsMapped != 0 { - origin := c.mappedSymbolLinks.Get(symbol).syntheticOrigin - if origin != nil && c.getExplicitTypeOfSymbol(origin, diagnostic) != nil { - return c.getTypeOfSymbol(symbol) - } - } - declaration := symbol.ValueDeclaration - if declaration != nil { - if c.isDeclarationWithExplicitTypeAnnotation(declaration) { - return c.getTypeOfSymbol(symbol) - } - if ast.IsVariableDeclaration(declaration) && ast.IsForOfStatement(declaration.Parent.Parent) { - statement := declaration.Parent.Parent - expressionType := c.getTypeOfDottedName(statement.Expression(), nil /*diagnostic*/) - if expressionType != nil { - var use IterationUse - if statement.AsForInOrOfStatement().AwaitModifier != nil { - use = IterationUseForAwaitOf - } else { - use = IterationUseForOf - } - return c.checkIteratedTypeOrElementType(use, expressionType, c.undefinedType, nil /*errorNode*/) - } - } - if diagnostic != nil { - diagnostic.AddRelatedInfo(createDiagnosticForNode(declaration, diagnostics.X_0_needs_an_explicit_type_annotation, c.symbolToString(symbol))) - } - } - } - return nil -} - -func (c *Checker) isDeclarationWithExplicitTypeAnnotation(node *ast.Node) bool { - return (ast.IsVariableDeclaration(node) || ast.IsPropertyDeclaration(node) || ast.IsPropertySignatureDeclaration(node) || ast.IsParameter(node)) && node.Type() != nil || - c.isExpandoPropertyFunctionWithReturnTypeAnnotation(node) -} - -func (c *Checker) isExpandoPropertyFunctionWithReturnTypeAnnotation(node *ast.Node) bool { - if ast.IsBinaryExpression(node) { - if expr := node.AsBinaryExpression().Right; ast.IsFunctionLike(expr) && expr.Type() != nil { - return true - } - } - return false -} - -func (c *Checker) hasTypePredicateOrNeverReturnType(sig *Signature) bool { - return c.getTypePredicateOfSignature(sig) != nil || sig.declaration != nil && core.OrElse(c.getReturnTypeFromAnnotation(sig.declaration), c.unknownType).flags&TypeFlagsNever != 0 -} - -func (c *Checker) getExplicitThisType(node *ast.Node) *Type { - container := ast.GetThisContainer(node, false /*includeArrowFunctions*/, false /*includeClassComputedPropertyName*/) - if ast.IsFunctionLike(container) { - signature := c.getSignatureFromDeclaration(container) - if signature.thisParameter != nil { - return c.getExplicitTypeOfSymbol(signature.thisParameter, nil) - } - } - if container.Parent != nil && ast.IsClassLike(container.Parent) { - symbol := c.getSymbolOfDeclaration(container.Parent) - if ast.IsStatic(container) { - return c.getTypeOfSymbol(symbol) - } else { - return c.getDeclaredTypeOfSymbol(symbol).AsInterfaceType().thisType - } - } - return nil -} - -func (c *Checker) getInitialType(node *ast.Node) *Type { - switch node.Kind { - case ast.KindVariableDeclaration: - return c.getInitialTypeOfVariableDeclaration(node) - case ast.KindBindingElement: - return c.getInitialTypeOfBindingElement(node) - } - panic("Unhandled case in getInitialType") -} - -func (c *Checker) getInitialTypeOfVariableDeclaration(node *ast.Node) *Type { - if node.Initializer() != nil { - return c.getTypeOfInitializer(node.Initializer()) - } - if ast.IsForInStatement(node.Parent.Parent) { - return c.stringType - } - if ast.IsForOfStatement(node.Parent.Parent) { - t := c.checkRightHandSideOfForOf(node.Parent.Parent) - if t != nil { - return t - } - } - return c.errorType -} - -func (c *Checker) getTypeOfInitializer(node *ast.Node) *Type { - // Return the cached type if one is available. If the type of the variable was inferred - // from its initializer, we'll already have cached the type. Otherwise we compute it now - // without caching such that transient types are reflected. - if c.typeNodeLinks.Has(node) { - t := c.typeNodeLinks.Get(node).resolvedType - if t != nil { - return t - } - } - return c.getTypeOfExpression(node) -} - -func (c *Checker) getInitialTypeOfBindingElement(node *ast.Node) *Type { - pattern := node.Parent - parentType := c.getInitialType(pattern.Parent) - var t *Type - switch { - case ast.IsObjectBindingPattern(pattern): - t = c.getTypeOfDestructuredProperty(parentType, getBindingElementPropertyName(node)) - case !hasDotDotDotToken(node): - t = c.getTypeOfDestructuredArrayElement(parentType, slices.Index(pattern.AsBindingPattern().Elements.Nodes, node)) - default: - t = c.getTypeOfDestructuredSpreadExpression(parentType) - } - return c.getTypeWithDefault(t, node.Initializer()) -} - -func (c *Checker) getAssignedType(node *ast.Node) *Type { - parent := node.Parent - switch parent.Kind { - case ast.KindForInStatement: - return c.stringType - case ast.KindForOfStatement: - t := c.checkRightHandSideOfForOf(parent) - if t != nil { - return t - } - case ast.KindBinaryExpression: - return c.getAssignedTypeOfBinaryExpression(parent) - case ast.KindDeleteExpression: - return c.undefinedType - case ast.KindArrayLiteralExpression: - return c.getAssignedTypeOfArrayLiteralElement(parent, node) - case ast.KindSpreadElement: - return c.getAssignedTypeOfSpreadExpression(parent) - case ast.KindPropertyAssignment: - return c.getAssignedTypeOfPropertyAssignment(parent) - case ast.KindShorthandPropertyAssignment: - return c.getAssignedTypeOfShorthandPropertyAssignment(parent) - } - return c.errorType -} - -func (c *Checker) getAssignedTypeOfBinaryExpression(node *ast.Node) *Type { - isDestructuringDefaultAssignment := ast.IsArrayLiteralExpression(node.Parent) && c.isDestructuringAssignmentTarget(node.Parent) || - ast.IsPropertyAssignment(node.Parent) && c.isDestructuringAssignmentTarget(node.Parent.Parent) - if isDestructuringDefaultAssignment { - return c.getTypeWithDefault(c.getAssignedType(node), node.AsBinaryExpression().Right) - } - return c.getTypeOfExpression(node.AsBinaryExpression().Right) -} - -func (c *Checker) getAssignedTypeOfArrayLiteralElement(node *ast.Node, element *ast.Node) *Type { - return c.getTypeOfDestructuredArrayElement(c.getAssignedType(node), slices.Index(node.AsArrayLiteralExpression().Elements.Nodes, element)) -} - -func (c *Checker) getTypeOfDestructuredArrayElement(t *Type, index int) *Type { - if everyType(t, c.isTupleLikeType) { - if elementType := c.getTupleElementType(t, index); elementType != nil { - return elementType - } - } - if elementType := c.checkIteratedTypeOrElementType(IterationUseDestructuring, t, c.undefinedType, nil /*errorNode*/); elementType != nil { - return c.includeUndefinedInIndexSignature(elementType) - } - return c.errorType -} - -func (c *Checker) includeUndefinedInIndexSignature(t *Type) *Type { - if c.compilerOptions.NoUncheckedIndexedAccess == core.TSTrue { - return c.getUnionType([]*Type{t, c.missingType}) - } - return t -} - -func (c *Checker) getAssignedTypeOfSpreadExpression(node *ast.Node) *Type { - return c.getTypeOfDestructuredSpreadExpression(c.getAssignedType(node.Parent)) -} - -func (c *Checker) getTypeOfDestructuredSpreadExpression(t *Type) *Type { - elementType := c.checkIteratedTypeOrElementType(IterationUseDestructuring, t, c.undefinedType, nil /*errorNode*/) - if elementType == nil { - elementType = c.errorType - } - return c.createArrayType(elementType) -} - -func (c *Checker) getAssignedTypeOfPropertyAssignment(node *ast.Node) *Type { - return c.getTypeOfDestructuredProperty(c.getAssignedType(node.Parent), node.Name()) -} - -func (c *Checker) getTypeOfDestructuredProperty(t *Type, name *ast.Node) *Type { - nameType := c.getLiteralTypeFromPropertyName(name) - if !isTypeUsableAsPropertyName(nameType) { - return c.errorType - } - text := getPropertyNameFromType(nameType) - if propType := c.getTypeOfPropertyOfType(t, text); propType != nil { - return propType - } - if indexInfo := c.getApplicableIndexInfoForName(t, text); indexInfo != nil { - return c.includeUndefinedInIndexSignature(indexInfo.valueType) - } - return c.errorType -} - -func (c *Checker) getAssignedTypeOfShorthandPropertyAssignment(node *ast.Node) *Type { - return c.getTypeWithDefault(c.getAssignedTypeOfPropertyAssignment(node), node.AsShorthandPropertyAssignment().ObjectAssignmentInitializer) -} - -func (c *Checker) isDestructuringAssignmentTarget(parent *ast.Node) bool { - return ast.IsBinaryExpression(parent.Parent) && parent.Parent.AsBinaryExpression().Left == parent || - ast.IsForOfStatement(parent.Parent) && parent.Parent.Initializer() == parent -} - -func (c *Checker) getTypeWithDefault(t *Type, defaultExpression *ast.Node) *Type { - if defaultExpression != nil { - return c.getUnionType([]*Type{c.getNonUndefinedType(t), c.getTypeOfExpression(defaultExpression)}) - } - return t -} - -// Remove those constituent types of declaredType to which no constituent type of assignedType is assignable. -// For example, when a variable of type number | string | boolean is assigned a value of type number | boolean, -// we remove type string. -func (c *Checker) getAssignmentReducedType(declaredType *Type, assignedType *Type) *Type { - if declaredType == assignedType { - return declaredType - } - if assignedType.flags&TypeFlagsNever != 0 { - return assignedType - } - key := AssignmentReducedKey{id1: declaredType.id, id2: assignedType.id} - result := c.assignmentReducedTypes[key] - if result == nil { - result = c.getAssignmentReducedTypeWorker(declaredType, assignedType) - c.assignmentReducedTypes[key] = result - } - return result -} - -func (c *Checker) getAssignmentReducedTypeWorker(declaredType *Type, assignedType *Type) *Type { - filteredType := c.filterType(declaredType, func(t *Type) bool { - return c.typeMaybeAssignableTo(assignedType, t) - }) - // Ensure that we narrow to fresh types if the assignment is a fresh boolean literal type. - reducedType := filteredType - if assignedType.flags&TypeFlagsBooleanLiteral != 0 && isFreshLiteralType(assignedType) { - reducedType = c.mapType(filteredType, c.getFreshTypeOfLiteralType) - } - // Our crude heuristic produces an invalid result in some cases: see GH#26130. - // For now, when that happens, we give up and don't narrow at all. (This also - // means we'll never narrow for erroneous assignments where the assigned type - // is not assignable to the declared type.) - if c.isTypeAssignableTo(assignedType, reducedType) { - return reducedType - } - return declaredType -} - -func (c *Checker) typeMaybeAssignableTo(source *Type, target *Type) bool { - if source.flags&TypeFlagsUnion == 0 { - return c.isTypeAssignableTo(source, target) - } - for _, t := range source.AsUnionType().types { - if c.isTypeAssignableTo(t, target) { - return true - } - } - return false -} - -func (c *Checker) getTypePredicateArgument(predicate *TypePredicate, callExpression *ast.Node) *ast.Node { - if predicate.kind == TypePredicateKindIdentifier || predicate.kind == TypePredicateKindAssertsIdentifier { - arguments := callExpression.Arguments() - if int(predicate.parameterIndex) < len(arguments) { - return arguments[predicate.parameterIndex] - } - } else { - invokedExpression := ast.SkipParentheses(callExpression.Expression()) - if ast.IsAccessExpression(invokedExpression) { - return ast.SkipParentheses(invokedExpression.Expression()) - } - } - return nil -} - -func (c *Checker) getFlowTypeInConstructor(symbol *ast.Symbol, constructor *ast.Node) *Type { - var accessName *ast.Node - if strings.HasPrefix(symbol.Name, ast.InternalSymbolNamePrefix+"#") { - accessName = c.factory.NewPrivateIdentifier(symbol.Name[strings.Index(symbol.Name, "@")+1:]) - } else { - accessName = c.factory.NewIdentifier(symbol.Name) - } - reference := c.factory.NewPropertyAccessExpression(c.factory.NewKeywordExpression(ast.KindThisKeyword), nil, accessName, ast.NodeFlagsNone) - reference.Expression().Parent = reference - reference.Parent = constructor - reference.FlowNodeData().FlowNode = constructor.AsConstructorDeclaration().ReturnFlowNode - flowType := c.getFlowTypeOfProperty(reference, symbol) - if c.noImplicitAny && (flowType == c.autoType || flowType == c.autoArrayType) { - c.error(symbol.ValueDeclaration, diagnostics.Member_0_implicitly_has_an_1_type, c.symbolToString(symbol), c.TypeToString(flowType)) - } - // We don't infer a type if assignments are only null or undefined. - if everyType(flowType, c.IsNullableType) { - return nil - } - return c.convertAutoToAny(flowType) -} - -func (c *Checker) getFlowTypeInStaticBlocks(symbol *ast.Symbol, staticBlocks []*ast.Node) *Type { - var accessName *ast.Node - if strings.HasPrefix(symbol.Name, ast.InternalSymbolNamePrefix+"#") { - accessName = c.factory.NewPrivateIdentifier(symbol.Name[strings.Index(symbol.Name, "@")+1:]) - } else { - accessName = c.factory.NewIdentifier(symbol.Name) - } - for _, staticBlock := range staticBlocks { - reference := c.factory.NewPropertyAccessExpression(c.factory.NewKeywordExpression(ast.KindThisKeyword), nil, accessName, ast.NodeFlagsNone) - reference.Expression().Parent = reference - reference.Parent = staticBlock - reference.FlowNodeData().FlowNode = staticBlock.AsClassStaticBlockDeclaration().ReturnFlowNode - flowType := c.getFlowTypeOfProperty(reference, symbol) - if c.noImplicitAny && (flowType == c.autoType || flowType == c.autoArrayType) { - c.error(symbol.ValueDeclaration, diagnostics.Member_0_implicitly_has_an_1_type, c.symbolToString(symbol), c.TypeToString(flowType)) - } - // We don't infer a type if assignments are only null or undefined. - if everyType(flowType, c.IsNullableType) { - continue - } - return c.convertAutoToAny(flowType) - } - return nil -} - -func (c *Checker) isReachableFlowNode(flow *ast.FlowNode) bool { - f := c.getFlowState() - result := c.isReachableFlowNodeWorker(f, flow, false /*noCacheCheck*/) - c.putFlowState(f) - c.lastFlowNode = flow - c.lastFlowNodeReachable = result - return result -} - -func (c *Checker) isReachableFlowNodeWorker(f *FlowState, flow *ast.FlowNode, noCacheCheck bool) bool { - for { - if flow == c.lastFlowNode { - return c.lastFlowNodeReachable - } - flags := flow.Flags - if flags&ast.FlowFlagsShared != 0 { - if !noCacheCheck { - if reachable, ok := c.flowNodeReachable[flow]; ok { - return reachable - } - reachable := c.isReachableFlowNodeWorker(f, flow, true /*noCacheCheck*/) - c.flowNodeReachable[flow] = reachable - return reachable - } - noCacheCheck = false - } - switch { - case flags&(ast.FlowFlagsAssignment|ast.FlowFlagsCondition|ast.FlowFlagsArrayMutation) != 0: - flow = flow.Antecedent - case flags&ast.FlowFlagsCall != 0: - if signature := c.getEffectsSignature(flow.Node); signature != nil { - if predicate := c.getTypePredicateOfSignature(signature); predicate != nil && predicate.kind == TypePredicateKindAssertsIdentifier && predicate.t == nil { - if arguments := flow.Node.Arguments(); int(predicate.parameterIndex) < len(arguments) && c.isFalseExpression(arguments[predicate.parameterIndex]) { - return false - } - } - if c.getReturnTypeOfSignature(signature).flags&TypeFlagsNever != 0 { - return false - } - } - flow = flow.Antecedent - case flags&ast.FlowFlagsBranchLabel != 0: - // A branching point is reachable if any branch is reachable. - for list := getBranchLabelAntecedents(flow, f.reduceLabels); list != nil; list = list.Next { - if c.isReachableFlowNodeWorker(f, list.Flow, false /*noCacheCheck*/) { - return true - } - } - return false - case flags&ast.FlowFlagsLoopLabel != 0: - if flow.Antecedents == nil { - return false - } - // A loop is reachable if the control flow path that leads to the top is reachable. - flow = flow.Antecedents.Flow - case flags&ast.FlowFlagsSwitchClause != 0: - // The control flow path representing an unmatched value in a switch statement with - // no default clause is unreachable if the switch statement is exhaustive. - data := flow.Node.AsFlowSwitchClauseData() - if data.ClauseStart == data.ClauseEnd && c.isExhaustiveSwitchStatement(data.SwitchStatement) { - return false - } - flow = flow.Antecedent - case flags&ast.FlowFlagsReduceLabel != 0: - // Cache is unreliable once we start adjusting labels - c.lastFlowNode = nil - f.reduceLabels = append(f.reduceLabels, flow.Node.AsFlowReduceLabelData()) - result := c.isReachableFlowNodeWorker(f, flow.Antecedent, false /*noCacheCheck*/) - f.reduceLabels = f.reduceLabels[:len(f.reduceLabels)-1] - return result - default: - return flags&ast.FlowFlagsUnreachable == 0 - } - } -} - -func (c *Checker) isFalseExpression(expr *ast.Node) bool { - node := ast.SkipParentheses(expr) - if node.Kind == ast.KindFalseKeyword { - return true - } - if ast.IsBinaryExpression(node) { - binary := node.AsBinaryExpression() - return binary.OperatorToken.Kind == ast.KindAmpersandAmpersandToken && (c.isFalseExpression(binary.Left) || c.isFalseExpression(binary.Right)) || - binary.OperatorToken.Kind == ast.KindBarBarToken && c.isFalseExpression(binary.Left) && c.isFalseExpression(binary.Right) - } - return false -} - -// Return true if the given flow node is preceded by a 'super(...)' call in every possible code path -// leading to the node. -func (c *Checker) isPostSuperFlowNode(flow *ast.FlowNode, noCacheCheck bool) bool { - f := c.getFlowState() - result := c.isPostSuperFlowNodeWorker(f, flow, noCacheCheck) - c.putFlowState(f) - return result -} - -func (c *Checker) isPostSuperFlowNodeWorker(f *FlowState, flow *ast.FlowNode, noCacheCheck bool) bool { - for { - flags := flow.Flags - if flags&ast.FlowFlagsShared != 0 { - if !noCacheCheck { - if postSuper, ok := c.flowNodePostSuper[flow]; ok { - return postSuper - } - postSuper := c.isPostSuperFlowNodeWorker(f, flow, true /*noCacheCheck*/) - c.flowNodePostSuper[flow] = postSuper - } - noCacheCheck = false - } - switch { - case flags&(ast.FlowFlagsAssignment|ast.FlowFlagsCondition|ast.FlowFlagsArrayMutation|ast.FlowFlagsSwitchClause) != 0: - flow = flow.Antecedent - case flags&ast.FlowFlagsCall != 0: - if flow.Node.Expression().Kind == ast.KindSuperKeyword { - return true - } - flow = flow.Antecedent - case flags&ast.FlowFlagsBranchLabel != 0: - for list := getBranchLabelAntecedents(flow, f.reduceLabels); list != nil; list = list.Next { - if !c.isPostSuperFlowNodeWorker(f, list.Flow, false /*noCacheCheck*/) { - return false - } - } - return true - case flags&ast.FlowFlagsLoopLabel != 0: - // A loop is post-super if the control flow path that leads to the top is post-super. - flow = flow.Antecedents.Flow - case flags&ast.FlowFlagsReduceLabel != 0: - f.reduceLabels = append(f.reduceLabels, flow.Node.AsFlowReduceLabelData()) - result := c.isPostSuperFlowNodeWorker(f, flow.Antecedent, false /*noCacheCheck*/) - f.reduceLabels = f.reduceLabels[:len(f.reduceLabels)-1] - return result - default: - // Unreachable nodes are considered post-super to silence errors - return flags&ast.FlowFlagsUnreachable != 0 - } - } -} - -// Check if a parameter, catch variable, or mutable local variable is definitely assigned anywhere -func (c *Checker) isSymbolAssignedDefinitely(symbol *ast.Symbol) bool { - c.ensureAssignmentsMarked(symbol) - return c.markedAssignmentSymbolLinks.Get(symbol).hasDefiniteAssignment -} - -// Check if a parameter, catch variable, or mutable local variable is assigned anywhere -func (c *Checker) isSymbolAssigned(symbol *ast.Symbol) bool { - c.ensureAssignmentsMarked(symbol) - return c.markedAssignmentSymbolLinks.Get(symbol).lastAssignmentPos != 0 -} - -// Return true if there are no assignments to the given symbol or if the given location -// is past the last assignment to the symbol. -func (c *Checker) isPastLastAssignment(symbol *ast.Symbol, location *ast.Node) bool { - c.ensureAssignmentsMarked(symbol) - lastAssignmentPos := c.markedAssignmentSymbolLinks.Get(symbol).lastAssignmentPos - return lastAssignmentPos == 0 || location != nil && int(lastAssignmentPos) < location.Pos() -} - -func (c *Checker) ensureAssignmentsMarked(symbol *ast.Symbol) { - if c.markedAssignmentSymbolLinks.Get(symbol).lastAssignmentPos != 0 { - return - } - parent := ast.FindAncestor(symbol.ValueDeclaration, ast.IsFunctionOrSourceFile) - if parent == nil { - return - } - links := c.nodeLinks.Get(parent) - if links.flags&NodeCheckFlagsAssignmentsMarked == 0 { - links.flags |= NodeCheckFlagsAssignmentsMarked - if !c.hasParentWithAssignmentsMarked(parent) { - c.markNodeAssignments(parent) - } - } -} - -func (c *Checker) hasParentWithAssignmentsMarked(node *ast.Node) bool { - return ast.FindAncestor(node.Parent, func(node *ast.Node) bool { - return ast.IsFunctionOrSourceFile(node) && c.nodeLinks.Get(node).flags&NodeCheckFlagsAssignmentsMarked != 0 - }) != nil -} - -// For all assignments within the given root node, record the last assignment source position for all -// referenced parameters and mutable local variables. When assignments occur in nested functions or -// references occur in export specifiers, record math.MaxInt32 as the assignment position. When -// assignments occur in compound statements, record the ending source position of the compound statement -// as the assignment position (this is more conservative than full control flow analysis, but requires -// only a single walk over the AST). -func (c *Checker) markNodeAssignmentsWorker(node *ast.Node) bool { - switch node.Kind { - case ast.KindIdentifier: - assignmentKind := getAssignmentTargetKind(node) - if assignmentKind != AssignmentKindNone { - symbol := c.getResolvedSymbol(node) - if c.isParameterOrMutableLocalVariable(symbol) { - links := c.markedAssignmentSymbolLinks.Get(symbol) - if pos := links.lastAssignmentPos; pos == 0 || pos != math.MaxInt32 { - referencingFunction := ast.FindAncestor(node, ast.IsFunctionOrSourceFile) - declaringFunction := ast.FindAncestor(symbol.ValueDeclaration, ast.IsFunctionOrSourceFile) - if referencingFunction == declaringFunction { - links.lastAssignmentPos = int32(c.extendAssignmentPosition(node, symbol.ValueDeclaration)) - } else { - links.lastAssignmentPos = math.MaxInt32 - } - } - if assignmentKind == AssignmentKindDefinite { - links.hasDefiniteAssignment = true - } - } - } - return false - case ast.KindExportSpecifier: - exportDeclaration := node.AsExportSpecifier().Parent.Parent.AsExportDeclaration() - name := node.AsExportSpecifier().PropertyName - if name == nil { - name = node.Name() - } - if !node.AsExportSpecifier().IsTypeOnly && !exportDeclaration.IsTypeOnly && exportDeclaration.ModuleSpecifier == nil && !ast.IsStringLiteral(name) { - symbol := c.resolveEntityName(name, ast.SymbolFlagsValue, true /*ignoreErrors*/, true /*dontResolveAlias*/, nil) - if symbol != nil && c.isParameterOrMutableLocalVariable(symbol) { - links := c.markedAssignmentSymbolLinks.Get(symbol) - links.lastAssignmentPos = math.MaxInt32 - } - } - return false - case ast.KindInterfaceDeclaration, - ast.KindTypeAliasDeclaration, - ast.KindJSTypeAliasDeclaration, - ast.KindEnumDeclaration: - return false - } - if ast.IsTypeNode(node) { - return false - } - return node.ForEachChild(c.markNodeAssignments) -} - -// Extend the position of the given assignment target node to the end of any intervening variable statement, -// expression statement, compound statement, or class declaration occurring between the node and the given -// declaration node. -func (c *Checker) extendAssignmentPosition(node *ast.Node, declaration *ast.Node) int { - pos := node.Pos() - for node != nil && node.Pos() > declaration.Pos() { - switch node.Kind { - case ast.KindVariableStatement, ast.KindExpressionStatement, ast.KindIfStatement, ast.KindDoStatement, ast.KindWhileStatement, - ast.KindForStatement, ast.KindForInStatement, ast.KindForOfStatement, ast.KindWithStatement, ast.KindSwitchStatement, - ast.KindTryStatement, ast.KindClassDeclaration: - pos = node.End() - } - node = node.Parent - } - return pos -} diff --git a/kitcom/internal/tsgo/checker/grammarchecks.go b/kitcom/internal/tsgo/checker/grammarchecks.go deleted file mode 100644 index 6f0e7bc..0000000 --- a/kitcom/internal/tsgo/checker/grammarchecks.go +++ /dev/null @@ -1,2260 +0,0 @@ -package checker - -import ( - "fmt" - "strings" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/binder" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/debug" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/jsnum" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/scanner" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" -) - -func (c *Checker) grammarErrorOnFirstToken(node *ast.Node, message *diagnostics.Message, args ...any) bool { - sourceFile := ast.GetSourceFileOfNode(node) - if !c.hasParseDiagnostics(sourceFile) { - span := scanner.GetRangeOfTokenAtPosition(sourceFile, node.Pos()) - c.diagnostics.Add(ast.NewDiagnostic(sourceFile, span, message, args...)) - return true - } - return false -} - -func (c *Checker) grammarErrorAtPos(nodeForSourceFile *ast.Node, start int, length int, message *diagnostics.Message, args ...any) bool { - sourceFile := ast.GetSourceFileOfNode(nodeForSourceFile) - if !c.hasParseDiagnostics(sourceFile) { - c.diagnostics.Add(ast.NewDiagnostic(sourceFile, core.NewTextRange(start, start+length), message, args...)) - return true - } - return false -} - -func (c *Checker) grammarErrorOnNode(node *ast.Node, message *diagnostics.Message, args ...any) bool { - sourceFile := ast.GetSourceFileOfNode(node) - if !c.hasParseDiagnostics(sourceFile) { - c.error(node, message, args...) - return true - } - return false -} - -func (c *Checker) grammarErrorOnNodeSkippedOnNoEmit(node *ast.Node, message *diagnostics.Message, args ...any) bool { - sourceFile := ast.GetSourceFileOfNode(node) - if !c.hasParseDiagnostics(sourceFile) { - d := NewDiagnosticForNode(node, message, args...) - d.SetSkippedOnNoEmit() - c.diagnostics.Add(d) - return true - } - return false -} - -func (c *Checker) checkGrammarRegularExpressionLiteral(_ *ast.RegularExpressionLiteral) bool { - // !!! - // Unclear if this is needed until regular expression parsing is more thoroughly implemented. - return false - // sourceFile := ast.GetSourceFileOfNode(node.AsNode()) - // if !c.hasParseDiagnostics(sourceFile) && !node.IsUnterminated { - // var lastError *ast.Diagnostic - // scanner := NewScanner() - // scanner.skipTrivia = true - // scanner.SetScriptTarget(sourceFile.LanguageVersion) - // scanner.SetLanguageVariant(sourceFile.LanguageVariant) - // scanner.SetOnError(func(message *diagnostics.Message, start int, length int, args ...any) { - // // !!! - // // Original uses `tokenEnd()` - unclear if this is the same as the `start` passed in here. - // // const start = scanner.TokenEnd() - - // // The scanner is operating on a slice of the original source text, so we need to adjust the start - // // for error reporting. - // start = start + node.Pos() - - // // For providing spelling suggestions - // if message.Category() == diagnostics.CategoryMessage && lastError != nil && start == lastError.Pos() && length == lastError.Len() { - // err := ast.NewDiagnostic(sourceFile, core.NewTextRange(start, start+length), message, args) - // lastError.AddRelatedInfo(err) - // } else if !(lastError != nil) || start != lastError.Pos() { - // lastError = ast.NewDiagnostic(sourceFile, core.NewTextRange(start, start+length), message, args) - // c.diagnostics.Add(lastError) - // } - // }) - // scanner.SetText(sourceFile.Text[node.Pos():node.Loc.Len()]) - // scanner.Scan() - // if scanner.ReScanSlashToken() != ast.KindRegularExpressionLiteral { - // panic("Expected to rescan RegularExpressionLiteral") - // } - // return lastError != nil - // } - // return false -} - -func (c *Checker) checkGrammarPrivateIdentifierExpression(privId *ast.PrivateIdentifier) bool { - privIdAsNode := privId.AsNode() - if ast.GetContainingClass(privId.AsNode()) == nil { - return c.grammarErrorOnNode(privId.AsNode(), diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies) - } - - if !ast.IsForInStatement(privId.Parent) { - if !ast.IsExpressionNode(privIdAsNode) { - return c.grammarErrorOnNode(privIdAsNode, diagnostics.Private_identifiers_are_only_allowed_in_class_bodies_and_may_only_be_used_as_part_of_a_class_member_declaration_property_access_or_on_the_left_hand_side_of_an_in_expression) - } - - isInOperation := ast.IsBinaryExpression(privId.Parent) && privId.Parent.AsBinaryExpression().OperatorToken.Kind == ast.KindInKeyword - if c.getSymbolForPrivateIdentifierExpression(privIdAsNode) == nil && !isInOperation { - return c.grammarErrorOnNode(privIdAsNode, diagnostics.Cannot_find_name_0, privId.Text) - } - } - - return false -} - -func (c *Checker) checkGrammarMappedType(node *ast.MappedTypeNode) bool { - if len(node.Members.Nodes) > 0 { - return c.grammarErrorOnNode(node.Members.Nodes[0], diagnostics.A_mapped_type_may_not_declare_properties_or_methods) - } - return false -} - -func (c *Checker) checkGrammarDecorator(decorator *ast.Decorator) bool { - sourceFile := ast.GetSourceFileOfNode(decorator.AsNode()) - if !c.hasParseDiagnostics(sourceFile) { - node := decorator.Expression - - // DecoratorParenthesizedExpression : - // `(` Expression `)` - - if ast.IsParenthesizedExpression(node) { - return false - } - - canHaveCallExpression := true - var errorNode *ast.Node - for { - // Allow TS syntax such as non-null assertions and instantiation expressions - if ast.IsExpressionWithTypeArguments(node) || ast.IsNonNullExpression(node) { - node = node.Expression() - continue - } - - // DecoratorCallExpression : - // DecoratorMemberExpression Arguments - - if ast.IsCallExpression(node) { - callExpr := node.AsCallExpression() - if !canHaveCallExpression { - errorNode = node - } - if callExpr.QuestionDotToken != nil { - // Even if we already have an error node, error at the `?.` token since it appears earlier. - errorNode = callExpr.QuestionDotToken - } - node = callExpr.Expression - canHaveCallExpression = false - continue - } - - // DecoratorMemberExpression : - // IdentifierReference - // DecoratorMemberExpression `.` IdentifierName - // DecoratorMemberExpression `.` PrivateIdentifier - - if ast.IsPropertyAccessExpression(node) { - propertyAccessExpr := node.AsPropertyAccessExpression() - if propertyAccessExpr.QuestionDotToken != nil { - // Even if we already have an error node, error at the `?.` token since it appears earlier. - errorNode = propertyAccessExpr.QuestionDotToken - } - node = propertyAccessExpr.Expression - canHaveCallExpression = false - continue - } - - if !ast.IsIdentifier(node) { - // Even if we already have an error node, error at this node since it appears earlier. - errorNode = node - } - - break - } - - if errorNode != nil { - err := c.error(decorator.Expression, diagnostics.Expression_must_be_enclosed_in_parentheses_to_be_used_as_a_decorator) - err.AddRelatedInfo(createDiagnosticForNode(errorNode, diagnostics.Invalid_syntax_in_decorator)) - return true - } - } - - return false -} - -func (c *Checker) checkGrammarExportDeclaration(node *ast.ExportDeclaration) bool { - if node.IsTypeOnly && node.ExportClause != nil && node.ExportClause.Kind == ast.KindNamedExports { - return c.checkGrammarTypeOnlyNamedImportsOrExports(node.ExportClause) - } - return false -} - -func (c *Checker) checkGrammarModuleElementContext(node *ast.Statement, errorMessage *diagnostics.Message) bool { - isInAppropriateContext := node.Parent.Kind == ast.KindSourceFile || node.Parent.Kind == ast.KindModuleBlock || node.Parent.Kind == ast.KindModuleDeclaration - if !isInAppropriateContext { - c.grammarErrorOnFirstToken(node, errorMessage) - } - return !isInAppropriateContext -} - -func (c *Checker) checkGrammarModifiers(node *ast.Node /*Union[HasModifiers, HasDecorators, HasIllegalModifiers, HasIllegalDecorators]*/) bool { - if node.Modifiers() == nil { - return false - } - if c.reportObviousDecoratorErrors(node) || c.reportObviousModifierErrors(node) { - return true - } - if ast.IsParameter(node) && ast.IsThisParameter(node) { - return c.grammarErrorOnFirstToken(node, diagnostics.Neither_decorators_nor_modifiers_may_be_applied_to_this_parameters) - } - blockScopeKind := ast.NodeFlagsNone - if ast.IsVariableStatement(node) { - blockScopeKind = node.AsVariableStatement().DeclarationList.Flags & ast.NodeFlagsBlockScoped - } - var lastStatic *ast.Node - var lastDeclare *ast.Node - var lastAsync *ast.Node - var lastOverride *ast.Node - var firstDecorator *ast.Node - flags := ast.ModifierFlagsNone - sawExportBeforeDecorators := false - // We parse decorators and modifiers in four contiguous chunks: - // [...leadingDecorators, ...leadingModifiers, ...trailingDecorators, ...trailingModifiers]. It is an error to - // have both leading and trailing decorators. - hasLeadingDecorators := false - var modifiers []*ast.Node - if node.Modifiers() != nil { - modifiers = node.Modifiers().Nodes - } - for _, modifier := range modifiers { - if ast.IsDecorator(modifier) { - if !nodeCanBeDecorated(c.legacyDecorators, node, node.Parent, node.Parent.Parent) { - if node.Kind == ast.KindMethodDeclaration && !ast.NodeIsPresent(node.Body()) { - return c.grammarErrorOnFirstToken(node, diagnostics.A_decorator_can_only_decorate_a_method_implementation_not_an_overload) - } else { - return c.grammarErrorOnFirstToken(node, diagnostics.Decorators_are_not_valid_here) - } - } else if c.legacyDecorators && (node.Kind == ast.KindGetAccessor || node.Kind == ast.KindSetAccessor) { - accessors := c.getAllAccessorDeclarationsForDeclaration(node) - if ast.HasDecorators(accessors.firstAccessor) && node == accessors.secondAccessor { - return c.grammarErrorOnFirstToken(node, diagnostics.Decorators_cannot_be_applied_to_multiple_get_Slashset_accessors_of_the_same_name) - } - } - - // if we've seen any modifiers aside from `export`, `default`, or another decorator, then this is an invalid position - if flags&^(ast.ModifierFlagsExportDefault|ast.ModifierFlagsDecorator) != 0 { - return c.grammarErrorOnNode(modifier, diagnostics.Decorators_are_not_valid_here) - } - - // if we've already seen leading decorators and leading modifiers, then trailing decorators are an invalid position - if hasLeadingDecorators && flags&ast.ModifierFlagsModifier != 0 { - if firstDecorator == nil { - panic("Expected firstDecorator to be set") - } - sourceFile := ast.GetSourceFileOfNode(modifier) - if !c.hasParseDiagnostics(sourceFile) { - err := c.error(modifier, diagnostics.Decorators_may_not_appear_after_export_or_export_default_if_they_also_appear_before_export) - err.AddRelatedInfo(createDiagnosticForNode(firstDecorator, diagnostics.Decorator_used_before_export_here)) - return true - } - return false - } - - flags |= ast.ModifierFlagsDecorator - - // if we have not yet seen a modifier, then these are leading decorators - if flags&ast.ModifierFlagsModifier == 0 { - hasLeadingDecorators = true - } else if flags&ast.ModifierFlagsExport != 0 { - sawExportBeforeDecorators = true - } - - if firstDecorator == nil { - firstDecorator = modifier - } - } else { - if modifier.Kind != ast.KindReadonlyKeyword { - if node.Kind == ast.KindPropertySignature || node.Kind == ast.KindMethodSignature { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_appear_on_a_type_member, scanner.TokenToString(modifier.Kind)) - } - if node.Kind == ast.KindIndexSignature && (modifier.Kind != ast.KindStaticKeyword || !ast.IsClassLike(node.Parent)) { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_appear_on_an_index_signature, scanner.TokenToString(modifier.Kind)) - } - } - if modifier.Kind != ast.KindInKeyword && modifier.Kind != ast.KindOutKeyword && modifier.Kind != ast.KindConstKeyword { - if node.Kind == ast.KindTypeParameter { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_appear_on_a_type_parameter, scanner.TokenToString(modifier.Kind)) - } - } - switch modifier.Kind { - case ast.KindConstKeyword: - if node.Kind != ast.KindEnumDeclaration && node.Kind != ast.KindTypeParameter { - return c.grammarErrorOnNode(node, diagnostics.A_class_member_cannot_have_the_0_keyword, scanner.TokenToString(ast.KindConstKeyword)) - } - - // !!! - // parent := (isJSDocTemplateTag(node.Parent) && getEffectiveJSDocHost(node.Parent)) || node.Parent - parent := node.Parent - - if node.Kind == ast.KindTypeParameter { - if !(ast.IsFunctionLikeDeclaration(parent) || ast.IsClassLike(parent) || - ast.IsFunctionTypeNode(parent) || ast.IsConstructorTypeNode(parent) || - ast.IsCallSignatureDeclaration(parent) || ast.IsConstructSignatureDeclaration(parent) || - ast.IsMethodSignatureDeclaration(parent)) { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_can_only_appear_on_a_type_parameter_of_a_function_method_or_class, scanner.TokenToString(modifier.Kind)) - } - } - case ast.KindOverrideKeyword: - // If node.kind === SyntaxKind.Parameter, checkParameter reports an error if it's not a parameter property. - if flags&ast.ModifierFlagsOverride != 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_already_seen, "override") - } else if flags&ast.ModifierFlagsAmbient != 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_be_used_with_1_modifier, "override", "declare") - } else if flags&ast.ModifierFlagsReadonly != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, "override", "readonly") - } else if flags&ast.ModifierFlagsAccessor != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, "override", "accessor") - } else if flags&ast.ModifierFlagsAsync != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, "override", "async") - } - flags |= ast.ModifierFlagsOverride - lastOverride = modifier - - case ast.KindPublicKeyword, - ast.KindProtectedKeyword, - ast.KindPrivateKeyword: - text := visibilityToString(ast.ModifierToFlag(modifier.Kind)) - - if flags&ast.ModifierFlagsAccessibilityModifier != 0 { - return c.grammarErrorOnNode(modifier, diagnostics.Accessibility_modifier_already_seen) - } else if flags&ast.ModifierFlagsOverride != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, text, "override") - } else if flags&ast.ModifierFlagsStatic != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, text, "static") - } else if flags&ast.ModifierFlagsAccessor != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, text, "accessor") - } else if flags&ast.ModifierFlagsReadonly != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, text, "readonly") - } else if flags&ast.ModifierFlagsAsync != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, text, "async") - } else if node.Parent.Kind == ast.KindModuleBlock || node.Parent.Kind == ast.KindSourceFile { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_appear_on_a_module_or_namespace_element, text) - } else if flags&ast.ModifierFlagsAbstract != 0 { - if modifier.Kind == ast.KindPrivateKeyword { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_be_used_with_1_modifier, text, "abstract") - } else if modifier.Flags&ast.NodeFlagsReparsed == 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, text, "abstract") - } - } else if ast.IsPrivateIdentifierClassElementDeclaration(node) { - return c.grammarErrorOnNode(modifier, diagnostics.An_accessibility_modifier_cannot_be_used_with_a_private_identifier) - } - flags |= ast.ModifierToFlag(modifier.Kind) - case ast.KindStaticKeyword: - if flags&ast.ModifierFlagsStatic != 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_already_seen, "static") - } else if flags&ast.ModifierFlagsReadonly != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, "static", "readonly") - } else if flags&ast.ModifierFlagsAsync != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, "static", "async") - } else if flags&ast.ModifierFlagsAccessor != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, "static", "accessor") - } else if node.Parent.Kind == ast.KindModuleBlock || node.Parent.Kind == ast.KindSourceFile { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_appear_on_a_module_or_namespace_element, "static") - } else if node.Kind == ast.KindParameter { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_appear_on_a_parameter, "static") - } else if flags&ast.ModifierFlagsAbstract != 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_be_used_with_1_modifier, "static", "abstract") - } else if flags&ast.ModifierFlagsOverride != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, "static", "override") - } - flags |= ast.ModifierFlagsStatic - lastStatic = modifier - case ast.KindAccessorKeyword: - if flags&ast.ModifierFlagsAccessor != 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_already_seen, "accessor") - } else if flags&ast.ModifierFlagsReadonly != 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_be_used_with_1_modifier, "accessor", "readonly") - } else if flags&ast.ModifierFlagsAmbient != 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_be_used_with_1_modifier, "accessor", "declare") - } else if node.Kind != ast.KindPropertyDeclaration { - return c.grammarErrorOnNode(modifier, diagnostics.X_accessor_modifier_can_only_appear_on_a_property_declaration) - } - - flags |= ast.ModifierFlagsAccessor - case ast.KindReadonlyKeyword: - if flags&ast.ModifierFlagsReadonly != 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_already_seen, "readonly") - } else if node.Kind != ast.KindPropertyDeclaration && node.Kind != ast.KindPropertySignature && node.Kind != ast.KindIndexSignature && node.Kind != ast.KindParameter { - // If node.kind === SyntaxKind.Parameter, checkParameter reports an error if it's not a parameter property. - return c.grammarErrorOnNode(modifier, diagnostics.X_readonly_modifier_can_only_appear_on_a_property_declaration_or_index_signature) - } else if flags&ast.ModifierFlagsAccessor != 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_be_used_with_1_modifier, "readonly", "accessor") - } - flags |= ast.ModifierFlagsReadonly - case ast.KindExportKeyword: - if c.compilerOptions.VerbatimModuleSyntax == core.TSTrue && node.Flags&ast.NodeFlagsAmbient == 0 && node.Kind != ast.KindTypeAliasDeclaration && node.Kind != ast.KindInterfaceDeclaration && node.Kind != ast.KindModuleDeclaration && node.Parent.Kind == ast.KindSourceFile && c.program.GetEmitModuleFormatOfFile(ast.GetSourceFileOfNode(node)) == core.ModuleKindCommonJS { - return c.grammarErrorOnNode(modifier, diagnostics.A_top_level_export_modifier_cannot_be_used_on_value_declarations_in_a_CommonJS_module_when_verbatimModuleSyntax_is_enabled) - } - if flags&ast.ModifierFlagsExport != 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_already_seen, "export") - } else if flags&ast.ModifierFlagsAmbient != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, "export", "declare") - } else if flags&ast.ModifierFlagsAbstract != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, "export", "abstract") - } else if flags&ast.ModifierFlagsAsync != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, "export", "async") - } else if ast.IsClassLike(node.Parent) && !ast.IsJSTypeAliasDeclaration(node) { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_appear_on_class_elements_of_this_kind, "export") - } else if node.Kind == ast.KindParameter { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_appear_on_a_parameter, "export") - } else if blockScopeKind == ast.NodeFlagsUsing { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_appear_on_a_using_declaration, "export") - } else if blockScopeKind == ast.NodeFlagsAwaitUsing { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_appear_on_an_await_using_declaration, "export") - } - flags |= ast.ModifierFlagsExport - case ast.KindDefaultKeyword: - var container *ast.Node - if node.Parent.Kind == ast.KindSourceFile { - container = node.Parent - } else { - container = node.Parent.Parent - } - if container.Kind == ast.KindModuleDeclaration && !ast.IsAmbientModule(container) { - return c.grammarErrorOnNode(modifier, diagnostics.A_default_export_can_only_be_used_in_an_ECMAScript_style_module) - } else if blockScopeKind == ast.NodeFlagsUsing { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_appear_on_a_using_declaration, "default") - } else if blockScopeKind == ast.NodeFlagsAwaitUsing { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_appear_on_an_await_using_declaration, "default") - } else if flags&ast.ModifierFlagsExport == 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, "export", "default") - } else if sawExportBeforeDecorators { - return c.grammarErrorOnNode(firstDecorator, diagnostics.Decorators_are_not_valid_here) - } - - flags |= ast.ModifierFlagsDefault - case ast.KindDeclareKeyword: - if flags&ast.ModifierFlagsAmbient != 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_already_seen, "declare") - } else if flags&ast.ModifierFlagsAsync != 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_be_used_in_an_ambient_context, "async") - } else if flags&ast.ModifierFlagsOverride != 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_be_used_in_an_ambient_context, "override") - } else if ast.IsClassLike(node.Parent) && !ast.IsPropertyDeclaration(node) { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_appear_on_class_elements_of_this_kind, "declare") - } else if node.Kind == ast.KindParameter { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_appear_on_a_parameter, "declare") - } else if blockScopeKind == ast.NodeFlagsUsing { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_appear_on_a_using_declaration, "declare") - } else if blockScopeKind == ast.NodeFlagsAwaitUsing { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_appear_on_an_await_using_declaration, "declare") - } else if (node.Parent.Flags&ast.NodeFlagsAmbient != 0) && node.Parent.Kind == ast.KindModuleBlock { - return c.grammarErrorOnNode(modifier, diagnostics.A_declare_modifier_cannot_be_used_in_an_already_ambient_context) - } else if ast.IsPrivateIdentifierClassElementDeclaration(node) { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_be_used_with_a_private_identifier, "declare") - } else if flags&ast.ModifierFlagsAccessor != 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_be_used_with_1_modifier, "declare", "accessor") - } - flags |= ast.ModifierFlagsAmbient - lastDeclare = modifier - case ast.KindAbstractKeyword: - if flags&ast.ModifierFlagsAbstract != 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_already_seen, "abstract") - } - if node.Kind != ast.KindClassDeclaration && node.Kind != ast.KindConstructorType { - if node.Kind != ast.KindMethodDeclaration && node.Kind != ast.KindPropertyDeclaration && node.Kind != ast.KindGetAccessor && node.Kind != ast.KindSetAccessor { - return c.grammarErrorOnNode(modifier, diagnostics.X_abstract_modifier_can_only_appear_on_a_class_method_or_property_declaration) - } - if !(node.Parent.Kind == ast.KindClassDeclaration && ast.HasSyntacticModifier(node.Parent, ast.ModifierFlagsAbstract)) { - var message *diagnostics.Message - if node.Kind == ast.KindPropertyDeclaration { - message = diagnostics.Abstract_properties_can_only_appear_within_an_abstract_class - } else { - message = diagnostics.Abstract_methods_can_only_appear_within_an_abstract_class - } - return c.grammarErrorOnNode(modifier, message) - } - if flags&ast.ModifierFlagsStatic != 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_be_used_with_1_modifier, "static", "abstract") - } - if flags&ast.ModifierFlagsPrivate != 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_be_used_with_1_modifier, "private", "abstract") - } - if flags&ast.ModifierFlagsAsync != 0 && lastAsync != nil { - return c.grammarErrorOnNode(lastAsync, diagnostics.X_0_modifier_cannot_be_used_with_1_modifier, "async", "abstract") - } - if flags&ast.ModifierFlagsOverride != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, "abstract", "override") - } - if flags&ast.ModifierFlagsAccessor != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, "abstract", "accessor") - } - } - if name := node.Name(); name != nil && name.Kind == ast.KindPrivateIdentifier { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_be_used_with_a_private_identifier, "abstract") - } - - flags |= ast.ModifierFlagsAbstract - case ast.KindAsyncKeyword: - if flags&ast.ModifierFlagsAsync != 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_already_seen, "async") - } else if flags&ast.ModifierFlagsAmbient != 0 || node.Parent.Flags&ast.NodeFlagsAmbient != 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_be_used_in_an_ambient_context, "async") - } else if node.Kind == ast.KindParameter { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_appear_on_a_parameter, "async") - } - if flags&ast.ModifierFlagsAbstract != 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_be_used_with_1_modifier, "async", "abstract") - } - flags |= ast.ModifierFlagsAsync - lastAsync = modifier - case ast.KindInKeyword, - ast.KindOutKeyword: - var inOutFlag ast.ModifierFlags - if modifier.Kind == ast.KindInKeyword { - inOutFlag = ast.ModifierFlagsIn - } else { - inOutFlag = ast.ModifierFlagsOut - } - var inOutText string - if modifier.Kind == ast.KindInKeyword { - inOutText = "in" - } else { - inOutText = "out" - } - // !!! - // parent := isJSDocTemplateTag(node.Parent) && (getEffectiveJSDocHost(node.Parent) || core.Find(getJSDocRoot(node.Parent). /* ? */ tags, isJSDocTypedefTag)) || node.Parent - parent := node.Parent - if node.Kind != ast.KindTypeParameter || parent != nil && !(ast.IsInterfaceDeclaration(parent) || ast.IsClassLike(parent) || ast.IsTypeAliasDeclaration(parent) || isJSDocTypedefTag(parent)) { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_can_only_appear_on_a_type_parameter_of_a_class_interface_or_type_alias, inOutText) - } - if flags&inOutFlag != 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_already_seen, inOutText) - } - if inOutFlag&ast.ModifierFlagsIn != 0 && flags&ast.ModifierFlagsOut != 0 { - return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, "in", "out") - } - flags |= inOutFlag - } - } - } - - if node.Kind == ast.KindConstructor { - if flags&ast.ModifierFlagsStatic != 0 { - return c.grammarErrorOnNode(lastStatic, diagnostics.X_0_modifier_cannot_appear_on_a_constructor_declaration, "static") - } - if flags&ast.ModifierFlagsOverride != 0 { - return c.grammarErrorOnNode(lastOverride, diagnostics.X_0_modifier_cannot_appear_on_a_constructor_declaration, "override") - } - if flags&ast.ModifierFlagsAsync != 0 { - return c.grammarErrorOnNode(lastAsync, diagnostics.X_0_modifier_cannot_appear_on_a_constructor_declaration, "async") - } - return false - } else if (node.Kind == ast.KindImportDeclaration || node.Kind == ast.KindJSImportDeclaration || node.Kind == ast.KindImportEqualsDeclaration) && flags&ast.ModifierFlagsAmbient != 0 { - return c.grammarErrorOnNode(lastDeclare, diagnostics.A_0_modifier_cannot_be_used_with_an_import_declaration, "declare") - } else if node.Kind == ast.KindParameter && (flags&ast.ModifierFlagsParameterPropertyModifier != 0) && ast.IsBindingPattern(node.Name()) { - return c.grammarErrorOnNode(node, diagnostics.A_parameter_property_may_not_be_declared_using_a_binding_pattern) - } else if node.Kind == ast.KindParameter && (flags&ast.ModifierFlagsParameterPropertyModifier != 0) && node.AsParameterDeclaration().DotDotDotToken != nil { - return c.grammarErrorOnNode(node, diagnostics.A_parameter_property_cannot_be_declared_using_a_rest_parameter) - } - if flags&ast.ModifierFlagsAsync != 0 { - return c.checkGrammarAsyncModifier(node, lastAsync) - } - return false -} - -func isJSDocTypedefTag(_ *ast.Node) bool { - // !!! - return false -} - -func (c *Checker) reportObviousModifierErrors(node *ast.Node) bool { - modifier := c.findFirstIllegalModifier(node) - if modifier == nil { - return false - } - return c.grammarErrorOnFirstToken(modifier, diagnostics.Modifiers_cannot_appear_here) -} - -func (c *Checker) findFirstModifierExcept(node *ast.Node, allowedModifier ast.Kind) *ast.Node { - modifiers := node.Modifiers() - if modifiers == nil { - return nil - } - modifier := core.Find(modifiers.Nodes, ast.IsModifier) - if modifier != nil && modifier.Kind != allowedModifier { - return modifier - } else { - return nil - } -} - -func (c *Checker) findFirstIllegalModifier(node *ast.Node) *ast.Node { - switch node.Kind { - case ast.KindGetAccessor, - ast.KindSetAccessor, - ast.KindConstructor, - ast.KindPropertyDeclaration, - ast.KindPropertySignature, - ast.KindMethodDeclaration, - ast.KindMethodSignature, - ast.KindIndexSignature, - ast.KindModuleDeclaration, - ast.KindImportDeclaration, - ast.KindJSImportDeclaration, - ast.KindImportEqualsDeclaration, - ast.KindExportDeclaration, - ast.KindExportAssignment, - ast.KindJSExportAssignment, - ast.KindFunctionExpression, - ast.KindArrowFunction, - ast.KindParameter, - ast.KindTypeParameter, - ast.KindJSTypeAliasDeclaration: - return nil - case ast.KindClassStaticBlockDeclaration, - ast.KindPropertyAssignment, - ast.KindShorthandPropertyAssignment, - ast.KindNamespaceExportDeclaration, - ast.KindMissingDeclaration: - if modifiers := node.Modifiers(); modifiers != nil { - return core.Find(modifiers.Nodes, ast.IsModifier) - } - return nil - default: - if node.Parent.Kind == ast.KindModuleBlock || node.Parent.Kind == ast.KindSourceFile { - return nil - } - switch node.Kind { - case ast.KindFunctionDeclaration: - return c.findFirstModifierExcept(node, ast.KindAsyncKeyword) - case ast.KindClassDeclaration, - ast.KindConstructorType: - return c.findFirstModifierExcept(node, ast.KindAbstractKeyword) - case ast.KindClassExpression, - ast.KindInterfaceDeclaration, - ast.KindTypeAliasDeclaration: - if modifiers := node.Modifiers(); modifiers != nil { - return core.Find(modifiers.Nodes, ast.IsModifier) - } - return nil - case ast.KindVariableStatement: - if node.AsVariableStatement().DeclarationList.Flags&ast.NodeFlagsUsing != 0 { - return c.findFirstModifierExcept(node, ast.KindAwaitKeyword) - } else if modifiers := node.Modifiers(); modifiers != nil { - return core.Find(modifiers.Nodes, ast.IsModifier) - } - return nil - case ast.KindEnumDeclaration: - return c.findFirstModifierExcept(node, ast.KindConstKeyword) - default: - panic("Unhandled case in findFirstIllegalModifier.") - } - } -} - -func (c *Checker) reportObviousDecoratorErrors(node *ast.Node) bool { - decorator := c.findFirstIllegalDecorator(node) - if decorator == nil { - return false - } - return c.grammarErrorOnFirstToken(decorator, diagnostics.Decorators_are_not_valid_here) -} - -func (c *Checker) findFirstIllegalDecorator(node *ast.Node) *ast.Node { - if ast.CanHaveIllegalDecorators(node) { - decorator := core.Find(node.Modifiers().Nodes, ast.IsDecorator) - return decorator - } else { - return nil - } -} - -func (c *Checker) checkGrammarAsyncModifier(node *ast.Node, asyncModifier *ast.Node) bool { - switch node.Kind { - case ast.KindMethodDeclaration, - ast.KindFunctionDeclaration, - ast.KindFunctionExpression, - ast.KindArrowFunction: - return false - } - - return c.grammarErrorOnNode(asyncModifier, diagnostics.X_0_modifier_cannot_be_used_here, "async") -} - -func (c *Checker) checkGrammarForDisallowedTrailingComma(list *ast.NodeList, diag *diagnostics.Message) bool { - if list != nil && list.HasTrailingComma() { - return c.grammarErrorAtPos(list.Nodes[0], list.End()-len(","), len(","), diag) - } - return false -} - -func (c *Checker) checkGrammarTypeParameterList(typeParameters *ast.NodeList, file *ast.SourceFile) bool { - if typeParameters != nil && len(typeParameters.Nodes) == 0 { - start := typeParameters.Pos() - len("<") - end := scanner.SkipTrivia(file.Text(), typeParameters.End()) + len(">") - return c.grammarErrorAtPos(file.AsNode(), start, end-start, diagnostics.Type_parameter_list_cannot_be_empty) - } - return false -} - -func (c *Checker) checkGrammarParameterList(parameters *ast.NodeList) bool { - seenOptionalParameter := false - parameterCount := len(parameters.Nodes) - - for i := range parameterCount { - parameter := parameters.Nodes[i].AsParameterDeclaration() - if parameter.DotDotDotToken != nil { - if i != parameterCount-1 { - return c.grammarErrorOnNode(parameter.DotDotDotToken, diagnostics.A_rest_parameter_must_be_last_in_a_parameter_list) - } - if parameter.Flags&ast.NodeFlagsAmbient == 0 { - c.checkGrammarForDisallowedTrailingComma(parameters, diagnostics.A_rest_parameter_or_binding_pattern_may_not_have_a_trailing_comma) - } - - if parameter.QuestionToken != nil { - return c.grammarErrorOnNode(parameter.QuestionToken, diagnostics.A_rest_parameter_cannot_be_optional) - } - - if parameter.Initializer != nil { - return c.grammarErrorOnNode(parameter.Name(), diagnostics.A_rest_parameter_cannot_have_an_initializer) - } - } else if isOptionalDeclaration(parameter.AsNode()) { - // !!! - // used to be hasEffectiveQuestionToken for JSDoc - seenOptionalParameter = true - if parameter.QuestionToken != nil && parameter.Initializer != nil { - return c.grammarErrorOnNode(parameter.Name(), diagnostics.Parameter_cannot_have_question_mark_and_initializer) - } - } else if seenOptionalParameter && parameter.Initializer == nil { - return c.grammarErrorOnNode(parameter.Name(), diagnostics.A_required_parameter_cannot_follow_an_optional_parameter) - } - } - - return false -} - -func (c *Checker) checkGrammarForUseStrictSimpleParameterList(node *ast.Node) bool { - if c.languageVersion >= core.ScriptTargetES2016 { - body := node.Body() - var useStrictDirective *ast.Node - if body != nil && ast.IsBlock(body) { - useStrictDirective = binder.FindUseStrictPrologue(ast.GetSourceFileOfNode(node), body.AsBlock().Statements.Nodes) - } - if useStrictDirective != nil { - nonSimpleParameters := core.Filter(node.Parameters(), func(n *ast.Node) bool { - parameter := n.AsParameterDeclaration() - return parameter.Initializer != nil || ast.IsBindingPattern(parameter.Name()) || isRestParameter(parameter.AsNode()) - }) - if len(nonSimpleParameters) != 0 { - for _, parameter := range nonSimpleParameters { - err := c.error(parameter, diagnostics.This_parameter_is_not_allowed_with_use_strict_directive) - err.AddRelatedInfo(createDiagnosticForNode(useStrictDirective, diagnostics.X_use_strict_directive_used_here)) - } - - err := c.error(useStrictDirective, diagnostics.X_use_strict_directive_cannot_be_used_with_non_simple_parameter_list) - for index, parameter := range nonSimpleParameters { - var relatedMessage *diagnostics.Message - if index == 0 { - relatedMessage = diagnostics.Non_simple_parameter_declared_here - } else { - relatedMessage = diagnostics.X_and_here - } - err.AddRelatedInfo(createDiagnosticForNode(parameter, relatedMessage)) - } - - return true - } - } - } - return false -} - -func (c *Checker) checkGrammarFunctionLikeDeclaration(node *ast.Node) bool { - // Prevent cascading error by short-circuit - file := ast.GetSourceFileOfNode(node) - funcData := node.FunctionLikeData() - return c.checkGrammarModifiers(node) || c.checkGrammarTypeParameterList(funcData.TypeParameters, file) || - c.checkGrammarParameterList(funcData.Parameters) || c.checkGrammarArrowFunction(node, file) || - (ast.IsFunctionLikeDeclaration(node) && c.checkGrammarForUseStrictSimpleParameterList(node)) -} - -func (c *Checker) checkGrammarClassLikeDeclaration(node *ast.Node) bool { - file := ast.GetSourceFileOfNode(node) - return c.checkGrammarClassDeclarationHeritageClauses(node, file) || c.checkGrammarTypeParameterList(node.ClassLikeData().TypeParameters, file) -} - -func (c *Checker) checkGrammarArrowFunction(node *ast.Node, file *ast.SourceFile) bool { - if !ast.IsArrowFunction(node) { - return false - } - - arrowFunc := node.AsArrowFunction() - typeParameters := arrowFunc.TypeParameters - if typeParameters != nil { - typeParamNodes := typeParameters.Nodes - if len(typeParamNodes) == 0 || - len(typeParamNodes) == 1 && typeParamNodes[0].AsTypeParameter().Constraint == nil || - typeParameters.HasTrailingComma() { - if tspath.FileExtensionIsOneOf(file.FileName(), []string{tspath.ExtensionMts, tspath.ExtensionCts}) { - // TODO(danielr): should we return early here? - c.grammarErrorOnNode(typeParameters.Nodes[0], diagnostics.This_syntax_is_reserved_in_files_with_the_mts_or_cts_extension_Add_a_trailing_comma_or_explicit_constraint) - } - } - } - - equalsGreaterThanToken := arrowFunc.EqualsGreaterThanToken - startLine, _ := scanner.GetECMALineAndCharacterOfPosition(file, equalsGreaterThanToken.Pos()) - endLine, _ := scanner.GetECMALineAndCharacterOfPosition(file, equalsGreaterThanToken.End()) - return startLine != endLine && c.grammarErrorOnNode(equalsGreaterThanToken, diagnostics.Line_terminator_not_permitted_before_arrow) -} - -func (c *Checker) checkGrammarIndexSignatureParameters(node *ast.IndexSignatureDeclaration) bool { - paramNodes := node.Parameters.Nodes - - if len(paramNodes) == 0 { - return c.grammarErrorOnNode(node.AsNode(), diagnostics.An_index_signature_must_have_exactly_one_parameter) - } - - parameter := paramNodes[0].AsParameterDeclaration() - if len(paramNodes) != 1 { - return c.grammarErrorOnNode(parameter.Name(), diagnostics.An_index_signature_must_have_exactly_one_parameter) - } - - c.checkGrammarForDisallowedTrailingComma(node.Parameters, diagnostics.An_index_signature_cannot_have_a_trailing_comma) - if parameter.DotDotDotToken != nil { - return c.grammarErrorOnNode(parameter.DotDotDotToken, diagnostics.An_index_signature_cannot_have_a_rest_parameter) - } - if parameter.Modifiers() != nil { - return c.grammarErrorOnNode(parameter.Name(), diagnostics.An_index_signature_parameter_cannot_have_an_accessibility_modifier) - } - if parameter.QuestionToken != nil { - return c.grammarErrorOnNode(parameter.QuestionToken, diagnostics.An_index_signature_parameter_cannot_have_a_question_mark) - } - if parameter.Initializer != nil { - return c.grammarErrorOnNode(parameter.Name(), diagnostics.An_index_signature_parameter_cannot_have_an_initializer) - } - typeNode := parameter.Type - if typeNode == nil { - return c.grammarErrorOnNode(parameter.Name(), diagnostics.An_index_signature_parameter_must_have_a_type_annotation) - } - t := c.getTypeFromTypeNode(typeNode) - if someType(t, func(t *Type) bool { - return t.flags&TypeFlagsStringOrNumberLiteralOrUnique != 0 - }) || c.isGenericType(t) { - return c.grammarErrorOnNode(parameter.Name(), diagnostics.An_index_signature_parameter_type_cannot_be_a_literal_type_or_generic_type_Consider_using_a_mapped_object_type_instead) - } - if !everyType(t, c.isValidIndexKeyType) { - return c.grammarErrorOnNode(parameter.Name(), diagnostics.An_index_signature_parameter_type_must_be_string_number_symbol_or_a_template_literal_type) - } - if node.Type == nil { - return c.grammarErrorOnNode(node.AsNode(), diagnostics.An_index_signature_must_have_a_type_annotation) - } - return false -} - -func (c *Checker) checkGrammarIndexSignature(node *ast.IndexSignatureDeclaration) bool { - // Prevent cascading error by short-circuit - return c.checkGrammarModifiers(node.AsNode()) || c.checkGrammarIndexSignatureParameters(node) -} - -func (c *Checker) checkGrammarForAtLeastOneTypeArgument(node *ast.Node, typeArguments *ast.NodeList) bool { - if typeArguments != nil && len(typeArguments.Nodes) == 0 { - sourceFile := ast.GetSourceFileOfNode(node) - start := typeArguments.Pos() - len("<") - end := scanner.SkipTrivia(sourceFile.Text(), typeArguments.End()) + len(">") - return c.grammarErrorAtPos(sourceFile.AsNode(), start, end-start, diagnostics.Type_argument_list_cannot_be_empty) - } - return false -} - -func (c *Checker) checkGrammarTypeArguments(node *ast.Node, typeArguments *ast.NodeList) bool { - return c.checkGrammarForDisallowedTrailingComma(typeArguments, diagnostics.Trailing_comma_not_allowed) || c.checkGrammarForAtLeastOneTypeArgument(node, typeArguments) -} - -func (c *Checker) checkGrammarTaggedTemplateChain(node *ast.TaggedTemplateExpression) bool { - if node.QuestionDotToken != nil || node.Flags&ast.NodeFlagsOptionalChain != 0 { - return c.grammarErrorOnNode(node.Template, diagnostics.Tagged_template_expressions_are_not_permitted_in_an_optional_chain) - } - return false -} - -func (c *Checker) checkGrammarHeritageClause(node *ast.HeritageClause) bool { - types := node.Types - if c.checkGrammarForDisallowedTrailingComma(types, diagnostics.Trailing_comma_not_allowed) { - return true - } - if types != nil && len(types.Nodes) == 0 { - listType := scanner.TokenToString(node.Token) - // TODO(danielr): why not error on the token? - return c.grammarErrorAtPos(node.AsNode(), types.Pos(), 0, diagnostics.X_0_list_cannot_be_empty, listType) - } - - for _, node := range types.Nodes { - if c.checkGrammarExpressionWithTypeArguments(node) { - return true - } - } - return false -} - -func (c *Checker) checkGrammarExpressionWithTypeArguments(node *ast.Node /*Union[ExpressionWithTypeArguments, TypeQueryNode]*/) bool { - if !ast.IsExpressionWithTypeArguments(node) { - return false - } - exprWithTypeArgs := node.AsExpressionWithTypeArguments() - if node.Expression().Kind == ast.KindImportKeyword && exprWithTypeArgs.TypeArguments != nil { - return c.grammarErrorOnNode(node, diagnostics.This_use_of_import_is_invalid_import_calls_can_be_written_but_they_must_have_parentheses_and_cannot_have_type_arguments) - } - return c.checkGrammarTypeArguments(node, exprWithTypeArgs.TypeArguments) -} - -func (c *Checker) checkGrammarClassDeclarationHeritageClauses(node *ast.ClassLikeDeclaration, file *ast.SourceFile) bool { - seenExtendsClause := false - seenImplementsClause := false - - classLikeData := node.ClassLikeData() - - if !c.checkGrammarModifiers(node) && classLikeData.HeritageClauses != nil { - for _, heritageClauseNode := range classLikeData.HeritageClauses.Nodes { - heritageClause := heritageClauseNode.AsHeritageClause() - if heritageClause.Token == ast.KindExtendsKeyword { - if seenExtendsClause { - return c.grammarErrorOnFirstToken(heritageClauseNode, diagnostics.X_extends_clause_already_seen) - } - - if seenImplementsClause { - return c.grammarErrorOnFirstToken(heritageClauseNode, diagnostics.X_extends_clause_must_precede_implements_clause) - } - - typeNodes := heritageClause.Types.Nodes - if len(typeNodes) > 1 { - return c.grammarErrorOnFirstToken(typeNodes[1], diagnostics.Classes_can_only_extend_a_single_class) - } - - for _, j := range node.JSDoc(file) { - if j.AsJSDoc().Tags == nil { - continue - } - for _, tag := range j.AsJSDoc().Tags.Nodes { - if tag.Kind == ast.KindJSDocAugmentsTag { - target := typeNodes[0].AsExpressionWithTypeArguments() - source := tag.AsJSDocAugmentsTag().ClassName.AsExpressionWithTypeArguments() - if !ast.HasSamePropertyAccessName(target.Expression, source.Expression) && - target.Expression.Kind == ast.KindIdentifier && - source.Expression.Kind == ast.KindIdentifier { - return c.grammarErrorOnNode(tag.AsJSDocAugmentsTag().ClassName, diagnostics.JSDoc_0_1_does_not_match_the_extends_2_clause, tag.AsJSDocAugmentsTag().TagName.Text(), source.Expression.Text(), target.Expression.Text()) - } - } - } - } - seenExtendsClause = true - } else { - if heritageClause.Token != ast.KindImplementsKeyword { - panic(fmt.Sprintf("Unexpected token %q", heritageClause.Token)) - } - if seenImplementsClause { - return c.grammarErrorOnFirstToken(heritageClauseNode, diagnostics.X_implements_clause_already_seen) - } - - seenImplementsClause = true - } - - // Grammar checking heritageClause inside class declaration - c.checkGrammarHeritageClause(heritageClause) - } - } - - return false -} - -func (c *Checker) checkGrammarInterfaceDeclaration(node *ast.InterfaceDeclaration) bool { - if node.HeritageClauses != nil { - seenExtendsClause := false - for _, heritageClauseNode := range node.HeritageClauses.Nodes { - heritageClause := heritageClauseNode.AsHeritageClause() - - switch heritageClause.Token { - case ast.KindExtendsKeyword: - if seenExtendsClause { - return c.grammarErrorOnFirstToken(heritageClauseNode, diagnostics.X_extends_clause_already_seen) - } - seenExtendsClause = true - case ast.KindImplementsKeyword: - return c.grammarErrorOnFirstToken(heritageClauseNode, diagnostics.Interface_declaration_cannot_have_implements_clause) - default: - panic(fmt.Sprintf("Unexpected token %q", heritageClause.Token.String())) - } - - // Grammar checking heritageClause inside class declaration - c.checkGrammarHeritageClause(heritageClause) - } - } - - return false -} - -func (c *Checker) checkGrammarComputedPropertyName(node *ast.Node) bool { - // If node is not a computedPropertyName, just skip the grammar checking - if node.Kind != ast.KindComputedPropertyName { - return false - } - - computedPropertyName := node.AsComputedPropertyName() - if computedPropertyName.Expression.Kind == ast.KindBinaryExpression && (computedPropertyName.Expression.AsBinaryExpression()).OperatorToken.Kind == ast.KindCommaToken { - return c.grammarErrorOnNode(computedPropertyName.Expression, diagnostics.A_comma_expression_is_not_allowed_in_a_computed_property_name) - } - return false -} - -func (c *Checker) checkGrammarForGenerator(node *ast.Node) bool { - if bodyData := node.BodyData(); bodyData != nil && bodyData.AsteriskToken != nil { - if node.Kind != ast.KindFunctionDeclaration && node.Kind != ast.KindFunctionExpression && node.Kind != ast.KindMethodDeclaration { - panic(fmt.Sprintf("Unexpected node kind %q", node.Kind)) - } - if node.Flags&ast.NodeFlagsAmbient != 0 { - return c.grammarErrorOnNode(bodyData.AsteriskToken, diagnostics.Generators_are_not_allowed_in_an_ambient_context) - } - if bodyData.Body == nil { - return c.grammarErrorOnNode(bodyData.AsteriskToken, diagnostics.An_overload_signature_cannot_be_declared_as_a_generator) - } - } - - return false -} - -func (c *Checker) checkGrammarForInvalidQuestionMark(postfixToken *ast.TokenNode, message *diagnostics.Message) bool { - return postfixToken != nil && postfixToken.Kind == ast.KindQuestionToken && c.grammarErrorOnNode(postfixToken, message) -} - -func (c *Checker) checkGrammarForInvalidExclamationToken(postfixToken *ast.TokenNode, message *diagnostics.Message) bool { - return postfixToken != nil && postfixToken.Kind == ast.KindExclamationToken && c.grammarErrorOnNode(postfixToken, message) -} - -func (c *Checker) checkGrammarObjectLiteralExpression(node *ast.ObjectLiteralExpression, inDestructuring bool) bool { - seen := make(map[string]DeclarationMeaning) - - var properties []*ast.Node - if node.Properties != nil { - properties = node.Properties.Nodes - } - for _, prop := range properties { - if prop.Kind == ast.KindSpreadAssignment { - spreadAssignment := prop.AsSpreadAssignment() - if inDestructuring { - // a rest property cannot be destructured any further - expression := ast.SkipParentheses(spreadAssignment.Expression) - if ast.IsArrayLiteralExpression(expression) || ast.IsObjectLiteralExpression(expression) { - return c.grammarErrorOnNode(spreadAssignment.Expression, diagnostics.A_rest_element_cannot_contain_a_binding_pattern) - } - } - continue - } - name := prop.Name() - if name.Kind == ast.KindComputedPropertyName { - // If the name is not a ComputedPropertyName, the grammar checking will skip it - c.checkGrammarComputedPropertyName(name) - } - - if prop.Kind == ast.KindShorthandPropertyAssignment && !inDestructuring { - shorthandProp := prop.AsShorthandPropertyAssignment() - if shorthandProp.ObjectAssignmentInitializer != nil { - // having objectAssignmentInitializer is only valid in an ObjectAssignmentPattern. - // Outside of destructuring, it is a syntax error. - - // Try to grab the last node prior to the initializer, - // then error on the first token following (which should be the `=` token). - var lastNodeBeforeInitializer *ast.Node - shorthandProp.ForEachChild(func(child *ast.Node) bool { - if child != shorthandProp.ObjectAssignmentInitializer { - lastNodeBeforeInitializer = child - return false - } - return true - }) - - c.grammarErrorOnFirstToken(lastNodeBeforeInitializer, diagnostics.Did_you_mean_to_use_a_Colon_An_can_only_follow_a_property_name_when_the_containing_object_literal_is_part_of_a_destructuring_pattern) - } - } - - if name.Kind == ast.KindPrivateIdentifier { - c.grammarErrorOnNode(name, diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies) - } - - // Modifiers are never allowed on properties except for 'async' on a method declaration - if modifiers := prop.Modifiers(); modifiers != nil { - if ast.CanHaveModifiers(prop) { - for _, mod := range modifiers.Nodes { - if ast.IsModifier(mod) && (mod.Kind != ast.KindAsyncKeyword || prop.Kind != ast.KindMethodDeclaration) { - c.grammarErrorOnNode(mod, diagnostics.X_0_modifier_cannot_be_used_here, scanner.GetTextOfNode(mod)) - } - } - } else if ast.CanHaveIllegalModifiers(prop) { - for _, mod := range modifiers.Nodes { - if ast.IsModifier(mod) { - c.grammarErrorOnNode(mod, diagnostics.X_0_modifier_cannot_be_used_here, scanner.GetTextOfNode(mod)) - } - } - } - } - - // ECMA-262 11.1.5 Object Initializer - // If previous is not undefined then throw a SyntaxError exception if any of the following conditions are true - // a.This production is contained in strict code and IsDataDescriptor(previous) is true and - // IsDataDescriptor(propId.descriptor) is true. - // b.IsDataDescriptor(previous) is true and IsAccessorDescriptor(propId.descriptor) is true. - // c.IsAccessorDescriptor(previous) is true and IsDataDescriptor(propId.descriptor) is true. - // d.IsAccessorDescriptor(previous) is true and IsAccessorDescriptor(propId.descriptor) is true - // and either both previous and propId.descriptor have[[Get]] fields or both previous and propId.descriptor have[[Set]] fields - var currentKind DeclarationMeaning - switch prop.Kind { - case ast.KindShorthandPropertyAssignment, - ast.KindPropertyAssignment: - var commonProp *ast.NamedMemberBase - if prop.Kind == ast.KindShorthandPropertyAssignment { - prop.ClassLikeData() - commonProp = &prop.AsShorthandPropertyAssignment().NamedMemberBase - } else { - commonProp = &prop.AsPropertyAssignment().NamedMemberBase - } - - // Grammar checking for computedPropertyName and shorthandPropertyAssignment - c.checkGrammarForInvalidExclamationToken(commonProp.PostfixToken, diagnostics.A_definite_assignment_assertion_is_not_permitted_in_this_context) - c.checkGrammarForInvalidQuestionMark(commonProp.PostfixToken, diagnostics.An_object_member_cannot_be_declared_optional) - - if name.Kind == ast.KindNumericLiteral { - c.checkGrammarNumericLiteral(name.AsNumericLiteral()) - } - - if name.Kind == ast.KindBigIntLiteral { - c.addErrorOrSuggestion(true, createDiagnosticForNode(name, diagnostics.A_bigint_literal_cannot_be_used_as_a_property_name)) - } - - currentKind = DeclarationMeaningPropertyAssignment - case ast.KindMethodDeclaration: - currentKind = DeclarationMeaningMethod - case ast.KindGetAccessor: - currentKind = DeclarationMeaningGetAccessor - case ast.KindSetAccessor: - currentKind = DeclarationMeaningSetAccessor - default: - panic(fmt.Sprintf("Unexpected node kind %q", prop.Kind)) - } - - if !inDestructuring { - effectiveName, ok := c.getEffectivePropertyNameForPropertyNameNode(name) - if !ok { - continue - } - - existingKind := seen[effectiveName] - if existingKind == 0 { - seen[effectiveName] = currentKind - } else { - if (currentKind&DeclarationMeaningMethod != 0) && (existingKind&DeclarationMeaningMethod != 0) { - c.grammarErrorOnNode(name, diagnostics.Duplicate_identifier_0, scanner.GetTextOfNode(name)) - } else if (currentKind&DeclarationMeaningPropertyAssignment != 0) && (existingKind&DeclarationMeaningPropertyAssignment != 0) { - c.grammarErrorOnNode(name, diagnostics.An_object_literal_cannot_have_multiple_properties_with_the_same_name, scanner.GetTextOfNode(name)) - } else if (currentKind&DeclarationMeaningGetOrSetAccessor != 0) && (existingKind&DeclarationMeaningGetOrSetAccessor != 0) { - if existingKind != DeclarationMeaningGetOrSetAccessor && currentKind != existingKind { - seen[effectiveName] = currentKind | existingKind - } else { - return c.grammarErrorOnNode(name, diagnostics.An_object_literal_cannot_have_multiple_get_Slashset_accessors_with_the_same_name) - } - } else { - return c.grammarErrorOnNode(name, diagnostics.An_object_literal_cannot_have_property_and_accessor_with_the_same_name) - } - } - } - } - - return false -} - -func (c *Checker) checkGrammarJsxElement(node *ast.Node) bool { - c.checkGrammarJsxName(node.TagName()) - c.checkGrammarTypeArguments(node, node.TypeArgumentList()) - var seen collections.Set[string] - for _, attrNode := range node.Attributes().AsJsxAttributes().Properties.Nodes { - if attrNode.Kind == ast.KindJsxSpreadAttribute { - continue - } - attr := attrNode.AsJsxAttribute() - name := attr.Name() - initializer := attr.Initializer - textOfName := name.Text() - if !seen.Has(textOfName) { - seen.Add(textOfName) - } else { - return c.grammarErrorOnNode(name, diagnostics.JSX_elements_cannot_have_multiple_attributes_with_the_same_name) - } - if initializer != nil && initializer.Kind == ast.KindJsxExpression && initializer.Expression() == nil { - return c.grammarErrorOnNode(initializer, diagnostics.JSX_attributes_must_only_be_assigned_a_non_empty_expression) - } - } - return false -} - -func (c *Checker) checkGrammarJsxName(node *ast.JsxTagNameExpression) bool { - if ast.IsPropertyAccessExpression(node) && ast.IsJsxNamespacedName(node.Expression()) { - return c.grammarErrorOnNode(node.Expression(), diagnostics.JSX_property_access_expressions_cannot_include_JSX_namespace_names) - } - - if ast.IsJsxNamespacedName(node) && c.compilerOptions.GetJSXTransformEnabled() && !scanner.IsIntrinsicJsxName(node.AsJsxNamespacedName().Namespace.Text()) { - return c.grammarErrorOnNode(node, diagnostics.React_components_cannot_include_JSX_namespace_names) - } - - return false -} - -func (c *Checker) checkGrammarJsxExpression(node *ast.JsxExpression) bool { - if node.Expression != nil && ast.IsCommaSequence(node.Expression) { - return c.grammarErrorOnNode(node.Expression, diagnostics.JSX_expressions_may_not_use_the_comma_operator_Did_you_mean_to_write_an_array) - } - - return false -} - -func (c *Checker) checkGrammarForInOrForOfStatement(forInOrOfStatement *ast.ForInOrOfStatement) bool { - asNode := forInOrOfStatement.AsNode() - if c.checkGrammarStatementInAmbientContext(asNode) { - return true - } - - if forInOrOfStatement.Kind == ast.KindForOfStatement && forInOrOfStatement.AwaitModifier != nil { - if forInOrOfStatement.Flags&ast.NodeFlagsAwaitContext == 0 { - sourceFile := ast.GetSourceFileOfNode(asNode) - if ast.IsInTopLevelContext(asNode) { - if !c.hasParseDiagnostics(sourceFile) { - if !ast.IsEffectiveExternalModule(sourceFile, c.compilerOptions) { - c.diagnostics.Add(createDiagnosticForNode(forInOrOfStatement.AwaitModifier, diagnostics.X_for_await_loops_are_only_allowed_at_the_top_level_of_a_file_when_that_file_is_a_module_but_this_file_has_no_imports_or_exports_Consider_adding_an_empty_export_to_make_this_file_a_module)) - } - switch c.moduleKind { - case core.ModuleKindNode16, core.ModuleKindNode18, core.ModuleKindNode20, core.ModuleKindNodeNext: - sourceFileMetaData := c.program.GetSourceFileMetaData(sourceFile.Path()) - if sourceFileMetaData.ImpliedNodeFormat == core.ModuleKindCommonJS { - c.diagnostics.Add(createDiagnosticForNode(forInOrOfStatement.AwaitModifier, diagnostics.The_current_file_is_a_CommonJS_module_and_cannot_use_await_at_the_top_level)) - break - } - fallthrough - case core.ModuleKindES2022, - core.ModuleKindESNext, - core.ModuleKindPreserve, - core.ModuleKindSystem: - if c.languageVersion >= core.ScriptTargetES2017 { - break - } - fallthrough - default: - c.diagnostics.Add(createDiagnosticForNode(forInOrOfStatement.AwaitModifier, diagnostics.Top_level_for_await_loops_are_only_allowed_when_the_module_option_is_set_to_es2022_esnext_system_node16_node18_node20_nodenext_or_preserve_and_the_target_option_is_set_to_es2017_or_higher)) - } - } - } else { - // use of 'for-await-of' in non-async function - if !c.hasParseDiagnostics(sourceFile) { - diagnostic := createDiagnosticForNode(forInOrOfStatement.AwaitModifier, diagnostics.X_for_await_loops_are_only_allowed_within_async_functions_and_at_the_top_levels_of_modules) - containingFunc := ast.GetContainingFunction(forInOrOfStatement.AsNode()) - if containingFunc != nil && containingFunc.Kind != ast.KindConstructor { - debug.Assert((getFunctionFlags(containingFunc)&FunctionFlagsAsync) == 0, "Enclosing function should never be an async function.") - if hasAsyncModifier(containingFunc) { - panic("Enclosing function should never be an async function.") - } - relatedInfo := createDiagnosticForNode(containingFunc, diagnostics.Did_you_mean_to_mark_this_function_as_async) - diagnostic.AddRelatedInfo(relatedInfo) - } - c.diagnostics.Add(diagnostic) - return true - } - } - } - } - - if ast.IsForOfStatement(asNode) && forInOrOfStatement.Flags&ast.NodeFlagsAwaitContext == 0 && ast.IsIdentifier(forInOrOfStatement.Initializer) && forInOrOfStatement.Initializer.Text() == "async" { - c.grammarErrorOnNode(forInOrOfStatement.Initializer, diagnostics.The_left_hand_side_of_a_for_of_statement_may_not_be_async) - return false - } - - if forInOrOfStatement.Initializer.Kind == ast.KindVariableDeclarationList { - variableList := forInOrOfStatement.Initializer.AsVariableDeclarationList() - if !c.checkGrammarVariableDeclarationList(variableList) { - declarations := variableList.Declarations - - // declarations.length can be zero if there is an error in variable declaration in for-of or for-in - // See http://www.ecma-international.org/ecma-262/6.0/#sec-for-in-and-for-of-statements for details - // For example: - // var let = 10; - // for (let of [1,2,3]) {} // this is invalid ES6 syntax - // for (let in [1,2,3]) {} // this is invalid ES6 syntax - // We will then want to skip on grammar checking on variableList declaration - if len(declarations.Nodes) == 0 { - return false - } - - if len(declarations.Nodes) > 1 { - var diagnostic *diagnostics.Message - if forInOrOfStatement.Kind == ast.KindForInStatement { - diagnostic = diagnostics.Only_a_single_variable_declaration_is_allowed_in_a_for_in_statement - } else { - diagnostic = diagnostics.Only_a_single_variable_declaration_is_allowed_in_a_for_of_statement - } - return c.grammarErrorOnFirstToken(declarations.Nodes[1], diagnostic) - } - - firstVariableDeclaration := declarations.Nodes[0].AsVariableDeclaration() - if firstVariableDeclaration.Initializer != nil { - var diagnostic *diagnostics.Message - if forInOrOfStatement.Kind == ast.KindForInStatement { - diagnostic = diagnostics.The_variable_declaration_of_a_for_in_statement_cannot_have_an_initializer - } else { - diagnostic = diagnostics.The_variable_declaration_of_a_for_of_statement_cannot_have_an_initializer - } - return c.grammarErrorOnNode(firstVariableDeclaration.Name(), diagnostic) - } - if firstVariableDeclaration.Type != nil { - var diagnostic *diagnostics.Message - if forInOrOfStatement.Kind == ast.KindForInStatement { - diagnostic = diagnostics.The_left_hand_side_of_a_for_in_statement_cannot_use_a_type_annotation - } else { - diagnostic = diagnostics.The_left_hand_side_of_a_for_of_statement_cannot_use_a_type_annotation - } - return c.grammarErrorOnNode(firstVariableDeclaration.AsNode(), diagnostic) - } - } - } - - return false -} - -func (c *Checker) checkGrammarAccessor(accessor *ast.AccessorDeclaration) bool { - body := accessor.Body() - if accessor.Flags&ast.NodeFlagsAmbient == 0 && (accessor.Parent.Kind != ast.KindTypeLiteral) && (accessor.Parent.Kind != ast.KindInterfaceDeclaration) { - if c.languageVersion < core.ScriptTargetES2015 && ast.IsPrivateIdentifier(accessor.Name()) { - return c.grammarErrorOnNode(accessor.Name(), diagnostics.Private_identifiers_are_only_available_when_targeting_ECMAScript_2015_and_higher) - } - if body == nil && !ast.HasSyntacticModifier(accessor, ast.ModifierFlagsAbstract) { - return c.grammarErrorAtPos(accessor, accessor.End()-1, len(";"), diagnostics.X_0_expected, "{") - } - } - if body != nil { - if ast.HasSyntacticModifier(accessor, ast.ModifierFlagsAbstract) { - return c.grammarErrorOnNode(accessor, diagnostics.An_abstract_accessor_cannot_have_an_implementation) - } - if accessor.Parent.Kind == ast.KindTypeLiteral || accessor.Parent.Kind == ast.KindInterfaceDeclaration { - return c.grammarErrorOnNode(body, diagnostics.An_implementation_cannot_be_declared_in_ambient_contexts) - } - } - - funcData := accessor.FunctionLikeData() - var typeParameters *ast.NodeList - if funcData != nil { - typeParameters = funcData.TypeParameters - } - - if typeParameters != nil { - return c.grammarErrorOnNode(accessor.Name(), diagnostics.An_accessor_cannot_have_type_parameters) - } - if !c.doesAccessorHaveCorrectParameterCount(accessor) { - return c.grammarErrorOnNode(accessor.Name(), core.IfElse(accessor.Kind == ast.KindGetAccessor, diagnostics.A_get_accessor_cannot_have_parameters, diagnostics.A_set_accessor_must_have_exactly_one_parameter)) - } - if accessor.Kind == ast.KindSetAccessor { - if funcData.Type != nil { - return c.grammarErrorOnNode(accessor.Name(), diagnostics.A_set_accessor_cannot_have_a_return_type_annotation) - } - - parameterNode := getSetAccessorValueParameter(accessor) - if parameterNode == nil { - panic("Return value does not match parameter count assertion.") - } - parameter := parameterNode.AsParameterDeclaration() - if parameter.DotDotDotToken != nil { - return c.grammarErrorOnNode(parameter.DotDotDotToken, diagnostics.A_set_accessor_cannot_have_rest_parameter) - } - if parameter.QuestionToken != nil { - return c.grammarErrorOnNode(parameter.QuestionToken, diagnostics.A_set_accessor_cannot_have_an_optional_parameter) - } - if parameter.Initializer != nil { - return c.grammarErrorOnNode(accessor.Name(), diagnostics.A_set_accessor_parameter_cannot_have_an_initializer) - } - } - - return false -} - -// Does the accessor have the right number of parameters? -// -// A `get` accessor has no parameters or a single `this` parameter. -// A `set` accessor has one parameter or a `this` parameter and one more parameter. -func (c *Checker) doesAccessorHaveCorrectParameterCount(accessor *ast.AccessorDeclaration) bool { - // `getAccessorThisParameter` returns `nil` if the accessor's arity is incorrect, - // even if there is a `this` parameter declared. - return c.getAccessorThisParameter(accessor) != nil || len(accessor.Parameters()) == (core.IfElse(accessor.Kind == ast.KindGetAccessor, 0, 1)) -} - -func (c *Checker) checkGrammarTypeOperatorNode(node *ast.TypeOperatorNode) bool { - if node.Operator == ast.KindUniqueKeyword { - innerType := node.AsTypeOperatorNode().Type - if innerType.Kind != ast.KindSymbolKeyword { - return c.grammarErrorOnNode(innerType, diagnostics.X_0_expected, scanner.TokenToString(ast.KindSymbolKeyword)) - } - parent := ast.WalkUpParenthesizedTypes(node.Parent) - switch parent.Kind { - case ast.KindVariableDeclaration: - decl := parent.AsVariableDeclaration() - if decl.Name().Kind != ast.KindIdentifier { - return c.grammarErrorOnNode(node.AsNode(), diagnostics.X_unique_symbol_types_may_not_be_used_on_a_variable_declaration_with_a_binding_name) - } - if !isVariableDeclarationInVariableStatement(decl.AsNode()) { - return c.grammarErrorOnNode(node.AsNode(), diagnostics.X_unique_symbol_types_are_only_allowed_on_variables_in_a_variable_statement) - } - if decl.Parent.Flags&ast.NodeFlagsConst == 0 { - return c.grammarErrorOnNode((parent.AsVariableDeclaration()).Name(), diagnostics.A_variable_whose_type_is_a_unique_symbol_type_must_be_const) - } - case ast.KindPropertyDeclaration: - if !ast.IsStatic(parent) || !hasReadonlyModifier(parent) { - return c.grammarErrorOnNode((parent.AsPropertyDeclaration()).Name(), diagnostics.A_property_of_a_class_whose_type_is_a_unique_symbol_type_must_be_both_static_and_readonly) - } - case ast.KindPropertySignature: - if !ast.HasSyntacticModifier(parent, ast.ModifierFlagsReadonly) { - return c.grammarErrorOnNode((parent.AsPropertySignatureDeclaration()).Name(), diagnostics.A_property_of_an_interface_or_type_literal_whose_type_is_a_unique_symbol_type_must_be_readonly) - } - default: - return c.grammarErrorOnNode(node.AsNode(), diagnostics.X_unique_symbol_types_are_not_allowed_here) - } - } else if node.Operator == ast.KindReadonlyKeyword { - innerType := node.AsTypeOperatorNode().Type - if innerType.Kind != ast.KindArrayType && innerType.Kind != ast.KindTupleType { - return c.grammarErrorOnFirstToken(node.AsNode(), diagnostics.X_readonly_type_modifier_is_only_permitted_on_array_and_tuple_literal_types, scanner.TokenToString(ast.KindSymbolKeyword)) - } - } - - return false -} - -func (c *Checker) checkGrammarForInvalidDynamicName(node *ast.DeclarationName, message *diagnostics.Message) bool { - if !c.isNonBindableDynamicName(node) { - return false - } - var expression *ast.Node - if ast.IsElementAccessExpression(node) { - expression = ast.SkipParentheses(node.AsElementAccessExpression().ArgumentExpression) - } else { - expression = node.Expression() - } - - if !ast.IsEntityNameExpression(expression) { - return c.grammarErrorOnNode(node, message) - } - - return false -} - -// Indicates whether a declaration name is a dynamic name that cannot be late-bound. -func (c *Checker) isNonBindableDynamicName(node *ast.DeclarationName) bool { - return ast.IsDynamicName(node) && !c.isLateBindableName(node) -} - -func (c *Checker) checkGrammarMethod(node *ast.Node /*Union[MethodDeclaration, MethodSignature]*/) bool { - if c.checkGrammarFunctionLikeDeclaration(node) { - return true - } - - if node.Kind == ast.KindMethodDeclaration { - if node.Parent.Kind == ast.KindObjectLiteralExpression { - // We only disallow modifier on a method declaration if it is a property of object-literal-expression - if modifiers := node.Modifiers(); modifiers != nil && !(len(modifiers.Nodes) == 1 && modifiers.Nodes[0].Kind == ast.KindAsyncKeyword) { - return c.grammarErrorOnFirstToken(node, diagnostics.Modifiers_cannot_appear_here) - } - - methodDecl := node.AsMethodDeclaration() - if c.checkGrammarForInvalidQuestionMark(methodDecl.PostfixToken, diagnostics.An_object_member_cannot_be_declared_optional) { - return true - } - if c.checkGrammarForInvalidExclamationToken(methodDecl.PostfixToken, diagnostics.A_definite_assignment_assertion_is_not_permitted_in_this_context) { - return true - } - if node.Body() == nil { - return c.grammarErrorAtPos(node, node.End()-1, len(";"), diagnostics.X_0_expected, "{") - } - } - if c.checkGrammarForGenerator(node) { - return true - } - } - - if ast.IsClassLike(node.Parent) { - if c.languageVersion < core.ScriptTargetES2015 && ast.IsPrivateIdentifier(node.Name()) { - return c.grammarErrorOnNode(node.Name(), diagnostics.Private_identifiers_are_only_available_when_targeting_ECMAScript_2015_and_higher) - } - // Technically, computed properties in ambient contexts is disallowed - // for property declarations and accessors too, not just methods. - // However, property declarations disallow computed names in general, - // and accessors are not allowed in ambient contexts in general, - // so this error only really matters for methods. - if node.Flags&ast.NodeFlagsAmbient != 0 { - return c.checkGrammarForInvalidDynamicName(node.Name(), diagnostics.A_computed_property_name_in_an_ambient_context_must_refer_to_an_expression_whose_type_is_a_literal_type_or_a_unique_symbol_type) - } else if node.Kind == ast.KindMethodDeclaration && node.Body() == nil { - return c.checkGrammarForInvalidDynamicName(node.Name(), diagnostics.A_computed_property_name_in_a_method_overload_must_refer_to_an_expression_whose_type_is_a_literal_type_or_a_unique_symbol_type) - } - } else if node.Parent.Kind == ast.KindInterfaceDeclaration { - return c.checkGrammarForInvalidDynamicName(node.Name(), diagnostics.A_computed_property_name_in_an_interface_must_refer_to_an_expression_whose_type_is_a_literal_type_or_a_unique_symbol_type) - } else if node.Parent.Kind == ast.KindTypeLiteral { - return c.checkGrammarForInvalidDynamicName(node.Name(), diagnostics.A_computed_property_name_in_a_type_literal_must_refer_to_an_expression_whose_type_is_a_literal_type_or_a_unique_symbol_type) - } - - return false -} - -func (c *Checker) checkGrammarBreakOrContinueStatement(node *ast.Node) bool { - var targetLabel *ast.IdentifierNode - switch node.Kind { - case ast.KindBreakStatement: - targetLabel = node.AsBreakStatement().Label - case ast.KindContinueStatement: - targetLabel = node.AsContinueStatement().Label - default: - panic(fmt.Sprintf("Unexpected node kind %q", node.Kind)) - } - - var current *ast.Node = node - for current != nil { - if ast.IsFunctionLikeOrClassStaticBlockDeclaration(current) { - return c.grammarErrorOnNode(node, diagnostics.Jump_target_cannot_cross_function_boundary) - } - - switch current.Kind { - case ast.KindLabeledStatement: - if targetLabel != nil && (current.AsLabeledStatement()).Label.Text() == targetLabel.Text() { - // found matching label - verify that label usage is correct - // continue can only target labels that are on iteration statements - isMisplacedContinueLabel := node.Kind == ast.KindContinueStatement && !ast.IsIterationStatement((current.AsLabeledStatement()).Statement, true /*lookInLabeledStatements*/) - - if isMisplacedContinueLabel { - return c.grammarErrorOnNode(node, diagnostics.A_continue_statement_can_only_jump_to_a_label_of_an_enclosing_iteration_statement) - } - - return false - } - case ast.KindSwitchStatement: - if node.Kind == ast.KindBreakStatement && targetLabel == nil { - // unlabeled break within switch statement - ok - return false - } - default: - if ast.IsIterationStatement(current, false /*lookInLabeledStatements*/) && targetLabel == nil { - // unlabeled break or continue within iteration statement - ok - return false - } - } - - current = current.Parent - } - - if targetLabel != nil { - var message *diagnostics.Message - if node.Kind == ast.KindBreakStatement { - message = diagnostics.A_break_statement_can_only_jump_to_a_label_of_an_enclosing_statement - } else { - message = diagnostics.A_continue_statement_can_only_jump_to_a_label_of_an_enclosing_iteration_statement - } - - return c.grammarErrorOnNode(node, message) - } else { - var message *diagnostics.Message - if node.Kind == ast.KindBreakStatement { - message = diagnostics.A_break_statement_can_only_be_used_within_an_enclosing_iteration_or_switch_statement - } else { - message = diagnostics.A_continue_statement_can_only_be_used_within_an_enclosing_iteration_statement - } - return c.grammarErrorOnNode(node, message) - } -} - -func (c *Checker) checkGrammarBindingElement(node *ast.BindingElement) bool { - if node.DotDotDotToken != nil { - elements := node.Parent.AsBindingPattern().Elements - if node.AsNode() != core.LastOrNil(elements.Nodes) { - return c.grammarErrorOnNode(&node.Node, diagnostics.A_rest_element_must_be_last_in_a_destructuring_pattern) - } - c.checkGrammarForDisallowedTrailingComma(elements, diagnostics.A_rest_parameter_or_binding_pattern_may_not_have_a_trailing_comma) - - if node.PropertyName != nil { - return c.grammarErrorOnNode(node.Name(), diagnostics.A_rest_element_cannot_have_a_property_name) - } - } - - if node.DotDotDotToken != nil && node.Initializer != nil { - // Error on equals token which immediately precedes the initializer - return c.grammarErrorAtPos(node.AsNode(), node.Initializer.Pos()-1, 1, diagnostics.A_rest_element_cannot_have_an_initializer) - } - - return false -} - -func (c *Checker) checkGrammarVariableDeclaration(node *ast.VariableDeclaration) bool { - nodeFlags := c.getCombinedNodeFlagsCached(node.AsNode()) - blockScopeKind := nodeFlags & ast.NodeFlagsBlockScoped - if ast.IsBindingPattern(node.Name()) { - switch blockScopeKind { - case ast.NodeFlagsAwaitUsing: - return c.grammarErrorOnNode(node.AsNode(), diagnostics.X_0_declarations_may_not_have_binding_patterns, "await using") - case ast.NodeFlagsUsing: - return c.grammarErrorOnNode(node.AsNode(), diagnostics.X_0_declarations_may_not_have_binding_patterns, "using") - } - } - - if node.Parent.Parent.Kind != ast.KindForInStatement && node.Parent.Parent.Kind != ast.KindForOfStatement { - if nodeFlags&ast.NodeFlagsAmbient != 0 { - c.checkAmbientInitializer(node.AsNode()) - } else if node.Initializer == nil { - if ast.IsBindingPattern(node.Name()) && !ast.IsBindingPattern(node.Parent) { - return c.grammarErrorOnNode(node.AsNode(), diagnostics.A_destructuring_declaration_must_have_an_initializer) - } - switch blockScopeKind { - case ast.NodeFlagsAwaitUsing: - return c.grammarErrorOnNode(node.AsNode(), diagnostics.X_0_declarations_must_be_initialized, "await using") - case ast.NodeFlagsUsing: - return c.grammarErrorOnNode(node.AsNode(), diagnostics.X_0_declarations_must_be_initialized, "using") - case ast.NodeFlagsConst: - return c.grammarErrorOnNode(node.AsNode(), diagnostics.X_0_declarations_must_be_initialized, "const") - } - } - } - - if node.ExclamationToken != nil && (node.Parent.Parent.Kind != ast.KindVariableStatement || node.Type == nil || node.Initializer != nil || nodeFlags&ast.NodeFlagsAmbient != 0) { - var message *diagnostics.Message - switch { - case node.Initializer != nil: - message = diagnostics.Declarations_with_initializers_cannot_also_have_definite_assignment_assertions - case node.Type == nil: - message = diagnostics.Declarations_with_definite_assignment_assertions_must_also_have_type_annotations - default: - message = diagnostics.A_definite_assignment_assertion_is_not_permitted_in_this_context - } - return c.grammarErrorOnNode(node.ExclamationToken, message) - } - - if c.program.GetEmitModuleFormatOfFile(ast.GetSourceFileOfNode(node.AsNode())) < core.ModuleKindSystem && (node.Parent.Parent.Flags&ast.NodeFlagsAmbient == 0) && ast.HasSyntacticModifier(node.Parent.Parent, ast.ModifierFlagsExport) { - c.checkGrammarForEsModuleMarkerInBindingName(node.Name()) - } - - // 1. LexicalDeclaration : LetOrConst BindingList ; - // It is a Syntax Error if the BoundNames of BindingList contains "let". - // 2. ForDeclaration: ForDeclaration : LetOrConst ForBinding - // It is a Syntax Error if the BoundNames of ForDeclaration contains "let". - - // It is a SyntaxError if a VariableDeclaration or VariableDeclarationNoIn occurs within strict code - // and its Identifier is eval or arguments - return blockScopeKind != 0 && c.checkGrammarNameInLetOrConstDeclarations(node.Name()) -} - -func (c *Checker) checkGrammarForEsModuleMarkerInBindingName(name *ast.Node) bool { - if ast.IsIdentifier(name) { - if name.Text() == "__esModule" { - return c.grammarErrorOnNodeSkippedOnNoEmit(name, diagnostics.Identifier_expected_esModule_is_reserved_as_an_exported_marker_when_transforming_ECMAScript_modules) - } - } else { - for _, element := range name.AsBindingPattern().Elements.Nodes { - if element.Name() != nil { - return c.checkGrammarForEsModuleMarkerInBindingName(element.Name()) - } - } - } - return false -} - -func (c *Checker) checkGrammarNameInLetOrConstDeclarations(name *ast.Node /*Union[Identifier, BindingPattern]*/) bool { - if name.Kind == ast.KindIdentifier { - if name.AsIdentifier().Text == "let" { - return c.grammarErrorOnNode(name, diagnostics.X_let_is_not_allowed_to_be_used_as_a_name_in_let_or_const_declarations) - } - } else { - elements := name.AsBindingPattern().Elements.Nodes - for _, element := range elements { - bindingElement := element.AsBindingElement() - if bindingElement.Name() != nil { - c.checkGrammarNameInLetOrConstDeclarations(bindingElement.Name()) - } - } - } - return false -} - -func (c *Checker) checkGrammarVariableDeclarationList(declarationList *ast.VariableDeclarationList) bool { - declarations := declarationList.Declarations - if c.checkGrammarForDisallowedTrailingComma(declarations, diagnostics.Trailing_comma_not_allowed) { - return true - } - - if len(declarations.Nodes) == 0 { - return c.grammarErrorAtPos(declarationList.AsNode(), declarations.Pos(), declarations.End()-declarations.Pos(), diagnostics.Variable_declaration_list_cannot_be_empty) - } - - blockScopeFlags := declarationList.Flags & ast.NodeFlagsBlockScoped - if blockScopeFlags == ast.NodeFlagsUsing || blockScopeFlags == ast.NodeFlagsAwaitUsing { - if ast.IsForInStatement(declarationList.Parent) { - return c.grammarErrorOnNode(declarationList.AsNode(), core.IfElse(blockScopeFlags == ast.NodeFlagsUsing, diagnostics.The_left_hand_side_of_a_for_in_statement_cannot_be_a_using_declaration, diagnostics.The_left_hand_side_of_a_for_in_statement_cannot_be_an_await_using_declaration)) - } - if declarationList.Flags&ast.NodeFlagsAmbient != 0 { - return c.grammarErrorOnNode(declarationList.AsNode(), core.IfElse(blockScopeFlags == ast.NodeFlagsUsing, diagnostics.X_using_declarations_are_not_allowed_in_ambient_contexts, diagnostics.X_await_using_declarations_are_not_allowed_in_ambient_contexts)) - } - } - - if blockScopeFlags == ast.NodeFlagsAwaitUsing { - return c.checkGrammarAwaitOrAwaitUsing(declarationList.AsNode()) - } - - return false -} - -func (c *Checker) checkGrammarAwaitOrAwaitUsing(node *ast.Node) bool { - // Grammar checking - hasError := false - container := getContainingFunctionOrClassStaticBlock(node) - if container != nil && ast.IsClassStaticBlockDeclaration(container) { - // NOTE: We report this regardless as to whether there are parse diagnostics. - var message *diagnostics.Message - if ast.IsAwaitExpression(node) { - message = diagnostics.X_await_expression_cannot_be_used_inside_a_class_static_block - } else { - message = diagnostics.X_await_using_statements_cannot_be_used_inside_a_class_static_block - } - c.error(node, message) - hasError = true - } else if node.Flags&ast.NodeFlagsAwaitContext == 0 { - if ast.IsInTopLevelContext(node) { - sourceFile := ast.GetSourceFileOfNode(node) - if !c.hasParseDiagnostics(sourceFile) { - var span core.TextRange - var spanCalculated bool - if !ast.IsEffectiveExternalModule(sourceFile, c.compilerOptions) { - span = scanner.GetRangeOfTokenAtPosition(sourceFile, node.Pos()) - spanCalculated = true - var message *diagnostics.Message - if ast.IsAwaitExpression(node) { - message = diagnostics.X_await_expressions_are_only_allowed_at_the_top_level_of_a_file_when_that_file_is_a_module_but_this_file_has_no_imports_or_exports_Consider_adding_an_empty_export_to_make_this_file_a_module - } else { - message = diagnostics.X_await_using_statements_are_only_allowed_at_the_top_level_of_a_file_when_that_file_is_a_module_but_this_file_has_no_imports_or_exports_Consider_adding_an_empty_export_to_make_this_file_a_module - } - diagnostic := ast.NewDiagnostic(sourceFile, span, message) - c.diagnostics.Add(diagnostic) - hasError = true - } - switch c.moduleKind { - case core.ModuleKindNode16, - core.ModuleKindNode18, - core.ModuleKindNode20, - core.ModuleKindNodeNext: - sourceFileMetaData := c.program.GetSourceFileMetaData(sourceFile.Path()) - if sourceFileMetaData.ImpliedNodeFormat == core.ModuleKindCommonJS { - if !spanCalculated { - span = scanner.GetRangeOfTokenAtPosition(sourceFile, node.Pos()) - } - c.diagnostics.Add(ast.NewDiagnostic(sourceFile, span, diagnostics.The_current_file_is_a_CommonJS_module_and_cannot_use_await_at_the_top_level)) - hasError = true - break - } - fallthrough - case core.ModuleKindES2022, - core.ModuleKindESNext, - core.ModuleKindPreserve, - core.ModuleKindSystem: - if c.languageVersion >= core.ScriptTargetES2017 { - break - } - fallthrough - default: - if !spanCalculated { - span = scanner.GetRangeOfTokenAtPosition(sourceFile, node.Pos()) - } - var message *diagnostics.Message - if ast.IsAwaitExpression(node) { - message = diagnostics.Top_level_await_expressions_are_only_allowed_when_the_module_option_is_set_to_es2022_esnext_system_node16_node18_node20_nodenext_or_preserve_and_the_target_option_is_set_to_es2017_or_higher - } else { - message = diagnostics.Top_level_await_using_statements_are_only_allowed_when_the_module_option_is_set_to_es2022_esnext_system_node16_node18_node20_nodenext_or_preserve_and_the_target_option_is_set_to_es2017_or_higher - } - c.diagnostics.Add(ast.NewDiagnostic(sourceFile, span, message)) - hasError = true - } - } - } else { - // use of 'await' in non-async function - sourceFile := ast.GetSourceFileOfNode(node) - if !c.hasParseDiagnostics(sourceFile) { - span := scanner.GetRangeOfTokenAtPosition(sourceFile, node.Pos()) - var message *diagnostics.Message - if ast.IsAwaitExpression(node) { - message = diagnostics.X_await_expressions_are_only_allowed_within_async_functions_and_at_the_top_levels_of_modules - } else { - message = diagnostics.X_await_using_statements_are_only_allowed_within_async_functions_and_at_the_top_levels_of_modules - } - diagnostic := ast.NewDiagnostic(sourceFile, span, message) - if container != nil && container.Kind != ast.KindConstructor && !hasAsyncModifier(container) { - relatedInfo := NewDiagnosticForNode(container, diagnostics.Did_you_mean_to_mark_this_function_as_async) - diagnostic.AddRelatedInfo(relatedInfo) - } - c.diagnostics.Add(diagnostic) - hasError = true - } - } - } - - if ast.IsAwaitExpression(node) && c.isInParameterInitializerBeforeContainingFunction(node) { - // NOTE: We report this regardless as to whether there are parse diagnostics. - c.error(node, diagnostics.X_await_expressions_cannot_be_used_in_a_parameter_initializer) - hasError = true - } - - return hasError -} - -func (c *Checker) checkGrammarYieldExpression(node *ast.Node) bool { - hasError := false - if node.Flags&ast.NodeFlagsYieldContext == 0 { - c.grammarErrorOnFirstToken(node, diagnostics.A_yield_expression_is_only_allowed_in_a_generator_body) - hasError = true - } - if c.isInParameterInitializerBeforeContainingFunction(node) { - c.error(node, diagnostics.X_yield_expressions_cannot_be_used_in_a_parameter_initializer) - hasError = true - } - return hasError -} - -func (c *Checker) checkGrammarForDisallowedBlockScopedVariableStatement(node *ast.VariableStatement) bool { - if !c.containerAllowsBlockScopedVariable(node.Parent) { - blockScopeKind := c.getCombinedNodeFlagsCached(node.DeclarationList) & ast.NodeFlagsBlockScoped - if blockScopeKind != 0 { - var keyword string - switch { - case blockScopeKind == ast.NodeFlagsLet: - keyword = "let" - case blockScopeKind == ast.NodeFlagsConst: - keyword = "const" - case blockScopeKind == ast.NodeFlagsUsing: - keyword = "using" - case blockScopeKind == ast.NodeFlagsAwaitUsing: - keyword = "await using" - default: - panic("Unknown BlockScope flag") - } - c.error(node.AsNode(), diagnostics.X_0_declarations_can_only_be_declared_inside_a_block, keyword) - } - } - - return false -} - -func (c *Checker) containerAllowsBlockScopedVariable(parent *ast.Node) bool { - switch parent.Kind { - case ast.KindIfStatement, - ast.KindDoStatement, - ast.KindWhileStatement, - ast.KindWithStatement, - ast.KindForStatement, - ast.KindForInStatement, - ast.KindForOfStatement: - return false - case ast.KindLabeledStatement: - return c.containerAllowsBlockScopedVariable(parent.Parent) - } - - return true -} - -func (c *Checker) checkGrammarMetaProperty(node *ast.MetaProperty) bool { - nodeName := node.Name() - nameText := nodeName.Text() - - switch node.KeywordToken { - case ast.KindNewKeyword: - if nameText != "target" { - return c.grammarErrorOnNode(nodeName, diagnostics.X_0_is_not_a_valid_meta_property_for_keyword_1_Did_you_mean_2, nameText, scanner.TokenToString(node.KeywordToken), "target") - } - case ast.KindImportKeyword: - if nameText != "meta" { - isCallee := ast.IsCallExpression(node.Parent) && node.Parent.AsCallExpression().Expression == node.AsNode() - if nameText == "defer" { - if !isCallee { - return c.grammarErrorAtPos(node.AsNode(), node.AsNode().End(), 0, diagnostics.X_0_expected, "(") - } - } else { - if isCallee { - return c.grammarErrorOnNode(nodeName, diagnostics.X_0_is_not_a_valid_meta_property_for_keyword_import_Did_you_mean_meta_or_defer, nameText) - } - return c.grammarErrorOnNode(nodeName, diagnostics.X_0_is_not_a_valid_meta_property_for_keyword_1_Did_you_mean_2, nameText, scanner.TokenToString(node.KeywordToken), "meta") - } - } - } - - return false -} - -func (c *Checker) checkGrammarConstructorTypeParameters(node *ast.ConstructorDeclaration) bool { - range_ := node.TypeParameters - if range_ != nil { - var pos int - if range_.Pos() == range_.End() { - pos = range_.Pos() - } else { - pos = scanner.SkipTrivia(ast.GetSourceFileOfNode(node.AsNode()).Text(), range_.Pos()) - } - return c.grammarErrorAtPos(node.AsNode(), pos, range_.End()-pos, diagnostics.Type_parameters_cannot_appear_on_a_constructor_declaration) - } - - return false -} - -func (c *Checker) checkGrammarConstructorTypeAnnotation(node *ast.ConstructorDeclaration) bool { - t := node.Type - if t != nil { - return c.grammarErrorOnNode(t, diagnostics.Type_annotation_cannot_appear_on_a_constructor_declaration) - } - return false -} - -func (c *Checker) checkGrammarProperty(node *ast.Node /*Union[PropertyDeclaration, PropertySignature]*/) bool { - propertyName := node.Name() - if ast.IsComputedPropertyName(propertyName) && ast.IsBinaryExpression(propertyName.Expression()) && propertyName.Expression().AsBinaryExpression().OperatorToken.Kind == ast.KindInKeyword { - return c.grammarErrorOnNode(node.Parent.Members()[0], diagnostics.A_mapped_type_may_not_declare_properties_or_methods) - } - if ast.IsClassLike(node.Parent) { - if ast.IsStringLiteral(propertyName) && propertyName.Text() == "constructor" { - return c.grammarErrorOnNode(propertyName, diagnostics.Classes_may_not_have_a_field_named_constructor) - } - if c.checkGrammarForInvalidDynamicName(propertyName, diagnostics.A_computed_property_name_in_a_class_property_declaration_must_have_a_simple_literal_type_or_a_unique_symbol_type) { - return true - } - if c.languageVersion < core.ScriptTargetES2015 && ast.IsPrivateIdentifier(propertyName) { - return c.grammarErrorOnNode(propertyName, diagnostics.Private_identifiers_are_only_available_when_targeting_ECMAScript_2015_and_higher) - } - if c.languageVersion < core.ScriptTargetES2015 && ast.IsAutoAccessorPropertyDeclaration(node) && node.Flags&ast.NodeFlagsAmbient == 0 { - return c.grammarErrorOnNode(propertyName, diagnostics.Properties_with_the_accessor_modifier_are_only_available_when_targeting_ECMAScript_2015_and_higher) - } - if ast.IsAutoAccessorPropertyDeclaration(node) && c.checkGrammarForInvalidQuestionMark(node.AsPropertyDeclaration().PostfixToken, diagnostics.An_accessor_property_cannot_be_declared_optional) { - return true - } - } else if ast.IsInterfaceDeclaration(node.Parent) { - if c.checkGrammarForInvalidDynamicName(propertyName, diagnostics.A_computed_property_name_in_an_interface_must_refer_to_an_expression_whose_type_is_a_literal_type_or_a_unique_symbol_type) { - return true - } - if !ast.IsPropertySignatureDeclaration(node) { - // Interfaces cannot contain property declarations - panic(fmt.Sprintf("Unexpected node kind %q", node.Kind)) - } - if initializer := node.AsPropertySignatureDeclaration().Initializer; initializer != nil { - return c.grammarErrorOnNode(initializer, diagnostics.An_interface_property_cannot_have_an_initializer) - } - } else if ast.IsTypeLiteralNode(node.Parent) { - if c.checkGrammarForInvalidDynamicName(node.Name(), diagnostics.A_computed_property_name_in_a_type_literal_must_refer_to_an_expression_whose_type_is_a_literal_type_or_a_unique_symbol_type) { - return true - } - if !ast.IsPropertySignatureDeclaration(node) { - // Type literals cannot contain property declarations - panic(fmt.Sprintf("Unexpected node kind %q", node.Kind)) - } - if initializer := node.AsPropertySignatureDeclaration().Initializer; initializer != nil { - return c.grammarErrorOnNode(initializer, diagnostics.A_type_literal_property_cannot_have_an_initializer) - } - } - - if node.Flags&ast.NodeFlagsAmbient != 0 { - c.checkAmbientInitializer(node) - } - - if ast.IsPropertyDeclaration(node) { - propDecl := node.AsPropertyDeclaration() - postfixToken := propDecl.PostfixToken - if postfixToken != nil && postfixToken.Kind == ast.KindExclamationToken { - switch { - case propDecl.Initializer != nil: - return c.grammarErrorOnNode(postfixToken, diagnostics.Declarations_with_initializers_cannot_also_have_definite_assignment_assertions) - case propDecl.Type == nil: - return c.grammarErrorOnNode(postfixToken, diagnostics.Declarations_with_definite_assignment_assertions_must_also_have_type_annotations) - case !ast.IsClassLike(node.Parent) || node.Flags&ast.NodeFlagsAmbient != 0 || ast.IsStatic(node) || hasAbstractModifier(node): - return c.grammarErrorOnNode(postfixToken, diagnostics.A_definite_assignment_assertion_is_not_permitted_in_this_context) - } - } - } - - return false -} - -func (c *Checker) checkAmbientInitializer(node *ast.Node) bool { - var initializer *ast.Expression - var typeNode *ast.TypeNode - switch node.Kind { - case ast.KindVariableDeclaration: - varDecl := node.AsVariableDeclaration() - initializer = varDecl.Initializer - typeNode = varDecl.Type - case ast.KindPropertyDeclaration: - propDecl := node.AsPropertyDeclaration() - initializer = propDecl.Initializer - typeNode = propDecl.Type - case ast.KindPropertySignature: - propSig := node.AsPropertySignatureDeclaration() - initializer = propSig.Initializer - typeNode = propSig.Type - default: - panic(fmt.Sprintf("Unexpected node kind %q", node.Kind)) - } - - if initializer != nil { - isInvalidInitializer := !(isInitializerStringOrNumberLiteralExpression(initializer) || c.isInitializerSimpleLiteralEnumReference(initializer) || initializer.Kind == ast.KindTrueKeyword || initializer.Kind == ast.KindFalseKeyword || isInitializerBigIntLiteralExpression(initializer)) - isConstOrReadonly := isDeclarationReadonly(node) || ast.IsVariableDeclaration(node) && (c.isVarConstLike(node)) - if isConstOrReadonly && (typeNode == nil) { - if isInvalidInitializer { - return c.grammarErrorOnNode(initializer, diagnostics.A_const_initializer_in_an_ambient_context_must_be_a_string_or_numeric_literal_or_literal_enum_reference) - } - } else { - return c.grammarErrorOnNode(initializer, diagnostics.Initializers_are_not_allowed_in_ambient_contexts) - } - } - - return false -} - -func isInitializerStringOrNumberLiteralExpression(expr *ast.Expression) bool { - return ast.IsStringOrNumericLiteralLike(expr) || - expr.Kind == ast.KindPrefixUnaryExpression && (expr.AsPrefixUnaryExpression()).Operator == ast.KindMinusToken && (expr.AsPrefixUnaryExpression()).Operand.Kind == ast.KindNumericLiteral -} - -func isInitializerBigIntLiteralExpression(expr *ast.Expression) bool { - if expr.Kind == ast.KindBigIntLiteral { - return true - } - - if expr.Kind == ast.KindPrefixUnaryExpression { - unaryExpr := expr.AsPrefixUnaryExpression() - return unaryExpr.Operator == ast.KindMinusToken && unaryExpr.Operand.Kind == ast.KindBigIntLiteral - } - - return false -} - -func (c *Checker) isInitializerSimpleLiteralEnumReference(expr *ast.Expression) bool { - if ast.IsPropertyAccessExpression(expr) { - return c.checkExpressionCached(expr).flags&TypeFlagsEnumLike != 0 - } - - if ast.IsElementAccessExpression(expr) { - elementAccess := expr.AsElementAccessExpression() - - return isInitializerStringOrNumberLiteralExpression(elementAccess.ArgumentExpression) && - ast.IsEntityNameExpression(elementAccess.Expression) && - c.checkExpressionCached(expr).flags&TypeFlagsEnumLike != 0 - } - - return false -} - -func (c *Checker) checkGrammarTopLevelElementForRequiredDeclareModifier(node *ast.Node) bool { - // A declare modifier is required for any top level .d.ts declaration except export=, export default, export as namespace - // interfaces and imports categories: - // - // DeclarationElement: - // ExportAssignment - // export_opt InterfaceDeclaration - // export_opt TypeAliasDeclaration - // export_opt ImportDeclaration - // export_opt ExternalImportDeclaration - // export_opt AmbientDeclaration - // - // TODO: The spec needs to be amended to reflect this grammar. - if node.Kind == ast.KindInterfaceDeclaration || node.Kind == ast.KindTypeAliasDeclaration || node.Kind == ast.KindImportDeclaration || node.Kind == ast.KindJSImportDeclaration || node.Kind == ast.KindImportEqualsDeclaration || node.Kind == ast.KindExportDeclaration || node.Kind == ast.KindExportAssignment || node.Kind == ast.KindJSExportAssignment || node.Kind == ast.KindNamespaceExportDeclaration || ast.HasSyntacticModifier(node, ast.ModifierFlagsAmbient|ast.ModifierFlagsExport|ast.ModifierFlagsDefault) { - return false - } - - return c.grammarErrorOnFirstToken(node, diagnostics.Top_level_declarations_in_d_ts_files_must_start_with_either_a_declare_or_export_modifier) -} - -func (c *Checker) checkGrammarTopLevelElementsForRequiredDeclareModifier(file *ast.SourceFile) bool { - for _, decl := range file.Statements.Nodes { - if ast.IsDeclarationNode(decl) || decl.Kind == ast.KindVariableStatement { - if c.checkGrammarTopLevelElementForRequiredDeclareModifier(decl) { - return true - } - } - } - return false -} - -func (c *Checker) checkGrammarSourceFile(node *ast.SourceFile) bool { - return node.Flags&ast.NodeFlagsAmbient != 0 && c.checkGrammarTopLevelElementsForRequiredDeclareModifier(node) -} - -func (c *Checker) checkGrammarStatementInAmbientContext(node *ast.Node) bool { - if node.Flags&ast.NodeFlagsAmbient != 0 { - // Find containing block which is either Block, ModuleBlock, SourceFile - links := c.nodeLinks.Get(node) - if !links.hasReportedStatementInAmbientContext && (ast.IsFunctionLike(node.Parent) || ast.IsAccessor(node.Parent)) { - links.hasReportedStatementInAmbientContext = c.grammarErrorOnFirstToken(node, diagnostics.An_implementation_cannot_be_declared_in_ambient_contexts) - return links.hasReportedStatementInAmbientContext - } - - // We are either parented by another statement, or some sort of block. - // If we're in a block, we only want to really report an error once - // to prevent noisiness. So use a bit on the block to indicate if - // this has already been reported, and don't report if it has. - // - if node.Parent.Kind == ast.KindBlock || node.Parent.Kind == ast.KindModuleBlock || node.Parent.Kind == ast.KindSourceFile { - links := c.nodeLinks.Get(node.Parent) - // Check if the containing block ever report this error - if !links.hasReportedStatementInAmbientContext { - links.hasReportedStatementInAmbientContext = c.grammarErrorOnFirstToken(node, diagnostics.Statements_are_not_allowed_in_ambient_contexts) - return links.hasReportedStatementInAmbientContext - } - } else { - // We must be parented by a statement. If so, there's no need - // to report the error as our parent will have already done it. - // debug.Assert(ast.IsStatement(node.Parent)) // !!! commented out in strada - fails if uncommented - } - } - return false -} - -func (c *Checker) checkGrammarNumericLiteral(node *ast.NumericLiteral) { - nodeText := scanner.GetTextOfNode(node.AsNode()) - - // Realism (size) checking - // We should test against `getTextOfNode(node)` rather than `node.text`, because `node.text` for large numeric literals can contain "." - // e.g. `node.text` for numeric literal `1100000000000000000000` is `1.1e21`. - isFractional := strings.ContainsRune(nodeText, '.') - // !!! - // isScientific := node.NumericLiteralFlags & ast.TokenFlagsScientific - isScientific := strings.ContainsRune(nodeText, 'e') - - // Scientific notation (e.g. 2e54 and 1e00000000010) can't be converted to bigint - // Fractional numbers (e.g. 9000000000000000.001) are inherently imprecise anyway - if isFractional || isScientific { - return - } - - // Here `node` is guaranteed to be a numeric literal representing an integer. - // We need to judge whether the integer `node` represents is <= 2 ** 53 - 1, which can be accomplished by comparing to `value` defined below because: - // 1) when `node` represents an integer <= 2 ** 53 - 1, `node.text` is its exact string representation and thus `value` precisely represents the integer. - // 2) otherwise, although `node.text` may be imprecise string representation, its mathematical value and consequently `value` cannot be less than 2 ** 53, - // thus the result of the predicate won't be affected. - value := jsnum.FromString(node.Text) - if value <= jsnum.MaxSafeInteger { - return - } - - c.addErrorOrSuggestion(false, createDiagnosticForNode(node.AsNode(), diagnostics.Numeric_literals_with_absolute_values_equal_to_2_53_or_greater_are_too_large_to_be_represented_accurately_as_integers)) -} - -func (c *Checker) checkGrammarBigIntLiteral(node *ast.BigIntLiteral) bool { - literalType := ast.IsLiteralTypeNode(node.Parent) || ast.IsPrefixUnaryExpression(node.Parent) && ast.IsLiteralTypeNode(node.Parent.Parent) - if !literalType { - // Don't error on BigInt literals in ambient contexts - if node.Flags&ast.NodeFlagsAmbient == 0 && c.languageVersion < core.ScriptTargetES2020 { - if c.grammarErrorOnNode(node.AsNode(), diagnostics.BigInt_literals_are_not_available_when_targeting_lower_than_ES2020) { - return true - } - } - } - return false -} - -func (c *Checker) checkGrammarImportClause(node *ast.ImportClause) bool { - switch node.PhaseModifier { - case ast.KindTypeKeyword: - if node.Flags&ast.NodeFlagsJSDoc == 0 && node.Name() != nil && node.NamedBindings != nil { - return c.grammarErrorOnNode(&node.Node, diagnostics.A_type_only_import_can_specify_a_default_import_or_named_bindings_but_not_both) - } - if node.NamedBindings != nil && node.NamedBindings.Kind == ast.KindNamedImports { - return c.checkGrammarTypeOnlyNamedImportsOrExports(node.NamedBindings) - } - case ast.KindDeferKeyword: - if node.Name() != nil { - return c.grammarErrorOnNode(&node.Node, diagnostics.Default_imports_are_not_allowed_in_a_deferred_import) - } - if node.NamedBindings != nil && node.NamedBindings.Kind == ast.KindNamedImports { - return c.grammarErrorOnNode(&node.Node, diagnostics.Named_imports_are_not_allowed_in_a_deferred_import) - } - if c.moduleKind != core.ModuleKindESNext && c.moduleKind != core.ModuleKindPreserve { - return c.grammarErrorOnNode(&node.Node, diagnostics.Deferred_imports_are_only_supported_when_the_module_flag_is_set_to_esnext_or_preserve) - } - } - return false -} - -func (c *Checker) checkGrammarTypeOnlyNamedImportsOrExports(namedBindings *ast.Node) bool { - var nodeList *ast.NodeList - if namedBindings.Kind == ast.KindNamedImports { - nodeList = namedBindings.AsNamedImports().Elements - } else { - nodeList = namedBindings.AsNamedExports().Elements - } - - for _, specifier := range nodeList.Nodes { - var specifierIsTypeOnly bool - var message *diagnostics.Message - if specifier.Kind == ast.KindImportSpecifier { - specifierIsTypeOnly = specifier.AsImportSpecifier().IsTypeOnly - message = diagnostics.The_type_modifier_cannot_be_used_on_a_named_import_when_import_type_is_used_on_its_import_statement - } else { - specifierIsTypeOnly = specifier.AsExportSpecifier().IsTypeOnly - message = diagnostics.The_type_modifier_cannot_be_used_on_a_named_export_when_export_type_is_used_on_its_export_statement - } - - if specifierIsTypeOnly { - return c.grammarErrorOnFirstToken(specifier, message) - } - } - - return false -} - -func (c *Checker) checkGrammarImportCallExpression(node *ast.Node) bool { - if c.compilerOptions.VerbatimModuleSyntax == core.TSTrue && c.moduleKind == core.ModuleKindCommonJS { - return c.grammarErrorOnNode(node, getVerbatimModuleSyntaxErrorMessage(node)) - } - - if node.Expression().Kind == ast.KindMetaProperty { - if c.moduleKind != core.ModuleKindESNext && c.moduleKind != core.ModuleKindPreserve { - return c.grammarErrorOnNode(node, diagnostics.Deferred_imports_are_only_supported_when_the_module_flag_is_set_to_esnext_or_preserve) - } - } else if c.moduleKind == core.ModuleKindES2015 { - return c.grammarErrorOnNode(node, diagnostics.Dynamic_imports_are_only_supported_when_the_module_flag_is_set_to_es2020_es2022_esnext_commonjs_amd_system_umd_node16_node18_node20_or_nodenext) - } - - nodeAsCall := node.AsCallExpression() - if nodeAsCall.TypeArguments != nil { - return c.grammarErrorOnNode(node, diagnostics.This_use_of_import_is_invalid_import_calls_can_be_written_but_they_must_have_parentheses_and_cannot_have_type_arguments) - } - - nodeArguments := nodeAsCall.Arguments - argumentNodes := nodeArguments.Nodes - if c.moduleKind != core.ModuleKindESNext && c.moduleKind != core.ModuleKindNodeNext && c.moduleKind != core.ModuleKindNode16 && c.moduleKind != core.ModuleKindPreserve { - // We are allowed trailing comma after proposal-import-assertions. - c.checkGrammarForDisallowedTrailingComma(nodeArguments, diagnostics.Trailing_comma_not_allowed) - - if len(argumentNodes) > 1 { - importAttributesArgument := argumentNodes[1] - return c.grammarErrorOnNode(importAttributesArgument, diagnostics.Dynamic_imports_only_support_a_second_argument_when_the_module_option_is_set_to_esnext_node16_node18_node20_nodenext_or_preserve) - } - } - - if len(argumentNodes) == 0 || len(argumentNodes) > 2 { - return c.grammarErrorOnNode(node, diagnostics.Dynamic_imports_can_only_accept_a_module_specifier_and_an_optional_set_of_attributes_as_arguments) - } - - // see: parseArgumentOrArrayLiteralElement...we use this function which parse arguments of callExpression to parse specifier for dynamic import. - // parseArgumentOrArrayLiteralElement allows spread element to be in an argument list which is not allowed as specifier in dynamic import. - spreadElement := core.Find(argumentNodes, ast.IsSpreadElement) - if spreadElement != nil { - return c.grammarErrorOnNode(spreadElement, diagnostics.Argument_of_dynamic_import_cannot_be_spread_element) - } - return false -} diff --git a/kitcom/internal/tsgo/checker/inference.go b/kitcom/internal/tsgo/checker/inference.go deleted file mode 100644 index 7829bde..0000000 --- a/kitcom/internal/tsgo/checker/inference.go +++ /dev/null @@ -1,1620 +0,0 @@ -package checker - -import ( - "slices" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/jsnum" -) - -type InferenceKey struct { - s TypeId - t TypeId -} - -type InferenceState struct { - inferences []*InferenceInfo - originalSource *Type - originalTarget *Type - priority InferencePriority - inferencePriority InferencePriority - contravariant bool - bivariant bool - expandingFlags ExpandingFlags - propagationType *Type - visited map[InferenceKey]InferencePriority - sourceStack []*Type - targetStack []*Type - next *InferenceState -} - -func (c *Checker) getInferenceState() *InferenceState { - n := c.freeinferenceState - if n == nil { - n = &InferenceState{} - } - c.freeinferenceState = n.next - return n -} - -func (c *Checker) putInferenceState(n *InferenceState) { - clear(n.visited) - *n = InferenceState{ - inferences: n.inferences[:0], - visited: n.visited, - sourceStack: n.sourceStack[:0], - targetStack: n.targetStack[:0], - next: c.freeinferenceState, - } - c.freeinferenceState = n -} - -func (c *Checker) inferTypes(inferences []*InferenceInfo, originalSource *Type, originalTarget *Type, priority InferencePriority, contravariant bool) { - n := c.getInferenceState() - n.inferences = inferences - n.originalSource = originalSource - n.originalTarget = originalTarget - n.priority = priority - n.inferencePriority = InferencePriorityMaxValue - n.contravariant = contravariant - c.inferFromTypes(n, originalSource, originalTarget) - c.putInferenceState(n) -} - -func (c *Checker) inferFromTypes(n *InferenceState, source *Type, target *Type) { - if !c.couldContainTypeVariables(target) || c.isNoInferType(target) { - return - } - if source == c.wildcardType || source == c.blockedStringType { - // We are inferring from an 'any' type. We want to infer this type for every type parameter - // referenced in the target type, so we record it as the propagation type and infer from the - // target to itself. Then, as we find candidates we substitute the propagation type. - savePropagationType := n.propagationType - n.propagationType = source - c.inferFromTypes(n, target, target) - n.propagationType = savePropagationType - return - } - if source.alias != nil && target.alias != nil && source.alias.symbol == target.alias.symbol { - if len(source.alias.typeArguments) != 0 || len(target.alias.typeArguments) != 0 { - // Source and target are types originating in the same generic type alias declaration. - // Simply infer from source type arguments to target type arguments, with defaults applied. - params := c.typeAliasLinks.Get(source.alias.symbol).typeParameters - minParams := c.getMinTypeArgumentCount(params) - nodeIsInJsFile := ast.IsInJSFile(source.alias.symbol.ValueDeclaration) - sourceTypes := c.fillMissingTypeArguments(source.alias.typeArguments, params, minParams, nodeIsInJsFile) - targetTypes := c.fillMissingTypeArguments(target.alias.typeArguments, params, minParams, nodeIsInJsFile) - c.inferFromTypeArguments(n, sourceTypes, targetTypes, c.getAliasVariances(source.alias.symbol)) - } - // And if there weren't any type arguments, there's no reason to run inference as the types must be the same. - return - } - if source == target && source.flags&TypeFlagsUnionOrIntersection != 0 { - // When source and target are the same union or intersection type, just relate each constituent - // type to itself. - for _, t := range source.Types() { - c.inferFromTypes(n, t, t) - } - return - } - if target.flags&TypeFlagsUnion != 0 && source.flags&TypeFlagsNever == 0 { - // First, infer between identically matching source and target constituents and remove the - // matching types. - tempSources, tempTargets := c.inferFromMatchingTypes(n, source.Distributed(), target.Distributed(), (*Checker).isTypeOrBaseIdenticalTo) - // Next, infer between closely matching source and target constituents and remove - // the matching types. Types closely match when they are instantiations of the same - // object type or instantiations of the same type alias. - sources, targets := c.inferFromMatchingTypes(n, tempSources, tempTargets, (*Checker).isTypeCloselyMatchedBy) - if len(targets) == 0 { - return - } - target = c.getUnionType(targets) - if len(sources) == 0 { - // All source constituents have been matched and there is nothing further to infer from. - // However, simply making no inferences is undesirable because it could ultimately mean - // inferring a type parameter constraint. Instead, make a lower priority inference from - // the full source to whatever remains in the target. For example, when inferring from - // string to 'string | T', make a lower priority inference of string for T. - c.inferWithPriority(n, source, target, InferencePriorityNakedTypeVariable) - return - } - source = c.getUnionType(sources) - } else if target.flags&TypeFlagsIntersection != 0 && !core.Every(target.Types(), c.isNonGenericObjectType) { - // We reduce intersection types unless they're simple combinations of object types. For example, - // when inferring from 'string[] & { extra: any }' to 'string[] & T' we want to remove string[] and - // infer { extra: any } for T. But when inferring to 'string[] & Iterable' we want to keep the - // string[] on the source side and infer string for T. - if source.flags&TypeFlagsUnion == 0 { - var sourceTypes []*Type - if source.flags&TypeFlagsIntersection != 0 { - sourceTypes = source.Types() - } else { - sourceTypes = []*Type{source} - } - // Infer between identically matching source and target constituents and remove the matching types. - sources, targets := c.inferFromMatchingTypes(n, sourceTypes, target.Types(), (*Checker).isTypeIdenticalTo) - if len(sources) == 0 || len(targets) == 0 { - return - } - source = c.getIntersectionType(sources) - target = c.getIntersectionType(targets) - } - } - if target.flags&(TypeFlagsIndexedAccess|TypeFlagsSubstitution) != 0 { - if c.isNoInferType(target) { - return - } - target = c.getActualTypeVariable(target) - } - if target.flags&TypeFlagsTypeVariable != 0 { - // Skip inference if the source is "blocked", which is used by the language service to - // prevent inference on nodes currently being edited. - if c.isFromInferenceBlockedSource(source) { - return - } - inference := getInferenceInfoForType(n, target) - if inference != nil { - // If target is a type parameter, make an inference, unless the source type contains - // a "non-inferrable" type. Types with this flag set are markers used to prevent inference. - // - // For example: - // - anyFunctionType is a wildcard type that's used to avoid contextually typing functions; - // it's internal, so should not be exposed to the user by adding it as a candidate. - // - autoType (and autoArrayType) is a special "any" used in control flow; like anyFunctionType, - // it's internal and should not be observable. - // - silentNeverType is returned by getInferredType when instantiating a generic function for - // inference (and a type variable has no mapping). - // - // This flag is infectious; if we produce Box (where never is silentNeverType), Box is - // also non-inferrable. - // - // As a special case, also ignore nonInferrableAnyType, which is a special form of the any type - // used as a stand-in for binding elements when they are being inferred. - if source.objectFlags&ObjectFlagsNonInferrableType != 0 || source == c.nonInferrableAnyType { - return - } - if !inference.isFixed { - candidate := core.OrElse(n.propagationType, source) - if candidate == c.blockedStringType { - return - } - if n.priority < inference.priority { - inference.candidates = nil - inference.contraCandidates = nil - inference.topLevel = true - inference.priority = n.priority - } - if n.priority == inference.priority { - // Inferring A to [A[0]] is a zero information inference (it guarantees A becomes its constraint), but oft arises from generic argument list inferences - // By discarding it early, we can allow more fruitful results to be used instead. - if c.isTupleOfSelf(inference.typeParameter, candidate) { - return - } - // We make contravariant inferences only if we are in a pure contravariant position, - // i.e. only if we have not descended into a bivariant position. - if n.contravariant && !n.bivariant { - if !slices.Contains(inference.contraCandidates, candidate) { - inference.contraCandidates = append(inference.contraCandidates, candidate) - clearCachedInferences(n.inferences) - } - } else if !slices.Contains(inference.candidates, candidate) { - inference.candidates = append(inference.candidates, candidate) - clearCachedInferences(n.inferences) - } - } - if n.priority&InferencePriorityReturnType == 0 && target.flags&TypeFlagsTypeParameter != 0 && inference.topLevel && !c.isTypeParameterAtTopLevel(n.originalTarget, target, 0) { - inference.topLevel = false - clearCachedInferences(n.inferences) - } - } - n.inferencePriority = min(n.inferencePriority, n.priority) - return - } - // Infer to the simplified version of an indexed access, if possible, to (hopefully) expose more bare type parameters to the inference engine - simplified := c.getSimplifiedType(target, false /*writing*/) - if simplified != target { - c.inferFromTypes(n, source, simplified) - } else if target.flags&TypeFlagsIndexedAccess != 0 { - indexType := c.getSimplifiedType(target.AsIndexedAccessType().indexType, false /*writing*/) - // Generally simplifications of instantiable indexes are avoided to keep relationship checking correct, however if our target is an access, we can consider - // that key of that access to be "instantiated", since we're looking to find the infernce goal in any way we can. - if indexType.flags&TypeFlagsInstantiable != 0 { - simplified := c.distributeIndexOverObjectType(c.getSimplifiedType(target.AsIndexedAccessType().objectType, false /*writing*/), indexType, false /*writing*/) - if simplified != nil && simplified != target { - c.inferFromTypes(n, source, simplified) - } - } - } - } - switch { - case source.objectFlags&ObjectFlagsReference != 0 && target.objectFlags&ObjectFlagsReference != 0 && (source.AsTypeReference().target == target.AsTypeReference().target || c.isArrayType(source) && c.isArrayType(target)) && !(source.AsTypeReference().node != nil && target.AsTypeReference().node != nil): - // If source and target are references to the same generic type, infer from type arguments - c.inferFromTypeArguments(n, c.getTypeArguments(source), c.getTypeArguments(target), c.getVariances(source.AsTypeReference().target)) - case source.flags&TypeFlagsIndex != 0 && target.flags&TypeFlagsIndex != 0: - c.inferFromContravariantTypes(n, source.AsIndexType().target, target.AsIndexType().target) - case (isLiteralType(source) || source.flags&TypeFlagsString != 0) && target.flags&TypeFlagsIndex != 0: - empty := c.createEmptyObjectTypeFromStringLiteral(source) - c.inferFromContravariantTypesWithPriority(n, empty, target.AsIndexType().target, InferencePriorityLiteralKeyof) - case source.flags&TypeFlagsIndexedAccess != 0 && target.flags&TypeFlagsIndexedAccess != 0: - c.inferFromTypes(n, source.AsIndexedAccessType().objectType, target.AsIndexedAccessType().objectType) - c.inferFromTypes(n, source.AsIndexedAccessType().indexType, target.AsIndexedAccessType().indexType) - case source.flags&TypeFlagsStringMapping != 0 && target.flags&TypeFlagsStringMapping != 0: - if source.symbol == target.symbol { - c.inferFromTypes(n, source.AsStringMappingType().target, target.AsStringMappingType().target) - } - case source.flags&TypeFlagsSubstitution != 0: - c.inferFromTypes(n, source.AsSubstitutionType().baseType, target) - // Make substitute inference at a lower priority - c.inferWithPriority(n, c.getSubstitutionIntersection(source), target, InferencePrioritySubstituteSource) - case target.flags&TypeFlagsConditional != 0: - c.invokeOnce(n, source, target, (*Checker).inferToConditionalType) - case target.flags&TypeFlagsUnionOrIntersection != 0: - c.inferToMultipleTypes(n, source, target.Types(), target.flags) - case source.flags&TypeFlagsUnion != 0: - // Source is a union or intersection type, infer from each constituent type - for _, sourceType := range source.Types() { - c.inferFromTypes(n, sourceType, target) - } - case target.flags&TypeFlagsTemplateLiteral != 0: - c.inferToTemplateLiteralType(n, source, target.AsTemplateLiteralType()) - default: - source = c.getReducedType(source) - if c.isGenericMappedType(source) && c.isGenericMappedType(target) { - c.invokeOnce(n, source, target, (*Checker).inferFromGenericMappedTypes) - } - if !(n.priority&InferencePriorityNoConstraints != 0 && source.flags&(TypeFlagsIntersection|TypeFlagsInstantiable) != 0) { - apparentSource := c.getApparentType(source) - // getApparentType can return _any_ type, since an indexed access or conditional may simplify to any other type. - // If that occurs and it doesn't simplify to an object or intersection, we'll need to restart `inferFromTypes` - // with the simplified source. - if apparentSource != source && apparentSource.flags&(TypeFlagsObject|TypeFlagsIntersection) == 0 { - c.inferFromTypes(n, apparentSource, target) - return - } - source = apparentSource - } - if source.flags&(TypeFlagsObject|TypeFlagsIntersection) != 0 { - c.invokeOnce(n, source, target, (*Checker).inferFromObjectTypes) - } - } -} - -func (c *Checker) inferFromTypeArguments(n *InferenceState, sourceTypes []*Type, targetTypes []*Type, variances []VarianceFlags) { - for i := range min(len(sourceTypes), len(targetTypes)) { - if i < len(variances) && variances[i]&VarianceFlagsVarianceMask == VarianceFlagsContravariant { - c.inferFromContravariantTypes(n, sourceTypes[i], targetTypes[i]) - } else { - c.inferFromTypes(n, sourceTypes[i], targetTypes[i]) - } - } -} - -func (c *Checker) inferWithPriority(n *InferenceState, source *Type, target *Type, newPriority InferencePriority) { - savePriority := n.priority - n.priority |= newPriority - c.inferFromTypes(n, source, target) - n.priority = savePriority -} - -func (c *Checker) inferFromContravariantTypesWithPriority(n *InferenceState, source *Type, target *Type, newPriority InferencePriority) { - savePriority := n.priority - n.priority |= newPriority - c.inferFromContravariantTypes(n, source, target) - n.priority = savePriority -} - -func (c *Checker) inferFromContravariantTypes(n *InferenceState, source *Type, target *Type) { - n.contravariant = !n.contravariant - c.inferFromTypes(n, source, target) - n.contravariant = !n.contravariant -} - -func (c *Checker) inferFromContravariantTypesIfStrictFunctionTypes(n *InferenceState, source *Type, target *Type) { - if c.strictFunctionTypes || n.priority&InferencePriorityAlwaysStrict != 0 { - c.inferFromContravariantTypes(n, source, target) - } else { - c.inferFromTypes(n, source, target) - } -} - -// Ensure an inference action is performed only once for the given source and target types. -// This includes two things: -// Avoiding inferring between the same pair of source and target types, -// and avoiding circularly inferring between source and target types. -// For an example of the last, consider if we are inferring between source type -// `type Deep = { next: Deep> }` and target type `type Loop = { next: Loop }`. -// We would then infer between the types of the `next` property: `Deep>` = `{ next: Deep>> }` and `Loop` = `{ next: Loop }`. -// We will then infer again between the types of the `next` property: -// `Deep>>` and `Loop`, and so on, such that we would be forever inferring -// between instantiations of the same types `Deep` and `Loop`. -// In particular, we would be inferring from increasingly deep instantiations of `Deep` to `Loop`, -// such that we would go on inferring forever, even though we would never infer -// between the same pair of types. -func (c *Checker) invokeOnce(n *InferenceState, source *Type, target *Type, action func(c *Checker, n *InferenceState, source *Type, target *Type)) { - key := InferenceKey{s: source.id, t: target.id} - if status, ok := n.visited[key]; ok { - n.inferencePriority = min(n.inferencePriority, status) - return - } - if n.visited == nil { - n.visited = make(map[InferenceKey]InferencePriority) - } - n.visited[key] = InferencePriorityCircularity - saveInferencePriority := n.inferencePriority - n.inferencePriority = InferencePriorityMaxValue - // We stop inferring and report a circularity if we encounter duplicate recursion identities on both - // the source side and the target side. - saveExpandingFlags := n.expandingFlags - n.sourceStack = append(n.sourceStack, source) - n.targetStack = append(n.targetStack, target) - if c.isDeeplyNestedType(source, n.sourceStack, 2) { - n.expandingFlags |= ExpandingFlagsSource - } - if c.isDeeplyNestedType(target, n.targetStack, 2) { - n.expandingFlags |= ExpandingFlagsTarget - } - if n.expandingFlags != ExpandingFlagsBoth { - action(c, n, source, target) - } else { - n.inferencePriority = InferencePriorityCircularity - } - n.targetStack = n.targetStack[:len(n.targetStack)-1] - n.sourceStack = n.sourceStack[:len(n.sourceStack)-1] - n.expandingFlags = saveExpandingFlags - n.visited[key] = n.inferencePriority - n.inferencePriority = min(n.inferencePriority, saveInferencePriority) -} - -func (c *Checker) inferFromMatchingTypes(n *InferenceState, sources []*Type, targets []*Type, matches func(c *Checker, s *Type, t *Type) bool) ([]*Type, []*Type) { - var matchedSources []*Type - var matchedTargets []*Type - for _, t := range targets { - for _, s := range sources { - if matches(c, s, t) { - c.inferFromTypes(n, s, t) - matchedSources = core.AppendIfUnique(matchedSources, s) - matchedTargets = core.AppendIfUnique(matchedTargets, t) - } - } - } - if len(matchedSources) != 0 { - sources = core.Filter(sources, func(t *Type) bool { return !slices.Contains(matchedSources, t) }) - } - if len(matchedTargets) != 0 { - targets = core.Filter(targets, func(t *Type) bool { return !slices.Contains(matchedTargets, t) }) - } - return sources, targets -} - -func (c *Checker) inferToMultipleTypes(n *InferenceState, source *Type, targets []*Type, targetFlags TypeFlags) { - typeVariableCount := 0 - if targetFlags&TypeFlagsUnion != 0 { - var nakedTypeVariable *Type - sources := source.Distributed() - matched := make([]bool, len(sources)) - inferenceCircularity := false - // First infer to types that are not naked type variables. For each source type we - // track whether inferences were made from that particular type to some target with - // equal priority (i.e. of equal quality) to what we would infer for a naked type - // parameter. - for _, t := range targets { - if getInferenceInfoForType(n, t) != nil { - nakedTypeVariable = t - typeVariableCount++ - } else { - for i := range sources { - saveInferencePriority := n.inferencePriority - n.inferencePriority = InferencePriorityMaxValue - c.inferFromTypes(n, sources[i], t) - if n.inferencePriority == n.priority { - matched[i] = true - } - inferenceCircularity = inferenceCircularity || n.inferencePriority == InferencePriorityCircularity - n.inferencePriority = min(n.inferencePriority, saveInferencePriority) - } - } - } - if typeVariableCount == 0 { - // If every target is an intersection of types containing a single naked type variable, - // make a lower priority inference to that type variable. This handles inferring from - // 'A | B' to 'T & (X | Y)' where we want to infer 'A | B' for T. - intersectionTypeVariable := getSingleTypeVariableFromIntersectionTypes(n, targets) - if intersectionTypeVariable != nil { - c.inferWithPriority(n, source, intersectionTypeVariable, InferencePriorityNakedTypeVariable) - } - return - } - // If the target has a single naked type variable and no inference circularities were - // encountered above (meaning we explored the types fully), create a union of the source - // types from which no inferences have been made so far and infer from that union to the - // naked type variable. - if typeVariableCount == 1 && !inferenceCircularity { - var unmatched []*Type - for i, s := range sources { - if !matched[i] { - unmatched = append(unmatched, s) - } - } - if len(unmatched) != 0 { - c.inferFromTypes(n, c.getUnionType(unmatched), nakedTypeVariable) - return - } - } - } else { - // We infer from types that are not naked type variables first so that inferences we - // make from nested naked type variables and given slightly higher priority by virtue - // of being first in the candidates array. - for _, t := range targets { - if getInferenceInfoForType(n, t) != nil { - typeVariableCount++ - } else { - c.inferFromTypes(n, source, t) - } - } - } - // Inferences directly to naked type variables are given lower priority as they are - // less specific. For example, when inferring from Promise to T | Promise, - // we want to infer string for T, not Promise | string. For intersection types - // we only infer to single naked type variables. - if targetFlags&TypeFlagsIntersection != 0 && typeVariableCount == 1 || targetFlags&TypeFlagsIntersection == 0 && typeVariableCount > 0 { - for _, t := range targets { - if getInferenceInfoForType(n, t) != nil { - c.inferWithPriority(n, source, t, InferencePriorityNakedTypeVariable) - } - } - } -} - -func getSingleTypeVariableFromIntersectionTypes(n *InferenceState, types []*Type) *Type { - var typeVariable *Type - for _, t := range types { - if t.flags&TypeFlagsIntersection == 0 { - return nil - } - v := core.Find(t.Types(), func(t *Type) bool { return getInferenceInfoForType(n, t) != nil }) - if v == nil || typeVariable != nil && v != typeVariable { - return nil - } - typeVariable = v - } - return typeVariable -} - -func (c *Checker) inferToMultipleTypesWithPriority(n *InferenceState, source *Type, targets []*Type, targetFlags TypeFlags, newPriority InferencePriority) { - savePriority := n.priority - n.priority |= newPriority - c.inferToMultipleTypes(n, source, targets, targetFlags) - n.priority = savePriority -} - -func (c *Checker) inferToConditionalType(n *InferenceState, source *Type, target *Type) { - if source.flags&TypeFlagsConditional != 0 { - c.inferFromTypes(n, source.AsConditionalType().checkType, target.AsConditionalType().checkType) - c.inferFromTypes(n, source.AsConditionalType().extendsType, target.AsConditionalType().extendsType) - c.inferFromTypes(n, c.getTrueTypeFromConditionalType(source), c.getTrueTypeFromConditionalType(target)) - c.inferFromTypes(n, c.getFalseTypeFromConditionalType(source), c.getFalseTypeFromConditionalType(target)) - } else { - targetTypes := []*Type{c.getTrueTypeFromConditionalType(target), c.getFalseTypeFromConditionalType(target)} - c.inferToMultipleTypesWithPriority(n, source, targetTypes, target.flags, core.IfElse(n.contravariant, InferencePriorityContravariantConditional, 0)) - } -} - -func (c *Checker) inferToTemplateLiteralType(n *InferenceState, source *Type, target *TemplateLiteralType) { - matches := c.inferTypesFromTemplateLiteralType(source, target) - types := target.types - // When the target template literal contains only placeholders (meaning that inference is intended to extract - // single characters and remainder strings) and inference fails to produce matches, we want to infer 'never' for - // each placeholder such that instantiation with the inferred value(s) produces 'never', a type for which an - // assignment check will fail. If we make no inferences, we'll likely end up with the constraint 'string' which, - // upon instantiation, would collapse all the placeholders to just 'string', and an assignment check might - // succeed. That would be a pointless and confusing outcome. - if len(matches) != 0 || core.Every(target.texts, func(s string) bool { return s == "" }) { - for i, target := range types { - var source *Type - if len(matches) != 0 { - source = matches[i] - } else { - source = c.neverType - } - // If we are inferring from a string literal type to a type variable whose constraint includes one of the - // allowed template literal placeholder types, infer from a literal type corresponding to the constraint. - if source.flags&TypeFlagsStringLiteral != 0 && target.flags&TypeFlagsTypeVariable != 0 { - if inferenceContext := getInferenceInfoForType(n, target); inferenceContext != nil { - if constraint := c.getBaseConstraintOfType(inferenceContext.typeParameter); constraint != nil && !IsTypeAny(constraint) { - allTypeFlags := TypeFlagsNone - for _, t := range constraint.Distributed() { - allTypeFlags |= t.flags - } - // If the constraint contains `string`, we don't need to look for a more preferred type - if allTypeFlags&TypeFlagsString == 0 { - str := getStringLiteralValue(source) - // If the type contains `number` or a number literal and the string isn't a valid number, exclude numbers - if allTypeFlags&TypeFlagsNumberLike != 0 && !isValidNumberString(str, true /*roundTripOnly*/) { - allTypeFlags &^= TypeFlagsNumberLike - } - // If the type contains `bigint` or a bigint literal and the string isn't a valid bigint, exclude bigints - if allTypeFlags&TypeFlagsBigIntLike != 0 && !isValidBigIntString(str, true /*roundTripOnly*/) { - allTypeFlags &^= TypeFlagsBigIntLike - } - choose := func(left *Type, right *Type) *Type { - switch { - case right.flags&allTypeFlags == 0: - return left - case left.flags&TypeFlagsString != 0: - return left - case right.flags&TypeFlagsString != 0: - return source - case left.flags&TypeFlagsTemplateLiteral != 0: - return left - case right.flags&TypeFlagsTemplateLiteral != 0 && c.isTypeMatchedByTemplateLiteralType(source, right.AsTemplateLiteralType()): - return source - case left.flags&TypeFlagsStringMapping != 0: - return left - case right.flags&TypeFlagsStringMapping != 0 && str == applyStringMapping(right.symbol, str): - return source - case left.flags&TypeFlagsStringLiteral != 0: - return left - case right.flags&TypeFlagsStringLiteral != 0 && getStringLiteralValue(right) == str: - return right - case left.flags&TypeFlagsNumber != 0: - return left - case right.flags&TypeFlagsNumber != 0: - return c.getNumberLiteralType(jsnum.FromString(str)) - case left.flags&TypeFlagsEnum != 0: - return left - case right.flags&TypeFlagsEnum != 0: - return c.getNumberLiteralType(jsnum.FromString(str)) - case left.flags&TypeFlagsNumberLiteral != 0: - return left - case right.flags&TypeFlagsNumberLiteral != 0 && getNumberLiteralValue(right) == jsnum.FromString(str): - return right - case left.flags&TypeFlagsBigInt != 0: - return left - case right.flags&TypeFlagsBigInt != 0: - return c.parseBigIntLiteralType(str) - case left.flags&TypeFlagsBigIntLiteral != 0: - return left - case right.flags&TypeFlagsBigIntLiteral != 0 && pseudoBigIntToString(getBigIntLiteralValue(right)) == str: - return right - case left.flags&TypeFlagsBoolean != 0: - return left - case right.flags&TypeFlagsBoolean != 0: - switch { - case str == "true": - return c.trueType - case str == "false": - return c.falseType - default: - return c.booleanType - } - case left.flags&TypeFlagsBooleanLiteral != 0: - return left - case right.flags&TypeFlagsBooleanLiteral != 0 && core.IfElse(getBooleanLiteralValue(right), "true", "false") == str: - return right - case left.flags&TypeFlagsUndefined != 0: - return left - case right.flags&TypeFlagsUndefined != 0 && right.AsIntrinsicType().intrinsicName == str: - return right - case left.flags&TypeFlagsNull != 0: - return left - case right.flags&TypeFlagsNull != 0 && right.AsIntrinsicType().intrinsicName == str: - return right - default: - return left - } - } - matchingType := c.neverType - for _, t := range constraint.Distributed() { - matchingType = choose(matchingType, t) - } - if matchingType.flags&TypeFlagsNever == 0 { - c.inferFromTypes(n, matchingType, target) - continue - } - } - } - } - } - c.inferFromTypes(n, source, target) - } - } -} - -func (c *Checker) inferFromGenericMappedTypes(n *InferenceState, source *Type, target *Type) { - // The source and target types are generic types { [P in S]: X } and { [P in T]: Y }, so we infer - // from S to T and from X to Y. - c.inferFromTypes(n, c.getConstraintTypeFromMappedType(source), c.getConstraintTypeFromMappedType(target)) - c.inferFromTypes(n, c.getTemplateTypeFromMappedType(source), c.getTemplateTypeFromMappedType(target)) - sourceNameType := c.getNameTypeFromMappedType(source) - targetNameType := c.getNameTypeFromMappedType(target) - if sourceNameType != nil && targetNameType != nil { - c.inferFromTypes(n, sourceNameType, targetNameType) - } -} - -func (c *Checker) inferFromObjectTypes(n *InferenceState, source *Type, target *Type) { - if source.objectFlags&ObjectFlagsReference != 0 && target.objectFlags&ObjectFlagsReference != 0 && (source.Target() == target.Target() || c.isArrayType(source) && c.isArrayType(target)) { - // If source and target are references to the same generic type, infer from type arguments - c.inferFromTypeArguments(n, c.getTypeArguments(source), c.getTypeArguments(target), c.getVariances(source.Target())) - return - } - if c.isGenericMappedType(source) && c.isGenericMappedType(target) { - c.inferFromGenericMappedTypes(n, source, target) - } - if target.objectFlags&ObjectFlagsMapped != 0 && target.AsMappedType().declaration.NameType == nil { - constraintType := c.getConstraintTypeFromMappedType(target) - if c.inferToMappedType(n, source, target, constraintType) { - return - } - } - // Infer from the members of source and target only if the two types are possibly related - if c.typesDefinitelyUnrelated(source, target) { - return - } - if c.isArrayOrTupleType(source) { - if isTupleType(target) { - sourceArity := c.getTypeReferenceArity(source) - targetArity := c.getTypeReferenceArity(target) - elementTypes := c.getTypeArguments(target) - elementInfos := target.TargetTupleType().elementInfos - // When source and target are tuple types with the same structure (fixed, variadic, and rest are matched - // to the same kind in each position), simply infer between the element types. - if isTupleType(source) && c.isTupleTypeStructureMatching(source, target) { - for i := range targetArity { - c.inferFromTypes(n, c.getTypeArguments(source)[i], elementTypes[i]) - } - return - } - startLength := 0 - endLength := 0 - if isTupleType(source) { - startLength = min(source.TargetTupleType().fixedLength, target.TargetTupleType().fixedLength) - if target.TargetTupleType().combinedFlags&ElementFlagsVariable != 0 { - endLength = min(getEndElementCount(source.TargetTupleType(), ElementFlagsFixed), getEndElementCount(target.TargetTupleType(), ElementFlagsFixed)) - } - } - // Infer between starting fixed elements. - for i := range startLength { - c.inferFromTypes(n, c.getTypeArguments(source)[i], elementTypes[i]) - } - if !isTupleType(source) || sourceArity-startLength-endLength == 1 && source.TargetTupleType().elementInfos[startLength].flags&ElementFlagsRest != 0 { - // Single rest element remains in source, infer from that to every element in target - restType := c.getTypeArguments(source)[startLength] - for i := startLength; i < targetArity-endLength; i++ { - t := restType - if elementInfos[i].flags&ElementFlagsVariadic != 0 { - t = c.createArrayType(t) - } - c.inferFromTypes(n, t, elementTypes[i]) - } - } else { - middleLength := targetArity - startLength - endLength - if middleLength == 2 { - if elementInfos[startLength].flags&elementInfos[startLength+1].flags&ElementFlagsVariadic != 0 { - // Middle of target is [...T, ...U] and source is tuple type - targetInfo := getInferenceInfoForType(n, elementTypes[startLength]) - if targetInfo != nil && targetInfo.impliedArity >= 0 { - // Infer slices from source based on implied arity of T. - c.inferFromTypes(n, c.sliceTupleType(source, startLength, endLength+sourceArity-targetInfo.impliedArity), elementTypes[startLength]) - c.inferFromTypes(n, c.sliceTupleType(source, startLength+targetInfo.impliedArity, endLength), elementTypes[startLength+1]) - } - } else if elementInfos[startLength].flags&ElementFlagsVariadic != 0 && elementInfos[startLength+1].flags&ElementFlagsRest != 0 { - // Middle of target is [...T, ...rest] and source is tuple type - // if T is constrained by a fixed-size tuple we might be able to use its arity to infer T - if info := getInferenceInfoForType(n, elementTypes[startLength]); info != nil { - constraint := c.getBaseConstraintOfType(info.typeParameter) - if constraint != nil && isTupleType(constraint) && constraint.TargetTupleType().combinedFlags&ElementFlagsVariable == 0 { - impliedArity := constraint.TargetTupleType().fixedLength - c.inferFromTypes(n, c.sliceTupleType(source, startLength, sourceArity-(startLength+impliedArity)), elementTypes[startLength]) - c.inferFromTypes(n, c.getElementTypeOfSliceOfTupleType(source, startLength+impliedArity, endLength, false, false), elementTypes[startLength+1]) - } - } - } else if elementInfos[startLength].flags&ElementFlagsRest != 0 && elementInfos[startLength+1].flags&ElementFlagsVariadic != 0 { - // Middle of target is [...rest, ...T] and source is tuple type - // if T is constrained by a fixed-size tuple we might be able to use its arity to infer T - if info := getInferenceInfoForType(n, elementTypes[startLength+1]); info != nil { - constraint := c.getBaseConstraintOfType(info.typeParameter) - if constraint != nil && isTupleType(constraint) && constraint.TargetTupleType().combinedFlags&ElementFlagsVariable == 0 { - impliedArity := constraint.TargetTupleType().fixedLength - endIndex := sourceArity - getEndElementCount(target.TargetTupleType(), ElementFlagsFixed) - startIndex := endIndex - impliedArity - trailingSlice := c.createTupleTypeEx(c.getTypeArguments(source)[startIndex:endIndex], source.TargetTupleType().elementInfos[startIndex:endIndex], false /*readonly*/) - c.inferFromTypes(n, c.getElementTypeOfSliceOfTupleType(source, startLength, endLength+impliedArity, false, false), elementTypes[startLength]) - c.inferFromTypes(n, trailingSlice, elementTypes[startLength+1]) - } - } - } - } else if middleLength == 1 && elementInfos[startLength].flags&ElementFlagsVariadic != 0 { - // Middle of target is exactly one variadic element. Infer the slice between the fixed parts in the source. - // If target ends in optional element(s), make a lower priority a speculative inference. - priority := core.IfElse(elementInfos[targetArity-1].flags&ElementFlagsOptional != 0, InferencePrioritySpeculativeTuple, 0) - sourceSlice := c.sliceTupleType(source, startLength, endLength) - c.inferWithPriority(n, sourceSlice, elementTypes[startLength], priority) - } else if middleLength == 1 && elementInfos[startLength].flags&ElementFlagsRest != 0 { - // Middle of target is exactly one rest element. If middle of source is not empty, infer union of middle element types. - restType := c.getElementTypeOfSliceOfTupleType(source, startLength, endLength, false, false) - if restType != nil { - c.inferFromTypes(n, restType, elementTypes[startLength]) - } - } - } - // Infer between ending fixed elements - for i := range endLength { - c.inferFromTypes(n, c.getTypeArguments(source)[sourceArity-i-1], elementTypes[targetArity-i-1]) - } - return - } - if c.isArrayType(target) { - c.inferFromIndexTypes(n, source, target) - return - } - } - c.inferFromProperties(n, source, target) - c.inferFromSignatures(n, source, target, SignatureKindCall) - c.inferFromSignatures(n, source, target, SignatureKindConstruct) - c.inferFromIndexTypes(n, source, target) -} - -func (c *Checker) inferFromProperties(n *InferenceState, source *Type, target *Type) { - properties := c.getPropertiesOfObjectType(target) - for _, targetProp := range properties { - sourceProp := c.getPropertyOfType(source, targetProp.Name) - if sourceProp != nil && !core.Some(sourceProp.Declarations, c.isSkipDirectInferenceNode) { - c.inferFromTypes(n, c.removeMissingType(c.getTypeOfSymbol(sourceProp), sourceProp.Flags&ast.SymbolFlagsOptional != 0), c.removeMissingType(c.getTypeOfSymbol(targetProp), targetProp.Flags&ast.SymbolFlagsOptional != 0)) - } - } -} - -func (c *Checker) inferFromSignatures(n *InferenceState, source *Type, target *Type, kind SignatureKind) { - sourceSignatures := c.getSignaturesOfType(source, kind) - sourceLen := len(sourceSignatures) - if sourceLen > 0 { - // We match source and target signatures from the bottom up, and if the source has fewer signatures - // than the target, we infer from the first source signature to the excess target signatures. - targetSignatures := c.getSignaturesOfType(target, kind) - targetLen := len(targetSignatures) - for i := range targetLen { - sourceIndex := max(sourceLen-targetLen+i, 0) - c.inferFromSignature(n, c.getBaseSignature(sourceSignatures[sourceIndex]), c.getErasedSignature(targetSignatures[i])) - } - } -} - -func (c *Checker) inferFromSignature(n *InferenceState, source *Signature, target *Signature) { - if source.flags&SignatureFlagsIsNonInferrable == 0 { - saveBivariant := n.bivariant - kind := ast.KindUnknown - if target.declaration != nil { - kind = target.declaration.Kind - } - // Once we descend into a bivariant signature we remain bivariant for all nested inferences - n.bivariant = n.bivariant || kind == ast.KindMethodDeclaration || kind == ast.KindMethodSignature || kind == ast.KindConstructor - c.applyToParameterTypes(source, target, func(s, t *Type) { c.inferFromContravariantTypesIfStrictFunctionTypes(n, s, t) }) - n.bivariant = saveBivariant - } - c.applyToReturnTypes(source, target, func(s, t *Type) { c.inferFromTypes(n, s, t) }) -} - -func (c *Checker) applyToParameterTypes(source *Signature, target *Signature, callback func(s *Type, t *Type)) { - sourceCount := c.getParameterCount(source) - targetCount := c.getParameterCount(target) - sourceRestType := c.getEffectiveRestType(source) - targetRestType := c.getEffectiveRestType(target) - targetNonRestCount := targetCount - if targetRestType != nil { - targetNonRestCount-- - } - paramCount := targetNonRestCount - if sourceRestType == nil { - paramCount = min(sourceCount, targetNonRestCount) - } - sourceThisType := c.getThisTypeOfSignature(source) - if sourceThisType != nil { - targetThisType := c.getThisTypeOfSignature(target) - if targetThisType != nil { - callback(sourceThisType, targetThisType) - } - } - for i := range paramCount { - callback(c.getTypeAtPosition(source, i), c.getTypeAtPosition(target, i)) - } - if targetRestType != nil { - callback(c.getRestTypeAtPosition(source, paramCount, c.isConstTypeVariable(targetRestType, 0) && !someType(targetRestType, c.isMutableArrayLikeType) /*readonly*/), targetRestType) - } -} - -func (c *Checker) applyToReturnTypes(source *Signature, target *Signature, callback func(s *Type, t *Type)) { - targetTypePredicate := c.getTypePredicateOfSignature(target) - if targetTypePredicate != nil { - sourceTypePredicate := c.getTypePredicateOfSignature(source) - if sourceTypePredicate != nil && c.typePredicateKindsMatch(sourceTypePredicate, targetTypePredicate) && sourceTypePredicate.t != nil && targetTypePredicate.t != nil { - callback(sourceTypePredicate.t, targetTypePredicate.t) - return - } - } - targetReturnType := c.getReturnTypeOfSignature(target) - if c.couldContainTypeVariables(targetReturnType) { - callback(c.getReturnTypeOfSignature(source), targetReturnType) - } -} - -func (c *Checker) inferFromIndexTypes(n *InferenceState, source *Type, target *Type) { - // Inferences across mapped type index signatures are pretty much the same a inferences to homomorphic variables - priority := InferencePriorityNone - if source.objectFlags&target.objectFlags&ObjectFlagsMapped != 0 { - priority = InferencePriorityHomomorphicMappedType - } - indexInfos := c.getIndexInfosOfType(target) - if c.isObjectTypeWithInferableIndex(source) { - for _, targetInfo := range indexInfos { - var propTypes []*Type - for _, prop := range c.getPropertiesOfType(source) { - if c.isApplicableIndexType(c.getLiteralTypeFromProperty(prop, TypeFlagsStringOrNumberLiteralOrUnique, false), targetInfo.keyType) { - propType := c.getTypeOfSymbol(prop) - if prop.Flags&ast.SymbolFlagsOptional != 0 { - propType = c.removeMissingOrUndefinedType(propType) - } - propTypes = append(propTypes, propType) - } - } - for _, info := range c.getIndexInfosOfType(source) { - if c.isApplicableIndexType(info.keyType, targetInfo.keyType) { - propTypes = append(propTypes, info.valueType) - } - } - if len(propTypes) != 0 { - c.inferWithPriority(n, c.getUnionType(propTypes), targetInfo.valueType, priority) - } - } - } - for _, targetInfo := range indexInfos { - sourceInfo := c.getApplicableIndexInfo(source, targetInfo.keyType) - if sourceInfo != nil { - c.inferWithPriority(n, sourceInfo.valueType, targetInfo.valueType, priority) - } - } -} - -func (c *Checker) inferToMappedType(n *InferenceState, source *Type, target *Type, constraintType *Type) bool { - if constraintType.flags&TypeFlagsUnion != 0 || constraintType.flags&TypeFlagsIntersection != 0 { - result := false - for _, t := range constraintType.Types() { - result = core.OrElse(c.inferToMappedType(n, source, target, t), result) - } - return result - } - if constraintType.flags&TypeFlagsIndex != 0 { - // We're inferring from some source type S to a homomorphic mapped type { [P in keyof T]: X }, - // where T is a type variable. Use inferTypeForHomomorphicMappedType to infer a suitable source - // type and then make a secondary inference from that type to T. We make a secondary inference - // such that direct inferences to T get priority over inferences to Partial, for example. - inference := getInferenceInfoForType(n, constraintType.AsIndexType().target) - if inference != nil && !inference.isFixed && !c.isFromInferenceBlockedSource(source) { - inferredType := c.inferTypeForHomomorphicMappedType(source, target, constraintType) - if inferredType != nil { - // We assign a lower priority to inferences made from types containing non-inferrable - // types because we may only have a partial result (i.e. we may have failed to make - // reverse inferences for some properties). - c.inferWithPriority(n, inferredType, inference.typeParameter, core.IfElse(source.objectFlags&ObjectFlagsNonInferrableType != 0, InferencePriorityPartialHomomorphicMappedType, InferencePriorityHomomorphicMappedType)) - } - } - return true - } - if constraintType.flags&TypeFlagsTypeParameter != 0 { - // We're inferring from some source type S to a mapped type { [P in K]: X }, where K is a type - // parameter. First infer from 'keyof S' to K. - c.inferWithPriority(n, c.getIndexTypeEx(source, core.IfElse(c.patternForType[source] != nil, IndexFlagsNoIndexSignatures, IndexFlagsNone)), constraintType, InferencePriorityMappedTypeConstraint) - // If K is constrained to a type C, also infer to C. Thus, for a mapped type { [P in K]: X }, - // where K extends keyof T, we make the same inferences as for a homomorphic mapped type - // { [P in keyof T]: X }. This enables us to make meaningful inferences when the target is a - // Pick. - extendedConstraint := c.getConstraintOfType(constraintType) - if extendedConstraint != nil && c.inferToMappedType(n, source, target, extendedConstraint) { - return true - } - // If no inferences can be made to K's constraint, infer from a union of the property types - // in the source to the template type X. - propTypes := core.Map(c.getPropertiesOfType(source), c.getTypeOfSymbol) - indexTypes := core.Map(c.getIndexInfosOfType(source), func(info *IndexInfo) *Type { - if info != c.enumNumberIndexInfo { - return info.valueType - } - return c.neverType - }) - c.inferFromTypes(n, c.getUnionType(core.Concatenate(propTypes, indexTypes)), c.getTemplateTypeFromMappedType(target)) - return true - } - return false -} - -// Infer a suitable input type for a homomorphic mapped type { [P in keyof T]: X }. We construct -// an object type with the same set of properties as the source type, where the type of each -// property is computed by inferring from the source property type to X for the type -// variable T[P] (i.e. we treat the type T[P] as the type variable we're inferring for). -func (c *Checker) inferTypeForHomomorphicMappedType(source *Type, target *Type, constraint *Type) *Type { - key := ReverseMappedTypeKey{sourceId: source.id, targetId: target.id, constraintId: constraint.id} - if cached := c.reverseHomomorphicMappedCache[key]; cached != nil { - return cached - } - t := c.createReverseMappedType(source, target, constraint) - c.reverseHomomorphicMappedCache[key] = t - return t -} - -func (c *Checker) createReverseMappedType(source *Type, target *Type, constraint *Type) *Type { - // We consider a source type reverse mappable if it has a string index signature or if - // it has one or more properties and is of a partially inferable type. - if !(c.getIndexInfoOfType(source, c.stringType) != nil || len(c.getPropertiesOfType(source)) != 0 && c.isPartiallyInferableType(source)) { - return nil - } - // For arrays and tuples we infer new arrays and tuples where the reverse mapping has been - // applied to the element type(s). - if c.isArrayType(source) { - elementType := c.inferReverseMappedType(c.getTypeArguments(source)[0], target, constraint) - if elementType == nil { - return nil - } - return c.createArrayTypeEx(elementType, c.isReadonlyArrayType(source)) - } - if isTupleType(source) { - elementTypes := core.Map(c.getElementTypes(source), func(t *Type) *Type { - return c.inferReverseMappedType(t, target, constraint) - }) - if !core.Every(elementTypes, func(t *Type) bool { return t != nil }) { - return nil - } - elementInfos := source.TargetTupleType().elementInfos - if getMappedTypeModifiers(target)&MappedTypeModifiersIncludeOptional != 0 { - elementInfos = core.SameMap(elementInfos, func(info TupleElementInfo) TupleElementInfo { - if info.flags&ElementFlagsOptional != 0 { - return TupleElementInfo{flags: ElementFlagsRequired, labeledDeclaration: info.labeledDeclaration} - } - return info - }) - } - return c.createTupleTypeEx(elementTypes, elementInfos, source.TargetTupleType().readonly) - } - // For all other object types we infer a new object type where the reverse mapping has been - // applied to the type of each property. - reversed := c.newObjectType(ObjectFlagsReverseMapped|ObjectFlagsAnonymous, nil /*symbol*/) - reversed.AsReverseMappedType().source = source - reversed.AsReverseMappedType().mappedType = target - reversed.AsReverseMappedType().constraintType = constraint - return reversed -} - -// We consider a type to be partially inferable if it isn't marked non-inferable or if it is -// an object literal type with at least one property of an inferable type. For example, an object -// literal { a: 123, b: x => true } is marked non-inferable because it contains a context sensitive -// arrow function, but is considered partially inferable because property 'a' has an inferable type. -func (c *Checker) isPartiallyInferableType(t *Type) bool { - return t.objectFlags&ObjectFlagsNonInferrableType == 0 || isObjectLiteralType(t) && core.Some(c.getPropertiesOfType(t), func(prop *ast.Symbol) bool { - return c.isPartiallyInferableType(c.getTypeOfSymbol(prop)) - }) || isTupleType(t) && core.Some(c.getElementTypes(t), c.isPartiallyInferableType) -} - -func (c *Checker) inferReverseMappedType(source *Type, target *Type, constraint *Type) *Type { - key := ReverseMappedTypeKey{sourceId: source.id, targetId: target.id, constraintId: constraint.id} - if cached, ok := c.reverseMappedCache[key]; ok { - return core.OrElse(cached, c.unknownType) - } - c.reverseMappedSourceStack = append(c.reverseMappedSourceStack, source) - c.reverseMappedTargetStack = append(c.reverseMappedTargetStack, target) - saveExpandingFlags := c.reverseExpandingFlags - if c.isDeeplyNestedType(source, c.reverseMappedSourceStack, 2) { - c.reverseExpandingFlags |= ExpandingFlagsSource - } - if c.isDeeplyNestedType(target, c.reverseMappedTargetStack, 2) { - c.reverseExpandingFlags |= ExpandingFlagsTarget - } - var t *Type - if c.reverseExpandingFlags != ExpandingFlagsBoth { - t = c.inferReverseMappedTypeWorker(source, target, constraint) - } - c.reverseMappedSourceStack = c.reverseMappedSourceStack[:len(c.reverseMappedSourceStack)-1] - c.reverseMappedTargetStack = c.reverseMappedTargetStack[:len(c.reverseMappedTargetStack)-1] - c.reverseExpandingFlags = saveExpandingFlags - c.reverseMappedCache[key] = t - return t -} - -func (c *Checker) inferReverseMappedTypeWorker(source *Type, target *Type, constraint *Type) *Type { - typeParameter := c.getIndexedAccessType(constraint.AsIndexType().target, c.getTypeParameterFromMappedType(target)) - templateType := c.getTemplateTypeFromMappedType(target) - inference := newInferenceInfo(typeParameter) - c.inferTypes([]*InferenceInfo{inference}, source, templateType, InferencePriorityNone, false) - return core.OrElse(c.getTypeFromInference(inference), c.unknownType) -} - -func (c *Checker) resolveReverseMappedTypeMembers(t *Type) { - r := t.AsReverseMappedType() - indexInfo := c.getIndexInfoOfType(r.source, c.stringType) - modifiers := getMappedTypeModifiers(r.mappedType) - readonlyMask := modifiers&MappedTypeModifiersIncludeReadonly == 0 - optionalMask := core.IfElse(modifiers&MappedTypeModifiersIncludeOptional != 0, 0, ast.SymbolFlagsOptional) - var indexInfos []*IndexInfo - if indexInfo != nil { - indexInfos = []*IndexInfo{c.newIndexInfo(c.stringType, core.OrElse(c.inferReverseMappedType(indexInfo.valueType, r.mappedType, r.constraintType), c.unknownType), readonlyMask && indexInfo.isReadonly, nil, nil)} - } - members := make(ast.SymbolTable) - limitedConstraint := c.getLimitedConstraint(t) - for _, prop := range c.getPropertiesOfType(r.source) { - // In case of a reverse mapped type with an intersection constraint, if we were able to - // extract the filtering type literals we skip those properties that are not assignable to them, - // because the extra properties wouldn't get through the application of the mapped type anyway - if limitedConstraint != nil { - propertyNameType := c.getLiteralTypeFromProperty(prop, TypeFlagsStringOrNumberLiteralOrUnique, false) - if !c.isTypeAssignableTo(propertyNameType, limitedConstraint) { - continue - } - } - checkFlags := ast.CheckFlagsReverseMapped | core.IfElse(readonlyMask && c.isReadonlySymbol(prop), ast.CheckFlagsReadonly, 0) - inferredProp := c.newSymbolEx(ast.SymbolFlagsProperty|prop.Flags&optionalMask, prop.Name, checkFlags) - inferredProp.Declarations = prop.Declarations - c.valueSymbolLinks.Get(inferredProp).nameType = c.valueSymbolLinks.Get(prop).nameType - links := c.ReverseMappedSymbolLinks.Get(inferredProp) - links.propertyType = c.getTypeOfSymbol(prop) - constraintTarget := r.constraintType.AsIndexType().target - if constraintTarget.flags&TypeFlagsIndexedAccess != 0 && constraintTarget.AsIndexedAccessType().objectType.flags&TypeFlagsTypeParameter != 0 && constraintTarget.AsIndexedAccessType().indexType.flags&TypeFlagsTypeParameter != 0 { - // A reverse mapping of `{[K in keyof T[K_1]]: T[K_1]}` is the same as that of `{[K in keyof T]: T}`, since all we care about is - // inferring to the "type parameter" (or indexed access) shared by the constraint and template. So, to reduce the number of - // type identities produced, we simplify such indexed access occurrences - newTypeParam := constraintTarget.AsIndexedAccessType().objectType - newMappedType := c.replaceIndexedAccess(r.mappedType, constraintTarget, newTypeParam) - links.mappedType = newMappedType - links.constraintType = c.getIndexType(newTypeParam) - } else { - links.mappedType = r.mappedType - links.constraintType = r.constraintType - } - members[prop.Name] = inferredProp - } - c.setStructuredTypeMembers(t, members, nil, nil, indexInfos) -} - -func (c *Checker) getTypeOfReverseMappedSymbol(symbol *ast.Symbol) *Type { - links := c.valueSymbolLinks.Get(symbol) - if links.resolvedType == nil { - reverseLinks := c.ReverseMappedSymbolLinks.Get(symbol) - links.resolvedType = core.OrElse(c.inferReverseMappedType(reverseLinks.propertyType, reverseLinks.mappedType, reverseLinks.constraintType), c.unknownType) - } - return links.resolvedType -} - -// If the original mapped type had an intersection constraint we extract its components, -// and we make an attempt to do so even if the intersection has been reduced to a union. -// This entire process allows us to possibly retrieve the filtering type literals. -// e.g. { [K in keyof U & ("a" | "b") ] } -> "a" | "b" -func (c *Checker) getLimitedConstraint(t *Type) *Type { - constraint := c.getConstraintTypeFromMappedType(t.AsReverseMappedType().mappedType) - if !(constraint.flags&TypeFlagsUnion != 0 || constraint.flags&TypeFlagsIntersection != 0) { - return nil - } - origin := constraint - if constraint.flags&TypeFlagsUnion != 0 { - origin = constraint.AsUnionType().origin - } - if origin == nil || origin.flags&TypeFlagsIntersection == 0 { - return nil - } - constraintType := t.AsReverseMappedType().constraintType - limitedConstraint := c.getIntersectionType(core.Filter(origin.Types(), func(t *Type) bool { return t != constraintType })) - if limitedConstraint != c.neverType { - return limitedConstraint - } - return nil -} - -func (c *Checker) replaceIndexedAccess(instantiable *Type, t *Type, replacement *Type) *Type { - // map type.indexType to 0 - // map type.objectType to `[TReplacement]` - // thus making the indexed access `[TReplacement][0]` or `TReplacement` - return c.instantiateType(instantiable, newTypeMapper([]*Type{t.AsIndexedAccessType().indexType, t.AsIndexedAccessType().objectType}, []*Type{c.getNumberLiteralType(0), c.createTupleType([]*Type{replacement})})) -} - -func (c *Checker) typesDefinitelyUnrelated(source *Type, target *Type) bool { - // Two tuple types with incompatible arities are definitely unrelated. - // Two object types that each have a property that is unmatched in the other are definitely unrelated. - if isTupleType(source) && isTupleType(target) { - return tupleTypesDefinitelyUnrelated(source, target) - } - return c.getUnmatchedProperty(source, target, false /*requireOptionalProperties*/, true /*matchDiscriminantProperties*/) != nil && - c.getUnmatchedProperty(target, source, false /*requireOptionalProperties*/, false /*matchDiscriminantProperties*/) != nil -} - -func tupleTypesDefinitelyUnrelated(source *Type, target *Type) bool { - s := source.TargetTupleType() - t := target.TargetTupleType() - return t.combinedFlags&ElementFlagsVariadic == 0 && t.minLength > s.minLength || - t.combinedFlags&ElementFlagsVariable == 0 && (s.combinedFlags&ElementFlagsVariable != 0 || t.fixedLength < s.fixedLength) -} - -func (c *Checker) isTupleTypeStructureMatching(t1 *Type, t2 *Type) bool { - if c.getTypeReferenceArity(t1) != c.getTypeReferenceArity(t2) { - return false - } - for i, e := range t1.TargetTupleType().elementInfos { - if e.flags&ElementFlagsVariable != t2.TargetTupleType().elementInfos[i].flags&ElementFlagsVariable { - return false - } - } - return true -} - -func (c *Checker) isTypeOrBaseIdenticalTo(s *Type, t *Type) bool { - if t == c.missingType { - return s == t - } - return c.isTypeIdenticalTo(s, t) || - t.flags&TypeFlagsString != 0 && s.flags&TypeFlagsStringLiteral != 0 || - t.flags&TypeFlagsNumber != 0 && s.flags&TypeFlagsNumberLiteral != 0 -} - -func (c *Checker) isTypeCloselyMatchedBy(s *Type, t *Type) bool { - return s.flags&TypeFlagsObject != 0 && t.flags&TypeFlagsObject != 0 && s.symbol != nil && s.symbol == t.symbol || - s.alias != nil && t.alias != nil && len(s.alias.typeArguments) != 0 && s.alias.symbol == t.alias.symbol -} - -// Create an object with properties named in the string literal type. Every property has type `any`. -func (c *Checker) createEmptyObjectTypeFromStringLiteral(t *Type) *Type { - members := make(ast.SymbolTable) - for _, t := range t.Distributed() { - if t.flags&TypeFlagsStringLiteral == 0 { - continue - } - name := getStringLiteralValue(t) - literalProp := c.newSymbol(ast.SymbolFlagsProperty, name) - c.valueSymbolLinks.Get(literalProp).resolvedType = c.anyType - if t.symbol != nil { - literalProp.Declarations = t.symbol.Declarations - literalProp.ValueDeclaration = t.symbol.ValueDeclaration - } - members[name] = literalProp - } - var indexInfos []*IndexInfo - if t.flags&TypeFlagsString != 0 { - indexInfos = []*IndexInfo{c.newIndexInfo(c.stringType, c.emptyObjectType, false /*isReadonly*/, nil, nil)} - } - return c.newAnonymousType(nil, members, nil, nil, indexInfos) -} - -func (c *Checker) newInferenceContext(typeParameters []*Type, signature *Signature, flags InferenceFlags, compareTypes TypeComparer) *InferenceContext { - if compareTypes == nil { - compareTypes = c.compareTypesAssignable - } - return c.newInferenceContextWorker(core.Map(typeParameters, newInferenceInfo), signature, flags, compareTypes) -} - -func (c *Checker) cloneInferenceContext(n *InferenceContext, extraFlags InferenceFlags) *InferenceContext { - if n == nil { - return nil - } - return c.newInferenceContextWorker(core.Map(n.inferences, cloneInferenceInfo), n.signature, n.flags|extraFlags, n.compareTypes) -} - -func (c *Checker) cloneInferredPartOfContext(n *InferenceContext) *InferenceContext { - inferences := core.Filter(n.inferences, hasInferenceCandidates) - if len(inferences) == 0 { - return nil - } - return c.newInferenceContextWorker(core.Map(inferences, cloneInferenceInfo), n.signature, n.flags, n.compareTypes) -} - -func (c *Checker) newInferenceContextWorker(inferences []*InferenceInfo, signature *Signature, flags InferenceFlags, compareTypes TypeComparer) *InferenceContext { - n := &InferenceContext{ - inferences: inferences, - signature: signature, - flags: flags, - compareTypes: compareTypes, - } - n.mapper = c.newInferenceTypeMapper(n, true /*fixing*/) - n.nonFixingMapper = c.newInferenceTypeMapper(n, false /*fixing*/) - return n -} - -func (c *Checker) addIntraExpressionInferenceSite(n *InferenceContext, node *ast.Node, t *Type) { - n.intraExpressionInferenceSites = append(n.intraExpressionInferenceSites, IntraExpressionInferenceSite{node: node, t: t}) -} - -// We collect intra-expression inference sites within object and array literals to handle cases where -// inferred types flow between context sensitive element expressions. For example: -// -// declare function foo(arg: [(n: number) => T, (x: T) => void]): void; -// foo([_a => 0, n => n.toFixed()]); -// -// Above, both arrow functions in the tuple argument are context sensitive, thus both are omitted from the -// pass that collects inferences from the non-context sensitive parts of the arguments. In the subsequent -// pass where nothing is omitted, we need to commit to an inference for T in order to contextually type the -// parameter in the second arrow function, but we want to first infer from the return type of the first -// arrow function. This happens automatically when the arrow functions are discrete arguments (because we -// infer from each argument before processing the next), but when the arrow functions are elements of an -// object or array literal, we need to perform intra-expression inferences early. -func (c *Checker) inferFromIntraExpressionSites(n *InferenceContext) { - for _, site := range n.intraExpressionInferenceSites { - var contextualType *Type - if ast.IsMethodDeclaration(site.node) { - contextualType = c.getContextualTypeForObjectLiteralMethod(site.node, ContextFlagsNoConstraints) - } else { - contextualType = c.getContextualType(site.node, ContextFlagsNoConstraints) - } - if contextualType != nil { - c.inferTypes(n.inferences, site.t, contextualType, InferencePriorityNone, false) - } - } - n.intraExpressionInferenceSites = nil -} - -func (c *Checker) getInferredType(n *InferenceContext, index int) *Type { - inference := n.inferences[index] - if inference.inferredType == nil { - if inference.typeParameter == c.errorType { - return inference.typeParameter - } - var inferredType *Type - var fallbackType *Type - if n.signature != nil { - var inferredCovariantType *Type - if len(inference.candidates) != 0 { - inferredCovariantType = c.getCovariantInference(inference, n.signature) - } - var inferredContravariantType *Type - if len(inference.contraCandidates) != 0 { - inferredContravariantType = c.getContravariantInference(inference) - } - if inferredCovariantType != nil || inferredContravariantType != nil { - // If we have both co- and contra-variant inferences, we prefer the co-variant inference if it is not 'never', - // all co-variant inferences are assignable to it (i.e. it isn't one of a conflicting set of candidates), it is - // assignable to some contra-variant inference, and no other type parameter is constrained to this type parameter - // and has inferences that would conflict. Otherwise, we prefer the contra-variant inference. - // Similarly ignore co-variant `any` inference when both are available as almost everything is assignable to it - // and it would spoil the overall inference. - preferCovariantType := inferredCovariantType != nil && (inferredContravariantType == nil || - inferredCovariantType.flags&(TypeFlagsNever|TypeFlagsAny) == 0 && - core.Some(inference.contraCandidates, func(t *Type) bool { return c.isTypeAssignableTo(inferredCovariantType, t) }) && - core.Every(n.inferences, func(other *InferenceInfo) bool { - return other != inference && c.getConstraintOfTypeParameter(other.typeParameter) != inference.typeParameter || - core.Every(other.candidates, func(t *Type) bool { return c.isTypeAssignableTo(t, inferredCovariantType) }) - })) - if preferCovariantType { - inferredType = inferredCovariantType - fallbackType = inferredContravariantType - } else { - inferredType = inferredContravariantType - fallbackType = inferredCovariantType - } - } else if n.flags&InferenceFlagsNoDefault != 0 { - // We use silentNeverType as the wildcard that signals no inferences. - inferredType = c.silentNeverType - } else { - // Infer either the default or the empty object type when no inferences were - // made. It is important to remember that in this case, inference still - // succeeds, meaning there is no error for not having inference candidates. An - // inference error only occurs when there are *conflicting* candidates, i.e. - // candidates with no common supertype. - defaultType := c.getDefaultFromTypeParameter(inference.typeParameter) - if defaultType != nil { - // Instantiate the default type. Any forward reference to a type - // parameter should be instantiated to the empty object type. - inferredType = c.instantiateType(defaultType, mergeTypeMappers(c.newBackreferenceMapper(n, index), n.nonFixingMapper)) - } - } - } else { - inferredType = c.getTypeFromInference(inference) - } - inference.inferredType = inferredType - if inference.inferredType == nil { - inference.inferredType = core.IfElse(n.flags&InferenceFlagsAnyDefault != 0, c.anyType, c.unknownType) - } - constraint := c.getConstraintOfTypeParameter(inference.typeParameter) - if constraint != nil { - instantiatedConstraint := c.instantiateType(constraint, n.nonFixingMapper) - if inferredType != nil { - constraintWithThis := c.getTypeWithThisArgument(instantiatedConstraint, inferredType, false) - if n.compareTypes(inferredType, constraintWithThis, false) == TernaryFalse { - var filteredByConstraint *Type - if inference.priority == InferencePriorityReturnType { - // If we have a pure return type inference, we may succeed by removing constituents of the inferred type - // that aren't assignable to the constraint type (pure return type inferences are speculation anyway). - filteredByConstraint = c.mapType(inferredType, func(t *Type) *Type { - return core.IfElse(n.compareTypes(t, constraintWithThis, false) != TernaryFalse, t, c.neverType) - }) - } - inferredType = core.IfElse(filteredByConstraint != nil && filteredByConstraint.flags&TypeFlagsNever == 0, filteredByConstraint, nil) - } - } - if inferredType == nil { - // If the fallback type satisfies the constraint, we pick it. Otherwise, we pick the constraint. - inferredType = core.IfElse(fallbackType != nil && n.compareTypes(fallbackType, c.getTypeWithThisArgument(instantiatedConstraint, fallbackType, false), false) != TernaryFalse, fallbackType, instantiatedConstraint) - } - inference.inferredType = inferredType - } - c.clearActiveMapperCaches() - } - return inference.inferredType -} - -func (c *Checker) getInferredTypes(n *InferenceContext) []*Type { - result := make([]*Type, len(n.inferences)) - for i := range n.inferences { - result[i] = c.getInferredType(n, i) - } - return result -} - -func (c *Checker) getMapperFromContext(n *InferenceContext) *TypeMapper { - if n == nil { - return nil - } - return n.mapper -} - -// Return a type mapper that combines the context's return mapper with a mapper that erases any additional type parameters -// to their inferences at the time of creation. -func (c *Checker) createOuterReturnMapper(context *InferenceContext) *TypeMapper { - if context.outerReturnMapper == nil { - mapper := c.cloneInferenceContext(context, InferenceFlagsNone).mapper - if context.returnMapper != nil { - mapper = newMergedTypeMapper(context.returnMapper, mapper) - } - context.outerReturnMapper = mapper - } - return context.outerReturnMapper -} - -func (c *Checker) getCovariantInference(inference *InferenceInfo, signature *Signature) *Type { - // Extract all object and array literal types and replace them with a single widened and normalized type. - candidates := c.unionObjectAndArrayLiteralCandidates(inference.candidates) - // We widen inferred literal types if - // all inferences were made to top-level occurrences of the type parameter, and - // the type parameter has no constraint or its constraint includes no primitive or literal types, and - // the type parameter was fixed during inference or does not occur at top-level in the return type. - primitiveConstraint := c.hasPrimitiveConstraint(inference.typeParameter) || c.isConstTypeVariable(inference.typeParameter, 0) - widenLiteralTypes := !primitiveConstraint && inference.topLevel && (inference.isFixed || !c.isTypeParameterAtTopLevelInReturnType(signature, inference.typeParameter)) - var baseCandidates []*Type - switch { - case primitiveConstraint: - baseCandidates = core.SameMap(candidates, c.getRegularTypeOfLiteralType) - case widenLiteralTypes: - baseCandidates = core.SameMap(candidates, c.getWidenedLiteralType) - default: - baseCandidates = candidates - } - // If all inferences were made from a position that implies a combined result, infer a union type. - // Otherwise, infer a common supertype. - var unwidenedType *Type - if inference.priority&InferencePriorityPriorityImpliesCombination != 0 { - unwidenedType = c.getUnionTypeEx(baseCandidates, UnionReductionSubtype, nil, nil) - } else { - unwidenedType = c.getCommonSupertype(baseCandidates) - } - return c.getWidenedType(unwidenedType) -} - -func (c *Checker) getContravariantInference(inference *InferenceInfo) *Type { - if inference.priority&InferencePriorityPriorityImpliesCombination != 0 { - return c.getIntersectionType(inference.contraCandidates) - } - return c.getCommonSubtype(inference.contraCandidates) -} - -func (c *Checker) unionObjectAndArrayLiteralCandidates(candidates []*Type) []*Type { - if len(candidates) > 1 { - objectLiterals := core.Filter(candidates, isObjectOrArrayLiteralType) - if len(objectLiterals) != 0 { - literalsType := c.getUnionTypeEx(objectLiterals, UnionReductionSubtype, nil, nil) - nonLiteralTypes := core.Filter(candidates, func(t *Type) bool { return !isObjectOrArrayLiteralType(t) }) - return core.Concatenate(nonLiteralTypes, []*Type{literalsType}) - } - } - return candidates -} - -func (c *Checker) hasPrimitiveConstraint(t *Type) bool { - constraint := c.getConstraintOfTypeParameter(t) - if constraint != nil { - if constraint.flags&TypeFlagsConditional != 0 { - constraint = c.getDefaultConstraintOfConditionalType(constraint) - } - return c.maybeTypeOfKind(constraint, TypeFlagsPrimitive|TypeFlagsIndex|TypeFlagsTemplateLiteral|TypeFlagsStringMapping) - } - return false -} - -func (c *Checker) isTypeParameterAtTopLevel(t *Type, tp *Type, depth int) bool { - return t == tp || - t.flags&TypeFlagsUnionOrIntersection != 0 && core.Some(t.Types(), func(t *Type) bool { return c.isTypeParameterAtTopLevel(t, tp, depth) }) || - depth < 3 && t.flags&TypeFlagsConditional != 0 && - (c.isTypeParameterAtTopLevel(c.getTrueTypeFromConditionalType(t), tp, depth+1) || - c.isTypeParameterAtTopLevel(c.getFalseTypeFromConditionalType(t), tp, depth+1)) -} - -func (c *Checker) isTypeParameterAtTopLevelInReturnType(signature *Signature, typeParameter *Type) bool { - typePredicate := c.getTypePredicateOfSignature(signature) - if typePredicate != nil { - return typePredicate.t != nil && c.isTypeParameterAtTopLevel(typePredicate.t, typeParameter, 0) - } - return c.isTypeParameterAtTopLevel(c.getReturnTypeOfSignature(signature), typeParameter, 0) -} - -func (c *Checker) getTypeFromInference(inference *InferenceInfo) *Type { - switch { - case inference.candidates != nil: - return c.getUnionTypeEx(inference.candidates, UnionReductionSubtype, nil, nil) - case inference.contraCandidates != nil: - return c.getIntersectionType(inference.contraCandidates) - } - return nil -} - -func getInferenceInfoForType(n *InferenceState, t *Type) *InferenceInfo { - if t.flags&TypeFlagsTypeVariable != 0 { - for _, inference := range n.inferences { - if t == inference.typeParameter { - return inference - } - } - } - return nil -} - -func (c *Checker) getCommonSupertype(types []*Type) *Type { - if len(types) == 1 { - return types[0] - } - // Remove nullable types from each of the candidates. - primaryTypes := types - if c.strictNullChecks { - primaryTypes = core.SameMap(types, func(t *Type) *Type { - return c.filterType(t, func(u *Type) bool { return u.flags&TypeFlagsNullable == 0 }) - }) - } - // When the candidate types are all literal types with the same base type, return a union - // of those literal types. Otherwise, return the leftmost type for which no type to the - // right is a supertype. - var supertype *Type - if c.literalTypesWithSameBaseType(primaryTypes) { - supertype = c.getUnionType(primaryTypes) - } else { - supertype = c.getSingleCommonSupertype(primaryTypes) - } - // Add any nullable types that occurred in the candidates back to the result. - if core.Same(primaryTypes, types) { - return supertype - } - return c.getNullableType(supertype, c.getCombinedTypeFlags(types)&TypeFlagsNullable) -} - -func (c *Checker) getSingleCommonSupertype(types []*Type) *Type { - // First, find the leftmost type for which no type to the right is a strict supertype, and if that - // type is a strict supertype of all other candidates, return it. Otherwise, return the leftmost type - // for which no type to the right is a (regular) supertype. - candidate := c.findLeftmostType(types, (*Checker).isTypeStrictSubtypeOf) - if core.Every(types, func(t *Type) bool { return t == candidate || c.isTypeStrictSubtypeOf(t, candidate) }) { - return candidate - } - return c.findLeftmostType(types, (*Checker).isTypeSubtypeOf) -} - -func (c *Checker) findLeftmostType(types []*Type, f func(c *Checker, s *Type, t *Type) bool) *Type { - var candidate *Type - for _, t := range types { - if candidate == nil || f(c, candidate, t) { - candidate = t - } - } - return candidate -} - -// Return the leftmost type for which no type to the right is a subtype. -func (c *Checker) getCommonSubtype(types []*Type) *Type { - var subtype *Type - for _, t := range types { - if subtype == nil || c.isTypeSubtypeOf(t, subtype) { - subtype = t - } - } - return subtype -} - -func (c *Checker) getCombinedTypeFlags(types []*Type) TypeFlags { - flags := TypeFlagsNone - for _, t := range types { - if t.flags&TypeFlagsUnion != 0 { - flags |= c.getCombinedTypeFlags(t.Types()) - } else { - flags |= t.flags - } - } - return flags -} - -func (c *Checker) literalTypesWithSameBaseType(types []*Type) bool { - var commonBaseType *Type - for _, t := range types { - if t.flags&TypeFlagsNever == 0 { - baseType := c.getBaseTypeOfLiteralType(t) - if commonBaseType == nil { - commonBaseType = baseType - } - if baseType == t || baseType != commonBaseType { - return false - } - } - } - return true -} - -func (c *Checker) isFromInferenceBlockedSource(t *Type) bool { - return t.symbol != nil && core.Some(t.symbol.Declarations, c.isSkipDirectInferenceNode) -} - -func (c *Checker) isSkipDirectInferenceNode(node *ast.Node) bool { - return c.skipDirectInferenceNodes.Has(node) -} - -// Returns `true` if `type` has the shape `[T[0]]` where `T` is `typeParameter` -func (c *Checker) isTupleOfSelf(tp *Type, t *Type) bool { - return isTupleType(t) && c.getTupleElementType(t, 0) == c.getIndexedAccessType(tp, c.getNumberLiteralType(0)) && c.getTypeOfPropertyOfType(t, "1") == nil -} - -func newInferenceInfo(typeParameter *Type) *InferenceInfo { - return &InferenceInfo{typeParameter: typeParameter, priority: InferencePriorityMaxValue, topLevel: true, impliedArity: -1} -} - -func cloneInferenceInfo(info *InferenceInfo) *InferenceInfo { - return &InferenceInfo{ - typeParameter: info.typeParameter, - candidates: slices.Clone(info.candidates), - contraCandidates: slices.Clone(info.contraCandidates), - inferredType: info.inferredType, - priority: info.priority, - topLevel: info.topLevel, - isFixed: info.isFixed, - impliedArity: info.impliedArity, - } -} - -func clearCachedInferences(inferences []*InferenceInfo) { - for _, inference := range inferences { - if !inference.isFixed { - inference.inferredType = nil - } - } -} - -func hasInferenceCandidates(info *InferenceInfo) bool { - return len(info.candidates) != 0 || len(info.contraCandidates) != 0 -} - -func hasInferenceCandidatesOrDefault(info *InferenceInfo) bool { - return info.candidates != nil || info.contraCandidates != nil || hasTypeParameterDefault(info.typeParameter) -} - -func hasTypeParameterDefault(tp *Type) bool { - if tp.symbol != nil { - for _, d := range tp.symbol.Declarations { - if ast.IsTypeParameterDeclaration(d) && d.AsTypeParameter().DefaultType != nil { - return true - } - } - } - return false -} - -func hasOverlappingInferences(a []*InferenceInfo, b []*InferenceInfo) bool { - for i := range a { - if hasInferenceCandidates(a[i]) && hasInferenceCandidates(b[i]) { - return true - } - } - return false -} - -func (c *Checker) mergeInferences(target []*InferenceInfo, source []*InferenceInfo) { - for i := range target { - if !hasInferenceCandidates(target[i]) && hasInferenceCandidates(source[i]) { - target[i] = source[i] - } - } -} diff --git a/kitcom/internal/tsgo/checker/jsdoc.go b/kitcom/internal/tsgo/checker/jsdoc.go deleted file mode 100644 index f2ce4b2..0000000 --- a/kitcom/internal/tsgo/checker/jsdoc.go +++ /dev/null @@ -1,97 +0,0 @@ -package checker - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics" -) - -func (c *Checker) checkUnmatchedJSDocParameters(node *ast.Node) { - var jsdocParameters []*ast.Node - for _, tag := range getAllJSDocTags(node) { - if tag.Kind == ast.KindJSDocParameterTag { - name := tag.AsJSDocParameterOrPropertyTag().Name() - if ast.IsIdentifier(name) && len(name.Text()) == 0 { - continue - } - jsdocParameters = append(jsdocParameters, tag) - } - } - - if len(jsdocParameters) == 0 { - return - } - - isJs := ast.IsInJSFile(node) - parameters := collections.Set[string]{} - excludedParameters := collections.Set[int]{} - - for i, param := range node.Parameters() { - name := param.AsParameterDeclaration().Name() - if ast.IsIdentifier(name) { - parameters.Add(name.Text()) - } - if ast.IsBindingPattern(name) { - excludedParameters.Add(i) - } - } - if c.containsArgumentsReference(node) { - if isJs { - lastJSDocParamIndex := len(jsdocParameters) - 1 - lastJSDocParam := jsdocParameters[lastJSDocParamIndex].AsJSDocParameterOrPropertyTag() - if lastJSDocParam == nil || !ast.IsIdentifier(lastJSDocParam.Name()) { - return - } - if excludedParameters.Has(lastJSDocParamIndex) || parameters.Has(lastJSDocParam.Name().Text()) { - return - } - if lastJSDocParam.TypeExpression == nil || lastJSDocParam.TypeExpression.Type() == nil { - return - } - if c.isArrayType(c.getTypeFromTypeNode(lastJSDocParam.TypeExpression.Type())) { - return - } - c.error(lastJSDocParam.Name(), diagnostics.JSDoc_param_tag_has_name_0_but_there_is_no_parameter_with_that_name_It_would_match_arguments_if_it_had_an_array_type, lastJSDocParam.Name().Text()) - } - } else { - for index, tag := range jsdocParameters { - name := tag.AsJSDocParameterOrPropertyTag().Name() - isNameFirst := tag.AsJSDocParameterOrPropertyTag().IsNameFirst - - if excludedParameters.Has(index) || (ast.IsIdentifier(name) && parameters.Has(name.Text())) { - continue - } - - if ast.IsQualifiedName(name) { - if isJs { - c.error(name, diagnostics.Qualified_name_0_is_not_allowed_without_a_leading_param_object_1, - entityNameToString(name), - entityNameToString(name.AsQualifiedName().Left), - ) - } - } else { - if !isNameFirst { - c.errorOrSuggestion(isJs, name, - diagnostics.JSDoc_param_tag_has_name_0_but_there_is_no_parameter_with_that_name, - name.Text(), - ) - } - } - } - } -} - -func getAllJSDocTags(node *ast.Node) []*ast.Node { - if node == nil { - return nil - } - jsdocs := node.JSDoc(nil) - if len(jsdocs) == 0 { - return nil - } - lastJSDoc := jsdocs[len(jsdocs)-1].AsJSDoc() - if lastJSDoc.Tags == nil { - return nil - } - return lastJSDoc.Tags.Nodes -} diff --git a/kitcom/internal/tsgo/checker/jsx.go b/kitcom/internal/tsgo/checker/jsx.go deleted file mode 100644 index c083058..0000000 --- a/kitcom/internal/tsgo/checker/jsx.go +++ /dev/null @@ -1,1467 +0,0 @@ -package checker - -import ( - "iter" - "math" - "slices" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/debug" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/jsnum" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/parser" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/scanner" -) - -type JsxFlags uint32 - -const ( - JsxFlagsNone JsxFlags = 0 - JsxFlagsIntrinsicNamedElement JsxFlags = 1 << 0 // An element from a named property of the JSX.IntrinsicElements interface - JsxFlagsIntrinsicIndexedElement JsxFlags = 1 << 1 // An element inferred from the string index signature of the JSX.IntrinsicElements interface - JsxFlagsIntrinsicElement JsxFlags = JsxFlagsIntrinsicNamedElement | JsxFlagsIntrinsicIndexedElement -) - -type JsxReferenceKind int32 - -const ( - JsxReferenceKindComponent JsxReferenceKind = iota - JsxReferenceKindFunction - JsxReferenceKindMixed -) - -type JsxElementLinks struct { - jsxFlags JsxFlags // Flags for the JSX element - resolvedJsxElementAttributesType *Type // Resolved element attributes type of a JSX opening-like element - jsxNamespace *ast.Symbol // Resolved JSX namespace symbol for this node - jsxImplicitImportContainer *ast.Symbol // Resolved module symbol the implicit JSX import of this file should refer to -} - -var JsxNames = struct { - JSX string - IntrinsicElements string - ElementClass string - ElementAttributesPropertyNameContainer string - ElementChildrenAttributeNameContainer string - Element string - ElementType string - IntrinsicAttributes string - IntrinsicClassAttributes string - LibraryManagedAttributes string -}{ - JSX: "JSX", - IntrinsicElements: "IntrinsicElements", - ElementClass: "ElementClass", - ElementAttributesPropertyNameContainer: "ElementAttributesProperty", - ElementChildrenAttributeNameContainer: "ElementChildrenAttribute", - Element: "Element", - ElementType: "ElementType", - IntrinsicAttributes: "IntrinsicAttributes", - IntrinsicClassAttributes: "IntrinsicClassAttributes", - LibraryManagedAttributes: "LibraryManagedAttributes", -} - -var ReactNames = struct { - Fragment string -}{ - Fragment: "Fragment", -} - -func (c *Checker) checkJsxElement(node *ast.Node, checkMode CheckMode) *Type { - c.checkNodeDeferred(node) - return c.getJsxElementTypeAt(node) -} - -func (c *Checker) checkJsxElementDeferred(node *ast.Node) { - jsxElement := node.AsJsxElement() - c.checkJsxOpeningLikeElementOrOpeningFragment(jsxElement.OpeningElement) - // Perform resolution on the closing tag so that rename/go to definition/etc work - if isJsxIntrinsicTagName(jsxElement.ClosingElement.TagName()) { - c.getIntrinsicTagSymbol(jsxElement.ClosingElement) - } else { - c.checkExpression(jsxElement.ClosingElement.TagName()) - } - c.checkJsxChildren(node, CheckModeNormal) -} - -func (c *Checker) checkJsxExpression(node *ast.Node, checkMode CheckMode) *Type { - c.checkGrammarJsxExpression(node.AsJsxExpression()) - if node.Expression() == nil { - return c.errorType - } - t := c.checkExpressionEx(node.Expression(), checkMode) - if node.AsJsxExpression().DotDotDotToken != nil && t != c.anyType && !c.isArrayType(t) { - c.error(node, diagnostics.JSX_spread_child_must_be_an_array_type) - } - return t -} - -func (c *Checker) checkJsxSelfClosingElement(node *ast.Node, checkMode CheckMode) *Type { - c.checkNodeDeferred(node) - return c.getJsxElementTypeAt(node) -} - -func (c *Checker) checkJsxSelfClosingElementDeferred(node *ast.Node) { - c.checkJsxOpeningLikeElementOrOpeningFragment(node) -} - -func (c *Checker) checkJsxFragment(node *ast.Node) *Type { - c.checkJsxOpeningLikeElementOrOpeningFragment(node.AsJsxFragment().OpeningFragment) - // by default, jsx:'react' will use jsxFactory = React.createElement and jsxFragmentFactory = React.Fragment - // if jsxFactory compiler option is provided, ensure jsxFragmentFactory compiler option or @jsxFrag pragma is provided too - nodeSourceFile := ast.GetSourceFileOfNode(node) - if c.compilerOptions.GetJSXTransformEnabled() && (c.compilerOptions.JsxFactory != "" || ast.GetPragmaFromSourceFile(nodeSourceFile, "jsx") != nil) && c.compilerOptions.JsxFragmentFactory == "" && ast.GetPragmaFromSourceFile(nodeSourceFile, "jsxfrag") == nil { - message := core.IfElse(c.compilerOptions.JsxFactory != "", - diagnostics.The_jsxFragmentFactory_compiler_option_must_be_provided_to_use_JSX_fragments_with_the_jsxFactory_compiler_option, - diagnostics.An_jsxFrag_pragma_is_required_when_using_an_jsx_pragma_with_JSX_fragments) - c.error(node, message) - } - c.checkJsxChildren(node, CheckModeNormal) - t := c.getJsxElementTypeAt(node) - return core.IfElse(c.isErrorType(t), c.anyType, t) -} - -func (c *Checker) checkJsxAttributes(node *ast.Node, checkMode CheckMode) *Type { - return c.createJsxAttributesTypeFromAttributesProperty(node.Parent, checkMode) -} - -func (c *Checker) checkJsxOpeningLikeElementOrOpeningFragment(node *ast.Node) { - isNodeOpeningLikeElement := ast.IsJsxOpeningLikeElement(node) - if isNodeOpeningLikeElement { - c.checkGrammarJsxElement(node) - } - c.checkJsxPreconditions(node) - c.markJsxAliasReferenced(node) - sig := c.getResolvedSignature(node, nil, CheckModeNormal) - c.checkDeprecatedSignature(sig, node) - if isNodeOpeningLikeElement { - elementTypeConstraint := c.getJsxElementTypeTypeAt(node) - if elementTypeConstraint != nil { - tagName := node.TagName() - var tagType *Type - if isJsxIntrinsicTagName(tagName) { - tagType = c.getStringLiteralType(tagName.Text()) - } else { - tagType = c.checkExpression(tagName) - } - var diags []*ast.Diagnostic - if !c.checkTypeRelatedToEx(tagType, elementTypeConstraint, c.assignableRelation, tagName, diagnostics.Its_type_0_is_not_a_valid_JSX_element_type, &diags) { - c.diagnostics.Add(ast.NewDiagnosticChain(diags[0], diagnostics.X_0_cannot_be_used_as_a_JSX_component, scanner.GetTextOfNode(tagName))) - } - } else { - c.checkJsxReturnAssignableToAppropriateBound(c.getJsxReferenceKind(node), c.getReturnTypeOfSignature(sig), node) - } - } -} - -func (c *Checker) checkJsxPreconditions(errorNode *ast.Node) { - // Preconditions for using JSX - if c.compilerOptions.Jsx == core.JsxEmitNone { - c.error(errorNode, diagnostics.Cannot_use_JSX_unless_the_jsx_flag_is_provided) - } - if c.noImplicitAny && c.getJsxElementTypeAt(errorNode) == nil { - c.error(errorNode, diagnostics.JSX_element_implicitly_has_type_any_because_the_global_type_JSX_Element_does_not_exist) - } -} - -func (c *Checker) checkJsxReturnAssignableToAppropriateBound(refKind JsxReferenceKind, elemInstanceType *Type, openingLikeElement *ast.Node) { - var diags []*ast.Diagnostic - switch refKind { - case JsxReferenceKindFunction: - sfcReturnConstraint := c.getJsxStatelessElementTypeAt(openingLikeElement) - if sfcReturnConstraint != nil { - c.checkTypeRelatedToEx(elemInstanceType, sfcReturnConstraint, c.assignableRelation, openingLikeElement.TagName(), diagnostics.Its_return_type_0_is_not_a_valid_JSX_element, &diags) - } - case JsxReferenceKindComponent: - classConstraint := c.getJsxElementClassTypeAt(openingLikeElement) - if classConstraint != nil { - // Issue an error if this return type isn't assignable to JSX.ElementClass, failing that - c.checkTypeRelatedToEx(elemInstanceType, classConstraint, c.assignableRelation, openingLikeElement.TagName(), diagnostics.Its_instance_type_0_is_not_a_valid_JSX_element, &diags) - } - default: - sfcReturnConstraint := c.getJsxStatelessElementTypeAt(openingLikeElement) - classConstraint := c.getJsxElementClassTypeAt(openingLikeElement) - if sfcReturnConstraint == nil || classConstraint == nil { - return - } - combined := c.getUnionType([]*Type{sfcReturnConstraint, classConstraint}) - c.checkTypeRelatedToEx(elemInstanceType, combined, c.assignableRelation, openingLikeElement.TagName(), diagnostics.Its_element_type_0_is_not_a_valid_JSX_element, &diags) - } - if len(diags) != 0 { - c.diagnostics.Add(ast.NewDiagnosticChain(diags[0], diagnostics.X_0_cannot_be_used_as_a_JSX_component, scanner.GetTextOfNode(openingLikeElement.TagName()))) - } -} - -func (c *Checker) inferJsxTypeArguments(node *ast.Node, signature *Signature, checkMode CheckMode, context *InferenceContext) []*Type { - paramType := c.getEffectiveFirstArgumentForJsxSignature(signature, node) - checkAttrType := c.checkExpressionWithContextualType(node.Attributes(), paramType, context, checkMode) - c.inferTypes(context.inferences, checkAttrType, paramType, InferencePriorityNone, false) - return c.getInferredTypes(context) -} - -func (c *Checker) getContextualTypeForJsxExpression(node *ast.Node, contextFlags ContextFlags) *Type { - switch { - case ast.IsJsxAttributeLike(node.Parent): - return c.getContextualType(node, contextFlags) - case ast.IsJsxElement(node.Parent): - return c.getContextualTypeForChildJsxExpression(node.Parent, node, contextFlags) - } - return nil -} - -func (c *Checker) getContextualTypeForJsxAttribute(attribute *ast.Node, contextFlags ContextFlags) *Type { - // When we trying to resolve JsxOpeningLikeElement as a stateless function element, we will already give its attributes a contextual type - // which is a type of the parameter of the signature we are trying out. - // If there is no contextual type (e.g. we are trying to resolve stateful component), get attributes type from resolving element's tagName - if ast.IsJsxAttribute(attribute) { - attributesType := c.getApparentTypeOfContextualType(attribute.Parent, contextFlags) - if attributesType == nil || IsTypeAny(attributesType) { - return nil - } - return c.getTypeOfPropertyOfContextualType(attributesType, attribute.Name().Text()) - } - return c.getContextualType(attribute.Parent, contextFlags) -} - -func (c *Checker) getContextualJsxElementAttributesType(node *ast.Node, contextFlags ContextFlags) *Type { - if ast.IsJsxOpeningElement(node) && contextFlags != ContextFlagsCompletions { - index := c.findContextualNode(node.Parent, contextFlags == ContextFlagsNone) - if index >= 0 { - // Contextually applied type is moved from attributes up to the outer jsx attributes so when walking up from the children they get hit - // _However_ to hit them from the _attributes_ we must look for them here; otherwise we'll used the declared type - // (as below) instead! - return c.contextualInfos[index].t - } - } - return c.getContextualTypeForArgumentAtIndex(node, 0) -} - -func (c *Checker) getContextualTypeForChildJsxExpression(node *ast.Node, child *ast.JsxChild, contextFlags ContextFlags) *Type { - attributesType := c.getApparentTypeOfContextualType(node.AsJsxElement().OpeningElement.Attributes(), contextFlags) - // JSX expression is in children of JSX Element, we will look for an "children" attribute (we get the name from JSX.ElementAttributesProperty) - jsxChildrenPropertyName := c.getJsxElementChildrenPropertyName(c.getJsxNamespaceAt(node)) - if !(attributesType != nil && !IsTypeAny(attributesType) && jsxChildrenPropertyName != ast.InternalSymbolNameMissing && jsxChildrenPropertyName != "") { - return nil - } - realChildren := ast.GetSemanticJsxChildren(node.Children().Nodes) - childIndex := slices.Index(realChildren, child) - childFieldType := c.getTypeOfPropertyOfContextualType(attributesType, jsxChildrenPropertyName) - if childFieldType == nil { - return nil - } - if len(realChildren) == 1 { - return childFieldType - } - return c.mapTypeEx(childFieldType, func(t *Type) *Type { - if c.isArrayLikeType(t) { - return c.getIndexedAccessType(t, c.getNumberLiteralType(jsnum.Number(childIndex))) - } - return t - }, true /*noReductions*/) -} - -func (c *Checker) discriminateContextualTypeByJSXAttributes(node *ast.Node, contextualType *Type) *Type { - key := DiscriminatedContextualTypeKey{nodeId: ast.GetNodeId(node), typeId: contextualType.id} - if discriminated := c.discriminatedContextualTypes[key]; discriminated != nil { - return discriminated - } - jsxChildrenPropertyName := c.getJsxElementChildrenPropertyName(c.getJsxNamespaceAt(node)) - discriminantProperties := core.Filter(node.AsJsxAttributes().Properties.Nodes, func(p *ast.Node) bool { - symbol := p.Symbol() - if symbol == nil || !ast.IsJsxAttribute(p) { - return false - } - initializer := p.Initializer() - return (initializer == nil || c.isPossiblyDiscriminantValue(initializer)) && c.isDiscriminantProperty(contextualType, symbol.Name) - }) - discriminantMembers := core.Filter(c.getPropertiesOfType(contextualType), func(s *ast.Symbol) bool { - if s.Flags&ast.SymbolFlagsOptional == 0 || node.Symbol() == nil || len(node.Symbol().Members) == 0 { - return false - } - element := node.Parent.Parent - if s.Name == jsxChildrenPropertyName && ast.IsJsxElement(element) && len(ast.GetSemanticJsxChildren(element.Children().Nodes)) != 0 { - return false - } - return node.Symbol().Members[s.Name] == nil && c.isDiscriminantProperty(contextualType, s.Name) - }) - discriminator := &ObjectLiteralDiscriminator{c: c, props: discriminantProperties, members: discriminantMembers} - discriminated := c.discriminateTypeByDiscriminableItems(contextualType, discriminator) - c.discriminatedContextualTypes[key] = discriminated - return discriminated -} - -func (c *Checker) elaborateJsxComponents(node *ast.Node, source *Type, target *Type, relation *Relation, diagnosticOutput *[]*ast.Diagnostic) bool { - reportedError := false - for _, prop := range node.AsJsxAttributes().Properties.Nodes { - if !ast.IsJsxSpreadAttribute(prop) && !isHyphenatedJsxName(prop.Name().Text()) { - nameType := c.getStringLiteralType(prop.Name().Text()) - if nameType != nil && nameType.flags&TypeFlagsNever == 0 { - reportedError = c.elaborateElement(source, target, relation, prop.Name(), prop.Initializer(), nameType, nil, diagnosticOutput) || reportedError - } - } - } - if ast.IsJsxOpeningElement(node.Parent) && ast.IsJsxElement(node.Parent.Parent) { - containingElement := node.Parent.Parent // Containing JSXElement - childrenPropName := c.getJsxElementChildrenPropertyName(c.getJsxNamespaceAt(node)) - if childrenPropName == ast.InternalSymbolNameMissing { - childrenPropName = "children" - } - childrenNameType := c.getStringLiteralType(childrenPropName) - childrenTargetType := c.getIndexedAccessType(target, childrenNameType) - validChildren := ast.GetSemanticJsxChildren(containingElement.Children().Nodes) - if len(validChildren) == 0 { - return reportedError - } - moreThanOneRealChildren := len(validChildren) > 1 - var arrayLikeTargetParts *Type - var nonArrayLikeTargetParts *Type - iterableType := c.getGlobalIterableType() - if iterableType != c.emptyGenericType { - anyIterable := c.createIterableType(c.anyType) - arrayLikeTargetParts = c.filterType(childrenTargetType, func(t *Type) bool { return c.isTypeAssignableTo(t, anyIterable) }) - nonArrayLikeTargetParts = c.filterType(childrenTargetType, func(t *Type) bool { return !c.isTypeAssignableTo(t, anyIterable) }) - } else { - arrayLikeTargetParts = c.filterType(childrenTargetType, c.isArrayOrTupleLikeType) - nonArrayLikeTargetParts = c.filterType(childrenTargetType, func(t *Type) bool { return !c.isArrayOrTupleLikeType(t) }) - } - var invalidTextDiagnostic *diagnostics.Message - getInvalidTextualChildDiagnostic := func() *diagnostics.Message { - if invalidTextDiagnostic == nil { - tagNameText := scanner.GetTextOfNode(node.Parent.TagName()) - diagnostic := diagnostics.X_0_components_don_t_accept_text_as_child_elements_Text_in_JSX_has_the_type_string_but_the_expected_type_of_1_is_2 - invalidTextDiagnostic = diagnostics.FormatMessage(diagnostic, tagNameText, childrenPropName, c.TypeToString(childrenTargetType)) - } - return invalidTextDiagnostic - } - if moreThanOneRealChildren { - if arrayLikeTargetParts != c.neverType { - realSource := c.createTupleType(c.checkJsxChildren(containingElement, CheckModeNormal)) - children := c.generateJsxChildren(containingElement, getInvalidTextualChildDiagnostic) - reportedError = c.elaborateIterableOrArrayLikeTargetElementwise(children, realSource, arrayLikeTargetParts, relation, diagnosticOutput) || reportedError - } else if !c.isTypeRelatedTo(c.getIndexedAccessType(source, childrenNameType), childrenTargetType, relation) { - // arity mismatch - diag := c.error(containingElement.AsJsxElement().OpeningElement.TagName(), diagnostics.This_JSX_tag_s_0_prop_expects_a_single_child_of_type_1_but_multiple_children_were_provided, childrenPropName, c.TypeToString(childrenTargetType)) - c.reportDiagnostic(diag, diagnosticOutput) - reportedError = true - } - } else { - if nonArrayLikeTargetParts != c.neverType { - child := validChildren[0] - e := c.getElaborationElementForJsxChild(child, childrenNameType, getInvalidTextualChildDiagnostic) - if e.errorNode != nil { - reportedError = c.elaborateElement(source, target, relation, e.errorNode, e.innerExpression, e.nameType, e.errorMessage, diagnosticOutput) || reportedError - } - } else if !c.isTypeRelatedTo(c.getIndexedAccessType(source, childrenNameType), childrenTargetType, relation) { - // arity mismatch - diag := c.error(containingElement.AsJsxElement().OpeningElement.TagName(), diagnostics.This_JSX_tag_s_0_prop_expects_type_1_which_requires_multiple_children_but_only_a_single_child_was_provided, childrenPropName, c.TypeToString(childrenTargetType)) - c.reportDiagnostic(diag, diagnosticOutput) - reportedError = true - } - } - } - return reportedError -} - -type JsxElaborationElement struct { - errorNode *ast.Node - innerExpression *ast.Node - nameType *Type - errorMessage *diagnostics.Message -} - -func (c *Checker) generateJsxChildren(node *ast.Node, getInvalidTextDiagnostic func() *diagnostics.Message) iter.Seq[JsxElaborationElement] { - return func(yield func(JsxElaborationElement) bool) { - memberOffset := 0 - for i, child := range node.Children().Nodes { - nameType := c.getNumberLiteralType(jsnum.Number(i - memberOffset)) - e := c.getElaborationElementForJsxChild(child, nameType, getInvalidTextDiagnostic) - if e.errorNode != nil { - if !yield(e) { - return - } - } else { - memberOffset++ - } - } - } -} - -func (c *Checker) getElaborationElementForJsxChild(child *ast.Node, nameType *Type, getInvalidTextDiagnostic func() *diagnostics.Message) JsxElaborationElement { - switch child.Kind { - case ast.KindJsxExpression: - // child is of the type of the expression - return JsxElaborationElement{errorNode: child, innerExpression: child.Expression(), nameType: nameType} - case ast.KindJsxText: - if child.AsJsxText().ContainsOnlyTriviaWhiteSpaces { - // Whitespace only jsx text isn't real jsx text - return JsxElaborationElement{} - } - // child is a string - return JsxElaborationElement{errorNode: child, innerExpression: nil, nameType: nameType, errorMessage: getInvalidTextDiagnostic()} - case ast.KindJsxElement, ast.KindJsxSelfClosingElement, ast.KindJsxFragment: - // child is of type JSX.Element - return JsxElaborationElement{errorNode: child, innerExpression: child, nameType: nameType} - } - panic("Unhandled case in getElaborationElementForJsxChild") -} - -func (c *Checker) elaborateIterableOrArrayLikeTargetElementwise(iterator iter.Seq[JsxElaborationElement], source *Type, target *Type, relation *Relation, diagnosticOutput *[]*ast.Diagnostic) bool { - tupleOrArrayLikeTargetParts := c.filterType(target, c.isArrayOrTupleLikeType) - nonTupleOrArrayLikeTargetParts := c.filterType(target, func(t *Type) bool { return !c.isArrayOrTupleLikeType(t) }) - // If `nonTupleOrArrayLikeTargetParts` is not `never`, then that should mean `Iterable` is defined. - var iterationType *Type - if nonTupleOrArrayLikeTargetParts != c.neverType { - iterationType = c.getIterationTypeOfIterable(IterationUseForOf, IterationTypeKindYield, nonTupleOrArrayLikeTargetParts, nil /*errorNode*/) - } - reportedError := false - for e := range iterator { - prop := e.errorNode - next := e.innerExpression - nameType := e.nameType - targetPropType := iterationType - var targetIndexedPropType *Type - if tupleOrArrayLikeTargetParts != c.neverType { - targetIndexedPropType = c.getBestMatchIndexedAccessTypeOrUndefined(source, tupleOrArrayLikeTargetParts, nameType) - } - if targetIndexedPropType != nil && targetIndexedPropType.flags&TypeFlagsIndexedAccess == 0 { - if iterationType != nil { - targetPropType = c.getUnionType([]*Type{iterationType, targetIndexedPropType}) - } else { - targetPropType = targetIndexedPropType - } - } - if targetPropType == nil { - continue - } - sourcePropType := c.getIndexedAccessTypeOrUndefined(source, nameType, AccessFlagsNone, nil, nil) - if sourcePropType == nil { - continue - } - propName := c.getPropertyNameFromIndex(nameType, nil /*accessNode*/) - if !c.checkTypeRelatedTo(sourcePropType, targetPropType, relation, nil /*errorNode*/) { - elaborated := next != nil && c.elaborateError(next, sourcePropType, targetPropType, relation, nil /*headMessage*/, diagnosticOutput) - reportedError = true - if !elaborated { - // Issue error on the prop itself, since the prop couldn't elaborate the error. Use the expression type, if available. - specificSource := sourcePropType - if next != nil { - specificSource = c.checkExpressionForMutableLocationWithContextualType(next, sourcePropType) - } - if c.exactOptionalPropertyTypes && c.isExactOptionalPropertyMismatch(specificSource, targetPropType) { - diag := createDiagnosticForNode(prop, diagnostics.Type_0_is_not_assignable_to_type_1_with_exactOptionalPropertyTypes_Colon_true_Consider_adding_undefined_to_the_type_of_the_target, c.TypeToString(specificSource), c.TypeToString(targetPropType)) - c.reportDiagnostic(diag, diagnosticOutput) - } else { - targetIsOptional := propName != ast.InternalSymbolNameMissing && core.OrElse(c.getPropertyOfType(tupleOrArrayLikeTargetParts, propName), c.unknownSymbol).Flags&ast.SymbolFlagsOptional != 0 - sourceIsOptional := propName != ast.InternalSymbolNameMissing && core.OrElse(c.getPropertyOfType(source, propName), c.unknownSymbol).Flags&ast.SymbolFlagsOptional != 0 - targetPropType = c.removeMissingType(targetPropType, targetIsOptional) - sourcePropType = c.removeMissingType(sourcePropType, targetIsOptional && sourceIsOptional) - result := c.checkTypeRelatedToEx(specificSource, targetPropType, relation, prop, e.errorMessage, diagnosticOutput) - if result && specificSource != sourcePropType { - // If for whatever reason the expression type doesn't yield an error, make sure we still issue an error on the sourcePropType - c.checkTypeRelatedToEx(sourcePropType, targetPropType, relation, prop, e.errorMessage, diagnosticOutput) - } - } - } - } - } - return reportedError -} - -func (c *Checker) getSuggestedSymbolForNonexistentJSXAttribute(name string, containingType *Type) *ast.Symbol { - properties := c.getPropertiesOfType(containingType) - var jsxSpecific *ast.Symbol - switch name { - case "for": - jsxSpecific = core.Find(properties, func(x *ast.Symbol) bool { return ast.SymbolName(x) == "htmlFor" }) - case "class": - jsxSpecific = core.Find(properties, func(x *ast.Symbol) bool { return ast.SymbolName(x) == "className" }) - } - if jsxSpecific != nil { - return jsxSpecific - } - return c.getSpellingSuggestionForName(name, properties, ast.SymbolFlagsValue) -} - -func (c *Checker) getJSXFragmentType(node *ast.Node) *Type { - // An opening fragment is required in order for `getJsxNamespace` to give the fragment factory - links := c.sourceFileLinks.Get(ast.GetSourceFileOfNode(node)) - if links.jsxFragmentType != nil { - return links.jsxFragmentType - } - jsxFragmentFactoryName := c.getJsxNamespace(node) - // #38720/60122, allow null as jsxFragmentFactory - shouldResolveFactoryReference := (c.compilerOptions.Jsx == core.JsxEmitReact || c.compilerOptions.JsxFragmentFactory != "") && jsxFragmentFactoryName != "null" - if !shouldResolveFactoryReference { - links.jsxFragmentType = c.anyType - return links.jsxFragmentType - } - jsxFactorySymbol := c.getJsxNamespaceContainerForImplicitImport(node) - if jsxFactorySymbol == nil { - shouldModuleRefErr := c.compilerOptions.Jsx != core.JsxEmitPreserve && c.compilerOptions.Jsx != core.JsxEmitReactNative - flags := ast.SymbolFlagsValue - if !shouldModuleRefErr { - flags &= ^ast.SymbolFlagsEnum - } - jsxFactorySymbol = c.resolveName(node, jsxFragmentFactoryName, flags, diagnostics.Using_JSX_fragments_requires_fragment_factory_0_to_be_in_scope_but_it_could_not_be_found, true /*isUse*/, false /*excludeGlobals*/) - } - if jsxFactorySymbol == nil { - links.jsxFragmentType = c.errorType - return links.jsxFragmentType - } - if jsxFactorySymbol.Name == ReactNames.Fragment { - links.jsxFragmentType = c.getTypeOfSymbol(jsxFactorySymbol) - return links.jsxFragmentType - } - resolvedAlias := jsxFactorySymbol - if jsxFactorySymbol.Flags&ast.SymbolFlagsAlias != 0 { - resolvedAlias = c.resolveAlias(jsxFactorySymbol) - } - - reactExports := c.getExportsOfSymbol(resolvedAlias) - typeSymbol := c.getSymbol(reactExports, ReactNames.Fragment, ast.SymbolFlagsBlockScopedVariable) - if typeSymbol != nil { - links.jsxFragmentType = c.getTypeOfSymbol(typeSymbol) - } else { - links.jsxFragmentType = c.errorType - } - return links.jsxFragmentType -} - -func (c *Checker) resolveJsxOpeningLikeElement(node *ast.Node, candidatesOutArray *[]*Signature, checkMode CheckMode) *Signature { - isJsxOpenFragment := ast.IsJsxOpeningFragment(node) - var exprTypes *Type - if !isJsxOpenFragment { - if isJsxIntrinsicTagName(node.TagName()) { - result := c.getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node) - fakeSignature := c.createSignatureForJSXIntrinsic(node, result) - c.checkTypeAssignableToAndOptionallyElaborate(c.checkExpressionWithContextualType(node.Attributes(), c.getEffectiveFirstArgumentForJsxSignature(fakeSignature, node), nil /*inferenceContext*/, CheckModeNormal), result, node.TagName(), node.Attributes(), nil, nil) - typeArguments := node.TypeArguments() - if len(typeArguments) != 0 { - c.checkSourceElements(typeArguments) - c.diagnostics.Add(ast.NewDiagnostic(ast.GetSourceFileOfNode(node), node.TypeArgumentList().Loc, diagnostics.Expected_0_type_arguments_but_got_1, 0, len(typeArguments))) - } - return fakeSignature - } - exprTypes = c.checkExpression(node.TagName()) - } else { - exprTypes = c.getJSXFragmentType(node) - } - apparentType := c.getApparentType(exprTypes) - if c.isErrorType(apparentType) { - return c.resolveErrorCall(node) - } - signatures := c.getUninstantiatedJsxSignaturesOfType(exprTypes, node) - if c.isUntypedFunctionCall(exprTypes, apparentType, len(signatures), 0 /*constructSignatures*/) { - return c.resolveUntypedCall(node) - } - if len(signatures) == 0 { - // We found no signatures at all, which is an error - if isJsxOpenFragment { - c.error(node, diagnostics.JSX_element_type_0_does_not_have_any_construct_or_call_signatures, scanner.GetTextOfNode(node)) - } else { - c.error(node.TagName(), diagnostics.JSX_element_type_0_does_not_have_any_construct_or_call_signatures, scanner.GetTextOfNode(node.TagName())) - } - return c.resolveErrorCall(node) - } - return c.resolveCall(node, signatures, candidatesOutArray, checkMode, SignatureFlagsNone, nil) -} - -// Check if the given signature can possibly be a signature called by the JSX opening-like element. -// @param node a JSX opening-like element we are trying to figure its call signature -// @param signature a candidate signature we are trying whether it is a call signature -// @param relation a relationship to check parameter and argument type -func (c *Checker) checkApplicableSignatureForJsxCallLikeElement(node *ast.Node, signature *Signature, relation *Relation, checkMode CheckMode, reportErrors bool, diagnosticOutput *[]*ast.Diagnostic) bool { - // Stateless function components can have maximum of three arguments: "props", "context", and "updater". - // However "context" and "updater" are implicit and can't be specify by users. Only the first parameter, props, - // can be specified by users through attributes property. - paramType := c.getEffectiveFirstArgumentForJsxSignature(signature, node) - var attributesType *Type - if ast.IsJsxOpeningFragment(node) { - attributesType = c.createJsxAttributesTypeFromAttributesProperty(node, CheckModeNormal) - } else { - attributesType = c.checkExpressionWithContextualType(node.Attributes(), paramType, nil /*inferenceContext*/, checkMode) - } - var checkAttributesType *Type - checkTagNameDoesNotExpectTooManyArguments := func() bool { - if c.getJsxNamespaceContainerForImplicitImport(node) != nil { - return true // factory is implicitly jsx/jsxdev - assume it fits the bill, since we don't strongly look for the jsx/jsxs/jsxDEV factory APIs anywhere else (at least not yet) - } - // We assume fragments have the correct arity since the node does not have attributes - var tagType *Type - if (ast.IsJsxOpeningElement(node) || ast.IsJsxSelfClosingElement(node)) && !(isJsxIntrinsicTagName(node.TagName()) || ast.IsJsxNamespacedName(node.TagName())) { - tagType = c.checkExpression(node.TagName()) - } - if tagType == nil { - return true - } - tagCallSignatures := c.getSignaturesOfType(tagType, SignatureKindCall) - if len(tagCallSignatures) == 0 { - return true - } - factory := c.getJsxFactoryEntity(node) - if factory == nil { - return true - } - factorySymbol := c.resolveEntityName(factory, ast.SymbolFlagsValue, true /*ignoreErrors*/, false /*dontResolveAlias*/, node) - if factorySymbol == nil { - return true - } - - factoryType := c.getTypeOfSymbol(factorySymbol) - callSignatures := c.getSignaturesOfType(factoryType, SignatureKindCall) - if len(callSignatures) == 0 { - return true - } - hasFirstParamSignatures := false - maxParamCount := 0 - // Check that _some_ first parameter expects a FC-like thing, and that some overload of the SFC expects an acceptable number of arguments - for _, sig := range callSignatures { - firstparam := c.getTypeAtPosition(sig, 0) - signaturesOfParam := c.getSignaturesOfType(firstparam, SignatureKindCall) - if len(signaturesOfParam) == 0 { - continue - } - for _, paramSig := range signaturesOfParam { - hasFirstParamSignatures = true - if c.hasEffectiveRestParameter(paramSig) { - return true // some signature has a rest param, so function components can have an arbitrary number of arguments - } - paramCount := c.getParameterCount(paramSig) - if paramCount > maxParamCount { - maxParamCount = paramCount - } - } - } - if !hasFirstParamSignatures { - // Not a single signature had a first parameter which expected a signature - for back compat, and - // to guard against generic factories which won't have signatures directly, do not error - return true - } - absoluteMinArgCount := math.MaxInt - for _, tagSig := range tagCallSignatures { - tagRequiredArgCount := c.getMinArgumentCount(tagSig) - if tagRequiredArgCount < absoluteMinArgCount { - absoluteMinArgCount = tagRequiredArgCount - } - } - if absoluteMinArgCount <= maxParamCount { - return true // some signature accepts the number of arguments the function component provides - } - if reportErrors { - tagName := node.TagName() - // We will not report errors in this function for fragments, since we do not check them in this function - diag := NewDiagnosticForNode(tagName, diagnostics.Tag_0_expects_at_least_1_arguments_but_the_JSX_factory_2_provides_at_most_3, entityNameToString(tagName), absoluteMinArgCount, entityNameToString(factory), maxParamCount) - tagNameSymbol := c.getSymbolAtLocation(tagName, false) - if tagNameSymbol != nil && tagNameSymbol.ValueDeclaration != nil { - diag.AddRelatedInfo(NewDiagnosticForNode(tagNameSymbol.ValueDeclaration, diagnostics.X_0_is_declared_here, entityNameToString(tagName))) - } - c.reportDiagnostic(diag, diagnosticOutput) - } - return false - } - if checkMode&CheckModeSkipContextSensitive != 0 { - checkAttributesType = c.getRegularTypeOfObjectLiteral(attributesType) - } else { - checkAttributesType = attributesType - } - if !checkTagNameDoesNotExpectTooManyArguments() { - return false - } - var errorNode *ast.Node - if reportErrors { - if ast.IsJsxOpeningFragment(node) { - errorNode = node - } else { - errorNode = node.TagName() - } - } - var attributes *ast.Node - if !ast.IsJsxOpeningFragment(node) { - attributes = node.Attributes() - } - return c.checkTypeRelatedToAndOptionallyElaborate(checkAttributesType, paramType, relation, errorNode, attributes, nil, diagnosticOutput) -} - -// Get attributes type of the JSX opening-like element. The result is from resolving "attributes" property of the opening-like element. -// -// @param openingLikeElement a JSX opening-like element -// @param filter a function to remove attributes that will not participate in checking whether attributes are assignable -// @return an anonymous type (similar to the one returned by checkObjectLiteral) in which its properties are attributes property. -// @remarks Because this function calls getSpreadType, it needs to use the same checks as checkObjectLiteral, -// which also calls getSpreadType. -func (c *Checker) createJsxAttributesTypeFromAttributesProperty(openingLikeElement *ast.Node, checkMode CheckMode) *Type { - var allAttributesTable ast.SymbolTable - if c.strictNullChecks { - allAttributesTable = make(ast.SymbolTable) - } - attributesTable := make(ast.SymbolTable) - var attributesSymbol *ast.Symbol - attributeParent := openingLikeElement - spread := c.emptyJsxObjectType - var hasSpreadAnyType bool - var typeToIntersect *Type - var explicitlySpecifyChildrenAttribute bool - objectFlags := ObjectFlagsJsxAttributes - createJsxAttributesType := func() *Type { - objectFlags |= ObjectFlagsFreshLiteral - result := c.newAnonymousType(attributesSymbol, attributesTable, nil, nil, nil) - result.objectFlags |= objectFlags | ObjectFlagsObjectLiteral | ObjectFlagsContainsObjectOrArrayLiteral - return result - } - jsxChildrenPropertyName := c.getJsxElementChildrenPropertyName(c.getJsxNamespaceAt(openingLikeElement)) - isJsxOpenFragment := ast.IsJsxOpeningFragment(openingLikeElement) - if !isJsxOpenFragment { - attributes := openingLikeElement.Attributes() - attributesSymbol = attributes.Symbol() - attributeParent = attributes - contextualType := c.getContextualType(attributes, ContextFlagsNone) - // Create anonymous type from given attributes symbol table. - // @param symbol a symbol of JsxAttributes containing attributes corresponding to attributesTable - // @param attributesTable a symbol table of attributes property - for _, attributeDecl := range attributes.AsJsxAttributes().Properties.Nodes { - member := attributeDecl.Symbol() - if ast.IsJsxAttribute(attributeDecl) { - exprType := c.checkJsxAttribute(attributeDecl, checkMode) - objectFlags |= exprType.objectFlags & ObjectFlagsPropagatingFlags - attributeSymbol := c.newSymbol(ast.SymbolFlagsProperty|member.Flags, member.Name) - attributeSymbol.Declarations = member.Declarations - attributeSymbol.Parent = member.Parent - if member.ValueDeclaration != nil { - attributeSymbol.ValueDeclaration = member.ValueDeclaration - } - links := c.valueSymbolLinks.Get(attributeSymbol) - links.resolvedType = exprType - links.target = member - attributesTable[attributeSymbol.Name] = attributeSymbol - if allAttributesTable != nil { - allAttributesTable[attributeSymbol.Name] = attributeSymbol - } - if attributeDecl.Name().Text() == jsxChildrenPropertyName { - explicitlySpecifyChildrenAttribute = true - } - if contextualType != nil { - prop := c.getPropertyOfType(contextualType, member.Name) - if prop != nil && prop.Declarations != nil && c.isDeprecatedSymbol(prop) && ast.IsIdentifier(attributeDecl.Name()) { - c.addDeprecatedSuggestion(attributeDecl.Name(), prop.Declarations, attributeDecl.Name().Text()) - } - } - if contextualType != nil && checkMode&CheckModeInferential != 0 && checkMode&CheckModeSkipContextSensitive == 0 && c.isContextSensitive(attributeDecl) { - inferenceContext := c.getInferenceContext(attributes) - debug.AssertIsDefined(inferenceContext) - // In CheckMode.Inferential we should always have an inference context - inferenceNode := attributeDecl.Initializer().Expression() - c.addIntraExpressionInferenceSite(inferenceContext, inferenceNode, exprType) - } - } else { - debug.Assert(attributeDecl.Kind == ast.KindJsxSpreadAttribute) - if len(attributesTable) != 0 { - spread = c.getSpreadType(spread, createJsxAttributesType(), attributesSymbol, objectFlags, false /*readonly*/) - attributesTable = make(ast.SymbolTable) - } - exprType := c.getReducedType(c.checkExpressionEx(attributeDecl.Expression(), checkMode&CheckModeInferential)) - if IsTypeAny(exprType) { - hasSpreadAnyType = true - } - if c.isValidSpreadType(exprType) { - spread = c.getSpreadType(spread, exprType, attributesSymbol, objectFlags, false /*readonly*/) - if allAttributesTable != nil { - c.checkSpreadPropOverrides(exprType, allAttributesTable, attributeDecl) - } - } else { - c.error(attributeDecl.Expression(), diagnostics.Spread_types_may_only_be_created_from_object_types) - if typeToIntersect != nil { - typeToIntersect = c.getIntersectionType([]*Type{typeToIntersect, exprType}) - } else { - typeToIntersect = exprType - } - } - } - } - if !hasSpreadAnyType { - if len(attributesTable) != 0 { - spread = c.getSpreadType(spread, createJsxAttributesType(), attributesSymbol, objectFlags, false /*readonly*/) - } - } - } - parentHasSemanticJsxChildren := func(openingLikeElement *ast.Node) bool { - // Handle children attribute - parent := openingLikeElement.Parent - if parent == nil { - return false - } - var children []*ast.Node - - switch { - case ast.IsJsxElement(parent): - // We have to check that openingElement of the parent is the one we are visiting as this may not be true for selfClosingElement - if parent.AsJsxElement().OpeningElement == openingLikeElement { - children = parent.AsJsxElement().Children.Nodes - } - case ast.IsJsxFragment(parent): - if parent.AsJsxFragment().OpeningFragment == openingLikeElement { - children = parent.AsJsxFragment().Children.Nodes - } - } - return len(ast.GetSemanticJsxChildren(children)) != 0 - } - if parentHasSemanticJsxChildren(openingLikeElement) { - var childTypes []*Type = c.checkJsxChildren(openingLikeElement.Parent, checkMode) - if !hasSpreadAnyType && jsxChildrenPropertyName != ast.InternalSymbolNameMissing && jsxChildrenPropertyName != "" { - // Error if there is a attribute named "children" explicitly specified and children element. - // This is because children element will overwrite the value from attributes. - // Note: we will not warn "children" attribute overwritten if "children" attribute is specified in object spread. - if explicitlySpecifyChildrenAttribute { - c.error(attributeParent, diagnostics.X_0_are_specified_twice_The_attribute_named_0_will_be_overwritten, jsxChildrenPropertyName) - } - var childrenContextualType *Type - if ast.IsJsxOpeningElement(openingLikeElement) { - if contextualType := c.getApparentTypeOfContextualType(openingLikeElement.Attributes(), ContextFlagsNone); contextualType != nil { - childrenContextualType = c.getTypeOfPropertyOfContextualType(contextualType, jsxChildrenPropertyName) - } - } - // If there are children in the body of JSX element, create dummy attribute "children" with the union of children types so that it will pass the attribute checking process - childrenPropSymbol := c.newSymbol(ast.SymbolFlagsProperty, jsxChildrenPropertyName) - links := c.valueSymbolLinks.Get(childrenPropSymbol) - switch { - case len(childTypes) == 1: - links.resolvedType = childTypes[0] - case childrenContextualType != nil && someType(childrenContextualType, c.isTupleLikeType): - links.resolvedType = c.createTupleType(childTypes) - default: - links.resolvedType = c.createArrayType(c.getUnionType(childTypes)) - } - // Fake up a property declaration for the children - childrenPropSymbol.ValueDeclaration = c.factory.NewPropertySignatureDeclaration(nil, c.factory.NewIdentifier(jsxChildrenPropertyName), nil /*postfixToken*/, nil /*type*/, nil /*initializer*/) - childrenPropSymbol.ValueDeclaration.Parent = attributeParent - childrenPropSymbol.ValueDeclaration.AsPropertySignatureDeclaration().Symbol = childrenPropSymbol - childPropMap := make(ast.SymbolTable) - childPropMap[jsxChildrenPropertyName] = childrenPropSymbol - spread = c.getSpreadType(spread, c.newAnonymousType(attributesSymbol, childPropMap, nil, nil, nil), attributesSymbol, objectFlags, false /*readonly*/) - } - } - if hasSpreadAnyType { - return c.anyType - } - if typeToIntersect != nil { - if spread != c.emptyJsxObjectType { - return c.getIntersectionType([]*Type{typeToIntersect, spread}) - } - return typeToIntersect - } - if spread == c.emptyJsxObjectType { - return createJsxAttributesType() - } - return spread -} - -func (c *Checker) checkJsxAttribute(node *ast.Node, checkMode CheckMode) *Type { - if node.Initializer() != nil { - return c.checkExpressionForMutableLocation(node.Initializer(), checkMode) - } - // is sugar for - return c.trueType -} - -func (c *Checker) checkJsxChildren(node *ast.Node, checkMode CheckMode) []*Type { - var childTypes []*Type - for _, child := range node.Children().Nodes { - // In React, JSX text that contains only whitespaces will be ignored so we don't want to type-check that - // because then type of children property will have constituent of string type. - if ast.IsJsxText(child) { - if !child.AsJsxText().ContainsOnlyTriviaWhiteSpaces { - childTypes = append(childTypes, c.stringType) - } - } else if ast.IsJsxExpression(child) && child.Expression() == nil { - // empty jsx expressions don't *really* count as present children - continue - } else { - childTypes = append(childTypes, c.checkExpressionForMutableLocation(child, checkMode)) - } - } - return childTypes -} - -func (c *Checker) getUninstantiatedJsxSignaturesOfType(elementType *Type, caller *ast.Node) []*Signature { - if elementType.flags&TypeFlagsString != 0 { - return []*Signature{c.anySignature} - } - if elementType.flags&TypeFlagsStringLiteral != 0 { - intrinsicType := c.getIntrinsicAttributesTypeFromStringLiteralType(elementType, caller) - if intrinsicType == nil { - c.error(caller, diagnostics.Property_0_does_not_exist_on_type_1, getStringLiteralValue(elementType), "JSX."+JsxNames.IntrinsicElements) - return nil - } - fakeSignature := c.createSignatureForJSXIntrinsic(caller, intrinsicType) - return []*Signature{fakeSignature} - } - apparentElemType := c.getApparentType(elementType) - // Resolve the signatures, preferring constructor - signatures := c.getSignaturesOfType(apparentElemType, SignatureKindConstruct) - if len(signatures) == 0 { - // No construct signatures, try call signatures - signatures = c.getSignaturesOfType(apparentElemType, SignatureKindCall) - } - if len(signatures) == 0 && apparentElemType.flags&TypeFlagsUnion != 0 { - // If each member has some combination of new/call signatures; make a union signature list for those - signatures = c.getUnionSignatures(core.Map(apparentElemType.Types(), func(t *Type) []*Signature { - return c.getUninstantiatedJsxSignaturesOfType(t, caller) - })) - } - return signatures -} - -func (c *Checker) getEffectiveFirstArgumentForJsxSignature(signature *Signature, node *ast.Node) *Type { - if ast.IsJsxOpeningFragment(node) || c.getJsxReferenceKind(node) != JsxReferenceKindComponent { - return c.getJsxPropsTypeFromCallSignature(signature, node) - } - return c.getJsxPropsTypeFromClassType(signature, node) -} - -func (c *Checker) getJsxPropsTypeFromCallSignature(sig *Signature, context *ast.Node) *Type { - propsType := c.getTypeOfFirstParameterOfSignatureWithFallback(sig, c.unknownType) - propsType = c.getJsxManagedAttributesFromLocatedAttributes(context, c.getJsxNamespaceAt(context), propsType) - intrinsicAttribs := c.getJsxType(JsxNames.IntrinsicAttributes, context) - if !c.isErrorType(intrinsicAttribs) { - propsType = c.intersectTypes(intrinsicAttribs, propsType) - } - return propsType -} - -func (c *Checker) getJsxPropsTypeFromClassType(sig *Signature, context *ast.Node) *Type { - ns := c.getJsxNamespaceAt(context) - forcedLookupLocation := c.getJsxElementPropertiesName(ns) - var attributesType *Type - switch forcedLookupLocation { - case ast.InternalSymbolNameMissing: - attributesType = c.getTypeOfFirstParameterOfSignatureWithFallback(sig, c.unknownType) - case "": - attributesType = c.getReturnTypeOfSignature(sig) - default: - attributesType = c.getJsxPropsTypeForSignatureFromMember(sig, forcedLookupLocation) - if attributesType == nil && len(context.Attributes().AsJsxAttributes().Properties.Nodes) != 0 { - // There is no property named 'props' on this instance type - c.error(context, diagnostics.JSX_element_class_does_not_support_attributes_because_it_does_not_have_a_0_property, forcedLookupLocation) - } - } - if attributesType == nil { - return c.unknownType - } - attributesType = c.getJsxManagedAttributesFromLocatedAttributes(context, ns, attributesType) - if IsTypeAny(attributesType) { - // Props is of type 'any' or unknown - return attributesType - } - // Normal case -- add in IntrinsicClassAttributes and IntrinsicAttributes - apparentAttributesType := attributesType - intrinsicClassAttribs := c.getJsxType(JsxNames.IntrinsicClassAttributes, context) - if !c.isErrorType(intrinsicClassAttribs) { - typeParams := c.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(intrinsicClassAttribs.symbol) - hostClassType := c.getReturnTypeOfSignature(sig) - var libraryManagedAttributeType *Type - if typeParams != nil { - // apply JSX.IntrinsicClassAttributes - inferredArgs := c.fillMissingTypeArguments([]*Type{hostClassType}, typeParams, c.getMinTypeArgumentCount(typeParams), ast.IsInJSFile(context)) - libraryManagedAttributeType = c.instantiateType(intrinsicClassAttribs, newTypeMapper(typeParams, inferredArgs)) - } else { - libraryManagedAttributeType = intrinsicClassAttribs - } - apparentAttributesType = c.intersectTypes(libraryManagedAttributeType, apparentAttributesType) - } - intrinsicAttribs := c.getJsxType(JsxNames.IntrinsicAttributes, context) - if !c.isErrorType(intrinsicAttribs) { - apparentAttributesType = c.intersectTypes(intrinsicAttribs, apparentAttributesType) - } - return apparentAttributesType -} - -func (c *Checker) getJsxPropsTypeForSignatureFromMember(sig *Signature, forcedLookupLocation string) *Type { - if sig.composite != nil { - // JSX Elements using the legacy `props`-field based lookup (eg, react class components) need to treat the `props` member as an input - // instead of an output position when resolving the signature. We need to go back to the input signatures of the composite signature, - // get the type of `props` on each return type individually, and then _intersect them_, rather than union them (as would normally occur - // for a union signature). It's an unfortunate quirk of looking in the output of the signature for the type we want to use for the input. - // The default behavior of `getTypeOfFirstParameterOfSignatureWithFallback` when no `props` member name is defined is much more sane. - var results []*Type - for _, signature := range sig.composite.signatures { - instance := c.getReturnTypeOfSignature(signature) - if IsTypeAny(instance) { - return instance - } - propType := c.getTypeOfPropertyOfType(instance, forcedLookupLocation) - if propType == nil { - return nil - } - results = append(results, propType) - } - return c.getIntersectionType(results) - // Same result for both union and intersection signatures - } - instanceType := c.getReturnTypeOfSignature(sig) - if IsTypeAny(instanceType) { - return instanceType - } - return c.getTypeOfPropertyOfType(instanceType, forcedLookupLocation) -} - -func (c *Checker) getJsxManagedAttributesFromLocatedAttributes(context *ast.Node, ns *ast.Symbol, attributesType *Type) *Type { - managedSym := c.getJsxLibraryManagedAttributes(ns) - if managedSym != nil { - ctorType := c.getStaticTypeOfReferencedJsxConstructor(context) - result := c.instantiateAliasOrInterfaceWithDefaults(managedSym, []*Type{ctorType, attributesType}, ast.IsInJSFile(context)) - if result != nil { - return result - } - } - return attributesType -} - -func (c *Checker) instantiateAliasOrInterfaceWithDefaults(managedSym *ast.Symbol, typeArguments []*Type, inJavaScript bool) *Type { - declaredManagedType := c.getDeclaredTypeOfSymbol(managedSym) - // fetches interface type, or initializes symbol links type parmaeters - if managedSym.Flags&ast.SymbolFlagsTypeAlias != 0 { - params := c.typeAliasLinks.Get(managedSym).typeParameters - if len(params) >= len(typeArguments) { - args := c.fillMissingTypeArguments(typeArguments, params, len(typeArguments), inJavaScript) - if len(args) == 0 { - return declaredManagedType - } - return c.getTypeAliasInstantiation(managedSym, args, nil) - } - } - if len(declaredManagedType.AsInterfaceType().TypeParameters()) >= len(typeArguments) { - args := c.fillMissingTypeArguments(typeArguments, declaredManagedType.AsInterfaceType().TypeParameters(), len(typeArguments), inJavaScript) - return c.createTypeReference(declaredManagedType, args) - } - return nil -} - -func (c *Checker) getJsxLibraryManagedAttributes(jsxNamespace *ast.Symbol) *ast.Symbol { - if jsxNamespace != nil { - return c.getSymbol(jsxNamespace.Exports, JsxNames.LibraryManagedAttributes, ast.SymbolFlagsType) - } - return nil -} - -func (c *Checker) getJsxElementTypeSymbol(jsxNamespace *ast.Symbol) *ast.Symbol { - // JSX.ElementType [symbol] - if jsxNamespace != nil { - return c.getSymbol(jsxNamespace.Exports, JsxNames.ElementType, ast.SymbolFlagsType) - } - return nil -} - -// e.g. "props" for React.d.ts, -// or InternalSymbolNameMissing if ElementAttributesProperty doesn't exist (which means all -// -// non-intrinsic elements' attributes type is 'any'), -// -// or "" if it has 0 properties (which means every -// -// non-intrinsic elements' attributes type is the element instance type) -func (c *Checker) getJsxElementPropertiesName(jsxNamespace *ast.Symbol) string { - return c.getNameFromJsxElementAttributesContainer(JsxNames.ElementAttributesPropertyNameContainer, jsxNamespace) -} - -func (c *Checker) getJsxElementChildrenPropertyName(jsxNamespace *ast.Symbol) string { - if c.compilerOptions.Jsx == core.JsxEmitReactJSX || c.compilerOptions.Jsx == core.JsxEmitReactJSXDev { - // In these JsxEmit modes the children property is fixed to 'children' - return "children" - } - return c.getNameFromJsxElementAttributesContainer(JsxNames.ElementChildrenAttributeNameContainer, jsxNamespace) -} - -// Look into JSX namespace and then look for container with matching name as nameOfAttribPropContainer. -// Get a single property from that container if existed. Report an error if there are more than one property. -// -// @param nameOfAttribPropContainer a string of value JsxNames.ElementAttributesPropertyNameContainer or JsxNames.ElementChildrenAttributeNameContainer -// -// if other string is given or the container doesn't exist, return undefined. -func (c *Checker) getNameFromJsxElementAttributesContainer(nameOfAttribPropContainer string, jsxNamespace *ast.Symbol) string { - // JSX.ElementAttributesProperty | JSX.ElementChildrenAttribute [symbol] - if jsxNamespace != nil { - jsxElementAttribPropInterfaceSym := c.getSymbol(jsxNamespace.Exports, nameOfAttribPropContainer, ast.SymbolFlagsType) - if jsxElementAttribPropInterfaceSym != nil { - jsxElementAttribPropInterfaceType := c.getDeclaredTypeOfSymbol(jsxElementAttribPropInterfaceSym) - propertiesOfJsxElementAttribPropInterface := c.getPropertiesOfType(jsxElementAttribPropInterfaceType) - // Element Attributes has zero properties, so the element attributes type will be the class instance type - if len(propertiesOfJsxElementAttribPropInterface) == 0 { - return "" - } - if len(propertiesOfJsxElementAttribPropInterface) == 1 { - return propertiesOfJsxElementAttribPropInterface[0].Name - } - if len(propertiesOfJsxElementAttribPropInterface) > 1 && len(jsxElementAttribPropInterfaceSym.Declarations) != 0 { - // More than one property on ElementAttributesProperty is an error - c.error(jsxElementAttribPropInterfaceSym.Declarations[0], diagnostics.The_global_type_JSX_0_may_not_have_more_than_one_property, nameOfAttribPropContainer) - } - } - } - return ast.InternalSymbolNameMissing -} - -func (c *Checker) getStaticTypeOfReferencedJsxConstructor(context *ast.Node) *Type { - if ast.IsJsxOpeningFragment(context) { - return c.getJSXFragmentType(context) - } - if isJsxIntrinsicTagName(context.TagName()) { - result := c.getIntrinsicAttributesTypeFromJsxOpeningLikeElement(context) - fakeSignature := c.createSignatureForJSXIntrinsic(context, result) - return c.getOrCreateTypeFromSignature(fakeSignature) - } - tagType := c.checkExpressionCached(context.TagName()) - if tagType.flags&TypeFlagsStringLiteral != 0 { - result := c.getIntrinsicAttributesTypeFromStringLiteralType(tagType, context) - if result == nil { - return c.errorType - } - fakeSignature := c.createSignatureForJSXIntrinsic(context, result) - return c.getOrCreateTypeFromSignature(fakeSignature) - } - return tagType -} - -func (c *Checker) getIntrinsicAttributesTypeFromStringLiteralType(t *Type, location *ast.Node) *Type { - // If the elemType is a stringLiteral type, we can then provide a check to make sure that the string literal type is one of the Jsx intrinsic element type - // For example: - // var CustomTag: "h1" = "h1"; - // Hello World - intrinsicElementsType := c.getJsxType(JsxNames.IntrinsicElements, location) - if !c.isErrorType(intrinsicElementsType) { - stringLiteralTypeName := getStringLiteralValue(t) - intrinsicProp := c.getPropertyOfType(intrinsicElementsType, stringLiteralTypeName) - if intrinsicProp != nil { - return c.getTypeOfSymbol(intrinsicProp) - } - indexSignatureType := c.getIndexTypeOfType(intrinsicElementsType, c.stringType) - if indexSignatureType != nil { - return indexSignatureType - } - return nil - } - // If we need to report an error, we already done so here. So just return any to prevent any more error downstream - return c.anyType -} - -func (c *Checker) getJsxReferenceKind(node *ast.Node) JsxReferenceKind { - if isJsxIntrinsicTagName(node.TagName()) { - return JsxReferenceKindMixed - } - tagType := c.getApparentType(c.checkExpression(node.TagName())) - if len(c.getSignaturesOfType(tagType, SignatureKindConstruct)) != 0 { - return JsxReferenceKindComponent - } - if len(c.getSignaturesOfType(tagType, SignatureKindCall)) != 0 { - return JsxReferenceKindFunction - } - return JsxReferenceKindMixed -} - -func (c *Checker) createSignatureForJSXIntrinsic(node *ast.Node, result *Type) *Signature { - elementType := c.errorType - if namespace := c.getJsxNamespaceAt(node); namespace != nil { - if typeSymbol := c.getSymbol(c.getExportsOfSymbol(namespace), JsxNames.Element, ast.SymbolFlagsType); typeSymbol != nil { - elementType = c.getDeclaredTypeOfSymbol(typeSymbol) - } - } - // returnNode := typeSymbol && c.nodeBuilder.symbolToEntityName(typeSymbol, ast.SymbolFlagsType, node) - // declaration := factory.createFunctionTypeNode(nil, []ParameterDeclaration{factory.createParameterDeclaration(nil, nil /*dotDotDotToken*/, "props", nil /*questionToken*/, c.nodeBuilder.typeToTypeNode(result, node))}, ifElse(returnNode != nil, factory.createTypeReferenceNode(returnNode, nil /*typeArguments*/), factory.createKeywordTypeNode(ast.KindAnyKeyword))) - parameterSymbol := c.newSymbol(ast.SymbolFlagsFunctionScopedVariable, "props") - c.valueSymbolLinks.Get(parameterSymbol).resolvedType = result - return c.newSignature(SignatureFlagsNone, nil, nil, nil, []*ast.Symbol{parameterSymbol}, elementType, nil, 1) -} - -// Get attributes type of the given intrinsic opening-like Jsx element by resolving the tag name. -// The function is intended to be called from a function which has checked that the opening element is an intrinsic element. -// @param node an intrinsic JSX opening-like element -func (c *Checker) getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node *ast.Node) *Type { - debug.Assert(isJsxIntrinsicTagName(node.TagName())) - links := c.jsxElementLinks.Get(node) - if links.resolvedJsxElementAttributesType != nil { - return links.resolvedJsxElementAttributesType - } - symbol := c.getIntrinsicTagSymbol(node) - if links.jsxFlags&JsxFlagsIntrinsicNamedElement != 0 { - links.resolvedJsxElementAttributesType = core.OrElse(c.getTypeOfSymbol(symbol), c.errorType) - return links.resolvedJsxElementAttributesType - } - if links.jsxFlags&JsxFlagsIntrinsicIndexedElement != 0 { - indexInfo := c.getApplicableIndexInfoForName(c.getJsxType(JsxNames.IntrinsicElements, node), node.TagName().Text()) - if indexInfo != nil { - links.resolvedJsxElementAttributesType = indexInfo.valueType - return links.resolvedJsxElementAttributesType - } - } - links.resolvedJsxElementAttributesType = c.errorType - return links.resolvedJsxElementAttributesType -} - -// Looks up an intrinsic tag name and returns a symbol that either points to an intrinsic -// property (in which case nodeLinks.jsxFlags will be IntrinsicNamedElement) or an intrinsic -// string index signature (in which case nodeLinks.jsxFlags will be IntrinsicIndexedElement). -// May also return unknownSymbol if both of these lookups fail. -func (c *Checker) getIntrinsicTagSymbol(node *ast.Node) *ast.Symbol { - links := c.symbolNodeLinks.Get(node) - if links.resolvedSymbol != nil { - return links.resolvedSymbol - } - intrinsicElementsType := c.getJsxType(JsxNames.IntrinsicElements, node) - if !c.isErrorType(intrinsicElementsType) { - // Property case - tagName := node.TagName() - if !ast.IsIdentifier(tagName) && !ast.IsJsxNamespacedName(tagName) { - panic("Invalid tag name") - } - propName := tagName.Text() - intrinsicProp := c.getPropertyOfType(intrinsicElementsType, propName) - if intrinsicProp != nil { - c.jsxElementLinks.Get(node).jsxFlags |= JsxFlagsIntrinsicNamedElement - links.resolvedSymbol = intrinsicProp - return links.resolvedSymbol - } - // Intrinsic string indexer case - indexSymbol := c.getApplicableIndexSymbol(intrinsicElementsType, c.getStringLiteralType(propName)) - if indexSymbol != nil { - c.jsxElementLinks.Get(node).jsxFlags |= JsxFlagsIntrinsicIndexedElement - links.resolvedSymbol = indexSymbol - return links.resolvedSymbol - } - if c.getTypeOfPropertyOrIndexSignatureOfType(intrinsicElementsType, propName) != nil { - c.jsxElementLinks.Get(node).jsxFlags |= JsxFlagsIntrinsicIndexedElement - links.resolvedSymbol = intrinsicElementsType.symbol - return links.resolvedSymbol - } - // Wasn't found - c.error(node, diagnostics.Property_0_does_not_exist_on_type_1, tagName.Text(), "JSX."+JsxNames.IntrinsicElements) - links.resolvedSymbol = c.unknownSymbol - return links.resolvedSymbol - } - if c.noImplicitAny { - c.error(node, diagnostics.JSX_element_implicitly_has_type_any_because_no_interface_JSX_0_exists, JsxNames.IntrinsicElements) - } - links.resolvedSymbol = c.unknownSymbol - return links.resolvedSymbol -} - -func (c *Checker) getJsxStatelessElementTypeAt(location *ast.Node) *Type { - jsxElementType := c.getJsxElementTypeAt(location) - if jsxElementType == nil { - return nil - } - return c.getUnionType([]*Type{jsxElementType, c.nullType}) -} - -func (c *Checker) getJsxElementClassTypeAt(location *ast.Node) *Type { - t := c.getJsxType(JsxNames.ElementClass, location) - if c.isErrorType(t) { - return nil - } - return t -} - -func (c *Checker) getJsxElementTypeAt(location *ast.Node) *Type { - return c.getJsxType(JsxNames.Element, location) -} - -func (c *Checker) getJsxElementTypeTypeAt(location *ast.Node) *Type { - ns := c.getJsxNamespaceAt(location) - if ns == nil { - return nil - } - sym := c.getJsxElementTypeSymbol(ns) - if sym == nil { - return nil - } - t := c.instantiateAliasOrInterfaceWithDefaults(sym, nil, ast.IsInJSFile(location)) - if t == nil || c.isErrorType(t) { - return nil - } - return t -} - -func (c *Checker) getJsxType(name string, location *ast.Node) *Type { - if namespace := c.getJsxNamespaceAt(location); namespace != nil { - if exports := c.getExportsOfSymbol(namespace); exports != nil { - if typeSymbol := c.getSymbol(exports, name, ast.SymbolFlagsType); typeSymbol != nil { - return c.getDeclaredTypeOfSymbol(typeSymbol) - } - } - } - return c.errorType -} - -func (c *Checker) getJsxNamespaceAt(location *ast.Node) *ast.Symbol { - var links *JsxElementLinks - if location != nil { - links = c.jsxElementLinks.Get(location) - } - if links != nil && links.jsxNamespace != nil && links.jsxNamespace != c.unknownSymbol { - return links.jsxNamespace - } - if links == nil || links.jsxNamespace != c.unknownSymbol { - resolvedNamespace := c.getJsxNamespaceContainerForImplicitImport(location) - if resolvedNamespace == nil || resolvedNamespace == c.unknownSymbol { - namespaceName := c.getJsxNamespace(location) - resolvedNamespace = c.resolveName(location, namespaceName, ast.SymbolFlagsNamespace, nil /*nameNotFoundMessage*/, false /*isUse*/, false /*excludeGlobals*/) - } - if resolvedNamespace != nil { - candidate := c.resolveSymbol(c.getSymbol(c.getExportsOfSymbol(c.resolveSymbol(resolvedNamespace)), JsxNames.JSX, ast.SymbolFlagsNamespace)) - if candidate != nil && candidate != c.unknownSymbol { - if links != nil { - links.jsxNamespace = candidate - } - return candidate - } - } - if links != nil { - links.jsxNamespace = c.unknownSymbol - } - } - // JSX global fallback - s := c.resolveSymbol(c.getGlobalSymbol(JsxNames.JSX, ast.SymbolFlagsNamespace, nil /*diagnostic*/)) - if s == c.unknownSymbol { - return nil - } - return s -} - -func (c *Checker) getJsxNamespace(location *ast.Node) string { - if location != nil { - file := ast.GetSourceFileOfNode(location) - if file != nil { - links := c.sourceFileLinks.Get(file) - if ast.IsJsxOpeningFragment(location) { - if links.localJsxFragmentNamespace != "" { - return links.localJsxFragmentNamespace - } - jsxFragmentPragma := ast.GetPragmaFromSourceFile(file, "jsxfrag") - if jsxFragmentPragma != nil { - links.localJsxFragmentFactory = c.parseIsolatedEntityName(jsxFragmentPragma.Args["factory"].Value) - if links.localJsxFragmentFactory != nil { - links.localJsxFragmentNamespace = ast.GetFirstIdentifier(links.localJsxFragmentFactory).Text() - return links.localJsxFragmentNamespace - } - } - entity := c.getJsxFragmentFactoryEntity(location) - if entity != nil { - links.localJsxFragmentFactory = entity - links.localJsxFragmentNamespace = ast.GetFirstIdentifier(entity).Text() - return links.localJsxFragmentNamespace - } - } else { - localJsxNamespace := c.getLocalJsxNamespace(file) - if localJsxNamespace != "" { - links.localJsxNamespace = localJsxNamespace - return links.localJsxNamespace - } - } - } - } - if c._jsxNamespace == "" { - c._jsxNamespace = "React" - if c.compilerOptions.JsxFactory != "" { - c._jsxFactoryEntity = c.parseIsolatedEntityName(c.compilerOptions.JsxFactory) - if c._jsxFactoryEntity != nil { - c._jsxNamespace = ast.GetFirstIdentifier(c._jsxFactoryEntity).Text() - } - } else if c.compilerOptions.ReactNamespace != "" { - c._jsxNamespace = c.compilerOptions.ReactNamespace - } - } - if c._jsxFactoryEntity == nil { - c._jsxFactoryEntity = c.factory.NewQualifiedName(c.factory.NewIdentifier(c._jsxNamespace), c.factory.NewIdentifier("createElement")) - } - return c._jsxNamespace -} - -func (c *Checker) getLocalJsxNamespace(file *ast.SourceFile) string { - links := c.sourceFileLinks.Get(file) - if links.localJsxNamespace != "" { - return links.localJsxNamespace - } - jsxPragma := ast.GetPragmaFromSourceFile(file, "jsx") - if jsxPragma != nil { - links.localJsxFactory = c.parseIsolatedEntityName(jsxPragma.Args["factory"].Value) - if links.localJsxFactory != nil { - links.localJsxNamespace = ast.GetFirstIdentifier(links.localJsxFactory).Text() - return links.localJsxNamespace - } - } - return "" -} - -func (c *Checker) getJsxFactoryEntity(location *ast.Node) *ast.Node { - if location != nil { - c.getJsxNamespace(location) - if localJsxFactory := c.sourceFileLinks.Get(ast.GetSourceFileOfNode(location)).localJsxFactory; localJsxFactory != nil { - return localJsxFactory - } - } - return c._jsxFactoryEntity -} - -func (c *Checker) getJsxFragmentFactoryEntity(location *ast.Node) *ast.EntityName { - if location != nil { - file := ast.GetSourceFileOfNode(location) - if file != nil { - links := c.sourceFileLinks.Get(file) - if links.localJsxFragmentFactory != nil { - return links.localJsxFragmentFactory - } - jsxFragPragma := ast.GetPragmaFromSourceFile(file, "jsxfrag") - if jsxFragPragma != nil { - links.localJsxFragmentFactory = c.parseIsolatedEntityName(jsxFragPragma.Args["factory"].Value) - return links.localJsxFragmentFactory - } - } - } - if c.compilerOptions.JsxFragmentFactory != "" { - return c.parseIsolatedEntityName(c.compilerOptions.JsxFragmentFactory) - } - return nil -} - -func (c *Checker) parseIsolatedEntityName(name string) *ast.Node { - result := parser.ParseIsolatedEntityName(name) - if result != nil { - markAsSynthetic(result) - } - return result -} - -func markAsSynthetic(node *ast.Node) bool { - node.Loc = core.NewTextRange(-1, -1) - node.ForEachChild(markAsSynthetic) - return false -} - -func (c *Checker) getJsxNamespaceContainerForImplicitImport(location *ast.Node) *ast.Symbol { - var file *ast.SourceFile - var links *JsxElementLinks - if location != nil { - if file = ast.GetSourceFileOfNode(location); file != nil { - links = c.jsxElementLinks.Get(file.AsNode()) - } - } - if links != nil && links.jsxImplicitImportContainer != nil { - return core.IfElse(links.jsxImplicitImportContainer == c.unknownSymbol, nil, links.jsxImplicitImportContainer) - } - moduleReference, specifier := c.getJSXRuntimeImportSpecifier(file) - if moduleReference == "" { - return nil - } - errorMessage := diagnostics.This_JSX_tag_requires_the_module_path_0_to_exist_but_none_could_be_found_Make_sure_you_have_types_for_the_appropriate_package_installed - mod := c.resolveExternalModule(core.OrElse(specifier, location), moduleReference, errorMessage, location, false) - var result *ast.Symbol - if mod != nil && mod != c.unknownSymbol { - result = c.getMergedSymbol(c.resolveSymbol(mod)) - } - if links != nil { - links.jsxImplicitImportContainer = core.OrElse(result, c.unknownSymbol) - } - return result -} - -func (c *Checker) getJSXRuntimeImportSpecifier(file *ast.SourceFile) (moduleReference string, specifier *ast.Node) { - return c.program.GetJSXRuntimeImportSpecifier(file.Path()) -} diff --git a/kitcom/internal/tsgo/checker/mapper.go b/kitcom/internal/tsgo/checker/mapper.go deleted file mode 100644 index 44d823a..0000000 --- a/kitcom/internal/tsgo/checker/mapper.go +++ /dev/null @@ -1,294 +0,0 @@ -package checker - -import "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - -// TypeMapperKind - -type TypeMapperKind int32 - -const ( - TypeMapperKindUnknown TypeMapperKind = iota - TypeMapperKindSimple - TypeMapperKindArray - TypeMapperKindMerged -) - -// TypeMapper - -type TypeMapper struct { - data TypeMapperData -} - -func (m *TypeMapper) Map(t *Type) *Type { return m.data.Map(t) } -func (m *TypeMapper) Kind() TypeMapperKind { return m.data.Kind() } - -// TypeMapperData - -type TypeMapperData interface { - Map(t *Type) *Type - Kind() TypeMapperKind -} - -// Factory functions - -func newTypeMapper(sources []*Type, targets []*Type) *TypeMapper { - if len(sources) == 1 { - return newSimpleTypeMapper(sources[0], targets[0]) - } - return newArrayTypeMapper(sources, targets) -} - -func (c *Checker) combineTypeMappers(m1 *TypeMapper, m2 *TypeMapper) *TypeMapper { - if m1 != nil { - return newCompositeTypeMapper(c, m1, m2) - } - return m2 -} - -func mergeTypeMappers(m1 *TypeMapper, m2 *TypeMapper) *TypeMapper { - if m1 != nil { - return newMergedTypeMapper(m1, m2) - } - return m2 -} - -func prependTypeMapping(source *Type, target *Type, mapper *TypeMapper) *TypeMapper { - if mapper == nil { - return newSimpleTypeMapper(source, target) - } - return newMergedTypeMapper(newSimpleTypeMapper(source, target), mapper) -} - -func appendTypeMapping(mapper *TypeMapper, source *Type, target *Type) *TypeMapper { - if mapper == nil { - return newSimpleTypeMapper(source, target) - } - return newMergedTypeMapper(mapper, newSimpleTypeMapper(source, target)) -} - -// Maps forward-references to later types parameters to the empty object type. -// This is used during inference when instantiating type parameter defaults. -func (c *Checker) newBackreferenceMapper(context *InferenceContext, index int) *TypeMapper { - forwardInferences := context.inferences[index:] - typeParameters := core.Map(forwardInferences, func(i *InferenceInfo) *Type { - return i.typeParameter - }) - return newArrayToSingleTypeMapper(typeParameters, c.unknownType) -} - -// TypeMapperBase - -type TypeMapperBase struct { - TypeMapper -} - -func (m *TypeMapperBase) Map(t *Type) *Type { return t } -func (m *TypeMapperBase) Kind() TypeMapperKind { return TypeMapperKindUnknown } - -// SimpleTypeMapper - -type SimpleTypeMapper struct { - TypeMapperBase - source *Type - target *Type -} - -func newSimpleTypeMapper(source *Type, target *Type) *TypeMapper { - m := &SimpleTypeMapper{} - m.data = m - m.source = source - m.target = target - return &m.TypeMapper -} - -func (m *SimpleTypeMapper) Map(t *Type) *Type { - if t == m.source { - return m.target - } - return t -} - -func (m *SimpleTypeMapper) Kind() TypeMapperKind { - return TypeMapperKindSimple -} - -// ArrayTypeMapper - -type ArrayTypeMapper struct { - TypeMapperBase - sources []*Type - targets []*Type -} - -func newArrayTypeMapper(sources []*Type, targets []*Type) *TypeMapper { - m := &ArrayTypeMapper{} - m.data = m - m.sources = sources - m.targets = targets - return &m.TypeMapper -} - -func (m *ArrayTypeMapper) Map(t *Type) *Type { - for i, s := range m.sources { - if t == s { - return m.targets[i] - } - } - return t -} - -func (m *ArrayTypeMapper) Kind() TypeMapperKind { - return TypeMapperKindArray -} - -// ArrayToSingleTypeMapper - -type ArrayToSingleTypeMapper struct { - TypeMapperBase - sources []*Type - target *Type -} - -func newArrayToSingleTypeMapper(sources []*Type, target *Type) *TypeMapper { - m := &ArrayToSingleTypeMapper{} - m.data = m - m.sources = sources - m.target = target - return &m.TypeMapper -} - -func (m *ArrayToSingleTypeMapper) Map(t *Type) *Type { - for _, s := range m.sources { - if t == s { - return m.target - } - } - return t -} - -// DeferredTypeMapper - -type DeferredTypeMapper struct { - TypeMapperBase - sources []*Type - targets []func() *Type -} - -func newDeferredTypeMapper(sources []*Type, targets []func() *Type) *TypeMapper { - m := &DeferredTypeMapper{} - m.data = m - m.sources = sources - m.targets = targets - return &m.TypeMapper -} - -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 { - TypeMapperBase - fn func(*Type) *Type -} - -func newFunctionTypeMapper(fn func(*Type) *Type) *TypeMapper { - m := &FunctionTypeMapper{} - m.data = m - m.fn = fn - return &m.TypeMapper -} - -func (m *FunctionTypeMapper) Map(t *Type) *Type { - return m.fn(t) -} - -// MergedTypeMapper - -type MergedTypeMapper struct { - TypeMapperBase - m1 *TypeMapper - m2 *TypeMapper -} - -func newMergedTypeMapper(m1 *TypeMapper, m2 *TypeMapper) *TypeMapper { - m := &MergedTypeMapper{} - m.data = m - m.m1 = m1 - m.m2 = m2 - return &m.TypeMapper -} - -func (m *MergedTypeMapper) Map(t *Type) *Type { - return m.m2.Map(m.m1.Map(t)) -} - -func (m *MergedTypeMapper) Kind() TypeMapperKind { - return TypeMapperKindMerged -} - -// CompositeTypeMapper - -type CompositeTypeMapper struct { - TypeMapperBase - c *Checker - m1 *TypeMapper - m2 *TypeMapper -} - -func newCompositeTypeMapper(c *Checker, m1 *TypeMapper, m2 *TypeMapper) *TypeMapper { - m := &CompositeTypeMapper{} - m.data = m - m.c = c - m.m1 = m1 - m.m2 = m2 - return &m.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) -} - -// InferenceTypeMapper - -type InferenceTypeMapper struct { - TypeMapperBase - c *Checker - n *InferenceContext - fixing bool -} - -func (c *Checker) newInferenceTypeMapper(n *InferenceContext, fixing bool) *TypeMapper { - m := &InferenceTypeMapper{} - m.data = m - m.c = c - m.n = n - m.fixing = fixing - return &m.TypeMapper -} - -func (m *InferenceTypeMapper) Map(t *Type) *Type { - for i, inference := range m.n.inferences { - if t == inference.typeParameter { - if m.fixing && !inference.isFixed { - // Before we commit to a particular inference (and thus lock out any further inferences), - // we infer from any intra-expression inference sites we have collected. - m.c.inferFromIntraExpressionSites(m.n) - clearCachedInferences(m.n.inferences) - inference.isFixed = true - } - return m.c.getInferredType(m.n, i) - } - } - return t -} diff --git a/kitcom/internal/tsgo/checker/nodebuilder.go b/kitcom/internal/tsgo/checker/nodebuilder.go deleted file mode 100644 index c62e094..0000000 --- a/kitcom/internal/tsgo/checker/nodebuilder.go +++ /dev/null @@ -1,186 +0,0 @@ -package checker - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/nodebuilder" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" -) - -type NodeBuilder struct { - ctxStack []*NodeBuilderContext - basicHost Host - impl *nodeBuilderImpl -} - -// EmitContext implements NodeBuilderInterface. -func (b *NodeBuilder) EmitContext() *printer.EmitContext { - return b.impl.e -} - -func (b *NodeBuilder) enterContext(enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) { - b.ctxStack = append(b.ctxStack, b.impl.ctx) - b.impl.ctx = &NodeBuilderContext{ - tracker: tracker, - flags: flags, - internalFlags: internalFlags, - enclosingDeclaration: enclosingDeclaration, - enclosingFile: ast.GetSourceFileOfNode(enclosingDeclaration), - inferTypeParameters: make([]*Type, 0), - symbolDepth: make(map[CompositeSymbolIdentity]int), - trackedSymbols: make([]*TrackedSymbolArgs, 0), - reverseMappedStack: make([]*ast.Symbol, 0), - enclosingSymbolTypes: make(map[ast.SymbolId]*Type), - remappedSymbolReferences: make(map[ast.SymbolId]*ast.Symbol), - } - // TODO: always provide this; see https://github.com/microsoft/typescript-go/pull/1588#pullrequestreview-3125218673 - var moduleResolverHost Host - if tracker != nil { - moduleResolverHost = tracker.GetModuleSpecifierGenerationHost() - } else if internalFlags&nodebuilder.InternalFlagsDoNotIncludeSymbolChain != 0 { - moduleResolverHost = b.basicHost - } - tracker = NewSymbolTrackerImpl(b.impl.ctx, tracker, moduleResolverHost) - b.impl.ctx.tracker = tracker -} - -func (b *NodeBuilder) popContext() { - stackSize := len(b.ctxStack) - if stackSize == 0 { - b.impl.ctx = nil - } else { - b.impl.ctx = b.ctxStack[stackSize-1] - b.ctxStack = b.ctxStack[:stackSize-1] - } -} - -func (b *NodeBuilder) exitContext(result *ast.Node) *ast.Node { - b.exitContextCheck() - defer b.popContext() - if b.impl.ctx.encounteredError { - return nil - } - return result -} - -func (b *NodeBuilder) exitContextSlice(result []*ast.Node) []*ast.Node { - b.exitContextCheck() - defer b.popContext() - if b.impl.ctx.encounteredError { - return nil - } - return result -} - -func (b *NodeBuilder) exitContextCheck() { - if b.impl.ctx.truncating && b.impl.ctx.flags&nodebuilder.FlagsNoTruncation != 0 { - b.impl.ctx.tracker.ReportTruncationError() - } -} - -// IndexInfoToIndexSignatureDeclaration implements NodeBuilderInterface. -func (b *NodeBuilder) IndexInfoToIndexSignatureDeclaration(info *IndexInfo, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { - b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) - return b.exitContext(b.impl.indexInfoToIndexSignatureDeclarationHelper(info, nil)) -} - -// SerializeReturnTypeForSignature implements NodeBuilderInterface. -func (b *NodeBuilder) SerializeReturnTypeForSignature(signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { - b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) - signature := b.impl.ch.getSignatureFromDeclaration(signatureDeclaration) - symbol := b.impl.ch.getSymbolOfDeclaration(signatureDeclaration) - returnType, ok := b.impl.ctx.enclosingSymbolTypes[ast.GetSymbolId(symbol)] - if !ok || returnType == nil { - returnType = b.impl.ch.instantiateType(b.impl.ch.getReturnTypeOfSignature(signature), b.impl.ctx.mapper) - } - return b.exitContext(b.impl.serializeInferredReturnTypeForSignature(signature, returnType)) -} - -func (b *NodeBuilder) SerializeTypeParametersForSignature(signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node { - b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) - symbol := b.impl.ch.getSymbolOfDeclaration(signatureDeclaration) - typeParams := b.SymbolToTypeParameterDeclarations(symbol, enclosingDeclaration, flags, internalFlags, tracker) - return b.exitContextSlice(typeParams) -} - -// SerializeTypeForDeclaration implements NodeBuilderInterface. -func (b *NodeBuilder) SerializeTypeForDeclaration(declaration *ast.Node, symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { - b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) - return b.exitContext(b.impl.serializeTypeForDeclaration(declaration, nil, symbol)) -} - -// SerializeTypeForExpression implements NodeBuilderInterface. -func (b *NodeBuilder) SerializeTypeForExpression(expr *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { - b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) - return b.exitContext(b.impl.serializeTypeForExpression(expr)) -} - -// SignatureToSignatureDeclaration implements NodeBuilderInterface. -func (b *NodeBuilder) SignatureToSignatureDeclaration(signature *Signature, kind ast.Kind, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { - b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) - return b.exitContext(b.impl.signatureToSignatureDeclarationHelper(signature, kind, nil)) -} - -// SymbolTableToDeclarationStatements implements NodeBuilderInterface. -func (b *NodeBuilder) SymbolTableToDeclarationStatements(symbolTable *ast.SymbolTable, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node { - b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) - return b.exitContextSlice(b.impl.symbolTableToDeclarationStatements(symbolTable)) -} - -// SymbolToEntityName implements NodeBuilderInterface. -func (b *NodeBuilder) SymbolToEntityName(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { - b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) - return b.exitContext(b.impl.symbolToName(symbol, meaning, false)) -} - -// SymbolToExpression implements NodeBuilderInterface. -func (b *NodeBuilder) SymbolToExpression(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { - b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) - return b.exitContext(b.impl.symbolToExpression(symbol, meaning)) -} - -// SymbolToNode implements NodeBuilderInterface. -func (b *NodeBuilder) SymbolToNode(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { - b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) - return b.exitContext(b.impl.symbolToNode(symbol, meaning)) -} - -// SymbolToParameterDeclaration implements NodeBuilderInterface. -func (b NodeBuilder) SymbolToParameterDeclaration(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { - b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) - return b.exitContext(b.impl.symbolToParameterDeclaration(symbol, false)) -} - -// SymbolToTypeParameterDeclarations implements NodeBuilderInterface. -func (b *NodeBuilder) SymbolToTypeParameterDeclarations(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node { - b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) - return b.exitContextSlice(b.impl.symbolToTypeParameterDeclarations(symbol)) -} - -// TypeParameterToDeclaration implements NodeBuilderInterface. -func (b *NodeBuilder) TypeParameterToDeclaration(parameter *Type, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { - b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) - return b.exitContext(b.impl.typeParameterToDeclaration(parameter)) -} - -// TypePredicateToTypePredicateNode implements NodeBuilderInterface. -func (b *NodeBuilder) TypePredicateToTypePredicateNode(predicate *TypePredicate, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { - b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) - return b.exitContext(b.impl.typePredicateToTypePredicateNode(predicate)) -} - -// TypeToTypeNode implements NodeBuilderInterface. -func (b *NodeBuilder) TypeToTypeNode(typ *Type, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node { - b.enterContext(enclosingDeclaration, flags, internalFlags, tracker) - return b.exitContext(b.impl.typeToTypeNode(typ)) -} - -// var _ NodeBuilderInterface = NewNodeBuilderAPI(nil, nil) - -func NewNodeBuilder(ch *Checker, e *printer.EmitContext) *NodeBuilder { - impl := newNodeBuilderImpl(ch, e) - return &NodeBuilder{impl: impl, ctxStack: make([]*NodeBuilderContext, 0, 1), basicHost: ch.program} -} - -func (c *Checker) getNodeBuilder() *NodeBuilder { - return NewNodeBuilder(c, printer.NewEmitContext()) -} diff --git a/kitcom/internal/tsgo/checker/nodebuilderimpl.go b/kitcom/internal/tsgo/checker/nodebuilderimpl.go deleted file mode 100644 index ce53f31..0000000 --- a/kitcom/internal/tsgo/checker/nodebuilderimpl.go +++ /dev/null @@ -1,3112 +0,0 @@ -package checker - -import ( - "fmt" - "maps" - "slices" - "strings" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/debug" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/jsnum" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/module" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/modulespecifiers" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/nodebuilder" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/scanner" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/stringutil" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" -) - -type CompositeSymbolIdentity struct { - isConstructorNode bool - symbolId ast.SymbolId - nodeId ast.NodeId -} - -type TrackedSymbolArgs struct { - symbol *ast.Symbol - enclosingDeclaration *ast.Node - meaning ast.SymbolFlags -} - -type SerializedTypeEntry struct { - node *ast.Node - truncating bool - addedLength int - trackedSymbols []*TrackedSymbolArgs -} - -type CompositeTypeCacheIdentity struct { - typeId TypeId - flags nodebuilder.Flags - internalFlags nodebuilder.InternalFlags -} - -type NodeBuilderLinks struct { - serializedTypes map[CompositeTypeCacheIdentity]*SerializedTypeEntry // Collection of types serialized at this location - fakeScopeForSignatureDeclaration *string // If present, this is a fake scope injected into an enclosing declaration chain. -} - -type NodeBuilderSymbolLinks struct { - specifierCache module.ModeAwareCache[string] -} -type NodeBuilderContext struct { - tracker nodebuilder.SymbolTracker - approximateLength int - encounteredError bool - truncating bool - reportedDiagnostic bool - flags nodebuilder.Flags - internalFlags nodebuilder.InternalFlags - depth int - enclosingDeclaration *ast.Node - enclosingFile *ast.SourceFile - inferTypeParameters []*Type - visitedTypes collections.Set[TypeId] - symbolDepth map[CompositeSymbolIdentity]int - trackedSymbols []*TrackedSymbolArgs - mapper *TypeMapper - reverseMappedStack []*ast.Symbol - enclosingSymbolTypes map[ast.SymbolId]*Type - suppressReportInferenceFallback bool - remappedSymbolReferences map[ast.SymbolId]*ast.Symbol - - // per signature scope state - hasCreatedTypeParameterSymbolList bool - hasCreatedTypeParametersNamesLookups bool - typeParameterNames map[TypeId]*ast.Identifier - typeParameterNamesByText map[string]struct{} - typeParameterNamesByTextNextNameCount map[string]int - typeParameterSymbolList map[ast.SymbolId]struct{} -} - -type nodeBuilderImpl struct { - // host members - f *ast.NodeFactory - ch *Checker - e *printer.EmitContext - - // cache - links core.LinkStore[*ast.Node, NodeBuilderLinks] - symbolLinks core.LinkStore[*ast.Symbol, NodeBuilderSymbolLinks] - - // state - ctx *NodeBuilderContext - - // reusable visitor - cloneBindingNameVisitor *ast.NodeVisitor -} - -const ( - defaultMaximumTruncationLength = 160 - noTruncationMaximumTruncationLength = 1_000_000 -) - -// Node builder utility functions - -func newNodeBuilderImpl(ch *Checker, e *printer.EmitContext) *nodeBuilderImpl { - b := &nodeBuilderImpl{f: e.Factory.AsNodeFactory(), ch: ch, e: e} - b.cloneBindingNameVisitor = ast.NewNodeVisitor(b.cloneBindingName, b.f, ast.NodeVisitorHooks{}) - return b -} - -func (b *nodeBuilderImpl) saveRestoreFlags() func() { - flags := b.ctx.flags - internalFlags := b.ctx.internalFlags - depth := b.ctx.depth - - return func() { - b.ctx.flags = flags - b.ctx.internalFlags = internalFlags - b.ctx.depth = depth - } -} - -func (b *nodeBuilderImpl) checkTruncationLength() bool { - if b.ctx.truncating { - return b.ctx.truncating - } - b.ctx.truncating = b.ctx.approximateLength > (core.IfElse((b.ctx.flags&nodebuilder.FlagsNoTruncation != 0), noTruncationMaximumTruncationLength, defaultMaximumTruncationLength)) - return b.ctx.truncating -} - -func (b *nodeBuilderImpl) appendReferenceToType(root *ast.TypeNode, ref *ast.TypeNode) *ast.TypeNode { - if ast.IsImportTypeNode(root) { - // first shift type arguments - - // !!! In the old emitter, an Identifier could have type arguments for use with quickinfo: - // typeArguments := root.TypeArguments - // qualifier := root.AsImportTypeNode().Qualifier - // if qualifier != nil { - // if ast.IsIdentifier(qualifier) { - // if typeArguments != getIdentifierTypeArguments(qualifier) { - // qualifier = setIdentifierTypeArguments(b.f.CloneNode(qualifier), typeArguments) - // } - // } else { - // if typeArguments != getIdentifierTypeArguments(qualifier.Right) { - // qualifier = b.f.UpdateQualifiedName(qualifier, qualifier.Left, setIdentifierTypeArguments(b.f.cloneNode(qualifier.Right), typeArguments)) - // } - // } - // } - // !!! Without the above, nested type args are silently elided - imprt := root.AsImportTypeNode() - // then move qualifiers - ids := getAccessStack(ref) - var qualifier *ast.Node - for _, id := range ids { - if qualifier != nil { - qualifier = b.f.NewQualifiedName(qualifier, id) - } else { - qualifier = id - } - } - return b.f.UpdateImportTypeNode(imprt, imprt.IsTypeOf, imprt.Argument, imprt.Attributes, qualifier, ref.AsTypeReferenceNode().TypeArguments) - } else { - // first shift type arguments - // !!! In the old emitter, an Identifier could have type arguments for use with quickinfo: - // typeArguments := root.TypeArguments - // typeName := root.AsTypeReferenceNode().TypeName - // if ast.IsIdentifier(typeName) { - // if typeArguments != getIdentifierTypeArguments(typeName) { - // typeName = setIdentifierTypeArguments(b.f.cloneNode(typeName), typeArguments) - // } - // } else { - // if typeArguments != getIdentifierTypeArguments(typeName.Right) { - // typeName = b.f.UpdateQualifiedName(typeName, typeName.Left, setIdentifierTypeArguments(b.f.cloneNode(typeName.Right), typeArguments)) - // } - // } - // !!! Without the above, nested type args are silently elided - // then move qualifiers - ids := getAccessStack(ref) - var typeName *ast.Node = root.AsTypeReferenceNode().TypeName - for _, id := range ids { - typeName = b.f.NewQualifiedName(typeName, id) - } - return b.f.UpdateTypeReferenceNode(root.AsTypeReferenceNode(), typeName, ref.AsTypeReferenceNode().TypeArguments) - } -} - -func getAccessStack(ref *ast.Node) []*ast.Node { - var state *ast.Node = ref.AsTypeReferenceNode().TypeName - ids := []*ast.Node{} - for !ast.IsIdentifier(state) { - entity := state.AsQualifiedName() - ids = append([]*ast.Node{entity.Right}, ids...) - state = entity.Left - } - ids = append([]*ast.Node{state}, ids...) - return ids -} - -func isClassInstanceSide(c *Checker, t *Type) bool { - return t.symbol != nil && t.symbol.Flags&ast.SymbolFlagsClass != 0 && (t == c.getDeclaredTypeOfClassOrInterface(t.symbol) || (t.flags&TypeFlagsObject != 0 && t.objectFlags&ObjectFlagsIsClassInstanceClone != 0)) -} - -func (b *nodeBuilderImpl) createElidedInformationPlaceholder() *ast.TypeNode { - b.ctx.approximateLength += 3 - if b.ctx.flags&nodebuilder.FlagsNoTruncation == 0 { - return b.f.NewTypeReferenceNode(b.f.NewIdentifier("..."), nil /*typeArguments*/) - } - return b.e.AddSyntheticLeadingComment(b.f.NewKeywordTypeNode(ast.KindAnyKeyword), ast.KindMultiLineCommentTrivia, "elided", false /*hasTrailingNewLine*/) -} - -func (b *nodeBuilderImpl) mapToTypeNodes(list []*Type, isBareList bool) *ast.NodeList { - if len(list) == 0 { - return nil - } - - if b.checkTruncationLength() { - if !isBareList { - var node *ast.Node - if b.ctx.flags&nodebuilder.FlagsNoTruncation != 0 { - node = b.e.AddSyntheticLeadingComment(b.f.NewKeywordTypeNode(ast.KindAnyKeyword), ast.KindMultiLineCommentTrivia, "elided", false /*hasTrailingNewLine*/) - } else { - node = b.f.NewTypeReferenceNode(b.f.NewIdentifier("..."), nil /*typeArguments*/) - } - return b.f.NewNodeList([]*ast.Node{node}) - } else if len(list) > 2 { - nodes := []*ast.Node{ - b.typeToTypeNode(list[0]), - nil, - b.typeToTypeNode(list[len(list)-1]), - } - - if b.ctx.flags&nodebuilder.FlagsNoTruncation != 0 { - nodes[1] = b.e.AddSyntheticLeadingComment(b.f.NewKeywordTypeNode(ast.KindAnyKeyword), ast.KindMultiLineCommentTrivia, fmt.Sprintf("... %d more elided ...", len(list)-2), false /*hasTrailingNewLine*/) - } else { - text := fmt.Sprintf("... %d more ...", len(list)-2) - nodes[1] = b.f.NewTypeReferenceNode(b.f.NewIdentifier(text), nil /*typeArguments*/) - } - return b.f.NewNodeList(nodes) - } - } - - mayHaveNameCollisions := b.ctx.flags&nodebuilder.FlagsUseFullyQualifiedType == 0 - type seenName struct { - t *Type - i int - } - var seenNames *collections.MultiMap[string, seenName] - if mayHaveNameCollisions { - seenNames = &collections.MultiMap[string, seenName]{} - } - - result := make([]*ast.Node, 0, len(list)) - - for i, t := range list { - if b.checkTruncationLength() && (i+2 < len(list)-1) { - if b.ctx.flags&nodebuilder.FlagsNoTruncation != 0 { - result = append(result, b.e.AddSyntheticLeadingComment(b.f.NewKeywordTypeNode(ast.KindAnyKeyword), ast.KindMultiLineCommentTrivia, fmt.Sprintf("... %d more elided ...", len(list)-i), false /*hasTrailingNewLine*/)) - } else { - text := fmt.Sprintf("... %d more ...", len(list)-i) - result = append(result, b.f.NewTypeReferenceNode(b.f.NewIdentifier(text), nil /*typeArguments*/)) - } - typeNode := b.typeToTypeNode(list[len(list)-1]) - if typeNode != nil { - result = append(result, typeNode) - } - break - } - b.ctx.approximateLength += 2 // Account for whitespace + separator - typeNode := b.typeToTypeNode(t) - if typeNode != nil { - result = append(result, typeNode) - if seenNames != nil && isIdentifierTypeReference(typeNode) { - seenNames.Add(typeNode.AsTypeReferenceNode().TypeName.Text(), seenName{t, len(result) - 1}) - } - } - } - - if seenNames != nil { - // To avoid printing types like `[Foo, Foo]` or `Bar & Bar` where - // occurrences of the same name actually come from different - // namespaces, go through the single-identifier type reference nodes - // we just generated, and see if any names were generated more than - // once while referring to different types. If so, regenerate the - // type node for each entry by that name with the - // `UseFullyQualifiedType` flag enabled. - restoreFlags := b.saveRestoreFlags() - b.ctx.flags |= nodebuilder.FlagsUseFullyQualifiedType - for types := range seenNames.Values() { - if !arrayIsHomogeneous(types, func(a, b seenName) bool { - return typesAreSameReference(a.t, b.t) - }) { - for _, seen := range types { - result[seen.i] = b.typeToTypeNode(seen.t) - } - } - } - restoreFlags() - } - - return b.f.NewNodeList(result) -} - -func isIdentifierTypeReference(node *ast.Node) bool { - return ast.IsTypeReferenceNode(node) && ast.IsIdentifier(node.AsTypeReferenceNode().TypeName) -} - -func arrayIsHomogeneous[T any](array []T, comparer func(a, B T) bool) bool { - if len(array) < 2 { - return true - } - first := array[0] - for i := 1; i < len(array); i++ { - target := array[i] - if !comparer(first, target) { - return false - } - } - return true -} - -func typesAreSameReference(a, b *Type) bool { - return a == b || a.symbol != nil && a.symbol == b.symbol || a.alias != nil && a.alias == b.alias -} - -func (b *nodeBuilderImpl) setCommentRange(node *ast.Node, range_ *ast.Node) { - if range_ != nil && b.ctx.enclosingFile != nil && b.ctx.enclosingFile == ast.GetSourceFileOfNode(range_) { - // Copy comments to node for declaration emit - b.e.AssignCommentRange(node, range_) - } -} - -func (b *nodeBuilderImpl) tryReuseExistingTypeNodeHelper(existing *ast.TypeNode) *ast.TypeNode { - return nil // !!! -} - -func (b *nodeBuilderImpl) tryReuseExistingTypeNode(typeNode *ast.TypeNode, t *Type, host *ast.Node, addUndefined bool) *ast.TypeNode { - originalType := t - if addUndefined { - t = b.ch.getOptionalType(t, !ast.IsParameter(host)) - } - clone := b.tryReuseExistingNonParameterTypeNode(typeNode, t, host, nil) - if clone != nil { - // explicitly add `| undefined` if it's missing from the input type nodes and the type contains `undefined` (and not the missing type) - if addUndefined && containsNonMissingUndefinedType(b.ch, t) && !someType(b.getTypeFromTypeNode(typeNode, false), func(t *Type) bool { - return t.flags&TypeFlagsUndefined != 0 - }) { - return b.f.NewUnionTypeNode(b.f.NewNodeList([]*ast.TypeNode{clone, b.f.NewKeywordTypeNode(ast.KindUndefinedKeyword)})) - } - return clone - } - if addUndefined && originalType != t { - cloneMissingUndefined := b.tryReuseExistingNonParameterTypeNode(typeNode, originalType, host, nil) - if cloneMissingUndefined != nil { - return b.f.NewUnionTypeNode(b.f.NewNodeList([]*ast.TypeNode{cloneMissingUndefined, b.f.NewKeywordTypeNode(ast.KindUndefinedKeyword)})) - } - } - return nil -} - -func (b *nodeBuilderImpl) typeNodeIsEquivalentToType(annotatedDeclaration *ast.Node, t *Type, typeFromTypeNode *Type) bool { - if typeFromTypeNode == t { - return true - } - if annotatedDeclaration == nil { - return false - } - // !!! - // used to be hasEffectiveQuestionToken for JSDoc - if isOptionalDeclaration(annotatedDeclaration) { - return b.ch.getTypeWithFacts(t, TypeFactsNEUndefined) == typeFromTypeNode - } - return false -} - -func (b *nodeBuilderImpl) existingTypeNodeIsNotReferenceOrIsReferenceWithCompatibleTypeArgumentCount(existing *ast.TypeNode, t *Type) bool { - // In JS, you can say something like `Foo` and get a `Foo` implicitly - we don't want to preserve that original `Foo` in these cases, though. - if t.objectFlags&ObjectFlagsReference == 0 { - return true - } - if !ast.IsTypeReferenceNode(existing) { - return true - } - // `type` is a reference type, and `existing` is a type reference node, but we still need to make sure they refer to the _same_ target type - // before we go comparing their type argument counts. - b.ch.getTypeFromTypeReference(existing) - // call to ensure symbol is resolved - links := b.ch.symbolNodeLinks.TryGet(existing) - if links == nil { - return true - } - symbol := links.resolvedSymbol - if symbol == nil { - return true - } - existingTarget := b.ch.getDeclaredTypeOfSymbol(symbol) - if existingTarget == nil || existingTarget != t.AsTypeReference().target { - return true - } - return len(existing.TypeArguments()) >= b.ch.getMinTypeArgumentCount(t.AsTypeReference().target.AsInterfaceType().TypeParameters()) -} - -func (b *nodeBuilderImpl) tryReuseExistingNonParameterTypeNode(existing *ast.TypeNode, t *Type, host *ast.Node, annotationType *Type) *ast.TypeNode { - if host == nil { - host = b.ctx.enclosingDeclaration - } - if annotationType == nil { - annotationType = b.getTypeFromTypeNode(existing, true) - } - if annotationType != nil && b.typeNodeIsEquivalentToType(host, t, annotationType) && b.existingTypeNodeIsNotReferenceOrIsReferenceWithCompatibleTypeArgumentCount(existing, t) { - result := b.tryReuseExistingTypeNodeHelper(existing) - if result != nil { - return result - } - } - return nil -} - -func (b *nodeBuilderImpl) getResolvedTypeWithoutAbstractConstructSignatures(t *StructuredType) *Type { - if len(t.ConstructSignatures()) == 0 { - return t.AsType() - } - if t.objectTypeWithoutAbstractConstructSignatures != nil { - return t.objectTypeWithoutAbstractConstructSignatures - } - constructSignatures := core.Filter(t.ConstructSignatures(), func(signature *Signature) bool { - return signature.flags&SignatureFlagsAbstract == 0 - }) - if len(constructSignatures) == len(t.ConstructSignatures()) { - t.objectTypeWithoutAbstractConstructSignatures = t.AsType() - return t.AsType() - } - typeCopy := b.ch.newAnonymousType(t.symbol, t.members, t.CallSignatures(), core.IfElse(len(constructSignatures) > 0, constructSignatures, []*Signature{}), t.indexInfos) - t.objectTypeWithoutAbstractConstructSignatures = typeCopy - typeCopy.AsStructuredType().objectTypeWithoutAbstractConstructSignatures = typeCopy - return typeCopy -} - -func (b *nodeBuilderImpl) symbolToNode(symbol *ast.Symbol, meaning ast.SymbolFlags) *ast.Node { - if b.ctx.internalFlags&nodebuilder.InternalFlagsWriteComputedProps != 0 { - if symbol.ValueDeclaration != nil { - name := ast.GetNameOfDeclaration(symbol.ValueDeclaration) - if name != nil && ast.IsComputedPropertyName(name) { - return name - } - } - if b.ch.valueSymbolLinks.Has(symbol) { - nameType := b.ch.valueSymbolLinks.Get(symbol).nameType - if nameType != nil && nameType.flags&(TypeFlagsEnumLiteral|TypeFlagsUniqueESSymbol) != 0 { - oldEnclosing := b.ctx.enclosingDeclaration - b.ctx.enclosingDeclaration = nameType.symbol.ValueDeclaration - result := b.f.NewComputedPropertyName(b.symbolToExpression(nameType.symbol, meaning)) - b.ctx.enclosingDeclaration = oldEnclosing - return result - } - } - } - return b.symbolToExpression(symbol, meaning) -} - -func (b *nodeBuilderImpl) symbolToName(symbol *ast.Symbol, meaning ast.SymbolFlags, expectsIdentifier bool) *ast.Node { - chain := b.lookupSymbolChain(symbol, meaning, false) - if expectsIdentifier && len(chain) != 1 && !b.ctx.encounteredError && (b.ctx.flags&nodebuilder.FlagsAllowQualifiedNameInPlaceOfIdentifier != 0) { - b.ctx.encounteredError = true - } - return b.createEntityNameFromSymbolChain(chain, len(chain)-1) -} - -func (b *nodeBuilderImpl) createEntityNameFromSymbolChain(chain []*ast.Symbol, index int) *ast.Node { - // typeParameterNodes := b.lookupTypeParameterNodes(chain, index) - symbol := chain[index] - - if index == 0 { - b.ctx.flags |= nodebuilder.FlagsInInitialEntityName - } - symbolName := b.getNameOfSymbolAsWritten(symbol) - if index == 0 { - b.ctx.flags ^= nodebuilder.FlagsInInitialEntityName - } - - identifier := b.f.NewIdentifier(symbolName) - b.e.AddEmitFlags(identifier, printer.EFNoAsciiEscaping) - // !!! TODO: smuggle type arguments out - // if (typeParameterNodes) setIdentifierTypeArguments(identifier, factory.createNodeArray(typeParameterNodes)); - // identifier.symbol = symbol; - // expression = identifier; - if index > 0 { - return b.f.NewQualifiedName( - b.createEntityNameFromSymbolChain(chain, index-1), - identifier, - ) - } - return identifier -} - -// TODO: Audit usages of symbolToEntityNameNode - they should probably all be symbolToName -func (b *nodeBuilderImpl) symbolToEntityNameNode(symbol *ast.Symbol) *ast.EntityName { - identifier := b.f.NewIdentifier(symbol.Name) - if symbol.Parent != nil { - return b.f.NewQualifiedName(b.symbolToEntityNameNode(symbol.Parent), identifier) - } - return identifier -} - -func (b *nodeBuilderImpl) symbolToTypeNode(symbol *ast.Symbol, mask ast.SymbolFlags, typeArguments *ast.NodeList) *ast.TypeNode { - chain := b.lookupSymbolChain(symbol, mask, (b.ctx.flags&nodebuilder.FlagsUseAliasDefinedOutsideCurrentScope == 0)) // If we're using aliases outside the current scope, dont bother with the module - if len(chain) == 0 { - return nil // TODO: shouldn't be possible, `lookupSymbolChain` should always at least return the input symbol and issue an error - } - isTypeOf := mask == ast.SymbolFlagsValue - if core.Some(chain[0].Declarations, hasNonGlobalAugmentationExternalModuleSymbol) { - // module is root, must use `ImportTypeNode` - var nonRootParts *ast.Node - if len(chain) > 1 { - nonRootParts = b.createAccessFromSymbolChain(chain, len(chain)-1, 1, typeArguments) - } - typeParameterNodes := typeArguments - if typeParameterNodes == nil { - typeParameterNodes = b.lookupTypeParameterNodes(chain, 0) - } - contextFile := ast.GetSourceFileOfNode(b.e.MostOriginal(b.ctx.enclosingDeclaration)) // TODO: Just use b.ctx.enclosingFile ? Or is the delayed lookup important for context moves? - targetFile := ast.GetSourceFileOfModule(chain[0]) - var specifier string - var attributes *ast.Node - if b.ch.compilerOptions.GetModuleResolutionKind() == core.ModuleResolutionKindNode16 || b.ch.compilerOptions.GetModuleResolutionKind() == core.ModuleResolutionKindNodeNext { - // An `import` type directed at an esm format file is only going to resolve in esm mode - set the esm mode assertion - if targetFile != nil && contextFile != nil && b.ch.program.GetEmitModuleFormatOfFile(targetFile) == core.ModuleKindESNext && b.ch.program.GetEmitModuleFormatOfFile(targetFile) != b.ch.program.GetEmitModuleFormatOfFile(contextFile) { - specifier = b.getSpecifierForModuleSymbol(chain[0], core.ModuleKindESNext) - attributes = b.f.NewImportAttributes( - ast.KindWithKeyword, - b.f.NewNodeList([]*ast.Node{b.f.NewImportAttribute(b.f.NewStringLiteral("resolution-mode"), b.f.NewStringLiteral("import"))}), - false, - ) - } - } - if len(specifier) == 0 { - specifier = b.getSpecifierForModuleSymbol(chain[0], core.ResolutionModeNone) - } - if (b.ctx.flags&nodebuilder.FlagsAllowNodeModulesRelativePaths == 0) /* && b.ch.compilerOptions.GetModuleResolutionKind() != core.ModuleResolutionKindClassic */ && strings.Contains(specifier, "/node_modules/") { - oldSpecifier := specifier - - if b.ch.compilerOptions.GetModuleResolutionKind() == core.ModuleResolutionKindNode16 || b.ch.compilerOptions.GetModuleResolutionKind() == core.ModuleResolutionKindNodeNext { - // We might be able to write a portable import type using a mode override; try specifier generation again, but with a different mode set - swappedMode := core.ModuleKindESNext - if b.ch.program.GetEmitModuleFormatOfFile(contextFile) == core.ModuleKindESNext { - swappedMode = core.ModuleKindCommonJS - } - specifier = b.getSpecifierForModuleSymbol(chain[0], swappedMode) - - if strings.Contains(specifier, "/node_modules/") { - // Still unreachable :( - specifier = oldSpecifier - } else { - modeStr := "require" - if swappedMode == core.ModuleKindESNext { - modeStr = "import" - } - attributes = b.f.NewImportAttributes( - ast.KindWithKeyword, - b.f.NewNodeList([]*ast.Node{b.f.NewImportAttribute(b.f.NewStringLiteral("resolution-mode"), b.f.NewStringLiteral(modeStr))}), - false, - ) - } - } - - if attributes == nil { - // If ultimately we can only name the symbol with a reference that dives into a `node_modules` folder, we should error - // since declaration files with these kinds of references are liable to fail when published :( - b.ctx.encounteredError = true - b.ctx.tracker.ReportLikelyUnsafeImportRequiredError(oldSpecifier) - } - } - - lit := b.f.NewLiteralTypeNode(b.f.NewStringLiteral(specifier)) - b.ctx.approximateLength += len(specifier) + 10 // specifier + import("") - if nonRootParts == nil || ast.IsEntityName(nonRootParts) { - if nonRootParts != nil { - // !!! TODO: smuggle type arguments out - // const lastId = isIdentifier(nonRootParts) ? nonRootParts : nonRootParts.right; - // setIdentifierTypeArguments(lastId, /*typeArguments*/ undefined); - } - return b.f.NewImportTypeNode(isTypeOf, lit, attributes, nonRootParts, typeParameterNodes) - } - - splitNode := getTopmostIndexedAccessType(nonRootParts.AsIndexedAccessTypeNode()) - qualifier := splitNode.ObjectType.AsTypeReference().TypeName - return b.f.NewIndexedAccessTypeNode( - b.f.NewImportTypeNode(isTypeOf, lit, attributes, qualifier, typeParameterNodes), - splitNode.IndexType, - ) - - } - - entityName := b.createAccessFromSymbolChain(chain, len(chain)-1, 0, typeArguments) - if ast.IsIndexedAccessTypeNode(entityName) { - return entityName // Indexed accesses can never be `typeof` - } - if isTypeOf { - return b.f.NewTypeQueryNode(entityName, nil) - } - // !!! TODO: smuggle type arguments out - // Move type arguments from last identifier on chain to type reference - // const lastId = isIdentifier(entityName) ? entityName : entityName.right; - // const lastTypeArgs = getIdentifierTypeArguments(lastId); - // setIdentifierTypeArguments(lastId, /*typeArguments*/ undefined); - return b.f.NewTypeReferenceNode(entityName, typeArguments) -} - -func getTopmostIndexedAccessType(node *ast.IndexedAccessTypeNode) *ast.IndexedAccessTypeNode { - if ast.IsIndexedAccessTypeNode(node.ObjectType) { - return getTopmostIndexedAccessType(node.ObjectType.AsIndexedAccessTypeNode()) - } - return node -} - -func (b *nodeBuilderImpl) createAccessFromSymbolChain(chain []*ast.Symbol, index int, stopper int, overrideTypeArguments *ast.NodeList) *ast.Node { - // !!! TODO: smuggle type arguments out - typeParameterNodes := overrideTypeArguments - if index != (len(chain) - 1) { - typeParameterNodes = b.lookupTypeParameterNodes(chain, index) - } - symbol := chain[index] - var parent *ast.Symbol - if index > 0 { - parent = chain[index-1] - } - - var symbolName string - if index == 0 { - b.ctx.flags |= nodebuilder.FlagsInInitialEntityName - symbolName = b.getNameOfSymbolAsWritten(symbol) - b.ctx.approximateLength += len(symbolName) + 1 - b.ctx.flags ^= nodebuilder.FlagsInInitialEntityName - } else { - // lookup a ref to symbol within parent to handle export aliases - if parent != nil { - exports := b.ch.getExportsOfSymbol(parent) - if exports != nil { - // avoid exhaustive iteration in the common case - res, ok := exports[symbol.Name] - if symbol.Name != ast.InternalSymbolNameExportEquals && !isLateBoundName(symbol.Name) && ok && res != nil && b.ch.getSymbolIfSameReference(res, symbol) != nil { - symbolName = symbol.Name - } else { - results := make(map[*ast.Symbol]string, 1) - for name, ex := range exports { - if b.ch.getSymbolIfSameReference(ex, symbol) != nil && !isLateBoundName(name) && name != ast.InternalSymbolNameExportEquals { - results[ex] = name - // break // must collect all results and sort them - exports are randomly iterated - } - } - resultSymbols := slices.Collect(maps.Keys(results)) - if len(resultSymbols) > 0 { - b.ch.sortSymbols(resultSymbols) - symbolName = results[resultSymbols[0]] - } - } - } - } - } - - if len(symbolName) == 0 { - var name *ast.Node - for _, d := range symbol.Declarations { - name = ast.GetNameOfDeclaration(d) - if name != nil { - break - } - } - if name != nil && ast.IsComputedPropertyName(name) && ast.IsEntityName(name.AsComputedPropertyName().Expression) { - lhs := b.createAccessFromSymbolChain(chain, index-1, stopper, overrideTypeArguments) - if ast.IsEntityName(lhs) { - return b.f.NewIndexedAccessTypeNode( - b.f.NewParenthesizedTypeNode(b.f.NewTypeQueryNode(lhs, nil)), - b.f.NewTypeQueryNode(name.Expression(), nil), - ) - } - return lhs - } - symbolName = b.getNameOfSymbolAsWritten(symbol) - } - b.ctx.approximateLength += len(symbolName) + 1 - - if (b.ctx.flags&nodebuilder.FlagsForbidIndexedAccessSymbolReferences == 0) && parent != nil && - b.ch.getMembersOfSymbol(parent) != nil && b.ch.getMembersOfSymbol(parent)[symbol.Name] != nil && - b.ch.getSymbolIfSameReference(b.ch.getMembersOfSymbol(parent)[symbol.Name], symbol) != nil { - // Should use an indexed access - lhs := b.createAccessFromSymbolChain(chain, index-1, stopper, overrideTypeArguments) - if ast.IsIndexedAccessTypeNode(lhs) { - return b.f.NewIndexedAccessTypeNode( - lhs, - b.f.NewLiteralTypeNode(b.f.NewStringLiteral(symbolName)), - ) - } - return b.f.NewIndexedAccessTypeNode( - b.f.NewTypeReferenceNode(lhs, typeParameterNodes), - b.f.NewLiteralTypeNode(b.f.NewStringLiteral(symbolName)), - ) - } - - identifier := b.f.NewIdentifier(symbolName) - b.e.AddEmitFlags(identifier, printer.EFNoAsciiEscaping) - // !!! TODO: smuggle type arguments out - // if (typeParameterNodes) setIdentifierTypeArguments(identifier, factory.createNodeArray(typeParameterNodes)); - // identifier.symbol = symbol; - - if index > stopper { - lhs := b.createAccessFromSymbolChain(chain, index-1, stopper, overrideTypeArguments) - if !ast.IsEntityName(lhs) { - panic("Impossible construct - an export of an indexed access cannot be reachable") - } - return b.f.NewQualifiedName(lhs, identifier) - } - - return identifier -} - -func (b *nodeBuilderImpl) symbolToExpression(symbol *ast.Symbol, mask ast.SymbolFlags) *ast.Expression { - chain := b.lookupSymbolChain(symbol, mask, false) - return b.createExpressionFromSymbolChain(chain, len(chain)-1) -} - -func (b *nodeBuilderImpl) createExpressionFromSymbolChain(chain []*ast.Symbol, index int) *ast.Expression { - // typeParameterNodes := b.lookupTypeParameterNodes(chain, index) - symbol := chain[index] - - if index == 0 { - b.ctx.flags |= nodebuilder.FlagsInInitialEntityName - } - symbolName := b.getNameOfSymbolAsWritten(symbol) - if index == 0 { - b.ctx.flags ^= nodebuilder.FlagsInInitialEntityName - } - - if startsWithSingleOrDoubleQuote(symbolName) && core.Some(symbol.Declarations, hasNonGlobalAugmentationExternalModuleSymbol) { - return b.f.NewStringLiteral(b.getSpecifierForModuleSymbol(symbol, core.ResolutionModeNone)) - } - - if index == 0 || canUsePropertyAccess(symbolName) { - identifier := b.f.NewIdentifier(symbolName) - b.e.AddEmitFlags(identifier, printer.EFNoAsciiEscaping) - // !!! TODO: smuggle type arguments out - // if (typeParameterNodes) setIdentifierTypeArguments(identifier, factory.createNodeArray(typeParameterNodes)); - // identifier.symbol = symbol; - if index > 0 { - return b.f.NewPropertyAccessExpression(b.createExpressionFromSymbolChain(chain, index-1), nil, identifier, ast.NodeFlagsNone) - } - return identifier - } - - if startsWithSquareBracket(symbolName) { - symbolName = symbolName[1 : len(symbolName)-1] - } - - var expression *ast.Expression - if startsWithSingleOrDoubleQuote(symbolName) && symbol.Flags&ast.SymbolFlagsEnumMember == 0 { - expression = b.f.NewStringLiteral(stringutil.UnquoteString(symbolName)) - } else if jsnum.FromString(symbolName).String() == symbolName { - // TODO: the follwing in strada would assert if the number is negative, but no such assertion exists here - // Moreover, what's even guaranteeing the name *isn't* -1 here anyway? Needs double-checking. - expression = b.f.NewNumericLiteral(symbolName) - } - if expression == nil { - expression = b.f.NewIdentifier(symbolName) - b.e.AddEmitFlags(expression, printer.EFNoAsciiEscaping) - // !!! TODO: smuggle type arguments out - // if (typeParameterNodes) setIdentifierTypeArguments(identifier, factory.createNodeArray(typeParameterNodes)); - // identifier.symbol = symbol; - // expression = identifier; - } - return b.f.NewElementAccessExpression(b.createExpressionFromSymbolChain(chain, index-1), nil, expression, ast.NodeFlagsNone) -} - -func canUsePropertyAccess(name string) bool { - if len(name) == 0 { - return false - } - // TODO: in strada, this only used `isIdentifierStart` on the first character, while this checks the whole string for validity - // - possible strada bug? - if strings.HasPrefix(name, "#") { - return len(name) > 1 && scanner.IsIdentifierText(name[1:], core.LanguageVariantStandard) - } - return scanner.IsIdentifierText(name, core.LanguageVariantStandard) -} - -func startsWithSingleOrDoubleQuote(str string) bool { - return strings.HasPrefix(str, "'") || strings.HasPrefix(str, "\"") -} - -func startsWithSquareBracket(str string) bool { - return strings.HasPrefix(str, "[") -} - -func isDefaultBindingContext(location *ast.Node) bool { - return location.Kind == ast.KindSourceFile || ast.IsAmbientModule(location) -} - -func (b *nodeBuilderImpl) getNameOfSymbolFromNameType(symbol *ast.Symbol) string { - if b.ch.valueSymbolLinks.Has(symbol) { - nameType := b.ch.valueSymbolLinks.Get(symbol).nameType - if nameType == nil { - return "" - } - if nameType.flags&TypeFlagsStringOrNumberLiteral != 0 { - var name string - switch v := nameType.AsLiteralType().value.(type) { - case string: - name = v - case jsnum.Number: - name = v.String() - } - if !scanner.IsIdentifierText(name, core.LanguageVariantStandard) && !isNumericLiteralName(name) { - return b.ch.valueToString(nameType.AsLiteralType().value) - } - if isNumericLiteralName(name) && strings.HasPrefix(name, "-") { - return fmt.Sprintf("[%s]", name) - } - return name - } - if nameType.flags&TypeFlagsUniqueESSymbol != 0 { - text := b.getNameOfSymbolAsWritten(nameType.AsUniqueESSymbolType().symbol) - return fmt.Sprintf("[%s]", text) - } - } - return "" -} - -/** -* Gets a human-readable name for a symbol. -* Should *not* be used for the right-hand side of a `.` -- use `symbolName(symbol)` for that instead. -* -* Unlike `symbolName(symbol)`, this will include quotes if the name is from a string literal. -* It will also use a representation of a number as written instead of a decimal form, e.g. `0o11` instead of `9`. - */ -func (b *nodeBuilderImpl) getNameOfSymbolAsWritten(symbol *ast.Symbol) string { - result, ok := b.ctx.remappedSymbolReferences[ast.GetSymbolId(symbol)] - if ok { - symbol = result - } - if symbol.Name == ast.InternalSymbolNameDefault && (b.ctx.flags&nodebuilder.FlagsUseAliasDefinedOutsideCurrentScope == 0) && - // If it's not the first part of an entity name, it must print as `default` - ((b.ctx.flags&nodebuilder.FlagsInInitialEntityName == 0) || - // if the symbol is synthesized, it will only be referenced externally it must print as `default` - len(symbol.Declarations) == 0 || - // if not in the same binding context (source file, module declaration), it must print as `default` - (b.ctx.enclosingDeclaration != nil && ast.FindAncestor(symbol.Declarations[0], isDefaultBindingContext) != ast.FindAncestor(b.ctx.enclosingDeclaration, isDefaultBindingContext))) { - return "default" - } - if len(symbol.Declarations) > 0 { - name := core.FirstNonNil(symbol.Declarations, ast.GetNameOfDeclaration) // Try using a declaration with a name, first - if name != nil { - // !!! TODO: JS Object.defineProperty declarations - // if ast.IsCallExpression(declaration) && ast.IsBindableObjectDefinePropertyCall(declaration) { - // return symbol.Name - // } - if ast.IsComputedPropertyName(name) && symbol.CheckFlags&ast.CheckFlagsLate == 0 { - if b.ch.valueSymbolLinks.Has(symbol) && b.ch.valueSymbolLinks.Get(symbol).nameType != nil && b.ch.valueSymbolLinks.Get(symbol).nameType.flags&TypeFlagsStringOrNumberLiteral != 0 { - result := b.getNameOfSymbolFromNameType(symbol) - if len(result) > 0 { - return result - } - } - } - return scanner.DeclarationNameToString(name) - } - declaration := symbol.Declarations[0] // Declaration may be nameless, but we'll try anyway - if declaration.Parent != nil && declaration.Parent.Kind == ast.KindVariableDeclaration { - return scanner.DeclarationNameToString(declaration.Parent.AsVariableDeclaration().Name()) - } - if ast.IsClassExpression(declaration) || ast.IsFunctionExpression(declaration) || ast.IsArrowFunction(declaration) { - if b.ctx != nil && !b.ctx.encounteredError && b.ctx.flags&nodebuilder.FlagsAllowAnonymousIdentifier == 0 { - b.ctx.encounteredError = true - } - switch declaration.Kind { - case ast.KindClassExpression: - return "(Anonymous class)" - case ast.KindFunctionExpression, ast.KindArrowFunction: - return "(Anonymous function)" - } - } - } - name := b.getNameOfSymbolFromNameType(symbol) - if len(name) > 0 { - return name - } - return symbol.Name -} - -// The full set of type parameters for a generic class or interface type consists of its outer type parameters plus -// its locally declared type parameters. -func (b *nodeBuilderImpl) getTypeParametersOfClassOrInterface(symbol *ast.Symbol) []*Type { - result := make([]*Type, 0) - result = append(result, b.ch.getOuterTypeParametersOfClassOrInterface(symbol)...) - result = append(result, b.ch.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol)...) - return result -} - -func (b *nodeBuilderImpl) lookupTypeParameterNodes(chain []*ast.Symbol, index int) *ast.TypeParameterList { - debug.Assert(chain != nil && 0 <= index && index < len(chain)) - symbol := chain[index] - symbolId := ast.GetSymbolId(symbol) - if !b.ctx.hasCreatedTypeParameterSymbolList { - b.ctx.hasCreatedTypeParameterSymbolList = true - b.ctx.typeParameterSymbolList = make(map[ast.SymbolId]struct{}) - } - _, ok := b.ctx.typeParameterSymbolList[symbolId] - if ok { - return nil - } - b.ctx.typeParameterSymbolList[symbolId] = struct{}{} - - if b.ctx.flags&nodebuilder.FlagsWriteTypeParametersInQualifiedName != 0 && index < (len(chain)-1) { - parentSymbol := symbol - nextSymbol := chain[index+1] - - if nextSymbol.CheckFlags&ast.CheckFlagsInstantiated != 0 { - targetSymbol := parentSymbol - if parentSymbol.Flags&ast.SymbolFlagsAlias != 0 { - targetSymbol = b.ch.resolveAlias(parentSymbol) - } - params := b.getTypeParametersOfClassOrInterface(targetSymbol) - targetMapper := b.ch.valueSymbolLinks.Get(nextSymbol).mapper - if targetMapper != nil { - params = core.Map(params, targetMapper.Map) - } - return b.mapToTypeNodes(params, false /*isBareList*/) - } else { - typeParameterNodes := b.typeParametersToTypeParameterDeclarations(symbol) - if len(typeParameterNodes) > 0 { - return b.f.NewNodeList(typeParameterNodes) - } - return nil - } - } - - return nil -} - -// TODO: move `lookupSymbolChain` and co to `symbolaccessibility.go` (but getSpecifierForModuleSymbol uses much context which makes that hard?) -func (b *nodeBuilderImpl) lookupSymbolChain(symbol *ast.Symbol, meaning ast.SymbolFlags, yieldModuleSymbol bool) []*ast.Symbol { - b.ctx.tracker.TrackSymbol(symbol, b.ctx.enclosingDeclaration, meaning) - return b.lookupSymbolChainWorker(symbol, meaning, yieldModuleSymbol) -} - -func (b *nodeBuilderImpl) lookupSymbolChainWorker(symbol *ast.Symbol, meaning ast.SymbolFlags, yieldModuleSymbol bool) []*ast.Symbol { - // Try to get qualified name if the symbol is not a type parameter and there is an enclosing declaration. - var chain []*ast.Symbol - isTypeParameter := symbol.Flags&ast.SymbolFlagsTypeParameter != 0 - if !isTypeParameter && (b.ctx.enclosingDeclaration != nil || b.ctx.flags&nodebuilder.FlagsUseFullyQualifiedType != 0) && (b.ctx.internalFlags&nodebuilder.InternalFlagsDoNotIncludeSymbolChain == 0) { - res := b.getSymbolChain(symbol, meaning /*endOfChain*/, true, yieldModuleSymbol) - chain = res - debug.CheckDefined(chain) - debug.Assert(len(chain) > 0) - } else { - chain = append(chain, symbol) - } - return chain -} - -type sortedSymbolNamePair struct { - sym *ast.Symbol - name string -} - -/** @param endOfChain Set to false for recursive calls; non-recursive calls should always output something. */ -func (b *nodeBuilderImpl) getSymbolChain(symbol *ast.Symbol, meaning ast.SymbolFlags, endOfChain bool, yieldModuleSymbol bool) []*ast.Symbol { - accessibleSymbolChain := b.ch.getAccessibleSymbolChain(symbol, b.ctx.enclosingDeclaration, meaning, b.ctx.flags&nodebuilder.FlagsUseOnlyExternalAliasing != 0) - qualifierMeaning := meaning - if len(accessibleSymbolChain) > 1 { - qualifierMeaning = getQualifiedLeftMeaning(meaning) - } - if len(accessibleSymbolChain) == 0 || - b.ch.needsQualification(accessibleSymbolChain[0], b.ctx.enclosingDeclaration, qualifierMeaning) { - // Go up and add our parent. - root := symbol - if len(accessibleSymbolChain) > 0 { - root = accessibleSymbolChain[0] - } - parents := b.ch.getContainersOfSymbol(root, b.ctx.enclosingDeclaration, meaning) - if len(parents) > 0 { - parentSpecifiers := core.Map(parents, func(symbol *ast.Symbol) sortedSymbolNamePair { - if core.Some(symbol.Declarations, hasNonGlobalAugmentationExternalModuleSymbol) { - return sortedSymbolNamePair{symbol, b.getSpecifierForModuleSymbol(symbol, core.ResolutionModeNone)} - } - return sortedSymbolNamePair{symbol, ""} - }) - slices.SortStableFunc(parentSpecifiers, b.sortByBestName) - for _, pair := range parentSpecifiers { - parent := pair.sym - parentChain := b.getSymbolChain(parent, getQualifiedLeftMeaning(meaning), false, yieldModuleSymbol) - if len(parentChain) > 0 { - if parent.Exports != nil { - exported, ok := parent.Exports[ast.InternalSymbolNameExportEquals] - if ok && b.ch.getSymbolIfSameReference(exported, symbol) != nil { - // parentChain root _is_ symbol - symbol is a module export=, so it kinda looks like it's own parent - // No need to lookup an alias for the symbol in itself - accessibleSymbolChain = parentChain - break - } - } - nextSyms := accessibleSymbolChain - if len(nextSyms) == 0 { - fallback := b.ch.getAliasForSymbolInContainer(parent, symbol) - if fallback == nil { - fallback = symbol - } - nextSyms = append(nextSyms, fallback) - } - accessibleSymbolChain = append(parentChain, nextSyms...) - break - } - } - } - } - if len(accessibleSymbolChain) > 0 { - return accessibleSymbolChain - } - if - // If this is the last part of outputting the symbol, always output. The cases apply only to parent symbols. - endOfChain || - // If a parent symbol is an anonymous type, don't write it. - (symbol.Flags&(ast.SymbolFlagsTypeLiteral|ast.SymbolFlagsObjectLiteral) == 0) { - // If a parent symbol is an external module, don't write it. (We prefer just `x` vs `"foo/bar".x`.) - if !endOfChain && !yieldModuleSymbol && core.Some(symbol.Declarations, hasNonGlobalAugmentationExternalModuleSymbol) { - return nil - } - return []*ast.Symbol{symbol} - } - return nil -} - -func (b_ *nodeBuilderImpl) sortByBestName(a sortedSymbolNamePair, b sortedSymbolNamePair) int { - specifierA := a.name - specifierB := b.name - if len(specifierA) > 0 && len(specifierB) > 0 { - isBRelative := tspath.PathIsRelative(specifierB) - if tspath.PathIsRelative(specifierA) == isBRelative { - // Both relative or both non-relative, sort by number of parts - return modulespecifiers.CountPathComponents(specifierA) - modulespecifiers.CountPathComponents(specifierB) - } - if isBRelative { - // A is non-relative, B is relative: prefer A - return -1 - } - // A is relative, B is non-relative: prefer B - return 1 - } - return b_.ch.compareSymbols(a.sym, b.sym) // must sort symbols for stable ordering -} - -func isAmbientModuleSymbolName(s string) bool { - return strings.HasPrefix(s, "\"") && strings.HasSuffix(s, "\"") -} - -func canHaveModuleSpecifier(node *ast.Node) bool { - if node == nil { - return false - } - switch node.Kind { - case ast.KindVariableDeclaration, - ast.KindBindingElement, - ast.KindImportDeclaration, - ast.KindExportDeclaration, - ast.KindImportEqualsDeclaration, - ast.KindImportClause, - ast.KindNamespaceExport, - ast.KindNamespaceImport, - ast.KindExportSpecifier, - ast.KindImportSpecifier, - ast.KindImportType: - return true - } - return false -} - -func TryGetModuleSpecifierFromDeclaration(node *ast.Node) *ast.Node { - res := tryGetModuleSpecifierFromDeclarationWorker(node) - if res == nil || !ast.IsStringLiteral(res) { - return nil - } - return res -} - -func tryGetModuleSpecifierFromDeclarationWorker(node *ast.Node) *ast.Node { - switch node.Kind { - case ast.KindVariableDeclaration, ast.KindBindingElement: - requireCall := ast.FindAncestor(node.Initializer(), func(node *ast.Node) bool { - return ast.IsRequireCall(node, true /*requireStringLiteralLikeArgument*/) - }) - if requireCall == nil { - return nil - } - return requireCall.AsCallExpression().Arguments.Nodes[0] - case ast.KindImportDeclaration: - return node.AsImportDeclaration().ModuleSpecifier - case ast.KindExportDeclaration: - return node.AsExportDeclaration().ModuleSpecifier - case ast.KindJSDocImportTag: - return node.AsJSDocImportTag().ModuleSpecifier - case ast.KindImportEqualsDeclaration: - ref := node.AsImportEqualsDeclaration().ModuleReference - if ref.Kind != ast.KindExternalModuleReference { - return nil - } - return ref.AsExternalModuleReference().Expression - case ast.KindImportClause: - if ast.IsImportDeclaration(node.Parent) { - return node.Parent.AsImportDeclaration().ModuleSpecifier - } - return node.Parent.AsJSDocImportTag().ModuleSpecifier - case ast.KindNamespaceExport: - return node.Parent.AsExportDeclaration().ModuleSpecifier - case ast.KindNamespaceImport: - if ast.IsImportDeclaration(node.Parent.Parent) { - return node.Parent.Parent.AsImportDeclaration().ModuleSpecifier - } - return node.Parent.Parent.AsJSDocImportTag().ModuleSpecifier - case ast.KindExportSpecifier: - return node.Parent.Parent.AsExportDeclaration().ModuleSpecifier - case ast.KindImportSpecifier: - if ast.IsImportDeclaration(node.Parent.Parent.Parent) { - return node.Parent.Parent.Parent.AsImportDeclaration().ModuleSpecifier - } - return node.Parent.Parent.Parent.AsJSDocImportTag().ModuleSpecifier - case ast.KindImportType: - if ast.IsLiteralImportTypeNode(node) { - return node.AsImportTypeNode().Argument.AsLiteralTypeNode().Literal - } - return nil - default: - debug.AssertNever(node) - return nil - } -} - -func (b *nodeBuilderImpl) getSpecifierForModuleSymbol(symbol *ast.Symbol, overrideImportMode core.ResolutionMode) string { - file := ast.GetDeclarationOfKind(symbol, ast.KindSourceFile) - if file == nil { - equivalentSymbol := core.FirstNonNil(symbol.Declarations, func(d *ast.Node) *ast.Symbol { - return b.ch.getFileSymbolIfFileSymbolExportEqualsContainer(d, symbol) - }) - if equivalentSymbol != nil { - file = ast.GetDeclarationOfKind(equivalentSymbol, ast.KindSourceFile) - } - } - - if file == nil { - if isAmbientModuleSymbolName(symbol.Name) { - return stringutil.StripQuotes(symbol.Name) - } - } - if b.ctx.enclosingFile == nil || b.ctx.tracker.GetModuleSpecifierGenerationHost() == nil { - if isAmbientModuleSymbolName(symbol.Name) { - return stringutil.StripQuotes(symbol.Name) - } - return ast.GetSourceFileOfModule(symbol).FileName() - } - - enclosingDeclaration := b.e.MostOriginal(b.ctx.enclosingDeclaration) - var originalModuleSpecifier *ast.Node - if canHaveModuleSpecifier(enclosingDeclaration) { - originalModuleSpecifier = TryGetModuleSpecifierFromDeclaration(enclosingDeclaration) - } - contextFile := b.ctx.enclosingFile - resolutionMode := overrideImportMode - if resolutionMode == core.ResolutionModeNone && originalModuleSpecifier != nil { - resolutionMode = b.ch.program.GetModeForUsageLocation(contextFile, originalModuleSpecifier) - } else if resolutionMode == core.ResolutionModeNone && contextFile != nil { - resolutionMode = b.ch.program.GetDefaultResolutionModeForFile(contextFile) - } - cacheKey := module.ModeAwareCacheKey{Name: string(contextFile.Path()), Mode: resolutionMode} - links := b.symbolLinks.Get(symbol) - if links.specifierCache == nil { - links.specifierCache = make(module.ModeAwareCache[string]) - } - result, ok := links.specifierCache[cacheKey] - if ok { - return result - } - isBundle := false // !!! remove me - // For declaration bundles, we need to generate absolute paths relative to the common source dir for imports, - // just like how the declaration emitter does for the ambient module declarations - we can easily accomplish this - // using the `baseUrl` compiler option (which we would otherwise never use in declaration emit) and a non-relative - // specifier preference - host := b.ctx.tracker.GetModuleSpecifierGenerationHost() - specifierCompilerOptions := b.ch.compilerOptions - specifierPref := modulespecifiers.ImportModuleSpecifierPreferenceProjectRelative - endingPref := modulespecifiers.ImportModuleSpecifierEndingPreferenceNone - if resolutionMode == core.ResolutionModeESM { - endingPref = modulespecifiers.ImportModuleSpecifierEndingPreferenceJs - } - if isBundle { - // !!! relies on option cloning and specifier host implementation - // specifierCompilerOptions = &core.CompilerOptions{BaseUrl: host.CommonSourceDirectory()} - // TODO: merge with b.ch.compilerOptions - specifierPref = modulespecifiers.ImportModuleSpecifierPreferenceNonRelative - endingPref = modulespecifiers.ImportModuleSpecifierEndingPreferenceMinimal - } - - allSpecifiers := modulespecifiers.GetModuleSpecifiers( - symbol, - b.ch, - specifierCompilerOptions, - contextFile, - host, - modulespecifiers.UserPreferences{ - ImportModuleSpecifierPreference: specifierPref, - ImportModuleSpecifierEnding: endingPref, - }, - modulespecifiers.ModuleSpecifierOptions{ - OverrideImportMode: overrideImportMode, - }, - false, /*forAutoImports*/ - ) - specifier := allSpecifiers[0] - links.specifierCache[cacheKey] = specifier - return specifier -} - -func (b *nodeBuilderImpl) typeParameterToDeclarationWithConstraint(typeParameter *Type, constraintNode *ast.TypeNode) *ast.TypeParameterDeclarationNode { - restoreFlags := b.saveRestoreFlags() - b.ctx.flags &= ^nodebuilder.FlagsWriteTypeParametersInQualifiedName // Avoids potential infinite loop when building for a claimspace with a generic - modifiers := ast.CreateModifiersFromModifierFlags(b.ch.getTypeParameterModifiers(typeParameter), b.f.NewModifier) - var modifiersList *ast.ModifierList - if len(modifiers) > 0 { - modifiersList = b.f.NewModifierList(modifiers) - } - name := b.typeParameterToName(typeParameter) - defaultParameter := b.ch.getDefaultFromTypeParameter(typeParameter) - var defaultParameterNode *ast.Node - if defaultParameter != nil { - defaultParameterNode = b.typeToTypeNode(defaultParameter) - } - restoreFlags() - return b.f.NewTypeParameterDeclaration( - modifiersList, - name.AsNode(), - constraintNode, - defaultParameterNode, - ) -} - -/** -* Unlike the utilities `setTextRange`, this checks if the `location` we're trying to set on `range` is within the -* same file as the active context. If not, the range is not applied. This prevents us from copying ranges across files, -* which will confuse the node printer (as it assumes all node ranges are within the current file). -* Additionally, if `range` _isn't synthetic_, or isn't in the current file, it will _copy_ it to _remove_ its' position -* information. -* -* It also calls `setOriginalNode` to setup a `.original` pointer, since you basically *always* want these in the node builder. - */ -func (b *nodeBuilderImpl) setTextRange(range_ *ast.Node, location *ast.Node) *ast.Node { - if range_ == nil { - return range_ - } - if !ast.NodeIsSynthesized(range_) || (range_.Flags&ast.NodeFlagsSynthesized == 0) || b.ctx.enclosingFile == nil || b.ctx.enclosingFile != ast.GetSourceFileOfNode(b.e.MostOriginal(range_)) { - range_ = range_.Clone(b.f) // if `range` is synthesized or originates in another file, copy it so it definitely has synthetic positions - } - if range_ == location || location == nil { - return range_ - } - // Don't overwrite the original node if `range` has an `original` node that points either directly or indirectly to `location` - original := b.e.Original(range_) - for original != nil && original != location { - original = b.e.Original(original) - } - if original == nil { - b.e.SetOriginalEx(range_, location, true) - } - - // only set positions if range comes from the same file since copying text across files isn't supported by the emitter - if b.ctx.enclosingFile != nil && b.ctx.enclosingFile == ast.GetSourceFileOfNode(b.e.MostOriginal(range_)) { - range_.Loc = location.Loc - return range_ - } - return range_ -} - -func (b *nodeBuilderImpl) typeParameterShadowsOtherTypeParameterInScope(name string, typeParameter *Type) bool { - result := b.ch.resolveName(b.ctx.enclosingDeclaration, name, ast.SymbolFlagsType, nil, false, false) - if result != nil && result.Flags&ast.SymbolFlagsTypeParameter != 0 { - return result != typeParameter.symbol - } - return false -} - -func (b *nodeBuilderImpl) typeParameterToName(typeParameter *Type) *ast.Identifier { - if b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 && b.ctx.typeParameterNames != nil { - cached, ok := b.ctx.typeParameterNames[typeParameter.id] - if ok { - return cached - } - } - result := b.symbolToName(typeParameter.symbol, ast.SymbolFlagsType /*expectsIdentifier*/, true) - if !ast.IsIdentifier(result) { - return b.f.NewIdentifier("(Missing type parameter)").AsIdentifier() - } - if typeParameter.symbol != nil && len(typeParameter.symbol.Declarations) > 0 { - decl := typeParameter.symbol.Declarations[0] - if decl != nil && ast.IsTypeParameterDeclaration(decl) { - result = b.setTextRange(result, decl.Name()) - } - } - if b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 { - if !b.ctx.hasCreatedTypeParametersNamesLookups { - b.ctx.hasCreatedTypeParametersNamesLookups = true - b.ctx.typeParameterNames = make(map[TypeId]*ast.Identifier) - b.ctx.typeParameterNamesByText = make(map[string]struct{}) - b.ctx.typeParameterNamesByTextNextNameCount = make(map[string]int) - } - - rawText := result.AsIdentifier().Text - i := 0 - cached, ok := b.ctx.typeParameterNamesByTextNextNameCount[rawText] - if ok { - i = cached - } - text := rawText - - for true { - _, present := b.ctx.typeParameterNamesByText[text] - if !present && !b.typeParameterShadowsOtherTypeParameterInScope(text, typeParameter) { - break - } - i++ - text = fmt.Sprintf("%s_%d", rawText, i) - } - - if text != rawText { - // !!! TODO: smuggle type arguments out - // const typeArguments = getIdentifierTypeArguments(result); - result = b.f.NewIdentifier(text) - // setIdentifierTypeArguments(result, typeArguments); - } - - // avoiding iterations of the above loop turns out to be worth it when `i` starts to get large, so we cache the max - // `i` we've used thus far, to save work later - b.ctx.typeParameterNamesByTextNextNameCount[rawText] = i - b.ctx.typeParameterNames[typeParameter.id] = result.AsIdentifier() - b.ctx.typeParameterNamesByText[text] = struct{}{} - } - - return result.AsIdentifier() -} - -func (b *nodeBuilderImpl) isMappedTypeHomomorphic(mapped *Type) bool { - return b.ch.getHomomorphicTypeVariable(mapped) != nil -} - -func (b *nodeBuilderImpl) isHomomorphicMappedTypeWithNonHomomorphicInstantiation(mapped *MappedType) bool { - return mapped.target != nil && !b.isMappedTypeHomomorphic(mapped.AsType()) && b.isMappedTypeHomomorphic(mapped.target) -} - -func (b *nodeBuilderImpl) createMappedTypeNodeFromType(t *Type) *ast.TypeNode { - debug.Assert(t.Flags()&TypeFlagsObject != 0) - mapped := t.AsMappedType() - var readonlyToken *ast.Node - if mapped.declaration.ReadonlyToken != nil { - readonlyToken = b.f.NewToken(mapped.declaration.ReadonlyToken.Kind) - } - var questionToken *ast.Node - if mapped.declaration.QuestionToken != nil { - questionToken = b.f.NewToken(mapped.declaration.QuestionToken.Kind) - } - var appropriateConstraintTypeNode *ast.Node - var newTypeVariable *ast.Node - templateType := b.ch.getTemplateTypeFromMappedType(t) - typeParameter := b.ch.getTypeParameterFromMappedType(t) - - // If the mapped type isn't `keyof` constraint-declared, _but_ still has modifiers preserved, and its naive instantiation won't preserve modifiers because its constraint isn't `keyof` constrained, we have work to do - needsModifierPreservingWrapper := !b.ch.isMappedTypeWithKeyofConstraintDeclaration(t) && - b.ch.getModifiersTypeFromMappedType(t).flags&TypeFlagsUnknown == 0 && - b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 && - !(b.ch.getConstraintTypeFromMappedType(t).flags&TypeFlagsTypeParameter != 0 && b.ch.getConstraintOfTypeParameter(b.ch.getConstraintTypeFromMappedType(t)).flags&TypeFlagsIndex != 0) - - if b.ch.isMappedTypeWithKeyofConstraintDeclaration(t) { - // We have a { [P in keyof T]: X } - // We do this to ensure we retain the toplevel keyof-ness of the type which may be lost due to keyof distribution during `getConstraintTypeFromMappedType` - if b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 && b.isHomomorphicMappedTypeWithNonHomomorphicInstantiation(mapped) { - newConstraintParam := b.ch.newTypeParameter( - b.ch.newSymbol(ast.SymbolFlagsTypeParameter, "T"), - ) - name := b.typeParameterToName(newConstraintParam) - target := t.Target() - newTypeVariable = b.f.NewTypeReferenceNode(name.AsNode(), nil) - templateType = b.ch.instantiateType(b.ch.getTemplateTypeFromMappedType(target), newTypeMapper([]*Type{b.ch.getTypeParameterFromMappedType(target), b.ch.getModifiersTypeFromMappedType(target)}, []*Type{typeParameter, newConstraintParam})) - } - indexTarget := newTypeVariable - if indexTarget == nil { - indexTarget = b.typeToTypeNode(b.ch.getModifiersTypeFromMappedType(t)) - } - appropriateConstraintTypeNode = b.f.NewTypeOperatorNode(ast.KindKeyOfKeyword, indexTarget) - } else if needsModifierPreservingWrapper { - // So, step 1: new type variable - newParam := b.ch.newTypeParameter( - b.ch.newSymbol(ast.SymbolFlagsTypeParameter, "T"), - ) - name := b.typeParameterToName(newParam) - newTypeVariable = b.f.NewTypeReferenceNode(name.AsNode(), nil) - // step 2: make that new type variable itself the constraint node, making the mapped type `{[K in T_1]: Template}` - appropriateConstraintTypeNode = newTypeVariable - } else { - appropriateConstraintTypeNode = b.typeToTypeNode(b.ch.getConstraintTypeFromMappedType(t)) - } - - // nameType and templateType nodes have to be in the new scope - cleanup := b.enterNewScope(mapped.declaration.AsNode(), nil, []*Type{b.ch.getTypeParameterFromMappedType(t)}, nil, nil) - typeParameterNode := b.typeParameterToDeclarationWithConstraint(typeParameter, appropriateConstraintTypeNode) - var nameTypeNode *ast.Node - if mapped.declaration.NameType != nil { - nameTypeNode = b.typeToTypeNode(b.ch.getNameTypeFromMappedType(t)) - } - templateTypeNode := b.typeToTypeNode(b.ch.removeMissingType( - templateType, - getMappedTypeModifiers(t)&MappedTypeModifiersIncludeOptional != 0, - )) - cleanup() - result := b.f.NewMappedTypeNode( - readonlyToken, - typeParameterNode, - nameTypeNode, - questionToken, - templateTypeNode, - nil, - ) - b.ctx.approximateLength += 10 - b.e.AddEmitFlags(result, printer.EFSingleLine) - - if b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 && b.isHomomorphicMappedTypeWithNonHomomorphicInstantiation(mapped) { - // homomorphic mapped type with a non-homomorphic naive inlining - // wrap it with a conditional like `SomeModifiersType extends infer U ? {..the mapped type...} : never` to ensure the resulting - // type stays homomorphic - - rawConstraintTypeFromDeclaration := b.getTypeFromTypeNode(mapped.declaration.TypeParameter.AsTypeParameter().Constraint.AsTypeOperatorNode().Type, false) - if rawConstraintTypeFromDeclaration != nil { - rawConstraintTypeFromDeclaration = b.ch.getConstraintOfTypeParameter(rawConstraintTypeFromDeclaration) - } - if rawConstraintTypeFromDeclaration == nil { - rawConstraintTypeFromDeclaration = b.ch.unknownType - } - originalConstraint := b.ch.instantiateType(rawConstraintTypeFromDeclaration, mapped.mapper) - - var originalConstraintNode *ast.Node - if originalConstraint.flags&TypeFlagsUnknown == 0 { - originalConstraintNode = b.typeToTypeNode(originalConstraint) - } - - return b.f.NewConditionalTypeNode( - b.typeToTypeNode(b.ch.getModifiersTypeFromMappedType(t)), - b.f.NewInferTypeNode(b.f.NewTypeParameterDeclaration(nil, newTypeVariable.AsTypeReference().TypeName.Clone(b.f), originalConstraintNode, nil)), - result, - b.f.NewKeywordTypeNode(ast.KindNeverKeyword), - ) - } else if needsModifierPreservingWrapper { - // and step 3: once the mapped type is reconstructed, create a `ConstraintType extends infer T_1 extends keyof ModifiersType ? {[K in T_1]: Template} : never` - // subtly different from the `keyof` constraint case, by including the `keyof` constraint on the `infer` type parameter, it doesn't rely on the constraint type being itself - // constrained to a `keyof` type to preserve its modifier-preserving behavior. This is all basically because we preserve modifiers for a wider set of mapped types than - // just homomorphic ones. - return b.f.NewConditionalTypeNode( - b.typeToTypeNode(b.ch.getConstraintTypeFromMappedType(t)), - b.f.NewInferTypeNode(b.f.NewTypeParameterDeclaration(nil, newTypeVariable.AsTypeReference().TypeName.Clone(b.f), b.f.NewTypeOperatorNode(ast.KindKeyOfKeyword, b.typeToTypeNode(b.ch.getModifiersTypeFromMappedType(t))), nil)), - result, - b.f.NewKeywordTypeNode(ast.KindNeverKeyword), - ) - } - - return result -} - -func (b *nodeBuilderImpl) typePredicateToTypePredicateNode(predicate *TypePredicate) *ast.Node { - var assertsModifier *ast.Node - if predicate.kind == TypePredicateKindAssertsIdentifier || predicate.kind == TypePredicateKindAssertsThis { - assertsModifier = b.f.NewToken(ast.KindAssertsKeyword) - } - var parameterName *ast.Node - if predicate.kind == TypePredicateKindIdentifier || predicate.kind == TypePredicateKindAssertsIdentifier { - parameterName = b.f.NewIdentifier(predicate.parameterName) - b.e.AddEmitFlags(parameterName, printer.EFNoAsciiEscaping) - } else { - parameterName = b.f.NewThisTypeNode() - } - var typeNode *ast.Node - if predicate.t != nil { - typeNode = b.typeToTypeNode(predicate.t) - } - return b.f.NewTypePredicateNode( - assertsModifier, - parameterName, - typeNode, - ) -} - -func (b *nodeBuilderImpl) typeToTypeNodeHelperWithPossibleReusableTypeNode(t *Type, typeNode *ast.TypeNode) *ast.TypeNode { - if t == nil { - return b.f.NewKeywordTypeNode(ast.KindAnyKeyword) - } - if typeNode != nil && b.getTypeFromTypeNode(typeNode, false) == t { - reused := b.tryReuseExistingTypeNodeHelper(typeNode) - if reused != nil { - return reused - } - } - return b.typeToTypeNode(t) -} - -func (b *nodeBuilderImpl) typeParameterToDeclaration(parameter *Type) *ast.Node { - constraint := b.ch.getConstraintOfTypeParameter(parameter) - var constraintNode *ast.Node - if constraint != nil { - constraintNode = b.typeToTypeNodeHelperWithPossibleReusableTypeNode(constraint, b.ch.getConstraintDeclaration(parameter)) - } - return b.typeParameterToDeclarationWithConstraint(parameter, constraintNode) -} - -func (b *nodeBuilderImpl) symbolToTypeParameterDeclarations(symbol *ast.Symbol) []*ast.Node { - return b.typeParametersToTypeParameterDeclarations(symbol) -} - -func (b *nodeBuilderImpl) typeParametersToTypeParameterDeclarations(symbol *ast.Symbol) []*ast.Node { - targetSymbol := b.ch.getTargetSymbol(symbol) - if targetSymbol.Flags&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface|ast.SymbolFlagsAlias) != 0 { - var results []*ast.Node - params := b.ch.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol) - for _, param := range params { - results = append(results, b.typeParameterToDeclaration(param)) - } - return results - } else if targetSymbol.Flags&ast.SymbolFlagsFunction != 0 { - var results []*ast.Node - for _, param := range b.ch.getTypeParametersFromDeclaration(symbol.ValueDeclaration) { - results = append(results, b.typeParameterToDeclaration(param)) - } - return results - } - return nil -} - -func getEffectiveParameterDeclaration(symbol *ast.Symbol) *ast.Node { - parameterDeclaration := ast.GetDeclarationOfKind(symbol, ast.KindParameter) - if parameterDeclaration != nil { - return parameterDeclaration - } - if symbol.Flags&ast.SymbolFlagsTransient == 0 { - return ast.GetDeclarationOfKind(symbol, ast.KindJSDocParameterTag) - } - return nil -} - -func (b *nodeBuilderImpl) symbolToParameterDeclaration(parameterSymbol *ast.Symbol, preserveModifierFlags bool) *ast.Node { - parameterDeclaration := getEffectiveParameterDeclaration(parameterSymbol) - - parameterType := b.ch.getTypeOfSymbol(parameterSymbol) - parameterTypeNode := b.serializeTypeForDeclaration(parameterDeclaration, parameterType, parameterSymbol) - var modifiers *ast.ModifierList - if b.ctx.flags&nodebuilder.FlagsOmitParameterModifiers == 0 && preserveModifierFlags && parameterDeclaration != nil && ast.CanHaveModifiers(parameterDeclaration) { - originals := core.Filter(parameterDeclaration.Modifiers().Nodes, ast.IsModifier) - clones := core.Map(originals, func(node *ast.Node) *ast.Node { return node.Clone(b.f) }) - if len(clones) > 0 { - modifiers = b.f.NewModifierList(clones) - } - } - isRest := parameterDeclaration != nil && isRestParameter(parameterDeclaration) || parameterSymbol.CheckFlags&ast.CheckFlagsRestParameter != 0 - var dotDotDotToken *ast.Node - if isRest { - dotDotDotToken = b.f.NewToken(ast.KindDotDotDotToken) - } - name := b.parameterToParameterDeclarationName(parameterSymbol, parameterDeclaration) - // TODO: isOptionalParameter on emit resolver here is silly - hoist to checker and reexpose on emit resolver? - isOptional := parameterDeclaration != nil && b.ch.GetEmitResolver().isOptionalParameter(parameterDeclaration) || parameterSymbol.CheckFlags&ast.CheckFlagsOptionalParameter != 0 - var questionToken *ast.Node - if isOptional { - questionToken = b.f.NewToken(ast.KindQuestionToken) - } - - parameterNode := b.f.NewParameterDeclaration( - modifiers, - dotDotDotToken, - name, - questionToken, - parameterTypeNode, - /*initializer*/ nil, - ) - b.ctx.approximateLength += len(parameterSymbol.Name) + 3 - return parameterNode -} - -func (b *nodeBuilderImpl) parameterToParameterDeclarationName(parameterSymbol *ast.Symbol, parameterDeclaration *ast.Node) *ast.Node { - if parameterDeclaration == nil || parameterDeclaration.Name() == nil { - return b.f.NewIdentifier(parameterSymbol.Name) - } - - name := parameterDeclaration.Name() - switch name.Kind { - case ast.KindIdentifier: - cloned := b.f.DeepCloneNode(name) - b.e.SetEmitFlags(cloned, printer.EFNoAsciiEscaping) - return cloned - case ast.KindQualifiedName: - cloned := b.f.DeepCloneNode(name.AsQualifiedName().Right) - b.e.SetEmitFlags(cloned, printer.EFNoAsciiEscaping) - return cloned - default: - return b.cloneBindingName(name) - } -} - -func (b *nodeBuilderImpl) cloneBindingName(node *ast.Node) *ast.Node { - if ast.IsComputedPropertyName(node) && b.ch.isLateBindableName(node) { - b.trackComputedName(node.Expression(), b.ctx.enclosingDeclaration) - } - - visited := b.cloneBindingNameVisitor.VisitEachChild(node) - - if ast.IsBindingElement(visited) { - bindingElement := visited.AsBindingElement() - visited = b.f.UpdateBindingElement( - bindingElement, - bindingElement.DotDotDotToken, - bindingElement.PropertyName, - bindingElement.Name(), - nil, // remove initializer - ) - } - - if !ast.NodeIsSynthesized(visited) { - visited = b.f.DeepCloneNode(visited) - } - - b.e.SetEmitFlags(visited, printer.EFSingleLine|printer.EFNoAsciiEscaping) - return visited -} - -func (b *nodeBuilderImpl) symbolTableToDeclarationStatements(symbolTable *ast.SymbolTable) []*ast.Node { - panic("unimplemented") // !!! -} - -func (b *nodeBuilderImpl) serializeTypeForExpression(expr *ast.Node) *ast.Node { - // !!! TODO: shim, add node reuse - t := b.ch.instantiateType(b.ch.getWidenedType(b.ch.getRegularTypeOfExpression(expr)), b.ctx.mapper) - return b.typeToTypeNode(t) -} - -func (b *nodeBuilderImpl) serializeInferredReturnTypeForSignature(signature *Signature, returnType *Type) *ast.Node { - oldSuppressReportInferenceFallback := b.ctx.suppressReportInferenceFallback - b.ctx.suppressReportInferenceFallback = true - typePredicate := b.ch.getTypePredicateOfSignature(signature) - var returnTypeNode *ast.Node - if typePredicate != nil { - var predicate *TypePredicate - if b.ctx.mapper != nil { - predicate = b.ch.instantiateTypePredicate(typePredicate, b.ctx.mapper) - } else { - predicate = typePredicate - } - returnTypeNode = b.typePredicateToTypePredicateNodeHelper(predicate) - } else { - returnTypeNode = b.typeToTypeNode(returnType) - } - b.ctx.suppressReportInferenceFallback = oldSuppressReportInferenceFallback - return returnTypeNode -} - -func (b *nodeBuilderImpl) typePredicateToTypePredicateNodeHelper(typePredicate *TypePredicate) *ast.Node { - var assertsModifier *ast.Node - if typePredicate.kind == TypePredicateKindAssertsThis || typePredicate.kind == TypePredicateKindAssertsIdentifier { - assertsModifier = b.f.NewToken(ast.KindAssertsKeyword) - } else { - assertsModifier = nil - } - var parameterName *ast.Node - if typePredicate.kind == TypePredicateKindIdentifier || typePredicate.kind == TypePredicateKindAssertsIdentifier { - parameterName = b.f.NewIdentifier(typePredicate.parameterName) - b.e.SetEmitFlags(parameterName, printer.EFNoAsciiEscaping) - } else { - parameterName = b.f.NewThisTypeNode() - } - var typeNode *ast.Node - if typePredicate.t != nil { - typeNode = b.typeToTypeNode(typePredicate.t) - } - return b.f.NewTypePredicateNode(assertsModifier, parameterName, typeNode) -} - -type SignatureToSignatureDeclarationOptions struct { - modifiers []*ast.Node - name *ast.PropertyName - questionToken *ast.Node -} - -func (b *nodeBuilderImpl) signatureToSignatureDeclarationHelper(signature *Signature, kind ast.Kind, options *SignatureToSignatureDeclarationOptions) *ast.Node { - var typeParameters []*ast.Node - - expandedParams := b.ch.getExpandedParameters(signature, true /*skipUnionExpanding*/)[0] - cleanup := b.enterNewScope(signature.declaration, expandedParams, signature.typeParameters, signature.parameters, signature.mapper) - b.ctx.approximateLength += 3 - // Usually a signature contributes a few more characters than this, but 3 is the minimum - - if b.ctx.flags&nodebuilder.FlagsWriteTypeArgumentsOfSignature != 0 && signature.target != nil && signature.mapper != nil && len(signature.target.typeParameters) != 0 { - for _, parameter := range signature.target.typeParameters { - typeParameters = append(typeParameters, b.typeToTypeNode(b.ch.instantiateType(parameter, signature.mapper))) - } - } else { - for _, parameter := range signature.typeParameters { - typeParameters = append(typeParameters, b.typeParameterToDeclaration(parameter)) - } - } - - restoreFlags := b.saveRestoreFlags() - b.ctx.flags &^= nodebuilder.FlagsSuppressAnyReturnType - // If the expanded parameter list had a variadic in a non-trailing position, don't expand it - parameters := core.Map(core.IfElse(core.Some(expandedParams, func(p *ast.Symbol) bool { - return p != expandedParams[len(expandedParams)-1] && p.CheckFlags&ast.CheckFlagsRestParameter != 0 - }), signature.parameters, expandedParams), func(parameter *ast.Symbol) *ast.Node { - return b.symbolToParameterDeclaration(parameter, kind == ast.KindConstructor) - }) - var thisParameter *ast.Node - if b.ctx.flags&nodebuilder.FlagsOmitThisParameter != 0 { - thisParameter = nil - } else { - thisParameter = b.tryGetThisParameterDeclaration(signature) - } - if thisParameter != nil { - parameters = append([]*ast.Node{thisParameter}, parameters...) - } - restoreFlags() - - returnTypeNode := b.serializeReturnTypeForSignature(signature) - - var modifiers []*ast.Node - if options != nil { - modifiers = options.modifiers - } - if (kind == ast.KindConstructorType) && signature.flags&SignatureFlagsAbstract != 0 { - flags := ast.ModifiersToFlags(modifiers) - modifiers = ast.CreateModifiersFromModifierFlags(flags|ast.ModifierFlagsAbstract, b.f.NewModifier) - } - - paramList := b.f.NewNodeList(parameters) - var typeParamList *ast.NodeList - if len(typeParameters) != 0 { - typeParamList = b.f.NewNodeList(typeParameters) - } - var modifierList *ast.ModifierList - if len(modifiers) > 0 { - modifierList = b.f.NewModifierList(modifiers) - } - var name *ast.Node - if options != nil { - name = options.name - } - if name == nil { - name = b.f.NewIdentifier("") - } - - var node *ast.Node - switch { - case kind == ast.KindCallSignature: - node = b.f.NewCallSignatureDeclaration(typeParamList, paramList, returnTypeNode) - case kind == ast.KindConstructSignature: - node = b.f.NewConstructSignatureDeclaration(typeParamList, paramList, returnTypeNode) - case kind == ast.KindMethodSignature: - var questionToken *ast.Node - if options != nil { - questionToken = options.questionToken - } - node = b.f.NewMethodSignatureDeclaration(modifierList, name, questionToken, typeParamList, paramList, returnTypeNode) - case kind == ast.KindMethodDeclaration: - node = b.f.NewMethodDeclaration(modifierList, nil /*asteriskToken*/, name, nil /*questionToken*/, typeParamList, paramList, returnTypeNode, nil /*fullSignature*/, nil /*body*/) - case kind == ast.KindConstructor: - node = b.f.NewConstructorDeclaration(modifierList, nil /*typeParamList*/, paramList, nil /*returnTypeNode*/, nil /*fullSignature*/, nil /*body*/) - case kind == ast.KindGetAccessor: - node = b.f.NewGetAccessorDeclaration(modifierList, name, nil /*typeParamList*/, paramList, returnTypeNode, nil /*fullSignature*/, nil /*body*/) - case kind == ast.KindSetAccessor: - node = b.f.NewSetAccessorDeclaration(modifierList, name, nil /*typeParamList*/, paramList, nil /*returnTypeNode*/, nil /*fullSignature*/, nil /*body*/) - case kind == ast.KindIndexSignature: - node = b.f.NewIndexSignatureDeclaration(modifierList, paramList, returnTypeNode) - // !!! JSDoc Support - // case kind == ast.KindJSDocFunctionType: - // node = b.f.NewJSDocFunctionType(parameters, returnTypeNode) - case kind == ast.KindFunctionType: - if returnTypeNode == nil { - returnTypeNode = b.f.NewTypeReferenceNode(b.f.NewIdentifier(""), nil) - } - node = b.f.NewFunctionTypeNode(typeParamList, paramList, returnTypeNode) - case kind == ast.KindConstructorType: - if returnTypeNode == nil { - returnTypeNode = b.f.NewTypeReferenceNode(b.f.NewIdentifier(""), nil) - } - node = b.f.NewConstructorTypeNode(modifierList, typeParamList, paramList, returnTypeNode) - case kind == ast.KindFunctionDeclaration: - // TODO: assert name is Identifier - node = b.f.NewFunctionDeclaration(modifierList, nil /*asteriskToken*/, name, typeParamList, paramList, returnTypeNode, nil /*fullSignature*/, nil /*body*/) - case kind == ast.KindFunctionExpression: - // TODO: assert name is Identifier - node = b.f.NewFunctionExpression(modifierList, nil /*asteriskToken*/, name, typeParamList, paramList, returnTypeNode, nil /*fullSignature*/, b.f.NewBlock(b.f.NewNodeList([]*ast.Node{}), false)) - case kind == ast.KindArrowFunction: - node = b.f.NewArrowFunction(modifierList, typeParamList, paramList, returnTypeNode, nil /*fullSignature*/, nil /*equalsGreaterThanToken*/, b.f.NewBlock(b.f.NewNodeList([]*ast.Node{}), false)) - default: - panic("Unhandled kind in signatureToSignatureDeclarationHelper") - } - - // !!! TODO: Smuggle type arguments of signatures out for quickinfo - // if typeArguments != nil { - // node.TypeArguments = b.f.NewNodeList(typeArguments) - // } - - cleanup() - return node -} - -func (c *Checker) getExpandedParameters(sig *Signature, skipUnionExpanding bool) [][]*ast.Symbol { - if signatureHasRestParameter(sig) { - restIndex := len(sig.parameters) - 1 - restSymbol := sig.parameters[restIndex] - restType := c.getTypeOfSymbol(restSymbol) - getUniqAssociatedNamesFromTupleType := func(t *Type, restSymbol *ast.Symbol) []string { - names := core.MapIndex(t.Target().AsTupleType().elementInfos, func(info TupleElementInfo, i int) string { - return c.getTupleElementLabel(info, restSymbol, i) - }) - if len(names) > 0 { - duplicates := []int{} - uniqueNames := make(map[string]bool) - for i, name := range names { - _, ok := uniqueNames[name] - if ok { - duplicates = append(duplicates, i) - } else { - uniqueNames[name] = true - } - } - counters := make(map[string]int) - for _, i := range duplicates { - counter, ok := counters[names[i]] - if !ok { - counter = 1 - } - var name string - for true { - name = fmt.Sprintf("%s_%d", names[i], counter) - _, ok := uniqueNames[name] - if ok { - counter++ - continue - } else { - uniqueNames[name] = true - break - } - } - names[i] = name - counters[names[i]] = counter + 1 - } - } - return names - } - expandSignatureParametersWithTupleMembers := func(restType *Type, restIndex int, restSymbol *ast.Symbol) []*ast.Symbol { - elementTypes := c.getTypeArguments(restType) - associatedNames := getUniqAssociatedNamesFromTupleType(restType, restSymbol) - restParams := core.MapIndex(elementTypes, func(t *Type, i int) *ast.Symbol { - // Lookup the label from the individual tuple passed in before falling back to the signature `rest` parameter name - // TODO: getTupleElementLabel can no longer fail, investigate if this lack of falliability meaningfully changes output - // var name *string - // if associatedNames != nil && associatedNames[i] != nil { - // name = associatedNames[i] - // } else { - // name = c.getParameterNameAtPosition(sig, restIndex+i, restType) - // } - name := associatedNames[i] - flags := restType.Target().AsTupleType().elementInfos[i].flags - var checkFlags ast.CheckFlags - switch { - case flags&ElementFlagsVariable != 0: - checkFlags = ast.CheckFlagsRestParameter - case flags&ElementFlagsOptional != 0: - checkFlags = ast.CheckFlagsOptionalParameter - } - symbol := c.newSymbolEx(ast.SymbolFlagsFunctionScopedVariable, name, checkFlags) - links := c.valueSymbolLinks.Get(symbol) - if flags&ElementFlagsRest != 0 { - links.resolvedType = c.createArrayType(t) - } else { - links.resolvedType = t - } - return symbol - }) - return core.Concatenate(sig.parameters[0:restIndex], restParams) - } - - if isTupleType(restType) { - return [][]*ast.Symbol{expandSignatureParametersWithTupleMembers(restType, restIndex, restSymbol)} - } else if !skipUnionExpanding && restType.flags&TypeFlagsUnion != 0 && core.Every(restType.AsUnionType().types, isTupleType) { - return core.Map(restType.AsUnionType().types, func(t *Type) []*ast.Symbol { - return expandSignatureParametersWithTupleMembers(t, restIndex, restSymbol) - }) - } - } - return [][]*ast.Symbol{sig.parameters} -} - -func (b *nodeBuilderImpl) tryGetThisParameterDeclaration(signature *Signature) *ast.Node { - if signature.thisParameter != nil { - return b.symbolToParameterDeclaration(signature.thisParameter, false) - } - if signature.declaration != nil && ast.IsInJSFile(signature.declaration) { - // !!! JSDoc Support - // thisTag := getJSDocThisTag(signature.declaration) - // if (thisTag && thisTag.typeExpression) { - // return factory.createParameterDeclaration( - // /*modifiers*/ undefined, - // /*dotDotDotToken*/ undefined, - // "this", - // /*questionToken*/ undefined, - // typeToTypeNodeHelper(getTypeFromTypeNode(context, thisTag.typeExpression), context), - // ); - // } - } - return nil -} - -/** -* Serializes the return type of the signature by first trying to use the syntactic printer if possible and falling back to the checker type if not. - */ -func (b *nodeBuilderImpl) serializeReturnTypeForSignature(signature *Signature) *ast.Node { - suppressAny := b.ctx.flags&nodebuilder.FlagsSuppressAnyReturnType != 0 - restoreFlags := b.saveRestoreFlags() - if suppressAny { - b.ctx.flags &= ^nodebuilder.FlagsSuppressAnyReturnType // suppress only toplevel `any`s - } - var returnTypeNode *ast.Node - - returnType := b.ch.getReturnTypeOfSignature(signature) - if !(suppressAny && IsTypeAny(returnType)) { - // !!! IsolatedDeclaration support - // if signature.declaration != nil && !ast.NodeIsSynthesized(signature.declaration) { - // declarationSymbol := b.ch.getSymbolOfDeclaration(signature.declaration) - // restore := addSymbolTypeToContext(declarationSymbol, returnType) - // returnTypeNode = syntacticNodeBuilder.serializeReturnTypeForSignature(signature.declaration, declarationSymbol) - // restore() - // } - if returnTypeNode == nil { - returnTypeNode = b.serializeInferredReturnTypeForSignature(signature, returnType) - } - } - - if returnTypeNode == nil && !suppressAny { - returnTypeNode = b.f.NewKeywordTypeNode(ast.KindAnyKeyword) - } - restoreFlags() - return returnTypeNode -} - -func (b *nodeBuilderImpl) indexInfoToIndexSignatureDeclarationHelper(indexInfo *IndexInfo, typeNode *ast.TypeNode) *ast.Node { - name := getNameFromIndexInfo(indexInfo) - indexerTypeNode := b.typeToTypeNode(indexInfo.keyType) - - indexingParameter := b.f.NewParameterDeclaration(nil, nil, b.f.NewIdentifier(name), nil, indexerTypeNode, nil) - if typeNode == nil { - if indexInfo.valueType == nil { - typeNode = b.f.NewKeywordTypeNode(ast.KindAnyKeyword) - } else { - typeNode = b.typeToTypeNode(indexInfo.valueType) - } - } - if indexInfo.valueType == nil && b.ctx.flags&nodebuilder.FlagsAllowEmptyIndexInfoType == 0 { - b.ctx.encounteredError = true - } - b.ctx.approximateLength += len(name) + 4 - var modifiers *ast.ModifierList - if indexInfo.isReadonly { - b.ctx.approximateLength += 9 - modifiers = b.f.NewModifierList([]*ast.Node{b.f.NewModifier(ast.KindReadonlyKeyword)}) - } - return b.f.NewIndexSignatureDeclaration(modifiers, b.f.NewNodeList([]*ast.Node{indexingParameter}), typeNode) -} - -/** -* Unlike `typeToTypeNodeHelper`, this handles setting up the `AllowUniqueESSymbolType` flag -* so a `unique symbol` is returned when appropriate for the input symbol, rather than `typeof sym` -* @param declaration - The preferred declaration to pull existing type nodes from (the symbol will be used as a fallback to find any annotated declaration) -* @param type - The type to write; an existing annotation must match this type if it's used, otherwise this is the type serialized as a new type node -* @param symbol - The symbol is used both to find an existing annotation if declaration is not provided, and to determine if `unique symbol` should be printed - */ -func (b *nodeBuilderImpl) serializeTypeForDeclaration(declaration *ast.Declaration, t *Type, symbol *ast.Symbol) *ast.Node { - // !!! node reuse logic - if symbol == nil { - symbol = b.ch.getSymbolOfDeclaration(declaration) - } - if t == nil { - t = b.ctx.enclosingSymbolTypes[ast.GetSymbolId(symbol)] - if t == nil { - if symbol.Flags&ast.SymbolFlagsAccessor != 0 && declaration.Kind == ast.KindSetAccessor { - t = b.ch.instantiateType(b.ch.getWriteTypeOfSymbol(symbol), b.ctx.mapper) - } else if symbol != nil && (symbol.Flags&(ast.SymbolFlagsTypeLiteral|ast.SymbolFlagsSignature) == 0) { - t = b.ch.instantiateType(b.ch.getWidenedLiteralType(b.ch.getTypeOfSymbol(symbol)), b.ctx.mapper) - } else { - t = b.ch.errorType - } - } - } - - // !!! TODO: JSDoc, getEmitResolver call is unfortunate layering for the helper - hoist it into checker - addUndefinedForParameter := declaration != nil && (ast.IsParameter(declaration) /*|| ast.IsJSDocParameterTag(declaration)*/) && b.ch.GetEmitResolver().requiresAddingImplicitUndefined(declaration, symbol, b.ctx.enclosingDeclaration) - if addUndefinedForParameter { - t = b.ch.getOptionalType(t, false) - } - - restoreFlags := b.saveRestoreFlags() - if t.flags&TypeFlagsUniqueESSymbol != 0 && t.symbol == symbol && (b.ctx.enclosingDeclaration == nil || core.Some(symbol.Declarations, func(d *ast.Declaration) bool { - return ast.GetSourceFileOfNode(d) == b.ctx.enclosingFile - })) { - b.ctx.flags |= nodebuilder.FlagsAllowUniqueESSymbolType - } - result := b.typeToTypeNode(t) // !!! expressionOrTypeToTypeNode - restoreFlags() - return result -} - -const MAX_REVERSE_MAPPED_NESTING_INSPECTION_DEPTH = 3 - -func (b *nodeBuilderImpl) shouldUsePlaceholderForProperty(propertySymbol *ast.Symbol) bool { - // Use placeholders for reverse mapped types we've either - // (1) already descended into, or - // (2) are nested reverse mappings within a mapping over a non-anonymous type, or - // (3) are deeply nested properties that originate from the same mapped type. - // Condition (2) is a restriction mostly just to - // reduce the blowup in printback size from doing, eg, a deep reverse mapping over `Window`. - // Since anonymous types usually come from expressions, this allows us to preserve the output - // for deep mappings which likely come from expressions, while truncating those parts which - // come from mappings over library functions. - // Condition (3) limits printing of possibly infinitely deep reverse mapped types. - if propertySymbol.CheckFlags&ast.CheckFlagsReverseMapped == 0 { - return false - } - // (1) - for _, elem := range b.ctx.reverseMappedStack { - if elem == propertySymbol { - return true - } - } - // (2) - if len(b.ctx.reverseMappedStack) > 0 { - last := b.ctx.reverseMappedStack[len(b.ctx.reverseMappedStack)-1] - if b.ch.ReverseMappedSymbolLinks.Has(last) { - links := b.ch.ReverseMappedSymbolLinks.TryGet(last) - propertyType := links.propertyType - if propertyType != nil && propertyType.objectFlags&ObjectFlagsAnonymous == 0 { - return true - } - } - } - // (3) - we only inspect the last MAX_REVERSE_MAPPED_NESTING_INSPECTION_DEPTH elements of the - // stack for approximate matches to catch tight infinite loops - // TODO: Why? Reasoning lost to time. this could probably stand to be improved? - if len(b.ctx.reverseMappedStack) < MAX_REVERSE_MAPPED_NESTING_INSPECTION_DEPTH { - return false - } - if !b.ch.ReverseMappedSymbolLinks.Has(propertySymbol) { - return false - } - propertyLinks := b.ch.ReverseMappedSymbolLinks.TryGet(propertySymbol) - propMappedType := propertyLinks.mappedType - if propMappedType == nil || propMappedType.symbol == nil { - return false - } - for i := range b.ctx.reverseMappedStack { - if i > MAX_REVERSE_MAPPED_NESTING_INSPECTION_DEPTH { - break - } - prop := b.ctx.reverseMappedStack[len(b.ctx.reverseMappedStack)-1-i] - if b.ch.ReverseMappedSymbolLinks.Has(prop) { - links := b.ch.ReverseMappedSymbolLinks.TryGet(prop) - mappedType := links.mappedType - if mappedType != nil && mappedType.symbol == propMappedType.symbol { - return true - } - } - } - return false -} - -func (b *nodeBuilderImpl) trackComputedName(accessExpression *ast.Node, enclosingDeclaration *ast.Node) { - // get symbol of the first identifier of the entityName - firstIdentifier := ast.GetFirstIdentifier(accessExpression) - name := b.ch.resolveName(firstIdentifier, firstIdentifier.Text(), ast.SymbolFlagsValue|ast.SymbolFlagsExportValue, nil /*nameNotFoundMessage*/, true /*isUse*/, false) - if name != nil { - b.ctx.tracker.TrackSymbol(name, enclosingDeclaration, ast.SymbolFlagsValue) - } -} - -func (b *nodeBuilderImpl) createPropertyNameNodeForIdentifierOrLiteral(name string, _singleQuote bool, stringNamed bool, isMethod bool) *ast.Node { - isMethodNamedNew := isMethod && name == "new" - if !isMethodNamedNew && scanner.IsIdentifierText(name, core.LanguageVariantStandard) { - return b.f.NewIdentifier(name) - } - if !stringNamed && !isMethodNamedNew && isNumericLiteralName(name) && jsnum.FromString(name) >= 0 { - return b.f.NewNumericLiteral(name) - } - result := b.f.NewStringLiteral(name) - // !!! TODO: set singleQuote - return result -} - -func (b *nodeBuilderImpl) isStringNamed(d *ast.Declaration) bool { - name := ast.GetNameOfDeclaration(d) - if name == nil { - return false - } - if ast.IsComputedPropertyName(name) { - t := b.ch.checkExpression(name.AsComputedPropertyName().Expression) - return t.flags&TypeFlagsStringLike != 0 - } - if ast.IsElementAccessExpression(name) { - t := b.ch.checkExpression(name.AsElementAccessExpression().ArgumentExpression) - return t.flags&TypeFlagsStringLike != 0 - } - return ast.IsStringLiteral(name) -} - -func (b *nodeBuilderImpl) isSingleQuotedStringNamed(d *ast.Declaration) bool { - return false // !!! - // TODO: actually support single-quote-style-maintenance - // name := ast.GetNameOfDeclaration(d) - // return name != nil && ast.IsStringLiteral(name) && (name.AsStringLiteral().SingleQuote || !nodeIsSynthesized(name) && startsWith(getTextOfNode(name, false /*includeTrivia*/), "'")) -} - -func (b *nodeBuilderImpl) getPropertyNameNodeForSymbol(symbol *ast.Symbol) *ast.Node { - stringNamed := len(symbol.Declarations) != 0 && core.Every(symbol.Declarations, b.isStringNamed) - singleQuote := len(symbol.Declarations) != 0 && core.Every(symbol.Declarations, b.isSingleQuotedStringNamed) - isMethod := symbol.Flags&ast.SymbolFlagsMethod != 0 - fromNameType := b.getPropertyNameNodeForSymbolFromNameType(symbol, singleQuote, stringNamed, isMethod) - if fromNameType != nil { - return fromNameType - } - - name := symbol.Name - const privateNamePrefix = ast.InternalSymbolNamePrefix + "#" - if strings.HasPrefix(name, privateNamePrefix) { - // symbol IDs are unstable - replace #nnn# with #private# - name = name[len(privateNamePrefix):] - name = strings.TrimLeftFunc(name, stringutil.IsDigit) - name = "__#private" + name - } - - return b.createPropertyNameNodeForIdentifierOrLiteral(name, singleQuote, stringNamed, isMethod) -} - -// See getNameForSymbolFromNameType for a stringy equivalent -func (b *nodeBuilderImpl) getPropertyNameNodeForSymbolFromNameType(symbol *ast.Symbol, singleQuote bool, stringNamed bool, isMethod bool) *ast.Node { - if !b.ch.valueSymbolLinks.Has(symbol) { - return nil - } - nameType := b.ch.valueSymbolLinks.TryGet(symbol).nameType - if nameType == nil { - return nil - } - if nameType.flags&TypeFlagsStringOrNumberLiteral != 0 { - var name string - switch nameType.AsLiteralType().value.(type) { - case jsnum.Number: - name = nameType.AsLiteralType().value.(jsnum.Number).String() - case string: - name = nameType.AsLiteralType().value.(string) - } - if !scanner.IsIdentifierText(name, core.LanguageVariantStandard) && (stringNamed || !isNumericLiteralName(name)) { - // !!! TODO: set singleQuote - return b.f.NewStringLiteral(name) - } - if isNumericLiteralName(name) && name[0] == '-' { - return b.f.NewComputedPropertyName(b.f.NewPrefixUnaryExpression(ast.KindMinusToken, b.f.NewNumericLiteral(name[1:]))) - } - return b.createPropertyNameNodeForIdentifierOrLiteral(name, singleQuote, stringNamed, isMethod) - } - if nameType.flags&TypeFlagsUniqueESSymbol != 0 { - return b.f.NewComputedPropertyName(b.symbolToExpression(nameType.AsUniqueESSymbolType().symbol, ast.SymbolFlagsValue)) - } - return nil -} - -func (b *nodeBuilderImpl) addPropertyToElementList(propertySymbol *ast.Symbol, typeElements []*ast.TypeElement) []*ast.TypeElement { - propertyIsReverseMapped := propertySymbol.CheckFlags&ast.CheckFlagsReverseMapped != 0 - var propertyType *Type - if b.shouldUsePlaceholderForProperty(propertySymbol) { - propertyType = b.ch.anyType - } else { - propertyType = b.ch.getNonMissingTypeOfSymbol(propertySymbol) - } - saveEnclosingDeclaration := b.ctx.enclosingDeclaration - b.ctx.enclosingDeclaration = nil - if isLateBoundName(propertySymbol.Name) { - if len(propertySymbol.Declarations) > 0 { - decl := propertySymbol.Declarations[0] - if b.ch.hasLateBindableName(decl) { - if ast.IsBinaryExpression(decl) { - name := ast.GetNameOfDeclaration(decl) - if name != nil && ast.IsElementAccessExpression(name) && ast.IsPropertyAccessEntityNameExpression(name.AsElementAccessExpression().ArgumentExpression, false /*allowJs*/) { - b.trackComputedName(name.AsElementAccessExpression().ArgumentExpression, saveEnclosingDeclaration) - } - } else { - b.trackComputedName(decl.Name().Expression(), saveEnclosingDeclaration) - } - } - } else { - b.ctx.tracker.ReportNonSerializableProperty(b.ch.symbolToString(propertySymbol)) - } - } - if propertySymbol.ValueDeclaration != nil { - b.ctx.enclosingDeclaration = propertySymbol.ValueDeclaration - } else if len(propertySymbol.Declarations) > 0 && propertySymbol.Declarations[0] != nil { - b.ctx.enclosingDeclaration = propertySymbol.Declarations[0] - } else { - b.ctx.enclosingDeclaration = saveEnclosingDeclaration - } - propertyName := b.getPropertyNameNodeForSymbol(propertySymbol) - b.ctx.enclosingDeclaration = saveEnclosingDeclaration - b.ctx.approximateLength += len(ast.SymbolName(propertySymbol)) + 1 - - if propertySymbol.Flags&ast.SymbolFlagsAccessor != 0 { - writeType := b.ch.getWriteTypeOfSymbol(propertySymbol) - if !b.ch.isErrorType(propertyType) && !b.ch.isErrorType(writeType) { - propDeclaration := ast.GetDeclarationOfKind(propertySymbol, ast.KindPropertyDeclaration) - if propertyType != writeType || propertySymbol.Parent.Flags&ast.SymbolFlagsClass != 0 && propDeclaration == nil { - if getterDeclaration := ast.GetDeclarationOfKind(propertySymbol, ast.KindGetAccessor); getterDeclaration != nil { - getterSignature := b.ch.getSignatureFromDeclaration(getterDeclaration) - getter := b.signatureToSignatureDeclarationHelper(getterSignature, ast.KindGetAccessor, &SignatureToSignatureDeclarationOptions{ - name: propertyName, - }) - b.setCommentRange(getter, getterDeclaration) - typeElements = append(typeElements, getter) - } - if setterDeclaration := ast.GetDeclarationOfKind(propertySymbol, ast.KindSetAccessor); setterDeclaration != nil { - setterSignature := b.ch.getSignatureFromDeclaration(setterDeclaration) - setter := b.signatureToSignatureDeclarationHelper(setterSignature, ast.KindSetAccessor, &SignatureToSignatureDeclarationOptions{ - name: propertyName, - }) - b.setCommentRange(setter, setterDeclaration) - typeElements = append(typeElements, setter) - } - return typeElements - } else if propertySymbol.Parent.Flags&ast.SymbolFlagsClass != 0 && propDeclaration != nil && core.Find(propDeclaration.ModifierNodes(), func(m *ast.Node) bool { - return m.Kind == ast.KindAccessorKeyword - }) != nil { - fakeGetterSignature := b.ch.newSignature(SignatureFlagsNone, nil, nil, nil, nil, propertyType, nil, 0) - fakeGetterDeclaration := b.signatureToSignatureDeclarationHelper(fakeGetterSignature, ast.KindGetAccessor, &SignatureToSignatureDeclarationOptions{ - name: propertyName, - }) - b.setCommentRange(fakeGetterDeclaration, propDeclaration) - typeElements = append(typeElements, fakeGetterDeclaration) - - setterParam := b.ch.newSymbol(ast.SymbolFlagsFunctionScopedVariable, "arg") - b.ch.valueSymbolLinks.Get(setterParam).resolvedType = writeType - fakeSetterSignature := b.ch.newSignature(SignatureFlagsNone, nil, nil, nil, []*ast.Symbol{setterParam}, b.ch.voidType, nil, 0) - fakeSetterDeclaration := b.signatureToSignatureDeclarationHelper(fakeSetterSignature, ast.KindSetAccessor, &SignatureToSignatureDeclarationOptions{ - name: propertyName, - }) - typeElements = append(typeElements, fakeSetterDeclaration) - return typeElements - } - } - } - - var optionalToken *ast.Node - if propertySymbol.Flags&ast.SymbolFlagsOptional != 0 { - optionalToken = b.f.NewToken(ast.KindQuestionToken) - } else { - optionalToken = nil - } - if propertySymbol.Flags&(ast.SymbolFlagsFunction|ast.SymbolFlagsMethod) != 0 && len(b.ch.getPropertiesOfObjectType(propertyType)) == 0 && !b.ch.isReadonlySymbol(propertySymbol) { - signatures := b.ch.getSignaturesOfType(b.ch.filterType(propertyType, func(t *Type) bool { - return t.flags&TypeFlagsUndefined == 0 - }), SignatureKindCall) - for _, signature := range signatures { - methodDeclaration := b.signatureToSignatureDeclarationHelper(signature, ast.KindMethodSignature, &SignatureToSignatureDeclarationOptions{ - name: propertyName, - questionToken: optionalToken, - }) - b.setCommentRange(methodDeclaration, core.Coalesce(signature.declaration, propertySymbol.ValueDeclaration)) - typeElements = append(typeElements, methodDeclaration) - } - if len(signatures) != 0 || optionalToken == nil { - return typeElements - } - } - var propertyTypeNode *ast.TypeNode - if b.shouldUsePlaceholderForProperty(propertySymbol) { - propertyTypeNode = b.createElidedInformationPlaceholder() - } else { - if propertyIsReverseMapped { - b.ctx.reverseMappedStack = append(b.ctx.reverseMappedStack, propertySymbol) - } - if propertyType != nil { - propertyTypeNode = b.serializeTypeForDeclaration(nil /*declaration*/, propertyType, propertySymbol) - } else { - propertyTypeNode = b.f.NewKeywordTypeNode(ast.KindAnyKeyword) - } - if propertyIsReverseMapped { - b.ctx.reverseMappedStack = b.ctx.reverseMappedStack[:len(b.ctx.reverseMappedStack)-1] - } - } - - var modifiers *ast.ModifierList - if b.ch.isReadonlySymbol(propertySymbol) { - modifiers = b.f.NewModifierList([]*ast.Node{b.f.NewModifier(ast.KindReadonlyKeyword)}) - b.ctx.approximateLength += 9 - } - propertySignature := b.f.NewPropertySignatureDeclaration(modifiers, propertyName, optionalToken, propertyTypeNode, nil) - - b.setCommentRange(propertySignature, propertySymbol.ValueDeclaration) - typeElements = append(typeElements, propertySignature) - - return typeElements -} - -func (b *nodeBuilderImpl) createTypeNodesFromResolvedType(resolvedType *StructuredType) *ast.NodeList { - if b.checkTruncationLength() { - if b.ctx.flags&nodebuilder.FlagsNoTruncation != 0 { - elem := b.f.NewNotEmittedTypeElement() - return b.f.NewNodeList([]*ast.TypeElement{b.e.AddSyntheticLeadingComment(elem, ast.KindMultiLineCommentTrivia, "elided", false /*hasTrailingNewLine*/)}) - } - return b.f.NewNodeList([]*ast.Node{b.f.NewPropertySignatureDeclaration(nil, b.f.NewIdentifier("..."), nil, nil, nil)}) - } - var typeElements []*ast.TypeElement - for _, signature := range resolvedType.CallSignatures() { - typeElements = append(typeElements, b.signatureToSignatureDeclarationHelper(signature, ast.KindCallSignature, nil)) - } - for _, signature := range resolvedType.ConstructSignatures() { - if signature.flags&SignatureFlagsAbstract != 0 { - continue - } - typeElements = append(typeElements, b.signatureToSignatureDeclarationHelper(signature, ast.KindConstructSignature, nil)) - } - for _, info := range resolvedType.indexInfos { - typeElements = append(typeElements, b.indexInfoToIndexSignatureDeclarationHelper(info, core.IfElse(resolvedType.objectFlags&ObjectFlagsReverseMapped != 0, b.createElidedInformationPlaceholder(), nil))) - } - - properties := resolvedType.properties - if len(properties) == 0 { - return b.f.NewNodeList(typeElements) - } - - i := 0 - for _, propertySymbol := range properties { - i++ - if b.ctx.flags&nodebuilder.FlagsWriteClassExpressionAsTypeLiteral != 0 { - if propertySymbol.Flags&ast.SymbolFlagsPrototype != 0 { - continue - } - if getDeclarationModifierFlagsFromSymbol(propertySymbol)&(ast.ModifierFlagsPrivate|ast.ModifierFlagsProtected) != 0 { - b.ctx.tracker.ReportPrivateInBaseOfClassExpression(propertySymbol.Name) - } - } - if b.checkTruncationLength() && (i+2 < len(properties)-1) { - if b.ctx.flags&nodebuilder.FlagsNoTruncation != 0 { - typeElements[len(typeElements)-1] = b.e.AddSyntheticLeadingComment(typeElements[len(typeElements)-1], ast.KindMultiLineCommentTrivia, fmt.Sprintf("... %d more elided ...", len(properties)-i), false /*hasTrailingNewLine*/) - } else { - text := fmt.Sprintf("... %d more ...", len(properties)-i) - typeElements = append(typeElements, b.f.NewPropertySignatureDeclaration(nil, b.f.NewIdentifier(text), nil, nil, nil)) - } - typeElements = b.addPropertyToElementList(properties[len(properties)-1], typeElements) - break - } - typeElements = b.addPropertyToElementList(propertySymbol, typeElements) - } - if len(typeElements) != 0 { - return b.f.NewNodeList(typeElements) - } else { - return nil - } -} - -func (b *nodeBuilderImpl) createTypeNodeFromObjectType(t *Type) *ast.TypeNode { - if b.ch.isGenericMappedType(t) || (t.objectFlags&ObjectFlagsMapped != 0 && t.AsMappedType().containsError) { - return b.createMappedTypeNodeFromType(t) - } - - resolved := b.ch.resolveStructuredTypeMembers(t) - callSigs := resolved.CallSignatures() - ctorSigs := resolved.ConstructSignatures() - if len(resolved.properties) == 0 && len(resolved.indexInfos) == 0 { - if len(callSigs) == 0 && len(ctorSigs) == 0 { - b.ctx.approximateLength += 2 - result := b.f.NewTypeLiteralNode(b.f.NewNodeList([]*ast.Node{})) - b.e.SetEmitFlags(result, printer.EFSingleLine) - return result - } - - if len(callSigs) == 1 && len(ctorSigs) == 0 { - signature := callSigs[0] - signatureNode := b.signatureToSignatureDeclarationHelper(signature, ast.KindFunctionType, nil) - return signatureNode - } - - if len(ctorSigs) == 1 && len(callSigs) == 0 { - signature := ctorSigs[0] - signatureNode := b.signatureToSignatureDeclarationHelper(signature, ast.KindConstructorType, nil) - return signatureNode - } - } - - abstractSignatures := core.Filter(ctorSigs, func(signature *Signature) bool { - return signature.flags&SignatureFlagsAbstract != 0 - }) - if len(abstractSignatures) > 0 { - types := core.Map(abstractSignatures, func(s *Signature) *Type { - return b.ch.getOrCreateTypeFromSignature(s) - }) - // count the number of type elements excluding abstract constructors - typeElementCount := len(callSigs) + (len(ctorSigs) - len(abstractSignatures)) + len(resolved.indexInfos) + (core.IfElse(b.ctx.flags&nodebuilder.FlagsWriteClassExpressionAsTypeLiteral != 0, core.CountWhere(resolved.properties, func(p *ast.Symbol) bool { - return p.Flags&ast.SymbolFlagsPrototype == 0 - }), len(resolved.properties))) - // don't include an empty object literal if there were no other static-side - // properties to write, i.e. `abstract class C { }` becomes `abstract new () => {}` - // and not `(abstract new () => {}) & {}` - if typeElementCount != 0 { - // create a copy of the object type without any abstract construct signatures. - types = append(types, b.getResolvedTypeWithoutAbstractConstructSignatures(resolved)) - } - return b.typeToTypeNode(b.ch.getIntersectionType(types)) - } - - restoreFlags := b.saveRestoreFlags() - b.ctx.flags |= nodebuilder.FlagsInObjectTypeLiteral - members := b.createTypeNodesFromResolvedType(resolved) - restoreFlags() - typeLiteralNode := b.f.NewTypeLiteralNode(members) - b.ctx.approximateLength += 2 - b.e.SetEmitFlags(typeLiteralNode, core.IfElse((b.ctx.flags&nodebuilder.FlagsMultilineObjectLiterals != 0), 0, printer.EFSingleLine)) - return typeLiteralNode -} - -func getTypeAliasForTypeLiteral(c *Checker, t *Type) *ast.Symbol { - if t.symbol != nil && t.symbol.Flags&ast.SymbolFlagsTypeLiteral != 0 && t.symbol.Declarations != nil { - node := ast.WalkUpParenthesizedTypes(t.symbol.Declarations[0].Parent) - if ast.IsTypeAliasDeclaration(node) { - return c.getSymbolOfDeclaration(node) - } - } - return nil -} - -func (b *nodeBuilderImpl) shouldWriteTypeOfFunctionSymbol(symbol *ast.Symbol, typeId TypeId) bool { - isStaticMethodSymbol := symbol.Flags&ast.SymbolFlagsMethod != 0 && core.Some(symbol.Declarations, func(declaration *ast.Node) bool { - return ast.IsStatic(declaration) && !b.ch.isLateBindableIndexSignature(ast.GetNameOfDeclaration(declaration)) - }) - isNonLocalFunctionSymbol := false - if symbol.Flags&ast.SymbolFlagsFunction != 0 { - if symbol.Parent != nil { - isNonLocalFunctionSymbol = true - } else { - for _, declaration := range symbol.Declarations { - if declaration.Parent.Kind == ast.KindSourceFile || declaration.Parent.Kind == ast.KindModuleBlock { - isNonLocalFunctionSymbol = true - break - } - } - } - } - if isStaticMethodSymbol || isNonLocalFunctionSymbol { - // typeof is allowed only for static/non local functions - return (b.ctx.flags&nodebuilder.FlagsUseTypeOfFunction != 0 || b.ctx.visitedTypes.Has(typeId)) && // it is type of the symbol uses itself recursively - (b.ctx.flags&nodebuilder.FlagsUseStructuralFallback == 0 || b.ch.IsValueSymbolAccessible(symbol, b.ctx.enclosingDeclaration)) // And the build is going to succeed without visibility error or there is no structural fallback allowed - } - return false -} - -func (b *nodeBuilderImpl) createAnonymousTypeNode(t *Type) *ast.TypeNode { - typeId := t.id - symbol := t.symbol - if symbol != nil { - isInstantiationExpressionType := t.objectFlags&ObjectFlagsInstantiationExpressionType != 0 - if isInstantiationExpressionType { - instantiationExpressionType := t.AsInstantiationExpressionType() - existing := instantiationExpressionType.node - if ast.IsTypeQueryNode(existing) { - typeNode := b.tryReuseExistingNonParameterTypeNode(existing, t, nil, nil) - if typeNode != nil { - return typeNode - } - } - if b.ctx.visitedTypes.Has(typeId) { - return b.createElidedInformationPlaceholder() - } - return b.visitAndTransformType(t, (*nodeBuilderImpl).createTypeNodeFromObjectType) - } - var isInstanceType ast.SymbolFlags - if isClassInstanceSide(b.ch, t) { - isInstanceType = ast.SymbolFlagsType - } else { - isInstanceType = ast.SymbolFlagsValue - } - - // !!! JS support - // if c.isJSConstructor(symbol.ValueDeclaration) { - // // Instance and static types share the same symbol; only add 'typeof' for the static side. - // return b.symbolToTypeNode(symbol, isInstanceType, nil) - // } else - if symbol.Flags&ast.SymbolFlagsClass != 0 && b.ch.getBaseTypeVariableOfClass(symbol) == nil && !(symbol.ValueDeclaration != nil && ast.IsClassLike(symbol.ValueDeclaration) && b.ctx.flags&nodebuilder.FlagsWriteClassExpressionAsTypeLiteral != 0 && (!ast.IsClassDeclaration(symbol.ValueDeclaration) || b.ch.IsSymbolAccessible(symbol, b.ctx.enclosingDeclaration, isInstanceType, false /*shouldComputeAliasesToMakeVisible*/).Accessibility != printer.SymbolAccessibilityAccessible)) || symbol.Flags&(ast.SymbolFlagsEnum|ast.SymbolFlagsValueModule) != 0 || b.shouldWriteTypeOfFunctionSymbol(symbol, typeId) { - return b.symbolToTypeNode(symbol, isInstanceType, nil) - } else if b.ctx.visitedTypes.Has(typeId) { - // If type is an anonymous type literal in a type alias declaration, use type alias name - typeAlias := getTypeAliasForTypeLiteral(b.ch, t) - if typeAlias != nil { - // The specified symbol flags need to be reinterpreted as type flags - return b.symbolToTypeNode(typeAlias, ast.SymbolFlagsType, nil) - } else { - return b.createElidedInformationPlaceholder() - } - } else { - return b.visitAndTransformType(t, (*nodeBuilderImpl).createTypeNodeFromObjectType) - } - } else { - // Anonymous types without a symbol are never circular. - return b.createTypeNodeFromObjectType(t) - } -} - -func (b *nodeBuilderImpl) getTypeFromTypeNode(node *ast.TypeNode, noMappedTypes bool) *Type { - // !!! noMappedTypes optional param support - t := b.ch.getTypeFromTypeNode(node) - if b.ctx.mapper == nil { - return t - } - - instantiated := b.ch.instantiateType(t, b.ctx.mapper) - if noMappedTypes && instantiated != t { - return nil - } - return instantiated -} - -func (b *nodeBuilderImpl) typeToTypeNodeOrCircularityElision(t *Type) *ast.TypeNode { - if t.flags&TypeFlagsUnion != 0 { - if b.ctx.visitedTypes.Has(t.id) { - if b.ctx.flags&nodebuilder.FlagsAllowAnonymousIdentifier == 0 { - b.ctx.encounteredError = true - b.ctx.tracker.ReportCyclicStructureError() - } - return b.createElidedInformationPlaceholder() - } - return b.visitAndTransformType(t, (*nodeBuilderImpl).typeToTypeNode) - } - return b.typeToTypeNode(t) -} - -func (b *nodeBuilderImpl) conditionalTypeToTypeNode(_t *Type) *ast.TypeNode { - t := _t.AsConditionalType() - checkTypeNode := b.typeToTypeNode(t.checkType) - b.ctx.approximateLength += 15 - if b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 && t.root.isDistributive && t.checkType.flags&TypeFlagsTypeParameter == 0 { - newParam := b.ch.newTypeParameter(b.ch.newSymbol(ast.SymbolFlagsTypeParameter, "T" /* as __String */)) - name := b.typeParameterToName(newParam) - newTypeVariable := b.f.NewTypeReferenceNode(name.AsNode(), nil) - b.ctx.approximateLength += 37 - // 15 each for two added conditionals, 7 for an added infer type - newMapper := prependTypeMapping(t.root.checkType, newParam, t.mapper) - saveInferTypeParameters := b.ctx.inferTypeParameters - b.ctx.inferTypeParameters = t.root.inferTypeParameters - extendsTypeNode := b.typeToTypeNode(b.ch.instantiateType(t.root.extendsType, newMapper)) - b.ctx.inferTypeParameters = saveInferTypeParameters - trueTypeNode := b.typeToTypeNodeOrCircularityElision(b.ch.instantiateType(b.getTypeFromTypeNode(t.root.node.TrueType, false), newMapper)) - falseTypeNode := b.typeToTypeNodeOrCircularityElision(b.ch.instantiateType(b.getTypeFromTypeNode(t.root.node.FalseType, false), newMapper)) - - // outermost conditional makes `T` a type parameter, allowing the inner conditionals to be distributive - // second conditional makes `T` have `T & checkType` substitution, so it is correctly usable as the checkType - // inner conditional runs the check the user provided on the check type (distributively) and returns the result - // checkType extends infer T ? T extends checkType ? T extends extendsType ? trueType : falseType : never : never; - // this is potentially simplifiable to - // checkType extends infer T ? T extends checkType & extendsType ? trueType : falseType : never; - // but that may confuse users who read the output more. - // On the other hand, - // checkType extends infer T extends checkType ? T extends extendsType ? trueType : falseType : never; - // may also work with `infer ... extends ...` in, but would produce declarations only compatible with the latest TS. - newId := newTypeVariable.AsTypeReferenceNode().TypeName.AsIdentifier().Clone(b.f) - syntheticExtendsNode := b.f.NewInferTypeNode(b.f.NewTypeParameterDeclaration(nil, newId, nil, nil)) - innerCheckConditionalNode := b.f.NewConditionalTypeNode(newTypeVariable, extendsTypeNode, trueTypeNode, falseTypeNode) - syntheticTrueNode := b.f.NewConditionalTypeNode(b.f.NewTypeReferenceNode(name.Clone(b.f), nil), b.f.DeepCloneNode(checkTypeNode), innerCheckConditionalNode, b.f.NewKeywordTypeNode(ast.KindNeverKeyword)) - return b.f.NewConditionalTypeNode(checkTypeNode, syntheticExtendsNode, syntheticTrueNode, b.f.NewKeywordTypeNode(ast.KindNeverKeyword)) - } - saveInferTypeParameters := b.ctx.inferTypeParameters - b.ctx.inferTypeParameters = t.root.inferTypeParameters - extendsTypeNode := b.typeToTypeNode(t.extendsType) - b.ctx.inferTypeParameters = saveInferTypeParameters - trueTypeNode := b.typeToTypeNodeOrCircularityElision(b.ch.getTrueTypeFromConditionalType(_t)) - falseTypeNode := b.typeToTypeNodeOrCircularityElision(b.ch.getFalseTypeFromConditionalType(_t)) - return b.f.NewConditionalTypeNode(checkTypeNode, extendsTypeNode, trueTypeNode, falseTypeNode) -} - -func (b *nodeBuilderImpl) getParentSymbolOfTypeParameter(typeParameter *TypeParameter) *ast.Symbol { - tp := ast.GetDeclarationOfKind(typeParameter.symbol, ast.KindTypeParameter) - var host *ast.Node - // !!! JSDoc support - // if ast.IsJSDocTemplateTag(tp.Parent) { - // host = getEffectiveContainerForJSDocTemplateTag(tp.Parent) - // } else { - host = tp.Parent - // } - if host == nil { - return nil - } - return b.ch.getSymbolOfNode(host) -} - -func (b *nodeBuilderImpl) typeReferenceToTypeNode(t *Type) *ast.TypeNode { - var typeArguments []*Type = b.ch.getTypeArguments(t) - if t.Target() == b.ch.globalArrayType || t.Target() == b.ch.globalReadonlyArrayType { - if b.ctx.flags&nodebuilder.FlagsWriteArrayAsGenericType != 0 { - typeArgumentNode := b.typeToTypeNode(typeArguments[0]) - return b.f.NewTypeReferenceNode(b.f.NewIdentifier(core.IfElse(t.Target() == b.ch.globalArrayType, "Array", "ReadonlyArray")), b.f.NewNodeList([]*ast.TypeNode{typeArgumentNode})) - } - elementType := b.typeToTypeNode(typeArguments[0]) - arrayType := b.f.NewArrayTypeNode(elementType) - if t.Target() == b.ch.globalArrayType { - return arrayType - } else { - return b.f.NewTypeOperatorNode(ast.KindReadonlyKeyword, arrayType) - } - } else if t.Target().objectFlags&ObjectFlagsTuple != 0 { - typeArguments = core.SameMapIndex(typeArguments, func(arg *Type, i int) *Type { - isOptional := false - if i < len(t.Target().AsTupleType().elementInfos) { - isOptional = t.Target().AsTupleType().elementInfos[i].flags&ElementFlagsOptional != 0 - } - return b.ch.removeMissingType(arg, isOptional) - }) - if len(typeArguments) > 0 { - arity := b.ch.getTypeReferenceArity(t) - tupleConstituentNodes := b.mapToTypeNodes(typeArguments[0:arity], false /*isBareList*/) - if tupleConstituentNodes != nil { - for i := 0; i < len(tupleConstituentNodes.Nodes); i++ { - flags := t.Target().AsTupleType().elementInfos[i].flags - labeledElementDeclaration := t.Target().AsTupleType().elementInfos[i].labeledDeclaration - - if labeledElementDeclaration != nil { - tupleConstituentNodes.Nodes[i] = b.f.NewNamedTupleMember(core.IfElse(flags&ElementFlagsVariable != 0, b.f.NewToken(ast.KindDotDotDotToken), nil), b.f.NewIdentifier(b.ch.getTupleElementLabel(t.Target().AsTupleType().elementInfos[i], nil, i)), core.IfElse(flags&ElementFlagsOptional != 0, b.f.NewToken(ast.KindQuestionToken), nil), core.IfElse(flags&ElementFlagsRest != 0, b.f.NewArrayTypeNode(tupleConstituentNodes.Nodes[i]), tupleConstituentNodes.Nodes[i])) - } else { - switch { - case flags&ElementFlagsVariable != 0: - tupleConstituentNodes.Nodes[i] = b.f.NewRestTypeNode(core.IfElse(flags&ElementFlagsRest != 0, b.f.NewArrayTypeNode(tupleConstituentNodes.Nodes[i]), tupleConstituentNodes.Nodes[i])) - case flags&ElementFlagsOptional != 0: - tupleConstituentNodes.Nodes[i] = b.f.NewOptionalTypeNode(tupleConstituentNodes.Nodes[i]) - } - } - } - tupleTypeNode := b.f.NewTupleTypeNode(tupleConstituentNodes) - b.e.SetEmitFlags(tupleTypeNode, printer.EFSingleLine) - if t.Target().AsTupleType().readonly { - return b.f.NewTypeOperatorNode(ast.KindReadonlyKeyword, tupleTypeNode) - } else { - return tupleTypeNode - } - } - } - if b.ctx.encounteredError || (b.ctx.flags&nodebuilder.FlagsAllowEmptyTuple != 0) { - tupleTypeNode := b.f.NewTupleTypeNode(b.f.NewNodeList([]*ast.TypeNode{})) - b.e.SetEmitFlags(tupleTypeNode, printer.EFSingleLine) - if t.Target().AsTupleType().readonly { - return b.f.NewTypeOperatorNode(ast.KindReadonlyKeyword, tupleTypeNode) - } else { - return tupleTypeNode - } - } - b.ctx.encounteredError = true - return nil - // TODO: GH#18217 - } else if b.ctx.flags&nodebuilder.FlagsWriteClassExpressionAsTypeLiteral != 0 && t.symbol.ValueDeclaration != nil && ast.IsClassLike(t.symbol.ValueDeclaration) && !b.ch.IsValueSymbolAccessible(t.symbol, b.ctx.enclosingDeclaration) { - return b.createAnonymousTypeNode(t) - } else { - outerTypeParameters := t.Target().AsInterfaceType().OuterTypeParameters() - i := 0 - var resultType *ast.TypeNode - if outerTypeParameters != nil { - length := len(outerTypeParameters) - for i < length { - // Find group of type arguments for type parameters with the same declaring container. - start := i - parent := b.getParentSymbolOfTypeParameter(outerTypeParameters[i].AsTypeParameter()) - for ok := true; ok; ok = i < length && b.getParentSymbolOfTypeParameter(outerTypeParameters[i].AsTypeParameter()) == parent { // do-while loop - i++ - } - // When type parameters are their own type arguments for the whole group (i.e. we have - // the default outer type arguments), we don't show the group. - - if !slices.Equal(outerTypeParameters[start:i], typeArguments[start:i]) { - typeArgumentSlice := b.mapToTypeNodes(typeArguments[start:i], false /*isBareList*/) - restoreFlags := b.saveRestoreFlags() - b.ctx.flags |= nodebuilder.FlagsForbidIndexedAccessSymbolReferences - ref := b.symbolToTypeNode(parent, ast.SymbolFlagsType, typeArgumentSlice) - restoreFlags() - if resultType == nil { - resultType = ref - } else { - resultType = b.appendReferenceToType(resultType, ref) - } - } - } - } - var typeArgumentNodes *ast.NodeList - if len(typeArguments) > 0 { - typeParameterCount := 0 - typeParams := t.Target().AsInterfaceType().TypeParameters() - if typeParams != nil { - typeParameterCount = min(len(typeParams), len(typeArguments)) - - // Maybe we should do this for more types, but for now we only elide type arguments that are - // identical to their associated type parameters' defaults for `Iterable`, `IterableIterator`, - // `AsyncIterable`, and `AsyncIterableIterator` to provide backwards-compatible .d.ts emit due - // to each now having three type parameters instead of only one. - if b.ch.isReferenceToType(t, b.ch.getGlobalIterableType()) || b.ch.isReferenceToType(t, b.ch.getGlobalIterableIteratorType()) || b.ch.isReferenceToType(t, b.ch.getGlobalAsyncIterableType()) || b.ch.isReferenceToType(t, b.ch.getGlobalAsyncIterableIteratorType()) { - if t.AsTypeReference().node == nil || !ast.IsTypeReferenceNode(t.AsTypeReference().node) || t.AsTypeReference().node.TypeArguments() == nil || len(t.AsTypeReference().node.TypeArguments()) < typeParameterCount { - for typeParameterCount > 0 { - typeArgument := typeArguments[typeParameterCount-1] - typeParameter := t.Target().AsInterfaceType().TypeParameters()[typeParameterCount-1] - defaultType := b.ch.getDefaultFromTypeParameter(typeParameter) - if defaultType == nil || !b.ch.isTypeIdenticalTo(typeArgument, defaultType) { - break - } - typeParameterCount-- - } - } - } - } - - typeArgumentNodes = b.mapToTypeNodes(typeArguments[i:typeParameterCount], false /*isBareList*/) - } - restoreFlags := b.saveRestoreFlags() - b.ctx.flags |= nodebuilder.FlagsForbidIndexedAccessSymbolReferences - finalRef := b.symbolToTypeNode(t.symbol, ast.SymbolFlagsType, typeArgumentNodes) - restoreFlags() - if resultType == nil { - return finalRef - } else { - return b.appendReferenceToType(resultType, finalRef) - } - } -} - -func (b *nodeBuilderImpl) visitAndTransformType(t *Type, transform func(b *nodeBuilderImpl, t *Type) *ast.TypeNode) *ast.TypeNode { - typeId := t.id - isConstructorObject := t.objectFlags&ObjectFlagsAnonymous != 0 && t.symbol != nil && t.symbol.Flags&ast.SymbolFlagsClass != 0 - var id *CompositeSymbolIdentity - switch { - case t.objectFlags&ObjectFlagsReference != 0 && t.AsTypeReference().node != nil: - id = &CompositeSymbolIdentity{false, 0, ast.GetNodeId(t.AsTypeReference().node)} - case t.flags&TypeFlagsConditional != 0: - id = &CompositeSymbolIdentity{false, 0, ast.GetNodeId(t.AsConditionalType().root.node.AsNode())} - case t.symbol != nil: - id = &CompositeSymbolIdentity{isConstructorObject, ast.GetSymbolId(t.symbol), 0} - default: - id = nil - } - // Since instantiations of the same anonymous type have the same symbol, tracking symbols instead - // of types allows us to catch circular references to instantiations of the same anonymous type - - key := CompositeTypeCacheIdentity{typeId, b.ctx.flags, b.ctx.internalFlags} - if b.ctx.enclosingDeclaration != nil && b.links.Has(b.ctx.enclosingDeclaration) { - links := b.links.Get(b.ctx.enclosingDeclaration) - cachedResult, ok := links.serializedTypes[key] - if ok { - // TODO:: check if we instead store late painted statements associated with this? - for _, arg := range cachedResult.trackedSymbols { - b.ctx.tracker.TrackSymbol(arg.symbol, arg.enclosingDeclaration, arg.meaning) - } - if cachedResult.truncating { - b.ctx.truncating = true - } - b.ctx.approximateLength += cachedResult.addedLength - return b.f.DeepCloneNode(cachedResult.node) - } - } - - var depth int - if id != nil { - depth = b.ctx.symbolDepth[*id] - if depth > 10 { - return b.createElidedInformationPlaceholder() - } - b.ctx.symbolDepth[*id] = depth + 1 - } - b.ctx.visitedTypes.Add(typeId) - prevTrackedSymbols := b.ctx.trackedSymbols - b.ctx.trackedSymbols = nil - startLength := b.ctx.approximateLength - result := transform(b, t) - addedLength := b.ctx.approximateLength - startLength - if !b.ctx.reportedDiagnostic && !b.ctx.encounteredError { - links := b.links.Get(b.ctx.enclosingDeclaration) - if links.serializedTypes == nil { - links.serializedTypes = make(map[CompositeTypeCacheIdentity]*SerializedTypeEntry) - } - links.serializedTypes[key] = &SerializedTypeEntry{ - node: result, - truncating: b.ctx.truncating, - addedLength: addedLength, - trackedSymbols: b.ctx.trackedSymbols, - } - } - b.ctx.visitedTypes.Delete(typeId) - if id != nil { - b.ctx.symbolDepth[*id] = depth - } - b.ctx.trackedSymbols = prevTrackedSymbols - return result - - // !!! TODO: Attempt node reuse or parse nodes to minimize copying once text range setting is set up - // deepCloneOrReuseNode := func(node T) T { - // if !nodeIsSynthesized(node) && getParseTreeNode(node) == node { - // return node - // } - // return setTextRange(b.ctx, b.f.cloneNode(visitEachChildWorker(node, deepCloneOrReuseNode, nil /*b.ctx*/, deepCloneOrReuseNodes, deepCloneOrReuseNode)), node) - // } - - // deepCloneOrReuseNodes := func(nodes *NodeArray[*ast.Node], visitor Visitor, test func(node *ast.Node) bool, start number, count number) *NodeArray[*ast.Node] { - // if nodes != nil && nodes.length == 0 { - // // Ensure we explicitly make a copy of an empty array; visitNodes will not do this unless the array has elements, - // // which can lead to us reusing the same empty NodeArray more than once within the same AST during type noding. - // return setTextRangeWorker(b.f.NewNodeArray(nil, nodes.hasTrailingComma), nodes) - // } - // return visitNodes(nodes, visitor, test, start, count) - // } -} - -func (b *nodeBuilderImpl) typeToTypeNode(t *Type) *ast.TypeNode { - inTypeAlias := b.ctx.flags & nodebuilder.FlagsInTypeAlias - b.ctx.flags &^= nodebuilder.FlagsInTypeAlias - - if t == nil { - if b.ctx.flags&nodebuilder.FlagsAllowEmptyUnionOrIntersection == 0 { - b.ctx.encounteredError = true - return nil - // TODO: GH#18217 - } - b.ctx.approximateLength += 3 - return b.f.NewKeywordTypeNode(ast.KindAnyKeyword) - } - - if b.ctx.flags&nodebuilder.FlagsNoTypeReduction == 0 { - t = b.ch.getReducedType(t) - } - - if t.flags&TypeFlagsAny != 0 { - if t.alias != nil { - return t.alias.ToTypeReferenceNode(b) - } - if t == b.ch.unresolvedType { - return b.e.AddSyntheticLeadingComment(b.f.NewKeywordTypeNode(ast.KindAnyKeyword), ast.KindMultiLineCommentTrivia, "unresolved", false /*hasTrailingNewLine*/) - } - b.ctx.approximateLength += 3 - return b.f.NewKeywordTypeNode(core.IfElse(t == b.ch.intrinsicMarkerType, ast.KindIntrinsicKeyword, ast.KindAnyKeyword)) - } - if t.flags&TypeFlagsUnknown != 0 { - return b.f.NewKeywordTypeNode(ast.KindUnknownKeyword) - } - if t.flags&TypeFlagsString != 0 { - b.ctx.approximateLength += 6 - return b.f.NewKeywordTypeNode(ast.KindStringKeyword) - } - if t.flags&TypeFlagsNumber != 0 { - b.ctx.approximateLength += 6 - return b.f.NewKeywordTypeNode(ast.KindNumberKeyword) - } - if t.flags&TypeFlagsBigInt != 0 { - b.ctx.approximateLength += 6 - return b.f.NewKeywordTypeNode(ast.KindBigIntKeyword) - } - if t.flags&TypeFlagsBoolean != 0 && t.alias == nil { - b.ctx.approximateLength += 7 - return b.f.NewKeywordTypeNode(ast.KindBooleanKeyword) - } - if t.flags&TypeFlagsEnumLike != 0 { - if t.symbol.Flags&ast.SymbolFlagsEnumMember != 0 { - parentSymbol := b.ch.getParentOfSymbol(t.symbol) - parentName := b.symbolToTypeNode(parentSymbol, ast.SymbolFlagsType, nil) - if b.ch.getDeclaredTypeOfSymbol(parentSymbol) == t { - return parentName - } - memberName := ast.SymbolName(t.symbol) - if scanner.IsIdentifierText(memberName, core.LanguageVariantStandard) { - return b.appendReferenceToType(parentName /* as TypeReferenceNode | ImportTypeNode */, b.f.NewTypeReferenceNode(b.f.NewIdentifier(memberName), nil /*typeArguments*/)) - } - if ast.IsImportTypeNode(parentName) { - parentName.AsImportTypeNode().IsTypeOf = true - // mutably update, node is freshly manufactured anyhow - return b.f.NewIndexedAccessTypeNode(parentName, b.f.NewLiteralTypeNode(b.f.NewStringLiteral(memberName))) - } else if ast.IsTypeReferenceNode(parentName) { - return b.f.NewIndexedAccessTypeNode(b.f.NewTypeQueryNode(parentName.AsTypeReferenceNode().TypeName, nil), b.f.NewLiteralTypeNode(b.f.NewStringLiteral(memberName))) - } else { - panic("Unhandled type node kind returned from `symbolToTypeNode`.") - } - } - return b.symbolToTypeNode(t.symbol, ast.SymbolFlagsType, nil) - } - if t.flags&TypeFlagsStringLiteral != 0 { - b.ctx.approximateLength += len(t.AsLiteralType().value.(string)) + 2 - lit := b.f.NewStringLiteral(t.AsLiteralType().value.(string) /*, b.flags&nodebuilder.FlagsUseSingleQuotesForStringLiteralType != 0*/) - b.e.AddEmitFlags(lit, printer.EFNoAsciiEscaping) - return b.f.NewLiteralTypeNode(lit) - } - if t.flags&TypeFlagsNumberLiteral != 0 { - value := t.AsLiteralType().value.(jsnum.Number) - b.ctx.approximateLength += len(value.String()) - if value < 0 { - return b.f.NewLiteralTypeNode(b.f.NewPrefixUnaryExpression(ast.KindMinusToken, b.f.NewNumericLiteral(value.String()[1:]))) - } else { - return b.f.NewLiteralTypeNode(b.f.NewNumericLiteral(value.String())) - } - } - if t.flags&TypeFlagsBigIntLiteral != 0 { - b.ctx.approximateLength += len(pseudoBigIntToString(getBigIntLiteralValue(t))) + 1 - return b.f.NewLiteralTypeNode(b.f.NewBigIntLiteral(pseudoBigIntToString(getBigIntLiteralValue(t)) + "n")) - } - if t.flags&TypeFlagsBooleanLiteral != 0 { - if t.AsLiteralType().value.(bool) { - b.ctx.approximateLength += 4 - return b.f.NewLiteralTypeNode(b.f.NewKeywordExpression(ast.KindTrueKeyword)) - } else { - b.ctx.approximateLength += 5 - return b.f.NewLiteralTypeNode(b.f.NewKeywordExpression(ast.KindFalseKeyword)) - } - } - if t.flags&TypeFlagsUniqueESSymbol != 0 { - if b.ctx.flags&nodebuilder.FlagsAllowUniqueESSymbolType == 0 { - if b.ch.IsValueSymbolAccessible(t.symbol, b.ctx.enclosingDeclaration) { - b.ctx.approximateLength += 6 - return b.symbolToTypeNode(t.symbol, ast.SymbolFlagsValue, nil) - } - b.ctx.tracker.ReportInaccessibleUniqueSymbolError() - } - b.ctx.approximateLength += 13 - return b.f.NewTypeOperatorNode(ast.KindUniqueKeyword, b.f.NewKeywordTypeNode(ast.KindSymbolKeyword)) - } - if t.flags&TypeFlagsVoid != 0 { - b.ctx.approximateLength += 4 - return b.f.NewKeywordTypeNode(ast.KindVoidKeyword) - } - if t.flags&TypeFlagsUndefined != 0 { - b.ctx.approximateLength += 9 - return b.f.NewKeywordTypeNode(ast.KindUndefinedKeyword) - } - if t.flags&TypeFlagsNull != 0 { - b.ctx.approximateLength += 4 - return b.f.NewLiteralTypeNode(b.f.NewKeywordExpression(ast.KindNullKeyword)) - } - if t.flags&TypeFlagsNever != 0 { - b.ctx.approximateLength += 5 - return b.f.NewKeywordTypeNode(ast.KindNeverKeyword) - } - if t.flags&TypeFlagsESSymbol != 0 { - b.ctx.approximateLength += 6 - return b.f.NewKeywordTypeNode(ast.KindSymbolKeyword) - } - if t.flags&TypeFlagsNonPrimitive != 0 { - b.ctx.approximateLength += 6 - return b.f.NewKeywordTypeNode(ast.KindObjectKeyword) - } - if isThisTypeParameter(t) { - if b.ctx.flags&nodebuilder.FlagsInObjectTypeLiteral != 0 { - if !b.ctx.encounteredError && b.ctx.flags&nodebuilder.FlagsAllowThisInObjectLiteral == 0 { - b.ctx.encounteredError = true - } - b.ctx.tracker.ReportInaccessibleThisError() - } - b.ctx.approximateLength += 4 - return b.f.NewThisTypeNode() - } - - if inTypeAlias == 0 && t.alias != nil && (b.ctx.flags&nodebuilder.FlagsUseAliasDefinedOutsideCurrentScope != 0 || b.ch.IsTypeSymbolAccessible(t.alias.Symbol(), b.ctx.enclosingDeclaration)) { - sym := t.alias.Symbol() - typeArgumentNodes := b.mapToTypeNodes(t.alias.TypeArguments(), false /*isBareList*/) - if isReservedMemberName(sym.Name) && sym.Flags&ast.SymbolFlagsClass == 0 { - return b.f.NewTypeReferenceNode(b.f.NewIdentifier(""), typeArgumentNodes) - } - if typeArgumentNodes != nil && len(typeArgumentNodes.Nodes) == 1 && sym == b.ch.globalArrayType.symbol { - return b.f.NewArrayTypeNode(typeArgumentNodes.Nodes[0]) - } - return b.symbolToTypeNode(sym, ast.SymbolFlagsType, typeArgumentNodes) - } - - objectFlags := t.objectFlags - - if objectFlags&ObjectFlagsReference != 0 { - debug.Assert(t.Flags()&TypeFlagsObject != 0) - if t.AsTypeReference().node != nil { - return b.visitAndTransformType(t, (*nodeBuilderImpl).typeReferenceToTypeNode) - } else { - return b.typeReferenceToTypeNode(t) - } - } - if t.flags&TypeFlagsTypeParameter != 0 || objectFlags&ObjectFlagsClassOrInterface != 0 { - if t.flags&TypeFlagsTypeParameter != 0 && slices.Contains(b.ctx.inferTypeParameters, t) { - b.ctx.approximateLength += len(ast.SymbolName(t.symbol)) + 6 - var constraintNode *ast.TypeNode - constraint := b.ch.getConstraintOfTypeParameter(t) - if constraint != nil { - // If the infer type has a constraint that is not the same as the constraint - // we would have normally inferred based on b, we emit the constraint - // using `infer T extends ?`. We omit inferred constraints from type references - // as they may be elided. - inferredConstraint := b.ch.getInferredTypeParameterConstraint(t, true /*omitTypeReferences*/) - if !(inferredConstraint != nil && b.ch.isTypeIdenticalTo(constraint, inferredConstraint)) { - b.ctx.approximateLength += 9 - constraintNode = b.typeToTypeNode(constraint) - } - } - return b.f.NewInferTypeNode(b.typeParameterToDeclarationWithConstraint(t, constraintNode)) - } - if b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 && t.flags&TypeFlagsTypeParameter != 0 { - name := b.typeParameterToName(t) - b.ctx.approximateLength += len(name.Text) - return b.f.NewTypeReferenceNode(b.f.NewIdentifier(name.Text), nil /*typeArguments*/) - } - // Ignore constraint/default when creating a usage (as opposed to declaration) of a type parameter. - if t.symbol != nil { - return b.symbolToTypeNode(t.symbol, ast.SymbolFlagsType, nil) - } - var name string - if (t == b.ch.markerSuperTypeForCheck || t == b.ch.markerSubTypeForCheck) && b.ch.varianceTypeParameter != nil && b.ch.varianceTypeParameter.symbol != nil { - name = (core.IfElse(t == b.ch.markerSubTypeForCheck, "sub-", "super-")) + ast.SymbolName(b.ch.varianceTypeParameter.symbol) - } else { - name = "?" - } - return b.f.NewTypeReferenceNode(b.f.NewIdentifier(name), nil /*typeArguments*/) - } - if t.flags&TypeFlagsUnion != 0 && t.AsUnionType().origin != nil { - t = t.AsUnionType().origin - } - if t.flags&(TypeFlagsUnion|TypeFlagsIntersection) != 0 { - var types []*Type - if t.flags&TypeFlagsUnion != 0 { - types = b.ch.formatUnionTypes(t.AsUnionType().types) - } else { - types = t.AsIntersectionType().types - } - if len(types) == 1 { - return b.typeToTypeNode(types[0]) - } - typeNodes := b.mapToTypeNodes(types, true /*isBareList*/) - if typeNodes != nil && len(typeNodes.Nodes) > 0 { - if t.flags&TypeFlagsUnion != 0 { - return b.f.NewUnionTypeNode(typeNodes) - } else { - return b.f.NewIntersectionTypeNode(typeNodes) - } - } else { - if !b.ctx.encounteredError && b.ctx.flags&nodebuilder.FlagsAllowEmptyUnionOrIntersection == 0 { - b.ctx.encounteredError = true - } - return nil - // TODO: GH#18217 - } - } - if objectFlags&(ObjectFlagsAnonymous|ObjectFlagsMapped) != 0 { - debug.Assert(t.Flags()&TypeFlagsObject != 0) - // The type is an object literal type. - return b.createAnonymousTypeNode(t) - } - if t.flags&TypeFlagsIndex != 0 { - indexedType := t.Target() - b.ctx.approximateLength += 6 - indexTypeNode := b.typeToTypeNode(indexedType) - return b.f.NewTypeOperatorNode(ast.KindKeyOfKeyword, indexTypeNode) - } - if t.flags&TypeFlagsTemplateLiteral != 0 { - texts := t.AsTemplateLiteralType().texts - types := t.AsTemplateLiteralType().types - templateHead := b.f.NewTemplateHead(texts[0], texts[0], ast.TokenFlagsNone) - templateSpans := b.f.NewNodeList(core.MapIndex(types, func(t *Type, i int) *ast.Node { - var res *ast.TemplateMiddleOrTail - if i < len(types)-1 { - res = b.f.NewTemplateMiddle(texts[i+1], texts[i+1], ast.TokenFlagsNone) - } else { - res = b.f.NewTemplateTail(texts[i+1], texts[i+1], ast.TokenFlagsNone) - } - return b.f.NewTemplateLiteralTypeSpan(b.typeToTypeNode(t), res) - })) - b.ctx.approximateLength += 2 - return b.f.NewTemplateLiteralTypeNode(templateHead, templateSpans) - } - if t.flags&TypeFlagsStringMapping != 0 { - typeNode := b.typeToTypeNode(t.Target()) - return b.symbolToTypeNode(t.AsStringMappingType().symbol, ast.SymbolFlagsType, b.f.NewNodeList([]*ast.Node{typeNode})) - } - if t.flags&TypeFlagsIndexedAccess != 0 { - objectTypeNode := b.typeToTypeNode(t.AsIndexedAccessType().objectType) - indexTypeNode := b.typeToTypeNode(t.AsIndexedAccessType().indexType) - b.ctx.approximateLength += 2 - return b.f.NewIndexedAccessTypeNode(objectTypeNode, indexTypeNode) - } - if t.flags&TypeFlagsConditional != 0 { - return b.visitAndTransformType(t, (*nodeBuilderImpl).conditionalTypeToTypeNode) - } - if t.flags&TypeFlagsSubstitution != 0 { - typeNode := b.typeToTypeNode(t.AsSubstitutionType().baseType) - if !b.ch.isNoInferType(t) { - return typeNode - } - noInferSymbol := b.ch.getGlobalTypeAliasSymbol("NoInfer", 1, false) - if noInferSymbol != nil { - return b.symbolToTypeNode(noInferSymbol, ast.SymbolFlagsType, b.f.NewNodeList([]*ast.Node{typeNode})) - } else { - return typeNode - } - } - - panic("Should be unreachable.") -} - -// Direct serialization core functions for types, type aliases, and symbols - -func (t *TypeAlias) ToTypeReferenceNode(b *nodeBuilderImpl) *ast.Node { - return b.f.NewTypeReferenceNode(b.symbolToEntityNameNode(t.Symbol()), b.mapToTypeNodes(t.TypeArguments(), false /*isBareList*/)) -} diff --git a/kitcom/internal/tsgo/checker/nodebuilderscopes.go b/kitcom/internal/tsgo/checker/nodebuilderscopes.go deleted file mode 100644 index 6e15a31..0000000 --- a/kitcom/internal/tsgo/checker/nodebuilderscopes.go +++ /dev/null @@ -1,246 +0,0 @@ -package checker - -import ( - "maps" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/debug" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/nodebuilder" -) - -func cloneNodeBuilderContext(context *NodeBuilderContext) func() { - // Make type parameters created within this context not consume the name outside this context - // The symbol serializer ends up creating many sibling scopes that all need "separate" contexts when - // it comes to naming things - within a normal `typeToTypeNode` call, the node builder only ever descends - // through the type tree, so the only cases where we could have used distinct sibling scopes was when there - // were multiple generic overloads with similar generated type parameter names - // The effect: - // When we write out - // export const x: (x: T) => T - // export const y: (x: T) => T - // we write it out like that, rather than as - // export const x: (x: T) => T - // export const y: (x: T_1) => T_1 - oldMustCreateTypeParameterSymbolList := context.hasCreatedTypeParameterSymbolList - oldMustCreateTypeParametersNamesLookups := context.hasCreatedTypeParametersNamesLookups - oldTypeParameterNames := context.typeParameterNames - oldTypeParameterNamesByText := context.typeParameterNamesByText - oldTypeParameterNamesByTextNextNameCount := context.typeParameterNamesByTextNextNameCount - oldTypeParameterSymbolList := context.typeParameterSymbolList - context.hasCreatedTypeParameterSymbolList = oldTypeParameterSymbolList != nil - context.hasCreatedTypeParametersNamesLookups = oldTypeParameterNames != nil - context.typeParameterNames = maps.Clone(context.typeParameterNames) - context.typeParameterNamesByText = maps.Clone(context.typeParameterNamesByText) - context.typeParameterNamesByTextNextNameCount = maps.Clone(context.typeParameterNamesByTextNextNameCount) - context.typeParameterSymbolList = maps.Clone(context.typeParameterSymbolList) - return func() { - context.typeParameterNames = oldTypeParameterNames - context.typeParameterNamesByText = oldTypeParameterNamesByText - context.typeParameterNamesByTextNextNameCount = oldTypeParameterNamesByTextNextNameCount - context.typeParameterSymbolList = oldTypeParameterSymbolList - context.hasCreatedTypeParameterSymbolList = oldMustCreateTypeParameterSymbolList - context.hasCreatedTypeParametersNamesLookups = oldMustCreateTypeParametersNamesLookups - } -} - -type localsRecord struct { - name string - oldSymbol *ast.Symbol -} - -func (b *nodeBuilderImpl) enterNewScope(declaration *ast.Node, expandedParams []*ast.Symbol, typeParameters []*Type, originalParameters []*ast.Symbol, mapper *TypeMapper) func() { - cleanupContext := cloneNodeBuilderContext(b.ctx) - // For regular function/method declarations, the enclosing declaration will already be signature.declaration, - // so this is a no-op, but for arrow functions and function expressions, the enclosing declaration will be - // the declaration that the arrow function / function expression is assigned to. - // - // If the parameters or return type include "typeof globalThis.paramName", using the wrong scope will lead - // us to believe that we can emit "typeof paramName" instead, even though that would refer to the parameter, - // not the global. Make sure we are in the right scope by changing the enclosingDeclaration to the function. - // - // We can't use the declaration directly; it may be in another file and so we may lose access to symbols - // accessible to the current enclosing declaration, or gain access to symbols not accessible to the current - // enclosing declaration. To keep this chain accurate, insert a fake scope into the chain which makes the - // function's parameters visible. - var cleanupParams func() - var cleanupTypeParams func() - oldEnclosingDecl := b.ctx.enclosingDeclaration - oldMapper := b.ctx.mapper - if mapper != nil { - b.ctx.mapper = mapper - } - if b.ctx.enclosingDeclaration != nil && declaration != nil { - // As a performance optimization, reuse the same fake scope within this chain. - // This is especially needed when we are working on an excessively deep type; - // if we don't do this, then we spend all of our time adding more and more - // scopes that need to be searched in isSymbolAccessible later. Since all we - // really want to do is to mark certain names as unavailable, we can just keep - // all of the names we're introducing in one large table and push/pop from it as - // needed; isSymbolAccessible will walk upward and find the closest "fake" scope, - // which will conveniently report on any and all faked scopes in the chain. - // - // It'd likely be better to store this somewhere else for isSymbolAccessible, but - // since that API _only_ uses the enclosing declaration (and its parents), this is - // seems like the best way to inject names into that search process. - // - // Note that we only check the most immediate enclosingDeclaration; the only place we - // could potentially add another fake scope into the chain is right here, so we don't - // traverse all ancestors. - pushFakeScope := func(kind string, addAll func(addSymbol func(name string, symbol *ast.Symbol))) func() { - // We only ever need to look two declarations upward. - debug.AssertIsDefined(b.ctx.enclosingDeclaration) - var existingFakeScope *ast.Node - if b.links.Has(b.ctx.enclosingDeclaration) { - links := b.links.Get(b.ctx.enclosingDeclaration) - if links.fakeScopeForSignatureDeclaration != nil && *links.fakeScopeForSignatureDeclaration == kind { - existingFakeScope = b.ctx.enclosingDeclaration - } - } - if existingFakeScope == nil && b.ctx.enclosingDeclaration.Parent != nil { - if b.links.Has(b.ctx.enclosingDeclaration.Parent) { - links := b.links.Get(b.ctx.enclosingDeclaration.Parent) - if links.fakeScopeForSignatureDeclaration != nil && *links.fakeScopeForSignatureDeclaration == kind { - existingFakeScope = b.ctx.enclosingDeclaration.Parent - } - } - } - debug.AssertOptionalNode(existingFakeScope, ast.IsBlock) - - var locals ast.SymbolTable - if existingFakeScope != nil { - locals = existingFakeScope.Locals() - } - if locals == nil { - locals = make(ast.SymbolTable) - } - newLocals := []string{} - oldLocals := []localsRecord{} - addAll(func(name string, symbol *ast.Symbol) { - // Add cleanup information only if we don't own the fake scope - if existingFakeScope != nil { - oldSymbol, ok := locals[name] - if !ok || oldSymbol == nil { - newLocals = append(newLocals, name) - } else { - oldLocals = append(oldLocals, localsRecord{name, oldSymbol}) - } - } - locals[name] = symbol - }) - - if existingFakeScope == nil { - // Use a Block for this; the type of the node doesn't matter so long as it - // has locals, and this is cheaper/easier than using a function-ish Node. - fakeScope := b.f.NewBlock(b.f.NewNodeList([]*ast.Node{}), false) - b.links.Get(fakeScope).fakeScopeForSignatureDeclaration = &kind - data := fakeScope.LocalsContainerData() - data.Locals = locals - fakeScope.Parent = b.ctx.enclosingDeclaration - b.ctx.enclosingDeclaration = fakeScope - return nil - } else { - // We did not create the current scope, so we have to clean it up - undo := func() { - for _, s := range newLocals { - delete(locals, s) - } - for _, s := range oldLocals { - locals[s.name] = s.oldSymbol - } - } - return undo - } - } - - if expandedParams == nil || !core.Some(expandedParams, func(p *ast.Symbol) bool { return p != nil }) { - cleanupParams = nil - } else { - cleanupParams = pushFakeScope("params", func(add func(name string, symbol *ast.Symbol)) { - if expandedParams == nil { - return - } - for pIndex, param := range expandedParams { - var originalParam *ast.Symbol - if pIndex < len(originalParameters) { - originalParam = (originalParameters)[pIndex] - } - if originalParameters != nil && originalParam != param { - // Can't reference parameters that come from an expansion - add(param.Name, b.ch.unknownSymbol) - // Can't reference the original expanded parameter either - if originalParam != nil { - add(originalParam.Name, b.ch.unknownSymbol) - } - } else if !core.Some(param.Declarations, func(d *ast.Node) bool { - var bindElement func(e *ast.BindingElement) - var bindPattern func(e *ast.BindingPattern) - - bindPatternWorker := func(p *ast.BindingPattern) { - for _, e := range p.Elements.Nodes { - switch e.Kind { - case ast.KindOmittedExpression: - return - case ast.KindBindingElement: - bindElement(e.AsBindingElement()) - return - default: - panic("Unhandled binding element kind") - } - } - } - - bindElementWorker := func(e *ast.BindingElement) { - if e.Name() != nil && ast.IsBindingPattern(e.Name()) { - bindPattern(e.Name().AsBindingPattern()) - return - } - symbol := b.ch.getSymbolOfDeclaration(e.AsNode()) - if symbol != nil { // omitted expressions are now parsed as nameless binding patterns and also have no symbol - add(symbol.Name, symbol) - } - } - bindElement = bindElementWorker - bindPattern = bindPatternWorker - - if ast.IsParameter(d) && d.Name() != nil && ast.IsBindingPattern(d.Name()) { - bindPattern(d.Name().AsBindingPattern()) - return true - } - return false - }) { - add(param.Name, param) - } - } - }) - } - - if b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 && typeParameters != nil && core.Some(typeParameters, func(p *Type) bool { return p != nil }) { - cleanupTypeParams = pushFakeScope("typeParams", func(add func(name string, symbol *ast.Symbol)) { - if typeParameters == nil { - return - } - for _, typeParam := range typeParameters { - if typeParam == nil { - continue - } - typeParamName := b.typeParameterToName(typeParam).Text - add(typeParamName, typeParam.symbol) - } - }) - } - - } - - return func() { - if cleanupParams != nil { - cleanupParams() - } - if cleanupTypeParams != nil { - cleanupTypeParams() - } - cleanupContext() - b.ctx.enclosingDeclaration = oldEnclosingDecl - b.ctx.mapper = oldMapper - } -} diff --git a/kitcom/internal/tsgo/checker/printer.go b/kitcom/internal/tsgo/checker/printer.go deleted file mode 100644 index 83af72d..0000000 --- a/kitcom/internal/tsgo/checker/printer.go +++ /dev/null @@ -1,379 +0,0 @@ -package checker - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/nodebuilder" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" -) - -// TODO: Memoize once per checker to retain threadsafety -func createPrinterWithDefaults(emitContext *printer.EmitContext) *printer.Printer { - return printer.NewPrinter(printer.PrinterOptions{}, printer.PrintHandlers{}, emitContext) -} - -func createPrinterWithRemoveComments(emitContext *printer.EmitContext) *printer.Printer { - return printer.NewPrinter(printer.PrinterOptions{RemoveComments: true}, printer.PrintHandlers{}, emitContext) -} - -func createPrinterWithRemoveCommentsOmitTrailingSemicolon(emitContext *printer.EmitContext) *printer.Printer { - // TODO: OmitTrailingSemicolon support - return printer.NewPrinter(printer.PrinterOptions{RemoveComments: true}, printer.PrintHandlers{}, emitContext) -} - -func createPrinterWithRemoveCommentsNeverAsciiEscape(emitContext *printer.EmitContext) *printer.Printer { - return printer.NewPrinter(printer.PrinterOptions{ - RemoveComments: true, - NeverAsciiEscape: true, - }, printer.PrintHandlers{}, emitContext) -} - -type semicolonRemoverWriter struct { - hasPendingSemicolon bool - inner printer.EmitTextWriter -} - -func (s *semicolonRemoverWriter) commitSemicolon() { - if s.hasPendingSemicolon { - s.inner.WriteTrailingSemicolon(";") - s.hasPendingSemicolon = false - } -} - -func (s *semicolonRemoverWriter) Clear() { - s.inner.Clear() -} - -func (s *semicolonRemoverWriter) DecreaseIndent() { - s.commitSemicolon() - s.inner.DecreaseIndent() -} - -func (s *semicolonRemoverWriter) GetColumn() int { - return s.inner.GetColumn() -} - -func (s *semicolonRemoverWriter) GetIndent() int { - return s.inner.GetIndent() -} - -func (s *semicolonRemoverWriter) GetLine() int { - return s.inner.GetLine() -} - -func (s *semicolonRemoverWriter) GetTextPos() int { - return s.inner.GetTextPos() -} - -func (s *semicolonRemoverWriter) HasTrailingComment() bool { - return s.inner.HasTrailingComment() -} - -func (s *semicolonRemoverWriter) HasTrailingWhitespace() bool { - return s.inner.HasTrailingWhitespace() -} - -func (s *semicolonRemoverWriter) IncreaseIndent() { - s.commitSemicolon() - s.inner.IncreaseIndent() -} - -func (s *semicolonRemoverWriter) IsAtStartOfLine() bool { - return s.inner.IsAtStartOfLine() -} - -func (s *semicolonRemoverWriter) RawWrite(s1 string) { - s.commitSemicolon() - s.inner.RawWrite(s1) -} - -func (s *semicolonRemoverWriter) String() string { - s.commitSemicolon() - return s.inner.String() -} - -func (s *semicolonRemoverWriter) Write(s1 string) { - s.commitSemicolon() - s.inner.Write(s1) -} - -func (s *semicolonRemoverWriter) WriteComment(text string) { - s.commitSemicolon() - s.inner.WriteComment(text) -} - -func (s *semicolonRemoverWriter) WriteKeyword(text string) { - s.commitSemicolon() - s.inner.WriteKeyword(text) -} - -func (s *semicolonRemoverWriter) WriteLine() { - s.commitSemicolon() - s.inner.WriteLine() -} - -func (s *semicolonRemoverWriter) WriteLineForce(force bool) { - s.commitSemicolon() - s.inner.WriteLineForce(force) -} - -func (s *semicolonRemoverWriter) WriteLiteral(s1 string) { - s.commitSemicolon() - s.inner.WriteLiteral(s1) -} - -func (s *semicolonRemoverWriter) WriteOperator(text string) { - s.commitSemicolon() - s.inner.WriteOperator(text) -} - -func (s *semicolonRemoverWriter) WriteParameter(text string) { - s.commitSemicolon() - s.inner.WriteParameter(text) -} - -func (s *semicolonRemoverWriter) WriteProperty(text string) { - s.commitSemicolon() - s.inner.WriteProperty(text) -} - -func (s *semicolonRemoverWriter) WritePunctuation(text string) { - s.commitSemicolon() - s.inner.WritePunctuation(text) -} - -func (s *semicolonRemoverWriter) WriteSpace(text string) { - s.commitSemicolon() - s.inner.WriteSpace(text) -} - -func (s *semicolonRemoverWriter) WriteStringLiteral(text string) { - s.commitSemicolon() - s.inner.WriteStringLiteral(text) -} - -func (s *semicolonRemoverWriter) WriteSymbol(text string, symbol *ast.Symbol) { - s.commitSemicolon() - s.inner.WriteSymbol(text, symbol) -} - -func (s *semicolonRemoverWriter) WriteTrailingSemicolon(text string) { - s.hasPendingSemicolon = true -} - -func getTrailingSemicolonDeferringWriter(writer printer.EmitTextWriter) printer.EmitTextWriter { - return &semicolonRemoverWriter{false, writer} -} - -func (c *Checker) TypeToString(t *Type) string { - return c.typeToString(t, nil) -} - -func (c *Checker) typeToString(t *Type, enclosingDeclaration *ast.Node) string { - return c.typeToStringEx(t, enclosingDeclaration, TypeFormatFlagsAllowUniqueESSymbolType|TypeFormatFlagsUseAliasDefinedOutsideCurrentScope) -} - -func toNodeBuilderFlags(flags TypeFormatFlags) nodebuilder.Flags { - return nodebuilder.Flags(flags & TypeFormatFlagsNodeBuilderFlagsMask) -} - -func (c *Checker) TypeToStringEx(t *Type, enclosingDeclaration *ast.Node, flags TypeFormatFlags) string { - return c.typeToStringEx(t, enclosingDeclaration, flags) -} - -func (c *Checker) typeToStringEx(t *Type, enclosingDeclaration *ast.Node, flags TypeFormatFlags) string { - writer := printer.NewTextWriter("") - noTruncation := (c.compilerOptions.NoErrorTruncation == core.TSTrue) || (flags&TypeFormatFlagsNoTruncation != 0) - combinedFlags := toNodeBuilderFlags(flags) | nodebuilder.FlagsIgnoreErrors - if noTruncation { - combinedFlags = combinedFlags | nodebuilder.FlagsNoTruncation - } - nodeBuilder := c.getNodeBuilder() - typeNode := nodeBuilder.TypeToTypeNode(t, enclosingDeclaration, combinedFlags, nodebuilder.InternalFlagsNone, nil) - if typeNode == nil { - panic("should always get typenode") - } - // The unresolved type gets a synthesized comment on `any` to hint to users that it's not a plain `any`. - // Otherwise, we always strip comments out. - var printer *printer.Printer - if t == c.unresolvedType { - printer = createPrinterWithDefaults(nodeBuilder.EmitContext()) - } else { - printer = createPrinterWithRemoveComments(nodeBuilder.EmitContext()) - } - var sourceFile *ast.SourceFile - if enclosingDeclaration != nil { - sourceFile = ast.GetSourceFileOfNode(enclosingDeclaration) - } - printer.Write(typeNode /*sourceFile*/, sourceFile, writer, nil) - result := writer.String() - - maxLength := defaultMaximumTruncationLength * 2 - if noTruncation { - maxLength = noTruncationMaximumTruncationLength * 2 - } - if maxLength > 0 && result != "" && len(result) >= maxLength { - return result[0:maxLength-len("...")] + "..." - } - return result -} - -func (c *Checker) SymbolToString(s *ast.Symbol) string { - return c.symbolToString(s) -} - -func (c *Checker) symbolToString(symbol *ast.Symbol) string { - return c.symbolToStringEx(symbol, nil, ast.SymbolFlagsAll, SymbolFormatFlagsAllowAnyNodeKind) -} - -func (c *Checker) SymbolToStringEx(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags, flags SymbolFormatFlags) string { - return c.symbolToStringEx(symbol, enclosingDeclaration, meaning, flags) -} - -func (c *Checker) symbolToStringEx(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags, flags SymbolFormatFlags) string { - writer, putWriter := printer.GetSingleLineStringWriter() - defer putWriter() - - nodeFlags := nodebuilder.FlagsIgnoreErrors - internalNodeFlags := nodebuilder.InternalFlagsNone - if flags&SymbolFormatFlagsUseOnlyExternalAliasing != 0 { - nodeFlags |= nodebuilder.FlagsUseOnlyExternalAliasing - } - if flags&SymbolFormatFlagsWriteTypeParametersOrArguments != 0 { - nodeFlags |= nodebuilder.FlagsWriteTypeParametersInQualifiedName - } - if flags&SymbolFormatFlagsUseAliasDefinedOutsideCurrentScope != 0 { - nodeFlags |= nodebuilder.FlagsUseAliasDefinedOutsideCurrentScope - } - if flags&SymbolFormatFlagsDoNotIncludeSymbolChain != 0 { - internalNodeFlags |= nodebuilder.InternalFlagsDoNotIncludeSymbolChain - } - if flags&SymbolFormatFlagsWriteComputedProps != 0 { - internalNodeFlags |= nodebuilder.InternalFlagsWriteComputedProps - } - - nodeBuilder := c.getNodeBuilder() - var sourceFile *ast.SourceFile - if enclosingDeclaration != nil { - sourceFile = ast.GetSourceFileOfNode(enclosingDeclaration) - } - var printer_ *printer.Printer - // add neverAsciiEscape for GH#39027 - if enclosingDeclaration != nil && enclosingDeclaration.Kind == ast.KindSourceFile { - printer_ = createPrinterWithRemoveCommentsNeverAsciiEscape(nodeBuilder.EmitContext()) - } else { - printer_ = createPrinterWithRemoveComments(nodeBuilder.EmitContext()) - } - - var builder func(symbol *ast.Symbol, meaning ast.SymbolFlags, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node - if flags&SymbolFormatFlagsAllowAnyNodeKind != 0 { - builder = nodeBuilder.SymbolToNode - } else { - builder = nodeBuilder.SymbolToEntityName - } - entity := builder(symbol, meaning, enclosingDeclaration, nodeFlags, internalNodeFlags, nil) // TODO: GH#18217 - printer_.Write(entity /*sourceFile*/, sourceFile, getTrailingSemicolonDeferringWriter(writer), nil) // TODO: GH#18217 - return writer.String() -} - -func (c *Checker) signatureToString(signature *Signature) string { - return c.signatureToStringEx(signature, nil, TypeFormatFlagsNone) -} - -func (c *Checker) SignatureToStringEx(signature *Signature, enclosingDeclaration *ast.Node, flags TypeFormatFlags) string { - return c.signatureToStringEx(signature, enclosingDeclaration, flags) -} - -func (c *Checker) signatureToStringEx(signature *Signature, enclosingDeclaration *ast.Node, flags TypeFormatFlags) string { - isConstructor := signature.flags&SignatureFlagsConstruct != 0 && flags&TypeFormatFlagsWriteCallStyleSignature == 0 - var sigOutput ast.Kind - if flags&TypeFormatFlagsWriteArrowStyleSignature != 0 { - if isConstructor { - sigOutput = ast.KindConstructorType - } else { - sigOutput = ast.KindFunctionType - } - } else { - if isConstructor { - sigOutput = ast.KindConstructSignature - } else { - sigOutput = ast.KindCallSignature - } - } - writer, putWriter := printer.GetSingleLineStringWriter() - defer putWriter() - - nodeBuilder := c.getNodeBuilder() - combinedFlags := toNodeBuilderFlags(flags) | nodebuilder.FlagsIgnoreErrors | nodebuilder.FlagsWriteTypeParametersInQualifiedName - sig := nodeBuilder.SignatureToSignatureDeclaration(signature, sigOutput, enclosingDeclaration, combinedFlags, nodebuilder.InternalFlagsNone, nil) - printer_ := createPrinterWithRemoveCommentsOmitTrailingSemicolon(nodeBuilder.EmitContext()) - var sourceFile *ast.SourceFile - if enclosingDeclaration != nil { - sourceFile = ast.GetSourceFileOfNode(enclosingDeclaration) - } - printer_.Write(sig /*sourceFile*/, sourceFile, getTrailingSemicolonDeferringWriter(writer), nil) // TODO: GH#18217 - return writer.String() -} - -func (c *Checker) typePredicateToString(typePredicate *TypePredicate) string { - return c.typePredicateToStringEx(typePredicate, nil, TypeFormatFlagsUseAliasDefinedOutsideCurrentScope) -} - -func (c *Checker) typePredicateToStringEx(typePredicate *TypePredicate, enclosingDeclaration *ast.Node, flags TypeFormatFlags) string { - writer, putWriter := printer.GetSingleLineStringWriter() - defer putWriter() - nodeBuilder := c.getNodeBuilder() - combinedFlags := toNodeBuilderFlags(flags) | nodebuilder.FlagsIgnoreErrors | nodebuilder.FlagsWriteTypeParametersInQualifiedName - predicate := nodeBuilder.TypePredicateToTypePredicateNode(typePredicate, enclosingDeclaration, combinedFlags, nodebuilder.InternalFlagsNone, nil) // TODO: GH#18217 - printer_ := createPrinterWithRemoveComments(nodeBuilder.EmitContext()) - var sourceFile *ast.SourceFile - if enclosingDeclaration != nil { - sourceFile = ast.GetSourceFileOfNode(enclosingDeclaration) - } - printer_.Write(predicate /*sourceFile*/, sourceFile, writer, nil) - return writer.String() -} - -func (c *Checker) valueToString(value any) string { - return ValueToString(value) -} - -func (c *Checker) formatUnionTypes(types []*Type) []*Type { - var result []*Type - var flags TypeFlags - for i := 0; i < len(types); i++ { - t := types[i] - flags |= t.flags - if t.flags&TypeFlagsNullable == 0 { - if t.flags&(TypeFlagsBooleanLiteral|TypeFlagsEnumLike) != 0 { - var baseType *Type - if t.flags&TypeFlagsBooleanLiteral != 0 { - baseType = c.booleanType - } else { - baseType = c.getBaseTypeOfEnumLikeType(t) - } - if baseType.flags&TypeFlagsUnion != 0 { - count := len(baseType.AsUnionType().types) - if i+count <= len(types) && c.getRegularTypeOfLiteralType(types[i+count-1]) == c.getRegularTypeOfLiteralType(baseType.AsUnionType().types[count-1]) { - result = append(result, baseType) - i += count - 1 - continue - } - } - } - result = append(result, t) - } - } - if flags&TypeFlagsNull != 0 { - result = append(result, c.nullType) - } - if flags&TypeFlagsUndefined != 0 { - result = append(result, c.undefinedType) - } - return result -} - -func (c *Checker) TypeToTypeNode(t *Type, enclosingDeclaration *ast.Node, flags nodebuilder.Flags) *ast.TypeNode { - nodeBuilder := c.getNodeBuilder() - return nodeBuilder.TypeToTypeNode(t, enclosingDeclaration, flags, nodebuilder.InternalFlagsNone, nil) -} diff --git a/kitcom/internal/tsgo/checker/relater.go b/kitcom/internal/tsgo/checker/relater.go deleted file mode 100644 index 646c1bb..0000000 --- a/kitcom/internal/tsgo/checker/relater.go +++ /dev/null @@ -1,4923 +0,0 @@ -package checker - -import ( - "slices" - "strconv" - "strings" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/binder" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/debug" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/jsnum" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/scanner" -) - -type SignatureCheckMode uint32 - -const ( - SignatureCheckModeNone SignatureCheckMode = 0 - SignatureCheckModeBivariantCallback SignatureCheckMode = 1 << 0 - SignatureCheckModeStrictCallback SignatureCheckMode = 1 << 1 - SignatureCheckModeIgnoreReturnTypes SignatureCheckMode = 1 << 2 - SignatureCheckModeStrictArity SignatureCheckMode = 1 << 3 - SignatureCheckModeStrictTopSignature SignatureCheckMode = 1 << 4 - SignatureCheckModeCallback SignatureCheckMode = SignatureCheckModeBivariantCallback | SignatureCheckModeStrictCallback -) - -type MinArgumentCountFlags uint32 - -const ( - MinArgumentCountFlagsNone MinArgumentCountFlags = 0 - MinArgumentCountFlagsStrongArityForUntypedJS MinArgumentCountFlags = 1 << 0 - MinArgumentCountFlagsVoidIsNonOptional MinArgumentCountFlags = 1 << 1 -) - -type IntersectionState uint32 - -const ( - IntersectionStateNone IntersectionState = 0 - IntersectionStateSource IntersectionState = 1 << 0 // Source type is a constituent of an outer intersection - IntersectionStateTarget IntersectionState = 1 << 1 // Target type is a constituent of an outer intersection -) - -type RecursionFlags uint32 - -const ( - RecursionFlagsNone RecursionFlags = 0 - RecursionFlagsSource RecursionFlags = 1 << 0 - RecursionFlagsTarget RecursionFlags = 1 << 1 - RecursionFlagsBoth = RecursionFlagsSource | RecursionFlagsTarget -) - -type ExpandingFlags uint8 - -const ( - ExpandingFlagsNone ExpandingFlags = 0 - ExpandingFlagsSource ExpandingFlags = 1 << 0 - ExpandingFlagsTarget ExpandingFlags = 1 << 1 - ExpandingFlagsBoth = ExpandingFlagsSource | ExpandingFlagsTarget -) - -type RelationComparisonResult uint32 - -const ( - RelationComparisonResultNone RelationComparisonResult = 0 - RelationComparisonResultSucceeded RelationComparisonResult = 1 << 0 - RelationComparisonResultFailed RelationComparisonResult = 1 << 1 - RelationComparisonResultReportsUnmeasurable RelationComparisonResult = 1 << 3 - RelationComparisonResultReportsUnreliable RelationComparisonResult = 1 << 4 - RelationComparisonResultComplexityOverflow RelationComparisonResult = 1 << 5 - RelationComparisonResultStackDepthOverflow RelationComparisonResult = 1 << 6 - RelationComparisonResultReportsMask = RelationComparisonResultReportsUnmeasurable | RelationComparisonResultReportsUnreliable - RelationComparisonResultOverflow = RelationComparisonResultComplexityOverflow | RelationComparisonResultStackDepthOverflow -) - -type DiagnosticAndArguments struct { - message *diagnostics.Message - arguments []any -} - -type ErrorOutputContainer struct { - errors []*ast.Diagnostic - skipLogging bool -} - -type ErrorReporter func(message *diagnostics.Message, args ...any) - -type RecursionId struct { - value any -} - -// This function exists to constrain the types of values that can be used as recursion IDs. -func asRecursionId[T *ast.Node | *ast.Symbol | *Type](value T) RecursionId { - return RecursionId{value: value} -} - -type Relation struct { - results map[string]RelationComparisonResult -} - -func (r *Relation) get(key string) RelationComparisonResult { - return r.results[key] -} - -func (r *Relation) set(key string, result RelationComparisonResult) { - if r.results == nil { - r.results = make(map[string]RelationComparisonResult) - } - r.results[key] = result -} - -func (r *Relation) size() int { - return len(r.results) -} - -func (c *Checker) isTypeIdenticalTo(source *Type, target *Type) bool { - return c.isTypeRelatedTo(source, target, c.identityRelation) -} - -func (c *Checker) compareTypesIdentical(source *Type, target *Type) Ternary { - if c.isTypeRelatedTo(source, target, c.identityRelation) { - return TernaryTrue - } - return TernaryFalse -} - -func (c *Checker) compareTypesAssignableSimple(source *Type, target *Type) Ternary { - if c.isTypeRelatedTo(source, target, c.assignableRelation) { - return TernaryTrue - } - return TernaryFalse -} - -func (c *Checker) compareTypesAssignable(source *Type, target *Type, reportErrors bool) Ternary { - if c.isTypeRelatedTo(source, target, c.assignableRelation) { - return TernaryTrue - } - return TernaryFalse -} - -func (c *Checker) compareTypesSubtypeOf(source *Type, target *Type) Ternary { - if c.isTypeRelatedTo(source, target, c.subtypeRelation) { - return TernaryTrue - } - return TernaryFalse -} - -func (c *Checker) isTypeAssignableTo(source *Type, target *Type) bool { - return c.isTypeRelatedTo(source, target, c.assignableRelation) -} - -func (c *Checker) isTypeSubtypeOf(source *Type, target *Type) bool { - return c.isTypeRelatedTo(source, target, c.subtypeRelation) -} - -func (c *Checker) isTypeStrictSubtypeOf(source *Type, target *Type) bool { - return c.isTypeRelatedTo(source, target, c.strictSubtypeRelation) -} - -func (c *Checker) isTypeComparableTo(source *Type, target *Type) bool { - return c.isTypeRelatedTo(source, target, c.comparableRelation) -} - -func (c *Checker) areTypesComparable(type1 *Type, type2 *Type) bool { - return c.isTypeComparableTo(type1, type2) || c.isTypeComparableTo(type2, type1) -} - -func (c *Checker) isTypeRelatedTo(source *Type, target *Type, relation *Relation) bool { - if isFreshLiteralType(source) { - source = source.AsLiteralType().regularType - } - if isFreshLiteralType(target) { - target = target.AsLiteralType().regularType - } - if source == target { - return true - } - if relation != c.identityRelation { - if relation == c.comparableRelation && target.flags&TypeFlagsNever == 0 && c.isSimpleTypeRelatedTo(target, source, relation, nil) || c.isSimpleTypeRelatedTo(source, target, relation, nil) { - return true - } - } else if !((source.flags|target.flags)&(TypeFlagsUnionOrIntersection|TypeFlagsIndexedAccess|TypeFlagsConditional|TypeFlagsSubstitution) != 0) { - // We have excluded types that may simplify to other forms, so types must have identical flags - if source.flags != target.flags { - return false - } - if source.flags&TypeFlagsSingleton != 0 { - return true - } - } - if source.flags&TypeFlagsObject != 0 && target.flags&TypeFlagsObject != 0 { - related := relation.get(getRelationKey(source, target, IntersectionStateNone, relation == c.identityRelation, false)) - if related != RelationComparisonResultNone { - return related&RelationComparisonResultSucceeded != 0 - } - } - if source.flags&TypeFlagsStructuredOrInstantiable != 0 || target.flags&TypeFlagsStructuredOrInstantiable != 0 { - return c.checkTypeRelatedTo(source, target, relation, nil /*errorNode*/) - } - return false -} - -func (c *Checker) isSimpleTypeRelatedTo(source *Type, target *Type, relation *Relation, errorReporter ErrorReporter) bool { - s := source.flags - t := target.flags - if t&TypeFlagsAny != 0 || s&TypeFlagsNever != 0 || source == c.wildcardType { - return true - } - if t&TypeFlagsUnknown != 0 && !(relation == c.strictSubtypeRelation && s&TypeFlagsAny != 0) { - return true - } - if t&TypeFlagsNever != 0 { - return false - } - if s&TypeFlagsStringLike != 0 && t&TypeFlagsString != 0 { - return true - } - if s&TypeFlagsStringLiteral != 0 && s&TypeFlagsEnumLiteral != 0 && t&TypeFlagsStringLiteral != 0 && t&TypeFlagsEnumLiteral == 0 && source.AsLiteralType().value == target.AsLiteralType().value { - return true - } - if s&TypeFlagsNumberLike != 0 && t&TypeFlagsNumber != 0 { - return true - } - if s&TypeFlagsNumberLiteral != 0 && s&TypeFlagsEnumLiteral != 0 && t&TypeFlagsNumberLiteral != 0 && t&TypeFlagsEnumLiteral == 0 && source.AsLiteralType().value == target.AsLiteralType().value { - return true - } - if s&TypeFlagsBigIntLike != 0 && t&TypeFlagsBigInt != 0 { - return true - } - if s&TypeFlagsBooleanLike != 0 && t&TypeFlagsBoolean != 0 { - return true - } - if s&TypeFlagsESSymbolLike != 0 && t&TypeFlagsESSymbol != 0 { - return true - } - if s&TypeFlagsEnum != 0 && t&TypeFlagsEnum != 0 && source.symbol.Name == target.symbol.Name && c.isEnumTypeRelatedTo(source.symbol, target.symbol, errorReporter) { - return true - } - if s&TypeFlagsEnumLiteral != 0 && t&TypeFlagsEnumLiteral != 0 { - if s&TypeFlagsUnion != 0 && t&TypeFlagsUnion != 0 && c.isEnumTypeRelatedTo(source.symbol, target.symbol, errorReporter) { - return true - } - if s&TypeFlagsLiteral != 0 && t&TypeFlagsLiteral != 0 && source.AsLiteralType().value == target.AsLiteralType().value && c.isEnumTypeRelatedTo(source.symbol, target.symbol, errorReporter) { - return true - } - } - // In non-strictNullChecks mode, `undefined` and `null` are assignable to anything except `never`. - // Since unions and intersections may reduce to `never`, we exclude them here. - if s&TypeFlagsUndefined != 0 && (!c.strictNullChecks && t&TypeFlagsUnionOrIntersection == 0 || t&(TypeFlagsUndefined|TypeFlagsVoid) != 0) { - return true - } - if s&TypeFlagsNull != 0 && (!c.strictNullChecks && t&TypeFlagsUnionOrIntersection == 0 || t&TypeFlagsNull != 0) { - return true - } - if s&TypeFlagsObject != 0 && t&TypeFlagsNonPrimitive != 0 && !(relation == c.strictSubtypeRelation && c.IsEmptyAnonymousObjectType(source) && source.objectFlags&ObjectFlagsFreshLiteral == 0) { - return true - } - if relation == c.assignableRelation || relation == c.comparableRelation { - if s&TypeFlagsAny != 0 { - return true - } - // Type number is assignable to any computed numeric enum type or any numeric enum literal type, and - // a numeric literal type is assignable any computed numeric enum type or any numeric enum literal type - // with a matching value. These rules exist such that enums can be used for bit-flag purposes. - if s&TypeFlagsNumber != 0 && (t&TypeFlagsEnum != 0 || t&TypeFlagsNumberLiteral != 0 && t&TypeFlagsEnumLiteral != 0) { - return true - } - if s&TypeFlagsNumberLiteral != 0 && s&TypeFlagsEnumLiteral == 0 && (t&TypeFlagsEnum != 0 || t&TypeFlagsNumberLiteral != 0 && t&TypeFlagsEnumLiteral != 0 && source.AsLiteralType().value == target.AsLiteralType().value) { - return true - } - // Anything is assignable to a union containing undefined, null, and {} - if c.isUnknownLikeUnionType(target) { - return true - } - } - return false -} - -func (c *Checker) isEnumTypeRelatedTo(source *ast.Symbol, target *ast.Symbol, errorReporter ErrorReporter) bool { - sourceSymbol := core.IfElse(source.Flags&ast.SymbolFlagsEnumMember != 0, c.getParentOfSymbol(source), source) - targetSymbol := core.IfElse(target.Flags&ast.SymbolFlagsEnumMember != 0, c.getParentOfSymbol(target), target) - if sourceSymbol == targetSymbol { - return true - } - if sourceSymbol.Name != targetSymbol.Name || sourceSymbol.Flags&ast.SymbolFlagsRegularEnum == 0 || targetSymbol.Flags&ast.SymbolFlagsRegularEnum == 0 { - return false - } - key := EnumRelationKey{sourceId: ast.GetSymbolId(sourceSymbol), targetId: ast.GetSymbolId(targetSymbol)} - if entry := c.enumRelation[key]; entry != RelationComparisonResultNone && !(entry&RelationComparisonResultFailed != 0 && errorReporter != nil) { - return entry&RelationComparisonResultSucceeded != 0 - } - targetEnumType := c.getTypeOfSymbol(targetSymbol) - for _, sourceProperty := range c.getPropertiesOfType(c.getTypeOfSymbol(sourceSymbol)) { - if sourceProperty.Flags&ast.SymbolFlagsEnumMember != 0 { - targetProperty := c.getPropertyOfType(targetEnumType, sourceProperty.Name) - if targetProperty == nil || targetProperty.Flags&ast.SymbolFlagsEnumMember == 0 { - if errorReporter != nil { - errorReporter(diagnostics.Property_0_is_missing_in_type_1, c.symbolToString(sourceProperty), c.TypeToString(c.getDeclaredTypeOfSymbol(targetSymbol))) - } - c.enumRelation[key] = RelationComparisonResultFailed - return false - } - sourceValue := c.getEnumMemberValue(ast.GetDeclarationOfKind(sourceProperty, ast.KindEnumMember)).Value - targetValue := c.getEnumMemberValue(ast.GetDeclarationOfKind(targetProperty, ast.KindEnumMember)).Value - if sourceValue != targetValue { - // If we have 2 enums with *known* values that differ, they are incompatible. - if sourceValue != nil && targetValue != nil { - if errorReporter != nil { - errorReporter(diagnostics.Each_declaration_of_0_1_differs_in_its_value_where_2_was_expected_but_3_was_given, c.symbolToString(targetSymbol), c.symbolToString(targetProperty), c.valueToString(targetValue), c.valueToString(sourceValue)) - } - c.enumRelation[key] = RelationComparisonResultFailed - return false - } - // At this point we know that at least one of the values is 'undefined'. - // This may mean that we have an opaque member from an ambient enum declaration, - // or that we were not able to calculate it (which is basically an error). - // - // Either way, we can assume that it's numeric. - // If the other is a string, we have a mismatch in types. - _, sourceIsString := sourceValue.(string) - _, targetIsString := targetValue.(string) - if sourceIsString || targetIsString { - if errorReporter != nil { - knownStringValue := core.OrElse(sourceValue, targetValue) - errorReporter(diagnostics.One_value_of_0_1_is_the_string_2_and_the_other_is_assumed_to_be_an_unknown_numeric_value, c.symbolToString(targetSymbol), c.symbolToString(targetProperty), c.valueToString(knownStringValue)) - } - c.enumRelation[key] = RelationComparisonResultFailed - return false - } - } - } - } - c.enumRelation[key] = RelationComparisonResultSucceeded - return true -} - -func (c *Checker) checkTypeAssignableTo(source *Type, target *Type, errorNode *ast.Node, headMessage *diagnostics.Message) bool { - return c.checkTypeRelatedToEx(source, target, c.assignableRelation, errorNode, headMessage, nil) -} - -func (c *Checker) checkTypeAssignableToEx(source *Type, target *Type, errorNode *ast.Node, headMessage *diagnostics.Message, diagnosticOutput *[]*ast.Diagnostic) bool { - return c.checkTypeRelatedToEx(source, target, c.assignableRelation, errorNode, headMessage, diagnosticOutput) -} - -func (c *Checker) checkTypeComparableTo(source *Type, target *Type, errorNode *ast.Node, headMessage *diagnostics.Message) bool { - return c.checkTypeRelatedToEx(source, target, c.comparableRelation, errorNode, headMessage, nil) -} - -func (c *Checker) checkTypeRelatedTo(source *Type, target *Type, relation *Relation, errorNode *ast.Node) bool { - return c.checkTypeRelatedToEx(source, target, relation, errorNode, nil, nil) -} - -// Check that source is related to target according to the given relation. When errorNode is non-nil, errors are -// reported to the checker's diagnostic collection or through diagnosticOutput when non-nil. Callers can assume that -// this function only reports zero or one error to diagnosticOutput (unlike checkTypeRelatedToAndOptionallyElaborate). -func (c *Checker) checkTypeRelatedToEx( - source *Type, - target *Type, - relation *Relation, - errorNode *ast.Node, - headMessage *diagnostics.Message, - diagnosticOutput *[]*ast.Diagnostic, -) bool { - r := c.getRelater() - r.relation = relation - r.errorNode = errorNode - r.relationCount = (16_000_000 - relation.size()) / 8 - result := r.isRelatedToEx(source, target, RecursionFlagsBoth, errorNode != nil /*reportErrors*/, headMessage, IntersectionStateNone) - if r.overflow { - // Record this relation as having failed such that we don't attempt the overflowing operation again. - id := getRelationKey(source, target, IntersectionStateNone, relation == c.identityRelation, false /*ignoreConstraints*/) - relation.set(id, RelationComparisonResultFailed|core.IfElse(r.relationCount <= 0, RelationComparisonResultComplexityOverflow, RelationComparisonResultStackDepthOverflow)) - message := core.IfElse(r.relationCount <= 0, diagnostics.Excessive_complexity_comparing_types_0_and_1, diagnostics.Excessive_stack_depth_comparing_types_0_and_1) - if errorNode == nil { - errorNode = c.currentNode - } - c.reportDiagnostic(NewDiagnosticForNode(errorNode, message, c.TypeToString(source), c.TypeToString(target)), diagnosticOutput) - } else if r.errorChain != nil { - // Check if we should issue an extra diagnostic to produce a quickfix for a slightly incorrect import statement - if headMessage != nil && errorNode != nil && result == TernaryFalse && source.symbol != nil && c.exportTypeLinks.Has(source.symbol) { - links := c.exportTypeLinks.Get(source.symbol) - if links.originatingImport != nil && !ast.IsImportCall(links.originatingImport) { - helpfulRetry := c.checkTypeRelatedTo(c.getTypeOfSymbol(links.target), target, relation /*errorNode*/, nil) - if helpfulRetry { - // Likely an incorrect import. Issue a helpful diagnostic to produce a quickfix to change the import - r.relatedInfo = append(r.relatedInfo, createDiagnosticForNode(links.originatingImport, diagnostics.Type_originates_at_this_import_A_namespace_style_import_cannot_be_called_or_constructed_and_will_cause_a_failure_at_runtime_Consider_using_a_default_import_or_import_require_here_instead)) - } - } - } - c.reportDiagnostic(createDiagnosticChainFromErrorChain(r.errorChain, r.errorNode, r.relatedInfo), diagnosticOutput) - } - c.putRelater(r) - return result != TernaryFalse -} - -func createDiagnosticChainFromErrorChain(chain *ErrorChain, errorNode *ast.Node, relatedInfo []*ast.Diagnostic) *ast.Diagnostic { - for chain != nil && chain.message.ElidedInCompatibilityPyramid() { - chain = chain.next - } - if chain == nil { - return nil - } - next := createDiagnosticChainFromErrorChain(chain.next, errorNode, relatedInfo) - if next == nil { - return NewDiagnosticForNode(errorNode, chain.message, chain.args...).SetRelatedInfo(relatedInfo) - } - return ast.NewDiagnosticChain(next, chain.message, chain.args...) -} - -func (c *Checker) reportDiagnostic(diagnostic *ast.Diagnostic, diagnosticOutput *[]*ast.Diagnostic) { - if diagnostic != nil { - if diagnosticOutput != nil { - *diagnosticOutput = append(*diagnosticOutput, diagnostic) - } else { - c.diagnostics.Add(diagnostic) - } - } -} - -func (c *Checker) checkTypeAssignableToAndOptionallyElaborate(source *Type, target *Type, errorNode *ast.Node, expr *ast.Node, headMessage *diagnostics.Message, diagnosticOutput *[]*ast.Diagnostic) bool { - return c.checkTypeRelatedToAndOptionallyElaborate(source, target, c.assignableRelation, errorNode, expr, headMessage, diagnosticOutput) -} - -func (c *Checker) checkTypeRelatedToAndOptionallyElaborate(source *Type, target *Type, relation *Relation, errorNode *ast.Node, expr *ast.Node, headMessage *diagnostics.Message, diagnosticOutput *[]*ast.Diagnostic) bool { - if c.isTypeRelatedTo(source, target, relation) { - return true - } - if errorNode != nil && !c.elaborateError(expr, source, target, relation, headMessage, diagnosticOutput) { - return c.checkTypeRelatedToEx(source, target, relation, errorNode, headMessage, diagnosticOutput) - } - return false -} - -func (c *Checker) elaborateError(node *ast.Node, source *Type, target *Type, relation *Relation, headMessage *diagnostics.Message, diagnosticOutput *[]*ast.Diagnostic) bool { - if node == nil || c.isOrHasGenericConditional(target) { - return false - } - if c.elaborateDidYouMeanToCallOrConstruct(node, source, target, relation, SignatureKindConstruct, headMessage, diagnosticOutput) || - c.elaborateDidYouMeanToCallOrConstruct(node, source, target, relation, SignatureKindCall, headMessage, diagnosticOutput) { - return true - } - switch node.Kind { - case ast.KindAsExpression: - if !isConstAssertion(node) { - break - } - fallthrough - case ast.KindJsxExpression, ast.KindParenthesizedExpression: - return c.elaborateError(node.Expression(), source, target, relation, headMessage, diagnosticOutput) - case ast.KindBinaryExpression: - switch node.AsBinaryExpression().OperatorToken.Kind { - case ast.KindEqualsToken, ast.KindCommaToken: - return c.elaborateError(node.AsBinaryExpression().Right, source, target, relation, headMessage, diagnosticOutput) - } - case ast.KindObjectLiteralExpression: - return c.elaborateObjectLiteral(node, source, target, relation, diagnosticOutput) - case ast.KindArrayLiteralExpression: - return c.elaborateArrayLiteral(node, source, target, relation, diagnosticOutput) - case ast.KindArrowFunction: - return c.elaborateArrowFunction(node, source, target, relation, diagnosticOutput) - case ast.KindJsxAttributes: - return c.elaborateJsxComponents(node, source, target, relation, diagnosticOutput) - } - return false -} - -func (c *Checker) isOrHasGenericConditional(t *Type) bool { - return t.flags&TypeFlagsConditional != 0 || (t.flags&TypeFlagsIntersection != 0 && core.Some(t.Types(), c.isOrHasGenericConditional)) -} - -func (c *Checker) elaborateDidYouMeanToCallOrConstruct(node *ast.Node, source *Type, target *Type, relation *Relation, kind SignatureKind, headMessage *diagnostics.Message, diagnosticOutput *[]*ast.Diagnostic) bool { - if core.Some(c.getSignaturesOfType(source, kind), func(s *Signature) bool { - returnType := c.getReturnTypeOfSignature(s) - return returnType.flags&(TypeFlagsAny|TypeFlagsNever) == 0 && c.checkTypeRelatedTo(returnType, target, relation, nil /*errorNode*/) - }) { - var diags []*ast.Diagnostic - if !c.checkTypeRelatedToEx(source, target, relation, node, headMessage, &diags) { - diagnostic := diags[0] - message := core.IfElse(kind == SignatureKindConstruct, - diagnostics.Did_you_mean_to_use_new_with_this_expression, - diagnostics.Did_you_mean_to_call_this_expression) - c.reportDiagnostic(diagnostic.AddRelatedInfo(createDiagnosticForNode(node, message)), diagnosticOutput) - return true - } - } - return false -} - -func (c *Checker) elaborateObjectLiteral(node *ast.Node, source *Type, target *Type, relation *Relation, diagnosticOutput *[]*ast.Diagnostic) bool { - if target.flags&(TypeFlagsPrimitive|TypeFlagsNever) != 0 { - return false - } - reportedError := false - for _, prop := range node.AsObjectLiteralExpression().Properties.Nodes { - if ast.IsSpreadAssignment(prop) { - continue - } - nameType := c.getLiteralTypeFromProperty(c.getSymbolOfDeclaration(prop), TypeFlagsStringOrNumberLiteralOrUnique, false) - if nameType == nil || nameType.flags&TypeFlagsNever != 0 { - continue - } - switch prop.Kind { - case ast.KindSetAccessor, ast.KindGetAccessor, ast.KindMethodDeclaration, ast.KindShorthandPropertyAssignment: - reportedError = c.elaborateElement(source, target, relation, prop.Name(), nil, nameType, nil, diagnosticOutput) || reportedError - case ast.KindPropertyAssignment: - message := core.IfElse(ast.IsComputedNonLiteralName(prop.Name()), diagnostics.Type_of_computed_property_s_value_is_0_which_is_not_assignable_to_type_1, nil) - reportedError = c.elaborateElement(source, target, relation, prop.Name(), prop.Initializer(), nameType, message, diagnosticOutput) || reportedError - } - } - return reportedError -} - -func (c *Checker) elaborateArrayLiteral(node *ast.Node, source *Type, target *Type, relation *Relation, diagnosticOutput *[]*ast.Diagnostic) bool { - if target.flags&(TypeFlagsPrimitive|TypeFlagsNever) != 0 { - return false - } - if !c.isTupleLikeType(source) { - c.pushContextualType(node, target, false /*isCache*/) - source = c.checkArrayLiteral(node, CheckModeContextual|CheckModeForceTuple) - c.popContextualType() - if !c.isTupleLikeType(source) { - return false - } - } - reportedError := false - for i, element := range node.AsArrayLiteralExpression().Elements.Nodes { - if ast.IsOmittedExpression(element) || c.isTupleLikeType(target) && c.getPropertyOfType(target, jsnum.Number(i).String()) == nil { - continue - } - nameType := c.getNumberLiteralType(jsnum.Number(i)) - checkNode := c.getEffectiveCheckNode(element) - reportedError = c.elaborateElement(source, target, relation, checkNode, checkNode, nameType, nil, diagnosticOutput) || reportedError - } - return reportedError -} - -func (c *Checker) elaborateElement(source *Type, target *Type, relation *Relation, prop *ast.Node, next *ast.Node, nameType *Type, errorMessage *diagnostics.Message, diagnosticOutput *[]*ast.Diagnostic) bool { - targetPropType := c.getBestMatchIndexedAccessTypeOrUndefined(source, target, nameType) - if targetPropType == nil || targetPropType.flags&TypeFlagsIndexedAccess != 0 { - // Don't elaborate on indexes on generic variables - return false - } - sourcePropType := c.getIndexedAccessTypeOrUndefined(source, nameType, AccessFlagsNone, nil, nil) - if sourcePropType == nil || c.checkTypeRelatedTo(sourcePropType, targetPropType, relation, nil /*errorNode*/) { - // Don't elaborate on indexes on generic variables or when types match - return false - } - if next != nil && c.elaborateError(next, sourcePropType, targetPropType, relation, nil /*headMessage*/, diagnosticOutput) { - return true - } - // Issue error on the prop itself, since the prop couldn't elaborate the error - var diags []*ast.Diagnostic - // Use the expression type, if available - specificSource := sourcePropType - if next != nil { - specificSource = c.checkExpressionForMutableLocationWithContextualType(next, sourcePropType) - } - if c.exactOptionalPropertyTypes && c.isExactOptionalPropertyMismatch(specificSource, targetPropType) { - diags = append(diags, createDiagnosticForNode(prop, diagnostics.Type_0_is_not_assignable_to_type_1_with_exactOptionalPropertyTypes_Colon_true_Consider_adding_undefined_to_the_type_of_the_target, c.TypeToString(specificSource), c.TypeToString(targetPropType))) - } else { - propName := c.getPropertyNameFromIndex(nameType, nil /*accessNode*/) - targetIsOptional := core.OrElse(c.getPropertyOfType(target, propName), c.unknownSymbol).Flags&ast.SymbolFlagsOptional != 0 - sourceIsOptional := core.OrElse(c.getPropertyOfType(source, propName), c.unknownSymbol).Flags&ast.SymbolFlagsOptional != 0 - targetPropType = c.removeMissingType(targetPropType, targetIsOptional) - sourcePropType = c.removeMissingType(sourcePropType, targetIsOptional && sourceIsOptional) - result := c.checkTypeRelatedToEx(specificSource, targetPropType, relation, prop, errorMessage, &diags) - if result && specificSource != sourcePropType { - // If for whatever reason the expression type doesn't yield an error, make sure we still issue an error on the sourcePropType - c.checkTypeRelatedToEx(sourcePropType, targetPropType, relation, prop, errorMessage, &diags) - } - } - if len(diags) == 0 { - return false - } - diagnostic := diags[0] - var propertyName string - var targetProp *ast.Symbol - if isTypeUsableAsPropertyName(nameType) { - propertyName = getPropertyNameFromType(nameType) - targetProp = c.getPropertyOfType(target, propertyName) - } - issuedElaboration := false - if targetProp == nil { - indexInfo := c.getApplicableIndexInfo(target, nameType) - if indexInfo != nil && indexInfo.declaration != nil && !c.program.IsSourceFileDefaultLibrary(ast.GetSourceFileOfNode(indexInfo.declaration).Path()) { - issuedElaboration = true - diagnostic.AddRelatedInfo(createDiagnosticForNode(indexInfo.declaration, diagnostics.The_expected_type_comes_from_this_index_signature)) - } - } - if !issuedElaboration && (targetProp != nil && len(targetProp.Declarations) != 0 || target.symbol != nil && len(target.symbol.Declarations) != 0) { - var targetNode *ast.Node - if targetProp != nil && len(targetProp.Declarations) != 0 { - targetNode = targetProp.Declarations[0] - } else { - targetNode = target.symbol.Declarations[0] - } - if propertyName == "" || nameType.flags&TypeFlagsUniqueESSymbol != 0 { - propertyName = c.TypeToString(nameType) - } - if !c.program.IsSourceFileDefaultLibrary(ast.GetSourceFileOfNode(targetNode).Path()) { - diagnostic.AddRelatedInfo(createDiagnosticForNode(targetNode, diagnostics.The_expected_type_comes_from_property_0_which_is_declared_here_on_type_1, propertyName, c.TypeToString(target))) - } - } - c.reportDiagnostic(diagnostic, diagnosticOutput) - return true -} - -func (c *Checker) getBestMatchIndexedAccessTypeOrUndefined(source *Type, target *Type, nameType *Type) *Type { - idx := c.getIndexedAccessTypeOrUndefined(target, nameType, AccessFlagsNone, nil, nil) - if idx != nil { - return idx - } - if target.flags&TypeFlagsUnion != 0 { - best := c.getBestMatchingType(source, target, c.compareTypesAssignableSimple) - if best != nil { - return c.getIndexedAccessTypeOrUndefined(best, nameType, AccessFlagsNone, nil, nil) - } - } - return nil -} - -func (c *Checker) checkExpressionForMutableLocationWithContextualType(next *ast.Node, sourcePropType *Type) *Type { - c.pushContextualType(next, sourcePropType, false /*isCache*/) - result := c.checkExpressionForMutableLocation(next, CheckModeContextual) - c.popContextualType() - return result -} - -func (c *Checker) elaborateArrowFunction(node *ast.Node, source *Type, target *Type, relation *Relation, diagnosticOutput *[]*ast.Diagnostic) bool { - // Don't elaborate blocks or functions with annotated parameter types - if ast.IsBlock(node.Body()) || core.Some(node.Parameters(), hasType) { - return false - } - sourceSig := c.getSingleCallSignature(source) - if sourceSig == nil { - return false - } - targetSignatures := c.getSignaturesOfType(target, SignatureKindCall) - if len(targetSignatures) == 0 { - return false - } - returnExpression := node.Body() - sourceReturn := c.getReturnTypeOfSignature(sourceSig) - targetReturn := c.getUnionType(core.Map(targetSignatures, c.getReturnTypeOfSignature)) - if c.checkTypeRelatedTo(sourceReturn, targetReturn, relation, nil /*errorNode*/) { - return false - } - if returnExpression != nil && c.elaborateError(returnExpression, sourceReturn, targetReturn, relation, nil /*headMessage*/, diagnosticOutput) { - return true - } - var diags []*ast.Diagnostic - c.checkTypeRelatedToEx(sourceReturn, targetReturn, relation, returnExpression, nil /*headMessage*/, &diags) - if len(diags) != 0 { - diagnostic := diags[0] - if target.symbol != nil && len(target.symbol.Declarations) != 0 { - diagnostic.AddRelatedInfo(createDiagnosticForNode(target.symbol.Declarations[0], diagnostics.The_expected_type_comes_from_the_return_type_of_this_signature)) - } - if getFunctionFlags(node)&FunctionFlagsAsync == 0 && c.getTypeOfPropertyOfType(sourceReturn, "then") == nil && c.checkTypeRelatedTo(c.createPromiseType(sourceReturn), targetReturn, relation, nil /*errorNode*/) { - diagnostic.AddRelatedInfo(createDiagnosticForNode(node, diagnostics.Did_you_mean_to_mark_this_function_as_async)) - } - c.reportDiagnostic(diagnostic, diagnosticOutput) - return true - } - return false -} - -// A type is 'weak' if it is an object type with at least one optional property -// and no required properties, call/construct signatures or index signatures -func (c *Checker) isWeakType(t *Type) bool { - if t.flags&TypeFlagsObject != 0 { - resolved := c.resolveStructuredTypeMembers(t) - return len(resolved.signatures) == 0 && len(resolved.indexInfos) == 0 && len(resolved.properties) > 0 && core.Every(resolved.properties, func(p *ast.Symbol) bool { - return p.Flags&ast.SymbolFlagsOptional != 0 - }) - } - if t.flags&TypeFlagsSubstitution != 0 { - return c.isWeakType(t.AsSubstitutionType().baseType) - } - if t.flags&TypeFlagsIntersection != 0 { - return core.Every(t.Types(), c.isWeakType) - } - return false -} - -func (c *Checker) hasCommonProperties(source *Type, target *Type, isComparingJsxAttributes bool) bool { - for _, prop := range c.getPropertiesOfType(source) { - if c.isKnownProperty(target, prop.Name, isComparingJsxAttributes) { - return true - } - } - return false -} - -/** - * Check if a property with the given name is known anywhere in the given type. In an object type, a property - * is considered known if - * 1. the object type is empty and the check is for assignability, or - * 2. if the object type has index signatures, or - * 3. if the property is actually declared in the object type - * (this means that 'toString', for example, is not usually a known property). - * 4. In a union or intersection type, - * a property is considered known if it is known in any constituent type. - * @param targetType a type to search a given name in - * @param name a property name to search - * @param isComparingJsxAttributes a boolean flag indicating whether we are searching in JsxAttributesType - */ -func (c *Checker) isKnownProperty(targetType *Type, name string, isComparingJsxAttributes bool) bool { - if targetType.flags&TypeFlagsObject != 0 { - // For backwards compatibility a symbol-named property is satisfied by a string index signature. This - // is incorrect and inconsistent with element access expressions, where it is an error, so eventually - // we should remove this exception. - if c.getPropertyOfObjectType(targetType, name) != nil || - c.getApplicableIndexInfoForName(targetType, name) != nil || - isLateBoundName(name) && c.getIndexInfoOfType(targetType, c.stringType) != nil || - isComparingJsxAttributes && isHyphenatedJsxName(name) { - // For JSXAttributes, if the attribute has a hyphenated name, consider that the attribute to be known. - return true - } - } - if targetType.flags&TypeFlagsSubstitution != 0 { - return c.isKnownProperty(targetType.AsSubstitutionType().baseType, name, isComparingJsxAttributes) - } - if targetType.flags&TypeFlagsUnionOrIntersection != 0 && isExcessPropertyCheckTarget(targetType) { - for _, t := range targetType.Types() { - if c.isKnownProperty(t, name, isComparingJsxAttributes) { - return true - } - } - } - return false -} - -func isHyphenatedJsxName(name string) bool { - return strings.Contains(name, "-") -} - -func isExcessPropertyCheckTarget(t *Type) bool { - return t.flags&TypeFlagsObject != 0 && t.objectFlags&ObjectFlagsObjectLiteralPatternWithComputedProperties == 0 || - t.flags&TypeFlagsNonPrimitive != 0 || - t.flags&TypeFlagsSubstitution != 0 && isExcessPropertyCheckTarget(t.AsSubstitutionType().baseType) || - t.flags&TypeFlagsUnion != 0 && core.Some(t.Types(), isExcessPropertyCheckTarget) || - t.flags&TypeFlagsIntersection != 0 && core.Every(t.Types(), isExcessPropertyCheckTarget) -} - -// Return true if the given type is deeply nested. We consider this to be the case when the given stack contains -// maxDepth or more occurrences of types with the same recursion identity as the given type. The recursion identity -// provides a shared identity for type instantiations that repeat in some (possibly infinite) pattern. For example, -// in `type Deep = { next: Deep> }`, repeatedly referencing the `next` property leads to an infinite -// sequence of ever deeper instantiations with the same recursion identity (in this case the symbol associated with -// the object type literal). -// A homomorphic mapped type is considered deeply nested if its target type is deeply nested, and an intersection is -// considered deeply nested if any constituent of the intersection is deeply nested. -// It is possible, though highly unlikely, for the deeply nested check to be true in a situation where a chain of -// instantiations is not infinitely expanding. Effectively, we will generate a false positive when two types are -// structurally equal to at least maxDepth levels, but unequal at some level beyond that. -func (c *Checker) isDeeplyNestedType(t *Type, stack []*Type, maxDepth int) bool { - if len(stack) >= maxDepth { - if t.objectFlags&ObjectFlagsInstantiatedMapped == ObjectFlagsInstantiatedMapped { - t = c.getMappedTargetWithSymbol(t) - } - if t.flags&TypeFlagsIntersection != 0 { - for _, t := range t.Types() { - if c.isDeeplyNestedType(t, stack, maxDepth) { - return true - } - } - } - identity := getRecursionIdentity(t) - count := 0 - lastTypeId := TypeId(0) - for _, t := range stack { - if c.hasMatchingRecursionIdentity(t, identity) { - // We only count occurrences with a higher type id than the previous occurrence, since higher - // type ids are an indicator of newer instantiations caused by recursion. - if t.id >= lastTypeId { - count++ - if count >= maxDepth { - return true - } - } - lastTypeId = t.id - } - } - } - return false -} - -// Unwrap nested homomorphic mapped types and return the deepest target type that has a symbol. This better -// preserves unique type identities for mapped types applied to explicitly written object literals. For example -// in `Mapped<{ x: Mapped<{ x: Mapped<{ x: string }>}>}>`, each of the mapped type applications will have a -// unique recursion identity (that of their target object type literal) and thus avoid appearing deeply nested. -func (c *Checker) getMappedTargetWithSymbol(t *Type) *Type { - for { - if t.objectFlags&ObjectFlagsInstantiatedMapped == ObjectFlagsInstantiatedMapped { - target := c.getModifiersTypeFromMappedType(t) - if target != nil && (target.symbol != nil || target.flags&TypeFlagsIntersection != 0 && - core.Some(target.Types(), func(t *Type) bool { return t.symbol != nil })) { - t = target - continue - } - } - return t - } -} - -func (c *Checker) hasMatchingRecursionIdentity(t *Type, identity RecursionId) bool { - if t.objectFlags&ObjectFlagsInstantiatedMapped == ObjectFlagsInstantiatedMapped { - t = c.getMappedTargetWithSymbol(t) - } - if t.flags&TypeFlagsIntersection != 0 { - for _, t := range t.Types() { - if c.hasMatchingRecursionIdentity(t, identity) { - return true - } - } - return false - } - return getRecursionIdentity(t) == identity -} - -// The recursion identity of a type is an object identity that is shared among multiple instantiations of the type. -// We track recursion identities in order to identify deeply nested and possibly infinite type instantiations with -// the same origin. For example, when type parameters are in scope in an object type such as { x: T }, all -// instantiations of that type have the same recursion identity. The default recursion identity is the object -// identity of the type, meaning that every type is unique. Generally, types with constituents that could circularly -// reference the type have a recursion identity that differs from the object identity. -func getRecursionIdentity(t *Type) RecursionId { - // Object and array literals are known not to contain recursive references and don't need a recursion identity. - if t.flags&TypeFlagsObject != 0 && !isObjectOrArrayLiteralType(t) { - if t.objectFlags&ObjectFlagsReference != 0 && t.AsTypeReference().node != nil { - // Deferred type references are tracked through their associated AST node. This gives us finer - // granularity than using their associated target because each manifest type reference has a - // unique AST node. - return asRecursionId(t.AsTypeReference().node) - } - if t.symbol != nil && !(t.objectFlags&ObjectFlagsAnonymous != 0 && t.symbol.Flags&ast.SymbolFlagsClass != 0) { - // We track object types that have a symbol by that symbol (representing the origin of the type), but - // exclude the static side of a class since it shares its symbol with the instance side. - return asRecursionId(t.symbol) - } - if isTupleType(t) { - return asRecursionId(t.Target()) - } - } - if t.flags&TypeFlagsTypeParameter != 0 && t.symbol != nil { - // We use the symbol of the type parameter such that all "fresh" instantiations of that type parameter - // have the same recursion identity. - return asRecursionId(t.symbol) - } - if t.flags&TypeFlagsIndexedAccess != 0 { - // Identity is the leftmost object type in a chain of indexed accesses, eg, in A[P1][P2][P3] it is A. - t = t.AsIndexedAccessType().objectType - for t.flags&TypeFlagsIndexedAccess != 0 { - t = t.AsIndexedAccessType().objectType - } - return asRecursionId(t) - } - if t.flags&TypeFlagsConditional != 0 { - // The root object represents the origin of the conditional type - return asRecursionId(t.AsConditionalType().root.node.AsNode()) - } - return asRecursionId(t) -} - -func (c *Checker) getBestMatchingType(source *Type, target *Type, isRelatedTo func(source *Type, target *Type) Ternary) *Type { - if t := c.findMatchingDiscriminantType(source, target, isRelatedTo); t != nil { - return t - } - if t := c.findMatchingTypeReferenceOrTypeAliasReference(source, target); t != nil { - return t - } - if t := c.findBestTypeForObjectLiteral(source, target); t != nil { - return t - } - if t := c.findBestTypeForInvokable(source, target, SignatureKindCall); t != nil { - return t - } - if t := c.findBestTypeForInvokable(source, target, SignatureKindConstruct); t != nil { - return t - } - return c.findMostOverlappyType(source, target) -} - -func (c *Checker) findMatchingTypeReferenceOrTypeAliasReference(source *Type, unionTarget *Type) *Type { - sourceObjectFlags := source.objectFlags - if sourceObjectFlags&(ObjectFlagsReference|ObjectFlagsAnonymous) != 0 && unionTarget.flags&TypeFlagsUnion != 0 { - for _, target := range unionTarget.Types() { - if target.flags&TypeFlagsObject != 0 { - overlapObjFlags := sourceObjectFlags & target.objectFlags - if overlapObjFlags&ObjectFlagsReference != 0 && source.Target() == target.Target() { - return target - } - if overlapObjFlags&ObjectFlagsAnonymous != 0 && source.alias != nil && target.alias != nil && source.alias.symbol == target.alias.symbol { - return target - } - } - } - } - return nil -} - -func (c *Checker) findBestTypeForInvokable(source *Type, unionTarget *Type, kind SignatureKind) *Type { - if len(c.getSignaturesOfType(source, kind)) != 0 { - return core.Find(unionTarget.Types(), func(t *Type) bool { return len(c.getSignaturesOfType(t, kind)) != 0 }) - } - return nil -} - -func (c *Checker) findMostOverlappyType(source *Type, unionTarget *Type) *Type { - var bestMatch *Type - if source.flags&(TypeFlagsPrimitive|TypeFlagsInstantiablePrimitive) == 0 { - matchingCount := 0 - for _, target := range unionTarget.Types() { - if target.flags&(TypeFlagsPrimitive|TypeFlagsInstantiablePrimitive) == 0 { - overlap := c.getIntersectionType([]*Type{c.getIndexType(source), c.getIndexType(target)}) - if overlap.flags&TypeFlagsIndex != 0 { - // perfect overlap of keys - return target - } else if isUnitType(overlap) || overlap.flags&TypeFlagsUnion != 0 { - // We only want to account for literal types otherwise. - // If we have a union of index types, it seems likely that we - // needed to elaborate between two generic mapped types anyway. - length := 1 - if overlap.flags&TypeFlagsUnion != 0 { - length = core.CountWhere(overlap.Types(), isUnitType) - } - if length >= matchingCount { - bestMatch = target - matchingCount = length - } - } - } - } - } - return bestMatch -} - -func (c *Checker) findBestTypeForObjectLiteral(source *Type, unionTarget *Type) *Type { - if source.objectFlags&ObjectFlagsObjectLiteral != 0 && someType(unionTarget, c.isArrayLikeType) { - return core.Find(unionTarget.Types(), func(t *Type) bool { return !c.isArrayLikeType(t) }) - } - return nil -} - -func (c *Checker) shouldReportUnmatchedPropertyError(source *Type, target *Type) bool { - typeCallSignatures := c.getSignaturesOfStructuredType(source, SignatureKindCall) - typeConstructSignatures := c.getSignaturesOfStructuredType(source, SignatureKindConstruct) - typeProperties := c.getPropertiesOfObjectType(source) - if (len(typeCallSignatures) != 0 || len(typeConstructSignatures) != 0) && len(typeProperties) == 0 { - if (len(c.getSignaturesOfType(target, SignatureKindCall)) != 0 && len(typeCallSignatures) != 0) || - len(c.getSignaturesOfType(target, SignatureKindConstruct)) != 0 && len(typeConstructSignatures) != 0 { - // target has similar signature kinds to source, still focus on the unmatched property - return true - } - return false - } - return true -} - -func (c *Checker) getUnmatchedProperty(source *Type, target *Type, requireOptionalProperties bool, matchDiscriminantProperties bool) *ast.Symbol { - return c.getUnmatchedPropertiesWorker(source, target, requireOptionalProperties, matchDiscriminantProperties, nil) -} - -func (c *Checker) getUnmatchedProperties(source *Type, target *Type, requireOptionalProperties bool, matchDiscriminantProperties bool) []*ast.Symbol { - var props []*ast.Symbol - c.getUnmatchedPropertiesWorker(source, target, requireOptionalProperties, matchDiscriminantProperties, &props) - return props -} - -func (c *Checker) getUnmatchedPropertiesWorker(source *Type, target *Type, requireOptionalProperties bool, matchDiscriminantProperties bool, propsOut *[]*ast.Symbol) *ast.Symbol { - properties := c.getPropertiesOfType(target) - for _, targetProp := range properties { - // TODO: remove this when we support static private identifier fields and find other solutions to get privateNamesAndStaticFields test to pass - if isStaticPrivateIdentifierProperty(targetProp) { - continue - } - if requireOptionalProperties || targetProp.Flags&ast.SymbolFlagsOptional == 0 && targetProp.CheckFlags&ast.CheckFlagsPartial == 0 { - sourceProp := c.getPropertyOfType(source, targetProp.Name) - if sourceProp == nil { - if propsOut == nil { - return targetProp - } - *propsOut = append(*propsOut, targetProp) - } else if matchDiscriminantProperties { - targetType := c.getTypeOfSymbol(targetProp) - if targetType.flags&TypeFlagsUnit != 0 { - sourceType := c.getTypeOfSymbol(sourceProp) - if !(sourceType.flags&TypeFlagsAny != 0 || c.getRegularTypeOfLiteralType(sourceType) == c.getRegularTypeOfLiteralType(targetType)) { - if propsOut == nil { - return targetProp - } - *propsOut = append(*propsOut, targetProp) - } - } - } - } - } - return nil -} - -func excludeProperties(properties []*ast.Symbol, excludedProperties collections.Set[string]) []*ast.Symbol { - if excludedProperties.Len() == 0 || len(properties) == 0 { - return properties - } - var reduced []*ast.Symbol - var excluded bool - for i, prop := range properties { - if !excludedProperties.Has(prop.Name) { - if excluded { - reduced = append(reduced, prop) - } - } else if !excluded { - reduced = slices.Clip(properties[:i]) - excluded = true - } - } - if excluded { - return reduced - } - return properties -} - -type TypeDiscriminator struct { - c *Checker - props []*ast.Symbol - isRelatedTo func(*Type, *Type) Ternary -} - -func (d *TypeDiscriminator) len() int { - return len(d.props) -} - -func (d *TypeDiscriminator) name(index int) string { - return d.props[index].Name -} - -func (d *TypeDiscriminator) matches(index int, t *Type) bool { - propType := d.c.getTypeOfSymbol(d.props[index]) - for _, s := range propType.Distributed() { - if d.isRelatedTo(s, t) != TernaryFalse { - return true - } - } - return false -} - -// Keep this up-to-date with the same logic within `getApparentTypeOfContextualType`, since they should behave similarly -func (c *Checker) findMatchingDiscriminantType(source *Type, target *Type, isRelatedTo func(source *Type, target *Type) Ternary) *Type { - if target.flags&TypeFlagsUnion != 0 && source.flags&(TypeFlagsIntersection|TypeFlagsObject) != 0 { - if match := c.getMatchingUnionConstituentForType(target, source); match != nil { - return match - } - if discriminantProperties := c.findDiscriminantProperties(c.getPropertiesOfType(source), target); len(discriminantProperties) != 0 { - discriminator := &TypeDiscriminator{c: c, props: discriminantProperties, isRelatedTo: isRelatedTo} - if discriminated := c.discriminateTypeByDiscriminableItems(target, discriminator); discriminated != target { - return discriminated - } - } - } - return nil -} - -func (c *Checker) findDiscriminantProperties(sourceProperties []*ast.Symbol, target *Type) []*ast.Symbol { - var result []*ast.Symbol - for _, sourceProperty := range sourceProperties { - if c.isDiscriminantProperty(target, sourceProperty.Name) { - result = append(result, sourceProperty) - } - } - return result -} - -func (c *Checker) isDiscriminantProperty(t *Type, name string) bool { - if t != nil && t.flags&TypeFlagsUnion != 0 { - prop := c.getUnionOrIntersectionProperty(t, name, false /*skipObjectFunctionPropertyAugment*/) - if prop != nil && prop.CheckFlags&ast.CheckFlagsSyntheticProperty != 0 { - if prop.CheckFlags&ast.CheckFlagsIsDiscriminantComputed == 0 { - prop.CheckFlags |= ast.CheckFlagsIsDiscriminantComputed - if prop.CheckFlags&ast.CheckFlagsNonUniformAndLiteral == ast.CheckFlagsNonUniformAndLiteral && !c.isGenericType(c.getTypeOfSymbol(prop)) { - prop.CheckFlags |= ast.CheckFlagsIsDiscriminant - } - } - return prop.CheckFlags&ast.CheckFlagsIsDiscriminant != 0 - } - } - return false -} - -func (c *Checker) getMatchingUnionConstituentForType(unionType *Type, t *Type) *Type { - keyPropertyName := c.getKeyPropertyName(unionType) - if keyPropertyName == "" { - return nil - } - propType := c.getTypeOfPropertyOfType(t, keyPropertyName) - if propType == nil { - return nil - } - return c.getConstituentTypeForKeyType(unionType, propType) -} - -// Return the name of a discriminant property for which it was possible and feasible to construct a map of -// constituent types keyed by the literal types of the property by that name in each constituent type. Return -// an empty string if no such discriminant property exists. -func (c *Checker) getKeyPropertyName(t *Type) string { - u := t.AsUnionType() - if u.keyPropertyName == "" { - u.keyPropertyName, u.constituentMap = c.computeKeyPropertyNameAndMap(t) - } - if u.keyPropertyName == ast.InternalSymbolNameMissing { - return "" - } - return u.keyPropertyName -} - -// Given a union type for which getKeyPropertyName returned a non-empty string, return the constituent -// that corresponds to the given key type for that property name. -func (c *Checker) getConstituentTypeForKeyType(t *Type, keyType *Type) *Type { - result := t.AsUnionType().constituentMap[c.getRegularTypeOfLiteralType(keyType)] - if result != c.unknownType { - return result - } - return nil -} - -func (c *Checker) computeKeyPropertyNameAndMap(t *Type) (string, map[*Type]*Type) { - types := t.Types() - if len(types) < 10 || t.objectFlags&ObjectFlagsPrimitiveUnion != 0 || core.CountWhere(types, isObjectOrInstantiableNonPrimitive) < 10 { - return ast.InternalSymbolNameMissing, nil - } - keyPropertyName := c.getKeyPropertyCandidateName(types) - if keyPropertyName == "" { - return ast.InternalSymbolNameMissing, nil - } - mapByKeyProperty := c.mapTypesByKeyProperty(types, keyPropertyName) - if mapByKeyProperty == nil { - return ast.InternalSymbolNameMissing, nil - } - return keyPropertyName, mapByKeyProperty -} - -func isObjectOrInstantiableNonPrimitive(t *Type) bool { - return t.flags&(TypeFlagsObject|TypeFlagsInstantiableNonPrimitive) != 0 -} - -func (c *Checker) getKeyPropertyCandidateName(types []*Type) string { - for _, t := range types { - if t.flags&(TypeFlagsObject|TypeFlagsInstantiableNonPrimitive) != 0 { - for _, p := range c.getPropertiesOfType(t) { - if isUnitType(c.getTypeOfSymbol(p)) { - return p.Name - } - } - } - } - return "" -} - -// Given a set of constituent types and a property name, create and return a map keyed by the literal -// types of the property by that name in each constituent type. No map is returned if some key property -// has a non-literal type or if less than 10 or less than 50% of the constituents have a unique key. -// Entries with duplicate keys have unknownType as the value. -func (c *Checker) mapTypesByKeyProperty(types []*Type, keyPropertyName string) map[*Type]*Type { - typesByKey := make(map[*Type]*Type) - count := 0 - for _, t := range types { - if t.flags&(TypeFlagsObject|TypeFlagsIntersection|TypeFlagsInstantiableNonPrimitive) != 0 { - discriminant := c.getTypeOfPropertyOfType(t, keyPropertyName) - if discriminant == nil || !isLiteralType(discriminant) { - return nil - } - duplicate := false - for _, d := range discriminant.Distributed() { - key := c.getRegularTypeOfLiteralType(d) - if existing := typesByKey[key]; existing == nil { - typesByKey[key] = t - } else if existing != c.unknownType { - typesByKey[key] = c.unknownType - duplicate = true - } - } - if !duplicate { - count++ - } - } - } - if count >= 10 && count*2 >= len(types) { - return typesByKey - } - return nil -} - -type Discriminator interface { - len() int // Number of discriminant properties - name(index int) string // Property name of index-th discriminator - matches(index int, t *Type) bool // True if index-th discriminator matches the given type -} - -func (c *Checker) discriminateTypeByDiscriminableItems(target *Type, discriminator Discriminator) *Type { - types := target.Types() - include := make([]Ternary, len(types)) - for i, t := range types { - if t.flags&TypeFlagsPrimitive == 0 { - include[i] = TernaryTrue - } - } - for n := range discriminator.len() { - // If the remaining target types include at least one with a matching discriminant, eliminate those that - // have non-matching discriminants. This ensures that we ignore erroneous discriminators and gradually - // refine the target set without eliminating every constituent (which would lead to `never`). - matched := false - for i := range types { - if include[i] != TernaryFalse { - targetType := c.getTypeOfPropertyOrIndexSignatureOfType(types[i], discriminator.name(n)) - if targetType != nil { - if discriminator.matches(n, targetType) { - matched = true - } else { - include[i] = TernaryMaybe - } - } - } - } - // Turn each Ternary.Maybe into Ternary.False if there was a match. Otherwise, revert to Ternary.True. - for i := range types { - if include[i] == TernaryMaybe { - if matched { - include[i] = TernaryFalse - } else { - include[i] = TernaryTrue - } - } - } - } - if slices.Contains(include, TernaryFalse) { - var filteredTypes []*Type - for i, t := range types { - if include[i] == TernaryTrue { - filteredTypes = append(filteredTypes, t) - } - } - filtered := c.getUnionTypeEx(filteredTypes, UnionReductionNone, nil, nil) - if filtered.flags&TypeFlagsNever == 0 { - return filtered - } - } - return target -} - -func (c *Checker) filterPrimitivesIfContainsNonPrimitive(unionType *Type) *Type { - if c.maybeTypeOfKind(unionType, TypeFlagsNonPrimitive) { - result := c.filterType(unionType, isNonPrimitiveType) - if result.flags&TypeFlagsNever == 0 { - return result - } - } - return unionType -} - -func isNonPrimitiveType(t *Type) bool { - return t.flags&TypeFlagsPrimitive == 0 -} - -func (c *Checker) getTypeNamesForErrorDisplay(left *Type, right *Type) (string, string) { - var leftStr string - if c.symbolValueDeclarationIsContextSensitive(left.symbol) { - leftStr = c.typeToString(left, left.symbol.ValueDeclaration) - } else { - leftStr = c.TypeToString(left) - } - var rightStr string - if c.symbolValueDeclarationIsContextSensitive(right.symbol) { - rightStr = c.typeToString(right, right.symbol.ValueDeclaration) - } else { - rightStr = c.TypeToString(right) - } - if leftStr == rightStr { - leftStr = c.getTypeNameForErrorDisplay(left) - rightStr = c.getTypeNameForErrorDisplay(right) - } - return leftStr, rightStr -} - -func (c *Checker) getTypeNameForErrorDisplay(t *Type) string { - return c.typeToStringEx(t, nil /*enclosingDeclaration*/, TypeFormatFlagsUseFullyQualifiedType) -} - -func (c *Checker) symbolValueDeclarationIsContextSensitive(symbol *ast.Symbol) bool { - return symbol != nil && symbol.ValueDeclaration != nil && ast.IsExpression(symbol.ValueDeclaration) && !c.isContextSensitive(symbol.ValueDeclaration) -} - -func (c *Checker) typeCouldHaveTopLevelSingletonTypes(t *Type) bool { - // Okay, yes, 'boolean' is a union of 'true | false', but that's not useful - // in error reporting scenarios. If you need to use this function but that detail matters, - // feel free to add a flag. - if t.flags&TypeFlagsBoolean != 0 { - return false - } - if t.flags&TypeFlagsUnionOrIntersection != 0 { - return core.Some(t.Types(), c.typeCouldHaveTopLevelSingletonTypes) - } - if t.flags&TypeFlagsInstantiable != 0 { - constraint := c.getConstraintOfType(t) - if constraint != nil && constraint != t { - return c.typeCouldHaveTopLevelSingletonTypes(constraint) - } - } - return isUnitType(t) || t.flags&TypeFlagsTemplateLiteral != 0 || t.flags&TypeFlagsStringMapping != 0 -} - -func (c *Checker) getVariances(t *Type) []VarianceFlags { - // Arrays and tuples are known to be covariant, no need to spend time computing this. - if t == c.globalArrayType || t == c.globalReadonlyArrayType || t.objectFlags&ObjectFlagsTuple != 0 { - return c.arrayVariances - } - return c.getVariancesWorker(t.symbol, t.AsInterfaceType().TypeParameters()) -} - -func (c *Checker) getAliasVariances(symbol *ast.Symbol) []VarianceFlags { - return c.getVariancesWorker(symbol, c.typeAliasLinks.Get(symbol).typeParameters) -} - -// Return an array containing the variance of each type parameter. The variance is effectively -// a digest of the type comparisons that occur for each type argument when instantiations of the -// generic type are structurally compared. We infer the variance information by comparing -// instantiations of the generic type for type arguments with known relations. The function -// returns an empty slice when invoked recursively for the given generic type. -func (c *Checker) getVariancesWorker(symbol *ast.Symbol, typeParameters []*Type) []VarianceFlags { - links := c.varianceLinks.Get(symbol) - if links.variances == nil { - oldVarianceComputation := c.inVarianceComputation - saveResolutionStart := c.resolutionStart - if !c.inVarianceComputation { - c.inVarianceComputation = true - c.resolutionStart = len(c.typeResolutions) - } - links.variances = []VarianceFlags{} - variances := make([]VarianceFlags, len(typeParameters)) - for i, tp := range typeParameters { - modifiers := c.getTypeParameterModifiers(tp) - var variance VarianceFlags - switch { - case modifiers&ast.ModifierFlagsOut != 0: - if modifiers&ast.ModifierFlagsIn != 0 { - variance = VarianceFlagsInvariant - } else { - variance = VarianceFlagsCovariant - } - case modifiers&ast.ModifierFlagsIn != 0: - variance = VarianceFlagsContravariant - default: - saveReliabilityFlags := c.reliabilityFlags - c.reliabilityFlags = 0 - // We first compare instantiations where the type parameter is replaced with - // marker types that have a known subtype relationship. From this we can infer - // invariance, covariance, contravariance or bivariance. - typeWithSuper := c.createMarkerType(symbol, tp, c.markerSuperType) - typeWithSub := c.createMarkerType(symbol, tp, c.markerSubType) - variance = (core.IfElse(c.isTypeAssignableTo(typeWithSub, typeWithSuper), VarianceFlagsCovariant, 0)) | - (core.IfElse(c.isTypeAssignableTo(typeWithSuper, typeWithSub), VarianceFlagsContravariant, 0)) - // If the instantiations appear to be related bivariantly it may be because the - // type parameter is independent (i.e. it isn't witnessed anywhere in the generic - // type). To determine this we compare instantiations where the type parameter is - // replaced with marker types that are known to be unrelated. - if variance == VarianceFlagsBivariant && c.isTypeAssignableTo(c.createMarkerType(symbol, tp, c.markerOtherType), typeWithSuper) { - variance = VarianceFlagsIndependent - } - if c.reliabilityFlags&RelationComparisonResultReportsUnmeasurable != 0 { - variance |= VarianceFlagsUnmeasurable - } - if c.reliabilityFlags&RelationComparisonResultReportsUnreliable != 0 { - variance |= VarianceFlagsUnreliable - } - c.reliabilityFlags = saveReliabilityFlags - } - variances[i] = variance - } - if !oldVarianceComputation { - c.inVarianceComputation = false - c.resolutionStart = saveResolutionStart - } - links.variances = variances - } - return links.variances -} - -func (c *Checker) createMarkerType(symbol *ast.Symbol, source *Type, target *Type) *Type { - mapper := newSimpleTypeMapper(source, target) - t := c.getDeclaredTypeOfSymbol(symbol) - if c.isErrorType(t) { - return t - } - var result *Type - if symbol.Flags&ast.SymbolFlagsTypeAlias != 0 { - result = c.getTypeAliasInstantiation(symbol, c.instantiateTypes(c.typeAliasLinks.Get(symbol).typeParameters, mapper), nil) - } else { - result = c.createTypeReference(t, c.instantiateTypes(t.AsInterfaceType().TypeParameters(), mapper)) - } - c.markerTypes.Add(result) - return result -} - -func (c *Checker) isMarkerType(t *Type) bool { - return c.markerTypes.Has(t) -} - -func (c *Checker) getTypeParameterModifiers(tp *Type) ast.ModifierFlags { - var flags ast.ModifierFlags - if tp.symbol != nil { - for _, d := range tp.symbol.Declarations { - flags |= d.ModifierFlags() - } - } - return flags & (ast.ModifierFlagsIn | ast.ModifierFlagsOut | ast.ModifierFlagsConst) -} - -// Return true if the given type reference has a 'void' type argument for a covariant type parameter. -// See comment at call in recursiveTypeRelatedTo for when this case matters. -func (c *Checker) hasCovariantVoidArgument(typeArguments []*Type, variances []VarianceFlags) bool { - for i, v := range variances { - if v&VarianceFlagsVarianceMask == VarianceFlagsCovariant && typeArguments[i].flags&TypeFlagsVoid != 0 { - return true - } - } - return false -} - -func (c *Checker) isSignatureAssignableTo(source *Signature, target *Signature, ignoreReturnTypes bool) bool { - return c.compareSignaturesRelated(source, target, core.IfElse(ignoreReturnTypes, SignatureCheckModeIgnoreReturnTypes, SignatureCheckModeNone), false /*reportErrors*/, nil /*errorReporter*/, c.compareTypesAssignable, nil /*reportUnreliableMarkers*/) != TernaryFalse -} - -func (c *Checker) compareSignaturesRelated(source *Signature, target *Signature, checkMode SignatureCheckMode, reportErrors bool, errorReporter ErrorReporter, compareTypes TypeComparer, reportUnreliableMarkers *TypeMapper) Ternary { - if source == target { - return TernaryTrue - } - if !(checkMode&SignatureCheckModeStrictTopSignature != 0 && c.isTopSignature(source)) && c.isTopSignature(target) { - return TernaryTrue - } - if checkMode&SignatureCheckModeStrictTopSignature != 0 && c.isTopSignature(source) && !c.isTopSignature(target) { - return TernaryFalse - } - targetCount := c.getParameterCount(target) - var sourceHasMoreParameters bool - if !c.hasEffectiveRestParameter(target) { - if checkMode&SignatureCheckModeStrictArity != 0 { - sourceHasMoreParameters = c.hasEffectiveRestParameter(source) || c.getParameterCount(source) > targetCount - } else { - sourceHasMoreParameters = c.getMinArgumentCount(source) > targetCount - } - } - if sourceHasMoreParameters { - if reportErrors && (checkMode&SignatureCheckModeStrictArity == 0) { - // the second condition should be redundant, because there is no error reporting when comparing signatures by strict arity - // since it is only done for subtype reduction - errorReporter(diagnostics.Target_signature_provides_too_few_arguments_Expected_0_or_more_but_got_1, c.getMinArgumentCount(source), targetCount) - } - return TernaryFalse - } - if len(source.typeParameters) != 0 && !core.Same(source.typeParameters, target.typeParameters) { - target = c.getCanonicalSignature(target) - source = c.instantiateSignatureInContextOf(source, target /*inferenceContext*/, nil, compareTypes) - } - sourceCount := c.getParameterCount(source) - sourceRestType := c.getNonArrayRestType(source) - targetRestType := c.getNonArrayRestType(target) - if sourceRestType != nil || targetRestType != nil { - c.instantiateType(core.IfElse(sourceRestType != nil, sourceRestType, targetRestType), reportUnreliableMarkers) - } - kind := ast.KindUnknown - if target.declaration != nil { - kind = target.declaration.Kind - } - strictVariance := checkMode&SignatureCheckModeCallback == 0 && c.strictFunctionTypes && kind != ast.KindMethodDeclaration && kind != ast.KindMethodSignature && kind != ast.KindConstructor - result := TernaryTrue - sourceThisType := c.getThisTypeOfSignature(source) - if sourceThisType != nil && sourceThisType != c.voidType { - targetThisType := c.getThisTypeOfSignature(target) - if targetThisType != nil { - // void sources are assignable to anything. - var related Ternary - if !strictVariance { - related = compareTypes(sourceThisType, targetThisType, false /*reportErrors*/) - } - if related == TernaryFalse { - related = compareTypes(targetThisType, sourceThisType, reportErrors) - } - if related == TernaryFalse { - if reportErrors { - errorReporter(diagnostics.The_this_types_of_each_signature_are_incompatible) - } - return TernaryFalse - } - result &= related - } - } - var paramCount int - if sourceRestType != nil || targetRestType != nil { - paramCount = min(sourceCount, targetCount) - } else { - paramCount = max(sourceCount, targetCount) - } - var restIndex int - if sourceRestType != nil || targetRestType != nil { - restIndex = paramCount - 1 - } else { - restIndex = -1 - } - for i := range paramCount { - var sourceType *Type - if i == restIndex { - sourceType = c.getRestOrAnyTypeAtPosition(source, i) - } else { - sourceType = c.tryGetTypeAtPosition(source, i) - } - var targetType *Type - if i == restIndex { - targetType = c.getRestOrAnyTypeAtPosition(target, i) - } else { - targetType = c.tryGetTypeAtPosition(target, i) - } - if sourceType != nil && targetType != nil && (sourceType != targetType || checkMode&SignatureCheckModeStrictArity != 0) { - // In order to ensure that any generic type Foo is at least co-variant with respect to T no matter - // how Foo uses T, we need to relate parameters bi-variantly (given that parameters are input positions, - // they naturally relate only contra-variantly). However, if the source and target parameters both have - // function types with a single call signature, we know we are relating two callback parameters. In - // that case it is sufficient to only relate the parameters of the signatures co-variantly because, - // similar to return values, callback parameters are output positions. This means that a Promise, - // where T is used only in callback parameter positions, will be co-variant (as opposed to bi-variant) - // with respect to T. - var sourceSig *Signature - var targetSig *Signature - if checkMode&SignatureCheckModeCallback == 0 && !c.isInstantiatedGenericParameter(source, i) { - sourceSig = c.getSingleCallSignature(c.GetNonNullableType(sourceType)) - } - if checkMode&SignatureCheckModeCallback == 0 && !c.isInstantiatedGenericParameter(target, i) { - targetSig = c.getSingleCallSignature(c.GetNonNullableType(targetType)) - } - callbacks := sourceSig != nil && targetSig != nil && c.getTypePredicateOfSignature(sourceSig) == nil && c.getTypePredicateOfSignature(targetSig) == nil && - c.getTypeFacts(sourceType, TypeFactsIsUndefinedOrNull) == c.getTypeFacts(targetType, TypeFactsIsUndefinedOrNull) - var related Ternary - if callbacks { - related = c.compareSignaturesRelated(targetSig, sourceSig, checkMode&SignatureCheckModeStrictArity|core.IfElse(strictVariance, SignatureCheckModeStrictCallback, SignatureCheckModeBivariantCallback), reportErrors, errorReporter, compareTypes, reportUnreliableMarkers) - } else { - if checkMode&SignatureCheckModeCallback == 0 && !strictVariance { - related = compareTypes(sourceType, targetType, false /*reportErrors*/) - } - if related == TernaryFalse { - related = compareTypes(targetType, sourceType, reportErrors) - } - } - // With strict arity, (x: number | undefined) => void is a subtype of (x?: number | undefined) => void - if related != TernaryFalse && checkMode&SignatureCheckModeStrictArity != 0 && i >= c.getMinArgumentCount(source) && i < c.getMinArgumentCount(target) && compareTypes(sourceType, targetType, false /*reportErrors*/) != TernaryFalse { - related = TernaryFalse - } - if related == TernaryFalse { - if reportErrors { - errorReporter(diagnostics.Types_of_parameters_0_and_1_are_incompatible, c.getParameterNameAtPosition(source, i), c.getParameterNameAtPosition(target, i)) - } - return TernaryFalse - } - result &= related - } - } - if checkMode&SignatureCheckModeIgnoreReturnTypes == 0 { - // If a signature resolution is already in-flight, skip issuing a circularity error - // here and just use the `any` type directly - targetReturnType := c.getNonCircularReturnTypeOfSignature(target) - if targetReturnType == c.voidType || targetReturnType == c.anyType { - return result - } - sourceReturnType := c.getNonCircularReturnTypeOfSignature(source) - // The following block preserves behavior forbidding boolean returning functions from being assignable to type guard returning functions - targetTypePredicate := c.getTypePredicateOfSignature(target) - if targetTypePredicate != nil { - sourceTypePredicate := c.getTypePredicateOfSignature(source) - if sourceTypePredicate != nil { - result &= c.compareTypePredicateRelatedTo(sourceTypePredicate, targetTypePredicate, reportErrors, errorReporter, compareTypes) - } else if targetTypePredicate.kind == TypePredicateKindIdentifier || targetTypePredicate.kind == TypePredicateKindThis { - if reportErrors { - errorReporter(diagnostics.Signature_0_must_be_a_type_predicate, c.signatureToString(source)) - } - return TernaryFalse - } - } else { - // When relating callback signatures, we still need to relate return types bi-variantly as otherwise - // the containing type wouldn't be co-variant. For example, interface Foo { add(cb: () => T): void } - // wouldn't be co-variant for T without this rule. - var related Ternary - if checkMode&SignatureCheckModeBivariantCallback != 0 { - related = compareTypes(targetReturnType, sourceReturnType, false /*reportErrors*/) - } - if related == TernaryFalse { - related = compareTypes(sourceReturnType, targetReturnType, reportErrors) - } - result &= related - if result == TernaryFalse && reportErrors { - // The errors reported here serve as markers that trigger error chain reduction in the (*Relater).reportError - // method. The markers are elided in the final diagnostic chain and never actually reported. - var message *diagnostics.Message - if len(source.parameters) == 0 && len(target.parameters) == 0 { - message = core.IfElse(source.flags&SignatureFlagsConstruct != 0, - diagnostics.Construct_signatures_with_no_arguments_have_incompatible_return_types_0_and_1, - diagnostics.Call_signatures_with_no_arguments_have_incompatible_return_types_0_and_1) - } else { - message = core.IfElse(source.flags&SignatureFlagsConstruct != 0, - diagnostics.Construct_signature_return_types_0_and_1_are_incompatible, - diagnostics.Call_signature_return_types_0_and_1_are_incompatible) - } - errorReporter(message, c.TypeToString(sourceReturnType), c.TypeToString(targetReturnType)) - } - } - } - return result -} - -func (c *Checker) compareTypePredicateRelatedTo(source *TypePredicate, target *TypePredicate, reportErrors bool, errorReporter ErrorReporter, compareTypes TypeComparer) Ternary { - if source.kind != target.kind { - if reportErrors { - errorReporter(diagnostics.A_this_based_type_guard_is_not_compatible_with_a_parameter_based_type_guard) - errorReporter(diagnostics.Type_predicate_0_is_not_assignable_to_1, c.typePredicateToString(source), c.typePredicateToString(target)) - } - return TernaryFalse - } - if source.kind == TypePredicateKindIdentifier || source.kind == TypePredicateKindAssertsIdentifier { - if source.parameterIndex != target.parameterIndex { - if reportErrors { - errorReporter(diagnostics.Parameter_0_is_not_in_the_same_position_as_parameter_1, source.parameterName, target.parameterName) - errorReporter(diagnostics.Type_predicate_0_is_not_assignable_to_1, c.typePredicateToString(source), c.typePredicateToString(target)) - } - return TernaryFalse - } - } - var related Ternary - switch { - case source.t == target.t: - related = TernaryTrue - case source.t != nil && target.t != nil: - related = compareTypes(source.t, target.t, reportErrors) - default: - related = TernaryFalse - } - if related == TernaryFalse && reportErrors { - errorReporter(diagnostics.Type_predicate_0_is_not_assignable_to_1, c.typePredicateToString(source), c.typePredicateToString(target)) - } - return related -} - -// Returns true if `s` is `(...args: A) => R` where `A` is `any`, `any[]`, `never`, or `never[]`, and `R` is `any` or `unknown`. -func (c *Checker) isTopSignature(s *Signature) bool { - if len(s.typeParameters) == 0 && (s.thisParameter == nil || IsTypeAny(c.getTypeOfParameter(s.thisParameter))) && len(s.parameters) == 1 && signatureHasRestParameter(s) { - paramType := c.getTypeOfParameter(s.parameters[0]) - var restType *Type - if c.isArrayType(paramType) { - restType = c.getTypeArguments(paramType)[0] - } else { - restType = paramType - } - return restType.flags&(TypeFlagsAny|TypeFlagsNever) != 0 && c.getReturnTypeOfSignature(s).flags&TypeFlagsAnyOrUnknown != 0 - } - return false -} - -// Return the number of parameters in a signature. The rest parameter, if present, counts as one -// parameter. For example, the parameter count of (x: number, y: number, ...z: string[]) is 3 and -// the parameter count of (x: number, ...args: [number, ...string[], boolean])) is also 3. In the -// latter example, the effective rest type is [...string[], boolean]. -func (c *Checker) getParameterCount(signature *Signature) int { - length := len(signature.parameters) - if signatureHasRestParameter(signature) { - restType := c.getTypeOfSymbol(signature.parameters[length-1]) - if isTupleType(restType) { - return length + restType.TargetTupleType().fixedLength - core.IfElse(restType.TargetTupleType().combinedFlags&ElementFlagsVariable != 0, 0, 1) - } - } - return length -} - -func (c *Checker) getMinArgumentCount(signature *Signature) int { - return c.getMinArgumentCountEx(signature, MinArgumentCountFlagsNone) -} - -func (c *Checker) getMinArgumentCountEx(signature *Signature, flags MinArgumentCountFlags) int { - strongArityForUntypedJS := flags & MinArgumentCountFlagsStrongArityForUntypedJS - voidIsNonOptional := flags & MinArgumentCountFlagsVoidIsNonOptional - if voidIsNonOptional != 0 || signature.resolvedMinArgumentCount == -1 { - minArgumentCount := -1 - if signatureHasRestParameter(signature) { - restType := c.getTypeOfSymbol(signature.parameters[len(signature.parameters)-1]) - if isTupleType(restType) { - firstOptionalIndex := core.FindIndex(restType.TargetTupleType().elementInfos, func(info TupleElementInfo) bool { - return info.flags&ElementFlagsRequired == 0 - }) - requiredCount := firstOptionalIndex - if firstOptionalIndex < 0 { - requiredCount = restType.TargetTupleType().fixedLength - } - if requiredCount > 0 { - minArgumentCount = len(signature.parameters) - 1 + requiredCount - } - } - } - if minArgumentCount == -1 { - if strongArityForUntypedJS == 0 && signature.flags&SignatureFlagsIsUntypedSignatureInJSFile != 0 { - return 0 - } - minArgumentCount = int(signature.minArgumentCount) - } - if voidIsNonOptional != 0 { - return minArgumentCount - } - for i := minArgumentCount - 1; i >= 0; i-- { - t := c.getTypeAtPosition(signature, i) - if !someType(t, func(t *Type) bool { return t.flags&TypeFlagsVoid != 0 }) { - break - } - minArgumentCount = i - } - signature.resolvedMinArgumentCount = int32(minArgumentCount) - } - return int(signature.resolvedMinArgumentCount) -} - -func (c *Checker) hasEffectiveRestParameter(signature *Signature) bool { - if signatureHasRestParameter(signature) { - restType := c.getTypeOfSymbol(signature.parameters[len(signature.parameters)-1]) - return !isTupleType(restType) || restType.TargetTupleType().combinedFlags&ElementFlagsVariable != 0 - } - return false -} - -func (c *Checker) getTypeAtPosition(signature *Signature, pos int) *Type { - t := c.tryGetTypeAtPosition(signature, pos) - if t != nil { - return t - } - return c.anyType -} - -func (c *Checker) tryGetTypeAtPosition(signature *Signature, pos int) *Type { - paramCount := len(signature.parameters) - core.IfElse(signatureHasRestParameter(signature), 1, 0) - if pos < paramCount { - return c.getTypeOfParameter(signature.parameters[pos]) - } - if signatureHasRestParameter(signature) { - // We want to return the value undefined for an out of bounds parameter position, - // so we need to check bounds here before calling getIndexedAccessType (which - // otherwise would return the type 'undefined'). - restType := c.getTypeOfSymbol(signature.parameters[paramCount]) - index := pos - paramCount - if !isTupleType(restType) || restType.TargetTupleType().combinedFlags&ElementFlagsVariable != 0 || index < restType.TargetTupleType().fixedLength { - return c.getIndexedAccessType(restType, c.getNumberLiteralType(jsnum.Number(index))) - } - } - return nil -} - -// Return the rest type at the given position, transforming `any[]` into just `any`. We do this because -// in signatures we want `any[]` in a rest position to be compatible with anything, but `any[]` isn't -// assignable to tuple types with required elements. -func (c *Checker) getRestOrAnyTypeAtPosition(source *Signature, pos int) *Type { - restType := c.getRestTypeAtPosition(source, pos, false) - if restType != nil { - if elementType := c.getElementTypeOfArrayType(restType); elementType != nil && IsTypeAny(elementType) { - return c.anyType - } - } - return restType -} - -func (c *Checker) getRestTypeAtPosition(source *Signature, pos int, readonly bool) *Type { - parameterCount := c.getParameterCount(source) - minArgumentCount := c.getMinArgumentCount(source) - restType := c.getEffectiveRestType(source) - if restType != nil && pos >= parameterCount-1 { - if pos == parameterCount-1 { - return restType - } else { - return c.createArrayType(c.getIndexedAccessType(restType, c.numberType)) - } - } - types := make([]*Type, parameterCount-pos) - infos := make([]TupleElementInfo, parameterCount-pos) - for i := range types { - var flags ElementFlags - if restType == nil || i < len(types)-1 { - types[i] = c.getTypeAtPosition(source, i+pos) - flags = core.IfElse(i+pos < minArgumentCount, ElementFlagsRequired, ElementFlagsOptional) - } else { - types[i] = restType - flags = ElementFlagsVariadic - } - infos[i] = TupleElementInfo{flags: flags, labeledDeclaration: c.getNameableDeclarationAtPosition(source, i+pos)} - } - return c.createTupleTypeEx(types, infos, readonly) -} - -func (c *Checker) getNameableDeclarationAtPosition(signature *Signature, pos int) *ast.Node { - paramCount := len(signature.parameters) - core.IfElse(signatureHasRestParameter(signature), 1, 0) - if pos < paramCount { - decl := signature.parameters[pos].ValueDeclaration - if decl != nil && c.isValidDeclarationForTupleLabel(decl) { - return decl - } - return nil - } - if signatureHasRestParameter(signature) { - restParameter := signature.parameters[paramCount] - restType := c.getTypeOfSymbol(restParameter) - if isTupleType(restType) { - elementInfos := restType.TargetTupleType().elementInfos - index := pos - paramCount - if index < len(elementInfos) { - return elementInfos[index].labeledDeclaration - } - return nil - } - if restParameter.ValueDeclaration != nil && c.isValidDeclarationForTupleLabel(restParameter.ValueDeclaration) { - return restParameter.ValueDeclaration - } - } - return nil -} - -func (c *Checker) isValidDeclarationForTupleLabel(d *ast.Node) bool { - return ast.IsNamedTupleMember(d) || ast.IsParameter(d) && d.Name() != nil && ast.IsIdentifier(d.Name()) -} - -func (c *Checker) getNonArrayRestType(signature *Signature) *Type { - restType := c.getEffectiveRestType(signature) - if restType != nil && !c.isArrayType(restType) && !IsTypeAny(restType) { - return restType - } - return nil -} - -func (c *Checker) getEffectiveRestType(signature *Signature) *Type { - if signatureHasRestParameter(signature) { - restType := c.getTypeOfSymbol(signature.parameters[len(signature.parameters)-1]) - if !isTupleType(restType) { - if IsTypeAny(restType) { - return c.anyArrayType - } - return restType - } - if restType.TargetTupleType().combinedFlags&ElementFlagsVariable != 0 { - return c.sliceTupleType(restType, restType.TargetTupleType().fixedLength, 0) - } - } - return nil -} - -func (c *Checker) sliceTupleType(t *Type, index int, endSkipCount int) *Type { - target := t.TargetTupleType() - endIndex := c.getTypeReferenceArity(t) - max(endSkipCount, 0) - if index > target.fixedLength { - if restArrayType := c.getRestArrayTypeOfTupleType(t); restArrayType != nil { - return restArrayType - } - return c.createTupleType(nil) - } - return c.createTupleTypeEx(c.getTypeArguments(t)[index:endIndex], target.elementInfos[index:endIndex], false /*readonly*/) -} - -func (c *Checker) getKnownKeysOfTupleType(t *Type) *Type { - fixedLength := t.TargetTupleType().fixedLength - keys := make([]*Type, fixedLength+1) - for i := range fixedLength { - keys[i] = c.getStringLiteralType(strconv.Itoa(i)) - } - keys[fixedLength] = c.getIndexType(core.IfElse(t.TargetTupleType().readonly, c.globalReadonlyArrayType, c.globalArrayType)) - return c.getUnionType(keys) -} - -func (c *Checker) getRestArrayTypeOfTupleType(t *Type) *Type { - if restType := c.getRestTypeOfTupleType(t); restType != nil { - return c.createArrayType(restType) - } - return nil -} - -func (c *Checker) getThisTypeOfSignature(signature *Signature) *Type { - if signature.thisParameter != nil { - return c.getTypeOfSymbol(signature.thisParameter) - } - return nil -} - -func (c *Checker) isInstantiatedGenericParameter(signature *Signature, pos int) bool { - if signature.target == nil { - return false - } - t := c.tryGetTypeAtPosition(signature.target, pos) - return t != nil && c.isGenericType(t) -} - -func (c *Checker) getParameterNameAtPosition(signature *Signature, pos int) string { - paramCount := len(signature.parameters) - core.IfElse(signatureHasRestParameter(signature), 1, 0) - if pos < paramCount { - return signature.parameters[pos].Name - } - restParameter := signature.parameters[paramCount] - restType := c.getTypeOfSymbol(restParameter) - if isTupleType(restType) { - index := pos - paramCount - c.getTupleElementLabel(restType.TargetTupleType().elementInfos[index], restParameter, index) - } - return restParameter.Name -} - -func (c *Checker) getTupleElementLabel(elementInfo TupleElementInfo, restSymbol *ast.Symbol, index int) string { - if elementInfo.labeledDeclaration != nil { - return elementInfo.labeledDeclaration.Name().Text() - } - if restSymbol != nil && restSymbol.ValueDeclaration != nil && ast.IsParameter(restSymbol.ValueDeclaration) { - return c.getTupleElementLabelFromBindingElement(restSymbol.ValueDeclaration, index, elementInfo.flags) - } - var rootName string - if restSymbol != nil { - rootName = restSymbol.Name - } else { - rootName = "arg" - } - return rootName + "_" + strconv.Itoa(index) -} - -func (c *Checker) getTupleElementLabelFromBindingElement(node *ast.Node, index int, elementFlags ElementFlags) string { - if node.Name() != nil { - switch node.Name().Kind { - case ast.KindIdentifier: - name := node.Name().Text() - if hasDotDotDotToken(node) { - // given - // (...[x, y, ...z]: [number, number, ...number[]]) => ... - // this produces - // (x: number, y: number, ...z: number[]) => ... - // which preserves rest elements of 'z' - - // given - // (...[x, y, ...z]: [number, number, ...[...number[], number]]) => ... - // this produces - // (x: number, y: number, ...z: number[], z_1: number) => ... - // which preserves rest elements of z but gives distinct numbers to fixed elements of 'z' - if elementFlags&ElementFlagsVariable != 0 { - return name - } - return name + "_" + strconv.Itoa(index) - } - // given - // (...[x]: [number]) => ... - // this produces - // (x: number) => ... - // which preserves fixed elements of 'x' - - // given - // (...[x]: ...number[]) => ... - // this produces - // (x_0: number) => ... - // which which numbers fixed elements of 'x' whose tuple element type is variable - if elementFlags&ElementFlagsFixed != 0 { - return name - } - return name + "_n" - case ast.KindArrayBindingPattern: - if hasDotDotDotToken(node) { - elements := node.Name().AsBindingPattern().Elements.Nodes - lastElement := core.LastOrNil(elements) - lastElementIsBindingElementRest := lastElement != nil && ast.IsBindingElement(lastElement) && hasDotDotDotToken(lastElement) - elementCount := len(elements) - core.IfElse(lastElementIsBindingElementRest, 1, 0) - if index < elementCount { - element := elements[index] - if ast.IsBindingElement(element) { - return c.getTupleElementLabelFromBindingElement(element, index, elementFlags) - } - } else if lastElementIsBindingElementRest { - return c.getTupleElementLabelFromBindingElement(lastElement, index-elementCount, elementFlags) - } - } - } - } - return "arg_" + strconv.Itoa(index) -} - -func (c *Checker) getTypePredicateOfSignature(sig *Signature) *TypePredicate { - if sig.resolvedTypePredicate == nil { - switch { - case sig.target != nil: - targetTypePredicate := c.getTypePredicateOfSignature(sig.target) - if targetTypePredicate != nil { - sig.resolvedTypePredicate = c.instantiateTypePredicate(targetTypePredicate, sig.mapper) - } - case sig.composite != nil: - sig.resolvedTypePredicate = c.getUnionOrIntersectionTypePredicate(sig.composite.signatures, sig.composite.isUnion) - default: - if sig.declaration != nil { - typeNode := sig.declaration.Type() - var jsdocTypePredicate *TypePredicate - if typeNode == nil { - if jsdocSignature := c.getSignatureOfFullSignatureType(sig.declaration); jsdocSignature != nil { - jsdocTypePredicate = c.getTypePredicateOfSignature(jsdocSignature) - } - } - switch { - case typeNode != nil: - if ast.IsTypePredicateNode(typeNode) { - sig.resolvedTypePredicate = c.createTypePredicateFromTypePredicateNode(typeNode, sig) - } - case jsdocTypePredicate != nil: - sig.resolvedTypePredicate = jsdocTypePredicate - case ast.IsFunctionLikeDeclaration(sig.declaration) && (sig.resolvedReturnType == nil || sig.resolvedReturnType.flags&TypeFlagsBoolean != 0) && c.getParameterCount(sig) > 0: - sig.resolvedTypePredicate = c.noTypePredicate // avoid infinite loop - sig.resolvedTypePredicate = c.getTypePredicateFromBody(sig.declaration) - } - } - } - if sig.resolvedTypePredicate == nil { - sig.resolvedTypePredicate = c.noTypePredicate - } - } - if sig.resolvedTypePredicate == c.noTypePredicate { - return nil - } - return sig.resolvedTypePredicate -} - -func (c *Checker) getUnionOrIntersectionTypePredicate(signatures []*Signature, isUnion bool) *TypePredicate { - var last *TypePredicate - var types []*Type - for _, sig := range signatures { - pred := c.getTypePredicateOfSignature(sig) - if pred != nil { - // Constituent type predicates must all have matching kinds. We don't create composite type predicates for assertions. - if pred.kind != TypePredicateKindThis && pred.kind != TypePredicateKindIdentifier || last != nil && !c.typePredicateKindsMatch(last, pred) { - return nil - } - last = pred - types = append(types, pred.t) - } else { - // In composite union signatures we permit and ignore signatures with a return type `false`. - var returnType *Type - if isUnion { - returnType = c.getReturnTypeOfSignature(sig) - } - if returnType != c.falseType && returnType != c.regularFalseType { - return nil - } - } - } - if last == nil { - return nil - } - compositeType := c.getUnionOrIntersectionType(types, isUnion, UnionReductionLiteral) - return c.newTypePredicate(last.kind, last.parameterName, last.parameterIndex, compositeType) -} - -func (c *Checker) typePredicateKindsMatch(a *TypePredicate, b *TypePredicate) bool { - return a.kind == b.kind && a.parameterIndex == b.parameterIndex -} - -func (c *Checker) createTypePredicateFromTypePredicateNode(node *ast.Node, signature *Signature) *TypePredicate { - predicateNode := node.AsTypePredicateNode() - var t *Type - if predicateNode.Type != nil { - t = c.getTypeFromTypeNode(predicateNode.Type) - } - if ast.IsThisTypeNode(predicateNode.ParameterName) { - kind := core.IfElse(predicateNode.AssertsModifier != nil, TypePredicateKindAssertsThis, TypePredicateKindThis) - return c.newTypePredicate(kind, "" /*parameterName*/, 0 /*parameterIndex*/, t) - } - kind := core.IfElse(predicateNode.AssertsModifier != nil, TypePredicateKindAssertsIdentifier, TypePredicateKindIdentifier) - name := predicateNode.ParameterName.Text() - index := core.FindIndex(signature.parameters, func(p *ast.Symbol) bool { return p.Name == name }) - return c.newTypePredicate(kind, name, int32(index), t) -} - -func (c *Checker) instantiateTypePredicate(predicate *TypePredicate, mapper *TypeMapper) *TypePredicate { - t := c.instantiateType(predicate.t, mapper) - if t == predicate.t { - return predicate - } - return c.newTypePredicate(predicate.kind, predicate.parameterName, predicate.parameterIndex, t) -} - -func (c *Checker) newTypePredicate(kind TypePredicateKind, parameterName string, parameterIndex int32, t *Type) *TypePredicate { - return &TypePredicate{kind: kind, parameterIndex: parameterIndex, parameterName: parameterName, t: t} -} - -func (c *Checker) isResolvingReturnTypeOfSignature(signature *Signature) bool { - if signature.composite != nil && core.Some(signature.composite.signatures, c.isResolvingReturnTypeOfSignature) { - return true - } - return signature.resolvedReturnType == nil && c.findResolutionCycleStartIndex(signature, TypeSystemPropertyNameResolvedReturnType) >= 0 -} - -func (c *Checker) findMatchingSignatures(signatureLists [][]*Signature, signature *Signature, listIndex int) []*Signature { - if len(signature.typeParameters) != 0 { - // We require an exact match for generic signatures, so we only return signatures from the first - // signature list and only if they have exact matches in the other signature lists. - if listIndex > 0 { - return nil - } - for i := 1; i < len(signatureLists); i++ { - if c.findMatchingSignature(signatureLists[i], signature, false /*partialMatch*/, false /*ignoreThisTypes*/, false /*ignoreReturnTypes*/) == nil { - return nil - } - } - return []*Signature{signature} - } - var result []*Signature - for i := range signatureLists { - // Allow matching non-generic signatures to have excess parameters (as a fallback if exact parameter match is not found) and different return types. - // Prefer matching this types if possible. - var match *Signature - if i == listIndex { - match = signature - } else { - match = c.findMatchingSignature(signatureLists[i], signature, false /*partialMatch*/, false /*ignoreThisTypes*/, true /*ignoreReturnTypes*/) - if match == nil { - match = c.findMatchingSignature(signatureLists[i], signature, true /*partialMatch*/, false /*ignoreThisTypes*/, true /*ignoreReturnTypes*/) - } - } - if match == nil { - return nil - } - result = core.AppendIfUnique(result, match) - } - return result -} - -func (c *Checker) findMatchingSignature(signatureList []*Signature, signature *Signature, partialMatch bool, ignoreThisTypes bool, ignoreReturnTypes bool) *Signature { - compareTypes := core.IfElse(partialMatch, c.compareTypesSubtypeOf, c.compareTypesIdentical) - for _, s := range signatureList { - if c.compareSignaturesIdentical(s, signature, partialMatch, ignoreThisTypes, ignoreReturnTypes, compareTypes) != 0 { - return s - } - } - return nil -} - -/** - * See signatureRelatedTo, compareSignaturesIdentical - */ -func (c *Checker) compareSignaturesIdentical(source *Signature, target *Signature, partialMatch bool, ignoreThisTypes bool, ignoreReturnTypes bool, compareTypes func(s *Type, t *Type) Ternary) Ternary { - if source == target { - return TernaryTrue - } - if !c.isMatchingSignature(source, target, partialMatch) { - return TernaryFalse - } - // Check that the two signatures have the same number of type parameters. - if len(source.typeParameters) != len(target.typeParameters) { - return TernaryFalse - } - // Check that type parameter constraints and defaults match. If they do, instantiate the source - // signature with the type parameters of the target signature and continue the comparison. - if len(target.typeParameters) != 0 { - mapper := newTypeMapper(source.typeParameters, target.typeParameters) - for i := range len(target.typeParameters) { - s := source.typeParameters[i] - t := target.typeParameters[i] - if !(s == t || compareTypes(c.instantiateType(c.getConstraintOrUnknownFromTypeParameter(s), mapper), c.getConstraintOrUnknownFromTypeParameter(t)) != TernaryFalse && - compareTypes(c.instantiateType(c.getDefaultOrUnknownFromTypeParameter(s), mapper), c.getDefaultOrUnknownFromTypeParameter(t)) != TernaryFalse) { - return TernaryFalse - } - } - source = c.instantiateSignatureEx(source, mapper, true /*eraseTypeParameters*/) - } - result := TernaryTrue - if !ignoreThisTypes { - sourceThisType := c.getThisTypeOfSignature(source) - if sourceThisType != nil { - targetThisType := c.getThisTypeOfSignature(target) - if targetThisType != nil { - related := compareTypes(sourceThisType, targetThisType) - if related == TernaryFalse { - return TernaryFalse - } - result &= related - } - } - } - for i := range c.getParameterCount(target) { - s := c.getTypeAtPosition(source, i) - t := c.getTypeAtPosition(target, i) - related := compareTypes(t, s) - if related == TernaryFalse { - return TernaryFalse - } - result &= related - } - if !ignoreReturnTypes { - sourceTypePredicate := c.getTypePredicateOfSignature(source) - targetTypePredicate := c.getTypePredicateOfSignature(target) - if sourceTypePredicate != nil || targetTypePredicate != nil { - result &= c.compareTypePredicatesIdentical(sourceTypePredicate, targetTypePredicate, compareTypes) - } else { - result &= compareTypes(c.getReturnTypeOfSignature(source), c.getReturnTypeOfSignature(target)) - } - } - return result -} - -func (c *Checker) isMatchingSignature(source *Signature, target *Signature, partialMatch bool) bool { - sourceParameterCount := c.getParameterCount(source) - targetParameterCount := c.getParameterCount(target) - sourceMinArgumentCount := c.getMinArgumentCount(source) - targetMinArgumentCount := c.getMinArgumentCount(target) - sourceHasRestParameter := c.hasEffectiveRestParameter(source) - targetHasRestParameter := c.hasEffectiveRestParameter(target) - // A source signature matches a target signature if the two signatures have the same number of required, - // optional, and rest parameters. - if sourceParameterCount == targetParameterCount && sourceMinArgumentCount == targetMinArgumentCount && sourceHasRestParameter == targetHasRestParameter { - return true - } - // A source signature partially matches a target signature if the target signature has no fewer required - // parameters - if partialMatch && sourceMinArgumentCount <= targetMinArgumentCount { - return true - } - return false -} - -func (c *Checker) compareTypeParametersIdentical(sourceParams []*Type, targetParams []*Type) bool { - if len(sourceParams) != len(targetParams) { - return false - } - mapper := newTypeMapper(targetParams, sourceParams) - for i := range sourceParams { - source := sourceParams[i] - target := targetParams[i] - if source == target { - continue - } - // We instantiate the target type parameter constraints into the source types so we can recognize `` as the same as `` - if !c.isTypeIdenticalTo(core.OrElse(c.getConstraintFromTypeParameter(source), c.unknownType), c.instantiateType(core.OrElse(c.getConstraintFromTypeParameter(target), c.unknownType), mapper)) { - return false - } - // We don't compare defaults - we just use the type parameter defaults from the first signature that seems to match. - // It might make sense to combine these defaults in the future, but doing so intelligently requires knowing - // if the parameter is used covariantly or contravariantly (so we intersect if it's used like a parameter or union if used like a return type) - // and, since it's just an inference _default_, just picking one arbitrarily works OK. - } - return true -} - -func (c *Checker) compareTypePredicatesIdentical(source *TypePredicate, target *TypePredicate, compareTypes func(s *Type, t *Type) Ternary) Ternary { - switch { - case source == nil || target == nil || !c.typePredicateKindsMatch(source, target): - return TernaryFalse - case source.t == target.t: - return TernaryTrue - case source.t != nil && target.t != nil: - return compareTypes(source.t, target.t) - } - return TernaryFalse -} - -func (c *Checker) getEffectiveConstraintOfIntersection(types []*Type, targetIsUnion bool) *Type { - var constraints []*Type - hasDisjointDomainType := false - for _, t := range types { - if t.flags&TypeFlagsInstantiable != 0 { - // We keep following constraints as long as we have an instantiable type that is known - // not to be circular or infinite (hence we stop on index access types). - constraint := c.getConstraintOfType(t) - for constraint != nil && constraint.flags&(TypeFlagsTypeParameter|TypeFlagsIndex|TypeFlagsConditional) != 0 { - constraint = c.getConstraintOfType(constraint) - } - if constraint != nil { - constraints = append(constraints, constraint) - if targetIsUnion { - constraints = append(constraints, t) - } - } - } else if t.flags&TypeFlagsDisjointDomains != 0 || c.IsEmptyAnonymousObjectType(t) { - hasDisjointDomainType = true - } - } - // If the target is a union type or if we are intersecting with types belonging to one of the - // disjoint domains, we may end up producing a constraint that hasn't been examined before. - if constraints != nil && (targetIsUnion || hasDisjointDomainType) { - if hasDisjointDomainType { - // We add any types belong to one of the disjoint domains because they might cause the final - // intersection operation to reduce the union constraints. - for _, t := range types { - if t.flags&TypeFlagsDisjointDomains != 0 || c.IsEmptyAnonymousObjectType(t) { - constraints = append(constraints, t) - } - } - } - // The source types were normalized; ensure the result is normalized too. - return c.getNormalizedType(c.getIntersectionTypeEx(constraints, IntersectionFlagsNoConstraintReduction, nil), false /*writing*/) - } - return nil -} - -func (c *Checker) templateLiteralTypesDefinitelyUnrelated(source *TemplateLiteralType, target *TemplateLiteralType) bool { - // Two template literal types with differences in their starting or ending text spans are definitely unrelated. - sourceStart := source.texts[0] - targetStart := target.texts[0] - sourceEnd := source.texts[len(source.texts)-1] - targetEnd := target.texts[len(target.texts)-1] - startLen := min(len(sourceStart), len(targetStart)) - endLen := min(len(sourceEnd), len(targetEnd)) - return sourceStart[:startLen] != targetStart[:startLen] || sourceEnd[len(sourceEnd)-endLen:] != targetEnd[len(targetEnd)-endLen:] -} - -func (c *Checker) isTypeMatchedByTemplateLiteralType(source *Type, target *TemplateLiteralType) bool { - inferences := c.inferTypesFromTemplateLiteralType(source, target) - if inferences != nil { - for i, inference := range inferences { - if !c.isValidTypeForTemplateLiteralPlaceholder(inference, target.types[i]) { - return false - } - } - return true - } - return false -} - -func (c *Checker) inferTypesFromTemplateLiteralType(source *Type, target *TemplateLiteralType) []*Type { - switch { - case source.flags&TypeFlagsStringLiteral != 0: - return c.inferFromLiteralPartsToTemplateLiteral([]string{getStringLiteralValue(source)}, nil, target) - case source.flags&TypeFlagsTemplateLiteral != 0: - if slices.Equal(source.AsTemplateLiteralType().texts, target.texts) { - return core.MapIndex(source.AsTemplateLiteralType().types, func(s *Type, i int) *Type { - if c.isTypeAssignableTo(c.getBaseConstraintOrType(s), c.getBaseConstraintOrType(target.types[i])) { - return s - } - return c.getStringLikeTypeForType(s) - }) - } - return c.inferFromLiteralPartsToTemplateLiteral(source.AsTemplateLiteralType().texts, source.AsTemplateLiteralType().types, target) - default: - return nil - } -} - -// This function infers from the text parts and type parts of a source literal to a target template literal. The number -// of text parts is always one more than the number of type parts, and a source string literal is treated as a source -// with one text part and zero type parts. The function returns an array of inferred string or template literal types -// corresponding to the placeholders in the target template literal, or undefined if the source doesn't match the target. -// -// We first check that the starting source text part matches the starting target text part, and that the ending source -// text part ends matches the ending target text part. We then iterate through the remaining target text parts, finding -// a match for each in the source and inferring string or template literal types created from the segments of the source -// that occur between the matches. During this iteration, seg holds the index of the current text part in the sourceTexts -// array and pos holds the current character position in the current text part. -// -// Consider inference from type `<<${string}>.<${number}-${number}>>` to type `<${string}.${string}>`, i.e. -// -// sourceTexts = ['<<', '>.<', '-', '>>'] -// sourceTypes = [string, number, number] -// target.texts = ['<', '.', '>'] -// -// We first match '<' in the target to the start of '<<' in the source and '>' in the target to the end of '>>' in -// the source. The first match for the '.' in target occurs at character 1 in the source text part at index 1, and thus -// the first inference is the template literal type `<${string}>`. The remainder of the source makes up the second -// inference, the template literal type `<${number}-${number}>`. -func (c *Checker) inferFromLiteralPartsToTemplateLiteral(sourceTexts []string, sourceTypes []*Type, target *TemplateLiteralType) []*Type { - lastSourceIndex := len(sourceTexts) - 1 - sourceStartText := sourceTexts[0] - sourceEndText := sourceTexts[lastSourceIndex] - targetTexts := target.texts - lastTargetIndex := len(targetTexts) - 1 - targetStartText := targetTexts[0] - targetEndText := targetTexts[lastTargetIndex] - if lastSourceIndex == 0 && len(sourceStartText) < len(targetStartText)+len(targetEndText) || !strings.HasPrefix(sourceStartText, targetStartText) || !strings.HasSuffix(sourceEndText, targetEndText) { - return nil - } - remainingEndText := sourceEndText[:len(sourceEndText)-len(targetEndText)] - seg := 0 - pos := len(targetStartText) - var matches []*Type - getSourceText := func(index int) string { - if index < lastSourceIndex { - return sourceTexts[index] - } - return remainingEndText - } - addMatch := func(s int, p int) { - var matchType *Type - if s == seg { - matchType = c.getStringLiteralType(getSourceText(s)[pos:p]) - } else { - matchTexts := make([]string, s-seg+1) - matchTexts[0] = sourceTexts[seg][pos:] - copy(matchTexts[1:], sourceTexts[seg+1:s]) - matchTexts[s-seg] = getSourceText(s)[:p] - matchType = c.getTemplateLiteralType(matchTexts, sourceTypes[seg:s]) - } - matches = append(matches, matchType) - seg = s - pos = p - } - for i := 1; i < lastTargetIndex; i++ { - delim := targetTexts[i] - if len(delim) > 0 { - s := seg - p := pos - for { - d := strings.Index(getSourceText(s)[p:], delim) - if d >= 0 { - p += d - break - } - s++ - if s == len(sourceTexts) { - return nil - } - p = 0 - } - addMatch(s, p) - pos += len(delim) - } else if pos < len(getSourceText(seg)) { - addMatch(seg, pos+1) - } else if seg < lastSourceIndex { - addMatch(seg+1, 0) - } else { - return nil - } - } - addMatch(lastSourceIndex, len(getSourceText(lastSourceIndex))) - return matches -} - -func (c *Checker) getStringLikeTypeForType(t *Type) *Type { - if t.flags&(TypeFlagsAny|TypeFlagsStringLike) != 0 { - return t - } - return c.getTemplateLiteralType([]string{"", ""}, []*Type{t}) -} - -func (c *Checker) isValidTypeForTemplateLiteralPlaceholder(source *Type, target *Type) bool { - switch { - case target.flags&TypeFlagsIntersection != 0: - return core.Every(target.Types(), func(t *Type) bool { - return t == c.emptyTypeLiteralType || c.isValidTypeForTemplateLiteralPlaceholder(source, t) - }) - case target.flags&TypeFlagsString != 0 || c.isTypeAssignableTo(source, target): - return true - case source.flags&TypeFlagsStringLiteral != 0: - value := getStringLiteralValue(source) - return target.flags&TypeFlagsNumber != 0 && isValidNumberString(value, false /*roundTripOnly*/) || - target.flags&TypeFlagsBigInt != 0 && isValidBigIntString(value, false /*roundTripOnly*/) || - target.flags&(TypeFlagsBooleanLiteral|TypeFlagsNullable) != 0 && value == target.AsIntrinsicType().intrinsicName || - target.flags&TypeFlagsStringMapping != 0 && c.isMemberOfStringMapping(source, target) || - target.flags&TypeFlagsTemplateLiteral != 0 && c.isTypeMatchedByTemplateLiteralType(source, target.AsTemplateLiteralType()) - case source.flags&TypeFlagsTemplateLiteral != 0: - texts := source.AsTemplateLiteralType().texts - return len(texts) == 2 && texts[0] == "" && texts[1] == "" && c.isTypeAssignableTo(source.AsTemplateLiteralType().types[0], target) - } - return false -} - -func (c *Checker) isMemberOfStringMapping(source *Type, target *Type) bool { - switch { - case target.flags&TypeFlagsAny != 0: - return true - case target.flags&(TypeFlagsString|TypeFlagsTemplateLiteral) != 0: - return c.isTypeAssignableTo(source, target) - case target.flags&TypeFlagsStringMapping != 0: - // We need to see whether applying the same mappings of the target - // onto the source would produce an identical type *and* that - // it's compatible with the inner-most non-string-mapped type. - // - // The intuition here is that if same mappings don't affect the source at all, - // and the source is compatible with the unmapped target, then they must - // still reside in the same domain. - mapped, inner := c.applyTargetStringMappingToSource(source, target) - return mapped == source && c.isMemberOfStringMapping(source, inner) - } - return false -} - -func (c *Checker) applyTargetStringMappingToSource(source *Type, target *Type) (*Type, *Type) { - inner := target.AsStringMappingType().target - if inner.flags&TypeFlagsStringMapping != 0 { - source, inner = c.applyTargetStringMappingToSource(source, inner) - } - return c.getStringMappingType(target.symbol, source), inner -} - -func visibilityToString(flags ast.ModifierFlags) string { - if flags == ast.ModifierFlagsPrivate { - return "private" - } - if flags == ast.ModifierFlagsProtected { - return "protected" - } - return "public" -} - -type errorState struct { - errorChain *ErrorChain - relatedInfo []*ast.Diagnostic -} - -type ErrorChain struct { - next *ErrorChain - message *diagnostics.Message - args []any -} - -type Relater struct { - c *Checker - relation *Relation - errorNode *ast.Node - errorChain *ErrorChain - relatedInfo []*ast.Diagnostic - maybeKeys []string - maybeKeysSet collections.Set[string] - sourceStack []*Type - targetStack []*Type - maybeCount int - sourceDepth int - targetDepth int - expandingFlags ExpandingFlags - overflow bool - relationCount int - next *Relater -} - -func (c *Checker) getRelater() *Relater { - r := c.freeRelater - if r == nil { - r = &Relater{c: c} - } - c.freeRelater = r.next - return r -} - -func (c *Checker) putRelater(r *Relater) { - r.maybeKeysSet.Clear() - *r = Relater{ - c: c, - maybeKeys: r.maybeKeys[:0], - maybeKeysSet: r.maybeKeysSet, - sourceStack: r.sourceStack[:0], - targetStack: r.targetStack[:0], - next: c.freeRelater, - } - c.freeRelater = r -} - -func (r *Relater) isRelatedToSimple(source *Type, target *Type) Ternary { - return r.isRelatedToEx(source, target, RecursionFlagsBoth, false /*reportErrors*/, nil /*headMessage*/, IntersectionStateNone) -} - -func (r *Relater) isRelatedToWorker(source *Type, target *Type, reportErrors bool) Ternary { - return r.isRelatedToEx(source, target, RecursionFlagsBoth, reportErrors, nil, IntersectionStateNone) -} - -func (r *Relater) isRelatedTo(source *Type, target *Type, recursionFlags RecursionFlags, reportErrors bool) Ternary { - return r.isRelatedToEx(source, target, recursionFlags, reportErrors, nil, IntersectionStateNone) -} - -func (r *Relater) isRelatedToEx(originalSource *Type, originalTarget *Type, recursionFlags RecursionFlags, reportErrors bool, headMessage *diagnostics.Message, intersectionState IntersectionState) Ternary { - if originalSource == originalTarget { - return TernaryTrue - } - // Before normalization: if `source` is type an object type, and `target` is primitive, - // skip all the checks we don't need and just return `isSimpleTypeRelatedTo` result - if originalSource.flags&TypeFlagsObject != 0 && originalTarget.flags&TypeFlagsPrimitive != 0 { - if r.relation == r.c.comparableRelation && originalTarget.flags&TypeFlagsNever == 0 && r.c.isSimpleTypeRelatedTo(originalTarget, originalSource, r.relation, nil) || - r.c.isSimpleTypeRelatedTo(originalSource, originalTarget, r.relation, core.IfElse(reportErrors, r.reportError, nil)) { - return TernaryTrue - } - if reportErrors { - r.reportErrorResults(originalSource, originalTarget, originalSource, originalTarget, headMessage) - } - return TernaryFalse - } - // Normalize the source and target types: Turn fresh literal types into regular literal types, - // turn deferred type references into regular type references, simplify indexed access and - // conditional types, and resolve substitution types to either the substitution (on the source - // side) or the type variable (on the target side). - source := r.c.getNormalizedType(originalSource, false /*writing*/) - target := r.c.getNormalizedType(originalTarget, true /*writing*/) - if source == target { - return TernaryTrue - } - if r.relation == r.c.identityRelation { - if source.flags != target.flags { - return TernaryFalse - } - if source.flags&TypeFlagsSingleton != 0 { - return TernaryTrue - } - return r.recursiveTypeRelatedTo(source, target, false /*reportErrors*/, IntersectionStateNone, recursionFlags) - } - // We fastpath comparing a type parameter to exactly its constraint, as this is _super_ common, - // and otherwise, for type parameters in large unions, causes us to need to compare the union to itself, - // as we break down the _target_ union first, _then_ get the source constraint - so for every - // member of the target, we attempt to find a match in the source. This avoids that in cases where - // the target is exactly the constraint. - if source.flags&TypeFlagsTypeParameter != 0 && r.c.getConstraintOfType(source) == target { - return TernaryTrue - } - // See if we're relating a definitely non-nullable type to a union that includes null and/or undefined - // plus a single non-nullable type. If so, remove null and/or undefined from the target type. - if source.flags&TypeFlagsDefinitelyNonNullable != 0 && target.flags&TypeFlagsUnion != 0 { - types := target.Types() - var candidate *Type - switch { - case len(types) == 2 && types[0].flags&TypeFlagsNullable != 0: - candidate = types[1] - case len(types) == 3 && types[0].flags&TypeFlagsNullable != 0 && types[1].flags&TypeFlagsNullable != 0: - candidate = types[2] - } - if candidate != nil && candidate.flags&TypeFlagsNullable == 0 { - target = r.c.getNormalizedType(candidate /*writing*/, true) - if source == target { - return TernaryTrue - } - } - } - if r.relation == r.c.comparableRelation && target.flags&TypeFlagsNever == 0 && r.c.isSimpleTypeRelatedTo(target, source, r.relation, nil) || - r.c.isSimpleTypeRelatedTo(source, target, r.relation, core.IfElse(reportErrors, r.reportError, nil)) { - return TernaryTrue - } - if source.flags&TypeFlagsStructuredOrInstantiable != 0 || target.flags&TypeFlagsStructuredOrInstantiable != 0 { - isPerformingExcessPropertyChecks := intersectionState&IntersectionStateTarget == 0 && isObjectLiteralType(source) && source.objectFlags&ObjectFlagsFreshLiteral != 0 - if isPerformingExcessPropertyChecks { - if r.hasExcessProperties(source, target, reportErrors) { - if reportErrors { - r.reportRelationError(headMessage, source, core.IfElse(originalTarget.alias != nil, originalTarget, target)) - } - return TernaryFalse - } - } - isPerformingCommonPropertyChecks := (r.relation != r.c.comparableRelation || isUnitType(source)) && - intersectionState&IntersectionStateTarget == 0 && - source.flags&(TypeFlagsPrimitive|TypeFlagsObject|TypeFlagsIntersection) != 0 && source != r.c.globalObjectType && - target.flags&(TypeFlagsObject|TypeFlagsIntersection) != 0 && r.c.isWeakType(target) && (len(r.c.getPropertiesOfType(source)) > 0 || r.c.typeHasCallOrConstructSignatures(source)) - isComparingJsxAttributes := source.objectFlags&ObjectFlagsJsxAttributes != 0 - if isPerformingCommonPropertyChecks && !r.c.hasCommonProperties(source, target, isComparingJsxAttributes) { - if reportErrors { - sourceString := r.c.TypeToString(core.IfElse(originalSource.alias != nil, originalSource, source)) - targetString := r.c.TypeToString(core.IfElse(originalTarget.alias != nil, originalTarget, target)) - calls := r.c.getSignaturesOfType(source, SignatureKindCall) - constructs := r.c.getSignaturesOfType(source, SignatureKindConstruct) - if len(calls) > 0 && r.isRelatedTo(r.c.getReturnTypeOfSignature(calls[0]), target, RecursionFlagsSource, false /*reportErrors*/) != TernaryFalse || - len(constructs) > 0 && r.isRelatedTo(r.c.getReturnTypeOfSignature(constructs[0]), target, RecursionFlagsSource, false /*reportErrors*/) != TernaryFalse { - r.reportError(diagnostics.Value_of_type_0_has_no_properties_in_common_with_type_1_Did_you_mean_to_call_it, sourceString, targetString) - } else { - r.reportError(diagnostics.Type_0_has_no_properties_in_common_with_type_1, sourceString, targetString) - } - } - return TernaryFalse - } - skipCaching := source.flags&TypeFlagsUnion != 0 && len(source.Types()) < 4 && target.flags&TypeFlagsUnion == 0 || - target.flags&TypeFlagsUnion != 0 && len(target.Types()) < 4 && source.flags&TypeFlagsStructuredOrInstantiable == 0 - var result Ternary - if skipCaching { - result = r.unionOrIntersectionRelatedTo(source, target, reportErrors, intersectionState) - } else { - result = r.recursiveTypeRelatedTo(source, target, reportErrors, intersectionState, recursionFlags) - } - if result != TernaryFalse { - return result - } - } - if reportErrors { - r.reportErrorResults(originalSource, originalTarget, source, target, headMessage) - } - return TernaryFalse -} - -func (r *Relater) hasExcessProperties(source *Type, target *Type, reportErrors bool) bool { - if !isExcessPropertyCheckTarget(target) || !r.c.noImplicitAny && target.objectFlags&ObjectFlagsJSLiteral != 0 { - // Disable excess property checks on JS literals to simulate having an implicit "index signature" - but only outside of noImplicitAny - return false - } - isComparingJsxAttributes := source.objectFlags&ObjectFlagsJsxAttributes != 0 - if (r.relation == r.c.assignableRelation || r.relation == r.c.comparableRelation) && (r.c.isTypeSubsetOf(r.c.globalObjectType, target) || (!isComparingJsxAttributes && r.c.isEmptyObjectType(target))) { - return false - } - reducedTarget := target - var checkTypes []*Type - if target.flags&TypeFlagsUnion != 0 { - reducedTarget = r.c.findMatchingDiscriminantType(source, target, r.isRelatedToSimple) - if reducedTarget == nil { - reducedTarget = r.c.filterPrimitivesIfContainsNonPrimitive(target) - } - checkTypes = reducedTarget.Distributed() - } - for _, prop := range r.c.getPropertiesOfType(source) { - if shouldCheckAsExcessProperty(prop, source.symbol) && !isIgnoredJsxProperty(source, prop) { - if !r.c.isKnownProperty(reducedTarget, prop.Name, isComparingJsxAttributes) { - if reportErrors { - // Report error in terms of object types in the target as those are the only ones - // we check in isKnownProperty. - errorTarget := r.c.filterType(reducedTarget, isExcessPropertyCheckTarget) - // We know *exactly* where things went wrong when comparing the types. - // Use this property as the error node as this will be more helpful in - // reasoning about what went wrong. - if r.errorNode == nil { - panic("No errorNode in hasExcessProperties") - } - if ast.IsJsxAttributes(r.errorNode) || ast.IsJsxOpeningLikeElement(r.errorNode) || ast.IsJsxOpeningLikeElement(r.errorNode.Parent) { - // JsxAttributes has an object-literal flag and undergo same type-assignablity check as normal object-literal. - // However, using an object-literal error message will be very confusing to the users so we give different a message. - if prop.ValueDeclaration != nil && ast.IsJsxAttribute(prop.ValueDeclaration) && ast.GetSourceFileOfNode(r.errorNode) == ast.GetSourceFileOfNode(prop.ValueDeclaration.Name()) { - // Note that extraneous children (as in `extra`) don't pass this check, - // since `children` is a Kind.PropertySignature instead of a Kind.JsxAttribute. - r.errorNode = prop.ValueDeclaration.Name() - } - propName := r.c.symbolToString(prop) - suggestionSymbol := r.c.getSuggestedSymbolForNonexistentJSXAttribute(propName, errorTarget) - if suggestionSymbol != nil { - r.reportError(diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, propName, r.c.TypeToString(errorTarget), r.c.symbolToString(suggestionSymbol)) - } else { - r.reportError(diagnostics.Property_0_does_not_exist_on_type_1, propName, r.c.TypeToString(errorTarget)) - } - } else { - // use the property's value declaration if the property is assigned inside the literal itself - var objectLiteralDeclaration *ast.Node - if source.symbol != nil { - objectLiteralDeclaration = core.FirstOrNil(source.symbol.Declarations) - } - var suggestion string - if prop.ValueDeclaration != nil && ast.IsObjectLiteralElement(prop.ValueDeclaration) && - ast.FindAncestor(prop.ValueDeclaration, func(d *ast.Node) bool { return d == objectLiteralDeclaration }) != nil && - ast.GetSourceFileOfNode(objectLiteralDeclaration) == ast.GetSourceFileOfNode(r.errorNode) { - name := prop.ValueDeclaration.Name() - r.errorNode = name - if ast.IsIdentifier(name) { - suggestion = r.c.getSuggestionForNonexistentProperty(name.Text(), errorTarget) - } - } - if suggestion != "" { - r.reportError(diagnostics.Object_literal_may_only_specify_known_properties_but_0_does_not_exist_in_type_1_Did_you_mean_to_write_2, r.c.symbolToString(prop), r.c.TypeToString(errorTarget), suggestion) - } else { - r.reportError(diagnostics.Object_literal_may_only_specify_known_properties_and_0_does_not_exist_in_type_1, r.c.symbolToString(prop), r.c.TypeToString(errorTarget)) - } - } - } - return true - } - if checkTypes != nil && r.isRelatedTo(r.c.getTypeOfSymbol(prop), r.c.getTypeOfPropertyInTypes(checkTypes, prop.Name), RecursionFlagsBoth, reportErrors) == TernaryFalse { - if reportErrors { - r.reportError(diagnostics.Types_of_property_0_are_incompatible, r.c.symbolToString(prop)) - } - return true - } - } - } - return false -} - -func (c *Checker) getTypeOfPropertyInTypes(types []*Type, name string) *Type { - var propTypes []*Type - for _, t := range types { - propTypes = append(propTypes, c.getTypeOfPropertyInType(t, name)) - } - return c.getUnionType(propTypes) -} - -func (c *Checker) getTypeOfPropertyInType(t *Type, name string) *Type { - t = c.getApparentType(t) - var prop *ast.Symbol - if t.flags&TypeFlagsUnionOrIntersection != 0 { - prop = c.getPropertyOfUnionOrIntersectionType(t, name, false) - } else { - prop = c.getPropertyOfObjectType(t, name) - } - if prop != nil { - return c.getTypeOfSymbol(prop) - } - indexInfo := c.getApplicableIndexInfoForName(t, name) - if indexInfo != nil { - return indexInfo.valueType - } - return c.undefinedType -} - -func shouldCheckAsExcessProperty(prop *ast.Symbol, container *ast.Symbol) bool { - return prop.ValueDeclaration != nil && container.ValueDeclaration != nil && prop.ValueDeclaration.Parent == container.ValueDeclaration -} - -func isIgnoredJsxProperty(source *Type, sourceProp *ast.Symbol) bool { - return source.objectFlags&ObjectFlagsJsxAttributes != 0 && isHyphenatedJsxName(sourceProp.Name) -} - -func (c *Checker) isTypeSubsetOf(source *Type, target *Type) bool { - return source == target || source.flags&TypeFlagsNever != 0 || target.flags&TypeFlagsUnion != 0 && c.isTypeSubsetOfUnion(source, target) -} - -func (c *Checker) isTypeSubsetOfUnion(source *Type, target *Type) bool { - if source.flags&TypeFlagsUnion != 0 { - for _, t := range source.Types() { - if !containsType(target.Types(), t) { - return false - } - } - return true - } - if source.flags&TypeFlagsEnumLike != 0 && c.getBaseTypeOfEnumLikeType(source) == target { - return true - } - return containsType(target.Types(), source) -} - -func (r *Relater) unionOrIntersectionRelatedTo(source *Type, target *Type, reportErrors bool, intersectionState IntersectionState) Ternary { - // Note that these checks are specifically ordered to produce correct results. In particular, - // we need to deconstruct unions before intersections (because unions are always at the top), - // and we need to handle "each" relations before "some" relations for the same kind of type. - if source.flags&TypeFlagsUnion != 0 { - if target.flags&TypeFlagsUnion != 0 { - // Intersections of union types are normalized into unions of intersection types, and such normalized - // unions can get very large and expensive to relate. The following fast path checks if the source union - // originated in an intersection. If so, and if that intersection contains the target type, then we know - // the result to be true (for any two types A and B, A & B is related to both A and B). - sourceOrigin := source.AsUnionType().origin - if sourceOrigin != nil && sourceOrigin.flags&TypeFlagsIntersection != 0 && target.alias != nil && slices.Contains(sourceOrigin.Types(), target) { - return TernaryTrue - } - // Similarly, in unions of unions the we preserve the original list of unions. This original list is often - // much shorter than the normalized result, so we scan it in the following fast path. - targetOrigin := target.AsUnionType().origin - if targetOrigin != nil && targetOrigin.flags&TypeFlagsUnion != 0 && source.alias != nil && slices.Contains(targetOrigin.Types(), source) { - return TernaryTrue - } - } - if r.relation == r.c.comparableRelation { - return r.someTypeRelatedToType(source, target, reportErrors && source.flags&TypeFlagsPrimitive == 0, intersectionState) - } - return r.eachTypeRelatedToType(source, target, reportErrors && source.flags&TypeFlagsPrimitive == 0, intersectionState) - } - if target.flags&TypeFlagsUnion != 0 { - return r.typeRelatedToSomeType(r.c.getRegularTypeOfObjectLiteral(source), target, reportErrors && source.flags&TypeFlagsPrimitive == 0 && target.flags&TypeFlagsPrimitive == 0, intersectionState) - } - if target.flags&TypeFlagsIntersection != 0 { - return r.typeRelatedToEachType(source, target, reportErrors, IntersectionStateTarget) - } - // Source is an intersection. For the comparable relation, if the target is a primitive type we hoist the - // constraints of all non-primitive types in the source into a new intersection. We do this because the - // intersection may further constrain the constraints of the non-primitive types. For example, given a type - // parameter 'T extends 1 | 2', the intersection 'T & 1' should be reduced to '1' such that it doesn't - // appear to be comparable to '2'. - if r.relation == r.c.comparableRelation && target.flags&TypeFlagsPrimitive != 0 { - constraints := core.SameMap(source.Types(), func(t *Type) *Type { - if t.flags&TypeFlagsInstantiable != 0 { - constraint := r.c.getBaseConstraintOfType(t) - if constraint != nil { - return constraint - } - return r.c.unknownType - } - return t - }) - if !core.Same(constraints, source.Types()) { - source = r.c.getIntersectionType(constraints) - if source.flags&TypeFlagsNever != 0 { - return TernaryFalse - } - if source.flags&TypeFlagsIntersection == 0 { - result := r.isRelatedTo(source, target, RecursionFlagsSource, false /*reportErrors*/) - if result != TernaryFalse { - return result - } - return r.isRelatedTo(target, source, RecursionFlagsSource, false /*reportErrors*/) - } - } - } - // Check to see if any constituents of the intersection are immediately related to the target. - // Don't report errors though. Elaborating on whether a source constituent is related to the target is - // not actually useful and leads to some confusing error messages. Instead, we rely on the caller - // checking whether the full intersection viewed as an object is related to the target. - return r.someTypeRelatedToType(source, target, false /*reportErrors*/, IntersectionStateSource) -} - -func (r *Relater) someTypeRelatedToType(source *Type, target *Type, reportErrors bool, intersectionState IntersectionState) Ternary { - sourceTypes := source.Types() - if source.flags&TypeFlagsUnion != 0 && containsType(sourceTypes, target) { - return TernaryTrue - } - for i, t := range sourceTypes { - related := r.isRelatedToEx(t, target, RecursionFlagsSource, reportErrors && i == len(sourceTypes)-1, nil /*headMessage*/, intersectionState) - if related != TernaryFalse { - return related - } - } - return TernaryFalse -} - -func (r *Relater) eachTypeRelatedToType(source *Type, target *Type, reportErrors bool, intersectionState IntersectionState) Ternary { - result := TernaryTrue - sourceTypes := source.Types() - // We strip `undefined` from the target if the `source` trivially doesn't contain it for our correspondence-checking fastpath - // since `undefined` is frequently added by optionality and would otherwise spoil a potentially useful correspondence - strippedTarget := r.getUndefinedStrippedTargetIfNeeded(source, target) - var strippedTypes []*Type - if strippedTarget.flags&TypeFlagsUnion != 0 { - strippedTypes = strippedTarget.Types() - } - for i, sourceType := range sourceTypes { - if strippedTarget.flags&TypeFlagsUnion != 0 && len(sourceTypes) >= len(strippedTypes) && len(sourceTypes)%len(strippedTypes) == 0 { - // many unions are mappings of one another; in such cases, simply comparing members at the same index can shortcut the comparison - // such unions will have identical lengths, and their corresponding elements will match up. Another common scenario is where a large - // union has a union of objects intersected with it. In such cases, if the input was, eg `("a" | "b" | "c") & (string | boolean | {} | {whatever})`, - // the result will have the structure `"a" | "b" | "c" | "a" & {} | "b" & {} | "c" & {} | "a" & {whatever} | "b" & {whatever} | "c" & {whatever}` - // - the resulting union has a length which is a multiple of the original union, and the elements correspond modulo the length of the original union - related := r.isRelatedToEx(sourceType, strippedTypes[i%len(strippedTypes)], RecursionFlagsBoth, false /*reportErrors*/, nil /*headMessage*/, intersectionState) - if related != TernaryFalse { - result &= related - continue - } - } - related := r.isRelatedToEx(sourceType, target, RecursionFlagsSource, reportErrors, nil /*headMessage*/, intersectionState) - if related == TernaryFalse { - return TernaryFalse - } - result &= related - } - return result -} - -func (r *Relater) getUndefinedStrippedTargetIfNeeded(source *Type, target *Type) *Type { - if source.flags&TypeFlagsUnion != 0 && target.flags&TypeFlagsUnion != 0 && source.Types()[0].flags&TypeFlagsUndefined == 0 && target.Types()[0].flags&TypeFlagsUndefined != 0 { - return r.c.extractTypesOfKind(target, ^TypeFlagsUndefined) - } - return target -} - -func (r *Relater) typeRelatedToSomeType(source *Type, target *Type, reportErrors bool, intersectionState IntersectionState) Ternary { - targetTypes := target.Types() - if target.flags&TypeFlagsUnion != 0 { - if containsType(targetTypes, source) { - return TernaryTrue - } - if r.relation != r.c.comparableRelation && target.objectFlags&ObjectFlagsPrimitiveUnion != 0 && source.flags&TypeFlagsEnumLiteral == 0 && - (source.flags&(TypeFlagsStringLiteral|TypeFlagsBooleanLiteral|TypeFlagsBigIntLiteral) != 0 || - (r.relation == r.c.subtypeRelation || r.relation == r.c.strictSubtypeRelation) && source.flags&TypeFlagsNumberLiteral != 0) { - // When relating a literal type to a union of primitive types, we know the relation is false unless - // the union contains the base primitive type or the literal type in one of its fresh/regular forms. - // We exclude numeric literals for non-subtype relations because numeric literals are assignable to - // numeric enum literals with the same value. Similarly, we exclude enum literal types because - // identically named enum types are related (see isEnumTypeRelatedTo). We exclude the comparable - // relation in entirety because it needs to be checked in both directions. - var alternateForm *Type - if source == source.AsLiteralType().regularType { - alternateForm = source.AsLiteralType().freshType - } else { - alternateForm = source.AsLiteralType().regularType - } - var primitive *Type - switch { - case source.flags&TypeFlagsStringLiteral != 0: - primitive = r.c.stringType - case source.flags&TypeFlagsNumberLiteral != 0: - primitive = r.c.numberType - case source.flags&TypeFlagsBigIntLiteral != 0: - primitive = r.c.bigintType - } - if primitive != nil && containsType(targetTypes, primitive) || alternateForm != nil && containsType(targetTypes, alternateForm) { - return TernaryTrue - } - return TernaryFalse - } - match := r.c.getMatchingUnionConstituentForType(target, source) - if match != nil { - related := r.isRelatedToEx(source, match, RecursionFlagsTarget, false /*reportErrors*/, nil /*headMessage*/, intersectionState) - if related != TernaryFalse { - return related - } - } - } - for _, t := range targetTypes { - related := r.isRelatedToEx(source, t, RecursionFlagsTarget, false /*reportErrors*/, nil /*headMessage*/, intersectionState) - if related != TernaryFalse { - return related - } - } - if reportErrors { - // Elaborate only if we can find a best matching type in the target union - bestMatchingType := r.c.getBestMatchingType(source, target, r.isRelatedToSimple) - if bestMatchingType != nil { - r.isRelatedToEx(source, bestMatchingType, RecursionFlagsTarget, true /*reportErrors*/, nil /*headMessage*/, intersectionState) - } - } - return TernaryFalse -} - -func (r *Relater) typeRelatedToEachType(source *Type, target *Type, reportErrors bool, intersectionState IntersectionState) Ternary { - result := TernaryTrue - targetTypes := target.Types() - for _, targetType := range targetTypes { - related := r.isRelatedToEx(source, targetType, RecursionFlagsTarget, reportErrors /*headMessage*/, nil, intersectionState) - if related == TernaryFalse { - return TernaryFalse - } - result &= related - } - return result -} - -func (r *Relater) eachTypeRelatedToSomeType(source *Type, target *Type) Ternary { - result := TernaryTrue - sourceTypes := source.Types() - for _, sourceType := range sourceTypes { - related := r.typeRelatedToSomeType(sourceType, target, false /*reportErrors*/, IntersectionStateNone) - if related == TernaryFalse { - return TernaryFalse - } - result &= related - } - return result -} - -// Determine if possibly recursive types are related. First, check if the result is already available in the global cache. -// Second, check if we have already started a comparison of the given two types in which case we assume the result to be true. -// Third, check if both types are part of deeply nested chains of generic type instantiations and if so assume the types are -// equal and infinitely expanding. Fourth, if we have reached a depth of 100 nested comparisons, assume we have runaway recursion -// and issue an error. Otherwise, actually compare the structure of the two types. -func (r *Relater) recursiveTypeRelatedTo(source *Type, target *Type, reportErrors bool, intersectionState IntersectionState, recursionFlags RecursionFlags) Ternary { - if r.overflow { - return TernaryFalse - } - id := getRelationKey(source, target, intersectionState, r.relation == r.c.identityRelation, false /*ignoreConstraints*/) - if entry := r.relation.get(id); entry != RelationComparisonResultNone { - if reportErrors && entry&RelationComparisonResultFailed != 0 && entry&RelationComparisonResultOverflow == 0 { - // We are elaborating errors and the cached result is a failure not due to a comparison overflow, - // so we will do the comparison again to generate an error message. - } else { - r.c.reliabilityFlags |= entry & (RelationComparisonResultReportsUnmeasurable | RelationComparisonResultReportsUnreliable) - if reportErrors && entry&RelationComparisonResultOverflow != 0 { - message := core.IfElse(entry&RelationComparisonResultComplexityOverflow != 0, - diagnostics.Excessive_complexity_comparing_types_0_and_1, - diagnostics.Excessive_stack_depth_comparing_types_0_and_1) - r.reportError(message, r.c.TypeToString(source), r.c.TypeToString(target)) - } - if entry&RelationComparisonResultSucceeded != 0 { - return TernaryTrue - } - return TernaryFalse - } - } - if r.relationCount <= 0 { - r.overflow = true - return TernaryFalse - } - // If source and target are already being compared, consider them related with assumptions - if r.maybeKeysSet.Has(id) { - return TernaryMaybe - } - // A key that ends with "*" is an indication that we have type references that reference constrained - // type parameters. For such keys we also check against the key we would have gotten if all type parameters - // were unconstrained. - if strings.HasSuffix(id, "*") { - broadestEquivalentId := getRelationKey(source, target, intersectionState, r.relation == r.c.identityRelation, true /*ignoreConstraints*/) - if r.maybeKeysSet.Has(broadestEquivalentId) { - return TernaryMaybe - } - } - if len(r.sourceStack) == 100 || len(r.targetStack) == 100 { - r.overflow = true - return TernaryFalse - } - maybeStart := len(r.maybeKeys) - r.maybeKeys = append(r.maybeKeys, id) - r.maybeKeysSet.Add(id) - saveExpandingFlags := r.expandingFlags - if recursionFlags&RecursionFlagsSource != 0 { - r.sourceStack = append(r.sourceStack, source) - if r.expandingFlags&ExpandingFlagsSource == 0 && r.c.isDeeplyNestedType(source, r.sourceStack, 3) { - r.expandingFlags |= ExpandingFlagsSource - } - } - if recursionFlags&RecursionFlagsTarget != 0 { - r.targetStack = append(r.targetStack, target) - if r.expandingFlags&ExpandingFlagsTarget == 0 && r.c.isDeeplyNestedType(target, r.targetStack, 3) { - r.expandingFlags |= ExpandingFlagsTarget - } - } - saveReliabilityFlags := r.c.reliabilityFlags - r.c.reliabilityFlags = 0 - var result Ternary - if r.expandingFlags == ExpandingFlagsBoth { - result = TernaryMaybe - } else { - result = r.structuredTypeRelatedTo(source, target, reportErrors, intersectionState) - } - propagatingVarianceFlags := r.c.reliabilityFlags - r.c.reliabilityFlags |= saveReliabilityFlags - if recursionFlags&RecursionFlagsSource != 0 { - r.sourceStack = r.sourceStack[:len(r.sourceStack)-1] - } - if recursionFlags&RecursionFlagsTarget != 0 { - r.targetStack = r.targetStack[:len(r.targetStack)-1] - } - r.expandingFlags = saveExpandingFlags - if result != TernaryFalse { - if result == TernaryTrue || (len(r.sourceStack) == 0 && len(r.targetStack) == 0) { - if result == TernaryTrue || result == TernaryMaybe { - // If result is definitely true, record all maybe keys as having succeeded. Also, record Ternary.Maybe - // results as having succeeded once we reach depth 0, but never record Ternary.Unknown results. - r.resetMaybeStack(maybeStart, propagatingVarianceFlags, true) - } else { - r.resetMaybeStack(maybeStart, propagatingVarianceFlags, false) - } - } - // Note: it's intentional that we don't reset in the else case; - // we leave them on the stack such that when we hit depth zero - // above, we can report all of them as successful. - } else { - // A false result goes straight into global cache (when something is false under - // assumptions it will also be false without assumptions) - r.relation.set(id, RelationComparisonResultFailed|propagatingVarianceFlags) - r.relationCount-- - r.resetMaybeStack(maybeStart, propagatingVarianceFlags, false) - } - return result -} - -func (r *Relater) resetMaybeStack(maybeStart int, propagatingVarianceFlags RelationComparisonResult, markAllAsSucceeded bool) { - for i := maybeStart; i < len(r.maybeKeys); i++ { - r.maybeKeysSet.Delete(r.maybeKeys[i]) - if markAllAsSucceeded { - r.relation.set(r.maybeKeys[i], RelationComparisonResultSucceeded|propagatingVarianceFlags) - r.relationCount-- - } - } - r.maybeKeys = r.maybeKeys[:maybeStart] -} - -func (r *Relater) getErrorState() errorState { - return errorState{ - errorChain: r.errorChain, - relatedInfo: r.relatedInfo, - } -} - -func (r *Relater) restoreErrorState(e errorState) { - r.errorChain = e.errorChain - r.relatedInfo = e.relatedInfo -} - -func (r *Relater) structuredTypeRelatedTo(source *Type, target *Type, reportErrors bool, intersectionState IntersectionState) Ternary { - saveErrorState := r.getErrorState() - result := r.structuredTypeRelatedToWorker(source, target, reportErrors, intersectionState) - if r.relation != r.c.identityRelation { - // The combined constraint of an intersection type is the intersection of the constraints of - // the constituents. When an intersection type contains instantiable types with union type - // constraints, there are situations where we need to examine the combined constraint. One is - // when the target is a union type. Another is when the intersection contains types belonging - // to one of the disjoint domains. For example, given type variables T and U, each with the - // constraint 'string | number', the combined constraint of 'T & U' is 'string | number' and - // we need to check this constraint against a union on the target side. Also, given a type - // variable V constrained to 'string | number', 'V & number' has a combined constraint of - // 'string & number | number & number' which reduces to just 'number'. - // This also handles type parameters, as a type parameter with a union constraint compared against a union - // needs to have its constraint hoisted into an intersection with said type parameter, this way - // the type param can be compared with itself in the target (with the influence of its constraint to match other parts) - // For example, if `T extends 1 | 2` and `U extends 2 | 3` and we compare `T & U` to `T & U & (1 | 2 | 3)` - if result == TernaryFalse && (source.flags&TypeFlagsIntersection != 0 || source.flags&TypeFlagsTypeParameter != 0 && target.flags&TypeFlagsUnion != 0) { - var sourceTypes []*Type - if source.flags&TypeFlagsIntersection != 0 { - sourceTypes = source.Types() - } else { - sourceTypes = []*Type{source} - } - constraint := r.c.getEffectiveConstraintOfIntersection(sourceTypes, target.flags&TypeFlagsUnion != 0) - if constraint != nil && everyType(constraint, func(c *Type) bool { return c != source }) { - // TODO: Stack errors so we get a pyramid for the "normal" comparison above, _and_ a second for this - result = r.isRelatedToEx(constraint, target, RecursionFlagsSource, false /*reportErrors*/, nil /*headMessage*/, intersectionState) - } - } - switch { - // When the target is an intersection we need an extra property check in order to detect nested excess - // properties and nested weak types. The following are motivating examples that all should be errors, but - // aren't without this extra property check: - // - // let obj: { a: { x: string } } & { c: number } = { a: { x: 'hello', y: 2 }, c: 5 }; // Nested excess property - // - // declare let wrong: { a: { y: string } }; - // let weak: { a?: { x?: number } } & { c?: string } = wrong; // Nested weak object type - // - case result != TernaryFalse && intersectionState&IntersectionStateTarget == 0 && target.flags&TypeFlagsIntersection != 0 && !r.c.isGenericObjectType(target) && source.flags&(TypeFlagsObject|TypeFlagsIntersection) != 0: - result &= r.propertiesRelatedTo(source, target, reportErrors, collections.Set[string]{} /*excludedProperties*/, false /*optionalsOnly*/, IntersectionStateNone) - if result != 0 && isObjectLiteralType(source) && source.objectFlags&ObjectFlagsFreshLiteral != 0 { - result &= r.indexSignaturesRelatedTo(source, target, false /*sourceIsPrimitive*/, reportErrors, IntersectionStateNone) - } - // When the source is an intersection we need an extra check of any optional properties in the target to - // detect possible mismatched property types. For example: - // - // function foo(x: { a?: string }, y: T & { a: boolean }) { - // x = y; // Mismatched property in source intersection - // } - // - case result != 0 && r.c.isNonGenericObjectType(target) && !r.c.isArrayOrTupleType(target) && r.isSourceIntersectionNeedingExtraCheck(source, target): - result &= r.propertiesRelatedTo(source, target, reportErrors, collections.Set[string]{} /*excludedProperties*/, true /*optionalsOnly*/, intersectionState) - } - } - if result != TernaryFalse { - r.restoreErrorState(saveErrorState) - } - return result -} - -func (r *Relater) isSourceIntersectionNeedingExtraCheck(source *Type, target *Type) bool { - return source.flags&TypeFlagsIntersection != 0 && r.c.getApparentType(source).flags&TypeFlagsStructuredType != 0 && - !core.Some(source.Types(), func(t *Type) bool { - return t == target || t.objectFlags&ObjectFlagsNonInferrableType != 0 - }) -} - -func (r *Relater) structuredTypeRelatedToWorker(source *Type, target *Type, reportErrors bool, intersectionState IntersectionState) Ternary { - var result Ternary - var varianceCheckFailed bool - var originalErrorChain *ErrorChain - saveErrorState := r.getErrorState() - relateVariances := func(sourceTypeArguments []*Type, targetTypeArguments []*Type, variances []VarianceFlags, intersectionState IntersectionState) (Ternary, bool) { - if result = r.typeArgumentsRelatedTo(sourceTypeArguments, targetTypeArguments, variances, reportErrors, intersectionState); result != TernaryFalse { - return result, true - } - if core.Some(variances, func(v VarianceFlags) bool { return v&VarianceFlagsAllowsStructuralFallback != 0 }) { - // If some type parameter was `Unmeasurable` or `Unreliable`, and we couldn't pass by assuming it was identical, then we - // have to allow a structural fallback check - // We elide the variance-based error elaborations, since those might not be too helpful, since we'll potentially - // be assuming identity of the type parameter. - originalErrorChain = nil - r.restoreErrorState(saveErrorState) - return TernaryFalse, false - } - allowStructuralFallback := r.c.hasCovariantVoidArgument(targetTypeArguments, variances) - varianceCheckFailed = !allowStructuralFallback - // The type arguments did not relate appropriately, but it may be because we have no variance - // information (in which case typeArgumentsRelatedTo defaulted to covariance for all type - // arguments). It might also be the case that the target type has a 'void' type argument for - // a covariant type parameter that is only used in return positions within the generic type - // (in which case any type argument is permitted on the source side). In those cases we proceed - // with a structural comparison. Otherwise, we know for certain the instantiations aren't - // related and we can return here. - if len(variances) != 0 && !allowStructuralFallback { - // In some cases generic types that are covariant in regular type checking mode become - // invariant in --strictFunctionTypes mode because one or more type parameters are used in - // both co- and contravariant positions. In order to make it easier to diagnose *why* such - // types are invariant, if any of the type parameters are invariant we reset the reported - // errors and instead force a structural comparison (which will include elaborations that - // reveal the reason). - // We can switch on `reportErrors` here, since varianceCheckFailed guarantees we return `False`, - // we can return `False` early here to skip calculating the structural error message we don't need. - if varianceCheckFailed && !(reportErrors && core.Some(variances, func(v VarianceFlags) bool { return (v & VarianceFlagsVarianceMask) == VarianceFlagsInvariant })) { - return TernaryFalse, true - } - // We remember the original error information so we can restore it in case the structural - // comparison unexpectedly succeeds. This can happen when the structural comparison result - // is a Ternary.Maybe for example caused by the recursion depth limiter. - originalErrorChain = r.errorChain - r.restoreErrorState(saveErrorState) - } - return TernaryFalse, false - } - switch { - case r.relation == r.c.identityRelation: - // We've already checked that source.flags and target.flags are identical - switch { - case source.flags&TypeFlagsUnionOrIntersection != 0: - result := r.eachTypeRelatedToSomeType(source, target) - if result != TernaryFalse { - result &= r.eachTypeRelatedToSomeType(target, source) - } - return result - case source.flags&TypeFlagsIndex != 0: - return r.isRelatedTo(source.Target(), target.Target(), RecursionFlagsBoth, false /*reportErrors*/) - case source.flags&TypeFlagsIndexedAccess != 0: - result = r.isRelatedTo(source.AsIndexedAccessType().objectType, target.AsIndexedAccessType().objectType, RecursionFlagsBoth, false /*reportErrors*/) - if result != TernaryFalse { - result &= r.isRelatedTo(source.AsIndexedAccessType().indexType, target.AsIndexedAccessType().indexType, RecursionFlagsBoth, false /*reportErrors*/) - if result != TernaryFalse { - return result - } - } - case source.flags&TypeFlagsConditional != 0: - if source.AsConditionalType().root.isDistributive == target.AsConditionalType().root.isDistributive { - result = r.isRelatedTo(source.AsConditionalType().checkType, target.AsConditionalType().checkType, RecursionFlagsBoth, false /*reportErrors*/) - if result != TernaryFalse { - result &= r.isRelatedTo(source.AsConditionalType().extendsType, target.AsConditionalType().extendsType, RecursionFlagsBoth, false /*reportErrors*/) - if result != TernaryFalse { - result &= r.isRelatedTo(r.c.getTrueTypeFromConditionalType(source), r.c.getTrueTypeFromConditionalType(target), RecursionFlagsBoth, false /*reportErrors*/) - if result != TernaryFalse { - result &= r.isRelatedTo(r.c.getFalseTypeFromConditionalType(source), r.c.getFalseTypeFromConditionalType(target), RecursionFlagsBoth, false /*reportErrors*/) - if result != TernaryFalse { - return result - } - } - } - } - } - case source.flags&TypeFlagsSubstitution != 0: - result = r.isRelatedTo(source.AsSubstitutionType().baseType, target.AsSubstitutionType().baseType, RecursionFlagsBoth, false /*reportErrors*/) - if result != TernaryFalse { - result &= r.isRelatedTo(source.AsSubstitutionType().constraint, target.AsSubstitutionType().constraint, RecursionFlagsBoth, false /*reportErrors*/) - if result != TernaryFalse { - return result - } - } - case source.flags&TypeFlagsTemplateLiteral != 0: - if slices.Equal(source.AsTemplateLiteralType().texts, target.AsTemplateLiteralType().texts) { - result = TernaryTrue - for i, sourceType := range source.AsTemplateLiteralType().types { - targetType := target.AsTemplateLiteralType().types[i] - result &= r.isRelatedTo(sourceType, targetType, RecursionFlagsBoth, false /*reportErrors*/) - if result == TernaryFalse { - return result - } - } - return result - } - case source.flags&TypeFlagsStringMapping != 0: - if source.AsStringMappingType().Symbol() == target.AsStringMappingType().Symbol() { - return r.isRelatedTo(source.AsStringMappingType().target, target.AsStringMappingType().target, RecursionFlagsBoth, false /*reportErrors*/) - } - } - if source.flags&TypeFlagsObject == 0 { - return TernaryFalse - } - case source.flags&TypeFlagsUnionOrIntersection != 0 || target.flags&TypeFlagsUnionOrIntersection != 0: - result = r.unionOrIntersectionRelatedTo(source, target, reportErrors, intersectionState) - if result != TernaryFalse { - return result - } - // The ordered decomposition above doesn't handle all cases. Specifically, we also need to handle: - // Source is instantiable (e.g. source has union or intersection constraint). - // Source is an object, target is a union (e.g. { a, b: boolean } <=> { a, b: true } | { a, b: false }). - // Source is an intersection, target is an object (e.g. { a } & { b } <=> { a, b }). - // Source is an intersection, target is a union (e.g. { a } & { b: boolean } <=> { a, b: true } | { a, b: false }). - // Source is an intersection, target instantiable (e.g. string & { tag } <=> T["a"] constrained to string & { tag }). - if !(source.flags&TypeFlagsInstantiable != 0 || - source.flags&TypeFlagsObject != 0 && target.flags&TypeFlagsUnion != 0 || - source.flags&TypeFlagsIntersection != 0 && target.flags&(TypeFlagsObject|TypeFlagsUnion|TypeFlagsInstantiable) != 0) { - return TernaryFalse - } - } - // We limit alias variance probing to only object and conditional types since their alias behavior - // is more predictable than other, interned types, which may or may not have an alias depending on - // the order in which things were checked. - if source.flags&(TypeFlagsObject|TypeFlagsConditional) != 0 && source.alias != nil && len(source.alias.typeArguments) != 0 && - target.alias != nil && source.alias.symbol == target.alias.symbol && !(r.c.isMarkerType(source) || r.c.isMarkerType(target)) { - variances := r.c.getAliasVariances(source.alias.symbol) - if len(variances) == 0 { - return TernaryUnknown - } - params := r.c.typeAliasLinks.Get(source.alias.symbol).typeParameters - minParams := r.c.getMinTypeArgumentCount(params) - nodeIsInJsFile := ast.IsInJSFile(source.alias.symbol.ValueDeclaration) - sourceTypes := r.c.fillMissingTypeArguments(source.alias.typeArguments, params, minParams, nodeIsInJsFile) - targetTypes := r.c.fillMissingTypeArguments(target.alias.typeArguments, params, minParams, nodeIsInJsFile) - varianceResult, ok := relateVariances(sourceTypes, targetTypes, variances, intersectionState) - if ok { - return varianceResult - } - } - // For a generic type T and a type U that is assignable to T, [...U] is assignable to T, U is assignable to readonly [...T], - // and U is assignable to [...T] when U is constrained to a mutable array or tuple type. - if isSingleElementGenericTupleType(source) && !source.TargetTupleType().readonly { - result = r.isRelatedTo(r.c.getTypeArguments(source)[0], target, RecursionFlagsSource, false /*reportErrors*/) - if result != TernaryFalse { - return result - } - } - if isSingleElementGenericTupleType(target) && (target.TargetTupleType().readonly || r.c.isMutableArrayOrTuple(r.c.getBaseConstraintOrType(source))) { - result = r.isRelatedTo(source, r.c.getTypeArguments(target)[0], RecursionFlagsTarget, false /*reportErrors*/) - if result != TernaryFalse { - return result - } - } - switch { - case target.flags&TypeFlagsTypeParameter != 0: - // A source type { [P in Q]: X } is related to a target type T if keyof T is related to Q and X is related to T[Q]. - if source.objectFlags&ObjectFlagsMapped != 0 && source.AsMappedType().declaration.NameType == nil && r.isRelatedTo(r.c.getIndexType(target), r.c.getConstraintTypeFromMappedType(source), RecursionFlagsBoth, false) != TernaryFalse { - if getMappedTypeModifiers(source)&MappedTypeModifiersIncludeOptional == 0 { - templateType := r.c.getTemplateTypeFromMappedType(source) - indexedAccessType := r.c.getIndexedAccessType(target, r.c.getTypeParameterFromMappedType(source)) - result = r.isRelatedTo(templateType, indexedAccessType, RecursionFlagsBoth, reportErrors) - if result != TernaryFalse { - return result - } - } - } - if r.relation == r.c.comparableRelation && source.flags&TypeFlagsTypeParameter != 0 { - // This is a carve-out in comparability to essentially forbid comparing a type parameter with another type parameter - // unless one extends the other. (Remember: comparability is mostly bidirectional!) - if constraint := r.c.getConstraintOfTypeParameter(source); constraint != nil && someType(constraint, func(c *Type) bool { return c.flags&TypeFlagsTypeParameter != 0 }) { - return r.isRelatedTo(constraint, target, RecursionFlagsSource, false /*reportErrors*/) - } - return TernaryFalse - } - case target.flags&TypeFlagsIndexedAccess != 0: - if source.flags&TypeFlagsIndexedAccess != 0 { - // Relate components directly before falling back to constraint relationships - // A type S[K] is related to a type T[J] if S is related to T and K is related to J. - result = r.isRelatedTo(source.AsIndexedAccessType().objectType, target.AsIndexedAccessType().objectType, RecursionFlagsBoth, reportErrors) - if result != TernaryFalse { - result &= r.isRelatedTo(source.AsIndexedAccessType().indexType, target.AsIndexedAccessType().indexType, RecursionFlagsBoth, reportErrors) - } - if result != TernaryFalse { - return result - } - if reportErrors { - originalErrorChain = r.errorChain - } - } - // A type S is related to a type T[K] if S is related to C, where C is the base - // constraint of T[K] for writing. - if r.relation == r.c.assignableRelation || r.relation == r.c.comparableRelation { - objectType := target.AsIndexedAccessType().objectType - indexType := target.AsIndexedAccessType().indexType - baseObjectType := r.c.getBaseConstraintOrType(objectType) - baseIndexType := r.c.getBaseConstraintOrType(indexType) - if !r.c.isGenericObjectType(baseObjectType) && !r.c.isGenericIndexType(baseIndexType) { - accessFlags := AccessFlagsWriting | (core.IfElse(baseObjectType != objectType, AccessFlagsNoIndexSignatures, 0)) - constraint := r.c.getIndexedAccessTypeOrUndefined(baseObjectType, baseIndexType, accessFlags, nil, nil) - if constraint != nil { - if reportErrors && originalErrorChain != nil { - // create a new chain for the constraint error - r.restoreErrorState(saveErrorState) - } - result = r.isRelatedToEx(source, constraint, RecursionFlagsTarget, reportErrors, nil /*headMessage*/, intersectionState) - if result != TernaryFalse { - return result - } - // prefer the shorter chain of the constraint comparison chain, and the direct comparison chain - if reportErrors && originalErrorChain != nil && r.errorChain != nil { - if chainDepth(originalErrorChain) <= chainDepth(r.errorChain) { - r.errorChain = originalErrorChain - } - } - } - } - } - if reportErrors { - originalErrorChain = nil - } - case target.flags&TypeFlagsIndex != 0: - targetType := target.AsIndexType().target - // A keyof S is related to a keyof T if T is related to S. - if source.flags&TypeFlagsIndex != 0 { - result = r.isRelatedTo(targetType, source.AsIndexType().target, RecursionFlagsBoth, false /*reportErrors*/) - if result != TernaryFalse { - return result - } - } - if isTupleType(targetType) { - // An index type can have a tuple type target when the tuple type contains variadic elements. - // Check if the source is related to the known keys of the tuple type. - result = r.isRelatedTo(source, r.c.getKnownKeysOfTupleType(targetType), RecursionFlagsTarget, reportErrors) - if result != TernaryFalse { - return result - } - } else { - // A type S is assignable to keyof T if S is assignable to keyof C, where C is the - // simplified form of T or, if T doesn't simplify, the constraint of T. - constraint := r.c.getSimplifiedTypeOrConstraint(targetType) - if constraint != nil { - // We require Ternary.True here such that circular constraints don't cause - // false positives. For example, given 'T extends { [K in keyof T]: string }', - // 'keyof T' has itself as its constraint and produces a Ternary.Maybe when - // related to other types. - if r.isRelatedTo(source, r.c.getIndexTypeEx(constraint, target.AsIndexType().indexFlags|IndexFlagsNoReducibleCheck), RecursionFlagsTarget, reportErrors) == TernaryTrue { - return TernaryTrue - } - } else if r.c.isGenericMappedType(targetType) { - // generic mapped types that don't simplify or have a constraint still have a very simple set of keys we can compare against - // - their nameType or constraintType. - // In many ways, this comparison is a deferred version of what `getIndexTypeForMappedType` does to actually resolve the keys for _non_-generic types - nameType := r.c.getNameTypeFromMappedType(targetType) - constraintType := r.c.getConstraintTypeFromMappedType(targetType) - var targetKeys *Type - if nameType != nil && r.c.isMappedTypeWithKeyofConstraintDeclaration(targetType) { - // we need to get the apparent mappings and union them with the generic mappings, since some properties may be - // missing from the `constraintType` which will otherwise be mapped in the object - mappedKeys := r.c.getApparentMappedTypeKeys(nameType, targetType) - // We still need to include the non-apparent (and thus still generic) keys in the target side of the comparison (in case they're in the source side) - targetKeys = r.c.getUnionType([]*Type{mappedKeys, nameType}) - } else if nameType != nil { - targetKeys = nameType - } else { - targetKeys = constraintType - } - if r.isRelatedTo(source, targetKeys, RecursionFlagsTarget, reportErrors) == TernaryTrue { - return TernaryTrue - } - } - } - case target.flags&TypeFlagsConditional != 0: - // If we reach 10 levels of nesting for the same conditional type, assume it is an infinitely expanding recursive - // conditional type and bail out with a Ternary.Maybe result. - if r.c.isDeeplyNestedType(target, r.targetStack, 10) { - return TernaryMaybe - } - c := target.AsConditionalType() - // We check for a relationship to a conditional type target only when the conditional type has no - // 'infer' positions, is not distributive or is distributive but doesn't reference the check type - // parameter in either of the result types, and the source isn't an instantiation of the same - // conditional type (as happens when computing variance). - if c.root.inferTypeParameters == nil && !r.c.isDistributionDependent(c.root) && !(source.flags&TypeFlagsConditional != 0 && source.AsConditionalType().root == c.root) { - // Check if the conditional is always true or always false but still deferred for distribution purposes. - skipTrue := !r.c.isTypeAssignableTo(r.c.getPermissiveInstantiation(c.checkType), r.c.getPermissiveInstantiation(c.extendsType)) - skipFalse := !skipTrue && r.c.isTypeAssignableTo(r.c.getRestrictiveInstantiation(c.checkType), r.c.getRestrictiveInstantiation(c.extendsType)) - // TODO: Find a nice way to include potential conditional type breakdowns in error output, if they seem good (they usually don't) - if skipTrue { - result = TernaryTrue - } else { - result = r.isRelatedToEx(source, r.c.getTrueTypeFromConditionalType(target), RecursionFlagsTarget, false /*reportErrors*/, nil /*headMessage*/, intersectionState) - } - if result != TernaryFalse { - if skipFalse { - result &= TernaryTrue - } else { - result &= r.isRelatedToEx(source, r.c.getFalseTypeFromConditionalType(target), RecursionFlagsTarget, false /*reportErrors*/, nil /*headMessage*/, intersectionState) - } - if result != TernaryFalse { - return result - } - } - } - case target.flags&TypeFlagsTemplateLiteral != 0: - if source.flags&TypeFlagsTemplateLiteral != 0 { - if r.relation == r.c.comparableRelation { - if r.c.templateLiteralTypesDefinitelyUnrelated(source.AsTemplateLiteralType(), target.AsTemplateLiteralType()) { - return TernaryFalse - } - return TernaryTrue - } - // Report unreliable variance for type variables referenced in template literal type placeholders. - // For example, `foo-${number}` is related to `foo-${string}` even though number isn't related to string. - r.c.instantiateType(source, r.c.reportUnreliableMapper) - } - if r.c.isTypeMatchedByTemplateLiteralType(source, target.AsTemplateLiteralType()) { - return TernaryTrue - } - case target.flags&TypeFlagsStringMapping != 0: - if source.flags&TypeFlagsStringMapping == 0 { - if r.c.isMemberOfStringMapping(source, target) { - return TernaryTrue - } - } - case r.c.isGenericMappedType(target) && r.relation != r.c.identityRelation: - // Check if source type `S` is related to target type `{ [P in Q]: T }` or `{ [P in Q as R]: T}`. - keysRemapped := target.AsMappedType().declaration.NameType != nil - templateType := r.c.getTemplateTypeFromMappedType(target) - modifiers := getMappedTypeModifiers(target) - if modifiers&MappedTypeModifiersExcludeOptional == 0 { - // If the mapped type has shape `{ [P in Q]: T[P] }`, - // source `S` is related to target if `T` = `S`, i.e. `S` is related to `{ [P in Q]: S[P] }`. - if !keysRemapped && templateType.flags&TypeFlagsIndexedAccess != 0 && templateType.AsIndexedAccessType().objectType == source && templateType.AsIndexedAccessType().indexType == r.c.getTypeParameterFromMappedType(target) { - return TernaryTrue - } - if !r.c.isGenericMappedType(source) { - // If target has shape `{ [P in Q as R]: T}`, then its keys have type `R`. - // If target has shape `{ [P in Q]: T }`, then its keys have type `Q`. - var targetKeys *Type - if keysRemapped { - targetKeys = r.c.getNameTypeFromMappedType(target) - } else { - targetKeys = r.c.getConstraintTypeFromMappedType(target) - } - // Type of the keys of source type `S`, i.e. `keyof S`. - sourceKeys := r.c.getIndexTypeEx(source, IndexFlagsNoIndexSignatures) - includeOptional := modifiers&MappedTypeModifiersIncludeOptional != 0 - var filteredByApplicability *Type - if includeOptional { - filteredByApplicability = r.c.intersectTypes(targetKeys, sourceKeys) - } - // A source type `S` is related to a target type `{ [P in Q]: T }` if `Q` is related to `keyof S` and `S[Q]` is related to `T`. - // A source type `S` is related to a target type `{ [P in Q as R]: T }` if `R` is related to `keyof S` and `S[R]` is related to `T. - // A source type `S` is related to a target type `{ [P in Q]?: T }` if some constituent `Q'` of `Q` is related to `keyof S` and `S[Q']` is related to `T`. - // A source type `S` is related to a target type `{ [P in Q as R]?: T }` if some constituent `R'` of `R` is related to `keyof S` and `S[R']` is related to `T`. - if includeOptional && filteredByApplicability.flags&TypeFlagsNever == 0 || !includeOptional && r.isRelatedTo(targetKeys, sourceKeys, RecursionFlagsBoth, false) != TernaryFalse { - templateType := r.c.getTemplateTypeFromMappedType(target) - typeParameter := r.c.getTypeParameterFromMappedType(target) - // Fastpath: When the template type has the form `Obj[P]` where `P` is the mapped type parameter, directly compare source `S` with `Obj` - // to avoid creating the (potentially very large) number of new intermediate types made by manufacturing `S[P]`. - nonNullComponent := r.c.extractTypesOfKind(templateType, ^TypeFlagsNullable) - if !keysRemapped && nonNullComponent.flags&TypeFlagsIndexedAccess != 0 && nonNullComponent.AsIndexedAccessType().indexType == typeParameter { - result = r.isRelatedTo(source, nonNullComponent.AsIndexedAccessType().objectType, RecursionFlagsTarget, reportErrors) - if result != TernaryFalse { - return result - } - } else { - // We need to compare the type of a property on the source type `S` to the type of the same property on the target type, - // so we need to construct an indexing type representing a property, and then use indexing type to index the source type for comparison. - // If the target type has shape `{ [P in Q]: T }`, then a property of the target has type `P`. - // If the target type has shape `{ [P in Q]?: T }`, then a property of the target has type `P`, - // but the property is optional, so we only want to compare properties `P` that are common between `keyof S` and `Q`. - // If the target type has shape `{ [P in Q as R]: T }`, then a property of the target has type `R`. - // If the target type has shape `{ [P in Q as R]?: T }`, then a property of the target has type `R`, - // but the property is optional, so we only want to compare properties `R` that are common between `keyof S` and `R`. - indexingType := typeParameter - switch { - case keysRemapped: - indexingType = core.OrElse(filteredByApplicability, targetKeys) - case filteredByApplicability != nil: - indexingType = r.c.getIntersectionType([]*Type{filteredByApplicability, typeParameter}) - } - indexedAccessType := r.c.getIndexedAccessType(source, indexingType) - // Compare `S[indexingType]` to `T`, where `T` is the type of a property of the target type. - result = r.isRelatedTo(indexedAccessType, templateType, RecursionFlagsBoth, reportErrors) - if result != TernaryFalse { - return result - } - } - } - originalErrorChain = r.errorChain - r.restoreErrorState(saveErrorState) - } - } - } - switch { - case source.flags&TypeFlagsTypeVariable != 0: - // IndexedAccess comparisons are handled above in the `target.flags&TypeFlagsIndexedAccess` branch - if source.flags&TypeFlagsIndexedAccess == 0 || target.flags&TypeFlagsIndexedAccess == 0 { - constraint := r.c.getConstraintOfType(source) - if constraint == nil { - constraint = r.c.unknownType - } - // hi-speed no-this-instantiation check (less accurate, but avoids costly `this`-instantiation when the constraint will suffice), see #28231 for report on why this is needed - result = r.isRelatedToEx(constraint, target, RecursionFlagsSource, false /*reportErrors*/, nil /*headMessage*/, intersectionState) - if result != TernaryFalse { - return result - } - constraintWithThis := r.c.getTypeWithThisArgument(constraint, source, false /*needApparentType*/) - result = r.isRelatedToEx(constraintWithThis, target, RecursionFlagsSource, reportErrors && constraint != r.c.unknownType && target.flags&source.flags&TypeFlagsTypeParameter == 0, nil /*headMessage*/, intersectionState) - if result != TernaryFalse { - return result - } - if r.c.isMappedTypeGenericIndexedAccess(source) { - // For an indexed access type { [P in K]: E}[X], above we have already explored an instantiation of E with X - // substituted for P. We also want to explore type { [P in K]: E }[C], where C is the constraint of X. - indexConstraint := r.c.getConstraintOfType(source.AsIndexedAccessType().indexType) - if indexConstraint != nil { - result = r.isRelatedTo(r.c.getIndexedAccessType(source.AsIndexedAccessType().objectType, indexConstraint), target, RecursionFlagsSource, reportErrors) - if result != TernaryFalse { - return result - } - } - } - } - case source.flags&TypeFlagsIndex != 0: - isDeferredMappedIndex := r.c.shouldDeferIndexType(source.AsIndexType().target, source.AsIndexType().indexFlags) && source.AsIndexType().target.objectFlags&ObjectFlagsMapped != 0 - result = r.isRelatedTo(r.c.stringNumberSymbolType, target, RecursionFlagsSource, reportErrors && !isDeferredMappedIndex) - if result != TernaryFalse { - return result - } - if isDeferredMappedIndex { - mappedType := source.AsIndexType().target - nameType := r.c.getNameTypeFromMappedType(mappedType) - // Unlike on the target side, on the source side we do *not* include the generic part of the `nameType`, since that comes from a - // (potentially anonymous) mapped type local type parameter, so that'd never assign outside the mapped type body, but we still want to - // allow assignments of index types of identical (or similar enough) mapped types. - // eg, `keyof {[X in keyof A]: Obj[X]}` should be assignable to `keyof {[Y in keyof A]: Tup[Y]}` because both map over the same set of keys (`keyof A`). - // Without this source-side breakdown, a `keyof {[X in keyof A]: Obj[X]}` style type won't be assignable to anything except itself, which is much too strict. - var sourceMappedKeys *Type - if nameType != nil && r.c.isMappedTypeWithKeyofConstraintDeclaration(mappedType) { - sourceMappedKeys = r.c.getApparentMappedTypeKeys(nameType, mappedType) - } else if nameType != nil { - sourceMappedKeys = nameType - } else { - sourceMappedKeys = r.c.getConstraintTypeFromMappedType(mappedType) - } - result = r.isRelatedTo(sourceMappedKeys, target, RecursionFlagsSource, reportErrors) - if result != TernaryFalse { - return result - } - } - case source.flags&TypeFlagsConditional != 0: - // If we reach 10 levels of nesting for the same conditional type, assume it is an infinitely expanding recursive - // conditional type and bail out with a Ternary.Maybe result. - if r.c.isDeeplyNestedType(source, r.sourceStack, 10) { - return TernaryMaybe - } - if target.flags&TypeFlagsConditional != 0 { - // Two conditional types 'T1 extends U1 ? X1 : Y1' and 'T2 extends U2 ? X2 : Y2' are related if - // one of T1 and T2 is related to the other, U1 and U2 are identical types, X1 is related to X2, - // and Y1 is related to Y2. - sourceParams := source.AsConditionalType().root.inferTypeParameters - sourceExtends := source.AsConditionalType().extendsType - var mapper *TypeMapper - if len(sourceParams) != 0 { - // If the source has infer type parameters, we instantiate them in the context of the target - ctx := r.c.newInferenceContext(sourceParams, nil /*signature*/, InferenceFlagsNone, r.isRelatedToWorker) - r.c.inferTypes(ctx.inferences, target.AsConditionalType().extendsType, sourceExtends, InferencePriorityNoConstraints|InferencePriorityAlwaysStrict, false) - sourceExtends = r.c.instantiateType(sourceExtends, ctx.mapper) - mapper = ctx.mapper - } - if r.c.isTypeIdenticalTo(sourceExtends, target.AsConditionalType().extendsType) && (r.isRelatedTo(source.AsConditionalType().checkType, target.AsConditionalType().checkType, RecursionFlagsBoth, false) != 0 || r.isRelatedTo(target.AsConditionalType().checkType, source.AsConditionalType().checkType, RecursionFlagsBoth, false) != 0) { - result = r.isRelatedTo(r.c.instantiateType(r.c.getTrueTypeFromConditionalType(source), mapper), r.c.getTrueTypeFromConditionalType(target), RecursionFlagsBoth, reportErrors) - if result != TernaryFalse { - result &= r.isRelatedTo(r.c.getFalseTypeFromConditionalType(source), r.c.getFalseTypeFromConditionalType(target), RecursionFlagsBoth, reportErrors) - } - if result != TernaryFalse { - return result - } - } - } - // conditionals can be related to one another via normal constraint, as, eg, `A extends B ? O : never` should be assignable to `O` - // when `O` is a conditional (`never` is trivially assignable to `O`, as is `O`!). - defaultConstraint := r.c.getDefaultConstraintOfConditionalType(source) - if defaultConstraint != nil { - result = r.isRelatedTo(defaultConstraint, target, RecursionFlagsSource, reportErrors) - if result != TernaryFalse { - return result - } - } - // conditionals aren't related to one another via distributive constraint as it is much too inaccurate and allows way - // more assignments than are desirable (since it maps the source check type to its constraint, it loses information). - if target.flags&TypeFlagsConditional == 0 && r.c.hasNonCircularBaseConstraint(source) { - distributiveConstraint := r.c.getConstraintOfDistributiveConditionalType(source) - if distributiveConstraint != nil { - r.restoreErrorState(saveErrorState) - result = r.isRelatedTo(distributiveConstraint, target, RecursionFlagsSource, reportErrors) - if result != TernaryFalse { - return result - } - } - } - case source.flags&TypeFlagsTemplateLiteral != 0 && target.flags&TypeFlagsObject == 0: - if target.flags&TypeFlagsTemplateLiteral == 0 { - constraint := r.c.getBaseConstraintOfType(source) - if constraint != nil && constraint != source { - result = r.isRelatedTo(constraint, target, RecursionFlagsSource, reportErrors) - if result != TernaryFalse { - return result - } - } - } - case source.flags&TypeFlagsStringMapping != 0: - if target.flags&TypeFlagsStringMapping != 0 { - if source.AsStringMappingType().symbol != target.AsStringMappingType().symbol { - return TernaryFalse - } - result = r.isRelatedTo(source.AsStringMappingType().target, target.AsStringMappingType().target, RecursionFlagsBoth, reportErrors) - if result != TernaryFalse { - return result - } - } else { - constraint := r.c.getBaseConstraintOfType(source) - if constraint != nil { - result = r.isRelatedTo(constraint, target, RecursionFlagsSource, reportErrors) - if result != TernaryFalse { - return result - } - } - } - default: - // An empty object type is related to any mapped type that includes a '?' modifier. - if r.relation != r.c.subtypeRelation && r.relation != r.c.strictSubtypeRelation && isPartialMappedType(target) && r.c.isEmptyObjectType(source) { - return TernaryTrue - } - if r.c.isGenericMappedType(target) { - if r.c.isGenericMappedType(source) { - result = r.mappedTypeRelatedTo(source, target, reportErrors) - if result != TernaryFalse { - return result - } - } - return TernaryFalse - } - sourceIsPrimitive := source.flags&TypeFlagsPrimitive != 0 - if r.relation != r.c.identityRelation { - source = r.c.getApparentType(source) - } else if r.c.isGenericMappedType(source) { - return TernaryFalse - } - switch { - case source.objectFlags&ObjectFlagsReference != 0 && target.objectFlags&ObjectFlagsReference != 0 && source.Target() == target.Target() && !isTupleType(source) && !r.c.isMarkerType(source) && !r.c.isMarkerType(target): - // When strictNullChecks is disabled, the element type of the empty array literal is undefinedWideningType, - // and an empty array literal wouldn't be assignable to a `never[]` without this check. - if r.c.isEmptyArrayLiteralType(source) { - return TernaryTrue - } - // We have type references to the same generic type, and the type references are not marker - // type references (which are intended by be compared structurally). Obtain the variance - // information for the type parameters and relate the type arguments accordingly. - variances := r.c.getVariances(source.Target()) - // We return Ternary.Maybe for a recursive invocation of getVariances (signaled by emptyArray). This - // effectively means we measure variance only from type parameter occurrences that aren't nested in - // recursive instantiations of the generic type. - if len(variances) == 0 { - return TernaryUnknown - } - varianceResult, ok := relateVariances(r.c.getTypeArguments(source), r.c.getTypeArguments(target), variances, intersectionState) - if ok { - return varianceResult - } - case r.c.isArrayType(target) && (r.c.isReadonlyArrayType(target) && everyType(source, r.c.isArrayOrTupleType) || everyType(source, isMutableTupleType)): - if r.relation != r.c.identityRelation { - return r.isRelatedTo(r.c.getIndexTypeOfTypeEx(source, r.c.numberType, r.c.anyType), r.c.getIndexTypeOfTypeEx(target, r.c.numberType, r.c.anyType), RecursionFlagsBoth, reportErrors) - } - // By flags alone, we know that the `target` is a readonly array while the source is a normal array or tuple - // or `target` is an array and source is a tuple - in both cases the types cannot be identical, by construction - return TernaryFalse - case r.c.isGenericTupleType(source) && isTupleType(target) && !r.c.isGenericTupleType(target): - constraint := r.c.getBaseConstraintOrType(source) - if constraint != source { - return r.isRelatedTo(constraint, target, RecursionFlagsSource, reportErrors) - } - case (r.relation == r.c.subtypeRelation || r.relation == r.c.strictSubtypeRelation) && r.c.isEmptyObjectType(target) && target.objectFlags&ObjectFlagsFreshLiteral != 0 && !r.c.isEmptyObjectType(source): - return TernaryFalse - } - // Even if relationship doesn't hold for unions, intersections, or generic type references, - // it may hold in a structural comparison. - // In a check of the form X = A & B, we will have previously checked if A relates to X or B relates - // to X. Failing both of those we want to check if the aggregation of A and B's members structurally - // relates to X. Thus, we include intersection types on the source side here. - if source.flags&(TypeFlagsObject|TypeFlagsIntersection) != 0 && target.flags&TypeFlagsObject != 0 { - // Report structural errors only if we haven't reported any errors yet - reportStructuralErrors := reportErrors && r.errorChain == saveErrorState.errorChain && !sourceIsPrimitive - result = r.propertiesRelatedTo(source, target, reportStructuralErrors, collections.Set[string]{} /*excludedProperties*/, false /*optionalsOnly*/, intersectionState) - if result != TernaryFalse { - result &= r.signaturesRelatedTo(source, target, SignatureKindCall, reportStructuralErrors, intersectionState) - if result != TernaryFalse { - result &= r.signaturesRelatedTo(source, target, SignatureKindConstruct, reportStructuralErrors, intersectionState) - if result != TernaryFalse { - result &= r.indexSignaturesRelatedTo(source, target, sourceIsPrimitive, reportStructuralErrors, intersectionState) - } - } - } - if result != TernaryFalse { - if !varianceCheckFailed { - return result - } - if originalErrorChain != nil { - r.errorChain = originalErrorChain - } else if r.errorChain == nil { - r.errorChain = saveErrorState.errorChain - } - // Use variance error (there is no structural one) and return false - } - } - // If S is an object type and T is a discriminated union, S may be related to T if - // there exists a constituent of T for every combination of the discriminants of S - // with respect to T. We do not report errors here, as we will use the existing - // error result from checking each constituent of the union. - if source.flags&(TypeFlagsObject|TypeFlagsIntersection) != 0 && target.flags&TypeFlagsUnion != 0 { - objectOnlyTarget := r.c.extractTypesOfKind(target, TypeFlagsObject|TypeFlagsIntersection|TypeFlagsSubstitution) - if objectOnlyTarget.flags&TypeFlagsUnion != 0 { - result := r.typeRelatedToDiscriminatedType(source, objectOnlyTarget) - if result != TernaryFalse { - return result - } - } - } - } - return TernaryFalse -} - -func (r *Relater) typeArgumentsRelatedTo(sources []*Type, targets []*Type, variances []VarianceFlags, reportErrors bool, intersectionState IntersectionState) Ternary { - if len(sources) != len(targets) && r.relation == r.c.identityRelation { - return TernaryFalse - } - length := min(len(sources), len(targets)) - result := TernaryTrue - for i := range length { - // When variance information isn't available we default to covariance. This happens - // in the process of computing variance information for recursive types and when - // comparing 'this' type arguments. - varianceFlags := VarianceFlagsCovariant - if i < len(variances) { - varianceFlags = variances[i] - } - variance := varianceFlags & VarianceFlagsVarianceMask - // We ignore arguments for independent type parameters (because they're never witnessed). - if variance != VarianceFlagsIndependent { - s := sources[i] - t := targets[i] - var related Ternary - if varianceFlags&VarianceFlagsUnmeasurable != 0 { - // Even an `Unmeasurable` variance works out without a structural check if the source and target are _identical_. - // We can't simply assume invariance, because `Unmeasurable` marks nonlinear relations, for example, a relation tainted by - // the `-?` modifier in a mapped type (where, no matter how the inputs are related, the outputs still might not be) - if r.relation == r.c.identityRelation { - related = r.isRelatedTo(s, t, RecursionFlagsBoth, false /*reportErrors*/) - } else { - related = r.c.compareTypesIdentical(s, t) - } - } else if variance == VarianceFlagsCovariant { - related = r.isRelatedToEx(s, t, RecursionFlagsBoth, reportErrors, nil /*headMessage*/, intersectionState) - } else if variance == VarianceFlagsContravariant { - related = r.isRelatedToEx(t, s, RecursionFlagsBoth, reportErrors, nil /*headMessage*/, intersectionState) - } else if variance == VarianceFlagsBivariant { - // In the bivariant case we first compare contravariantly without reporting - // errors. Then, if that doesn't succeed, we compare covariantly with error - // reporting. Thus, error elaboration will be based on the covariant check, - // which is generally easier to reason about. - related = r.isRelatedTo(t, s, RecursionFlagsBoth, false /*reportErrors*/) - if related == TernaryFalse { - related = r.isRelatedToEx(s, t, RecursionFlagsBoth, reportErrors, nil /*headMessage*/, intersectionState) - } - } else { - // In the invariant case we first compare covariantly, and only when that - // succeeds do we proceed to compare contravariantly. Thus, error elaboration - // will typically be based on the covariant check. - related = r.isRelatedToEx(s, t, RecursionFlagsBoth, reportErrors, nil /*headMessage*/, intersectionState) - if related != TernaryFalse { - related &= r.isRelatedToEx(t, s, RecursionFlagsBoth, reportErrors, nil /*headMessage*/, intersectionState) - } - } - if related == TernaryFalse { - return TernaryFalse - } - result &= related - } - } - return result -} - -// A type [P in S]: X is related to a type [Q in T]: Y if T is related to S and X' is -// related to Y, where X' is an instantiation of X in which P is replaced with Q. Notice -// that S and T are contra-variant whereas X and Y are co-variant. -func (r *Relater) mappedTypeRelatedTo(source *Type, target *Type, reportErrors bool) Ternary { - modifiersRelated := r.relation == r.c.comparableRelation || - r.relation == r.c.identityRelation && getMappedTypeModifiers(source) == getMappedTypeModifiers(target) || - r.relation != r.c.identityRelation && r.c.getCombinedMappedTypeOptionality(source) <= r.c.getCombinedMappedTypeOptionality(target) - if modifiersRelated { - targetConstraint := r.c.getConstraintTypeFromMappedType(target) - sourceConstraint := r.c.instantiateType(r.c.getConstraintTypeFromMappedType(source), core.IfElse(r.c.getCombinedMappedTypeOptionality(source) < 0, r.c.reportUnmeasurableMapper, r.c.reportUnreliableMapper)) - if result := r.isRelatedTo(targetConstraint, sourceConstraint, RecursionFlagsBoth, reportErrors); result != TernaryFalse { - mapper := newSimpleTypeMapper(r.c.getTypeParameterFromMappedType(source), r.c.getTypeParameterFromMappedType(target)) - if r.c.instantiateType(r.c.getNameTypeFromMappedType(source), mapper) == r.c.instantiateType(r.c.getNameTypeFromMappedType(target), mapper) { - return result & r.isRelatedTo(r.c.instantiateType(r.c.getTemplateTypeFromMappedType(source), mapper), r.c.getTemplateTypeFromMappedType(target), RecursionFlagsBoth, reportErrors) - } - } - } - return TernaryFalse -} - -func (r *Relater) typeRelatedToDiscriminatedType(source *Type, target *Type) Ternary { - // 1. Generate the combinations of discriminant properties & types 'source' can satisfy. - // a. If the number of combinations is above a set limit, the comparison is too complex. - // 2. Filter 'target' to the subset of types whose discriminants exist in the matrix. - // a. If 'target' does not satisfy all discriminants in the matrix, 'source' is not related. - // 3. For each type in the filtered 'target', determine if all non-discriminant properties of - // 'target' are related to a property in 'source'. - // - // NOTE: See ~/tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithDiscriminatedUnion.ts - // for examples. - sourceProperties := r.c.getPropertiesOfType(source) - sourcePropertiesFiltered := r.c.findDiscriminantProperties(sourceProperties, target) - if len(sourcePropertiesFiltered) == 0 { - return TernaryFalse - } - // Though we could compute the number of combinations as we generate - // the matrix, this would incur additional memory overhead due to - // array allocations. To reduce this overhead, we first compute - // the number of combinations to ensure we will not surpass our - // fixed limit before incurring the cost of any allocations: - numCombinations := 1 - for _, sourceProperty := range sourcePropertiesFiltered { - numCombinations *= countTypes(r.c.getNonMissingTypeOfSymbol(sourceProperty)) - if numCombinations == 0 || numCombinations > 25 { - return TernaryFalse - } - } - // Compute the set of types for each discriminant property. - sourceDiscriminantTypes := make([][]*Type, len(sourcePropertiesFiltered)) - var excludedProperties collections.Set[string] - for i, sourceProperty := range sourcePropertiesFiltered { - sourcePropertyType := r.c.getNonMissingTypeOfSymbol(sourceProperty) - sourceDiscriminantTypes[i] = sourcePropertyType.Distributed() - excludedProperties.Add(sourceProperty.Name) - } - // Build the cartesian product - discriminantCombinations := make([][]*Type, numCombinations) - for i := range numCombinations { - combination := make([]*Type, len(sourceDiscriminantTypes)) - n := i - for j := len(sourceDiscriminantTypes) - 1; j >= 0; j-- { - sourceTypes := sourceDiscriminantTypes[j] - length := len(sourceTypes) - combination[j] = sourceTypes[n%length] - n = n / length - } - discriminantCombinations[i] = combination - } - // Match each combination of the cartesian product of discriminant properties to one or more - // constituents of 'target'. If any combination does not have a match then 'source' is not relatable. - var matchingTypes []*Type - for _, combination := range discriminantCombinations { - hasMatch := false - outer: - for _, t := range target.Types() { - for i := range sourcePropertiesFiltered { - sourceProperty := sourcePropertiesFiltered[i] - targetProperty := r.c.getPropertyOfType(t, sourceProperty.Name) - if targetProperty == nil { - continue outer - } - if sourceProperty == targetProperty { - continue - } - // We compare the source property to the target in the context of a single discriminant type. - related := r.propertyRelatedTo(source, target, sourceProperty, targetProperty, func(*ast.Symbol) *Type { return combination[i] }, - false /*reportErrors*/, IntersectionStateNone, r.c.strictNullChecks || r.relation == r.c.comparableRelation /*skipOptional*/) - // If the target property could not be found, or if the properties were not related, - // then this constituent is not a match. - if related == TernaryFalse { - continue outer - } - } - matchingTypes = core.AppendIfUnique(matchingTypes, t) - hasMatch = true - } - if !hasMatch { - // We failed to match any type for this combination. - return TernaryFalse - } - } - // Compare the remaining non-discriminant properties of each match. - result := TernaryTrue - for _, t := range matchingTypes { - result &= r.propertiesRelatedTo(source, t /*reportErrors*/, false, excludedProperties /*optionalsOnly*/, false, IntersectionStateNone) - if result != TernaryFalse { - result &= r.signaturesRelatedTo(source, t, SignatureKindCall /*reportErrors*/, false, IntersectionStateNone) - if result != TernaryFalse { - result &= r.signaturesRelatedTo(source, t, SignatureKindConstruct /*reportErrors*/, false, IntersectionStateNone) - if result != TernaryFalse && !(isTupleType(source) && isTupleType(t)) { - // Comparing numeric index types when both `source` and `type` are tuples is unnecessary as the - // element types should be sufficiently covered by `propertiesRelatedTo`. It also causes problems - // with index type assignability as the types for the excluded discriminants are still included - // in the index type. - result &= r.indexSignaturesRelatedTo(source, t /*sourceIsPrimitive*/, false /*reportErrors*/, false, IntersectionStateNone) - } - } - } - if result == TernaryFalse { - return result - } - } - return result -} - -func (r *Relater) propertiesRelatedTo(source *Type, target *Type, reportErrors bool, excludedProperties collections.Set[string], optionalsOnly bool, intersectionState IntersectionState) Ternary { - if r.relation == r.c.identityRelation { - return r.propertiesIdenticalTo(source, target, excludedProperties) - } - result := TernaryTrue - if isTupleType(target) { - if r.c.isArrayOrTupleType(source) { - if !target.TargetTupleType().readonly && (r.c.isReadonlyArrayType(source) || isTupleType(source) && source.TargetTupleType().readonly) { - return TernaryFalse - } - sourceArity := r.c.getTypeReferenceArity(source) - targetArity := r.c.getTypeReferenceArity(target) - var sourceRest bool - if isTupleType(source) { - sourceRest = source.TargetTupleType().combinedFlags&ElementFlagsRest != 0 - } else { - sourceRest = true - } - targetHasRestElement := target.TargetTupleType().combinedFlags&ElementFlagsVariable != 0 - var sourceMinLength int - if isTupleType(source) { - sourceMinLength = source.TargetTupleType().minLength - } else { - sourceMinLength = 0 - } - targetMinLength := target.TargetTupleType().minLength - if !sourceRest && sourceArity < targetMinLength { - if reportErrors { - r.reportError(diagnostics.Source_has_0_element_s_but_target_requires_1, sourceArity, targetMinLength) - } - return TernaryFalse - } - if !targetHasRestElement && targetArity < sourceMinLength { - if reportErrors { - r.reportError(diagnostics.Source_has_0_element_s_but_target_allows_only_1, sourceMinLength, targetArity) - } - return TernaryFalse - } - if !targetHasRestElement && (sourceRest || targetArity < sourceArity) { - if reportErrors { - if sourceMinLength < targetMinLength { - r.reportError(diagnostics.Target_requires_0_element_s_but_source_may_have_fewer, targetMinLength) - } else { - r.reportError(diagnostics.Target_allows_only_0_element_s_but_source_may_have_more, targetArity) - } - } - return TernaryFalse - } - sourceTypeArguments := r.c.getTypeArguments(source) - targetTypeArguments := r.c.getTypeArguments(target) - targetStartCount := getStartElementCount(target.TargetTupleType(), ElementFlagsNonRest) - targetEndCount := getEndElementCount(target.TargetTupleType(), ElementFlagsNonRest) - canExcludeDiscriminants := excludedProperties.Len() != 0 - for sourcePosition := range sourceArity { - var sourceFlags ElementFlags - if isTupleType(source) { - sourceFlags = source.TargetTupleType().elementInfos[sourcePosition].flags - } else { - sourceFlags = ElementFlagsRest - } - sourcePositionFromEnd := sourceArity - 1 - sourcePosition - var targetPosition int - if targetHasRestElement && sourcePosition >= targetStartCount { - targetPosition = targetArity - 1 - min(sourcePositionFromEnd, targetEndCount) - } else { - targetPosition = sourcePosition - } - targetFlags := ElementFlagsNone - if targetPosition >= 0 { - targetFlags = target.TargetTupleType().elementInfos[targetPosition].flags - } - if targetFlags&ElementFlagsVariadic != 0 && sourceFlags&ElementFlagsVariadic == 0 { - if reportErrors { - r.reportError(diagnostics.Source_provides_no_match_for_variadic_element_at_position_0_in_target, targetPosition) - } - return TernaryFalse - } - if sourceFlags&ElementFlagsVariadic != 0 && targetFlags&ElementFlagsVariable == 0 { - if reportErrors { - r.reportError(diagnostics.Variadic_element_at_position_0_in_source_does_not_match_element_at_position_1_in_target, sourcePosition, targetPosition) - } - return TernaryFalse - } - if targetFlags&ElementFlagsRequired != 0 && sourceFlags&ElementFlagsRequired == 0 { - if reportErrors { - r.reportError(diagnostics.Source_provides_no_match_for_required_element_at_position_0_in_target, targetPosition) - } - return TernaryFalse - } - // We can only exclude discriminant properties if we have not yet encountered a variable-length element. - if canExcludeDiscriminants { - if sourceFlags&ElementFlagsVariable != 0 || targetFlags&ElementFlagsVariable != 0 { - canExcludeDiscriminants = false - } - if canExcludeDiscriminants && excludedProperties.Has(strconv.Itoa(sourcePosition)) { - continue - } - } - sourceType := r.c.removeMissingType(sourceTypeArguments[sourcePosition], sourceFlags&targetFlags&ElementFlagsOptional != 0) - targetType := targetTypeArguments[targetPosition] - var targetCheckType *Type - if sourceFlags&ElementFlagsVariadic != 0 && targetFlags&ElementFlagsRest != 0 { - targetCheckType = r.c.createArrayType(targetType) - } else { - targetCheckType = r.c.removeMissingType(targetType, targetFlags&ElementFlagsOptional != 0) - } - related := r.isRelatedToEx(sourceType, targetCheckType, RecursionFlagsBoth, reportErrors, nil /*headMessage*/, intersectionState) - if related == TernaryFalse { - if reportErrors && (targetArity > 1 || sourceArity > 1) { - if targetHasRestElement && sourcePosition >= targetStartCount && sourcePositionFromEnd >= targetEndCount && targetStartCount != sourceArity-targetEndCount-1 { - r.reportError(diagnostics.Type_at_positions_0_through_1_in_source_is_not_compatible_with_type_at_position_2_in_target, targetStartCount, sourceArity-targetEndCount-1, targetPosition) - } else { - r.reportError(diagnostics.Type_at_position_0_in_source_is_not_compatible_with_type_at_position_1_in_target, sourcePosition, targetPosition) - } - } - return TernaryFalse - } - result &= related - } - return result - } - if target.TargetTupleType().combinedFlags&ElementFlagsVariable != 0 { - return TernaryFalse - } - } - requireOptionalProperties := (r.relation == r.c.subtypeRelation || r.relation == r.c.strictSubtypeRelation) && !isObjectLiteralType(source) && !r.c.isEmptyArrayLiteralType(source) && !isTupleType(source) - unmatchedProperty := r.c.getUnmatchedProperty(source, target, requireOptionalProperties, false /*matchDiscriminantProperties*/) - if unmatchedProperty != nil { - if reportErrors && r.c.shouldReportUnmatchedPropertyError(source, target) { - r.reportUnmatchedProperty(source, target, unmatchedProperty, requireOptionalProperties) - } - return TernaryFalse - } - if isObjectLiteralType(target) { - for _, sourceProp := range excludeProperties(r.c.getPropertiesOfType(source), excludedProperties) { - if r.c.getPropertyOfObjectType(target, sourceProp.Name) == nil { - if reportErrors { - r.reportError(diagnostics.Property_0_does_not_exist_on_type_1, r.c.symbolToString(sourceProp), r.c.TypeToString(target)) - } - return TernaryFalse - } - } - } - // We only call this for union target types when we're attempting to do excess property checking - in those cases, we want to get _all possible props_ - // from the target union, across all members - properties := r.c.getPropertiesOfType(target) - numericNamesOnly := isTupleType(source) && isTupleType(target) - for _, targetProp := range excludeProperties(properties, excludedProperties) { - name := targetProp.Name - if targetProp.Flags&ast.SymbolFlagsPrototype == 0 && (!numericNamesOnly || isNumericLiteralName(name) || name == "length") && (!optionalsOnly || targetProp.Flags&ast.SymbolFlagsOptional != 0) { - sourceProp := r.c.getPropertyOfType(source, name) - if sourceProp != nil && sourceProp != targetProp { - related := r.propertyRelatedTo(source, target, sourceProp, targetProp, r.c.getNonMissingTypeOfSymbol, reportErrors, intersectionState, r.relation == r.c.comparableRelation) - if related == TernaryFalse { - return TernaryFalse - } - result &= related - } - } - } - return result -} - -func (r *Relater) propertyRelatedTo(source *Type, target *Type, sourceProp *ast.Symbol, targetProp *ast.Symbol, getTypeOfSourceProperty func(sym *ast.Symbol) *Type, reportErrors bool, intersectionState IntersectionState, skipOptional bool) Ternary { - sourcePropFlags := getDeclarationModifierFlagsFromSymbol(sourceProp) - targetPropFlags := getDeclarationModifierFlagsFromSymbol(targetProp) - switch { - case sourcePropFlags&ast.ModifierFlagsPrivate != 0 || targetPropFlags&ast.ModifierFlagsPrivate != 0: - if sourceProp.ValueDeclaration != targetProp.ValueDeclaration { - if reportErrors { - if sourcePropFlags&ast.ModifierFlagsPrivate != 0 && targetPropFlags&ast.ModifierFlagsPrivate != 0 { - r.reportError(diagnostics.Types_have_separate_declarations_of_a_private_property_0, r.c.symbolToString(targetProp)) - } else { - r.reportError(diagnostics.Property_0_is_private_in_type_1_but_not_in_type_2, r.c.symbolToString(targetProp), r.c.TypeToString(core.IfElse(sourcePropFlags&ast.ModifierFlagsPrivate != 0, source, target)), r.c.TypeToString(core.IfElse(sourcePropFlags&ast.ModifierFlagsPrivate != 0, target, source))) - } - } - return TernaryFalse - } - case targetPropFlags&ast.ModifierFlagsProtected != 0: - if !r.c.isValidOverrideOf(sourceProp, targetProp) { - if reportErrors { - sourceType := core.OrElse(r.c.getDeclaringClass(sourceProp), source) - targetType := core.OrElse(r.c.getDeclaringClass(targetProp), target) - r.reportError(diagnostics.Property_0_is_protected_but_type_1_is_not_a_class_derived_from_2, r.c.symbolToString(targetProp), r.c.TypeToString(sourceType), r.c.TypeToString(targetType)) - } - return TernaryFalse - } - case sourcePropFlags&ast.ModifierFlagsProtected != 0: - if reportErrors { - r.reportError(diagnostics.Property_0_is_protected_in_type_1_but_public_in_type_2, r.c.symbolToString(targetProp), r.c.TypeToString(source), r.c.TypeToString(target)) - } - return TernaryFalse - } - // Ensure {readonly a: whatever} is not a subtype of {a: whatever}, - // while {a: whatever} is a subtype of {readonly a: whatever}. - // This ensures the subtype relationship is ordered, and preventing declaration order - // from deciding which type "wins" in union subtype reduction. - // They're still assignable to one another, since `readonly` doesn't affect assignability. - // This is only applied during the strictSubtypeRelation -- currently used in subtype reduction - if r.relation == r.c.strictSubtypeRelation && r.c.isReadonlySymbol(sourceProp) && !r.c.isReadonlySymbol(targetProp) { - return TernaryFalse - } - // If the target comes from a partial union prop, allow `undefined` in the target type - related := r.isPropertySymbolTypeRelated(sourceProp, targetProp, getTypeOfSourceProperty, reportErrors, intersectionState) - if related == TernaryFalse { - if reportErrors { - r.reportError(diagnostics.Types_of_property_0_are_incompatible, r.c.symbolToString(targetProp)) - } - return TernaryFalse - } - // When checking for comparability, be more lenient with optional properties. - if !skipOptional && sourceProp.Flags&ast.SymbolFlagsOptional != 0 && targetProp.Flags&ast.SymbolFlagsClassMember != 0 && targetProp.Flags&ast.SymbolFlagsOptional == 0 { - // TypeScript 1.0 spec (April 2014): 3.8.3 - // S is a subtype of a type T, and T is a supertype of S if ... - // S' and T are object types and, for each member M in T.. - // M is a property and S' contains a property N where - // if M is a required property, N is also a required property - // (M - property in T) - // (N - property in S) - if reportErrors { - r.reportError(diagnostics.Property_0_is_optional_in_type_1_but_required_in_type_2, r.c.symbolToString(targetProp), r.c.TypeToString(source), r.c.TypeToString(target)) - } - return TernaryFalse - } - return related -} - -func (r *Relater) isPropertySymbolTypeRelated(sourceProp *ast.Symbol, targetProp *ast.Symbol, getTypeOfSourceProperty func(sym *ast.Symbol) *Type, reportErrors bool, intersectionState IntersectionState) Ternary { - targetIsOptional := r.c.strictNullChecks && targetProp.CheckFlags&ast.CheckFlagsPartial != 0 - effectiveTarget := r.c.addOptionalityEx(r.c.getNonMissingTypeOfSymbol(targetProp), false /*isProperty*/, targetIsOptional) - // source could resolve to `any` and that's not related to `unknown` target under strict subtype relation - if effectiveTarget.flags&core.IfElse(r.relation == r.c.strictSubtypeRelation, TypeFlagsAny, TypeFlagsAnyOrUnknown) != 0 { - return TernaryTrue - } - effectiveSource := getTypeOfSourceProperty(sourceProp) - return r.isRelatedToEx(effectiveSource, effectiveTarget, RecursionFlagsBoth, reportErrors, nil /*headMessage*/, intersectionState) -} - -func (r *Relater) reportUnmatchedProperty(source *Type, target *Type, unmatchedProperty *ast.Symbol, requireOptionalProperties bool) { - // give specific error in case where private names have the same description - if unmatchedProperty.ValueDeclaration != nil && - unmatchedProperty.ValueDeclaration.Name() != nil && - ast.IsPrivateIdentifier(unmatchedProperty.ValueDeclaration.Name()) && - source.symbol != nil && - source.symbol.Flags&ast.SymbolFlagsClass != 0 { - privateIdentifierDescription := unmatchedProperty.ValueDeclaration.Name().Text() - symbolTableKey := binder.GetSymbolNameForPrivateIdentifier(source.symbol, privateIdentifierDescription) - if r.c.getPropertyOfType(source, symbolTableKey) != nil { - sourceName := scanner.DeclarationNameToString(ast.GetNameOfDeclaration(source.symbol.ValueDeclaration)) - targetName := scanner.DeclarationNameToString(ast.GetNameOfDeclaration(target.symbol.ValueDeclaration)) - r.reportError(diagnostics.Property_0_in_type_1_refers_to_a_different_member_that_cannot_be_accessed_from_within_type_2, privateIdentifierDescription, sourceName, targetName) - return - } - } - props := r.c.getUnmatchedProperties(source, target, requireOptionalProperties, false /*matchDiscriminantProperties*/) - if len(props) == 1 { - sourceType, targetType := r.c.getTypeNamesForErrorDisplay(source, target) - propName := r.c.symbolToString(unmatchedProperty) - r.reportError(diagnostics.Property_0_is_missing_in_type_1_but_required_in_type_2, propName, sourceType, targetType) - if len(unmatchedProperty.Declarations) != 0 { - r.relatedInfo = append(r.relatedInfo, createDiagnosticForNode(unmatchedProperty.Declarations[0], diagnostics.X_0_is_declared_here, propName)) - } - } else if r.tryElaborateArrayLikeErrors(source, target, false /*reportErrors*/) { - sourceType, targetType := r.c.getTypeNamesForErrorDisplay(source, target) - if len(props) > 5 { - propNames := strings.Join(core.Map(props[:4], r.c.symbolToString), ", ") - r.reportError(diagnostics.Type_0_is_missing_the_following_properties_from_type_1_Colon_2_and_3_more, sourceType, targetType, propNames, len(props)-4) - } else { - propNames := strings.Join(core.Map(props, r.c.symbolToString), ", ") - r.reportError(diagnostics.Type_0_is_missing_the_following_properties_from_type_1_Colon_2, sourceType, targetType, propNames) - } - } -} - -func (r *Relater) tryElaborateArrayLikeErrors(source *Type, target *Type, reportErrors bool) bool { - /** - * The spec for elaboration is: - * - If the source is a readonly tuple and the target is a mutable array or tuple, elaborate on mutability and skip property elaborations. - * - If the source is a tuple then skip property elaborations if the target is an array or tuple. - * - If the source is a readonly array and the target is a mutable array or tuple, elaborate on mutability and skip property elaborations. - * - If the source an array then skip property elaborations if the target is a tuple. - */ - if isTupleType(source) { - if source.TargetTupleType().readonly && r.c.isMutableArrayOrTuple(target) { - if reportErrors { - r.reportError(diagnostics.The_type_0_is_readonly_and_cannot_be_assigned_to_the_mutable_type_1, r.c.TypeToString(source), r.c.TypeToString(target)) - } - return false - } - return r.c.isArrayOrTupleType(target) - } - if r.c.isReadonlyArrayType(source) && r.c.isMutableArrayOrTuple(target) { - if reportErrors { - r.reportError(diagnostics.The_type_0_is_readonly_and_cannot_be_assigned_to_the_mutable_type_1, r.c.TypeToString(source), r.c.TypeToString(target)) - } - return false - } - if isTupleType(target) { - return r.c.isArrayType(source) - } - return true -} - -func (r *Relater) tryElaborateErrorsForPrimitivesAndObjects(source *Type, target *Type) { - if (source == r.c.globalStringType && target == r.c.stringType) || - (source == r.c.globalNumberType && target == r.c.numberType) || - (source == r.c.globalBooleanType && target == r.c.booleanType) || - (source == r.c.getGlobalESSymbolType() && target == r.c.esSymbolType) { - r.reportError(diagnostics.X_0_is_a_primitive_but_1_is_a_wrapper_object_Prefer_using_0_when_possible, r.c.TypeToString(target), r.c.TypeToString(source)) - } -} - -func (r *Relater) propertiesIdenticalTo(source *Type, target *Type, excludedProperties collections.Set[string]) Ternary { - if source.flags&TypeFlagsObject == 0 || target.flags&TypeFlagsObject == 0 { - return TernaryFalse - } - sourceProperties := excludeProperties(r.c.getPropertiesOfObjectType(source), excludedProperties) - targetProperties := excludeProperties(r.c.getPropertiesOfObjectType(target), excludedProperties) - if len(sourceProperties) != len(targetProperties) { - return TernaryFalse - } - result := TernaryTrue - for _, sourceProp := range sourceProperties { - targetProp := r.c.getPropertyOfObjectType(target, sourceProp.Name) - if targetProp == nil { - return TernaryFalse - } - related := r.c.compareProperties(sourceProp, targetProp, r.isRelatedToSimple) - if related == TernaryFalse { - return TernaryFalse - } - result &= related - } - return result -} - -func (r *Relater) signaturesRelatedTo(source *Type, target *Type, kind SignatureKind, reportErrors bool, intersectionState IntersectionState) Ternary { - if r.relation == r.c.identityRelation { - return r.signaturesIdenticalTo(source, target, kind) - } - // With respect to signatures, the anyFunctionType wildcard is a subtype of every other function type. - if source == r.c.anyFunctionType { - return TernaryTrue - } - if target == r.c.anyFunctionType { - return TernaryFalse - } - sourceSignatures := r.c.getSignaturesOfType(source, kind) - targetSignatures := r.c.getSignaturesOfType(target, kind) - if kind == SignatureKindConstruct && len(sourceSignatures) != 0 && len(targetSignatures) != 0 { - sourceIsAbstract := sourceSignatures[0].flags&SignatureFlagsAbstract != 0 - targetIsAbstract := targetSignatures[0].flags&SignatureFlagsAbstract != 0 - if sourceIsAbstract && !targetIsAbstract { - // An abstract constructor type is not assignable to a non-abstract constructor type - // as it would otherwise be possible to new an abstract class. Note that the assignability - // check we perform for an extends clause excludes construct signatures from the target, - // so this check never proceeds. - if reportErrors { - r.reportError(diagnostics.Cannot_assign_an_abstract_constructor_type_to_a_non_abstract_constructor_type) - } - return TernaryFalse - } - if !r.constructorVisibilitiesAreCompatible(sourceSignatures[0], targetSignatures[0], reportErrors) { - return TernaryFalse - } - } - result := TernaryTrue - switch { - case source.objectFlags&ObjectFlagsInstantiated != 0 && target.objectFlags&ObjectFlagsInstantiated != 0 && source.symbol == target.symbol || - source.objectFlags&ObjectFlagsReference != 0 && target.objectFlags&ObjectFlagsReference != 0 && source.Target() == target.Target(): - // We have instantiations of the same anonymous type (which typically will be the type of a - // method). Simply do a pairwise comparison of the signatures in the two signature lists instead - // of the much more expensive N * M comparison matrix we explore below. We erase type parameters - // as they are known to always be the same. - for i := range targetSignatures { - related := r.signatureRelatedTo(sourceSignatures[i], targetSignatures[i], true /*erase*/, reportErrors, intersectionState) - if related == TernaryFalse { - return TernaryFalse - } - result &= related - } - case len(sourceSignatures) == 1 && len(targetSignatures) == 1: - // For simple functions (functions with a single signature) we only erase type parameters for - // the comparable relation. Otherwise, if the source signature is generic, we instantiate it - // in the context of the target signature before checking the relationship. Ideally we'd do - // this regardless of the number of signatures, but the potential costs are prohibitive due - // to the quadratic nature of the logic below. - eraseGenerics := r.relation == r.c.comparableRelation - result = r.signatureRelatedTo(sourceSignatures[0], targetSignatures[0], eraseGenerics, reportErrors, intersectionState) - default: - outer: - for _, t := range targetSignatures { - saveErrorState := r.getErrorState() - // Only elaborate errors from the first failure - shouldElaborateErrors := reportErrors - for _, s := range sourceSignatures { - related := r.signatureRelatedTo(s, t, true /*erase*/, shouldElaborateErrors, intersectionState) - if related != TernaryFalse { - result &= related - r.restoreErrorState(saveErrorState) - continue outer - } - shouldElaborateErrors = false - } - if shouldElaborateErrors { - r.reportError(diagnostics.Type_0_provides_no_match_for_the_signature_1, r.c.TypeToString(source), r.c.signatureToString(t)) - } - return TernaryFalse - } - } - return result -} - -func (r *Relater) constructorVisibilitiesAreCompatible(sourceSignature *Signature, targetSignature *Signature, reportErrors bool) bool { - if sourceSignature.declaration == nil || targetSignature.declaration == nil { - return true - } - sourceAccessibility := sourceSignature.declaration.ModifierFlags() & ast.ModifierFlagsNonPublicAccessibilityModifier - targetAccessibility := targetSignature.declaration.ModifierFlags() & ast.ModifierFlagsNonPublicAccessibilityModifier - // A public, protected and private signature is assignable to a private signature. - if targetAccessibility == ast.ModifierFlagsPrivate { - return true - } - // A public and protected signature is assignable to a protected signature. - if targetAccessibility == ast.ModifierFlagsProtected && sourceAccessibility != ast.ModifierFlagsPrivate { - return true - } - // Only a public signature is assignable to public signature. - if targetAccessibility != ast.ModifierFlagsProtected && sourceAccessibility == 0 { - return true - } - if reportErrors { - r.reportError(diagnostics.Cannot_assign_a_0_constructor_type_to_a_1_constructor_type, visibilityToString(sourceAccessibility), visibilityToString(targetAccessibility)) - } - return false -} - -// See signatureAssignableTo, compareSignaturesIdentical -func (r *Relater) signatureRelatedTo(source *Signature, target *Signature, erase bool, reportErrors bool, intersectionState IntersectionState) Ternary { - checkMode := SignatureCheckModeNone - switch { - case r.relation == r.c.subtypeRelation: - checkMode = SignatureCheckModeStrictTopSignature - case r.relation == r.c.strictSubtypeRelation: - checkMode = SignatureCheckModeStrictTopSignature | SignatureCheckModeStrictArity - } - if erase { - source = r.c.getErasedSignature(source) - target = r.c.getErasedSignature(target) - } - isRelatedToWorker := func(source *Type, target *Type, reportErrors bool) Ternary { - return r.isRelatedToEx(source, target, RecursionFlagsBoth, reportErrors, nil /*headMessage*/, intersectionState) - } - return r.c.compareSignaturesRelated(source, target, checkMode, reportErrors, r.reportError, isRelatedToWorker, r.c.reportUnreliableMapper) -} - -func (r *Relater) signaturesIdenticalTo(source *Type, target *Type, kind SignatureKind) Ternary { - sourceSignatures := r.c.getSignaturesOfType(source, kind) - targetSignatures := r.c.getSignaturesOfType(target, kind) - if len(sourceSignatures) != len(targetSignatures) { - return TernaryFalse - } - result := TernaryTrue - for i := range sourceSignatures { - related := r.c.compareSignaturesIdentical(sourceSignatures[i], targetSignatures[i], false /*partialMatch*/, false /*ignoreThisTypes*/, false /*ignoreReturnTypes*/, r.isRelatedToSimple) - if related == 0 { - return TernaryFalse - } - result &= related - } - return result -} - -func (r *Relater) indexSignaturesRelatedTo(source *Type, target *Type, sourceIsPrimitive bool, reportErrors bool, intersectionState IntersectionState) Ternary { - if r.relation == r.c.identityRelation { - return r.indexSignaturesIdenticalTo(source, target) - } - indexInfos := r.c.getIndexInfosOfType(target) - targetHasStringIndex := core.Some(indexInfos, func(info *IndexInfo) bool { return info.keyType == r.c.stringType }) - result := TernaryTrue - for _, targetInfo := range indexInfos { - var related Ternary - switch { - case r.relation != r.c.strictSubtypeRelation && !sourceIsPrimitive && targetHasStringIndex && targetInfo.valueType.flags&TypeFlagsAny != 0: - related = TernaryTrue - case r.c.isGenericMappedType(source) && targetHasStringIndex: - related = r.isRelatedTo(r.c.getTemplateTypeFromMappedType(source), targetInfo.valueType, RecursionFlagsBoth, reportErrors) - default: - related = r.typeRelatedToIndexInfo(source, targetInfo, reportErrors, intersectionState) - } - if related == TernaryFalse { - return TernaryFalse - } - result &= related - } - return result -} - -func (r *Relater) typeRelatedToIndexInfo(source *Type, targetInfo *IndexInfo, reportErrors bool, intersectionState IntersectionState) Ternary { - sourceInfo := r.c.getApplicableIndexInfo(source, targetInfo.keyType) - if sourceInfo != nil { - return r.indexInfoRelatedTo(sourceInfo, targetInfo, reportErrors, intersectionState) - } - // Intersection constituents are never considered to have an inferred index signature. Also, in the strict subtype relation, - // only fresh object literals are considered to have inferred index signatures. This ensures { [x: string]: xxx } <: {} but - // not vice-versa. Without this rule, those types would be mutual strict subtypes. - if intersectionState&IntersectionStateSource == 0 && (r.relation != r.c.strictSubtypeRelation || source.objectFlags&ObjectFlagsFreshLiteral != 0) && r.c.isObjectTypeWithInferableIndex(source) { - return r.membersRelatedToIndexInfo(source, targetInfo, reportErrors, intersectionState) - } - if reportErrors { - r.reportError(diagnostics.Index_signature_for_type_0_is_missing_in_type_1, r.c.TypeToString(targetInfo.keyType), r.c.TypeToString(source)) - } - return TernaryFalse -} - -/** - * Return true if type was inferred from an object literal, written as an object type literal, or is the shape of a module - * with no call or construct signatures. - */ -func (c *Checker) isObjectTypeWithInferableIndex(t *Type) bool { - if t.flags&TypeFlagsIntersection != 0 { - return core.Every(t.Types(), c.isObjectTypeWithInferableIndex) - } - return t.symbol != nil && t.symbol.Flags&(ast.SymbolFlagsObjectLiteral|ast.SymbolFlagsTypeLiteral|ast.SymbolFlagsEnum|ast.SymbolFlagsValueModule) != 0 && - t.symbol.Flags&ast.SymbolFlagsClass == 0 && !c.typeHasCallOrConstructSignatures(t) || - t.objectFlags&ObjectFlagsObjectRestType != 0 || - t.objectFlags&ObjectFlagsReverseMapped != 0 && c.isObjectTypeWithInferableIndex(t.AsReverseMappedType().source) -} - -func (r *Relater) membersRelatedToIndexInfo(source *Type, targetInfo *IndexInfo, reportErrors bool, intersectionState IntersectionState) Ternary { - result := TernaryTrue - keyType := targetInfo.keyType - var props []*ast.Symbol - if source.flags&TypeFlagsIntersection != 0 { - props = r.c.getPropertiesOfUnionOrIntersectionType(source) - } else { - props = r.c.getPropertiesOfObjectType(source) - } - for _, prop := range props { - // Skip over ignored JSX and symbol-named members - if isIgnoredJsxProperty(source, prop) { - continue - } - if r.c.isApplicableIndexType(r.c.getLiteralTypeFromProperty(prop, TypeFlagsStringOrNumberLiteralOrUnique, false), keyType) { - propType := r.c.getNonMissingTypeOfSymbol(prop) - var t *Type - if r.c.exactOptionalPropertyTypes || propType.flags&TypeFlagsUndefined != 0 || keyType == r.c.numberType || prop.Flags&ast.SymbolFlagsOptional == 0 { - t = propType - } else { - t = r.c.getTypeWithFacts(propType, TypeFactsNEUndefined) - } - related := r.isRelatedToEx(t, targetInfo.valueType, RecursionFlagsBoth, reportErrors, nil /*headMessage*/, intersectionState) - if related == TernaryFalse { - if reportErrors { - r.reportError(diagnostics.Property_0_is_incompatible_with_index_signature, r.c.symbolToString(prop)) - } - return TernaryFalse - } - result &= related - } - } - for _, info := range r.c.getIndexInfosOfType(source) { - if r.c.isApplicableIndexType(info.keyType, keyType) { - related := r.indexInfoRelatedTo(info, targetInfo, reportErrors, intersectionState) - if !(related != 0) { - return TernaryFalse - } - result &= related - } - } - return result -} - -func (r *Relater) indexInfoRelatedTo(sourceInfo *IndexInfo, targetInfo *IndexInfo, reportErrors bool, intersectionState IntersectionState) Ternary { - related := r.isRelatedToEx(sourceInfo.valueType, targetInfo.valueType, RecursionFlagsBoth, reportErrors, nil /*headMessage*/, intersectionState) - if related == TernaryFalse && reportErrors { - if sourceInfo.keyType == targetInfo.keyType { - r.reportError(diagnostics.X_0_index_signatures_are_incompatible, r.c.TypeToString(sourceInfo.keyType)) - } else { - r.reportError(diagnostics.X_0_and_1_index_signatures_are_incompatible, r.c.TypeToString(sourceInfo.keyType), r.c.TypeToString(targetInfo.keyType)) - } - } - return related -} - -func (r *Relater) indexSignaturesIdenticalTo(source *Type, target *Type) Ternary { - sourceInfos := r.c.getIndexInfosOfType(source) - targetInfos := r.c.getIndexInfosOfType(target) - if len(sourceInfos) != len(targetInfos) { - return TernaryFalse - } - for _, targetInfo := range targetInfos { - sourceInfo := r.c.getIndexInfoOfType(source, targetInfo.keyType) - if !(sourceInfo != nil && r.isRelatedTo(sourceInfo.valueType, targetInfo.valueType, RecursionFlagsBoth, false) != TernaryFalse && sourceInfo.isReadonly == targetInfo.isReadonly) { - return TernaryFalse - } - } - return TernaryTrue -} - -func (r *Relater) reportErrorResults(originalSource *Type, originalTarget *Type, source *Type, target *Type, headMessage *diagnostics.Message) { - sourceHasBase := r.c.getSingleBaseForNonAugmentingSubtype(originalSource) != nil - targetHasBase := r.c.getSingleBaseForNonAugmentingSubtype(originalTarget) != nil - if originalSource.alias != nil || sourceHasBase { - source = originalSource - } - if originalTarget.alias != nil || targetHasBase { - target = originalTarget - } - if source.flags&TypeFlagsObject != 0 && target.flags&TypeFlagsObject != 0 { - r.tryElaborateArrayLikeErrors(source, target, true /*reportErrors*/) - } - switch { - case source.flags&TypeFlagsObject != 0 && target.flags&TypeFlagsPrimitive != 0: - r.tryElaborateErrorsForPrimitivesAndObjects(source, target) - case source.symbol != nil && source.flags&TypeFlagsObject != 0 && r.c.globalObjectType == source: - r.reportError(diagnostics.The_Object_type_is_assignable_to_very_few_other_types_Did_you_mean_to_use_the_any_type_instead) - case source.objectFlags&ObjectFlagsJsxAttributes != 0 && target.flags&TypeFlagsIntersection != 0: - targetTypes := target.Types() - intrinsicAttributes := r.c.getJsxType(JsxNames.IntrinsicAttributes, r.errorNode) - intrinsicClassAttributes := r.c.getJsxType(JsxNames.IntrinsicClassAttributes, r.errorNode) - if !r.c.isErrorType(intrinsicAttributes) && !r.c.isErrorType(intrinsicClassAttributes) && (slices.Contains(targetTypes, intrinsicAttributes) || slices.Contains(targetTypes, intrinsicClassAttributes)) { - return - } - case originalTarget.flags&TypeFlagsIntersection != 0 && originalTarget.objectFlags&ObjectFlagsIsNeverIntersection != 0: - message := diagnostics.The_intersection_0_was_reduced_to_never_because_property_1_has_conflicting_types_in_some_constituents - prop := core.Find(r.c.getPropertiesOfUnionOrIntersectionType(originalTarget), r.c.isDiscriminantWithNeverType) - if prop == nil { - message = diagnostics.The_intersection_0_was_reduced_to_never_because_property_1_exists_in_multiple_constituents_and_is_private_in_some - prop = core.Find(r.c.getPropertiesOfUnionOrIntersectionType(originalTarget), isConflictingPrivateProperty) - } - if prop != nil { - r.reportError(message, r.c.typeToStringEx(originalTarget, nil /*enclosingDeclaration*/, TypeFormatFlagsNoTypeReduction), r.c.symbolToString(prop)) - } - } - r.reportRelationError(headMessage, source, target) - if source.flags&TypeFlagsTypeParameter != 0 && source.symbol != nil && len(source.symbol.Declarations) != 0 && r.c.getConstraintOfType(source) == nil { - syntheticParam := r.c.cloneTypeParameter(source) - syntheticParam.AsTypeParameter().constraint = r.c.instantiateType(target, newSimpleTypeMapper(source, syntheticParam)) - if r.c.hasNonCircularBaseConstraint(syntheticParam) { - targetConstraintString := r.c.TypeToString(target) - r.relatedInfo = append(r.relatedInfo, NewDiagnosticForNode(source.symbol.Declarations[0], diagnostics.This_type_parameter_might_need_an_extends_0_constraint, targetConstraintString)) - } - } -} - -func (r *Relater) reportRelationError(message *diagnostics.Message, source *Type, target *Type) { - sourceType, targetType := r.c.getTypeNamesForErrorDisplay(source, target) - generalizedSource := source - generalizedSourceType := sourceType - // Don't generalize on 'never' - we really want the original type - // to be displayed for use-cases like 'assertNever'. - if target.flags&TypeFlagsNever == 0 && isLiteralType(source) && !r.c.typeCouldHaveTopLevelSingletonTypes(target) { - generalizedSource = r.c.getBaseTypeOfLiteralType(source) - debug.Assert(!r.c.isTypeAssignableTo(generalizedSource, target), "generalized source shouldn't be assignable") - generalizedSourceType = r.c.getTypeNameForErrorDisplay(generalizedSource) - } - // If `target` is of indexed access type (and `source` it is not), we use the object type of `target` for better error reporting - var targetFlags TypeFlags - if target.flags&TypeFlagsIndexedAccess != 0 && source.flags&TypeFlagsIndexedAccess == 0 { - targetFlags = target.AsIndexedAccessType().objectType.flags - } else { - targetFlags = target.flags - } - if targetFlags&TypeFlagsTypeParameter != 0 && target != r.c.markerSuperTypeForCheck && target != r.c.markerSubTypeForCheck { - constraint := r.c.getBaseConstraintOfType(target) - switch { - case constraint != nil && r.c.isTypeAssignableTo(generalizedSource, constraint): - r.reportError(diagnostics.X_0_is_assignable_to_the_constraint_of_type_1_but_1_could_be_instantiated_with_a_different_subtype_of_constraint_2, generalizedSourceType, targetType, r.c.TypeToString(constraint)) - case constraint != nil && r.c.isTypeAssignableTo(source, constraint): - r.reportError(diagnostics.X_0_is_assignable_to_the_constraint_of_type_1_but_1_could_be_instantiated_with_a_different_subtype_of_constraint_2, sourceType, targetType, r.c.TypeToString(constraint)) - default: - r.errorChain = nil // Only report this error once - r.reportError(diagnostics.X_0_could_be_instantiated_with_an_arbitrary_type_which_could_be_unrelated_to_1, targetType, generalizedSourceType) - } - } - if message == nil { - switch { - case r.relation == r.c.comparableRelation: - message = diagnostics.Type_0_is_not_comparable_to_type_1 - case sourceType == targetType: - message = diagnostics.Type_0_is_not_assignable_to_type_1_Two_different_types_with_this_name_exist_but_they_are_unrelated - case r.c.exactOptionalPropertyTypes && len(r.c.getExactOptionalUnassignableProperties(source, target)) != 0: - message = diagnostics.Type_0_is_not_assignable_to_type_1_with_exactOptionalPropertyTypes_Colon_true_Consider_adding_undefined_to_the_types_of_the_target_s_properties - default: - if source.flags&TypeFlagsStringLiteral != 0 && target.flags&TypeFlagsUnion != 0 { - suggestedType := r.c.getSuggestedTypeForNonexistentStringLiteralType(source, target) - if suggestedType != nil { - r.reportError(diagnostics.Type_0_is_not_assignable_to_type_1_Did_you_mean_2, generalizedSourceType, targetType, r.c.TypeToString(suggestedType)) - return - } - } - message = diagnostics.Type_0_is_not_assignable_to_type_1 - } - } - switch r.getChainMessage(0) { - // Suppress if next message is an excess property error - case diagnostics.Object_literal_may_only_specify_known_properties_and_0_does_not_exist_in_type_1, - diagnostics.Object_literal_may_only_specify_known_properties_but_0_does_not_exist_in_type_1_Did_you_mean_to_write_2: - return - // Suppress if next message is an excessive complexity/stack depth message for source and target or a readonly - // vs. mutable error for source and target - case diagnostics.Excessive_complexity_comparing_types_0_and_1, - diagnostics.Excessive_stack_depth_comparing_types_0_and_1, - diagnostics.The_type_0_is_readonly_and_cannot_be_assigned_to_the_mutable_type_1: - if r.chainArgsMatch(generalizedSourceType, targetType) { - return - } - // Suppress if next message is a missing property message for source and target and we're not - // reporting on conversion or interface implementation - case diagnostics.Property_0_is_missing_in_type_1_but_required_in_type_2: - if !isConversionOrInterfaceImplementationMessage(message) && r.chainArgsMatch(nil, generalizedSourceType, targetType) { - return - } - // Suppress if next message is a missing property message for source and target and we're not - // reporting on conversion or interface implementation - case diagnostics.Type_0_is_missing_the_following_properties_from_type_1_Colon_2_and_3_more, - diagnostics.Type_0_is_missing_the_following_properties_from_type_1_Colon_2: - if !isConversionOrInterfaceImplementationMessage(message) && r.chainArgsMatch(generalizedSourceType, targetType) { - return - } - } - r.reportError(message, generalizedSourceType, targetType) -} - -func (r *Relater) reportError(message *diagnostics.Message, args ...any) { - if message == diagnostics.Types_of_property_0_are_incompatible { - // Suppress if next message is an excess property error - switch r.getChainMessage(0) { - case diagnostics.Object_literal_may_only_specify_known_properties_and_0_does_not_exist_in_type_1, - diagnostics.Object_literal_may_only_specify_known_properties_but_0_does_not_exist_in_type_1_Did_you_mean_to_write_2: - return - } - // Transform a property incompatibility message for property 'x' followed by some elaboration message - // followed by a signature return type incompatibility message into a single return type incompatibility - // message for 'x()' or 'x(...)' - var arg string - switch r.getChainMessage(1) { - case diagnostics.Call_signatures_with_no_arguments_have_incompatible_return_types_0_and_1: - arg = getPropertyNameArg(args[0]) + "()" - case diagnostics.Construct_signatures_with_no_arguments_have_incompatible_return_types_0_and_1: - arg = "new " + getPropertyNameArg(args[0]) + "()" - case diagnostics.Call_signature_return_types_0_and_1_are_incompatible: - arg = getPropertyNameArg(args[0]) + "(...)" - case diagnostics.Construct_signature_return_types_0_and_1_are_incompatible: - arg = "new " + getPropertyNameArg(args[0]) + "(...)" - } - if arg != "" { - message = diagnostics.The_types_returned_by_0_are_incompatible_between_these_types - args[0] = arg - r.errorChain = r.errorChain.next.next - } - // Transform a property incompatibility message for property 'x' followed by some elaboration message - // followed by a property incompatibility message for property 'y' into a single property incompatibility - // message for 'x.y' - switch r.getChainMessage(1) { - case diagnostics.Types_of_property_0_are_incompatible, - diagnostics.The_types_of_0_are_incompatible_between_these_types, - diagnostics.The_types_returned_by_0_are_incompatible_between_these_types: - head := getPropertyNameArg(args[0]) - tail := getPropertyNameArg(r.errorChain.next.args[0]) - arg := addToDottedName(head, tail) - r.errorChain = r.errorChain.next.next - if message == diagnostics.Types_of_property_0_are_incompatible { - message = diagnostics.The_types_of_0_are_incompatible_between_these_types - } - r.reportError(message, arg) - return - } - } - r.errorChain = &ErrorChain{next: r.errorChain, message: message, args: args} -} - -func addToDottedName(head string, tail string) string { - if strings.HasPrefix(head, "new ") { - head = "(" + head + ")" - } - pos := 0 - for { - if strings.HasPrefix(tail[pos:], "(") { - pos++ - } else if strings.HasPrefix(tail[pos:], "new ") { - pos += 4 - } else { - break - } - } - prefix := tail[:pos] - suffix := tail[pos:] - if strings.HasPrefix(suffix, "[") { - return prefix + head + suffix - } - return prefix + head + "." + suffix -} - -func (r *Relater) getChainMessage(index int) *diagnostics.Message { - e := r.errorChain - for { - if e == nil { - return nil - } - if index == 0 { - return e.message - } - e = e.next - index-- - } -} - -// Return true if the arguments of the first entry on the error chain match the -// given arguments (where nil acts as a wildcard). -func (r *Relater) chainArgsMatch(args ...any) bool { - for i, a := range args { - if a != nil && a != r.errorChain.args[i] { - return false - } - } - return true -} - -func getPropertyNameArg(arg any) string { - s := arg.(string) - if len(s) != 0 && (s[0] == '"' || s[0] == '\'' || s[0] == '`') { - return "[" + s + "]" - } - return s -} - -func isConversionOrInterfaceImplementationMessage(message *diagnostics.Message) bool { - return message == diagnostics.Class_0_incorrectly_implements_interface_1 || - message == diagnostics.Class_0_incorrectly_implements_class_1_Did_you_mean_to_extend_1_and_inherit_its_members_as_a_subclass || - message == diagnostics.Conversion_of_type_0_to_type_1_may_be_a_mistake_because_neither_type_sufficiently_overlaps_with_the_other_If_this_was_intentional_convert_the_expression_to_unknown_first -} - -func chainDepth(chain *ErrorChain) int { - depth := 0 - for chain != nil { - depth++ - chain = chain.next - } - return depth -} - -// An object type S is considered to be derived from an object type T if -// S is a union type and every constituent of S is derived from T, -// T is a union type and S is derived from at least one constituent of T, or -// S is an intersection type and some constituent of S is derived from T, or -// S is a type variable with a base constraint that is derived from T, or -// T is {} and S is an object-like type (ensuring {} is less derived than Object), or -// T is one of the global types Object and Function and S is a subtype of T, or -// T occurs directly or indirectly in an 'extends' clause of S. -// Note that this check ignores type parameters and only considers the -// inheritance hierarchy. -func (c *Checker) isTypeDerivedFrom(source *Type, target *Type) bool { - switch { - case source.flags&TypeFlagsUnion != 0: - return core.Every(source.AsUnionType().types, func(t *Type) bool { - return c.isTypeDerivedFrom(t, target) - }) - case target.flags&TypeFlagsUnion != 0: - return core.Some(target.AsUnionType().types, func(t *Type) bool { - return c.isTypeDerivedFrom(source, t) - }) - case source.flags&TypeFlagsIntersection != 0: - return core.Some(source.AsIntersectionType().types, func(t *Type) bool { - return c.isTypeDerivedFrom(t, target) - }) - case source.flags&TypeFlagsInstantiableNonPrimitive != 0: - constraint := c.getBaseConstraintOfType(source) - if constraint == nil { - constraint = c.unknownType - } - return c.isTypeDerivedFrom(constraint, target) - case c.IsEmptyAnonymousObjectType(target): - return source.flags&(TypeFlagsObject|TypeFlagsNonPrimitive) != 0 - case target == c.globalObjectType: - return source.flags&(TypeFlagsObject|TypeFlagsNonPrimitive) != 0 && !c.IsEmptyAnonymousObjectType(source) - case target == c.globalFunctionType: - return source.flags&TypeFlagsObject != 0 && c.isFunctionObjectType(source) - default: - return c.hasBaseType(source, c.getTargetType(target)) || (c.isArrayType(target) && !c.isReadonlyArrayType(target) && c.isTypeDerivedFrom(source, c.globalReadonlyArrayType)) - } -} - -func (c *Checker) isDistributionDependent(root *ConditionalRoot) bool { - return root.isDistributive && (c.isTypeParameterPossiblyReferenced(root.checkType, root.node.TrueType) || c.isTypeParameterPossiblyReferenced(root.checkType, root.node.FalseType)) -} diff --git a/kitcom/internal/tsgo/checker/services.go b/kitcom/internal/tsgo/checker/services.go deleted file mode 100644 index d68c417..0000000 --- a/kitcom/internal/tsgo/checker/services.go +++ /dev/null @@ -1,811 +0,0 @@ -package checker - -import ( - "maps" - "slices" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" -) - -func (c *Checker) GetSymbolsInScope(location *ast.Node, meaning ast.SymbolFlags) []*ast.Symbol { - return c.getSymbolsInScope(location, meaning) -} - -func (c *Checker) getSymbolsInScope(location *ast.Node, meaning ast.SymbolFlags) []*ast.Symbol { - if location.Flags&ast.NodeFlagsInWithStatement != 0 { - // We cannot answer semantic questions within a with block, do not proceed any further - return nil - } - - symbols := make(ast.SymbolTable) - isStaticSymbol := false - - // Copy the given symbol into symbol tables if the symbol has the given meaning - // and it doesn't already exists in the symbol table. - copySymbol := func(symbol *ast.Symbol, meaning ast.SymbolFlags) { - if GetCombinedLocalAndExportSymbolFlags(symbol)&meaning != 0 { - id := symbol.Name - // We will copy all symbol regardless of its reserved name because - // symbolsToArray will check whether the key is a reserved name and - // it will not copy symbol with reserved name to the array - if _, ok := symbols[id]; !ok { - symbols[id] = symbol - } - } - } - - copySymbols := func(source ast.SymbolTable, meaning ast.SymbolFlags) { - if meaning != 0 { - for _, symbol := range source { - copySymbol(symbol, meaning) - } - } - } - - copyLocallyVisibleExportSymbols := func(source ast.SymbolTable, meaning ast.SymbolFlags) { - if meaning != 0 { - for _, symbol := range source { - // Similar condition as in `resolveNameHelper` - if ast.GetDeclarationOfKind(symbol, ast.KindExportSpecifier) == nil && - ast.GetDeclarationOfKind(symbol, ast.KindNamespaceExport) == nil && - symbol.Name != ast.InternalSymbolNameDefault { - copySymbol(symbol, meaning) - } - } - } - } - - populateSymbols := func() { - for location != nil { - if canHaveLocals(location) && location.Locals() != nil && !ast.IsGlobalSourceFile(location) { - copySymbols(location.Locals(), meaning) - } - - switch location.Kind { - case ast.KindSourceFile: - if !ast.IsExternalModule(location.AsSourceFile()) { - break - } - fallthrough - case ast.KindModuleDeclaration: - copyLocallyVisibleExportSymbols(c.getSymbolOfDeclaration(location).Exports, meaning&ast.SymbolFlagsModuleMember) - case ast.KindEnumDeclaration: - copySymbols(c.getSymbolOfDeclaration(location).Exports, meaning&ast.SymbolFlagsEnumMember) - case ast.KindClassExpression: - className := location.AsClassExpression().Name() - if className != nil { - copySymbol(location.Symbol(), meaning) - } - // this fall-through is necessary because we would like to handle - // type parameter inside class expression similar to how we handle it in classDeclaration and interface Declaration. - fallthrough - case ast.KindClassDeclaration, ast.KindInterfaceDeclaration: - // If we didn't come from static member of class or interface, - // add the type parameters into the symbol table - // (type parameters of classDeclaration/classExpression and interface are in member property of the symbol. - // Note: that the memberFlags come from previous iteration. - if !isStaticSymbol { - copySymbols(c.getMembersOfSymbol(c.getSymbolOfDeclaration(location)), meaning&ast.SymbolFlagsType) - } - case ast.KindFunctionExpression: - funcName := location.Name() - if funcName != nil { - copySymbol(location.Symbol(), meaning) - } - } - - if introducesArgumentsExoticObject(location) { - copySymbol(c.argumentsSymbol, meaning) - } - - isStaticSymbol = ast.IsStatic(location) - location = location.Parent - } - - copySymbols(c.globals, meaning) - } - - populateSymbols() - - delete(symbols, ast.InternalSymbolNameThis) // Not a symbol, a keyword - return symbolsToArray(symbols) -} - -func (c *Checker) GetExportsOfModule(symbol *ast.Symbol) []*ast.Symbol { - return symbolsToArray(c.getExportsOfModule(symbol)) -} - -func (c *Checker) ForEachExportAndPropertyOfModule(moduleSymbol *ast.Symbol, cb func(*ast.Symbol, string)) { - for key, exportedSymbol := range c.getExportsOfModule(moduleSymbol) { - if !isReservedMemberName(key) { - cb(exportedSymbol, key) - } - } - - exportEquals := c.resolveExternalModuleSymbol(moduleSymbol, false /*dontResolveAlias*/) - if exportEquals == moduleSymbol { - return - } - - typeOfSymbol := c.getTypeOfSymbol(exportEquals) - if !c.shouldTreatPropertiesOfExternalModuleAsExports(typeOfSymbol) { - return - } - - // forEachPropertyOfType - reducedType := c.getReducedApparentType(typeOfSymbol) - if reducedType.flags&TypeFlagsStructuredType == 0 { - return - } - for name, symbol := range c.resolveStructuredTypeMembers(reducedType).members { - if c.isNamedMember(symbol, name) { - cb(symbol, name) - } - } -} - -func (c *Checker) IsValidPropertyAccess(node *ast.Node, propertyName string) bool { - return c.isValidPropertyAccess(node, propertyName) -} - -func (c *Checker) isValidPropertyAccess(node *ast.Node, propertyName string) bool { - switch node.Kind { - case ast.KindPropertyAccessExpression: - return c.isValidPropertyAccessWithType(node, node.Expression().Kind == ast.KindSuperKeyword, propertyName, c.getWidenedType(c.checkExpression(node.Expression()))) - case ast.KindQualifiedName: - return c.isValidPropertyAccessWithType(node, false /*isSuper*/, propertyName, c.getWidenedType(c.checkExpression(node.AsQualifiedName().Left))) - case ast.KindImportType: - return c.isValidPropertyAccessWithType(node, false /*isSuper*/, propertyName, c.getTypeFromTypeNode(node)) - } - panic("Unexpected node kind in isValidPropertyAccess: " + node.Kind.String()) -} - -func (c *Checker) isValidPropertyAccessWithType(node *ast.Node, isSuper bool, propertyName string, t *Type) bool { - // Short-circuiting for improved performance. - if IsTypeAny(t) { - return true - } - - prop := c.getPropertyOfType(t, propertyName) - return prop != nil && c.isPropertyAccessible(node, isSuper, false /*isWrite*/, t, prop) -} - -// Checks if an existing property access is valid for completions purposes. -// node: a property access-like node where we want to check if we can access a property. -// This node does not need to be an access of the property we are checking. -// e.g. in completions, this node will often be an incomplete property access node, as in `foo.`. -// Besides providing a location (i.e. scope) used to check property accessibility, we use this node for -// computing whether this is a `super` property access. -// type: the type whose property we are checking. -// property: the accessed property's symbol. -func (c *Checker) IsValidPropertyAccessForCompletions(node *ast.Node, t *Type, property *ast.Symbol) bool { - return c.isPropertyAccessible( - node, - node.Kind == ast.KindPropertyAccessExpression && node.Expression().Kind == ast.KindSuperKeyword, - false, /*isWrite*/ - t, - property, - ) - // Previously we validated the 'this' type of methods but this adversely affected performance. See #31377 for more context. -} - -func (c *Checker) GetAllPossiblePropertiesOfTypes(types []*Type) []*ast.Symbol { - unionType := c.getUnionType(types) - if unionType.flags&TypeFlagsUnion == 0 { - return c.getAugmentedPropertiesOfType(unionType) - } - - props := make(ast.SymbolTable) - for _, memberType := range types { - augmentedProps := c.getAugmentedPropertiesOfType(memberType) - for _, p := range augmentedProps { - if _, ok := props[p.Name]; !ok { - prop := c.createUnionOrIntersectionProperty(unionType, p.Name, false /*skipObjectFunctionPropertyAugment*/) - // May be undefined if the property is private - if prop != nil { - props[p.Name] = prop - } - } - } - } - return slices.Collect(maps.Values(props)) -} - -func (c *Checker) IsUnknownSymbol(symbol *ast.Symbol) bool { - return symbol == c.unknownSymbol -} - -func (c *Checker) IsUndefinedSymbol(symbol *ast.Symbol) bool { - return symbol == c.undefinedSymbol -} - -func (c *Checker) IsArgumentsSymbol(symbol *ast.Symbol) bool { - return symbol == c.argumentsSymbol -} - -// Originally from services.ts -func (c *Checker) GetNonOptionalType(t *Type) *Type { - return c.removeOptionalTypeMarker(t) -} - -func (c *Checker) GetStringIndexType(t *Type) *Type { - return c.getIndexTypeOfType(t, c.stringType) -} - -func (c *Checker) GetNumberIndexType(t *Type) *Type { - return c.getIndexTypeOfType(t, c.numberType) -} - -func (c *Checker) GetCallSignatures(t *Type) []*Signature { - return c.getSignaturesOfType(t, SignatureKindCall) -} - -func (c *Checker) GetConstructSignatures(t *Type) []*Signature { - return c.getSignaturesOfType(t, SignatureKindConstruct) -} - -func (c *Checker) GetApparentProperties(t *Type) []*ast.Symbol { - return c.getAugmentedPropertiesOfType(t) -} - -func (c *Checker) getAugmentedPropertiesOfType(t *Type) []*ast.Symbol { - t = c.getApparentType(t) - propsByName := createSymbolTable(c.getPropertiesOfType(t)) - var functionType *Type - if len(c.getSignaturesOfType(t, SignatureKindCall)) > 0 { - functionType = c.globalCallableFunctionType - } else if len(c.getSignaturesOfType(t, SignatureKindConstruct)) > 0 { - functionType = c.globalNewableFunctionType - } - - if propsByName == nil { - propsByName = make(ast.SymbolTable) - } - if functionType != nil { - for _, p := range c.getPropertiesOfType(functionType) { - if _, ok := propsByName[p.Name]; !ok { - propsByName[p.Name] = p - } - } - } - return c.getNamedMembers(propsByName) -} - -func (c *Checker) TryGetMemberInModuleExportsAndProperties(memberName string, moduleSymbol *ast.Symbol) *ast.Symbol { - symbol := c.TryGetMemberInModuleExports(memberName, moduleSymbol) - if symbol != nil { - return symbol - } - - exportEquals := c.resolveExternalModuleSymbol(moduleSymbol, false /*dontResolveAlias*/) - if exportEquals == moduleSymbol { - return nil - } - - t := c.getTypeOfSymbol(exportEquals) - if c.shouldTreatPropertiesOfExternalModuleAsExports(t) { - return c.getPropertyOfType(t, memberName) - } - return nil -} - -func (c *Checker) TryGetMemberInModuleExports(memberName string, moduleSymbol *ast.Symbol) *ast.Symbol { - symbolTable := c.getExportsOfModule(moduleSymbol) - return symbolTable[memberName] -} - -func (c *Checker) shouldTreatPropertiesOfExternalModuleAsExports(resolvedExternalModuleType *Type) bool { - return resolvedExternalModuleType.flags&TypeFlagsPrimitive == 0 || - resolvedExternalModuleType.objectFlags&ObjectFlagsClass != 0 || - // `isArrayOrTupleLikeType` is too expensive to use in this auto-imports hot path. - c.isArrayType(resolvedExternalModuleType) || - isTupleType(resolvedExternalModuleType) -} - -func (c *Checker) GetContextualType(node *ast.Expression, contextFlags ContextFlags) *Type { - if contextFlags&ContextFlagsCompletions != 0 { - return runWithInferenceBlockedFromSourceNode(c, node, func() *Type { return c.getContextualType(node, contextFlags) }) - } - return c.getContextualType(node, contextFlags) -} - -func runWithInferenceBlockedFromSourceNode[T any](c *Checker, node *ast.Node, fn func() T) T { - containingCall := ast.FindAncestor(node, ast.IsCallLikeExpression) - if containingCall != nil { - toMarkSkip := node - for { - c.skipDirectInferenceNodes.Add(toMarkSkip) - toMarkSkip = toMarkSkip.Parent - if toMarkSkip == nil || toMarkSkip == containingCall { - break - } - } - } - - c.isInferencePartiallyBlocked = true - result := runWithoutResolvedSignatureCaching(c, node, fn) - c.isInferencePartiallyBlocked = false - - c.skipDirectInferenceNodes.Clear() - return result -} - -func GetResolvedSignatureForSignatureHelp(node *ast.Node, argumentCount int, c *Checker) (*Signature, []*Signature) { - type result struct { - signature *Signature - candidates []*Signature - } - res := runWithoutResolvedSignatureCaching(c, node, func() result { - signature, candidates := c.getResolvedSignatureWorker(node, CheckModeIsForSignatureHelp, argumentCount) - return result{signature, candidates} - }) - return res.signature, res.candidates -} - -func runWithoutResolvedSignatureCaching[T any](c *Checker, node *ast.Node, fn func() T) T { - ancestorNode := ast.FindAncestor(node, ast.IsCallLikeOrFunctionLikeExpression) - if ancestorNode != nil { - cachedResolvedSignatures := make(map[*SignatureLinks]*Signature) - cachedTypes := make(map[*ValueSymbolLinks]*Type) - for ancestorNode != nil { - signatureLinks := c.signatureLinks.Get(ancestorNode) - cachedResolvedSignatures[signatureLinks] = signatureLinks.resolvedSignature - signatureLinks.resolvedSignature = nil - if ast.IsFunctionExpressionOrArrowFunction(ancestorNode) { - symbolLinks := c.valueSymbolLinks.Get(c.getSymbolOfDeclaration(ancestorNode)) - resolvedType := symbolLinks.resolvedType - cachedTypes[symbolLinks] = resolvedType - symbolLinks.resolvedType = nil - } - ancestorNode = ast.FindAncestor(ancestorNode.Parent, ast.IsCallLikeOrFunctionLikeExpression) - } - result := fn() - for signatureLinks, resolvedSignature := range cachedResolvedSignatures { - signatureLinks.resolvedSignature = resolvedSignature - } - for symbolLinks, resolvedType := range cachedTypes { - symbolLinks.resolvedType = resolvedType - } - return result - } - return fn() -} - -func (c *Checker) SkipAlias(symbol *ast.Symbol) *ast.Symbol { - if symbol.Flags&ast.SymbolFlagsAlias != 0 { - return c.GetAliasedSymbol(symbol) - } - return symbol -} - -func (c *Checker) GetRootSymbols(symbol *ast.Symbol) []*ast.Symbol { - roots := c.getImmediateRootSymbols(symbol) - if len(roots) == 0 { - return []*ast.Symbol{symbol} - } - var result []*ast.Symbol - for _, root := range roots { - result = append(result, c.GetRootSymbols(root)...) - } - return result -} - -func (c *Checker) getImmediateRootSymbols(symbol *ast.Symbol) []*ast.Symbol { - if symbol.CheckFlags&ast.CheckFlagsSynthetic != 0 { - return core.MapNonNil( - c.valueSymbolLinks.Get(symbol).containingType.Types(), - func(t *Type) *ast.Symbol { - return c.getPropertyOfType(t, symbol.Name) - }) - } - if symbol.Flags&ast.SymbolFlagsTransient != 0 { - if c.spreadLinks.Has(symbol) { - leftSpread := c.spreadLinks.Get(symbol).leftSpread - rightSpread := c.spreadLinks.Get(symbol).rightSpread - if leftSpread != nil { - return []*ast.Symbol{leftSpread, rightSpread} - } - } - if c.mappedSymbolLinks.Has(symbol) { - syntheticOrigin := c.mappedSymbolLinks.Get(symbol).syntheticOrigin - if syntheticOrigin != nil { - return []*ast.Symbol{syntheticOrigin} - } - } - target := c.tryGetTarget(symbol) - if target != nil { - return []*ast.Symbol{target} - } - } - return nil -} - -func (c *Checker) tryGetTarget(symbol *ast.Symbol) *ast.Symbol { - var target *ast.Symbol - next := symbol - for { - if c.valueSymbolLinks.Has(next) { - next = c.valueSymbolLinks.Get(next).target - } else if c.exportTypeLinks.Has(next) { - next = c.exportTypeLinks.Get(next).target - } else { - next = nil - } - if next == nil { - break - } - target = next - } - return target -} - -func (c *Checker) GetExportSymbolOfSymbol(symbol *ast.Symbol) *ast.Symbol { - return c.getMergedSymbol(core.IfElse(symbol.ExportSymbol != nil, symbol.ExportSymbol, symbol)) -} - -func (c *Checker) GetExportSpecifierLocalTargetSymbol(node *ast.Node) *ast.Symbol { - // node should be ExportSpecifier | Identifier - switch node.Kind { - case ast.KindExportSpecifier: - if node.Parent.Parent.AsExportDeclaration().ModuleSpecifier != nil { - return c.getExternalModuleMember(node.Parent.Parent, node, false /*dontResolveAlias*/) - } - name := node.PropertyNameOrName() - if name.Kind == ast.KindStringLiteral { - // Skip for invalid syntax like this: export { "x" } - return nil - } - return c.resolveEntityName(name, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace|ast.SymbolFlagsAlias, true /*ignoreErrors*/, false, nil) - case ast.KindIdentifier: - return c.resolveEntityName(node, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace|ast.SymbolFlagsAlias, true /*ignoreErrors*/, false, nil) - } - panic("Unhandled case in getExportSpecifierLocalTargetSymbol, node should be ExportSpecifier | Identifier") -} - -func (c *Checker) GetShorthandAssignmentValueSymbol(location *ast.Node) *ast.Symbol { - if location != nil && location.Kind == ast.KindShorthandPropertyAssignment { - return c.resolveEntityName(location.Name(), ast.SymbolFlagsValue|ast.SymbolFlagsAlias, true /*ignoreErrors*/, false, nil) - } - return nil -} - -/** -* Get symbols that represent parameter-property-declaration as parameter and as property declaration -* @param parameter a parameterDeclaration node -* @param parameterName a name of the parameter to get the symbols for. -* @return a tuple of two symbols - */ -func (c *Checker) GetSymbolsOfParameterPropertyDeclaration(parameter *ast.Node /*ParameterPropertyDeclaration*/, parameterName string) (*ast.Symbol, *ast.Symbol) { - constructorDeclaration := parameter.Parent - classDeclaration := parameter.Parent.Parent - - parameterSymbol := c.getSymbol(constructorDeclaration.Locals(), parameterName, ast.SymbolFlagsValue) - propertySymbol := c.getSymbol(c.getMembersOfSymbol(classDeclaration.Symbol()), parameterName, ast.SymbolFlagsValue) - - if parameterSymbol != nil && propertySymbol != nil { - return parameterSymbol, propertySymbol - } - - panic("There should exist two symbols, one as property declaration and one as parameter declaration") -} - -func (c *Checker) GetTypeArgumentConstraint(node *ast.Node) *Type { - if !ast.IsTypeNode(node) { - return nil - } - return c.getTypeArgumentConstraint(node) -} - -func (c *Checker) getTypeArgumentConstraint(node *ast.Node) *Type { - typeReferenceNode := core.IfElse(ast.IsTypeReferenceType(node.Parent), node.Parent, nil) - if typeReferenceNode == nil { - return nil - } - typeParameters := c.getTypeParametersForTypeReferenceOrImport(typeReferenceNode) - if len(typeParameters) == 0 { - return nil - } - - typeParamIndex := core.FindIndex(typeReferenceNode.TypeArguments(), func(n *ast.Node) bool { - return n == node - }) - constraint := c.getConstraintOfTypeParameter(typeParameters[typeParamIndex]) - if constraint != nil { - return c.instantiateType( - constraint, - newTypeMapper(typeParameters, c.getEffectiveTypeArguments(typeReferenceNode, typeParameters))) - } - return nil -} - -func (c *Checker) IsTypeInvalidDueToUnionDiscriminant(contextualType *Type, obj *ast.Node) bool { - properties := obj.Properties() - return core.Some(properties, func(property *ast.Node) bool { - var nameType *Type - propertyName := property.Name() - if propertyName != nil { - if ast.IsJsxNamespacedName(propertyName) { - nameType = c.getStringLiteralType(propertyName.Text()) - } else { - nameType = c.getLiteralTypeFromPropertyName(propertyName) - } - } - var name string - if nameType != nil && isTypeUsableAsPropertyName(nameType) { - name = getPropertyNameFromType(nameType) - } - var expected *Type - if name != "" { - expected = c.getTypeOfPropertyOfType(contextualType, name) - } - return expected != nil && isLiteralType(expected) && !c.isTypeAssignableTo(c.getTypeOfNode(property), expected) - }) -} - -// Unlike `getExportsOfModule`, this includes properties of an `export =` value. -func (c *Checker) GetExportsAndPropertiesOfModule(moduleSymbol *ast.Symbol) []*ast.Symbol { - exports := c.getExportsOfModuleAsArray(moduleSymbol) - exportEquals := c.resolveExternalModuleSymbol(moduleSymbol, false /*dontResolveAlias*/) - if exportEquals != moduleSymbol { - t := c.getTypeOfSymbol(exportEquals) - if c.shouldTreatPropertiesOfExternalModuleAsExports(t) { - exports = append(exports, c.getPropertiesOfType(t)...) - } - } - return exports -} - -func (c *Checker) getExportsOfModuleAsArray(moduleSymbol *ast.Symbol) []*ast.Symbol { - return symbolsToArray(c.getExportsOfModule(moduleSymbol)) -} - -// Returns all the properties of the Jsx.IntrinsicElements interface. -func (c *Checker) GetJsxIntrinsicTagNamesAt(location *ast.Node) []*ast.Symbol { - intrinsics := c.getJsxType(JsxNames.IntrinsicElements, location) - if intrinsics == nil { - return nil - } - return c.GetPropertiesOfType(intrinsics) -} - -func (c *Checker) GetContextualTypeForJsxAttribute(attribute *ast.JsxAttributeLike) *Type { - return c.getContextualTypeForJsxAttribute(attribute, ContextFlagsNone) -} - -func (c *Checker) GetConstantValue(node *ast.Node) any { - if node.Kind == ast.KindEnumMember { - return c.getEnumMemberValue(node).Value - } - - if c.symbolNodeLinks.Get(node).resolvedSymbol == nil { - c.checkExpressionCached(node) // ensure cached resolved symbol is set - } - symbol := c.symbolNodeLinks.Get(node).resolvedSymbol - if symbol == nil && ast.IsEntityNameExpression(node) { - symbol = c.resolveEntityName( - node, - ast.SymbolFlagsValue, - true, /*ignoreErrors*/ - false, /*dontResolveAlias*/ - nil /*location*/) - } - if symbol != nil && symbol.Flags&ast.SymbolFlagsEnumMember != 0 { - // inline property\index accesses only for const enums - member := symbol.ValueDeclaration - if ast.IsEnumConst(member.Parent) { - return c.getEnumMemberValue(member).Value - } - } - - return nil -} - -func (c *Checker) getResolvedSignatureWorker(node *ast.Node, checkMode CheckMode, argumentCount int) (*Signature, []*Signature) { - parsedNode := printer.NewEmitContext().ParseNode(node) - c.apparentArgumentCount = &argumentCount - candidatesOutArray := &[]*Signature{} - var res *Signature - if parsedNode != nil { - res = c.getResolvedSignature(parsedNode, candidatesOutArray, checkMode) - } - c.apparentArgumentCount = nil - return res, *candidatesOutArray -} - -func (c *Checker) GetCandidateSignaturesForStringLiteralCompletions(call *ast.CallLikeExpression, editingArgument *ast.Node) []*Signature { - // first, get candidates when inference is blocked from the source node. - candidates := runWithInferenceBlockedFromSourceNode(c, editingArgument, func() []*Signature { - _, blockedInferenceCandidates := c.getResolvedSignatureWorker(call, CheckModeNormal, 0) - return blockedInferenceCandidates - }) - candidatesSet := collections.NewSetFromItems(candidates...) - - // next, get candidates where the source node is considered for inference. - otherCandidates := runWithoutResolvedSignatureCaching(c, editingArgument, func() []*Signature { - _, inferenceCandidates := c.getResolvedSignatureWorker(call, CheckModeNormal, 0) - return inferenceCandidates - }) - - for _, candidate := range otherCandidates { - if candidatesSet.Has(candidate) { - continue - } - candidates = append(candidates, candidate) - } - - return candidates -} - -func (c *Checker) GetTypeParameterAtPosition(s *Signature, pos int) *Type { - t := c.getTypeAtPosition(s, pos) - if t.IsIndex() && isThisTypeParameter(t.AsIndexType().target) { - constraint := c.getBaseConstraintOfType(t.AsIndexType().target) - if constraint != nil { - return c.getIndexType(constraint) - } - } - return t -} - -func (c *Checker) GetContextualDeclarationsForObjectLiteralElement(objectLiteral *ast.Node, name string) []*ast.Node { - var result []*ast.Node - if t := c.getApparentTypeOfContextualType(objectLiteral, ContextFlagsNone); t != nil { - for _, t := range t.Distributed() { - prop := c.getPropertyOfType(t, name) - if prop != nil { - for _, declaration := range prop.Declarations { - result = core.AppendIfUnique(result, declaration) - } - } else { - for _, info := range c.getApplicableIndexInfos(t, c.getStringLiteralType(name)) { - if info.declaration != nil { - result = core.AppendIfUnique(result, info.declaration) - } - } - } - } - } - return result -} - -var knownGenericTypeNames = map[string]struct{}{ - "Array": {}, - "ArrayLike": {}, - "ReadonlyArray": {}, - "Promise": {}, - "PromiseLike": {}, - "Iterable": {}, - "IterableIterator": {}, - "AsyncIterable": {}, - "Set": {}, - "WeakSet": {}, - "ReadonlySet": {}, - "Map": {}, - "WeakMap": {}, - "ReadonlyMap": {}, - "Partial": {}, - "Required": {}, - "Readonly": {}, - "Pick": {}, - "Omit": {}, - "NonNullable": {}, -} - -func isKnownGenericTypeName(name string) bool { - _, exists := knownGenericTypeNames[name] - return exists -} - -func (c *Checker) GetFirstTypeArgumentFromKnownType(t *Type) *Type { - if t.objectFlags&ObjectFlagsReference != 0 && isKnownGenericTypeName(t.symbol.Name) { - symbol := c.getGlobalSymbol(t.symbol.Name, ast.SymbolFlagsType, nil) - if symbol != nil && symbol == t.Target().symbol { - return core.FirstOrNil(c.getTypeArguments(t)) - } - } - if t.alias != nil && isKnownGenericTypeName(t.alias.symbol.Name) { - symbol := c.getGlobalSymbol(t.alias.symbol.Name, ast.SymbolFlagsType, nil) - if symbol != nil && symbol == t.alias.symbol { - return core.FirstOrNil(t.alias.typeArguments) - } - } - return nil -} - -// Gets all symbols for one property. Does not get symbols for every property. -func (c *Checker) GetPropertySymbolsFromContextualType(node *ast.Node, contextualType *Type, unionSymbolOk bool) []*ast.Symbol { - name := ast.GetTextOfPropertyName(node.Name()) - if name == "" { - return nil - } - if contextualType.flags&TypeFlagsUnion == 0 { - if symbol := c.getPropertyOfType(contextualType, name); symbol != nil { - return []*ast.Symbol{symbol} - } - return nil - } - filteredTypes := contextualType.Types() - if ast.IsObjectLiteralExpression(node.Parent) || ast.IsJsxAttributes(node.Parent) { - filteredTypes = core.Filter(filteredTypes, func(t *Type) bool { - return !c.IsTypeInvalidDueToUnionDiscriminant(t, node.Parent) - }) - } - discriminatedPropertySymbols := core.MapNonNil(filteredTypes, func(t *Type) *ast.Symbol { - return c.getPropertyOfType(t, name) - }) - if unionSymbolOk && (len(discriminatedPropertySymbols) == 0 || len(discriminatedPropertySymbols) == len(contextualType.Types())) { - if symbol := c.getPropertyOfType(contextualType, name); symbol != nil { - return []*ast.Symbol{symbol} - } - } - if len(filteredTypes) == 0 && len(discriminatedPropertySymbols) == 0 { - // Bad discriminant -- do again without discriminating - return core.MapNonNil(contextualType.Types(), func(t *Type) *ast.Symbol { - return c.getPropertyOfType(t, name) - }) - } - // by eliminating duplicates we might even end up with a single symbol - // that helps with displaying better quick infos on properties of union types - return core.Deduplicate(discriminatedPropertySymbols) -} - -// Gets the property symbol corresponding to the property in destructuring assignment -// 'property1' from -// -// for ( { property1: a } of elems) { -// } -// -// 'property1' at location 'a' from: -// -// [a] = [ property1, property2 ] -func (c *Checker) GetPropertySymbolOfDestructuringAssignment(location *ast.Node) *ast.Symbol { - if ast.IsArrayLiteralOrObjectLiteralDestructuringPattern(location.Parent.Parent) { - // Get the type of the object or array literal and then look for property of given name in the type - if typeOfObjectLiteral := c.getTypeOfAssignmentPattern(location.Parent.Parent); typeOfObjectLiteral != nil { - return c.getPropertyOfType(typeOfObjectLiteral, location.Text()) - } - } - return nil -} - -// Gets the type of object literal or array literal of destructuring assignment. -// { a } from -// -// for ( { a } of elems) { -// } -// -// [ a ] from -// -// [a] = [ some array ...] -func (c *Checker) getTypeOfAssignmentPattern(expr *ast.Node) *Type { - // If this is from "for of" - // for ( { a } of elems) { - // } - if ast.IsForOfStatement(expr.Parent) { - iteratedType := c.checkRightHandSideOfForOf(expr.Parent) - return c.checkDestructuringAssignment(expr, core.OrElse(iteratedType, c.errorType), CheckModeNormal, false) - } - // If this is from "for" initializer - // for ({a } = elems[0];.....) { } - if ast.IsBinaryExpression(expr.Parent) { - iteratedType := c.getTypeOfExpression(expr.Parent.AsBinaryExpression().Right) - return c.checkDestructuringAssignment(expr, core.OrElse(iteratedType, c.errorType), CheckModeNormal, false) - } - // If this is from nested object binding pattern - // for ({ skills: { primary, secondary } } = multiRobot, i = 0; i < 1; i++) { - if ast.IsPropertyAssignment(expr.Parent) { - node := expr.Parent.Parent - typeOfParentObjectLiteral := core.OrElse(c.getTypeOfAssignmentPattern(node), c.errorType) - propertyIndex := slices.Index(node.AsObjectLiteralExpression().Properties.Nodes, expr.Parent) - return c.checkObjectLiteralDestructuringPropertyAssignment(node, typeOfParentObjectLiteral, propertyIndex, nil, false) - } - // Array literal assignment - array destructuring pattern - node := expr.Parent - // [{ property1: p1, property2 }] = elems; - typeOfArrayLiteral := core.OrElse(c.getTypeOfAssignmentPattern(node), c.errorType) - elementType := core.OrElse(c.checkIteratedTypeOrElementType(IterationUseDestructuring, typeOfArrayLiteral, c.undefinedType, expr.Parent), c.errorType) - return c.checkArrayLiteralDestructuringElementAssignment(node, typeOfArrayLiteral, slices.Index(node.AsArrayLiteralExpression().Elements.Nodes, expr), elementType, CheckModeNormal) -} diff --git a/kitcom/internal/tsgo/checker/stringer_generated.go b/kitcom/internal/tsgo/checker/stringer_generated.go deleted file mode 100644 index b910ac0..0000000 --- a/kitcom/internal/tsgo/checker/stringer_generated.go +++ /dev/null @@ -1,24 +0,0 @@ -// Code generated by "stringer -type=SignatureKind -output=stringer_generated.go"; DO NOT EDIT. - -package checker - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[SignatureKindCall-0] - _ = x[SignatureKindConstruct-1] -} - -const _SignatureKind_name = "SignatureKindCallSignatureKindConstruct" - -var _SignatureKind_index = [...]uint8{0, 17, 39} - -func (i SignatureKind) String() string { - if i < 0 || i >= SignatureKind(len(_SignatureKind_index)-1) { - return "SignatureKind(" + strconv.FormatInt(int64(i), 10) + ")" - } - return _SignatureKind_name[_SignatureKind_index[i]:_SignatureKind_index[i+1]] -} diff --git a/kitcom/internal/tsgo/checker/symbolaccessibility.go b/kitcom/internal/tsgo/checker/symbolaccessibility.go deleted file mode 100644 index b491e1c..0000000 --- a/kitcom/internal/tsgo/checker/symbolaccessibility.go +++ /dev/null @@ -1,759 +0,0 @@ -package checker - -import ( - "reflect" - "slices" - "unsafe" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" -) - -func (ch *Checker) IsTypeSymbolAccessible(typeSymbol *ast.Symbol, enclosingDeclaration *ast.Node) bool { - access := ch.isSymbolAccessibleWorker(typeSymbol, enclosingDeclaration, ast.SymbolFlagsType /*shouldComputeAliasesToMakeVisible*/, false /*allowModules*/, true) - return access.Accessibility == printer.SymbolAccessibilityAccessible -} - -func (ch *Checker) IsValueSymbolAccessible(symbol *ast.Symbol, enclosingDeclaration *ast.Node) bool { - access := ch.isSymbolAccessibleWorker(symbol, enclosingDeclaration, ast.SymbolFlagsValue /*shouldComputeAliasesToMakeVisible*/, false /*allowModules*/, true) - return access.Accessibility == printer.SymbolAccessibilityAccessible -} - -func (ch *Checker) IsSymbolAccessibleByFlags(symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags ast.SymbolFlags) bool { - access := ch.isSymbolAccessibleWorker(symbol, enclosingDeclaration, flags /*shouldComputeAliasesToMakeVisible*/, false /*allowModules*/, false) // TODO: Strada bug? Why is this allowModules: false? - return access.Accessibility == printer.SymbolAccessibilityAccessible -} - -func (ch *Checker) IsAnySymbolAccessible(symbols []*ast.Symbol, enclosingDeclaration *ast.Node, initialSymbol *ast.Symbol, meaning ast.SymbolFlags, shouldComputeAliasesToMakeVisible bool, allowModules bool) *printer.SymbolAccessibilityResult { - if len(symbols) == 0 { - return nil - } - - var hadAccessibleChain *ast.Symbol - earlyModuleBail := false - for _, symbol := range symbols { - // Symbol is accessible if it by itself is accessible - accessibleSymbolChain := ch.getAccessibleSymbolChain(symbol, enclosingDeclaration, meaning /*useOnlyExternalAliasing*/, false) - if len(accessibleSymbolChain) > 0 { - hadAccessibleChain = symbol - // TODO: going through emit resolver here is weird. Relayer these APIs. - hasAccessibleDeclarations := ch.GetEmitResolver().hasVisibleDeclarations(accessibleSymbolChain[0], shouldComputeAliasesToMakeVisible) - if hasAccessibleDeclarations != nil { - return hasAccessibleDeclarations - } - } - if allowModules { - if core.Some(symbol.Declarations, hasNonGlobalAugmentationExternalModuleSymbol) { - if shouldComputeAliasesToMakeVisible { - earlyModuleBail = true - // Generally speaking, we want to use the aliases that already exist to refer to a module, if present - // In order to do so, we need to find those aliases in order to retain them in declaration emit; so - // if we are in declaration emit, we cannot use the fast path for module visibility until we've exhausted - // all other visibility options (in order to capture the possible aliases used to reference the module) - continue - } - // Any meaning of a module symbol is always accessible via an `import` type - return &printer.SymbolAccessibilityResult{ - Accessibility: printer.SymbolAccessibilityAccessible, - } - } - } - - // If we haven't got the accessible symbol, it doesn't mean the symbol is actually inaccessible. - // It could be a qualified symbol and hence verify the path - // e.g.: - // module m { - // export class c { - // } - // } - // const x: typeof m.c - // In the above example when we start with checking if typeof m.c symbol is accessible, - // we are going to see if c can be accessed in scope directly. - // But it can't, hence the accessible is going to be undefined, but that doesn't mean m.c is inaccessible - // It is accessible if the parent m is accessible because then m.c can be accessed through qualification - - containers := ch.getContainersOfSymbol(symbol, enclosingDeclaration, meaning) - nextMeaning := meaning - if initialSymbol == symbol { - nextMeaning = getQualifiedLeftMeaning(meaning) - } - parentResult := ch.IsAnySymbolAccessible(containers, enclosingDeclaration, initialSymbol, nextMeaning, shouldComputeAliasesToMakeVisible, allowModules) - if parentResult != nil { - return parentResult - } - } - - if earlyModuleBail { - return &printer.SymbolAccessibilityResult{ - Accessibility: printer.SymbolAccessibilityAccessible, - } - } - - if hadAccessibleChain != nil { - var moduleName string - if hadAccessibleChain != initialSymbol { - moduleName = ch.symbolToStringEx(hadAccessibleChain, enclosingDeclaration, ast.SymbolFlagsNamespace, SymbolFormatFlagsAllowAnyNodeKind) - } - return &printer.SymbolAccessibilityResult{ - Accessibility: printer.SymbolAccessibilityNotAccessible, - ErrorSymbolName: ch.symbolToStringEx(initialSymbol, enclosingDeclaration, meaning, SymbolFormatFlagsAllowAnyNodeKind), - ErrorModuleName: moduleName, - } - } - return nil -} - -func hasNonGlobalAugmentationExternalModuleSymbol(declaration *ast.Node) bool { - return ast.IsModuleWithStringLiteralName(declaration) || (declaration.Kind == ast.KindSourceFile && ast.IsExternalOrCommonJSModule(declaration.AsSourceFile())) -} - -func getQualifiedLeftMeaning(rightMeaning ast.SymbolFlags) ast.SymbolFlags { - // If we are looking in value space, the parent meaning is value, other wise it is namespace - if rightMeaning == ast.SymbolFlagsValue { - return ast.SymbolFlagsValue - } - return ast.SymbolFlagsNamespace -} - -func (ch *Checker) getWithAlternativeContainers(container *ast.Symbol, symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags) []*ast.Symbol { - additionalContainers := core.MapNonNil(container.Declarations, func(d *ast.Node) *ast.Symbol { - return ch.getFileSymbolIfFileSymbolExportEqualsContainer(d, container) - }) - var reexportContainers []*ast.Symbol - if enclosingDeclaration != nil { - reexportContainers = ch.getAlternativeContainingModules(symbol, enclosingDeclaration) - } - objectLiteralContainer := ch.getVariableDeclarationOfObjectLiteral(container, meaning) - leftMeaning := getQualifiedLeftMeaning(meaning) - if enclosingDeclaration != nil && - container.Flags&leftMeaning != 0 && - len(ch.getAccessibleSymbolChain(container, enclosingDeclaration, ast.SymbolFlagsNamespace /*useOnlyExternalAliasing*/, false)) > 0 { - // This order expresses a preference for the real container if it is in scope - res := append(append([]*ast.Symbol{container}, additionalContainers...), reexportContainers...) - if objectLiteralContainer != nil { - res = append(res, objectLiteralContainer) - } - return res - } - // we potentially have a symbol which is a member of the instance side of something - look for a variable in scope with the container's type - // which may be acting like a namespace (eg, `Symbol` acts like a namespace when looking up `Symbol.toStringTag`) - var firstVariableMatch *ast.Symbol - if (meaning == ast.SymbolFlagsValue && - container.Flags&leftMeaning == 0) && - container.Flags&ast.SymbolFlagsType != 0 && - ch.getDeclaredTypeOfSymbol(container).flags&TypeFlagsObject != 0 { - ch.someSymbolTableInScope(enclosingDeclaration, func(t ast.SymbolTable, _ bool, _ bool, _ *ast.Node) bool { - for _, s := range t { - if s.Flags&leftMeaning != 0 && ch.getTypeOfSymbol(s) == ch.getDeclaredTypeOfSymbol(container) { - firstVariableMatch = s - return true - } - } - return false - }) - } - - var res []*ast.Symbol - if firstVariableMatch != nil { - res = append(res, firstVariableMatch) - } - res = append(res, additionalContainers...) - res = append(res, container) - if objectLiteralContainer != nil { - res = append(res, objectLiteralContainer) - } - res = append(res, reexportContainers...) - return res -} - -func (ch *Checker) getAlternativeContainingModules(symbol *ast.Symbol, enclosingDeclaration *ast.Node) []*ast.Symbol { - if enclosingDeclaration == nil { - return nil - } - containingFile := ast.GetSourceFileOfNode(enclosingDeclaration) - id := ast.GetNodeId(containingFile.AsNode()) - links := ch.symbolContainerLinks.Get(symbol) - if links.extendedContainersByFile == nil { - links.extendedContainersByFile = make(map[ast.NodeId][]*ast.Symbol) - } - existing, ok := links.extendedContainersByFile[id] - if ok && existing != nil { - return existing - } - var results []*ast.Symbol - if len(containingFile.Imports()) > 0 { - // Try to make an import using an import already in the enclosing file, if possible - for _, importRef := range containingFile.Imports() { - if ast.NodeIsSynthesized(importRef) { - // Synthetic names can't be resolved by `resolveExternalModuleName` - they'll cause a debug assert if they error - continue - } - resolvedModule := ch.resolveExternalModuleName(enclosingDeclaration, importRef /*ignoreErrors*/, true) - if resolvedModule == nil { - continue - } - ref := ch.getAliasForSymbolInContainer(resolvedModule, symbol) - if ref == nil { - continue - } - results = append(results, resolvedModule) - } - if len(results) > 0 { - links.extendedContainersByFile[id] = results - return results - } - } - - if links.extendedContainers != nil { - return *links.extendedContainers - } - // No results from files already being imported by this file - expand search (expensive, but not location-specific, so cached) - otherFiles := ch.program.SourceFiles() - for _, file := range otherFiles { - if !ast.IsExternalModule(file) { - continue - } - sym := ch.getSymbolOfDeclaration(file.AsNode()) - ref := ch.getAliasForSymbolInContainer(sym, symbol) - if ref == nil { - continue - } - results = append(results, sym) - } - links.extendedContainers = &results - return results -} - -func (ch *Checker) getVariableDeclarationOfObjectLiteral(symbol *ast.Symbol, meaning ast.SymbolFlags) *ast.Symbol { - // If we're trying to reference some object literal in, eg `var a = { x: 1 }`, the symbol for the literal, `__object`, is distinct - // from the symbol of the declaration it is being assigned to. Since we can use the declaration to refer to the literal, however, - // we'd like to make that connection here - potentially causing us to paint the declaration's visibility, and therefore the literal. - if meaning&ast.SymbolFlagsValue == 0 { - return nil - } - if len(symbol.Declarations) == 0 { - return nil - } - firstDecl := symbol.Declarations[0] - if firstDecl.Parent == nil { - return nil - } - if !ast.IsVariableDeclaration(firstDecl.Parent) { - return nil - } - if ast.IsObjectLiteralExpression(firstDecl) && firstDecl == firstDecl.Parent.Initializer() || ast.IsTypeLiteralNode(firstDecl) && firstDecl == firstDecl.Parent.Type() { - return ch.getSymbolOfDeclaration(firstDecl.Parent) - } - return nil -} - -func hasExternalModuleSymbol(declaration *ast.Node) bool { - return ast.IsAmbientModule(declaration) || (declaration.Kind == ast.KindSourceFile && ast.IsExternalOrCommonJSModule(declaration.AsSourceFile())) -} - -func (ch *Checker) getExternalModuleContainer(declaration *ast.Node) *ast.Symbol { - node := ast.FindAncestor(declaration, hasExternalModuleSymbol) - if node == nil { - return nil - } - return ch.getSymbolOfDeclaration(node) -} - -func (ch *Checker) getFileSymbolIfFileSymbolExportEqualsContainer(d *ast.Node, container *ast.Symbol) *ast.Symbol { - fileSymbol := ch.getExternalModuleContainer(d) - if fileSymbol == nil || fileSymbol.Exports == nil { - return nil - } - exported, ok := fileSymbol.Exports[ast.InternalSymbolNameExportEquals] - if !ok || exported == nil { - return nil - } - if ch.getSymbolIfSameReference(exported, container) != nil { - return fileSymbol - } - return nil -} - -/** -* Attempts to find the symbol corresponding to the container a symbol is in - usually this -* is just its' `.parent`, but for locals, this value is `undefined` - */ -func (ch *Checker) getContainersOfSymbol(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags) []*ast.Symbol { - container := ch.getParentOfSymbol(symbol) - // Type parameters end up in the `members` lists but are not externally visible - if container != nil && (symbol.Flags&ast.SymbolFlagsTypeParameter == 0) { - return ch.getWithAlternativeContainers(container, symbol, enclosingDeclaration, meaning) - } - var candidates []*ast.Symbol - for _, d := range symbol.Declarations { - if !ast.IsAmbientModule(d) && d.Parent != nil { - // direct children of a module - if hasNonGlobalAugmentationExternalModuleSymbol(d.Parent) { - sym := ch.getSymbolOfDeclaration(d.Parent) - if sym != nil && !slices.Contains(candidates, sym) { - candidates = append(candidates, sym) - } - continue - } - // export ='d member of an ambient module - if ast.IsModuleBlock(d.Parent) && d.Parent.Parent != nil && ch.resolveExternalModuleSymbol(ch.getSymbolOfDeclaration(d.Parent.Parent), false) == symbol { - sym := ch.getSymbolOfDeclaration(d.Parent.Parent) - if sym != nil && !slices.Contains(candidates, sym) { - candidates = append(candidates, sym) - } - continue - } - } - if ast.IsClassExpression(d) && ast.IsBinaryExpression(d.Parent) && d.Parent.AsBinaryExpression().OperatorToken.Kind == ast.KindEqualsToken && ast.IsAccessExpression(d.Parent.AsBinaryExpression().Left) && ast.IsEntityNameExpression(d.Parent.AsBinaryExpression().Left.Expression()) { - if ast.IsModuleExportsAccessExpression(d.Parent.AsBinaryExpression().Left) || ast.IsExportsIdentifier(d.Parent.AsBinaryExpression().Left.Expression()) { - sym := ch.getSymbolOfDeclaration(ast.GetSourceFileOfNode(d).AsNode()) - if sym != nil && !slices.Contains(candidates, sym) { - candidates = append(candidates, sym) - } - continue - } - ch.checkExpressionCached(d.Parent.AsBinaryExpression().Left.Expression()) - sym := ch.symbolNodeLinks.Get(d.Parent.AsBinaryExpression().Left.Expression()).resolvedSymbol - if sym != nil && !slices.Contains(candidates, sym) { - candidates = append(candidates, sym) - } - continue - } - } - if len(candidates) == 0 { - return nil - } - - var bestContainers []*ast.Symbol - var alternativeContainers []*ast.Symbol - for _, container := range candidates { - if ch.getAliasForSymbolInContainer(container, symbol) == nil { - continue - } - allAlts := ch.getWithAlternativeContainers(container, symbol, enclosingDeclaration, meaning) - if len(allAlts) == 0 { - continue - } - bestContainers = append(bestContainers, allAlts[0]) - alternativeContainers = append(alternativeContainers, allAlts[1:]...) - } - return append(bestContainers, alternativeContainers...) -} - -func (ch *Checker) getAliasForSymbolInContainer(container *ast.Symbol, symbol *ast.Symbol) *ast.Symbol { - if container == ch.getParentOfSymbol(symbol) { - // fast path, `symbol` is either already the alias or isn't aliased - return symbol - } - // Check if container is a thing with an `export=` which points directly at `symbol`, and if so, return - // the container itself as the alias for the symbol - if container.Exports != nil { - exportEquals, ok := container.Exports[ast.InternalSymbolNameExportEquals] - if ok && exportEquals != nil && ch.getSymbolIfSameReference(exportEquals, symbol) != nil { - return container - } - } - exports := ch.getExportsOfSymbol(container) - quick, ok := exports[symbol.Name] - if ok && quick != nil && ch.getSymbolIfSameReference(quick, symbol) != nil { - return quick - } - var candidates []*ast.Symbol - for _, exported := range exports { - if ch.getSymbolIfSameReference(exported, symbol) != nil { - candidates = append(candidates, exported) - } - } - if len(candidates) > 0 { - ch.sortSymbols(candidates) // _must_ sort exports for stable results - symbol table is randomly iterated - return candidates[0] - } - return nil -} - -func (ch *Checker) getAccessibleSymbolChain( - symbol *ast.Symbol, - enclosingDeclaration *ast.Node, - meaning ast.SymbolFlags, - useOnlyExternalAliasing bool, -) []*ast.Symbol { - return ch.getAccessibleSymbolChainEx(accessibleSymbolChainContext{symbol, enclosingDeclaration, meaning, useOnlyExternalAliasing, make(map[ast.SymbolId]map[unsafe.Pointer]struct{})}) -} - -func (ch *Checker) GetAccessibleSymbolChain( - symbol *ast.Symbol, - enclosingDeclaration *ast.Node, - meaning ast.SymbolFlags, - useOnlyExternalAliasing bool, -) []*ast.Symbol { - return ch.getAccessibleSymbolChain(symbol, enclosingDeclaration, meaning, useOnlyExternalAliasing) -} - -type accessibleSymbolChainContext struct { - symbol *ast.Symbol - enclosingDeclaration *ast.Node - meaning ast.SymbolFlags - useOnlyExternalAliasing bool - visitedSymbolTablesMap map[ast.SymbolId]map[unsafe.Pointer]struct{} -} - -func (ch *Checker) getAccessibleSymbolChainEx(ctx accessibleSymbolChainContext) []*ast.Symbol { - if ctx.symbol == nil { - return nil - } - if isPropertyOrMethodDeclarationSymbol(ctx.symbol) { - return nil - } - // Go from enclosingDeclaration to the first scope we check, so the cache is keyed off the scope and thus shared more - var firstRelevantLocation *ast.Node - ch.someSymbolTableInScope(ctx.enclosingDeclaration, func(_ ast.SymbolTable, _ bool, _ bool, node *ast.Node) bool { - firstRelevantLocation = node - return true - }) - links := ch.symbolContainerLinks.Get(ctx.symbol) - linkKey := accessibleChainCacheKey{ctx.useOnlyExternalAliasing, firstRelevantLocation, ctx.meaning} - if links.accessibleChainCache == nil { - links.accessibleChainCache = make(map[accessibleChainCacheKey][]*ast.Symbol) - } - existing, ok := links.accessibleChainCache[linkKey] - if ok { - return existing - } - - var result []*ast.Symbol - - ch.someSymbolTableInScope(ctx.enclosingDeclaration, func(t ast.SymbolTable, ignoreQualification bool, isLocalNameLookup bool, _ *ast.Node) bool { - res := ch.getAccessibleSymbolChainFromSymbolTable(ctx, t, ignoreQualification, isLocalNameLookup) - if len(res) > 0 { - result = res - return true - } - return false - }) - links.accessibleChainCache[linkKey] = result - return result -} - -/** -* @param {ignoreQualification} boolean Set when a symbol is being looked for through the exports of another symbol (meaning we have a route to qualify it already) - */ -func (ch *Checker) getAccessibleSymbolChainFromSymbolTable(ctx accessibleSymbolChainContext, t ast.SymbolTable, ignoreQualification bool, isLocalNameLookup bool) []*ast.Symbol { - symId := ast.GetSymbolId(ctx.symbol) - visitedSymbolTables, ok := ctx.visitedSymbolTablesMap[symId] - if !ok { - visitedSymbolTables = make(map[unsafe.Pointer]struct{}) - ctx.visitedSymbolTablesMap[symId] = visitedSymbolTables - } - - id := reflect.ValueOf(t).UnsafePointer() // TODO: Is this seriously the only way to check reference equality of maps? - _, present := visitedSymbolTables[id] - if present { - return nil - } - visitedSymbolTables[id] = struct{}{} - - res := ch.trySymbolTable(ctx, t, ignoreQualification, isLocalNameLookup) - - delete(visitedSymbolTables, id) - return res -} - -func (ch *Checker) trySymbolTable( - ctx accessibleSymbolChainContext, - symbols ast.SymbolTable, - ignoreQualification bool, - isLocalNameLookup bool, -) []*ast.Symbol { - // If symbol is directly available by its name in the symbol table - res, ok := symbols[ctx.symbol.Name] - if ok && res != nil && ch.isAccessible(ctx, res /*resolvedAliasSymbol*/, nil, ignoreQualification) { - return []*ast.Symbol{ctx.symbol} - } - - var candidateChains [][]*ast.Symbol - // collect all possible chains to sort them and return the shortest/best - for _, symbolFromSymbolTable := range symbols { - // for every non-default, non-export= alias symbol in scope, check if it refers to or can chain to the target symbol - if symbolFromSymbolTable.Flags&ast.SymbolFlagsAlias != 0 && - symbolFromSymbolTable.Name != ast.InternalSymbolNameExportEquals && - symbolFromSymbolTable.Name != ast.InternalSymbolNameDefault && - !(isUMDExportSymbol(symbolFromSymbolTable) && ctx.enclosingDeclaration != nil && ast.IsExternalModule(ast.GetSourceFileOfNode(ctx.enclosingDeclaration))) && - // If `!useOnlyExternalAliasing`, we can use any type of alias to get the name - (!ctx.useOnlyExternalAliasing || core.Some(symbolFromSymbolTable.Declarations, ast.IsExternalModuleImportEqualsDeclaration)) && - // If we're looking up a local name to reference directly, omit namespace reexports, otherwise when we're trawling through an export list to make a dotted name, we can keep it - (isLocalNameLookup && !core.Some(symbolFromSymbolTable.Declarations, isNamespaceReexportDeclaration) || !isLocalNameLookup) && - // While exports are generally considered to be in scope, export-specifier declared symbols are _not_ - // See similar comment in `resolveName` for details - (ignoreQualification || len(getDeclarationsOfKind(symbolFromSymbolTable, ast.KindExportSpecifier)) == 0) { - resolvedImportedSymbol := ch.resolveAlias(symbolFromSymbolTable) - candidate := ch.getCandidateListForSymbol(ctx, symbolFromSymbolTable, resolvedImportedSymbol, ignoreQualification) - if len(candidate) > 0 { - candidateChains = append(candidateChains, candidate) - } - } - if symbolFromSymbolTable.Name == ctx.symbol.Name && symbolFromSymbolTable.ExportSymbol != nil { - if ch.isAccessible(ctx, ch.getMergedSymbol(symbolFromSymbolTable.ExportSymbol) /*resolvedAliasSymbol*/, nil, ignoreQualification) { - candidateChains = append(candidateChains, []*ast.Symbol{ctx.symbol}) - } - } - } - - if len(candidateChains) > 0 { - // pick first, shortest - slices.SortStableFunc(candidateChains, ch.compareSymbolChains) - return candidateChains[0] - } - - // If there's no result and we're looking at the global symbol table, treat `globalThis` like an alias and try to lookup thru that - if reflect.ValueOf(ch.globals).UnsafePointer() == reflect.ValueOf(symbols).UnsafePointer() { - return ch.getCandidateListForSymbol(ctx, ch.globalThisSymbol, ch.globalThisSymbol, ignoreQualification) - } - return nil -} - -func (ch *Checker) compareSymbolChainsWorker(a []*ast.Symbol, b []*ast.Symbol) int { - chainLen := len(a) - len(b) - if chainLen != 0 { - return chainLen - } - - idx := 0 - for idx < len(a) { - comparison := ch.compareSymbols(a[idx], b[idx]) - if comparison != 0 { - return comparison - } - idx++ - } - return 0 -} - -func isUMDExportSymbol(symbol *ast.Symbol) bool { - return symbol != nil && len(symbol.Declarations) > 0 && symbol.Declarations[0] != nil && ast.IsNamespaceExportDeclaration(symbol.Declarations[0]) -} - -func isNamespaceReexportDeclaration(node *ast.Node) bool { - return ast.IsNamespaceExport(node) && node.Parent.AsExportDeclaration().ModuleSpecifier != nil -} - -func (ch *Checker) getCandidateListForSymbol( - ctx accessibleSymbolChainContext, - symbolFromSymbolTable *ast.Symbol, - resolvedImportedSymbol *ast.Symbol, - ignoreQualification bool, -) []*ast.Symbol { - if ch.isAccessible(ctx, symbolFromSymbolTable, resolvedImportedSymbol, ignoreQualification) { - return []*ast.Symbol{symbolFromSymbolTable} - } - - // Look in the exported members, if we can find accessibleSymbolChain, symbol is accessible using this chain - // but only if the symbolFromSymbolTable can be qualified - candidateTable := ch.getExportsOfSymbol(resolvedImportedSymbol) - if candidateTable == nil { - return nil - } - accessibleSymbolsFromExports := ch.getAccessibleSymbolChainFromSymbolTable(ctx, candidateTable /*ignoreQualification*/, true, false) - if len(accessibleSymbolsFromExports) == 0 { - return nil - } - if !ch.canQualifySymbol(ctx, symbolFromSymbolTable, getQualifiedLeftMeaning(ctx.meaning)) { - return nil - } - return append([]*ast.Symbol{symbolFromSymbolTable}, accessibleSymbolsFromExports...) -} - -func (ch *Checker) isAccessible( - ctx accessibleSymbolChainContext, - symbolFromSymbolTable *ast.Symbol, - resolvedAliasSymbol *ast.Symbol, - ignoreQualification bool, -) bool { - likeSymbols := false - if ctx.symbol == resolvedAliasSymbol { - likeSymbols = true - } - if ctx.symbol == symbolFromSymbolTable { - likeSymbols = true - } - symbol := ch.getMergedSymbol(ctx.symbol) - if symbol == ch.getMergedSymbol(resolvedAliasSymbol) { - likeSymbols = true - } - if symbol == ch.getMergedSymbol(symbolFromSymbolTable) { - likeSymbols = true - } - if !likeSymbols { - return false - } - // if the symbolFromSymbolTable is not external module (it could be if it was determined as ambient external module and would be in globals table) - // and if symbolFromSymbolTable or alias resolution matches the symbol, - // check the symbol can be qualified, it is only then this symbol is accessible - return !core.Some(symbolFromSymbolTable.Declarations, hasNonGlobalAugmentationExternalModuleSymbol) && - (ignoreQualification || ch.canQualifySymbol(ctx, ch.getMergedSymbol(symbolFromSymbolTable), ctx.meaning)) -} - -func (ch *Checker) canQualifySymbol( - ctx accessibleSymbolChainContext, - symbolFromSymbolTable *ast.Symbol, - meaning ast.SymbolFlags, -) bool { - // If the symbol is equivalent and doesn't need further qualification, this symbol is accessible - return !ch.needsQualification(symbolFromSymbolTable, ctx.enclosingDeclaration, meaning) || - // If symbol needs qualification, make sure that parent is accessible, if it is then this symbol is accessible too - len(ch.getAccessibleSymbolChainEx(accessibleSymbolChainContext{symbolFromSymbolTable.Parent, ctx.enclosingDeclaration, getQualifiedLeftMeaning(meaning), ctx.useOnlyExternalAliasing, ctx.visitedSymbolTablesMap})) > 0 -} - -func (ch *Checker) needsQualification(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags) bool { - qualify := false - ch.someSymbolTableInScope(enclosingDeclaration, func(symbolTable ast.SymbolTable, _ bool, _ bool, _ *ast.Node) bool { - // If symbol of this name is not available in the symbol table we are ok - res, ok := symbolTable[symbol.Name] - if !ok || res == nil { - return false - } - symbolFromSymbolTable := ch.getMergedSymbol(res) - if symbolFromSymbolTable == nil { - // Continue to the next symbol table - return false - } - // If the symbol with this name is present it should refer to the symbol - if symbolFromSymbolTable == symbol { - // No need to qualify - return true - } - - // Qualify if the symbol from symbol table has same meaning as expected - shouldResolveAlias := symbolFromSymbolTable.Flags&ast.SymbolFlagsAlias != 0 && ast.GetDeclarationOfKind(symbolFromSymbolTable, ast.KindExportSpecifier) == nil - if shouldResolveAlias { - symbolFromSymbolTable = ch.resolveAlias(symbolFromSymbolTable) - } - flags := symbolFromSymbolTable.Flags - if shouldResolveAlias { - flags = ch.getSymbolFlags(symbolFromSymbolTable) - } - if flags&meaning != 0 { - qualify = true - return true - } - - // Continue to the next symbol table - return false - }) - - return qualify -} - -func isPropertyOrMethodDeclarationSymbol(symbol *ast.Symbol) bool { - if len(symbol.Declarations) > 0 { - for _, declaration := range symbol.Declarations { - switch declaration.Kind { - case ast.KindPropertyDeclaration, - ast.KindMethodDeclaration, - ast.KindGetAccessor, - ast.KindSetAccessor: - continue - default: - return false - } - } - return true - } - return false -} - -func (ch *Checker) someSymbolTableInScope( - enclosingDeclaration *ast.Node, - callback func(symbolTable ast.SymbolTable, ignoreQualification bool, isLocalNameLookup bool, scopeNode *ast.Node) bool, -) bool { - for location := enclosingDeclaration; location != nil; location = location.Parent { - // Locals of a source file are not in scope (because they get merged into the global symbol table) - if canHaveLocals(location) && location.Locals() != nil && !ast.IsGlobalSourceFile(location) { - if callback(location.Locals(), false, true, location) { - return true - } - } - switch location.Kind { - case ast.KindSourceFile, ast.KindModuleDeclaration: - if ast.IsSourceFile(location) && !ast.IsExternalOrCommonJSModule(location.AsSourceFile()) { - break - } - sym := ch.getSymbolOfDeclaration(location) - if callback(sym.Exports, false, true, location) { - return true - } - case ast.KindClassDeclaration, ast.KindClassExpression, ast.KindInterfaceDeclaration: - // Type parameters are bound into `members` lists so they can merge across declarations - // This is troublesome, since in all other respects, they behave like locals :cries: - // TODO: the below is shared with similar code in `resolveName` - in fact, rephrasing all this symbol - // lookup logic in terms of `resolveName` would be nice - // The below is used to lookup type parameters within a class or interface, as they are added to the class/interface locals - // These can never be latebound, so the symbol's raw members are sufficient. `getMembersOfNode` cannot be used, as it would - // trigger resolving late-bound names, which we may already be in the process of doing while we're here! - var table ast.SymbolTable - sym := ch.getSymbolOfDeclaration(location) - // TODO: Should this filtered table be cached in some way? - for key, memberSymbol := range sym.Members { - if memberSymbol.Flags&(ast.SymbolFlagsType & ^ast.SymbolFlagsAssignment) != 0 { - if table == nil { - table = make(ast.SymbolTable) - } - table[key] = memberSymbol - } - } - if table != nil && callback(table, false, false, location) { - return true - } - } - } - - return callback(ch.globals, false, true, nil) -} - -/** - * Check if the given symbol in given enclosing declaration is accessible and mark all associated alias to be visible if requested - * - * @param symbol a Symbol to check if accessible - * @param enclosingDeclaration a Node containing reference to the symbol - * @param meaning a SymbolFlags to check if such meaning of the symbol is accessible - * @param shouldComputeAliasToMakeVisible a boolean value to indicate whether to return aliases to be mark visible in case the symbol is accessible - */ - -func (c *Checker) IsSymbolAccessible(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags, shouldComputeAliasesToMakeVisible bool) printer.SymbolAccessibilityResult { - return c.isSymbolAccessibleWorker(symbol, enclosingDeclaration, meaning, shouldComputeAliasesToMakeVisible, true /*allowModules*/) -} - -func (c *Checker) isSymbolAccessibleWorker(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags, shouldComputeAliasesToMakeVisible bool, allowModules bool) printer.SymbolAccessibilityResult { - if symbol != nil && enclosingDeclaration != nil { - result := c.IsAnySymbolAccessible([]*ast.Symbol{symbol}, enclosingDeclaration, symbol, meaning, shouldComputeAliasesToMakeVisible, allowModules) - if result != nil { - return *result - } - - // This could be a symbol that is not exported in the external module - // or it could be a symbol from different external module that is not aliased and hence cannot be named - symbolExternalModule := core.FirstNonNil(symbol.Declarations, c.getExternalModuleContainer) - if symbolExternalModule != nil { - enclosingExternalModule := c.getExternalModuleContainer(enclosingDeclaration) - if symbolExternalModule != enclosingExternalModule { - // name from different external module that is not visible - return printer.SymbolAccessibilityResult{ - Accessibility: printer.SymbolAccessibilityCannotBeNamed, - ErrorSymbolName: c.symbolToStringEx(symbol, enclosingDeclaration, meaning, SymbolFormatFlagsAllowAnyNodeKind), - ErrorModuleName: c.symbolToString(symbolExternalModule), - ErrorNode: core.IfElse(ast.IsInJSFile(enclosingDeclaration), enclosingDeclaration, nil), - } - } - } - - // Just a local name that is not accessible - return printer.SymbolAccessibilityResult{ - Accessibility: printer.SymbolAccessibilityNotAccessible, - ErrorSymbolName: c.symbolToStringEx(symbol, enclosingDeclaration, meaning, SymbolFormatFlagsAllowAnyNodeKind), - } - } - - return printer.SymbolAccessibilityResult{ - Accessibility: printer.SymbolAccessibilityAccessible, - } -} diff --git a/kitcom/internal/tsgo/checker/symboltracker.go b/kitcom/internal/tsgo/checker/symboltracker.go deleted file mode 100644 index b23aa76..0000000 --- a/kitcom/internal/tsgo/checker/symboltracker.go +++ /dev/null @@ -1,138 +0,0 @@ -package checker - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/modulespecifiers" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/nodebuilder" -) - -type SymbolTrackerImpl struct { - context *NodeBuilderContext - inner nodebuilder.SymbolTracker - DisableTrackSymbol bool - tchost Host -} - -func NewSymbolTrackerImpl(context *NodeBuilderContext, tracker nodebuilder.SymbolTracker, tchost Host) *SymbolTrackerImpl { - if tracker != nil { - for { - t, ok := tracker.(*SymbolTrackerImpl) - if !ok { - break - } - tracker = t.inner - } - } - - return &SymbolTrackerImpl{context, tracker, false, tchost} -} - -func (this *SymbolTrackerImpl) GetModuleSpecifierGenerationHost() modulespecifiers.ModuleSpecifierGenerationHost { - if this.inner == nil { - return this.tchost - } - return this.inner.GetModuleSpecifierGenerationHost() -} - -func (this *SymbolTrackerImpl) TrackSymbol(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags) bool { - if !this.DisableTrackSymbol { - if this.inner != nil && this.inner.TrackSymbol(symbol, enclosingDeclaration, meaning) { - this.onDiagnosticReported() - return true - } - // Skip recording type parameters as they dont contribute to late painted statements - if symbol.Flags&ast.SymbolFlagsTypeParameter == 0 { - this.context.trackedSymbols = append(this.context.trackedSymbols, &TrackedSymbolArgs{symbol, enclosingDeclaration, meaning}) - } - } - return false -} - -func (this *SymbolTrackerImpl) ReportInaccessibleThisError() { - this.onDiagnosticReported() - if this.inner == nil { - return - } - this.inner.ReportInaccessibleThisError() -} - -func (this *SymbolTrackerImpl) ReportPrivateInBaseOfClassExpression(propertyName string) { - this.onDiagnosticReported() - if this.inner == nil { - return - } - this.inner.ReportPrivateInBaseOfClassExpression(propertyName) -} - -func (this *SymbolTrackerImpl) ReportInaccessibleUniqueSymbolError() { - this.onDiagnosticReported() - if this.inner == nil { - return - } - this.inner.ReportInaccessibleUniqueSymbolError() -} - -func (this *SymbolTrackerImpl) ReportCyclicStructureError() { - this.onDiagnosticReported() - if this.inner == nil { - return - } - this.inner.ReportCyclicStructureError() -} - -func (this *SymbolTrackerImpl) ReportLikelyUnsafeImportRequiredError(specifier string) { - this.onDiagnosticReported() - if this.inner == nil { - return - } - this.inner.ReportLikelyUnsafeImportRequiredError(specifier) -} - -func (this *SymbolTrackerImpl) ReportTruncationError() { - this.onDiagnosticReported() - if this.inner == nil { - return - } - this.inner.ReportTruncationError() -} - -func (this *SymbolTrackerImpl) ReportNonlocalAugmentation(containingFile *ast.SourceFile, parentSymbol *ast.Symbol, augmentingSymbol *ast.Symbol) { - this.onDiagnosticReported() - if this.inner == nil { - return - } - this.inner.ReportNonlocalAugmentation(containingFile, parentSymbol, augmentingSymbol) -} - -func (this *SymbolTrackerImpl) ReportNonSerializableProperty(propertyName string) { - this.onDiagnosticReported() - if this.inner == nil { - return - } - this.inner.ReportNonSerializableProperty(propertyName) -} - -func (this *SymbolTrackerImpl) onDiagnosticReported() { - this.context.reportedDiagnostic = true -} - -func (this *SymbolTrackerImpl) ReportInferenceFallback(node *ast.Node) { - if this.inner == nil { - return - } - this.inner.ReportInferenceFallback(node) -} - -func (this *SymbolTrackerImpl) PushErrorFallbackNode(node *ast.Node) { - if this.inner == nil { - return - } - this.inner.PushErrorFallbackNode(node) -} - -func (this *SymbolTrackerImpl) PopErrorFallbackNode() { - if this.inner == nil { - return - } - this.inner.PopErrorFallbackNode() -} diff --git a/kitcom/internal/tsgo/checker/types.go b/kitcom/internal/tsgo/checker/types.go deleted file mode 100644 index d09fc0d..0000000 --- a/kitcom/internal/tsgo/checker/types.go +++ /dev/null @@ -1,1287 +0,0 @@ -package checker - -import ( - "slices" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/evaluator" -) - -//go:generate go tool golang.org/x/tools/cmd/stringer -type=SignatureKind -output=stringer_generated.go -//go:generate go tool mvdan.cc/gofumpt -w stringer_generated.go - -// ParseFlags - -type ParseFlags uint32 - -const ( - ParseFlagsNone ParseFlags = 0 - ParseFlagsYield ParseFlags = 1 << 0 - ParseFlagsAwait ParseFlags = 1 << 1 - ParseFlagsType ParseFlags = 1 << 2 - ParseFlagsIgnoreMissingOpenBrace ParseFlags = 1 << 4 - ParseFlagsJSDoc ParseFlags = 1 << 5 -) - -type SignatureKind int32 - -const ( - SignatureKindCall SignatureKind = iota - SignatureKindConstruct -) - -type ContextFlags uint32 - -const ( - ContextFlagsNone ContextFlags = 0 - ContextFlagsSignature ContextFlags = 1 << 0 // Obtaining contextual signature - ContextFlagsNoConstraints ContextFlags = 1 << 1 // Don't obtain type variable constraints - ContextFlagsCompletions ContextFlags = 1 << 2 // Ignore inference to current node and parent nodes out to the containing call for completions - ContextFlagsSkipBindingPatterns ContextFlags = 1 << 3 // Ignore contextual types applied by binding patterns -) - -type TypeFormatFlags uint32 - -const ( - TypeFormatFlagsNone TypeFormatFlags = 0 - TypeFormatFlagsNoTruncation TypeFormatFlags = 1 << 0 // Don't truncate typeToString result - TypeFormatFlagsWriteArrayAsGenericType TypeFormatFlags = 1 << 1 // Write Array instead T[] - TypeFormatFlagsGenerateNamesForShadowedTypeParams TypeFormatFlags = 1 << 2 // When a type parameter T is shadowing another T, generate a name for it so it can still be referenced - TypeFormatFlagsUseStructuralFallback TypeFormatFlags = 1 << 3 // When an alias cannot be named by its symbol, rather than report an error, fallback to a structural printout if possible - // hole because there's a hole in node builder flags - TypeFormatFlagsWriteTypeArgumentsOfSignature TypeFormatFlags = 1 << 5 // Write the type arguments instead of type parameters of the signature - TypeFormatFlagsUseFullyQualifiedType TypeFormatFlags = 1 << 6 // Write out the fully qualified type name (eg. Module.Type, instead of Type) - // hole because `UseOnlyExternalAliasing` is here in node builder flags, but functions which take old flags use `SymbolFormatFlags` instead - TypeFormatFlagsSuppressAnyReturnType TypeFormatFlags = 1 << 8 // If the return type is any-like, don't offer a return type. - // hole because `WriteTypeParametersInQualifiedName` is here in node builder flags, but functions which take old flags use `SymbolFormatFlags` for this instead - TypeFormatFlagsMultilineObjectLiterals TypeFormatFlags = 1 << 10 // Always print object literals across multiple lines (only used to map into node builder flags) - TypeFormatFlagsWriteClassExpressionAsTypeLiteral TypeFormatFlags = 1 << 11 // Write a type literal instead of (Anonymous class) - TypeFormatFlagsUseTypeOfFunction TypeFormatFlags = 1 << 12 // Write typeof instead of function type literal - TypeFormatFlagsOmitParameterModifiers TypeFormatFlags = 1 << 13 // Omit modifiers on parameters - TypeFormatFlagsUseAliasDefinedOutsideCurrentScope TypeFormatFlags = 1 << 14 // For a `type T = ... ` defined in a different file, write `T` instead of its value, even though `T` can't be accessed in the current scope. - TypeFormatFlagsUseSingleQuotesForStringLiteralType TypeFormatFlags = 1 << 28 // Use single quotes for string literal type - TypeFormatFlagsNoTypeReduction TypeFormatFlags = 1 << 29 // Don't call getReducedType - TypeFormatFlagsOmitThisParameter TypeFormatFlags = 1 << 25 - TypeFormatFlagsWriteCallStyleSignature TypeFormatFlags = 1 << 27 // Write construct signatures as call style signatures - // Error Handling - TypeFormatFlagsAllowUniqueESSymbolType TypeFormatFlags = 1 << 20 // This is bit 20 to align with the same bit in `NodeBuilderFlags` - // TypeFormatFlags exclusive - TypeFormatFlagsAddUndefined TypeFormatFlags = 1 << 17 // Add undefined to types of initialized, non-optional parameters - TypeFormatFlagsWriteArrowStyleSignature TypeFormatFlags = 1 << 18 // Write arrow style signature - // State - TypeFormatFlagsInArrayType TypeFormatFlags = 1 << 19 // Writing an array element type - TypeFormatFlagsInElementType TypeFormatFlags = 1 << 21 // Writing an array or union element type - TypeFormatFlagsInFirstTypeArgument TypeFormatFlags = 1 << 22 // Writing first type argument of the instantiated type - TypeFormatFlagsInTypeAlias TypeFormatFlags = 1 << 23 // Writing type in type alias declaration -) - -const TypeFormatFlagsNodeBuilderFlagsMask = TypeFormatFlagsNoTruncation | TypeFormatFlagsWriteArrayAsGenericType | TypeFormatFlagsGenerateNamesForShadowedTypeParams | TypeFormatFlagsUseStructuralFallback | TypeFormatFlagsWriteTypeArgumentsOfSignature | - TypeFormatFlagsUseFullyQualifiedType | TypeFormatFlagsSuppressAnyReturnType | TypeFormatFlagsMultilineObjectLiterals | TypeFormatFlagsWriteClassExpressionAsTypeLiteral | - TypeFormatFlagsUseTypeOfFunction | TypeFormatFlagsOmitParameterModifiers | TypeFormatFlagsUseAliasDefinedOutsideCurrentScope | TypeFormatFlagsAllowUniqueESSymbolType | TypeFormatFlagsInTypeAlias | - TypeFormatFlagsUseSingleQuotesForStringLiteralType | TypeFormatFlagsNoTypeReduction | TypeFormatFlagsOmitThisParameter - -type SymbolFormatFlags int32 - -const ( - SymbolFormatFlagsNone SymbolFormatFlags = 0 - // Write symbols's type argument if it is instantiated symbol - // eg. class C { p: T } <-- Show p as C.p here - // var a: C; - // var p = a.p; <--- Here p is property of C so show it as C.p instead of just C.p - SymbolFormatFlagsWriteTypeParametersOrArguments SymbolFormatFlags = 1 << 0 - // Use only external alias information to get the symbol name in the given context - // eg. module m { export class c { } } import x = m.c; - // When this flag is specified m.c will be used to refer to the class instead of alias symbol x - SymbolFormatFlagsUseOnlyExternalAliasing SymbolFormatFlags = 1 << 1 - // Build symbol name using any nodes needed, instead of just components of an entity name - SymbolFormatFlagsAllowAnyNodeKind SymbolFormatFlags = 1 << 2 - // Prefer aliases which are not directly visible - SymbolFormatFlagsUseAliasDefinedOutsideCurrentScope SymbolFormatFlags = 1 << 3 - // { [E.A]: 1 } - /** @internal */ - SymbolFormatFlagsWriteComputedProps SymbolFormatFlags = 1 << 4 - // Skip building an accessible symbol chain - /** @internal */ - SymbolFormatFlagsDoNotIncludeSymbolChain SymbolFormatFlags = 1 << 5 -) - -// Ids - -type TypeId uint32 - -// Links for referenced symbols - -type SymbolReferenceLinks struct { - referenceKinds ast.SymbolFlags // Flags for the meanings of the symbol that were referenced -} - -// Links for value symbols - -type ValueSymbolLinks struct { - resolvedType *Type // Type of value symbol - writeType *Type - target *ast.Symbol - mapper *TypeMapper - nameType *Type - containingType *Type // Mapped type for mapped type property, containing union or intersection type for synthetic property -} - -// Additional links for mapped symbols - -type MappedSymbolLinks struct { - keyType *Type // Key type for mapped type member - syntheticOrigin *ast.Symbol // For a property on a mapped or spread type, points back to the original property -} - -// Additional links for deferred type symbols - -type DeferredSymbolLinks struct { - parent *Type // Source union/intersection of a deferred type - constituents []*Type // Calculated list of constituents for a deferred type - writeConstituents []*Type // Constituents of a deferred `writeType` -} - -// Links for alias symbols - -type AliasSymbolLinks struct { - immediateTarget *ast.Symbol // Immediate target of an alias. May be another alias. Do not access directly, use `checker.getImmediateAliasedSymbol` instead. - aliasTarget *ast.Symbol // Resolved (non-alias) target of an alias - referenced bool // True if alias symbol has been referenced as a value that can be emitted - typeOnlyDeclarationResolved bool // True when typeOnlyDeclaration resolution in process - typeOnlyDeclaration *ast.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 ast.SymbolTable // Resolved exports of module or combined early- and late-bound static members of a class. - typeOnlyExportStarMap map[string]*ast.Node // Set on a module symbol when some of its exports were resolved through a 'export type * from "mod"' declaration - exportsChecked bool -} - -type ReverseMappedSymbolLinks struct { - propertyType *Type - mappedType *Type // References a mapped type - constraintType *Type // References an index type -} - -// Links for late-bound symbols - -type LateBoundLinks struct { - lateSymbol *ast.Symbol -} - -// Links for export type symbols - -type ExportTypeLinks struct { - target *ast.Symbol // Target symbol - originatingImport *ast.Node // Import declaration which produced the symbol, present if the symbol is marked as uncallable but had call signatures in `resolveESModuleSymbol` -} - -// Links for type aliases - -type TypeAliasLinks struct { - declaredType *Type - typeParameters []*Type // Type parameters of type alias (undefined if non-generic) - instantiations map[string]*Type // Instantiations of generic type alias (undefined if non-generic) - isConstructorDeclaredProperty bool -} - -// Links for declared types (type parameters, class types, interface types, enums) - -type DeclaredTypeLinks struct { - declaredType *Type - typeParametersChecked bool -} - -// Links for switch clauses - -type ExhaustiveState byte - -const ( - ExhaustiveStateUnknown ExhaustiveState = iota // Exhaustive state not computed - ExhaustiveStateComputing // Exhaustive state computation in progress - ExhaustiveStateFalse // Switch statement is not exhaustive - ExhaustiveStateTrue // Switch statement is exhaustive -) - -type SwitchStatementLinks struct { - exhaustiveState ExhaustiveState // Switch statement exhaustiveness - switchTypesComputed bool - witnessesComputed bool - switchTypes []*Type - witnesses []string -} - -type ArrayLiteralLinks struct { - indicesComputed bool - firstSpreadIndex int // Index of first spread expression (or -1 if none) - lastSpreadIndex int // Index of last spread expression (or -1 if none) -} - -// Links for late-binding containers - -type MembersOrExportsResolutionKind int - -const ( - MembersOrExportsResolutionKindResolvedExports MembersOrExportsResolutionKind = 0 - MembersOrExportsResolutionKindResolvedMembers MembersOrExportsResolutionKind = 1 -) - -type MembersAndExportsLinks [2]ast.SymbolTable // Indexed by MembersOrExportsResolutionKind - -// Links for synthetic spread properties - -type SpreadLinks struct { - leftSpread *ast.Symbol // Left source for synthetic spread property - rightSpread *ast.Symbol // Right source for synthetic spread property -} - -// Links for variances of type aliases and interface types - -type VarianceLinks struct { - variances []VarianceFlags -} - -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 IndexSymbolLinks struct { - filteredIndexSymbolCache map[string]*ast.Symbol // Symbol with applicable declarations -} - -type MarkedAssignmentSymbolLinks struct { - lastAssignmentPos int32 - hasDefiniteAssignment bool // Symbol is definitely assigned somewhere -} - -type accessibleChainCacheKey struct { - useOnlyExternalAliasing bool - location *ast.Node - meaning ast.SymbolFlags -} - -type ContainingSymbolLinks struct { - extendedContainersByFile map[ast.NodeId][]*ast.Symbol // Symbols of nodes which which logically contain this one, cached by file the request is made within - extendedContainers *[]*ast.Symbol // Containers (other than the parent) which this symbol is aliased in - accessibleChainCache map[accessibleChainCacheKey][]*ast.Symbol -} - -type AccessFlags uint32 - -const ( - AccessFlagsNone AccessFlags = 0 - AccessFlagsIncludeUndefined AccessFlags = 1 << 0 - AccessFlagsNoIndexSignatures AccessFlags = 1 << 1 - AccessFlagsWriting AccessFlags = 1 << 2 - AccessFlagsCacheSymbol AccessFlags = 1 << 3 - AccessFlagsAllowMissing AccessFlags = 1 << 4 - AccessFlagsExpressionPosition AccessFlags = 1 << 5 - AccessFlagsReportDeprecated AccessFlags = 1 << 6 - AccessFlagsSuppressNoImplicitAnyError AccessFlags = 1 << 7 - AccessFlagsContextual AccessFlags = 1 << 8 - AccessFlagsPersistent = AccessFlagsIncludeUndefined -) - -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. - 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 - NodeCheckFlagsInitializerIsUndefined NodeCheckFlags = 1 << 24 - NodeCheckFlagsInitializerIsUndefinedComputed NodeCheckFlags = 1 << 25 -) - -// Common links - -type NodeLinks struct { - flags NodeCheckFlags // Set of flags specific to Node - declarationRequiresScopeChange core.Tristate // Set by `useOuterVariableScopeInParameter` in checker when downlevel emit would change the name resolution scope inside of a parameter. - hasReportedStatementInAmbientContext bool // Cache boolean if we report statements in ambient context -} - -type SymbolNodeLinks struct { - resolvedSymbol *ast.Symbol // Resolved symbol associated with node -} - -type TypeNodeLinks struct { - resolvedType *Type // Resolved type associated with node - outerTypeParameters []*Type // Outer type parameters of anonymous object type -} - -// Links for enum members - -type EnumMemberLinks struct { - value evaluator.Result // Constant value of enum member -} - -// Links for assertion expressions - -type AssertionLinks struct { - exprType *Type // Assertion expression type -} - -// SourceFile links - -type SourceFileLinks struct { - typeChecked bool - deferredNodes collections.OrderedSet[*ast.Node] - identifierCheckNodes []*ast.Node - localJsxNamespace string - localJsxFragmentNamespace string - localJsxFactory *ast.EntityName - localJsxFragmentFactory *ast.EntityName - jsxFragmentType *Type -} - -// Signature specific links - -type SignatureLinks struct { - resolvedSignature *Signature // Cached signature of signature node or call expression - effectsSignature *Signature // Signature with possible control flow effects - decoratorSignature *Signature // Signature for decorator as if invoked by the runtime -} - -type TypeFlags uint32 - -// Note that for types of different kinds, the numeric values of TypeFlags determine the order -// computed by the CompareTypes function and therefore the order of constituent types in union types. -// Since union type processing often bails out early when a result is known, it is important to order -// TypeFlags in increasing order of potential type complexity. In particular, indexed access and -// conditional types should sort last as those types are potentially recursive and possibly infinite. - -const ( - TypeFlagsNone TypeFlags = 0 - TypeFlagsAny TypeFlags = 1 << 0 - TypeFlagsUnknown TypeFlags = 1 << 1 - TypeFlagsUndefined TypeFlags = 1 << 2 - TypeFlagsNull TypeFlags = 1 << 3 - TypeFlagsVoid TypeFlags = 1 << 4 - TypeFlagsString TypeFlags = 1 << 5 - TypeFlagsNumber TypeFlags = 1 << 6 - TypeFlagsBigInt TypeFlags = 1 << 7 - TypeFlagsBoolean TypeFlags = 1 << 8 - TypeFlagsESSymbol TypeFlags = 1 << 9 // Type of symbol primitive introduced in ES6 - TypeFlagsStringLiteral TypeFlags = 1 << 10 - TypeFlagsNumberLiteral TypeFlags = 1 << 11 - TypeFlagsBigIntLiteral TypeFlags = 1 << 12 - TypeFlagsBooleanLiteral TypeFlags = 1 << 13 - TypeFlagsUniqueESSymbol TypeFlags = 1 << 14 // unique symbol - TypeFlagsEnumLiteral TypeFlags = 1 << 15 // Always combined with StringLiteral, NumberLiteral, or Union - TypeFlagsEnum TypeFlags = 1 << 16 // Numeric computed enum member value (must be right after EnumLiteral, see getSortOrderFlags) - TypeFlagsNonPrimitive TypeFlags = 1 << 17 // intrinsic object type - TypeFlagsNever TypeFlags = 1 << 18 // Never type - TypeFlagsTypeParameter TypeFlags = 1 << 19 // Type parameter - TypeFlagsObject TypeFlags = 1 << 20 // Object type - TypeFlagsIndex TypeFlags = 1 << 21 // keyof T - TypeFlagsTemplateLiteral TypeFlags = 1 << 22 // Template literal type - TypeFlagsStringMapping TypeFlags = 1 << 23 // Uppercase/Lowercase type - TypeFlagsSubstitution TypeFlags = 1 << 24 // Type parameter substitution - TypeFlagsIndexedAccess TypeFlags = 1 << 25 // T[K] - TypeFlagsConditional TypeFlags = 1 << 26 // T extends U ? X : Y - TypeFlagsUnion TypeFlags = 1 << 27 // Union (T | U) - TypeFlagsIntersection TypeFlags = 1 << 28 // Intersection (T & U) - 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. -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 | ObjectFlagsInstantiationExpressionType | ObjectFlagsSingleSignatureType - // 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 *ast.Symbol - typeArguments []*Type -} - -func (a *TypeAlias) Symbol() *ast.Symbol { - if a == nil { - return nil - } - return a.symbol -} - -func (a *TypeAlias) TypeArguments() []*Type { - if a == nil { - return nil - } - return a.typeArguments -} - -// Type - -type Type struct { - flags TypeFlags - objectFlags ObjectFlags - id TypeId - symbol *ast.Symbol - alias *TypeAlias - checker *Checker - data TypeData // Type specific data -} - -func (t *Type) Id() TypeId { - return t.id -} - -func (t *Type) Flags() TypeFlags { - return t.flags -} - -func (t *Type) ObjectFlags() ObjectFlags { - return t.objectFlags -} - -// Casts for concrete struct types - -func (t *Type) AsIntrinsicType() *IntrinsicType { return t.data.(*IntrinsicType) } -func (t *Type) AsLiteralType() *LiteralType { return t.data.(*LiteralType) } -func (t *Type) AsUniqueESSymbolType() *UniqueESSymbolType { return t.data.(*UniqueESSymbolType) } -func (t *Type) AsTupleType() *TupleType { return t.data.(*TupleType) } -func (t *Type) AsInstantiationExpressionType() *InstantiationExpressionType { - return t.data.(*InstantiationExpressionType) -} -func (t *Type) AsMappedType() *MappedType { return t.data.(*MappedType) } -func (t *Type) AsReverseMappedType() *ReverseMappedType { return t.data.(*ReverseMappedType) } -func (t *Type) AsEvolvingArrayType() *EvolvingArrayType { return t.data.(*EvolvingArrayType) } -func (t *Type) AsTypeParameter() *TypeParameter { return t.data.(*TypeParameter) } -func (t *Type) AsUnionType() *UnionType { return t.data.(*UnionType) } -func (t *Type) AsIntersectionType() *IntersectionType { return t.data.(*IntersectionType) } -func (t *Type) AsIndexType() *IndexType { return t.data.(*IndexType) } -func (t *Type) AsIndexedAccessType() *IndexedAccessType { return t.data.(*IndexedAccessType) } -func (t *Type) AsTemplateLiteralType() *TemplateLiteralType { return t.data.(*TemplateLiteralType) } -func (t *Type) AsStringMappingType() *StringMappingType { return t.data.(*StringMappingType) } -func (t *Type) AsSubstitutionType() *SubstitutionType { return t.data.(*SubstitutionType) } -func (t *Type) AsConditionalType() *ConditionalType { return t.data.(*ConditionalType) } - -// Casts for embedded struct types - -func (t *Type) AsConstrainedType() *ConstrainedType { return t.data.AsConstrainedType() } -func (t *Type) AsStructuredType() *StructuredType { return t.data.AsStructuredType() } -func (t *Type) AsObjectType() *ObjectType { return t.data.AsObjectType() } -func (t *Type) AsTypeReference() *TypeReference { return t.data.AsTypeReference() } -func (t *Type) AsInterfaceType() *InterfaceType { return t.data.AsInterfaceType() } -func (t *Type) AsUnionOrIntersectionType() *UnionOrIntersectionType { - return t.data.AsUnionOrIntersectionType() -} - -func (t *Type) Distributed() []*Type { - switch { - case t.flags&TypeFlagsUnion != 0: - return t.AsUnionType().types - case t.flags&TypeFlagsNever != 0: - return nil - } - return []*Type{t} -} - -// Common accessors - -func (t *Type) Target() *Type { - switch { - case t.flags&TypeFlagsObject != 0: - return t.AsObjectType().target - case t.flags&TypeFlagsTypeParameter != 0: - return t.AsTypeParameter().target - case t.flags&TypeFlagsIndex != 0: - return t.AsIndexType().target - case t.flags&TypeFlagsStringMapping != 0: - return t.AsStringMappingType().target - case t.flags&TypeFlagsObject != 0 && t.objectFlags&ObjectFlagsMapped != 0: - return t.AsMappedType().target - } - panic("Unhandled case in Type.Target") -} - -func (t *Type) Mapper() *TypeMapper { - switch { - case t.flags&TypeFlagsObject != 0: - return t.AsObjectType().mapper - case t.flags&TypeFlagsTypeParameter != 0: - return t.AsTypeParameter().mapper - case t.flags&TypeFlagsConditional != 0: - return t.AsConditionalType().mapper - } - panic("Unhandled case in Type.Mapper") -} - -func (t *Type) Types() []*Type { - switch { - case t.flags&TypeFlagsUnionOrIntersection != 0: - return t.AsUnionOrIntersectionType().types - case t.flags&TypeFlagsTemplateLiteral != 0: - return t.AsTemplateLiteralType().types - } - panic("Unhandled case in Type.Types") -} - -func (t *Type) TargetInterfaceType() *InterfaceType { - return t.AsTypeReference().target.AsInterfaceType() -} - -func (t *Type) TargetTupleType() *TupleType { - return t.AsTypeReference().target.AsTupleType() -} - -func (t *Type) Symbol() *ast.Symbol { - return t.symbol -} - -func (t *Type) IsUnion() bool { - return t.flags&TypeFlagsUnion != 0 -} - -func (t *Type) IsString() bool { - return t.flags&TypeFlagsString != 0 -} - -func (t *Type) IsIntersection() bool { - return t.flags&TypeFlagsIntersection != 0 -} - -func (t *Type) IsStringLiteral() bool { - return t.flags&TypeFlagsStringLiteral != 0 -} - -func (t *Type) IsNumberLiteral() bool { - return t.flags&TypeFlagsNumberLiteral != 0 -} - -func (t *Type) IsBigIntLiteral() bool { - return t.flags&TypeFlagsBigIntLiteral != 0 -} - -func (t *Type) IsEnumLiteral() bool { - return t.flags&TypeFlagsEnumLiteral != 0 -} - -func (t *Type) IsBooleanLike() bool { - return t.flags&TypeFlagsBooleanLike != 0 -} - -func (t *Type) IsStringLike() bool { - return t.flags&TypeFlagsStringLike != 0 -} - -func (t *Type) IsClass() bool { - return t.objectFlags&ObjectFlagsClass != 0 -} - -func (t *Type) IsTypeParameter() bool { - return t.flags&TypeFlagsTypeParameter != 0 -} - -func (t *Type) IsIndex() bool { - return t.flags&TypeFlagsIndex != 0 -} - -// TypeData - -type TypeData interface { - AsType() *Type - AsConstrainedType() *ConstrainedType - AsStructuredType() *StructuredType - AsObjectType() *ObjectType - AsTypeReference() *TypeReference - AsInterfaceType() *InterfaceType - AsUnionOrIntersectionType() *UnionOrIntersectionType -} - -// TypeBase - -type TypeBase struct { - Type -} - -func (t *TypeBase) AsType() *Type { return &t.Type } -func (t *TypeBase) AsConstrainedType() *ConstrainedType { return nil } -func (t *TypeBase) AsStructuredType() *StructuredType { return nil } -func (t *TypeBase) AsObjectType() *ObjectType { return nil } -func (t *TypeBase) AsTypeReference() *TypeReference { return nil } -func (t *TypeBase) AsInterfaceType() *InterfaceType { return nil } -func (t *TypeBase) AsUnionOrIntersectionType() *UnionOrIntersectionType { return nil } - -// IntrinsicTypeData - -type IntrinsicType struct { - TypeBase - intrinsicName string -} - -func (t *IntrinsicType) IntrinsicName() string { return t.intrinsicName } - -// LiteralTypeData - -type LiteralType struct { - TypeBase - value any // string | jsnum.Number | bool | PseudoBigInt | nil (computed enum) - freshType *Type // Fresh version of type - regularType *Type // Regular version of type -} - -func (t *LiteralType) Value() any { - return t.value -} - -func (t *LiteralType) String() string { - return ValueToString(t.value) -} - -// UniqueESSymbolTypeData - -type UniqueESSymbolType struct { - TypeBase - name string -} - -// ConstrainedType (type with computed base constraint) - -type ConstrainedType struct { - TypeBase - resolvedBaseConstraint *Type -} - -func (t *ConstrainedType) AsConstrainedType() *ConstrainedType { return t } - -// StructuredType (base of all types with members) - -type StructuredType struct { - ConstrainedType - members ast.SymbolTable - properties []*ast.Symbol - signatures []*Signature // Signatures (call + construct) - callSignatureCount int // Count of call signatures - indexInfos []*IndexInfo - - objectTypeWithoutAbstractConstructSignatures *Type -} - -func (t *StructuredType) AsStructuredType() *StructuredType { return t } - -func (t *StructuredType) CallSignatures() []*Signature { - return slices.Clip(t.signatures[:t.callSignatureCount]) -} - -func (t *StructuredType) ConstructSignatures() []*Signature { - return slices.Clip(t.signatures[t.callSignatureCount:]) -} - -func (t *StructuredType) Properties() []*ast.Symbol { - return t.properties -} - -// Except for tuple type references and reverse mapped types, all object types have an associated symbol. -// Possible object type instances are listed in the following. - -// InterfaceType: -// ObjectFlagsClass: Originating non-generic class type -// ObjectFlagsClass|ObjectFlagsReference: Originating generic class type -// ObjectFlagsInterface: Originating non-generic interface type -// ObjectFlagsInterface|ObjectFlagsReference: Originating generic interface type - -// TupleType: -// ObjectFlagsReference|ObjectFlagsTuple: Originating generic tuple type (synthesized) - -// TypeReference -// ObjectFlagsReference: Instantiated generic class, interface, or tuple type - -// ObjectType: -// ObjectFlagsAnonymous: Originating anonymous object type -// ObjectFlagsAnonymous|ObjectFlagsInstantiated: Instantiated anonymous object type - -// MappedType: -// ObjectFlagsMapped: Originating mapped type -// ObjectFlagsMapped|ObjectFlagsInstantiated: Instantiated mapped type - -// InstantiationExpressionType: -// ObjectFlagsAnonymous|ObjectFlagsInstantiationExpression: Originating instantiation expression type -// ObjectFlagsAnonymous|ObjectFlagsInstantiated|ObjectFlagsInstantiationExpression: Instantiated instantiation expression type - -// ReverseMappedType: -// ObjectFlagsAnonymous|ObjectFlagsReverseMapped: Reverse mapped type - -// EvolvingArrayType: -// ObjectFlagsEvolvingArray: Evolving array type - -type ObjectType struct { - StructuredType - target *Type // Target of instantiated type - mapper *TypeMapper // Type mapper for instantiated type - instantiations map[string]*Type // Map of type instantiations -} - -func (t *ObjectType) AsObjectType() *ObjectType { return t } - -// TypeReference (instantiation of an InterfaceType) - -type TypeReference struct { - ObjectType - node *ast.Node // TypeReferenceNode | ArrayTypeNode | TupleTypeNode when deferred, else nil - resolvedTypeArguments []*Type -} - -func (t *TypeReference) AsTypeReference() *TypeReference { return t } - -// InterfaceType (when generic, serves as reference to instantiation of itself) - -type InterfaceType struct { - TypeReference - allTypeParameters []*Type // Type parameters (outer + local + thisType) - outerTypeParameterCount int // Count of outer type parameters - thisType *Type // The "this" type (nil if none) - baseTypesResolved bool - declaredMembersResolved bool - resolvedBaseConstructorType *Type - resolvedBaseTypes []*Type - declaredMembers ast.SymbolTable // Declared members - declaredCallSignatures []*Signature // Declared call signatures - declaredConstructSignatures []*Signature // Declared construct signatures - declaredIndexInfos []*IndexInfo // Declared index signatures -} - -func (t *InterfaceType) AsInterfaceType() *InterfaceType { return t } - -func (t *InterfaceType) OuterTypeParameters() []*Type { - if len(t.allTypeParameters) == 0 { - return nil - } - return slices.Clip(t.allTypeParameters[:t.outerTypeParameterCount]) -} - -func (t *InterfaceType) LocalTypeParameters() []*Type { - if len(t.allTypeParameters) == 0 { - return nil - } - return slices.Clip(t.allTypeParameters[t.outerTypeParameterCount : len(t.allTypeParameters)-1]) -} - -func (t *InterfaceType) TypeParameters() []*Type { - if len(t.allTypeParameters) == 0 { - return nil - } - return slices.Clip(t.allTypeParameters[:len(t.allTypeParameters)-1]) -} - -// TupleType - -type ElementFlags uint32 - -const ( - ElementFlagsNone ElementFlags = 0 - ElementFlagsRequired ElementFlags = 1 << 0 // T - ElementFlagsOptional ElementFlags = 1 << 1 // T? - ElementFlagsRest ElementFlags = 1 << 2 // ...T[] - ElementFlagsVariadic ElementFlags = 1 << 3 // ...T - ElementFlagsFixed = ElementFlagsRequired | ElementFlagsOptional - ElementFlagsVariable = ElementFlagsRest | ElementFlagsVariadic - ElementFlagsNonRequired = ElementFlagsOptional | ElementFlagsRest | ElementFlagsVariadic - ElementFlagsNonRest = ElementFlagsRequired | ElementFlagsOptional | ElementFlagsVariadic -) - -type TupleElementInfo struct { - flags ElementFlags - labeledDeclaration *ast.Node // NamedTupleMember | ParameterDeclaration | nil -} - -func (t *TupleElementInfo) TupleElementFlags() ElementFlags { return t.flags } - -type TupleType struct { - InterfaceType - elementInfos []TupleElementInfo - minLength int // Number of required or variadic elements - fixedLength int // Number of initial required or optional elements - combinedFlags ElementFlags - readonly bool -} - -func (t *TupleType) FixedLength() int { return t.fixedLength } -func (t *TupleType) ElementFlags() []ElementFlags { - elementFlags := make([]ElementFlags, len(t.elementInfos)) - for i, info := range t.elementInfos { - elementFlags[i] = info.flags - } - return elementFlags -} - -// InstantiationExpressionType - -type InstantiationExpressionType struct { - ObjectType - node *ast.Node -} - -// MappedType - -type MappedType struct { - ObjectType - declaration *ast.MappedTypeNode - typeParameter *Type - constraintType *Type - nameType *Type - templateType *Type - modifiersType *Type - resolvedApparentType *Type - containsError bool -} - -// ReverseMappedType - -type ReverseMappedType struct { - ObjectType - source *Type - mappedType *Type - constraintType *Type -} - -// EvolvingArrayType - -type EvolvingArrayType struct { - ObjectType - elementType *Type - finalArrayType *Type -} - -// UnionOrIntersectionTypeData - -type UnionOrIntersectionType struct { - StructuredType - types []*Type - propertyCache ast.SymbolTable - propertyCacheWithoutFunctionPropertyAugment ast.SymbolTable - resolvedProperties []*ast.Symbol -} - -func (t *UnionOrIntersectionType) AsUnionOrIntersectionType() *UnionOrIntersectionType { return t } - -// UnionType - -type UnionType struct { - UnionOrIntersectionType - 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[*Type]*Type // Constituents keyed by unit type discriminants -} - -// IntersectionType - -type IntersectionType struct { - UnionOrIntersectionType - resolvedApparentType *Type - uniqueLiteralFilledInstantiation *Type // Instantiation with type parameters mapped to never type -} - -// TypeParameter - -type TypeParameter struct { - ConstrainedType - constraint *Type - target *Type - mapper *TypeMapper - isThisType bool - resolvedDefaultType *Type -} - -// IndexFlags - -type IndexFlags uint32 - -const ( - IndexFlagsNone IndexFlags = 0 - IndexFlagsStringsOnly IndexFlags = 1 << 0 - IndexFlagsNoIndexSignatures IndexFlags = 1 << 1 - IndexFlagsNoReducibleCheck IndexFlags = 1 << 2 -) - -// IndexType - -type IndexType struct { - ConstrainedType - target *Type - indexFlags IndexFlags -} - -// IndexedAccessType - -type IndexedAccessType struct { - ConstrainedType - objectType *Type - indexType *Type - accessFlags AccessFlags // Only includes AccessFlags.Persistent -} - -type TemplateLiteralType struct { - ConstrainedType - texts []string // Always one element longer than types - types []*Type // Always at least one element -} - -type StringMappingType struct { - ConstrainedType - target *Type -} - -type SubstitutionType struct { - ConstrainedType - baseType *Type // Target type - constraint *Type // Constraint that target type is known to satisfy -} - -type ConditionalRoot struct { - node *ast.ConditionalTypeNode - checkType *Type - extendsType *Type - isDistributive bool - inferTypeParameters []*Type - outerTypeParameters []*Type - instantiations map[string]*Type - alias *TypeAlias -} - -type ConditionalType struct { - ConstrainedType - root *ConditionalRoot - checkType *Type - extendsType *Type - resolvedTrueType *Type - resolvedFalseType *Type - resolvedInferredTrueType *Type // The `trueType` instantiated with the `combinedMapper`, if present - resolvedDefaultConstraint *Type - resolvedConstraintOfDistributive *Type - mapper *TypeMapper - combinedMapper *TypeMapper -} - -// SignatureFlags - -type SignatureFlags uint32 - -const ( - SignatureFlagsNone SignatureFlags = 0 - // Propagating flags - SignatureFlagsHasRestParameter SignatureFlags = 1 << 0 // Indicates last parameter is rest parameter - SignatureFlagsHasLiteralTypes SignatureFlags = 1 << 1 // Indicates signature is specialized - SignatureFlagsConstruct SignatureFlags = 1 << 2 // Indicates signature is a construct signature - SignatureFlagsAbstract SignatureFlags = 1 << 3 // Indicates signature comes from an abstract class, abstract construct signature, or abstract constructor type - // Non-propagating flags - SignatureFlagsIsInnerCallChain SignatureFlags = 1 << 4 // Indicates signature comes from a CallChain nested in an outer OptionalChain - SignatureFlagsIsOuterCallChain SignatureFlags = 1 << 5 // Indicates signature comes from a CallChain that is the outermost chain of an optional expression - SignatureFlagsIsUntypedSignatureInJSFile SignatureFlags = 1 << 6 // Indicates signature is from a js file and has no types - SignatureFlagsIsNonInferrable SignatureFlags = 1 << 7 // Indicates signature comes from a non-inferrable type - SignatureFlagsIsSignatureCandidateForOverloadFailure SignatureFlags = 1 << 8 - // We do not propagate `IsInnerCallChain` or `IsOuterCallChain` to instantiated signatures, as that would result in us - // attempting to add `| undefined` on each recursive call to `getReturnTypeOfSignature` when - // instantiating the return type. - SignatureFlagsPropagatingFlags = SignatureFlagsHasRestParameter | SignatureFlagsHasLiteralTypes | SignatureFlagsConstruct | SignatureFlagsAbstract | SignatureFlagsIsUntypedSignatureInJSFile | SignatureFlagsIsSignatureCandidateForOverloadFailure - SignatureFlagsCallChainFlags = SignatureFlagsIsInnerCallChain | SignatureFlagsIsOuterCallChain -) - -// Signature - -type Signature struct { - flags SignatureFlags - minArgumentCount int32 - resolvedMinArgumentCount int32 - declaration *ast.Node - typeParameters []*Type - parameters []*ast.Symbol - thisParameter *ast.Symbol - resolvedReturnType *Type - resolvedTypePredicate *TypePredicate - target *Signature - mapper *TypeMapper - isolatedSignatureType *Type - composite *CompositeSignature -} - -func (s *Signature) TypeParameters() []*Type { - return s.typeParameters -} - -func (s *Signature) Declaration() *ast.Node { - return s.declaration -} - -func (s *Signature) Target() *Signature { - return s.target -} - -func (s *Signature) ThisParameter() *ast.Symbol { - return s.thisParameter -} - -func (s *Signature) Parameters() []*ast.Symbol { - return s.parameters -} - -func (s *Signature) HasRestParameter() bool { - return s.flags&SignatureFlagsHasRestParameter != 0 -} - -type CompositeSignature struct { - isUnion bool // True for union, false for intersection - signatures []*Signature // Individual signatures -} - -type TypePredicateKind int32 - -const ( - TypePredicateKindThis TypePredicateKind = iota - TypePredicateKindIdentifier - TypePredicateKindAssertsThis - TypePredicateKindAssertsIdentifier -) - -type TypePredicate struct { - kind TypePredicateKind - parameterIndex int32 - parameterName string - t *Type -} - -// IndexInfo - -type IndexInfo struct { - keyType *Type - valueType *Type - isReadonly bool - declaration *ast.Node // IndexSignatureDeclaration - components []*ast.Node // ElementWithComputedPropertyName -} - -/** - * Ternary values are defined such that - * x & y picks the lesser in the order False < Unknown < Maybe < True, and - * x | y picks the greater in the order False < Unknown < Maybe < True. - * Generally, Ternary.Maybe is used as the result of a relation that depends on itself, and - * Ternary.Unknown is used as the result of a variance check that depends on itself. We make - * a distinction because we don't want to cache circular variance check results. - */ -type Ternary int8 - -const ( - TernaryFalse Ternary = 0 - TernaryUnknown Ternary = 1 - TernaryMaybe Ternary = 3 - TernaryTrue Ternary = -1 -) - -type TypeComparer func(s *Type, t *Type, reportErrors bool) Ternary - -type LanguageFeatureMinimumTargetMap struct { - Classes core.ScriptTarget - ForOf core.ScriptTarget - Generators core.ScriptTarget - Iteration core.ScriptTarget - SpreadElements core.ScriptTarget - RestElements core.ScriptTarget - TaggedTemplates core.ScriptTarget - DestructuringAssignment core.ScriptTarget - BindingPatterns core.ScriptTarget - ArrowFunctions core.ScriptTarget - BlockScopedVariables core.ScriptTarget - ObjectAssign core.ScriptTarget - RegularExpressionFlagsUnicode core.ScriptTarget - RegularExpressionFlagsSticky core.ScriptTarget - Exponentiation core.ScriptTarget - AsyncFunctions core.ScriptTarget - ForAwaitOf core.ScriptTarget - AsyncGenerators core.ScriptTarget - AsyncIteration core.ScriptTarget - ObjectSpreadRest core.ScriptTarget - RegularExpressionFlagsDotAll core.ScriptTarget - BindinglessCatch core.ScriptTarget - BigInt core.ScriptTarget - NullishCoalesce core.ScriptTarget - OptionalChaining core.ScriptTarget - LogicalAssignment core.ScriptTarget - TopLevelAwait core.ScriptTarget - ClassFields core.ScriptTarget - PrivateNamesAndClassStaticBlocks core.ScriptTarget - RegularExpressionFlagsHasIndices core.ScriptTarget - ShebangComments core.ScriptTarget - UsingAndAwaitUsing core.ScriptTarget - ClassAndClassElementDecorators core.ScriptTarget - RegularExpressionFlagsUnicodeSets core.ScriptTarget -} - -var LanguageFeatureMinimumTarget = LanguageFeatureMinimumTargetMap{ - Classes: core.ScriptTargetES2015, - ForOf: core.ScriptTargetES2015, - Generators: core.ScriptTargetES2015, - Iteration: core.ScriptTargetES2015, - SpreadElements: core.ScriptTargetES2015, - RestElements: core.ScriptTargetES2015, - TaggedTemplates: core.ScriptTargetES2015, - DestructuringAssignment: core.ScriptTargetES2015, - BindingPatterns: core.ScriptTargetES2015, - ArrowFunctions: core.ScriptTargetES2015, - BlockScopedVariables: core.ScriptTargetES2015, - ObjectAssign: core.ScriptTargetES2015, - RegularExpressionFlagsUnicode: core.ScriptTargetES2015, - RegularExpressionFlagsSticky: core.ScriptTargetES2015, - Exponentiation: core.ScriptTargetES2016, - AsyncFunctions: core.ScriptTargetES2017, - ForAwaitOf: core.ScriptTargetES2018, - AsyncGenerators: core.ScriptTargetES2018, - AsyncIteration: core.ScriptTargetES2018, - ObjectSpreadRest: core.ScriptTargetES2018, - RegularExpressionFlagsDotAll: core.ScriptTargetES2018, - BindinglessCatch: core.ScriptTargetES2019, - BigInt: core.ScriptTargetES2020, - NullishCoalesce: core.ScriptTargetES2020, - OptionalChaining: core.ScriptTargetES2020, - LogicalAssignment: core.ScriptTargetES2021, - TopLevelAwait: core.ScriptTargetES2022, - ClassFields: core.ScriptTargetES2022, - PrivateNamesAndClassStaticBlocks: core.ScriptTargetES2022, - RegularExpressionFlagsHasIndices: core.ScriptTargetES2022, - ShebangComments: core.ScriptTargetESNext, - UsingAndAwaitUsing: core.ScriptTargetESNext, - ClassAndClassElementDecorators: core.ScriptTargetESNext, - RegularExpressionFlagsUnicodeSets: core.ScriptTargetESNext, -} - -// Aliases for types -type StringLiteralType = Type diff --git a/kitcom/internal/tsgo/checker/utilities.go b/kitcom/internal/tsgo/checker/utilities.go deleted file mode 100644 index 4b09b95..0000000 --- a/kitcom/internal/tsgo/checker/utilities.go +++ /dev/null @@ -1,1860 +0,0 @@ -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 -}