From d9d8756f3d10f60da8705ecd450012934a42499e Mon Sep 17 00:00:00 2001
From: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com>
Date: Fri, 6 Nov 2026 13:51:19 -0800
Subject: [PATCH] =?UTF-8?q?Revert=20"[http-client-csharp]=20Rename=20input?=
=?UTF-8?q?-only=20Content=20model=20suffix=20to=20Reques=E2=80=A6"?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This reverts commit c0876527c7b21e117f35681b29fd61389ec52e49.
---
.../src/Providers/ClientProvider.cs | 12 -
.../src/ScmTypeFactory.cs | 3 -
.../ScmModelProvider/ScmModelProviderTests.cs | 79 ----
...xUsageIsReflectedInGeneratedCode(Input).cs | 26 --
...ReflectedInGeneratedCode(Input, Output).cs | 26 --
...UsageIsReflectedInGeneratedCode(Output).cs | 26 --
.../CustomizedClients.cs | 17 -
.../src/Providers/ModelProvider.cs | 54 +--
.../src/Providers/TypeProvider.cs | 13 +-
.../src/SourceInput/SourceInputModel.cs | 3 -
.../src/TypeFactory.cs | 81 ----
.../ModelProviders/ModelProviderTests.cs | 346 +-----------------
.../ExistingModel.cs | 10 -
.../CustomizedModel.cs | 9 -
.../CustomizedModel.cs | 9 -
.../CustomizedModel.cs | 12 -
.../CustomizedModel.cs | 12 -
.../CustomizedModels.cs | 15 -
.../ExistingModels.cs | 12 -
.../generator/docs/naming-conventions.md | 28 +-
20 files changed, 21 insertions(+), 772 deletions(-)
delete mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/TestData/ScmModelProviderTests/ContentSuffixUsageIsReflectedInGeneratedCode(Input).cs
delete mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/TestData/ScmModelProviderTests/ContentSuffixUsageIsReflectedInGeneratedCode(Input, Output).cs
delete mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/TestData/ScmModelProviderTests/ContentSuffixUsageIsReflectedInGeneratedCode(Output).cs
delete mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/TestData/ScmModelProviderTests/ModelSuffixDoesNotCollideWithCustomizedClient/CustomizedClients.cs
delete mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ModelSuffixCollisionPreservesContractName/ExistingModel.cs
delete mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ModelSuffixCollisionPreservesCustomName/CustomizedModel.cs
delete mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ModelSuffixCollisionPreservesMappedSuffixName/CustomizedModel.cs
delete mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ModelSuffixFallbackAvoidsMappedInputName/CustomizedModel.cs
delete mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ModelSuffixMappingBelongsToOriginalInput/CustomizedModel.cs
rename packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/{TestBuildName_ModelSuffixPreservesCustomName => TestBuildName_ResponseSuffixPreservesCustomName}/CustomizedModels.cs (55%)
rename packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/{TestBuildName_ModelSuffixPreservesExistingName => TestBuildName_ResponseSuffixPreservesExistingName}/ExistingModels.cs (54%)
diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientProvider.cs
index e2d81811311..0b30994ff42 100644
--- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientProvider.cs
+++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientProvider.cs
@@ -450,18 +450,6 @@ private string BuildNamespaceCore()
return ns;
}
- internal static (string Name, string Namespace, string OriginalName) GetNameForCollisionCheck(InputClient inputClient)
- {
- // Constructing a client here can resolve parameter models and reenter the model-name index.
- var typeNamespace = string.IsNullOrEmpty(inputClient.Namespace)
- ? ScmCodeModelGenerator.Instance.TypeFactory.PrimaryNamespace
- : ScmCodeModelGenerator.Instance.TypeFactory.GetCleanNameSpace(inputClient.Namespace);
- var name = inputClient.IsExactName ? inputClient.Name : NormalizeTypeNameForNewContract(
- inputClient.Name.ToIdentifierName(), typeNamespace, null, static name => name.NormalizeCSharpAcronyms());
- var customType = ScmCodeModelGenerator.Instance.SourceInputModel.FindForTypeInCurrentCompilation(typeNamespace, name);
- return (customType?.Name ?? name, customType?.Type.Namespace ?? typeNamespace, name);
- }
-
///
/// Checks if two namespaces share the same last segment
///
diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmTypeFactory.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmTypeFactory.cs
index 728abfa263f..d3d4c16f722 100644
--- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmTypeFactory.cs
+++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmTypeFactory.cs
@@ -25,9 +25,6 @@ public class ScmTypeFactory : TypeFactory
private Dictionary? _clientCache;
private Dictionary ClientCache => _clientCache ??= [];
- protected override IEnumerable<(string Name, string Namespace, string OriginalName)> GetReservedModelTypeNames()
- => ScmCodeModelGenerator.Instance.InputLibrary.InputNamespace.Clients.Select(ClientProvider.GetNameForCollisionCheck);
-
public virtual CSharpType MatchConditionsType => typeof(PipelineMessageClassifier);
public virtual IClientResponseApi ClientResponseApi => ClientResultProvider.Instance;
diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/ScmModelProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/ScmModelProviderTests.cs
index 2fca68a742d..4d12b0f1a00 100644
--- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/ScmModelProviderTests.cs
+++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/ScmModelProviderTests.cs
@@ -25,85 +25,6 @@ namespace Microsoft.TypeSpec.Generator.ClientModel.Tests.Providers.ScmModelProvi
{
public class ScmModelProviderTests
{
- [TestCase(InputModelTypeUsage.Input, "WidgetRequest")]
- [TestCase(InputModelTypeUsage.Output, "WidgetContent")]
- [TestCase(InputModelTypeUsage.Input | InputModelTypeUsage.Output, "WidgetContent")]
- public void ContentSuffixUsageIsReflectedInGeneratedCode(InputModelTypeUsage usage, string expectedName)
- {
- var input = InputFactory.Model("WidgetContent",
- usage: usage | InputModelTypeUsage.Json,
- properties: [InputFactory.Property("serviceContent", InputPrimitiveType.String)]);
- MockHelpers.LoadMockGenerator(inputModels: () => [input]);
- var factory = ScmCodeModelGenerator.Instance.TypeFactory;
- var model = factory.CreateModel(input)!;
- var code = new TypeProviderWriter(model).Write().Content;
-
- Assert.AreEqual(Helpers.GetExpectedFromFile(usage.ToString()).ReplaceLineEndings("\n"), code);
- Assert.AreEqual($"{expectedName}.cs", System.IO.Path.GetFileName(model.RelativeFilePath));
- Assert.AreSame(model, factory.CSharpTypeMap[model.Type]);
- }
-
- [TestCase("WidgetRequest", "Sample.Models", true, "WidgetContent", false)]
- [TestCase("WidgetRequest", "Sample.Models", true, "WidgetContent", true)]
- [TestCase("WidgetRequest", "Other.Models", true, "WidgetRequest", false)]
- [TestCase("WidgetRequest", "Other.Models", true, "WidgetRequest", true)]
- [TestCase("IpRequest", "Sample.Models", false, "IPContent", false)]
- [TestCase("IpRequest", "Sample.Models", true, "IPRequest", true)]
- public void ModelSuffixDoesNotCollideWithClient(
- string clientName, string clientNamespace, bool exactName, string expectedName, bool clientFirst)
- {
- var input = InputFactory.Model(clientName.StartsWith("Ip", StringComparison.Ordinal) ? "IpContent" : "WidgetContent",
- usage: InputModelTypeUsage.Input | InputModelTypeUsage.Json);
- var inputClient = InputFactory.Client(clientName, clientNamespace: clientNamespace, isExactName: exactName);
- MockHelpers.LoadMockGenerator(inputModels: () => [input], clients: () => [inputClient]);
- var factory = ScmCodeModelGenerator.Instance.TypeFactory;
- ClientProvider? client = clientFirst ? factory.CreateClient(inputClient) : null;
- var model = factory.CreateModel(input)!;
- client ??= factory.CreateClient(inputClient)!;
-
- Assert.AreEqual(expectedName, model.Name);
- Assert.AreSame(model, factory.CSharpTypeMap[model.Type]);
- Assert.AreSame(client, factory.CSharpTypeMap[client.Type]);
- Assert.AreNotEqual(model.Type, client.Type);
- }
-
- [TestCase("GadgetClient", "WidgetResponse", "WidgetResponse", "WidgetResult")]
- [TestCase("WidgetRequest", "WidgetContent", "WidgetContent", "CustomizedRequest")]
- public async Task ModelSuffixDoesNotCollideWithCustomizedClient(
- string clientName, string modelName, string expectedName, string expectedClientName)
- {
- var input = InputFactory.Model(modelName, usage: InputModelTypeUsage.Input | InputModelTypeUsage.Json);
- var inputClient = InputFactory.Client(clientName, clientNamespace: "Sample.Models", isExactName: true);
- await MockHelpers.LoadMockGeneratorAsync(
- inputModels: () => [input],
- clients: () => [inputClient],
- compilation: async () => await Helpers.GetCompilationFromDirectoryAsync());
- var factory = ScmCodeModelGenerator.Instance.TypeFactory;
- var model = factory.CreateModel(input)!;
- var client = factory.CreateClient(inputClient)!;
-
- Assert.AreEqual(expectedName, model.Name);
- Assert.AreEqual(expectedClientName, client.Name);
- Assert.IsNull(model.CustomCodeView);
- Assert.AreSame(model, factory.CSharpTypeMap[model.Type]);
- Assert.AreSame(client, factory.CSharpTypeMap[client.Type]);
- }
-
- [Test]
- public void ModelSuffixFallbackDoesNotCollideWithClient()
- {
- var input = InputFactory.Model("WidgetContent", usage: InputModelTypeUsage.Input | InputModelTypeUsage.Json);
- MockHelpers.LoadMockGenerator(inputModels: () => [input], clients: () =>
- [
- InputFactory.Client("WidgetRequest", clientNamespace: "Sample.Models", isExactName: true),
- InputFactory.Client("WidgetContent", clientNamespace: "Sample.Models", isExactName: true)
- ]);
-
- var model = ScmCodeModelGenerator.Instance.TypeFactory.CreateModel(input)!;
-
- Assert.AreEqual("WidgetContent1", model.Name);
- }
-
[TestCase(false, "json")]
[TestCase(true, "json")]
[TestCase(false, "xml")]
diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/TestData/ScmModelProviderTests/ContentSuffixUsageIsReflectedInGeneratedCode(Input).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/TestData/ScmModelProviderTests/ContentSuffixUsageIsReflectedInGeneratedCode(Input).cs
deleted file mode 100644
index 3c9175226b3..00000000000
--- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/TestData/ScmModelProviderTests/ContentSuffixUsageIsReflectedInGeneratedCode(Input).cs
+++ /dev/null
@@ -1,26 +0,0 @@
-//
-
-#nullable disable
-
-using System;
-using System.Collections.Generic;
-
-namespace Sample.Models
-{
- public partial class WidgetRequest
- {
- private protected readonly global::System.Collections.Generic.IDictionary _additionalBinaryDataProperties;
-
- public WidgetRequest()
- {
- }
-
- internal WidgetRequest(string serviceContent, global::System.Collections.Generic.IDictionary additionalBinaryDataProperties)
- {
- ServiceContent = serviceContent;
- _additionalBinaryDataProperties = additionalBinaryDataProperties;
- }
-
- public string ServiceContent { get; set; }
- }
-}
diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/TestData/ScmModelProviderTests/ContentSuffixUsageIsReflectedInGeneratedCode(Input, Output).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/TestData/ScmModelProviderTests/ContentSuffixUsageIsReflectedInGeneratedCode(Input, Output).cs
deleted file mode 100644
index 413ac1c7fff..00000000000
--- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/TestData/ScmModelProviderTests/ContentSuffixUsageIsReflectedInGeneratedCode(Input, Output).cs
+++ /dev/null
@@ -1,26 +0,0 @@
-//
-
-#nullable disable
-
-using System;
-using System.Collections.Generic;
-
-namespace Sample.Models
-{
- public partial class WidgetContent
- {
- private protected readonly global::System.Collections.Generic.IDictionary _additionalBinaryDataProperties;
-
- public WidgetContent()
- {
- }
-
- internal WidgetContent(string serviceContent, global::System.Collections.Generic.IDictionary additionalBinaryDataProperties)
- {
- ServiceContent = serviceContent;
- _additionalBinaryDataProperties = additionalBinaryDataProperties;
- }
-
- public string ServiceContent { get; set; }
- }
-}
diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/TestData/ScmModelProviderTests/ContentSuffixUsageIsReflectedInGeneratedCode(Output).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/TestData/ScmModelProviderTests/ContentSuffixUsageIsReflectedInGeneratedCode(Output).cs
deleted file mode 100644
index ff642e13863..00000000000
--- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/TestData/ScmModelProviderTests/ContentSuffixUsageIsReflectedInGeneratedCode(Output).cs
+++ /dev/null
@@ -1,26 +0,0 @@
-//
-
-#nullable disable
-
-using System;
-using System.Collections.Generic;
-
-namespace Sample.Models
-{
- public partial class WidgetContent
- {
- private protected readonly global::System.Collections.Generic.IDictionary _additionalBinaryDataProperties;
-
- internal WidgetContent()
- {
- }
-
- internal WidgetContent(string serviceContent, global::System.Collections.Generic.IDictionary additionalBinaryDataProperties)
- {
- ServiceContent = serviceContent;
- _additionalBinaryDataProperties = additionalBinaryDataProperties;
- }
-
- public string ServiceContent { get; }
- }
-}
diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/TestData/ScmModelProviderTests/ModelSuffixDoesNotCollideWithCustomizedClient/CustomizedClients.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/TestData/ScmModelProviderTests/ModelSuffixDoesNotCollideWithCustomizedClient/CustomizedClients.cs
deleted file mode 100644
index 11979589e42..00000000000
--- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmModelProvider/TestData/ScmModelProviderTests/ModelSuffixDoesNotCollideWithCustomizedClient/CustomizedClients.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-
-using Microsoft.TypeSpec.Generator.Customizations;
-
-namespace Sample.Models
-{
- [CodeGenType("GadgetClient")]
- public partial class WidgetResult
- {
- }
-
- [CodeGenType("WidgetRequest")]
- public partial class CustomizedRequest
- {
- }
-}
diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ModelProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ModelProvider.cs
index 77313b3e8d3..c4f42670f07 100644
--- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ModelProvider.cs
+++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ModelProvider.cs
@@ -21,7 +21,6 @@ namespace Microsoft.TypeSpec.Generator.Providers
public class ModelProvider : TypeProvider
{
private const string AdditionalBinaryDataPropsFieldDescription = "Keeps track of any properties unknown to the library.";
- private const string ContentSuffix = "Content";
private const string ResponseSuffix = "Response";
private readonly InputModelType _inputModel;
internal InputModelType InputModel => _inputModel;
@@ -433,25 +432,6 @@ protected override TypeProvider[] BuildSerializationProviders()
protected override string BuildRelativeFilePath() => Path.Combine("src", "Generated", "Models", $"{Name}.cs");
protected override string BuildName()
- {
- return CodeModelGenerator.Instance.TypeFactory.ModelNames.TryGetValue(_inputModel, out var name)
- ? name
- : BuildNameWithoutCollisionCheck();
- }
-
- internal (string Name, string FinalName, string OriginalName, string Namespace, string FinalNamespace, bool CanRename, bool HasTypeMapping) GetNameForCollisionCheck()
- {
- var name = BuildNameWithoutCollisionCheck();
- var originalName = _inputModel.IsExactName ? _inputModel.Name : _inputModel.Name.ToIdentifierName().NormalizeCSharpAcronyms();
- var typeNamespace = BuildNamespace();
- var customType = base.BuildCustomCodeView(name, typeNamespace);
- var canRename = !_inputModel.IsExactName && name != originalName &&
- name == RenameModelSuffix(originalName);
- return (name, customType?.Name ?? name, originalName, typeNamespace, customType?.Type.Namespace ?? typeNamespace,
- canRename, CodeModelGenerator.Instance.SourceInputModel.HasTypeMapping(typeNamespace, name, DeclaringTypeName));
- }
-
- private string BuildNameWithoutCollisionCheck()
{
if (_inputModel.IsExactName)
{
@@ -464,8 +444,8 @@ private string BuildNameWithoutCollisionCheck()
private protected override string NormalizeTypeName(string name)
{
var normalizedName = base.NormalizeTypeName(name);
- var renamedName = RenameModelSuffix(normalizedName);
- if (renamedName == normalizedName)
+ if (_inputModel.Usage.HasFlag(InputModelTypeUsage.Error) ||
+ !normalizedName.EndsWith(ResponseSuffix, StringComparison.Ordinal))
{
return normalizedName;
}
@@ -478,24 +458,7 @@ private protected override string NormalizeTypeName(string name)
return normalizedName;
}
- return renamedName;
- }
-
- private string RenameModelSuffix(string name)
- {
- if ((_inputModel.Usage & (InputModelTypeUsage.Input | InputModelTypeUsage.Output | InputModelTypeUsage.Error)) == InputModelTypeUsage.Input &&
- name.EndsWith(ContentSuffix, StringComparison.Ordinal))
- {
- return $"{name[..^ContentSuffix.Length]}Request";
- }
-
- if (!_inputModel.Usage.HasFlag(InputModelTypeUsage.Error) &&
- name.EndsWith(ResponseSuffix, StringComparison.Ordinal))
- {
- return $"{name[..^ResponseSuffix.Length]}Result";
- }
-
- return name;
+ return $"{normalizedName[..^ResponseSuffix.Length]}Result";
}
private protected override TypeProvider? BuildCustomCodeView(string? generatedTypeName = null, string? generatedTypeNamespace = null)
@@ -503,7 +466,7 @@ private string RenameModelSuffix(string name)
var typeNamespace = generatedTypeNamespace ?? BuildNamespace();
var typeName = generatedTypeName ?? BuildName();
var customCodeView = base.BuildCustomCodeView(typeName, typeNamespace);
- return customCodeView ?? BuildModelSuffixFallbackView(
+ return customCodeView ?? BuildResponseSuffixFallbackView(
typeName,
typeNamespace,
(name, ns) => base.BuildCustomCodeView(name, ns));
@@ -514,13 +477,13 @@ private string RenameModelSuffix(string name)
var typeNamespace = generatedTypeNamespace ?? CustomCodeView?.Type.Namespace ?? BuildNamespace();
var typeName = generatedTypeName ?? CustomCodeView?.Name ?? BuildName();
var lastContractView = base.BuildLastContractView(typeName, typeNamespace);
- return lastContractView ?? BuildModelSuffixFallbackView(
+ return lastContractView ?? BuildResponseSuffixFallbackView(
typeName,
typeNamespace,
(name, ns) => base.BuildLastContractView(name, ns));
}
- private TypeProvider? BuildModelSuffixFallbackView(
+ private TypeProvider? BuildResponseSuffixFallbackView(
string typeName,
string typeNamespace,
Func buildView)
@@ -532,10 +495,9 @@ private string RenameModelSuffix(string name)
var originalName = _inputModel.Name.ToIdentifierName();
var normalizedOriginalName = originalName.NormalizeCSharpAcronyms();
- var renamedName = RenameModelSuffix(normalizedOriginalName);
- if (renamedName == normalizedOriginalName ||
+ if (!normalizedOriginalName.EndsWith(ResponseSuffix, StringComparison.Ordinal) ||
originalName == typeName ||
- typeName != renamedName)
+ typeName != $"{normalizedOriginalName[..^ResponseSuffix.Length]}Result")
{
return null;
}
diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/TypeProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/TypeProvider.cs
index 43ad82b0fa9..498998b6778 100644
--- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/TypeProvider.cs
+++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/TypeProvider.cs
@@ -849,28 +849,25 @@ protected virtual XmlDocProvider BuildXmlDocs()
protected virtual string? BuildOriginalName() => _inputType is { IsExactName: false } ? _inputType.Name.ToIdentifierName() : null;
protected string NormalizeTypeNameForNewContract(string name)
- => NormalizeTypeNameForNewContract(name, BuildNamespace(), _declaringTypeName.Value, NormalizeTypeName);
-
- protected static string NormalizeTypeNameForNewContract(
- string name, string typeNamespace, string? declaringTypeName, Func normalizeTypeName)
{
+ var typeNamespace = BuildNamespace();
var currentType = CodeModelGenerator.Instance.SourceInputModel.FindForTypeInCurrentCompilation(
typeNamespace,
name,
- declaringTypeName);
+ _declaringTypeName.Value);
if (currentType is not null)
{
return name;
}
- var normalizedName = normalizeTypeName(name);
+ var normalizedName = NormalizeTypeName(name);
if (normalizedName == name)
{
return name;
}
if (CodeModelGenerator.Instance.SourceInputModel.FindForTypeInCurrentCompilation(
- typeNamespace, normalizedName, declaringTypeName) is not null)
+ typeNamespace, normalizedName, _declaringTypeName.Value) is not null)
{
return normalizedName;
}
@@ -878,7 +875,7 @@ protected static string NormalizeTypeNameForNewContract(
var lastContractType = CodeModelGenerator.Instance.SourceInputModel.FindForTypeInLastContract(
typeNamespace,
name,
- declaringTypeName);
+ _declaringTypeName.Value);
return lastContractType is null ? normalizedName : name;
}
diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/SourceInput/SourceInputModel.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/SourceInput/SourceInputModel.cs
index e8e6edd28d2..71f096d5f78 100644
--- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/SourceInput/SourceInputModel.cs
+++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/SourceInput/SourceInputModel.cs
@@ -68,9 +68,6 @@ private IReadOnlyDictionary PopulateNameMap()
return FindTypeInCompilation(Customization, ns, name, includeReferencedAssemblies, declaringTypeName);
}
- internal bool HasTypeMapping(string ns, string name, string? declaringTypeName = null)
- => _nameMap.Value.TryGetValue(name, out var type) && IsContainingTypeMatch(type, ns, declaringTypeName);
-
public TypeProvider? FindForTypeInLastContract(string ns, string name, string? declaringTypeName = null)
{
var type = FindTypeInCompilation(LastContract, ns, name, true, declaringTypeName, includeInternal: false);
diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/TypeFactory.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/TypeFactory.cs
index 2ce3c86928d..640a30fbe0e 100644
--- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/TypeFactory.cs
+++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/TypeFactory.cs
@@ -33,87 +33,6 @@ private ChangeTrackingDictionaryDefinition ChangeTrackingDictionaryProvider
internal IEnumerable CreatedModelProviders
=> InputTypeToModelProvider.Values.OfType();
- private IReadOnlyDictionary? _modelNames;
- internal IReadOnlyDictionary ModelNames => _modelNames ??= BuildModelNames();
-
- protected virtual IEnumerable<(string Name, string Namespace, string OriginalName)> GetReservedModelTypeNames() => [];
-
- private IReadOnlyDictionary BuildModelNames()
- {
- var input = CodeModelGenerator.Instance.InputLibrary.InputNamespace;
- var candidates = new Dictionary();
- var nameCounts = new Dictionary(StringComparer.OrdinalIgnoreCase);
- var reservedNames = GetReservedModelTypeNames().ToArray();
- var originalNames = reservedNames.Select(type => type.OriginalName).ToHashSet(StringComparer.Ordinal);
- var reservedTypes = reservedNames.Select(type => (type.Name, type.Namespace)).ToHashSet();
-
- void ReserveName(string name)
- {
- nameCounts.TryGetValue(name, out var count);
- nameCounts[name] = count + 1;
- }
-
- void AddModel(InputModelType model)
- {
- if (model.External is not null || candidates.ContainsKey(model))
- {
- return;
- }
-
- // Do not construct CSharpTypes while collecting names: their creation calls BuildName.
- var candidate = new ModelProvider(model).GetNameForCollisionCheck();
- candidates.Add(model, candidate);
- originalNames.Add(candidate.OriginalName);
- ReserveName(candidate.FinalName);
- var unknown = model.DiscriminatedSubtypes.Values.FirstOrDefault(m => m.IsUnknownDiscriminatorModel);
- if (unknown is not null)
- {
- AddModel(unknown);
- }
- }
-
- foreach (var model in input.Models)
- {
- AddModel(model);
- }
-
- foreach (var inputEnum in input.Enums)
- {
- if (inputEnum.External is null && !inputEnum.Usage.HasFlag(InputModelTypeUsage.ApiVersionEnum))
- {
- ReserveName(EnumProvider.Create(inputEnum).Name);
- originalNames.Add(inputEnum.IsExactName ? inputEnum.Name : inputEnum.Name.ToIdentifierName().NormalizeCSharpAcronyms());
- }
- }
-
- var result = new Dictionary();
- foreach (var (model, candidate) in candidates)
- {
- // A direct input takes precedence over an implicit suffix match for the same mapping.
- var mappingBelongsToModel = candidate.HasTypeMapping &&
- !originalNames.Contains(candidate.Name);
- if (!candidate.CanRename || mappingBelongsToModel ||
- (nameCounts[candidate.FinalName] == 1 && !reservedTypes.Contains((candidate.FinalName, candidate.FinalNamespace))))
- {
- result.Add(model, candidate.Name);
- continue;
- }
-
- // Models and enums share an output directory, including across namespaces.
- var name = candidate.OriginalName;
- for (var suffix = 1; nameCounts.ContainsKey(name) ||
- reservedTypes.Contains((name, candidate.Namespace)) ||
- (name != candidate.OriginalName && originalNames.Contains(name)); suffix++)
- {
- name = $"{candidate.OriginalName}{suffix}";
- }
- ReserveName(name);
- result.Add(model, name);
- }
-
- return result;
- }
-
public IDictionary CSharpTypeMap { get; } = new Dictionary(CSharpType.IgnoreNullableComparer);
// Maps C# type names to TypeProviders for efficient lookup when resolving types by name
diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/ModelProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/ModelProviderTests.cs
index ec1b729b2b7..20021d9888d 100644
--- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/ModelProviderTests.cs
+++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/ModelProviderTests.cs
@@ -158,322 +158,6 @@ public void TestBuildName_ResponseSuffix(string inputName, bool isExactName, str
Assert.AreEqual("ServiceResponse", model.Properties[0].Name);
}
- [TestCase("WidgetContent", false, "WidgetRequest")]
- [TestCase("Content", false, "Request")]
- [TestCase("widget_content", false, "WidgetRequest")]
- [TestCase("IpContent", false, "IPRequest")]
- [TestCase("WidgetContentContent", false, "WidgetContentRequest")]
- [TestCase("WidgetResponseContent", false, "WidgetResponseRequest")]
- [TestCase("WidgetContentResponse", false, "WidgetContentResult")]
- [TestCase("WidgetContent", true, "WidgetContent")]
- [TestCase("IpContent", true, "IpContent")]
- [TestCase("WidgetRequest", false, "WidgetRequest")]
- [TestCase("ContentWidget", false, "ContentWidget")]
- [TestCase("WidgetContents", false, "WidgetContents")]
- [TestCase("Widgetcontent", false, "Widgetcontent")]
- public void TestBuildName_ContentSuffix(string inputName, bool isExactName, string expectedName)
- {
- var inputModel = InputFactory.Model(
- inputName,
- usage: InputModelTypeUsage.Input | InputModelTypeUsage.Json,
- isExactName: isExactName,
- properties: [InputFactory.Property("serviceContent", InputPrimitiveType.String)]);
- var model = new ModelProvider(inputModel);
-
- Assert.AreEqual(expectedName, model.Name);
- Assert.AreEqual(expectedName, model.Type.Name);
- Assert.AreEqual($"{expectedName}.cs", Path.GetFileName(model.RelativeFilePath));
- Assert.AreEqual("ServiceContent", model.Properties[0].Name);
- }
-
- [TestCase(InputModelTypeUsage.Input, "WidgetRequest")]
- [TestCase(InputModelTypeUsage.Output, "WidgetContent")]
- [TestCase(InputModelTypeUsage.Input | InputModelTypeUsage.Output, "WidgetContent")]
- [TestCase(InputModelTypeUsage.Error | InputModelTypeUsage.Output, "WidgetContent")]
- [TestCase(InputModelTypeUsage.Error | InputModelTypeUsage.Input, "WidgetContent")]
- [TestCase(InputModelTypeUsage.Error | InputModelTypeUsage.Input | InputModelTypeUsage.Output, "WidgetContent")]
- [TestCase(InputModelTypeUsage.None, "WidgetContent")]
- public void TestBuildName_ContentSuffixRequiresInputOnlyUsage(InputModelTypeUsage usage, string expectedName)
- {
- var inputModel = InputFactory.Model("WidgetContent", usage: usage | InputModelTypeUsage.Json);
- var model = new ModelProvider(inputModel);
-
- Assert.AreEqual(expectedName, model.Name);
- }
-
- [TestCase(InputModelTypeUsage.Input, "IPRequest")]
- [TestCase(InputModelTypeUsage.Output, "IPContent")]
- [TestCase(InputModelTypeUsage.Input | InputModelTypeUsage.Output, "IPContent")]
- public void TestBuildName_ContentSuffixUsagePreservesAcronymsAndProperties(InputModelTypeUsage usage, string expectedName)
- {
- var inputModel = InputFactory.Model("IpContent",
- usage: usage | InputModelTypeUsage.Json,
- properties: [InputFactory.Property("serviceContent", InputPrimitiveType.String)]);
- var model = new ModelProvider(inputModel);
-
- Assert.AreEqual(expectedName, model.Type.Name);
- Assert.AreEqual($"{expectedName}.cs", Path.GetFileName(model.RelativeFilePath));
- Assert.AreEqual("ServiceContent", model.Properties[0].Name);
- }
-
- [TestCase("WidgetContent", "WidgetRequest", false, false)]
- [TestCase("WidgetContent", "WidgetRequest", false, true)]
- [TestCase("WidgetContent", "WidgetRequest", true, false)]
- [TestCase("WidgetContent", "WidgetRequest", true, true)]
- [TestCase("WidgetResponse", "WidgetResult", false, false)]
- [TestCase("WidgetResponse", "WidgetResult", false, true)]
- [TestCase("IpContent", "IPRequest", false, false)]
- [TestCase("IpContent", "IpRequest", false, true)]
- [TestCase("IpResponse", "IPResult", false, true)]
- public void TestBuildName_ModelSuffixCollision(
- string inputName, string reservedName, bool exactReservedName, bool reverseOrder)
- {
- var inputModel = InputFactory.Model(inputName, usage: InputModelTypeUsage.Input | InputModelTypeUsage.Json);
- var reservedModel = InputFactory.Model(reservedName, isExactName: exactReservedName);
- var wrapper = InputFactory.Model("Wrapper", properties:
- [
- InputFactory.Property("content", inputModel),
- InputFactory.Property("request", reservedModel)
- ]);
- InputModelType[] models = reverseOrder
- ? [reservedModel, inputModel, wrapper]
- : [inputModel, reservedModel, wrapper];
- MockHelpers.LoadMockGenerator(inputModelTypes: models);
- var factory = CodeModelGenerator.Instance.TypeFactory;
- var providers = CodeModelGenerator.Instance.OutputLibrary.TypeProviders.OfType().ToList();
- var model = factory.CreateModel(inputModel)!;
- var reserved = factory.CreateModel(reservedModel)!;
- var wrapperProvider = factory.CreateModel(wrapper)!;
-
- Assert.AreEqual(inputName.ToIdentifierName().NormalizeCSharpAcronyms(), model.Name);
- Assert.AreEqual(exactReservedName ? reservedName : reservedName.NormalizeCSharpAcronyms(), reserved.Name);
- Assert.AreEqual(providers.Count, providers.Select(p => p.RelativeFilePath).Distinct().Count());
- Assert.AreSame(model, factory.CSharpTypeMap[model.Type]);
- Assert.AreSame(reserved, factory.CSharpTypeMap[reserved.Type]);
- Assert.AreSame(model, factory.TypeProvidersByName[model.Name]);
- Assert.AreSame(reserved, factory.TypeProvidersByName[reserved.Name]);
- Assert.AreSame(model, factory.CSharpTypeMap[wrapperProvider.Properties[0].Type]);
- Assert.AreSame(reserved, factory.CSharpTypeMap[wrapperProvider.Properties[1].Type]);
- }
-
- [TestCase(false)]
- [TestCase(true)]
- public async Task TestBuildName_ModelSuffixCollisionPreservesCustomName(bool reverseOrder)
- {
- var inputModel = InputFactory.Model("WidgetContent", usage: InputModelTypeUsage.Input | InputModelTypeUsage.Json);
- var customizedModel = InputFactory.Model("Gadget");
- InputModelType[] models = reverseOrder ? [customizedModel, inputModel] : [inputModel, customizedModel];
- await MockHelpers.LoadMockGeneratorAsync(
- inputModelTypes: models,
- compilation: async () => await Helpers.GetCompilationFromDirectoryAsync());
- var factory = CodeModelGenerator.Instance.TypeFactory;
- var model = factory.CreateModel(inputModel)!;
- var customized = factory.CreateModel(customizedModel)!;
-
- Assert.AreEqual("WidgetContent", model.Name);
- Assert.AreEqual("WidgetRequest", customized.Name);
- Assert.IsNull(model.CustomCodeView);
- Assert.IsNotNull(customized.CustomCodeView);
- Assert.AreSame(model, factory.CSharpTypeMap[model.Type]);
- Assert.AreSame(customized, factory.CSharpTypeMap[customized.Type]);
- }
-
- [TestCase("WidgetContent", "WidgetRequest", "WidgetContent", false)]
- [TestCase("WidgetContent", "WidgetRequest", "WidgetContent", true)]
- [TestCase("IpContent", "IpRequest", "IPContent", false)]
- [TestCase("IpContent", "IpRequest", "IPContent", true)]
- public async Task TestBuildName_ModelSuffixCollisionPreservesContractName(
- string inputName, string contractName, string expectedName, bool reverseOrder)
- {
- var inputModel = InputFactory.Model(inputName, usage: InputModelTypeUsage.Input | InputModelTypeUsage.Json);
- var shippedModel = InputFactory.Model(contractName);
- InputModelType[] models = reverseOrder ? [shippedModel, inputModel] : [inputModel, shippedModel];
- await MockHelpers.LoadMockGeneratorAsync(
- inputModelTypes: models,
- lastContractCompilation: async () => await Helpers.GetCompilationFromDirectoryAsync());
- var factory = CodeModelGenerator.Instance.TypeFactory;
- var model = factory.CreateModel(inputModel)!;
- var shipped = factory.CreateModel(shippedModel)!;
-
- Assert.AreEqual(expectedName, model.Name);
- Assert.AreEqual(contractName, shipped.Name);
- Assert.IsNotNull(shipped.LastContractView);
- Assert.AreSame(model, factory.CSharpTypeMap[model.Type]);
- Assert.AreSame(shipped, factory.CSharpTypeMap[shipped.Type]);
- }
-
- [Test]
- public void TestBuildName_ModelSuffixDoesNotCollideWithDistinctExactName()
- {
- var inputModel = InputFactory.Model("IpContent", usage: InputModelTypeUsage.Input | InputModelTypeUsage.Json);
- var exactModel = InputFactory.Model("IpRequestModel", isExactName: true);
- MockHelpers.LoadMockGenerator(inputModelTypes: [inputModel, exactModel]);
- var factory = CodeModelGenerator.Instance.TypeFactory;
-
- Assert.AreEqual("IPRequest", factory.CreateModel(inputModel)!.Name);
- Assert.AreEqual("IpRequestModel", factory.CreateModel(exactModel)!.Name);
- }
-
- [TestCase("Sample.Models")]
- [TestCase("Other.Models")]
- public void TestBuildName_ModelSuffixAvoidsCaseInsensitiveFileCollision(string reservedNamespace)
- {
- var inputModel = InputFactory.Model("IpContent", usage: InputModelTypeUsage.Input | InputModelTypeUsage.Json);
- var exactModel = InputFactory.Model("IpRequest", @namespace: reservedNamespace, isExactName: true);
- MockHelpers.LoadMockGenerator(inputModelTypes: [inputModel, exactModel]);
- var factory = CodeModelGenerator.Instance.TypeFactory;
- var model = factory.CreateModel(inputModel)!;
- var exact = factory.CreateModel(exactModel)!;
-
- Assert.AreEqual("IPContent", model.Name);
- Assert.AreEqual("IpRequest", exact.Name);
- Assert.IsFalse(string.Equals(model.RelativeFilePath, exact.RelativeFilePath, StringComparison.OrdinalIgnoreCase));
- }
-
- [TestCase("WidgetContent", "WidgetRequest", false)]
- [TestCase("WidgetContent", "WidgetRequest", true)]
- [TestCase("WidgetResponse", "WidgetResult", false)]
- [TestCase("IpContent", "IpRequest", true)]
- public void TestBuildName_ModelSuffixReservesEnumNames(string modelName, string enumName, bool extensible)
- {
- var inputModel = InputFactory.Model(modelName, usage: InputModelTypeUsage.Input | InputModelTypeUsage.Json);
- var inputEnum = InputFactory.StringEnum(enumName, [("One", "one")], isExtensible: extensible);
- MockHelpers.LoadMockGenerator(inputModelTypes: [inputModel], inputEnumTypes: [inputEnum]);
- var factory = CodeModelGenerator.Instance.TypeFactory;
- var model = factory.CreateModel(inputModel)!;
- var enumProvider = factory.CreateEnum(inputEnum)!;
-
- Assert.AreEqual(modelName.NormalizeCSharpAcronyms(), model.Name);
- Assert.AreEqual(enumName.NormalizeCSharpAcronyms(), enumProvider.Name);
- Assert.IsFalse(string.Equals(model.RelativeFilePath, enumProvider.RelativeFilePath, StringComparison.OrdinalIgnoreCase));
- Assert.AreSame(model, factory.CSharpTypeMap[model.Type]);
- Assert.AreSame(enumProvider, factory.CSharpTypeMap[enumProvider.Type]);
- }
-
- [TestCase(false)]
- [TestCase(true)]
- public void TestBuildName_ModelSuffixReservesUnknownDiscriminatorName(bool reverseOrder)
- {
- var inputModel = InputFactory.Model("UnknownPetContent", usage: InputModelTypeUsage.Input | InputModelTypeUsage.Json);
- var discriminator = InputFactory.Property("kind", InputPrimitiveType.String, isDiscriminator: true);
- var baseModel = InputFactory.Model("PetRequest", properties: [discriminator]);
- InputModelType[] models = reverseOrder ? [baseModel, inputModel] : [inputModel, baseModel];
- MockHelpers.LoadMockGenerator(inputModelTypes: models);
- var factory = CodeModelGenerator.Instance.TypeFactory;
- var providers = CodeModelGenerator.Instance.OutputLibrary.TypeProviders.OfType().ToList();
- var model = factory.CreateModel(inputModel)!;
- var unknown = factory.CreateModel(baseModel.DiscriminatedSubtypes.Values.Single(m => m.IsUnknownDiscriminatorModel))!;
-
- Assert.AreEqual("UnknownPetContent", model.Name);
- Assert.AreEqual("UnknownPetRequest", unknown.Name);
- Assert.Contains(unknown, providers);
- Assert.AreEqual(providers.Count, providers.Select(p => p.RelativeFilePath).Distinct(StringComparer.OrdinalIgnoreCase).Count());
- Assert.AreSame(model, factory.CSharpTypeMap[model.Type]);
- Assert.AreSame(unknown, factory.CSharpTypeMap[unknown.Type]);
- }
-
- [TestCase(false)]
- [TestCase(true)]
- public async Task TestBuildName_ModelSuffixCollisionPreservesMappedSuffixName(bool reverseOrder)
- {
- var inputModel = InputFactory.Model("WidgetContent", usage: InputModelTypeUsage.Input | InputModelTypeUsage.Json);
- var customizedModel = InputFactory.Model("GadgetResponse");
- InputModelType[] models = reverseOrder ? [customizedModel, inputModel] : [inputModel, customizedModel];
- await MockHelpers.LoadMockGeneratorAsync(
- inputModelTypes: models,
- compilation: async () => await Helpers.GetCompilationFromDirectoryAsync());
- var factory = CodeModelGenerator.Instance.TypeFactory;
- var model = factory.CreateModel(inputModel)!;
- var customized = factory.CreateModel(customizedModel)!;
-
- Assert.AreEqual("WidgetContent", model.Name);
- Assert.AreEqual("WidgetRequest", customized.Name);
- Assert.IsNull(model.CustomCodeView);
- Assert.IsNotNull(customized.CustomCodeView);
- Assert.AreSame(model, factory.CSharpTypeMap[model.Type]);
- Assert.AreSame(customized, factory.CSharpTypeMap[customized.Type]);
- }
-
- [TestCase(false)]
- [TestCase(true)]
- public async Task TestBuildName_ModelSuffixMappingBelongsToOriginalInput(bool reverseOrder)
- {
- var inputModel = InputFactory.Model("WidgetContent", usage: InputModelTypeUsage.Input | InputModelTypeUsage.Json);
- var mappedModel = InputFactory.Model("WidgetRequest");
- InputModelType[] models = reverseOrder ? [mappedModel, inputModel] : [inputModel, mappedModel];
- await MockHelpers.LoadMockGeneratorAsync(
- inputModelTypes: models,
- compilation: async () => await Helpers.GetCompilationFromDirectoryAsync());
- var factory = CodeModelGenerator.Instance.TypeFactory;
- var model = factory.CreateModel(inputModel)!;
- var mapped = factory.CreateModel(mappedModel)!;
-
- Assert.AreEqual("WidgetContent", model.Name);
- Assert.AreEqual("CustomizedRequest", mapped.Name);
- Assert.IsNull(model.CustomCodeView);
- Assert.IsNotNull(mapped.CustomCodeView);
- Assert.AreSame(model, factory.CSharpTypeMap[model.Type]);
- Assert.AreSame(mapped, factory.CSharpTypeMap[mapped.Type]);
- Assert.AreNotEqual(model.RelativeFilePath, mapped.RelativeFilePath);
- }
-
- [Test]
- public void TestBuildName_ModelSuffixNamesAreCachedForLibrary()
- {
- var models = Enumerable.Range(0, 1000).Select(i => InputFactory.Model($"Widget{i}Content", usage: InputModelTypeUsage.Input | InputModelTypeUsage.Json)).ToArray();
- MockHelpers.LoadMockGenerator(inputModelTypes: models);
- var factory = CodeModelGenerator.Instance.TypeFactory;
- var names = factory.ModelNames;
-
- Assert.AreEqual(models.Length, names.Count);
- Assert.IsEmpty(factory.CreatedModelProviders);
- foreach (var model in models)
- {
- var provider = factory.CreateModel(model)!;
- Assert.AreEqual($"{model.Name[..^"Content".Length]}Request", provider.Name);
- Assert.AreEqual(names[model], provider.Name);
- Assert.AreSame(names, factory.ModelNames);
- }
- }
-
- [Test]
- public void TestBuildName_ModelSuffixFallbackAvoidsReservedOriginalName()
- {
- var inputModel = InputFactory.Model("WidgetContent", usage: InputModelTypeUsage.Input | InputModelTypeUsage.Json);
- var reserved = InputFactory.Model("WidgetRequest", isExactName: true);
- var inputEnum = InputFactory.StringEnum("WidgetContent", [("One", "one")]);
- MockHelpers.LoadMockGenerator(inputModelTypes: [inputModel, reserved], inputEnumTypes: [inputEnum]);
- var model = CodeModelGenerator.Instance.TypeFactory.CreateModel(inputModel)!;
-
- Assert.AreEqual("WidgetContent1", model.Name);
- }
-
- [TestCase(false)]
- [TestCase(true)]
- public async Task TestBuildName_ModelSuffixFallbackAvoidsMappedInputName(bool reverseOrder)
- {
- var inputModel = InputFactory.Model("WidgetContent", usage: InputModelTypeUsage.Input | InputModelTypeUsage.Json);
- var reserved = InputFactory.Model("WidgetRequest", isExactName: true);
- var mappedModel = InputFactory.Model("WidgetContent1");
- var inputEnum = InputFactory.StringEnum("WidgetContent", [("One", "one")]);
- InputModelType[] models = reverseOrder ? [mappedModel, reserved, inputModel] : [inputModel, reserved, mappedModel];
- await MockHelpers.LoadMockGeneratorAsync(
- inputModelTypes: models,
- inputEnumTypes: [inputEnum],
- compilation: async () => await Helpers.GetCompilationFromDirectoryAsync());
- var factory = CodeModelGenerator.Instance.TypeFactory;
- var model = factory.CreateModel(inputModel)!;
- var mapped = factory.CreateModel(mappedModel)!;
-
- Assert.AreEqual("WidgetContent2", model.Name);
- Assert.AreEqual("CustomizedContent", mapped.Name);
- Assert.IsNull(model.CustomCodeView);
- Assert.IsNotNull(mapped.CustomCodeView);
- Assert.AreNotEqual(model.RelativeFilePath, mapped.RelativeFilePath);
- Assert.AreSame(model, factory.CSharpTypeMap[model.Type]);
- Assert.AreSame(mapped, factory.CSharpTypeMap[mapped.Type]);
- }
-
[TestCase("ErrorResponse", false, "ErrorResponse")]
[TestCase("ServiceErrorResponse", false, "ServiceErrorResponse")]
[TestCase("IpResponse", false, "IPResponse")]
@@ -502,24 +186,11 @@ public void TestBuildName_ErrorResponseSuffix(string inputName, bool isExactName
[TestCase("DbResponse", "DbResponse", true, false)]
[TestCase("DbResponse", "DbResponse", false, true)]
[TestCase("DbResponse", "DbResponse", true, true)]
- [TestCase("WidgetContent", "WidgetContent", false, false)]
- [TestCase("WidgetContent", "WidgetContent", true, false)]
- [TestCase("WidgetContent", "WidgetContent", false, true)]
- [TestCase("WidgetContent", "WidgetContent", true, true)]
- [TestCase("IpContent", "IPContent", false, false)]
- [TestCase("IpContent", "IPContent", true, false)]
- [TestCase("IpContent", "IPContent", false, true)]
- [TestCase("IpContent", "IPContent", true, true)]
- [TestCase("DbContent", "DbContent", false, false)]
- [TestCase("DbContent", "DbContent", true, false)]
- [TestCase("DbContent", "DbContent", false, true)]
- [TestCase("DbContent", "DbContent", true, true)]
- public async Task TestBuildName_ModelSuffixPreservesExistingName(
+ public async Task TestBuildName_ResponseSuffixPreservesExistingName(
string inputName, string expectedName, bool lastContract, bool updateNamespace)
{
var inputModel = InputFactory.Model(
- inputName, @namespace: updateNamespace ? "Sample" : "Sample.Models",
- usage: InputModelTypeUsage.Input | InputModelTypeUsage.Json);
+ inputName, @namespace: updateNamespace ? "Sample" : "Sample.Models");
var compilation = await Helpers.GetCompilationFromDirectoryAsync();
await MockHelpers.LoadMockGeneratorAsync(
inputModelTypes: [inputModel],
@@ -529,7 +200,7 @@ await MockHelpers.LoadMockGeneratorAsync(
if (updateNamespace)
{
- Assert.That(model.Name, Does.EndWith(inputName.EndsWith("Content", StringComparison.Ordinal) ? "Request" : "Result"));
+ Assert.That(model.Name, Does.EndWith("Result"));
model.Update(@namespace: "Sample.Models");
}
@@ -542,17 +213,12 @@ await MockHelpers.LoadMockGeneratorAsync(
[TestCase("IpResponse", "CustomizedIP", false)]
[TestCase("IpResponse", "CustomizedIP", true)]
[TestCase("GadgetResponse", "CustomizedGadget", false)]
- [TestCase("WidgetContent", "CustomizedContent", false)]
- [TestCase("WidgetContent", "CustomizedContent", true)]
- [TestCase("IpContent", "CustomizedIPContent", false)]
- [TestCase("IpContent", "CustomizedIPContent", true)]
- [TestCase("GadgetContent", "CustomizedGadgetContent", false)]
- public async Task TestBuildName_ModelSuffixPreservesCustomName(
+ public async Task TestBuildName_ResponseSuffixPreservesCustomName(
string inputName, string expectedName, bool isError)
{
var inputModel = InputFactory.Model(inputName,
- usage: InputModelTypeUsage.Json |
- (isError ? InputModelTypeUsage.Error | InputModelTypeUsage.Output : InputModelTypeUsage.Input));
+ usage: InputModelTypeUsage.Output | InputModelTypeUsage.Json |
+ (isError ? InputModelTypeUsage.Error : InputModelTypeUsage.None));
await MockHelpers.LoadMockGeneratorAsync(
inputModelTypes: [inputModel],
compilation: async () => await Helpers.GetCompilationFromDirectoryAsync());
diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ModelSuffixCollisionPreservesContractName/ExistingModel.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ModelSuffixCollisionPreservesContractName/ExistingModel.cs
deleted file mode 100644
index 458ea354438..00000000000
--- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ModelSuffixCollisionPreservesContractName/ExistingModel.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace Sample.Models
-{
- public partial class WidgetRequest
- {
- }
-
- public partial class IpRequest
- {
- }
-}
diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ModelSuffixCollisionPreservesCustomName/CustomizedModel.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ModelSuffixCollisionPreservesCustomName/CustomizedModel.cs
deleted file mode 100644
index ce59b441e1b..00000000000
--- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ModelSuffixCollisionPreservesCustomName/CustomizedModel.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using Microsoft.TypeSpec.Generator.Customizations;
-
-namespace Sample.Models
-{
- [CodeGenType("Gadget")]
- public partial class WidgetRequest
- {
- }
-}
diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ModelSuffixCollisionPreservesMappedSuffixName/CustomizedModel.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ModelSuffixCollisionPreservesMappedSuffixName/CustomizedModel.cs
deleted file mode 100644
index 72b5db636a8..00000000000
--- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ModelSuffixCollisionPreservesMappedSuffixName/CustomizedModel.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using Microsoft.TypeSpec.Generator.Customizations;
-
-namespace Sample.Models
-{
- [CodeGenType("GadgetResult")]
- public partial class WidgetRequest
- {
- }
-}
diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ModelSuffixFallbackAvoidsMappedInputName/CustomizedModel.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ModelSuffixFallbackAvoidsMappedInputName/CustomizedModel.cs
deleted file mode 100644
index da25e76ec21..00000000000
--- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ModelSuffixFallbackAvoidsMappedInputName/CustomizedModel.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-
-using Microsoft.TypeSpec.Generator.Customizations;
-
-namespace Sample.Models
-{
- [CodeGenType("WidgetContent1")]
- public partial class CustomizedContent
- {
- }
-}
diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ModelSuffixMappingBelongsToOriginalInput/CustomizedModel.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ModelSuffixMappingBelongsToOriginalInput/CustomizedModel.cs
deleted file mode 100644
index a131d937376..00000000000
--- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ModelSuffixMappingBelongsToOriginalInput/CustomizedModel.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-
-using Microsoft.TypeSpec.Generator.Customizations;
-
-namespace Sample.Models
-{
- [CodeGenType("WidgetRequest")]
- public partial class CustomizedRequest
- {
- }
-}
diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ModelSuffixPreservesCustomName/CustomizedModels.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ResponseSuffixPreservesCustomName/CustomizedModels.cs
similarity index 55%
rename from packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ModelSuffixPreservesCustomName/CustomizedModels.cs
rename to packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ResponseSuffixPreservesCustomName/CustomizedModels.cs
index 360e1b0ffc7..9f06d5460bb 100644
--- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ModelSuffixPreservesCustomName/CustomizedModels.cs
+++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ResponseSuffixPreservesCustomName/CustomizedModels.cs
@@ -16,19 +16,4 @@ public partial class CustomizedIP
public partial class CustomizedGadget
{
}
-
- [CodeGenType("WidgetContent")]
- public partial class CustomizedContent
- {
- }
-
- [CodeGenType("IPContent")]
- public partial class CustomizedIPContent
- {
- }
-
- [CodeGenType("GadgetRequest")]
- public partial class CustomizedGadgetContent
- {
- }
}
diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ModelSuffixPreservesExistingName/ExistingModels.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ResponseSuffixPreservesExistingName/ExistingModels.cs
similarity index 54%
rename from packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ModelSuffixPreservesExistingName/ExistingModels.cs
rename to packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ResponseSuffixPreservesExistingName/ExistingModels.cs
index baa8ef37525..fc09d2295c4 100644
--- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ModelSuffixPreservesExistingName/ExistingModels.cs
+++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/TestBuildName_ResponseSuffixPreservesExistingName/ExistingModels.cs
@@ -11,16 +11,4 @@ public partial class IPResponse
public partial class DbResponse
{
}
-
- public partial class WidgetContent
- {
- }
-
- public partial class IPContent
- {
- }
-
- public partial class DbContent
- {
- }
}
diff --git a/packages/http-client-csharp/generator/docs/naming-conventions.md b/packages/http-client-csharp/generator/docs/naming-conventions.md
index 2ece080037a..80624203a42 100644
--- a/packages/http-client-csharp/generator/docs/naming-conventions.md
+++ b/packages/http-client-csharp/generator/docs/naming-conventions.md
@@ -122,15 +122,7 @@ and acronym rules can still apply:
The `From`/`To` exclusion checks a prefix, not a word boundary. The `StatusTimestamp` exclusion is
an exact-name exclusion: `lastSyncTimestamp` still becomes `LastSyncOn` as a property.
-## Model names ending in Content or Response
-
-A non-error, input-only model whose generated name ends with the case-sensitive suffix `Content` uses `Request`
-instead. For example, `WidgetContent` becomes `WidgetRequest`, and `IpContent` becomes `IPRequest`.
-Output-only models, models used as both input and output, and error models retain `Content`. This changes
-only the final suffix, not property names or occurrences of `Content` elsewhere in the name.
-
-An exact name, an existing handwritten type, or a previous contract can retain the `Content`
-spelling.
+## Model names ending in Response
A non-error model whose generated name ends with the case-sensitive suffix `Response` uses `Result`
instead. For example, `WidgetResponse` becomes `WidgetResult`. This rule is not limited to models
@@ -140,22 +132,6 @@ The emitter keeps `Response` when the model is an error model, has an exact name
handwritten type or previous contract requires the `Response` spelling. Acronym normalization can
combine with this rule: a new `IpResponse` model becomes `IPResult`.
-Before applying either suffix conversion, the generator reserves the generated model and enum names,
-including synthesized unknown-discriminator models, acronym normalization, exact names, custom names,
-and previous contract spellings. Explicit customizations retain ownership of their names; when a
-direct input name and an implicit suffix conversion match the same customization, the direct input
-owns that customization and the implicit conversion is reverted.
-The comparison ignores case and namespace because model files share an output directory and must
-remain distinct on case-insensitive file systems. If the converted name is already in use, the model
-retains its name before suffix conversion. For example, a library containing both `WidgetContent` and `WidgetRequest`
-generates two distinct types with those names, rather than overwriting `WidgetRequest`. Likewise,
-`WidgetResponse` stays `WidgetResponse` if another model already uses `WidgetResult`. If the name before
-suffix conversion is also occupied, the generator appends `1`, `2`, and subsequent numeric suffixes
-until the filename is available and the name does not claim another input's customization lookup
-name. Resolved client names are also reserved within their namespaces to
-prevent a model's suffix conversion from merging unrelated client and model declarations. A client
-with the same name in another namespace does not block the conversion.
-
## Operation names
After identifier cleanup, operations receive these conversions:
@@ -370,7 +346,7 @@ for `[CodeGenType]`, `[CodeGenMember]`, and partial-method customization.
When a prior published contract is available, the generator preserves compatible existing names
rather than treating the tables above as a migration:
-- Matching type names can retain their previous acronym, `Content`, or `Response` spelling.
+- Matching type names can retain their previous acronym or `Response` spelling.
- Matching public properties can retain their pre-normalization name. Date/time properties can
also match a previous `On` spelling when the type and semantic stem agree and no sibling or
explicit customization owns the name.