Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ protected override TypeSignatureModifiers BuildDeclarationModifiers()

protected override string BuildRelativeFilePath() => Path.Combine("src", "Generated", "Internal", $"{Name}.cs");

protected override IReadOnlyList<CSharpType> BuildBodyDependencyTypes() =>
[ScmCodeModelGenerator.Instance.ModelSerializationExtensionsDefinition.Type];

protected override MethodProvider[] BuildMethods()
{
var methods = new List<MethodProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ public class ClientOptionsProvider : TypeProvider
private readonly ClientProvider _clientProvider;
private readonly Dictionary<InputEnumType, EnumProvider>? _serviceVersionsEnums;
private static ClientOptionsProvider? _singletonInstance;
// All clients sharing the singleton are tracked so its accessibility reflects every owner.
private static readonly List<ClientProvider> _singletonClientProviders = [];
Comment thread
live1206 marked this conversation as resolved.

internal ClientOptionsProvider(InputClient inputClient, ClientProvider clientProvider)
{
Expand Down Expand Up @@ -77,12 +79,15 @@ public static ClientOptionsProvider CreateClientOptionsProvider(InputClient inpu
{
if (UseSingletonInstance(inputClient))
{
// Use singleton instance
if (_singletonInstance == null)
{
// Create singleton with namespace-based naming
_singletonClientProviders.Clear();
_singletonInstance = new ClientOptionsProvider(inputClient, clientProvider);
}
if (!_singletonClientProviders.Contains(clientProvider))
{
_singletonClientProviders.Add(clientProvider);
}
return _singletonInstance;
}

Expand Down Expand Up @@ -248,6 +253,28 @@ private static bool UseSingletonInstance(InputClient inputClient)

protected override string BuildRelativeFilePath() => Path.Combine("src", "Generated", $"{Name}.cs");

// TODO https://github.com/microsoft/typespec/issues/11181: Remove this once generated source parity no longer depends on internal ClientOptions XML docs.
protected override bool ShouldWriteTypeXmlDocs => CustomCodeView is null;

protected override TypeSignatureModifiers BuildDeclarationModifiers()
Comment thread
jorgerangel-msft marked this conversation as resolved.
{
if (this == _singletonInstance)
{
TypeSignatureModifiers singletonAccessibility = TypeSignatureModifiers.None;
foreach (var clientProvider in _singletonClientProviders)
{
singletonAccessibility |= GetAccessibilityModifiers(clientProvider.DeclarationModifiers);
}

return singletonAccessibility;
}

return GetAccessibilityModifiers(_clientProvider.DeclarationModifiers);
}

private static TypeSignatureModifiers GetAccessibilityModifiers(TypeSignatureModifiers modifiers)
=> modifiers & (TypeSignatureModifiers.Public | TypeSignatureModifiers.Internal | TypeSignatureModifiers.Protected | TypeSignatureModifiers.Private);

protected override string BuildName()
{
if (UseSingletonInstance(_inputClient))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,42 @@ private IReadOnlyList<ParameterProvider> GetClientParameters()

protected override string BuildName() => _inputClient.IsExactName ? _inputClient.Name : _inputClient.Name.ToIdentifierName();

protected override IReadOnlyList<CSharpType> BuildHelperDependencyTypes()
{
foreach (var method in Methods.OfType<ScmMethodProvider>())
{
if (method.BodyStatements != null)
{
return [new ClientPipelineExtensionsDefinition().Type];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another case of creating a new instance of a type. There are several more places where we are doing this.

}
}

return [];
}

protected override IReadOnlyList<CSharpType> BuildBodyDependencyTypes()
{
var dependencies = new List<CSharpType>();
foreach (var method in Methods.OfType<ScmMethodProvider>())
{
if (method.BodyStatements == null)
{
continue;
}

if (method.CollectionDefinition != null)
{
dependencies.Add(method.CollectionDefinition.Type);
}

// Service method metadata can mention wire-only request/response models that are not
// emitted in the generated method signature or body. The graph builder and structured
// body scanner capture the generated types that are actually referenced.
}

return dependencies;
}

protected override FieldProvider[] BuildFields()
{
List<FieldProvider> fields = [EndpointField];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,22 @@ private bool HasPagingOperationNameCollision(string operationName)
protected override TypeSignatureModifiers BuildDeclarationModifiers()
=> TypeSignatureModifiers.Internal | TypeSignatureModifiers.Partial | TypeSignatureModifiers.Class;

protected override IReadOnlyList<CSharpType> BuildBodyDependencyTypes()
{
var dependencies = new List<CSharpType> { Client.Type, ResponseModelType, NextPagePropertyType };
if (ItemModelType != null)
{
dependencies.Add(ItemModelType);
}

foreach (var field in RequestFields)
{
dependencies.Add(field.Type);
}

return dependencies;
}

protected override FieldProvider[] BuildFields() => [ClientField, .. RequestFields];

protected override CSharpType[] BuildImplements() =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.TypeSpec.Generator.Expressions;
using Microsoft.TypeSpec.Generator.Primitives;
using Microsoft.TypeSpec.Generator.Providers;
using Microsoft.TypeSpec.Generator.Statements;
Expand All @@ -32,14 +33,22 @@ protected override TypeSignatureModifiers BuildDeclarationModifiers()

protected override CSharpType BuildBaseType() => typeof(ModelReaderWriterContext);

protected override bool ShouldAnalyzeAttributesInReferenceMap => false;

protected override IReadOnlyList<MethodBodyStatement> BuildAttributes()
{
var attributes = new Dictionary<string, MethodBodyStatement>();
var customizedBuildableTypes = GetCustomizedBuildableTypes();

// Add ModelReaderWriterBuildableAttribute for all IPersistableModel types
(HashSet<CSharpType> buildableTypes, HashSet<TypeProvider> buildableProviders) = CollectBuildableTypes();
foreach (var type in buildableTypes)
{
if (customizedBuildableTypes.Contains(GetTypeIdentity(type)))
{
continue;
}

// Use the full attribute type name to ensure proper compilation
var attributeType = new CSharpType(typeof(ModelReaderWriterBuildableAttribute));
var attributeStatement = new AttributeStatement(attributeType, TypeOf(type));
Expand All @@ -56,6 +65,11 @@ protected override IReadOnlyList<MethodBodyStatement> BuildAttributes()
}
foreach (var provider in buildableProviders)
{
if (!ShouldWriteProvider(provider) || customizedBuildableTypes.Contains(GetTypeIdentity(provider.Type)))
{
continue;
}

// Use the full attribute type name to ensure proper compilation
var attributeType = new CSharpType(typeof(ModelReaderWriterBuildableAttribute));
var attributeStatement = new AttributeStatement(attributeType, TypeOf(provider.Type));
Expand All @@ -76,6 +90,39 @@ protected override IReadOnlyList<MethodBodyStatement> BuildAttributes()
return attributes.OrderBy(a => GetSimpleTypeName(a.Key)).Select(kvp => kvp.Value).ToList();
}

private HashSet<string> GetCustomizedBuildableTypes()
{
var customizedTypes = new HashSet<string>(StringComparer.Ordinal);
foreach (var attribute in CustomCodeView?.Attributes ?? [])
{
if (!string.Equals(
attribute.Type.FullyQualifiedName,
typeof(ModelReaderWriterBuildableAttribute).FullName,
StringComparison.Ordinal))
{
continue;
}

foreach (var argument in attribute.Arguments)
{
if (argument is TypeOfExpression typeOf)
{
customizedTypes.Add(GetTypeIdentity(typeOf.Type));
}
}
}

return customizedTypes;
}

private static string GetTypeIdentity(CSharpType type)
{
var name = type.FullyQualifiedName.TrimStart('.');
return type.Arguments.Count == 0
? name
: $"{name}<{string.Join(",", type.Arguments.Select(GetTypeIdentity))}>";
}

/// <summary>
/// Collects all types that implement IPersistableModel, including all models and their properties
/// that are also IPersistableModel types, recursively without duplicates.
Expand All @@ -87,7 +134,6 @@ protected override IReadOnlyList<MethodBodyStatement> BuildAttributes()
var visitedBaseProviders = new HashSet<TypeProvider>(ReferenceEqualityComparer.Instance);
var buildableProviders = new HashSet<TypeProvider>(s_typeProviderNameComparer);
var buildableTypes = new HashSet<CSharpType>(s_cSharpTypeNameComparer);

// Base-model traversal can encounter equivalent provider instances that are not reference-equal to
// the output-library roots, so keep the output-library provider set name-comparable.
var contextEligibleOutputProviders = new HashSet<TypeProvider>(
Expand All @@ -97,8 +143,13 @@ protected override IReadOnlyList<MethodBodyStatement> BuildAttributes()
// Process each output-library provider recursively to discover types from methods and properties.
foreach (var provider in contextEligibleOutputProviders)
{
if (!ShouldWriteProvider(provider))
{
continue;
}

// Only output-library providers get standalone context entries.
if (ImplementsModelReaderWriter(provider))
if (ShouldAddStandaloneBuildableProvider(provider))
{
buildableProviders.Add(provider);
}
Expand Down Expand Up @@ -190,9 +241,10 @@ private void CollectBuildableTypesRecursiveCore(
{
// Traverse base model properties for discoverable types, but do not add the base model
// itself as a context entry unless it was in the output-library seed set.
if (visitedBaseProviders.Add(modelProvider.BaseModelProvider))
var baseModelProvider = modelProvider.BaseModelProvider;
if (visitedBaseProviders.Add(baseModelProvider))
{
CollectBuildableTypesRecursiveCore(modelProvider.BaseModelProvider, visitedTypes, visitedTypeProviders, visitedBaseProviders, buildableProviders, buildableTypes);
CollectBuildableTypesRecursiveCore(baseModelProvider, visitedTypes, visitedTypeProviders, visitedBaseProviders, buildableProviders, buildableTypes);
}
}
else
Expand Down Expand Up @@ -220,6 +272,11 @@ private void CollectBuildableTypesFromFrameworkType(

try
{
if (!IsResolvableBuildableType(frameworkType))
{
return;
}

buildableTypes.Add(frameworkType.FrameworkType);
var type = frameworkType.FrameworkType;
var properties = type.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
Expand Down Expand Up @@ -426,6 +483,26 @@ private static bool ImplementsModelReaderWriter(TypeProvider typeProvider)
return false;
}

private static bool ShouldAddStandaloneBuildableProvider(TypeProvider provider)
{
var isResolvable = IsResolvableBuildableType(provider.Type);
var implementsMrw = ImplementsModelReaderWriter(provider);
var hasWritableSerialization = HasWritableModelReaderWriterSerialization(provider);
return isResolvable && implementsMrw && hasWritableSerialization;
}

private static bool HasWritableModelReaderWriterSerialization(TypeProvider provider)
{
if (provider is not ModelProvider)
{
return true;
}

return provider.SerializationProviders
.OfType<MrwSerializationTypeDefinition>()
.Any(ShouldWriteProvider);
}

private static void AddAttributeForType(
Dictionary<string, MethodBodyStatement> attributes,
AttributeStatement attributeStatement,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ protected override TypeSignatureModifiers BuildDeclarationModifiers()

protected override string BuildName() => "ModelSerializationExtensions";

protected override IReadOnlyList<CSharpType> BuildBodyDependencyTypes() =>
[ScmCodeModelGenerator.Instance.TypeFormattersDefinition.Type];

protected override FieldProvider[] BuildFields()
{
return [WireOptionsField, _jsonDocumentOptionsField, .. BuildXmlFields()];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ private MethodProvider BuildXmlModelWriteCoreMethod()
MethodSignatureModifiers modifiers = _isStruct
? MethodSignatureModifiers.Private
: MethodSignatureModifiers.Internal | MethodSignatureModifiers.Virtual;
if (_shouldOverrideXmlMethods)
if (_shouldOverrideMethods)
{
modifiers = MethodSignatureModifiers.Internal | MethodSignatureModifiers.Override;
}
Expand All @@ -81,7 +81,7 @@ private MethodProvider BuildXmlModelWriteCoreMethod()

private MethodBodyStatement[] BuildXmlModelWriteCoreMethodBody()
{
var categorizedProperties = _shouldOverrideXmlMethods
var categorizedProperties = _shouldOverrideMethods
? CategorizedXmlProperties
: AllCategorizedXmlProperties;
var statements = new List<MethodBodyStatement>
Expand All @@ -90,7 +90,7 @@ private MethodBodyStatement[] BuildXmlModelWriteCoreMethodBody()
MethodBodyStatement.EmptyLine
};

if (_shouldOverrideXmlMethods)
if (_shouldOverrideMethods)
{
statements.Add(Base.Invoke(XmlModelWriteCoreMethodName, _xmlWriterParameter, _serializationOptionsParameter).Terminate());
}
Expand Down
Loading
Loading