From 3f6744c6e4d711b5bb20bcc16e3a3c1c2ceb2bf6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 2 Jun 2026 02:14:05 +0000 Subject: [PATCH 1/2] Initial plan From 0f667928c00af093e8c859cbcddc740cd6825367 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 2 Jun 2026 02:27:22 +0000 Subject: [PATCH 2/2] fix: prevent compiler crash when decorator fn argument has invalid reference When an extern fn is called with an invalid reference as an argument, return errorType instead of the declared return type constraint. This prevents the decorator from being called with an incorrect type and avoids internal compiler crashes. Also skip assignability checks in checkFunctionCallArguments when the resolved argument is errorType to avoid spurious secondary diagnostics. Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com> --- ...or-crash-invalid-fn-arg-2026-6-2-2-25-0.md | 7 ++++ packages/compiler/src/core/checker.ts | 8 ++++- .../compiler/test/checker/decorators.test.ts | 32 +++++++++++++++++++ .../compiler/test/checker/functions.test.ts | 10 +++--- 4 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 .chronus/changes/fix-decorator-crash-invalid-fn-arg-2026-6-2-2-25-0.md diff --git a/.chronus/changes/fix-decorator-crash-invalid-fn-arg-2026-6-2-2-25-0.md b/.chronus/changes/fix-decorator-crash-invalid-fn-arg-2026-6-2-2-25-0.md new file mode 100644 index 00000000000..04d814cf362 --- /dev/null +++ b/.chronus/changes/fix-decorator-crash-invalid-fn-arg-2026-6-2-2-25-0.md @@ -0,0 +1,7 @@ +--- +changeKind: fix +packages: + - "@typespec/compiler" +--- + +Fixed compiler crash when a decorator is called with a function argument that contains an invalid reference. The decorator is now correctly skipped and an error diagnostic is reported instead of passing the raw return type to the decorator implementation. diff --git a/packages/compiler/src/core/checker.ts b/packages/compiler/src/core/checker.ts index 58fbf7579a4..2c678028712 100644 --- a/packages/compiler/src/core/checker.ts +++ b/packages/compiler/src/core/checker.ts @@ -5822,6 +5822,12 @@ export function createChecker(program: Program, resolver: NameResolver): Checker const fnCtx = createFunctionContext(program, node); if (!canCall) { + // If arguments were not satisfied and we are not in a template declaration, + // return errorType so that the invalid result propagates as an error and + // prevents downstream usage (e.g., decorator application with wrong argument types). + if (!satisfied && !ctx.hasFlags(CheckFlags.InTemplateDeclaration)) { + return errorType; + } return getDefaultFunctionResult(target.returnType); } @@ -5995,7 +6001,7 @@ export function createChecker(program: Program, resolver: NameResolver): Checker constraint: param.type, }); - if (!checkedArg) { + if (!checkedArg || (isType(checkedArg) && isErrorType(checkedArg))) { satisfied = false; continue; } diff --git a/packages/compiler/test/checker/decorators.test.ts b/packages/compiler/test/checker/decorators.test.ts index cd46988fc77..cdd2d20966a 100644 --- a/packages/compiler/test/checker/decorators.test.ts +++ b/packages/compiler/test/checker/decorators.test.ts @@ -325,6 +325,38 @@ describe("compiler: checker: decorators", () => { expectDecoratorNotCalled(); }); + it("does not call decorator when augment decorator fn argument has an invalid reference", async () => { + const FnTester = Tester.files({ + "test.js": mockFile.js({ + $flags: {}, + $testDec: (...args: any[]) => (calledArgs = args), + $functions: { + "": { + myFn: (_ctx: any, target: any) => target, + }, + }, + }), + }) + .import("./test.js") + .using("TypeSpec.Reflection"); + + const diagnostics = await FnTester.diagnose(` + #suppress "experimental-feature" "testing" + extern fn myFn(target: Reflection.Operation): Reflection.Operation; + extern dec testDec(target: unknown, override: Reflection.Operation); + + op test(): void; + + @@testDec(test, myFn(invalidref)); + `); + + expectDiagnostics(diagnostics, { + code: "invalid-ref", + message: /Unknown identifier invalidref/, + }); + expectDecoratorNotCalled(); + }); + // Regresssion test for https://github.com/microsoft/typespec/issues/3211 it("augmenting a template model property before a decorator declaration resolve the declaration correctly", async () => { await UsageTester.compile(` diff --git a/packages/compiler/test/checker/functions.test.ts b/packages/compiler/test/checker/functions.test.ts index fee33f9343b..04a9fc312b8 100644 --- a/packages/compiler/test/checker/functions.test.ts +++ b/packages/compiler/test/checker/functions.test.ts @@ -379,9 +379,9 @@ describe("usage", () => { expectNotCalled(); - // In this case, we did not call the function, so we expect the constraint. + // In this case, we did not call the function due to argument errors, so we expect errorType. strictEqual(t.kind, "Intrinsic"); - strictEqual(t.name, "unknown"); + strictEqual(t.name, "ErrorType"); }); it("errors if argument type mismatch (value)", async () => { @@ -780,9 +780,9 @@ describe("specific type constraints", () => { ]); strictEqual(receivedType, undefined); - // Since we didn't call the function, we get back the constraint type, which is Reflection.Model. - strictEqual(t.kind, "Model"); - strictEqual(t.name, "Model"); + // Since we didn't call the function due to argument errors, we get back errorType. + strictEqual(t.kind, "Intrinsic"); + strictEqual(t.name, "ErrorType"); }); });