Constructor function methods:Add two missing tag lookups (#47742)

1. During name resolution, `@param` and `@return` tags should walk up
through the jsdoc comment and then jump to the host function. Previously they
did not, which would cause them to not resolve type parameters bound in
the scope of a host that was not a sibling of the comment. The example
from #46618 is a prototype method:

```js
/**
 * @template {T}
 * @param {T} t
 */
C.prototype.m = function (t) {
}
```

2. During name resolution, prototype methods are supposed to resolve
types both from the host function's location and from the containing
class' location. The containing class lookup happens in a separate call
to `resolveName`. Previously, the code that finds the containing class
only worked for the above style of comment, which is on the outer
ExpressionStatement, but not for the below style, which is on the
function expression itself:

```js
C.prototype.m =
  /**
   * @template {T}
   * @param {T} t
   */
  function (t) {
}
```
This commit is contained in:
Nathan Shively-Sanders
2022-02-09 11:22:33 -08:00
committed by GitHub
parent 2cf5afd49e
commit d5c3015516
5 changed files with 212 additions and 8 deletions

View File

@@ -1833,6 +1833,8 @@ namespace ts {
// type parameters are visible in parameter list, return type and type parameter list
? lastLocation === (location as FunctionLikeDeclaration).type ||
lastLocation.kind === SyntaxKind.Parameter ||
lastLocation.kind === SyntaxKind.JSDocParameterTag ||
lastLocation.kind === SyntaxKind.JSDocReturnTag ||
lastLocation.kind === SyntaxKind.TypeParameter
// local types not visible outside the function body
: false;
@@ -2101,8 +2103,8 @@ namespace ts {
lastSelfReferenceLocation = location;
}
lastLocation = location;
location = isJSDocTemplateTag(location) ?
getEffectiveContainerForJSDocTemplateTag(location) || location.parent :
location = isJSDocTemplateTag(location) ? getEffectiveContainerForJSDocTemplateTag(location) || location.parent :
isJSDocParameterTag(location) || isJSDocReturnTag(location) ? getHostSignatureFromJSDoc(location) || location.parent :
location.parent;
}
@@ -3374,16 +3376,20 @@ namespace ts {
return;
}
const host = getJSDocHost(node);
if (host &&
isExpressionStatement(host) &&
isBinaryExpression(host.expression) &&
getAssignmentDeclarationKind(host.expression) === AssignmentDeclarationKind.PrototypeProperty) {
// X.prototype.m = /** @param {K} p */ function () { } <-- look for K on X's declaration
if (host && isExpressionStatement(host) && isPrototypePropertyAssignment(host.expression)) {
// /** @param {K} p */ X.prototype.m = function () { } <-- look for K on X's declaration
const symbol = getSymbolOfNode(host.expression.left);
if (symbol) {
return getDeclarationOfJSPrototypeContainer(symbol);
}
}
if (host && isFunctionExpression(host) && isPrototypePropertyAssignment(host.parent) && isExpressionStatement(host.parent.parent)) {
// X.prototype.m = /** @param {K} p */ function () { } <-- look for K on X's declaration
const symbol = getSymbolOfNode(host.parent.left);
if (symbol) {
return getDeclarationOfJSPrototypeContainer(symbol);
}
}
if (host && (isObjectLiteralMethod(host) || isPropertyAssignment(host)) &&
isBinaryExpression(host.parent.parent) &&
getAssignmentDeclarationKind(host.parent.parent) === AssignmentDeclarationKind.Prototype) {

View File

@@ -2510,7 +2510,7 @@ namespace ts {
return expr.right;
}
export function isPrototypePropertyAssignment(node: Node): boolean {
export function isPrototypePropertyAssignment(node: Node): node is BinaryExpression {
return isBinaryExpression(node) && getAssignmentDeclarationKind(node) === AssignmentDeclarationKind.PrototypeProperty;
}