Skip to content

Commit a10e960

Browse files
committed
Fix polymorphic issue and address feedback
1 parent f6058ef commit a10e960

File tree

57 files changed

+784
-1582
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+784
-1582
lines changed

src/libraries/Common/src/SourceGenerators/GeneratorHelpers.cs

Lines changed: 0 additions & 12 deletions
This file was deleted.

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Emitter.cs

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,17 @@ private sealed partial class Emitter
1717
private readonly SourceProductionContext _context;
1818
private readonly SourceGenerationSpec _sourceGenSpec;
1919

20-
private readonly string _generatedNamespaceName = ProjectName;
2120
private bool _emitBlankLineBeforeNextStatement;
2221
private int _valueSuffixIndex;
2322

2423
private static readonly Regex s_arrayBracketsRegex = new(Regex.Escape("[]"));
2524

2625
private readonly SourceWriter _writer = new();
2726

28-
public Emitter(SourceProductionContext context, SourceGenerationSpec sourceGenSpec, bool emitUniqueHelperNames)
27+
public Emitter(SourceProductionContext context, SourceGenerationSpec sourceGenSpec)
2928
{
3029
_context = context;
3130
_sourceGenSpec = sourceGenSpec;
32-
33-
if (emitUniqueHelperNames)
34-
{
35-
GeneratorHelpers.MakeNameUnique(ref _generatedNamespaceName);
36-
}
3731
}
3832

3933
public void Emit()
@@ -51,7 +45,7 @@ public void Emit()
5145

5246
EmitInterceptsLocationAttrDecl();
5347

54-
EmitStartBlock($"namespace {_generatedNamespaceName}");
48+
EmitStartBlock($"namespace {ProjectName}");
5549
EmitUsingStatements();
5650

5751
_writer.WriteLine();
@@ -260,10 +254,8 @@ private void EmitUsingStatements()
260254
private void EmitIConfigurationHasValueOrChildrenCheck(bool voidReturn)
261255
{
262256
string returnPostfix = voidReturn ? string.Empty : " null";
263-
string methodIdentifier = Identifier.HasValueOrChildren;
264-
265257
_writer.WriteLine($$"""
266-
if (!{{methodIdentifier}}({{Identifier.configuration}}))
258+
if (!{{Identifier.HasValueOrChildren}}({{Identifier.configuration}}))
267259
{
268260
return{{returnPostfix}};
269261
}

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Parser.cs

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -177,15 +177,14 @@ type.TypeKind is TypeKind.TypeParameter or TypeKind.Pointer or TypeKind.Error ||
177177
return _createdSpecs[type] = spec;
178178
}
179179

180-
private void RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper method, TypeSpec type)
180+
private void RegisterTypeForBindCoreMainGen(TypeSpec typeSpec)
181181
{
182-
if (!_sourceGenSpec.TypesForGen_CoreBindingHelper_Methods.TryGetValue(method, out HashSet<TypeSpec>? types))
182+
if (typeSpec.NeedsMemberBinding)
183183
{
184-
_sourceGenSpec.TypesForGen_CoreBindingHelper_Methods[method] = types = new HashSet<TypeSpec>();
184+
RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.BindCoreMain, typeSpec);
185+
RegisterTypeForBindCoreGen(typeSpec);
186+
_sourceGenSpec.MethodsToGen_CoreBindingHelper |= MethodsToGen_CoreBindingHelper.AsConfigWithChildren;
185187
}
186-
187-
types.Add(type);
188-
_sourceGenSpec.MethodsToGen_CoreBindingHelper |= method;
189188
}
190189

191190
private void RegisterTypeForBindCoreGen(TypeSpec typeSpec)
@@ -196,16 +195,30 @@ private void RegisterTypeForBindCoreGen(TypeSpec typeSpec)
196195
}
197196
}
198197

199-
private void RegisterGenMethodAsInterceptor(Enum method, IInvocationOperation operation)
198+
private void RegisterTypeForGetCoreGen(TypeSpec typeSpec)
199+
{
200+
RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.GetCore, typeSpec);
201+
_sourceGenSpec.MethodsToGen_CoreBindingHelper |= MethodsToGen_CoreBindingHelper.AsConfigWithChildren;
202+
}
203+
204+
private void RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper method, TypeSpec type)
200205
{
201-
if (!_sourceGenSpec.GenMethodsInterceptionInfo.TryGetValue(method, out List<InterceptorLocationInfo>? info))
206+
if (!_sourceGenSpec.TypesForGen_CoreBindingHelper_Methods.TryGetValue(method, out HashSet<TypeSpec>? types))
202207
{
203-
_sourceGenSpec.GenMethodsInterceptionInfo[method] = info = new List<InterceptorLocationInfo>();
208+
_sourceGenSpec.TypesForGen_CoreBindingHelper_Methods[method] = types = new HashSet<TypeSpec>();
204209
}
205210

206-
info.Add(new InterceptorLocationInfo(operation));
211+
types.Add(type);
212+
_sourceGenSpec.MethodsToGen_CoreBindingHelper |= method;
207213
}
208214

215+
/// <summary>
216+
/// Registers interceptors for root binding methods, except for ConfigurationBinder.Bind,
217+
/// which is handled by <see cref="RegisterAsInterceptor_ConfigBinder_BindMethod"/>
218+
/// </summary>
219+
private void RegisterAsInterceptor(Enum method, IInvocationOperation operation) =>
220+
_sourceGenSpec.InterceptionInfo.RegisterCacheEntry(method, new InterceptorLocationInfo(operation));
221+
209222
private static bool IsNullable(ITypeSymbol type, [NotNullWhen(true)] out ITypeSymbol? underlyingType)
210223
{
211224
if (type is INamedTypeSymbol { IsGenericType: true } genericType &&
@@ -901,4 +914,19 @@ private void RegisterTypeDiagnostic(ITypeSymbol causingType, InvocationDiagnosti
901914
}
902915
}
903916
}
917+
918+
public static class ParserExtensions
919+
{
920+
public static void RegisterCacheEntry<TKey, TValue, TEntry>(this Dictionary<TKey, TValue> cache, TKey key, TEntry entry)
921+
where TKey : notnull
922+
where TValue : ICollection<TEntry>, new()
923+
{
924+
if (!cache.TryGetValue(key, out TValue? entryCollection))
925+
{
926+
cache[key] = entryCollection = new TValue();
927+
}
928+
929+
entryCollection.Add(entry);
930+
}
931+
}
904932
}

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Suppressor.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,23 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
1212
public sealed partial class ConfigurationBindingGenerator
1313
{
1414
/// <summary>
15-
/// Supresses false-positive diagnostics emitted by the linker analyzers
16-
/// when analyzing binding invocations that we have substituted.Workaround
17-
/// for https://github.com/dotnet/roslyn/issues/68669.
15+
/// Supresses false-positive diagnostics emitted by the linker
16+
/// when analyzing binding invocations that we have intercepted.
17+
/// Workaround for https://github.com/dotnet/roslyn/issues/68669.
1818
/// </summary>
1919
[DiagnosticAnalyzer(LanguageNames.CSharp)]
2020
public sealed class Suppressor : DiagnosticSuppressor
2121
{
2222
private const string Justification = "The target method has been intercepted by a generated static variant.";
2323

24+
/// <summary>
25+
/// Suppression descriptor for IL2026: Members attributed with RequiresUnreferencedCode may break when trimming.
26+
/// </summary>
2427
private static readonly SuppressionDescriptor RUCDiagnostic = new(id: "SYSLIBSUPPRESS0002", suppressedDiagnosticId: "IL2026", Justification);
2528

29+
/// <summary>
30+
/// Suppression descriptor for IL3050: Avoid calling members annotated with 'RequiresDynamicCodeAttribute' when publishing as native AOT.
31+
/// </summary>
2632
private static readonly SuppressionDescriptor RDCDiagnostic = new(id: "SYSLIBSUPPRESS0003", suppressedDiagnosticId: "IL3050", Justification);
2733

2834
public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions => ImmutableArray.Create(RUCDiagnostic, RDCDiagnostic);

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ public sealed partial class ConfigurationBindingGenerator : IIncrementalGenerato
1616
{
1717
private static readonly string ProjectName = Emitter.s_assemblyName.Name;
1818

19-
public bool EmitUniqueHelperNames { get; init; } = true;
20-
2119
public void Initialize(IncrementalGeneratorInitializationContext context)
2220
{
2321
#if LAUNCH_DEBUGGER
@@ -43,7 +41,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
4341
context.RegisterSourceOutput(inputData, (spc, source) => Execute(source.Item1, source.Item2, spc));
4442
}
4543

46-
private void Execute(CompilationData compilationData, ImmutableArray<BinderInvocation> inputCalls, SourceProductionContext context)
44+
private static void Execute(CompilationData compilationData, ImmutableArray<BinderInvocation> inputCalls, SourceProductionContext context)
4745
{
4846
if (inputCalls.IsDefaultOrEmpty)
4947
{
@@ -59,7 +57,7 @@ private void Execute(CompilationData compilationData, ImmutableArray<BinderInvoc
5957
Parser parser = new(context, compilationData.TypeSymbols!, inputCalls);
6058
if (parser.GetSourceGenerationSpec() is SourceGenerationSpec spec)
6159
{
62-
Emitter emitter = new(context, spec, EmitUniqueHelperNames);
60+
Emitter emitter = new(context, spec);
6361
emitter.Emit();
6462
}
6563
}
@@ -71,7 +69,11 @@ private sealed record CompilationData
7169

7270
public CompilationData(CSharpCompilation compilation)
7371
{
74-
LanguageVersionIsSupported = compilation.LanguageVersion >= LanguageVersion.Preview;
72+
// We don't have a CSharp21 value available yet. Polyfill the value here for forward compat, rather than use the LangugeVersion.Preview enum value.
73+
// https://github.com/dotnet/roslyn/blob/168689931cb4e3150641ec2fb188a64ce4b3b790/src/Compilers/CSharp/Portable/LanguageVersion.cs#L218-L232
74+
const int LangVersion_CSharp12 = 1200;
75+
LanguageVersionIsSupported = (int)compilation.LanguageVersion >= LangVersion_CSharp12;
76+
7577
if (LanguageVersionIsSupported)
7678
{
7779
TypeSymbols = new KnownTypeSymbols(compilation);

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/ConfigurationBinder.cs

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Collections.Generic;
5+
using SourceGenerators;
6+
47
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
58
{
69
public sealed partial class ConfigurationBindingGenerator
@@ -45,14 +48,14 @@ private void EmitGetMethods()
4548
if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Get_TypeOf))
4649
{
4750
StartMethodDefinition(MethodsToGen_ConfigurationBinder.Get_TypeOf, documentation);
48-
_writer.WriteLine($"public static object? {Identifier.Get}(this {Identifier.IConfiguration} {Identifier.configuration}, {Identifier.Type} {Identifier.type}) => " +
51+
_writer.WriteLine($"public static object? {Identifier.Get}(this {Identifier.IConfiguration} {Identifier.configuration}, Type {Identifier.type}) => " +
4952
$"{expressionForGetCore}({Identifier.configuration}, {Identifier.type}, {Identifier.configureOptions}: null);");
5053
}
5154

5255
if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Get_TypeOf_BinderOptions))
5356
{
5457
StartMethodDefinition(MethodsToGen_ConfigurationBinder.Get_TypeOf_BinderOptions, documentation);
55-
_writer.WriteLine($"public static object? {Identifier.Get}(this {Identifier.IConfiguration} {Identifier.configuration}, {Identifier.Type} {Identifier.type}, {TypeDisplayString.NullableActionOfBinderOptions} {Identifier.configureOptions}) => " +
58+
_writer.WriteLine($"public static object? {Identifier.Get}(this {Identifier.IConfiguration} {Identifier.configuration}, Type {Identifier.type}, {TypeDisplayString.NullableActionOfBinderOptions} {Identifier.configureOptions}) => " +
5659
$"{expressionForGetCore}({Identifier.configuration}, {Identifier.type}, {Identifier.configureOptions});");
5760
}
5861
}
@@ -79,25 +82,30 @@ private void EmitGetValueMethods()
7982
if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.GetValue_TypeOf_key))
8083
{
8184
StartMethodDefinition(MethodsToGen_ConfigurationBinder.GetValue_TypeOf_key, documentation);
82-
_writer.WriteLine($"public static object? {Identifier.GetValue}(this {Identifier.IConfiguration} {Identifier.configuration}, {Identifier.Type} {Identifier.type}, string {Identifier.key}) => " +
85+
_writer.WriteLine($"public static object? {Identifier.GetValue}(this {Identifier.IConfiguration} {Identifier.configuration}, Type {Identifier.type}, string {Identifier.key}) => " +
8386
$"{expressionForGetValueCore}({Identifier.configuration}, {Identifier.type}, {Identifier.key});");
8487
}
8588

8689
if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.GetValue_TypeOf_key_defaultValue))
8790
{
8891
StartMethodDefinition(MethodsToGen_ConfigurationBinder.GetValue_TypeOf_key_defaultValue, documentation);
89-
_writer.WriteLine($"public static object? {Identifier.GetValue}(this {Identifier.IConfiguration} {Identifier.configuration}, {Identifier.Type} {Identifier.type}, string {Identifier.key}, object? {Identifier.defaultValue}) => " +
92+
_writer.WriteLine($"public static object? {Identifier.GetValue}(this {Identifier.IConfiguration} {Identifier.configuration}, Type {Identifier.type}, string {Identifier.key}, object? {Identifier.defaultValue}) => " +
9093
$"{expressionForGetValueCore}({Identifier.configuration}, {Identifier.type}, {Identifier.key}) ?? {Identifier.defaultValue};");
9194
}
9295
}
9396

9497
private void EmitBindMethods_ConfigurationBinder()
9598
{
99+
if (!ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Bind))
100+
{
101+
return;
102+
}
103+
96104
string objParamExpr = $"object? {Identifier.obj}";
97105

98106
if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Bind_instance))
99107
{
100-
EmitMethodImplementation(
108+
EmitMethods(
101109
MethodsToGen_ConfigurationBinder.Bind_instance,
102110
additionalParams: objParamExpr,
103111
configExpression: Identifier.configuration,
@@ -106,7 +114,7 @@ private void EmitBindMethods_ConfigurationBinder()
106114

107115
if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Bind_instance_BinderOptions))
108116
{
109-
EmitMethodImplementation(
117+
EmitMethods(
110118
MethodsToGen_ConfigurationBinder.Bind_instance_BinderOptions,
111119
additionalParams: $"{objParamExpr}, {TypeDisplayString.NullableActionOfBinderOptions} {Identifier.configureOptions}",
112120
configExpression: Identifier.configuration,
@@ -115,21 +123,41 @@ private void EmitBindMethods_ConfigurationBinder()
115123

116124
if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Bind_key_instance))
117125
{
118-
EmitMethodImplementation(
126+
EmitMethods(
119127
MethodsToGen_ConfigurationBinder.Bind_key_instance,
120128
additionalParams: $"string {Identifier.key}, {objParamExpr}",
121-
configExpression: $"{Identifier.configuration}?.{Identifier.GetSection}({Identifier.key})",
129+
configExpression: $"{Expression.configurationGetSection}({Identifier.key})",
122130
configureOptions: false);
123131
}
124132

125-
void EmitMethodImplementation(MethodsToGen_ConfigurationBinder method, string additionalParams, string configExpression, bool configureOptions)
133+
void EmitMethods(MethodsToGen_ConfigurationBinder method, string additionalParams, string configExpression, bool configureOptions)
126134
{
127-
string configureOptionsArg = configureOptions ? Identifier.configureOptions : $"{Identifier.configureOptions}: null";
128-
string returnExpression = $"{Identifier.BindCoreMain}({configExpression}, {Identifier.obj}, {configureOptionsArg})";
129-
130-
StartMethodDefinition(method, "Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively.");
131-
_writer.WriteLine($"public static void {Identifier.Bind}(this {Identifier.IConfiguration} {Identifier.configuration}, {additionalParams}) => "
132-
+ $"{returnExpression};");
135+
foreach (KeyValuePair<TypeSpec, List<InterceptorLocationInfo>> pair in _sourceGenSpec.InterceptionInfo_ConfigBinder.GetOverloadInfo(method))
136+
{
137+
(TypeSpec type, List<InterceptorLocationInfo> interceptorInfoList) = (pair.Key, pair.Value);
138+
139+
EmitBlankLineIfRequired();
140+
_writer.WriteLine($"/// <summary>Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively.</summary>");
141+
EmitInterceptsLocationAnnotations(interceptorInfoList);
142+
EmitStartBlock($"public static void {Identifier.Bind}_{type.DisplayString.ToIdentifierSubstring()}(this {Identifier.IConfiguration} {Identifier.configuration}, {additionalParams})");
143+
144+
if (!EmitInitException(type) && type.NeedsMemberBinding)
145+
{
146+
string binderOptionsArg = configureOptions ? $"{Identifier.GetBinderOptions}({Identifier.configureOptions})" : $"{Identifier.binderOptions}: null";
147+
148+
EmitCheckForNullArgument_WithBlankLine(Identifier.configuration);
149+
if (!type.IsValueType)
150+
{
151+
EmitCheckForNullArgument_WithBlankLine(Identifier.obj);
152+
}
153+
_writer.WriteLine($$"""
154+
var {{Identifier.typedObj}} = ({{type.EffectiveType.DisplayString}}){{Identifier.obj}};
155+
{{nameof(MethodsToGen_CoreBindingHelper.BindCore)}}({{configExpression}}, ref {{Identifier.typedObj}}, {{binderOptionsArg}});
156+
""");
157+
}
158+
159+
EmitEndBlock();
160+
}
133161
}
134162
}
135163

0 commit comments

Comments
 (0)