From 00be26f567fe97bb552c75d302df7f150941d645 Mon Sep 17 00:00:00 2001 From: jolov Date: Thu, 16 Jul 2026 16:17:21 -0700 Subject: [PATCH 1/5] fix(csharp): preserve internal helper types Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../BinaryContentHelperDefinition.cs | 6 +- .../CancellationTokenExtensionsDefinition.cs | 7 +- .../ClientPipelineExtensionsDefinition.cs | 7 +- .../Providers/ClientUriBuilderDefinition.cs | 7 +- .../FixedEnumSerializationProvider.cs | 6 +- .../ModelSerializationExtensionsDefinition.cs | 7 +- ...ultiPartFormDataBinaryContentDefinition.cs | 4 +- .../MultipartFormDataHelperDefinition.cs | 6 +- ...elineRequestHeadersExtensionsDefinition.cs | 7 +- .../src/Providers/TypeFormattersDefinition.cs | 7 +- .../Utf8JsonBinaryContentDefinition.cs | 6 +- .../src/ScmCodeModelGenerator.cs | 1 + .../src/CodeModelGenerator.cs | 32 ++++ .../PostProcessing/GeneratedCodeWorkspace.cs | 10 +- .../src/Providers/ArgumentDefinition.cs | 7 +- .../ChangeTrackingDictionaryDefinition.cs | 7 +- .../Providers/ChangeTrackingListDefinition.cs | 7 +- .../src/Providers/InternalHelperProvider.cs | 21 +++ .../src/Providers/OptionalDefinition.cs | 7 +- .../test/Providers/TypeProviderTests.cs | 21 +++ .../Local.Tests/UnreferencedTypeTests.cs | 8 +- .../Generated/Internal/BinaryContentHelper.cs | 175 ++++++++++++++++++ .../PipelineRequestHeadersExtensions.cs | 48 +++++ .../Internal/Utf8JsonBinaryContent.cs | 61 ++++++ 24 files changed, 401 insertions(+), 74 deletions(-) create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/InternalHelperProvider.cs create mode 100644 packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Internal/BinaryContentHelper.cs create mode 100644 packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Internal/PipelineRequestHeadersExtensions.cs create mode 100644 packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Internal/Utf8JsonBinaryContent.cs diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/BinaryContentHelperDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/BinaryContentHelperDefinition.cs index a2ee813e298..be8f2b996d0 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/BinaryContentHelperDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/BinaryContentHelperDefinition.cs @@ -17,7 +17,7 @@ namespace Microsoft.TypeSpec.Generator.ClientModel.Providers { - public class BinaryContentHelperDefinition : TypeProvider + public class BinaryContentHelperDefinition : InternalHelperProvider { private const string _fromEnumerableName = "FromEnumerable"; private const string _fromDictionaryName = "FromDictionary"; @@ -30,8 +30,8 @@ public class BinaryContentHelperDefinition : TypeProvider protected override string BuildName() => "BinaryContentHelper"; - protected override TypeSignatureModifiers BuildDeclarationModifiers() - => TypeSignatureModifiers.Internal | TypeSignatureModifiers.Static | TypeSignatureModifiers.Partial | TypeSignatureModifiers.Class; + protected override TypeSignatureModifiers BuildTypeModifiers() + => TypeSignatureModifiers.Static | TypeSignatureModifiers.Partial | TypeSignatureModifiers.Class; protected override string BuildRelativeFilePath() => Path.Combine("src", "Generated", "Internal", $"{Name}.cs"); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/CancellationTokenExtensionsDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/CancellationTokenExtensionsDefinition.cs index 6168a29ba2f..77291404702 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/CancellationTokenExtensionsDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/CancellationTokenExtensionsDefinition.cs @@ -12,7 +12,7 @@ namespace Microsoft.TypeSpec.Generator.ClientModel.Providers { - internal class CancellationTokenExtensionsDefinition : TypeProvider + internal class CancellationTokenExtensionsDefinition : InternalHelperProvider { private ParameterProvider _cancellationTokenParam; @@ -21,10 +21,7 @@ public CancellationTokenExtensionsDefinition() _cancellationTokenParam = new ParameterProvider("cancellationToken", FormattableStringHelpers.Empty, typeof(CancellationToken)); } - protected override TypeSignatureModifiers BuildDeclarationModifiers() - { - return TypeSignatureModifiers.Internal | TypeSignatureModifiers.Static; - } + protected override TypeSignatureModifiers BuildTypeModifiers() => TypeSignatureModifiers.Static; protected override string BuildRelativeFilePath() => Path.Combine("src", "Generated", "Internal", $"{Name}.cs"); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientPipelineExtensionsDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientPipelineExtensionsDefinition.cs index 02864633aca..386cabe2d4f 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientPipelineExtensionsDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientPipelineExtensionsDefinition.cs @@ -13,7 +13,7 @@ namespace Microsoft.TypeSpec.Generator.ClientModel.Providers { - internal class ClientPipelineExtensionsDefinition : TypeProvider + internal class ClientPipelineExtensionsDefinition : InternalHelperProvider { private ParameterProvider _pipelineParam; private ParameterProvider _messageParam; @@ -32,10 +32,7 @@ public ClientPipelineExtensionsDefinition() _options = _requestOptionsParam.ToApi(); } - protected override TypeSignatureModifiers BuildDeclarationModifiers() - { - return TypeSignatureModifiers.Internal | TypeSignatureModifiers.Static; - } + protected override TypeSignatureModifiers BuildTypeModifiers() => TypeSignatureModifiers.Static; protected override string BuildRelativeFilePath() => Path.Combine("src", "Generated", "Internal", $"{Name}.cs"); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientUriBuilderDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientUriBuilderDefinition.cs index 92e0cf23a67..2f51d5b6941 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientUriBuilderDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientUriBuilderDefinition.cs @@ -15,7 +15,7 @@ namespace Microsoft.TypeSpec.Generator.ClientModel.Providers { - internal sealed class ClientUriBuilderDefinition : TypeProvider + internal sealed class ClientUriBuilderDefinition : InternalHelperProvider { private const string ToUriMethodName = "ToUri"; private const string ResetMethodName = "Reset"; @@ -63,11 +63,6 @@ public ClientUriBuilderDefinition() _pathLengthField = new(FieldModifiers.Private, typeof(int), "_pathLength", this); } - protected override TypeSignatureModifiers BuildDeclarationModifiers() - { - return TypeSignatureModifiers.Internal; - } - protected override string BuildRelativeFilePath() => Path.Combine("src", "Generated", "Internal", $"{Name}.cs"); protected override string BuildName() => "ClientUriBuilder"; diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/FixedEnumSerializationProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/FixedEnumSerializationProvider.cs index f5224038ede..f5b1f96665a 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/FixedEnumSerializationProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/FixedEnumSerializationProvider.cs @@ -18,7 +18,7 @@ namespace Microsoft.TypeSpec.Generator.ClientModel.Providers /// /// This defines a class with extension methods for enums to convert an enum to its underlying value, or from its underlying value to an instance of the enum /// - public class FixedEnumSerializationProvider : TypeProvider + public class FixedEnumSerializationProvider : InternalHelperProvider { private readonly InputEnumType _enumType; private TypeProvider _enumProvider; @@ -30,8 +30,8 @@ public FixedEnumSerializationProvider(InputEnumType enumType, TypeProvider enumP } protected override string BuildNamespace() => _enumProvider.Type.Namespace; - protected override TypeSignatureModifiers BuildDeclarationModifiers() - => TypeSignatureModifiers.Internal | TypeSignatureModifiers.Static | TypeSignatureModifiers.Partial | TypeSignatureModifiers.Class; + protected override TypeSignatureModifiers BuildTypeModifiers() + => TypeSignatureModifiers.Static | TypeSignatureModifiers.Partial | TypeSignatureModifiers.Class; protected override string BuildRelativeFilePath() { diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ModelSerializationExtensionsDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ModelSerializationExtensionsDefinition.cs index f6f6cebbd49..b33202076d9 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ModelSerializationExtensionsDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ModelSerializationExtensionsDefinition.cs @@ -27,7 +27,7 @@ #pragma warning disable SCME0004 // FileBinaryContent is evaluation-only. namespace Microsoft.TypeSpec.Generator.ClientModel.Providers { - public sealed partial class ModelSerializationExtensionsDefinition : TypeProvider + public sealed partial class ModelSerializationExtensionsDefinition : InternalHelperProvider { public const string WireOptionsFieldName = "WireOptions"; public const string JsonDocumentOptionsFieldName = "JsonDocumentOptions"; @@ -96,10 +96,7 @@ public ModelSerializationExtensionsDefinition() enclosingType: this); } - protected override TypeSignatureModifiers BuildDeclarationModifiers() - { - return TypeSignatureModifiers.Internal | TypeSignatureModifiers.Static; - } + protected override TypeSignatureModifiers BuildTypeModifiers() => TypeSignatureModifiers.Static; internal FieldProvider WireOptionsField { get; } private readonly FieldProvider _jsonDocumentOptionsField; diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MultiPartFormDataBinaryContentDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MultiPartFormDataBinaryContentDefinition.cs index 3ba1fad08c9..4170e4b5ea8 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MultiPartFormDataBinaryContentDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MultiPartFormDataBinaryContentDefinition.cs @@ -21,7 +21,7 @@ namespace Microsoft.TypeSpec.Generator.ClientModel.Providers { - public class MultiPartFormDataBinaryContentDefinition : TypeProvider + public class MultiPartFormDataBinaryContentDefinition : InternalHelperProvider { private readonly ScopedApi _multipartContentExpression; private readonly FieldProvider _multipartContentField; @@ -77,7 +77,7 @@ public MultiPartFormDataBinaryContentDefinition() protected override string BuildName() => $"MultiPartFormData{_requestContentType.Name}"; - protected override TypeSignatureModifiers BuildDeclarationModifiers() => TypeSignatureModifiers.Class | TypeSignatureModifiers.Internal; + protected override TypeSignatureModifiers BuildTypeModifiers() => TypeSignatureModifiers.Class; protected override CSharpType BuildBaseType() => _requestContentType; diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MultipartFormDataHelperDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MultipartFormDataHelperDefinition.cs index 14f332d2cbc..bda9953623a 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MultipartFormDataHelperDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MultipartFormDataHelperDefinition.cs @@ -14,7 +14,7 @@ namespace Microsoft.TypeSpec.Generator.ClientModel.Providers { - internal class MultipartFormDataHelperDefinition : TypeProvider + internal class MultipartFormDataHelperDefinition : InternalHelperProvider { private const string _fromEnumerableName = "FromEnumerable"; private const string _fromDictionaryName = "FromDictionary"; @@ -24,8 +24,8 @@ internal class MultipartFormDataHelperDefinition : TypeProvider protected override string BuildName() => "MultipartFormDataHelper"; - protected override TypeSignatureModifiers BuildDeclarationModifiers() - => TypeSignatureModifiers.Internal | TypeSignatureModifiers.Static | TypeSignatureModifiers.Partial | TypeSignatureModifiers.Class; + protected override TypeSignatureModifiers BuildTypeModifiers() + => TypeSignatureModifiers.Static | TypeSignatureModifiers.Partial | TypeSignatureModifiers.Class; protected override string BuildRelativeFilePath() => Path.Combine("src", "Generated", "Internal", $"{Name}.cs"); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/PipelineRequestHeadersExtensionsDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/PipelineRequestHeadersExtensionsDefinition.cs index 288b7f1af24..6e28cf95f16 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/PipelineRequestHeadersExtensionsDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/PipelineRequestHeadersExtensionsDefinition.cs @@ -15,7 +15,7 @@ namespace Microsoft.TypeSpec.Generator.ClientModel.Providers { - internal class PipelineRequestHeadersExtensionsDefinition : TypeProvider + internal class PipelineRequestHeadersExtensionsDefinition : InternalHelperProvider { private const string _setDelimited = "SetDelimited"; private const string _addWithPrefix = "Add"; @@ -25,10 +25,7 @@ public PipelineRequestHeadersExtensionsDefinition() _pipelineRequestHeadersParam = new ParameterProvider("headers", FormattableStringHelpers.Empty, typeof(PipelineRequestHeaders)); } private readonly CSharpType _t = typeof(IEnumerable<>).GetGenericArguments()[0]; - protected override TypeSignatureModifiers BuildDeclarationModifiers() - { - return TypeSignatureModifiers.Internal | TypeSignatureModifiers.Static; - } + protected override TypeSignatureModifiers BuildTypeModifiers() => TypeSignatureModifiers.Static; protected override string BuildRelativeFilePath() => Path.Combine("src", "Generated", "Internal", $"{Name}.cs"); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/TypeFormattersDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/TypeFormattersDefinition.cs index 4e0509bc78e..b51e10cdea2 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/TypeFormattersDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/TypeFormattersDefinition.cs @@ -16,7 +16,7 @@ namespace Microsoft.TypeSpec.Generator.ClientModel.Providers { - internal sealed class TypeFormattersDefinition : TypeProvider + internal sealed class TypeFormattersDefinition : InternalHelperProvider { private readonly ValueExpression _invariantCultureExpression = new MemberExpression(typeof(CultureInfo), nameof(CultureInfo.InvariantCulture)); private const string ToStringMethodName = "ToString"; @@ -33,10 +33,7 @@ internal TypeFormattersDefinition() _defaultNumberFormatField = new(FieldModifiers.Public | FieldModifiers.Const, typeof(string), "DefaultNumberFormat", this, initializationValue: Literal("G")); } - protected override TypeSignatureModifiers BuildDeclarationModifiers() - { - return TypeSignatureModifiers.Internal | TypeSignatureModifiers.Static; - } + protected override TypeSignatureModifiers BuildTypeModifiers() => TypeSignatureModifiers.Static; protected override string BuildRelativeFilePath() => Path.Combine("src", "Generated", "Internal", $"{Name}.cs"); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Utf8JsonBinaryContentDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Utf8JsonBinaryContentDefinition.cs index d470b06b8d6..84536ca9190 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Utf8JsonBinaryContentDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Utf8JsonBinaryContentDefinition.cs @@ -16,7 +16,7 @@ namespace Microsoft.TypeSpec.Generator.ClientModel.Providers { - public class Utf8JsonBinaryContentDefinition : TypeProvider + public class Utf8JsonBinaryContentDefinition : InternalHelperProvider { private const string _jsonWriterName = "JsonWriter"; @@ -49,8 +49,8 @@ public Utf8JsonBinaryContentDefinition() protected override string BuildName() => $"Utf8Json{BuildBaseType().Name}"; - protected override TypeSignatureModifiers BuildDeclarationModifiers() - => TypeSignatureModifiers.Internal | TypeSignatureModifiers.Partial | TypeSignatureModifiers.Class; + protected override TypeSignatureModifiers BuildTypeModifiers() + => TypeSignatureModifiers.Partial | TypeSignatureModifiers.Class; protected override string BuildRelativeFilePath() => Path.Combine("src", "Generated", "Internal", $"{Name}.cs"); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmCodeModelGenerator.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmCodeModelGenerator.cs index ba4c5ffe6a1..3caeb42520c 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmCodeModelGenerator.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmCodeModelGenerator.cs @@ -50,6 +50,7 @@ protected override void Configure() AddMetadataReference(MetadataReference.CreateFromFile(typeof(BinaryData).Assembly.Location)); AddMetadataReference(MetadataReference.CreateFromFile(typeof(JsonSerializer).Assembly.Location)); AddTypeToKeep(ModelReaderWriterContextDefinition.s_name, isRoot: false); + AddTypeToKeep(SerializationFormatDefinition); } public override async Task WriteAdditionalFiles(string outputPath) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/CodeModelGenerator.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/CodeModelGenerator.cs index e8732f15ffd..0aaf0f983bf 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/CodeModelGenerator.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/CodeModelGenerator.cs @@ -268,6 +268,38 @@ public void AddTypeToKeep(TypeProvider type, bool isRoot = true) } } + internal void AddInternalHelperTypesToKeep(IEnumerable types) + { + var visited = new HashSet(); + foreach (var type in types) + { + AddInternalHelperTypesToKeep(type, visited); + } + } + + private void AddInternalHelperTypesToKeep(TypeProvider type, HashSet visited) + { + if (!visited.Add(type)) + { + return; + } + + if (type is InternalHelperProvider) + { + AddTypeToKeep(type); + } + + foreach (var serializationProvider in type.SerializationProviders) + { + AddInternalHelperTypesToKeep(serializationProvider, visited); + } + + foreach (var nestedType in type.NestedTypes) + { + AddInternalHelperTypesToKeep(nestedType, visited); + } + } + /// /// Writes additional output files (e.g. configuration schemas) after the main code generation is complete. /// Override this method to generate non-C# output files. diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/PostProcessing/GeneratedCodeWorkspace.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/PostProcessing/GeneratedCodeWorkspace.cs index c36686f637f..bd88c9c48d0 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/PostProcessing/GeneratedCodeWorkspace.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/PostProcessing/GeneratedCodeWorkspace.cs @@ -266,10 +266,14 @@ internal static Project AddDirectory(Project project, string directory, Func public async Task PostProcessAsync() { - var modelFactory = CodeModelGenerator.Instance.OutputLibrary.ModelFactory.Value; - var nonRootTypes = CodeModelGenerator.Instance.NonRootTypes; + var generator = CodeModelGenerator.Instance; + var outputLibrary = generator.OutputLibrary; + generator.AddInternalHelperTypesToKeep(outputLibrary.TypeProviders); + + var modelFactory = outputLibrary.ModelFactory.Value; + var nonRootTypes = generator.NonRootTypes; var postProcessor = new PostProcessor( - [.. CodeModelGenerator.Instance.TypeFactory.UnionVariantTypesToKeep, .. CodeModelGenerator.Instance.AdditionalRootTypes], + [.. generator.TypeFactory.UnionVariantTypesToKeep, .. generator.AdditionalRootTypes], modelFactoryFullName: modelFactory.Type.FullyQualifiedName, additionalNonRootTypeNames: nonRootTypes); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ArgumentDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ArgumentDefinition.cs index 6c7e62bd102..157ce689474 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ArgumentDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ArgumentDefinition.cs @@ -15,7 +15,7 @@ namespace Microsoft.TypeSpec.Generator.Providers { - internal class ArgumentDefinition : TypeProvider + internal class ArgumentDefinition : InternalHelperProvider { private class Template { } @@ -34,10 +34,7 @@ public ArgumentDefinition() _nullableT = _t.WithNullable(true); } - protected override TypeSignatureModifiers BuildDeclarationModifiers() - { - return TypeSignatureModifiers.Internal | TypeSignatureModifiers.Static; - } + protected override TypeSignatureModifiers BuildTypeModifiers() => TypeSignatureModifiers.Static; private MethodSignature GetSignature( string name, diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ChangeTrackingDictionaryDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ChangeTrackingDictionaryDefinition.cs index 97e38db6102..ea6461b0a91 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ChangeTrackingDictionaryDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ChangeTrackingDictionaryDefinition.cs @@ -13,7 +13,7 @@ namespace Microsoft.TypeSpec.Generator.Providers { - internal sealed class ChangeTrackingDictionaryDefinition : TypeProvider + internal sealed class ChangeTrackingDictionaryDefinition : InternalHelperProvider { private class ChangeTrackingDictionaryTemplate { } private readonly CSharpType _tKey = typeof(ChangeTrackingDictionaryTemplate<,>).GetGenericArguments()[0]; @@ -48,11 +48,6 @@ public ChangeTrackingDictionaryDefinition() protected override WhereExpression BuildWhereClause() => Where.NotNull(_tKey); - protected override TypeSignatureModifiers BuildDeclarationModifiers() - { - return TypeSignatureModifiers.Internal; - } - protected override string BuildRelativeFilePath() => Path.Combine("src", "Generated", "Internal", $"{Name}.cs"); protected override string BuildName() => "ChangeTrackingDictionary"; diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ChangeTrackingListDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ChangeTrackingListDefinition.cs index 6cbe1ba6469..74bb6ca0e8b 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ChangeTrackingListDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ChangeTrackingListDefinition.cs @@ -13,7 +13,7 @@ namespace Microsoft.TypeSpec.Generator.Providers { - internal sealed class ChangeTrackingListDefinition : TypeProvider + internal sealed class ChangeTrackingListDefinition : InternalHelperProvider { private class ChangeTrackingListTemplate { } @@ -46,11 +46,6 @@ public ChangeTrackingListDefinition() EnsureList = new(This.Invoke(_ensureListSignature)); } - protected override TypeSignatureModifiers BuildDeclarationModifiers() - { - return TypeSignatureModifiers.Internal; - } - protected override string BuildRelativeFilePath() => Path.Combine("src", "Generated", "Internal", $"{Name}.cs"); protected override string BuildName() => "ChangeTrackingList"; diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/InternalHelperProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/InternalHelperProvider.cs new file mode 100644 index 00000000000..26fa9d61eb0 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/InternalHelperProvider.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.TypeSpec.Generator.Input; +using Microsoft.TypeSpec.Generator.Primitives; + +namespace Microsoft.TypeSpec.Generator.Providers +{ + /// + /// A base provider for generator-owned internal helper types that must not be removed during post-processing. + /// + public abstract class InternalHelperProvider : TypeProvider + { + protected InternalHelperProvider(InputType? inputType = default) : base(inputType) { } + + protected sealed override TypeSignatureModifiers BuildDeclarationModifiers() + => TypeSignatureModifiers.Internal | BuildTypeModifiers(); + + protected virtual TypeSignatureModifiers BuildTypeModifiers() => TypeSignatureModifiers.None; + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/OptionalDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/OptionalDefinition.cs index cfd71ebb0a6..0d47190ce28 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/OptionalDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/OptionalDefinition.cs @@ -11,7 +11,7 @@ namespace Microsoft.TypeSpec.Generator.Providers { - public class OptionalDefinition : TypeProvider + public class OptionalDefinition : InternalHelperProvider { private class ListTemplate { } @@ -29,10 +29,7 @@ public OptionalDefinition() _tValue = _genericChangeTrackingDictionary.Arguments[1]; } - protected override TypeSignatureModifiers BuildDeclarationModifiers() - { - return TypeSignatureModifiers.Internal | TypeSignatureModifiers.Static; - } + protected override TypeSignatureModifiers BuildTypeModifiers() => TypeSignatureModifiers.Static; protected override string BuildRelativeFilePath() => Path.Combine("src", "Generated", "Internal", $"{Name}.cs"); 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 9ec0b21c32d..4f755c69361 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 @@ -552,6 +552,18 @@ public void TestSpecViewIsNotNull() Assert.IsInstanceOf(specView); } + [Test] + public void InternalHelperProviderIsKeptAsRoot() + { + var typeProvider = new TestInternalHelperProvider(); + CodeModelGenerator.Instance.AddInternalHelperTypesToKeep([typeProvider]); + + Assert.IsTrue(typeProvider.DeclarationModifiers.HasFlag(TypeSignatureModifiers.Internal)); + Assert.IsTrue(typeProvider.DeclarationModifiers.HasFlag(TypeSignatureModifiers.Static)); + Assert.IsTrue(CodeModelGenerator.Instance.AdditionalRootTypes.Contains(typeProvider.Type.FullyQualifiedName)); + Assert.IsFalse(CodeModelGenerator.Instance.NonRootTypes.Contains(typeProvider.Type.FullyQualifiedName)); + } + [Test] public void TestSpecViewDelegatesCorrectly() { @@ -617,5 +629,14 @@ public async Task CanonicalViewDedupesCustomOperators() !m.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Implicit) && !m.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Explicit))); } + + private sealed class TestInternalHelperProvider : InternalHelperProvider + { + protected override string BuildName() => nameof(TestInternalHelperProvider); + + protected override string BuildRelativeFilePath() => $"{Name}.cs"; + + protected override TypeSignatureModifiers BuildTypeModifiers() => TypeSignatureModifiers.Static; + } } } diff --git a/packages/http-client-csharp/generator/TestProjects/Local.Tests/UnreferencedTypeTests.cs b/packages/http-client-csharp/generator/TestProjects/Local.Tests/UnreferencedTypeTests.cs index 44eeaceec62..b664bf1d2c4 100644 --- a/packages/http-client-csharp/generator/TestProjects/Local.Tests/UnreferencedTypeTests.cs +++ b/packages/http-client-csharp/generator/TestProjects/Local.Tests/UnreferencedTypeTests.cs @@ -11,12 +11,12 @@ namespace TestProjects.Local.Tests public class UnreferencedTypeTests { [Test] - public void UnreferencedTypesAreRemoved() + public void InternalHelperTypesAreKept() { var types = Assembly.GetAssembly(typeof(SampleTypeSpecClient))!.GetTypes(); - Assert.IsFalse(types.Any(t => t.Name == "BinaryContentHelper")); - Assert.IsFalse(types.Any(t => t.Name == "PipelineRequestHeadersExtensions")); - Assert.IsFalse(types.Any(t => t.Name == "Utf8JsonBinaryContent")); + Assert.IsTrue(types.Any(t => t.Name == "BinaryContentHelper")); + Assert.IsTrue(types.Any(t => t.Name == "PipelineRequestHeadersExtensions")); + Assert.IsTrue(types.Any(t => t.Name == "Utf8JsonBinaryContent")); } } } diff --git a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Internal/BinaryContentHelper.cs b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Internal/BinaryContentHelper.cs new file mode 100644 index 00000000000..b76cf24aa37 --- /dev/null +++ b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Internal/BinaryContentHelper.cs @@ -0,0 +1,175 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ClientModel; +using System.Collections.Generic; +using System.IO; +using System.Text.Json; +using System.Xml; + +namespace SampleTypeSpec +{ + internal static partial class BinaryContentHelper + { + /// + public static BinaryContent FromEnumerable(IEnumerable enumerable) + where T : notnull + { + Utf8JsonBinaryContent content = new Utf8JsonBinaryContent(); + content.JsonWriter.WriteStartArray(); + foreach (var item in enumerable) + { + content.JsonWriter.WriteObjectValue(item, ModelSerializationExtensions.WireOptions); + } + content.JsonWriter.WriteEndArray(); + + return content; + } + + /// + public static BinaryContent FromEnumerable(IEnumerable enumerable) + { + Utf8JsonBinaryContent content = new Utf8JsonBinaryContent(); + content.JsonWriter.WriteStartArray(); + foreach (var item in enumerable) + { + if (item == null) + { + content.JsonWriter.WriteNullValue(); + } + else + { +#if NET6_0_OR_GREATER + content.JsonWriter.WriteRawValue(item); +#else + using (JsonDocument document = JsonDocument.Parse(item)) + { + JsonSerializer.Serialize(content.JsonWriter, document.RootElement); + } +#endif + } + } + content.JsonWriter.WriteEndArray(); + + return content; + } + + /// + public static BinaryContent FromEnumerable(ReadOnlySpan span) + where T : notnull + { + Utf8JsonBinaryContent content = new Utf8JsonBinaryContent(); + content.JsonWriter.WriteStartArray(); + int i = 0; + for (; i < span.Length; i++) + { + content.JsonWriter.WriteObjectValue(span[i], ModelSerializationExtensions.WireOptions); + } + content.JsonWriter.WriteEndArray(); + + return content; + } + + /// + public static BinaryContent FromDictionary(IDictionary dictionary) + where TValue : notnull + { + Utf8JsonBinaryContent content = new Utf8JsonBinaryContent(); + content.JsonWriter.WriteStartObject(); + foreach (var item in dictionary) + { + content.JsonWriter.WritePropertyName(item.Key); + content.JsonWriter.WriteObjectValue(item.Value, ModelSerializationExtensions.WireOptions); + } + content.JsonWriter.WriteEndObject(); + + return content; + } + + /// + public static BinaryContent FromDictionary(IDictionary dictionary) + { + Utf8JsonBinaryContent content = new Utf8JsonBinaryContent(); + content.JsonWriter.WriteStartObject(); + foreach (var item in dictionary) + { + content.JsonWriter.WritePropertyName(item.Key); + if (item.Value == null) + { + content.JsonWriter.WriteNullValue(); + } + else + { +#if NET6_0_OR_GREATER + content.JsonWriter.WriteRawValue(item.Value); +#else + using (JsonDocument document = JsonDocument.Parse(item.Value)) + { + JsonSerializer.Serialize(content.JsonWriter, document.RootElement); + } +#endif + } + } + content.JsonWriter.WriteEndObject(); + + return content; + } + + /// + public static BinaryContent FromObject(object value) + { + Utf8JsonBinaryContent content = new Utf8JsonBinaryContent(); + content.JsonWriter.WriteObjectValue(value, ModelSerializationExtensions.WireOptions); + return content; + } + + /// + public static BinaryContent FromObject(BinaryData value) + { + Utf8JsonBinaryContent content = new Utf8JsonBinaryContent(); +#if NET6_0_OR_GREATER + content.JsonWriter.WriteRawValue(value); +#else + using (JsonDocument document = JsonDocument.Parse(value)) + { + JsonSerializer.Serialize(content.JsonWriter, document.RootElement); + } +#endif + return content; + } + + /// + /// + /// + public static BinaryContent FromEnumerable(IEnumerable enumerable, string rootNameHint, string childNameHint) + where T : notnull + { + using (MemoryStream stream = new MemoryStream(256)) + { + using (XmlWriter writer = XmlWriter.Create(stream, ModelSerializationExtensions.XmlWriterSettings)) + { + writer.WriteStartElement(rootNameHint); + foreach (var item in enumerable) + { + writer.WriteObjectValue(item, ModelSerializationExtensions.WireOptions, childNameHint); + } + writer.WriteEndElement(); + } + + if (stream.Position > int.MaxValue) + { + return BinaryContent.Create(BinaryData.FromStream(stream)); + } + else + { + return BinaryContent.Create(new BinaryData(stream.GetBuffer().AsMemory(0, (int)stream.Position))); + } + } + } + } +} diff --git a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Internal/PipelineRequestHeadersExtensions.cs b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Internal/PipelineRequestHeadersExtensions.cs new file mode 100644 index 00000000000..69ddd4aee41 --- /dev/null +++ b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Internal/PipelineRequestHeadersExtensions.cs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Linq; + +namespace SampleTypeSpec +{ + internal static partial class PipelineRequestHeadersExtensions + { + /// + /// The name. + /// The value. + /// The delimiter. + public static void SetDelimited(this PipelineRequestHeaders headers, string name, IEnumerable value, string delimiter) + { + IEnumerable stringValues = value.Select(v => TypeFormatters.ConvertToString(v)); + headers.Set(name, string.Join(delimiter, stringValues)); + } + + /// + /// The name. + /// The value. + /// The delimiter. + /// The format. + public static void SetDelimited(this PipelineRequestHeaders headers, string name, IEnumerable value, string delimiter, SerializationFormat format) + { + IEnumerable stringValues = value.Select(v => TypeFormatters.ConvertToString(v, format)); + headers.Set(name, string.Join(delimiter, stringValues)); + } + + /// + /// The prefix to prepend to each header key. + /// The dictionary of headers to add. + public static void Add(this PipelineRequestHeaders headers, string prefix, IDictionary value) + { + foreach (var header in value) + { + headers.Add(prefix + header.Key, header.Value); + } + } + } +} diff --git a/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Internal/Utf8JsonBinaryContent.cs b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Internal/Utf8JsonBinaryContent.cs new file mode 100644 index 00000000000..f4586e305fc --- /dev/null +++ b/packages/http-client-csharp/generator/TestProjects/Local/Sample-TypeSpec/src/Generated/Internal/Utf8JsonBinaryContent.cs @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System.ClientModel; +using System.IO; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +namespace SampleTypeSpec +{ + internal partial class Utf8JsonBinaryContent : BinaryContent + { + private readonly MemoryStream _stream; + private readonly BinaryContent _content; + + public Utf8JsonBinaryContent() + { + _stream = new MemoryStream(); + _content = Create(_stream); + JsonWriter = new Utf8JsonWriter(_stream); + } + + /// Gets the JsonWriter. + public Utf8JsonWriter JsonWriter { get; } + + /// The stream containing the data to be written. + /// The cancellation token to use. + public override async Task WriteToAsync(Stream stream, CancellationToken cancellationToken = default) + { + await JsonWriter.FlushAsync().ConfigureAwait(false); + await _content.WriteToAsync(stream, cancellationToken).ConfigureAwait(false); + } + + /// The stream containing the data to be written. + /// The cancellation token to use. + public override void WriteTo(Stream stream, CancellationToken cancellationToken = default) + { + JsonWriter.Flush(); + _content.WriteTo(stream, cancellationToken); + } + + /// + public override bool TryComputeLength(out long length) + { + length = JsonWriter.BytesCommitted + JsonWriter.BytesPending; + return true; + } + + public override void Dispose() + { + JsonWriter.Dispose(); + _content.Dispose(); + _stream.Dispose(); + } + } +} From b8c0fd664bd7e824ea6d5ec8ee6426f88dcec477 Mon Sep 17 00:00:00 2001 From: jolov Date: Thu, 16 Jul 2026 16:43:13 -0700 Subject: [PATCH 2/5] fix(csharp): register internal helpers as non-roots Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../BinaryContentHelperDefinition.cs | 3 -- .../CancellationTokenExtensionsDefinition.cs | 2 -- .../ClientPipelineExtensionsDefinition.cs | 2 -- .../Providers/ClientUriBuilderDefinition.cs | 2 ++ .../src/Providers/ErrorResultDefinition.cs | 2 +- .../FixedEnumSerializationProvider.cs | 3 -- .../ModelSerializationExtensionsDefinition.cs | 2 -- ...ultiPartFormDataBinaryContentDefinition.cs | 3 +- .../MultipartFormDataHelperDefinition.cs | 3 -- ...elineRequestHeadersExtensionsDefinition.cs | 2 -- .../src/Providers/TypeFormattersDefinition.cs | 2 -- .../Utf8JsonBinaryContentDefinition.cs | 4 +-- .../src/ScmCodeModelGenerator.cs | 6 ++-- .../src/CodeModelGenerator.cs | 32 ------------------- .../PostProcessing/GeneratedCodeWorkspace.cs | 10 ++---- .../src/PostProcessing/PostProcessor.cs | 17 ++++++++-- .../src/Providers/ArgumentDefinition.cs | 2 -- .../ChangeTrackingDictionaryDefinition.cs | 2 ++ .../Providers/ChangeTrackingListDefinition.cs | 2 ++ .../src/Providers/InternalHelperProvider.cs | 11 ++++--- .../src/Providers/OptionalDefinition.cs | 2 -- .../src/TypeFactory.cs | 8 +++-- .../test/Providers/TypeProviderTests.cs | 9 ++---- 23 files changed, 47 insertions(+), 84 deletions(-) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/BinaryContentHelperDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/BinaryContentHelperDefinition.cs index be8f2b996d0..3a7194ee1c4 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/BinaryContentHelperDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/BinaryContentHelperDefinition.cs @@ -30,9 +30,6 @@ public class BinaryContentHelperDefinition : InternalHelperProvider protected override string BuildName() => "BinaryContentHelper"; - protected override TypeSignatureModifiers BuildTypeModifiers() - => TypeSignatureModifiers.Static | TypeSignatureModifiers.Partial | TypeSignatureModifiers.Class; - protected override string BuildRelativeFilePath() => Path.Combine("src", "Generated", "Internal", $"{Name}.cs"); protected override MethodProvider[] BuildMethods() diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/CancellationTokenExtensionsDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/CancellationTokenExtensionsDefinition.cs index 77291404702..d1af1212824 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/CancellationTokenExtensionsDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/CancellationTokenExtensionsDefinition.cs @@ -21,8 +21,6 @@ public CancellationTokenExtensionsDefinition() _cancellationTokenParam = new ParameterProvider("cancellationToken", FormattableStringHelpers.Empty, typeof(CancellationToken)); } - protected override TypeSignatureModifiers BuildTypeModifiers() => TypeSignatureModifiers.Static; - protected override string BuildRelativeFilePath() => Path.Combine("src", "Generated", "Internal", $"{Name}.cs"); protected override string BuildName() => "CancellationTokenExtensions"; diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientPipelineExtensionsDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientPipelineExtensionsDefinition.cs index 386cabe2d4f..d039812772a 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientPipelineExtensionsDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientPipelineExtensionsDefinition.cs @@ -32,8 +32,6 @@ public ClientPipelineExtensionsDefinition() _options = _requestOptionsParam.ToApi(); } - protected override TypeSignatureModifiers BuildTypeModifiers() => TypeSignatureModifiers.Static; - protected override string BuildRelativeFilePath() => Path.Combine("src", "Generated", "Internal", $"{Name}.cs"); protected override string BuildName() => "ClientPipelineExtensions"; diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientUriBuilderDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientUriBuilderDefinition.cs index 2f51d5b6941..94b39a45088 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientUriBuilderDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientUriBuilderDefinition.cs @@ -63,6 +63,8 @@ public ClientUriBuilderDefinition() _pathLengthField = new(FieldModifiers.Private, typeof(int), "_pathLength", this); } + protected override TypeSignatureModifiers BuildDeclarationModifiers() => TypeSignatureModifiers.Internal; + protected override string BuildRelativeFilePath() => Path.Combine("src", "Generated", "Internal", $"{Name}.cs"); protected override string BuildName() => "ClientUriBuilder"; diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ErrorResultDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ErrorResultDefinition.cs index 05615bddea0..ee1abd3f5b9 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ErrorResultDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ErrorResultDefinition.cs @@ -12,7 +12,7 @@ namespace Microsoft.TypeSpec.Generator.ClientModel.Providers { - internal class ErrorResultDefinition : TypeProvider + internal class ErrorResultDefinition : InternalHelperProvider { private class ErrorResultTemplate { } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/FixedEnumSerializationProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/FixedEnumSerializationProvider.cs index f5b1f96665a..7b898cb9164 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/FixedEnumSerializationProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/FixedEnumSerializationProvider.cs @@ -30,9 +30,6 @@ public FixedEnumSerializationProvider(InputEnumType enumType, TypeProvider enumP } protected override string BuildNamespace() => _enumProvider.Type.Namespace; - protected override TypeSignatureModifiers BuildTypeModifiers() - => TypeSignatureModifiers.Static | TypeSignatureModifiers.Partial | TypeSignatureModifiers.Class; - protected override string BuildRelativeFilePath() { return Path.Combine("src", "Generated", "Models", $"{_enumProvider.Name}.Serialization.cs"); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ModelSerializationExtensionsDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ModelSerializationExtensionsDefinition.cs index b33202076d9..fe2d01a2a94 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ModelSerializationExtensionsDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ModelSerializationExtensionsDefinition.cs @@ -96,8 +96,6 @@ public ModelSerializationExtensionsDefinition() enclosingType: this); } - protected override TypeSignatureModifiers BuildTypeModifiers() => TypeSignatureModifiers.Static; - internal FieldProvider WireOptionsField { get; } private readonly FieldProvider _jsonDocumentOptionsField; diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MultiPartFormDataBinaryContentDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MultiPartFormDataBinaryContentDefinition.cs index 4170e4b5ea8..71fbc189889 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MultiPartFormDataBinaryContentDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MultiPartFormDataBinaryContentDefinition.cs @@ -77,7 +77,8 @@ public MultiPartFormDataBinaryContentDefinition() protected override string BuildName() => $"MultiPartFormData{_requestContentType.Name}"; - protected override TypeSignatureModifiers BuildTypeModifiers() => TypeSignatureModifiers.Class; + protected override TypeSignatureModifiers BuildDeclarationModifiers() + => TypeSignatureModifiers.Internal | TypeSignatureModifiers.Class; protected override CSharpType BuildBaseType() => _requestContentType; diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MultipartFormDataHelperDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MultipartFormDataHelperDefinition.cs index bda9953623a..61f4360d8c3 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MultipartFormDataHelperDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/MultipartFormDataHelperDefinition.cs @@ -24,9 +24,6 @@ internal class MultipartFormDataHelperDefinition : InternalHelperProvider protected override string BuildName() => "MultipartFormDataHelper"; - protected override TypeSignatureModifiers BuildTypeModifiers() - => TypeSignatureModifiers.Static | TypeSignatureModifiers.Partial | TypeSignatureModifiers.Class; - protected override string BuildRelativeFilePath() => Path.Combine("src", "Generated", "Internal", $"{Name}.cs"); protected override MethodProvider[] BuildMethods() diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/PipelineRequestHeadersExtensionsDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/PipelineRequestHeadersExtensionsDefinition.cs index 6e28cf95f16..12e90056ddb 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/PipelineRequestHeadersExtensionsDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/PipelineRequestHeadersExtensionsDefinition.cs @@ -25,8 +25,6 @@ public PipelineRequestHeadersExtensionsDefinition() _pipelineRequestHeadersParam = new ParameterProvider("headers", FormattableStringHelpers.Empty, typeof(PipelineRequestHeaders)); } private readonly CSharpType _t = typeof(IEnumerable<>).GetGenericArguments()[0]; - protected override TypeSignatureModifiers BuildTypeModifiers() => TypeSignatureModifiers.Static; - protected override string BuildRelativeFilePath() => Path.Combine("src", "Generated", "Internal", $"{Name}.cs"); protected override string BuildName() => "PipelineRequestHeadersExtensions"; diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/TypeFormattersDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/TypeFormattersDefinition.cs index b51e10cdea2..5cb9637827f 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/TypeFormattersDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/TypeFormattersDefinition.cs @@ -33,8 +33,6 @@ internal TypeFormattersDefinition() _defaultNumberFormatField = new(FieldModifiers.Public | FieldModifiers.Const, typeof(string), "DefaultNumberFormat", this, initializationValue: Literal("G")); } - protected override TypeSignatureModifiers BuildTypeModifiers() => TypeSignatureModifiers.Static; - protected override string BuildRelativeFilePath() => Path.Combine("src", "Generated", "Internal", $"{Name}.cs"); protected override string BuildName() => "TypeFormatters"; diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Utf8JsonBinaryContentDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Utf8JsonBinaryContentDefinition.cs index 84536ca9190..2d7f1ad298a 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Utf8JsonBinaryContentDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/Utf8JsonBinaryContentDefinition.cs @@ -49,8 +49,8 @@ public Utf8JsonBinaryContentDefinition() protected override string BuildName() => $"Utf8Json{BuildBaseType().Name}"; - protected override TypeSignatureModifiers BuildTypeModifiers() - => TypeSignatureModifiers.Partial | TypeSignatureModifiers.Class; + protected override TypeSignatureModifiers BuildDeclarationModifiers() + => TypeSignatureModifiers.Internal | TypeSignatureModifiers.Partial | TypeSignatureModifiers.Class; protected override string BuildRelativeFilePath() => Path.Combine("src", "Generated", "Internal", $"{Name}.cs"); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmCodeModelGenerator.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmCodeModelGenerator.cs index 3caeb42520c..7b7a4518559 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmCodeModelGenerator.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmCodeModelGenerator.cs @@ -24,8 +24,9 @@ public class ScmCodeModelGenerator : CodeModelGenerator public override ScmTypeFactory TypeFactory { get; } - internal ModelSerializationExtensionsDefinition ModelSerializationExtensionsDefinition { get; } = - new ModelSerializationExtensionsDefinition(); + internal ModelSerializationExtensionsDefinition ModelSerializationExtensionsDefinition + => _modelSerializationExtensionsDefinition ??= new ModelSerializationExtensionsDefinition(); + private ModelSerializationExtensionsDefinition? _modelSerializationExtensionsDefinition; internal SerializationFormatDefinition SerializationFormatDefinition { get; } = new SerializationFormatDefinition(); @@ -50,7 +51,6 @@ protected override void Configure() AddMetadataReference(MetadataReference.CreateFromFile(typeof(BinaryData).Assembly.Location)); AddMetadataReference(MetadataReference.CreateFromFile(typeof(JsonSerializer).Assembly.Location)); AddTypeToKeep(ModelReaderWriterContextDefinition.s_name, isRoot: false); - AddTypeToKeep(SerializationFormatDefinition); } public override async Task WriteAdditionalFiles(string outputPath) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/CodeModelGenerator.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/CodeModelGenerator.cs index 0aaf0f983bf..e8732f15ffd 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/CodeModelGenerator.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/CodeModelGenerator.cs @@ -268,38 +268,6 @@ public void AddTypeToKeep(TypeProvider type, bool isRoot = true) } } - internal void AddInternalHelperTypesToKeep(IEnumerable types) - { - var visited = new HashSet(); - foreach (var type in types) - { - AddInternalHelperTypesToKeep(type, visited); - } - } - - private void AddInternalHelperTypesToKeep(TypeProvider type, HashSet visited) - { - if (!visited.Add(type)) - { - return; - } - - if (type is InternalHelperProvider) - { - AddTypeToKeep(type); - } - - foreach (var serializationProvider in type.SerializationProviders) - { - AddInternalHelperTypesToKeep(serializationProvider, visited); - } - - foreach (var nestedType in type.NestedTypes) - { - AddInternalHelperTypesToKeep(nestedType, visited); - } - } - /// /// Writes additional output files (e.g. configuration schemas) after the main code generation is complete. /// Override this method to generate non-C# output files. diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/PostProcessing/GeneratedCodeWorkspace.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/PostProcessing/GeneratedCodeWorkspace.cs index bd88c9c48d0..c36686f637f 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/PostProcessing/GeneratedCodeWorkspace.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/PostProcessing/GeneratedCodeWorkspace.cs @@ -266,14 +266,10 @@ internal static Project AddDirectory(Project project, string directory, Func public async Task PostProcessAsync() { - var generator = CodeModelGenerator.Instance; - var outputLibrary = generator.OutputLibrary; - generator.AddInternalHelperTypesToKeep(outputLibrary.TypeProviders); - - var modelFactory = outputLibrary.ModelFactory.Value; - var nonRootTypes = generator.NonRootTypes; + var modelFactory = CodeModelGenerator.Instance.OutputLibrary.ModelFactory.Value; + var nonRootTypes = CodeModelGenerator.Instance.NonRootTypes; var postProcessor = new PostProcessor( - [.. generator.TypeFactory.UnionVariantTypesToKeep, .. generator.AdditionalRootTypes], + [.. CodeModelGenerator.Instance.TypeFactory.UnionVariantTypesToKeep, .. CodeModelGenerator.Instance.AdditionalRootTypes], modelFactoryFullName: modelFactory.Type.FullyQualifiedName, additionalNonRootTypeNames: nonRootTypes); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/PostProcessing/PostProcessor.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/PostProcessing/PostProcessor.cs index dc42f801732..b529adfc07c 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/PostProcessing/PostProcessor.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/PostProcessing/PostProcessor.cs @@ -91,8 +91,7 @@ private async Task GetTypeSymbolsAsync(Compilation compilation, // we do not add the model factory and additionalNonRootTypeSymbols to the declared symbol list // so that it will never be included in any process of internalization or removal if (SymbolEqualityComparer.Default.Equals(symbol, _modelFactorySymbol) - || _additionalNonRootTypeNames.Contains(symbol.Name) - || _additionalNonRootTypeNames.Contains(symbol.GetFullyQualifiedName())) + || IsAdditionalNonRootType(symbol)) { continue; } @@ -110,6 +109,20 @@ private async Task GetTypeSymbolsAsync(Compilation compilation, kv => kv.Value.ToHashSet(SymbolEqualityComparer.Default))); } + private bool IsAdditionalNonRootType(INamedTypeSymbol symbol) + { + var fullyQualifiedName = symbol.GetFullyQualifiedName(); + if (_additionalNonRootTypeNames.Contains(symbol.Name) + || _additionalNonRootTypeNames.Contains(fullyQualifiedName)) + { + return true; + } + + var genericArityIndex = fullyQualifiedName.IndexOf('`'); + return genericArityIndex > 0 + && _additionalNonRootTypeNames.Contains(fullyQualifiedName[..genericArityIndex]); + } + protected virtual bool ShouldIncludeDocument(Document document) => !GeneratedCodeWorkspace.IsGeneratedTestDocument(document); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ArgumentDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ArgumentDefinition.cs index 157ce689474..1a8e77b5554 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ArgumentDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ArgumentDefinition.cs @@ -34,8 +34,6 @@ public ArgumentDefinition() _nullableT = _t.WithNullable(true); } - protected override TypeSignatureModifiers BuildTypeModifiers() => TypeSignatureModifiers.Static; - private MethodSignature GetSignature( string name, IReadOnlyList parameters, diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ChangeTrackingDictionaryDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ChangeTrackingDictionaryDefinition.cs index ea6461b0a91..4e6385205ec 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ChangeTrackingDictionaryDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ChangeTrackingDictionaryDefinition.cs @@ -48,6 +48,8 @@ public ChangeTrackingDictionaryDefinition() protected override WhereExpression BuildWhereClause() => Where.NotNull(_tKey); + protected override TypeSignatureModifiers BuildDeclarationModifiers() => TypeSignatureModifiers.Internal; + protected override string BuildRelativeFilePath() => Path.Combine("src", "Generated", "Internal", $"{Name}.cs"); protected override string BuildName() => "ChangeTrackingDictionary"; diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ChangeTrackingListDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ChangeTrackingListDefinition.cs index 74bb6ca0e8b..ee4c5ce0e9d 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ChangeTrackingListDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ChangeTrackingListDefinition.cs @@ -46,6 +46,8 @@ public ChangeTrackingListDefinition() EnsureList = new(This.Invoke(_ensureListSignature)); } + protected override TypeSignatureModifiers BuildDeclarationModifiers() => TypeSignatureModifiers.Internal; + protected override string BuildRelativeFilePath() => Path.Combine("src", "Generated", "Internal", $"{Name}.cs"); protected override string BuildName() => "ChangeTrackingList"; diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/InternalHelperProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/InternalHelperProvider.cs index 26fa9d61eb0..d11a3490a4a 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/InternalHelperProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/InternalHelperProvider.cs @@ -11,11 +11,12 @@ namespace Microsoft.TypeSpec.Generator.Providers /// public abstract class InternalHelperProvider : TypeProvider { - protected InternalHelperProvider(InputType? inputType = default) : base(inputType) { } + protected InternalHelperProvider(InputType? inputType = default) : base(inputType) + { + CodeModelGenerator.Instance.AddTypeToKeep(this, isRoot: false); + } - protected sealed override TypeSignatureModifiers BuildDeclarationModifiers() - => TypeSignatureModifiers.Internal | BuildTypeModifiers(); - - protected virtual TypeSignatureModifiers BuildTypeModifiers() => TypeSignatureModifiers.None; + protected override TypeSignatureModifiers BuildDeclarationModifiers() + => TypeSignatureModifiers.Internal | TypeSignatureModifiers.Static; } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/OptionalDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/OptionalDefinition.cs index 0d47190ce28..af7b996bd59 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/OptionalDefinition.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/OptionalDefinition.cs @@ -29,8 +29,6 @@ public OptionalDefinition() _tValue = _genericChangeTrackingDictionary.Arguments[1]; } - protected override TypeSignatureModifiers BuildTypeModifiers() => TypeSignatureModifiers.Static; - protected override string BuildRelativeFilePath() => Path.Combine("src", "Generated", "Internal", $"{Name}.cs"); protected override string BuildName() => "Optional"; diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/TypeFactory.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/TypeFactory.cs index 07065943b7b..0495fdd0e30 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/TypeFactory.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/TypeFactory.cs @@ -17,9 +17,13 @@ namespace Microsoft.TypeSpec.Generator { public class TypeFactory { - private ChangeTrackingListDefinition ChangeTrackingListProvider { get; } = new(); + private ChangeTrackingListDefinition ChangeTrackingListProvider + => _changeTrackingListProvider ??= new(); + private ChangeTrackingListDefinition? _changeTrackingListProvider; - private ChangeTrackingDictionaryDefinition ChangeTrackingDictionaryProvider { get; } = new(); + private ChangeTrackingDictionaryDefinition ChangeTrackingDictionaryProvider + => _changeTrackingDictionaryProvider ??= new(); + private ChangeTrackingDictionaryDefinition? _changeTrackingDictionaryProvider; private Dictionary InputTypeToModelProvider { get; } = []; 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 4f755c69361..4e9bbb43ffe 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 @@ -553,15 +553,14 @@ public void TestSpecViewIsNotNull() } [Test] - public void InternalHelperProviderIsKeptAsRoot() + public void InternalHelperProviderIsKeptAsNonRoot() { var typeProvider = new TestInternalHelperProvider(); - CodeModelGenerator.Instance.AddInternalHelperTypesToKeep([typeProvider]); Assert.IsTrue(typeProvider.DeclarationModifiers.HasFlag(TypeSignatureModifiers.Internal)); Assert.IsTrue(typeProvider.DeclarationModifiers.HasFlag(TypeSignatureModifiers.Static)); - Assert.IsTrue(CodeModelGenerator.Instance.AdditionalRootTypes.Contains(typeProvider.Type.FullyQualifiedName)); - Assert.IsFalse(CodeModelGenerator.Instance.NonRootTypes.Contains(typeProvider.Type.FullyQualifiedName)); + Assert.IsFalse(CodeModelGenerator.Instance.AdditionalRootTypes.Contains(typeProvider.Type.FullyQualifiedName)); + Assert.IsTrue(CodeModelGenerator.Instance.NonRootTypes.Contains(typeProvider.Type.FullyQualifiedName)); } [Test] @@ -635,8 +634,6 @@ private sealed class TestInternalHelperProvider : InternalHelperProvider protected override string BuildName() => nameof(TestInternalHelperProvider); protected override string BuildRelativeFilePath() => $"{Name}.cs"; - - protected override TypeSignatureModifiers BuildTypeModifiers() => TypeSignatureModifiers.Static; } } } From a85cfefac5d86a0beca0c04e3d42a571ba822296 Mon Sep 17 00:00:00 2001 From: jolov Date: Thu, 16 Jul 2026 16:56:32 -0700 Subject: [PATCH 3/5] fix(csharp): exclude enum serialization from helpers Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../src/Providers/FixedEnumSerializationProvider.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/FixedEnumSerializationProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/FixedEnumSerializationProvider.cs index 7b898cb9164..f5224038ede 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/FixedEnumSerializationProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/FixedEnumSerializationProvider.cs @@ -18,7 +18,7 @@ namespace Microsoft.TypeSpec.Generator.ClientModel.Providers /// /// This defines a class with extension methods for enums to convert an enum to its underlying value, or from its underlying value to an instance of the enum /// - public class FixedEnumSerializationProvider : InternalHelperProvider + public class FixedEnumSerializationProvider : TypeProvider { private readonly InputEnumType _enumType; private TypeProvider _enumProvider; @@ -30,6 +30,9 @@ public FixedEnumSerializationProvider(InputEnumType enumType, TypeProvider enumP } protected override string BuildNamespace() => _enumProvider.Type.Namespace; + protected override TypeSignatureModifiers BuildDeclarationModifiers() + => TypeSignatureModifiers.Internal | TypeSignatureModifiers.Static | TypeSignatureModifiers.Partial | TypeSignatureModifiers.Class; + protected override string BuildRelativeFilePath() { return Path.Combine("src", "Generated", "Models", $"{_enumProvider.Name}.Serialization.cs"); From 9e0fed9dbb01db73dee320109b287fa4b7915362 Mon Sep 17 00:00:00 2001 From: jolov Date: Fri, 17 Jul 2026 12:23:33 -0700 Subject: [PATCH 4/5] refactor(csharp): short-circuit non-root matching Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../src/PostProcessing/PostProcessor.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/PostProcessing/PostProcessor.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/PostProcessing/PostProcessor.cs index b529adfc07c..9d84d3494c2 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/PostProcessing/PostProcessor.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/PostProcessing/PostProcessor.cs @@ -111,9 +111,13 @@ private async Task GetTypeSymbolsAsync(Compilation compilation, private bool IsAdditionalNonRootType(INamedTypeSymbol symbol) { + if (_additionalNonRootTypeNames.Contains(symbol.Name)) + { + return true; + } + var fullyQualifiedName = symbol.GetFullyQualifiedName(); - if (_additionalNonRootTypeNames.Contains(symbol.Name) - || _additionalNonRootTypeNames.Contains(fullyQualifiedName)) + if (_additionalNonRootTypeNames.Contains(fullyQualifiedName)) { return true; } From e1a70266028aa253b55c5bdbb680b37541be4dcd Mon Sep 17 00:00:00 2001 From: jolov Date: Fri, 17 Jul 2026 12:52:35 -0700 Subject: [PATCH 5/5] fix(csharp): retain serialization format support type Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../src/ScmCodeModelGenerator.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmCodeModelGenerator.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmCodeModelGenerator.cs index 7b7a4518559..a09c248b4f5 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmCodeModelGenerator.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/ScmCodeModelGenerator.cs @@ -51,6 +51,7 @@ protected override void Configure() AddMetadataReference(MetadataReference.CreateFromFile(typeof(BinaryData).Assembly.Location)); AddMetadataReference(MetadataReference.CreateFromFile(typeof(JsonSerializer).Assembly.Location)); AddTypeToKeep(ModelReaderWriterContextDefinition.s_name, isRoot: false); + AddTypeToKeep(SerializationFormatDefinition, isRoot: false); } public override async Task WriteAdditionalFiles(string outputPath)