From 7663906f54afbf1b673e8735151db1544d6bfde8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 27 Jul 2026 15:18:52 +0000 Subject: [PATCH 1/6] Initial plan From c7f2f403c8dbdc186db811ed9cd1bd7be0f19482 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 27 Jul 2026 15:22:12 +0000 Subject: [PATCH 2/6] [http-client-csharp] Build discriminated base description based on discriminated base type, not abstract modifier Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- .../src/Providers/ModelProvider.cs | 16 +++++++++-- .../ModelProviders/DiscriminatorTests.cs | 27 +++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) 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 e0766da6f75..87fa1cc7158 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 @@ -27,7 +27,10 @@ protected override FormattableString BuildDescription() { var description = DocHelpers.GetFormattableDescription(_inputModel.Summary, _inputModel.Doc) ?? $"The {Name}."; - if (DeclarationModifiers.HasFlag(TypeSignatureModifiers.Abstract)) + // Whether the model is a discriminated base type is not tied to whether it is declared abstract. + // Downstream emitters may choose not to model discriminated base types as abstract, but the + // description referencing the available derived classes should still be built. + if (IsDiscriminatedBaseType) { _derivedModels = BuildDerivedModels(); var publicDerivedModels = _derivedModels.Where(m => m.DeclarationModifiers.HasFlag(TypeSignatureModifiers.Public)).ToList(); @@ -65,6 +68,15 @@ protected override FormattableString BuildDescription() private ModelProvider? _baseModelProvider; private ConstructorProvider? _fullConstructor; internal PropertyProvider? DiscriminatorProperty { get; private set; } + + /// + /// Gets whether this model is a discriminated base type, i.e. it defines a discriminator property + /// but does not itself carry a discriminator value. This is independent of whether the type is + /// declared abstract, since downstream emitters may choose not to model discriminated base types + /// as abstract. + /// + private bool IsDiscriminatedBaseType => _inputModel.DiscriminatorProperty is not null && _inputModel.DiscriminatorValue is null; + private ValueExpression DiscriminatorLiteral => Literal(_inputModel.DiscriminatorValue ?? ""); public ModelProvider(InputModelType inputModel) : base(inputModel) @@ -324,7 +336,7 @@ protected override TypeSignatureModifiers BuildDeclarationModifiers() declarationModifiers |= TypeSignatureModifiers.Internal; } - if (_inputModel.DiscriminatorProperty is not null && _inputModel.DiscriminatorValue is null) + if (IsDiscriminatedBaseType) { declarationModifiers |= TypeSignatureModifiers.Abstract; } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/DiscriminatorTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/DiscriminatorTests.cs index a3666593986..4077bfb06f3 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/DiscriminatorTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/DiscriminatorTests.cs @@ -120,6 +120,33 @@ public void BaseShouldBeAbstract(InputModelType inputModel, string expectedSumma Assert.AreEqual(expectedSummary, baseModel.XmlDocs.Summary!.ToDisplayString()); } + [Test] + public void DiscriminatedBaseDescriptionIsBuiltEvenWhenNotAbstract() + { + MockHelpers.LoadMockGenerator(); + // Simulate a downstream emitter that does not model the discriminated base type as abstract. + var baseModel = new NonAbstractModelProvider(_baseModel); + Assert.IsFalse(baseModel.DeclarationModifiers.HasFlag(TypeSignatureModifiers.Abstract)); + + // The discriminated base description should still reference derived models. + Assert.IsNotNull(baseModel.XmlDocs.Summary); + StringAssert.Contains( + "Please note this is the abstract base class. The derived classes available for instantiation are: , , and .", + baseModel.XmlDocs.Summary!.ToDisplayString()); + } + + private class NonAbstractModelProvider : ModelProvider + { + public NonAbstractModelProvider(InputModelType inputModel) : base(inputModel) + { + } + + protected override TypeSignatureModifiers BuildDeclarationModifiers() + { + return base.BuildDeclarationModifiers() & ~TypeSignatureModifiers.Abstract; + } + } + [Test] public void DiscriminatorPropertyShouldBeInternal() { From 0345e35180f06db1f4037956c62906dacc007b71 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 27 Jul 2026 15:37:22 +0000 Subject: [PATCH 3/6] [http-client-csharp] Address review: use private field, drop comments and bespoke test provider Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- .../src/Providers/ModelProvider.cs | 16 +++-------- .../ModelProviders/DiscriminatorTests.cs | 27 ------------------- 2 files changed, 4 insertions(+), 39 deletions(-) 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 87fa1cc7158..c53fa7f4db8 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 @@ -27,10 +27,7 @@ protected override FormattableString BuildDescription() { var description = DocHelpers.GetFormattableDescription(_inputModel.Summary, _inputModel.Doc) ?? $"The {Name}."; - // Whether the model is a discriminated base type is not tied to whether it is declared abstract. - // Downstream emitters may choose not to model discriminated base types as abstract, but the - // description referencing the available derived classes should still be built. - if (IsDiscriminatedBaseType) + if (_isDiscriminatedBaseType) { _derivedModels = BuildDerivedModels(); var publicDerivedModels = _derivedModels.Where(m => m.DeclarationModifiers.HasFlag(TypeSignatureModifiers.Public)).ToList(); @@ -69,19 +66,14 @@ protected override FormattableString BuildDescription() private ConstructorProvider? _fullConstructor; internal PropertyProvider? DiscriminatorProperty { get; private set; } - /// - /// Gets whether this model is a discriminated base type, i.e. it defines a discriminator property - /// but does not itself carry a discriminator value. This is independent of whether the type is - /// declared abstract, since downstream emitters may choose not to model discriminated base types - /// as abstract. - /// - private bool IsDiscriminatedBaseType => _inputModel.DiscriminatorProperty is not null && _inputModel.DiscriminatorValue is null; + private readonly bool _isDiscriminatedBaseType; private ValueExpression DiscriminatorLiteral => Literal(_inputModel.DiscriminatorValue ?? ""); public ModelProvider(InputModelType inputModel) : base(inputModel) { _inputModel = inputModel; + _isDiscriminatedBaseType = inputModel.DiscriminatorProperty is not null && inputModel.DiscriminatorValue is null; _isMultiLevelDiscriminator = ComputeIsMultiLevelDiscriminator(); _useObjectAdditionalProperties = new Lazy(ShouldUseObjectAdditionalProperties); } @@ -336,7 +328,7 @@ protected override TypeSignatureModifiers BuildDeclarationModifiers() declarationModifiers |= TypeSignatureModifiers.Internal; } - if (IsDiscriminatedBaseType) + if (_isDiscriminatedBaseType) { declarationModifiers |= TypeSignatureModifiers.Abstract; } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/DiscriminatorTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/DiscriminatorTests.cs index 4077bfb06f3..a3666593986 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/DiscriminatorTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/DiscriminatorTests.cs @@ -120,33 +120,6 @@ public void BaseShouldBeAbstract(InputModelType inputModel, string expectedSumma Assert.AreEqual(expectedSummary, baseModel.XmlDocs.Summary!.ToDisplayString()); } - [Test] - public void DiscriminatedBaseDescriptionIsBuiltEvenWhenNotAbstract() - { - MockHelpers.LoadMockGenerator(); - // Simulate a downstream emitter that does not model the discriminated base type as abstract. - var baseModel = new NonAbstractModelProvider(_baseModel); - Assert.IsFalse(baseModel.DeclarationModifiers.HasFlag(TypeSignatureModifiers.Abstract)); - - // The discriminated base description should still reference derived models. - Assert.IsNotNull(baseModel.XmlDocs.Summary); - StringAssert.Contains( - "Please note this is the abstract base class. The derived classes available for instantiation are: , , and .", - baseModel.XmlDocs.Summary!.ToDisplayString()); - } - - private class NonAbstractModelProvider : ModelProvider - { - public NonAbstractModelProvider(InputModelType inputModel) : base(inputModel) - { - } - - protected override TypeSignatureModifiers BuildDeclarationModifiers() - { - return base.BuildDeclarationModifiers() & ~TypeSignatureModifiers.Abstract; - } - } - [Test] public void DiscriminatorPropertyShouldBeInternal() { From ffa8d5f6352bd23a8671330c5a797dff79d4c512 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 27 Jul 2026 15:42:19 +0000 Subject: [PATCH 4/6] [http-client-csharp] Add back DiscriminatedBaseDescriptionIsBuiltEvenWhenNotAbstract test Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- .../ModelProviders/DiscriminatorTests.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/DiscriminatorTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/DiscriminatorTests.cs index a3666593986..4077bfb06f3 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/DiscriminatorTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/DiscriminatorTests.cs @@ -120,6 +120,33 @@ public void BaseShouldBeAbstract(InputModelType inputModel, string expectedSumma Assert.AreEqual(expectedSummary, baseModel.XmlDocs.Summary!.ToDisplayString()); } + [Test] + public void DiscriminatedBaseDescriptionIsBuiltEvenWhenNotAbstract() + { + MockHelpers.LoadMockGenerator(); + // Simulate a downstream emitter that does not model the discriminated base type as abstract. + var baseModel = new NonAbstractModelProvider(_baseModel); + Assert.IsFalse(baseModel.DeclarationModifiers.HasFlag(TypeSignatureModifiers.Abstract)); + + // The discriminated base description should still reference derived models. + Assert.IsNotNull(baseModel.XmlDocs.Summary); + StringAssert.Contains( + "Please note this is the abstract base class. The derived classes available for instantiation are: , , and .", + baseModel.XmlDocs.Summary!.ToDisplayString()); + } + + private class NonAbstractModelProvider : ModelProvider + { + public NonAbstractModelProvider(InputModelType inputModel) : base(inputModel) + { + } + + protected override TypeSignatureModifiers BuildDeclarationModifiers() + { + return base.BuildDeclarationModifiers() & ~TypeSignatureModifiers.Abstract; + } + } + [Test] public void DiscriminatorPropertyShouldBeInternal() { From 1d1e2dcc31a29dbac08c33ee09af026efc4fe05f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 27 Jul 2026 17:43:21 +0000 Subject: [PATCH 5/6] [http-client-csharp] Use non-abstract base description when discriminated base type is not abstract Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- .../src/Providers/ModelProvider.cs | 5 +++-- .../test/Providers/ModelProviders/DiscriminatorTests.cs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) 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 c53fa7f4db8..5dae59bf948 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 @@ -31,8 +31,9 @@ protected override FormattableString BuildDescription() { _derivedModels = BuildDerivedModels(); var publicDerivedModels = _derivedModels.Where(m => m.DeclarationModifiers.HasFlag(TypeSignatureModifiers.Public)).ToList(); - var derivedClassesDescription = - "Please note this is the abstract base class. The derived classes available for instantiation are: "; + var derivedClassesDescription = DeclarationModifiers.HasFlag(TypeSignatureModifiers.Abstract) + ? "Please note this is the abstract base class. The derived classes available for instantiation are: " + : "Please note this is the base class. The derived classes available for instantiation are: "; bool addComma = publicDerivedModels.Count > 2; for (int i = 0; i < publicDerivedModels.Count; i++) { diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/DiscriminatorTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/DiscriminatorTests.cs index 4077bfb06f3..1e5941363e3 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/DiscriminatorTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/DiscriminatorTests.cs @@ -131,7 +131,7 @@ public void DiscriminatedBaseDescriptionIsBuiltEvenWhenNotAbstract() // The discriminated base description should still reference derived models. Assert.IsNotNull(baseModel.XmlDocs.Summary); StringAssert.Contains( - "Please note this is the abstract base class. The derived classes available for instantiation are: , , and .", + "Please note this is the base class. The derived classes available for instantiation are: , , and .", baseModel.XmlDocs.Summary!.ToDisplayString()); } From eb5834fdb797011fcfd62b7120c0880e92bd006f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 27 Jul 2026 17:47:42 +0000 Subject: [PATCH 6/6] [http-client-csharp] Parameterize discriminated base description test for abstract and non-abstract Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- .../ModelProviders/DiscriminatorTests.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/DiscriminatorTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/DiscriminatorTests.cs index 1e5941363e3..5e5e6cb31ec 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/DiscriminatorTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/DiscriminatorTests.cs @@ -120,19 +120,20 @@ public void BaseShouldBeAbstract(InputModelType inputModel, string expectedSumma Assert.AreEqual(expectedSummary, baseModel.XmlDocs.Summary!.ToDisplayString()); } - [Test] - public void DiscriminatedBaseDescriptionIsBuiltEvenWhenNotAbstract() + [TestCase(true, "Please note this is the abstract base class. The derived classes available for instantiation are: , , and .")] + [TestCase(false, "Please note this is the base class. The derived classes available for instantiation are: , , and .")] + public void DiscriminatedBaseDescriptionReflectsAbstractness(bool isAbstract, string expectedDescription) { MockHelpers.LoadMockGenerator(); - // Simulate a downstream emitter that does not model the discriminated base type as abstract. - var baseModel = new NonAbstractModelProvider(_baseModel); - Assert.IsFalse(baseModel.DeclarationModifiers.HasFlag(TypeSignatureModifiers.Abstract)); + // When not abstract, simulate a downstream emitter that does not model the discriminated base type as abstract. + var baseModel = isAbstract + ? CodeModelGenerator.Instance.TypeFactory.CreateModel(_baseModel)! + : new NonAbstractModelProvider(_baseModel); + Assert.AreEqual(isAbstract, baseModel.DeclarationModifiers.HasFlag(TypeSignatureModifiers.Abstract)); - // The discriminated base description should still reference derived models. + // The discriminated base description should reference derived models regardless of abstractness. Assert.IsNotNull(baseModel.XmlDocs.Summary); - StringAssert.Contains( - "Please note this is the base class. The derived classes available for instantiation are: , , and .", - baseModel.XmlDocs.Summary!.ToDisplayString()); + StringAssert.Contains(expectedDescription, baseModel.XmlDocs.Summary!.ToDisplayString()); } private class NonAbstractModelProvider : ModelProvider