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