mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-07-04 14:56:16 -05:00
Fix jsdoc type resolution [merge to master] (#24204)
* Fix JSDoc type resolution Breaks type parameter resolution that is looked up through prototype methods, though. I need to fix that still. * Check for prototype method assignments first * Undo dedupe changes to getJSDocTags * JS Type aliases can't refer to host type params Previously, js type aliases (@typedef and @callback) could refer to type paremeters defined in @template tags in a *different* jsdoc tag, as long as both tags were hosted on the same signature. * Reduce dedupe changes+update baseline The only reason I had undone them was to merge successfully with an older state of master.
This commit is contained in:
committed by
GitHub
parent
2b5ff29254
commit
6450490844
@@ -1453,6 +1453,12 @@ namespace ts {
|
||||
location = location.parent;
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.JSDocTypedefTag:
|
||||
case SyntaxKind.JSDocCallbackTag:
|
||||
// js type aliases do not resolve names from their host, so skip past it
|
||||
lastLocation = location;
|
||||
location = getJSDocHost(location).parent;
|
||||
continue;
|
||||
}
|
||||
if (isSelfReferenceLocation(location)) {
|
||||
lastSelfReferenceLocation = location;
|
||||
@@ -2153,25 +2159,31 @@ namespace ts {
|
||||
*/
|
||||
function resolveEntityNameFromJSSpecialAssignment(name: Identifier, meaning: SymbolFlags) {
|
||||
if (isJSDocTypeReference(name.parent)) {
|
||||
const host = getJSDocHost(name.parent);
|
||||
if (host) {
|
||||
const secondaryLocation = getJSSpecialAssignmentSymbol(getJSDocHost(name.parent.parent.parent as JSDocTag));
|
||||
return secondaryLocation && resolveName(secondaryLocation, name.escapedText, meaning, /*nameNotFoundMessage*/ undefined, name, /*isUse*/ true);
|
||||
const secondaryLocation = getJSSpecialAssignmentLocation(name.parent);
|
||||
if (secondaryLocation) {
|
||||
return resolveName(secondaryLocation, name.escapedText, meaning, /*nameNotFoundMessage*/ undefined, name, /*isUse*/ true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getJSSpecialAssignmentSymbol(host: HasJSDoc): Declaration | undefined {
|
||||
if (isPropertyAssignment(host) && isFunctionLike(host.initializer)) {
|
||||
const symbol = getSymbolOfNode(host.initializer);
|
||||
return symbol && symbol.valueDeclaration;
|
||||
function getJSSpecialAssignmentLocation(node: TypeReferenceNode): Declaration | undefined {
|
||||
const typeAlias = findAncestor(node, node => !(isJSDocNode(node) || node.flags & NodeFlags.JSDoc) ? "quit" : isJSDocTypeAlias(node));
|
||||
if (typeAlias) {
|
||||
return;
|
||||
}
|
||||
else if (isExpressionStatement(host) &&
|
||||
isBinaryExpression(host.expression) &&
|
||||
getSpecialPropertyAssignmentKind(host.expression) === SpecialPropertyAssignmentKind.PrototypeProperty) {
|
||||
const host = getJSDocHost(node);
|
||||
if (host &&
|
||||
isExpressionStatement(host) &&
|
||||
isBinaryExpression(host.expression) &&
|
||||
getSpecialPropertyAssignmentKind(host.expression) === SpecialPropertyAssignmentKind.PrototypeProperty) {
|
||||
const symbol = getSymbolOfNode(host.expression.left);
|
||||
return symbol && symbol.parent.valueDeclaration;
|
||||
}
|
||||
const sig = getHostSignatureFromJSDocHost(host);
|
||||
if (sig) {
|
||||
const symbol = getSymbolOfNode(sig);
|
||||
return symbol && symbol.valueDeclaration;
|
||||
}
|
||||
}
|
||||
|
||||
function resolveExternalModuleName(location: Node, moduleReferenceExpression: Expression): Symbol {
|
||||
@@ -9554,7 +9566,16 @@ namespace ts {
|
||||
// parameters that are in scope (and therefore potentially referenced). For type literals that
|
||||
// aren't the right hand side of a generic type alias declaration we optimize by reducing the
|
||||
// set of type parameters to those that are possibly referenced in the literal.
|
||||
const declaration = symbol.declarations[0];
|
||||
let declaration = symbol.declarations[0];
|
||||
if (isInJavaScriptFile(declaration)) {
|
||||
const paramTag = findAncestor(declaration, isJSDocParameterTag);
|
||||
if (paramTag) {
|
||||
const paramSymbol = getParameterSymbolFromJSDoc(paramTag);
|
||||
if (paramSymbol) {
|
||||
declaration = paramSymbol.valueDeclaration;
|
||||
}
|
||||
}
|
||||
}
|
||||
let outerTypeParameters = getOuterTypeParameters(declaration, /*includeThisTypes*/ true);
|
||||
if (isJavaScriptConstructor(declaration)) {
|
||||
const templateTagParameters = getTypeParametersFromDeclaration(declaration as DeclarationWithTypeParameters);
|
||||
|
||||
@@ -1865,8 +1865,7 @@ namespace ts {
|
||||
// * @returns {number}
|
||||
// */
|
||||
// var x = function(name) { return name.length; }
|
||||
if (parent.parent &&
|
||||
(getSingleVariableOfVariableStatement(parent.parent) === node)) {
|
||||
if (parent.parent && (getSingleVariableOfVariableStatement(parent.parent) === node)) {
|
||||
getJSDocCommentsAndTagsWorker(parent.parent);
|
||||
}
|
||||
if (parent.parent && parent.parent.parent &&
|
||||
@@ -1913,8 +1912,11 @@ namespace ts {
|
||||
return parameter && parameter.symbol;
|
||||
}
|
||||
|
||||
export function getHostSignatureFromJSDoc(node: JSDocTag): SignatureDeclaration | undefined {
|
||||
const host = getJSDocHost(node);
|
||||
export function getHostSignatureFromJSDoc(node: Node): SignatureDeclaration | undefined {
|
||||
return getHostSignatureFromJSDocHost(getJSDocHost(node));
|
||||
}
|
||||
|
||||
export function getHostSignatureFromJSDocHost(host: HasJSDoc): SignatureDeclaration | undefined {
|
||||
const decl = getSourceOfDefaultedAssignment(host) ||
|
||||
getSourceOfAssignment(host) ||
|
||||
getSingleInitializerOfVariableStatementOrPropertyDeclaration(host) ||
|
||||
|
||||
23
tests/baselines/reference/paramTagTypeResolution.symbols
Normal file
23
tests/baselines/reference/paramTagTypeResolution.symbols
Normal file
@@ -0,0 +1,23 @@
|
||||
=== tests/cases/conformance/jsdoc/main.js ===
|
||||
var f = require('./first');
|
||||
>f : Symbol(f, Decl(main.js, 0, 3))
|
||||
>require : Symbol(require)
|
||||
>'./first' : Symbol("tests/cases/conformance/jsdoc/first", Decl(first.js, 0, 0))
|
||||
|
||||
f(1, n => { })
|
||||
>f : Symbol(f, Decl(main.js, 0, 3))
|
||||
>n : Symbol(n, Decl(main.js, 1, 4))
|
||||
|
||||
=== tests/cases/conformance/jsdoc/first.js ===
|
||||
/** @template T
|
||||
* @param {T} x
|
||||
* @param {(t: T) => void} k
|
||||
*/
|
||||
module.exports = function (x, k) { return k(x) }
|
||||
>module : Symbol(export=, Decl(first.js, 0, 0))
|
||||
>exports : Symbol(export=, Decl(first.js, 0, 0))
|
||||
>x : Symbol(x, Decl(first.js, 4, 27))
|
||||
>k : Symbol(k, Decl(first.js, 4, 29))
|
||||
>k : Symbol(k, Decl(first.js, 4, 29))
|
||||
>x : Symbol(x, Decl(first.js, 4, 27))
|
||||
|
||||
31
tests/baselines/reference/paramTagTypeResolution.types
Normal file
31
tests/baselines/reference/paramTagTypeResolution.types
Normal file
@@ -0,0 +1,31 @@
|
||||
=== tests/cases/conformance/jsdoc/main.js ===
|
||||
var f = require('./first');
|
||||
>f : <T>(x: T, k: (t: T) => void) => void
|
||||
>require('./first') : <T>(x: T, k: (t: T) => void) => void
|
||||
>require : any
|
||||
>'./first' : "./first"
|
||||
|
||||
f(1, n => { })
|
||||
>f(1, n => { }) : void
|
||||
>f : <T>(x: T, k: (t: T) => void) => void
|
||||
>1 : 1
|
||||
>n => { } : (n: number) => void
|
||||
>n : number
|
||||
|
||||
=== tests/cases/conformance/jsdoc/first.js ===
|
||||
/** @template T
|
||||
* @param {T} x
|
||||
* @param {(t: T) => void} k
|
||||
*/
|
||||
module.exports = function (x, k) { return k(x) }
|
||||
>module.exports = function (x, k) { return k(x) } : <T>(x: T, k: (t: T) => void) => void
|
||||
>module.exports : any
|
||||
>module : any
|
||||
>exports : any
|
||||
>function (x, k) { return k(x) } : <T>(x: T, k: (t: T) => void) => void
|
||||
>x : T
|
||||
>k : (t: T) => void
|
||||
>k(x) : void
|
||||
>k : (t: T) => void
|
||||
>x : T
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
tests/cases/conformance/jsdoc/github20832.js(2,15): error TS2304: Cannot find name 'U'.
|
||||
tests/cases/conformance/jsdoc/github20832.js(17,12): error TS2304: Cannot find name 'V'.
|
||||
|
||||
|
||||
==== tests/cases/conformance/jsdoc/github20832.js (2 errors) ====
|
||||
// #20832
|
||||
/** @typedef {U} T - should be "error, can't find type named 'U' */
|
||||
~
|
||||
!!! error TS2304: Cannot find name 'U'.
|
||||
/**
|
||||
* @template U
|
||||
* @param {U} x
|
||||
* @return {T}
|
||||
*/
|
||||
function f(x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
/** @type T - should be fine, since T will be any */
|
||||
const x = 3;
|
||||
|
||||
/**
|
||||
* @callback Cb
|
||||
* @param {V} firstParam
|
||||
~
|
||||
!!! error TS2304: Cannot find name 'V'.
|
||||
*/
|
||||
/**
|
||||
* @template V
|
||||
* @param {V} vvvvv
|
||||
*/
|
||||
function g(vvvvv) {
|
||||
}
|
||||
|
||||
/** @type {Cb} */
|
||||
const cb = x => {}
|
||||
|
||||
38
tests/baselines/reference/typedefTagTypeResolution.symbols
Normal file
38
tests/baselines/reference/typedefTagTypeResolution.symbols
Normal file
@@ -0,0 +1,38 @@
|
||||
=== tests/cases/conformance/jsdoc/github20832.js ===
|
||||
// #20832
|
||||
/** @typedef {U} T - should be "error, can't find type named 'U' */
|
||||
/**
|
||||
* @template U
|
||||
* @param {U} x
|
||||
* @return {T}
|
||||
*/
|
||||
function f(x) {
|
||||
>f : Symbol(f, Decl(github20832.js, 0, 0))
|
||||
>x : Symbol(x, Decl(github20832.js, 7, 11))
|
||||
|
||||
return x;
|
||||
>x : Symbol(x, Decl(github20832.js, 7, 11))
|
||||
}
|
||||
|
||||
/** @type T - should be fine, since T will be any */
|
||||
const x = 3;
|
||||
>x : Symbol(x, Decl(github20832.js, 12, 5))
|
||||
|
||||
/**
|
||||
* @callback Cb
|
||||
* @param {V} firstParam
|
||||
*/
|
||||
/**
|
||||
* @template V
|
||||
* @param {V} vvvvv
|
||||
*/
|
||||
function g(vvvvv) {
|
||||
>g : Symbol(g, Decl(github20832.js, 12, 12))
|
||||
>vvvvv : Symbol(vvvvv, Decl(github20832.js, 22, 11))
|
||||
}
|
||||
|
||||
/** @type {Cb} */
|
||||
const cb = x => {}
|
||||
>cb : Symbol(cb, Decl(github20832.js, 26, 5))
|
||||
>x : Symbol(x, Decl(github20832.js, 26, 10))
|
||||
|
||||
40
tests/baselines/reference/typedefTagTypeResolution.types
Normal file
40
tests/baselines/reference/typedefTagTypeResolution.types
Normal file
@@ -0,0 +1,40 @@
|
||||
=== tests/cases/conformance/jsdoc/github20832.js ===
|
||||
// #20832
|
||||
/** @typedef {U} T - should be "error, can't find type named 'U' */
|
||||
/**
|
||||
* @template U
|
||||
* @param {U} x
|
||||
* @return {T}
|
||||
*/
|
||||
function f(x) {
|
||||
>f : <U>(x: U) => any
|
||||
>x : U
|
||||
|
||||
return x;
|
||||
>x : U
|
||||
}
|
||||
|
||||
/** @type T - should be fine, since T will be any */
|
||||
const x = 3;
|
||||
>x : any
|
||||
>3 : 3
|
||||
|
||||
/**
|
||||
* @callback Cb
|
||||
* @param {V} firstParam
|
||||
*/
|
||||
/**
|
||||
* @template V
|
||||
* @param {V} vvvvv
|
||||
*/
|
||||
function g(vvvvv) {
|
||||
>g : <V>(vvvvv: V) => void
|
||||
>vvvvv : V
|
||||
}
|
||||
|
||||
/** @type {Cb} */
|
||||
const cb = x => {}
|
||||
>cb : Cb
|
||||
>x => {} : (x: any) => void
|
||||
>x : any
|
||||
|
||||
13
tests/cases/conformance/jsdoc/paramTagTypeResolution.ts
Normal file
13
tests/cases/conformance/jsdoc/paramTagTypeResolution.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
// @noEmit: true
|
||||
// @allowJs: true
|
||||
// @checkJs: true
|
||||
// @Filename: first.js
|
||||
/** @template T
|
||||
* @param {T} x
|
||||
* @param {(t: T) => void} k
|
||||
*/
|
||||
module.exports = function (x, k) { return k(x) }
|
||||
|
||||
// @Filename: main.js
|
||||
var f = require('./first');
|
||||
f(1, n => { })
|
||||
32
tests/cases/conformance/jsdoc/typedefTagTypeResolution.ts
Normal file
32
tests/cases/conformance/jsdoc/typedefTagTypeResolution.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
// @noEmit: true
|
||||
// @allowJs: true
|
||||
// @checkJs: true
|
||||
// @Filename: github20832.js
|
||||
|
||||
// #20832
|
||||
/** @typedef {U} T - should be "error, can't find type named 'U' */
|
||||
/**
|
||||
* @template U
|
||||
* @param {U} x
|
||||
* @return {T}
|
||||
*/
|
||||
function f(x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
/** @type T - should be fine, since T will be any */
|
||||
const x = 3;
|
||||
|
||||
/**
|
||||
* @callback Cb
|
||||
* @param {V} firstParam
|
||||
*/
|
||||
/**
|
||||
* @template V
|
||||
* @param {V} vvvvv
|
||||
*/
|
||||
function g(vvvvv) {
|
||||
}
|
||||
|
||||
/** @type {Cb} */
|
||||
const cb = x => {}
|
||||
Reference in New Issue
Block a user