From b48a51a80bdc1fc6802706ae1c1c08420d590a5b Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 12 Apr 2021 18:43:58 -0700 Subject: [PATCH] Remove preprinter, add parenthesizer callback to emit --- src/compiler/emitter.ts | 2674 +++++------------ src/compiler/factory/nodeFactory.ts | 2 +- src/compiler/factory/parenthesizerRules.ts | 31 +- src/compiler/transformer.ts | 34 +- src/compiler/types.ts | 3 + .../inlineJsxFactoryDeclarationsLocalTypes.js | 4 +- .../jsxCheckJsxNoTypeArgumentsAllowed.js | 4 +- 7 files changed, 861 insertions(+), 1891 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index f45c86e9ccb8d..a3007559f0dd9 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -860,9 +860,20 @@ namespace ts { return outputFiles; } + const enum PipelinePhase { + Notification, + Substitution, + Comments, + SourceMaps, + Emit, + } + export function createPrinter(printerOptions: PrinterOptions = {}, handlers: PrintHandlers = {}): Printer { const { hasGlobalName, + onEmitNode = noEmitNotification, + isEmitNotificationEnabled, + substituteNode = noEmitSubstitution, onBeforeEmitNode, onAfterEmitNode, onBeforeEmitNodeArray, @@ -913,8 +924,10 @@ namespace ts { let detachedCommentsInfo: { nodePos: number, detachedCommentEndPos: number }[] | undefined; let hasWrittenComment = false; let commentsDisabled = !!printerOptions.removeComments; + let lastSubstitution: Node | undefined; + let currentParenthesizerRule: ((node: Node) => Node) | undefined; const { enter: enterComment, exit: exitComment } = performance.createTimerIf(extendedDiagnostics, "commentTime", "beforeComment", "afterComment"); - const preprint = createPreprinter(handlers); + const parenthesizer = factory.parenthesizer; const emitBinaryExpression = createEmitBinaryExpression(); reset(); @@ -1137,7 +1150,7 @@ namespace ts { setSourceFile(sourceFile); } - emit(preprint(hint, node)); + pipelineEmit(hint, node, /*parenthesizerRule*/ undefined); } function setSourceFile(sourceFile: SourceFile | undefined) { @@ -1176,15 +1189,48 @@ namespace ts { return currentLineMap || (currentLineMap = getLineStarts(currentSourceFile!)); } - function emit(node: Node): void; - function emit(node: Node | undefined): void; - function emit(node: Node | undefined) { + function emit(node: Node, parenthesizerRule?: (node: Node) => Node): void; + function emit(node: Node | undefined, parenthesizerRule?: (node: Node) => Node): void; + function emit(node: Node | undefined, parenthesizerRule?: (node: Node) => Node) { if (node === undefined) return; const prevSourceFileTextKind = recordBundleFileInternalSectionStart(node); + pipelineEmit(EmitHint.Unspecified, node, parenthesizerRule); + recordBundleFileInternalSectionEnd(prevSourceFileTextKind); + } - emitWithContext(node, emitWorker); + function emitIdentifierName(node: Identifier): void; + function emitIdentifierName(node: Identifier | undefined): void; + function emitIdentifierName(node: Identifier | undefined) { + if (node === undefined) return; + pipelineEmit(EmitHint.IdentifierName, node, /*parenthesizerRule*/ undefined); + } - recordBundleFileInternalSectionEnd(prevSourceFileTextKind); + function emitExpression(node: Expression, parenthesizerRule?: (node: Expression) => Expression): void; + function emitExpression(node: Expression | undefined, parenthesizerRule?: (node: Expression) => Expression): void; + function emitExpression(node: Expression | undefined, parenthesizerRule?: (node: Expression) => Expression) { + if (node === undefined) return; + pipelineEmit(EmitHint.Expression, node, parenthesizerRule); + } + + function emitJsxAttributeValue(node: StringLiteral | JsxExpression): void { + pipelineEmit(isStringLiteral(node) ? EmitHint.JsxAttributeValue : EmitHint.Unspecified, node); + } + + function beforeEmitNode(node: Node) { + if (preserveSourceNewlines && (getEmitFlags(node) & EmitFlags.IgnoreSourceNewlines)) { + preserveSourceNewlines = false; + } + } + + function afterEmitNode(savedPreserveSourceNewlines: boolean | undefined) { + preserveSourceNewlines = savedPreserveSourceNewlines; + } + + function pipelineEmit(emitHint: EmitHint, node: Node, parenthesizerRule?: (node: Node) => Node) { + currentParenthesizerRule = parenthesizerRule; + const pipelinePhase = getPipelinePhase(PipelinePhase.Notification, emitHint, node); + pipelinePhase(emitHint, node); + currentParenthesizerRule = undefined; } function shouldEmitComments(node: Node) { @@ -1199,549 +1245,562 @@ namespace ts { !isUnparsedPrepend(node); } - function beforeEmitWithContext(node: Node, shouldEmitComments: boolean, shouldEmitSourceMaps: boolean) { - onBeforeEmitNode?.(node); - - const emitFlags = getEmitFlags(node); - const commentRange = shouldEmitComments ? getCommentRange(node) : undefined; - const sourceMapRange = shouldEmitSourceMaps ? getSourceMapRange(node) : undefined; - - if (preserveSourceNewlines && (emitFlags & EmitFlags.IgnoreSourceNewlines)) { - preserveSourceNewlines = false; - } - - // Emit leading comments - if (commentRange) { - emitLeadingCommentsOfNode(node, emitFlags, commentRange.pos, commentRange.end); - if (emitFlags & EmitFlags.NoNestedComments) { - commentsDisabled = true; - } - } - - // Emit leading sourcemap - if (sourceMapRange) { - if (isUnparsedNode(node)) { - Debug.assertIsDefined(node.parent, "UnparsedNodes must have parent pointers"); - const parsed = getParsedSourceMap(node.parent); - if (parsed && sourceMapGenerator) { - sourceMapGenerator.appendSourceMap( - writer.getLine(), - writer.getColumn(), - parsed, - node.parent.sourceMapPath!, - node.parent.getLineAndCharacterOfPosition(node.pos), - node.parent.getLineAndCharacterOfPosition(node.end) - ); + function getPipelinePhase(phase: PipelinePhase, emitHint: EmitHint, node: Node) { + switch (phase) { + case PipelinePhase.Notification: + if (onEmitNode !== noEmitNotification && (!isEmitNotificationEnabled || isEmitNotificationEnabled(node))) { + return pipelineEmitWithNotification; } - } - else { - const source = sourceMapRange.source || sourceMapSource; - if (node.kind !== SyntaxKind.NotEmittedStatement - && (emitFlags & EmitFlags.NoLeadingSourceMap) === 0 - && sourceMapRange.pos >= 0) { - emitSourcePos(sourceMapRange.source || sourceMapSource, skipSourceTrivia(source, sourceMapRange.pos)); + // falls through + case PipelinePhase.Substitution: + if (substituteNode !== noEmitSubstitution && (lastSubstitution = substituteNode(emitHint, node) || node) !== node) { + if (currentParenthesizerRule) { + lastSubstitution = currentParenthesizerRule(lastSubstitution); + } + return pipelineEmitWithSubstitution; } - if (emitFlags & EmitFlags.NoNestedSourceMaps) { - sourceMapsDisabled = true; + // falls through + case PipelinePhase.Comments: + if (shouldEmitComments(node)) { + return pipelineEmitWithComments; } - } + // falls through + case PipelinePhase.SourceMaps: + if (shouldEmitSourceMaps(node)) { + return pipelineEmitWithSourceMaps; + } + // falls through + case PipelinePhase.Emit: + return pipelineEmitWithHint; + default: + return Debug.assertNever(phase); } } - function afterEmitWithContext(node: Node, shouldEmitComments: boolean, shouldEmitSourceMaps: boolean, savedContainerPos: number, savedContainerEnd: number, savedDeclarationListContainerEnd: number, savedPreserveSourceNewlines: boolean | undefined) { - const emitFlags = getEmitFlags(node); - const commentRange = shouldEmitComments ? getCommentRange(node) : undefined; - const sourceMapRange = shouldEmitSourceMaps ? getSourceMapRange(node) : undefined; + function getNextPipelinePhase(currentPhase: PipelinePhase, emitHint: EmitHint, node: Node) { + return getPipelinePhase(currentPhase + 1, emitHint, node); + } - // Emit trailing sourcemap - if (sourceMapRange) { - if (!isUnparsedNode(node)) { - if (emitFlags & EmitFlags.NoNestedSourceMaps) { - sourceMapsDisabled = false; - } - if (node.kind !== SyntaxKind.NotEmittedStatement - && (emitFlags & EmitFlags.NoTrailingSourceMap) === 0 - && sourceMapRange.end >= 0) { - emitSourcePos(sourceMapRange.source || sourceMapSource, sourceMapRange.end); - } - } - } + function pipelineEmitWithNotification(hint: EmitHint, node: Node) { + const pipelinePhase = getNextPipelinePhase(PipelinePhase.Notification, hint, node); + onEmitNode(hint, node, pipelinePhase); + } - // Emit trailing comments - if (commentRange) { - if (emitFlags & EmitFlags.NoNestedComments) { - commentsDisabled = false; - } - emitTrailingCommentsOfNode(node, emitFlags, commentRange.pos, commentRange.end, savedContainerPos, savedContainerEnd, savedDeclarationListContainerEnd); + function pipelineEmitWithHint(hint: EmitHint, node: Node): void { + onBeforeEmitNode?.(node); + if (preserveSourceNewlines) { + const savedPreserveSourceNewlines = preserveSourceNewlines; + beforeEmitNode(node); + pipelineEmitWithHintWorker(hint, node); + afterEmitNode(savedPreserveSourceNewlines); + } + else { + pipelineEmitWithHintWorker(hint, node); } - - preserveSourceNewlines = savedPreserveSourceNewlines; onAfterEmitNode?.(node); + // clear the parenthesizer rule as we ascend + currentParenthesizerRule = undefined; } - function emitWithContext(node: T, emitCallback: (node: T) => void) { - const savedPreserveSourceNewlines = preserveSourceNewlines; - const savedContainerPos = containerPos; - const savedContainerEnd = containerEnd; - const savedDeclarationListContainerEnd = declarationListContainerEnd; - const emitComments = shouldEmitComments(node); - const emitSourceMaps = shouldEmitSourceMaps(node); - beforeEmitWithContext(node, emitComments, emitSourceMaps); - emitCallback(node); - afterEmitWithContext(node, emitComments, emitSourceMaps, savedContainerPos, savedContainerEnd, savedDeclarationListContainerEnd, savedPreserveSourceNewlines); - } + function pipelineEmitWithHintWorker(hint: EmitHint, node: Node): void { + if (hint === EmitHint.SourceFile) return emitSourceFile(cast(node, isSourceFile)); + if (hint === EmitHint.IdentifierName) return emitIdentifier(cast(node, isIdentifier)); + if (hint === EmitHint.JsxAttributeValue) return emitLiteral(cast(node, isStringLiteral), /*jsxAttributeEscape*/ true); + if (hint === EmitHint.MappedTypeParameter) return emitMappedTypeParameter(cast(node, isTypeParameterDeclaration)); + if (hint === EmitHint.EmbeddedStatement) { + Debug.assertNode(node, isEmptyStatement); + return emitEmptyStatement(/*isEmbeddedStatement*/ true); + } + if (hint === EmitHint.Unspecified) { + switch (node.kind) { + // Pseudo-literals + case SyntaxKind.TemplateHead: + case SyntaxKind.TemplateMiddle: + case SyntaxKind.TemplateTail: + return emitLiteral(node, /*jsxAttributeEscape*/ false); - function emitWorker(node: Node): void { - switch (node.kind) { - // Literals - case SyntaxKind.NumericLiteral: - case SyntaxKind.BigIntLiteral: - return emitNumericOrBigIntLiteral(node); - - case SyntaxKind.StringLiteral: - case SyntaxKind.RegularExpressionLiteral: - case SyntaxKind.NoSubstitutionTemplateLiteral: - return emitLiteral(node, /*jsxAttributeEscape*/ false); - - case SyntaxKind.JsxText: - return emitJsxText(node); - - // Pseudo-literals - case SyntaxKind.TemplateHead: - case SyntaxKind.TemplateMiddle: - case SyntaxKind.TemplateTail: - return emitLiteral(node, /*jsxAttributeEscape*/ false); - - // Identifiers and PrivateIdentifiers - case SyntaxKind.Identifier: - return emitIdentifier(node); - case SyntaxKind.PrivateIdentifier: - return emitPrivateIdentifier(node as PrivateIdentifier); + // Identifiers + case SyntaxKind.Identifier: + return emitIdentifier(node); - // Names - case SyntaxKind.QualifiedName: - return emitQualifiedName(node); - case SyntaxKind.ComputedPropertyName: - return emitComputedPropertyName(node); + // PrivateIdentifiers + case SyntaxKind.PrivateIdentifier: + return emitPrivateIdentifier(node as PrivateIdentifier); - // Signature elements - case SyntaxKind.TypeParameter: - return emitTypeParameter(node); - case SyntaxKind.Parameter: - return emitParameter(node); - case SyntaxKind.Decorator: - return emitDecorator(node); + // Parse tree nodes + // Names + case SyntaxKind.QualifiedName: + return emitQualifiedName(node); + case SyntaxKind.ComputedPropertyName: + return emitComputedPropertyName(node); - // Type members - case SyntaxKind.PropertySignature: - return emitPropertySignature(node); - case SyntaxKind.PropertyDeclaration: - return emitPropertyDeclaration(node); - case SyntaxKind.MethodSignature: - return emitMethodSignature(node); - case SyntaxKind.MethodDeclaration: - return emitMethodDeclaration(node); - case SyntaxKind.Constructor: - return emitConstructor(node); - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - return emitAccessorDeclaration(node); - case SyntaxKind.CallSignature: - return emitCallSignature(node); - case SyntaxKind.ConstructSignature: - return emitConstructSignature(node); - case SyntaxKind.IndexSignature: - return emitIndexSignature(node); - - // Types - case SyntaxKind.TypePredicate: - return emitTypePredicate(node); - case SyntaxKind.TypeReference: - return emitTypeReference(node); - case SyntaxKind.FunctionType: - return emitFunctionType(node); - case SyntaxKind.ConstructorType: - return emitConstructorType(node); - case SyntaxKind.TypeQuery: - return emitTypeQuery(node); - case SyntaxKind.TypeLiteral: - return emitTypeLiteral(node); - case SyntaxKind.ArrayType: - return emitArrayType(node); - case SyntaxKind.TupleType: - return emitTupleType(node); - case SyntaxKind.OptionalType: - return emitOptionalType(node); - // SyntaxKind.RestType is handled below - case SyntaxKind.UnionType: - return emitUnionType(node); - case SyntaxKind.IntersectionType: - return emitIntersectionType(node); - case SyntaxKind.ConditionalType: - return emitConditionalType(node); - case SyntaxKind.InferType: - return emitInferType(node); - case SyntaxKind.ParenthesizedType: - return emitParenthesizedType(node); - case SyntaxKind.ThisType: - return emitThisType(); - case SyntaxKind.TypeOperator: - return emitTypeOperator(node); - case SyntaxKind.IndexedAccessType: - return emitIndexedAccessType(node); - case SyntaxKind.MappedType: - return emitMappedType(node); - case SyntaxKind.LiteralType: - return emitLiteralType(node); - case SyntaxKind.NamedTupleMember: - return emitNamedTupleMember(node as NamedTupleMember); - case SyntaxKind.TemplateLiteralType: - return emitTemplateType(node); - case SyntaxKind.TemplateLiteralTypeSpan: - return emitTemplateTypeSpan(node); - case SyntaxKind.ImportType: - return emitImportTypeNode(node); - - // Binding patterns - case SyntaxKind.ObjectBindingPattern: - return emitObjectBindingPattern(node); - case SyntaxKind.ArrayBindingPattern: - return emitArrayBindingPattern(node); - case SyntaxKind.BindingElement: - return emitBindingElement(node); - - // Expressions - case SyntaxKind.ArrayLiteralExpression: - return emitArrayLiteralExpression(node); - case SyntaxKind.ObjectLiteralExpression: - return emitObjectLiteralExpression(node); - case SyntaxKind.PropertyAccessExpression: - return emitPropertyAccessExpression(node); - case SyntaxKind.ElementAccessExpression: - return emitElementAccessExpression(node); - case SyntaxKind.CallExpression: - return emitCallExpression(node); - case SyntaxKind.NewExpression: - return emitNewExpression(node); - case SyntaxKind.TaggedTemplateExpression: - return emitTaggedTemplateExpression(node); - case SyntaxKind.TypeAssertionExpression: - return emitTypeAssertionExpression(node); - case SyntaxKind.ParenthesizedExpression: - return emitParenthesizedExpression(node); - case SyntaxKind.FunctionExpression: - return emitFunctionExpression(node); - case SyntaxKind.ArrowFunction: - return emitArrowFunction(node); - case SyntaxKind.DeleteExpression: - return emitDeleteExpression(node); - case SyntaxKind.TypeOfExpression: - return emitTypeOfExpression(node); - case SyntaxKind.VoidExpression: - return emitVoidExpression(node); - case SyntaxKind.AwaitExpression: - return emitAwaitExpression(node); - case SyntaxKind.PrefixUnaryExpression: - return emitPrefixUnaryExpression(node); - case SyntaxKind.PostfixUnaryExpression: - return emitPostfixUnaryExpression(node); - case SyntaxKind.BinaryExpression: - return emitBinaryExpression(node); - case SyntaxKind.ConditionalExpression: - return emitConditionalExpression(node); - case SyntaxKind.TemplateExpression: - return emitTemplateExpression(node); - case SyntaxKind.YieldExpression: - return emitYieldExpression(node); - case SyntaxKind.SpreadElement: - return emitSpreadExpression(node); - case SyntaxKind.ClassExpression: - return emitClassExpression(node); - case SyntaxKind.OmittedExpression: - return; - case SyntaxKind.ExpressionWithTypeArguments: - return emitExpressionWithTypeArguments(node); - case SyntaxKind.AsExpression: - return emitAsExpression(node); - case SyntaxKind.NonNullExpression: - return emitNonNullExpression(node); - case SyntaxKind.MetaProperty: - return emitMetaProperty(node); - case SyntaxKind.SyntheticExpression: - Debug.fail("SyntheticExpression should never be printed."); - break; + // Signature elements + case SyntaxKind.TypeParameter: + return emitTypeParameter(node); + case SyntaxKind.Parameter: + return emitParameter(node); + case SyntaxKind.Decorator: + return emitDecorator(node); - // Misc - case SyntaxKind.TemplateSpan: - return emitTemplateSpan(node); - case SyntaxKind.SemicolonClassElement: - return emitSemicolonClassElement(); + // Type members + case SyntaxKind.PropertySignature: + return emitPropertySignature(node); + case SyntaxKind.PropertyDeclaration: + return emitPropertyDeclaration(node); + case SyntaxKind.MethodSignature: + return emitMethodSignature(node); + case SyntaxKind.MethodDeclaration: + return emitMethodDeclaration(node); + case SyntaxKind.Constructor: + return emitConstructor(node); + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + return emitAccessorDeclaration(node); + case SyntaxKind.CallSignature: + return emitCallSignature(node); + case SyntaxKind.ConstructSignature: + return emitConstructSignature(node); + case SyntaxKind.IndexSignature: + return emitIndexSignature(node); - // Element - case SyntaxKind.Block: - return emitBlock(node); - case SyntaxKind.EmptyStatement: - return emitEmptyStatement(/*isEmbeddedStatement*/ false); - case SyntaxKind.VariableStatement: - return emitVariableStatement(node); - case SyntaxKind.ExpressionStatement: - return emitExpressionStatement(node); - case SyntaxKind.IfStatement: - return emitIfStatement(node); - case SyntaxKind.DoStatement: - return emitDoStatement(node); - case SyntaxKind.WhileStatement: - return emitWhileStatement(node); - case SyntaxKind.ForStatement: - return emitForStatement(node); - case SyntaxKind.ForInStatement: - return emitForInStatement(node); - case SyntaxKind.ForOfStatement: - return emitForOfStatement(node); - case SyntaxKind.ContinueStatement: - return emitContinueStatement(node); - case SyntaxKind.BreakStatement: - return emitBreakStatement(node); - case SyntaxKind.ReturnStatement: - return emitReturnStatement(node); - case SyntaxKind.WithStatement: - return emitWithStatement(node); - case SyntaxKind.SwitchStatement: - return emitSwitchStatement(node); - case SyntaxKind.LabeledStatement: - return emitLabeledStatement(node); - case SyntaxKind.ThrowStatement: - return emitThrowStatement(node); - case SyntaxKind.TryStatement: - return emitTryStatement(node); - case SyntaxKind.DebuggerStatement: - return emitDebuggerStatement(node); + // Types + case SyntaxKind.TypePredicate: + return emitTypePredicate(node); + case SyntaxKind.TypeReference: + return emitTypeReference(node); + case SyntaxKind.FunctionType: + return emitFunctionType(node); + case SyntaxKind.ConstructorType: + return emitConstructorType(node); + case SyntaxKind.TypeQuery: + return emitTypeQuery(node); + case SyntaxKind.TypeLiteral: + return emitTypeLiteral(node); + case SyntaxKind.ArrayType: + return emitArrayType(node); + case SyntaxKind.TupleType: + return emitTupleType(node); + case SyntaxKind.OptionalType: + return emitOptionalType(node); + // SyntaxKind.RestType is handled below + case SyntaxKind.UnionType: + return emitUnionType(node); + case SyntaxKind.IntersectionType: + return emitIntersectionType(node); + case SyntaxKind.ConditionalType: + return emitConditionalType(node); + case SyntaxKind.InferType: + return emitInferType(node); + case SyntaxKind.ParenthesizedType: + return emitParenthesizedType(node); + case SyntaxKind.ExpressionWithTypeArguments: + return emitExpressionWithTypeArguments(node); + case SyntaxKind.ThisType: + return emitThisType(); + case SyntaxKind.TypeOperator: + return emitTypeOperator(node); + case SyntaxKind.IndexedAccessType: + return emitIndexedAccessType(node); + case SyntaxKind.MappedType: + return emitMappedType(node); + case SyntaxKind.LiteralType: + return emitLiteralType(node); + case SyntaxKind.NamedTupleMember: + return emitNamedTupleMember(node as NamedTupleMember); + case SyntaxKind.TemplateLiteralType: + return emitTemplateType(node); + case SyntaxKind.TemplateLiteralTypeSpan: + return emitTemplateTypeSpan(node); + case SyntaxKind.ImportType: + return emitImportTypeNode(node); - // Declarations - case SyntaxKind.VariableDeclaration: - return emitVariableDeclaration(node); - case SyntaxKind.VariableDeclarationList: - return emitVariableDeclarationList(node); - case SyntaxKind.FunctionDeclaration: - return emitFunctionDeclaration(node); - case SyntaxKind.ClassDeclaration: - return emitClassDeclaration(node); - case SyntaxKind.InterfaceDeclaration: - return emitInterfaceDeclaration(node); - case SyntaxKind.TypeAliasDeclaration: - return emitTypeAliasDeclaration(node); - case SyntaxKind.EnumDeclaration: - return emitEnumDeclaration(node); - case SyntaxKind.ModuleDeclaration: - return emitModuleDeclaration(node); - case SyntaxKind.ModuleBlock: - return emitModuleBlock(node); - case SyntaxKind.CaseBlock: - return emitCaseBlock(node); - case SyntaxKind.NamespaceExportDeclaration: - return emitNamespaceExportDeclaration(node); - case SyntaxKind.ImportEqualsDeclaration: - return emitImportEqualsDeclaration(node); - case SyntaxKind.ImportDeclaration: - return emitImportDeclaration(node); - case SyntaxKind.ImportClause: - return emitImportClause(node); - case SyntaxKind.NamespaceImport: - return emitNamespaceImport(node); - case SyntaxKind.NamedImports: - return emitNamedImports(node); - case SyntaxKind.ImportSpecifier: - return emitImportSpecifier(node); - case SyntaxKind.ExportAssignment: - return emitExportAssignment(node); - case SyntaxKind.ExportDeclaration: - return emitExportDeclaration(node); - case SyntaxKind.NamedExports: - return emitNamedExports(node); - case SyntaxKind.NamespaceExport: - return emitNamespaceExport(node); - case SyntaxKind.ExportSpecifier: - return emitExportSpecifier(node); - case SyntaxKind.MissingDeclaration: - return; + // Binding patterns + case SyntaxKind.ObjectBindingPattern: + return emitObjectBindingPattern(node); + case SyntaxKind.ArrayBindingPattern: + return emitArrayBindingPattern(node); + case SyntaxKind.BindingElement: + return emitBindingElement(node); - // Module references - case SyntaxKind.ExternalModuleReference: - return emitExternalModuleReference(node); - - // JSX - case SyntaxKind.JsxElement: - return emitJsxElement(node); - case SyntaxKind.JsxSelfClosingElement: - return emitJsxSelfClosingElement(node); - case SyntaxKind.JsxFragment: - return emitJsxFragment(node); - case SyntaxKind.JsxOpeningElement: - case SyntaxKind.JsxOpeningFragment: - return emitJsxOpeningElementOrFragment(node); - case SyntaxKind.JsxClosingElement: - case SyntaxKind.JsxClosingFragment: - return emitJsxClosingElementOrFragment(node); - case SyntaxKind.JsxAttribute: - return emitJsxAttribute(node); - case SyntaxKind.JsxAttributes: - return emitJsxAttributes(node); - case SyntaxKind.JsxSpreadAttribute: - return emitJsxSpreadAttribute(node); - case SyntaxKind.JsxExpression: - return emitJsxExpression(node); - - // Clauses - case SyntaxKind.CaseClause: - return emitCaseClause(node); - case SyntaxKind.DefaultClause: - return emitDefaultClause(node); - case SyntaxKind.HeritageClause: - return emitHeritageClause(node); - case SyntaxKind.CatchClause: - return emitCatchClause(node); + // Misc + case SyntaxKind.TemplateSpan: + return emitTemplateSpan(node); + case SyntaxKind.SemicolonClassElement: + return emitSemicolonClassElement(); - // Property assignments - case SyntaxKind.PropertyAssignment: - return emitPropertyAssignment(node); - case SyntaxKind.ShorthandPropertyAssignment: - return emitShorthandPropertyAssignment(node); - case SyntaxKind.SpreadAssignment: - return emitSpreadAssignment(node as SpreadAssignment); - - // Enum - case SyntaxKind.EnumMember: - return emitEnumMember(node); - - // Unparsed - case SyntaxKind.UnparsedPrologue: - return writeUnparsedNode(node); - case SyntaxKind.UnparsedSource: - case SyntaxKind.UnparsedPrepend: - return emitUnparsedSourceOrPrepend(node); - case SyntaxKind.UnparsedText: - case SyntaxKind.UnparsedInternalText: - return emitUnparsedTextLike(node); - case SyntaxKind.UnparsedSyntheticReference: - return emitUnparsedSyntheticReference(node); - - // Top-level nodes - case SyntaxKind.SourceFile: - return emitSourceFile(node); - case SyntaxKind.Bundle: - Debug.fail("Bundles should be printed using printBundle"); - break; - // SyntaxKind.UnparsedSource (handled above) - case SyntaxKind.InputFiles: - Debug.fail("InputFiles should not be printed"); - break; + // Statements + case SyntaxKind.Block: + return emitBlock(node); + case SyntaxKind.VariableStatement: + return emitVariableStatement(node); + case SyntaxKind.EmptyStatement: + return emitEmptyStatement(/*isEmbeddedStatement*/ false); + case SyntaxKind.ExpressionStatement: + return emitExpressionStatement(node); + case SyntaxKind.IfStatement: + return emitIfStatement(node); + case SyntaxKind.DoStatement: + return emitDoStatement(node); + case SyntaxKind.WhileStatement: + return emitWhileStatement(node); + case SyntaxKind.ForStatement: + return emitForStatement(node); + case SyntaxKind.ForInStatement: + return emitForInStatement(node); + case SyntaxKind.ForOfStatement: + return emitForOfStatement(node); + case SyntaxKind.ContinueStatement: + return emitContinueStatement(node); + case SyntaxKind.BreakStatement: + return emitBreakStatement(node); + case SyntaxKind.ReturnStatement: + return emitReturnStatement(node); + case SyntaxKind.WithStatement: + return emitWithStatement(node); + case SyntaxKind.SwitchStatement: + return emitSwitchStatement(node); + case SyntaxKind.LabeledStatement: + return emitLabeledStatement(node); + case SyntaxKind.ThrowStatement: + return emitThrowStatement(node); + case SyntaxKind.TryStatement: + return emitTryStatement(node); + case SyntaxKind.DebuggerStatement: + return emitDebuggerStatement(node); - // JSDoc nodes (only used in codefixes currently) - case SyntaxKind.JSDocTypeExpression: - return emitJSDocTypeExpression(node as JSDocTypeExpression); - case SyntaxKind.JSDocNameReference: - return emitJSDocNameReference(node as JSDocNameReference); - case SyntaxKind.JSDocAllType: - return writePunctuation("*"); - case SyntaxKind.JSDocUnknownType: - return writePunctuation("?"); - case SyntaxKind.JSDocNullableType: - return emitJSDocNullableType(node as JSDocNullableType); - case SyntaxKind.JSDocNonNullableType: - return emitJSDocNonNullableType(node as JSDocNonNullableType); - case SyntaxKind.JSDocOptionalType: - return emitJSDocOptionalType(node as JSDocOptionalType); - case SyntaxKind.JSDocFunctionType: - return emitJSDocFunctionType(node as JSDocFunctionType); - case SyntaxKind.RestType: - case SyntaxKind.JSDocVariadicType: - return emitRestOrJSDocVariadicType(node as RestTypeNode | JSDocVariadicType); - // SyntaxKind.JSDocNamepathType (missing) - case SyntaxKind.JSDocComment: - return emitJSDoc(node as JSDoc); - case SyntaxKind.JSDocTypeLiteral: - return emitJSDocTypeLiteral(node as JSDocTypeLiteral); - case SyntaxKind.JSDocSignature: - return emitJSDocSignature(node as JSDocSignature); - case SyntaxKind.JSDocTag: - case SyntaxKind.JSDocClassTag: - return emitJSDocSimpleTag(node as JSDocTag); - case SyntaxKind.JSDocAugmentsTag: - case SyntaxKind.JSDocImplementsTag: - return emitJSDocHeritageTag(node as JSDocImplementsTag | JSDocAugmentsTag); - // SyntaxKind.JSDocAuthorTag (missing) - // SyntaxKind.JSDocDeprecatedTag (missing) - // SyntaxKind.JSDocClassTag (see JSDocTag, above) - // SyntaxKind.JSDocPublicTag (missing) - // SyntaxKind.JSDocPrivateTag (missing) - // SyntaxKind.JSDocProtectedTag (missing) - // SyntaxKind.JSDocReadonlyTag (missing) - case SyntaxKind.JSDocCallbackTag: - return emitJSDocCallbackTag(node as JSDocCallbackTag); - // SyntaxKind.JSDocEnumTag (see below) - case SyntaxKind.JSDocParameterTag: - case SyntaxKind.JSDocPropertyTag: - return emitJSDocPropertyLikeTag(node as JSDocPropertyLikeTag); - case SyntaxKind.JSDocEnumTag: - case SyntaxKind.JSDocReturnTag: - case SyntaxKind.JSDocThisTag: - case SyntaxKind.JSDocTypeTag: - return emitJSDocSimpleTypedTag(node as JSDocTypeTag); - case SyntaxKind.JSDocTemplateTag: - return emitJSDocTemplateTag(node as JSDocTemplateTag); - case SyntaxKind.JSDocTypedefTag: - return emitJSDocTypedefTag(node as JSDocTypedefTag); - case SyntaxKind.JSDocSeeTag: - return emitJSDocSeeTag(node as JSDocSeeTag); - // SyntaxKind.JSDocPropertyTag (see JSDocParameterTag, above) - - // Synthesized list - case SyntaxKind.SyntaxList: - Debug.fail("SyntaxList should not be printed"); - break; + // Declarations + case SyntaxKind.VariableDeclaration: + return emitVariableDeclaration(node); + case SyntaxKind.VariableDeclarationList: + return emitVariableDeclarationList(node); + case SyntaxKind.FunctionDeclaration: + return emitFunctionDeclaration(node); + case SyntaxKind.ClassDeclaration: + return emitClassDeclaration(node); + case SyntaxKind.InterfaceDeclaration: + return emitInterfaceDeclaration(node); + case SyntaxKind.TypeAliasDeclaration: + return emitTypeAliasDeclaration(node); + case SyntaxKind.EnumDeclaration: + return emitEnumDeclaration(node); + case SyntaxKind.ModuleDeclaration: + return emitModuleDeclaration(node); + case SyntaxKind.ModuleBlock: + return emitModuleBlock(node); + case SyntaxKind.CaseBlock: + return emitCaseBlock(node); + case SyntaxKind.NamespaceExportDeclaration: + return emitNamespaceExportDeclaration(node); + case SyntaxKind.ImportEqualsDeclaration: + return emitImportEqualsDeclaration(node); + case SyntaxKind.ImportDeclaration: + return emitImportDeclaration(node); + case SyntaxKind.ImportClause: + return emitImportClause(node); + case SyntaxKind.NamespaceImport: + return emitNamespaceImport(node); + case SyntaxKind.NamespaceExport: + return emitNamespaceExport(node); + case SyntaxKind.NamedImports: + return emitNamedImports(node); + case SyntaxKind.ImportSpecifier: + return emitImportSpecifier(node); + case SyntaxKind.ExportAssignment: + return emitExportAssignment(node); + case SyntaxKind.ExportDeclaration: + return emitExportDeclaration(node); + case SyntaxKind.NamedExports: + return emitNamedExports(node); + case SyntaxKind.ExportSpecifier: + return emitExportSpecifier(node); + case SyntaxKind.MissingDeclaration: + return; - // Transformation nodes - case SyntaxKind.NotEmittedStatement: - break; - case SyntaxKind.PartiallyEmittedExpression: - return emitPartiallyEmittedExpression(node); - case SyntaxKind.CommaListExpression: - return emitCommaList(node); - case SyntaxKind.MergeDeclarationMarker: - case SyntaxKind.EndOfDeclarationMarker: - break; - case SyntaxKind.SyntheticReferenceExpression: - Debug.fail("SyntheticReferenceExpression should not be printed"); - } - if (isKeyword(node.kind)) return writeTokenNode(node, writeKeyword); - if (isTokenKind(node.kind)) return writeTokenNode(node, writePunctuation); - } + // Module references + case SyntaxKind.ExternalModuleReference: + return emitExternalModuleReference(node); - function emitMappedTypeParameter(node: TypeParameterDeclaration): void { - emit(node.name); - writeSpace(); - writeKeyword("in"); - writeSpace(); - emit(node.constraint); - } + // JSX (non-expression) + case SyntaxKind.JsxText: + return emitJsxText(node); + case SyntaxKind.JsxOpeningElement: + case SyntaxKind.JsxOpeningFragment: + return emitJsxOpeningElementOrFragment(node); + case SyntaxKind.JsxClosingElement: + case SyntaxKind.JsxClosingFragment: + return emitJsxClosingElementOrFragment(node); + case SyntaxKind.JsxAttribute: + return emitJsxAttribute(node); + case SyntaxKind.JsxAttributes: + return emitJsxAttributes(node); + case SyntaxKind.JsxSpreadAttribute: + return emitJsxSpreadAttribute(node); + case SyntaxKind.JsxExpression: + return emitJsxExpression(node); - function getHelpersFromBundledSourceFiles(bundle: Bundle): string[] | undefined { - let result: string[] | undefined; - if (moduleKind === ModuleKind.None || printerOptions.noEmitHelpers) { - return undefined; - } - const bundledHelpers = new Map(); - for (const sourceFile of bundle.sourceFiles) { - const shouldSkip = getExternalHelpersModuleName(sourceFile) !== undefined; - const helpers = getSortedEmitHelpers(sourceFile); - if (!helpers) continue; - for (const helper of helpers) { - if (!helper.scoped && !shouldSkip && !bundledHelpers.get(helper.name)) { - bundledHelpers.set(helper.name, true); - (result || (result = [])).push(helper.name); - } - } - } + // Clauses + case SyntaxKind.CaseClause: + return emitCaseClause(node); + case SyntaxKind.DefaultClause: + return emitDefaultClause(node); + case SyntaxKind.HeritageClause: + return emitHeritageClause(node); + case SyntaxKind.CatchClause: + return emitCatchClause(node); + + // Property assignments + case SyntaxKind.PropertyAssignment: + return emitPropertyAssignment(node); + case SyntaxKind.ShorthandPropertyAssignment: + return emitShorthandPropertyAssignment(node); + case SyntaxKind.SpreadAssignment: + return emitSpreadAssignment(node as SpreadAssignment); + + // Enum + case SyntaxKind.EnumMember: + return emitEnumMember(node); + + // Unparsed + case SyntaxKind.UnparsedPrologue: + return writeUnparsedNode(node); + case SyntaxKind.UnparsedSource: + case SyntaxKind.UnparsedPrepend: + return emitUnparsedSourceOrPrepend(node); + case SyntaxKind.UnparsedText: + case SyntaxKind.UnparsedInternalText: + return emitUnparsedTextLike(node); + case SyntaxKind.UnparsedSyntheticReference: + return emitUnparsedSyntheticReference(node); + + // Top-level nodes + case SyntaxKind.SourceFile: + return emitSourceFile(node); + case SyntaxKind.Bundle: + return Debug.fail("Bundles should be printed using printBundle"); + // SyntaxKind.UnparsedSource (handled above) + case SyntaxKind.InputFiles: + return Debug.fail("InputFiles should not be printed"); + + // JSDoc nodes (only used in codefixes currently) + case SyntaxKind.JSDocTypeExpression: + return emitJSDocTypeExpression(node as JSDocTypeExpression); + case SyntaxKind.JSDocNameReference: + return emitJSDocNameReference(node as JSDocNameReference); + case SyntaxKind.JSDocAllType: + return writePunctuation("*"); + case SyntaxKind.JSDocUnknownType: + return writePunctuation("?"); + case SyntaxKind.JSDocNullableType: + return emitJSDocNullableType(node as JSDocNullableType); + case SyntaxKind.JSDocNonNullableType: + return emitJSDocNonNullableType(node as JSDocNonNullableType); + case SyntaxKind.JSDocOptionalType: + return emitJSDocOptionalType(node as JSDocOptionalType); + case SyntaxKind.JSDocFunctionType: + return emitJSDocFunctionType(node as JSDocFunctionType); + case SyntaxKind.RestType: + case SyntaxKind.JSDocVariadicType: + return emitRestOrJSDocVariadicType(node as RestTypeNode | JSDocVariadicType); + case SyntaxKind.JSDocNamepathType: + return; + case SyntaxKind.JSDocComment: + return emitJSDoc(node as JSDoc); + case SyntaxKind.JSDocTypeLiteral: + return emitJSDocTypeLiteral(node as JSDocTypeLiteral); + case SyntaxKind.JSDocSignature: + return emitJSDocSignature(node as JSDocSignature); + case SyntaxKind.JSDocTag: + case SyntaxKind.JSDocClassTag: + return emitJSDocSimpleTag(node as JSDocTag); + case SyntaxKind.JSDocAugmentsTag: + case SyntaxKind.JSDocImplementsTag: + return emitJSDocHeritageTag(node as JSDocImplementsTag | JSDocAugmentsTag); + case SyntaxKind.JSDocAuthorTag: + case SyntaxKind.JSDocDeprecatedTag: + return; + // SyntaxKind.JSDocClassTag (see JSDocTag, above) + case SyntaxKind.JSDocPublicTag: + case SyntaxKind.JSDocPrivateTag: + case SyntaxKind.JSDocProtectedTag: + case SyntaxKind.JSDocReadonlyTag: + case SyntaxKind.JSDocOverrideTag: + return; + case SyntaxKind.JSDocCallbackTag: + return emitJSDocCallbackTag(node as JSDocCallbackTag); + // SyntaxKind.JSDocEnumTag (see below) + case SyntaxKind.JSDocParameterTag: + case SyntaxKind.JSDocPropertyTag: + return emitJSDocPropertyLikeTag(node as JSDocPropertyLikeTag); + case SyntaxKind.JSDocEnumTag: + case SyntaxKind.JSDocReturnTag: + case SyntaxKind.JSDocThisTag: + case SyntaxKind.JSDocTypeTag: + return emitJSDocSimpleTypedTag(node as JSDocTypeTag); + case SyntaxKind.JSDocTemplateTag: + return emitJSDocTemplateTag(node as JSDocTemplateTag); + case SyntaxKind.JSDocTypedefTag: + return emitJSDocTypedefTag(node as JSDocTypedefTag); + case SyntaxKind.JSDocSeeTag: + return emitJSDocSeeTag(node as JSDocSeeTag); + // SyntaxKind.JSDocPropertyTag (see JSDocParameterTag, above) + + // Transformation nodes + case SyntaxKind.NotEmittedStatement: + case SyntaxKind.EndOfDeclarationMarker: + case SyntaxKind.MergeDeclarationMarker: + return; + } + if (isExpression(node)) { + hint = EmitHint.Expression; + if (substituteNode !== noEmitSubstitution) { + const substitute = substituteNode(hint, node) || node; + if (substitute !== node) { + node = substitute; + if (currentParenthesizerRule) { + node = currentParenthesizerRule(node); + } + } + } + } + } + if (hint === EmitHint.Expression) { + switch (node.kind) { + // Literals + case SyntaxKind.NumericLiteral: + case SyntaxKind.BigIntLiteral: + return emitNumericOrBigIntLiteral(node); + + case SyntaxKind.StringLiteral: + case SyntaxKind.RegularExpressionLiteral: + case SyntaxKind.NoSubstitutionTemplateLiteral: + return emitLiteral(node, /*jsxAttributeEscape*/ false); + + // Identifiers + case SyntaxKind.Identifier: + return emitIdentifier(node); + + // Expressions + case SyntaxKind.ArrayLiteralExpression: + return emitArrayLiteralExpression(node); + case SyntaxKind.ObjectLiteralExpression: + return emitObjectLiteralExpression(node); + case SyntaxKind.PropertyAccessExpression: + return emitPropertyAccessExpression(node); + case SyntaxKind.ElementAccessExpression: + return emitElementAccessExpression(node); + case SyntaxKind.CallExpression: + return emitCallExpression(node); + case SyntaxKind.NewExpression: + return emitNewExpression(node); + case SyntaxKind.TaggedTemplateExpression: + return emitTaggedTemplateExpression(node); + case SyntaxKind.TypeAssertionExpression: + return emitTypeAssertionExpression(node); + case SyntaxKind.ParenthesizedExpression: + return emitParenthesizedExpression(node); + case SyntaxKind.FunctionExpression: + return emitFunctionExpression(node); + case SyntaxKind.ArrowFunction: + return emitArrowFunction(node); + case SyntaxKind.DeleteExpression: + return emitDeleteExpression(node); + case SyntaxKind.TypeOfExpression: + return emitTypeOfExpression(node); + case SyntaxKind.VoidExpression: + return emitVoidExpression(node); + case SyntaxKind.AwaitExpression: + return emitAwaitExpression(node); + case SyntaxKind.PrefixUnaryExpression: + return emitPrefixUnaryExpression(node); + case SyntaxKind.PostfixUnaryExpression: + return emitPostfixUnaryExpression(node); + case SyntaxKind.BinaryExpression: + return emitBinaryExpression(node); + case SyntaxKind.ConditionalExpression: + return emitConditionalExpression(node); + case SyntaxKind.TemplateExpression: + return emitTemplateExpression(node); + case SyntaxKind.YieldExpression: + return emitYieldExpression(node); + case SyntaxKind.SpreadElement: + return emitSpreadElement(node); + case SyntaxKind.ClassExpression: + return emitClassExpression(node); + case SyntaxKind.OmittedExpression: + return; + case SyntaxKind.AsExpression: + return emitAsExpression(node); + case SyntaxKind.NonNullExpression: + return emitNonNullExpression(node); + case SyntaxKind.MetaProperty: + return emitMetaProperty(node); + case SyntaxKind.SyntheticExpression: + return Debug.fail("SyntheticExpression should never be printed."); + + // JSX + case SyntaxKind.JsxElement: + return emitJsxElement(node); + case SyntaxKind.JsxSelfClosingElement: + return emitJsxSelfClosingElement(node); + case SyntaxKind.JsxFragment: + return emitJsxFragment(node); + + // Synthesized list + case SyntaxKind.SyntaxList: + return Debug.fail("SyntaxList should not be printed"); + + // Transformation nodes + case SyntaxKind.NotEmittedStatement: + return; + case SyntaxKind.PartiallyEmittedExpression: + return emitPartiallyEmittedExpression(node); + case SyntaxKind.CommaListExpression: + return emitCommaList(node); + case SyntaxKind.MergeDeclarationMarker: + case SyntaxKind.EndOfDeclarationMarker: + return; + case SyntaxKind.SyntheticReferenceExpression: + return Debug.fail("SyntheticReferenceExpression should not be printed"); + } + } + if (isKeyword(node.kind)) return writeTokenNode(node, writeKeyword); + if (isTokenKind(node.kind)) return writeTokenNode(node, writePunctuation); + Debug.fail(`Unhandled SyntaxKind: ${Debug.formatSyntaxKind(node.kind)}.`); + } + + function emitMappedTypeParameter(node: TypeParameterDeclaration): void { + emit(node.name); + writeSpace(); + writeKeyword("in"); + writeSpace(); + emit(node.constraint); + } + + function pipelineEmitWithSubstitution(hint: EmitHint, node: Node) { + const pipelinePhase = getNextPipelinePhase(PipelinePhase.Substitution, hint, node); + Debug.assertIsDefined(lastSubstitution); + node = lastSubstitution; + lastSubstitution = undefined; + pipelinePhase(hint, node); + } + + function getHelpersFromBundledSourceFiles(bundle: Bundle): string[] | undefined { + let result: string[] | undefined; + if (moduleKind === ModuleKind.None || printerOptions.noEmitHelpers) { + return undefined; + } + const bundledHelpers = new Map(); + for (const sourceFile of bundle.sourceFiles) { + const shouldSkip = getExternalHelpersModuleName(sourceFile) !== undefined; + const helpers = getSortedEmitHelpers(sourceFile); + if (!helpers) continue; + for (const helper of helpers) { + if (!helper.scoped && !shouldSkip && !bundledHelpers.get(helper.name)) { + bundledHelpers.set(helper.name, true); + (result || (result = [])).push(helper.name); + } + } + } return result; } @@ -1831,10 +1890,6 @@ namespace ts { } } - function emitStringLiteralWithJsxAttributeEscape(node: StringLiteral) { - emitLiteral(node, /*jsxAttributeEscape*/ true); - } - // SyntaxKind.UnparsedSource // SyntaxKind.UnparsedPrepend function emitUnparsedSourceOrPrepend(unparsed: UnparsedSource | UnparsedPrepend) { @@ -1908,7 +1963,7 @@ namespace ts { function emitEntityName(node: EntityName) { if (node.kind === SyntaxKind.Identifier) { - emit(node); + emitExpression(node); } else { emit(node); @@ -1917,7 +1972,7 @@ namespace ts { function emitComputedPropertyName(node: ComputedPropertyName) { writePunctuation("["); - emit(node.expression); + emitExpression(node.expression, parenthesizer.parenthesizeExpressionOfComputedPropertyName); writePunctuation("]"); } @@ -1954,12 +2009,12 @@ namespace ts { emitTypeAnnotation(node.type); } // The comment position has to fallback to any present node within the parameterdeclaration because as it turns out, the parser can make parameter declarations with _just_ an initializer. - emitInitializer(node.initializer, node.type ? node.type.end : node.questionToken ? node.questionToken.end : node.name ? node.name.end : node.modifiers ? node.modifiers.end : node.decorators ? node.decorators.end : node.pos, node); + emitInitializer(node.initializer, node.type ? node.type.end : node.questionToken ? node.questionToken.end : node.name ? node.name.end : node.modifiers ? node.modifiers.end : node.decorators ? node.decorators.end : node.pos, node, parenthesizer.parenthesizeExpressionForDisallowedComma); } function emitDecorator(decorator: Decorator) { writePunctuation("@"); - emit(decorator.expression); + emitExpression(decorator.expression, parenthesizer.parenthesizeLeftSideOfAccess); } // @@ -2149,7 +2204,7 @@ namespace ts { } function emitArrayType(node: ArrayTypeNode) { - emit(node.elementType); + emit(node.elementType, parenthesizer.parenthesizeElementTypeOfArrayType); writePunctuation("["); writePunctuation("]"); } @@ -2176,24 +2231,24 @@ namespace ts { } function emitOptionalType(node: OptionalTypeNode) { - emit(node.type); + emit(node.type, parenthesizer.parenthesizeElementTypeOfArrayType); writePunctuation("?"); } function emitUnionType(node: UnionTypeNode) { - emitList(node, node.types, ListFormat.UnionTypeConstituents); + emitList(node, node.types, ListFormat.UnionTypeConstituents, parenthesizer.parenthesizeMemberOfElementType); } function emitIntersectionType(node: IntersectionTypeNode) { - emitList(node, node.types, ListFormat.IntersectionTypeConstituents); + emitList(node, node.types, ListFormat.IntersectionTypeConstituents, parenthesizer.parenthesizeMemberOfElementType); } function emitConditionalType(node: ConditionalTypeNode) { - emit(node.checkType); + emit(node.checkType, parenthesizer.parenthesizeMemberOfConditionalType); writeSpace(); writeKeyword("extends"); writeSpace(); - emit(node.extendsType); + emit(node.extendsType, parenthesizer.parenthesizeMemberOfConditionalType); writeSpace(); writePunctuation("?"); writeSpace(); @@ -2223,11 +2278,11 @@ namespace ts { function emitTypeOperator(node: TypeOperatorNode) { writeTokenText(node.operator, writeKeyword); writeSpace(); - emit(node.type); + emit(node.type, parenthesizer.parenthesizeMemberOfElementType); } function emitIndexedAccessType(node: IndexedAccessTypeNode) { - emit(node.objectType); + emit(node.objectType, parenthesizer.parenthesizeMemberOfElementType); writePunctuation("["); emit(node.indexType); writePunctuation("]"); @@ -2252,7 +2307,7 @@ namespace ts { } writePunctuation("["); - emitWithContext(node.typeParameter, emitMappedTypeParameter); + pipelineEmit(EmitHint.MappedTypeParameter, node.typeParameter); if (node.nameType) { writeSpace(); writeKeyword("as"); @@ -2282,7 +2337,7 @@ namespace ts { } function emitLiteralType(node: LiteralTypeNode) { - emit(node.literal); + emitExpression(node.literal); } function emitTemplateType(node: TemplateLiteralTypeNode) { @@ -2330,7 +2385,7 @@ namespace ts { writeSpace(); } emit(node.name); - emitInitializer(node.initializer, node.name.end, node); + emitInitializer(node.initializer, node.name.end, node, parenthesizer.parenthesizeExpressionForDisallowedComma); } // @@ -2340,7 +2395,7 @@ namespace ts { function emitArrayLiteralExpression(node: ArrayLiteralExpression) { const elements = node.elements; const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None; - emitList(node, elements, ListFormat.ArrayLiteralExpressionElements | preferNewLine); + emitExpressionList(node, elements, ListFormat.ArrayLiteralExpressionElements | preferNewLine, parenthesizer.parenthesizeExpressionForDisallowedComma); } function emitObjectLiteralExpression(node: ObjectLiteralExpression) { @@ -2361,7 +2416,7 @@ namespace ts { } function emitPropertyAccessExpression(node: PropertyAccessExpression) { - emit(node.expression); + emitExpression(node.expression, parenthesizer.parenthesizeLeftSideOfAccess); const token = node.questionDotToken || setTextRangePosEnd(factory.createToken(SyntaxKind.DotToken) as DotToken, node.expression.end, node.name.pos); const linesBeforeDot = getLinesBetweenNodes(node, node.expression, token); const linesAfterDot = getLinesBetweenNodes(node, token, node.name); @@ -2410,46 +2465,46 @@ namespace ts { } function emitElementAccessExpression(node: ElementAccessExpression) { - emit(node.expression); + emitExpression(node.expression, parenthesizer.parenthesizeLeftSideOfAccess); emit(node.questionDotToken); emitTokenWithComment(SyntaxKind.OpenBracketToken, node.expression.end, writePunctuation, node); - emit(node.argumentExpression); + emitExpression(node.argumentExpression); emitTokenWithComment(SyntaxKind.CloseBracketToken, node.argumentExpression.end, writePunctuation, node); } function emitCallExpression(node: CallExpression) { - emit(node.expression); + emitExpression(node.expression, parenthesizer.parenthesizeLeftSideOfAccess); emit(node.questionDotToken); emitTypeArguments(node, node.typeArguments); - emitList(node, node.arguments, ListFormat.CallExpressionArguments); + emitExpressionList(node, node.arguments, ListFormat.CallExpressionArguments, parenthesizer.parenthesizeExpressionForDisallowedComma); } function emitNewExpression(node: NewExpression) { emitTokenWithComment(SyntaxKind.NewKeyword, node.pos, writeKeyword, node); writeSpace(); - emit(node.expression); + emitExpression(node.expression, parenthesizer.parenthesizeExpressionOfNew); emitTypeArguments(node, node.typeArguments); - emitList(node, node.arguments, ListFormat.NewExpressionArguments); + emitExpressionList(node, node.arguments, ListFormat.NewExpressionArguments, parenthesizer.parenthesizeExpressionForDisallowedComma); } function emitTaggedTemplateExpression(node: TaggedTemplateExpression) { - emit(node.tag); + emitExpression(node.tag, parenthesizer.parenthesizeLeftSideOfAccess); emitTypeArguments(node, node.typeArguments); writeSpace(); - emit(node.template); + emitExpression(node.template); } function emitTypeAssertionExpression(node: TypeAssertion) { writePunctuation("<"); emit(node.type); writePunctuation(">"); - emit(node.expression); + emitExpression(node.expression, parenthesizer.parenthesizeOperandOfPrefixUnary); } function emitParenthesizedExpression(node: ParenthesizedExpression) { const openParenPos = emitTokenWithComment(SyntaxKind.OpenParenToken, node.pos, writePunctuation, node); const indented = writeLineSeparatorsAndIndentBefore(node.expression, node); - emit(node.expression); + emitExpression(node.expression, /*parenthesizerRules*/ undefined); writeLineSeparatorsAfter(node.expression, node); decreaseIndentIf(indented); emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression ? node.expression.end : openParenPos, writePunctuation, node); @@ -2477,25 +2532,25 @@ namespace ts { function emitDeleteExpression(node: DeleteExpression) { emitTokenWithComment(SyntaxKind.DeleteKeyword, node.pos, writeKeyword, node); writeSpace(); - emit(node.expression); + emitExpression(node.expression, parenthesizer.parenthesizeOperandOfPrefixUnary); } function emitTypeOfExpression(node: TypeOfExpression) { emitTokenWithComment(SyntaxKind.TypeOfKeyword, node.pos, writeKeyword, node); writeSpace(); - emit(node.expression); + emitExpression(node.expression, parenthesizer.parenthesizeOperandOfPrefixUnary); } function emitVoidExpression(node: VoidExpression) { emitTokenWithComment(SyntaxKind.VoidKeyword, node.pos, writeKeyword, node); writeSpace(); - emit(node.expression); + emitExpression(node.expression, parenthesizer.parenthesizeOperandOfPrefixUnary); } function emitAwaitExpression(node: AwaitExpression) { emitTokenWithComment(SyntaxKind.AwaitKeyword, node.pos, writeKeyword, node); writeSpace(); - emit(node.expression); + emitExpression(node.expression, parenthesizer.parenthesizeOperandOfPrefixUnary); } function emitPrefixUnaryExpression(node: PrefixUnaryExpression) { @@ -2503,7 +2558,7 @@ namespace ts { if (shouldEmitWhitespaceBeforeOperand(node)) { writeSpace(); } - emit(node.operand); + emitExpression(node.operand, parenthesizer.parenthesizeOperandOfPrefixUnary); } function shouldEmitWhitespaceBeforeOperand(node: PrefixUnaryExpression) { @@ -2526,7 +2581,7 @@ namespace ts { } function emitPostfixUnaryExpression(node: PostfixUnaryExpression) { - emit(node.operand); + emitExpression(node.operand, parenthesizer.parenthesizeOperandOfPostfixUnary); writeTokenText(node.operator, writeOperator); } @@ -2541,7 +2596,7 @@ namespace ts { shouldEmitSourceMapsStack: boolean[]; } - return createBinaryExpressionTrampoline(onEnter, maybeEmitExpression, onOperator, maybeEmitExpression, onExit, /*foldState*/ undefined); + return createBinaryExpressionTrampoline(onEnter, onLeft, onOperator, onRight, onExit, /*foldState*/ undefined); function onEnter(node: BinaryExpression, state: WorkArea | undefined) { if (state) { @@ -2552,7 +2607,10 @@ namespace ts { state.declarationListContainerEndStack[state.stackIndex] = declarationListContainerEnd; const emitComments = state.shouldEmitCommentsStack[state.stackIndex] = shouldEmitComments(node); const emitSourceMaps = state.shouldEmitSourceMapsStack[state.stackIndex] = shouldEmitSourceMaps(node); - beforeEmitWithContext(node, emitComments, emitSourceMaps); + onBeforeEmitNode?.(node); + if (emitComments) emitCommentsBeforeNode(node); + if (emitSourceMaps) emitSourceMapsBeforeNode(node); + beforeEmitNode(node); } else { state = { @@ -2568,6 +2626,10 @@ namespace ts { return state; } + function onLeft(next: Expression, _workArea: WorkArea, parent: BinaryExpression) { + return maybeEmitExpression(next, parent, "left"); + } + function onOperator(operatorToken: BinaryOperatorToken, _state: WorkArea, node: BinaryExpression) { const isCommaOperator = operatorToken.kind !== SyntaxKind.CommaToken; const linesBeforeOperator = getLinesBetweenNodes(node, node.left, operatorToken); @@ -2579,6 +2641,10 @@ namespace ts { writeLinesAndIndent(linesAfterOperator, /*writeSpaceIfNotIndenting*/ true); } + function onRight(next: Expression, _workArea: WorkArea, parent: BinaryExpression) { + return maybeEmitExpression(next, parent, "right"); + } + function onExit(node: BinaryExpression, state: WorkArea) { const linesBeforeOperator = getLinesBetweenNodes(node, node.left, node.operatorToken); const linesAfterOperator = getLinesBetweenNodes(node, node.operatorToken, node.right); @@ -2590,18 +2656,37 @@ namespace ts { const savedDeclarationListContainerEnd = state.declarationListContainerEndStack[state.stackIndex]; const shouldEmitComments = state.shouldEmitCommentsStack[state.stackIndex]; const shouldEmitSourceMaps = state.shouldEmitSourceMapsStack[state.stackIndex]; - afterEmitWithContext(node, shouldEmitComments, shouldEmitSourceMaps, savedContainerPos, savedContainerEnd, savedDeclarationListContainerEnd, savedPreserveSourceNewlines); + afterEmitNode(savedPreserveSourceNewlines); + if (shouldEmitSourceMaps) emitSourceMapsAfterNode(node); + if (shouldEmitComments) emitCommentsAfterNode(node, savedContainerPos, savedContainerEnd, savedDeclarationListContainerEnd); + onAfterEmitNode?.(node); state.stackIndex--; } } - function maybeEmitExpression(next: Expression) { - // Push a new frame for binary expressions, otherwise emit all other expressions. - if (isBinaryExpression(next)) { - return next; + function maybeEmitExpression(next: Expression, parent: BinaryExpression, side: "left" | "right") { + const parenthesizerRule = side === "left" ? + parenthesizer.getParenthesizeLeftSideOfBinaryForOperator(parent.operatorToken.kind) : + parenthesizer.getParenthesizeRightSideOfBinaryForOperator(parent.operatorToken.kind); + + let pipelinePhase = getPipelinePhase(PipelinePhase.Notification, EmitHint.Expression, next); + if (pipelinePhase === pipelineEmitWithSubstitution) { + Debug.assertIsDefined(lastSubstitution); + next = parenthesizerRule(cast(lastSubstitution, isExpression)); + pipelinePhase = getNextPipelinePhase(PipelinePhase.Substitution, EmitHint.Expression, next); + lastSubstitution = undefined; + } + + if (pipelinePhase === pipelineEmitWithComments || + pipelinePhase === pipelineEmitWithSourceMaps || + pipelinePhase === pipelineEmitWithHint) { + if (isBinaryExpression(next)) { + return next; + } } - emit(next); + currentParenthesizerRule = parenthesizerRule; + pipelinePhase(EmitHint.Expression, next); } } @@ -2611,17 +2696,17 @@ namespace ts { const linesBeforeColon = getLinesBetweenNodes(node, node.whenTrue, node.colonToken); const linesAfterColon = getLinesBetweenNodes(node, node.colonToken, node.whenFalse); - emit(node.condition); + emitExpression(node.condition, parenthesizer.parenthesizeConditionOfConditionalExpression); writeLinesAndIndent(linesBeforeQuestion, /*writeSpaceIfNotIndenting*/ true); emit(node.questionToken); writeLinesAndIndent(linesAfterQuestion, /*writeSpaceIfNotIndenting*/ true); - emit(node.whenTrue); + emitExpression(node.whenTrue, parenthesizer.parenthesizeBranchOfConditionalExpression); decreaseIndentIf(linesBeforeQuestion, linesAfterQuestion); writeLinesAndIndent(linesBeforeColon, /*writeSpaceIfNotIndenting*/ true); emit(node.colonToken); writeLinesAndIndent(linesAfterColon, /*writeSpaceIfNotIndenting*/ true); - emit(node.whenFalse); + emitExpression(node.whenFalse, parenthesizer.parenthesizeBranchOfConditionalExpression); decreaseIndentIf(linesBeforeColon, linesAfterColon); } @@ -2633,12 +2718,12 @@ namespace ts { function emitYieldExpression(node: YieldExpression) { emitTokenWithComment(SyntaxKind.YieldKeyword, node.pos, writeKeyword, node); emit(node.asteriskToken); - emitExpressionWithLeadingSpace(node.expression); + emitExpressionWithLeadingSpace(node.expression, parenthesizer.parenthesizeExpressionForDisallowedComma); } - function emitSpreadExpression(node: SpreadElement) { + function emitSpreadElement(node: SpreadElement) { emitTokenWithComment(SyntaxKind.DotDotDotToken, node.pos, writePunctuation, node); - emit(node.expression); + emitExpression(node.expression, parenthesizer.parenthesizeExpressionForDisallowedComma); } function emitClassExpression(node: ClassExpression) { @@ -2647,12 +2732,12 @@ namespace ts { } function emitExpressionWithTypeArguments(node: ExpressionWithTypeArguments) { - emit(node.expression); + emitExpression(node.expression, parenthesizer.parenthesizeLeftSideOfAccess); emitTypeArguments(node, node.typeArguments); } function emitAsExpression(node: AsExpression) { - emit(node.expression); + emitExpression(node.expression, /*parenthesizerRules*/ undefined); if (node.type) { writeSpace(); writeKeyword("as"); @@ -2662,7 +2747,7 @@ namespace ts { } function emitNonNullExpression(node: NonNullExpression) { - emit(node.expression); + emitExpression(node.expression, parenthesizer.parenthesizeLeftSideOfAccess); writeOperator("!"); } @@ -2677,7 +2762,7 @@ namespace ts { // function emitTemplateSpan(node: TemplateSpan) { - emit(node.expression); + emitExpression(node.expression); emit(node.literal); } @@ -2713,12 +2798,8 @@ namespace ts { } } - function emitEmbeddedEmptyStatement(_node: EmptyStatement) { - emitEmptyStatement(/*isEmbeddedStatement*/ true); - } - function emitExpressionStatement(node: ExpressionStatement) { - emit(node.expression); + emitExpression(node.expression, parenthesizer.parenthesizeExpressionOfExpressionStatement); // Emit semicolon in non json files // or if json file that created synthesized expression(eg.define expression statement when --out and amd code generation) if (!isJsonSourceFile(currentSourceFile!) || nodeIsSynthesized(node.expression)) { @@ -2730,7 +2811,7 @@ namespace ts { const openParenPos = emitTokenWithComment(SyntaxKind.IfKeyword, node.pos, writeKeyword, node); writeSpace(); emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node); - emit(node.expression); + emitExpression(node.expression); emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression.end, writePunctuation, node); emitEmbeddedStatement(node, node.thenStatement); if (node.elseStatement) { @@ -2750,7 +2831,7 @@ namespace ts { const openParenPos = emitTokenWithComment(SyntaxKind.WhileKeyword, startPos, writeKeyword, node); writeSpace(); emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node); - emit(node.expression); + emitExpression(node.expression); emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression.end, writePunctuation, node); } @@ -2794,7 +2875,7 @@ namespace ts { writeSpace(); emitTokenWithComment(SyntaxKind.InKeyword, node.initializer.end, writeKeyword, node); writeSpace(); - emit(node.expression); + emitExpression(node.expression); emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression.end, writePunctuation, node); emitEmbeddedStatement(node, node.statement); } @@ -2808,7 +2889,7 @@ namespace ts { writeSpace(); emitTokenWithComment(SyntaxKind.OfKeyword, node.initializer.end, writeKeyword, node); writeSpace(); - emit(node.expression); + emitExpression(node.expression); emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression.end, writePunctuation, node); emitEmbeddedStatement(node, node.statement); } @@ -2819,7 +2900,7 @@ namespace ts { emit(node); } else { - emit(node); + emitExpression(node); } } } @@ -2871,7 +2952,7 @@ namespace ts { const openParenPos = emitTokenWithComment(SyntaxKind.WithKeyword, node.pos, writeKeyword, node); writeSpace(); emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node); - emit(node.expression); + emitExpression(node.expression); emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression.end, writePunctuation, node); emitEmbeddedStatement(node, node.statement); } @@ -2880,7 +2961,7 @@ namespace ts { const openParenPos = emitTokenWithComment(SyntaxKind.SwitchKeyword, node.pos, writeKeyword, node); writeSpace(); emitTokenWithComment(SyntaxKind.OpenParenToken, openParenPos, writePunctuation, node); - emit(node.expression); + emitExpression(node.expression); emitTokenWithComment(SyntaxKind.CloseParenToken, node.expression.end, writePunctuation, node); writeSpace(); emit(node.caseBlock); @@ -2928,7 +3009,7 @@ namespace ts { emit(node.name); emit(node.exclamationToken); emitTypeAnnotation(node.type); - emitInitializer(node.initializer, node.type ? node.type.end : node.name.end, node); + emitInitializer(node.initializer, node.type ? node.type.end : node.name.end, node, parenthesizer.parenthesizeExpressionForDisallowedComma); } function emitVariableDeclarationList(node: VariableDeclarationList) { @@ -2947,7 +3028,7 @@ namespace ts { writeKeyword("function"); emit(node.asteriskToken); writeSpace(); - emit(node.name); + emitIdentifierName(node.name); emitSignatureAndBody(node, emitSignatureHead); } @@ -2975,7 +3056,7 @@ namespace ts { else { emitSignatureHead(node); writeSpace(); - emit(body); + emitExpression(body, parenthesizer.parenthesizeConciseBodyOfArrowFunction); } } else { @@ -3066,7 +3147,7 @@ namespace ts { increaseIndent(); } else { - emitList(body, body.statements, ListFormat.MultiLineFunctionBodyStatements, statementOffset); + emitList(body, body.statements, ListFormat.MultiLineFunctionBodyStatements, /*parenthesizerRule*/ undefined, statementOffset); } } @@ -3082,7 +3163,7 @@ namespace ts { writeKeyword("class"); if (node.name) { writeSpace(); - emit(node.name); + emitIdentifierName(node.name); } const indentedFlag = getEmitFlags(node) & EmitFlags.Indented; @@ -3194,7 +3275,7 @@ namespace ts { function emitModuleReference(node: ModuleReference) { if (node.kind === SyntaxKind.Identifier) { - emit(node); + emitExpression(node); } else { emit(node); @@ -3211,7 +3292,7 @@ namespace ts { emitTokenWithComment(SyntaxKind.FromKeyword, node.importClause.end, writeKeyword, node); writeSpace(); } - emit(node.moduleSpecifier); + emitExpression(node.moduleSpecifier); writeTrailingSemicolon(); } @@ -3254,7 +3335,9 @@ namespace ts { emitTokenWithComment(SyntaxKind.DefaultKeyword, nextPos, writeKeyword, node); } writeSpace(); - emit(node.expression); + emitExpression(node.expression, node.isExportEquals ? + parenthesizer.getParenthesizeRightSideOfBinaryForOperator(SyntaxKind.EqualsToken) : + parenthesizer.parenthesizeExpressionOfExportDefault); writeTrailingSemicolon(); } @@ -3276,7 +3359,7 @@ namespace ts { const fromPos = node.exportClause ? node.exportClause.end : nextPos; emitTokenWithComment(SyntaxKind.FromKeyword, fromPos, writeKeyword, node); writeSpace(); - emit(node.moduleSpecifier); + emitExpression(node.moduleSpecifier); } writeTrailingSemicolon(); } @@ -3332,7 +3415,7 @@ namespace ts { function emitExternalModuleReference(node: ExternalModuleReference) { writeKeyword("require"); writePunctuation("("); - emit(node.expression); + emitExpression(node.expression); writePunctuation(")"); } @@ -3395,11 +3478,6 @@ namespace ts { emitList(node, node.properties, ListFormat.JsxElementAttributes); } - function emitJsxAttributeValue(node: StringLiteral | JsxExpression): void { - const emitCallback = isStringLiteral(node) ? emitStringLiteralWithJsxAttributeEscape : emitWorker; - emitWithContext(node, emitCallback); - } - function emitJsxAttribute(node: JsxAttribute) { emit(node.name); emitNodeWithPrefix("=", writePunctuation, node.initializer, emitJsxAttributeValue); @@ -3407,7 +3485,7 @@ namespace ts { function emitJsxSpreadAttribute(node: JsxSpreadAttribute) { writePunctuation("{..."); - emit(node.expression); + emitExpression(node.expression); writePunctuation("}"); } @@ -3435,7 +3513,7 @@ namespace ts { } const end = emitTokenWithComment(SyntaxKind.OpenBraceToken, node.pos, writePunctuation, node); emit(node.dotDotDotToken); - emit(node.expression); + emitExpression(node.expression); emitTokenWithComment(SyntaxKind.CloseBraceToken, node.expression?.end || end, writePunctuation, node); if (isMultiline) { writer.decreaseIndent(); @@ -3445,7 +3523,7 @@ namespace ts { function emitJsxTagName(node: JsxTagNameExpression) { if (node.kind === SyntaxKind.Identifier) { - emit(node); + emitExpression(node); } else { emit(node); @@ -3459,7 +3537,7 @@ namespace ts { function emitCaseClause(node: CaseClause) { emitTokenWithComment(SyntaxKind.CaseKeyword, node.pos, writeKeyword, node); writeSpace(); - emit(node.expression); + emitExpression(node.expression, parenthesizer.parenthesizeExpressionForDisallowedComma); emitCaseOrDefaultClauseRest(node, node.statements, node.expression.end); } @@ -3530,7 +3608,7 @@ namespace ts { const commentRange = getCommentRange(initializer); emitTrailingCommentsOfPosition(commentRange.pos); } - emit(initializer); + emitExpression(initializer, parenthesizer.parenthesizeExpressionForDisallowedComma); } function emitShorthandPropertyAssignment(node: ShorthandPropertyAssignment) { @@ -3539,14 +3617,14 @@ namespace ts { writeSpace(); writePunctuation("="); writeSpace(); - emit(node.objectAssignmentInitializer); + emitExpression(node.objectAssignmentInitializer, parenthesizer.parenthesizeExpressionForDisallowedComma); } } function emitSpreadAssignment(node: SpreadAssignment) { if (node.expression) { emitTokenWithComment(SyntaxKind.DotDotDotToken, node.pos, writePunctuation, node); - emit(node.expression); + emitExpression(node.expression, parenthesizer.parenthesizeExpressionForDisallowedComma); } } @@ -3556,7 +3634,7 @@ namespace ts { function emitEnumMember(node: EnumMember) { emit(node.name); - emitInitializer(node.initializer, node.name.end, node); + emitInitializer(node.initializer, node.name.end, node, parenthesizer.parenthesizeExpressionForDisallowedComma); } // @@ -3810,18 +3888,18 @@ namespace ts { emitHelpers(node); const index = findIndex(statements, statement => !isPrologueDirective(statement)); emitTripleSlashDirectivesIfNeeded(node); - emitList(node, statements, ListFormat.MultiLine, index === -1 ? statements.length : index); + emitList(node, statements, ListFormat.MultiLine, /*parenthesizerRule*/ undefined, index === -1 ? statements.length : index); popNameGenerationScope(node); } // Transformation nodes function emitPartiallyEmittedExpression(node: PartiallyEmittedExpression) { - emit(node.expression); + emitExpression(node.expression); } function emitCommaList(node: CommaListExpression) { - emitList(node, node.elements, ListFormat.CommaListElements); + emitExpressionList(node, node.elements, ListFormat.CommaListElements, /*parenthesizerRule*/ undefined); } /** @@ -3966,12 +4044,12 @@ namespace ts { } } - function emitInitializer(node: Expression | undefined, equalCommentStartPos: number, container: Node) { + function emitInitializer(node: Expression | undefined, equalCommentStartPos: number, container: Node, parenthesizerRule?: (node: Expression) => Expression) { if (node) { writeSpace(); emitTokenWithComment(SyntaxKind.EqualsToken, equalCommentStartPos, writeOperator, container); writeSpace(); - emit(node); + emitExpression(node, parenthesizerRule); } } @@ -3989,10 +4067,10 @@ namespace ts { } } - function emitExpressionWithLeadingSpace(node: Expression | undefined) { + function emitExpressionWithLeadingSpace(node: Expression | undefined, parenthesizerRule?: (node: Expression) => Expression) { if (node) { writeSpace(); - emit(node); + emitExpression(node, parenthesizerRule); } } @@ -4012,7 +4090,7 @@ namespace ts { writeLine(); increaseIndent(); if (isEmptyStatement(node)) { - emitWithContext(node, emitEmbeddedEmptyStatement); + pipelineEmit(EmitHint.EmbeddedStatement, node); } else { emit(node); @@ -4026,7 +4104,7 @@ namespace ts { } function emitTypeArguments(parentNode: Node, typeArguments: NodeArray | undefined) { - emitList(parentNode, typeArguments, ListFormat.TypeArguments); + emitList(parentNode, typeArguments, ListFormat.TypeArguments, parenthesizer.parenthesizeMemberOfElementType); } function emitTypeParameters(parentNode: SignatureDeclaration | InterfaceDeclaration | TypeAliasDeclaration | ClassDeclaration | ClassExpression, typeParameters: NodeArray | undefined) { @@ -4094,7 +4172,15 @@ namespace ts { } } - function emitList(parentNode: Node | undefined, children: NodeArray | undefined, format: ListFormat, start = 0, count = children ? children.length - start : 0) { + function emitList(parentNode: Node | undefined, children: NodeArray | undefined, format: ListFormat, parenthesizerRule?: (node: Node) => Node, start?: number, count?: number) { + emitNodeList(emit, parentNode, children, format, parenthesizerRule, start, count); + } + + function emitExpressionList(parentNode: Node | undefined, children: NodeArray | undefined, format: ListFormat, parenthesizerRule?: (node: Expression) => Expression, start?: number, count?: number) { + emitNodeList(emitExpression, parentNode, children, format, parenthesizerRule, start, count); + } + + function emitNodeList(emit: (node: Node, parenthesizerRule?: ((node: Node) => Node) | undefined) => void, parentNode: Node | undefined, children: NodeArray | undefined, format: ListFormat, parenthesizerRule: ((node: Node) => Node) | undefined, start = 0, count = children ? children.length - start : 0) { const isUndefined = children === undefined; if (isUndefined && format & ListFormat.OptionalIfUndefined) { return; @@ -4207,7 +4293,12 @@ namespace ts { } nextListElementPos = child.pos; - emit(child); + if (emit.length === 1) { + emit(child); + } + else { + emit(child, parenthesizerRule); + } if (shouldDecreaseIndentAfterEmit) { decreaseIndent(); @@ -5055,6 +5146,38 @@ namespace ts { // Comments + function pipelineEmitWithComments(hint: EmitHint, node: Node) { + const pipelinePhase = getNextPipelinePhase(PipelinePhase.Comments, hint, node); + const savedContainerPos = containerPos; + const savedContainerEnd = containerEnd; + const savedDeclarationListContainerEnd = declarationListContainerEnd; + emitCommentsBeforeNode(node); + pipelinePhase(hint, node); + emitCommentsAfterNode(node, savedContainerPos, savedContainerEnd, savedDeclarationListContainerEnd); + } + + function emitCommentsBeforeNode(node: Node) { + const emitFlags = getEmitFlags(node); + const commentRange = getCommentRange(node); + + // Emit leading comments + emitLeadingCommentsOfNode(node, emitFlags, commentRange.pos, commentRange.end); + if (emitFlags & EmitFlags.NoNestedComments) { + commentsDisabled = true; + } + } + + function emitCommentsAfterNode(node: Node, savedContainerPos: number, savedContainerEnd: number, savedDeclarationListContainerEnd: number) { + const emitFlags = getEmitFlags(node); + const commentRange = getCommentRange(node); + + // Emit trailing comments + if (emitFlags & EmitFlags.NoNestedComments) { + commentsDisabled = false; + } + emitTrailingCommentsOfNode(node, emitFlags, commentRange.pos, commentRange.end, savedContainerPos, savedContainerEnd, savedDeclarationListContainerEnd); + } + function emitLeadingCommentsOfNode(node: Node, emitFlags: EmitFlags, pos: number, end: number) { enterComment(); hasWrittenComment = false; @@ -5398,6 +5521,62 @@ namespace ts { return node.parsedSourceMap || undefined; } + function pipelineEmitWithSourceMaps(hint: EmitHint, node: Node) { + const pipelinePhase = getNextPipelinePhase(PipelinePhase.SourceMaps, hint, node); + emitSourceMapsBeforeNode(node); + pipelinePhase(hint, node); + emitSourceMapsAfterNode(node); + } + + function emitSourceMapsBeforeNode(node: Node) { + const emitFlags = getEmitFlags(node); + const sourceMapRange = getSourceMapRange(node); + + // Emit leading sourcemap + if (isUnparsedNode(node)) { + Debug.assertIsDefined(node.parent, "UnparsedNodes must have parent pointers"); + const parsed = getParsedSourceMap(node.parent); + if (parsed && sourceMapGenerator) { + sourceMapGenerator.appendSourceMap( + writer.getLine(), + writer.getColumn(), + parsed, + node.parent.sourceMapPath!, + node.parent.getLineAndCharacterOfPosition(node.pos), + node.parent.getLineAndCharacterOfPosition(node.end) + ); + } + } + else { + const source = sourceMapRange.source || sourceMapSource; + if (node.kind !== SyntaxKind.NotEmittedStatement + && (emitFlags & EmitFlags.NoLeadingSourceMap) === 0 + && sourceMapRange.pos >= 0) { + emitSourcePos(sourceMapRange.source || sourceMapSource, skipSourceTrivia(source, sourceMapRange.pos)); + } + if (emitFlags & EmitFlags.NoNestedSourceMaps) { + sourceMapsDisabled = true; + } + } + } + + function emitSourceMapsAfterNode(node: Node) { + const emitFlags = getEmitFlags(node); + const sourceMapRange = getSourceMapRange(node); + + // Emit trailing sourcemap + if (!isUnparsedNode(node)) { + if (emitFlags & EmitFlags.NoNestedSourceMaps) { + sourceMapsDisabled = false; + } + if (node.kind !== SyntaxKind.NotEmittedStatement + && (emitFlags & EmitFlags.NoTrailingSourceMap) === 0 + && sourceMapRange.end >= 0) { + emitSourcePos(sourceMapRange.source || sourceMapSource, sourceMapRange.end); + } + } + } + /** * Skips trivia such as comments and white-space that can be optionally overridden by the source-map source */ @@ -5511,1249 +5690,6 @@ namespace ts { } } - const enum PreprintPipelinePhase { - Notification, - Substitution, - Visit - } - - function createPreprinter(handlers: PrintHandlers) { - const { - substituteNode = noEmitSubstitution, - onEmitNode = noEmitNotification, - isEmitNotificationEnabled - } = handlers; - - let pipelineResult: Node | undefined; - - // Outer visitors - // - // These visitors are invoked by inner visitors to re-enter the pipeline - // for notification and substitution. - - const visit = makeVisitor(pipelineVisitorForUnspecified); - const visitSourceFile = makeVisitor(pipelineVisitorForSourceFile, isSourceFile); - const visitIdentifierName = makeVisitor(pipelineVisitorForIdentifierName, isIdentifier); - const visitModuleName = makeVisitor(pipelineVisitorForIdentifierNameOrUnspecified, isModuleName); - const visitPropertyName = makeVisitor(pipelineVisitorForIdentifierNameOrUnspecified, isPropertyName); - const visitMemberName = makeVisitor(pipelineVisitorForIdentifierNameOrUnspecified, isMemberName); - const visitBindingName = makeVisitor(pipelineVisitorForIdentifierNameOrUnspecified, isBindingName); - const visitEntityName = makeVisitor(pipelineVisitorForIdentifierReferenceOrUnspecified, isEntityName); - const visitExpression = makeVisitor(pipelineVisitorForExpression, isExpression); - const visitForInitializer = makeVisitor(pipelineVisitorForForInitializer, isForInitializer); - const visitTypeNode = makeVisitor(pipelineVisitorForUnspecified, isTypeNode); - const visitEmbeddedStatement = makeVisitor(pipelineVisitorForEmbeddedStatement, isStatement, factory.liftToBlock); - const visitJsxAttributeValue = makeVisitor(pipelineVisitorForJsxAttributeValue, isStringLiteralOrJsxExpression); - const visitMappedTypeParameter = makeVisitor(pipelineVisitorForMappedTypeParameter, isTypeParameterDeclaration); - const visitConciseBody = makeVisitor(pipelineVisitorForConciseBody, isConciseBody); - const visitFunctionBody = makeVisitor(pipelineVisitorForUnspecified, isFunctionBody); - const visitList = makeListVisitor(pipelineVisitorForUnspecified); - const visitTypeNodeList = makeListVisitor(pipelineVisitorForUnspecified, isTypeNode); - const visitExpressionList = makeListVisitor(pipelineVisitorForExpression, isExpression); - const visitParameterList = makeListVisitor(pipelineVisitorForUnspecified, isParameter); - - function makeVisitor(outerVisitor: (node: Node) => Node | undefined, defaultTest?: (node: Node) => node is T, lift?: (nodes: readonly Node[]) => Node) { - function visit(node: T, test: (node: Node) => node is U): U; - function visit(node: T | undefined, test: (node: Node) => node is U): U | undefined; - function visit(node: T, test?: (node: Node) => node is T): T; - function visit(node: T | undefined, test?: (node: Node) => node is T): T | undefined; - function visit(node: Node | undefined, test?: (node: Node) => node is T): Node | undefined { - return visitNode(node, outerVisitor, test || defaultTest, lift); - } - return visit; - } - - function makeListVisitor(outerVisitor: (node: Node) => Node | undefined, defaultTest?: (node: Node) => node is T) { - function visitList(nodes: NodeArray, test: (node: Node) => node is U): NodeArray; - function visitList(nodes: NodeArray | undefined, test: (node: Node) => node is U): NodeArray | undefined; - function visitList(nodes: NodeArray, test?: (node: Node) => boolean): NodeArray; - function visitList(nodes: NodeArray | undefined, test?: (node: Node) => boolean): NodeArray | undefined; - function visitList(nodes: NodeArray | undefined, test: (node: Node) => boolean = defaultTest || returnTrue): NodeArray | undefined { - return visitNodes(nodes, outerVisitor, test); - } - return visitList; - } - - // Pipeline Visitors - // - // These visitors execute our existing pipeline logic for notification and substitution, - // but adapted to our visitor pattern. In some cases, we refine the `EmitHint` we pass - // to the `onEmitNode` and `substituteNode` APIs to ensure they receive the appropriate - // context. - // - // For example, the ConciseBody of an arrow function could be an Identifier, in which - // case we would want to use `EmitHint.Expression` to ensure we treat the identifier - // as an expression during substitution. - - function pipelineVisitorForSourceFile(node: SourceFile) { return pipelineVisitorWorker(EmitHint.SourceFile, node); } - function pipelineVisitorForExpression(node: Expression) { return pipelineVisitorWorker(EmitHint.Expression, node); } - function pipelineVisitorForIdentifierName(node: Identifier) { return pipelineVisitorWorker(EmitHint.IdentifierName, node); } - function pipelineVisitorForIdentifierNameOrUnspecified(node: Node) { return pipelineVisitorWorker(isIdentifier(node) ? EmitHint.IdentifierName : EmitHint.Unspecified, node); } - function pipelineVisitorForIdentifierReferenceOrUnspecified(node: Node) { return pipelineVisitorWorker(isIdentifier(node) ? EmitHint.Expression : EmitHint.Unspecified, node); } - function pipelineVisitorForForInitializer(node: ForInitializer) { return pipelineVisitorWorker(isVariableDeclarationList(node) ? EmitHint.Unspecified : EmitHint.Expression, node); } - function pipelineVisitorForMappedTypeParameter(node: TypeParameterDeclaration) { return pipelineVisitorWorker(EmitHint.MappedTypeParameter, node); } - function pipelineVisitorForEmbeddedStatement(node: Statement) { return pipelineVisitorWorker(isEmptyStatement(node) ? EmitHint.EmbeddedStatement : EmitHint.Unspecified, node); } - function pipelineVisitorForJsxAttributeValue(node: StringLiteral | JsxExpression) { return pipelineVisitorWorker(isStringLiteral(node) ? EmitHint.JsxAttributeValue : EmitHint.Unspecified, node); } - function pipelineVisitorForConciseBody(node: ConciseBody) { return pipelineVisitorWorker(isBlock(node) ? EmitHint.Unspecified : EmitHint.Expression, node); } - function pipelineVisitorForUnspecified(node: Node) { return pipelineVisitorWorker(EmitHint.Unspecified, node); } - - /** - * Adapts the emit pipeline API to work with the visitor API - */ - function pipelineVisitorWorker(hint: EmitHint, node: Node) { - resetPipelineResult(); - // Get the first supported pipeline phase for this node and evaluate it. We can skip several stack - // frames if we aren't doing emit notification, so we check for substitution and direct callbacks - // and execute those immediately. - const pipelinePhase = getPipelinePhase(PreprintPipelinePhase.Notification, node); - if (pipelinePhase === pipelineVisitDirect) { - return visitor(hint, node); - } - - if (pipelinePhase === pipelineVisitWithSubstitution) { - // The next phase after substitution is always direct visitation, so we can reduce the call stack - // depth by calling the visitor directly. - return visitor(hint, substituteNode(hint, node)); - } - - pipelinePhase(hint, node); - Debug.assertIsDefined(pipelineResult); - const result = pipelineResult; - resetPipelineResult(); - return result; - } - - function resetPipelineResult() { - pipelineResult = undefined; - } - - /** - * Gets the pipeline callback to pass to the relevant API (i.e., `substituteNode` or `onEmitNode`) - */ - function getPipelinePhase(phase: PreprintPipelinePhase, node: Node) { - switch (phase) { - case PreprintPipelinePhase.Notification: - if (onEmitNode !== noEmitNotification && (!isEmitNotificationEnabled || isEmitNotificationEnabled(node))) { - return pipelineVisitWithNotification; - } - // falls through - case PreprintPipelinePhase.Substitution: - if (substituteNode !== noEmitSubstitution) { - return pipelineVisitWithSubstitution; - } - // falls through - default: - return pipelineVisitDirect; - } - } - - /** - * A callback that can be evaluated to trigger emit notification as part of the emit pipeline. - */ - function pipelineVisitWithNotification(hint: EmitHint, node: Node) { - onEmitNode(hint, node, getPipelinePhase(PreprintPipelinePhase.Substitution, node)); - } - - /** - * A callback that can be evaluated to trigger JIT substitution as part of the emit pipeline. - */ - function pipelineVisitWithSubstitution(hint: EmitHint, node: Node) { - // Next phase is always direct visitation, so we can reduce the call stack - // depth by calling the visitor directly. - pipelineResult = visitor(hint, substituteNode(hint, node)); - } - - /** - * A callback that can be evaluated to visit the subtree of a node. - */ - function pipelineVisitDirect(hint: EmitHint, node: Node) { - pipelineResult = visitor(hint, node); - } - - /** - * Re-enters the visitor pattern from the pipeline pattern to perform - * tree updates and trigger parenthesization rules. - */ - function visitor(hint: EmitHint, node: Node): Node { - // This should align with the assertions in `pipelineEmitWithHint`. - if (hint === EmitHint.SourceFile) return preprintSourceFile(cast(node, isSourceFile)); - if (hint === EmitHint.IdentifierName) return preprintIdentifier(cast(node, isIdentifier)); - if (hint === EmitHint.JsxAttributeValue) return cast(node, isStringLiteral); - if (hint === EmitHint.MappedTypeParameter) return preprintTypeParameterDeclaration(cast(node, isTypeParameterDeclaration)); - if (hint === EmitHint.EmbeddedStatement) return cast(node, isEmptyStatement); - - const kind = node.kind; - // No need to visit nodes without children. - if ((kind > SyntaxKind.FirstToken && kind <= SyntaxKind.LastToken) || kind === SyntaxKind.ThisType) { - return node; - } - - if (hint === EmitHint.Unspecified) { - if (isKeyword(node.kind)) return node; - - switch (node.kind) { - // Identifiers - case SyntaxKind.Identifier: - return preprintIdentifier(node as Identifier); - - // Names - case SyntaxKind.QualifiedName: - Debug.type(node); - return factory.updateQualifiedName(node, - visitEntityName(node.left), - visitIdentifierName(node.right)); - - case SyntaxKind.ComputedPropertyName: - Debug.type(node); - return factory.updateComputedPropertyName(node, - visitExpression(node.expression)); - - // Signature elements - case SyntaxKind.TypeParameter: - return preprintTypeParameterDeclaration(node as TypeParameterDeclaration); - - case SyntaxKind.Parameter: - Debug.type(node); - return factory.updateParameterDeclaration(node, - visitList(node.decorators, isDecorator), - visitList(node.modifiers, isModifier), - visit(node.dotDotDotToken, isDotDotDotToken), - visitBindingName(node.name), - visit(node.questionToken, isQuestionToken), - visitTypeNode(node.type), - visitExpression(node.initializer)); - - case SyntaxKind.Decorator: - Debug.type(node); - return factory.updateDecorator(node, - visitExpression(node.expression)); - - // Type members - case SyntaxKind.PropertySignature: - Debug.type(node); - return factory.updatePropertySignature(node, - visitList(node.modifiers, isModifier), - visitPropertyName(node.name), - visit(node.questionToken, isQuestionToken), - visitTypeNode(node.type)); - - case SyntaxKind.PropertyDeclaration: - Debug.type(node); - return factory.updatePropertyDeclaration(node, - visitList(node.decorators, isDecorator), - visitList(node.modifiers, isModifier), - visitPropertyName(node.name), - visit(node.questionToken || node.exclamationToken, isQuestionOrExclamationToken), - visitTypeNode(node.type), - visitExpression(node.initializer)); - - case SyntaxKind.MethodSignature: - Debug.type(node); - return factory.updateMethodSignature(node, - visitList(node.modifiers, isModifier), - visitPropertyName(node.name), - visit(node.questionToken, isQuestionToken), - visitList(node.typeParameters, isTypeParameterDeclaration), - visitParameterList(node.parameters), - visitTypeNode(node.type)); - - case SyntaxKind.MethodDeclaration: - Debug.type(node); - return factory.updateMethodDeclaration(node, - visitList(node.decorators, isDecorator), - visitList(node.modifiers, isModifier), - visit(node.asteriskToken, isAsteriskToken), - visitPropertyName(node.name), - visit(node.questionToken, isQuestionToken), - visitList(node.typeParameters, isTypeParameterDeclaration), - visitParameterList(node.parameters), - visitTypeNode(node.type), - visitFunctionBody(node.body)); - - case SyntaxKind.Constructor: - Debug.type(node); - return factory.updateConstructorDeclaration(node, - visitList(node.decorators, isDecorator), - visitList(node.modifiers, isModifier), - visitParameterList(node.parameters), - visitFunctionBody(node.body)); - - case SyntaxKind.GetAccessor: - Debug.type(node); - return factory.updateGetAccessorDeclaration(node, - visitList(node.decorators, isDecorator), - visitList(node.modifiers, isModifier), - visitPropertyName(node.name), - visitParameterList(node.parameters), - visitTypeNode(node.type), - visitFunctionBody(node.body)); - - case SyntaxKind.SetAccessor: - Debug.type(node); - return factory.updateSetAccessorDeclaration(node, - visitList(node.decorators, isDecorator), - visitList(node.modifiers, isModifier), - visitPropertyName(node.name), - visitParameterList(node.parameters), - visitFunctionBody(node.body)); - - case SyntaxKind.CallSignature: - Debug.type(node); - return factory.updateCallSignature(node, - visitList(node.typeParameters, isTypeParameterDeclaration), - visitParameterList(node.parameters), - visitTypeNode(node.type)); - - case SyntaxKind.ConstructSignature: - Debug.type(node); - return factory.updateConstructSignature(node, - visitList(node.typeParameters, isTypeParameterDeclaration), - visitParameterList(node.parameters), - visitTypeNode(node.type)); - - case SyntaxKind.IndexSignature: - Debug.type(node); - return factory.updateIndexSignature(node, - visitList(node.decorators, isDecorator), - visitList(node.modifiers, isModifier), - visitParameterList(node.parameters), - visitTypeNode(node.type)); - - // Types - case SyntaxKind.TypePredicate: - Debug.type(node); - return factory.updateTypePredicateNode(node, - visit(node.assertsModifier, isAssertsKeyword), - visit(node.parameterName, isIdentifierOrThisTypeNode), - visitTypeNode(node.type)); - - case SyntaxKind.TypeReference: - Debug.type(node); - return factory.updateTypeReferenceNode(node, - visitEntityName(node.typeName), - visitTypeNodeList(node.typeArguments)); - - case SyntaxKind.FunctionType: - Debug.type(node); - return factory.updateFunctionTypeNode(node, - visitList(node.typeParameters, isTypeParameterDeclaration), - visitNodes(node.parameters, pipelineVisitorForUnspecified, isParameterDeclaration), - visitTypeNode(node.type)); - - case SyntaxKind.ConstructorType: - Debug.type(node); - return factory.updateConstructorTypeNode(node, - visitNodes(node.modifiers, pipelineVisitorForUnspecified, isModifier), - visitList(node.typeParameters, isTypeParameterDeclaration), - visitParameterList(node.parameters), - visitTypeNode(node.type)); - - case SyntaxKind.TypeQuery: - Debug.type(node); - return factory.updateTypeQueryNode(node, - visitEntityName(node.exprName)); - - case SyntaxKind.TypeLiteral: - Debug.type(node); - return factory.updateTypeLiteralNode(node, - visitList(node.members, isTypeElement)); - - case SyntaxKind.ArrayType: - Debug.type(node); - return factory.updateArrayTypeNode(node, - visitTypeNode(node.elementType)); - - case SyntaxKind.TupleType: - Debug.type(node); - return factory.updateTupleTypeNode(node, - visitTypeNodeList(node.elements)); - - case SyntaxKind.OptionalType: - Debug.type(node); - return factory.updateOptionalTypeNode(node, - visitTypeNode(node.type)); - - case SyntaxKind.RestType: - Debug.type(node); - return factory.updateRestTypeNode(node, - visitTypeNode(node.type)); - - case SyntaxKind.UnionType: - Debug.type(node); - return factory.updateUnionTypeNode(node, - visitTypeNodeList(node.types)); - - case SyntaxKind.IntersectionType: - Debug.type(node); - return factory.updateIntersectionTypeNode(node, - visitTypeNodeList(node.types)); - - case SyntaxKind.ConditionalType: - Debug.type(node); - return factory.updateConditionalTypeNode(node, - visitTypeNode(node.checkType), - visitTypeNode(node.extendsType), - visitTypeNode(node.trueType), - visitTypeNode(node.falseType)); - - case SyntaxKind.InferType: - Debug.type(node); - return factory.updateInferTypeNode(node, - visit(node.typeParameter, isTypeParameterDeclaration)); - - case SyntaxKind.ImportType: - Debug.type(node); - return factory.updateImportTypeNode(node, - visitTypeNode(node.argument), - visitEntityName(node.qualifier), - visitTypeNodeList(node.typeArguments), - node.isTypeOf - ); - - case SyntaxKind.NamedTupleMember: - Debug.type(node); - return factory.updateNamedTupleMember(node, - visit(node.dotDotDotToken, isDotDotDotToken), - visitIdentifierName(node.name), - visit(node.questionToken, isQuestionToken), - visitTypeNode(node.type), - ); - - case SyntaxKind.ParenthesizedType: - Debug.type(node); - return factory.updateParenthesizedType(node, - visitTypeNode(node.type)); - - case SyntaxKind.ExpressionWithTypeArguments: - Debug.type(node); - return factory.updateExpressionWithTypeArguments(node, - visitExpression(node.expression), - visitTypeNodeList(node.typeArguments)); - - case SyntaxKind.TypeOperator: - Debug.type(node); - return factory.updateTypeOperatorNode(node, - visitTypeNode(node.type)); - - case SyntaxKind.IndexedAccessType: - Debug.type(node); - return factory.updateIndexedAccessTypeNode(node, - visitTypeNode(node.objectType), - visitTypeNode(node.indexType)); - - case SyntaxKind.MappedType: - Debug.type(node); - return factory.updateMappedTypeNode(node, - visit(node.readonlyToken, isReadonlyKeywordOrPlusOrMinusToken), - visitMappedTypeParameter(node.typeParameter), - visitTypeNode(node.nameType), - visit(node.questionToken, isQuestionOrPlusOrMinusToken), - visitTypeNode(node.type)); - - case SyntaxKind.LiteralType: - Debug.type(node); - return factory.updateLiteralTypeNode(node, - visitExpression(node.literal, isLiteralTypeLikeExpression)); - - case SyntaxKind.TemplateLiteralType: - Debug.type(node); - return factory.updateTemplateLiteralType(node, - visit(node.head, isTemplateHead), - visitList(node.templateSpans, isTemplateLiteralTypeSpan)); - - case SyntaxKind.TemplateLiteralTypeSpan: - Debug.type(node); - return factory.updateTemplateLiteralTypeSpan(node, - visitTypeNode(node.type), - visit(node.literal, isTemplateMiddleOrTemplateTail)); - - // Binding patterns - case SyntaxKind.ObjectBindingPattern: - Debug.type(node); - return factory.updateObjectBindingPattern(node, - visitList(node.elements, isBindingElement)); - - case SyntaxKind.ArrayBindingPattern: - Debug.type(node); - return factory.updateArrayBindingPattern(node, - visitList(node.elements, isArrayBindingElement)); - - case SyntaxKind.BindingElement: - Debug.type(node); - return factory.updateBindingElement(node, - visit(node.dotDotDotToken, isDotDotDotToken), - visitPropertyName(node.propertyName), - visitBindingName(node.name), - visitExpression(node.initializer)); - - // Misc - case SyntaxKind.TemplateSpan: - Debug.type(node); - return factory.updateTemplateSpan(node, - visitExpression(node.expression), - visit(node.literal, isTemplateMiddleOrTemplateTail)); - - // Element - case SyntaxKind.Block: - Debug.type(node); - return factory.updateBlock(node, - visitList(node.statements, isStatement)); - - case SyntaxKind.VariableStatement: - Debug.type(node); - return factory.updateVariableStatement(node, - visitList(node.modifiers, isModifier), - visit(node.declarationList, isVariableDeclarationList)); - - case SyntaxKind.ExpressionStatement: - Debug.type(node); - return factory.updateExpressionStatement(node, - visitExpression(node.expression)); - - case SyntaxKind.IfStatement: - Debug.type(node); - return factory.updateIfStatement(node, - visitExpression(node.expression), - visitEmbeddedStatement(node.thenStatement), - visitEmbeddedStatement(node.elseStatement)); - - case SyntaxKind.DoStatement: - Debug.type(node); - return factory.updateDoStatement(node, - visitEmbeddedStatement(node.statement), - visitExpression(node.expression)); - - case SyntaxKind.WhileStatement: - Debug.type(node); - return factory.updateWhileStatement(node, - visitExpression(node.expression), - visitEmbeddedStatement(node.statement)); - - case SyntaxKind.ForStatement: - Debug.type(node); - return factory.updateForStatement(node, - visitForInitializer(node.initializer), - visitExpression(node.condition), - visitExpression(node.incrementor), - visitEmbeddedStatement(node.statement)); - - case SyntaxKind.ForInStatement: - Debug.type(node); - return factory.updateForInStatement(node, - visitForInitializer(node.initializer), - visitExpression(node.expression), - visitEmbeddedStatement(node.statement)); - - case SyntaxKind.ForOfStatement: - Debug.type(node); - return factory.updateForOfStatement(node, - visit(node.awaitModifier, isAwaitKeyword), - visitForInitializer(node.initializer), - visitExpression(node.expression), - visitEmbeddedStatement(node.statement)); - - case SyntaxKind.ContinueStatement: - Debug.type(node); - return factory.updateContinueStatement(node, - visitIdentifierName(node.label)); - - case SyntaxKind.BreakStatement: - Debug.type(node); - return factory.updateBreakStatement(node, - visitIdentifierName(node.label)); - - case SyntaxKind.ReturnStatement: - Debug.type(node); - return factory.updateReturnStatement(node, - visitExpression(node.expression)); - - case SyntaxKind.WithStatement: - Debug.type(node); - return factory.updateWithStatement(node, - visitExpression(node.expression), - visitEmbeddedStatement(node.statement)); - - case SyntaxKind.SwitchStatement: - Debug.type(node); - return factory.updateSwitchStatement(node, - visitExpression(node.expression), - visit(node.caseBlock, isCaseBlock)); - - case SyntaxKind.LabeledStatement: - Debug.type(node); - return factory.updateLabeledStatement(node, - visitIdentifierName(node.label), - visitEmbeddedStatement(node.statement)); - - case SyntaxKind.ThrowStatement: - Debug.type(node); - return factory.updateThrowStatement(node, - visitExpression(node.expression)); - - case SyntaxKind.TryStatement: - Debug.type(node); - return factory.updateTryStatement(node, - visit(node.tryBlock, isBlock), - visit(node.catchClause, isCatchClause), - visit(node.finallyBlock, isBlock)); - - // Declarations - case SyntaxKind.VariableDeclaration: - Debug.type(node); - return factory.updateVariableDeclaration(node, - visitBindingName(node.name), - visit(node.exclamationToken, isExclamationToken), - visitTypeNode(node.type), - visitExpression(node.initializer)); - - case SyntaxKind.VariableDeclarationList: - Debug.type(node); - return factory.updateVariableDeclarationList(node, - visitList(node.declarations, isVariableDeclaration)); - - case SyntaxKind.FunctionDeclaration: - Debug.type(node); - return factory.updateFunctionDeclaration(node, - visitList(node.decorators, isDecorator), - visitList(node.modifiers, isModifier), - visit(node.asteriskToken, isAsteriskToken), - visitIdentifierName(node.name), - visitList(node.typeParameters, isTypeParameterDeclaration), - visitParameterList(node.parameters), - visitTypeNode(node.type), - visitFunctionBody(node.body)); - - case SyntaxKind.ClassDeclaration: - Debug.type(node); - return factory.updateClassDeclaration(node, - visitList(node.decorators, isDecorator), - visitList(node.modifiers, isModifier), - visitIdentifierName(node.name), - visitList(node.typeParameters, isTypeParameterDeclaration), - visitList(node.heritageClauses, isHeritageClause), - visitList(node.members, isClassElement)); - - case SyntaxKind.InterfaceDeclaration: - Debug.type(node); - return factory.updateInterfaceDeclaration(node, - visitList(node.decorators, isDecorator), - visitList(node.modifiers, isModifier), - visitIdentifierName(node.name), - visitList(node.typeParameters, isTypeParameterDeclaration), - visitList(node.heritageClauses, isHeritageClause), - visitList(node.members, isTypeElement)); - - case SyntaxKind.TypeAliasDeclaration: - Debug.type(node); - return factory.updateTypeAliasDeclaration(node, - visitList(node.decorators, isDecorator), - visitList(node.modifiers, isModifier), - visitIdentifierName(node.name), - visitList(node.typeParameters, isTypeParameterDeclaration), - visitTypeNode(node.type)); - - case SyntaxKind.EnumDeclaration: - Debug.type(node); - return factory.updateEnumDeclaration(node, - visitList(node.decorators, isDecorator), - visitList(node.modifiers, isModifier), - visitIdentifierName(node.name), - visitList(node.members, isEnumMember)); - - case SyntaxKind.ModuleDeclaration: - Debug.type(node); - return factory.updateModuleDeclaration(node, - visitList(node.decorators, isDecorator), - visitList(node.modifiers, isModifier), - visitModuleName(node.name), - visit(node.body, isModuleBody)); - - case SyntaxKind.ModuleBlock: - Debug.type(node); - return factory.updateModuleBlock(node, - visitList(node.statements, isStatement)); - - case SyntaxKind.CaseBlock: - Debug.type(node); - return factory.updateCaseBlock(node, - visitList(node.clauses, isCaseOrDefaultClause)); - - case SyntaxKind.NamespaceExportDeclaration: - Debug.type(node); - return factory.updateNamespaceExportDeclaration(node, - visitIdentifierName(node.name)); - - case SyntaxKind.ImportEqualsDeclaration: - Debug.type(node); - return factory.updateImportEqualsDeclaration(node, - visitList(node.decorators, isDecorator), - visitList(node.modifiers, isModifier), - node.isTypeOnly, - visitIdentifierName(node.name), - visit(node.moduleReference, isModuleReference)); - - case SyntaxKind.ImportDeclaration: - Debug.type(node); - return factory.updateImportDeclaration(node, - visitList(node.decorators, isDecorator), - visitList(node.modifiers, isModifier), - visit(node.importClause, isImportClause), - visitExpression(node.moduleSpecifier)); - - case SyntaxKind.ImportClause: - Debug.type(node); - return factory.updateImportClause(node, - node.isTypeOnly, - visitIdentifierName(node.name), - visit(node.namedBindings, isNamedImportBindings)); - - case SyntaxKind.NamespaceImport: - Debug.type(node); - return factory.updateNamespaceImport(node, - visitIdentifierName(node.name)); - - case SyntaxKind.NamespaceExport: - Debug.type(node); - return factory.updateNamespaceExport(node, - visitIdentifierName(node.name)); - - case SyntaxKind.NamedImports: - Debug.type(node); - return factory.updateNamedImports(node, - visitList(node.elements, isImportSpecifier)); - - case SyntaxKind.ImportSpecifier: - Debug.type(node); - return factory.updateImportSpecifier(node, - visitIdentifierName(node.propertyName), - visitIdentifierName(node.name)); - - case SyntaxKind.ExportAssignment: - Debug.type(node); - return factory.updateExportAssignment(node, - visitList(node.decorators, isDecorator), - visitList(node.modifiers, isModifier), - visitExpression(node.expression)); - - case SyntaxKind.ExportDeclaration: - Debug.type(node); - return factory.updateExportDeclaration(node, - visitList(node.decorators, isDecorator), - visitList(node.modifiers, isModifier), - node.isTypeOnly, - visit(node.exportClause, isNamedExportBindings), - visitExpression(node.moduleSpecifier)); - - case SyntaxKind.NamedExports: - Debug.type(node); - return factory.updateNamedExports(node, - visitList(node.elements, isExportSpecifier)); - - case SyntaxKind.ExportSpecifier: - Debug.type(node); - return factory.updateExportSpecifier(node, - visitIdentifierName(node.propertyName), - visitIdentifierName(node.name)); - - case SyntaxKind.MissingDeclaration: - return node; - - // Module references - case SyntaxKind.ExternalModuleReference: - Debug.type(node); - return factory.updateExternalModuleReference(node, - visitExpression(node.expression)); - - // JSX (non-expression) - case SyntaxKind.JsxOpeningElement: - Debug.type(node); - return factory.updateJsxOpeningElement(node, - visitExpression(node.tagName, isJsxTagNameExpression), - visitList(node.typeArguments, isTypeNode), - visit(node.attributes, isJsxAttributes)); - - case SyntaxKind.JsxClosingElement: - Debug.type(node); - return factory.updateJsxClosingElement(node, - visitExpression(node.tagName, isJsxTagNameExpression)); - - case SyntaxKind.JsxAttribute: - Debug.type(node); - return factory.updateJsxAttribute(node, - visitIdentifierName(node.name), - visitJsxAttributeValue(node.initializer)); - - case SyntaxKind.JsxAttributes: - Debug.type(node); - return factory.updateJsxAttributes(node, - visitList(node.properties, isJsxAttributeLike)); - - case SyntaxKind.JsxSpreadAttribute: - Debug.type(node); - return factory.updateJsxSpreadAttribute(node, - visitExpression(node.expression)); - - case SyntaxKind.JsxExpression: - Debug.type(node); - return factory.updateJsxExpression(node, - visitExpression(node.expression)); - - // Clauses - case SyntaxKind.CaseClause: - Debug.type(node); - return factory.updateCaseClause(node, - visitExpression(node.expression), - visitList(node.statements, isStatement)); - - case SyntaxKind.DefaultClause: - Debug.type(node); - return factory.updateDefaultClause(node, - visitList(node.statements, isStatement)); - - case SyntaxKind.HeritageClause: - Debug.type(node); - return factory.updateHeritageClause(node, - visitList(node.types, isExpressionWithTypeArguments)); - - case SyntaxKind.CatchClause: - Debug.type(node); - return factory.updateCatchClause(node, - visit(node.variableDeclaration, isVariableDeclaration), - visit(node.block, isBlock)); - - // Property assignments - case SyntaxKind.PropertyAssignment: - Debug.type(node); - return factory.updatePropertyAssignment(node, - visitPropertyName(node.name), - visitExpression(node.initializer)); - - case SyntaxKind.ShorthandPropertyAssignment: - Debug.type(node); - return factory.updateShorthandPropertyAssignment(node, - visitIdentifierName(node.name, isIdentifier), - visitExpression(node.objectAssignmentInitializer)); - - case SyntaxKind.SpreadAssignment: - Debug.type(node); - return factory.updateSpreadAssignment(node, - visitExpression(node.expression)); - - // Enum - case SyntaxKind.EnumMember: - Debug.type(node); - return factory.updateEnumMember(node, - visitPropertyName(node.name), - visitExpression(node.initializer)); - - // JSDoc nodes (only used in codefixes currently) - case SyntaxKind.JSDocTypeExpression: - case SyntaxKind.JSDocNameReference: - case SyntaxKind.JSDocAllType: - case SyntaxKind.JSDocUnknownType: - case SyntaxKind.JSDocNullableType: - case SyntaxKind.JSDocNonNullableType: - case SyntaxKind.JSDocOptionalType: - case SyntaxKind.JSDocFunctionType: - case SyntaxKind.JSDocVariadicType: - case SyntaxKind.JSDocNamepathType: - case SyntaxKind.JSDocComment: - case SyntaxKind.JSDocTypeLiteral: - case SyntaxKind.JSDocSignature: - case SyntaxKind.JSDocTag: - case SyntaxKind.JSDocAugmentsTag: - case SyntaxKind.JSDocImplementsTag: - case SyntaxKind.JSDocAuthorTag: - case SyntaxKind.JSDocDeprecatedTag: - case SyntaxKind.JSDocClassTag: - case SyntaxKind.JSDocPublicTag: - case SyntaxKind.JSDocPrivateTag: - case SyntaxKind.JSDocProtectedTag: - case SyntaxKind.JSDocReadonlyTag: - case SyntaxKind.JSDocCallbackTag: - case SyntaxKind.JSDocEnumTag: - case SyntaxKind.JSDocParameterTag: - case SyntaxKind.JSDocPropertyTag: - case SyntaxKind.JSDocReturnTag: - case SyntaxKind.JSDocThisTag: - case SyntaxKind.JSDocTypeTag: - case SyntaxKind.JSDocTemplateTag: - case SyntaxKind.JSDocTypedefTag: - case SyntaxKind.JSDocSeeTag: - return node; - - // Transformation nodes (ignored) - } - - if (isExpression(node)) { - // If this was an expression that was originally in an `Unspecified` hint, - // re-trigger substitution using the correct hint. - hint = EmitHint.Expression; - if (substituteNode !== noEmitSubstitution) { - node = substituteNode(hint, node); - } - } - else if (isSourceFile(node)) { - return preprintSourceFile(node); - } - } - - if (hint === EmitHint.Expression) { - switch (node.kind) { - // Identifiers - case SyntaxKind.Identifier: - return preprintIdentifier(node as Identifier); - - // Expression - case SyntaxKind.ArrayLiteralExpression: - Debug.type(node); - return factory.updateArrayLiteralExpression(node, - visitExpressionList(node.elements)); - - case SyntaxKind.ObjectLiteralExpression: - Debug.type(node); - return factory.updateObjectLiteralExpression(node, - visitList(node.properties, isObjectLiteralElementLike)); - - case SyntaxKind.PropertyAccessExpression: - if (node.flags & NodeFlags.OptionalChain) { - Debug.type(node); - return factory.updatePropertyAccessChain(node, - visitExpression(node.expression), - visit(node.questionDotToken, isQuestionDotToken), - visitMemberName(node.name)); - } - Debug.type(node); - return factory.updatePropertyAccessExpression(node, - visitExpression(node.expression), - visitMemberName(node.name)); - - case SyntaxKind.ElementAccessExpression: - if (node.flags & NodeFlags.OptionalChain) { - Debug.type(node); - return factory.updateElementAccessChain(node, - visitExpression(node.expression), - visit(node.questionDotToken, isQuestionDotToken), - visitExpression(node.argumentExpression)); - } - Debug.type(node); - return factory.updateElementAccessExpression(node, - visitExpression(node.expression), - visitExpression(node.argumentExpression)); - - case SyntaxKind.CallExpression: - if (node.flags & NodeFlags.OptionalChain) { - Debug.type(node); - return factory.updateCallChain(node, - visitExpression(node.expression), - visit(node.questionDotToken, isQuestionDotToken), - visitTypeNodeList(node.typeArguments), - visitExpressionList(node.arguments)); - } - Debug.type(node); - return factory.updateCallExpression(node, - visitExpression(node.expression), - visitTypeNodeList(node.typeArguments), - visitExpressionList(node.arguments)); - - case SyntaxKind.NewExpression: - Debug.type(node); - return factory.updateNewExpression(node, - visitExpression(node.expression), - visitTypeNodeList(node.typeArguments), - visitExpressionList(node.arguments)); - - case SyntaxKind.TaggedTemplateExpression: - Debug.type(node); - return factory.updateTaggedTemplateExpression(node, - visitExpression(node.tag), - visitTypeNodeList(node.typeArguments), - visitExpression(node.template, isTemplateLiteral)); - - case SyntaxKind.TypeAssertionExpression: - Debug.type(node); - return factory.updateTypeAssertion(node, - visitTypeNode(node.type), - visitExpression(node.expression)); - - case SyntaxKind.ParenthesizedExpression: - Debug.type(node); - return factory.updateParenthesizedExpression(node, - visitExpression(node.expression)); - - case SyntaxKind.FunctionExpression: - Debug.type(node); - return factory.updateFunctionExpression(node, - visitList(node.modifiers, isModifier), - visit(node.asteriskToken, isAsteriskToken), - visitIdentifierName(node.name), - visitList(node.typeParameters, isTypeParameterDeclaration), - visitParameterList(node.parameters), - visitTypeNode(node.type), - visitFunctionBody(node.body)); - - case SyntaxKind.ArrowFunction: - Debug.type(node); - return factory.updateArrowFunction(node, - visitList(node.modifiers, isModifier), - visitList(node.typeParameters, isTypeParameterDeclaration), - visitParameterList(node.parameters), - visitTypeNode(node.type), - visit(node.equalsGreaterThanToken, isEqualsGreaterThanToken), - visitConciseBody(node.body)); - - case SyntaxKind.DeleteExpression: - Debug.type(node); - return factory.updateDeleteExpression(node, - visitExpression(node.expression)); - - case SyntaxKind.TypeOfExpression: - Debug.type(node); - return factory.updateTypeOfExpression(node, - visitExpression(node.expression)); - - case SyntaxKind.VoidExpression: - Debug.type(node); - return factory.updateVoidExpression(node, - visitExpression(node.expression)); - - case SyntaxKind.AwaitExpression: - Debug.type(node); - return factory.updateAwaitExpression(node, - visitExpression(node.expression)); - - case SyntaxKind.PrefixUnaryExpression: - Debug.type(node); - return factory.updatePrefixUnaryExpression(node, - visitExpression(node.operand)); - - case SyntaxKind.PostfixUnaryExpression: - Debug.type(node); - return factory.updatePostfixUnaryExpression(node, - visitExpression(node.operand)); - - case SyntaxKind.BinaryExpression: - return preprintBinaryExpression(node as BinaryExpression); - - case SyntaxKind.ConditionalExpression: - Debug.type(node); - return factory.updateConditionalExpression(node, - visitExpression(node.condition), - visit(node.questionToken, isQuestionToken), - visitExpression(node.whenTrue), - visit(node.colonToken, isColonToken), - visitExpression(node.whenFalse)); - - case SyntaxKind.TemplateExpression: - Debug.type(node); - return factory.updateTemplateExpression(node, - visit(node.head, isTemplateHead), - visitList(node.templateSpans, isTemplateSpan)); - - case SyntaxKind.YieldExpression: - Debug.type(node); - return factory.updateYieldExpression(node, - visit(node.asteriskToken, isAsteriskToken), - visitExpression(node.expression)); - - case SyntaxKind.SpreadElement: - Debug.type(node); - return factory.updateSpreadElement(node, - visitExpression(node.expression)); - - case SyntaxKind.ClassExpression: - Debug.type(node); - return factory.updateClassExpression(node, - visitList(node.decorators, isDecorator), - visitList(node.modifiers, isModifier), - visitIdentifierName(node.name), - visitList(node.typeParameters, isTypeParameterDeclaration), - visitList(node.heritageClauses, isHeritageClause), - visitList(node.members, isClassElement)); - - case SyntaxKind.AsExpression: - Debug.type(node); - return factory.updateAsExpression(node, - visitExpression(node.expression), - visitTypeNode(node.type)); - - case SyntaxKind.NonNullExpression: - if (node.flags & NodeFlags.OptionalChain) { - Debug.type(node); - return factory.updateNonNullChain(node, - visitExpression(node.expression)); - } - Debug.type(node); - return factory.updateNonNullExpression(node, - visitExpression(node.expression)); - - case SyntaxKind.MetaProperty: - Debug.type(node); - return factory.updateMetaProperty(node, - visitIdentifierName(node.name)); - - - // JSX (expression only) - case SyntaxKind.JsxElement: - Debug.type(node); - return factory.updateJsxElement(node, - visit(node.openingElement, isJsxOpeningElement), - visitList(node.children, isJsxChild), - visit(node.closingElement, isJsxClosingElement)); - - case SyntaxKind.JsxSelfClosingElement: - Debug.type(node); - return factory.updateJsxSelfClosingElement(node, - visitExpression(node.tagName, isJsxTagNameExpression), - visitList(node.typeArguments, isTypeNode), - visit(node.attributes, isJsxAttributes)); - - case SyntaxKind.JsxFragment: - Debug.type(node); - return factory.updateJsxFragment(node, - visit(node.openingFragment, isJsxOpeningFragment), - visitList(node.children, isJsxChild), - visit(node.closingFragment, isJsxClosingFragment)); - - // Transformation nodes - case SyntaxKind.PartiallyEmittedExpression: - Debug.type(node); - return factory.updatePartiallyEmittedExpression(node, - visitExpression(node.expression)); - - case SyntaxKind.CommaListExpression: - Debug.type(node); - return factory.updateCommaListExpression(node, - visitExpressionList(node.elements, isExpression)); - } - } - - if (Debug.shouldAssert(AssertionLevel.Normal)) { - // Any other node should not have children or this list isn't up to date. - Debug.assertMissingNode(forEachChild(node, identity), `Expected ${Debug.formatSyntaxKind(node.kind)} to contain no children.`); - } - - // No need to visit nodes with no children. - return node; - } - - function preprintSourceFile(node: SourceFile) { - return factory.updateSourceFile(node, - visitList(node.statements, isStatement)); - } - - function preprintIdentifier(node: Identifier) { - return factory.updateIdentifier(node, - visitList(node.typeArguments, isTypeNodeOrTypeParameterDeclaration)); - } - - function preprintTypeParameterDeclaration(node: TypeParameterDeclaration) { - return factory.updateTypeParameterDeclaration(node, - visitIdentifierName(node.name), - visitTypeNode(node.constraint), - visitTypeNode(node.default)); - } - - function createPreprintBinaryExpression() { - interface WorkArea { - stackIndex: number; - leftStack: Expression[]; - operatorStack: BinaryOperatorToken[]; - rightStack: Expression[]; - } - - return createBinaryExpressionTrampoline(onEnter, onLeft, onOperator, onRight, onExit, foldState); - - function onEnter(node: BinaryExpression, state: WorkArea | undefined) { - if (state) { - state.stackIndex++; - state.leftStack[state.stackIndex] = node.left; - state.operatorStack[state.stackIndex] = node.operatorToken; - state.rightStack[state.stackIndex] = node.right; - } - else { - state = { - stackIndex: 0, - leftStack: [node.left], - operatorStack: [node.operatorToken], - rightStack: [node.right], - }; - } - return state; - } - - function onLeft(left: Expression, state: WorkArea, _node: BinaryExpression) { - return maybeVisitExpression(left, state, "left"); - } - - function onOperator(operator: BinaryOperatorToken, state: WorkArea, _node: BinaryExpression) { - state.operatorStack[state.stackIndex] = visit(operator, isBinaryOperatorToken); - } - - function onRight(right: Expression, state: WorkArea, _node: BinaryExpression) { - return maybeVisitExpression(right, state, "right"); - } - - function onExit(node: BinaryExpression, state: WorkArea) { - const left = state.leftStack[state.stackIndex]; - const operator = state.operatorStack[state.stackIndex]; - const right = state.rightStack[state.stackIndex]; - if (state.stackIndex > 0) { - state.stackIndex--; - } - return factory.updateBinaryExpression(node, left, operator, right); - } - - function foldState(state: WorkArea, result: BinaryExpression, side: "left" | "right") { - (side === "left" ? state.leftStack : state.rightStack)[state.stackIndex] = result; - return state; - } - - function maybeVisitExpression(node: Expression, state: WorkArea, side: "left" | "right") { - // Get the first supported pipeline phase for this node. We can skip several stack - // frames if we aren't doing emit notification, so we check for substitution and - // direct callbacks and execute those immediately. - let pipelinePhase = getPipelinePhase(PreprintPipelinePhase.Notification, node); - if (pipelinePhase === pipelineVisitWithSubstitution) { - // The next phase after substitution is always direct visitation, so we can reduce the call stack - // depth by proceding to the direct visitor. - node = cast(substituteNode(EmitHint.Expression, node), isExpression); - pipelinePhase = pipelineVisitDirect; - } - if (pipelinePhase === pipelineVisitDirect && isBinaryExpression(node)) { - // If we are visiting directly and the next node is a BinaryExpression, we can - // add it to the stack and continue the trampoline. - return node; - } - else { - // Visit the expression and store the result on whichever side we are currently visiting. - (side === "left" ? state.leftStack : state.rightStack)[state.stackIndex] = visitExpression(node, isExpression); - } - } - } - - const preprintBinaryExpression = createPreprintBinaryExpression(); - - function preprint(hint: EmitHint, node: Node) { - // If we're not performing substitution or notification, we have no work to do here. - if (substituteNode === noEmitSubstitution && - onEmitNode === noEmitNotification) { - return node; - } - switch (hint) { - case EmitHint.SourceFile: return visitSourceFile(cast(node, isSourceFile)); - case EmitHint.Expression: return visitExpression(cast(node, isExpression)); - case EmitHint.IdentifierName: return visitIdentifierName(cast(node, isIdentifier)); - case EmitHint.MappedTypeParameter: return visitMappedTypeParameter(cast(node, isTypeParameterDeclaration)); - case EmitHint.EmbeddedStatement: return visitEmbeddedStatement(cast(node, isStatement)); - case EmitHint.JsxAttributeValue: return visitJsxAttributeValue(cast(node, isStringLiteralOrJsxExpression)); - default: return visit(node); - } - } - - return preprint; - } - function createBracketsMap() { const brackets: string[][] = []; brackets[ListFormat.Braces] = ["{", "}"]; diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index 7138f8d4c9f49..750d0d2c9ec02 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -2139,7 +2139,7 @@ namespace ts { /*decorators*/ undefined, /*modifiers*/ undefined, name, - initializer + initializer && parenthesizerRules().parenthesizeExpressionForDisallowedComma(initializer) ); node.propertyName = asName(propertyName); node.dotDotDotToken = dotDotDotToken; diff --git a/src/compiler/factory/parenthesizerRules.ts b/src/compiler/factory/parenthesizerRules.ts index 069df2d60c9ae..89492f82bde48 100644 --- a/src/compiler/factory/parenthesizerRules.ts +++ b/src/compiler/factory/parenthesizerRules.ts @@ -5,7 +5,12 @@ namespace ts { cachedLiteralKind: SyntaxKind; } + let binaryLeftOperandParenthesizerCache: ESMap Expression> | undefined; + let binaryRightOperandParenthesizerCache: ESMap Expression> | undefined; + return { + getParenthesizeLeftSideOfBinaryForOperator, + getParenthesizeRightSideOfBinaryForOperator, parenthesizeLeftSideOfBinary, parenthesizeRightSideOfBinary, parenthesizeExpressionOfComputedPropertyName, @@ -27,6 +32,26 @@ namespace ts { parenthesizeTypeArguments, }; + function getParenthesizeLeftSideOfBinaryForOperator(operatorKind: BinaryOperator) { + binaryLeftOperandParenthesizerCache ||= new Map(); + let parenthesizerRule = binaryLeftOperandParenthesizerCache.get(operatorKind); + if (!parenthesizerRule) { + parenthesizerRule = node => parenthesizeLeftSideOfBinary(operatorKind, node); + binaryLeftOperandParenthesizerCache.set(operatorKind, parenthesizerRule); + } + return parenthesizerRule; + } + + function getParenthesizeRightSideOfBinaryForOperator(operatorKind: BinaryOperator) { + binaryRightOperandParenthesizerCache ||= new Map(); + let parenthesizerRule = binaryRightOperandParenthesizerCache.get(operatorKind); + if (!parenthesizerRule) { + parenthesizerRule = node => parenthesizeRightSideOfBinary(operatorKind, /*leftSide*/ undefined, node); + binaryRightOperandParenthesizerCache.set(operatorKind, parenthesizerRule); + } + return parenthesizerRule; + } + /** * Determines whether the operand to a BinaryExpression needs to be parenthesized. * @@ -210,7 +235,7 @@ namespace ts { return parenthesizeBinaryOperand(binaryOperator, leftSide, /*isLeftSideOfBinary*/ true); } - function parenthesizeRightSideOfBinary(binaryOperator: SyntaxKind, leftSide: Expression, rightSide: Expression): Expression { + function parenthesizeRightSideOfBinary(binaryOperator: SyntaxKind, leftSide: Expression | undefined, rightSide: Expression): Expression { return parenthesizeBinaryOperand(binaryOperator, rightSide, /*isLeftSideOfBinary*/ false, leftSide); } @@ -352,6 +377,8 @@ namespace ts { return expression; } + function parenthesizeConciseBodyOfArrowFunction(body: Expression): Expression; + function parenthesizeConciseBodyOfArrowFunction(body: ConciseBody): ConciseBody; function parenthesizeConciseBodyOfArrowFunction(body: ConciseBody): ConciseBody { if (!isBlock(body) && (isCommaSequence(body) || getLeftmostExpression(body, /*stopAtCallExpressions*/ false).kind === SyntaxKind.ObjectLiteralExpression)) { // TODO(rbuckton): Verifiy whether `setTextRange` is needed. @@ -403,6 +430,8 @@ namespace ts { } export const nullParenthesizerRules: ParenthesizerRules = { + getParenthesizeLeftSideOfBinaryForOperator: _ => identity, + getParenthesizeRightSideOfBinaryForOperator: _ => identity, parenthesizeLeftSideOfBinary: (_binaryOperator, leftSide) => leftSide, parenthesizeRightSideOfBinary: (_binaryOperator, _leftSide, rightSide) => rightSide, parenthesizeExpressionOfComputedPropertyName: identity, diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index 015394dcc607b..732205c00a34b 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -562,31 +562,31 @@ namespace ts { } export const nullTransformationContext: TransformationContext = { - get factory() { return factory; }, - enableEmitNotification: noop, - enableSubstitution: noop, - endLexicalEnvironment: returnUndefined, + factory, getCompilerOptions: () => ({}), - getEmitHost: notImplemented, getEmitResolver: notImplemented, + getEmitHost: notImplemented, getEmitHelperFactory: notImplemented, + startLexicalEnvironment: noop, + resumeLexicalEnvironment: noop, + suspendLexicalEnvironment: noop, + endLexicalEnvironment: returnUndefined, setLexicalEnvironmentFlags: noop, getLexicalEnvironmentFlags: () => 0, - hoistFunctionDeclaration: noop, hoistVariableDeclaration: noop, + hoistFunctionDeclaration: noop, addInitializationStatement: noop, - isEmitNotificationEnabled: notImplemented, - isSubstitutionEnabled: notImplemented, - onEmitNode: noop, - onSubstituteNode: notImplemented, - readEmitHelpers: notImplemented, - requestEmitHelper: noop, - resumeLexicalEnvironment: noop, - startLexicalEnvironment: noop, - suspendLexicalEnvironment: noop, - addDiagnostic: noop, startBlockScope: noop, endBlockScope: returnUndefined, - addBlockScopedVariable: noop + addBlockScopedVariable: noop, + requestEmitHelper: noop, + readEmitHelpers: notImplemented, + enableSubstitution: noop, + enableEmitNotification: noop, + isSubstitutionEnabled: notImplemented, + isEmitNotificationEnabled: notImplemented, + onSubstituteNode: noEmitSubstitution, + onEmitNode: noEmitNotification, + addDiagnostic: noop, }; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 63b20f49139a6..cdce69a84a3d9 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6785,6 +6785,8 @@ namespace ts { /* @internal */ export interface ParenthesizerRules { + getParenthesizeLeftSideOfBinaryForOperator(binaryOperator: SyntaxKind): (leftSide: Expression) => Expression; + getParenthesizeRightSideOfBinaryForOperator(binaryOperator: SyntaxKind): (rightSide: Expression) => Expression; parenthesizeLeftSideOfBinary(binaryOperator: SyntaxKind, leftSide: Expression): Expression; parenthesizeRightSideOfBinary(binaryOperator: SyntaxKind, leftSide: Expression | undefined, rightSide: Expression): Expression; parenthesizeExpressionOfComputedPropertyName(expression: Expression): Expression; @@ -6798,6 +6800,7 @@ namespace ts { parenthesizeExpressionsOfCommaDelimitedList(elements: readonly Expression[]): NodeArray; parenthesizeExpressionForDisallowedComma(expression: Expression): Expression; parenthesizeExpressionOfExpressionStatement(expression: Expression): Expression; + parenthesizeConciseBodyOfArrowFunction(body: Expression): Expression; parenthesizeConciseBodyOfArrowFunction(body: ConciseBody): ConciseBody; parenthesizeMemberOfConditionalType(member: TypeNode): TypeNode; parenthesizeMemberOfElementType(member: TypeNode): TypeNode; diff --git a/tests/baselines/reference/inlineJsxFactoryDeclarationsLocalTypes.js b/tests/baselines/reference/inlineJsxFactoryDeclarationsLocalTypes.js index da09d825b0f95..177f997297b44 100644 --- a/tests/baselines/reference/inlineJsxFactoryDeclarationsLocalTypes.js +++ b/tests/baselines/reference/inlineJsxFactoryDeclarationsLocalTypes.js @@ -161,4 +161,6 @@ var _brokenTree = (0, renderer_1.dom)(component_1.MySFC, { x: 1, y: 2 }, (0, renderer_1.dom)(component_1.MyClass, { x: 3, y: 4 }), (0, renderer_1.dom)(component_1.MyClass, { x: 5, y: 6 })); // Should fail, nondom isn't allowed as children of dom -var _brokenTree2 = (0, renderer_1.dom)(DOMSFC, { x: 1, y: 2 }, component_1.tree, component_1.tree); +var _brokenTree2 = (0, renderer_1.dom)(DOMSFC, { x: 1, y: 2 }, + component_1.tree, + component_1.tree); diff --git a/tests/baselines/reference/jsxCheckJsxNoTypeArgumentsAllowed.js b/tests/baselines/reference/jsxCheckJsxNoTypeArgumentsAllowed.js index 8eb7b03f2b7ac..a1a7692ca7655 100644 --- a/tests/baselines/reference/jsxCheckJsxNoTypeArgumentsAllowed.js +++ b/tests/baselines/reference/jsxCheckJsxNoTypeArgumentsAllowed.js @@ -23,5 +23,5 @@ let x = a={10} b="hi" />; // error, no type arguments in js exports.__esModule = true; var component_1 = require("./component"); var React = require("react"); -var x = (, a={10} b="hi" />; // error, no type arguments in js -); +var x = , a={10} b="hi" />; // error, no type arguments in js +;