diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ModelFactoryProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ModelFactoryProvider.cs index 02905ca83f3..cbc6ac9c06c 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ModelFactoryProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ModelFactoryProvider.cs @@ -722,7 +722,8 @@ private static IReadOnlyList GetCtorArgs( } return modelProvider.DeclarationModifiers.HasFlag(TypeSignatureModifiers.Abstract) - ? modelProvider.DerivedModels.FirstOrDefault(m => m.IsUnknownDiscriminatorModel) + ? modelProvider.DerivedModels.FirstOrDefault(m => m.IsUnknownDiscriminatorModel + && !m.DeclarationModifiers.HasFlag(TypeSignatureModifiers.Abstract)) : modelProvider; } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelFactories/ModelFactoryProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelFactories/ModelFactoryProviderTests.cs index 5356a891f46..3c2c1199082 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelFactories/ModelFactoryProviderTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelFactories/ModelFactoryProviderTests.cs @@ -1821,6 +1821,59 @@ public async Task BackCompatibility_AbstractReturnTypeOverloadIsGenerated() Assert.AreEqual(Helpers.GetExpectedFromFile(), content); } + [Test] + public async Task BackCompatibility_AbstractReturnTypeWithoutCurrentOverload() + { + var derived = InputFactory.Model("DerivedModel", discriminatedKind: "derived"); + var baseModel = InputFactory.Model( + "AbstractModel", + properties: + [ + InputFactory.Property("kind", InputPrimitiveType.String, isRequired: true, isDiscriminator: true), + InputFactory.Property("prop1", InputPrimitiveType.String), + InputFactory.Property("prop2", InputPrimitiveType.String), + ], + derivedModels: [derived]); + + _instance = (await MockHelpers.LoadMockGeneratorAsync( + inputNamespaceName: "Sample.Namespace", + inputModelTypes: [baseModel, derived], + lastContractCompilation: async () => await Helpers.GetCompilationFromDirectoryAsync())).Object; + + var modelFactory = _instance!.OutputLibrary.ModelFactory.Value; + modelFactory.ProcessTypeForBackCompatibility(); + + var content = new TypeProviderWriter(modelFactory).Write().Content; + Assert.AreEqual(Helpers.GetExpectedFromFile(), content); + } + + [TestCase(false)] + [TestCase(true)] + public async Task BackCompatibility_AbstractReturnTypeWithoutConcreteUnknownModelIsSkipped(bool hasUnknownModel) + { + var derived = InputFactory.Model("DerivedModel", discriminatedKind: "derived"); + var baseModel = InputFactory.Model( + "AbstractModel", + properties: + [ + InputFactory.Property("kind", InputPrimitiveType.String, isRequired: true, isDiscriminator: hasUnknownModel), + InputFactory.Property("prop1", InputPrimitiveType.String), + ], + derivedModels: hasUnknownModel ? [derived] : []); + + _instance = (await MockHelpers.LoadMockGeneratorAsync( + inputNamespaceName: "Sample.Namespace", + inputModelTypes: hasUnknownModel ? [baseModel, derived] : [baseModel], + compilation: async () => await Helpers.GetCompilationFromDirectoryAsync(), + lastContractCompilation: async () => await Helpers.GetCompilationFromDirectoryAsync( + method: nameof(BackCompatibility_AbstractReturnTypeWithoutCurrentOverload)))).Object; + + var modelFactory = _instance!.OutputLibrary.ModelFactory.Value; + modelFactory.ProcessTypeForBackCompatibility(); + + Assert.IsEmpty(modelFactory.Methods.Where(m => m.Signature.Name is "AbstractModel" or "AbstractModelOldName")); + } + [Test] public async Task BackCompatibility_UnknownDiscriminatorReturnTypeOverloadIsGenerated() { diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelFactories/TestData/ModelFactoryProviderTests/BackCompatibility_AbstractReturnTypeWithoutConcreteUnknownModelIsSkipped/AbstractModel.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelFactories/TestData/ModelFactoryProviderTests/BackCompatibility_AbstractReturnTypeWithoutConcreteUnknownModelIsSkipped/AbstractModel.cs new file mode 100644 index 00000000000..db035d6e022 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelFactories/TestData/ModelFactoryProviderTests/BackCompatibility_AbstractReturnTypeWithoutConcreteUnknownModelIsSkipped/AbstractModel.cs @@ -0,0 +1,8 @@ +namespace Sample.Models +{ + public abstract partial class AbstractModel + { } + + internal abstract partial class UnknownAbstractModel : AbstractModel + { } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelFactories/TestData/ModelFactoryProviderTests/BackCompatibility_AbstractReturnTypeWithoutCurrentOverload.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelFactories/TestData/ModelFactoryProviderTests/BackCompatibility_AbstractReturnTypeWithoutCurrentOverload.cs new file mode 100644 index 00000000000..c9ff5d19a51 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelFactories/TestData/ModelFactoryProviderTests/BackCompatibility_AbstractReturnTypeWithoutCurrentOverload.cs @@ -0,0 +1,28 @@ +// + +#nullable disable + +using System.ComponentModel; +using Sample.Models; + +namespace Sample.Namespace +{ + public static partial class SampleNamespaceModelFactory + { + public static global::Sample.Models.AbstractModel AbstractModel(string kind = default, string prop1 = default, string prop2 = default) + { + return new global::Sample.Models.UnknownAbstractModel(kind, prop1, prop2, additionalBinaryDataProperties: null); + } + + public static global::Sample.Models.DerivedModel DerivedModel(string prop1 = default, string prop2 = default, string stringProperty = default) + { + return new global::Sample.Models.DerivedModel("derived", prop1, prop2, additionalBinaryDataProperties: null, stringProperty); + } + + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] + public static global::Sample.Models.AbstractModel AbstractModelOldName(string prop1, string kind) + { + return new global::Sample.Models.UnknownAbstractModel(kind, prop1, default, additionalBinaryDataProperties: null); + } + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelFactories/TestData/ModelFactoryProviderTests/BackCompatibility_AbstractReturnTypeWithoutCurrentOverload/SampleNamespaceModelFactory.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelFactories/TestData/ModelFactoryProviderTests/BackCompatibility_AbstractReturnTypeWithoutCurrentOverload/SampleNamespaceModelFactory.cs new file mode 100644 index 00000000000..e8ebabddec1 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelFactories/TestData/ModelFactoryProviderTests/BackCompatibility_AbstractReturnTypeWithoutCurrentOverload/SampleNamespaceModelFactory.cs @@ -0,0 +1,16 @@ +using Sample.Models; + +namespace Sample.Namespace +{ + public static partial class SampleNamespaceModelFactory + { + public static AbstractModel AbstractModelOldName(string prop1 = default, string kind = default) + { } + } +} + +namespace Sample.Models +{ + public abstract partial class AbstractModel + { } +}