Add unqualified JSDoc member references (#44202)

* 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

* Remove type/object literal container check

Since they don't work anyway
This commit is contained in:
Nathan Shively-Sanders 2021-05-26 09:54:05 -07:00 committed by GitHub
parent 3ffa245f07
commit 459bd19941
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 121 additions and 22 deletions

View File

@ -39335,7 +39335,14 @@ namespace ts {
const symbol = getIntrinsicTagSymbol(name.parent as JsxOpeningLikeElement);
return symbol === unknownSymbol ? undefined : symbol;
}
return resolveEntityName(name, meaning, /*ignoreErrors*/ false, /*dontResolveAlias*/ !isJSDoc, getHostSignatureFromJSDoc(name));
const result = resolveEntityName(name, meaning, /*ignoreErrors*/ false, /*dontResolveAlias*/ !isJSDoc, getHostSignatureFromJSDoc(name));
if (!result && isJSDoc) {
const container = findAncestor(name, or(isClassLike, isInterfaceDeclaration));
if (container) {
return resolveJSDocMemberName(name, getSymbolOfNode(container));
}
}
return result;
}
else if (name.kind === SyntaxKind.PropertyAccessExpression || name.kind === SyntaxKind.QualifiedName) {
const links = getNodeLinks(name);
@ -39374,25 +39381,27 @@ namespace ts {
* 1. K#m as K.prototype.m for a class (or other value) K
* 2. K.m as K.prototype.m
* 3. I.m as I.m for a type I, or any other I.m that fails to resolve in (1) or (2)
*
* For unqualified names, a container K may be provided as a second argument.
*/
function resolveJSDocMemberName(name: EntityName | JSDocMemberName): Symbol | undefined {
function resolveJSDocMemberName(name: EntityName | JSDocMemberName, container?: Symbol): Symbol | undefined {
if (isEntityName(name)) {
const symbol = resolveEntityName(
name,
SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Value,
/*ignoreErrors*/ false,
/*dontResolveAlias*/ true,
getHostSignatureFromJSDoc(name));
if (symbol || isIdentifier(name)) {
// can't recur on identifier, so just return when it's undefined
// resolve static values first
const meaning = SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Value;
let symbol = resolveEntityName(name, meaning, /*ignoreErrors*/ false, /*dontResolveAlias*/ true, getHostSignatureFromJSDoc(name));
if (!symbol && isIdentifier(name) && container) {
symbol = getMergedSymbol(getSymbol(getExportsOfSymbol(container), name.escapedText, meaning));
}
if (symbol) {
return symbol;
}
}
const left = resolveJSDocMemberName(name.left);
const left = isIdentifier(name) ? container : resolveJSDocMemberName(name.left);
const right = isIdentifier(name) ? name.escapedText : name.right.escapedText;
if (left) {
const proto = left.flags & SymbolFlags.Value && getPropertyOfType(getTypeOfSymbol(left), "prototype" as __String);
const t = proto ? getTypeOfSymbol(proto) : getDeclaredTypeOfSymbol(left);
return getPropertyOfType(t, name.right.escapedText);
return getPropertyOfType(t, right);
}
}

View File

@ -4,8 +4,8 @@
// n = 1
// static s() { }
// /**
// * {@link m}
// * @see {m}
// * {@link [|m|]}
// * @see {[|m|]}
// * {@link C.[|m|]}
// * @see {C.[|m|]}
// * {@link C#[|m|]}
@ -144,6 +144,24 @@
"isWriteAccess": true,
"isDefinition": true
},
{
"textSpan": {
"start": 73,
"length": 1
},
"fileName": "/tests/cases/fourslash/findAllReferencesLinkTag1.ts",
"isWriteAccess": false,
"isDefinition": false
},
{
"textSpan": {
"start": 89,
"length": 1
},
"fileName": "/tests/cases/fourslash/findAllReferencesLinkTag1.ts",
"isWriteAccess": false,
"isDefinition": false
},
{
"textSpan": {
"start": 108,
@ -219,8 +237,8 @@
// */
// p() { }
// /**
// * {@link n}
// * @see {n}
// * {@link [|n|]}
// * @see {[|n|]}
// * {@link C.[|n|]}
// * @see {C.[|n|]}
// * {@link C#[|n|]}
@ -340,6 +358,24 @@
"isWriteAccess": true,
"isDefinition": true
},
{
"textSpan": {
"start": 265,
"length": 1
},
"fileName": "/tests/cases/fourslash/findAllReferencesLinkTag1.ts",
"isWriteAccess": false,
"isDefinition": false
},
{
"textSpan": {
"start": 281,
"length": 1
},
"fileName": "/tests/cases/fourslash/findAllReferencesLinkTag1.ts",
"isWriteAccess": false,
"isDefinition": false
},
{
"textSpan": {
"start": 300,
@ -426,8 +462,8 @@
// */
// q() { }
// /**
// * {@link s}
// * @see {s}
// * {@link [|s|]}
// * @see {[|s|]}
// * {@link C.[|s|]}
// * @see {C.[|s|]}
// */
@ -544,6 +580,24 @@
"isWriteAccess": true,
"isDefinition": true
},
{
"textSpan": {
"start": 457,
"length": 1
},
"fileName": "/tests/cases/fourslash/findAllReferencesLinkTag1.ts",
"isWriteAccess": false,
"isDefinition": false
},
{
"textSpan": {
"start": 473,
"length": 1
},
"fileName": "/tests/cases/fourslash/findAllReferencesLinkTag1.ts",
"isWriteAccess": false,
"isDefinition": false
},
{
"textSpan": {
"start": 492,
@ -606,8 +660,8 @@
// [|a|]/*FIND ALL REFS*/()
// b: 1
// /**
// * {@link a}
// * @see {a}
// * {@link [|a|]}
// * @see {[|a|]}
// * {@link I.[|a|]}
// * @see {I.[|a|]}
// * {@link I#[|a|]}
@ -712,6 +766,24 @@
"isWriteAccess": false,
"isDefinition": true
},
{
"textSpan": {
"start": 589,
"length": 1
},
"fileName": "/tests/cases/fourslash/findAllReferencesLinkTag1.ts",
"isWriteAccess": false,
"isDefinition": false
},
{
"textSpan": {
"start": 605,
"length": 1
},
"fileName": "/tests/cases/fourslash/findAllReferencesLinkTag1.ts",
"isWriteAccess": false,
"isDefinition": false
},
{
"textSpan": {
"start": 624,
@ -801,8 +873,8 @@
// */
// c()
// /**
// * {@link b}
// * @see {b}
// * {@link [|b|]}
// * @see {[|b|]}
// * {@link I.[|b|]}
// * @see {I.[|b|]}
// */
@ -890,6 +962,24 @@
"isWriteAccess": false,
"isDefinition": true
},
{
"textSpan": {
"start": 720,
"length": 1
},
"fileName": "/tests/cases/fourslash/findAllReferencesLinkTag1.ts",
"isWriteAccess": false,
"isDefinition": false
},
{
"textSpan": {
"start": 736,
"length": 1
},
"fileName": "/tests/cases/fourslash/findAllReferencesLinkTag1.ts",
"isWriteAccess": false,
"isDefinition": false
},
{
"textSpan": {
"start": 755,