Improve @template lookup and resilience (#42851)

* Improve @template lookup and resilience

1. @template parsing may produce a template tag with a type parameter
whose name is the missing identifier. These tags should be skipped
in the checker because they receive an error in the parser.
2. The fix in #37819 was incorrect; there's no such thing as a type
parameter declared on a variable declaration. Instead, there needs to be a type
parameter declared on a jsdoc comment, because that's the scope for tags
like `@return` and `@typedef`.

There are 3 tests because either fix (1) and (2) fix the first test's
failure, but both are required to fix the last two tests' failures.

* remove containsParseError call
This commit is contained in:
Nathan Shively-Sanders 2021-02-17 17:21:17 -08:00 committed by GitHub
parent c3d7a56e90
commit 3d7ec8aab2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 269 additions and 5 deletions

View File

@ -9185,7 +9185,6 @@ namespace ts {
return undefined;
}
switch (node.kind) {
case SyntaxKind.VariableStatement:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
case SyntaxKind.InterfaceDeclaration:
@ -9205,7 +9204,7 @@ namespace ts {
case SyntaxKind.JSDocEnumTag:
case SyntaxKind.JSDocCallbackTag:
case SyntaxKind.MappedType:
case SyntaxKind.ConditionalType:
case SyntaxKind.ConditionalType: {
const outerTypeParameters = getOuterTypeParameters(node, includeThisTypes);
if (node.kind === SyntaxKind.MappedType) {
return append(outerTypeParameters, getDeclaredTypeOfTypeParameter(getSymbolOfNode((<MappedTypeNode>node).typeParameter)));
@ -9213,20 +9212,24 @@ namespace ts {
else if (node.kind === SyntaxKind.ConditionalType) {
return concatenate(outerTypeParameters, getInferTypeParameters(<ConditionalTypeNode>node));
}
else if (node.kind === SyntaxKind.VariableStatement && !isInJSFile(node)) {
break;
}
const outerAndOwnTypeParameters = appendTypeParameters(outerTypeParameters, getEffectiveTypeParameterDeclarations(<DeclarationWithTypeParameters>node));
const thisType = includeThisTypes &&
(node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression || node.kind === SyntaxKind.InterfaceDeclaration || isJSConstructor(node)) &&
getDeclaredTypeOfClassOrInterface(getSymbolOfNode(node as ClassLikeDeclaration | InterfaceDeclaration)).thisType;
return thisType ? append(outerAndOwnTypeParameters, thisType) : outerAndOwnTypeParameters;
}
case SyntaxKind.JSDocParameterTag:
const paramSymbol = getParameterSymbolFromJSDoc(node as JSDocParameterTag);
if (paramSymbol) {
node = paramSymbol.valueDeclaration;
}
break;
case SyntaxKind.JSDocComment: {
const outerTypeParameters = getOuterTypeParameters(node, includeThisTypes);
return (node as JSDoc).tags
? appendTypeParameters(outerTypeParameters, flatMap((node as JSDoc).tags, t => isJSDocTemplateTag(t) ? t.typeParameters : undefined))
: outerTypeParameters;
}
}
}
}

View File

@ -0,0 +1,30 @@
error TS5055: Cannot write file 'tests/cases/conformance/jsdoc/jsdocOuterTypeParameters1.js' because it would overwrite input file.
Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig.
tests/cases/conformance/jsdoc/jsdocOuterTypeParameters1.js(1,14): error TS2304: Cannot find name 'T'.
tests/cases/conformance/jsdoc/jsdocOuterTypeParameters1.js(4,17): error TS2304: Cannot find name 'T'.
tests/cases/conformance/jsdoc/jsdocOuterTypeParameters1.js(4,19): error TS1069: Unexpected token. A type parameter name was expected without curly braces.
tests/cases/conformance/jsdoc/jsdocOuterTypeParameters1.js(7,35): error TS2339: Property 'foo' does not exist on type 'Bar'.
!!! error TS5055: Cannot write file 'tests/cases/conformance/jsdoc/jsdocOuterTypeParameters1.js' because it would overwrite input file.
!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig.
==== tests/cases/conformance/jsdoc/jsdocOuterTypeParameters1.js (4 errors) ====
/** @return {T} */
~
!!! error TS2304: Cannot find name 'T'.
const dedupingMixin = function(mixin) {};
/** @template {T} */
~
!!! error TS2304: Cannot find name 'T'.
~
!!! error TS1069: Unexpected token. A type parameter name was expected without curly braces.
const PropertyAccessors = dedupingMixin(() => {
class Bar {
static bar() { this.prototype.foo(); }
~~~
!!! error TS2339: Property 'foo' does not exist on type 'Bar'.
}
});

View File

@ -0,0 +1,23 @@
=== tests/cases/conformance/jsdoc/jsdocOuterTypeParameters1.js ===
/** @return {T} */
const dedupingMixin = function(mixin) {};
>dedupingMixin : Symbol(dedupingMixin, Decl(jsdocOuterTypeParameters1.js, 1, 5))
>mixin : Symbol(mixin, Decl(jsdocOuterTypeParameters1.js, 1, 31))
/** @template {T} */
const PropertyAccessors = dedupingMixin(() => {
>PropertyAccessors : Symbol(PropertyAccessors, Decl(jsdocOuterTypeParameters1.js, 4, 5))
>dedupingMixin : Symbol(dedupingMixin, Decl(jsdocOuterTypeParameters1.js, 1, 5))
class Bar {
>Bar : Symbol(Bar, Decl(jsdocOuterTypeParameters1.js, 4, 47))
static bar() { this.prototype.foo(); }
>bar : Symbol(Bar.bar, Decl(jsdocOuterTypeParameters1.js, 5, 13))
>this.prototype : Symbol(Bar.prototype)
>this : Symbol(Bar, Decl(jsdocOuterTypeParameters1.js, 4, 47))
>prototype : Symbol(Bar.prototype)
}
});

View File

@ -0,0 +1,29 @@
=== tests/cases/conformance/jsdoc/jsdocOuterTypeParameters1.js ===
/** @return {T} */
const dedupingMixin = function(mixin) {};
>dedupingMixin : (mixin: any) => any
>function(mixin) {} : (mixin: any) => any
>mixin : any
/** @template {T} */
const PropertyAccessors = dedupingMixin(() => {
>PropertyAccessors : any
>dedupingMixin(() => { class Bar { static bar() { this.prototype.foo(); } }}) : any
>dedupingMixin : (mixin: any) => any
>() => { class Bar { static bar() { this.prototype.foo(); } }} : () => void
class Bar {
>Bar : Bar
static bar() { this.prototype.foo(); }
>bar : () => void
>this.prototype.foo() : any
>this.prototype.foo : any
>this.prototype : Bar
>this : typeof Bar
>prototype : Bar
>foo : any
}
});

View File

@ -0,0 +1,24 @@
error TS5055: Cannot write file 'tests/cases/conformance/jsdoc/jsdocOuterTypeParameters1.js' because it would overwrite input file.
Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig.
tests/cases/conformance/jsdoc/jsdocOuterTypeParameters1.js(1,14): error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.
tests/cases/conformance/jsdoc/jsdocOuterTypeParameters1.js(7,35): error TS2339: Property 'foo' does not exist on type 'Bar'.
!!! error TS5055: Cannot write file 'tests/cases/conformance/jsdoc/jsdocOuterTypeParameters1.js' because it would overwrite input file.
!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig.
==== tests/cases/conformance/jsdoc/jsdocOuterTypeParameters1.js (2 errors) ====
/** @return {T} */
~
!!! error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.
const dedupingMixin = function(mixin) {};
/** @template T */
const PropertyAccessors = dedupingMixin(() => {
class Bar {
static bar() { this.prototype.foo(); }
~~~
!!! error TS2339: Property 'foo' does not exist on type 'Bar'.
}
});

View File

@ -0,0 +1,23 @@
=== tests/cases/conformance/jsdoc/jsdocOuterTypeParameters1.js ===
/** @return {T} */
const dedupingMixin = function(mixin) {};
>dedupingMixin : Symbol(dedupingMixin, Decl(jsdocOuterTypeParameters1.js, 1, 5))
>mixin : Symbol(mixin, Decl(jsdocOuterTypeParameters1.js, 1, 31))
/** @template T */
const PropertyAccessors = dedupingMixin(() => {
>PropertyAccessors : Symbol(PropertyAccessors, Decl(jsdocOuterTypeParameters1.js, 4, 5))
>dedupingMixin : Symbol(dedupingMixin, Decl(jsdocOuterTypeParameters1.js, 1, 5))
class Bar {
>Bar : Symbol(Bar, Decl(jsdocOuterTypeParameters1.js, 4, 47))
static bar() { this.prototype.foo(); }
>bar : Symbol(Bar.bar, Decl(jsdocOuterTypeParameters1.js, 5, 13))
>this.prototype : Symbol(Bar.prototype)
>this : Symbol(Bar, Decl(jsdocOuterTypeParameters1.js, 4, 47))
>prototype : Symbol(Bar.prototype)
}
});

View File

@ -0,0 +1,29 @@
=== tests/cases/conformance/jsdoc/jsdocOuterTypeParameters1.js ===
/** @return {T} */
const dedupingMixin = function(mixin) {};
>dedupingMixin : (mixin: any) => T
>function(mixin) {} : (mixin: any) => T
>mixin : any
/** @template T */
const PropertyAccessors = dedupingMixin(() => {
>PropertyAccessors : T
>dedupingMixin(() => { class Bar { static bar() { this.prototype.foo(); } }}) : T
>dedupingMixin : (mixin: any) => T
>() => { class Bar { static bar() { this.prototype.foo(); } }} : () => void
class Bar {
>Bar : Bar
static bar() { this.prototype.foo(); }
>bar : () => void
>this.prototype.foo() : any
>this.prototype.foo : any
>this.prototype : Bar
>this : typeof Bar
>prototype : Bar
>foo : any
}
});

View File

@ -0,0 +1,25 @@
error TS5055: Cannot write file 'tests/cases/conformance/jsdoc/jsdocOuterTypeParameters3.js' because it would overwrite input file.
Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig.
tests/cases/conformance/jsdoc/jsdocOuterTypeParameters3.js(1,16): error TS2304: Cannot find name 'T'.
tests/cases/conformance/jsdoc/jsdocOuterTypeParameters3.js(1,18): error TS1069: Unexpected token. A type parameter name was expected without curly braces.
tests/cases/conformance/jsdoc/jsdocOuterTypeParameters3.js(5,43): error TS2339: Property 'foo' does not exist on type 'Bar'.
!!! error TS5055: Cannot write file 'tests/cases/conformance/jsdoc/jsdocOuterTypeParameters3.js' because it would overwrite input file.
!!! error TS5055: Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig.
==== tests/cases/conformance/jsdoc/jsdocOuterTypeParameters3.js (3 errors) ====
/** @template {T} */
~
!!! error TS2304: Cannot find name 'T'.
~
!!! error TS1069: Unexpected token. A type parameter name was expected without curly braces.
class Baz {
m() {
class Bar {
static bar() { this.prototype.foo(); }
~~~
!!! error TS2339: Property 'foo' does not exist on type 'Bar'.
}
}
}

View File

@ -0,0 +1,20 @@
=== tests/cases/conformance/jsdoc/jsdocOuterTypeParameters3.js ===
/** @template {T} */
class Baz {
>Baz : Symbol(Baz, Decl(jsdocOuterTypeParameters3.js, 0, 0))
m() {
>m : Symbol(Baz.m, Decl(jsdocOuterTypeParameters3.js, 1, 11))
class Bar {
>Bar : Symbol(Bar, Decl(jsdocOuterTypeParameters3.js, 2, 9))
static bar() { this.prototype.foo(); }
>bar : Symbol(Bar.bar, Decl(jsdocOuterTypeParameters3.js, 3, 19))
>this.prototype : Symbol(Bar.prototype)
>this : Symbol(Bar, Decl(jsdocOuterTypeParameters3.js, 2, 9))
>prototype : Symbol(Bar.prototype)
}
}
}

View File

@ -0,0 +1,23 @@
=== tests/cases/conformance/jsdoc/jsdocOuterTypeParameters3.js ===
/** @template {T} */
class Baz {
>Baz : Baz
m() {
>m : () => void
class Bar {
>Bar : Bar
static bar() { this.prototype.foo(); }
>bar : () => void
>this.prototype.foo() : any
>this.prototype.foo : any
>this.prototype : Bar
>this : typeof Bar
>prototype : Bar
>foo : any
}
}
}

View File

@ -0,0 +1,12 @@
// @checkjs: true
// @filename: jsdocOuterTypeParameters1.js
/** @return {T} */
const dedupingMixin = function(mixin) {};
/** @template {T} */
const PropertyAccessors = dedupingMixin(() => {
class Bar {
static bar() { this.prototype.foo(); }
}
});

View File

@ -0,0 +1,12 @@
// @checkjs: true
// @filename: jsdocOuterTypeParameters1.js
/** @return {T} */
const dedupingMixin = function(mixin) {};
/** @template T */
const PropertyAccessors = dedupingMixin(() => {
class Bar {
static bar() { this.prototype.foo(); }
}
});

View File

@ -0,0 +1,11 @@
// @checkjs: true
// @filename: jsdocOuterTypeParameters3.js
/** @template {T} */
class Baz {
m() {
class Bar {
static bar() { this.prototype.foo(); }
}
}
}