Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce truncation into node builder and symbol display part writer #24258

Merged
merged 4 commits into from
Jul 6, 2018
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 53 additions & 8 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2993,15 +2993,19 @@ namespace ts {
}

function typeToString(type: Type, enclosingDeclaration?: Node, flags: TypeFormatFlags = TypeFormatFlags.AllowUniqueESSymbolType | TypeFormatFlags.UseAliasDefinedOutsideCurrentScope, writer: EmitTextWriter = createTextWriter("")): string {
const typeNode = nodeBuilder.typeToTypeNode(type, enclosingDeclaration, toNodeBuilderFlags(flags) | NodeBuilderFlags.IgnoreErrors, writer);
if (writer.maximumApproximateLength === undefined) {
writer.maximumApproximateLength = 100;
}
const noTruncation = compilerOptions.noErrorTruncation || flags & TypeFormatFlags.NoTruncation;
const typeNode = nodeBuilder.typeToTypeNode(type, enclosingDeclaration, toNodeBuilderFlags(flags) | NodeBuilderFlags.IgnoreErrors | (noTruncation ? NodeBuilderFlags.NoTruncation : 0), writer);
if (typeNode === undefined) return Debug.fail("should always get typenode");
const options = { removeComments: true };
const printer = createPrinter(options);
const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration);
printer.writeNode(EmitHint.Unspecified, typeNode, /*sourceFile*/ sourceFile, writer);
const result = writer.getText();

const maxLength = compilerOptions.noErrorTruncation || flags & TypeFormatFlags.NoTruncation ? undefined : 100;
const maxLength = noTruncation ? undefined : writer.maximumApproximateLength;
if (maxLength && result && result.length >= maxLength) {
return result.substr(0, maxLength - "...".length) + "...";
}
Expand Down Expand Up @@ -3040,7 +3044,8 @@ namespace ts {
tracker: tracker && tracker.trackSymbol ? tracker : { trackSymbol: noop },
encounteredError: false,
visitedSymbols: undefined,
inferTypeParameters: undefined
inferTypeParameters: undefined,
approximateLength: 0
};
const resultingNode = cb(context);
return context.encounteredError ? undefined : resultingNode;
Expand All @@ -3058,67 +3063,84 @@ namespace ts {
return undefined!; // TODO: GH#18217
}

if (type.flags & TypeFlags.Any) {
if (type.flags & TypeFlags.Any || (!(context.flags & NodeBuilderFlags.NoTruncation) && context.approximateLength > (context.tracker.maximumApproximateLength || 2000))) {
context.approximateLength += 3;
return createKeywordTypeNode(SyntaxKind.AnyKeyword);
}
if (type.flags & TypeFlags.Unknown) {
return createKeywordTypeNode(SyntaxKind.UnknownKeyword);
}
if (type.flags & TypeFlags.String) {
context.approximateLength += 6;
return createKeywordTypeNode(SyntaxKind.StringKeyword);
}
if (type.flags & TypeFlags.Number) {
context.approximateLength += 6;
return createKeywordTypeNode(SyntaxKind.NumberKeyword);
}
if (type.flags & TypeFlags.Boolean) {
context.approximateLength += 7;
return createKeywordTypeNode(SyntaxKind.BooleanKeyword);
}
if (type.flags & TypeFlags.EnumLiteral && !(type.flags & TypeFlags.Union)) {
const parentSymbol = getParentOfSymbol(type.symbol)!;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracking the max length state at each level of the tree walk is a start, but I think a complete solution would also be able to abort at any level of the tree walk too. Specifically, globally abort and then truncate instead of aborting and truncating locally.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why the code here doesn't do that, actually. Why don't the short-circuits in witeKind et al implement a global truncation?

Copy link
Member Author

@weswigham weswigham May 21, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They're global within the context of a single writer, however signature help composes multiple writer calls to build the full display...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh no :(

const parentName = symbolToName(parentSymbol, context, SymbolFlags.Type, /*expectsIdentifier*/ false);
const enumLiteralName = getDeclaredTypeOfSymbol(parentSymbol) === type ? parentName : createQualifiedName(parentName, symbolName(type.symbol));
context.approximateLength += symbolName(type.symbol).length;
return createTypeReferenceNode(enumLiteralName, /*typeArguments*/ undefined);
}
if (type.flags & TypeFlags.EnumLike) {
const name = symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ false);
context.approximateLength += symbolName(type.symbol).length;
return createTypeReferenceNode(name, /*typeArguments*/ undefined);
}
if (type.flags & (TypeFlags.StringLiteral)) {
if (type.flags & TypeFlags.StringLiteral) {
context.approximateLength += ((<StringLiteralType>type).value.length + 2);
return createLiteralTypeNode(setEmitFlags(createLiteral((<StringLiteralType>type).value), EmitFlags.NoAsciiEscaping));
}
if (type.flags & (TypeFlags.NumberLiteral)) {
if (type.flags & TypeFlags.NumberLiteral) {
context.approximateLength += (("" + (<NumberLiteralType>type).value).length);
return createLiteralTypeNode((createLiteral((<NumberLiteralType>type).value)));
}
if (type.flags & TypeFlags.BooleanLiteral) {
context.approximateLength += (<IntrinsicType>type).intrinsicName.length;
return (<IntrinsicType>type).intrinsicName === "true" ? createTrue() : createFalse();
}
if (type.flags & TypeFlags.UniqueESSymbol) {
if (!(context.flags & NodeBuilderFlags.AllowUniqueESSymbolType)) {
if (isValueSymbolAccessible(type.symbol, context.enclosingDeclaration!)) {
context.approximateLength += 6;
return symbolToTypeNode(type.symbol, context, SymbolFlags.Value);
}
if (context.tracker.reportInaccessibleUniqueSymbolError) {
context.tracker.reportInaccessibleUniqueSymbolError();
}
}
context.approximateLength += 13;
return createTypeOperatorNode(SyntaxKind.UniqueKeyword, createKeywordTypeNode(SyntaxKind.SymbolKeyword));
}
if (type.flags & TypeFlags.Void) {
context.approximateLength += 4;
return createKeywordTypeNode(SyntaxKind.VoidKeyword);
}
if (type.flags & TypeFlags.Undefined) {
context.approximateLength += 9;
return createKeywordTypeNode(SyntaxKind.UndefinedKeyword);
}
if (type.flags & TypeFlags.Null) {
context.approximateLength += 4;
return createKeywordTypeNode(SyntaxKind.NullKeyword);
}
if (type.flags & TypeFlags.Never) {
context.approximateLength += 5;
return createKeywordTypeNode(SyntaxKind.NeverKeyword);
}
if (type.flags & TypeFlags.ESSymbol) {
context.approximateLength += 6;
return createKeywordTypeNode(SyntaxKind.SymbolKeyword);
}
if (type.flags & TypeFlags.NonPrimitive) {
context.approximateLength += 6;
return createKeywordTypeNode(SyntaxKind.ObjectKeyword);
}
if (type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType) {
Expand All @@ -3130,6 +3152,7 @@ namespace ts {
context.tracker.reportInaccessibleThisError();
}
}
context.approximateLength += 4;
return createThis();
}

Expand All @@ -3141,6 +3164,7 @@ namespace ts {
}
if (type.flags & TypeFlags.TypeParameter || objectFlags & ObjectFlags.ClassOrInterface) {
if (type.flags & TypeFlags.TypeParameter && contains(context.inferTypeParameters, type)) {
context.approximateLength += (symbolName(type.symbol).length + 6);
return createInferTypeNode(typeParameterToDeclarationWithConstraint(type as TypeParameter, context, /*constraintNode*/ undefined));
}
if (context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams &&
Expand All @@ -3149,7 +3173,9 @@ namespace ts {
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.declarations[0] as TypeParameterDeclaration).name;
context.approximateLength += idText(name).length;
return createTypeReferenceNode(getGeneratedNameForNode(name, GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.ReservedInNestedScopes), /*typeArguments*/ undefined);
}
// Ignore constraint/default when creating a usage (as opposed to declaration) of a type parameter.
return type.symbol
Expand All @@ -3166,6 +3192,7 @@ namespace ts {
const typeNodes = mapToTypeNodes(types, context);
if (typeNodes && typeNodes.length > 0) {
const unionOrIntersectionTypeNode = createUnionOrIntersectionTypeNode(type.flags & TypeFlags.Union ? SyntaxKind.UnionType : SyntaxKind.IntersectionType, typeNodes);
context.approximateLength += (3 * (types.length - 1));
return unionOrIntersectionTypeNode;
}
else {
Expand All @@ -3182,12 +3209,14 @@ namespace ts {
}
if (type.flags & TypeFlags.Index) {
const indexedType = (<IndexType>type).type;
context.approximateLength += 6;
const indexTypeNode = typeToTypeNodeHelper(indexedType, context);
return createTypeOperatorNode(indexTypeNode);
}
if (type.flags & TypeFlags.IndexedAccess) {
const objectTypeNode = typeToTypeNodeHelper((<IndexedAccessType>type).objectType, context);
const indexTypeNode = typeToTypeNodeHelper((<IndexedAccessType>type).indexType, context);
context.approximateLength += 2;
return createIndexedAccessTypeNode(objectTypeNode, indexTypeNode);
}
if (type.flags & TypeFlags.Conditional) {
Expand All @@ -3198,6 +3227,7 @@ namespace ts {
context.inferTypeParameters = saveInferTypeParameters;
const trueTypeNode = typeToTypeNodeHelper(getTrueTypeFromConditionalType(<ConditionalType>type), context);
const falseTypeNode = typeToTypeNodeHelper(getFalseTypeFromConditionalType(<ConditionalType>type), context);
context.approximateLength += 15;
return createConditionalTypeNode(checkTypeNode, extendsTypeNode, trueTypeNode, falseTypeNode);
}
if (type.flags & TypeFlags.Substitution) {
Expand All @@ -3222,6 +3252,7 @@ namespace ts {
const typeParameterNode = typeParameterToDeclarationWithConstraint(getTypeParameterFromMappedType(type), context, appropriateConstraintTypeNode);
const templateTypeNode = typeToTypeNodeHelper(getTemplateTypeFromMappedType(type), context);
const mappedTypeNode = createMappedTypeNode(readonlyToken, typeParameterNode, questionToken, templateTypeNode);
context.approximateLength += 10;
return setEmitFlags(mappedTypeNode, EmitFlags.SingleLine);
}

Expand Down Expand Up @@ -3250,6 +3281,7 @@ namespace ts {
return symbolToTypeNode(typeAlias, context, SymbolFlags.Type);
}
else {
context.approximateLength += 3;
return createKeywordTypeNode(SyntaxKind.AnyKeyword);
}
}
Expand Down Expand Up @@ -3294,6 +3326,7 @@ namespace ts {
const resolved = resolveStructuredTypeMembers(type);
if (!resolved.properties.length && !resolved.stringIndexInfo && !resolved.numberIndexInfo) {
if (!resolved.callSignatures.length && !resolved.constructSignatures.length) {
context.approximateLength += 2;
return setEmitFlags(createTypeLiteralNode(/*members*/ undefined), EmitFlags.SingleLine);
}

Expand All @@ -3316,6 +3349,7 @@ namespace ts {
const members = createTypeNodesFromResolvedType(resolved);
context.flags = savedFlags;
const typeLiteralNode = createTypeLiteralNode(members);
context.approximateLength += 2;
return setEmitFlags(typeLiteralNode, (context.flags & NodeBuilderFlags.MultilineObjectLiterals) ? 0 : EmitFlags.SingleLine);
}

Expand Down Expand Up @@ -3484,6 +3518,7 @@ namespace ts {
}
}
const propertyName = symbolToName(propertySymbol, context, SymbolFlags.Value, /*expectsIdentifier*/ true);
context.approximateLength += (symbolName(propertySymbol).length + 1);
context.enclosingDeclaration = saveEnclosingDeclaration;
const optionalToken = propertySymbol.flags & SymbolFlags.Optional ? createToken(SyntaxKind.QuestionToken) : undefined;
if (propertySymbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(propertyType).length) {
Expand All @@ -3506,6 +3541,9 @@ namespace ts {
context.flags = savedFlags;

const modifiers = isReadonlySymbol(propertySymbol) ? [createToken(SyntaxKind.ReadonlyKeyword)] : undefined;
if (modifiers) {
context.approximateLength += 9;
}
const propertySignature = createPropertySignature(
modifiers,
propertyName,
Expand Down Expand Up @@ -3553,6 +3591,7 @@ namespace ts {
if (!indexInfo.type && !(context.flags & NodeBuilderFlags.AllowEmptyIndexInfoType)) {
context.encounteredError = true;
}
context.approximateLength += (name.length + 4);
return createIndexSignature(
/*decorators*/ undefined,
indexInfo.isReadonly ? [createToken(SyntaxKind.ReadonlyKeyword)] : undefined,
Expand Down Expand Up @@ -3597,6 +3636,7 @@ namespace ts {
else if (!returnTypeNode) {
returnTypeNode = createKeywordTypeNode(SyntaxKind.AnyKeyword);
}
context.approximateLength += 3; // Usually a signature contributes a few more characters than this, but 3 is the minimum
return createSignatureDeclaration(kind, typeParameters, parameters, returnTypeNode, typeArguments);
}

Expand Down Expand Up @@ -3658,6 +3698,7 @@ namespace ts {
questionToken,
parameterTypeNode,
/*initializer*/ undefined);
context.approximateLength += symbolName(parameterSymbol).length + 3;
return parameterNode;

function cloneBindingName(node: BindingName): BindingName {
Expand Down Expand Up @@ -3812,7 +3853,9 @@ namespace ts {
// module is root, must use `ImportTypeNode`
const nonRootParts = chain.length > 1 ? createAccessFromSymbolChain(chain, chain.length - 1, 1) : undefined;
const typeParameterNodes = overrideTypeArguments || lookupTypeParameterNodes(chain, 0, context);
const lit = createLiteralTypeNode(createLiteral(getSpecifierForModuleSymbol(chain[0], context)));
const specifier = getSpecifierForModuleSymbol(chain[0], context);
const lit = createLiteralTypeNode(createLiteral(specifier));
context.approximateLength += specifier.length + 10; // specifier + import("")
if (!nonRootParts || isEntityName(nonRootParts)) {
if (nonRootParts) {
const lastId = isIdentifier(nonRootParts) ? nonRootParts : nonRootParts.right;
Expand Down Expand Up @@ -3849,6 +3892,7 @@ namespace ts {
context.flags |= NodeBuilderFlags.InInitialEntityName;
}
const symbolName = getNameOfSymbolAsWritten(symbol, context);
context.approximateLength += symbolName.length + 1;
if (index === 0) {
context.flags ^= NodeBuilderFlags.InInitialEntityName;
}
Expand Down Expand Up @@ -4033,6 +4077,7 @@ namespace ts {
encounteredError: boolean;
visitedSymbols: Map<true> | undefined;
inferTypeParameters: TypeParameter[] | undefined;
approximateLength: number;
}

function isDefaultBindingContext(location: Node) {
Expand Down
5 changes: 3 additions & 2 deletions src/compiler/transformers/declarations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ namespace ts {

const declarationEmitNodeBuilderFlags =
NodeBuilderFlags.MultilineObjectLiterals |
TypeFormatFlags.WriteClassExpressionAsTypeLiteral |
NodeBuilderFlags.WriteClassExpressionAsTypeLiteral |
NodeBuilderFlags.UseTypeOfFunction |
NodeBuilderFlags.UseStructuralFallback |
NodeBuilderFlags.AllowEmptyTuple |
NodeBuilderFlags.GenerateNamesForShadowedTypeParams;
NodeBuilderFlags.GenerateNamesForShadowedTypeParams |
NodeBuilderFlags.NoTruncation;

/**
* Transforms a ts file into a .d.ts file
Expand Down
1 change: 1 addition & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5324,6 +5324,7 @@ namespace ts {
reportInaccessibleUniqueSymbolError?(): void;
moduleResolverHost?: ModuleSpecifierResolutionHost;
trackReferencedAmbientModule?(decl: ModuleDeclaration, symbol: Symbol): void;
maximumApproximateLength?: number;
}

export interface TextSpan {
Expand Down
Loading