Ensure that emitter calls callbacks (#18284)

* Ensure that emitter calls calbacks

* Move new parameter to end of parameters

* Fix for ConditionalExpression

* Make suggested changes to emitter

* Fix parameter ordering

* Respond to minor comments

* Remove potentially expensive assertion

* More emitter cleanup
This commit is contained in:
Andy
2017-09-07 14:30:19 -07:00
committed by GitHub
parent 8c64937888
commit ed4e2e6e3b
20 changed files with 325 additions and 197 deletions

View File

@@ -406,6 +406,14 @@ namespace ts {
setWriter(/*output*/ undefined);
}
// TODO: Should this just be `emit`?
// See https://github.com/Microsoft/TypeScript/pull/18284#discussion_r137611034
function emitIfPresent(node: Node | undefined) {
if (node) {
emit(node);
}
}
function emit(node: Node) {
pipelineEmitWithNotification(EmitHint.Unspecified, node);
}
@@ -451,6 +459,7 @@ namespace ts {
case EmitHint.SourceFile: return pipelineEmitSourceFile(node);
case EmitHint.IdentifierName: return pipelineEmitIdentifierName(node);
case EmitHint.Expression: return pipelineEmitExpression(node);
case EmitHint.MappedTypeParameter: return emitMappedTypeParameter(cast(node, isTypeParameterDeclaration));
case EmitHint.Unspecified: return pipelineEmitUnspecified(node);
}
}
@@ -465,6 +474,12 @@ namespace ts {
emitIdentifier(<Identifier>node);
}
function emitMappedTypeParameter(node: TypeParameterDeclaration): void {
emit(node.name);
write(" in ");
emit(node.constraint);
}
function pipelineEmitUnspecified(node: Node): void {
const kind = node.kind;
@@ -898,9 +913,9 @@ namespace ts {
function emitParameter(node: ParameterDeclaration) {
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
writeIfPresent(node.dotDotDotToken, "...");
emitIfPresent(node.dotDotDotToken);
emit(node.name);
writeIfPresent(node.questionToken, "?");
emitIfPresent(node.questionToken);
emitWithPrefix(": ", node.type);
emitExpressionWithPrefix(" = ", node.initializer);
}
@@ -918,7 +933,7 @@ namespace ts {
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
emit(node.name);
writeIfPresent(node.questionToken, "?");
emitIfPresent(node.questionToken);
emitWithPrefix(": ", node.type);
write(";");
}
@@ -927,7 +942,7 @@ namespace ts {
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
emit(node.name);
writeIfPresent(node.questionToken, "?");
emitIfPresent(node.questionToken);
emitWithPrefix(": ", node.type);
emitExpressionWithPrefix(" = ", node.initializer);
write(";");
@@ -937,7 +952,7 @@ namespace ts {
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
emit(node.name);
writeIfPresent(node.questionToken, "?");
emitIfPresent(node.questionToken);
emitTypeParameters(node, node.typeParameters);
emitParameters(node, node.parameters);
emitWithPrefix(": ", node.type);
@@ -947,9 +962,9 @@ namespace ts {
function emitMethodDeclaration(node: MethodDeclaration) {
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
writeIfPresent(node.asteriskToken, "*");
emitIfPresent(node.asteriskToken);
emit(node.name);
writeIfPresent(node.questionToken, "?");
emitIfPresent(node.questionToken);
emitSignatureAndBody(node, emitSignatureHead);
}
@@ -1035,10 +1050,8 @@ namespace ts {
function emitTypeLiteral(node: TypeLiteralNode) {
write("{");
// If the literal is empty, do not add spaces between braces.
if (node.members.length > 0) {
emitList(node, node.members, getEmitFlags(node) & EmitFlags.SingleLine ? ListFormat.SingleLineTypeLiteralMembers : ListFormat.MultiLineTypeLiteralMembers);
}
const flags = getEmitFlags(node) & EmitFlags.SingleLine ? ListFormat.SingleLineTypeLiteralMembers : ListFormat.MultiLineTypeLiteralMembers;
emitList(node, node.members, flags | ListFormat.NoSpaceIfEmpty);
write("}");
}
@@ -1094,13 +1107,16 @@ namespace ts {
writeLine();
increaseIndent();
}
writeIfPresent(node.readonlyToken, "readonly ");
if (node.readonlyToken) {
emit(node.readonlyToken);
write(" ");
}
write("[");
emit(node.typeParameter.name);
write(" in ");
emit(node.typeParameter.constraint);
pipelineEmitWithNotification(EmitHint.MappedTypeParameter, node.typeParameter);
write("]");
writeIfPresent(node.questionToken, "?");
emitIfPresent(node.questionToken);
write(": ");
emit(node.type);
write(";");
@@ -1148,7 +1164,7 @@ namespace ts {
function emitBindingElement(node: BindingElement) {
emitWithSuffix(node.propertyName, ": ");
writeIfPresent(node.dotDotDotToken, "...");
emitIfPresent(node.dotDotDotToken);
emit(node.name);
emitExpressionWithPrefix(" = ", node.initializer);
}
@@ -1159,33 +1175,22 @@ namespace ts {
function emitArrayLiteralExpression(node: ArrayLiteralExpression) {
const elements = node.elements;
if (elements.length === 0) {
write("[]");
}
else {
const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None;
emitExpressionList(node, elements, ListFormat.ArrayLiteralExpressionElements | preferNewLine);
}
const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None;
emitExpressionList(node, elements, ListFormat.ArrayLiteralExpressionElements | preferNewLine);
}
function emitObjectLiteralExpression(node: ObjectLiteralExpression) {
const properties = node.properties;
if (properties.length === 0) {
write("{}");
const indentedFlag = getEmitFlags(node) & EmitFlags.Indented;
if (indentedFlag) {
increaseIndent();
}
else {
const indentedFlag = getEmitFlags(node) & EmitFlags.Indented;
if (indentedFlag) {
increaseIndent();
}
const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None;
const allowTrailingComma = currentSourceFile.languageVersion >= ScriptTarget.ES5 ? ListFormat.AllowTrailingComma : ListFormat.None;
emitList(node, properties, ListFormat.ObjectLiteralExpressionProperties | allowTrailingComma | preferNewLine);
const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None;
const allowTrailingComma = currentSourceFile.languageVersion >= ScriptTarget.ES5 ? ListFormat.AllowTrailingComma : ListFormat.None;
emitList(node, node.properties, ListFormat.ObjectLiteralExpressionProperties | allowTrailingComma | preferNewLine);
if (indentedFlag) {
decreaseIndent();
}
if (indentedFlag) {
decreaseIndent();
}
}
@@ -1286,7 +1291,8 @@ namespace ts {
emitTypeParameters(node, node.typeParameters);
emitParametersForArrow(node, node.parameters);
emitWithPrefix(": ", node.type);
write(" =>");
write(" ");
emit(node.equalsGreaterThanToken);
}
function emitDeleteExpression(node: DeleteExpression) {
@@ -1364,13 +1370,13 @@ namespace ts {
emitExpression(node.condition);
increaseIndentIf(indentBeforeQuestion, " ");
write("?");
emit(node.questionToken);
increaseIndentIf(indentAfterQuestion, " ");
emitExpression(node.whenTrue);
decreaseIndentIf(indentBeforeQuestion, indentAfterQuestion);
increaseIndentIf(indentBeforeColon, " ");
write(":");
emit(node.colonToken);
increaseIndentIf(indentAfterColon, " ");
emitExpression(node.whenFalse);
decreaseIndentIf(indentBeforeColon, indentAfterColon);
@@ -1382,7 +1388,8 @@ namespace ts {
}
function emitYieldExpression(node: YieldExpression) {
write(node.asteriskToken ? "yield*" : "yield");
write("yield");
emit(node.asteriskToken);
emitExpressionWithPrefix(" ", node.expression);
}
@@ -1662,7 +1669,9 @@ namespace ts {
function emitFunctionDeclarationOrExpression(node: FunctionDeclaration | FunctionExpression) {
emitDecorators(node, node.decorators);
emitModifiers(node, node.modifiers);
write(node.asteriskToken ? "function* " : "function ");
write("function");
emitIfPresent(node.asteriskToken);
write(" ");
emitIdentifierName(node.name);
emitSignatureAndBody(node, emitSignatureHead);
}
@@ -2068,9 +2077,7 @@ namespace ts {
function emitJsxExpression(node: JsxExpression) {
if (node.expression) {
write("{");
if (node.dotDotDotToken) {
write("...");
}
emitIfPresent(node.dotDotDotToken);
emitExpression(node.expression);
write("}");
}
@@ -2128,13 +2135,12 @@ namespace ts {
emitTrailingCommentsOfPosition(statements.pos);
}
let format = ListFormat.CaseOrDefaultClauseStatements;
if (emitAsSingleStatement) {
write(" ");
emit(statements[0]);
}
else {
emitList(parentNode, statements, ListFormat.CaseOrDefaultClauseStatements);
format &= ~(ListFormat.MultiLine | ListFormat.Indented);
}
emitList(parentNode, statements, format);
}
function emitHeritageClause(node: HeritageClause) {
@@ -2384,7 +2390,7 @@ namespace ts {
function emitParametersForArrow(parentNode: FunctionTypeNode | ArrowFunction, parameters: NodeArray<ParameterDeclaration>) {
if (canEmitSimpleArrowHead(parentNode, parameters)) {
emit(parameters[0]);
emitList(parentNode, parameters, ListFormat.Parameters & ~ListFormat.Parenthesis);
}
else {
emitParameters(parentNode, parameters);
@@ -2427,7 +2433,7 @@ namespace ts {
if (format & ListFormat.MultiLine) {
writeLine();
}
else if (format & ListFormat.SpaceBetweenBraces) {
else if (format & ListFormat.SpaceBetweenBraces && !(format & ListFormat.NoSpaceIfEmpty)) {
write(" ");
}
}
@@ -2568,12 +2574,6 @@ namespace ts {
}
}
function writeIfPresent(node: Node, text: string) {
if (node) {
write(text);
}
}
function writeToken(token: SyntaxKind, pos: number, contextNode?: Node) {
return onEmitSourceMapOfToken
? onEmitSourceMapOfToken(contextNode, token, pos, writeTokenText)
@@ -2584,7 +2584,7 @@ namespace ts {
if (onBeforeEmitToken) {
onBeforeEmitToken(node);
}
writeTokenText(node.kind);
write(tokenToString(node.kind));
if (onAfterEmitToken) {
onAfterEmitToken(node);
}
@@ -3107,6 +3107,9 @@ namespace ts {
NoTrailingNewLine = 1 << 16, // Do not emit a trailing NewLine for a MultiLine list.
NoInterveningComments = 1 << 17, // Do not emit comments between each node
NoSpaceIfEmpty = 1 << 18, // If the literal is empty, do not add spaces between braces.
SingleElement = 1 << 19,
// Precomputed Formats
Modifiers = SingleLine | SpaceBetweenSiblings | NoInterveningComments,
HeritageClauses = SingleLine | SpaceBetweenSiblings,
@@ -3118,7 +3121,7 @@ namespace ts {
IntersectionTypeConstituents = AmpersandDelimited | SpaceBetweenSiblings | SingleLine,
ObjectBindingPatternElements = SingleLine | AllowTrailingComma | SpaceBetweenBraces | CommaDelimited | SpaceBetweenSiblings,
ArrayBindingPatternElements = SingleLine | AllowTrailingComma | CommaDelimited | SpaceBetweenSiblings,
ObjectLiteralExpressionProperties = PreserveLines | CommaDelimited | SpaceBetweenSiblings | SpaceBetweenBraces | Indented | Braces,
ObjectLiteralExpressionProperties = PreserveLines | CommaDelimited | SpaceBetweenSiblings | SpaceBetweenBraces | Indented | Braces | NoSpaceIfEmpty,
ArrayLiteralExpressionElements = PreserveLines | CommaDelimited | SpaceBetweenSiblings | AllowTrailingComma | Indented | SquareBrackets,
CommaListElements = CommaDelimited | SpaceBetweenSiblings | SingleLine,
CallExpressionArguments = CommaDelimited | SpaceBetweenSiblings | SingleLine | Parenthesis,

View File

@@ -281,7 +281,7 @@ namespace ts {
|| node.questionToken !== questionToken
|| node.type !== type
|| node.initializer !== initializer
? updateNode(createParameter(decorators, modifiers, dotDotDotToken, name, node.questionToken, type, initializer), node)
? updateNode(createParameter(decorators, modifiers, dotDotDotToken, name, questionToken, type, initializer), node)
: node;
}
@@ -1016,19 +1016,49 @@ namespace ts {
return node;
}
/* @deprecated */ export function updateArrowFunction(
node: ArrowFunction,
modifiers: ReadonlyArray<Modifier> | undefined,
typeParameters: ReadonlyArray<TypeParameterDeclaration> | undefined,
parameters: ReadonlyArray<ParameterDeclaration>,
type: TypeNode | undefined,
body: ConciseBody): ArrowFunction;
export function updateArrowFunction(
node: ArrowFunction,
modifiers: ReadonlyArray<Modifier> | undefined,
typeParameters: ReadonlyArray<TypeParameterDeclaration> | undefined,
parameters: ReadonlyArray<ParameterDeclaration>,
type: TypeNode | undefined,
body: ConciseBody) {
equalsGreaterThanToken: Token<SyntaxKind.EqualsGreaterThanToken>,
body: ConciseBody): ArrowFunction;
export function updateArrowFunction(
node: ArrowFunction,
modifiers: ReadonlyArray<Modifier> | undefined,
typeParameters: ReadonlyArray<TypeParameterDeclaration> | undefined,
parameters: ReadonlyArray<ParameterDeclaration>,
type: TypeNode | undefined,
equalsGreaterThanTokenOrBody: Token<SyntaxKind.EqualsGreaterThanToken> | ConciseBody,
bodyOrUndefined?: ConciseBody,
): ArrowFunction {
let equalsGreaterThanToken: Token<SyntaxKind.EqualsGreaterThanToken>;
let body: ConciseBody;
if (bodyOrUndefined === undefined) {
equalsGreaterThanToken = node.equalsGreaterThanToken;
body = cast(equalsGreaterThanTokenOrBody, isConciseBody);
}
else {
equalsGreaterThanToken = cast(equalsGreaterThanTokenOrBody, (n): n is Token<SyntaxKind.EqualsGreaterThanToken> =>
n.kind === SyntaxKind.EqualsGreaterThanToken);
body = bodyOrUndefined;
}
return node.modifiers !== modifiers
|| node.typeParameters !== typeParameters
|| node.parameters !== parameters
|| node.type !== type
|| node.equalsGreaterThanToken !== equalsGreaterThanToken
|| node.body !== body
? updateNode(createArrowFunction(modifiers, typeParameters, parameters, type, node.equalsGreaterThanToken, body), node)
? updateNode(createArrowFunction(modifiers, typeParameters, parameters, type, equalsGreaterThanToken, body), node)
: node;
}
@@ -1135,11 +1165,31 @@ namespace ts {
return node;
}
export function updateConditional(node: ConditionalExpression, condition: Expression, whenTrue: Expression, whenFalse: Expression) {
/* @deprecated */ export function updateConditional(
node: ConditionalExpression,
condition: Expression,
whenTrue: Expression,
whenFalse: Expression): ConditionalExpression;
export function updateConditional(
node: ConditionalExpression,
condition: Expression,
questionToken: Token<SyntaxKind.QuestionToken>,
whenTrue: Expression,
colonToken: Token<SyntaxKind.ColonToken>,
whenFalse: Expression): ConditionalExpression;
export function updateConditional(node: ConditionalExpression, condition: Expression, ...args: any[]) {
if (args.length === 2) {
const [whenTrue, whenFalse] = args;
return updateConditional(node, condition, node.questionToken, whenTrue, node.colonToken, whenFalse);
}
Debug.assert(args.length === 4);
const [questionToken, whenTrue, colonToken, whenFalse] = args;
return node.condition !== condition
|| node.questionToken !== questionToken
|| node.whenTrue !== whenTrue
|| node.colonToken !== colonToken
|| node.whenFalse !== whenFalse
? updateNode(createConditional(condition, node.questionToken, whenTrue, node.colonToken, whenFalse), node)
? updateNode(createConditional(condition, questionToken, whenTrue, colonToken, whenFalse), node)
: node;
}

View File

@@ -197,9 +197,10 @@ namespace ts {
/*typeParameters*/ undefined,
visitParameterList(node.parameters, visitor, context),
/*type*/ undefined,
node.equalsGreaterThanToken,
getFunctionFlags(node) & FunctionFlags.Async
? transformAsyncFunctionBody(node)
: visitFunctionBody(node.body, visitor, context)
: visitFunctionBody(node.body, visitor, context),
);
}

View File

@@ -595,7 +595,8 @@ namespace ts {
/*typeParameters*/ undefined,
visitParameterList(node.parameters, visitor, context),
/*type*/ undefined,
transformFunctionBody(node)
node.equalsGreaterThanToken,
transformFunctionBody(node),
);
enclosingFunctionFlags = savedEnclosingFunctionFlags;
return updated;

View File

@@ -2309,7 +2309,8 @@ namespace ts {
/*typeParameters*/ undefined,
visitParameterList(node.parameters, visitor, context),
/*type*/ undefined,
visitFunctionBody(node.body, visitor, context)
node.equalsGreaterThanToken,
visitFunctionBody(node.body, visitor, context),
);
return updated;
}

View File

@@ -4299,10 +4299,11 @@ namespace ts {
}
export const enum EmitHint {
SourceFile, // Emitting a SourceFile
Expression, // Emitting an Expression
IdentifierName, // Emitting an IdentifierName
Unspecified, // Emitting an otherwise unspecified node
SourceFile, // Emitting a SourceFile
Expression, // Emitting an Expression
IdentifierName, // Emitting an IdentifierName
MappedTypeParameter, // Emitting a TypeParameterDeclaration inside of a MappedTypeNode
Unspecified, // Emitting an otherwise unspecified node
}
/* @internal */

View File

@@ -4728,8 +4728,7 @@ namespace ts {
/* @internal */
export function isNodeArray<T extends Node>(array: ReadonlyArray<T>): array is NodeArray<T> {
return array.hasOwnProperty("pos")
&& array.hasOwnProperty("end");
return array.hasOwnProperty("pos") && array.hasOwnProperty("end");
}
// Literals

View File

@@ -488,6 +488,7 @@ namespace ts {
nodesVisitor((<ArrowFunction>node).typeParameters, visitor, isTypeParameterDeclaration),
visitParameterList((<ArrowFunction>node).parameters, visitor, context, nodesVisitor),
visitNode((<ArrowFunction>node).type, visitor, isTypeNode),
visitNode((<ArrowFunction>node).equalsGreaterThanToken, visitor, isToken),
visitFunctionBody((<ArrowFunction>node).body, visitor, context));
case SyntaxKind.DeleteExpression:
@@ -523,7 +524,9 @@ namespace ts {
case SyntaxKind.ConditionalExpression:
return updateConditional(<ConditionalExpression>node,
visitNode((<ConditionalExpression>node).condition, visitor, isExpression),
visitNode((<ConditionalExpression>node).questionToken, visitor, isToken),
visitNode((<ConditionalExpression>node).whenTrue, visitor, isExpression),
visitNode((<ConditionalExpression>node).colonToken, visitor, isToken),
visitNode((<ConditionalExpression>node).whenFalse, visitor, isExpression));
case SyntaxKind.TemplateExpression:

View File

@@ -726,6 +726,7 @@ namespace ts.formatting {
parent: Node,
parentStartLine: number,
parentDynamicIndentation: DynamicIndentation): void {
Debug.assert(isNodeArray(nodes));
const listStartToken = getOpenTokenForList(parent, nodes);
const listEndToken = getCloseTokenForOpenToken(listStartToken);

View File

@@ -656,11 +656,13 @@ namespace ts.refactor.extractMethod {
const typeParametersAndDeclarations = arrayFrom(typeParameterUsages.values()).map(type => ({ type, declaration: getFirstDeclaration(type) }));
const sortedTypeParametersAndDeclarations = typeParametersAndDeclarations.sort(compareTypesByDeclarationOrder);
const typeParameters: ReadonlyArray<TypeParameterDeclaration> = sortedTypeParametersAndDeclarations.map(t => t.declaration as TypeParameterDeclaration);
const typeParameters: ReadonlyArray<TypeParameterDeclaration> | undefined = sortedTypeParametersAndDeclarations.length === 0
? undefined
: sortedTypeParametersAndDeclarations.map(t => t.declaration as TypeParameterDeclaration);
// Strictly speaking, we should check whether each name actually binds to the appropriate type
// parameter. In cases of shadowing, they may not.
const callTypeArguments: ReadonlyArray<TypeNode> | undefined = typeParameters.length > 0
const callTypeArguments: ReadonlyArray<TypeNode> | undefined = typeParameters !== undefined
? typeParameters.map(decl => createTypeReferenceNode(decl.name, /*typeArguments*/ undefined))
: undefined;

View File

@@ -5,19 +5,25 @@ namespace ts.textChanges {
* Currently for simplicity we store recovered positions on the node itself.
* It can be changed to side-table later if we decide that current design is too invasive.
*/
function getPos(n: TextRange) {
return (<any>n)["__pos"];
function getPos(n: TextRange): number {
const result = (<any>n)["__pos"];
Debug.assert(typeof result === "number");
return result;
}
function setPos(n: TextRange, pos: number) {
function setPos(n: TextRange, pos: number): void {
Debug.assert(typeof pos === "number");
(<any>n)["__pos"] = pos;
}
function getEnd(n: TextRange) {
return (<any>n)["__end"];
function getEnd(n: TextRange): number {
const result = (<any>n)["__end"];
Debug.assert(typeof result === "number");
return result;
}
function setEnd(n: TextRange, end: number) {
function setEnd(n: TextRange, end: number): void {
Debug.assert(typeof end === "number");
(<any>n)["__end"] = end;
}
@@ -582,7 +588,7 @@ namespace ts.textChanges {
readonly node: Node;
}
export function getNonformattedText(node: Node, sourceFile: SourceFile | undefined, newLine: NewLineKind): NonFormattedText {
function getNonformattedText(node: Node, sourceFile: SourceFile | undefined, newLine: NewLineKind): NonFormattedText {
const options = { newLine, target: sourceFile && sourceFile.languageVersion };
const writer = new Writer(getNewLineCharacter(options));
const printer = createPrinter(options, writer);
@@ -590,7 +596,7 @@ namespace ts.textChanges {
return { text: writer.getText(), node: assignPositionsToNode(node) };
}
export function applyFormatting(nonFormattedText: NonFormattedText, sourceFile: SourceFile, initialIndentation: number, delta: number, rulesProvider: formatting.RulesProvider) {
function applyFormatting(nonFormattedText: NonFormattedText, sourceFile: SourceFile, initialIndentation: number, delta: number, rulesProvider: formatting.RulesProvider) {
const lineMap = computeLineStarts(nonFormattedText.text);
const file: SourceFileLike = {
text: nonFormattedText.text,
@@ -616,14 +622,10 @@ namespace ts.textChanges {
function assignPositionsToNode(node: Node): Node {
const visited = visitEachChild(node, assignPositionsToNode, nullTransformationContext, assignPositionsToNodeArray, assignPositionsToNode);
// create proxy node for non synthesized nodes
const newNode = nodeIsSynthesized(visited)
? visited
: (Proxy.prototype = visited, new (<any>Proxy)());
const newNode = nodeIsSynthesized(visited) ? visited : Object.create(visited) as Node;
newNode.pos = getPos(node);
newNode.end = getEnd(node);
return newNode;
function Proxy() { }
}
function assignPositionsToNodeArray(nodes: NodeArray<any>, visitor: Visitor, test?: (node: Node) => boolean, start?: number, count?: number) {