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.