Skip to content

Commit 362ef85

Browse files
committed
Add unqualified JSDoc member references
This allows unqualified references like: ```ts class Zero { /** @param buddy Must be {@link D_HORSE} or {@link D_DOG}. */ deploy(buddy: number) { } static D_HORSE = 1 static D_DOG = 2 } ``` I surveyed @see and @link again to estimate how common this is. I found a little over 200 uses, which is around 2%. Sorted by frequency, this *is* the next feature on the list, along with the `module:` prefix. So I think this is about the right point to stop adding code. In this case, however, I liked most of the uses -- there were a lot of deprecated functions that referenced a function just below, where it would be wordy to qualify the name, but the reader would benefit from a link. Note that unqualified references do *not* work inside type or object literals. The code I ended up with is quite complicated and I didn't observe any uses in the wild. Fixes #43595
1 parent 0864237 commit 362ef85

File tree

2 files changed

+121
-22
lines changed

2 files changed

+121
-22
lines changed

src/compiler/checker.ts

+21-12
Original file line numberDiff line numberDiff line change
@@ -39320,7 +39320,14 @@ namespace ts {
3932039320
const symbol = getIntrinsicTagSymbol(name.parent as JsxOpeningLikeElement);
3932139321
return symbol === unknownSymbol ? undefined : symbol;
3932239322
}
39323-
return resolveEntityName(name, meaning, /*ignoreErrors*/ false, /*dontResolveAlias*/ !isJSDoc, getHostSignatureFromJSDoc(name));
39323+
const result = resolveEntityName(name, meaning, /*ignoreErrors*/ false, /*dontResolveAlias*/ !isJSDoc, getHostSignatureFromJSDoc(name));
39324+
if (!result && isJSDoc) {
39325+
const container = findAncestor(name, or(isClassLike, isInterfaceDeclaration, isObjectLiteralExpression, isTypeLiteralNode));
39326+
if (container) {
39327+
return resolveJSDocMemberName(name, getSymbolOfNode(container));
39328+
}
39329+
}
39330+
return result;
3932439331
}
3932539332
else if (name.kind === SyntaxKind.PropertyAccessExpression || name.kind === SyntaxKind.QualifiedName) {
3932639333
const links = getNodeLinks(name);
@@ -39359,25 +39366,27 @@ namespace ts {
3935939366
* 1. K#m as K.prototype.m for a class (or other value) K
3936039367
* 2. K.m as K.prototype.m
3936139368
* 3. I.m as I.m for a type I, or any other I.m that fails to resolve in (1) or (2)
39369+
*
39370+
* For unqualified names, a container K may be provided as a second argument.
3936239371
*/
39363-
function resolveJSDocMemberName(name: EntityName | JSDocMemberName): Symbol | undefined {
39372+
function resolveJSDocMemberName(name: EntityName | JSDocMemberName, container?: Symbol): Symbol | undefined {
3936439373
if (isEntityName(name)) {
39365-
const symbol = resolveEntityName(
39366-
name,
39367-
SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Value,
39368-
/*ignoreErrors*/ false,
39369-
/*dontResolveAlias*/ true,
39370-
getHostSignatureFromJSDoc(name));
39371-
if (symbol || isIdentifier(name)) {
39372-
// can't recur on identifier, so just return when it's undefined
39374+
// resolve static values first
39375+
const meaning = SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Value;
39376+
let symbol = resolveEntityName(name, meaning, /*ignoreErrors*/ false, /*dontResolveAlias*/ true, getHostSignatureFromJSDoc(name));
39377+
if (!symbol && isIdentifier(name) && container) {
39378+
symbol = getMergedSymbol(getSymbol(getExportsOfSymbol(container), name.escapedText, meaning));
39379+
}
39380+
if (symbol) {
3937339381
return symbol;
3937439382
}
3937539383
}
39376-
const left = resolveJSDocMemberName(name.left);
39384+
const left = isIdentifier(name) ? container : resolveJSDocMemberName(name.left);
39385+
const right = isIdentifier(name) ? name.escapedText : name.right.escapedText;
3937739386
if (left) {
3937839387
const proto = left.flags & SymbolFlags.Value && getPropertyOfType(getTypeOfSymbol(left), "prototype" as __String);
3937939388
const t = proto ? getTypeOfSymbol(proto) : getDeclaredTypeOfSymbol(left);
39380-
return getPropertyOfType(t, name.right.escapedText);
39389+
return getPropertyOfType(t, right);
3938139390
}
3938239391
}
3938339392

tests/baselines/reference/findAllReferencesLinkTag1.baseline.jsonc

+100-10
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
// n = 1
55
// static s() { }
66
// /**
7-
// * {@link m}
8-
// * @see {m}
7+
// * {@link [|m|]}
8+
// * @see {[|m|]}
99
// * {@link C.[|m|]}
1010
// * @see {C.[|m|]}
1111
// * {@link C#[|m|]}
@@ -144,6 +144,24 @@
144144
"isWriteAccess": true,
145145
"isDefinition": true
146146
},
147+
{
148+
"textSpan": {
149+
"start": 73,
150+
"length": 1
151+
},
152+
"fileName": "/tests/cases/fourslash/findAllReferencesLinkTag1.ts",
153+
"isWriteAccess": false,
154+
"isDefinition": false
155+
},
156+
{
157+
"textSpan": {
158+
"start": 89,
159+
"length": 1
160+
},
161+
"fileName": "/tests/cases/fourslash/findAllReferencesLinkTag1.ts",
162+
"isWriteAccess": false,
163+
"isDefinition": false
164+
},
147165
{
148166
"textSpan": {
149167
"start": 108,
@@ -219,8 +237,8 @@
219237
// */
220238
// p() { }
221239
// /**
222-
// * {@link n}
223-
// * @see {n}
240+
// * {@link [|n|]}
241+
// * @see {[|n|]}
224242
// * {@link C.[|n|]}
225243
// * @see {C.[|n|]}
226244
// * {@link C#[|n|]}
@@ -340,6 +358,24 @@
340358
"isWriteAccess": true,
341359
"isDefinition": true
342360
},
361+
{
362+
"textSpan": {
363+
"start": 265,
364+
"length": 1
365+
},
366+
"fileName": "/tests/cases/fourslash/findAllReferencesLinkTag1.ts",
367+
"isWriteAccess": false,
368+
"isDefinition": false
369+
},
370+
{
371+
"textSpan": {
372+
"start": 281,
373+
"length": 1
374+
},
375+
"fileName": "/tests/cases/fourslash/findAllReferencesLinkTag1.ts",
376+
"isWriteAccess": false,
377+
"isDefinition": false
378+
},
343379
{
344380
"textSpan": {
345381
"start": 300,
@@ -426,8 +462,8 @@
426462
// */
427463
// q() { }
428464
// /**
429-
// * {@link s}
430-
// * @see {s}
465+
// * {@link [|s|]}
466+
// * @see {[|s|]}
431467
// * {@link C.[|s|]}
432468
// * @see {C.[|s|]}
433469
// */
@@ -544,6 +580,24 @@
544580
"isWriteAccess": true,
545581
"isDefinition": true
546582
},
583+
{
584+
"textSpan": {
585+
"start": 457,
586+
"length": 1
587+
},
588+
"fileName": "/tests/cases/fourslash/findAllReferencesLinkTag1.ts",
589+
"isWriteAccess": false,
590+
"isDefinition": false
591+
},
592+
{
593+
"textSpan": {
594+
"start": 473,
595+
"length": 1
596+
},
597+
"fileName": "/tests/cases/fourslash/findAllReferencesLinkTag1.ts",
598+
"isWriteAccess": false,
599+
"isDefinition": false
600+
},
547601
{
548602
"textSpan": {
549603
"start": 492,
@@ -606,8 +660,8 @@
606660
// [|a|]/*FIND ALL REFS*/()
607661
// b: 1
608662
// /**
609-
// * {@link a}
610-
// * @see {a}
663+
// * {@link [|a|]}
664+
// * @see {[|a|]}
611665
// * {@link I.[|a|]}
612666
// * @see {I.[|a|]}
613667
// * {@link I#[|a|]}
@@ -712,6 +766,24 @@
712766
"isWriteAccess": false,
713767
"isDefinition": true
714768
},
769+
{
770+
"textSpan": {
771+
"start": 589,
772+
"length": 1
773+
},
774+
"fileName": "/tests/cases/fourslash/findAllReferencesLinkTag1.ts",
775+
"isWriteAccess": false,
776+
"isDefinition": false
777+
},
778+
{
779+
"textSpan": {
780+
"start": 605,
781+
"length": 1
782+
},
783+
"fileName": "/tests/cases/fourslash/findAllReferencesLinkTag1.ts",
784+
"isWriteAccess": false,
785+
"isDefinition": false
786+
},
715787
{
716788
"textSpan": {
717789
"start": 624,
@@ -801,8 +873,8 @@
801873
// */
802874
// c()
803875
// /**
804-
// * {@link b}
805-
// * @see {b}
876+
// * {@link [|b|]}
877+
// * @see {[|b|]}
806878
// * {@link I.[|b|]}
807879
// * @see {I.[|b|]}
808880
// */
@@ -890,6 +962,24 @@
890962
"isWriteAccess": false,
891963
"isDefinition": true
892964
},
965+
{
966+
"textSpan": {
967+
"start": 720,
968+
"length": 1
969+
},
970+
"fileName": "/tests/cases/fourslash/findAllReferencesLinkTag1.ts",
971+
"isWriteAccess": false,
972+
"isDefinition": false
973+
},
974+
{
975+
"textSpan": {
976+
"start": 736,
977+
"length": 1
978+
},
979+
"fileName": "/tests/cases/fourslash/findAllReferencesLinkTag1.ts",
980+
"isWriteAccess": false,
981+
"isDefinition": false
982+
},
893983
{
894984
"textSpan": {
895985
"start": 755,

0 commit comments

Comments
 (0)