From a03227d60e2da470a8a964903016e2b3d0106950 Mon Sep 17 00:00:00 2001 From: Martin Probst Date: Thu, 24 Oct 2019 23:04:42 +0200 Subject: [PATCH] Avoid a crash with `@typedef` in a script file. (#33847) * Avoid a crash with `@typedef` in a script file. Scripts (as opposed to modules) do not have a symbol object. If a script contains a `@typdef` defined on a namespace called `exports`, TypeScript crashes because it attempts to create an exported symbol on the (non-existent) symbol of the SourceFile. This change avoids the crash by explicitly checking if the source file has a symbol object, i.e. whether it is a module. * Add usage of exports.SomeName typedef. * Fix bug at bind site rather than in declare func --- src/compiler/binder.ts | 11 +++++++-- ...checkJsdocTypedefOnlySourceFile.errors.txt | 18 +++++++++++++++ .../checkJsdocTypedefOnlySourceFile.js | 23 +++++++++++++++++++ .../checkJsdocTypedefOnlySourceFile.symbols | 16 +++++++++++++ .../checkJsdocTypedefOnlySourceFile.types | 20 ++++++++++++++++ .../jsdoc/checkJsdocTypedefOnlySourceFile.ts | 15 ++++++++++++ 6 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/checkJsdocTypedefOnlySourceFile.errors.txt create mode 100644 tests/baselines/reference/checkJsdocTypedefOnlySourceFile.js create mode 100644 tests/baselines/reference/checkJsdocTypedefOnlySourceFile.symbols create mode 100644 tests/baselines/reference/checkJsdocTypedefOnlySourceFile.types create mode 100644 tests/cases/conformance/jsdoc/checkJsdocTypedefOnlySourceFile.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 10700204010..5f36d429556 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -2003,7 +2003,12 @@ namespace ts { switch (getAssignmentDeclarationPropertyAccessKind(declName.parent)) { case AssignmentDeclarationKind.ExportsProperty: case AssignmentDeclarationKind.ModuleExports: - container = file; + if (!isExternalOrCommonJsModule(file)) { + container = undefined!; + } + else { + container = file; + } break; case AssignmentDeclarationKind.ThisProperty: container = declName.parent.expression; @@ -2017,7 +2022,9 @@ namespace ts { case AssignmentDeclarationKind.None: return Debug.fail("Shouldn't have detected typedef or enum on non-assignment declaration"); } - declareModuleMember(typeAlias, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes); + if (container) { + declareModuleMember(typeAlias, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes); + } container = oldContainer; } } diff --git a/tests/baselines/reference/checkJsdocTypedefOnlySourceFile.errors.txt b/tests/baselines/reference/checkJsdocTypedefOnlySourceFile.errors.txt new file mode 100644 index 00000000000..52b43068c04 --- /dev/null +++ b/tests/baselines/reference/checkJsdocTypedefOnlySourceFile.errors.txt @@ -0,0 +1,18 @@ +tests/cases/conformance/jsdoc/0.js(10,20): error TS2694: Namespace 'exports' has no exported member 'SomeName'. + + +==== tests/cases/conformance/jsdoc/0.js (1 errors) ==== + // @ts-check + + var exports = {}; + + /** + * @typedef {string} + */ + exports.SomeName; + + /** @type {exports.SomeName} */ + ~~~~~~~~ +!!! error TS2694: Namespace 'exports' has no exported member 'SomeName'. + const myString = 'str'; + \ No newline at end of file diff --git a/tests/baselines/reference/checkJsdocTypedefOnlySourceFile.js b/tests/baselines/reference/checkJsdocTypedefOnlySourceFile.js new file mode 100644 index 00000000000..7f4d7f4bcfd --- /dev/null +++ b/tests/baselines/reference/checkJsdocTypedefOnlySourceFile.js @@ -0,0 +1,23 @@ +//// [0.js] +// @ts-check + +var exports = {}; + +/** + * @typedef {string} + */ +exports.SomeName; + +/** @type {exports.SomeName} */ +const myString = 'str'; + + +//// [0.js] +// @ts-check +var exports = {}; +/** + * @typedef {string} + */ +exports.SomeName; +/** @type {exports.SomeName} */ +var myString = 'str'; diff --git a/tests/baselines/reference/checkJsdocTypedefOnlySourceFile.symbols b/tests/baselines/reference/checkJsdocTypedefOnlySourceFile.symbols new file mode 100644 index 00000000000..0adf9a93444 --- /dev/null +++ b/tests/baselines/reference/checkJsdocTypedefOnlySourceFile.symbols @@ -0,0 +1,16 @@ +=== tests/cases/conformance/jsdoc/0.js === +// @ts-check + +var exports = {}; +>exports : Symbol(exports, Decl(0.js, 2, 3)) + +/** + * @typedef {string} + */ +exports.SomeName; +>exports : Symbol(exports, Decl(0.js, 2, 3)) + +/** @type {exports.SomeName} */ +const myString = 'str'; +>myString : Symbol(myString, Decl(0.js, 10, 5)) + diff --git a/tests/baselines/reference/checkJsdocTypedefOnlySourceFile.types b/tests/baselines/reference/checkJsdocTypedefOnlySourceFile.types new file mode 100644 index 00000000000..fbb4f3ed0ba --- /dev/null +++ b/tests/baselines/reference/checkJsdocTypedefOnlySourceFile.types @@ -0,0 +1,20 @@ +=== tests/cases/conformance/jsdoc/0.js === +// @ts-check + +var exports = {}; +>exports : {} +>{} : {} + +/** + * @typedef {string} + */ +exports.SomeName; +>exports.SomeName : any +>exports : {} +>SomeName : any + +/** @type {exports.SomeName} */ +const myString = 'str'; +>myString : any +>'str' : "str" + diff --git a/tests/cases/conformance/jsdoc/checkJsdocTypedefOnlySourceFile.ts b/tests/cases/conformance/jsdoc/checkJsdocTypedefOnlySourceFile.ts new file mode 100644 index 00000000000..d991d2f6e16 --- /dev/null +++ b/tests/cases/conformance/jsdoc/checkJsdocTypedefOnlySourceFile.ts @@ -0,0 +1,15 @@ +// @allowJS: true +// @suppressOutputPathCheck: true + +// @filename: 0.js +// @ts-check + +var exports = {}; + +/** + * @typedef {string} + */ +exports.SomeName; + +/** @type {exports.SomeName} */ +const myString = 'str';