From 392fd0ac0b5697b405f307700ea7f2a6e6126f71 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Mon, 2 Mar 2020 09:48:53 -0800 Subject: [PATCH] Remove bogus @implements errors (#37114) * Remove bogus @implements errors Make the search for the actual host more comprehensive by reusing the code that previously only searched for functions. I don't know what to call this function now, since the old name wasn't accurate either. * undo gratuitous name change * Improve name and make calling more uniform It's slightly less efficient but I think worthwhile for readability. --- src/compiler/checker.ts | 13 ++-- src/compiler/utilities.ts | 9 ++- .../jsdocImplements_class.errors.txt | 33 ++++++++ .../reference/jsdocImplements_class.js | 65 ++++++++++++++++ .../reference/jsdocImplements_class.symbols | 63 +++++++++++++++ .../reference/jsdocImplements_class.types | 78 +++++++++++++++++++ .../jsdoc/jsdocImplements_class.ts | 33 ++++++++ 7 files changed, 284 insertions(+), 10 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d3a50070d3c..2db4b3468ad 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2910,8 +2910,8 @@ namespace ts { return getDeclarationOfJSPrototypeContainer(symbol); } } - const sig = getHostSignatureFromJSDocHost(host); - if (sig) { + const sig = getEffectiveJSDocHost(node); + if (sig && isFunctionLike(sig)) { const symbol = getSymbolOfNode(sig); return symbol && symbol.valueDeclaration; } @@ -30444,15 +30444,14 @@ namespace ts { } function checkJSDocImplementsTag(node: JSDocImplementsTag): void { - const classLike = getJSDocHost(node); - if (!isClassDeclaration(classLike) && !isClassExpression(classLike)) { + const classLike = getEffectiveJSDocHost(node); + if (!classLike || !isClassDeclaration(classLike) && !isClassExpression(classLike)) { error(classLike, Diagnostics.JSDoc_0_is_not_attached_to_a_class, idText(node.tagName)); - return; } } function checkJSDocAugmentsTag(node: JSDocAugmentsTag): void { - const classLike = getJSDocHost(node); - if (!isClassDeclaration(classLike) && !isClassExpression(classLike)) { + const classLike = getEffectiveJSDocHost(node); + if (!classLike || !isClassDeclaration(classLike) && !isClassExpression(classLike)) { error(classLike, Diagnostics.JSDoc_0_is_not_attached_to_a_class, idText(node.tagName)); return; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index e911c3b5885..71257d1516f 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2466,19 +2466,22 @@ namespace ts { } export function getHostSignatureFromJSDoc(node: Node): SignatureDeclaration | undefined { - return getHostSignatureFromJSDocHost(getJSDocHost(node)); + const host = getEffectiveJSDocHost(node); + return host && isFunctionLike(host) ? host : undefined; } - export function getHostSignatureFromJSDocHost(host: HasJSDoc): SignatureDeclaration | undefined { + export function getEffectiveJSDocHost(node: Node): Node | undefined { + const host = getJSDocHost(node); const decl = getSourceOfDefaultedAssignment(host) || getSourceOfAssignment(host) || getSingleInitializerOfVariableStatementOrPropertyDeclaration(host) || getSingleVariableOfVariableStatement(host) || getNestedModuleDeclaration(host) || host; - return decl && isFunctionLike(decl) ? decl : undefined; + return decl; } + /** Use getEffectiveJSDocHost if you additionally need to look for jsdoc on parent nodes, like assignments. */ export function getJSDocHost(node: Node): HasJSDoc { return Debug.checkDefined(findAncestor(node.parent, isJSDoc)).parent; } diff --git a/tests/baselines/reference/jsdocImplements_class.errors.txt b/tests/baselines/reference/jsdocImplements_class.errors.txt index b69bf199758..8581f9865ba 100644 --- a/tests/baselines/reference/jsdocImplements_class.errors.txt +++ b/tests/baselines/reference/jsdocImplements_class.errors.txt @@ -32,4 +32,37 @@ !!! error TS2720: Property 'method' is missing in type 'B3' but required in type 'A'. !!! related TS2728 /a.js:3:5: 'method' is declared here. } + + + var Ns = {}; + /** @implements {A} */ + Ns.C1 = class { + method() { return 11; } + } + /** @implements {A} */ + var C2 = class { + method() { return 12; } + } + var o = { + /** @implements {A} */ + C3: class { + method() { return 13; } + } + } + class CC { + /** @implements {A} */ + C4 = class { + method() { + return 14; + } + } + } + + var C5; + /** @implements {A} */ + Ns.C5 = C5 || class { + method() { + return 15; + } + } \ No newline at end of file diff --git a/tests/baselines/reference/jsdocImplements_class.js b/tests/baselines/reference/jsdocImplements_class.js index 9f24bbb7203..f9df57c800f 100644 --- a/tests/baselines/reference/jsdocImplements_class.js +++ b/tests/baselines/reference/jsdocImplements_class.js @@ -17,6 +17,39 @@ class B2 { /** @implements {A} */ class B3 { } + + +var Ns = {}; +/** @implements {A} */ +Ns.C1 = class { + method() { return 11; } +} +/** @implements {A} */ +var C2 = class { + method() { return 12; } +} +var o = { + /** @implements {A} */ + C3: class { + method() { return 13; } + } +} +class CC { + /** @implements {A} */ + C4 = class { + method() { + return 14; + } + } +} + +var C5; +/** @implements {A} */ +Ns.C5 = C5 || class { + method() { + return 15; + } +} @@ -38,3 +71,35 @@ declare class B2 implements A { /** @implements {A} */ declare class B3 implements A { } +declare namespace Ns { + export { C1 }; + const C5: { + new (): { + method(): number; + }; + }; +} +/** @implements {A} */ +declare var C2: { + new (): { + method(): number; + }; +}; +declare namespace o { + export { C3 }; +} +declare class CC { + /** @implements {A} */ + C4: { + new (): { + method(): number; + }; + }; +} +declare var C5: any; +declare class C1 implements A { + method(): number; +} +declare class C3 implements A { + method(): number; +} diff --git a/tests/baselines/reference/jsdocImplements_class.symbols b/tests/baselines/reference/jsdocImplements_class.symbols index f7c45ed2567..ab8a02a7679 100644 --- a/tests/baselines/reference/jsdocImplements_class.symbols +++ b/tests/baselines/reference/jsdocImplements_class.symbols @@ -29,3 +29,66 @@ class B3 { >B3 : Symbol(B3, Decl(a.js, 13, 1)) } + +var Ns = {}; +>Ns : Symbol(Ns, Decl(a.js, 20, 3), Decl(a.js, 20, 12), Decl(a.js, 44, 7)) + +/** @implements {A} */ +Ns.C1 = class { +>Ns.C1 : Symbol(Ns.C1, Decl(a.js, 20, 12)) +>Ns : Symbol(Ns, Decl(a.js, 20, 3), Decl(a.js, 20, 12), Decl(a.js, 44, 7)) +>C1 : Symbol(Ns.C1, Decl(a.js, 20, 12)) + + method() { return 11; } +>method : Symbol(C1.method, Decl(a.js, 22, 15)) +} +/** @implements {A} */ +var C2 = class { +>C2 : Symbol(C2, Decl(a.js, 26, 3)) + + method() { return 12; } +>method : Symbol(C2.method, Decl(a.js, 26, 16)) +} +var o = { +>o : Symbol(o, Decl(a.js, 29, 3)) + + /** @implements {A} */ + C3: class { +>C3 : Symbol(C3, Decl(a.js, 29, 9)) + + method() { return 13; } +>method : Symbol(C3.method, Decl(a.js, 31, 15)) + } +} +class CC { +>CC : Symbol(CC, Decl(a.js, 34, 1)) + + /** @implements {A} */ + C4 = class { +>C4 : Symbol(CC.C4, Decl(a.js, 35, 10)) + + method() { +>method : Symbol((Anonymous class).method, Decl(a.js, 37, 16)) + + return 14; + } + } +} + +var C5; +>C5 : Symbol(C5, Decl(a.js, 44, 3)) + +/** @implements {A} */ +Ns.C5 = C5 || class { +>Ns.C5 : Symbol(Ns.C5, Decl(a.js, 44, 7)) +>Ns : Symbol(Ns, Decl(a.js, 20, 3), Decl(a.js, 20, 12), Decl(a.js, 44, 7)) +>C5 : Symbol(Ns.C5, Decl(a.js, 44, 7)) +>C5 : Symbol(C5, Decl(a.js, 44, 3)) + + method() { +>method : Symbol(C5.method, Decl(a.js, 46, 21)) + + return 15; + } +} + diff --git a/tests/baselines/reference/jsdocImplements_class.types b/tests/baselines/reference/jsdocImplements_class.types index fda48e992b6..4b73731d49a 100644 --- a/tests/baselines/reference/jsdocImplements_class.types +++ b/tests/baselines/reference/jsdocImplements_class.types @@ -32,3 +32,81 @@ class B3 { >B3 : B3 } + +var Ns = {}; +>Ns : typeof Ns +>{} : {} + +/** @implements {A} */ +Ns.C1 = class { +>Ns.C1 = class { method() { return 11; }} : typeof C1 +>Ns.C1 : typeof C1 +>Ns : typeof Ns +>C1 : typeof C1 +>class { method() { return 11; }} : typeof C1 + + method() { return 11; } +>method : () => number +>11 : 11 +} +/** @implements {A} */ +var C2 = class { +>C2 : typeof C2 +>class { method() { return 12; }} : typeof C2 + + method() { return 12; } +>method : () => number +>12 : 12 +} +var o = { +>o : { C3: typeof C3; } +>{ /** @implements {A} */ C3: class { method() { return 13; } }} : { C3: typeof C3; } + + /** @implements {A} */ + C3: class { +>C3 : typeof C3 +>class { method() { return 13; } } : typeof C3 + + method() { return 13; } +>method : () => number +>13 : 13 + } +} +class CC { +>CC : CC + + /** @implements {A} */ + C4 = class { +>C4 : typeof (Anonymous class) +>class { method() { return 14; } } : typeof (Anonymous class) + + method() { +>method : () => number + + return 14; +>14 : 14 + } + } +} + +var C5; +>C5 : any + +/** @implements {A} */ +Ns.C5 = C5 || class { +>Ns.C5 = C5 || class { method() { return 15; }} : typeof C5 +>Ns.C5 : typeof C5 +>Ns : typeof Ns +>C5 : typeof C5 +>C5 || class { method() { return 15; }} : typeof C5 +>C5 : undefined +>class { method() { return 15; }} : typeof C5 + + method() { +>method : () => number + + return 15; +>15 : 15 + } +} + diff --git a/tests/cases/conformance/jsdoc/jsdocImplements_class.ts b/tests/cases/conformance/jsdoc/jsdocImplements_class.ts index 2a9d63a072a..50c74ae380d 100644 --- a/tests/cases/conformance/jsdoc/jsdocImplements_class.ts +++ b/tests/cases/conformance/jsdoc/jsdocImplements_class.ts @@ -23,3 +23,36 @@ class B2 { /** @implements {A} */ class B3 { } + + +var Ns = {}; +/** @implements {A} */ +Ns.C1 = class { + method() { return 11; } +} +/** @implements {A} */ +var C2 = class { + method() { return 12; } +} +var o = { + /** @implements {A} */ + C3: class { + method() { return 13; } + } +} +class CC { + /** @implements {A} */ + C4 = class { + method() { + return 14; + } + } +} + +var C5; +/** @implements {A} */ +Ns.C5 = C5 || class { + method() { + return 15; + } +}