Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package fourslash_test

import (
"testing"

"github.com/microsoft/TypeScript/tsc/internal/fourslash"
"github.com/microsoft/TypeScript/tsc/internal/testutil"
)

func TestJsdocInlineLinksInTypePosition(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
const content = `class C {}
/**
* @throws {@link /*throwsLink*/C}
* @returns {@linkcode /*returnsLink*/C}
* @param value {@linkplain /*paramLink*/C}
*/
function /*inlineLinks*/f(value: C) {}

/**
* @throws {/*throwsType*/C} description
* @returns {/*returnsType*/C} description
* @param {/*paramType*/C} value description
*/
function /*types*/g(value: C) { return value; }`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyBaselineHover(t)
f.VerifyBaselineGoToDefinition(t, false, "throwsLink", "returnsLink", "paramLink", "throwsType", "returnsType", "paramType")
}
5 changes: 3 additions & 2 deletions tsc/internal/parser/jsdoc.go
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,8 @@ func (p *Parser) parseUnknownTag(start int, tagName *ast.IdentifierNode, indent

func (p *Parser) tryParseTypeExpression() *ast.Node {
p.skipWhitespaceOrAsterisk()
if p.token == ast.KindOpenBraceToken {
// An inline link starts the tag's description, not a type annotation.
if p.token == ast.KindOpenBraceToken && p.lookAhead(func(p *Parser) bool { _, ok := p.parseJSDocLinkPrefix(); return !ok }) {
return p.parseJSDocTypeExpression(false /*mayOmitBraces*/)
} else {
return nil
Expand Down Expand Up @@ -838,7 +839,7 @@ func (p *Parser) parseParameterOrPropertyTag(start int, tagName *ast.IdentifierN
name, isBracketed := p.parseBracketNameInPropertyAndParamTag(target)
indentText := p.skipWhitespaceOrAsterisk()

if isNameFirst && p.lookAhead(func(p *Parser) bool { _, ok := p.parseJSDocLinkPrefix(); return !ok }) {
if isNameFirst {
typeExpression = p.tryParseTypeExpression()
}

Expand Down
52 changes: 52 additions & 0 deletions tsc/internal/parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,58 @@ func TestParseEscapedDynamicImportPhase(t *testing.T) {
}
}

func TestJSDocInlineLinksInOptionalTypePositions(t *testing.T) {
t.Parallel()
for _, tag := range []string{"throws", "returns", "param value"} {
for _, link := range []struct {
name string
kind ast.Kind
}{
{"link", ast.KindJSDocLink},
{"linkcode", ast.KindJSDocLinkCode},
{"linkplain", ast.KindJSDocLinkPlain},
} {
t.Run(tag+"/"+link.name, func(t *testing.T) {
t.Parallel()
file := parser.ParseSourceFile(ast.SourceFileParseOptions{
FileName: "/index.ts",
PathKey: "/index.ts",
}, "/** @"+tag+" {@"+link.name+" C} */\nfunction f(value) {}", core.ScriptKindTS)
jsDocs := file.Statements.Nodes[0].JSDoc(file)
assert.Equal(t, len(jsDocs), 1)
tags := jsDocs[0].AsJSDoc().Tags.Nodes
assert.Equal(t, len(tags), 1)
assert.Assert(t, tags[0].TypeExpression() == nil)
comments := tags[0].CommentList()
assert.Assert(t, comments != nil)
links := core.Filter(comments.Nodes, ast.IsJSDocLinkLike)
assert.Equal(t, len(links), 1)
assert.Equal(t, links[0].Kind, link.kind)
})
}
}
}

func TestJSDocTypesInOptionalTypePositions(t *testing.T) {
t.Parallel()
for _, tag := range []string{"throws", "returns", "param value"} {
t.Run(tag, func(t *testing.T) {
t.Parallel()
file := parser.ParseSourceFile(ast.SourceFileParseOptions{
FileName: "/index.ts",
PathKey: "/index.ts",
}, "/** @"+tag+" {C} description */\nfunction f(value) {}", core.ScriptKindTS)
jsDocs := file.Statements.Nodes[0].JSDoc(file)
assert.Equal(t, len(jsDocs), 1)
tags := jsDocs[0].AsJSDoc().Tags.Nodes
assert.Equal(t, len(tags), 1)
typeExpression := tags[0].TypeExpression()
assert.Assert(t, typeExpression != nil)
assert.Equal(t, scanner.GetTextOfNode(typeExpression.Type()), "C")
})
}
}

func TestJSDocImportTypeParentChain(t *testing.T) {
t.Parallel()
sourceText := `test("", async function () {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// === goToDefinition ===
// === /jsdocInlineLinksInTypePosition.ts ===
// <|class [|C|] {}|>
// /**
// * @throws {@link /*GOTO DEF*/C}
// * @returns {@linkcode C}
// * @param value {@linkplain C}
// */
// --- (line: 7) skipped ---



// === goToDefinition ===
// === /jsdocInlineLinksInTypePosition.ts ===
// <|class [|C|] {}|>
// /**
// * @throws {@link C}
// * @returns {@linkcode /*GOTO DEF*/C}
// * @param value {@linkplain C}
// */
// function f(value: C) {}
// --- (line: 8) skipped ---



// === goToDefinition ===
// === /jsdocInlineLinksInTypePosition.ts ===
// <|class [|C|] {}|>
// /**
// * @throws {@link C}
// * @returns {@linkcode C}
// * @param value {@linkplain /*GOTO DEF*/C}
// */
// function f(value: C) {}
//
// --- (line: 9) skipped ---



// === goToDefinition ===
// === /jsdocInlineLinksInTypePosition.ts ===
// <|class [|C|] {}|>
// /**
// * @throws {@link C}
// * @returns {@linkcode C}
// * @param value {@linkplain C}
// */
// function f(value: C) {}
//
// /**
// * @throws {/*GOTO DEF*/C} description
// * @returns {C} description
// * @param {C} value description
// */
// function g(value: C) { return value; }



// === goToDefinition ===
// === /jsdocInlineLinksInTypePosition.ts ===
// <|class [|C|] {}|>
// /**
// * @throws {@link C}
// * @returns {@linkcode C}
// * @param value {@linkplain C}
// */
// function f(value: C) {}
//
// /**
// * @throws {C} description
// * @returns {/*GOTO DEF*/C} description
// * @param {C} value description
// */
// function g(value: C) { return value; }



// === goToDefinition ===
// === /jsdocInlineLinksInTypePosition.ts ===
// <|class [|C|] {}|>
// /**
// * @throws {@link C}
// * @returns {@linkcode C}
// --- (line: 5) skipped ---

// --- (line: 8) skipped ---
// /**
// * @throws {C} description
// * @returns {C} description
// * @param {/*GOTO DEF*/C} value description
// */
// function g(value: C) { return value; }
Loading