diff --git a/.chronus/changes/fix-mapper-model-expression-2024-7-12-17-20-15.md b/.chronus/changes/fix-mapper-model-expression-2024-7-12-17-20-15.md new file mode 100644 index 000000000..2bf58aa59 --- /dev/null +++ b/.chronus/changes/fix-mapper-model-expression-2024-7-12-17-20-15.md @@ -0,0 +1,8 @@ +--- +# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking +changeKind: fix +packages: + - "@typespec/compiler" +--- + +Fix: Model and union expression in template were not considered as template instances diff --git a/packages/compiler/src/core/checker.ts b/packages/compiler/src/core/checker.ts index c7ccc5927..f7b9668ee 100644 --- a/packages/compiler/src/core/checker.ts +++ b/packages/compiler/src/core/checker.ts @@ -2007,6 +2007,8 @@ export function createChecker(program: Program): Checker { } } + linkMapper(unionType, mapper); + return unionType; } @@ -4010,6 +4012,7 @@ export function createChecker(program: Program): Checker { derivedModels: [], sourceModels: [], }); + linkMapper(type, mapper); checkModelProperties(node, properties, type, mapper); return finishType(type); } diff --git a/packages/compiler/test/type-utils.test.ts b/packages/compiler/test/type-utils.test.ts index 3ec9b121b..e4ba79b8e 100644 --- a/packages/compiler/test/type-utils.test.ts +++ b/packages/compiler/test/type-utils.test.ts @@ -45,6 +45,36 @@ describe("compiler: type-utils", () => { ok(isTemplateInstance(Foo), "Should BE a template instance"); ok(!isTemplateDeclaration(Foo), "Should NOT be a template declaration"); }); + + it("check model expression inside a template instance is also a template instance", async () => { + const { Bar } = (await runner.compile(` + model Foo {a: { b: T }}; + + @test model Bar { + foo: Foo + } + `)) as { Bar: Model }; + const Foo = Bar.properties.get("foo")!.type as Model; + const expr = Foo.properties.get("a")!.type; + + ok(isTemplateInstance(expr), "Should BE a template instance"); + ok(!isTemplateDeclaration(expr), "Should NOT be a template declaration"); + }); + + it("check union expression inside a template instance is also a template instance", async () => { + const { Bar } = (await runner.compile(` + model Foo {a: int32 | T}; + + @test model Bar { + foo: Foo + } + `)) as { Bar: Model }; + const Foo = Bar.properties.get("foo")!.type as Model; + const expr = Foo.properties.get("a")!.type; + + ok(isTemplateInstance(expr), "Should BE a template instance"); + ok(!isTemplateDeclaration(expr), "Should NOT be a template declaration"); + }); }); describe("definition utils", () => { diff --git a/packages/versioning/test/versioned-dependencies.test.ts b/packages/versioning/test/versioned-dependencies.test.ts index 6fe362308..f0c907cf5 100644 --- a/packages/versioning/test/versioned-dependencies.test.ts +++ b/packages/versioning/test/versioned-dependencies.test.ts @@ -208,6 +208,48 @@ describe("versioning: reference versioned library", () => { "The provided version 'v2' from 'TestServiceVersions' is not declared as a version enum. Use '@versioned(TestServiceVersions)' on the containing namespace.", }); }); + + it("doesn't emit diagnostic when library template with model expression instantiated with user model", async () => { + const diagnostics = await runner.diagnose(` + namespace Library { + model Template { + a: { + b: T; + }; + } + } + + @versioned(Versions) + namespace Api { + enum Versions { v1 } + + model Model {} + + model Issue is Library.Template; + } + `); + expectDiagnosticEmpty(diagnostics); + }); + + it("doesn't emit diagnostic when library template with union expression instantiated with user model", async () => { + const diagnostics = await runner.diagnose(` + namespace Library { + model Template { + a: string | T; + } + } + + @versioned(Versions) + namespace Api { + enum Versions { v1 } + + model Model {} + + model Issue is Library.Template; + } + `); + expectDiagnosticEmpty(diagnostics); + }); }); describe("when using versioned library without @useDependency", () => {