diff --git a/internal/printer/emitcontext.go b/internal/printer/emitcontext.go index 68467e25d3a..24e0c43a98b 100644 --- a/internal/printer/emitcontext.go +++ b/internal/printer/emitcontext.go @@ -900,6 +900,19 @@ func (c *EmitContext) AddInitializationStatement(node *ast.Node) { scope.initializationStatements = append(scope.initializationStatements, node) } +func (c *EmitContext) ConvertToFunctionBlock(node *ast.Node, multiLine bool) *ast.Node { + if ast.IsBlock(node) { + return node + } + returnStatement := c.Factory.NewReturnStatement(node) + returnStatement.Loc = node.Loc + statements := c.Factory.NewNodeList([]*ast.Node{returnStatement}) + statements.Loc = node.Loc + block := c.Factory.NewBlock(statements, multiLine) + block.Loc = node.Loc + return block +} + func (c *EmitContext) VisitFunctionBody(node *ast.BlockOrExpression, visitor *ast.NodeVisitor) *ast.BlockOrExpression { // !!! c.resumeVariableEnvironment() updated := visitor.VisitNode(node) @@ -913,8 +926,13 @@ func (c *EmitContext) VisitFunctionBody(node *ast.BlockOrExpression, visitor *as } if !ast.IsBlock(updated) { - statements := c.MergeEnvironment([]*ast.Statement{c.Factory.NewReturnStatement(updated)}, declarations) - return c.Factory.NewBlock(c.Factory.NewNodeList(statements), false /*multiLine*/) + c.AddEmitFlags(updated, EFNoComments) + block := c.ConvertToFunctionBlock(updated, false /*multiLine*/) + return c.Factory.UpdateBlock( + block.AsBlock(), + c.MergeEnvironmentList(block.StatementList(), declarations), + block.AsBlock().MultiLine, + ) } return c.Factory.UpdateBlock( diff --git a/internal/transformers/estransforms/async.go b/internal/transformers/estransforms/async.go index a7c2d0b7250..0097159ee02 100644 --- a/internal/transformers/estransforms/async.go +++ b/internal/transformers/estransforms/async.go @@ -841,7 +841,10 @@ func (tx *asyncTransformer) transformAsyncFunctionBody(node *ast.Node, outerPara ) if captureLexicalArguments && tx.lexicalArguments.used { - block := tx.convertToFunctionBlock(result) + block := tx.EmitContext().ConvertToFunctionBlock(result, true /*multiLine*/) + if !ast.IsBlock(result) { + tx.EmitContext().SetOriginal(block.StatementList().Nodes[0], result) + } result = tx.Factory().UpdateBlock( block.AsBlock(), tx.EmitContext().MergeEnvironmentList(block.StatementList(), []*ast.Node{tx.createCaptureArgumentsStatement()}), @@ -889,20 +892,6 @@ func (tx *asyncTransformer) transformAsyncFunctionBodyWorker(body *ast.Node) *as return block } -func (tx *asyncTransformer) convertToFunctionBlock(node *ast.Node) *ast.Node { - if ast.IsBlock(node) { - return node - } - ret := tx.Factory().NewReturnStatement(node) - ret.Loc = node.Loc - tx.EmitContext().SetOriginal(ret, node) - list := tx.Factory().NewNodeList([]*ast.Node{ret}) - list.Loc = node.Loc - block := tx.Factory().NewBlock(list, true) - block.Loc = node.Loc - return block -} - // assignmentTargetContainsSuperProperty checks top-down whether an assignment target // expression contains a super property or element access (super.x or super[x]). // This avoids relying on parent pointers (IsAssignmentTarget) which may not be set diff --git a/testdata/baselines/reference/compiler/optionalChainingArrowFunctionCommentEs2018.js b/testdata/baselines/reference/compiler/optionalChainingArrowFunctionCommentEs2018.js new file mode 100644 index 00000000000..466d090cf07 --- /dev/null +++ b/testdata/baselines/reference/compiler/optionalChainingArrowFunctionCommentEs2018.js @@ -0,0 +1,40 @@ +//// [tests/cases/compiler/optionalChainingArrowFunctionCommentEs2018.ts] //// + +//// [optionalChainingArrowFunctionCommentEs2018.ts] +const thing = { nested: { condition: true } }; + +const wat = () => + // explanatory comment + thing?.nested?.condition ? "pass" : "fail"; + +const watInline = () => /* inline explanatory comment */ thing?.nested?.condition ? "pass" : "fail"; + +const watInlineMultiline = () => /* inline +explanatory comment */ thing?.nested?.condition ? "pass" : "fail"; + +const watInlineThenLeading = () => /* inline explanatory comment */ + // leading explanatory comment + thing?.nested?.condition ? "pass" : "fail"; + +declare const o: { a?: number[] }; + +export const f = () => + // comment + o.a?.length; + + +//// [optionalChainingArrowFunctionCommentEs2018.js] +const thing = { nested: { condition: true } }; +const wat = () => { var _a; +// explanatory comment +return ((_a = thing === null || thing === void 0 ? void 0 : thing.nested) === null || _a === void 0 ? void 0 : _a.condition) ? "pass" : "fail"; }; +const watInline = () => /* inline explanatory comment */ { var _a; /* inline explanatory comment */ return ((_a = thing === null || thing === void 0 ? void 0 : thing.nested) === null || _a === void 0 ? void 0 : _a.condition) ? "pass" : "fail"; }; +const watInlineMultiline = () => /* inline +explanatory comment */ { var _a; /* inline +explanatory comment */ return ((_a = thing === null || thing === void 0 ? void 0 : thing.nested) === null || _a === void 0 ? void 0 : _a.condition) ? "pass" : "fail"; }; +const watInlineThenLeading = () => /* inline explanatory comment */ { var _a; /* inline explanatory comment */ +// leading explanatory comment +return ((_a = thing === null || thing === void 0 ? void 0 : thing.nested) === null || _a === void 0 ? void 0 : _a.condition) ? "pass" : "fail"; }; +export const f = () => { var _a; +// comment +return (_a = o.a) === null || _a === void 0 ? void 0 : _a.length; }; diff --git a/testdata/baselines/reference/compiler/optionalChainingArrowFunctionCommentEs2018.symbols b/testdata/baselines/reference/compiler/optionalChainingArrowFunctionCommentEs2018.symbols new file mode 100644 index 00000000000..3f296faf1a9 --- /dev/null +++ b/testdata/baselines/reference/compiler/optionalChainingArrowFunctionCommentEs2018.symbols @@ -0,0 +1,63 @@ +//// [tests/cases/compiler/optionalChainingArrowFunctionCommentEs2018.ts] //// + +=== optionalChainingArrowFunctionCommentEs2018.ts === +const thing = { nested: { condition: true } }; +>thing : Symbol(thing, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 0, 5)) +>nested : Symbol(nested, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 0, 15)) +>condition : Symbol(condition, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 0, 25)) + +const wat = () => +>wat : Symbol(wat, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 2, 5)) + + // explanatory comment + thing?.nested?.condition ? "pass" : "fail"; +>thing?.nested?.condition : Symbol(condition, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 0, 25)) +>thing?.nested : Symbol(nested, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 0, 15)) +>thing : Symbol(thing, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 0, 5)) +>nested : Symbol(nested, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 0, 15)) +>condition : Symbol(condition, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 0, 25)) + +const watInline = () => /* inline explanatory comment */ thing?.nested?.condition ? "pass" : "fail"; +>watInline : Symbol(watInline, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 6, 5)) +>thing?.nested?.condition : Symbol(condition, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 0, 25)) +>thing?.nested : Symbol(nested, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 0, 15)) +>thing : Symbol(thing, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 0, 5)) +>nested : Symbol(nested, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 0, 15)) +>condition : Symbol(condition, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 0, 25)) + +const watInlineMultiline = () => /* inline +>watInlineMultiline : Symbol(watInlineMultiline, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 8, 5)) + +explanatory comment */ thing?.nested?.condition ? "pass" : "fail"; +>thing?.nested?.condition : Symbol(condition, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 0, 25)) +>thing?.nested : Symbol(nested, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 0, 15)) +>thing : Symbol(thing, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 0, 5)) +>nested : Symbol(nested, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 0, 15)) +>condition : Symbol(condition, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 0, 25)) + +const watInlineThenLeading = () => /* inline explanatory comment */ +>watInlineThenLeading : Symbol(watInlineThenLeading, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 11, 5)) + + // leading explanatory comment + thing?.nested?.condition ? "pass" : "fail"; +>thing?.nested?.condition : Symbol(condition, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 0, 25)) +>thing?.nested : Symbol(nested, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 0, 15)) +>thing : Symbol(thing, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 0, 5)) +>nested : Symbol(nested, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 0, 15)) +>condition : Symbol(condition, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 0, 25)) + +declare const o: { a?: number[] }; +>o : Symbol(o, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 15, 13)) +>a : Symbol(a, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 15, 18)) + +export const f = () => +>f : Symbol(f, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 17, 12)) + + // comment + o.a?.length; +>o.a?.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) +>o.a : Symbol(a, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 15, 18)) +>o : Symbol(o, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 15, 13)) +>a : Symbol(a, Decl(optionalChainingArrowFunctionCommentEs2018.ts, 15, 18)) +>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) + diff --git a/testdata/baselines/reference/compiler/optionalChainingArrowFunctionCommentEs2018.types b/testdata/baselines/reference/compiler/optionalChainingArrowFunctionCommentEs2018.types new file mode 100644 index 00000000000..5aabfba66c3 --- /dev/null +++ b/testdata/baselines/reference/compiler/optionalChainingArrowFunctionCommentEs2018.types @@ -0,0 +1,83 @@ +//// [tests/cases/compiler/optionalChainingArrowFunctionCommentEs2018.ts] //// + +=== optionalChainingArrowFunctionCommentEs2018.ts === +const thing = { nested: { condition: true } }; +>thing : { nested: { condition: boolean; }; } +>{ nested: { condition: true } } : { nested: { condition: boolean; }; } +>nested : { condition: boolean; } +>{ condition: true } : { condition: boolean; } +>condition : boolean +>true : true + +const wat = () => +>wat : () => "fail" | "pass" +>() => // explanatory comment thing?.nested?.condition ? "pass" : "fail" : () => "fail" | "pass" + + // explanatory comment + thing?.nested?.condition ? "pass" : "fail"; +>thing?.nested?.condition ? "pass" : "fail" : "fail" | "pass" +>thing?.nested?.condition : boolean +>thing?.nested : { condition: boolean; } +>thing : { nested: { condition: boolean; }; } +>nested : { condition: boolean; } +>condition : boolean +>"pass" : "pass" +>"fail" : "fail" + +const watInline = () => /* inline explanatory comment */ thing?.nested?.condition ? "pass" : "fail"; +>watInline : () => "fail" | "pass" +>() => /* inline explanatory comment */ thing?.nested?.condition ? "pass" : "fail" : () => "fail" | "pass" +>thing?.nested?.condition ? "pass" : "fail" : "fail" | "pass" +>thing?.nested?.condition : boolean +>thing?.nested : { condition: boolean; } +>thing : { nested: { condition: boolean; }; } +>nested : { condition: boolean; } +>condition : boolean +>"pass" : "pass" +>"fail" : "fail" + +const watInlineMultiline = () => /* inline +>watInlineMultiline : () => "fail" | "pass" +>() => /* inlineexplanatory comment */ thing?.nested?.condition ? "pass" : "fail" : () => "fail" | "pass" + +explanatory comment */ thing?.nested?.condition ? "pass" : "fail"; +>thing?.nested?.condition ? "pass" : "fail" : "fail" | "pass" +>thing?.nested?.condition : boolean +>thing?.nested : { condition: boolean; } +>thing : { nested: { condition: boolean; }; } +>nested : { condition: boolean; } +>condition : boolean +>"pass" : "pass" +>"fail" : "fail" + +const watInlineThenLeading = () => /* inline explanatory comment */ +>watInlineThenLeading : () => "fail" | "pass" +>() => /* inline explanatory comment */ // leading explanatory comment thing?.nested?.condition ? "pass" : "fail" : () => "fail" | "pass" + + // leading explanatory comment + thing?.nested?.condition ? "pass" : "fail"; +>thing?.nested?.condition ? "pass" : "fail" : "fail" | "pass" +>thing?.nested?.condition : boolean +>thing?.nested : { condition: boolean; } +>thing : { nested: { condition: boolean; }; } +>nested : { condition: boolean; } +>condition : boolean +>"pass" : "pass" +>"fail" : "fail" + +declare const o: { a?: number[] }; +>o : { a?: number[]; } +>a : number[] | undefined + +export const f = () => +>f : () => number | undefined +>() => // comment o.a?.length : () => number | undefined + + // comment + o.a?.length; +>o.a?.length : number | undefined +>o.a : number[] | undefined +>o : { a?: number[]; } +>a : number[] | undefined +>length : number | undefined + diff --git a/testdata/tests/cases/compiler/optionalChainingArrowFunctionCommentEs2018.ts b/testdata/tests/cases/compiler/optionalChainingArrowFunctionCommentEs2018.ts new file mode 100644 index 00000000000..f3d5d571e02 --- /dev/null +++ b/testdata/tests/cases/compiler/optionalChainingArrowFunctionCommentEs2018.ts @@ -0,0 +1,22 @@ +// @target: es2018 + +const thing = { nested: { condition: true } }; + +const wat = () => + // explanatory comment + thing?.nested?.condition ? "pass" : "fail"; + +const watInline = () => /* inline explanatory comment */ thing?.nested?.condition ? "pass" : "fail"; + +const watInlineMultiline = () => /* inline +explanatory comment */ thing?.nested?.condition ? "pass" : "fail"; + +const watInlineThenLeading = () => /* inline explanatory comment */ + // leading explanatory comment + thing?.nested?.condition ? "pass" : "fail"; + +declare const o: { a?: number[] }; + +export const f = () => + // comment + o.a?.length;