Skip to content

Commit a92cc3d

Browse files
authored
Validation preventing duplicate version values (#1898)
* Closes #1868. * Code review feedback. * Code review feedback.
1 parent 8925f8c commit a92cc3d

4 files changed

Lines changed: 71 additions & 0 deletions

File tree

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@typespec/versioning",
5+
"comment": "Added validation preventing version enums from having duplicate values.",
6+
"type": "none"
7+
}
8+
],
9+
"packageName": "@typespec/versioning"
10+
}

packages/versioning/src/lib.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ const libDef = {
3939
default: paramMessage`The provided version '${"version"}' from '${"enumName"}' is not declared as a version enum. Use '@versioned(${"enumName"})' on the containing namespace.`,
4040
},
4141
},
42+
"version-duplicate": {
43+
severity: "error",
44+
messages: {
45+
default: paramMessage`Multiple versions from '${"name"}' resolve to the same value. Version enums must resolve to unique values.`,
46+
},
47+
},
4248
"using-versioned-library": {
4349
severity: "error",
4450
messages: {

packages/versioning/src/validate.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ export function $onValidate(program: Program) {
9595
},
9696
namespace: (namespace) => {
9797
const [_, versionMap] = getVersions(program, namespace);
98+
validateVersionEnumValuesUnique(program, namespace);
9899
const serviceProps = getService(program, namespace);
99100
if (serviceProps?.version !== undefined && versionMap !== undefined) {
100101
reportDiagnostic(program, {
@@ -163,6 +164,23 @@ export function $onValidate(program: Program) {
163164
validateVersionedNamespaceUsage(program, namespaceDependencies);
164165
}
165166

167+
/**
168+
* Ensures that the version enum for a @versioned namespace has unique values.
169+
*/
170+
function validateVersionEnumValuesUnique(program: Program, namespace: Namespace) {
171+
const [_, versionMap] = getVersions(program, namespace);
172+
if (versionMap === undefined) return;
173+
const values = new Set(versionMap.getVersions().map((v) => v.value));
174+
if (versionMap.size !== values.size) {
175+
const enumName = versionMap.getVersions()[0].enumMember.enum.name;
176+
reportDiagnostic(program, {
177+
code: "version-duplicate",
178+
format: { name: enumName },
179+
target: namespace,
180+
});
181+
}
182+
}
183+
166184
function validateVersionedNamespaceUsage(
167185
program: Program,
168186
namespaceDependencies: Map<Namespace | undefined, Set<Namespace>>

packages/versioning/test/incompatible-versioning.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,43 @@ describe("versioning: incompatible use of decorators", () => {
4242
severity: "error",
4343
});
4444
});
45+
46+
it("emit diagnostic when version enum has duplicate values", async () => {
47+
const diagnostics = await runner.diagnose(`
48+
@versioned(Versions)
49+
namespace DemoService;
50+
51+
enum Versions {
52+
v1: "v1",
53+
v2: "v2",
54+
latest: "v2",
55+
}
56+
`);
57+
expectDiagnostics(diagnostics, {
58+
code: "@typespec/versioning/version-duplicate",
59+
message:
60+
"Multiple versions from 'Versions' resolve to the same value. Version enums must resolve to unique values.",
61+
severity: "error",
62+
});
63+
});
64+
65+
it("emit diagnostic when version enum has duplicate implicit values", async () => {
66+
const diagnostics = await runner.diagnose(`
67+
@versioned(Versions)
68+
namespace DemoService;
69+
70+
enum Versions {
71+
v1,
72+
v2: "v1",
73+
}
74+
`);
75+
expectDiagnostics(diagnostics, {
76+
code: "@typespec/versioning/version-duplicate",
77+
message:
78+
"Multiple versions from 'Versions' resolve to the same value. Version enums must resolve to unique values.",
79+
severity: "error",
80+
});
81+
});
4582
});
4683

4784
describe("versioning: validate incompatible references", () => {

0 commit comments

Comments
 (0)