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..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 @@ -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,9 +30,6 @@ public class BinaryContentHelperDefinition : TypeProvider protected override string BuildName() => "BinaryContentHelper"; - protected override TypeSignatureModifiers BuildDeclarationModifiers() - => TypeSignatureModifiers.Internal | 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 6168a29ba2f..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 @@ -12,7 +12,7 @@ namespace Microsoft.TypeSpec.Generator.ClientModel.Providers { - internal class CancellationTokenExtensionsDefinition : TypeProvider + internal class CancellationTokenExtensionsDefinition : InternalHelperProvider { private ParameterProvider _cancellationTokenParam; @@ -21,11 +21,6 @@ public CancellationTokenExtensionsDefinition() _cancellationTokenParam = new ParameterProvider("cancellationToken", FormattableStringHelpers.Empty, typeof(CancellationToken)); } - protected override TypeSignatureModifiers BuildDeclarationModifiers() - { - return TypeSignatureModifiers.Internal | 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 02864633aca..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 @@ -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,11 +32,6 @@ public ClientPipelineExtensionsDefinition() _options = _requestOptionsParam.ToApi(); } - protected override TypeSignatureModifiers BuildDeclarationModifiers() - { - return TypeSignatureModifiers.Internal | 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 92e0cf23a67..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 @@ -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,10 +63,7 @@ public ClientUriBuilderDefinition() _pathLengthField = new(FieldModifiers.Private, typeof(int), "_pathLength", this); } - protected override TypeSignatureModifiers BuildDeclarationModifiers() - { - return TypeSignatureModifiers.Internal; - } + protected override TypeSignatureModifiers BuildDeclarationModifiers() => TypeSignatureModifiers.Internal; 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/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/ModelSerializationExtensionsDefinition.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ModelSerializationExtensionsDefinition.cs index f6f6cebbd49..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 @@ -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,11 +96,6 @@ public ModelSerializationExtensionsDefinition() enclosingType: this); } - protected override TypeSignatureModifiers BuildDeclarationModifiers() - { - return TypeSignatureModifiers.Internal | 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..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 @@ -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,8 @@ public MultiPartFormDataBinaryContentDefinition() protected override string BuildName() => $"MultiPartFormData{_requestContentType.Name}"; - protected override TypeSignatureModifiers BuildDeclarationModifiers() => TypeSignatureModifiers.Class | TypeSignatureModifiers.Internal; + 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 14f332d2cbc..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 @@ -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,9 +24,6 @@ internal class MultipartFormDataHelperDefinition : TypeProvider protected override string BuildName() => "MultipartFormDataHelper"; - protected override TypeSignatureModifiers BuildDeclarationModifiers() - => TypeSignatureModifiers.Internal | 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 288b7f1af24..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 @@ -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,11 +25,6 @@ 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 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 4e0509bc78e..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 @@ -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,11 +33,6 @@ 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 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 d470b06b8d6..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 @@ -16,7 +16,7 @@ namespace Microsoft.TypeSpec.Generator.ClientModel.Providers { - public class Utf8JsonBinaryContentDefinition : TypeProvider + public class Utf8JsonBinaryContentDefinition : InternalHelperProvider { private const string _jsonWriterName = "JsonWriter"; 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..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 @@ -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,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) 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..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 @@ -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,24 @@ private async Task GetTypeSymbolsAsync(Compilation compilation, kv => kv.Value.ToHashSet(SymbolEqualityComparer.Default))); } + private bool IsAdditionalNonRootType(INamedTypeSymbol symbol) + { + if (_additionalNonRootTypeNames.Contains(symbol.Name)) + { + return true; + } + + var fullyQualifiedName = symbol.GetFullyQualifiedName(); + if (_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 6c7e62bd102..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 @@ -15,7 +15,7 @@ namespace Microsoft.TypeSpec.Generator.Providers { - internal class ArgumentDefinition : TypeProvider + internal class ArgumentDefinition : InternalHelperProvider { private class Template { } @@ -34,11 +34,6 @@ public ArgumentDefinition() _nullableT = _t.WithNullable(true); } - protected override TypeSignatureModifiers BuildDeclarationModifiers() - { - return TypeSignatureModifiers.Internal | 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 97e38db6102..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 @@ -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,10 +48,7 @@ public ChangeTrackingDictionaryDefinition() protected override WhereExpression BuildWhereClause() => Where.NotNull(_tKey); - protected override TypeSignatureModifiers BuildDeclarationModifiers() - { - return TypeSignatureModifiers.Internal; - } + protected override TypeSignatureModifiers BuildDeclarationModifiers() => TypeSignatureModifiers.Internal; protected override string BuildRelativeFilePath() => Path.Combine("src", "Generated", "Internal", $"{Name}.cs"); 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..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 @@ -13,7 +13,7 @@ namespace Microsoft.TypeSpec.Generator.Providers { - internal sealed class ChangeTrackingListDefinition : TypeProvider + internal sealed class ChangeTrackingListDefinition : InternalHelperProvider { private class ChangeTrackingListTemplate { } @@ -46,10 +46,7 @@ public ChangeTrackingListDefinition() EnsureList = new(This.Invoke(_ensureListSignature)); } - protected override TypeSignatureModifiers BuildDeclarationModifiers() - { - return TypeSignatureModifiers.Internal; - } + protected override TypeSignatureModifiers BuildDeclarationModifiers() => TypeSignatureModifiers.Internal; protected override string BuildRelativeFilePath() => Path.Combine("src", "Generated", "Internal", $"{Name}.cs"); 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..d11a3490a4a --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/InternalHelperProvider.cs @@ -0,0 +1,22 @@ +// 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) + { + CodeModelGenerator.Instance.AddTypeToKeep(this, isRoot: false); + } + + 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 cfd71ebb0a6..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 @@ -11,7 +11,7 @@ namespace Microsoft.TypeSpec.Generator.Providers { - public class OptionalDefinition : TypeProvider + public class OptionalDefinition : InternalHelperProvider { private class ListTemplate { } @@ -29,11 +29,6 @@ public OptionalDefinition() _tValue = _genericChangeTrackingDictionary.Arguments[1]; } - protected override TypeSignatureModifiers BuildDeclarationModifiers() - { - return TypeSignatureModifiers.Internal | 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 9ec0b21c32d..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 @@ -552,6 +552,17 @@ public void TestSpecViewIsNotNull() Assert.IsInstanceOf(specView); } + [Test] + public void InternalHelperProviderIsKeptAsNonRoot() + { + var typeProvider = new TestInternalHelperProvider(); + + Assert.IsTrue(typeProvider.DeclarationModifiers.HasFlag(TypeSignatureModifiers.Internal)); + Assert.IsTrue(typeProvider.DeclarationModifiers.HasFlag(TypeSignatureModifiers.Static)); + Assert.IsFalse(CodeModelGenerator.Instance.AdditionalRootTypes.Contains(typeProvider.Type.FullyQualifiedName)); + Assert.IsTrue(CodeModelGenerator.Instance.NonRootTypes.Contains(typeProvider.Type.FullyQualifiedName)); + } + [Test] public void TestSpecViewDelegatesCorrectly() { @@ -617,5 +628,12 @@ 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"; + } } } 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(); + } + } +}