diff --git a/.chronus/changes/fix-scalar-constructor-clone-2024-5-12-20-39-42.md b/.chronus/changes/fix-scalar-constructor-clone-2024-5-12-20-39-42.md new file mode 100644 index 00000000000..1a0bbbba55b --- /dev/null +++ b/.chronus/changes/fix-scalar-constructor-clone-2024-5-12-20-39-42.md @@ -0,0 +1,8 @@ +--- +# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking +changeKind: internal +packages: + - "@typespec/compiler" +--- + +Fix: Using a scalar constructor defined in a parent scalar doesn't reference the right scalar diff --git a/packages/compiler/src/core/checker.ts b/packages/compiler/src/core/checker.ts index 17d055307ca..5af258ad4b4 100644 --- a/packages/compiler/src/core/checker.ts +++ b/packages/compiler/src/core/checker.ts @@ -5621,7 +5621,6 @@ export function createChecker(program: Program): Checker { decorators, derivedScalars: [], }); - checkScalarConstructors(type, node, type.constructors, mapper); linkType(links, type, mapper); if (node.extends) { @@ -5631,6 +5630,7 @@ export function createChecker(program: Program): Checker { type.baseScalar.derivedScalars.push(type); } } + checkScalarConstructors(type, node, type.constructors, mapper); decorators.push(...checkDecorators(type, node, mapper)); if (mapper === undefined) { @@ -5695,6 +5695,19 @@ export function createChecker(program: Program): Checker { constructors: Map, mapper: TypeMapper | undefined ) { + if (parentScalar.baseScalar) { + for (const member of parentScalar.baseScalar.constructors.values()) { + const newConstructor: ScalarConstructor = cloneTypeForSymbol( + getMemberSymbol(node.symbol, member.name)!, + { + ...member, + scalar: parentScalar, + } + ); + linkIndirectMember(node, newConstructor, mapper); + constructors.set(member.name, newConstructor); + } + } for (const member of node.members) { const constructor = checkScalarConstructor(member, mapper, parentScalar); if (constructors.has(constructor.name as string)) { diff --git a/packages/compiler/src/core/types.ts b/packages/compiler/src/core/types.ts index 973a8472ed8..44fe2cc30e3 100644 --- a/packages/compiler/src/core/types.ts +++ b/packages/compiler/src/core/types.ts @@ -1127,7 +1127,8 @@ export type MemberContainerNode = | ModelExpressionNode | InterfaceStatementNode | EnumStatementNode - | UnionStatementNode; + | UnionStatementNode + | ScalarStatementNode; export type MemberNode = | ModelPropertyNode @@ -1141,7 +1142,7 @@ export type MemberContainerType = Model | Enum | Interface | Union | Scalar; /** * Type that can be used as members of a container type. */ -export type MemberType = ModelProperty | EnumMember | Operation | UnionVariant; +export type MemberType = ModelProperty | EnumMember | Operation | UnionVariant | ScalarConstructor; export type Comment = LineComment | BlockComment; diff --git a/packages/compiler/test/checker/values/scalar-values.test.ts b/packages/compiler/test/checker/values/scalar-values.test.ts index cb01e8ea201..aece17ec4d4 100644 --- a/packages/compiler/test/checker/values/scalar-values.test.ts +++ b/packages/compiler/test/checker/values/scalar-values.test.ts @@ -49,6 +49,21 @@ describe("instantiate with named constructor", () => { ]); }); + it("instantiate using constructor from parent scalar", async () => { + const value = await compileValue( + `b.fromString("a")`, + ` + scalar a { init fromString(val: string);} + scalar b extends a { } + ` + ); + strictEqual(value.valueKind, "ScalarValue"); + strictEqual(value.type.kind, "Scalar"); + strictEqual(value.type.name, "b"); + strictEqual(value.scalar?.name, "b"); + strictEqual(value.value.name, "fromString"); + }); + it("instantiate from another scalar", async () => { const value = await compileValue( `b.fromA(a.fromString("a"))`,