-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(60908): Unexpected "'Type' is declared but its value is never read." error with jsdoc @import syntax #60921
Open
a-tarasyuk
wants to merge
5
commits into
microsoft:main
Choose a base branch
from
a-tarasyuk:fix/60908
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 2 commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
7d1ed12
fix(60908): avoid binding jsdocimport children to the jsdoc host cont…
a-tarasyuk 49cdbda
add additional tests
a-tarasyuk 7c18a9c
prevent early binding of importClause in jsdoc imports
a-tarasyuk 9f439bb
Merge branch 'main' of https://github.com/microsoft/TypeScript into f…
a-tarasyuk 58f9d2f
add tests
a-tarasyuk File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
//// [tests/cases/conformance/jsdoc/importTag23.ts] //// | ||
|
||
=== types.d.ts === | ||
export type T = { | ||
>T : Symbol(T, Decl(types.d.ts, 0, 0)) | ||
|
||
a: number; | ||
>a : Symbol(a, Decl(types.d.ts, 0, 17)) | ||
|
||
}; | ||
|
||
=== foo.js === | ||
/** @import { T } from "./types.d.ts" */ | ||
|
||
export default async function f() { | ||
>f : Symbol(f, Decl(foo.js, 0, 0)) | ||
|
||
/** @type {T[]} */ | ||
const types = []; | ||
>types : Symbol(types, Decl(foo.js, 4, 6)) | ||
|
||
return types; | ||
>types : Symbol(types, Decl(foo.js, 4, 6)) | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
//// [tests/cases/conformance/jsdoc/importTag23.ts] //// | ||
|
||
=== types.d.ts === | ||
export type T = { | ||
>T : T | ||
> : ^ | ||
|
||
a: number; | ||
>a : number | ||
> : ^^^^^^ | ||
|
||
}; | ||
|
||
=== foo.js === | ||
/** @import { T } from "./types.d.ts" */ | ||
|
||
export default async function f() { | ||
>f : () => Promise<T[]> | ||
> : ^^^^^^^^^^^^^^^^^^ | ||
|
||
/** @type {T[]} */ | ||
const types = []; | ||
>types : T[] | ||
> : ^^^ | ||
>[] : undefined[] | ||
> : ^^^^^^^^^^^ | ||
|
||
return types; | ||
>types : T[] | ||
> : ^^^ | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
a.js(3,10): error TS6133: 'f1' is declared but its value is never read. | ||
a.js(11,10): error TS6133: 'f3' is declared but its value is never read. | ||
a.js(19,10): error TS6133: 'f4' is declared but its value is never read. | ||
a.js(19,17): error TS2322: Type 'number' is not assignable to type 'string'. | ||
|
||
|
||
==== types.d.ts (0 errors) ==== | ||
export type Foo = string; | ||
|
||
==== a.js (4 errors) ==== | ||
/** @import { Foo } from './types.d.ts') */ | ||
|
||
function f1() { return undefined; } | ||
~~ | ||
!!! error TS6133: 'f1' is declared but its value is never read. | ||
|
||
export function f2() { | ||
/** @type {Set<Foo>} */ | ||
const foo = new Set([ 'a', 'b' ]); | ||
return foo; | ||
} | ||
|
||
function f3() { return undefined; } | ||
~~ | ||
!!! error TS6133: 'f3' is declared but its value is never read. | ||
|
||
/** @type {Set<Foo>} */ | ||
export const foo = new Set([ 'a', 'b' ]); | ||
|
||
/** | ||
* @returns {Foo} | ||
*/ | ||
function f4() { return 1; } | ||
~~ | ||
!!! error TS6133: 'f4' is declared but its value is never read. | ||
~~~~~~ | ||
!!! error TS2322: Type 'number' is not assignable to type 'string'. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
//// [tests/cases/conformance/jsdoc/importTag24.ts] //// | ||
|
||
=== types.d.ts === | ||
export type Foo = string; | ||
>Foo : Symbol(Foo, Decl(types.d.ts, 0, 0)) | ||
|
||
=== a.js === | ||
/** @import { Foo } from './types.d.ts') */ | ||
|
||
function f1() { return undefined; } | ||
>f1 : Symbol(f1, Decl(a.js, 0, 0)) | ||
>undefined : Symbol(undefined) | ||
|
||
export function f2() { | ||
>f2 : Symbol(f2, Decl(a.js, 2, 35)) | ||
|
||
/** @type {Set<Foo>} */ | ||
const foo = new Set([ 'a', 'b' ]); | ||
>foo : Symbol(foo, Decl(a.js, 6, 9)) | ||
>Set : Symbol(Set, Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.esnext.collection.d.ts, --, --)) | ||
|
||
return foo; | ||
>foo : Symbol(foo, Decl(a.js, 6, 9)) | ||
} | ||
|
||
function f3() { return undefined; } | ||
>f3 : Symbol(f3, Decl(a.js, 8, 1)) | ||
>undefined : Symbol(undefined) | ||
|
||
/** @type {Set<Foo>} */ | ||
export const foo = new Set([ 'a', 'b' ]); | ||
>foo : Symbol(foo, Decl(a.js, 13, 12)) | ||
>Set : Symbol(Set, Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.esnext.collection.d.ts, --, --)) | ||
|
||
/** | ||
* @returns {Foo} | ||
*/ | ||
function f4() { return 1; } | ||
>f4 : Symbol(f4, Decl(a.js, 13, 41)) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
//// [tests/cases/conformance/jsdoc/importTag24.ts] //// | ||
|
||
=== Performance Stats === | ||
Type Count: 1,000 | ||
Instantiation count: 2,500 | ||
|
||
=== types.d.ts === | ||
export type Foo = string; | ||
>Foo : string | ||
> : ^^^^^^ | ||
|
||
=== a.js === | ||
/** @import { Foo } from './types.d.ts') */ | ||
|
||
function f1() { return undefined; } | ||
>f1 : () => any | ||
> : ^^^^^^^^^ | ||
>undefined : undefined | ||
> : ^^^^^^^^^ | ||
|
||
export function f2() { | ||
>f2 : () => Set<string> | ||
> : ^^^^^^^^^^^^^^^^^ | ||
|
||
/** @type {Set<Foo>} */ | ||
const foo = new Set([ 'a', 'b' ]); | ||
>foo : Set<string> | ||
> : ^^^^^^^^^^^ | ||
>new Set([ 'a', 'b' ]) : Set<string> | ||
> : ^^^^^^^^^^^ | ||
>Set : SetConstructor | ||
> : ^^^^^^^^^^^^^^ | ||
>[ 'a', 'b' ] : string[] | ||
> : ^^^^^^^^ | ||
>'a' : "a" | ||
> : ^^^ | ||
>'b' : "b" | ||
> : ^^^ | ||
|
||
return foo; | ||
>foo : Set<string> | ||
> : ^^^^^^^^^^^ | ||
} | ||
|
||
function f3() { return undefined; } | ||
>f3 : () => any | ||
> : ^^^^^^^^^ | ||
>undefined : undefined | ||
> : ^^^^^^^^^ | ||
|
||
/** @type {Set<Foo>} */ | ||
export const foo = new Set([ 'a', 'b' ]); | ||
>foo : Set<string> | ||
> : ^^^^^^^^^^^ | ||
>new Set([ 'a', 'b' ]) : Set<string> | ||
> : ^^^^^^^^^^^ | ||
>Set : SetConstructor | ||
> : ^^^^^^^^^^^^^^ | ||
>[ 'a', 'b' ] : string[] | ||
> : ^^^^^^^^ | ||
>'a' : "a" | ||
> : ^^^ | ||
>'b' : "b" | ||
> : ^^^ | ||
|
||
/** | ||
* @returns {Foo} | ||
*/ | ||
function f4() { return 1; } | ||
>f4 : () => Foo | ||
> : ^^^^^^^^^ | ||
>1 : 1 | ||
> : ^ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// @noUnusedLocals: true | ||
// @allowJs: true | ||
// @checkJs: true | ||
// @noEmit: true | ||
|
||
// @filename: types.d.ts | ||
export type T = { | ||
a: number; | ||
}; | ||
|
||
// @filename: foo.js | ||
/** @import { T } from "./types.d.ts" */ | ||
|
||
export default async function f() { | ||
/** @type {T[]} */ | ||
const types = []; | ||
return types; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// @checkJs: true | ||
// @allowJs: true | ||
// @noEmit: true | ||
// @lib: esnext | ||
// @moduleResolution: bundler | ||
// @module: preserve | ||
// @noUnusedLocals: true | ||
// @noUnusedParameters: true | ||
// @allowImportingTsExtensions: true | ||
|
||
// @filename: types.d.ts | ||
export type Foo = string; | ||
|
||
// @filename: a.js | ||
/** @import { Foo } from './types.d.ts') */ | ||
|
||
function f1() { return undefined; } | ||
|
||
export function f2() { | ||
/** @type {Set<Foo>} */ | ||
const foo = new Set([ 'a', 'b' ]); | ||
return foo; | ||
} | ||
|
||
function f3() { return undefined; } | ||
|
||
/** @type {Set<Foo>} */ | ||
export const foo = new Set([ 'a', 'b' ]); | ||
|
||
/** | ||
* @returns {Foo} | ||
*/ | ||
function f4() { return 1; } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is the import tag special here?
Also, this early return will leave
inAssignmentPattern
in a bad state, so I think it's better to skip the steps below that are broken with import tags if needed, or maybe there's something else wrong otherwise.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jakebailey thanks for the feedback. These changes are open for discussion :). This applies to the case when we have
The function acts as a container that holds locals (bind
importClause
), including the symbol Foo. Since this symbol is unused (and doesn't have anexportSymbol
like thetypedef
does), the checker triggers an error.Perhaps the container for the import tag should be treated like a
SourceFile
...Anyway, I’d be grateful for any ideas or suggestions regarding this case. thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mainly, I'm trying to understand why import tags have to be special cased, but other JSDoc "declarations" don't.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jakebailey
JsDocImportTag
reusesimportClause
, however, it doesn't bind it immediately, opting for a delayed binding insteadTypeScript/src/compiler/binder.ts
Lines 1202 to 1204 in 8da951c
TypeScript/src/compiler/binder.ts
Lines 2126 to 2135 in 8da951c
When the
binder
executesbindChildren
andbindEachChild
, it iterates through all child nodes, includingimportClause
, and binds themTypeScript/src/compiler/binder.ts
Lines 3062 to 3063 in 8da951c
TypeScript/src/compiler/binder.ts
Lines 3058 to 3059 in 8da951c
this creates incorrect locals for the parent container. Other tags, such as
@typedef
and@callback
, don't rely on such nodes.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, I see; this honestly makes me wonder why the code doesn't just fall through into the code below, which seems to have a lot more special cases that feel like they'd matter in the same way...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As in, be written:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean it falls over, so that can't be right either
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think unreachable flow cannot be handled properly without
I'm exploring other options to skip processing child nodes for JSDoc imports...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think unreachable flow can remain as is. Alternatively, an additional flag could be added to symbols bound from the JSDoc import (
ImportSpecifier
) to exclude them from the unused locals check. @sandersn @jakebailey what do you think?