From e3d54c3b475ebda6eb98e217ad28bc284dc2011b Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Thu, 4 May 2017 13:09:35 -0700 Subject: [PATCH] Add check for tslib __await helper --- src/compiler/checker.ts | 54 +++++++++++-------- src/compiler/transformers/esnext.ts | 2 +- src/compiler/types.ts | 15 ++++-- src/compiler/utilities.ts | 18 ++++--- ...persNoHelpersForAsyncGenerators.errors.txt | 27 ++++++++++ ...mportHelpersNoHelpersForAsyncGenerators.js | 37 +++++++++++++ ...mportHelpersNoHelpersForAsyncGenerators.ts | 16 ++++++ 7 files changed, 134 insertions(+), 35 deletions(-) create mode 100644 tests/baselines/reference/importHelpersNoHelpersForAsyncGenerators.errors.txt create mode 100644 tests/baselines/reference/importHelpersNoHelpersForAsyncGenerators.js create mode 100644 tests/cases/compiler/importHelpersNoHelpersForAsyncGenerators.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 75c160d6b6d..e133c1041e1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -15962,7 +15962,7 @@ namespace ts { // From within an async function you can return either a non-promise value or a promise. Any // Promise/A+ compatible implementation will always assimilate any foreign promise, so the // return type of the body is awaited type of the body, wrapped in a native Promise type. - return (functionFlags & FunctionFlags.AsyncOrAsyncGenerator) === FunctionFlags.Async + return (functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async ? createPromiseReturnType(func, widenedType) // Async function : widenedType; // Generator function, AsyncGenerator function, or normal function } @@ -16178,7 +16178,7 @@ namespace ts { const functionFlags = getFunctionFlags(node); const returnOrPromisedType = node.type && - ((functionFlags & FunctionFlags.AsyncOrAsyncGenerator) === FunctionFlags.Async ? + ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async ? checkAsyncFunctionReturnType(node) : // Async function getTypeFromTypeNode(node.type)); // AsyncGenerator function, Generator function, or normal function @@ -16208,7 +16208,7 @@ namespace ts { // its return type annotation. const exprType = checkExpression(node.body); if (returnOrPromisedType) { - if ((functionFlags & FunctionFlags.AsyncOrAsyncGenerator) === FunctionFlags.Async) { // Async function + if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async) { // Async function const awaitedType = checkAwaitedType(exprType, node.body, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member); checkTypeAssignableTo(awaitedType, returnOrPromisedType, node.body); } @@ -16948,12 +16948,16 @@ namespace ts { // we are in a yield context. const functionFlags = func && getFunctionFlags(func); if (node.asteriskToken) { - if (functionFlags & FunctionFlags.Async) { - if (languageVersion < ScriptTarget.ES2017) { - checkExternalEmitHelpers(node, ExternalEmitHelpers.AsyncDelegator); - } + // Async generator functions prior to ESNext require the __await, __asyncDelegator, + // and __asyncValues helpers + if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.AsyncGenerator && + languageVersion < ScriptTarget.ESNext) { + checkExternalEmitHelpers(node, ExternalEmitHelpers.AsyncDelegatorIncludes); } - else if (languageVersion < ScriptTarget.ES2015 && compilerOptions.downlevelIteration) { + + // Generator functions prior to ES2015 require the __values helper + if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Generator && + languageVersion < ScriptTarget.ES2015 && compilerOptions.downlevelIteration) { checkExternalEmitHelpers(node, ExternalEmitHelpers.Values); } } @@ -17472,18 +17476,20 @@ namespace ts { } const functionFlags = getFunctionFlags(node); - if ((functionFlags & FunctionFlags.InvalidAsyncOrAsyncGenerator) === FunctionFlags.Async && languageVersion < ScriptTarget.ES2017) { - checkExternalEmitHelpers(node, ExternalEmitHelpers.Awaiter); - if (languageVersion < ScriptTarget.ES2015) { - checkExternalEmitHelpers(node, ExternalEmitHelpers.Generator); + if (!(functionFlags & FunctionFlags.Invalid)) { + // Async generators prior to ESNext require the __await and __asyncGenerator helpers + if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.AsyncGenerator && languageVersion < ScriptTarget.ESNext) { + checkExternalEmitHelpers(node, ExternalEmitHelpers.AsyncGeneratorIncludes); } - } - if ((functionFlags & FunctionFlags.InvalidGenerator) === FunctionFlags.Generator) { - if (functionFlags & FunctionFlags.Async && languageVersion < ScriptTarget.ES2017) { - checkExternalEmitHelpers(node, ExternalEmitHelpers.AsyncGenerator); + // Async functions prior to ES2017 require the __awaiter helper + if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async && languageVersion < ScriptTarget.ES2017) { + checkExternalEmitHelpers(node, ExternalEmitHelpers.Awaiter); } - else if (languageVersion < ScriptTarget.ES2015) { + + // Generator functions, Async functions, and Async Generator functions prior to + // ES2015 require the __generator helper + if ((functionFlags & FunctionFlags.AsyncGenerator) !== FunctionFlags.Normal && languageVersion < ScriptTarget.ES2015) { checkExternalEmitHelpers(node, ExternalEmitHelpers.Generator); } } @@ -17511,7 +17517,7 @@ namespace ts { if (node.type) { const functionFlags = getFunctionFlags(node); - if ((functionFlags & FunctionFlags.InvalidGenerator) === FunctionFlags.Generator) { + if ((functionFlags & (FunctionFlags.Invalid | FunctionFlags.Generator)) === FunctionFlags.Generator) { const returnType = getTypeFromTypeNode(node.type); if (returnType === voidType) { error(node.type, Diagnostics.A_generator_cannot_have_a_void_type_annotation); @@ -17531,7 +17537,7 @@ namespace ts { checkTypeAssignableTo(iterableIteratorInstantiation, returnType, node.type); } } - else if ((functionFlags & FunctionFlags.AsyncOrAsyncGenerator) === FunctionFlags.Async) { + else if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async) { checkAsyncFunctionReturnType(node); } } @@ -19498,11 +19504,14 @@ namespace ts { if (node.kind === SyntaxKind.ForOfStatement) { if ((node).awaitModifier) { - if (languageVersion < ScriptTarget.ES2017) { + const functionFlags = getFunctionFlags(getContainingFunction(node)); + if ((functionFlags & (FunctionFlags.Invalid | FunctionFlags.Async)) === FunctionFlags.Async && languageVersion < ScriptTarget.ESNext) { + // for..await..of in an async function or async generator function prior to ESNext requires the __asyncValues helper checkExternalEmitHelpers(node, ExternalEmitHelpers.ForAwaitOfIncludes); } } - else if (languageVersion < ScriptTarget.ES2015 && compilerOptions.downlevelIteration) { + else if (compilerOptions.downlevelIteration && languageVersion < ScriptTarget.ES2015) { + // for..of prior to ES2015 requires the __values helper when downlevelIteration is enabled checkExternalEmitHelpers(node, ExternalEmitHelpers.ForOfIncludes); } } @@ -19932,7 +19941,7 @@ namespace ts { } function isUnwrappedReturnTypeVoidOrAny(func: FunctionLikeDeclaration, returnType: Type): boolean { - const unwrappedReturnType = (getFunctionFlags(func) & FunctionFlags.AsyncOrAsyncGenerator) === FunctionFlags.Async + const unwrappedReturnType = (getFunctionFlags(func) & FunctionFlags.AsyncGenerator) === FunctionFlags.Async ? getPromisedTypeOfPromise(returnType) // Async function : returnType; // AsyncGenerator function, Generator function, or normal function return unwrappedReturnType && maybeTypeOfKind(unwrappedReturnType, TypeFlags.Void | TypeFlags.Any); @@ -22942,6 +22951,7 @@ namespace ts { case ExternalEmitHelpers.Values: return "__values"; case ExternalEmitHelpers.Read: return "__read"; case ExternalEmitHelpers.Spread: return "__spread"; + case ExternalEmitHelpers.Await: return "__await"; case ExternalEmitHelpers.AsyncGenerator: return "__asyncGenerator"; case ExternalEmitHelpers.AsyncDelegator: return "__asyncDelegator"; case ExternalEmitHelpers.AsyncValues: return "__asyncValues"; diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index 8d88d9b286b..fef1b009ba3 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -884,7 +884,7 @@ namespace ts { function createAwaitHelper(context: TransformationContext, expression: Expression) { context.requestEmitHelper(awaitHelper); - return createCall(createIdentifier("__await"), /*typeArguments*/ undefined, [expression]); + return createCall(getHelperName("__await"), /*typeArguments*/ undefined, [expression]); } const asyncGeneratorHelper: EmitHelper = { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 57cf17f7100..ad3e181610a 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3973,11 +3973,12 @@ namespace ts { Awaiter = 1 << 6, // __awaiter (used by ES2017 async functions transformation) Generator = 1 << 7, // __generator (used by ES2015 generator transformation) Values = 1 << 8, // __values (used by ES2015 for..of and yield* transformations) - Read = 1 << 9, // __read (used by ES2015 iterator destructuring transformation) + Read = 1 << 9, // __read (used by ES2015 iterator destructuring transformation) Spread = 1 << 10, // __spread (used by ES2015 array spread and argument list spread transformations) - AsyncGenerator = 1 << 11, // __asyncGenerator (used by ES2017 async generator transformation) - AsyncDelegator = 1 << 12, // __asyncDelegator (used by ES2017 async generator yield* transformation) - AsyncValues = 1 << 13, // __asyncValues (used by ES2017 for..await..of transformation) + Await = 1 << 11, // __await (used by ES2017 async generator transformation) + AsyncGenerator = 1 << 12, // __asyncGenerator (used by ES2017 async generator transformation) + AsyncDelegator = 1 << 13, // __asyncDelegator (used by ES2017 async generator yield* transformation) + AsyncValues = 1 << 14, // __asyncValues (used by ES2017 for..await..of transformation) // Helpers included by ES2015 for..of ForOfIncludes = Values, @@ -3985,6 +3986,12 @@ namespace ts { // Helpers included by ES2017 for..await..of ForAwaitOfIncludes = AsyncValues, + // Helpers included by ES2017 async generators + AsyncGeneratorIncludes = Await | AsyncGenerator, + + // Helpers included by yield* in ES2017 async generators + AsyncDelegatorIncludes = Await | AsyncDelegator | AsyncValues, + // Helpers included by ES2015 spread SpreadIncludes = Read | Spread, diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 646a7b22e3f..0723a1fc74f 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1925,16 +1925,18 @@ namespace ts { } export const enum FunctionFlags { - Normal = 0, - Generator = 1 << 0, - Async = 1 << 1, - AsyncOrAsyncGenerator = Async | Generator, - Invalid = 1 << 2, - InvalidAsyncOrAsyncGenerator = AsyncOrAsyncGenerator | Invalid, - InvalidGenerator = Generator | Invalid, + Normal = 0, // Function is a normal function + Generator = 1 << 0, // Function is a generator function or async generator function + Async = 1 << 1, // Function is an async function or an async generator function + Invalid = 1 << 2, // Function is a signature or overload and does not have a body. + AsyncGenerator = Async | Generator, // Function is an async generator function } - export function getFunctionFlags(node: FunctionLikeDeclaration) { + export function getFunctionFlags(node: FunctionLikeDeclaration | undefined) { + if (!node) { + return FunctionFlags.Invalid; + } + let flags = FunctionFlags.Normal; switch (node.kind) { case SyntaxKind.FunctionDeclaration: diff --git a/tests/baselines/reference/importHelpersNoHelpersForAsyncGenerators.errors.txt b/tests/baselines/reference/importHelpersNoHelpersForAsyncGenerators.errors.txt new file mode 100644 index 00000000000..633b812c356 --- /dev/null +++ b/tests/baselines/reference/importHelpersNoHelpersForAsyncGenerators.errors.txt @@ -0,0 +1,27 @@ +tests/cases/compiler/main.ts(1,25): error TS2343: This syntax requires an imported helper named '__asyncGenerator', but module 'tslib' has no exported member '__asyncGenerator'. +tests/cases/compiler/main.ts(1,25): error TS2343: This syntax requires an imported helper named '__await', but module 'tslib' has no exported member '__await'. +tests/cases/compiler/main.ts(1,25): error TS2343: This syntax requires an imported helper named '__generator', but module 'tslib' has no exported member '__generator'. +tests/cases/compiler/main.ts(4,5): error TS2343: This syntax requires an imported helper named '__asyncDelegator', but module 'tslib' has no exported member '__asyncDelegator'. +tests/cases/compiler/main.ts(4,5): error TS2343: This syntax requires an imported helper named '__asyncValues', but module 'tslib' has no exported member '__asyncValues'. + + +==== tests/cases/compiler/main.ts (5 errors) ==== + export async function * f() { + ~ +!!! error TS2343: This syntax requires an imported helper named '__asyncGenerator', but module 'tslib' has no exported member '__asyncGenerator'. + ~ +!!! error TS2343: This syntax requires an imported helper named '__await', but module 'tslib' has no exported member '__await'. + ~ +!!! error TS2343: This syntax requires an imported helper named '__generator', but module 'tslib' has no exported member '__generator'. + await 1; + yield 2; + yield* [3]; + ~~~~~~~~~~ +!!! error TS2343: This syntax requires an imported helper named '__asyncDelegator', but module 'tslib' has no exported member '__asyncDelegator'. + ~~~~~~~~~~ +!!! error TS2343: This syntax requires an imported helper named '__asyncValues', but module 'tslib' has no exported member '__asyncValues'. + } + +==== tests/cases/compiler/tslib.d.ts (0 errors) ==== + export {} + \ No newline at end of file diff --git a/tests/baselines/reference/importHelpersNoHelpersForAsyncGenerators.js b/tests/baselines/reference/importHelpersNoHelpersForAsyncGenerators.js new file mode 100644 index 00000000000..699c4faf921 --- /dev/null +++ b/tests/baselines/reference/importHelpersNoHelpersForAsyncGenerators.js @@ -0,0 +1,37 @@ +//// [tests/cases/compiler/importHelpersNoHelpersForAsyncGenerators.ts] //// + +//// [main.ts] +export async function * f() { + await 1; + yield 2; + yield* [3]; +} + +//// [tslib.d.ts] +export {} + + +//// [main.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var tslib_1 = require("tslib"); +function f() { + return tslib_1.__asyncGenerator(this, arguments, function f_1() { + return tslib_1.__generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, tslib_1.__await(1)]; + case 1: + _a.sent(); + return [4 /*yield*/, 2]; + case 2: + _a.sent(); + return [5 /*yield**/, tslib_1.__values(tslib_1.__asyncDelegator(tslib_1.__asyncValues([3])))]; + case 3: return [4 /*yield*/, tslib_1.__await.apply(void 0, [_a.sent()])]; + case 4: + _a.sent(); + return [2 /*return*/]; + } + }); + }); +} +exports.f = f; diff --git a/tests/cases/compiler/importHelpersNoHelpersForAsyncGenerators.ts b/tests/cases/compiler/importHelpersNoHelpersForAsyncGenerators.ts new file mode 100644 index 00000000000..a42b5148b43 --- /dev/null +++ b/tests/cases/compiler/importHelpersNoHelpersForAsyncGenerators.ts @@ -0,0 +1,16 @@ +// @importHelpers: true +// @target: es5 +// @module: commonjs +// @lib: esnext +// @moduleResolution: classic +// @experimentalDecorators: true +// @emitDecoratorMetadata: true +// @filename: main.ts +export async function * f() { + await 1; + yield 2; + yield* [3]; +} + +// @filename: tslib.d.ts +export {}