From f02409cffd49bc92a65a5c3228d55b0999179618 Mon Sep 17 00:00:00 2001 From: Cold Fry Date: Mon, 27 Apr 2026 22:25:22 +0000 Subject: [PATCH 1/2] diagnose unsupported async generators and for-await-of --- src/transformation/utils/diagnostics.ts | 6 ++++ src/transformation/visitors/function.ts | 5 ++++ src/transformation/visitors/loops/for-of.ts | 5 ++++ test/unit/builtins/async-await.spec.ts | 33 +++++++++++++++++++-- 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/transformation/utils/diagnostics.ts b/src/transformation/utils/diagnostics.ts index 5fb1bf15e..4e8c259f8 100644 --- a/src/transformation/utils/diagnostics.ts +++ b/src/transformation/utils/diagnostics.ts @@ -145,6 +145,12 @@ export const awaitMustBeInAsyncFunction = createErrorDiagnosticFactory( "Await can only be used inside async functions." ); +export const unsupportedAsyncGenerator = createErrorDiagnosticFactory( + "Async generator functions are not supported." +); + +export const unsupportedForAwaitOf = createErrorDiagnosticFactory("'for await...of' loops are not supported."); + export const unsupportedBuiltinOptionalCall = createErrorDiagnosticFactory( "Optional calls are not supported for builtin or language extension functions." ); diff --git a/src/transformation/visitors/function.ts b/src/transformation/visitors/function.ts index 12b152353..115a1cd08 100644 --- a/src/transformation/visitors/function.ts +++ b/src/transformation/visitors/function.ts @@ -16,6 +16,7 @@ import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; import { transformInPrecedingStatementScope } from "../utils/preceding-statements"; import { peekScope, performHoisting, Scope, ScopeType } from "../utils/scope"; import { isFunctionType } from "../utils/typescript"; +import { unsupportedAsyncGenerator } from "../utils/diagnostics"; import { isAsyncFunction, wrapInAsyncAwaiter } from "./async-await"; import { transformIdentifier } from "./identifier"; import { transformExpressionBodyToReturnStatement } from "./return"; @@ -224,6 +225,10 @@ export function transformFunctionToExpression( ): [lua.Expression, Scope] { assert(node.body); + if (node.asteriskToken && isAsyncFunction(node)) { + context.diagnostics.push(unsupportedAsyncGenerator(node)); + } + const type = context.checker.getTypeAtLocation(node); let functionContext: lua.Identifier | undefined; diff --git a/src/transformation/visitors/loops/for-of.ts b/src/transformation/visitors/loops/for-of.ts index c2da717c5..5071623db 100644 --- a/src/transformation/visitors/loops/for-of.ts +++ b/src/transformation/visitors/loops/for-of.ts @@ -11,6 +11,7 @@ import { import { isRangeFunction, transformRangeStatement } from "../language-extensions/range"; import { transformForInitializer, transformLoopBody } from "./utils"; import { getIterableExtensionKindForNode, IterableExtensionKind } from "../../utils/language-extensions"; +import { unsupportedForAwaitOf } from "../../utils/diagnostics"; import { assertNever } from "../../../utils"; function transformForOfArrayStatement( @@ -43,6 +44,10 @@ function transformForOfIteratorStatement( } export const transformForOfStatement: FunctionVisitor = (node, context) => { + if (node.awaitModifier) { + context.diagnostics.push(unsupportedForAwaitOf(node.awaitModifier)); + } + const body = lua.createBlock(transformLoopBody(context, node)); if (ts.isCallExpression(node.expression) && isRangeFunction(context, node.expression)) { diff --git a/test/unit/builtins/async-await.spec.ts b/test/unit/builtins/async-await.spec.ts index ad64f031d..2929832eb 100644 --- a/test/unit/builtins/async-await.spec.ts +++ b/test/unit/builtins/async-await.spec.ts @@ -1,7 +1,11 @@ import { ModuleKind, ScriptTarget } from "typescript"; import { LuaTarget } from "../../../src"; -import { unsupportedForTargetButOverrideAvailable } from "../../../src/transformation/utils/diagnostics"; -import { awaitMustBeInAsyncFunction } from "../../../src/transformation/utils/diagnostics"; +import { + awaitMustBeInAsyncFunction, + unsupportedAsyncGenerator, + unsupportedForAwaitOf, + unsupportedForTargetButOverrideAvailable, +} from "../../../src/transformation/utils/diagnostics"; import * as util from "../../util"; const promiseTestLib = ` @@ -1123,3 +1127,28 @@ describe("try/catch in async function", () => { .expectToEqual(["finally", "ok"]); }); }); + +describe("async generators are unsupported", () => { + test.each([ + "async function* gen() { yield 1; }", + "const gen = async function*() { yield 1; };", + "const gen = { async *m() { yield 1; } };", + "class C { async *m() { yield 1; } }", + ])("diagnoses %p", source => { + util.testModule` + ${source} + export {} + `.expectToHaveDiagnostics([unsupportedAsyncGenerator.code]); + }); +}); + +test("for-await-of is unsupported", () => { + util.testModule` + async function run(items: AsyncIterable) { + for await (const x of items) { + void x; + } + } + export {} + `.expectToHaveDiagnostics([unsupportedForAwaitOf.code]); +}); From 134f6e83c23a03e0e3ea3ee8f9dafa91e9de9e56 Mon Sep 17 00:00:00 2001 From: Cold Fry Date: Mon, 27 Apr 2026 22:30:54 +0000 Subject: [PATCH 2/2] prettier --- src/transformation/utils/diagnostics.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/transformation/utils/diagnostics.ts b/src/transformation/utils/diagnostics.ts index 4e8c259f8..730fd9c7f 100644 --- a/src/transformation/utils/diagnostics.ts +++ b/src/transformation/utils/diagnostics.ts @@ -145,9 +145,7 @@ export const awaitMustBeInAsyncFunction = createErrorDiagnosticFactory( "Await can only be used inside async functions." ); -export const unsupportedAsyncGenerator = createErrorDiagnosticFactory( - "Async generator functions are not supported." -); +export const unsupportedAsyncGenerator = createErrorDiagnosticFactory("Async generator functions are not supported."); export const unsupportedForAwaitOf = createErrorDiagnosticFactory("'for await...of' loops are not supported.");