From 7da23ee576aea69cf63b7dab0c14c0433916bb49 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 14 Aug 2026 15:37:27 +0000 Subject: [PATCH 01/11] Initial plan From d0ffddc3b613151acd7c8be6754d96c7b170e25e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 14 Aug 2026 15:47:08 +0000 Subject: [PATCH 02/11] fix(http-client-csharp): preserve model operator parameter names Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- .../MrwSerializationTypeDefinition.cs | 24 +++++++-- .../MrwSerializationTypeDefinitionTests.cs | 49 +++++++++++++++++++ .../MockInputModel.cs | 9 ++++ 3 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/MrwSerializationTypeDefinitionTests/ExplicitClientResultOperatorPreservesLastContractParameterName/MockInputModel.cs diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs index 590d2d4b387..05d86c8fb73 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs @@ -401,17 +401,33 @@ protected override MethodProvider[] BuildMethods() private MethodProvider GetExplicitFromClientResultMethod(bool supportsJson, bool supportsXml) { + MethodProvider method; if (supportsJson && supportsXml) { - return BuildJsonAndXmlExplicitFromClientResult(); + method = BuildJsonAndXmlExplicitFromClientResult(); + } + else if (supportsXml) + { + method = BuildXmlExplicitFromClientResult(); + } + else + { + method = BuildExplicitFromClientResult(); } - if (supportsXml) + var previousMethod = LastContractView?.Methods.FirstOrDefault(m => + MethodSignature.MethodSignatureComparer.Equals(m.Signature, method.Signature)); + if (previousMethod?.Signature.Parameters is [var previousParameter] + && method.Signature.Parameters is [var parameter] + && !string.Equals(parameter.Name, previousParameter.Name, StringComparison.Ordinal)) { - return BuildXmlExplicitFromClientResult(); + CodeModelGenerator.Instance.Emitter.Debug( + $"Preserved parameter name '{previousParameter.Name}' on '{Name}.{method.Signature.Name}' from last contract (instead of '{parameter.Name}').", + BackCompatibilityChangeCategory.ParameterNamePreserved); + parameter.Update(name: previousParameter.Name); } - return BuildExplicitFromClientResult(); + return method; } private MethodProvider BuildExplicitFromClientResult() diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/MrwSerializationTypeDefinitionTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/MrwSerializationTypeDefinitionTests.cs index 32ade7fd931..0d4052a0e7e 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/MrwSerializationTypeDefinitionTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/MrwSerializationTypeDefinitionTests.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; using System.Text.Json; +using System.Threading.Tasks; using Microsoft.TypeSpec.Generator.ClientModel.Providers; using Microsoft.TypeSpec.Generator.Expressions; using Microsoft.TypeSpec.Generator.Input; @@ -14,6 +15,7 @@ using Microsoft.TypeSpec.Generator.Primitives; using Microsoft.TypeSpec.Generator.Providers; using Microsoft.TypeSpec.Generator.Snippets; +using Microsoft.TypeSpec.Generator.SourceInput; using Microsoft.TypeSpec.Generator.Statements; using Microsoft.TypeSpec.Generator.Tests.Common; using NUnit.Framework; @@ -804,6 +806,47 @@ public void TestBuildExplicitFromClientResult() Assert.IsNotNull(methodBody); } + [Test] + public async Task ExplicitClientResultOperatorPreservesLastContractParameterName() + { + var inputModel = InputFactory.Model("mockInputModel"); + var generator = MockHelpers.LoadMockGenerator( + inputModels: () => [inputModel], + clientResponseApi: new RenamedClientResultProvider(ValueExpression.Empty)); + generator.Object.SourceInputModel = new SourceInputModel( + null, + await Helpers.GetCompilationFromDirectoryAsync()); + generator.Object.TypeFactory.RootOutputModels.Add(inputModel); + var model = generator.Object.OutputLibrary.TypeProviders.OfType().Single(); + + model.EnsureBuilt(); + model.ProcessTypeForBackCompatibility(); + + var explicitOperator = model.SerializationProviders.Single().Methods.Single(m => + m.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Explicit) + && m.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Operator)); + Assert.AreEqual("result", explicitOperator.Signature.Parameters.Single().Name); + } + + [Test] + public void ExplicitClientResultOperatorKeepsCurrentParameterNameWithoutLastContract() + { + var inputModel = InputFactory.Model("mockInputModel"); + var generator = MockHelpers.LoadMockGenerator( + inputModels: () => [inputModel], + clientResponseApi: new RenamedClientResultProvider(ValueExpression.Empty)); + generator.Object.TypeFactory.RootOutputModels.Add(inputModel); + var model = generator.Object.OutputLibrary.TypeProviders.OfType().Single(); + + model.EnsureBuilt(); + model.ProcessTypeForBackCompatibility(); + + var explicitOperator = model.SerializationProviders.Single().Methods.Single(m => + m.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Explicit) + && m.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Operator)); + Assert.AreEqual("response", explicitOperator.Signature.Parameters.Single().Name); + } + [Test] public void TestExplicitFromClientResultNotGeneratedForNonRootOutputModel() { @@ -818,6 +861,12 @@ public void TestExplicitFromClientResultNotGeneratedForNonRootOutputModel() Assert.IsNull(method); } + private record RenamedClientResultProvider(ValueExpression Original) + : ClientResultProvider(Original) + { + public override string ResponseParameterName => "response"; + } + [TestCase(true)] [TestCase(false)] public void TestReadOnlyPropertiesHaveWireCheck(bool isRequired) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/MrwSerializationTypeDefinitionTests/ExplicitClientResultOperatorPreservesLastContractParameterName/MockInputModel.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/MrwSerializationTypeDefinitionTests/ExplicitClientResultOperatorPreservesLastContractParameterName/MockInputModel.cs new file mode 100644 index 00000000000..bd38edca86c --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/MrwSerializationTypeDefinitionTests/ExplicitClientResultOperatorPreservesLastContractParameterName/MockInputModel.cs @@ -0,0 +1,9 @@ +using System.ClientModel; + +namespace Sample.Models +{ + public partial class MockInputModel + { + public static explicit operator MockInputModel(ClientResult result) => throw null; + } +} From 24e87c32e1e1bf74b7e44426489b5d4eb0dd5637 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 14 Aug 2026 16:49:35 +0000 Subject: [PATCH 03/11] fix(http-client-csharp): apply backcompat to serialization providers Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- .../MrwSerializationTypeDefinition.cs | 27 +++---------------- .../src/Providers/TypeProvider.cs | 5 ++++ 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs index 05d86c8fb73..367d9d9913b 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs @@ -122,9 +122,6 @@ public MrwSerializationTypeDefinition(InputModelType inputModel, ModelProvider m protected override TypeSignatureModifiers BuildDeclarationModifiers() => _model.DeclarationModifiers; - protected override IReadOnlyList BuildMethodsForBackCompatibility(IEnumerable originalMethods) - => [.. originalMethods]; - protected override IReadOnlyList BuildConstructorsForBackCompatibility(IEnumerable originalConstructors) => [.. originalConstructors]; @@ -401,33 +398,17 @@ protected override MethodProvider[] BuildMethods() private MethodProvider GetExplicitFromClientResultMethod(bool supportsJson, bool supportsXml) { - MethodProvider method; if (supportsJson && supportsXml) { - method = BuildJsonAndXmlExplicitFromClientResult(); - } - else if (supportsXml) - { - method = BuildXmlExplicitFromClientResult(); - } - else - { - method = BuildExplicitFromClientResult(); + return BuildJsonAndXmlExplicitFromClientResult(); } - var previousMethod = LastContractView?.Methods.FirstOrDefault(m => - MethodSignature.MethodSignatureComparer.Equals(m.Signature, method.Signature)); - if (previousMethod?.Signature.Parameters is [var previousParameter] - && method.Signature.Parameters is [var parameter] - && !string.Equals(parameter.Name, previousParameter.Name, StringComparison.Ordinal)) + if (supportsXml) { - CodeModelGenerator.Instance.Emitter.Debug( - $"Preserved parameter name '{previousParameter.Name}' on '{Name}.{method.Signature.Name}' from last contract (instead of '{parameter.Name}').", - BackCompatibilityChangeCategory.ParameterNamePreserved); - parameter.Update(name: previousParameter.Name); + return BuildXmlExplicitFromClientResult(); } - return method; + return BuildExplicitFromClientResult(); } private MethodProvider BuildExplicitFromClientResult() 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 f4188536f02..1a4271970a1 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 @@ -949,6 +949,11 @@ internal void ProcessTypeForBackCompatibility() { RebuildAttributes(); } + + foreach (var serializationProvider in SerializationProviders) + { + serializationProvider.ProcessTypeForBackCompatibility(); + } } // Runs newly-added back-compatibility members through every registered visitor while leaving From 73c400662ad565d64a7c715f7d2b839480cf79aa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 14 Aug 2026 17:29:59 +0000 Subject: [PATCH 04/11] fix(http-client-csharp): process partial methods once for backcompat Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- .../src/Providers/MrwSerializationTypeDefinition.cs | 3 +++ .../src/Providers/TypeProvider.cs | 10 +++------- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs index 367d9d9913b..590d2d4b387 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs @@ -122,6 +122,9 @@ public MrwSerializationTypeDefinition(InputModelType inputModel, ModelProvider m protected override TypeSignatureModifiers BuildDeclarationModifiers() => _model.DeclarationModifiers; + protected override IReadOnlyList BuildMethodsForBackCompatibility(IEnumerable originalMethods) + => [.. originalMethods]; + protected override IReadOnlyList BuildConstructorsForBackCompatibility(IEnumerable originalConstructors) => [.. originalConstructors]; 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 1a4271970a1..4ceb56948aa 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 @@ -949,11 +949,6 @@ internal void ProcessTypeForBackCompatibility() { RebuildAttributes(); } - - foreach (var serializationProvider in SerializationProviders) - { - serializationProvider.ProcessTypeForBackCompatibility(); - } } // Runs newly-added back-compatibility members through every registered visitor while leaving @@ -1024,13 +1019,14 @@ protected internal virtual IReadOnlyList BuildAttributesFor protected internal virtual IReadOnlyList BuildMethodsForBackCompatibility(IEnumerable originalMethods) { var methods = new List(originalMethods); + var allMethods = methods.Concat(SerializationProviders.SelectMany(p => p.Methods)).ToList(); if (LastContractView?.Methods is not { Count: > 0 } previousMethods) { return methods; } - var currentMethodSignatures = BuildCurrentMethodSignatureMap(methods); + var currentMethodSignatures = BuildCurrentMethodSignatureMap(allMethods); foreach (var previousMethod in previousMethods) { @@ -1050,7 +1046,7 @@ protected internal virtual IReadOnlyList BuildMethodsForBackComp } } - BackCompatHelper.RestorePreviousParameterNames(this, methods); + BackCompatHelper.RestorePreviousParameterNames(this, allMethods); BackCompatHelper.AddBackCompatOverloads(this, methods); return methods; From a9dab1d05d0594dccb87c1f5e6edbdd7016c9164 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 14 Aug 2026 17:31:14 +0000 Subject: [PATCH 05/11] docs(http-client-csharp): clarify partial backcompat scope Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- .../Microsoft.TypeSpec.Generator/src/Providers/TypeProvider.cs | 1 + 1 file changed, 1 insertion(+) 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 4ceb56948aa..3b2e71bea60 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 @@ -1019,6 +1019,7 @@ protected internal virtual IReadOnlyList BuildAttributesFor protected internal virtual IReadOnlyList BuildMethodsForBackCompatibility(IEnumerable originalMethods) { var methods = new List(originalMethods); + // Serialization methods participate in matching and in-place restoration, but overloads remain on the owning provider. var allMethods = methods.Concat(SerializationProviders.SelectMany(p => p.Methods)).ToList(); if (LastContractView?.Methods is not { Count: > 0 } previousMethods) From 23bf62f0d2409fe78f03df31d677563839c1ad09 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 14 Aug 2026 18:34:03 +0000 Subject: [PATCH 06/11] fix(http-client-csharp): scope serialization backcompat to operators Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- .../MrwSerializationTypeDefinition.cs | 9 +++++++- .../MrwSerializationTypeDefinitionTests.cs | 18 +++++++++------- ...CurrentParameterNameWithoutLastContract.cs | 21 +++++++++++++++++++ ...ratorPreservesLastContractParameterName.cs | 21 +++++++++++++++++++ .../src/Providers/TypeProvider.cs | 11 ++++++---- 5 files changed, 67 insertions(+), 13 deletions(-) create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/MrwSerializationTypeDefinitionTests/ExplicitClientResultOperatorKeepsCurrentParameterNameWithoutLastContract.cs create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/MrwSerializationTypeDefinitionTests/ExplicitClientResultOperatorPreservesLastContractParameterName.cs diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs index 590d2d4b387..aadedbc89f6 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs @@ -123,7 +123,14 @@ public MrwSerializationTypeDefinition(InputModelType inputModel, ModelProvider m protected override TypeSignatureModifiers BuildDeclarationModifiers() => _model.DeclarationModifiers; protected override IReadOnlyList BuildMethodsForBackCompatibility(IEnumerable originalMethods) - => [.. originalMethods]; + { + var methods = originalMethods.ToList(); + var generatedOperators = methods + .Where(m => m.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Operator)) + .ToList(); + _ = base.BuildMethodsForBackCompatibility(generatedOperators); + return methods; + } protected override IReadOnlyList BuildConstructorsForBackCompatibility(IEnumerable originalConstructors) => [.. originalConstructors]; diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/MrwSerializationTypeDefinitionTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/MrwSerializationTypeDefinitionTests.cs index 0d4052a0e7e..b1f69171229 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/MrwSerializationTypeDefinitionTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/MrwSerializationTypeDefinitionTests.cs @@ -822,10 +822,11 @@ public async Task ExplicitClientResultOperatorPreservesLastContractParameterName model.EnsureBuilt(); model.ProcessTypeForBackCompatibility(); - var explicitOperator = model.SerializationProviders.Single().Methods.Single(m => - m.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Explicit) - && m.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Operator)); - Assert.AreEqual("result", explicitOperator.Signature.Parameters.Single().Name); + var writer = new TypeProviderWriter(new FilteredMethodsTypeProvider( + model.SerializationProviders.Single(), + name => name == model.Name)); + + Assert.AreEqual(Helpers.GetExpectedFromFile(), writer.Write().Content); } [Test] @@ -841,10 +842,11 @@ public void ExplicitClientResultOperatorKeepsCurrentParameterNameWithoutLastCont model.EnsureBuilt(); model.ProcessTypeForBackCompatibility(); - var explicitOperator = model.SerializationProviders.Single().Methods.Single(m => - m.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Explicit) - && m.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Operator)); - Assert.AreEqual("response", explicitOperator.Signature.Parameters.Single().Name); + var writer = new TypeProviderWriter(new FilteredMethodsTypeProvider( + model.SerializationProviders.Single(), + name => name == model.Name)); + + Assert.AreEqual(Helpers.GetExpectedFromFile(), writer.Write().Content); } [Test] diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/MrwSerializationTypeDefinitionTests/ExplicitClientResultOperatorKeepsCurrentParameterNameWithoutLastContract.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/MrwSerializationTypeDefinitionTests/ExplicitClientResultOperatorKeepsCurrentParameterNameWithoutLastContract.cs new file mode 100644 index 00000000000..d66342e3da8 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/MrwSerializationTypeDefinitionTests/ExplicitClientResultOperatorKeepsCurrentParameterNameWithoutLastContract.cs @@ -0,0 +1,21 @@ +// + +#nullable disable + +using System.ClientModel; +using System.ClientModel.Primitives; +using System.Text.Json; +using Sample.Models; + +namespace Sample +{ + public partial class MockInputModel + { + public static explicit operator MockInputModel(global::System.ClientModel.ClientResult response) + { + global::System.ClientModel.Primitives.PipelineResponse response0 = response.GetRawResponse(); + using global::System.Text.Json.JsonDocument document = global::System.Text.Json.JsonDocument.Parse(response0.Content, global::Sample.ModelSerializationExtensions.JsonDocumentOptions); + return global::Sample.Models.MockInputModel.DeserializeMockInputModel(document.RootElement, global::Sample.ModelSerializationExtensions.WireOptions); + } + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/MrwSerializationTypeDefinitionTests/ExplicitClientResultOperatorPreservesLastContractParameterName.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/MrwSerializationTypeDefinitionTests/ExplicitClientResultOperatorPreservesLastContractParameterName.cs new file mode 100644 index 00000000000..f8fb7af87a5 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/MrwSerializationTypeDefinitionTests/ExplicitClientResultOperatorPreservesLastContractParameterName.cs @@ -0,0 +1,21 @@ +// + +#nullable disable + +using System.ClientModel; +using System.ClientModel.Primitives; +using System.Text.Json; +using Sample.Models; + +namespace Sample +{ + public partial class MockInputModel + { + public static explicit operator MockInputModel(global::System.ClientModel.ClientResult result) + { + global::System.ClientModel.Primitives.PipelineResponse response = result.GetRawResponse(); + using global::System.Text.Json.JsonDocument document = global::System.Text.Json.JsonDocument.Parse(response.Content, global::Sample.ModelSerializationExtensions.JsonDocumentOptions); + return global::Sample.Models.MockInputModel.DeserializeMockInputModel(document.RootElement, global::Sample.ModelSerializationExtensions.WireOptions); + } + } +} 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 3b2e71bea60..1a4271970a1 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 @@ -949,6 +949,11 @@ internal void ProcessTypeForBackCompatibility() { RebuildAttributes(); } + + foreach (var serializationProvider in SerializationProviders) + { + serializationProvider.ProcessTypeForBackCompatibility(); + } } // Runs newly-added back-compatibility members through every registered visitor while leaving @@ -1019,15 +1024,13 @@ protected internal virtual IReadOnlyList BuildAttributesFor protected internal virtual IReadOnlyList BuildMethodsForBackCompatibility(IEnumerable originalMethods) { var methods = new List(originalMethods); - // Serialization methods participate in matching and in-place restoration, but overloads remain on the owning provider. - var allMethods = methods.Concat(SerializationProviders.SelectMany(p => p.Methods)).ToList(); if (LastContractView?.Methods is not { Count: > 0 } previousMethods) { return methods; } - var currentMethodSignatures = BuildCurrentMethodSignatureMap(allMethods); + var currentMethodSignatures = BuildCurrentMethodSignatureMap(methods); foreach (var previousMethod in previousMethods) { @@ -1047,7 +1050,7 @@ protected internal virtual IReadOnlyList BuildMethodsForBackComp } } - BackCompatHelper.RestorePreviousParameterNames(this, allMethods); + BackCompatHelper.RestorePreviousParameterNames(this, methods); BackCompatHelper.AddBackCompatOverloads(this, methods); return methods; From 1aded1e0b1be78508ad98a15f00ea1e4db62bad4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 14 Aug 2026 18:35:34 +0000 Subject: [PATCH 07/11] docs(http-client-csharp): clarify operator backcompat mutation Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- .../src/Providers/MrwSerializationTypeDefinition.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs index aadedbc89f6..5ba4673abc4 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs @@ -128,6 +128,7 @@ protected override IReadOnlyList BuildMethodsForBackCompatibilit var generatedOperators = methods .Where(m => m.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Operator)) .ToList(); + // The base implementation updates matching operators in place; this partial does not add back-compat overloads. _ = base.BuildMethodsForBackCompatibility(generatedOperators); return methods; } From 9bd662440c6368f70a7a3409994abac468898c46 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 14 Aug 2026 19:23:43 +0000 Subject: [PATCH 08/11] fix(http-client-csharp): move serialization backcompat to scm output providers Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- .../src/Providers/MrwSerializationTypeDefinition.cs | 10 +--------- .../src/ScmOutputLibrary.cs | 3 +++ .../MrwSerializationTypeDefinitionTests.cs | 10 ++++++++-- .../src/Providers/TypeProvider.cs | 5 ----- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs index 5ba4673abc4..a591ee63581 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MrwSerializationTypeDefinition.cs @@ -123,15 +123,7 @@ public MrwSerializationTypeDefinition(InputModelType inputModel, ModelProvider m protected override TypeSignatureModifiers BuildDeclarationModifiers() => _model.DeclarationModifiers; protected override IReadOnlyList BuildMethodsForBackCompatibility(IEnumerable originalMethods) - { - var methods = originalMethods.ToList(); - var generatedOperators = methods - .Where(m => m.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Operator)) - .ToList(); - // The base implementation updates matching operators in place; this partial does not add back-compat overloads. - _ = base.BuildMethodsForBackCompatibility(generatedOperators); - return methods; - } + => base.BuildMethodsForBackCompatibility(originalMethods); protected override IReadOnlyList BuildConstructorsForBackCompatibility(IEnumerable originalConstructors) => [.. originalConstructors]; diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmOutputLibrary.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmOutputLibrary.cs index 82e4a8061a8..cfefc62e6be 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmOutputLibrary.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmOutputLibrary.cs @@ -90,8 +90,11 @@ protected override TypeProvider[] BuildTypeProviders() } } + var serializationTypes = baseTypes.SelectMany(static type => type.SerializationProviders); + return [ ..baseTypes, + ..serializationTypes, ..BuildClientTypes(), ScmCodeModelGenerator.Instance.ModelSerializationExtensionsDefinition, ScmCodeModelGenerator.Instance.SerializationFormatDefinition, diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/MrwSerializationTypeDefinitionTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/MrwSerializationTypeDefinitionTests.cs index b1f69171229..a84b28d2641 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/MrwSerializationTypeDefinitionTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/MrwSerializationTypeDefinitionTests.cs @@ -820,7 +820,10 @@ public async Task ExplicitClientResultOperatorPreservesLastContractParameterName var model = generator.Object.OutputLibrary.TypeProviders.OfType().Single(); model.EnsureBuilt(); - model.ProcessTypeForBackCompatibility(); + foreach (var typeProvider in generator.Object.OutputLibrary.TypeProviders) + { + typeProvider.ProcessTypeForBackCompatibility(); + } var writer = new TypeProviderWriter(new FilteredMethodsTypeProvider( model.SerializationProviders.Single(), @@ -840,7 +843,10 @@ public void ExplicitClientResultOperatorKeepsCurrentParameterNameWithoutLastCont var model = generator.Object.OutputLibrary.TypeProviders.OfType().Single(); model.EnsureBuilt(); - model.ProcessTypeForBackCompatibility(); + foreach (var typeProvider in generator.Object.OutputLibrary.TypeProviders) + { + typeProvider.ProcessTypeForBackCompatibility(); + } var writer = new TypeProviderWriter(new FilteredMethodsTypeProvider( model.SerializationProviders.Single(), 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 1a4271970a1..f4188536f02 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 @@ -949,11 +949,6 @@ internal void ProcessTypeForBackCompatibility() { RebuildAttributes(); } - - foreach (var serializationProvider in SerializationProviders) - { - serializationProvider.ProcessTypeForBackCompatibility(); - } } // Runs newly-added back-compatibility members through every registered visitor while leaving From 1c7536a3df16fb2aff21541e6ebd478d7fb4d6bd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 14 Aug 2026 19:38:29 +0000 Subject: [PATCH 09/11] test(http-client-csharp): add serialization provider back-compat operator test Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- .../src/ScmOutputLibrary.cs | 2 +- ...torParameterNameOnSerializationProvider.cs | 14 +++++++++ .../SerializationBackCompatType.cs | 7 +++++ .../test/Providers/TypeProviderTests.cs | 31 +++++++++++++++++++ 4 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/TestData/TypeProviderTests/BuildMethodsForBackCompatibilityRestoresOperatorParameterNameOnSerializationProvider.cs create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/TestData/TypeProviderTests/BuildMethodsForBackCompatibilityRestoresOperatorParameterNameOnSerializationProvider/SerializationBackCompatType.cs diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmOutputLibrary.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmOutputLibrary.cs index cfefc62e6be..bffa99d670a 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmOutputLibrary.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmOutputLibrary.cs @@ -90,7 +90,7 @@ protected override TypeProvider[] BuildTypeProviders() } } - var serializationTypes = baseTypes.SelectMany(static type => type.SerializationProviders); + var serializationTypes = baseTypes.SelectMany(type => type.SerializationProviders); return [ ..baseTypes, diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/TestData/TypeProviderTests/BuildMethodsForBackCompatibilityRestoresOperatorParameterNameOnSerializationProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/TestData/TypeProviderTests/BuildMethodsForBackCompatibilityRestoresOperatorParameterNameOnSerializationProvider.cs new file mode 100644 index 00000000000..37a44a37e3f --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/TestData/TypeProviderTests/BuildMethodsForBackCompatibilityRestoresOperatorParameterNameOnSerializationProvider.cs @@ -0,0 +1,14 @@ +// + +#nullable disable + +namespace Test +{ + public partial class SerializationBackCompatType + { + public static explicit operator SerializationBackCompatType(string result) + { + return null; + } + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/TestData/TypeProviderTests/BuildMethodsForBackCompatibilityRestoresOperatorParameterNameOnSerializationProvider/SerializationBackCompatType.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/TestData/TypeProviderTests/BuildMethodsForBackCompatibilityRestoresOperatorParameterNameOnSerializationProvider/SerializationBackCompatType.cs new file mode 100644 index 00000000000..f98c65754f8 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/TestData/TypeProviderTests/BuildMethodsForBackCompatibilityRestoresOperatorParameterNameOnSerializationProvider/SerializationBackCompatType.cs @@ -0,0 +1,7 @@ +namespace Test +{ + public partial class SerializationBackCompatType + { + public static explicit operator SerializationBackCompatType(string result) => null; + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/TypeProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/TypeProviderTests.cs index b4b6d14584d..ad5fd5424fc 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/TypeProviderTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/TypeProviderTests.cs @@ -968,6 +968,37 @@ public async Task BuildMethodsForBackCompatibilityKeepsUnpublishedParameterName( Assert.AreEqual(Helpers.GetExpectedFromFile(), actual); } + [Test] + public async Task BuildMethodsForBackCompatibilityRestoresOperatorParameterNameOnSerializationProvider() + { + await MockHelpers.LoadMockGeneratorAsync(lastContractCompilation: async () => await Helpers.GetCompilationFromDirectoryAsync()); + + var owner = new TestTypeProvider(name: "SerializationBackCompatType", ns: "Test"); + var serializationProvider = new TestTypeProvider(name: "SerializationBackCompatType", ns: "Test"); + var response = new ParameterProvider("response", $"", new CSharpType(typeof(string))); + var explicitOperator = new MethodProvider( + new MethodSignature( + serializationProvider.Name, + $"", + MethodSignatureModifiers.Public | MethodSignatureModifiers.Static | MethodSignatureModifiers.Explicit | MethodSignatureModifiers.Operator, + serializationProvider.Type, + $"", + [response]), + Snippet.Return(Snippet.Null), + serializationProvider); + + serializationProvider.Update(methods: [explicitOperator]); + owner.Update(serializations: [serializationProvider]); + + foreach (var provider in owner.SerializationProviders.Prepend(owner)) + { + provider.ProcessTypeForBackCompatibility(); + } + + var actual = new TypeProviderWriter(serializationProvider).Write().Content; + Assert.AreEqual(Helpers.GetExpectedFromFile(), actual); + } + // Validates the positional fallback: when a spec parameter's previously-published name matches // neither its current name nor its spec original name (e.g. a rename by a different generator), // it is restored from the last-contract method that matches by signature (name and parameter types). From c387cf877094754fdbd0562b5b9e08fff777074f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 14 Aug 2026 20:01:25 +0000 Subject: [PATCH 10/11] fix(http-client-csharp): process serialization back-compat in CSharpGen traversal Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- .../src/ScmOutputLibrary.cs | 3 -- .../MrwSerializationTypeDefinitionTests.cs | 10 ++---- .../src/CSharpGen.cs | 36 ++++++++++++++++--- .../test/Providers/TypeProviderTests.cs | 7 ++-- 4 files changed, 36 insertions(+), 20 deletions(-) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmOutputLibrary.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmOutputLibrary.cs index bffa99d670a..82e4a8061a8 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmOutputLibrary.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmOutputLibrary.cs @@ -90,11 +90,8 @@ protected override TypeProvider[] BuildTypeProviders() } } - var serializationTypes = baseTypes.SelectMany(type => type.SerializationProviders); - return [ ..baseTypes, - ..serializationTypes, ..BuildClientTypes(), ScmCodeModelGenerator.Instance.ModelSerializationExtensionsDefinition, ScmCodeModelGenerator.Instance.SerializationFormatDefinition, diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/MrwSerializationTypeDefinitionTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/MrwSerializationTypeDefinitionTests.cs index a84b28d2641..c830ae3ff6d 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/MrwSerializationTypeDefinitionTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/MrwSerializationTypeDefinitionTests.cs @@ -820,10 +820,7 @@ public async Task ExplicitClientResultOperatorPreservesLastContractParameterName var model = generator.Object.OutputLibrary.TypeProviders.OfType().Single(); model.EnsureBuilt(); - foreach (var typeProvider in generator.Object.OutputLibrary.TypeProviders) - { - typeProvider.ProcessTypeForBackCompatibility(); - } + CSharpGen.ProcessTypeProvidersForBackCompatibility(generator.Object.OutputLibrary); var writer = new TypeProviderWriter(new FilteredMethodsTypeProvider( model.SerializationProviders.Single(), @@ -843,10 +840,7 @@ public void ExplicitClientResultOperatorKeepsCurrentParameterNameWithoutLastCont var model = generator.Object.OutputLibrary.TypeProviders.OfType().Single(); model.EnsureBuilt(); - foreach (var typeProvider in generator.Object.OutputLibrary.TypeProviders) - { - typeProvider.ProcessTypeForBackCompatibility(); - } + CSharpGen.ProcessTypeProvidersForBackCompatibility(generator.Object.OutputLibrary); var writer = new TypeProviderWriter(new FilteredMethodsTypeProvider( model.SerializationProviders.Single(), diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/CSharpGen.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/CSharpGen.cs index 94abf17e68e..a6de059ddd2 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/CSharpGen.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/CSharpGen.cs @@ -86,11 +86,8 @@ await GeneratedCodeWorkspace.LoadBaselineContract(), LoggingHelpers.LogElapsedTime("All visitors have been applied"); - foreach (var outputType in output.TypeProviders) - { - // Ensure back-compatibility processing is done after all visitors have run - outputType.ProcessTypeForBackCompatibility(); - } + // Ensure back-compatibility processing is done after all visitors have run + ProcessTypeProvidersForBackCompatibility(output); try { @@ -211,6 +208,35 @@ private static void FilterCustomizedMembers(TypeProvider typeProvider) typeProvider.Fields); } + internal static void ProcessTypeProvidersForBackCompatibility(OutputLibrary output) + { + var visited = new HashSet(); + foreach (var typeProvider in output.TypeProviders) + { + ProcessTypeForBackCompatibility(typeProvider, visited); + } + } + + private static void ProcessTypeForBackCompatibility(TypeProvider typeProvider, HashSet visited) + { + if (!visited.Add(typeProvider)) + { + return; + } + + typeProvider.ProcessTypeForBackCompatibility(); + + foreach (var serializationProvider in typeProvider.SerializationProviders) + { + ProcessTypeForBackCompatibility(serializationProvider, visited); + } + + foreach (var nestedType in typeProvider.NestedTypes) + { + ProcessTypeForBackCompatibility(nestedType, visited); + } + } + /// /// Clears the output directory specified by . If is not null, /// the specified files in the output directory will not be deleted. diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/TypeProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/TypeProviderTests.cs index ad5fd5424fc..e89c1e99d1f 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/TypeProviderTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/TypeProviderTests.cs @@ -12,6 +12,7 @@ using Microsoft.TypeSpec.Generator.Snippets; using Microsoft.TypeSpec.Generator.Statements; using Microsoft.TypeSpec.Generator.Tests.Common; +using Microsoft.TypeSpec.Generator.Tests.TestHelpers; using Microsoft.TypeSpec.Generator.Utilities; using NUnit.Framework; @@ -990,10 +991,8 @@ public async Task BuildMethodsForBackCompatibilityRestoresOperatorParameterNameO serializationProvider.Update(methods: [explicitOperator]); owner.Update(serializations: [serializationProvider]); - foreach (var provider in owner.SerializationProviders.Prepend(owner)) - { - provider.ProcessTypeForBackCompatibility(); - } + var outputLibrary = new TestOutputLibrary(owner); + CSharpGen.ProcessTypeProvidersForBackCompatibility(outputLibrary); var actual = new TypeProviderWriter(serializationProvider).Write().Content; Assert.AreEqual(Helpers.GetExpectedFromFile(), actual); From ec58d8a22fac31e80e5e48498f0b91074260b414 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 14 Aug 2026 21:07:40 +0000 Subject: [PATCH 11/11] test(http-client-csharp): add implicit operator back-compat coverage Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- ...torParameterNameOnSerializationProvider.cs | 14 +++++++++ .../SerializationImplicitBackCompatType.cs | 7 +++++ .../test/Providers/TypeProviderTests.cs | 29 +++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/TestData/TypeProviderTests/BuildMethodsForBackCompatibilityRestoresImplicitOperatorParameterNameOnSerializationProvider.cs create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/TestData/TypeProviderTests/BuildMethodsForBackCompatibilityRestoresImplicitOperatorParameterNameOnSerializationProvider/SerializationImplicitBackCompatType.cs diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/TestData/TypeProviderTests/BuildMethodsForBackCompatibilityRestoresImplicitOperatorParameterNameOnSerializationProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/TestData/TypeProviderTests/BuildMethodsForBackCompatibilityRestoresImplicitOperatorParameterNameOnSerializationProvider.cs new file mode 100644 index 00000000000..8835a4b5df4 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/TestData/TypeProviderTests/BuildMethodsForBackCompatibilityRestoresImplicitOperatorParameterNameOnSerializationProvider.cs @@ -0,0 +1,14 @@ +// + +#nullable disable + +namespace Test +{ + public partial class SerializationImplicitBackCompatType + { + public static implicit operator string(global::Test.SerializationImplicitBackCompatType published) + { + return null; + } + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/TestData/TypeProviderTests/BuildMethodsForBackCompatibilityRestoresImplicitOperatorParameterNameOnSerializationProvider/SerializationImplicitBackCompatType.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/TestData/TypeProviderTests/BuildMethodsForBackCompatibilityRestoresImplicitOperatorParameterNameOnSerializationProvider/SerializationImplicitBackCompatType.cs new file mode 100644 index 00000000000..bd888c79c2c --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/TestData/TypeProviderTests/BuildMethodsForBackCompatibilityRestoresImplicitOperatorParameterNameOnSerializationProvider/SerializationImplicitBackCompatType.cs @@ -0,0 +1,7 @@ +namespace Test +{ + public partial class SerializationImplicitBackCompatType + { + public static implicit operator string(SerializationImplicitBackCompatType published) => null; + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/TypeProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/TypeProviderTests.cs index e89c1e99d1f..975d0bfccfe 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/TypeProviderTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/TypeProviderTests.cs @@ -998,6 +998,35 @@ public async Task BuildMethodsForBackCompatibilityRestoresOperatorParameterNameO Assert.AreEqual(Helpers.GetExpectedFromFile(), actual); } + [Test] + public async Task BuildMethodsForBackCompatibilityRestoresImplicitOperatorParameterNameOnSerializationProvider() + { + await MockHelpers.LoadMockGeneratorAsync(lastContractCompilation: async () => await Helpers.GetCompilationFromDirectoryAsync()); + + var owner = new TestTypeProvider(name: "SerializationImplicitBackCompatType", ns: "Test"); + var serializationProvider = new TestTypeProvider(name: "SerializationImplicitBackCompatType", ns: "Test"); + var current = new ParameterProvider("current", $"", serializationProvider.Type); + var implicitOperator = new MethodProvider( + new MethodSignature( + "BinaryContent", + $"", + MethodSignatureModifiers.Public | MethodSignatureModifiers.Static | MethodSignatureModifiers.Implicit | MethodSignatureModifiers.Operator, + new CSharpType(typeof(string)), + $"", + [current]), + Snippet.Return(Snippet.Null), + serializationProvider); + + serializationProvider.Update(methods: [implicitOperator]); + owner.Update(serializations: [serializationProvider]); + + var outputLibrary = new TestOutputLibrary(owner); + CSharpGen.ProcessTypeProvidersForBackCompatibility(outputLibrary); + + var actual = new TypeProviderWriter(serializationProvider).Write().Content; + Assert.AreEqual(Helpers.GetExpectedFromFile(), actual); + } + // Validates the positional fallback: when a spec parameter's previously-published name matches // neither its current name nor its spec original name (e.g. a rename by a different generator), // it is restored from the last-contract method that matches by signature (name and parameter types).