diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index dbcffe1680e..7da0f03008a 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -8123,6 +8123,8 @@ namespace ts { SawAsterisk, SavingComments, SavingBackticks, // NOTE: Only used when parsing tag comments + SavingBackticksBeginningOfLine, + SavingBackticksSawAsterisk, } const enum PropertyLikeParse { @@ -8433,13 +8435,13 @@ namespace ts { loop: while (true) { switch (tok) { case SyntaxKind.NewLineTrivia: - state = JSDocState.BeginningOfLine; + state = (state as JSDocState) === JSDocState.SavingBackticks || (state as JSDocState) === JSDocState.SavingBackticksBeginningOfLine || (state as JSDocState) === JSDocState.SavingBackticksSawAsterisk ? JSDocState.SavingBackticksBeginningOfLine : JSDocState.BeginningOfLine; // don't use pushComment here because we want to keep the margin unchanged comments.push(scanner.getTokenText()); indent = 0; break; case SyntaxKind.AtToken: - if (state === JSDocState.SavingBackticks + if (state === JSDocState.SavingBackticks || state === JSDocState.SavingBackticksBeginningOfLine || state === JSDocState.SavingBackticksSawAsterisk || state === JSDocState.SavingComments && (!previousWhitespace || lookAhead(isNextJSDocTokenWhitespace))) { // @ doesn't start a new tag inside ``, and inside a comment, only after whitespace or not before whitespace comments.push(scanner.getTokenText()); @@ -8464,7 +8466,7 @@ namespace ts { } break; case SyntaxKind.OpenBraceToken: - state = JSDocState.SavingComments; + state = (state as JSDocState) === JSDocState.SavingBackticks || (state as JSDocState) === JSDocState.SavingBackticksBeginningOfLine || (state as JSDocState) === JSDocState.SavingBackticksSawAsterisk ? JSDocState.SavingBackticks : JSDocState.SavingComments; const commentEnd = scanner.getStartPos(); const linkStart = scanner.getTextPos() - 1; const link = parseJSDocLink(linkStart); @@ -8479,7 +8481,7 @@ namespace ts { } break; case SyntaxKind.BacktickToken: - if (state === JSDocState.SavingBackticks) { + if (state === JSDocState.SavingBackticks || state === JSDocState.SavingBackticksBeginningOfLine || state === JSDocState.SavingBackticksSawAsterisk) { state = JSDocState.SavingComments; } else { @@ -8494,11 +8496,22 @@ namespace ts { indent += 1; break; } + if (state === JSDocState.SavingBackticksBeginningOfLine) { + // leading asterisks start recording on the *next* (non-whitespace) token + state = JSDocState.SavingBackticksSawAsterisk; + indent += 1; + break; + } // record the * as a comment // falls through default: if (state !== JSDocState.SavingBackticks) { - state = JSDocState.SavingComments; // leading identifiers start recording as well + if (state === JSDocState.SavingBackticksBeginningOfLine || state === JSDocState.SavingBackticksSawAsterisk) { + state = JSDocState.SavingBackticks + } + else { + state = JSDocState.SavingComments; // leading identifiers start recording as well + } } pushComment(scanner.getTokenText()); break; diff --git a/tests/baselines/reference/jsdocParseMatchingBackticks.errors.txt b/tests/baselines/reference/jsdocParseMatchingBackticks.errors.txt new file mode 100644 index 00000000000..6d78551a307 --- /dev/null +++ b/tests/baselines/reference/jsdocParseMatchingBackticks.errors.txt @@ -0,0 +1,31 @@ +tests/cases/conformance/jsdoc/jsdocParseMatchingBackticks.js(22,22): error TS7006: Parameter 'gamma' implicitly has an 'any' type. + + +==== tests/cases/conformance/jsdoc/jsdocParseMatchingBackticks.js (1 errors) ==== + /** + * `@param` initial at-param is OK in title comment + * @param {string} x hi there `@param` + * @param {string} y hi there @ * param + * this is the margin + * so we'll drop everything before it + `@param` @param {string} z hello??? + * `@param` @param {string} alpha hello??? + * `@ * param` @param {string} beta hello??? + * @param {string} gamma + */ + export function f(x, y, z, alpha, beta, gamma) { + return x + y + z + alpha + beta + gamma + } + /** + * Unmatched backticks keep going past newlines + * @param {string} y hi there `@ * param + * this is the margin + * so we'll drop everything before it + * @param {string} gamma + */ + export function g(y, gamma) { + ~~~~~ +!!! error TS7006: Parameter 'gamma' implicitly has an 'any' type. + return y + gamma + } + \ No newline at end of file diff --git a/tests/baselines/reference/jsdocParseMatchingBackticks.symbols b/tests/baselines/reference/jsdocParseMatchingBackticks.symbols index 83bd81113aa..d25a7ebbb1c 100644 --- a/tests/baselines/reference/jsdocParseMatchingBackticks.symbols +++ b/tests/baselines/reference/jsdocParseMatchingBackticks.symbols @@ -2,7 +2,7 @@ /** * `@param` initial at-param is OK in title comment * @param {string} x hi there `@param` - * @param {string} y hi there `@ * param + * @param {string} y hi there @ * param * this is the margin * so we'll drop everything before it `@param` @param {string} z hello??? @@ -27,4 +27,20 @@ export function f(x, y, z, alpha, beta, gamma) { >beta : Symbol(beta, Decl(jsdocParseMatchingBackticks.js, 11, 33)) >gamma : Symbol(gamma, Decl(jsdocParseMatchingBackticks.js, 11, 39)) } +/** + * Unmatched backticks keep going past newlines + * @param {string} y hi there `@ * param + * this is the margin + * so we'll drop everything before it + * @param {string} gamma + */ +export function g(y, gamma) { +>g : Symbol(g, Decl(jsdocParseMatchingBackticks.js, 13, 1)) +>y : Symbol(y, Decl(jsdocParseMatchingBackticks.js, 21, 18)) +>gamma : Symbol(gamma, Decl(jsdocParseMatchingBackticks.js, 21, 20)) + + return y + gamma +>y : Symbol(y, Decl(jsdocParseMatchingBackticks.js, 21, 18)) +>gamma : Symbol(gamma, Decl(jsdocParseMatchingBackticks.js, 21, 20)) +} diff --git a/tests/baselines/reference/jsdocParseMatchingBackticks.types b/tests/baselines/reference/jsdocParseMatchingBackticks.types index 8127e836df4..42f10e8ea5b 100644 --- a/tests/baselines/reference/jsdocParseMatchingBackticks.types +++ b/tests/baselines/reference/jsdocParseMatchingBackticks.types @@ -2,7 +2,7 @@ /** * `@param` initial at-param is OK in title comment * @param {string} x hi there `@param` - * @param {string} y hi there `@ * param + * @param {string} y hi there @ * param * this is the margin * so we'll drop everything before it `@param` @param {string} z hello??? @@ -32,4 +32,21 @@ export function f(x, y, z, alpha, beta, gamma) { >beta : string >gamma : string } +/** + * Unmatched backticks keep going past newlines + * @param {string} y hi there `@ * param + * this is the margin + * so we'll drop everything before it + * @param {string} gamma + */ +export function g(y, gamma) { +>g : (y: string, gamma: any) => string +>y : string +>gamma : any + + return y + gamma +>y + gamma : string +>y : string +>gamma : any +} diff --git a/tests/cases/conformance/jsdoc/jsdocParseMatchingBackticks.ts b/tests/cases/conformance/jsdoc/jsdocParseMatchingBackticks.ts index 942ee98d52f..0a0c1d5406a 100644 --- a/tests/cases/conformance/jsdoc/jsdocParseMatchingBackticks.ts +++ b/tests/cases/conformance/jsdoc/jsdocParseMatchingBackticks.ts @@ -7,7 +7,7 @@ /** * `@param` initial at-param is OK in title comment * @param {string} x hi there `@param` - * @param {string} y hi there `@ * param + * @param {string} y hi there @ * param * this is the margin * so we'll drop everything before it `@param` @param {string} z hello??? @@ -18,3 +18,13 @@ export function f(x, y, z, alpha, beta, gamma) { return x + y + z + alpha + beta + gamma } +/** + * Unmatched backticks keep going past newlines + * @param {string} y hi there `@ * param + * this is the margin + * so we'll drop everything before it + * @param {string} gamma + */ +export function g(y, gamma) { + return y + gamma +} diff --git a/tests/cases/fourslash/quickInfoJSDocBackticks.ts b/tests/cases/fourslash/quickInfoJSDocBackticks.ts index 16ddce5a939..9610e1895e6 100644 --- a/tests/cases/fourslash/quickInfoJSDocBackticks.ts +++ b/tests/cases/fourslash/quickInfoJSDocBackticks.ts @@ -11,15 +11,18 @@ //// * @param {string} x hi there `@param` //// * @param {string} y hi there `@ * param //// * this is the margin +//// * @param {string} z OOPS, unclosed backtick! //// */ -////export function f(x, y) { -//// return x/*x*/ + y/*y*/ +////export function f(x, y, z) { +//// return x/*x*/ + y/*y*/ + z/*z*/ ////} ////f/*f*/ goTo.marker("f"); -verify.quickInfoIs("function f(x: string, y: string): string", "`@param` initial at-param is OK in title comment"); +verify.quickInfoIs("function f(x: string, y: string, z: any): string", "`@param` initial at-param is OK in title comment"); goTo.marker("x"); verify.quickInfoIs("(parameter) x: string", "hi there `@param`"); goTo.marker("y"); -verify.quickInfoIs("(parameter) y: string", "hi there `@ * param\nthis is the margin"); +verify.quickInfoIs("(parameter) y: string", "hi there `@ * param\nthis is the margin\n@param {string} z OOPS, unclosed backtick!"); +goTo.marker("z"); +verify.quickInfoIs("(parameter) z: any", undefined);