Skip to content

Commit 0ee3f54

Browse files
Skip internal/private custom constructors for settings and schema gen… (#10253)
…eration Only public custom constructors should be considered when discovering additional client parameters for settings properties, BindCore binding, and ConfigurationSchema.json generation. Internal/private constructors contain infrastructure parameters (HttpPipeline, AzureKeyCredential, sync tokens, etc.) that are not suitable for configuration binding. Fixes build failures in Azure SDK caused by generating bindings for abstract types and internal infrastructure types. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent b8a6abc commit 0ee3f54

4 files changed

Lines changed: 75 additions & 4 deletions

File tree

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ConfigurationSchemaGenerator.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,8 @@ private static JsonObject BuildClientEntry(ClientProvider client, string options
117117

118118
// Add custom constructor parameters from custom code (e.g., hand-written constructors
119119
// added via partial classes) that are not already covered by generated parameters.
120-
// Skip credential types, endpoint types (Uri), and options types as they are handled separately.
120+
// Only consider public constructors — internal/private constructors contain infrastructure
121+
// parameters (pipeline, key credentials, etc.) that are not suitable for configuration.
121122
var customConstructors = client.CustomCodeView?.Constructors;
122123
if (customConstructors != null)
123124
{
@@ -126,6 +127,11 @@ private static JsonObject BuildClientEntry(ClientProvider client, string options
126127
knownProps.Add("Options");
127128
foreach (var ctor in customConstructors)
128129
{
130+
if (!ctor.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Public))
131+
{
132+
continue;
133+
}
134+
129135
foreach (var param in ctor.Signature.Parameters)
130136
{
131137
var propName = param.Name.ToIdentifierName();

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientSettingsProvider.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ protected override PropertyProvider[] BuildProperties()
107107

108108
// Include custom constructor parameters from custom code (e.g., hand-written constructors
109109
// added via partial classes) that are not already covered by generated parameters.
110-
// Skip credential types, endpoint types (Uri), and options types as they are handled separately.
110+
// Only consider public constructors — internal/private constructors contain infrastructure
111+
// parameters (pipeline, key credentials, etc.) that are not suitable for configuration binding.
111112
var customConstructors = _clientProvider.CustomCodeView?.Constructors;
112113
if (customConstructors != null)
113114
{
@@ -116,6 +117,11 @@ protected override PropertyProvider[] BuildProperties()
116117
knownProps.Add("Options");
117118
foreach (var ctor in customConstructors)
118119
{
120+
if (!ctor.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Public))
121+
{
122+
continue;
123+
}
124+
119125
foreach (var param in ctor.Signature.Parameters)
120126
{
121127
var propName = param.Name.ToIdentifierName();
@@ -165,8 +171,9 @@ protected override MethodProvider[] BuildMethods()
165171
AppendBindingForProperty(body, sectionParam, propName, param.Name.ToVariableName(), param.Type);
166172
}
167173

168-
// Bind custom constructor parameters from custom code
169-
// Skip credential types, endpoint types (Uri), and options types as they are handled separately.
174+
// Bind custom constructor parameters from custom code.
175+
// Only consider public constructors — internal/private constructors contain infrastructure
176+
// parameters (pipeline, key credentials, etc.) that are not suitable for configuration binding.
170177
var customConstructors = _clientProvider.CustomCodeView?.Constructors;
171178
if (customConstructors != null)
172179
{
@@ -183,6 +190,11 @@ protected override MethodProvider[] BuildMethods()
183190
knownProps.Add("Options");
184191
foreach (var ctor in customConstructors)
185192
{
193+
if (!ctor.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Public))
194+
{
195+
continue;
196+
}
197+
186198
foreach (var param in ctor.Signature.Parameters)
187199
{
188200
var propName = param.Name.ToIdentifierName();

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/ConfigurationSchemaGeneratorTests.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -919,6 +919,49 @@ await MockHelpers.LoadMockGeneratorAsync(
919919
Assert.AreEqual("string", connectionStringProp!["type"]?.GetValue<string>());
920920
}
921921

922+
[Test]
923+
public async Task Generate_ExcludesInternalConstructorParameters()
924+
{
925+
// Reset singleton before loading async mock
926+
var singletonField = typeof(ClientOptionsProvider).GetField("_singletonInstance", BindingFlags.Static | BindingFlags.NonPublic);
927+
singletonField?.SetValue(null, null);
928+
929+
// Load mock generator with a compilation that contains a custom partial class
930+
// for TestService with a public constructor (connectionString) and an internal
931+
// constructor (internalParam, anotherInternalParam).
932+
await MockHelpers.LoadMockGeneratorAsync(
933+
compilation: async () => await Helpers.GetCompilationFromDirectoryAsync());
934+
935+
var client = InputFactory.Client("TestService");
936+
var clientProvider = new ClientProvider(client);
937+
938+
Assert.IsNotNull(clientProvider.CustomCodeView,
939+
"CustomCodeView should be available from the compilation");
940+
941+
var output = new TestOutputLibrary([clientProvider]);
942+
var result = ConfigurationSchemaGenerator.Generate(output);
943+
944+
Assert.IsNotNull(result);
945+
var doc = JsonNode.Parse(result!)!;
946+
947+
var clientEntry = doc["properties"]?["Clients"]?["properties"]?["TestService"];
948+
Assert.IsNotNull(clientEntry, "TestService client entry should exist");
949+
950+
// Public constructor param should be included
951+
var connectionStringProp = clientEntry!["properties"]?["ConnectionString"];
952+
Assert.IsNotNull(connectionStringProp,
953+
"Public constructor parameter 'connectionString' should appear in schema");
954+
955+
// Internal constructor params should NOT be included
956+
var internalParamProp = clientEntry["properties"]?["InternalParam"];
957+
Assert.IsNull(internalParamProp,
958+
"Internal constructor parameter 'internalParam' should NOT appear in schema");
959+
960+
var anotherInternalProp = clientEntry["properties"]?["AnotherInternalParam"];
961+
Assert.IsNull(anotherInternalProp,
962+
"Internal constructor parameter 'anotherInternalParam' should NOT appear in schema");
963+
}
964+
922965
/// <summary>
923966
/// Test output library that wraps provided TypeProviders.
924967
/// </summary>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#nullable disable
2+
3+
namespace Sample
4+
{
5+
public partial class TestService
6+
{
7+
public TestService(string connectionString) { }
8+
internal TestService(string internalParam, int anotherInternalParam) { }
9+
}
10+
}

0 commit comments

Comments
 (0)