From dabf2a6af2f8a70a5f44dd0dd5b4f2b99a3c77d4 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Fri, 5 Apr 2019 16:37:27 -0700 Subject: [PATCH] Always check extends clause of classes Even if (1) @extends is provided and (2) we're not producing diagnostics. That's because we need to know whether the extends clause references an imported alias. --- src/compiler/checker.ts | 9 +++++---- .../reference/extendsTagEmit.errors.txt | 17 ++++++++++++++++ tests/baselines/reference/extendsTagEmit.js | 20 +++++++++++++++++++ .../reference/extendsTagEmit.symbols | 14 +++++++++++++ .../baselines/reference/extendsTagEmit.types | 14 +++++++++++++ .../cases/conformance/jsdoc/extendsTagEmit.ts | 12 +++++++++++ 6 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 tests/baselines/reference/extendsTagEmit.errors.txt create mode 100644 tests/baselines/reference/extendsTagEmit.js create mode 100644 tests/baselines/reference/extendsTagEmit.symbols create mode 100644 tests/baselines/reference/extendsTagEmit.types create mode 100644 tests/cases/conformance/jsdoc/extendsTagEmit.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3fe96ca7ac45c..3ff978c862e07 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27424,6 +27424,11 @@ namespace ts { if (languageVersion < ScriptTarget.ES2015) { checkExternalEmitHelpers(baseTypeNode.parent, ExternalEmitHelpers.Extends); } + // check both @extends and extends if both are specified. + const extendsNode = getClassExtendsHeritageElement(node); + if (extendsNode && extendsNode !== baseTypeNode) { + checkExpression(extendsNode.expression); + } const baseTypes = getBaseTypes(type); if (baseTypes.length && produceDiagnostics) { @@ -27432,10 +27437,6 @@ namespace ts { const staticBaseType = getApparentType(baseConstructorType); checkBaseTypeAccessibility(staticBaseType, baseTypeNode); checkSourceElement(baseTypeNode.expression); - const extendsNode = getClassExtendsHeritageElement(node); - if (extendsNode && extendsNode !== baseTypeNode) { - checkExpression(extendsNode.expression); - } if (some(baseTypeNode.typeArguments)) { forEach(baseTypeNode.typeArguments, checkSourceElement); for (const constructor of getConstructorsForTypeArguments(staticBaseType, baseTypeNode.typeArguments, baseTypeNode)) { diff --git a/tests/baselines/reference/extendsTagEmit.errors.txt b/tests/baselines/reference/extendsTagEmit.errors.txt new file mode 100644 index 0000000000000..3aef84b933e70 --- /dev/null +++ b/tests/baselines/reference/extendsTagEmit.errors.txt @@ -0,0 +1,17 @@ +tests/cases/conformance/jsdoc/main.js(2,15): error TS2304: Cannot find name 'Mismatch'. +tests/cases/conformance/jsdoc/main.js(2,15): error TS8023: JSDoc '@extends Mismatch' does not match the 'extends B' clause. + + +==== tests/cases/conformance/jsdoc/super.js (0 errors) ==== + export class B { } + +==== tests/cases/conformance/jsdoc/main.js (2 errors) ==== + import { B } from './super' + /** @extends {Mismatch} */ + ~~~~~~~~ +!!! error TS2304: Cannot find name 'Mismatch'. + ~~~~~~~~ +!!! error TS8023: JSDoc '@extends Mismatch' does not match the 'extends B' clause. + class C extends B { } + + \ No newline at end of file diff --git a/tests/baselines/reference/extendsTagEmit.js b/tests/baselines/reference/extendsTagEmit.js new file mode 100644 index 0000000000000..e359eb0c83ac9 --- /dev/null +++ b/tests/baselines/reference/extendsTagEmit.js @@ -0,0 +1,20 @@ +//// [tests/cases/conformance/jsdoc/extendsTagEmit.ts] //// + +//// [super.js] +export class B { } + +//// [main.js] +import { B } from './super' +/** @extends {Mismatch} */ +class C extends B { } + + + +//// [super.js] +export class B { +} +//// [main.js] +import { B } from './super'; +/** @extends {Mismatch} */ +class C extends B { +} diff --git a/tests/baselines/reference/extendsTagEmit.symbols b/tests/baselines/reference/extendsTagEmit.symbols new file mode 100644 index 0000000000000..ff01e0059a4dd --- /dev/null +++ b/tests/baselines/reference/extendsTagEmit.symbols @@ -0,0 +1,14 @@ +=== tests/cases/conformance/jsdoc/super.js === +export class B { } +>B : Symbol(B, Decl(super.js, 0, 0)) + +=== tests/cases/conformance/jsdoc/main.js === +import { B } from './super' +>B : Symbol(B, Decl(main.js, 0, 8)) + +/** @extends {Mismatch} */ +class C extends B { } +>C : Symbol(C, Decl(main.js, 0, 27)) +>B : Symbol(B, Decl(main.js, 0, 8)) + + diff --git a/tests/baselines/reference/extendsTagEmit.types b/tests/baselines/reference/extendsTagEmit.types new file mode 100644 index 0000000000000..3268796e8ac95 --- /dev/null +++ b/tests/baselines/reference/extendsTagEmit.types @@ -0,0 +1,14 @@ +=== tests/cases/conformance/jsdoc/super.js === +export class B { } +>B : B + +=== tests/cases/conformance/jsdoc/main.js === +import { B } from './super' +>B : typeof B + +/** @extends {Mismatch} */ +class C extends B { } +>C : C +>B : typeof B + + diff --git a/tests/cases/conformance/jsdoc/extendsTagEmit.ts b/tests/cases/conformance/jsdoc/extendsTagEmit.ts new file mode 100644 index 0000000000000..a8b3c19be5b17 --- /dev/null +++ b/tests/cases/conformance/jsdoc/extendsTagEmit.ts @@ -0,0 +1,12 @@ +// @allowJs: true +// @checkJs: true +// @target: esnext +// @outDir: out +// @Filename: super.js +export class B { } + +// @Filename: main.js +import { B } from './super' +/** @extends {Mismatch} */ +class C extends B { } +