From d7cbf19df179e1964b9ee8521fbba101a32eba88 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 13 Dec 2022 15:40:28 -0500 Subject: [PATCH] Monomorphic Symbol access --- src/compiler/checker.ts | 229 +++++++++--------- src/compiler/types.ts | 36 ++- src/compiler/utilities.ts | 16 +- .../codefixes/fixInvalidImportSyntax.ts | 6 +- src/services/codefixes/inferFromUsage.ts | 12 +- src/services/codefixes/returnValueCorrect.ts | 2 +- src/services/refactors/extractSymbol.ts | 2 +- src/services/services.ts | 11 +- src/services/signatureHelp.ts | 6 +- src/services/symbolDisplay.ts | 7 +- src/services/utilities.ts | 12 +- 11 files changed, 187 insertions(+), 152 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 963981dde4743..229b9733a6e1f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -346,8 +346,8 @@ import { hasAccessorModifier, hasAmbientModifier, hasContextSensitiveParameters, - HasDecorators, hasDecorators, + HasDecorators, hasDynamicName, hasEffectiveModifier, hasEffectiveModifiers, @@ -356,8 +356,8 @@ import { hasExtension, HasIllegalDecorators, HasIllegalModifiers, - HasInitializer, hasInitializer, + HasInitializer, hasJSDocNodes, hasJSDocParameterTags, hasJsonModuleEmitEnabled, @@ -955,6 +955,7 @@ import { tracing, TracingNode, TransientSymbol, + TransientSymbolLinks, tryAddToSet, tryCast, tryExtractTSExtension, @@ -1302,8 +1303,9 @@ const intrinsicTypeKinds: ReadonlyMap = new Map(getEn Uncapitalize: IntrinsicTypeKind.Uncapitalize })); -function SymbolLinks(this: SymbolLinks) { -} +const SymbolLinks = class implements SymbolLinks { + declare _symbolLinksBrand: any; +}; function NodeLinks(this: NodeLinks) { this.flags = NodeCheckFlags.None; @@ -2314,8 +2316,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function createSymbol(flags: SymbolFlags, name: __String, checkFlags?: CheckFlags) { symbolCount++; - const symbol = (new Symbol(flags | SymbolFlags.Transient, name) as TransientSymbol); - symbol.checkFlags = checkFlags || CheckFlags.None; + const symbol = new Symbol(flags | SymbolFlags.Transient, name) as TransientSymbol; + symbol.links = new SymbolLinks() as TransientSymbolLinks; + symbol.links.checkFlags = checkFlags || CheckFlags.None; return symbol; } @@ -2348,7 +2351,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { mergedSymbols[source.mergeId] = target; } - function cloneSymbol(symbol: Symbol): Symbol { + function cloneSymbol(symbol: Symbol): TransientSymbol { const result = createSymbol(symbol.flags, symbol.escapedName); result.declarations = symbol.declarations ? symbol.declarations.slice() : []; result.parent = symbol.parent; @@ -2565,9 +2568,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function getSymbolLinks(symbol: Symbol): SymbolLinks { - if (symbol.flags & SymbolFlags.Transient) return symbol as TransientSymbol; + if (symbol.flags & SymbolFlags.Transient) return (symbol as TransientSymbol).links; const id = getSymbolId(symbol); - return symbolLinks[id] || (symbolLinks[id] = new (SymbolLinks as any)()); + return symbolLinks[id] ??= new SymbolLinks(); } function getNodeLinks(node: Node): NodeLinks { @@ -5012,14 +5015,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const result = createSymbol(symbol.flags, symbol.escapedName); result.declarations = symbol.declarations ? symbol.declarations.slice() : []; result.parent = symbol.parent; - result.target = symbol; - result.originatingImport = referenceParent; + result.links.target = symbol; + result.links.originatingImport = referenceParent; if (symbol.valueDeclaration) result.valueDeclaration = symbol.valueDeclaration; if (symbol.constEnumOnlyModule) result.constEnumOnlyModule = true; if (symbol.members) result.members = new Map(symbol.members); if (symbol.exports) result.exports = new Map(symbol.exports); const resolvedModuleType = resolveStructuredTypeMembers(moduleType as StructuredType); // Should already be resolved from the signature checks above - result.type = createAnonymousType(result, resolvedModuleType.members, emptyArray, emptyArray, resolvedModuleType.indexInfos); + result.links.type = createAnonymousType(result, resolvedModuleType.members, emptyArray, emptyArray, resolvedModuleType.indexInfos); return result; } @@ -6882,7 +6885,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { contains(context.reverseMappedStack, propertySymbol as ReverseMappedSymbol) || ( context.reverseMappedStack?.[0] - && !(getObjectFlags(last(context.reverseMappedStack).propertyType) & ObjectFlags.Anonymous) + && !(getObjectFlags(last(context.reverseMappedStack).links.propertyType) & ObjectFlags.Anonymous) ) ); } @@ -7355,7 +7358,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const params = getTypeParametersOfClassOrInterface( parentSymbol.flags & SymbolFlags.Alias ? resolveAlias(parentSymbol) : parentSymbol ); - typeParameterNodes = mapToTypeNodes(map(params, t => getMappedType(t, (nextSymbol as TransientSymbol).mapper!)), context); + // NOTE: cast to TransientSymbol should be safe because only TransientSymbol can have CheckFlags.Instantiated + typeParameterNodes = mapToTypeNodes(map(params, t => getMappedType(t, (nextSymbol as TransientSymbol).links.mapper!)), context); } else { typeParameterNodes = typeParametersToTypeParameterDeclarations(symbol, context); @@ -10521,7 +10525,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { createDiagnosticForNode(s.valueDeclaration, Diagnostics._0_was_also_declared_here, unescapedName)); } const union = createSymbol(s.flags | exportedMember.flags, name); - union.type = getUnionType([getTypeOfSymbol(s), getTypeOfSymbol(exportedMember)]); + union.links.type = getUnionType([getTypeOfSymbol(s), getTypeOfSymbol(exportedMember)]); union.valueDeclaration = exportedMember.valueDeclaration; union.declarations = concatenate(exportedMember.declarations, s.declarations); members.set(name, union); @@ -10634,8 +10638,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const text = getPropertyNameFromType(exprType); const flags = SymbolFlags.Property | (e.initializer ? SymbolFlags.Optional : 0); const symbol = createSymbol(flags, text); - symbol.type = getTypeFromBindingElement(e, includePatternInType, reportErrors); - symbol.bindingElement = e; + symbol.links.type = getTypeFromBindingElement(e, includePatternInType, reportErrors); + symbol.links.bindingElement = e; members.set(symbol.escapedName, symbol); }); const result = createAnonymousType(undefined, members, emptyArray, emptyArray, stringIndexInfo ? [stringIndexInfo] : emptyArray); @@ -10787,7 +10791,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const result = createSymbol(fileSymbol.flags, "exports" as __String); result.declarations = fileSymbol.declarations ? fileSymbol.declarations.slice() : []; result.parent = symbol; - result.target = fileSymbol; + result.links.target = fileSymbol; if (fileSymbol.valueDeclaration) result.valueDeclaration = fileSymbol.valueDeclaration; if (fileSymbol.members) result.members = new Map(fileSymbol.members); if (fileSymbol.exports) result.exports = new Map(fileSymbol.exports); @@ -11018,7 +11022,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const merged = mergeJSSymbols(symbol, expando); if (merged) { // note:we overwrite links because we just cloned the symbol - symbol = links = merged; + symbol = merged; + links = merged.links; } } originalLinks.type = links.type = getTypeOfFuncClassEnumModuleWorker(symbol); @@ -11145,7 +11150,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return checkFlags & CheckFlags.SyntheticProperty ? checkFlags & CheckFlags.DeferredType ? getWriteTypeOfSymbolWithDeferredType(symbol) || getTypeOfSymbolWithDeferredType(symbol) : - (symbol as TransientSymbol).writeType || (symbol as TransientSymbol).type! : + // NOTE: cast to TransientSymbol should be safe because only TransientSymbols can have CheckFlags.SyntheticProperty + (symbol as TransientSymbol).links.writeType || (symbol as TransientSymbol).links.type! : getTypeOfSymbol(symbol); } if (symbol.flags & SymbolFlags.Accessor) { @@ -11640,7 +11646,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const merged = mergeJSSymbols(symbol, symbol.valueDeclaration && getAssignedClassSymbol(symbol.valueDeclaration)); if (merged) { // note:we overwrite links because we just cloned the symbol - symbol = links = merged; + symbol = merged; + links = merged.links; } const type = originalLinks.declaredType = links.declaredType = createObjectType(kind, symbol) as InterfaceType; @@ -12057,7 +12064,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { error(declName || decl, Diagnostics.Duplicate_property_0, name); lateSymbol = createSymbol(SymbolFlags.None, memberName, CheckFlags.Late); } - lateSymbol.nameType = type; + lateSymbol.links.nameType = type; addDeclarationToLateBoundSymbol(lateSymbol, decl, symbolFlags); if (lateSymbol.parent) { Debug.assert(lateSymbol.parent === parent, "Existing symbol parent should match new one"); @@ -12311,7 +12318,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const checkFlags = flags & ElementFlags.Variable ? CheckFlags.RestParameter : flags & ElementFlags.Optional ? CheckFlags.OptionalParameter : 0; const symbol = createSymbol(SymbolFlags.FunctionScopedVariable, name, checkFlags); - symbol.type = flags & ElementFlags.Rest ? createArrayType(t) : t; + symbol.links.type = flags & ElementFlags.Rest ? createArrayType(t) : t; return symbol; }); return concatenate(sig.parameters.slice(0, restIndex), restParams); @@ -12503,14 +12510,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { SymbolFlags.FunctionScopedVariable | (isOptional && !isRestParam ? SymbolFlags.Optional : 0), paramName || `arg${i}` as __String ); - paramSymbol.type = isRestParam ? createArrayType(unionParamType) : unionParamType; + paramSymbol.links.type = isRestParam ? createArrayType(unionParamType) : unionParamType; params[i] = paramSymbol; } if (needsExtraRestElement) { const restParamSymbol = createSymbol(SymbolFlags.FunctionScopedVariable, "args" as __String); - restParamSymbol.type = createArrayType(getTypeAtPosition(shorter, longestCount)); + restParamSymbol.links.type = createArrayType(getTypeAtPosition(shorter, longestCount)); if (shorter === right) { - restParamSymbol.type = instantiateType(restParamSymbol.type, mapper); + restParamSymbol.links.type = instantiateType(restParamSymbol.links.type, mapper); } params[longestCount] = restParamSymbol; } @@ -12767,8 +12774,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const checkFlags = CheckFlags.ReverseMapped | (readonlyMask && isReadonlySymbol(prop) ? CheckFlags.Readonly : 0); const inferredProp = createSymbol(SymbolFlags.Property | prop.flags & optionalMask, prop.escapedName, checkFlags) as ReverseMappedSymbol; inferredProp.declarations = prop.declarations; - inferredProp.nameType = getSymbolLinks(prop).nameType; - inferredProp.propertyType = getTypeOfSymbol(prop); + inferredProp.links.nameType = getSymbolLinks(prop).nameType; + inferredProp.links.propertyType = getTypeOfSymbol(prop); if (type.constraintType.type.flags & TypeFlags.IndexedAccess && (type.constraintType.type as IndexedAccessType).objectType.flags & TypeFlags.TypeParameter && (type.constraintType.type as IndexedAccessType).indexType.flags & TypeFlags.TypeParameter) { @@ -12777,12 +12784,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // type identities produced, we simplify such indexed access occurences const newTypeParam = (type.constraintType.type as IndexedAccessType).objectType; const newMappedType = replaceIndexedAccess(type.mappedType, type.constraintType.type as ReplaceableIndexedAccessType, newTypeParam); - inferredProp.mappedType = newMappedType as MappedType; - inferredProp.constraintType = getIndexType(newTypeParam) as IndexType; + inferredProp.links.mappedType = newMappedType as MappedType; + inferredProp.links.constraintType = getIndexType(newTypeParam) as IndexType; } else { - inferredProp.mappedType = type.mappedType; - inferredProp.constraintType = type.constraintType; + inferredProp.links.mappedType = type.mappedType; + inferredProp.links.constraintType = type.constraintType; } members.set(prop.escapedName, inferredProp); } @@ -12881,8 +12888,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // property symbol's name type be the union of those enum member types. const existingProp = members.get(propName) as MappedSymbol | undefined; if (existingProp) { - existingProp.nameType = getUnionType([existingProp.nameType!, propNameType]); - existingProp.keyType = getUnionType([existingProp.keyType, keyType]); + existingProp.links.nameType = getUnionType([existingProp.links.nameType!, propNameType]); + existingProp.links.keyType = getUnionType([existingProp.links.keyType, keyType]); } else { const modifiersProp = isTypeUsableAsPropertyName(keyType) ? getPropertyOfType(modifiersType, getPropertyNameFromType(keyType)) : undefined; @@ -12894,11 +12901,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const lateFlag: CheckFlags = modifiersProp ? getIsLateCheckFlag(modifiersProp) : 0; const prop = createSymbol(SymbolFlags.Property | (isOptional ? SymbolFlags.Optional : 0), propName, lateFlag | CheckFlags.Mapped | (isReadonly ? CheckFlags.Readonly : 0) | (stripOptional ? CheckFlags.StripOptional : 0)) as MappedSymbol; - prop.mappedType = type; - prop.nameType = propNameType; - prop.keyType = keyType; + prop.links.mappedType = type; + prop.links.nameType = propNameType; + prop.links.keyType = keyType; if (modifiersProp) { - prop.syntheticOrigin = modifiersProp; + prop.links.syntheticOrigin = modifiersProp; // If the mapped type has an `as XXX` clause, the property name likely won't match the declaration name and // multiple properties may map to the same name. Thus, we attach no declarations to the symbol. prop.declarations = nameType ? undefined : modifiersProp.declarations; @@ -12918,28 +12925,28 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function getTypeOfMappedSymbol(symbol: MappedSymbol) { - if (!symbol.type) { - const mappedType = symbol.mappedType; + if (!symbol.links.type) { + const mappedType = symbol.links.mappedType; if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) { mappedType.containsError = true; return errorType; } const templateType = getTemplateTypeFromMappedType(mappedType.target as MappedType || mappedType); - const mapper = appendTypeMapping(mappedType.mapper, getTypeParameterFromMappedType(mappedType), symbol.keyType); + const mapper = appendTypeMapping(mappedType.mapper, getTypeParameterFromMappedType(mappedType), symbol.links.keyType); const propType = 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. let type = strictNullChecks && symbol.flags & SymbolFlags.Optional && !maybeTypeOfKind(propType, TypeFlags.Undefined | TypeFlags.Void) ? getOptionalType(propType, /*isProperty*/ true) : - symbol.checkFlags & CheckFlags.StripOptional ? removeMissingOrUndefinedType(propType) : + symbol.links.checkFlags & CheckFlags.StripOptional ? removeMissingOrUndefinedType(propType) : propType; if (!popTypeResolution()) { error(currentNode, Diagnostics.Type_of_property_0_circularly_references_itself_in_mapped_type_1, symbolToString(symbol), typeToString(mappedType)); type = errorType; } - symbol.type = type; + symbol.links.type = type; } - return symbol.type; + return symbol.links.type; } function getTypeParameterFromMappedType(type: MappedType) { @@ -13618,10 +13625,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // 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` - const clone = createSymbolWithType(singleProp, (singleProp as TransientSymbol).type); + const links = tryCast(singleProp, isTransientSymbol)?.links; + const clone = createSymbolWithType(singleProp, links?.type); clone.parent = singleProp.valueDeclaration?.symbol?.parent; - clone.containingType = containingType; - clone.mapper = (singleProp as TransientSymbol).mapper; + clone.links.containingType = containingType; + clone.links.mapper = links?.mapper; return clone; } else { @@ -13666,7 +13674,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } addRange(propTypes, indexTypes); const result = createSymbol(SymbolFlags.Property | (optionalFlag ?? 0), name, syntheticFlag | checkFlags); - result.containingType = containingType; + result.links.containingType = containingType; if (!hasNonUniformValueDeclaration && firstValueDeclaration) { result.valueDeclaration = firstValueDeclaration; @@ -13677,18 +13685,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } result.declarations = declarations; - result.nameType = nameType; + result.links.nameType = nameType; if (propTypes.length > 2) { // When `propTypes` has the potential to explode in size when normalized, defer normalization until absolutely needed - result.checkFlags |= CheckFlags.DeferredType; - result.deferralParent = containingType; - result.deferralConstituents = propTypes; - result.deferralWriteConstituents = writeTypes; + result.links.checkFlags |= CheckFlags.DeferredType; + result.links.deferralParent = containingType; + result.links.deferralConstituents = propTypes; + result.links.deferralWriteConstituents = writeTypes; } else { - result.type = isUnion ? getUnionType(propTypes) : getIntersectionType(propTypes); + result.links.type = isUnion ? getUnionType(propTypes) : getIntersectionType(propTypes); if (writeTypes) { - result.writeType = isUnion ? getUnionType(writeTypes) : getIntersectionType(writeTypes); + result.links.writeType = isUnion ? getUnionType(writeTypes) : getIntersectionType(writeTypes); } } return result; @@ -14150,16 +14158,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const syntheticArgsSymbol = createSymbol(SymbolFlags.Variable, "args" as __String, CheckFlags.RestParameter); if (lastParamVariadicType) { // Parameter has effective annotation, lock in type - syntheticArgsSymbol.type = createArrayType(getTypeFromTypeNode(lastParamVariadicType.type)); + syntheticArgsSymbol.links.type = createArrayType(getTypeFromTypeNode(lastParamVariadicType.type)); } else { // Parameter has no annotation // By using a `DeferredType` symbol, we allow the type of this rest arg to be overriden by contextual type assignment so long as its type hasn't been // cached by `getTypeOfSymbol` yet. - syntheticArgsSymbol.checkFlags |= CheckFlags.DeferredType; - syntheticArgsSymbol.deferralParent = neverType; - syntheticArgsSymbol.deferralConstituents = [anyArrayType]; - syntheticArgsSymbol.deferralWriteConstituents = [anyArrayType]; + syntheticArgsSymbol.links.checkFlags |= CheckFlags.DeferredType; + syntheticArgsSymbol.links.deferralParent = neverType; + syntheticArgsSymbol.links.deferralConstituents = [anyArrayType]; + syntheticArgsSymbol.links.deferralWriteConstituents = [anyArrayType]; } if (lastParamVariadicType) { // Replace the last parameter with a rest parameter. @@ -14914,7 +14922,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (!result) { unresolvedSymbols.set(path, result = createSymbol(SymbolFlags.TypeAlias, text, CheckFlags.Unresolved)); result.parent = parentSymbol; - result.declaredType = unresolvedType; + result.links.declaredType = unresolvedType; } return result; } @@ -15256,7 +15264,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const metaPropertySymbol = createSymbol(SymbolFlags.Property, "meta" as __String, CheckFlags.Readonly); metaPropertySymbol.parent = symbol; - metaPropertySymbol.type = importMetaType; + metaPropertySymbol.links.type = importMetaType; const members = createSymbolTable([metaPropertySymbol]); symbol.members = members; @@ -15554,8 +15562,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (!(combinedFlags & ElementFlags.Variable)) { const property = createSymbol(SymbolFlags.Property | (flags & ElementFlags.Optional ? SymbolFlags.Optional : 0), "" + i as __String, readonly ? CheckFlags.Readonly : 0); - property.tupleLabelDeclaration = namedMemberDeclarations?.[i]; - property.type = typeParameter; + property.links.tupleLabelDeclaration = namedMemberDeclarations?.[i]; + property.links.type = typeParameter; properties.push(property); } } @@ -15563,12 +15571,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const fixedLength = properties.length; const lengthSymbol = createSymbol(SymbolFlags.Property, "length" as __String, readonly ? CheckFlags.Readonly : 0); if (combinedFlags & ElementFlags.Variable) { - lengthSymbol.type = numberType; + lengthSymbol.links.type = numberType; } else { const literalTypes = []; for (let i = minLength; i <= arity; i++) literalTypes.push(getNumberLiteralType(i)); - lengthSymbol.type = getUnionType(literalTypes); + lengthSymbol.links.type = getUnionType(literalTypes); } properties.push(lengthSymbol); const type = createObjectType(ObjectFlags.Tuple | ObjectFlags.Reference) as TupleType & InterfaceTypeWithDeclaredMembers; @@ -17633,10 +17641,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const isSetonlyAccessor = prop.flags & SymbolFlags.SetAccessor && !(prop.flags & SymbolFlags.GetAccessor); const flags = SymbolFlags.Property | SymbolFlags.Optional; const result = createSymbol(flags, prop.escapedName, getIsLateCheckFlag(prop) | (readonly ? CheckFlags.Readonly : 0)); - result.type = isSetonlyAccessor ? undefinedType : addOptionality(getTypeOfSymbol(prop), /*isProperty*/ true); + result.links.type = isSetonlyAccessor ? undefinedType : addOptionality(getTypeOfSymbol(prop), /*isProperty*/ true); result.declarations = prop.declarations; - result.nameType = getSymbolLinks(prop).nameType; - result.syntheticOrigin = prop; + result.links.nameType = getSymbolLinks(prop).nameType; + result.links.syntheticOrigin = prop; members.set(prop.escapedName, result); } } @@ -17721,11 +17729,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const declarations = concatenate(leftProp.declarations, rightProp.declarations); const flags = SymbolFlags.Property | (leftProp.flags & SymbolFlags.Optional); const result = createSymbol(flags, leftProp.escapedName); - result.type = getUnionType([getTypeOfSymbol(leftProp), removeMissingOrUndefinedType(rightType)], UnionReduction.Subtype); - result.leftSpread = leftProp; - result.rightSpread = rightProp; + result.links.type = getUnionType([getTypeOfSymbol(leftProp), removeMissingOrUndefinedType(rightType)], UnionReduction.Subtype); + result.links.leftSpread = leftProp; + result.links.rightSpread = rightProp; result.declarations = declarations; - result.nameType = getSymbolLinks(leftProp).nameType; + result.links.nameType = getSymbolLinks(leftProp).nameType; members.set(leftProp.escapedName, result); } } @@ -17753,10 +17761,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } const flags = SymbolFlags.Property | (prop.flags & SymbolFlags.Optional); const result = createSymbol(flags, prop.escapedName, getIsLateCheckFlag(prop) | (readonly ? CheckFlags.Readonly : 0)); - result.type = isSetonlyAccessor ? undefinedType : getTypeOfSymbol(prop); + result.links.type = isSetonlyAccessor ? undefinedType : getTypeOfSymbol(prop); result.declarations = prop.declarations; - result.nameType = getSymbolLinks(prop).nameType; - result.syntheticOrigin = prop; + result.links.nameType = getSymbolLinks(prop).nameType; + result.links.syntheticOrigin = prop; return result; } @@ -18203,13 +18211,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const result = createSymbol(symbol.flags, symbol.escapedName, CheckFlags.Instantiated | getCheckFlags(symbol) & (CheckFlags.Readonly | CheckFlags.Late | CheckFlags.OptionalParameter | CheckFlags.RestParameter)); result.declarations = symbol.declarations; result.parent = symbol.parent; - result.target = symbol; - result.mapper = mapper; + result.links.target = symbol; + result.links.mapper = mapper; if (symbol.valueDeclaration) { result.valueDeclaration = symbol.valueDeclaration; } if (links.nameType) { - result.nameType = links.nameType; + result.links.nameType = links.nameType; } return result; } @@ -22215,7 +22223,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // value that isn't undefined. function forEachProperty(prop: Symbol, callback: (p: Symbol) => T): T | undefined { if (getCheckFlags(prop) & CheckFlags.Synthetic) { - for (const t of (prop as TransientSymbol).containingType!.types) { + // NOTE: cast to TransientSymbol should be safe because only TransientSymbols can have CheckFlags.Synthetic + for (const t of (prop as TransientSymbol).links.containingType!.types) { const p = getPropertyOfType(t, prop.escapedName); const result = p && forEachProperty(p, callback); if (result) { @@ -22847,14 +22856,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const symbol = createSymbol(source.flags, source.escapedName, getCheckFlags(source) & CheckFlags.Readonly); symbol.declarations = source.declarations; symbol.parent = source.parent; - symbol.type = type; - symbol.target = source; + symbol.links.type = type; + symbol.links.target = source; if (source.valueDeclaration) { symbol.valueDeclaration = source.valueDeclaration; } const nameType = getSymbolLinks(source).nameType; if (nameType) { - symbol.nameType = nameType; + symbol.links.nameType = nameType; } return symbol; } @@ -23330,7 +23339,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } const name = escapeLeadingUnderscores((t as StringLiteralType).value); const literalProp = createSymbol(SymbolFlags.Property, name); - literalProp.type = anyType; + literalProp.links.type = anyType; if (t.symbol) { literalProp.declarations = t.symbol.declarations; literalProp.valueDeclaration = t.symbol.valueDeclaration; @@ -23402,7 +23411,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function getTypeOfReverseMappedSymbol(symbol: ReverseMappedSymbol) { const links = getSymbolLinks(symbol); if (!links.type) { - links.type = inferReverseMappedType(symbol.propertyType, symbol.mappedType, symbol.constraintType); + links.type = inferReverseMappedType(symbol.links.propertyType, symbol.links.mappedType, symbol.links.constraintType); } return links.type; } @@ -24720,12 +24729,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (type && type.flags & TypeFlags.Union) { const prop = getUnionOrIntersectionProperty(type as UnionType, name); if (prop && getCheckFlags(prop) & CheckFlags.SyntheticProperty) { - if ((prop as TransientSymbol).isDiscriminantProperty === undefined) { - (prop as TransientSymbol).isDiscriminantProperty = - ((prop as TransientSymbol).checkFlags & CheckFlags.Discriminant) === CheckFlags.Discriminant && + // NOTE: cast to TransientSymbol should be safe because only TransientSymbols can have CheckFlags.SyntheticProperty + if ((prop as TransientSymbol).links.isDiscriminantProperty === undefined) { + (prop as TransientSymbol).links.isDiscriminantProperty = + ((prop as TransientSymbol).links.checkFlags & CheckFlags.Discriminant) === CheckFlags.Discriminant && !isGenericType(getTypeOfSymbol(prop)); } - return !!(prop as TransientSymbol).isDiscriminantProperty; + return !!(prop as TransientSymbol).links.isDiscriminantProperty; } } return false; @@ -25434,7 +25444,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } if (symbol.flags & (SymbolFlags.Variable | SymbolFlags.Property)) { if (getCheckFlags(symbol) & CheckFlags.Mapped) { - const origin = (symbol as MappedSymbol).syntheticOrigin; + const origin = (symbol as MappedSymbol).links.syntheticOrigin; if (origin && getExplicitTypeOfSymbol(origin)) { return getTypeOfSymbol(symbol); } @@ -28348,7 +28358,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function isCircularMappedProperty(symbol: Symbol) { - return !!(getCheckFlags(symbol) & CheckFlags.Mapped && !(symbol as MappedSymbol).type && findResolutionCycleStartIndex(symbol, TypeSystemPropertyName.Type) >= 0); + return !!(getCheckFlags(symbol) & CheckFlags.Mapped && !(symbol as MappedSymbol).links.type && findResolutionCycleStartIndex(symbol, TypeSystemPropertyName.Type) >= 0); } function getTypeOfPropertyOfContextualType(type: Type, name: __String, nameType?: Type) { @@ -28890,14 +28900,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { SymbolFlags.FunctionScopedVariable | (isOptional && !isRestParam ? SymbolFlags.Optional : 0), paramName || `arg${i}` as __String ); - paramSymbol.type = isRestParam ? createArrayType(unionParamType) : unionParamType; + paramSymbol.links.type = isRestParam ? createArrayType(unionParamType) : unionParamType; params[i] = paramSymbol; } if (needsExtraRestElement) { const restParamSymbol = createSymbol(SymbolFlags.FunctionScopedVariable, "args" as __String); - restParamSymbol.type = createArrayType(getTypeAtPosition(shorter, longestCount)); + restParamSymbol.links.type = createArrayType(getTypeAtPosition(shorter, longestCount)); if (shorter === right) { - restParamSymbol.type = instantiateType(restParamSymbol.type, mapper); + restParamSymbol.links.type = instantiateType(restParamSymbol.links.type, mapper); } params[longestCount] = restParamSymbol; } @@ -29263,7 +29273,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { createSymbol(SymbolFlags.Property | member.flags, getPropertyNameFromType(nameType), checkFlags | CheckFlags.Late) : createSymbol(SymbolFlags.Property | member.flags, member.escapedName, checkFlags); if (nameType) { - prop.nameType = nameType; + prop.links.nameType = nameType; } if (inDestructuringPattern) { @@ -29296,8 +29306,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { prop.valueDeclaration = member.valueDeclaration; } - prop.type = type; - prop.target = member; + prop.links.type = type; + prop.links.target = member; member = prop; allPropertiesTable?.set(prop.escapedName, prop); @@ -29393,7 +29403,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { for (const prop of getPropertiesOfType(contextualType)) { if (!propertiesTable.get(prop.escapedName) && !getPropertyOfType(spread, prop.escapedName)) { if (!(prop.flags & SymbolFlags.Optional)) { - error(prop.valueDeclaration || (prop as TransientSymbol).bindingElement, + error(prop.valueDeclaration || tryCast(prop, isTransientSymbol)?.links.bindingElement, Diagnostics.Initializer_provides_no_value_for_this_binding_element_and_the_binding_element_has_no_default_value); } propertiesTable.set(prop.escapedName, prop); @@ -29544,8 +29554,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (member.valueDeclaration) { attributeSymbol.valueDeclaration = member.valueDeclaration; } - attributeSymbol.type = exprType; - attributeSymbol.target = member; + attributeSymbol.links.type = exprType; + attributeSymbol.links.target = member; attributesTable.set(attributeSymbol.escapedName, attributeSymbol); allAttributesTable?.set(attributeSymbol.escapedName, attributeSymbol); if (attributeDecl.name.escapedText === jsxChildrenPropertyName) { @@ -29605,7 +29615,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const childrenContextualType = contextualType && 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 const childrenPropSymbol = createSymbol(SymbolFlags.Property, jsxChildrenPropertyName); - childrenPropSymbol.type = childrenTypes.length === 1 ? childrenTypes[0] : + childrenPropSymbol.links.type = childrenTypes.length === 1 ? childrenTypes[0] : childrenContextualType && someType(childrenContextualType, isTupleLikeType) ? createTupleType(childrenTypes) : createArrayType(getUnionType(childrenTypes)); // Fake up a property declaration for the children @@ -33041,7 +33051,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { returnNode ? factory.createTypeReferenceNode(returnNode, /*typeArguments*/ undefined) : factory.createKeywordTypeNode(SyntaxKind.AnyKeyword) ); const parameterSymbol = createSymbol(SymbolFlags.FunctionScopedVariable, "props" as __String); - parameterSymbol.type = result; + parameterSymbol.links.type = result; return createSignature( declaration, /*typeParameters*/ undefined, @@ -33173,7 +33183,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (source) { const links = getSymbolLinks(source); if (!links.inferredClassSymbol || !links.inferredClassSymbol.has(getSymbolId(target))) { - const inferred = isTransientSymbol(target) ? target : cloneSymbol(target) as TransientSymbol; + const inferred = isTransientSymbol(target) ? target : cloneSymbol(target); inferred.exports = inferred.exports || createSymbolTable(); inferred.members = inferred.members || createSymbolTable(); inferred.flags |= source.flags & SymbolFlags.Class; @@ -33426,8 +33436,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const memberTable = createSymbolTable(); const newSymbol = createSymbol(SymbolFlags.Alias, InternalSymbolName.Default); newSymbol.parent = originalSymbol; - newSymbol.nameType = getStringLiteralType("default"); - newSymbol.aliasTarget = resolveSymbol(symbol); + newSymbol.links.nameType = getStringLiteralType("default"); + newSymbol.links.aliasTarget = resolveSymbol(symbol); memberTable.set(InternalSymbolName.Default, newSymbol); return createAnonymousType(anonymousSymbol, memberTable, emptyArray, emptyArray, emptyArray); } @@ -33454,7 +33464,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (hasSyntheticDefault) { const anonymousSymbol = createSymbol(SymbolFlags.TypeLiteral, InternalSymbolName.Type); const defaultContainingObject = createDefaultPropertyWrapperForModule(symbol, originalSymbol, anonymousSymbol); - anonymousSymbol.type = defaultContainingObject; + anonymousSymbol.links.type = defaultContainingObject; synthType.syntheticType = isValidSpreadType(type) ? getSpreadType(type, defaultContainingObject, anonymousSymbol, /*objectFlags*/ 0, /*readonly*/ false) : defaultContainingObject; } else { @@ -34088,7 +34098,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const targetPropertySymbol = createSymbol(SymbolFlags.Property, "target" as __String, CheckFlags.Readonly); targetPropertySymbol.parent = symbol; - targetPropertySymbol.type = targetType; + targetPropertySymbol.links.type = targetType; const members = createSymbolTable([targetPropertySymbol]); symbol.members = members; @@ -41628,7 +41638,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function getTargetSymbol(s: 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 - return getCheckFlags(s) & CheckFlags.Instantiated ? (s as TransientSymbol).target! : s; + // NOTE: cast to TransientSymbol should be safe because only TransientSymbols have CheckFlags.Instantiated + return getCheckFlags(s) & CheckFlags.Instantiated ? (s as TransientSymbol).links.target! : s; } function getClassOrInterfaceDeclarationsOfSymbol(symbol: Symbol) { @@ -44271,7 +44282,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return mapDefined(getSymbolLinks(symbol).containingType!.types, type => getPropertyOfType(type, symbol.escapedName)); } else if (symbol.flags & SymbolFlags.Transient) { - const { leftSpread, rightSpread, syntheticOrigin } = symbol as TransientSymbol; + const { links: { leftSpread, rightSpread, syntheticOrigin } } = symbol as TransientSymbol; return leftSpread ? [leftSpread, rightSpread!] : syntheticOrigin ? [syntheticOrigin] : singleElementArray(tryGetTarget(symbol)); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 773d64df907ae..4e941b0545f31 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5735,19 +5735,20 @@ export interface Symbol { members?: SymbolTable; // Class, interface or object literal instance members exports?: SymbolTable; // Module exports globalExports?: SymbolTable; // Conditional global UMD exports - /** @internal */ id?: SymbolId; // Unique id (used to look up SymbolLinks) - /** @internal */ mergeId?: number; // Merge id (used to look up merged symbol) - /** @internal */ parent?: Symbol; // Parent symbol - /** @internal */ exportSymbol?: Symbol; // Exported symbol associated with this symbol - /** @internal */ constEnumOnlyModule?: boolean; // True if module contains only const enums or other modules with only const enums + /** @internal */ id: SymbolId; // Unique id (used to look up SymbolLinks) + /** @internal */ mergeId: number; // Merge id (used to look up merged symbol) + /** @internal */ parent?: Symbol; // Parent symbol + /** @internal */ exportSymbol?: Symbol; // Exported symbol associated with this symbol + /** @internal */ constEnumOnlyModule: boolean | undefined; // True if module contains only const enums or other modules with only const enums /** @internal */ isReferenced?: SymbolFlags; // True if the symbol is referenced elsewhere. Keeps track of the meaning of a reference in case a symbol is both a type parameter and parameter. /** @internal */ isReplaceableByMethod?: boolean; // Can this Javascript class property be replaced by a method symbol? - /** @internal */ isAssigned?: boolean; // True if the symbol is a parameter with assignments + /** @internal */ isAssigned?: boolean; // True if the symbol is a parameter with assignments /** @internal */ assignmentDeclarationMembers?: Map; // detected late-bound assignment declarations associated with the symbol } /** @internal */ export interface SymbolLinks { + _symbolLinksBrand: any; immediateTarget?: Symbol; // Immediate target of an alias. May be another alias. Do not access directly, use `checker.getImmediateAliasedSymbol` instead. aliasTarget?: Symbol, // Resolved (non-alias) target of an alias target?: Symbol; // Original version of an instantiated symbol @@ -5758,7 +5759,7 @@ export interface SymbolLinks { declaredType?: Type; // Type of class, interface, enum, type alias, or type parameter typeParameters?: TypeParameter[]; // Type parameters of type alias (undefined if non-generic) outerTypeParameters?: TypeParameter[]; // Outer type parameters of anonymous object type - instantiations?: Map; // Instantiations of generic type alias (undefined if non-generic) + instantiations?: Map; // Instantiations of generic type alias (undefined if non-generic) aliasSymbol?: Symbol; // Alias associated with generic type alias instantiation aliasTypeArguments?: readonly Type[] // Alias type arguments (if any) inferredClassSymbol?: Map; // Symbol of an inferred ES5 constructor function @@ -5831,23 +5832,38 @@ export const enum CheckFlags { } /** @internal */ -export interface TransientSymbol extends Symbol, SymbolLinks { +export interface TransientSymbolLinks extends SymbolLinks { checkFlags: CheckFlags; } /** @internal */ -export interface MappedSymbol extends TransientSymbol { +export interface TransientSymbol extends Symbol { + links: TransientSymbolLinks; +} + +/** @internal */ +export interface MappedSymbolLinks extends TransientSymbolLinks { mappedType: MappedType; keyType: Type; } /** @internal */ -export interface ReverseMappedSymbol extends TransientSymbol { +export interface MappedSymbol extends TransientSymbol { + links: MappedSymbolLinks; +} + +/** @internal */ +export interface ReverseMappedSymbolLinks extends TransientSymbolLinks { propertyType: Type; mappedType: MappedType; constraintType: IndexType; } +/** @internal */ +export interface ReverseMappedSymbol extends TransientSymbol { + links: ReverseMappedSymbolLinks; +} + export const enum InternalSymbolName { Call = "__call", // Call signatures Constructor = "__constructor", // Constructor implementations diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 5c65c652db3ef..d8adaeeb3c1b0 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -6821,7 +6821,7 @@ export function closeFileWatcher(watcher: FileWatcher) { /** @internal */ export function getCheckFlags(symbol: Symbol): CheckFlags { - return symbol.flags & SymbolFlags.Transient ? (symbol as TransientSymbol).checkFlags : 0; + return symbol.flags & SymbolFlags.Transient ? (symbol as TransientSymbol).links.checkFlags : 0; } /** @internal */ @@ -6833,7 +6833,8 @@ export function getDeclarationModifierFlagsFromSymbol(s: Symbol, isWrite = false return s.parent && s.parent.flags & SymbolFlags.Class ? flags : flags & ~ModifierFlags.AccessibilityModifier; } if (getCheckFlags(s) & CheckFlags.Synthetic) { - const checkFlags = (s as TransientSymbol).checkFlags; + // NOTE: potentially unchecked cast to TransientSymbol + const checkFlags = (s as TransientSymbol).links.checkFlags; const accessModifier = checkFlags & CheckFlags.ContainsPrivate ? ModifierFlags.Private : checkFlags & CheckFlags.ContainsPublic ? ModifierFlags.Public : ModifierFlags.Protected; @@ -7262,9 +7263,16 @@ function Symbol(this: Symbol, flags: SymbolFlags, name: __String) { this.escapedName = name; this.declarations = undefined; this.valueDeclaration = undefined; - this.id = undefined; - this.mergeId = undefined; + this.id = 0; + this.mergeId = 0; this.parent = undefined; + this.members = undefined; + this.exports = undefined; + this.exportSymbol = undefined; + this.constEnumOnlyModule = undefined; + this.isReferenced = undefined; + this.isAssigned = undefined; + (this as any).links = undefined; // used by TransientSymbol } function Type(this: Type, checker: TypeChecker, flags: TypeFlags) { diff --git a/src/services/codefixes/fixInvalidImportSyntax.ts b/src/services/codefixes/fixInvalidImportSyntax.ts index 698a7397878b1..bae3f2d2c0f16 100644 --- a/src/services/codefixes/fixInvalidImportSyntax.ts +++ b/src/services/codefixes/fixInvalidImportSyntax.ts @@ -15,6 +15,7 @@ import { isExpression, isImportCall, isNamedDeclaration, + isTransientSymbol, makeImport, ModuleKind, NamespaceImport, @@ -23,7 +24,6 @@ import { SourceFile, SyntaxKind, textChanges, - TransientSymbol, } from "../_namespaces/ts"; import { createCodeFixActionWithoutFixAll, @@ -107,11 +107,11 @@ function getActionsForInvalidImportLocation(context: CodeFixContext): CodeFixAct function getImportCodeFixesForExpression(context: CodeFixContext, expr: Node): CodeFixAction[] | undefined { const type = context.program.getTypeChecker().getTypeAtLocation(expr); - if (!(type.symbol && (type.symbol as TransientSymbol).originatingImport)) { + if (!(type.symbol && isTransientSymbol(type.symbol) && type.symbol.links.originatingImport)) { return []; } const fixes: CodeFixAction[] = []; - const relatedImport = (type.symbol as TransientSymbol).originatingImport!; // TODO: GH#18217 + const relatedImport = type.symbol.links.originatingImport; if (!isImportCall(relatedImport)) { addRange(fixes, getCodeFixesForImportDeclaration(context, relatedImport)); } diff --git a/src/services/codefixes/inferFromUsage.ts b/src/services/codefixes/inferFromUsage.ts index 7be223f9a8332..174b56cad88ff 100644 --- a/src/services/codefixes/inferFromUsage.ts +++ b/src/services/codefixes/inferFromUsage.ts @@ -53,6 +53,7 @@ import { isRestParameter, isRightSideOfQualifiedNameOrPropertyAccess, isSetAccessorDeclaration, + isTransientSymbol, isVariableDeclaration, isVariableStatement, LanguageServiceHost, @@ -88,7 +89,6 @@ import { SourceFile, Symbol, SymbolFlags, - SymbolLinks, SyntaxKind, textChanges, Token, @@ -1045,7 +1045,7 @@ function inferTypeFromReferences(program: Program, references: readonly Identifi const members = mapEntries(props, (name, types) => { const isOptional = types.length < anons.length ? SymbolFlags.Optional : 0; const s = checker.createSymbol(SymbolFlags.Property | isOptional, name as __String); - s.type = checker.getUnionType(types); + s.links.type = checker.getUnionType(types); return [name, s]; }); const indexInfos = []; @@ -1080,7 +1080,7 @@ function inferTypeFromReferences(program: Program, references: readonly Identifi const candidateTypes = (usage.candidateTypes || []).map(t => checker.getBaseTypeOfLiteralType(t)); const callsType = usage.calls?.length ? inferStructuralType(usage) : undefined; - if (callsType && candidateTypes) { + if (callsType && candidateTypes) { // TODO: should this be `some(candidateTypes)`? types.push(checker.getUnionType([callsType, ...candidateTypes], UnionReduction.Subtype)); } else { @@ -1101,7 +1101,7 @@ function inferTypeFromReferences(program: Program, references: readonly Identifi if (usage.properties) { usage.properties.forEach((u, name) => { const symbol = checker.createSymbol(SymbolFlags.Property, name); - symbol.type = combineFromUsage(u); + symbol.links.type = combineFromUsage(u); members.set(name, symbol); }); } @@ -1202,7 +1202,7 @@ function inferTypeFromReferences(program: Program, references: readonly Identifi if (elementType) { genericParamType = elementType; } - const targetType = (usageParam as SymbolLinks).type + const targetType = tryCast(usageParam, isTransientSymbol)?.links.type || (usageParam.valueDeclaration ? checker.getTypeOfSymbolAtLocation(usageParam, usageParam.valueDeclaration) : checker.getAnyType()); types.push(...inferTypeParameters(genericParamType, targetType, typeParameter)); } @@ -1221,7 +1221,7 @@ function inferTypeFromReferences(program: Program, references: readonly Identifi const length = Math.max(...calls.map(c => c.argumentTypes.length)); for (let i = 0; i < length; i++) { const symbol = checker.createSymbol(SymbolFlags.FunctionScopedVariable, escapeLeadingUnderscores(`arg${i}`)); - symbol.type = combineTypes(calls.map(call => call.argumentTypes[i] || checker.getUndefinedType())); + symbol.links.type = combineTypes(calls.map(call => call.argumentTypes[i] || checker.getUndefinedType())); if (calls.some(call => call.argumentTypes[i] === undefined)) { symbol.flags |= SymbolFlags.Optional; } diff --git a/src/services/codefixes/returnValueCorrect.ts b/src/services/codefixes/returnValueCorrect.ts index b6930f0b7efba..1492e6377ce26 100644 --- a/src/services/codefixes/returnValueCorrect.ts +++ b/src/services/codefixes/returnValueCorrect.ts @@ -120,7 +120,7 @@ registerCodeFix({ function createObjectTypeFromLabeledExpression(checker: TypeChecker, label: Identifier, expression: Expression) { const member = checker.createSymbol(SymbolFlags.Property, label.escapedText); - member.type = checker.getTypeAtLocation(expression); + member.links.type = checker.getTypeAtLocation(expression); const members = createSymbolTable([member]); return checker.createAnonymousType(/*symbol*/ undefined, members, [], [], []); } diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index 45aa5d1b90970..35aab0a65c361 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -2124,7 +2124,7 @@ function collectReadsAndWrites( const decl = find(visibleDeclarationsInExtractedRange, d => d.symbol === sym); if (decl) { if (isVariableDeclaration(decl)) { - const idString = decl.symbol.id!.toString(); + const idString = decl.symbol.id.toString(); if (!exposedVariableSymbolSet.has(idString)) { exposedVariableDeclarations.push(decl); exposedVariableSymbolSet.set(idString, true); diff --git a/src/services/services.ts b/src/services/services.ts index d227f3dee213f..1cd86fa44ab3b 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -183,8 +183,9 @@ import { isTagName, isTextWhiteSpaceLike, isThisTypeParameter, - JSDoc, + isTransientSymbol, JsDoc, + JSDoc, JSDocContainer, JSDocTagInfo, JsonSourceFile, @@ -304,7 +305,6 @@ import { toPath, tracing, TransformFlags, - TransientSymbol, Type, TypeChecker, TypeFlags, @@ -615,6 +615,9 @@ class SymbolObject implements Symbol { escapedName: __String; declarations!: Declaration[]; valueDeclaration!: Declaration; + id = 0; + mergeId = 0; + constEnumOnlyModule: boolean | undefined; // Undefined is used to indicate the value has not been computed. If, after computing, the // symbol has no doc comment, then the empty array will be returned. @@ -656,8 +659,8 @@ class SymbolObject implements Symbol { if (!this.documentationComment) { this.documentationComment = emptyArray; // Set temporarily to avoid an infinite loop finding inherited docs - if (!this.declarations && (this as Symbol as TransientSymbol).target && ((this as Symbol as TransientSymbol).target as TransientSymbol).tupleLabelDeclaration) { - const labelDecl = ((this as Symbol as TransientSymbol).target as TransientSymbol).tupleLabelDeclaration!; + if (!this.declarations && isTransientSymbol(this) && this.links.target && isTransientSymbol(this.links.target) && this.links.target.links.tupleLabelDeclaration) { + const labelDecl = this.links.target.links.tupleLabelDeclaration; this.documentationComment = getDocumentationComment([labelDecl], checker); } else { diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index a50c64117daa9..f662021481c4c 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -49,6 +49,7 @@ import { isTemplateLiteralToken, isTemplateSpan, isTemplateTail, + isTransientSymbol, last, lastOrUndefined, ListFormat, @@ -77,7 +78,6 @@ import { TaggedTemplateExpression, TemplateExpression, TextSpan, - TransientSymbol, tryCast, Type, TypeChecker, @@ -724,7 +724,7 @@ function itemInfoForParameters(candidateSignature: Signature, checker: TypeCheck const isVariadic: (parameterList: readonly Symbol[]) => boolean = !checker.hasEffectiveRestParameter(candidateSignature) ? _ => false : lists.length === 1 ? _ => true - : pList => !!(pList.length && (pList[pList.length - 1] as TransientSymbol).checkFlags & CheckFlags.RestParameter); + : pList => !!(pList.length && tryCast(pList[pList.length - 1], isTransientSymbol)?.links.checkFlags! & CheckFlags.RestParameter); return lists.map(parameterList => ({ isVariadic: isVariadic(parameterList), parameters: parameterList.map(p => createSignatureHelpParameterForParameter(p, checker, enclosingDeclaration, sourceFile, printer)), @@ -739,7 +739,7 @@ function createSignatureHelpParameterForParameter(parameter: Symbol, checker: Ty printer.writeNode(EmitHint.Unspecified, param, sourceFile, writer); }); const isOptional = checker.isOptionalParameter(parameter.valueDeclaration as ParameterDeclaration); - const isRest = !!((parameter as TransientSymbol).checkFlags & CheckFlags.RestParameter); + const isRest = isTransientSymbol(parameter) && !!(parameter.links.checkFlags & CheckFlags.RestParameter); return { name: parameter.name, documentation: parameter.getDocumentationComment(checker), displayParts, isOptional, isRest }; } diff --git a/src/services/symbolDisplay.ts b/src/services/symbolDisplay.ts index 3f31460b0a3e7..bda7ba595090c 100644 --- a/src/services/symbolDisplay.ts +++ b/src/services/symbolDisplay.ts @@ -58,6 +58,7 @@ import { isObjectBindingPattern, isTaggedTemplateExpression, isThisInTypeQuery, + isTransientSymbol, isTypeAliasDeclaration, isVarConst, JSDocTagInfo, @@ -176,7 +177,7 @@ function getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeCheck if (flags & SymbolFlags.Signature) return ScriptElementKind.indexSignatureElement; if (flags & SymbolFlags.Property) { - if (flags & SymbolFlags.Transient && (symbol as TransientSymbol).checkFlags & CheckFlags.Synthetic) { + if (flags & SymbolFlags.Transient && (symbol as TransientSymbol).links.checkFlags & CheckFlags.Synthetic) { // If union property is result of union of non method (property/accessors/variables), it is labeled as property const unionPropertyKind = forEach(typeChecker.getRootSymbols(symbol), rootSymbol => { const rootSymbolFlags = rootSymbol.getFlags(); @@ -645,8 +646,8 @@ export function getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker: Typ else { addRange(displayParts, typeToDisplayParts(typeChecker, type, enclosingDeclaration)); } - if ((symbol as TransientSymbol).target && ((symbol as TransientSymbol).target as TransientSymbol).tupleLabelDeclaration) { - const labelDecl = ((symbol as TransientSymbol).target as TransientSymbol).tupleLabelDeclaration!; + if (isTransientSymbol(symbol) && symbol.links.target && isTransientSymbol(symbol.links.target) && symbol.links.target.links.tupleLabelDeclaration) { + const labelDecl = symbol.links.target.links.tupleLabelDeclaration; Debug.assertNode(labelDecl.name, isIdentifier); displayParts.push(spacePart()); displayParts.push(punctuationPart(SyntaxKind.OpenParenToken)); diff --git a/src/services/utilities.ts b/src/services/utilities.ts index ac93d22ba7699..89531e4194a23 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -225,6 +225,7 @@ import { isTaggedTemplateExpression, isTemplateLiteralKind, isToken, + isTransientSymbol, isTypeAliasDeclaration, isTypeElement, isTypeNode, @@ -336,7 +337,6 @@ import { textSpanEnd, Token, tokenToString, - TransientSymbol, tryCast, Type, TypeChecker, @@ -3003,9 +3003,9 @@ export function getScriptKind(fileName: string, host: LanguageServiceHost): Scri /** @internal */ export function getSymbolTarget(symbol: Symbol, checker: TypeChecker): Symbol { let next: Symbol = symbol; - while (isAliasSymbol(next) || (isTransientSymbol(next) && next.target)) { - if (isTransientSymbol(next) && next.target) { - next = next.target; + while (isAliasSymbol(next) || (isTransientSymbol(next) && next.links.target)) { + if (isTransientSymbol(next) && next.links.target) { + next = next.links.target; } else { next = skipAlias(next, checker); @@ -3014,10 +3014,6 @@ export function getSymbolTarget(symbol: Symbol, checker: TypeChecker): Symbol { return next; } -function isTransientSymbol(symbol: Symbol): symbol is TransientSymbol { - return (symbol.flags & SymbolFlags.Transient) !== 0; -} - function isAliasSymbol(symbol: Symbol): boolean { return (symbol.flags & SymbolFlags.Alias) !== 0; }