Fix @typedef/@callback scope (#43682)

JSDoc typedefs don't actually have hosts, because they're not
semantically attached to a declaration. However, the parser still
attaches them to some declaration (or statement), but that declaration
is not related to the typedef.

Previously, delayedBindJSDocTypedefTag used getJSDocHost to walk past
the unrelated declaration, but #41858 correctly started categorising
typedefs as unattached, with no host, so the binder began falling
back to file scope.

The path to skip the unrelated declaration is always the same, though, so this
PR uses `typeAlias.parent.parent` instead of `getJSDocHost(typeAlias)`.
This commit is contained in:
Nathan Shively-Sanders 2021-04-14 16:56:37 -07:00 committed by GitHub
parent 3ab6809e38
commit 3f93d420bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 130 additions and 3 deletions

View File

@ -2123,9 +2123,9 @@ namespace ts {
const saveParent = parent;
const saveCurrentFlow = currentFlow;
for (const typeAlias of delayedTypeAliases) {
const host = getJSDocHost(typeAlias);
container = (host && findAncestor(host.parent, n => !!(getContainerFlags(n) & ContainerFlags.IsContainer))) || file;
blockScopeContainer = (host && getEnclosingBlockScopeContainer(host)) || file;
const host = typeAlias.parent.parent;
container = findAncestor(host.parent, n => !!(getContainerFlags(n) & ContainerFlags.IsContainer)) || file;
blockScopeContainer = getEnclosingBlockScopeContainer(host) || file;
currentFlow = initFlowNode({ flags: FlowFlags.Start });
parent = typeAlias;
bind(typeAlias.typeExpression);

View File

@ -0,0 +1,21 @@
tests/cases/conformance/jsdoc/typedefScope1.js(13,12): error TS2304: Cannot find name 'B'.
==== tests/cases/conformance/jsdoc/typedefScope1.js (1 errors) ====
function B1() {
/** @typedef {number} B */
/** @type {B} */
var ok1 = 0;
}
function B2() {
/** @typedef {string} B */
/** @type {B} */
var ok2 = 'hi';
}
/** @type {B} */
~
!!! error TS2304: Cannot find name 'B'.
var notOK = 0;

View File

@ -0,0 +1,38 @@
//// [typedefScope1.js]
function B1() {
/** @typedef {number} B */
/** @type {B} */
var ok1 = 0;
}
function B2() {
/** @typedef {string} B */
/** @type {B} */
var ok2 = 'hi';
}
/** @type {B} */
var notOK = 0;
//// [typedefScope1.js]
"use strict";
function B1() {
/** @typedef {number} B */
/** @type {B} */
var ok1 = 0;
}
function B2() {
/** @typedef {string} B */
/** @type {B} */
var ok2 = 'hi';
}
/** @type {B} */
var notOK = 0;
//// [typedefScope1.d.ts]
declare function B1(): void;
declare function B2(): void;
/** @type {B} */
declare var notOK: any;

View File

@ -0,0 +1,23 @@
=== tests/cases/conformance/jsdoc/typedefScope1.js ===
function B1() {
>B1 : Symbol(B1, Decl(typedefScope1.js, 0, 0))
/** @typedef {number} B */
/** @type {B} */
var ok1 = 0;
>ok1 : Symbol(ok1, Decl(typedefScope1.js, 3, 7))
}
function B2() {
>B2 : Symbol(B2, Decl(typedefScope1.js, 4, 1))
/** @typedef {string} B */
/** @type {B} */
var ok2 = 'hi';
>ok2 : Symbol(ok2, Decl(typedefScope1.js, 9, 7))
}
/** @type {B} */
var notOK = 0;
>notOK : Symbol(notOK, Decl(typedefScope1.js, 13, 3))

View File

@ -0,0 +1,26 @@
=== tests/cases/conformance/jsdoc/typedefScope1.js ===
function B1() {
>B1 : () => void
/** @typedef {number} B */
/** @type {B} */
var ok1 = 0;
>ok1 : number
>0 : 0
}
function B2() {
>B2 : () => void
/** @typedef {string} B */
/** @type {B} */
var ok2 = 'hi';
>ok2 : string
>'hi' : "hi"
}
/** @type {B} */
var notOK = 0;
>notOK : any
>0 : 0

View File

@ -0,0 +1,19 @@
// @strict: true
// @declaration: true
// @outdir: out/
// @checkJs: true
// @filename: typedefScope1.js
function B1() {
/** @typedef {number} B */
/** @type {B} */
var ok1 = 0;
}
function B2() {
/** @typedef {string} B */
/** @type {B} */
var ok2 = 'hi';
}
/** @type {B} */
var notOK = 0;