diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 2fbd0c3e141..d2a813e3f39 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1001,6 +1001,7 @@ namespace ts { function bindReturnOrThrow(node: ReturnStatement | ThrowStatement): void { bind(node.expression); if (node.kind === SyntaxKind.ReturnStatement) { + bind(node.returnKeyword); hasExplicitReturn = true; if (currentReturnTarget) { addAntecedent(currentReturnTarget, currentFlow); diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index de08b209d3e..fd32c28968c 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1571,7 +1571,7 @@ namespace ts { } function emitReturnStatement(node: ReturnStatement) { - writeToken(SyntaxKind.ReturnKeyword, node.pos, /*contextNode*/ node); + emit(node.returnKeyword); emitExpressionWithPrefix(" ", node.expression); write(";"); } diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 34727e4c414..45f26b4b2a8 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -1460,15 +1460,17 @@ namespace ts { : node; } - export function createReturn(expression?: Expression): ReturnStatement { + export function createReturn(expression?: Expression, returnKeyword: Token = createToken(SyntaxKind.ReturnKeyword)): ReturnStatement { const node = createSynthesizedNode(SyntaxKind.ReturnStatement); + node.returnKeyword = returnKeyword; node.expression = expression; return node; } - export function updateReturn(node: ReturnStatement, expression: Expression | undefined) { - return node.expression !== expression - ? updateNode(createReturn(expression), node) + export function updateReturn(node: ReturnStatement, returnKeyword: Token, expression: Expression | undefined) { + return node.expression !== expression || + node.returnKeyword !== returnKeyword + ? updateNode(createReturn(expression, returnKeyword), node) : node; } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 34670cd2834..260068bd33a 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -259,7 +259,8 @@ namespace ts { case SyntaxKind.BreakStatement: return visitNode(cbNode, (node).label); case SyntaxKind.ReturnStatement: - return visitNode(cbNode, (node).expression); + return visitNode(cbNode, (node).returnKeyword) || + visitNode(cbNode, (node).expression); case SyntaxKind.WithStatement: return visitNode(cbNode, (node).expression) || visitNode(cbNode, (node).statement); @@ -4687,7 +4688,7 @@ namespace ts { function parseReturnStatement(): ReturnStatement { const node = createNode(SyntaxKind.ReturnStatement); - parseExpected(SyntaxKind.ReturnKeyword); + node.returnKeyword = parseExpectedToken(SyntaxKind.ReturnKeyword, /*reportAtCurrentPosition*/ true, Diagnostics._0_expected, "return"); if (!canParseSemicolon()) { node.expression = allowInAnd(parseExpression); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 09f1b5cfcc5..931a29bc5a3 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1750,6 +1750,7 @@ namespace ts { export interface ReturnStatement extends Statement { kind: SyntaxKind.ReturnStatement; + returnKeyword: Token; expression?: Expression; } diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 1ce42199372..2fc15c56457 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -634,6 +634,7 @@ namespace ts { case SyntaxKind.ReturnStatement: return updateReturn(node, + visitNode((node).returnKeyword, visitor, isToken), visitNode((node).expression, visitor, isExpression)); case SyntaxKind.WithStatement: diff --git a/src/harness/unittests/incrementalParser.ts b/src/harness/unittests/incrementalParser.ts index fbd8a60da92..cc493fceaca 100644 --- a/src/harness/unittests/incrementalParser.ts +++ b/src/harness/unittests/incrementalParser.ts @@ -647,7 +647,7 @@ module m3 { }\ const oldText = ScriptSnapshot.fromString(source); const newTextAndChange = withInsert(oldText, 0, ""); - compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 7); + compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 8); }); it("Class to interface", () => { diff --git a/tests/baselines/reference/jsdocCastCommentEmit.js b/tests/baselines/reference/jsdocCastCommentEmit.js new file mode 100644 index 00000000000..d071f8f6b2b --- /dev/null +++ b/tests/baselines/reference/jsdocCastCommentEmit.js @@ -0,0 +1,17 @@ +//// [jsdocCastCommentEmit.ts] +// allowJs: true +// checkJs: true +// outDir: out/ +// filename: input.js +function f() { + return /* @type {number} */ 42; +} + +//// [jsdocCastCommentEmit.js] +// allowJs: true +// checkJs: true +// outDir: out/ +// filename: input.js +function f() { + return /* @type {number} */ 42; +} diff --git a/tests/baselines/reference/jsdocCastCommentEmit.symbols b/tests/baselines/reference/jsdocCastCommentEmit.symbols new file mode 100644 index 00000000000..5490315bc19 --- /dev/null +++ b/tests/baselines/reference/jsdocCastCommentEmit.symbols @@ -0,0 +1,10 @@ +=== tests/cases/compiler/jsdocCastCommentEmit.ts === +// allowJs: true +// checkJs: true +// outDir: out/ +// filename: input.js +function f() { +>f : Symbol(f, Decl(jsdocCastCommentEmit.ts, 0, 0)) + + return /* @type {number} */ 42; +} diff --git a/tests/baselines/reference/jsdocCastCommentEmit.types b/tests/baselines/reference/jsdocCastCommentEmit.types new file mode 100644 index 00000000000..3d4c3e47e46 --- /dev/null +++ b/tests/baselines/reference/jsdocCastCommentEmit.types @@ -0,0 +1,11 @@ +=== tests/cases/compiler/jsdocCastCommentEmit.ts === +// allowJs: true +// checkJs: true +// outDir: out/ +// filename: input.js +function f() { +>f : () => number + + return /* @type {number} */ 42; +>42 : 42 +} diff --git a/tests/cases/compiler/jsdocCastCommentEmit.ts b/tests/cases/compiler/jsdocCastCommentEmit.ts new file mode 100644 index 00000000000..5e7230bd049 --- /dev/null +++ b/tests/cases/compiler/jsdocCastCommentEmit.ts @@ -0,0 +1,7 @@ +// allowJs: true +// checkJs: true +// outDir: out/ +// filename: input.js +function f() { + return /* @type {number} */ 42; +} \ No newline at end of file