Basic parens handling and stubbed TypeFormatFlags

This commit is contained in:
Arthur Ozga
2017-05-04 15:27:50 -07:00
parent d0f3716590
commit 384d54603f
6 changed files with 107 additions and 25 deletions

View File

@@ -2287,8 +2287,16 @@ namespace ts {
interface NodeBuilderContext {
readonly enclosingDeclaration: Node | undefined;
readonly flags: NodeBuilderFlags | undefined;
// State
encounteredError: boolean;
inObjectTypeLiteral: boolean;
// TODO: needed for part of parens handling
InElementType: boolean; // Writing an array or union element type
// TODO: ???
InFirstTypeArgument: boolean; // Writing first type argument of the instantiated type
// TODO: ???
InTypeAlias: boolean; // Writing type in type alias declaration
checkAlias: boolean;
symbolStack: Symbol[] | undefined;
}
@@ -2299,18 +2307,28 @@ namespace ts {
flags,
encounteredError: false,
inObjectTypeLiteral: false,
InElementType: false,
InFirstTypeArgument: false,
InTypeAlias: false,
checkAlias: true,
symbolStack: undefined
};
}
function typeToTypeNodeHelper(type: Type, context: NodeBuilderContext): TypeNode {
const InElementType = context.InElementType;
// TODO: why doesn't tts unset the flag?
context.InElementType = false;
// TODO: should be assert?
if (!type) {
context.encounteredError = true;
// TODO(aozgaa): should we return implict any (undefined) or explicit any (keywordtypenode)?
return undefined;
}
if (type.flags & TypeFlags.Any) {
return createKeywordTypeNode(SyntaxKind.AnyKeyword);
}
@@ -2390,16 +2408,17 @@ namespace ts {
if (context.checkAlias && type.aliasSymbol) {
const name = symbolToName(type.aliasSymbol, /*expectsIdentifier*/ false, context);
const typeArgumentNodes = type.aliasTypeArguments && mapToTypeNodeArray(type.aliasTypeArguments);
const typeArgumentNodes = type.aliasTypeArguments && mapToTypeNodeArray(type.aliasTypeArguments, /*addInElementTypeFlag*/ false);
return createTypeReferenceNode(name, typeArgumentNodes);
}
context.checkAlias = false;
if (type.flags & TypeFlags.Union) {
const formattedUnionTypes = formatUnionTypes((<UnionType>type).types);
const unionTypeNodes = formattedUnionTypes && mapToTypeNodeArray(formattedUnionTypes);
if (unionTypeNodes && unionTypeNodes.length > 0) {
return createUnionOrIntersectionTypeNode(SyntaxKind.UnionType, unionTypeNodes);
if (type.flags & (TypeFlags.Union | TypeFlags.Intersection)) {
const types = type.flags & TypeFlags.Union ? formatUnionTypes((<UnionType>type).types) : (<IntersectionType>type).types;
const typeNodes = types && mapToTypeNodeArray(types, /*addInElementTypeFlag*/ true);
if (typeNodes && typeNodes.length > 0) {
const unionOrIntersectionTypeNode = createUnionOrIntersectionTypeNode(type.flags & TypeFlags.Union ? SyntaxKind.UnionType : SyntaxKind.IntersectionType, typeNodes);
return InElementType ? createParenthesizedTypeNode(unionOrIntersectionTypeNode) : unionOrIntersectionTypeNode;
}
else {
if (!context.encounteredError && !(context.flags & NodeBuilderFlags.allowEmptyUnionOrIntersection)) {
@@ -2409,10 +2428,6 @@ namespace ts {
}
}
if (type.flags & TypeFlags.Intersection) {
return createUnionOrIntersectionTypeNode(SyntaxKind.IntersectionType, mapToTypeNodeArray((type as UnionType).types));
}
if (objectFlags & (ObjectFlags.Anonymous | ObjectFlags.Mapped)) {
Debug.assert(!!(type.flags & TypeFlags.Object));
// The type is an object literal type.
@@ -2421,25 +2436,33 @@ namespace ts {
if (type.flags & TypeFlags.Index) {
const indexedType = (<IndexType>type).type;
context.InElementType = <boolean>true;
const indexTypeNode = typeToTypeNodeHelper(indexedType, context);
Debug.assert(context.InElementType === false);
return createTypeOperatorNode(indexTypeNode);
}
if (type.flags & TypeFlags.IndexedAccess) {
context.InElementType = <boolean>true;
const objectTypeNode = typeToTypeNodeHelper((<IndexedAccessType>type).objectType, context);
Debug.assert(context.InElementType === false);
const indexTypeNode = typeToTypeNodeHelper((<IndexedAccessType>type).indexType, context);
return createIndexedAccessTypeNode(objectTypeNode, indexTypeNode);
}
Debug.fail("Should be unreachable.");
function mapToTypeNodeArray(types: Type[]): TypeNode[] {
function mapToTypeNodeArray(types: Type[], addInElementTypeFlag: boolean): TypeNode[] {
const result = [];
Debug.assert(context.InElementType === false, "should be unset at the beginning of the helper");
for (const type of types) {
context.InElementType = addInElementTypeFlag;
const typeNode = typeToTypeNodeHelper(type, context);
if (typeNode) {
result.push(typeNode);
}
}
Debug.assert(context.InElementType === false, "should be unset at the beginning of the helper");
return result;
}
@@ -2523,6 +2546,7 @@ namespace ts {
if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length) {
const signature = resolved.callSignatures[0];
shouldAddParenthesisAroundFunctionType(signature, context);
return <FunctionTypeNode>signatureToSignatureDeclarationHelper(signature, SyntaxKind.FunctionType, context);
}
if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length) {
@@ -2538,6 +2562,20 @@ namespace ts {
return createTypeLiteralNode(members);
}
function shouldAddParenthesisAroundFunctionType(callSignature: Signature, context: NodeBuilderContext) {
if (context.InElementType) {
return true;
}
else if (context.InFirstTypeArgument) {
// Add parenthesis around function type for the first type argument to avoid ambiguity
const typeParameters = callSignature.target && (context.flags & NodeBuilderFlags.WriteTypeArgumentsOfSignature) ?
callSignature.target.typeParameters : callSignature.typeParameters;
return typeParameters && typeParameters.length !== 0;
}
return false;
}
function createTypeQueryNodeFromSymbol(symbol: Symbol) {
const entityName = symbolToName(symbol, /*expectsIdentifier*/ false, context);
return createTypeQueryNode(entityName);
@@ -2546,12 +2584,14 @@ namespace ts {
function typeReferenceToTypeNode(type: TypeReference) {
const typeArguments: Type[] = type.typeArguments || emptyArray;
if (type.target === globalArrayType) {
context.InElementType = true;
const elementType = typeToTypeNodeHelper(typeArguments[0], context);
context.InElementType = false;
return createArrayTypeNode(elementType);
}
else if (type.target.objectFlags & ObjectFlags.Tuple) {
if (typeArguments.length > 0) {
const tupleConstituentNodes = mapToTypeNodeArray(typeArguments.slice(0, getTypeReferenceArity(type)));
const tupleConstituentNodes = mapToTypeNodeArray(typeArguments.slice(0, getTypeReferenceArity(type)), /*addInElementTypeFlag*/ false);
if (tupleConstituentNodes && tupleConstituentNodes.length > 0) {
return createTupleTypeNode(tupleConstituentNodes);
}
@@ -2566,6 +2606,7 @@ namespace ts {
let i = 0;
let qualifiedName: QualifiedName | undefined = undefined;
if (outerTypeParameters) {
let inFirstTypeArgument = true;
const length = outerTypeParameters.length;
while (i < length) {
// Find group of type arguments for type parameters with the same declaring container.
@@ -2577,6 +2618,7 @@ namespace ts {
// 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 (!rangeEquals(outerTypeParameters, typeArguments, start, i)) {
// inFirstTypeArgument???
const qualifiedNamePart = symbolToName(parent, /*expectsIdentifier*/ true, context);
if (!qualifiedName) {
qualifiedName = createQualifiedName(qualifiedNamePart, /*right*/ undefined);
@@ -2587,6 +2629,7 @@ namespace ts {
qualifiedName = createQualifiedName(qualifiedName, /*right*/ undefined);
}
}
inFirstTypeArgument = false;
}
}
let entityName: EntityName = undefined;
@@ -2600,7 +2643,7 @@ namespace ts {
entityName = nameIdentifier;
}
const typeParameterCount = (type.target.typeParameters || emptyArray).length;
const typeArgumentNodes = some(typeArguments) ? mapToTypeNodeArray(typeArguments.slice(i, typeParameterCount - i)) : undefined;
const typeArgumentNodes = some(typeArguments) ? mapToTypeNodeArray(typeArguments.slice(i, typeParameterCount - i), /*addInElementTypeFlag*/ false) : undefined;
return createTypeReferenceNode(entityName, typeArgumentNodes);
}
}
@@ -2695,7 +2738,7 @@ namespace ts {
const returnType = getReturnTypeOfSignature(signature);
returnTypeNode = returnType && typeToTypeNodeHelper(returnType, context);
}
if(context.flags & NodeBuilderFlags.suppressAnyReturnType) {
if(context.flags & NodeBuilderFlags.SuppressAnyReturnType) {
if(returnTypeNode && returnTypeNode.kind === SyntaxKind.AnyKeyword) {
returnTypeNode = undefined;
}
@@ -2733,6 +2776,8 @@ namespace ts {
return parameterNode;
}
// TODO: add meaning: SymbolFlags argument.
// TODO: add SymbolFormatFlags?? Yes to add outer type parameters. Defer UseOnlyExternalAliasing until a separate symbolbuilder PR.
function symbolToName(symbol: Symbol, expectsIdentifier: true, context: NodeBuilderContext): Identifier;
function symbolToName(symbol: Symbol, expectsIdentifier: false, context: NodeBuilderContext): EntityName;
function symbolToName(symbol: Symbol, expectsIdentifier: boolean, context: NodeBuilderContext): EntityName {
@@ -2826,6 +2871,7 @@ namespace ts {
}
}
}
function getNameOfSymbol(symbol: Symbol, context: NodeBuilderContext): string {
const declaration = firstOrUndefined(symbol.declarations);
if (declaration) {

View File

@@ -2947,8 +2947,9 @@ namespace ts {
// Precomputed Formats
Modifiers = SingleLine | SpaceBetweenSiblings,
HeritageClauses = SingleLine | SpaceBetweenSiblings,
SingleLineTypeLiteralMembers = SpaceBetweenBraces | SpaceBetweenSiblings | Indented, // MultiLine | Indented,
SingleLineTypeLiteralMembers = SingleLine | SpaceBetweenBraces | SpaceBetweenSiblings | Indented, // MultiLine | Indented,
MultiLineTypeLiteralMembers = MultiLine | Indented,
TupleTypeElements = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented,
UnionTypeConstituents = BarDelimited | SpaceBetweenSiblings | SingleLine,
IntersectionTypeConstituents = AmpersandDelimited | SpaceBetweenSiblings | SingleLine,

View File

@@ -393,6 +393,18 @@ namespace ts {
: node;
}
export function createParenthesizedTypeNode(type: TypeNode) {
const parenthesizedTypeNode = createSynthesizedNode(SyntaxKind.ParenthesizedType) as ParenthesizedTypeNode;
parenthesizedTypeNode.type = type;
return parenthesizedTypeNode;
}
export function updateParenthesizedTypeNode(node: ParenthesizedTypeNode, type: TypeNode) {
return node.type !== type
? updateNode(createParenthesizedTypeNode(type), node)
: node;
}
export function createTypeLiteralNode(members: TypeElement[]) {
const typeLiteralNode = createSynthesizedNode(SyntaxKind.TypeLiteral) as TypeLiteralNode;
typeLiteralNode.members = createNodeArray(members);

View File

@@ -2559,14 +2559,36 @@ namespace ts {
export enum NodeBuilderFlags {
None = 0,
allowThisInObjectLiteral = 1 << 0,
allowQualifedNameInPlaceOfIdentifier = 1 << 1,
allowTypeParameterInQualifiedName = 1 << 2,
allowAnonymousIdentifier = 1 << 3,
allowEmptyUnionOrIntersection = 1 << 4,
allowEmptyTuple = 1 << 5,
suppressAnyReturnType = 1 << 6,
ignoreErrors = allowThisInObjectLiteral | allowQualifedNameInPlaceOfIdentifier | allowTypeParameterInQualifiedName | allowAnonymousIdentifier | allowEmptyUnionOrIntersection | allowEmptyTuple
// Options
NoTruncation = 1 << 0, // Don't truncate result
// TODO: part of emit.
WriteArrayAsGenericType = 1 << 1, // Write Array<T> instead T[]
// TODO: part of emit.
UseTypeOfFunction = 1 << 2, // Write typeof instead of function type literal
// TODO: part of emit.
WriteArrowStyleSignature = 1 << 3, // Write arrow style signature
// TODO: turn it into a failing type reference?
WriteOwnNameForAnyLike = 1 << 4, // Write symbol's own name instead of 'any' for any like types (eg. unknown, __resolving__ etc)
// TODO
WriteTypeArgumentsOfSignature = 1 << 5, // Write the type arguments instead of type parameters of the signature
// TODO
UseFullyQualifiedType = 1 << 6, // Write out the fully qualified type name (eg. Module.Type, instead of Type)
// TODO
UseTypeAliasValue = 1 << 7, // Serialize the type instead of using type-alias. This is needed when we emit declaration file.
SuppressAnyReturnType = 1 << 8, // If the return type is any-like, don't offer a return type.
// TODO
AddUndefined = 1 << 9, // Add undefined to types of initialized, non-optional parameters
// Error handling
allowThisInObjectLiteral = 1 << 10,
allowQualifedNameInPlaceOfIdentifier = 1 << 11,
allowTypeParameterInQualifiedName = 1 << 12,
allowAnonymousIdentifier = 1 << 13,
allowEmptyUnionOrIntersection = 1 << 14,
allowEmptyTuple = 1 << 15,
ignoreErrors = allowThisInObjectLiteral | allowQualifedNameInPlaceOfIdentifier | allowTypeParameterInQualifiedName | allowAnonymousIdentifier | allowEmptyUnionOrIntersection | allowEmptyTuple,
}
export interface SymbolDisplayBuilder {

View File

@@ -322,7 +322,8 @@ namespace ts {
nodesVisitor((<UnionOrIntersectionTypeNode>node).types, visitor, isTypeNode));
case SyntaxKind.ParenthesizedType:
throw Debug.fail("not implemented.");
return updateParenthesizedTypeNode(<ParenthesizedTypeNode>node,
visitNode((<ParenthesizedTypeNode>node).type, visitor, isTypeNode));
case SyntaxKind.TypeOperator:
return updateTypeOperatorNode(<TypeOperatorNode>node, visitNode((<TypeOperatorNode>node).type, visitor, isTypeNode));

View File

@@ -130,7 +130,7 @@ namespace ts.codefix {
}
function signatureToMethodDeclaration(signature: Signature, enclosingDeclaration: Node, body?: Block) {
const signatureDeclaration = <MethodDeclaration>checker.signatureToSignatureDeclaration(signature, SyntaxKind.MethodDeclaration, enclosingDeclaration, NodeBuilderFlags.suppressAnyReturnType);
const signatureDeclaration = <MethodDeclaration>checker.signatureToSignatureDeclaration(signature, SyntaxKind.MethodDeclaration, enclosingDeclaration, NodeBuilderFlags.SuppressAnyReturnType);
if (signatureDeclaration) {
signatureDeclaration.decorators = undefined;
signatureDeclaration.modifiers = modifiers;