Generate names for type parameter declarations in inferred types (#23902)

* Generate names for type parameter declarations in inferred types

* Fix lint

* Merge functions, make overload private

* Handle some edge cases better (nodes in differing files than current emit)

* Account for transformed nodes
This commit is contained in:
Wesley Wigham authored and GitHub committed 2018-05-10 11:16:27 -07:00
1 parent 1b796ed04d
commit 6149b41469
12 files changed
+192 -27

No files matched your search

+24 -4
View File
@@ -2746,7 +2746,7 @@ namespace ts {
* @param shouldComputeAliasToMakeVisible a boolean value to indicate whether to return aliases to be mark visible in case the symbol is accessible
*/
function isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags, shouldComputeAliasesToMakeVisible: boolean): SymbolAccessibilityResult {
if (symbol && enclosingDeclaration && !(symbol.flags & SymbolFlags.TypeParameter)) {
if (symbol && enclosingDeclaration) {
const initialSymbol = symbol;
let meaningToLook = meaning;
while (symbol) {
@@ -3118,7 +3118,15 @@ namespace ts {
}
if (type.flags & TypeFlags.TypeParameter || objectFlags & ObjectFlags.ClassOrInterface) {
if (type.flags & TypeFlags.TypeParameter && contains(context.inferTypeParameters, type)) {
return createInferTypeNode(createTypeParameterDeclaration(getNameOfSymbolAsWritten(type.symbol)));
return createInferTypeNode(typeParameterToDeclarationWithConstraint(type as TypeParameter, context, /*constraintNode*/ undefined));
}
if (context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams &&
type.flags & TypeFlags.TypeParameter &&
length(type.symbol.declarations) &&
isTypeParameterDeclaration(type.symbol.declarations[0]) &&
typeParameterShadowsNameInScope(type, context) &&
!isTypeSymbolAccessible(type.symbol, context.enclosingDeclaration)) {
return createTypeReferenceNode(getGeneratedNameForNode((type.symbol.declarations[0] as TypeParameterDeclaration).name, GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.ReservedInNestedScopes), /*typeArguments*/ undefined);
}
const name = type.symbol ? symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ false) : createIdentifier("?");
// Ignore constraint/default when creating a usage (as opposed to declaration) of a type parameter.
@@ -3563,10 +3571,21 @@ namespace ts {
return createSignatureDeclaration(kind, typeParameters, parameters, returnTypeNode, typeArguments);
}
function typeParameterToDeclarationWithConstraint(type: TypeParameter, context: NodeBuilderContext, constraintNode: TypeNode): TypeParameterDeclaration {
function typeParameterShadowsNameInScope(type: TypeParameter, context: NodeBuilderContext) {
return !!resolveName(context.enclosingDeclaration, type.symbol.escapedName, SymbolFlags.Type, /*nameNotFoundArg*/ undefined, type.symbol.escapedName, /*isUse*/ false);
}
function typeParameterToDeclarationWithConstraint(type: TypeParameter, context: NodeBuilderContext, constraintNode: TypeNode | undefined): TypeParameterDeclaration {
const savedContextFlags = context.flags;
context.flags &= ~NodeBuilderFlags.WriteTypeParametersInQualifiedName; // Avoids potential infinite loop when building for a claimspace with a generic
const name = symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ true);
const shouldUseGeneratedName =
context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams &&
type.symbol.declarations[0] &&
isTypeParameterDeclaration(type.symbol.declarations[0]) &&
typeParameterShadowsNameInScope(type, context);
const name = shouldUseGeneratedName
? getGeneratedNameForNode((type.symbol.declarations[0] as TypeParameterDeclaration).name, GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.ReservedInNestedScopes)
: symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ true);
const defaultParameter = getDefaultFromTypeParameter(type);
const defaultParameterNode = defaultParameter && typeToTypeNodeHelper(defaultParameter, context);
context.flags = savedContextFlags;
@@ -3799,6 +3818,7 @@ namespace ts {
if (index === 0) {
context.flags ^= NodeBuilderFlags.InInitialEntityName;
}
const identifier = setEmitFlags(createIdentifier(symbolName, typeParameterNodes), EmitFlags.NoAsciiEscaping);
identifier.symbol = symbol;
+38 -12
View File
@@ -1132,6 +1132,7 @@ namespace ts {
}
function emitMethodSignature(node: MethodSignature) {
pushNameGenerationScope(node);
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
emit(node.name);
@@ -1140,6 +1141,7 @@ namespace ts {
emitParameters(node, node.parameters);
emitTypeAnnotation(node.type);
writeSemicolon();
popNameGenerationScope(node);
}
function emitMethodDeclaration(node: MethodDeclaration) {
@@ -1167,15 +1169,18 @@ namespace ts {
}
function emitCallSignature(node: CallSignatureDeclaration) {
pushNameGenerationScope(node);
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
emitTypeParameters(node, node.typeParameters);
emitParameters(node, node.parameters);
emitTypeAnnotation(node.type);
writeSemicolon();
popNameGenerationScope(node);
}
function emitConstructSignature(node: ConstructSignatureDeclaration) {
pushNameGenerationScope(node);
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
writeKeyword("new");
@@ -1184,6 +1189,7 @@ namespace ts {
emitParameters(node, node.parameters);
emitTypeAnnotation(node.type);
writeSemicolon();
popNameGenerationScope(node);
}
function emitIndexSignature(node: IndexSignatureDeclaration) {
@@ -1216,12 +1222,14 @@ namespace ts {
}
function emitFunctionType(node: FunctionTypeNode) {
pushNameGenerationScope(node);
emitTypeParameters(node, node.typeParameters);
emitParametersForArrow(node, node.parameters);
writeSpace();
writePunctuation("=>");
writeSpace();
emit(node.type);
popNameGenerationScope(node);
}
function emitJSDocFunctionType(node: JSDocFunctionType) {
@@ -1248,6 +1256,7 @@ namespace ts {
}
function emitConstructorType(node: ConstructorTypeNode) {
pushNameGenerationScope(node);
writeKeyword("new");
writeSpace();
emitTypeParameters(node, node.typeParameters);
@@ -1256,6 +1265,7 @@ namespace ts {
writePunctuation("=>");
writeSpace();
emit(node.type);
popNameGenerationScope(node);
}
function emitTypeQuery(node: TypeQueryNode) {
@@ -3241,7 +3251,7 @@ namespace ts {
if (isGeneratedIdentifier(node)) {
return generateName(node);
}
else if (isIdentifier(node) && (nodeIsSynthesized(node) || !node.parent)) {
else if (isIdentifier(node) && (nodeIsSynthesized(node) || !node.parent || !currentSourceFile || (node.parent && currentSourceFile && getSourceFileOfNode(node) !== getOriginalNode(currentSourceFile)))) {
return idText(node);
}
else if (node.kind === SyntaxKind.StringLiteral && (<StringLiteral>node).textSourceNode) {
@@ -3415,7 +3425,7 @@ namespace ts {
if ((name.autoGenerateFlags & GeneratedIdentifierFlags.KindMask) === GeneratedIdentifierFlags.Node) {
// Node names generate unique names based on their original node
// and are cached based on that node's id.
return generateNameCached(getNodeForGeneratedName(name));
return generateNameCached(getNodeForGeneratedName(name), name.autoGenerateFlags);
}
else {
// Auto, Loop, and Unique names are cached based on their unique
@@ -3425,9 +3435,9 @@ namespace ts {
}
}
function generateNameCached(node: Node) {
function generateNameCached(node: Node, flags?: GeneratedIdentifierFlags) {
const nodeId = getNodeId(node);
return nodeIdToGeneratedName[nodeId] || (nodeIdToGeneratedName[nodeId] = generateNameForNode(node));
return nodeIdToGeneratedName[nodeId] || (nodeIdToGeneratedName[nodeId] = generateNameForNode(node, flags));
}
/**
@@ -3444,7 +3454,7 @@ namespace ts {
* Returns a value indicating whether a name is unique globally or within the current file.
*/
function isFileLevelUniqueName(name: string) {
return ts.isFileLevelUniqueName(currentSourceFile, name, hasGlobalName);
return currentSourceFile ? ts.isFileLevelUniqueName(currentSourceFile, name, hasGlobalName) : true;
}
/**
@@ -3504,10 +3514,15 @@ namespace ts {
* makeUniqueName are guaranteed to never conflict.
* If `optimistic` is set, the first instance will use 'baseName' verbatim instead of 'baseName_1'
*/
function makeUniqueName(baseName: string, checkFn: (name: string) => boolean = isUniqueName, optimistic?: boolean): string {
function makeUniqueName(baseName: string, checkFn: (name: string) => boolean = isUniqueName, optimistic?: boolean, scoped?: boolean): string {
if (optimistic) {
if (checkFn(baseName)) {
generatedNames.set(baseName, true);
if (scoped) {
reserveNameInNestedScopes(baseName);
}
else {
generatedNames.set(baseName, true);
}
return baseName;
}
}
@@ -3519,7 +3534,12 @@ namespace ts {
while (true) {
const generatedName = baseName + i;
if (checkFn(generatedName)) {
generatedNames.set(generatedName, true);
if (scoped) {
reserveNameInNestedScopes(generatedName);
}
else {
generatedNames.set(generatedName, true);
}
return generatedName;
}
i++;
@@ -3573,10 +3593,15 @@ namespace ts {
/**
* Generates a unique name from a node.
*/
function generateNameForNode(node: Node): string {
function generateNameForNode(node: Node, flags?: GeneratedIdentifierFlags): string {
switch (node.kind) {
case SyntaxKind.Identifier:
return makeUniqueName(getTextOfNode(node));
return makeUniqueName(
getTextOfNode(node),
isUniqueName,
!!(flags & GeneratedIdentifierFlags.Optimistic),
!!(flags & GeneratedIdentifierFlags.ReservedInNestedScopes)
);
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.EnumDeclaration:
return generateNameForModuleOrEnum(<ModuleDeclaration | EnumDeclaration>node);
@@ -3611,7 +3636,8 @@ namespace ts {
return makeUniqueName(
idText(name),
(name.autoGenerateFlags & GeneratedIdentifierFlags.FileLevel) ? isFileLevelUniqueName : isUniqueName,
!!(name.autoGenerateFlags & GeneratedIdentifierFlags.Optimistic)
!!(name.autoGenerateFlags & GeneratedIdentifierFlags.Optimistic),
!!(name.autoGenerateFlags & GeneratedIdentifierFlags.ReservedInNestedScopes)
);
}
@@ -3631,7 +3657,7 @@ namespace ts {
// if "node" is a different generated name (having a different
// "autoGenerateId"), use it and stop traversing.
if (isIdentifier(node)
&& node.autoGenerateFlags === GeneratedIdentifierFlags.Node
&& !!(node.autoGenerateFlags & GeneratedIdentifierFlags.Node)
&& node.autoGenerateId !== autoGenerateId) {
break;
}
+6 -3
View File
@@ -192,9 +192,11 @@ namespace ts {
}
/** Create a unique name generated for a node. */
export function getGeneratedNameForNode(node: Node): Identifier {
const name = createIdentifier("");
name.autoGenerateFlags = GeneratedIdentifierFlags.Node;
export function getGeneratedNameForNode(node: Node): Identifier;
/* @internal */ export function getGeneratedNameForNode(node: Node, flags: GeneratedIdentifierFlags): Identifier; // tslint:disable-line unified-signatures
export function getGeneratedNameForNode(node: Node, flags?: GeneratedIdentifierFlags): Identifier {
const name = createIdentifier(isIdentifier(node) ? idText(node) : "");
name.autoGenerateFlags = GeneratedIdentifierFlags.Node | flags;
name.autoGenerateId = nextAutoGenerateId;
name.original = node;
nextAutoGenerateId++;
@@ -4246,6 +4248,7 @@ namespace ts {
switch (member.kind) {
case SyntaxKind.TypeQuery:
case SyntaxKind.TypeOperator:
case SyntaxKind.InferType:
return createParenthesizedType(member);
}
return parenthesizeElementTypeMember(member);
+8 -1
View File
@@ -9,7 +9,13 @@ namespace ts {
return result.diagnostics;
}
const declarationEmitNodeBuilderFlags = NodeBuilderFlags.MultilineObjectLiterals | TypeFormatFlags.WriteClassExpressionAsTypeLiteral | NodeBuilderFlags.UseTypeOfFunction | NodeBuilderFlags.UseStructuralFallback | NodeBuilderFlags.AllowEmptyTuple;
const declarationEmitNodeBuilderFlags =
NodeBuilderFlags.MultilineObjectLiterals |
TypeFormatFlags.WriteClassExpressionAsTypeLiteral |
NodeBuilderFlags.UseTypeOfFunction |
NodeBuilderFlags.UseStructuralFallback |
NodeBuilderFlags.AllowEmptyTuple |
NodeBuilderFlags.GenerateNamesForShadowedTypeParams;
/**
* Transforms a ts file into a .d.ts file
@@ -95,6 +101,7 @@ namespace ts {
}
function trackSymbol(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags) {
if (symbol.flags & SymbolFlags.TypeParameter) return;
handleSymbolAccessibilityError(resolver.isSymbolAccessible(symbol, enclosingDeclaration, meaning, /*shouldComputeAliasesToMakeVisible*/ true));
recordTypeReferenceDirectivesIfNecessary(resolver.getTypeReferenceDirectivesForSymbol(symbol, meaning));
}
+1 -1
View File
@@ -3074,7 +3074,7 @@ namespace ts {
// Options
NoTruncation = 1 << 0, // Don't truncate result
WriteArrayAsGenericType = 1 << 1, // Write Array<T> instead T[]
// empty space
GenerateNamesForShadowedTypeParams = 1 << 2, // When a type parameter T is shadowing another T, generate a name for it so it can still be referenced
UseStructuralFallback = 1 << 3, // When an alias cannot be named by its symbol, rather than report an error, fallback to a structural printout if possible
// empty space
WriteTypeArgumentsOfSignature = 1 << 5, // Write the type arguments instead of type parameters of the signature
+1
View File
@@ -1883,6 +1883,7 @@ declare namespace ts {
None = 0,
NoTruncation = 1,
WriteArrayAsGenericType = 2,
GenerateNamesForShadowedTypeParams = 4,
UseStructuralFallback = 8,
WriteTypeArgumentsOfSignature = 32,
UseFullyQualifiedType = 64,
+1
View File
@@ -1883,6 +1883,7 @@ declare namespace ts {
None = 0,
NoTruncation = 1,
WriteArrayAsGenericType = 2,
GenerateNamesForShadowedTypeParams = 4,
UseStructuralFallback = 8,
WriteTypeArgumentsOfSignature = 32,
UseFullyQualifiedType = 64,
@@ -927,9 +927,9 @@ type T52 = IsNever<any>; // false
>IsNever : IsNever<T>
function f22<T>(x: T extends (infer U)[] ? U[] : never) {
>f22 : <T>(x: T extends infer U[] ? U[] : never) => void
>f22 : <T>(x: T extends (infer U)[] ? U[] : never) => void
>T : T
>x : T extends infer U[] ? U[] : never
>x : T extends (infer U)[] ? U[] : never
>T : T
>U : U
>U : U
@@ -937,14 +937,14 @@ function f22<T>(x: T extends (infer U)[] ? U[] : never) {
let e = x[0]; // {}
>e : {}
>x[0] : {}
>x : T extends infer U[] ? U[] : never
>x : T extends (infer U)[] ? U[] : never
>0 : 0
}
function f23<T extends string[]>(x: T extends (infer U)[] ? U[] : never) {
>f23 : <T extends string[]>(x: T extends infer U[] ? U[] : never) => void
>f23 : <T extends string[]>(x: T extends (infer U)[] ? U[] : never) => void
>T : T
>x : T extends infer U[] ? U[] : never
>x : T extends (infer U)[] ? U[] : never
>T : T
>U : U
>U : U
@@ -952,7 +952,7 @@ function f23<T extends string[]>(x: T extends (infer U)[] ? U[] : never) {
let e = x[0]; // string
>e : string
>x[0] : string
>x : T extends infer U[] ? U[] : never
>x : T extends (infer U)[] ? U[] : never
>0 : 0
}
@@ -0,0 +1,25 @@
//// [declarationEmitNestedGenerics.ts]
function f<T>(p: T) {
let g: <T>(x: T) => typeof p = null as any;
return g;
}
function g<T>(x: T) {
let y: typeof x extends (infer T)[] ? T : typeof x = null as any;
return y;
}
//// [declarationEmitNestedGenerics.js]
function f(p) {
var g = null;
return g;
}
function g(x) {
var y = null;
return y;
}
//// [declarationEmitNestedGenerics.d.ts]
declare function f<T>(p: T): <T_1>(x: T_1) => T;
declare function g<T>(x: T): T extends (infer T_1)[] ? T_1 : T;
@@ -0,0 +1,34 @@
=== tests/cases/compiler/declarationEmitNestedGenerics.ts ===
function f<T>(p: T) {
>f : Symbol(f, Decl(declarationEmitNestedGenerics.ts, 0, 0))
>T : Symbol(T, Decl(declarationEmitNestedGenerics.ts, 0, 11))
>p : Symbol(p, Decl(declarationEmitNestedGenerics.ts, 0, 14))
>T : Symbol(T, Decl(declarationEmitNestedGenerics.ts, 0, 11))
let g: <T>(x: T) => typeof p = null as any;
>g : Symbol(g, Decl(declarationEmitNestedGenerics.ts, 1, 7))
>T : Symbol(T, Decl(declarationEmitNestedGenerics.ts, 1, 12))
>x : Symbol(x, Decl(declarationEmitNestedGenerics.ts, 1, 15))
>T : Symbol(T, Decl(declarationEmitNestedGenerics.ts, 1, 12))
>p : Symbol(p, Decl(declarationEmitNestedGenerics.ts, 0, 14))
return g;
>g : Symbol(g, Decl(declarationEmitNestedGenerics.ts, 1, 7))
}
function g<T>(x: T) {
>g : Symbol(g, Decl(declarationEmitNestedGenerics.ts, 3, 1))
>T : Symbol(T, Decl(declarationEmitNestedGenerics.ts, 5, 11))
>x : Symbol(x, Decl(declarationEmitNestedGenerics.ts, 5, 14))
>T : Symbol(T, Decl(declarationEmitNestedGenerics.ts, 5, 11))
let y: typeof x extends (infer T)[] ? T : typeof x = null as any;
>y : Symbol(y, Decl(declarationEmitNestedGenerics.ts, 6, 7))
>x : Symbol(x, Decl(declarationEmitNestedGenerics.ts, 5, 14))
>T : Symbol(T, Decl(declarationEmitNestedGenerics.ts, 6, 34))
>T : Symbol(T, Decl(declarationEmitNestedGenerics.ts, 6, 34))
>x : Symbol(x, Decl(declarationEmitNestedGenerics.ts, 5, 14))
return y;
>y : Symbol(y, Decl(declarationEmitNestedGenerics.ts, 6, 7))
}
@@ -0,0 +1,38 @@
=== tests/cases/compiler/declarationEmitNestedGenerics.ts ===
function f<T>(p: T) {
>f : <T>(p: T) => <T>(x: T) => T
>T : T
>p : T
>T : T
let g: <T>(x: T) => typeof p = null as any;
>g : <T>(x: T) => T
>T : T
>x : T
>T : T
>p : T
>null as any : any
>null : null
return g;
>g : <T>(x: T) => T
}
function g<T>(x: T) {
>g : <T>(x: T) => T extends (infer T)[] ? T : T
>T : T
>x : T
>T : T
let y: typeof x extends (infer T)[] ? T : typeof x = null as any;
>y : T extends (infer T)[] ? T : T
>x : T
>T : T
>T : T
>x : T
>null as any : any
>null : null
return y;
>y : T extends (infer T)[] ? T : T
}
@@ -0,0 +1,10 @@
// @declaration: true
function f<T>(p: T) {
let g: <T>(x: T) => typeof p = null as any;
return g;
}
function g<T>(x: T) {
let y: typeof x extends (infer T)[] ? T : typeof x = null as any;
return y;
}