From 4eae150282bfddb89480aa125c1b8d20513804fb Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Wed, 29 Nov 2023 01:07:31 +0200 Subject: [PATCH] fix(50303): Using @linkcode in combination with private methods causes TS1003 in JavaScript files. (#56338) --- src/compiler/parser.ts | 31 ++++++++++++------- .../baselines/reference/jsdocLinkTag7.symbols | 26 ++++++++++++++++ tests/baselines/reference/jsdocLinkTag7.types | 26 ++++++++++++++++ .../cases/conformance/jsdoc/jsdocLinkTag7.ts | 20 ++++++++++++ 4 files changed, 91 insertions(+), 12 deletions(-) create mode 100644 tests/baselines/reference/jsdocLinkTag7.symbols create mode 100644 tests/baselines/reference/jsdocLinkTag7.types create mode 100644 tests/cases/conformance/jsdoc/jsdocLinkTag7.ts diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 59c96ecc5e6..fcf95696eb9 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -9210,18 +9210,7 @@ namespace Parser { } nextTokenJSDoc(); // start at token after link, then skip any whitespace skipWhitespace(); - // parseEntityName logs an error for non-identifier, so create a MissingNode ourselves to avoid the error - const p2 = getNodePos(); - let name: EntityName | JSDocMemberName | undefined = tokenIsIdentifierOrKeyword(token()) - ? parseEntityName(/*allowReservedWords*/ true) - : undefined; - if (name) { - while (token() === SyntaxKind.PrivateIdentifier) { - reScanHashToken(); // rescan #id as # id - nextTokenJSDoc(); // then skip the # - name = finishNode(factory.createJSDocMemberName(name, parseIdentifier()), p2); - } - } + const name = parseJSDocLinkName(); const text = []; while (token() !== SyntaxKind.CloseBraceToken && token() !== SyntaxKind.NewLineTrivia && token() !== SyntaxKind.EndOfFileToken) { text.push(scanner.getTokenText()); @@ -9233,6 +9222,24 @@ namespace Parser { return finishNode(create(name, text.join("")), start, scanner.getTokenEnd()); } + function parseJSDocLinkName() { + if (tokenIsIdentifierOrKeyword(token())) { + const pos = getNodePos(); + + let name: EntityName | JSDocMemberName = parseIdentifierName(); + while (parseOptional(SyntaxKind.DotToken)) { + name = finishNode(factory.createQualifiedName(name, token() === SyntaxKind.PrivateIdentifier ? createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ false) : parseIdentifier()), pos); + } + while (token() === SyntaxKind.PrivateIdentifier) { + reScanHashToken(); + nextTokenJSDoc(); + name = finishNode(factory.createJSDocMemberName(name, parseIdentifier()), pos); + } + return name; + } + return undefined; + } + function parseJSDocLinkPrefix() { skipWhitespaceOrAsterisk(); if ( diff --git a/tests/baselines/reference/jsdocLinkTag7.symbols b/tests/baselines/reference/jsdocLinkTag7.symbols new file mode 100644 index 00000000000..1d37cb07a9d --- /dev/null +++ b/tests/baselines/reference/jsdocLinkTag7.symbols @@ -0,0 +1,26 @@ +//// [tests/cases/conformance/jsdoc/jsdocLinkTag7.ts] //// + +=== /a.js === +class Foo { +>Foo : Symbol(Foo, Decl(a.js, 0, 0)) + + /** + * {@linkcode this.a} + * {@linkcode this.#c} + * + * {@link this.a} + * {@link this.#c} + * + * {@linkplain this.a} + * {@linkplain this.#c} + */ + a() { } +>a : Symbol(Foo.a, Decl(a.js, 0, 11)) + + b() { } +>b : Symbol(Foo.b, Decl(a.js, 11, 11)) + + #c() { } +>#c : Symbol(Foo.#c, Decl(a.js, 12, 11)) +} + diff --git a/tests/baselines/reference/jsdocLinkTag7.types b/tests/baselines/reference/jsdocLinkTag7.types new file mode 100644 index 00000000000..ca6cd364fc5 --- /dev/null +++ b/tests/baselines/reference/jsdocLinkTag7.types @@ -0,0 +1,26 @@ +//// [tests/cases/conformance/jsdoc/jsdocLinkTag7.ts] //// + +=== /a.js === +class Foo { +>Foo : Foo + + /** + * {@linkcode this.a} + * {@linkcode this.#c} + * + * {@link this.a} + * {@link this.#c} + * + * {@linkplain this.a} + * {@linkplain this.#c} + */ + a() { } +>a : () => void + + b() { } +>b : () => void + + #c() { } +>#c : () => void +} + diff --git a/tests/cases/conformance/jsdoc/jsdocLinkTag7.ts b/tests/cases/conformance/jsdoc/jsdocLinkTag7.ts new file mode 100644 index 00000000000..b149a6127b3 --- /dev/null +++ b/tests/cases/conformance/jsdoc/jsdocLinkTag7.ts @@ -0,0 +1,20 @@ +// @checkJs: true +// @allowJs: true +// @target: esnext +// @noEmit: true +// @filename: /a.js +class Foo { + /** + * {@linkcode this.a} + * {@linkcode this.#c} + * + * {@link this.a} + * {@link this.#c} + * + * {@linkplain this.a} + * {@linkplain this.#c} + */ + a() { } + b() { } + #c() { } +}