diff --git a/.chronus/changes/witemple-msft-fix-encodedname-dup-detect-2025-9-7-15-21-36.md b/.chronus/changes/witemple-msft-fix-encodedname-dup-detect-2025-9-7-15-21-36.md new file mode 100644 index 00000000000..88a93e23eb9 --- /dev/null +++ b/.chronus/changes/witemple-msft-fix-encodedname-dup-detect-2025-9-7-15-21-36.md @@ -0,0 +1,7 @@ +--- +changeKind: fix +packages: + - "@typespec/compiler" +--- + +Addressed a bug that could cause duplicate `@encodedName` applications to be detected when none actually exist. \ No newline at end of file diff --git a/packages/compiler/src/lib/encoded-names.ts b/packages/compiler/src/lib/encoded-names.ts index 3ccb45ecdd2..a7c04b792f9 100644 --- a/packages/compiler/src/lib/encoded-names.ts +++ b/packages/compiler/src/lib/encoded-names.ts @@ -106,7 +106,16 @@ export function validateEncodedNamesConflicts(program: Program) { for (const [target, map] of getEncodedNamesStateMap(program).entries()) { const scope = getScope(target); - if (scope === undefined) { + const memberValues = new Set(scope?.members.values()); + if ( + scope === undefined || + // Workaround: exclude members that aren't actually in the scope. This can happen when types of + // properties, enum members, or union variants are cloned for one reason or another, but are not + // actually attached to the parent type. This should probably be fixed in cloning logic (mutator + // implementation) instead, but for now this workaround solves some problems with false positives + // in duplication tracking here. + !memberValues.has(target) + ) { return; } for (const [mimeType, name] of map.entries()) { diff --git a/packages/compiler/src/lib/visibility.ts b/packages/compiler/src/lib/visibility.ts index b746dd3b4e2..591919c77d9 100644 --- a/packages/compiler/src/lib/visibility.ts +++ b/packages/compiler/src/lib/visibility.ts @@ -681,7 +681,7 @@ function createVisibilityFilterMutator( realm.remove(clone); modified = true; } else { - const mutated = mutateSubgraph(program, [mpMutator], prop); + const mutated = cachedMutateSubgraph(program, mpMutator, prop); clone.properties.set(key, mutated.type as ModelProperty); diff --git a/packages/compiler/test/visibility.test.ts b/packages/compiler/test/visibility.test.ts index 82a2d892dfd..5247547e08f 100644 --- a/packages/compiler/test/visibility.test.ts +++ b/packages/compiler/test/visibility.test.ts @@ -1239,6 +1239,20 @@ describe("compiler: visibility core", () => { ok(arrA.properties.has("a")); ok(!arrA.properties.has("invisible")); }); + + it("does not duplicate encodedName metadata", async () => { + const diagnostics = await runner.diagnose(` + model SomeModel { + @visibility(Lifecycle.Read) + @encodedName("application/json", "some_other_name") + someOtherName: string; + } + + alias ReadModel = Read; + `); + + expectDiagnosticEmpty(diagnostics); + }); }); function validateCreateOrUpdateTransform(