From b764c56ed72a6ce107676e6cd47ebfa1504ae840 Mon Sep 17 00:00:00 2001 From: Layomi Akinrinade Date: Thu, 10 Aug 2023 11:58:42 -0700 Subject: [PATCH 1/6] Use Roslyn interceptors feature in binder gen --- docs/project/list-of-diagnostics.md | 2 + .../src/SourceGenerators/GeneratorHelpers.cs | 12 ++ .../ConfigurationBindingGenerator.Emitter.cs | 95 +++++---- .../ConfigurationBindingGenerator.Parser.cs | 29 ++- ...onfigurationBindingGenerator.Suppressor.cs | 64 ++++++ .../gen/ConfigurationBindingGenerator.cs | 14 +- .../Helpers/Emitter/ConfigurationBinder.cs | 134 +++++------- ...BindingHelper.cs => CoreBindingHelpers.cs} | 173 +++++++-------- .../gen/Helpers/Emitter/Helpers.cs | 142 ++++++------- .../OptionsBuilderConfigurationExtensions.cs | 41 ++-- ...onfigurationServiceCollectionExtensions.cs | 49 ++--- .../gen/Helpers/InterceptorLocationInfo.cs | 38 ++++ .../gen/Helpers/MethodsToGen.cs | 9 +- .../gen/Helpers/Parser/BinderInvocation.cs | 2 +- .../gen/Helpers/Parser/ConfigurationBinder.cs | 22 +- .../OptionsBuilderConfigurationExtensions.cs | 44 ++-- ...onfigurationServiceCollectionExtensions.cs | 4 +- ...nfiguration.Binder.SourceGeneration.csproj | 7 +- .../gen/Model/ObjectSpec.cs | 2 +- .../gen/Model/ParsableFromStringSpec.cs | 2 +- .../gen/Model/SourceGenerationSpec.cs | 6 +- .../gen/Model/TypeSpec.cs | 9 +- .../tests/Common/ConfigurationBinderTests.cs | 2 + .../Baselines/Collections.generated.txt | 149 ++++++++++--- .../ConfigurationBinder/Bind.generated.txt | 132 +++++++++--- .../Bind_Instance.generated.txt | 136 +++++++++--- .../Bind_Instance_BinderOptions.generated.txt | 120 ++++++++--- .../Bind_Key_Instance.generated.txt | 136 +++++++++--- .../ConfigurationBinder/Get.generated.txt | 150 +++++++++---- .../GetValue.generated.txt | 48 +++-- .../GetValue_T_Key.generated.txt | 29 ++- .../GetValue_T_Key_DefaultValue.generated.txt | 29 ++- .../GetValue_TypeOf_Key.generated.txt | 29 ++- ...alue_TypeOf_Key_DefaultValue.generated.txt | 29 ++- .../ConfigurationBinder/Get_T.generated.txt | 121 ++++++++--- .../Get_T_BinderOptions.generated.txt | 121 ++++++++--- .../Get_TypeOf.generated.txt | 65 +++++- .../Get_TypeOf_BinderOptions.generated.txt | 65 +++++- .../BindConfiguration.generated.txt | 98 +++++---- .../OptionsBuilder/Bind_T.generated.txt | 129 +++++++----- .../Bind_T_BinderOptions.generated.txt | 117 ++++++----- .../Baselines/Primitives.generated.txt | 197 ++++++++++++------ .../Configure_T.generated.txt | 140 ++++++++----- .../Configure_T_BinderOptions.generated.txt | 140 ++++++++----- .../Configure_T_name.generated.txt | 140 ++++++++----- ...nfigure_T_name_BinderOptions.generated.txt | 130 ++++++++---- ...BindingGeneratorTests.Baselines.Options.cs | 4 + ...gurationBindingGeneratorTests.Baselines.cs | 4 +- .../ConfigurationBindingGeneratorTests.cs | 18 +- ...ation.Binder.SourceGeneration.Tests.csproj | 2 +- ...ft.Extensions.Logging.Configuration.csproj | 2 + ...icrosoft.Extensions.Logging.Console.csproj | 2 + ...onExtensions.SourceGeneration.Tests.csproj | 4 +- .../gen/Emitter.cs | 16 +- ...Extensions.Options.SourceGeneration.csproj | 1 + 55 files changed, 2296 insertions(+), 1109 deletions(-) create mode 100644 src/libraries/Common/src/SourceGenerators/GeneratorHelpers.cs create mode 100644 src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Suppressor.cs rename src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/{CoreBindingHelper.cs => CoreBindingHelpers.cs} (85%) create mode 100644 src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/InterceptorLocationInfo.cs diff --git a/docs/project/list-of-diagnostics.md b/docs/project/list-of-diagnostics.md index 4f78e9e711653..579b8d160d59b 100644 --- a/docs/project/list-of-diagnostics.md +++ b/docs/project/list-of-diagnostics.md @@ -270,3 +270,5 @@ The diagnostic id values reserved for .NET Libraries analyzer warnings are `SYSL | Suppression ID | Suppressed Diagnostic ID | Description | | :----------------------- | :----------------------- | :---------- | | __`SYSLIBSUPPRESS0001`__ | CA1822 | Do not offer to make methods static when the methods need to be instance methods for a custom marshaller shape. | +| __`SYSLIBSUPPRESS0002`__ | IL2026 | ConfigurationBindingGenerator: suppress RequiresUnreferencedCode diagnostic for binding call that has been intercepted by a generated static variant. | +| __`SYSLIBSUPPRESS0003`__ | IL3050 | ConfigurationBindingGenerator: suppress RequiresDynamicCode diagnostic for binding call that has been intercepted by a generated static variant. | diff --git a/src/libraries/Common/src/SourceGenerators/GeneratorHelpers.cs b/src/libraries/Common/src/SourceGenerators/GeneratorHelpers.cs new file mode 100644 index 0000000000000..1ec1d1883302c --- /dev/null +++ b/src/libraries/Common/src/SourceGenerators/GeneratorHelpers.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace SourceGenerators +{ + public static class GeneratorHelpers + { + public static string MakeNameUnique(ref string name) => name += $"_{new Random().Next():X8}"; + } +} diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Emitter.cs index a40cf2976b31f..351c211c6c18a 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Emitter.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Immutable; using System.Diagnostics; using System.Text.RegularExpressions; using Microsoft.CodeAnalysis; @@ -16,23 +17,28 @@ private sealed partial class Emitter private readonly SourceProductionContext _context; private readonly SourceGenerationSpec _sourceGenSpec; + private readonly string _generatedNamespaceName = ProjectName; private bool _emitBlankLineBeforeNextStatement; - private bool _useFullyQualifiedNames; private int _valueSuffixIndex; private static readonly Regex s_arrayBracketsRegex = new(Regex.Escape("[]")); private readonly SourceWriter _writer = new(); - public Emitter(SourceProductionContext context, SourceGenerationSpec sourceGenSpec) + public Emitter(SourceProductionContext context, SourceGenerationSpec sourceGenSpec, bool emitUniqueHelperNames) { _context = context; _sourceGenSpec = sourceGenSpec; + + if (emitUniqueHelperNames) + { + GeneratorHelpers.MakeNameUnique(ref _generatedNamespaceName); + } } public void Emit() { - if (!ShouldEmitBinders()) + if (!ShouldEmitBindingExtensions()) { return; } @@ -42,17 +48,26 @@ public void Emit() #nullable enable #pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code. """); - _writer.WriteLine(); - _useFullyQualifiedNames = true; - EmitBinder_Extensions_IConfiguration(); - EmitBinder_Extensions_OptionsBuilder(); - EmitBinder_Extensions_IServiceCollection(); + EmitInterceptsLocationAttrDecl(); - _useFullyQualifiedNames = false; - Emit_CoreBindingHelper(); + EmitStartBlock($"namespace {_generatedNamespaceName}"); + EmitUsingStatements(); + + _writer.WriteLine(); + EmitStartBlock($$""" + {{Expression.GeneratedCodeAnnotation}} + file static class {{Identifier.BindingExtensions}} + """); + EmitBindingExtensions_IConfiguration(); + EmitBindingExtensions_OptionsBuilder(); + EmitBindingExtensions_IServiceCollection(); + EmitCoreBindingHelpers(); + EmitEndBlock(); // BindingExtensions class - _context.AddSource($"{Identifier.GeneratedConfigurationBinder}.g.cs", _writer.ToSourceText()); + EmitEndBlock(); // Binding namespace. + + _context.AddSource($"{Identifier.BindingExtensions}.g.cs", _writer.ToSourceText()); } private void EmitBindCoreCall( @@ -74,7 +89,7 @@ private void EmitBindCoreCall( if (initKind is InitializationKind.AssignmentWithNullCheck) { Debug.Assert(!type.IsValueType); - _writer.WriteLine($"{type.MinimalDisplayString}? {tempIdentifier} = {memberAccessExpr};"); + _writer.WriteLine($"{type.DisplayString}? {tempIdentifier} = {memberAccessExpr};"); EmitBindCoreCall(tempIdentifier, InitializationKind.AssignmentWithNullCheck); } else if (initKind is InitializationKind.None && type.IsValueType) @@ -89,9 +104,7 @@ private void EmitBindCoreCall( void EmitBindCoreCall(string objExpression, InitializationKind initKind) { - string methodDisplayString = GetHelperMethodDisplayString(nameof(MethodsToGen_CoreBindingHelper.BindCore)); - string bindCoreCall = $@"{methodDisplayString}({configArgExpr}, ref {objExpression}, {Identifier.binderOptions});"; - + string bindCoreCall = $@"{nameof(MethodsToGen_CoreBindingHelper.BindCore)}({configArgExpr}, ref {objExpression}, {Identifier.binderOptions});"; EmitObjectInit(objExpression, initKind); _writer.WriteLine(bindCoreCall); writeOnSuccess?.Invoke(objExpression); @@ -127,12 +140,11 @@ private void EmitBindLogicFromString( } else if (typeKind is StringParsableTypeKind.Enum) { - parsedValueExpr = $"ParseEnum<{type.MinimalDisplayString}>({stringValueToParse_Expr}, () => {sectionPathExpr})"; + parsedValueExpr = $"ParseEnum<{type.DisplayString}>({stringValueToParse_Expr}, () => {sectionPathExpr})"; } else { - string helperMethodDisplayString = GetHelperMethodDisplayString(type.ParseMethodName); - parsedValueExpr = $"{helperMethodDisplayString}({stringValueToParse_Expr}, () => {sectionPathExpr})"; + parsedValueExpr = $"{type.ParseMethodName}({stringValueToParse_Expr}, () => {sectionPathExpr})"; } if (!checkForNullSectionValue) @@ -156,7 +168,7 @@ private bool EmitObjectInit(TypeSpec type, string memberAccessExpr, Initializati string initExpr; CollectionSpec? collectionType = type as CollectionSpec; - string effectiveDisplayString = GetTypeDisplayString(type); + string effectiveDisplayString = type.DisplayString; if (collectionType is not null) { if (collectionType is EnumerableSpec { InitializationStrategy: InitializationStrategy.Array }) @@ -165,7 +177,7 @@ private bool EmitObjectInit(TypeSpec type, string memberAccessExpr, Initializati } else { - effectiveDisplayString = GetTypeDisplayString(collectionType.ConcreteType ?? collectionType); + effectiveDisplayString = (collectionType.ConcreteType ?? collectionType).DisplayString; initExpr = $"new {effectiveDisplayString}()"; } } @@ -215,36 +227,43 @@ private bool EmitObjectInit(TypeSpec type, string memberAccessExpr, Initializati return true; } - private void EmitCastToIConfigurationSection() + private void EmitInterceptsLocationAttrDecl() { - string sectionTypeDisplayString; - string exceptionTypeDisplayString; - if (_useFullyQualifiedNames) - { - sectionTypeDisplayString = "global::Microsoft.Extensions.Configuration.IConfigurationSection"; - exceptionTypeDisplayString = FullyQualifiedDisplayString.InvalidOperationException; - } - else - { - sectionTypeDisplayString = Identifier.IConfigurationSection; - exceptionTypeDisplayString = nameof(InvalidOperationException); - } - + _writer.WriteLine(); _writer.WriteLine($$""" - if ({{Identifier.configuration}} is not {{sectionTypeDisplayString}} {{Identifier.section}}) + namespace System.Runtime.CompilerServices { - throw new {{exceptionTypeDisplayString}}(); + using System; + using System.CodeDom.Compiler; + + {{Expression.GeneratedCodeAnnotation}} + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : Attribute + { + public InterceptsLocationAttribute(string filePath, int line, int column) + { + } + } } """); + _writer.WriteLine(); + } + + private void EmitUsingStatements() + { + foreach (string @namespace in _sourceGenSpec.Namespaces.ToImmutableSortedSet()) + { + _writer.WriteLine($"using {@namespace};"); + } } private void EmitIConfigurationHasValueOrChildrenCheck(bool voidReturn) { string returnPostfix = voidReturn ? string.Empty : " null"; - string methodDisplayString = GetHelperMethodDisplayString(Identifier.HasValueOrChildren); + string methodIdentifier = Identifier.HasValueOrChildren; _writer.WriteLine($$""" - if (!{{methodDisplayString}}({{Identifier.configuration}})) + if (!{{methodIdentifier}}({{Identifier.configuration}})) { return{{returnPostfix}}; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Parser.cs index b0f53ef074078..874d2cf9eab60 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Parser.cs @@ -1,12 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Operations; namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { @@ -147,7 +149,7 @@ type.TypeKind is TypeKind.TypeParameter or TypeKind.Pointer or TypeKind.Error || { // List is used in generated code as a temp holder for formatting // an error for config properties that don't map to object properties. - _sourceGenSpec.TypeNamespaces.Add("System.Collections.Generic"); + _sourceGenSpec.Namespaces.Add("System.Collections.Generic"); spec = CreateObjectSpec(namedType); } @@ -169,7 +171,7 @@ type.TypeKind is TypeKind.TypeParameter or TypeKind.Pointer or TypeKind.Error || string @namespace = spec.Namespace; if (@namespace is not null and not "") { - _sourceGenSpec.TypeNamespaces.Add(@namespace); + _sourceGenSpec.Namespaces.Add(@namespace); } return _createdSpecs[type] = spec; @@ -186,15 +188,24 @@ private void RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper method, Typ _sourceGenSpec.MethodsToGen_CoreBindingHelper |= method; } - private void RegisterTypeForBindCoreUntypedGen(TypeSpec typeSpec) + private void RegisterTypeForBindCoreGen(TypeSpec typeSpec) { if (typeSpec.NeedsMemberBinding) { RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.BindCore, typeSpec); - RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.BindCoreUntyped, typeSpec); } } + private void RegisterGenMethodAsInterceptor(Enum method, IInvocationOperation operation) + { + if (!_sourceGenSpec.GenMethodsInterceptionInfo.TryGetValue(method, out List? info)) + { + _sourceGenSpec.GenMethodsInterceptionInfo[method] = info = new List(); + } + + info.Add(new InterceptorLocationInfo(operation)); + } + private static bool IsNullable(ITypeSymbol type, [NotNullWhen(true)] out ITypeSymbol? underlyingType) { if (type is INamedTypeSymbol { IsGenericType: true } genericType && @@ -335,7 +346,7 @@ private bool TryGetTypeSpec(ITypeSymbol type, DiagnosticDescriptor descriptor, o // We want a BindCore method for List as a temp holder for the array values. We know the element type is supported. EnumerableSpec listSpec = (GetOrCreateTypeSpec(_typeSymbols.List.Construct(arrayType.ElementType)) as EnumerableSpec)!; - RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.BindCore, listSpec); + RegisterTypeForBindCoreGen(listSpec); EnumerableSpec spec = new EnumerableSpec(arrayType) { @@ -347,7 +358,7 @@ private bool TryGetTypeSpec(ITypeSymbol type, DiagnosticDescriptor descriptor, o }; Debug.Assert(spec.CanInitialize); - RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.BindCore, spec); + RegisterTypeForBindCoreGen(spec); return spec; } @@ -383,7 +394,7 @@ private bool IsSupportedArrayType(ITypeSymbol type) if (spec is not null) { - RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.BindCore, spec); + RegisterTypeForBindCoreGen(spec); spec.InitExceptionMessage ??= spec.ElementType.InitExceptionMessage; } @@ -442,7 +453,7 @@ private DictionarySpec CreateDictionarySpec(INamedTypeSymbol type, ITypeSymbol k constructionStrategy = InitializationStrategy.ToEnumerableMethod; populationStrategy = CollectionPopulationStrategy.Cast_Then_Add; toEnumerableMethodCall = "ToDictionary(pair => pair.Key, pair => pair.Value)"; - _sourceGenSpec.TypeNamespaces.Add("System.Linq"); + _sourceGenSpec.Namespaces.Add("System.Linq"); } else { @@ -711,7 +722,7 @@ private DictionarySpec CreateDictionarySpec(INamedTypeSymbol type, ITypeSymbol k if (objectSpec.NeedsMemberBinding) { - RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.BindCore, objectSpec); + RegisterTypeForBindCoreGen(objectSpec); } return objectSpec; diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Suppressor.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Suppressor.cs new file mode 100644 index 0000000000000..98af88f143fcc --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Suppressor.cs @@ -0,0 +1,64 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration +{ + public sealed partial class ConfigurationBindingGenerator + { + /// + /// Supresses false-positive diagnostics emitted by the linker analyzers + /// when analyzing binding invocations that we have substituted.Workaround + /// for https://github.com/dotnet/roslyn/issues/68669. + /// + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class Suppressor : DiagnosticSuppressor + { + private const string Justification = "The target method has been intercepted by a generated static variant."; + + private static readonly SuppressionDescriptor RUCDiagnostic = new(id: "SYSLIBSUPPRESS0002", suppressedDiagnosticId: "IL2026", Justification); + + private static readonly SuppressionDescriptor RDCDiagnostic = new(id: "SYSLIBSUPPRESS0003", suppressedDiagnosticId: "IL3050", Justification); + + public override ImmutableArray SupportedSuppressions => ImmutableArray.Create(RUCDiagnostic, RDCDiagnostic); + + public override void ReportSuppressions(SuppressionAnalysisContext context) + { + foreach (Diagnostic diagnostic in context.ReportedDiagnostics) + { + string diagnosticId = diagnostic.Id; + + if (diagnosticId != RDCDiagnostic.SuppressedDiagnosticId && diagnosticId != RUCDiagnostic.SuppressedDiagnosticId) + { + continue; + } + + Location location = diagnostic.AdditionalLocations.Count > 0 + ? diagnostic.AdditionalLocations[0] + : diagnostic.Location; + + bool shouldSuppressDiagnostic = + location.SourceTree is SyntaxTree sourceTree && + sourceTree.GetRoot().FindNode(location.SourceSpan) is SyntaxNode syntaxNode && + BinderInvocation.IsCandidateSyntaxNode(syntaxNode) && + context.GetSemanticModel(sourceTree) + .GetOperation((InvocationExpressionSyntax)syntaxNode, context.CancellationToken) is IInvocationOperation operation && + BinderInvocation.IsBindingOperation(operation); + + if (shouldSuppressDiagnostic) + { + SuppressionDescriptor targetSuppression = diagnosticId == RUCDiagnostic.SuppressedDiagnosticId + ? RUCDiagnostic + : RDCDiagnostic; + context.ReportSuppression(Suppression.Create(targetSuppression, diagnostic)); + } + } + } + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs index 70da582dddf0c..870a7410344a9 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs @@ -5,7 +5,6 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { @@ -15,7 +14,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration [Generator] public sealed partial class ConfigurationBindingGenerator : IIncrementalGenerator { - internal const string ProjectName = "Microsoft.Extensions.Configuration.Binder.SourceGeneration"; + private static readonly string ProjectName = Emitter.s_assemblyName.Name; + + public bool EmitUniqueHelperNames { get; init; } = true; public void Initialize(IncrementalGeneratorInitializationContext context) { @@ -42,10 +43,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) context.RegisterSourceOutput(inputData, (spc, source) => Execute(source.Item1, source.Item2, spc)); } - /// - /// Generates source code to optimize binding with ConfigurationBinder. - /// - private static void Execute(CompilationData compilationData, ImmutableArray inputCalls, SourceProductionContext context) + private void Execute(CompilationData compilationData, ImmutableArray inputCalls, SourceProductionContext context) { if (inputCalls.IsDefaultOrEmpty) { @@ -61,7 +59,7 @@ private static void Execute(CompilationData compilationData, ImmutableArray= LanguageVersion.CSharp11; + LanguageVersionIsSupported = compilation.LanguageVersion >= LanguageVersion.Preview; if (LanguageVersionIsSupported) { TypeSymbols = new KnownTypeSymbols(compilation); diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/ConfigurationBinder.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/ConfigurationBinder.cs index c10e607df75d0..6040f0b63c8ca 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/ConfigurationBinder.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/ConfigurationBinder.cs @@ -1,11 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using Microsoft.CodeAnalysis; - namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { public sealed partial class ConfigurationBindingGenerator @@ -14,168 +9,135 @@ private sealed partial class Emitter { private bool ShouldEmitMethods(MethodsToGen_ConfigurationBinder methods) => (_sourceGenSpec.MethodsToGen_ConfigurationBinder & methods) != 0; - private void EmitBinder_Extensions_IConfiguration() + private void EmitBindingExtensions_IConfiguration() { - Debug.Assert(_sourceGenSpec.TypesForGen_ConfigurationBinder_BindMethods.Count <= 3 && - !_sourceGenSpec.TypesForGen_ConfigurationBinder_BindMethods.Keys.Any(overload => (overload & MethodsToGen_ConfigurationBinder.Bind) is 0)); - if (!ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Any)) { return; } - _emitBlankLineBeforeNextStatement = false; - EmitRootBindingClassStartBlock(Identifier.GeneratedConfigurationBinder); - + EmitBindingExtStartRegion(Identifier.IConfiguration); EmitGetMethods(); EmitGetValueMethods(); EmitBindMethods_ConfigurationBinder(); - - EmitEndBlock(); - _emitBlankLineBeforeNextStatement = true; + EmitBindingExtEndRegion(); } private void EmitGetMethods() { - const string expressionForGetCore = $"{FullyQualifiedDisplayString.CoreBindingHelper}.{nameof(MethodsToGen_CoreBindingHelper.GetCore)}"; + const string expressionForGetCore = nameof(MethodsToGen_CoreBindingHelper.GetCore); const string documentation = "Attempts to bind the configuration instance to a new instance of type T."; if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Get_T)) { - StartMethodDefinition(documentation); - _writer.WriteLine($"public static T? {Identifier.Get}(this {FullyQualifiedDisplayString.IConfiguration} {Identifier.configuration}) => " + + StartMethodDefinition(MethodsToGen_ConfigurationBinder.Get_T, documentation); + _writer.WriteLine($"public static T? {Identifier.Get}(this {Identifier.IConfiguration} {Identifier.configuration}) => " + $"(T?)({expressionForGetCore}({Identifier.configuration}, typeof(T), {Identifier.configureOptions}: null) ?? default(T));"); } if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Get_T_BinderOptions)) { - StartMethodDefinition(documentation); - _writer.WriteLine($"public static T? {Identifier.Get}(this {FullyQualifiedDisplayString.IConfiguration} {Identifier.configuration}, {FullyQualifiedDisplayString.ActionOfBinderOptions}? {Identifier.configureOptions}) => " + + StartMethodDefinition(MethodsToGen_ConfigurationBinder.Get_T_BinderOptions, documentation); + _writer.WriteLine($"public static T? {Identifier.Get}(this {Identifier.IConfiguration} {Identifier.configuration}, {TypeDisplayString.NullableActionOfBinderOptions} {Identifier.configureOptions}) => " + $"(T?)({expressionForGetCore}({Identifier.configuration}, typeof(T), {Identifier.configureOptions}) ?? default(T));"); } if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Get_TypeOf)) { - StartMethodDefinition(documentation); - _writer.WriteLine($"public static object? {Identifier.Get}(this {FullyQualifiedDisplayString.IConfiguration} {Identifier.configuration}, {FullyQualifiedDisplayString.Type} {Identifier.type}) => " + + StartMethodDefinition(MethodsToGen_ConfigurationBinder.Get_TypeOf, documentation); + _writer.WriteLine($"public static object? {Identifier.Get}(this {Identifier.IConfiguration} {Identifier.configuration}, {Identifier.Type} {Identifier.type}) => " + $"{expressionForGetCore}({Identifier.configuration}, {Identifier.type}, {Identifier.configureOptions}: null);"); } if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Get_TypeOf_BinderOptions)) { - StartMethodDefinition(documentation); - _writer.WriteLine($"public static object? {Identifier.Get}(this {FullyQualifiedDisplayString.IConfiguration} {Identifier.configuration}, {FullyQualifiedDisplayString.Type} {Identifier.type}, {FullyQualifiedDisplayString.ActionOfBinderOptions}? {Identifier.configureOptions}) => " + + StartMethodDefinition(MethodsToGen_ConfigurationBinder.Get_TypeOf_BinderOptions, documentation); + _writer.WriteLine($"public static object? {Identifier.Get}(this {Identifier.IConfiguration} {Identifier.configuration}, {Identifier.Type} {Identifier.type}, {TypeDisplayString.NullableActionOfBinderOptions} {Identifier.configureOptions}) => " + $"{expressionForGetCore}({Identifier.configuration}, {Identifier.type}, {Identifier.configureOptions});"); } } private void EmitGetValueMethods() { - const string expressionForGetValueCore = $"{FullyQualifiedDisplayString.CoreBindingHelper}.{nameof(MethodsToGen_CoreBindingHelper.GetValueCore)}"; + const string expressionForGetValueCore = $"{Identifier.BindingExtensions}.{nameof(MethodsToGen_CoreBindingHelper.GetValueCore)}"; const string documentation = "Extracts the value with the specified key and converts it to the specified type."; if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.GetValue_T_key)) { - StartMethodDefinition(documentation); - _writer.WriteLine($"public static T? {Identifier.GetValue}(this {FullyQualifiedDisplayString.IConfiguration} {Identifier.configuration}, string {Identifier.key}) => " + + StartMethodDefinition(MethodsToGen_ConfigurationBinder.GetValue_T_key, documentation); + _writer.WriteLine($"public static T? {Identifier.GetValue}(this {Identifier.IConfiguration} {Identifier.configuration}, string {Identifier.key}) => " + $"(T?)({expressionForGetValueCore}({Identifier.configuration}, typeof(T), {Identifier.key}) ?? default(T));"); } if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.GetValue_T_key_defaultValue)) { - StartMethodDefinition(documentation); - _writer.WriteLine($"public static T? {Identifier.GetValue}(this {FullyQualifiedDisplayString.IConfiguration} {Identifier.configuration}, string {Identifier.key}, T {Identifier.defaultValue}) => " + + StartMethodDefinition(MethodsToGen_ConfigurationBinder.GetValue_T_key_defaultValue, documentation); + _writer.WriteLine($"public static T? {Identifier.GetValue}(this {Identifier.IConfiguration} {Identifier.configuration}, string {Identifier.key}, T {Identifier.defaultValue}) => " + $"(T?)({expressionForGetValueCore}({Identifier.configuration}, typeof(T), {Identifier.key}) ?? {Identifier.defaultValue});"); } if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.GetValue_TypeOf_key)) { - StartMethodDefinition(documentation); - _writer.WriteLine($"public static object? {Identifier.GetValue}(this {FullyQualifiedDisplayString.IConfiguration} {Identifier.configuration}, {FullyQualifiedDisplayString.Type} {Identifier.type}, string {Identifier.key}) => " + + StartMethodDefinition(MethodsToGen_ConfigurationBinder.GetValue_TypeOf_key, documentation); + _writer.WriteLine($"public static object? {Identifier.GetValue}(this {Identifier.IConfiguration} {Identifier.configuration}, {Identifier.Type} {Identifier.type}, string {Identifier.key}) => " + $"{expressionForGetValueCore}({Identifier.configuration}, {Identifier.type}, {Identifier.key});"); } if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.GetValue_TypeOf_key_defaultValue)) { - StartMethodDefinition(documentation); - _writer.WriteLine($"public static object? {Identifier.GetValue}(this {FullyQualifiedDisplayString.IConfiguration} {Identifier.configuration}, {FullyQualifiedDisplayString.Type} {Identifier.type}, string {Identifier.key}, object? {Identifier.defaultValue}) => " + + StartMethodDefinition(MethodsToGen_ConfigurationBinder.GetValue_TypeOf_key_defaultValue, documentation); + _writer.WriteLine($"public static object? {Identifier.GetValue}(this {Identifier.IConfiguration} {Identifier.configuration}, {Identifier.Type} {Identifier.type}, string {Identifier.key}, object? {Identifier.defaultValue}) => " + $"{expressionForGetValueCore}({Identifier.configuration}, {Identifier.type}, {Identifier.key}) ?? {Identifier.defaultValue};"); } } private void EmitBindMethods_ConfigurationBinder() { - if (!ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Bind)) - { - return; - } - - Dictionary> types = _sourceGenSpec.TypesForGen_ConfigurationBinder_BindMethods; + string objParamExpr = $"object? {Identifier.obj}"; - if (types.TryGetValue(MethodsToGen_ConfigurationBinder.Bind_instance, out HashSet? typeSpecs)) + if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Bind_instance)) { - foreach (TypeSpec type in typeSpecs) - { - EmitMethodImplementation( - type, - additionalParams: GetObjParameter(type), - configExpression: Identifier.configuration, - configureOptions: false); - } + EmitMethodImplementation( + MethodsToGen_ConfigurationBinder.Bind_instance, + additionalParams: objParamExpr, + configExpression: Identifier.configuration, + configureOptions: false); } - if (types.TryGetValue(MethodsToGen_ConfigurationBinder.Bind_instance_BinderOptions, out typeSpecs)) + if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Bind_instance_BinderOptions)) { - foreach (TypeSpec type in typeSpecs) - { - EmitMethodImplementation( - type, - additionalParams: $"{GetObjParameter(type)}, {FullyQualifiedDisplayString.ActionOfBinderOptions}? {Identifier.configureOptions}", - configExpression: Identifier.configuration, - configureOptions: true); - } + EmitMethodImplementation( + MethodsToGen_ConfigurationBinder.Bind_instance_BinderOptions, + additionalParams: $"{objParamExpr}, {TypeDisplayString.NullableActionOfBinderOptions} {Identifier.configureOptions}", + configExpression: Identifier.configuration, + configureOptions: true); } - if (types.TryGetValue(MethodsToGen_ConfigurationBinder.Bind_key_instance, out typeSpecs)) + if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Bind_key_instance)) { - foreach (TypeSpec type in typeSpecs) - { - EmitMethodImplementation( - type, - additionalParams: $"string {Identifier.key}, {GetObjParameter(type)}", - configExpression: $"{Expression.configurationGetSection}({Identifier.key})", - configureOptions: false); - } + EmitMethodImplementation( + MethodsToGen_ConfigurationBinder.Bind_key_instance, + additionalParams: $"string {Identifier.key}, {objParamExpr}", + configExpression: $"{Identifier.configuration}?.{Identifier.GetSection}({Identifier.key})", + configureOptions: false); } - void EmitMethodImplementation(TypeSpec type, string additionalParams, string configExpression, bool configureOptions) + void EmitMethodImplementation(MethodsToGen_ConfigurationBinder method, string additionalParams, string configExpression, bool configureOptions) { - string binderOptionsArg = configureOptions ? $"{Expression.GetBinderOptions}({Identifier.configureOptions})" : $"{Identifier.binderOptions}: null"; - - string returnExpression; - if (type.CanInitialize) - { - returnExpression = type.NeedsMemberBinding - ? $"{FullyQualifiedDisplayString.CoreBindingHelper}.{nameof(MethodsToGen_CoreBindingHelper.BindCore)}({configExpression}, ref {Identifier.obj}, {binderOptionsArg})" - : "{ }"; - } - else - { - returnExpression = GetInitException(type.InitExceptionMessage); - } - - StartMethodDefinition("Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively."); - _writer.WriteLine($"public static void {Identifier.Bind}(this {FullyQualifiedDisplayString.IConfiguration} {Identifier.configuration}, {additionalParams}) => " + string configureOptionsArg = configureOptions ? Identifier.configureOptions : $"{Identifier.configureOptions}: null"; + string returnExpression = $"{Identifier.BindCoreMain}({configExpression}, {Identifier.obj}, {configureOptionsArg})"; + + StartMethodDefinition(method, "Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively."); + _writer.WriteLine($"public static void {Identifier.Bind}(this {Identifier.IConfiguration} {Identifier.configuration}, {additionalParams}) => " + $"{returnExpression};"); } - - string GetObjParameter(TypeSpec type) => $"{type.FullyQualifiedDisplayString} {Identifier.obj}"; } - private void StartMethodDefinition(string documentation) + private void StartMethodDefinition(MethodsToGen_ConfigurationBinder method, string documentation) { EmitBlankLineIfRequired(); _writer.WriteLine($"/// {documentation}"); + EmitInterceptsLocationAnnotations(method); } } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/CoreBindingHelper.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/CoreBindingHelpers.cs similarity index 85% rename from src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/CoreBindingHelper.cs rename to src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/CoreBindingHelpers.cs index a7b42b1329804..db3f6660561a9 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/CoreBindingHelper.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/CoreBindingHelpers.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using Microsoft.CodeAnalysis; @@ -16,41 +15,19 @@ private sealed partial class Emitter { private bool ShouldEmitMethods(MethodsToGen_CoreBindingHelper methods) => (_sourceGenSpec.MethodsToGen_CoreBindingHelper & methods) != 0; - private void Emit_CoreBindingHelper() + private void EmitCoreBindingHelpers() { Debug.Assert(_emitBlankLineBeforeNextStatement); - _writer.WriteLine(); - _emitBlankLineBeforeNextStatement = false; - - EmitStartBlock($"namespace {ProjectName}"); - EmitHelperUsingStatements(); - - _writer.WriteLine(); - - EmitStartBlock($$""" - /// Provide core binding logic. - {{GetGeneratedCodeAttributeSrc()}} - file static class {{Identifier.CoreBindingHelper}} - """); + EmitBindingExtStartRegion("Core binding"); EmitConfigurationKeyCaches(); EmitGetCoreMethod(); EmitGetValueCoreMethod(); - EmitBindCoreUntypedMethod(); + EmitBindCoreMainMethod(); EmitBindCoreMethods(); EmitInitializeMethods(); EmitHelperMethods(); - - EmitEndBlock(); // End helper class. - EmitEndBlock(); // End namespace. - } - - private void EmitHelperUsingStatements() - { - foreach (string @namespace in _sourceGenSpec.TypeNamespaces.ToImmutableSortedSet()) - { - _writer.WriteLine($"using {@namespace};"); - } + EmitBindingExtEndRegion(); } private void EmitConfigurationKeyCaches() @@ -60,6 +37,8 @@ private void EmitConfigurationKeyCaches() return; } + EmitBlankLineIfRequired(); + foreach (TypeSpec type in targetTypes) { if (type is not ObjectSpec objectType) @@ -73,10 +52,8 @@ private void EmitConfigurationKeyCaches() string configKeysSource = string.Join(", ", keys); string fieldName = GetConfigKeyCacheFieldName(objectType); - _writer.WriteLine($@"private readonly static Lazy<{MinimalDisplayString.HashSetOfString}> {fieldName} = new(() => new {MinimalDisplayString.HashSetOfString}(StringComparer.OrdinalIgnoreCase) {{ {configKeysSource} }});"); + _writer.WriteLine($@"private readonly static Lazy<{TypeDisplayString.HashSetOfString}> {fieldName} = new(() => new {TypeDisplayString.HashSetOfString}(StringComparer.OrdinalIgnoreCase) {{ {configKeysSource} }});"); } - - _emitBlankLineBeforeNextStatement = true; } private void EmitGetCoreMethod() @@ -101,11 +78,17 @@ private void EmitGetCoreMethod() TypeSpec effectiveType = type.EffectiveType; TypeSpecKind kind = effectiveType.SpecKind; - EmitStartBlock($"if (type == typeof({type.MinimalDisplayString}))"); + EmitStartBlock($"if (type == typeof({type.DisplayString}))"); if (effectiveType is ParsableFromStringSpec stringParsableType) { - EmitCastToIConfigurationSection(); + _writer.WriteLine($$""" + if ({{Identifier.configuration}} is not {{Identifier.IConfigurationSection}} {{Identifier.section}}) + { + throw new {{Identifier.InvalidOperationException}}(); + } + """); + EmitBindLogicFromString( stringParsableType, Expression.sectionValue, @@ -124,7 +107,7 @@ private void EmitGetCoreMethod() _writer.WriteLine(); } - Emit_NotSupportedException_TypeNotDetectedAsInput(); + Emit_NotSupportedException_TypeNotDetectedAsInput("'{type}'"); EmitEndBlock(); _emitBlankLineBeforeNextStatement = true; } @@ -154,7 +137,7 @@ private void EmitGetValueCoreMethod() foreach (TypeSpec type in targetTypes) { - EmitStartBlock($"if ({Identifier.type} == typeof({type.MinimalDisplayString}))"); + EmitStartBlock($"if ({Identifier.type} == typeof({type.DisplayString}))"); EmitBindLogicFromString( (ParsableFromStringSpec)type.EffectiveType, @@ -173,18 +156,19 @@ private void EmitGetValueCoreMethod() _emitBlankLineBeforeNextStatement = true; } - private void EmitBindCoreUntypedMethod() + private void EmitBindCoreMainMethod() { - if (!_sourceGenSpec.TypesForGen_CoreBindingHelper_Methods.TryGetValue(MethodsToGen_CoreBindingHelper.BindCoreUntyped, out HashSet? targetTypes)) + if (!_sourceGenSpec.TypesForGen_CoreBindingHelper_Methods.TryGetValue(MethodsToGen_CoreBindingHelper.BindCore, out HashSet? targetTypes)) { return; } EmitBlankLineIfRequired(); - EmitStartBlock($"public static void {nameof(MethodsToGen_CoreBindingHelper.BindCoreUntyped)}(this {Identifier.IConfiguration} {Identifier.configuration}, object {Identifier.obj}, Type {Identifier.type}, {MinimalDisplayString.NullableActionOfBinderOptions} {Identifier.configureOptions})"); + EmitStartBlock($"public static void {Identifier.BindCoreMain}({Identifier.IConfiguration}? {Identifier.configuration}, object? {Identifier.obj}, {TypeDisplayString.NullableActionOfBinderOptions} {Identifier.configureOptions})"); EmitCheckForNullArgument_WithBlankLine(Identifier.configuration); + EmitCheckForNullArgument_WithBlankLine(Identifier.obj); _writer.WriteLine($"{Identifier.BinderOptions}? {Identifier.binderOptions} = {Identifier.GetBinderOptions}({Identifier.configureOptions});"); _writer.WriteLine(); @@ -193,13 +177,24 @@ private void EmitBindCoreUntypedMethod() foreach (TypeSpec type in targetTypes) { - EmitStartBlock($"if (type == typeof({type.MinimalDisplayString}))"); + string typedObjIdentifier = GetIncrementalIdentifier(Identifier.typedObj); + EmitStartBlock($"if ({Identifier.obj} is {type.DisplayString} {typedObjIdentifier})"); TypeSpec effectiveType = type.EffectiveType; if (!EmitInitException(effectiveType)) { - _writer.WriteLine($"var {Identifier.temp} = ({effectiveType.MinimalDisplayString}){Identifier.obj};"); - EmitBindCoreCall(type, Identifier.temp, Identifier.configuration, InitializationKind.None); + string objIdentifier; + if (type == effectiveType) + { + objIdentifier = typedObjIdentifier; + } + else + { + objIdentifier = Identifier.temp; + _writer.WriteLine($"var {Identifier.temp} = ({effectiveType.DisplayString}){typedObjIdentifier};"); + } + + EmitBindCoreCall(type, objIdentifier, Identifier.configuration, InitializationKind.None); _writer.WriteLine($"return;"); } @@ -207,7 +202,7 @@ private void EmitBindCoreUntypedMethod() _writer.WriteLine(); } - Emit_NotSupportedException_TypeNotDetectedAsInput(); + Emit_NotSupportedException_TypeNotDetectedAsInput("'{obj.GetType()}'"); EmitEndBlock(); _emitBlankLineBeforeNextStatement = true; } @@ -231,10 +226,13 @@ private void EmitBindCoreMethod(TypeSpec type) { Debug.Assert(type.CanInitialize); - string objParameterExpression = $"ref {type.MinimalDisplayString} {Identifier.obj}"; + string objParameterExpression = $"ref {type.DisplayString} {Identifier.obj}"; EmitStartBlock(@$"public static void {nameof(MethodsToGen_CoreBindingHelper.BindCore)}({Identifier.IConfiguration} {Identifier.configuration}, {objParameterExpression}, {Identifier.BinderOptions}? {Identifier.binderOptions})"); - EmitCheckForNullArgument_WithBlankLine_IfRequired(type.IsValueType); + if (!type.IsValueType) + { + EmitCheckForNullArgument_WithBlankLine(Identifier.obj); + } TypeSpec effectiveType = type.EffectiveType; if (effectiveType is EnumerableSpec enumerable) @@ -281,9 +279,9 @@ private void EmitInitializeMethod(ObjectSpec type) List ctorParams = type.ConstructorParameters; IEnumerable initOnlyProps = type.Properties.Values.Where(prop => prop is { SetOnInit: true }); List ctorArgList = new(); - string displayString = type.MinimalDisplayString; + string displayString = type.DisplayString; - EmitStartBlock($"public static {type.MinimalDisplayString} {GetInitalizeMethodDisplayString(type)}({Identifier.IConfiguration} {Identifier.configuration}, {Identifier.BinderOptions}? {Identifier.binderOptions})"); + EmitStartBlock($"public static {type.DisplayString} {GetInitalizeMethodDisplayString(type)}({Identifier.IConfiguration} {Identifier.configuration}, {Identifier.BinderOptions}? {Identifier.binderOptions})"); _emitBlankLineBeforeNextStatement = false; foreach (ParameterSpec parameter in ctorParams) @@ -335,7 +333,7 @@ void EmitBindImplForMember(MemberSpec member) TypeSpec memberType = member.Type; bool errorOnFailedBinding = member.ErrorOnFailedBinding; - string parsedMemberIdentifierDeclarationPrefix = $"{memberType.MinimalDisplayString} {member.Name}"; + string parsedMemberIdentifierDeclarationPrefix = $"{memberType.DisplayString} {member.Name}"; string parsedMemberIdentifier; if (memberType is ParsableFromStringSpec { StringParsableTypeKind: StringParsableTypeKind.AssignFromSectionValue }) @@ -344,7 +342,7 @@ void EmitBindImplForMember(MemberSpec member) if (errorOnFailedBinding) { - string condition = $@" if ({Identifier.configuration}[""{member.ConfigurationKeyName}""] is not {memberType.MinimalDisplayString} {member.Name})"; + string condition = $@" if ({Identifier.configuration}[""{member.ConfigurationKeyName}""] is not {memberType.DisplayString} {member.Name})"; EmitThrowBlock(condition); _writer.WriteLine(); return; @@ -393,20 +391,16 @@ void EmitThrowBlock(string condition) => _writer.WriteLine($$""" {{condition}} { - throw new {{GetInvalidOperationDisplayName()}}("{{string.Format(ExceptionMessages.ParameterHasNoMatchingConfig, type.Name, member.Name)}}"); + throw new {{Identifier.InvalidOperationException}}("{{string.Format(ExceptionMessages.ParameterHasNoMatchingConfig, type.Name, member.Name)}}"); } """); } } private void EmitHelperMethods() { - if (ShouldEmitMethods(MethodsToGen_CoreBindingHelper.BindCore)) + if (ShouldEmitMethods(MethodsToGen_CoreBindingHelper.BindCore | MethodsToGen_CoreBindingHelper.GetCore)) { EmitValidateConfigurationKeysMethod(); - } - - if (ShouldEmitMethods(MethodsToGen_CoreBindingHelper.BindCoreUntyped | MethodsToGen_CoreBindingHelper.GetCore)) - { _writer.WriteLine(); EmitHasValueOrChildrenMethod(); _writer.WriteLine(); @@ -420,8 +414,7 @@ private void EmitHelperMethods() _emitBlankLineBeforeNextStatement = true; } - if (ShouldEmitMethods( - MethodsToGen_CoreBindingHelper.BindCoreUntyped | MethodsToGen_CoreBindingHelper.GetCore) || + if (ShouldEmitMethods(MethodsToGen_CoreBindingHelper.BindCore | MethodsToGen_CoreBindingHelper.GetCore) || ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Bind_instance_BinderOptions)) { _writer.WriteLine(); @@ -458,17 +451,17 @@ private void EmitValidateConfigurationKeysMethod() EmitBlankLineIfRequired(); _writer.WriteLine($$""" /// If required by the binder options, validates that there are no unknown keys in the input configuration object. - public static void {{Identifier.ValidateConfigurationKeys}}(Type {{Identifier.type}}, {{MinimalDisplayString.LazyHashSetOfString}} {{keysIdentifier}}, {{Identifier.IConfiguration}} {{Identifier.configuration}}, {{Identifier.BinderOptions}}? {{Identifier.binderOptions}}) + public static void {{Identifier.ValidateConfigurationKeys}}(Type {{Identifier.type}}, {{TypeDisplayString.LazyHashSetOfString}} {{keysIdentifier}}, {{Identifier.IConfiguration}} {{Identifier.configuration}}, {{Identifier.BinderOptions}}? {{Identifier.binderOptions}}) { if ({{Identifier.binderOptions}}?.{{Identifier.ErrorOnUnknownConfiguration}} is true) { - {{MinimalDisplayString.ListOfString}}? {{Identifier.temp}} = null; + {{TypeDisplayString.ListOfString}}? {{Identifier.temp}} = null; foreach ({{Identifier.IConfigurationSection}} {{Identifier.section}} in {{Identifier.configuration}}.{{Identifier.GetChildren}}()) { if (!{{keysIdentifier}}.Value.Contains({{Expression.sectionKey}})) { - ({{Identifier.temp}} ??= new {{MinimalDisplayString.ListOfString}}()).Add($"'{{{Expression.sectionKey}}}'"); + ({{Identifier.temp}} ??= new {{TypeDisplayString.ListOfString}}()).Add($"'{{{Expression.sectionKey}}}'"); } } @@ -512,7 +505,7 @@ private void EmitAsConfigWithChildrenMethod() private void EmitGetBinderOptionsHelper() { _writer.WriteLine($$""" - public static {{Identifier.BinderOptions}}? {{Identifier.GetBinderOptions}}({{MinimalDisplayString.NullableActionOfBinderOptions}} {{Identifier.configureOptions}}) + public static {{Identifier.BinderOptions}}? {{Identifier.GetBinderOptions}}({{TypeDisplayString.NullableActionOfBinderOptions}} {{Identifier.configureOptions}}) { if ({{Identifier.configureOptions}} is null) { @@ -524,7 +517,7 @@ private void EmitGetBinderOptionsHelper() if ({{Identifier.binderOptions}}.BindNonPublicProperties) { - throw new global::System.NotSupportedException($"{{string.Format(ExceptionMessages.CannotSpecifyBindNonPublicProperties)}}"); + throw new NotSupportedException($"{{string.Format(ExceptionMessages.CannotSpecifyBindNonPublicProperties)}}"); } return {{Identifier.binderOptions}}; @@ -534,7 +527,6 @@ private void EmitGetBinderOptionsHelper() private void EmitEnumParseMethod() { - string innerExceptionTypeDisplayString = _useFullyQualifiedNames ? "global::System.Exception" : "Exception"; string exceptionArg1 = string.Format(ExceptionMessages.FailedBinding, $"{{{Identifier.getPath}()}}", $"{{typeof(T)}}"); _writer.WriteLine($$""" @@ -548,9 +540,9 @@ public static T ParseEnum(string value, Func getPath) where T : stru return Enum.Parse(value, ignoreCase: true); #endif } - catch ({{innerExceptionTypeDisplayString}} {{Identifier.exception}}) + catch ({{Identifier.Exception}} {{Identifier.exception}}) { - throw new {{GetInvalidOperationDisplayName()}}($"{{exceptionArg1}}", {{Identifier.exception}}); + throw new {{Identifier.InvalidOperationException}}($"{{exceptionArg1}}", {{Identifier.exception}}); } } """); @@ -558,29 +550,12 @@ public static T ParseEnum(string value, Func getPath) where T : stru private void EmitPrimitiveParseMethod(ParsableFromStringSpec type) { - string innerExceptionTypeDisplayString; - string cultureInfoTypeDisplayString; - string numberStylesTypeDisplayString; - - if (_useFullyQualifiedNames) - { - innerExceptionTypeDisplayString = "global::System.Exception"; - cultureInfoTypeDisplayString = "global::System.Globalization.CultureInfo"; - numberStylesTypeDisplayString = "global::System.Globalization.NumberStyles"; - } - else - { - innerExceptionTypeDisplayString = "Exception"; - cultureInfoTypeDisplayString = "CultureInfo"; - numberStylesTypeDisplayString = "NumberStyles"; - } - StringParsableTypeKind typeKind = type.StringParsableTypeKind; - string typeDisplayString = type.MinimalDisplayString; - - string invariantCultureExpression = $"{cultureInfoTypeDisplayString}.InvariantCulture"; + string typeDisplayString = type.DisplayString; + string invariantCultureExpression = $"{Identifier.CultureInfo}.InvariantCulture"; string parsedValueExpr; + switch (typeKind) { case StringParsableTypeKind.Enum: @@ -592,12 +567,12 @@ private void EmitPrimitiveParseMethod(ParsableFromStringSpec type) break; case StringParsableTypeKind.Integer: { - parsedValueExpr = $"{typeDisplayString}.{Identifier.Parse}({Identifier.value}, {numberStylesTypeDisplayString}.Integer, {invariantCultureExpression})"; + parsedValueExpr = $"{typeDisplayString}.{Identifier.Parse}({Identifier.value}, {Identifier.NumberStyles}.Integer, {invariantCultureExpression})"; } break; case StringParsableTypeKind.Float: { - parsedValueExpr = $"{typeDisplayString}.{Identifier.Parse}({Identifier.value}, {numberStylesTypeDisplayString}.Float, {invariantCultureExpression})"; + parsedValueExpr = $"{typeDisplayString}.{Identifier.Parse}({Identifier.value}, {Identifier.NumberStyles}.Float, {invariantCultureExpression})"; } break; case StringParsableTypeKind.Parse: @@ -612,7 +587,7 @@ private void EmitPrimitiveParseMethod(ParsableFromStringSpec type) break; case StringParsableTypeKind.CultureInfo: { - parsedValueExpr = $"{cultureInfoTypeDisplayString}.GetCultureInfo({Identifier.value})"; + parsedValueExpr = $"{Identifier.CultureInfo}.GetCultureInfo({Identifier.value})"; } break; case StringParsableTypeKind.Uri: @@ -635,9 +610,9 @@ private void EmitPrimitiveParseMethod(ParsableFromStringSpec type) { return {{parsedValueExpr}}; } - catch ({{innerExceptionTypeDisplayString}} {{Identifier.exception}}) + catch ({{Identifier.Exception}} {{Identifier.exception}}) { - throw new {{GetInvalidOperationDisplayName()}}($"{{exceptionArg1}}", {{Identifier.exception}}); + throw new {{Identifier.InvalidOperationException}}($"{{exceptionArg1}}", {{Identifier.exception}}); } """); } @@ -722,13 +697,13 @@ void Emit_BindAndAddLogic_ForElement(string parsedKeyExpr) if (keyType.StringParsableTypeKind is not StringParsableTypeKind.AssignFromSectionValue) { // Save value to local to avoid parsing twice - during look-up and during add. - _writer.WriteLine($"{keyType.MinimalDisplayString} {Identifier.key} = {parsedKeyExpr};"); + _writer.WriteLine($"{keyType.DisplayString} {Identifier.key} = {parsedKeyExpr};"); parsedKeyExpr = Identifier.key; } bool isValueType = elementType.IsValueType; string expressionForElementIsNotNull = $"{Identifier.element} is not null"; - string elementTypeDisplayString = elementType.MinimalDisplayString + (elementType.IsValueType ? string.Empty : "?"); + string elementTypeDisplayString = elementType.DisplayString + (elementType.IsValueType ? string.Empty : "?"); string expressionForElementExists = $"{objIdentifier}.{Identifier.TryGetValue}({parsedKeyExpr}, out {elementTypeDisplayString} {Identifier.element})"; string conditionToUseExistingElement = expressionForElementExists; @@ -749,7 +724,7 @@ void Emit_BindAndAddLogic_ForElement(string parsedKeyExpr) // we need to copy its contents into a new instance & then append/bind to that. string initExpression = collectionSpec.InitializationStrategy is InitializationStrategy.ParameterizedConstructor - ? $"new {collectionSpec.ConcreteType.MinimalDisplayString}({Identifier.element})" + ? $"new {collectionSpec.ConcreteType.DisplayString}({Identifier.element})" : $"{Identifier.element}.{collectionSpec.ToEnumerableMethodCall!}"; _writer.WriteLine($$""" @@ -773,7 +748,7 @@ private void EmitBindCoreImplForObject(ObjectSpec type) Debug.Assert(type.NeedsMemberBinding); string keyCacheFieldName = GetConfigKeyCacheFieldName(type); - string validateMethodCallExpr = $"{Identifier.ValidateConfigurationKeys}(typeof({type.MinimalDisplayString}), {keyCacheFieldName}, {Identifier.configuration}, {Identifier.binderOptions});"; + string validateMethodCallExpr = $"{Identifier.ValidateConfigurationKeys}(typeof({type.DisplayString}), {keyCacheFieldName}, {Identifier.configuration}, {Identifier.binderOptions});"; _writer.WriteLine(validateMethodCallExpr); foreach (PropertySpec property in type.Properties.Values) @@ -781,7 +756,7 @@ private void EmitBindCoreImplForObject(ObjectSpec type) bool noSetter_And_IsReadonly = !property.CanSet && property.Type is CollectionSpec { InitializationStrategy: InitializationStrategy.ParameterizedConstructor }; if (property.ShouldBind() && !noSetter_And_IsReadonly) { - string containingTypeRef = property.IsStatic ? type.MinimalDisplayString : Identifier.obj; + string containingTypeRef = property.IsStatic ? type.DisplayString : Identifier.obj; EmitBindImplForMember( property, memberAccessExpr: $"{containingTypeRef}.{property.Name}", @@ -869,14 +844,14 @@ private void EmitBindCoreCallForMember( } Debug.Assert(canSet); - string effectiveMemberTypeDisplayString = effectiveMemberType.MinimalDisplayString; + string effectiveMemberTypeDisplayString = effectiveMemberType.DisplayString; initKind = InitializationKind.None; if (memberType.SpecKind is TypeSpecKind.Nullable) { string nullableTempIdentifier = GetIncrementalIdentifier(Identifier.temp); - _writer.WriteLine($"{memberType.MinimalDisplayString} {nullableTempIdentifier} = {memberAccessExpr};"); + _writer.WriteLine($"{memberType.DisplayString} {nullableTempIdentifier} = {memberAccessExpr};"); _writer.WriteLine( $"{effectiveMemberTypeDisplayString} {tempIdentifier} = {nullableTempIdentifier}.{Identifier.HasValue} ? {nullableTempIdentifier}.{Identifier.Value} : new {effectiveMemberTypeDisplayString}();"); @@ -924,7 +899,7 @@ private void EmitCollectionCastIfRequired(CollectionSpec type, out string objIde { objIdentifier = Identifier.temp; _writer.WriteLine($$""" - if ({{Identifier.obj}} is not {{type.PopulationCastType!.MinimalDisplayString}} {{objIdentifier}}) + if ({{Identifier.obj}} is not {{type.PopulationCastType!.DisplayString}} {{objIdentifier}}) { return; } @@ -942,14 +917,14 @@ private static string GetSectionPathFromConfigurationExpression(string configura private static string GetSectionFromConfigurationExpression(string configurationKeyName, bool addQuotes = true) { string argExpr = addQuotes ? $@"""{configurationKeyName}""" : configurationKeyName; - return $@"{Expression.configurationGetSection}({argExpr})"; + return $@"{Identifier.configuration}.{Identifier.GetSection}({argExpr})"; } private static string GetConfigKeyCacheFieldName(ObjectSpec type) => $"s_configKeys_{type.DisplayStringWithoutSpecialCharacters}"; - private void Emit_NotSupportedException_TypeNotDetectedAsInput() => - _writer.WriteLine(@$"throw new global::System.NotSupportedException($""{string.Format(ExceptionMessages.TypeNotDetectedAsInput, "{type}")}"");"); + private void Emit_NotSupportedException_TypeNotDetectedAsInput(string typeArgExpr) => + _writer.WriteLine(@$"throw new NotSupportedException($""{string.Format(ExceptionMessages.TypeNotDetectedAsInput, typeArgExpr)}"");"); } } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/Helpers.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/Helpers.cs index e0e6a36aabaa7..ab214fc739436 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/Helpers.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/Helpers.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; +using System.Collections.Generic; using System.Diagnostics; using System.Reflection; @@ -10,7 +12,9 @@ public sealed partial class ConfigurationBindingGenerator { private sealed partial class Emitter { - private static readonly AssemblyName s_assemblyName = typeof(Emitter).Assembly.GetName(); + private string? _emittedExtsTargetType; + + internal static readonly AssemblyName s_assemblyName = typeof(ConfigurationBindingGenerator).Assembly.GetName(); private enum InitializationKind { @@ -26,29 +30,13 @@ private static class Expression public const string sectionPath = "section.Path"; public const string sectionValue = "section.Value"; - public const string GetBinderOptions = $"{FullyQualifiedDisplayString.CoreBindingHelper}.{Identifier.GetBinderOptions}"; - } - - private static class FullyQualifiedDisplayString - { - public const string ActionOfBinderOptions = $"global::System.Action"; - public const string AddSingleton = $"{ServiceCollectionServiceExtensions}.AddSingleton"; - public const string ConfigurationChangeTokenSource = "global::Microsoft.Extensions.Options.ConfigurationChangeTokenSource"; - public const string CoreBindingHelper = $"global::{ProjectName}.{Identifier.CoreBindingHelper}"; - public const string IConfiguration = "global::Microsoft.Extensions.Configuration.IConfiguration"; - public const string IConfigurationSection = IConfiguration + "Section"; - public const string IOptionsChangeTokenSource = "global::Microsoft.Extensions.Options.IOptionsChangeTokenSource"; - public const string InvalidOperationException = "global::System.InvalidOperationException"; - public const string IServiceCollection = "global::Microsoft.Extensions.DependencyInjection.IServiceCollection"; - public const string NotSupportedException = "global::System.NotSupportedException"; - public const string OptionsBuilderOfTOptions = $"global::Microsoft.Extensions.Options.OptionsBuilder<{Identifier.TOptions}>"; - public const string ServiceCollectionServiceExtensions = "global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions"; - public const string Type = $"global::System.Type"; + public static string GeneratedCodeAnnotation = $@"[GeneratedCode(""{s_assemblyName.Name}"", ""{s_assemblyName.Version}"")]"; } - private static class MinimalDisplayString + private static class TypeDisplayString { public const string NullableActionOfBinderOptions = "Action?"; + public const string OptionsBuilderOfTOptions = $"OptionsBuilder<{Identifier.TOptions}>"; public const string HashSetOfString = "HashSet"; public const string LazyHashSetOfString = "Lazy>"; public const string ListOfString = "List"; @@ -75,6 +63,7 @@ private static class Identifier public const string services = nameof(services); public const string temp = nameof(temp); public const string type = nameof(type); + public const string typedObj = nameof(typedObj); public const string validateKeys = nameof(validateKeys); public const string value = nameof(value); @@ -84,19 +73,19 @@ private static class Identifier public const string Array = nameof(Array); public const string AsConfigWithChildren = nameof(AsConfigWithChildren); public const string Bind = nameof(Bind); + public const string BindCoreMain = nameof(BindCoreMain); public const string BinderOptions = nameof(BinderOptions); + public const string BindingExtensions = nameof(BindingExtensions); + public const string ConfigurationChangeTokenSource = nameof(ConfigurationChangeTokenSource); public const string Configure = nameof(Configure); public const string CopyTo = nameof(CopyTo); public const string ContainsKey = nameof(ContainsKey); - public const string CoreBindingHelper = nameof(CoreBindingHelper); public const string Count = nameof(Count); public const string CultureInfo = nameof(CultureInfo); public const string CultureNotFoundException = nameof(CultureNotFoundException); public const string Enum = nameof(Enum); public const string ErrorOnUnknownConfiguration = nameof(ErrorOnUnknownConfiguration); - public const string GeneratedConfigurationBinder = nameof(GeneratedConfigurationBinder); - public const string GeneratedOptionsBuilderBinder = nameof(GeneratedOptionsBuilderBinder); - public const string GeneratedServiceCollectionBinder = nameof(GeneratedServiceCollectionBinder); + public const string Exception = nameof(Exception); public const string Get = nameof(Get); public const string GetBinderOptions = nameof(GetBinderOptions); public const string GetChildren = nameof(GetChildren); @@ -108,9 +97,13 @@ private static class Identifier public const string IConfiguration = nameof(IConfiguration); public const string IConfigurationSection = nameof(IConfigurationSection); public const string Int32 = "int"; + public const string InterceptsLocation = nameof(InterceptsLocation); public const string InvalidOperationException = nameof(InvalidOperationException); public const string InvariantCulture = nameof(InvariantCulture); + public const string IOptionsChangeTokenSource = nameof(IOptionsChangeTokenSource); + public const string IServiceCollection = nameof(IServiceCollection); public const string Length = nameof(Length); + public const string NumberStyles = nameof(NumberStyles); public const string Parse = nameof(Parse); public const string Path = nameof(Path); public const string Resize = nameof(Resize); @@ -119,16 +112,61 @@ private static class Identifier public const string TryCreate = nameof(TryCreate); public const string TryGetValue = nameof(TryGetValue); public const string TryParse = nameof(TryParse); + public const string Type = nameof(Type); public const string Uri = nameof(Uri); public const string ValidateConfigurationKeys = nameof(ValidateConfigurationKeys); public const string Value = nameof(Value); } - private bool ShouldEmitBinders() => + private bool ShouldEmitBindingExtensions() => ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Any) || ShouldEmitMethods(MethodsToGen_Extensions_OptionsBuilder.Any) || ShouldEmitMethods(MethodsToGen_Extensions_ServiceCollection.Any); + private void EmitInterceptsLocationAnnotations(Enum generatedBindingOverload) + { + // The only time a generated binding method won't have any locations to + // intercept is when either of these methods are used as helpers for + // other generated OptionsBuilder or ServiceCollection binding extensions. + bool interceptsCalls = _sourceGenSpec.GenMethodsInterceptionInfo.TryGetValue(generatedBindingOverload, out List? infoList); + Debug.Assert(interceptsCalls || + generatedBindingOverload is MethodsToGen_Extensions_ServiceCollection.Configure_T_name_BinderOptions || + generatedBindingOverload is MethodsToGen_Extensions_OptionsBuilder.Bind_T_BinderOptions); + + if (interceptsCalls) + { + foreach (InterceptorLocationInfo info in infoList) + { + _writer.WriteLine($@"[{Identifier.InterceptsLocation}Attribute(@""{info.FilePath}"", {info.LineNumber}, {info.CharacterNumber})]"); + } + } + } + + private void EmitBindingExtStartRegion(string targetType) + { + Debug.Assert(_emittedExtsTargetType is null); + + EmitBlankLineIfRequired(); + _emittedExtsTargetType = targetType; + EmitBindingExtRegionText(isStart: true); + _emitBlankLineBeforeNextStatement = false; + } + + private void EmitBindingExtEndRegion() + { + Debug.Assert(_emittedExtsTargetType is not null); + + EmitBindingExtRegionText(isStart: false); + _emittedExtsTargetType = null; + _emitBlankLineBeforeNextStatement = true; + } + + private void EmitBindingExtRegionText(bool isStart) + { + string endSource = isStart ? string.Empty : "end"; + _writer.WriteLine($"#{endSource}region {_emittedExtsTargetType} extensions."); + } + /// /// Starts a block of source code. /// @@ -171,24 +209,12 @@ private void EmitBlankLineIfRequired() _emitBlankLineBeforeNextStatement = true; } - private void EmitCheckForNullArgument_WithBlankLine_IfRequired(bool isValueType) - { - if (!isValueType) - { - EmitCheckForNullArgument_WithBlankLine(Identifier.obj); - } - } - private void EmitCheckForNullArgument_WithBlankLine(string paramName) { - string exceptionTypeDisplayString = _useFullyQualifiedNames - ? "global::System.ArgumentNullException" - : "ArgumentNullException"; - _writer.WriteLine($$""" if ({{paramName}} is null) { - throw new {{exceptionTypeDisplayString}}(nameof({{paramName}})); + throw new ArgumentNullException(nameof({{paramName}})); } """); @@ -201,51 +227,17 @@ private bool EmitInitException(TypeSpec type) if (!type.CanInitialize) { - _writer.WriteLine(GetInitException(type.InitExceptionMessage) + ";"); + _writer.WriteLine($@"throw new {Identifier.InvalidOperationException}(""{type.InitExceptionMessage}"");"); return true; } return false; } - private void EmitRootBindingClassStartBlock(string className) - { - EmitBlankLineIfRequired(); - EmitStartBlock($$""" - /// Generated helper providing an AOT and linking compatible implementation for configuration binding. - {{GetGeneratedCodeAttributeSrc()}} - internal static class {{className}} - """); - - _emitBlankLineBeforeNextStatement = false; - } - - private string GetGeneratedCodeAttributeSrc() - { - string attributeRefExpr = _useFullyQualifiedNames ? $"global::System.CodeDom.Compiler.GeneratedCodeAttribute" : "GeneratedCode"; - return $@"[{attributeRefExpr}(""{s_assemblyName.Name}"", ""{s_assemblyName.Version}"")]"; - } - - private string GetInitException(string message) => $@"throw new {GetInvalidOperationDisplayName()}(""{message}"")"; - private string GetIncrementalIdentifier(string prefix) => $"{prefix}{_valueSuffixIndex++}"; - private string GetInitalizeMethodDisplayString(ObjectSpec type) => - GetHelperMethodDisplayString($"{nameof(MethodsToGen_CoreBindingHelper.Initialize)}{type.DisplayStringWithoutSpecialCharacters}"); - - private string GetTypeDisplayString(TypeSpec type) => _useFullyQualifiedNames ? type.FullyQualifiedDisplayString : type.MinimalDisplayString; - - private string GetHelperMethodDisplayString(string methodName) - { - if (_useFullyQualifiedNames) - { - methodName = FullyQualifiedDisplayString.CoreBindingHelper + "." + methodName; - } - - return methodName; - } - - private string GetInvalidOperationDisplayName() => _useFullyQualifiedNames ? FullyQualifiedDisplayString.InvalidOperationException : Identifier.InvalidOperationException; + private static string GetInitalizeMethodDisplayString(ObjectSpec type) => + $"{nameof(MethodsToGen_CoreBindingHelper.Initialize)}{type.DisplayStringWithoutSpecialCharacters}"; } } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsBuilderConfigurationExtensions.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsBuilderConfigurationExtensions.cs index 71d0b6989dd97..91f7fdd43478d 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsBuilderConfigurationExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsBuilderConfigurationExtensions.cs @@ -9,19 +9,17 @@ private sealed partial class Emitter { private bool ShouldEmitMethods(MethodsToGen_Extensions_OptionsBuilder methods) => (_sourceGenSpec.MethodsToGen_OptionsBuilderExt & methods) != 0; - private void EmitBinder_Extensions_OptionsBuilder() + private void EmitBindingExtensions_OptionsBuilder() { if (!ShouldEmitMethods(MethodsToGen_Extensions_OptionsBuilder.Any)) { return; } - EmitRootBindingClassStartBlock(Identifier.GeneratedOptionsBuilderBinder); - + EmitBindingExtStartRegion(TypeDisplayString.OptionsBuilderOfTOptions); EmitBindMethods_Extensions_OptionsBuilder(); EmitBindConfigurationMethod(); - - EmitEndBlock(); + EmitBindingExtEndRegion(); } private void EmitBindMethods_Extensions_OptionsBuilder() @@ -32,24 +30,25 @@ private void EmitBindMethods_Extensions_OptionsBuilder() } const string documentation = @"/// Registers a configuration instance which will bind against."; - const string paramList = $"{FullyQualifiedDisplayString.IConfiguration} {Identifier.configuration}"; + const string paramList = $"{Identifier.IConfiguration} {Identifier.configuration}"; if (ShouldEmitMethods(MethodsToGen_Extensions_OptionsBuilder.Bind_T)) { - EmitMethodStartBlock("Bind", paramList, documentation); - _writer.WriteLine($"return global::{Identifier.GeneratedOptionsBuilderBinder}.Bind({Identifier.optionsBuilder}, {Identifier.configuration}, {Identifier.configureOptions}: null);"); + EmitMethodStartBlock(MethodsToGen_Extensions_OptionsBuilder.Bind_T, "Bind", paramList, documentation); + _writer.WriteLine($"return Bind({Identifier.optionsBuilder}, {Identifier.configuration}, {Identifier.configureOptions}: null);"); EmitEndBlock(); } EmitMethodStartBlock( + MethodsToGen_Extensions_OptionsBuilder.Bind_T_BinderOptions, "Bind", - paramList + $", {FullyQualifiedDisplayString.ActionOfBinderOptions}? {Identifier.configureOptions}", + paramList + $", {TypeDisplayString.NullableActionOfBinderOptions} {Identifier.configureOptions}", documentation); EmitCheckForNullArgument_WithBlankLine(Identifier.optionsBuilder); _writer.WriteLine($$""" - global::{{Identifier.GeneratedServiceCollectionBinder}}.{{Identifier.Configure}}<{{Identifier.TOptions}}>({{Identifier.optionsBuilder}}.{{Identifier.Services}}, {{Identifier.optionsBuilder}}.Name, {{Identifier.configuration}}, {{Identifier.configureOptions}}); + {{Identifier.Configure}}<{{Identifier.TOptions}}>({{Identifier.optionsBuilder}}.{{Identifier.Services}}, {{Identifier.optionsBuilder}}.Name, {{Identifier.configuration}}, {{Identifier.configureOptions}}); return {{Identifier.optionsBuilder}}; """); @@ -63,19 +62,19 @@ private void EmitBindConfigurationMethod() return; } - const string documentation = $@"/// Registers the dependency injection container to bind against the obtained from the DI service provider."; - string paramList = $"string {Identifier.configSectionPath}, {FullyQualifiedDisplayString.ActionOfBinderOptions}? {Identifier.configureOptions} = null"; + const string documentation = $@"/// Registers the dependency injection container to bind against the obtained from the DI service provider."; + string paramList = $"string {Identifier.configSectionPath}, {TypeDisplayString.NullableActionOfBinderOptions} {Identifier.configureOptions} = null"; - EmitMethodStartBlock("BindConfiguration", paramList, documentation); + EmitMethodStartBlock(MethodsToGen_Extensions_OptionsBuilder.BindConfiguration, "BindConfiguration", paramList, documentation); EmitCheckForNullArgument_WithBlankLine(Identifier.optionsBuilder); EmitCheckForNullArgument_WithBlankLine(Identifier.configSectionPath); - EmitStartBlock($"{Identifier.optionsBuilder}.{Identifier.Configure}<{FullyQualifiedDisplayString.IConfiguration}>(({Identifier.obj}, {Identifier.configuration}) =>"); + EmitStartBlock($"{Identifier.optionsBuilder}.{Identifier.Configure}<{Identifier.IConfiguration}>(({Identifier.obj}, {Identifier.configuration}) =>"); _writer.WriteLine($$""" - {{FullyQualifiedDisplayString.IConfiguration}} {{Identifier.section}} = string.Equals(string.Empty, {{Identifier.configSectionPath}}, global::System.StringComparison.OrdinalIgnoreCase) ? {{Identifier.configuration}} : {{Identifier.configuration}}.{{Identifier.GetSection}}({{Identifier.configSectionPath}}); - {{FullyQualifiedDisplayString.CoreBindingHelper}}.{{nameof(MethodsToGen_CoreBindingHelper.BindCoreUntyped)}}({{Identifier.section}}, {{Identifier.obj}}, typeof({{Identifier.TOptions}}), {{Identifier.configureOptions}}); + {{Identifier.IConfiguration}} {{Identifier.section}} = string.Equals(string.Empty, {{Identifier.configSectionPath}}, StringComparison.OrdinalIgnoreCase) ? {{Identifier.configuration}} : {{Identifier.configuration}}.{{Identifier.GetSection}}({{Identifier.configSectionPath}}); + {{Identifier.BindCoreMain}}({{Identifier.section}}, {{Identifier.obj}}, {{Identifier.configureOptions}}); """); EmitEndBlock(endBraceTrailingSource: ");"); @@ -83,20 +82,20 @@ private void EmitBindConfigurationMethod() _writer.WriteLine(); _writer.WriteLine($$""" - {{FullyQualifiedDisplayString.AddSingleton}}<{{FullyQualifiedDisplayString.IOptionsChangeTokenSource}}<{{Identifier.TOptions}}>, {{FullyQualifiedDisplayString.ConfigurationChangeTokenSource}}<{{Identifier.TOptions}}>>({{Identifier.optionsBuilder}}.{{Identifier.Services}}); + {{Identifier.optionsBuilder}}.{{Identifier.Services}}.{{Identifier.AddSingleton}}<{{Identifier.IOptionsChangeTokenSource}}<{{Identifier.TOptions}}>, {{Identifier.ConfigurationChangeTokenSource}}<{{Identifier.TOptions}}>>(); return {{Identifier.optionsBuilder}}; """); EmitEndBlock(); } - private void EmitMethodStartBlock(string methodName, string paramList, string documentation) + private void EmitMethodStartBlock(MethodsToGen_Extensions_OptionsBuilder method, string methodName, string paramList, string documentation) { - paramList = $"this {FullyQualifiedDisplayString.OptionsBuilderOfTOptions} {Identifier.optionsBuilder}, {paramList}"; - + paramList = $"this {TypeDisplayString.OptionsBuilderOfTOptions} {Identifier.optionsBuilder}, {paramList}"; EmitBlankLineIfRequired(); _writer.WriteLine(documentation); - EmitStartBlock($"public static {FullyQualifiedDisplayString.OptionsBuilderOfTOptions} {methodName}<{Identifier.TOptions}>({paramList}) where {Identifier.TOptions} : class"); + EmitInterceptsLocationAnnotations(method); + EmitStartBlock($"public static {TypeDisplayString.OptionsBuilderOfTOptions} {methodName}<{Identifier.TOptions}>({paramList}) where {Identifier.TOptions} : class"); } } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsConfigurationServiceCollectionExtensions.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsConfigurationServiceCollectionExtensions.cs index f4cd4800df123..804159ccb4bf8 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsConfigurationServiceCollectionExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsConfigurationServiceCollectionExtensions.cs @@ -9,71 +9,72 @@ private sealed partial class Emitter { private bool ShouldEmitMethods(MethodsToGen_Extensions_ServiceCollection methods) => (_sourceGenSpec.MethodsToGen_ServiceCollectionExt & methods) != 0; - private void EmitBinder_Extensions_IServiceCollection() + private void EmitBindingExtensions_IServiceCollection() { if (!ShouldEmitMethods(MethodsToGen_Extensions_ServiceCollection.Any)) { return; } - EmitRootBindingClassStartBlock(Identifier.GeneratedServiceCollectionBinder); + EmitBindingExtStartRegion(Identifier.IServiceCollection); + EmitConfigureMethods(); + EmitBindingExtEndRegion(); + } + private void EmitConfigureMethods() + { const string defaultNameExpr = "string.Empty"; - const string configureMethodString = $"global::{Identifier.GeneratedServiceCollectionBinder}.{Identifier.Configure}"; - string configParam = $"{FullyQualifiedDisplayString.IConfiguration} {Identifier.configuration}"; + string configParam = $"{Identifier.IConfiguration} {Identifier.configuration}"; if (ShouldEmitMethods(MethodsToGen_Extensions_ServiceCollection.Configure_T)) { - EmitStartMethod(configParam); - _writer.WriteLine($"return {configureMethodString}<{Identifier.TOptions}>({Identifier.services}, {defaultNameExpr}, {Identifier.configuration}, {Identifier.configureOptions}: null);"); + EmitStartMethod(MethodsToGen_Extensions_ServiceCollection.Configure_T, configParam); + _writer.WriteLine($"return {Identifier.Configure}<{Identifier.TOptions}>({Identifier.services}, {defaultNameExpr}, {Identifier.configuration}, {Identifier.configureOptions}: null);"); EmitEndBlock(); } if (ShouldEmitMethods(MethodsToGen_Extensions_ServiceCollection.Configure_T_name)) { EmitStartMethod( + MethodsToGen_Extensions_ServiceCollection.Configure_T_name, paramList: $"string? {Identifier.name}, " + configParam); - _writer.WriteLine($"return {configureMethodString}<{Identifier.TOptions}>({Identifier.services}, {Identifier.name}, {Identifier.configuration}, {Identifier.configureOptions}: null);"); + _writer.WriteLine($"return {Identifier.Configure}<{Identifier.TOptions}>({Identifier.services}, {Identifier.name}, {Identifier.configuration}, {Identifier.configureOptions}: null);"); EmitEndBlock(); } if (ShouldEmitMethods(MethodsToGen_Extensions_ServiceCollection.Configure_T_BinderOptions)) { EmitStartMethod( - paramList: configParam + $", {FullyQualifiedDisplayString.ActionOfBinderOptions}? {Identifier.configureOptions}"); - _writer.WriteLine($"return {configureMethodString}<{Identifier.TOptions}>({Identifier.services}, {defaultNameExpr}, {Identifier.configuration}, {Identifier.configureOptions});"); + MethodsToGen_Extensions_ServiceCollection.Configure_T_BinderOptions, + paramList: configParam + $", {TypeDisplayString.NullableActionOfBinderOptions} {Identifier.configureOptions}"); + _writer.WriteLine($"return {Identifier.Configure}<{Identifier.TOptions}>({Identifier.services}, {defaultNameExpr}, {Identifier.configuration}, {Identifier.configureOptions});"); EmitEndBlock(); } // Core Configure method that the other overloads call. // Like the others, it is public API that could be called directly by users. // So, it is always generated whenever a Configure overload is called. - string optionsNamespaceName = "global::Microsoft.Extensions.Options"; - string bindCoreUntypedDisplayString = GetHelperMethodDisplayString(nameof(MethodsToGen_CoreBindingHelper.BindCoreUntyped)); + string optionsNamespaceName = "Microsoft.Extensions.Options"; - EmitStartMethod(paramList: $"string? {Identifier.name}, " + configParam + $", {FullyQualifiedDisplayString.ActionOfBinderOptions}? {Identifier.configureOptions}"); + EmitStartMethod(MethodsToGen_Extensions_ServiceCollection.Configure_T_name_BinderOptions, paramList: $"string? {Identifier.name}, " + configParam + $", {TypeDisplayString.NullableActionOfBinderOptions} {Identifier.configureOptions}"); EmitCheckForNullArgument_WithBlankLine(Identifier.services); EmitCheckForNullArgument_WithBlankLine(Identifier.configuration); _writer.WriteLine($$""" - global::Microsoft.Extensions.DependencyInjection.OptionsServiceCollectionExtensions.AddOptions({{Identifier.services}}); - {{FullyQualifiedDisplayString.AddSingleton}}<{{FullyQualifiedDisplayString.IOptionsChangeTokenSource}}<{{Identifier.TOptions}}>>({{Identifier.services}}, new {{FullyQualifiedDisplayString.ConfigurationChangeTokenSource}}<{{Identifier.TOptions}}>({{Identifier.name}}, {{Identifier.configuration}})); - return {{FullyQualifiedDisplayString.AddSingleton}}<{{optionsNamespaceName}}.IConfigureOptions<{{Identifier.TOptions}}>>({{Identifier.services}}, new {{optionsNamespaceName}}.ConfigureNamedOptions<{{Identifier.TOptions}}>({{Identifier.name}}, {{Identifier.obj}} => {{bindCoreUntypedDisplayString}}({{Identifier.configuration}}, {{Identifier.obj}}, typeof({{Identifier.TOptions}}), {{Identifier.configureOptions}}))); + OptionsServiceCollectionExtensions.AddOptions({{Identifier.services}}); + {{Identifier.services}}.{{Identifier.AddSingleton}}<{{Identifier.IOptionsChangeTokenSource}}<{{Identifier.TOptions}}>>(new {{Identifier.ConfigurationChangeTokenSource}}<{{Identifier.TOptions}}>({{Identifier.name}}, {{Identifier.configuration}})); + return {{Identifier.services}}.{{Identifier.AddSingleton}}<{{optionsNamespaceName}}.IConfigureOptions<{{Identifier.TOptions}}>>(new {{optionsNamespaceName}}.ConfigureNamedOptions<{{Identifier.TOptions}}>({{Identifier.name}}, {{Identifier.obj}} => {{Identifier.BindCoreMain}}({{Identifier.configuration}}, {{Identifier.obj}}, {{Identifier.configureOptions}}))); """); EmitEndBlock(); - - EmitEndBlock(); - _emitBlankLineBeforeNextStatement = true; } - private void EmitStartMethod(string paramList) + private void EmitStartMethod(MethodsToGen_Extensions_ServiceCollection overload, string paramList) { - paramList = $"this {FullyQualifiedDisplayString.IServiceCollection} {Identifier.services}, {paramList}"; + paramList = $"this {Identifier.IServiceCollection} {Identifier.services}, {paramList}"; EmitBlankLineIfRequired(); - EmitStartBlock($$""" - /// Registers a configuration instance which TOptions will bind against. - public static {{FullyQualifiedDisplayString.IServiceCollection}} {{Identifier.Configure}}<{{Identifier.TOptions}}>({{paramList}}) where {{Identifier.TOptions}} : class - """); + _writer.WriteLine("/// Registers a configuration instance which TOptions will bind against."); + EmitInterceptsLocationAnnotations(overload); + EmitStartBlock($"public static {Identifier.IServiceCollection} {Identifier.Configure}<{Identifier.TOptions}>({paramList}) where {Identifier.TOptions} : class"); } } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/InterceptorLocationInfo.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/InterceptorLocationInfo.cs new file mode 100644 index 0000000000000..d355e071904a3 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/InterceptorLocationInfo.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Operations; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration +{ + internal readonly record struct InterceptorLocationInfo + { + public InterceptorLocationInfo(IInvocationOperation operation) + { + SyntaxNode operationSyntax = operation.Syntax; + TextSpan operationSpan = operationSyntax.Span; + SyntaxTree operationSyntaxTree = operationSyntax.SyntaxTree; + + FilePath = GetInterceptorFilePath(operationSyntaxTree, operation.SemanticModel?.Compilation.Options.SourceReferenceResolver); + + FileLinePositionSpan span = operationSyntaxTree.GetLineSpan(operationSpan); + LineNumber = span.StartLinePosition.Line + 1; + + // Calculate the character offset to the end of the binding invocation detected. + int invocationLength = ((MemberAccessExpressionSyntax)((InvocationExpressionSyntax)operationSyntax).Expression).Expression.Span.Length; + CharacterNumber = span.StartLinePosition.Character + invocationLength + 2; + } + + public string FilePath { get; } + public int LineNumber { get; } + public int CharacterNumber { get; } + + // Utilize the same logic used by the interceptors API for resolving the source mapped value of a path. + // https://github.com/dotnet/roslyn/blob/f290437fcc75dad50a38c09e0977cce13a64f5ba/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs#L1063-L1064 + private static string GetInterceptorFilePath(SyntaxTree tree, SourceReferenceResolver? resolver) => + resolver?.NormalizePath(tree.FilePath, baseFilePath: null) ?? tree.FilePath; + } +} diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/MethodsToGen.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/MethodsToGen.cs index 7b40f198e08f2..d0ba0699b2aba 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/MethodsToGen.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/MethodsToGen.cs @@ -10,11 +10,10 @@ public enum MethodsToGen_CoreBindingHelper { None = 0x0, BindCore = 0x1, - BindCoreUntyped = 0x2, - GetCore = 0x4, - GetValueCore = 0x8, - Initialize = 0x10, - AsConfigWithChildren = 0x20, + GetCore = 0x2, + GetValueCore = 0x4, + Initialize = 0x8, + AsConfigWithChildren = 0x10, } /// diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/BinderInvocation.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/BinderInvocation.cs index 554786589c8c8..ad7c4c09204d4 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/BinderInvocation.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/BinderInvocation.cs @@ -40,7 +40,7 @@ static bool IsCandidateBindingMethodName(string name) => IsValidMethodName_OptionsConfigurationServiceCollectionExtensions(name); } - private static bool IsBindingOperation(IInvocationOperation operation) + public static bool IsBindingOperation(IInvocationOperation operation) { if (operation.TargetMethod is not IMethodSymbol { diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/ConfigurationBinder.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/ConfigurationBinder.cs index a663c441c55ce..5ff93853676a6 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/ConfigurationBinder.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/ConfigurationBinder.cs @@ -100,17 +100,9 @@ private void RegisterBindInvocation(BinderInvocation invocation) return; } - if (GetTargetTypeForRootInvocationCore(type, invocation.Location) is TypeSpec typeSpec) + if (GetTargetTypeForRootInvocationCore(type, invocation.Location) is not null) { - Dictionary> types = _sourceGenSpec.TypesForGen_ConfigurationBinder_BindMethods; - - if (!types.TryGetValue(overload, out HashSet? typeSpecs)) - { - types[overload] = typeSpecs = new HashSet(); - } - - _sourceGenSpec.MethodsToGen_ConfigurationBinder |= overload; - typeSpecs.Add(typeSpec); + RegisterAsInterceptor(overload, invocation.Operation); } static ITypeSymbol? ResolveType(IOperation conversionOperation) => @@ -184,7 +176,7 @@ private void RegisterGetInvocation(BinderInvocation invocation) if (GetTargetTypeForRootInvocation(type, invocation.Location) is TypeSpec typeSpec) { - _sourceGenSpec.MethodsToGen_ConfigurationBinder |= overload; + RegisterAsInterceptor(overload, invocation.Operation); RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.GetCore, typeSpec); } @@ -253,10 +245,16 @@ private void RegisterGetValueInvocation(BinderInvocation invocation) if (IsParsableFromString(effectiveType, out _) && GetTargetTypeForRootInvocationCore(type, invocation.Location) is TypeSpec typeSpec) { - _sourceGenSpec.MethodsToGen_ConfigurationBinder |= overload; + RegisterAsInterceptor(overload, invocation.Operation); RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.GetValueCore, typeSpec); } } + + private void RegisterAsInterceptor(MethodsToGen_ConfigurationBinder overload, IInvocationOperation operation) + { + _sourceGenSpec.MethodsToGen_ConfigurationBinder |= overload; + RegisterGenMethodAsInterceptor(overload, operation); + } } } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/OptionsBuilderConfigurationExtensions.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/OptionsBuilderConfigurationExtensions.cs index d01e80d708ca1..eb7407a0a571e 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/OptionsBuilderConfigurationExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/OptionsBuilderConfigurationExtensions.cs @@ -34,9 +34,6 @@ private void RegisterMethodInvocation_OptionsBuilderExt(BinderInvocation invocat return; } - // We are going to emit calls to APIs on IServiceCollection. - _sourceGenSpec.TypeNamespaces.Add("Microsoft.Extensions.DependencyInjection"); - if (targetMethod.Name is "Bind") { RegisterBindInvocation(invocation, typeSpec); @@ -61,20 +58,19 @@ private void RegisterBindInvocation(BinderInvocation invocation, TypeSpec typeSp return; } - if (paramCount is 2) - { - _sourceGenSpec.MethodsToGen_OptionsBuilderExt |= MethodsToGen_Extensions_OptionsBuilder.Bind_T; - } - else if (paramCount is 3 && SymbolEqualityComparer.Default.Equals(_typeSymbols.ActionOfBinderOptions, @params[2].Type)) + MethodsToGen_Extensions_OptionsBuilder overload = paramCount switch { - _sourceGenSpec.MethodsToGen_OptionsBuilderExt |= MethodsToGen_Extensions_OptionsBuilder.Bind_T_BinderOptions; - } - else + 2 => MethodsToGen_Extensions_OptionsBuilder.Bind_T, + 3 when SymbolEqualityComparer.Default.Equals(_typeSymbols.ActionOfBinderOptions, @params[2].Type) => + MethodsToGen_Extensions_OptionsBuilder.Bind_T_BinderOptions, + _ => MethodsToGen_Extensions_OptionsBuilder.None + }; + + if (overload is not MethodsToGen_Extensions_OptionsBuilder.None) { - return; + RegisterAsInterceptor(overload, operation); + RegisterTypeForMethodGen(MethodsToGen_Extensions_ServiceCollection.Configure_T_name_BinderOptions, typeSpec); } - - RegisterTypeForMethodGen(MethodsToGen_Extensions_ServiceCollection.Configure_T_name_BinderOptions, typeSpec); } private void ParseBindConfigurationInvocation(BinderInvocation invocation, TypeSpec typeSpec) @@ -85,12 +81,26 @@ private void ParseBindConfigurationInvocation(BinderInvocation invocation, TypeS int paramCount = @params.Length; Debug.Assert(paramCount >= 2); - if (paramCount is 3 && @params[1].Type.SpecialType is SpecialType.System_String && SymbolEqualityComparer.Default.Equals(_typeSymbols.ActionOfBinderOptions, @params[2].Type)) + if (paramCount is 3 && + @params[1].Type.SpecialType is SpecialType.System_String && + SymbolEqualityComparer.Default.Equals(_typeSymbols.ActionOfBinderOptions, @params[2].Type)) { - _sourceGenSpec.MethodsToGen_OptionsBuilderExt |= MethodsToGen_Extensions_OptionsBuilder.BindConfiguration_T_path_BinderOptions; - RegisterTypeForBindCoreUntypedGen(typeSpec); + RegisterAsInterceptor(MethodsToGen_Extensions_OptionsBuilder.BindConfiguration_T_path_BinderOptions, invocation.Operation); + RegisterTypeForBindCoreGen(typeSpec); } } + + private void RegisterAsInterceptor(MethodsToGen_Extensions_OptionsBuilder overload, IInvocationOperation operation) + { + _sourceGenSpec.MethodsToGen_OptionsBuilderExt |= overload; + RegisterGenMethodAsInterceptor(overload, operation); + + // Emitting refs to IOptionsChangeTokenSource, ConfigurationChangeTokenSource. + _sourceGenSpec.Namespaces.Add("Microsoft.Extensions.Options"); + + // Emitting refs to OptionsBuilder. + _sourceGenSpec.Namespaces.Add("Microsoft.Extensions.DependencyInjection"); + } } } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/OptionsConfigurationServiceCollectionExtensions.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/OptionsConfigurationServiceCollectionExtensions.cs index 02c75b4ab653b..d4b1bb30f1b1a 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/OptionsConfigurationServiceCollectionExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/OptionsConfigurationServiceCollectionExtensions.cs @@ -79,12 +79,14 @@ @params[1].Type.SpecialType is SpecialType.System_String && } RegisterTypeForMethodGen(overload, typeSpec); + RegisterGenMethodAsInterceptor(overload, operation); } private void RegisterTypeForMethodGen(MethodsToGen_Extensions_ServiceCollection overload, TypeSpec typeSpec) { _sourceGenSpec.MethodsToGen_ServiceCollectionExt |= overload; - RegisterTypeForBindCoreUntypedGen(typeSpec); + _sourceGenSpec.Namespaces.Add("Microsoft.Extensions.DependencyInjection"); + RegisterTypeForBindCoreGen(typeSpec); } } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Microsoft.Extensions.Configuration.Binder.SourceGeneration.csproj b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Microsoft.Extensions.Configuration.Binder.SourceGeneration.csproj index 785a18c5c0978..3ad3c09f9c2fd 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Microsoft.Extensions.Configuration.Binder.SourceGeneration.csproj +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Microsoft.Extensions.Configuration.Binder.SourceGeneration.csproj @@ -10,7 +10,7 @@ - + @@ -22,16 +22,19 @@ + + - + + diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/ObjectSpec.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/ObjectSpec.cs index 1696ee099fe46..c8911850badb6 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/ObjectSpec.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/ObjectSpec.cs @@ -24,7 +24,7 @@ public ObjectSpec(INamedTypeSymbol type) : base(type) { } private string _displayStringWithoutSpecialCharacters; public string DisplayStringWithoutSpecialCharacters => - _displayStringWithoutSpecialCharacters ??= $"{MinimalDisplayString.Replace(".", string.Empty).Replace("<", string.Empty).Replace(">", string.Empty)}"; + _displayStringWithoutSpecialCharacters ??= $"{DisplayString.Replace(".", string.Empty).Replace("<", string.Empty).Replace(">", string.Empty)}"; public override bool NeedsMemberBinding => CanInitialize && Properties.Values.Count > 0 && diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/ParsableFromStringSpec.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/ParsableFromStringSpec.cs index 6b5bb5b61ea37..e19e3e61d210f 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/ParsableFromStringSpec.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/ParsableFromStringSpec.cs @@ -24,7 +24,7 @@ public string ParseMethodName _parseMethodName ??= StringParsableTypeKind is StringParsableTypeKind.ByteArray ? "ParseByteArray" // MinimalDisplayString.Length is certainly > 2. - : $"Parse{(char.ToUpper(MinimalDisplayString[0]) + MinimalDisplayString.Substring(1)).Replace(".", "")}"; + : $"Parse{(char.ToUpper(DisplayString[0]) + DisplayString.Substring(1)).Replace(".", "")}"; return _parseMethodName; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/SourceGenerationSpec.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/SourceGenerationSpec.cs index 88c4b24f57a5e..cf37acd6f39db 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/SourceGenerationSpec.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/SourceGenerationSpec.cs @@ -1,21 +1,23 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Collections.Generic; namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { internal sealed record SourceGenerationSpec { + public Dictionary> GenMethodsInterceptionInfo { get; } = new(); public Dictionary> TypesForGen_CoreBindingHelper_Methods { get; } = new(); - public Dictionary> TypesForGen_ConfigurationBinder_BindMethods { get; } = new(); public HashSet PrimitivesForHelperGen { get; } = new(); - public HashSet TypeNamespaces { get; } = new() + public HashSet Namespaces { get; } = new() { "System", "System.CodeDom.Compiler", "System.Globalization", + "System.Runtime.CompilerServices", "Microsoft.Extensions.Configuration", }; diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/TypeSpec.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/TypeSpec.cs index 6a6292b7ebd0b..6f5e26bf3f6d3 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/TypeSpec.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/TypeSpec.cs @@ -17,17 +17,14 @@ public TypeSpec(ITypeSymbol type) { IsValueType = type.IsValueType; Namespace = type.ContainingNamespace?.ToDisplayString(); - FullyQualifiedDisplayString = type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); - MinimalDisplayString = type.ToDisplayString(s_minimalDisplayFormat); - Name = Namespace + "." + MinimalDisplayString.Replace(".", "+"); + DisplayString = type.ToDisplayString(s_minimalDisplayFormat); + Name = Namespace + "." + DisplayString.Replace(".", "+"); IsInterface = type.TypeKind is TypeKind.Interface; } public string Name { get; } - public string FullyQualifiedDisplayString { get; } - - public string MinimalDisplayString { get; } + public string DisplayString { get; } public string? Namespace { get; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs index 56dddc6f8bc83..4f16767be2f50 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs @@ -2048,6 +2048,7 @@ public void TraceSwitchTest() configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); +#if !BUILDING_SOURCE_GENERATOR_TESTS TraceSwitch ts = new(displayName: "TraceSwitch", description: "This switch is set via config."); ConfigurationBinder.Bind(config, "TraceSwitch", ts); Assert.Equal(TraceLevel.Info, ts.Level); @@ -2055,6 +2056,7 @@ public void TraceSwitchTest() // Value property is not publicly exposed in .NET Framework. Assert.Equal("Info", ts.Value); #endif // NETCOREAPP +#endif // !BUILDING_SOURCE_GENERATOR_TESTS } private void ValidateGeolocation(IGeolocation location) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/Collections.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/Collections.generated.txt index 2149dcaaa0714..52d0cd6a593ab 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/Collections.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/Collections.generated.txt @@ -2,12 +2,19 @@ #nullable enable #pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code. -/// Generated helper providing an AOT and linking compatible implementation for configuration binding. -[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] -internal static class GeneratedConfigurationBinder +namespace System.Runtime.CompilerServices { - /// Attempts to bind the configuration instance to a new instance of type T. - public static T? Get(this global::Microsoft.Extensions.Configuration.IConfiguration configuration) => (T?)(global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetCore(configuration, typeof(T), configureOptions: null) ?? default(T)); + using System; + using System.CodeDom.Compiler; + + [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : Attribute + { + public InterceptsLocationAttribute(string filePath, int line, int column) + { + } + } } namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration @@ -18,11 +25,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration using System.Collections.Generic; using System.Globalization; using System.Linq; + using System.Runtime.CompilerServices; - /// Provide core binding logic. [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] - file static class CoreBindingHelper + file static class BindingExtensions { + #region IConfiguration extensions. + /// Attempts to bind the configuration instance to a new instance of type T. + [InterceptsLocationAttribute(@"src-0.cs", 12, 17)] + public static T? Get(this IConfiguration configuration) => (T?)(GetCore(configuration, typeof(T), configureOptions: null) ?? default(T)); + #endregion IConfiguration extensions. + + #region Core binding extensions. private readonly static Lazy> s_configKeys_ProgramMyClassWithCustomCollections = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "CustomDictionary", "CustomList", "IReadOnlyList", "IReadOnlyDictionary" }); public static object? GetCore(this IConfiguration configuration, Type type, Action? configureOptions) @@ -46,7 +60,83 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return obj; } - throw new global::System.NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input."); + throw new NotSupportedException($"Unable to bind to type ''{type}'': generator did not detect the type as input."); + } + + public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) + { + if (configuration is null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + if (obj is null) + { + throw new ArgumentNullException(nameof(obj)); + } + + BinderOptions? binderOptions = GetBinderOptions(configureOptions); + + if (!HasValueOrChildren(configuration)) + { + return; + } + + if (obj is Program.CustomDictionary typedObj1) + { + BindCore(configuration, ref typedObj1, binderOptions); + return; + } + + if (obj is Program.CustomList typedObj3) + { + BindCore(configuration, ref typedObj3, binderOptions); + return; + } + + if (obj is List typedObj5) + { + BindCore(configuration, ref typedObj5, binderOptions); + return; + } + + if (obj is ICollection typedObj7) + { + BindCore(configuration, ref typedObj7, binderOptions); + return; + } + + if (obj is IReadOnlyList typedObj9) + { + BindCore(configuration, ref typedObj9, binderOptions); + return; + } + + if (obj is Dictionary typedObj11) + { + BindCore(configuration, ref typedObj11, binderOptions); + return; + } + + if (obj is IDictionary typedObj13) + { + BindCore(configuration, ref typedObj13, binderOptions); + return; + } + + if (obj is IReadOnlyDictionary typedObj15) + { + BindCore(configuration, ref typedObj15, binderOptions); + return; + } + + if (obj is Program.MyClassWithCustomCollections typedObj17) + { + BindCore(configuration, ref typedObj17, binderOptions); + return; + } + + throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); } public static void BindCore(IConfiguration configuration, ref Program.CustomDictionary obj, BinderOptions? binderOptions) @@ -196,36 +286,36 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration ValidateConfigurationKeys(typeof(Program.MyClassWithCustomCollections), s_configKeys_ProgramMyClassWithCustomCollections, configuration, binderOptions); - if (AsConfigWithChildren(configuration.GetSection("CustomDictionary")) is IConfigurationSection section1) + if (AsConfigWithChildren(configuration.GetSection("CustomDictionary")) is IConfigurationSection section19) { - Program.CustomDictionary? temp3 = obj.CustomDictionary; - temp3 ??= new Program.CustomDictionary(); - BindCore(section1, ref temp3, binderOptions); - obj.CustomDictionary = temp3; + Program.CustomDictionary? temp21 = obj.CustomDictionary; + temp21 ??= new Program.CustomDictionary(); + BindCore(section19, ref temp21, binderOptions); + obj.CustomDictionary = temp21; } - if (AsConfigWithChildren(configuration.GetSection("CustomList")) is IConfigurationSection section4) + if (AsConfigWithChildren(configuration.GetSection("CustomList")) is IConfigurationSection section22) { - Program.CustomList? temp6 = obj.CustomList; - temp6 ??= new Program.CustomList(); - BindCore(section4, ref temp6, binderOptions); - obj.CustomList = temp6; + Program.CustomList? temp24 = obj.CustomList; + temp24 ??= new Program.CustomList(); + BindCore(section22, ref temp24, binderOptions); + obj.CustomList = temp24; } - if (AsConfigWithChildren(configuration.GetSection("IReadOnlyList")) is IConfigurationSection section7) + if (AsConfigWithChildren(configuration.GetSection("IReadOnlyList")) is IConfigurationSection section25) { - IReadOnlyList? temp9 = obj.IReadOnlyList; - temp9 = temp9 is null ? new List() : new List(temp9); - BindCore(section7, ref temp9, binderOptions); - obj.IReadOnlyList = temp9; + IReadOnlyList? temp27 = obj.IReadOnlyList; + temp27 = temp27 is null ? new List() : new List(temp27); + BindCore(section25, ref temp27, binderOptions); + obj.IReadOnlyList = temp27; } - if (AsConfigWithChildren(configuration.GetSection("IReadOnlyDictionary")) is IConfigurationSection section10) + if (AsConfigWithChildren(configuration.GetSection("IReadOnlyDictionary")) is IConfigurationSection section28) { - IReadOnlyDictionary? temp12 = obj.IReadOnlyDictionary; - temp12 = temp12 is null ? new Dictionary() : temp12.ToDictionary(pair => pair.Key, pair => pair.Value); - BindCore(section10, ref temp12, binderOptions); - obj.IReadOnlyDictionary = temp12; + IReadOnlyDictionary? temp30 = obj.IReadOnlyDictionary; + temp30 = temp30 is null ? new Dictionary() : temp30.ToDictionary(pair => pair.Key, pair => pair.Value); + BindCore(section28, ref temp30, binderOptions); + obj.IReadOnlyDictionary = temp30; } } @@ -281,7 +371,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions.BindNonPublicProperties) { - throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); + throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } return binderOptions; @@ -298,5 +388,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception); } } + #endregion Core binding extensions. } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind.generated.txt index 406e8db671677..35187f8c8d65b 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind.generated.txt @@ -2,18 +2,19 @@ #nullable enable #pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code. -/// Generated helper providing an AOT and linking compatible implementation for configuration binding. -[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] -internal static class GeneratedConfigurationBinder +namespace System.Runtime.CompilerServices { - /// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively. - public static void Bind(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::Program.MyClass obj) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.BindCore(configuration, ref obj, binderOptions: null); - - /// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively. - public static void Bind(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::Program.MyClass obj, global::System.Action? configureOptions) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.BindCore(configuration, ref obj, global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetBinderOptions(configureOptions)); + using System; + using System.CodeDom.Compiler; - /// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively. - public static void Bind(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, string key, global::Program.MyClass obj) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.BindCore(configuration.GetSection(key), ref obj, binderOptions: null); + [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : Attribute + { + public InterceptsLocationAttribute(string filePath, int line, int column) + { + } + } } namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration @@ -23,13 +24,74 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration using System.CodeDom.Compiler; using System.Collections.Generic; using System.Globalization; + using System.Runtime.CompilerServices; - /// Provide core binding logic. [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] - file static class CoreBindingHelper + file static class BindingExtensions { + #region IConfiguration extensions. + /// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively. + [InterceptsLocationAttribute(@"src-0.cs", 13, 18)] + public static void Bind(this IConfiguration configuration, object? obj) => BindCoreMain(configuration, obj, configureOptions: null); + + /// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively. + [InterceptsLocationAttribute(@"src-0.cs", 14, 24)] + public static void Bind(this IConfiguration configuration, object? obj, Action? configureOptions) => BindCoreMain(configuration, obj, configureOptions); + + /// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively. + [InterceptsLocationAttribute(@"src-0.cs", 15, 24)] + public static void Bind(this IConfiguration configuration, string key, object? obj) => BindCoreMain(configuration?.GetSection(key), obj, configureOptions: null); + #endregion IConfiguration extensions. + + #region Core binding extensions. private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyDictionary", "MyComplexDictionary" }); + public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) + { + if (configuration is null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + if (obj is null) + { + throw new ArgumentNullException(nameof(obj)); + } + + BinderOptions? binderOptions = GetBinderOptions(configureOptions); + + if (!HasValueOrChildren(configuration)) + { + return; + } + + if (obj is List typedObj0) + { + BindCore(configuration, ref typedObj0, binderOptions); + return; + } + + if (obj is Dictionary typedObj2) + { + BindCore(configuration, ref typedObj2, binderOptions); + return; + } + + if (obj is Dictionary typedObj4) + { + BindCore(configuration, ref typedObj4, binderOptions); + return; + } + + if (obj is Program.MyClass typedObj6) + { + BindCore(configuration, ref typedObj6, binderOptions); + return; + } + + throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); + } + public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) { if (obj is null) @@ -90,33 +152,33 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration obj.MyString = configuration["MyString"]!; - if (configuration["MyInt"] is string value1) + if (configuration["MyInt"] is string value9) { - obj.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value9, () => configuration.GetSection("MyInt").Path); } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section10) { - List? temp4 = obj.MyList; - temp4 ??= new List(); - BindCore(section2, ref temp4, binderOptions); - obj.MyList = temp4; + List? temp12 = obj.MyList; + temp12 ??= new List(); + BindCore(section10, ref temp12, binderOptions); + obj.MyList = temp12; } - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section5) + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section13) { - Dictionary? temp7 = obj.MyDictionary; - temp7 ??= new Dictionary(); - BindCore(section5, ref temp7, binderOptions); - obj.MyDictionary = temp7; + Dictionary? temp15 = obj.MyDictionary; + temp15 ??= new Dictionary(); + BindCore(section13, ref temp15, binderOptions); + obj.MyDictionary = temp15; } - if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section8) + if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section16) { - Dictionary? temp10 = obj.MyComplexDictionary; - temp10 ??= new Dictionary(); - BindCore(section8, ref temp10, binderOptions); - obj.MyComplexDictionary = temp10; + Dictionary? temp18 = obj.MyComplexDictionary; + temp18 ??= new Dictionary(); + BindCore(section16, ref temp18, binderOptions); + obj.MyComplexDictionary = temp18; } } @@ -142,6 +204,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } } + public static bool HasValueOrChildren(IConfiguration configuration) + { + if ((configuration as IConfigurationSection)?.Value is not null) + { + return true; + } + return AsConfigWithChildren(configuration) is not null; + } + public static IConfiguration? AsConfigWithChildren(IConfiguration configuration) { foreach (IConfigurationSection _ in configuration.GetChildren()) @@ -163,7 +234,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions.BindNonPublicProperties) { - throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); + throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } return binderOptions; @@ -180,5 +251,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception); } } + #endregion Core binding extensions. } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance.generated.txt index 106e01795369e..e1946bd7caddd 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance.generated.txt @@ -2,12 +2,19 @@ #nullable enable #pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code. -/// Generated helper providing an AOT and linking compatible implementation for configuration binding. -[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] -internal static class GeneratedConfigurationBinder +namespace System.Runtime.CompilerServices { - /// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively. - public static void Bind(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::Program.MyClass obj) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.BindCore(configuration, ref obj, binderOptions: null); + using System; + using System.CodeDom.Compiler; + + [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : Attribute + { + public InterceptsLocationAttribute(string filePath, int line, int column) + { + } + } } namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration @@ -17,13 +24,66 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration using System.CodeDom.Compiler; using System.Collections.Generic; using System.Globalization; + using System.Runtime.CompilerServices; - /// Provide core binding logic. [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] - file static class CoreBindingHelper + file static class BindingExtensions { + #region IConfiguration extensions. + /// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively. + [InterceptsLocationAttribute(@"src-0.cs", 12, 20)] + public static void Bind(this IConfiguration configuration, object? obj) => BindCoreMain(configuration, obj, configureOptions: null); + #endregion IConfiguration extensions. + + #region Core binding extensions. private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyDictionary", "MyComplexDictionary" }); + public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) + { + if (configuration is null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + if (obj is null) + { + throw new ArgumentNullException(nameof(obj)); + } + + BinderOptions? binderOptions = GetBinderOptions(configureOptions); + + if (!HasValueOrChildren(configuration)) + { + return; + } + + if (obj is List typedObj0) + { + BindCore(configuration, ref typedObj0, binderOptions); + return; + } + + if (obj is Dictionary typedObj2) + { + BindCore(configuration, ref typedObj2, binderOptions); + return; + } + + if (obj is Dictionary typedObj4) + { + BindCore(configuration, ref typedObj4, binderOptions); + return; + } + + if (obj is Program.MyClass typedObj6) + { + BindCore(configuration, ref typedObj6, binderOptions); + return; + } + + throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); + } + public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) { if (obj is null) @@ -84,33 +144,33 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration obj.MyString = configuration["MyString"]!; - if (configuration["MyInt"] is string value1) + if (configuration["MyInt"] is string value9) { - obj.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value9, () => configuration.GetSection("MyInt").Path); } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section10) { - List? temp4 = obj.MyList; - temp4 ??= new List(); - BindCore(section2, ref temp4, binderOptions); - obj.MyList = temp4; + List? temp12 = obj.MyList; + temp12 ??= new List(); + BindCore(section10, ref temp12, binderOptions); + obj.MyList = temp12; } - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section5) + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section13) { - Dictionary? temp7 = obj.MyDictionary; - temp7 ??= new Dictionary(); - BindCore(section5, ref temp7, binderOptions); - obj.MyDictionary = temp7; + Dictionary? temp15 = obj.MyDictionary; + temp15 ??= new Dictionary(); + BindCore(section13, ref temp15, binderOptions); + obj.MyDictionary = temp15; } - if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section8) + if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section16) { - Dictionary? temp10 = obj.MyComplexDictionary; - temp10 ??= new Dictionary(); - BindCore(section8, ref temp10, binderOptions); - obj.MyComplexDictionary = temp10; + Dictionary? temp18 = obj.MyComplexDictionary; + temp18 ??= new Dictionary(); + BindCore(section16, ref temp18, binderOptions); + obj.MyComplexDictionary = temp18; } } @@ -136,6 +196,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } } + public static bool HasValueOrChildren(IConfiguration configuration) + { + if ((configuration as IConfigurationSection)?.Value is not null) + { + return true; + } + return AsConfigWithChildren(configuration) is not null; + } + public static IConfiguration? AsConfigWithChildren(IConfiguration configuration) { foreach (IConfigurationSection _ in configuration.GetChildren()) @@ -145,6 +214,24 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return null; } + public static BinderOptions? GetBinderOptions(Action? configureOptions) + { + if (configureOptions is null) + { + return null; + } + + BinderOptions binderOptions = new(); + configureOptions(binderOptions); + + if (binderOptions.BindNonPublicProperties) + { + throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); + } + + return binderOptions; + } + public static int ParseInt(string value, Func getPath) { try @@ -156,5 +243,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception); } } + #endregion Core binding extensions. } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance_BinderOptions.generated.txt index a1cb7d6b93b5d..9d099198acdc0 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance_BinderOptions.generated.txt @@ -2,12 +2,19 @@ #nullable enable #pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code. -/// Generated helper providing an AOT and linking compatible implementation for configuration binding. -[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] -internal static class GeneratedConfigurationBinder +namespace System.Runtime.CompilerServices { - /// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively. - public static void Bind(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::Program.MyClass obj, global::System.Action? configureOptions) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.BindCore(configuration, ref obj, global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetBinderOptions(configureOptions)); + using System; + using System.CodeDom.Compiler; + + [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : Attribute + { + public InterceptsLocationAttribute(string filePath, int line, int column) + { + } + } } namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration @@ -17,13 +24,66 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration using System.CodeDom.Compiler; using System.Collections.Generic; using System.Globalization; + using System.Runtime.CompilerServices; - /// Provide core binding logic. [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] - file static class CoreBindingHelper + file static class BindingExtensions { + #region IConfiguration extensions. + /// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively. + [InterceptsLocationAttribute(@"src-0.cs", 12, 20)] + public static void Bind(this IConfiguration configuration, object? obj, Action? configureOptions) => BindCoreMain(configuration, obj, configureOptions); + #endregion IConfiguration extensions. + + #region Core binding extensions. private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyDictionary", "MyComplexDictionary" }); + public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) + { + if (configuration is null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + if (obj is null) + { + throw new ArgumentNullException(nameof(obj)); + } + + BinderOptions? binderOptions = GetBinderOptions(configureOptions); + + if (!HasValueOrChildren(configuration)) + { + return; + } + + if (obj is List typedObj0) + { + BindCore(configuration, ref typedObj0, binderOptions); + return; + } + + if (obj is Dictionary typedObj2) + { + BindCore(configuration, ref typedObj2, binderOptions); + return; + } + + if (obj is Dictionary typedObj4) + { + BindCore(configuration, ref typedObj4, binderOptions); + return; + } + + if (obj is Program.MyClass typedObj6) + { + BindCore(configuration, ref typedObj6, binderOptions); + return; + } + + throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); + } + public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) { if (obj is null) @@ -84,33 +144,33 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration obj.MyString = configuration["MyString"]!; - if (configuration["MyInt"] is string value1) + if (configuration["MyInt"] is string value9) { - obj.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value9, () => configuration.GetSection("MyInt").Path); } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section10) { - List? temp4 = obj.MyList; - temp4 ??= new List(); - BindCore(section2, ref temp4, binderOptions); - obj.MyList = temp4; + List? temp12 = obj.MyList; + temp12 ??= new List(); + BindCore(section10, ref temp12, binderOptions); + obj.MyList = temp12; } - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section5) + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section13) { - Dictionary? temp7 = obj.MyDictionary; - temp7 ??= new Dictionary(); - BindCore(section5, ref temp7, binderOptions); - obj.MyDictionary = temp7; + Dictionary? temp15 = obj.MyDictionary; + temp15 ??= new Dictionary(); + BindCore(section13, ref temp15, binderOptions); + obj.MyDictionary = temp15; } - if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section8) + if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section16) { - Dictionary? temp10 = obj.MyComplexDictionary; - temp10 ??= new Dictionary(); - BindCore(section8, ref temp10, binderOptions); - obj.MyComplexDictionary = temp10; + Dictionary? temp18 = obj.MyComplexDictionary; + temp18 ??= new Dictionary(); + BindCore(section16, ref temp18, binderOptions); + obj.MyComplexDictionary = temp18; } } @@ -136,6 +196,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } } + public static bool HasValueOrChildren(IConfiguration configuration) + { + if ((configuration as IConfigurationSection)?.Value is not null) + { + return true; + } + return AsConfigWithChildren(configuration) is not null; + } + public static IConfiguration? AsConfigWithChildren(IConfiguration configuration) { foreach (IConfigurationSection _ in configuration.GetChildren()) @@ -157,7 +226,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions.BindNonPublicProperties) { - throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); + throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } return binderOptions; @@ -174,5 +243,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception); } } + #endregion Core binding extensions. } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Key_Instance.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Key_Instance.generated.txt index f3ee8a9ff4384..74af70d28e767 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Key_Instance.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Key_Instance.generated.txt @@ -2,12 +2,19 @@ #nullable enable #pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code. -/// Generated helper providing an AOT and linking compatible implementation for configuration binding. -[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] -internal static class GeneratedConfigurationBinder +namespace System.Runtime.CompilerServices { - /// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively. - public static void Bind(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, string key, global::Program.MyClass obj) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.BindCore(configuration.GetSection(key), ref obj, binderOptions: null); + using System; + using System.CodeDom.Compiler; + + [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : Attribute + { + public InterceptsLocationAttribute(string filePath, int line, int column) + { + } + } } namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration @@ -17,13 +24,66 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration using System.CodeDom.Compiler; using System.Collections.Generic; using System.Globalization; + using System.Runtime.CompilerServices; - /// Provide core binding logic. [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] - file static class CoreBindingHelper + file static class BindingExtensions { + #region IConfiguration extensions. + /// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively. + [InterceptsLocationAttribute(@"src-0.cs", 12, 20)] + public static void Bind(this IConfiguration configuration, string key, object? obj) => BindCoreMain(configuration?.GetSection(key), obj, configureOptions: null); + #endregion IConfiguration extensions. + + #region Core binding extensions. private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyDictionary", "MyComplexDictionary" }); + public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) + { + if (configuration is null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + if (obj is null) + { + throw new ArgumentNullException(nameof(obj)); + } + + BinderOptions? binderOptions = GetBinderOptions(configureOptions); + + if (!HasValueOrChildren(configuration)) + { + return; + } + + if (obj is List typedObj0) + { + BindCore(configuration, ref typedObj0, binderOptions); + return; + } + + if (obj is Dictionary typedObj2) + { + BindCore(configuration, ref typedObj2, binderOptions); + return; + } + + if (obj is Dictionary typedObj4) + { + BindCore(configuration, ref typedObj4, binderOptions); + return; + } + + if (obj is Program.MyClass typedObj6) + { + BindCore(configuration, ref typedObj6, binderOptions); + return; + } + + throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); + } + public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) { if (obj is null) @@ -84,33 +144,33 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration obj.MyString = configuration["MyString"]!; - if (configuration["MyInt"] is string value1) + if (configuration["MyInt"] is string value9) { - obj.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value9, () => configuration.GetSection("MyInt").Path); } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section10) { - List? temp4 = obj.MyList; - temp4 ??= new List(); - BindCore(section2, ref temp4, binderOptions); - obj.MyList = temp4; + List? temp12 = obj.MyList; + temp12 ??= new List(); + BindCore(section10, ref temp12, binderOptions); + obj.MyList = temp12; } - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section5) + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section13) { - Dictionary? temp7 = obj.MyDictionary; - temp7 ??= new Dictionary(); - BindCore(section5, ref temp7, binderOptions); - obj.MyDictionary = temp7; + Dictionary? temp15 = obj.MyDictionary; + temp15 ??= new Dictionary(); + BindCore(section13, ref temp15, binderOptions); + obj.MyDictionary = temp15; } - if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section8) + if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section16) { - Dictionary? temp10 = obj.MyComplexDictionary; - temp10 ??= new Dictionary(); - BindCore(section8, ref temp10, binderOptions); - obj.MyComplexDictionary = temp10; + Dictionary? temp18 = obj.MyComplexDictionary; + temp18 ??= new Dictionary(); + BindCore(section16, ref temp18, binderOptions); + obj.MyComplexDictionary = temp18; } } @@ -136,6 +196,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } } + public static bool HasValueOrChildren(IConfiguration configuration) + { + if ((configuration as IConfigurationSection)?.Value is not null) + { + return true; + } + return AsConfigWithChildren(configuration) is not null; + } + public static IConfiguration? AsConfigWithChildren(IConfiguration configuration) { foreach (IConfigurationSection _ in configuration.GetChildren()) @@ -145,6 +214,24 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return null; } + public static BinderOptions? GetBinderOptions(Action? configureOptions) + { + if (configureOptions is null) + { + return null; + } + + BinderOptions binderOptions = new(); + configureOptions(binderOptions); + + if (binderOptions.BindNonPublicProperties) + { + throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); + } + + return binderOptions; + } + public static int ParseInt(string value, Func getPath) { try @@ -156,5 +243,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception); } } + #endregion Core binding extensions. } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get.generated.txt index fb71c70b4dd3b..5b512b6414f94 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get.generated.txt @@ -2,21 +2,19 @@ #nullable enable #pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code. -/// Generated helper providing an AOT and linking compatible implementation for configuration binding. -[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] -internal static class GeneratedConfigurationBinder +namespace System.Runtime.CompilerServices { - /// Attempts to bind the configuration instance to a new instance of type T. - public static T? Get(this global::Microsoft.Extensions.Configuration.IConfiguration configuration) => (T?)(global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetCore(configuration, typeof(T), configureOptions: null) ?? default(T)); - - /// Attempts to bind the configuration instance to a new instance of type T. - public static T? Get(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Action? configureOptions) => (T?)(global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetCore(configuration, typeof(T), configureOptions) ?? default(T)); - - /// Attempts to bind the configuration instance to a new instance of type T. - public static object? Get(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Type type) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetCore(configuration, type, configureOptions: null); + using System; + using System.CodeDom.Compiler; - /// Attempts to bind the configuration instance to a new instance of type T. - public static object? Get(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Type type, global::System.Action? configureOptions) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetCore(configuration, type, configureOptions); + [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : Attribute + { + public InterceptsLocationAttribute(string filePath, int line, int column) + { + } + } } namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration @@ -26,11 +24,30 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration using System.CodeDom.Compiler; using System.Collections.Generic; using System.Globalization; + using System.Runtime.CompilerServices; - /// Provide core binding logic. [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] - file static class CoreBindingHelper + file static class BindingExtensions { + #region IConfiguration extensions. + /// Attempts to bind the configuration instance to a new instance of type T. + [InterceptsLocationAttribute(@"src-0.cs", 12, 38)] + public static T? Get(this IConfiguration configuration) => (T?)(GetCore(configuration, typeof(T), configureOptions: null) ?? default(T)); + + /// Attempts to bind the configuration instance to a new instance of type T. + [InterceptsLocationAttribute(@"src-0.cs", 14, 36)] + public static T? Get(this IConfiguration configuration, Action? configureOptions) => (T?)(GetCore(configuration, typeof(T), configureOptions) ?? default(T)); + + /// Attempts to bind the configuration instance to a new instance of type T. + [InterceptsLocationAttribute(@"src-0.cs", 13, 36)] + public static object? Get(this IConfiguration configuration, Type type) => GetCore(configuration, type, configureOptions: null); + + /// Attempts to bind the configuration instance to a new instance of type T. + [InterceptsLocationAttribute(@"src-0.cs", 15, 36)] + public static object? Get(this IConfiguration configuration, Type type, Action? configureOptions) => GetCore(configuration, type, configureOptions); + #endregion IConfiguration extensions. + + #region Core binding extensions. private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyArray", "MyDictionary" }); private readonly static Lazy> s_configKeys_ProgramMyClass2 = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyInt" }); @@ -62,7 +79,59 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return obj; } - throw new global::System.NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input."); + throw new NotSupportedException($"Unable to bind to type ''{type}'': generator did not detect the type as input."); + } + + public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) + { + if (configuration is null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + if (obj is null) + { + throw new ArgumentNullException(nameof(obj)); + } + + BinderOptions? binderOptions = GetBinderOptions(configureOptions); + + if (!HasValueOrChildren(configuration)) + { + return; + } + + if (obj is List typedObj2) + { + BindCore(configuration, ref typedObj2, binderOptions); + return; + } + + if (obj is int[] typedObj4) + { + BindCore(configuration, ref typedObj4, binderOptions); + return; + } + + if (obj is Dictionary typedObj6) + { + BindCore(configuration, ref typedObj6, binderOptions); + return; + } + + if (obj is Program.MyClass typedObj8) + { + BindCore(configuration, ref typedObj8, binderOptions); + return; + } + + if (obj is Program.MyClass2 typedObj10) + { + BindCore(configuration, ref typedObj10, binderOptions); + return; + } + + throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); } public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) @@ -88,11 +157,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new ArgumentNullException(nameof(obj)); } - var temp2 = new List(); - BindCore(configuration, ref temp2, binderOptions); + var temp12 = new List(); + BindCore(configuration, ref temp12, binderOptions); int originalCount = obj.Length; - Array.Resize(ref obj, originalCount + temp2.Count); - temp2.CopyTo(obj, originalCount); + Array.Resize(ref obj, originalCount + temp12.Count); + temp12.CopyTo(obj, originalCount); } public static void BindCore(IConfiguration configuration, ref Dictionary obj, BinderOptions? binderOptions) @@ -122,33 +191,33 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration obj.MyString = configuration["MyString"]!; - if (configuration["MyInt"] is string value5) + if (configuration["MyInt"] is string value15) { - obj.MyInt = ParseInt(value5, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value15, () => configuration.GetSection("MyInt").Path); } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section6) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section16) { - List? temp8 = obj.MyList; - temp8 ??= new List(); - BindCore(section6, ref temp8, binderOptions); - obj.MyList = temp8; + List? temp18 = obj.MyList; + temp18 ??= new List(); + BindCore(section16, ref temp18, binderOptions); + obj.MyList = temp18; } - if (AsConfigWithChildren(configuration.GetSection("MyArray")) is IConfigurationSection section9) + if (AsConfigWithChildren(configuration.GetSection("MyArray")) is IConfigurationSection section19) { - int[]? temp11 = obj.MyArray; - temp11 ??= new int[0]; - BindCore(section9, ref temp11, binderOptions); - obj.MyArray = temp11; + int[]? temp21 = obj.MyArray; + temp21 ??= new int[0]; + BindCore(section19, ref temp21, binderOptions); + obj.MyArray = temp21; } - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section12) + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section22) { - Dictionary? temp14 = obj.MyDictionary; - temp14 ??= new Dictionary(); - BindCore(section12, ref temp14, binderOptions); - obj.MyDictionary = temp14; + Dictionary? temp24 = obj.MyDictionary; + temp24 ??= new Dictionary(); + BindCore(section22, ref temp24, binderOptions); + obj.MyDictionary = temp24; } } @@ -161,9 +230,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions); - if (configuration["MyInt"] is string value15) + if (configuration["MyInt"] is string value25) { - obj.MyInt = ParseInt(value15, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value25, () => configuration.GetSection("MyInt").Path); } } @@ -219,7 +288,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions.BindNonPublicProperties) { - throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); + throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } return binderOptions; @@ -236,5 +305,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception); } } + #endregion Core binding extensions. } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue.generated.txt index c9d49faa93724..1cf54cb905bb5 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue.generated.txt @@ -2,21 +2,19 @@ #nullable enable #pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code. -/// Generated helper providing an AOT and linking compatible implementation for configuration binding. -[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] -internal static class GeneratedConfigurationBinder +namespace System.Runtime.CompilerServices { - /// Extracts the value with the specified key and converts it to the specified type. - public static T? GetValue(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, string key) => (T?)(global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetValueCore(configuration, typeof(T), key) ?? default(T)); - - /// Extracts the value with the specified key and converts it to the specified type. - public static T? GetValue(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, string key, T defaultValue) => (T?)(global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetValueCore(configuration, typeof(T), key) ?? defaultValue); - - /// Extracts the value with the specified key and converts it to the specified type. - public static object? GetValue(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Type type, string key) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetValueCore(configuration, type, key); + using System; + using System.CodeDom.Compiler; - /// Extracts the value with the specified key and converts it to the specified type. - public static object? GetValue(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Type type, string key, object? defaultValue) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetValueCore(configuration, type, key) ?? defaultValue; + [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : Attribute + { + public InterceptsLocationAttribute(string filePath, int line, int column) + { + } + } } namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration @@ -25,11 +23,30 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration using System; using System.CodeDom.Compiler; using System.Globalization; + using System.Runtime.CompilerServices; - /// Provide core binding logic. [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] - file static class CoreBindingHelper + file static class BindingExtensions { + #region IConfiguration extensions. + /// Extracts the value with the specified key and converts it to the specified type. + [InterceptsLocationAttribute(@"src-0.cs", 13, 18)] + public static T? GetValue(this IConfiguration configuration, string key) => (T?)(BindingExtensions.GetValueCore(configuration, typeof(T), key) ?? default(T)); + + /// Extracts the value with the specified key and converts it to the specified type. + [InterceptsLocationAttribute(@"src-0.cs", 16, 24)] + public static T? GetValue(this IConfiguration configuration, string key, T defaultValue) => (T?)(BindingExtensions.GetValueCore(configuration, typeof(T), key) ?? defaultValue); + + /// Extracts the value with the specified key and converts it to the specified type. + [InterceptsLocationAttribute(@"src-0.cs", 14, 24)] + public static object? GetValue(this IConfiguration configuration, Type type, string key) => BindingExtensions.GetValueCore(configuration, type, key); + + /// Extracts the value with the specified key and converts it to the specified type. + [InterceptsLocationAttribute(@"src-0.cs", 17, 24)] + public static object? GetValue(this IConfiguration configuration, Type type, string key, object? defaultValue) => BindingExtensions.GetValueCore(configuration, type, key) ?? defaultValue; + #endregion IConfiguration extensions. + + #region Core binding extensions. public static object? GetValueCore(this IConfiguration configuration, Type type, string key) { if (configuration is null) @@ -114,5 +131,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(CultureInfo)}'.", exception); } } + #endregion Core binding extensions. } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_T_Key.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_T_Key.generated.txt index 17c963bd980a7..2438cf530ca4c 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_T_Key.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_T_Key.generated.txt @@ -2,12 +2,19 @@ #nullable enable #pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code. -/// Generated helper providing an AOT and linking compatible implementation for configuration binding. -[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] -internal static class GeneratedConfigurationBinder +namespace System.Runtime.CompilerServices { - /// Extracts the value with the specified key and converts it to the specified type. - public static T? GetValue(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, string key) => (T?)(global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetValueCore(configuration, typeof(T), key) ?? default(T)); + using System; + using System.CodeDom.Compiler; + + [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : Attribute + { + public InterceptsLocationAttribute(string filePath, int line, int column) + { + } + } } namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration @@ -16,11 +23,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration using System; using System.CodeDom.Compiler; using System.Globalization; + using System.Runtime.CompilerServices; - /// Provide core binding logic. [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] - file static class CoreBindingHelper + file static class BindingExtensions { + #region IConfiguration extensions. + /// Extracts the value with the specified key and converts it to the specified type. + [InterceptsLocationAttribute(@"src-0.cs", 10, 20)] + public static T? GetValue(this IConfiguration configuration, string key) => (T?)(BindingExtensions.GetValueCore(configuration, typeof(T), key) ?? default(T)); + #endregion IConfiguration extensions. + + #region Core binding extensions. public static object? GetValueCore(this IConfiguration configuration, Type type, string key) { if (configuration is null) @@ -54,5 +68,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception); } } + #endregion Core binding extensions. } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_T_Key_DefaultValue.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_T_Key_DefaultValue.generated.txt index 1148109b9f5a8..e6db24d522f3c 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_T_Key_DefaultValue.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_T_Key_DefaultValue.generated.txt @@ -2,12 +2,19 @@ #nullable enable #pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code. -/// Generated helper providing an AOT and linking compatible implementation for configuration binding. -[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] -internal static class GeneratedConfigurationBinder +namespace System.Runtime.CompilerServices { - /// Extracts the value with the specified key and converts it to the specified type. - public static T? GetValue(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, string key, T defaultValue) => (T?)(global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetValueCore(configuration, typeof(T), key) ?? defaultValue); + using System; + using System.CodeDom.Compiler; + + [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : Attribute + { + public InterceptsLocationAttribute(string filePath, int line, int column) + { + } + } } namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration @@ -16,11 +23,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration using System; using System.CodeDom.Compiler; using System.Globalization; + using System.Runtime.CompilerServices; - /// Provide core binding logic. [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] - file static class CoreBindingHelper + file static class BindingExtensions { + #region IConfiguration extensions. + /// Extracts the value with the specified key and converts it to the specified type. + [InterceptsLocationAttribute(@"src-0.cs", 12, 20)] + public static T? GetValue(this IConfiguration configuration, string key, T defaultValue) => (T?)(BindingExtensions.GetValueCore(configuration, typeof(T), key) ?? defaultValue); + #endregion IConfiguration extensions. + + #region Core binding extensions. public static object? GetValueCore(this IConfiguration configuration, Type type, string key) { if (configuration is null) @@ -54,5 +68,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception); } } + #endregion Core binding extensions. } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_TypeOf_Key.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_TypeOf_Key.generated.txt index c833b20f18dcf..a36f9fafebcff 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_TypeOf_Key.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_TypeOf_Key.generated.txt @@ -2,12 +2,19 @@ #nullable enable #pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code. -/// Generated helper providing an AOT and linking compatible implementation for configuration binding. -[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] -internal static class GeneratedConfigurationBinder +namespace System.Runtime.CompilerServices { - /// Extracts the value with the specified key and converts it to the specified type. - public static object? GetValue(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Type type, string key) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetValueCore(configuration, type, key); + using System; + using System.CodeDom.Compiler; + + [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : Attribute + { + public InterceptsLocationAttribute(string filePath, int line, int column) + { + } + } } namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration @@ -16,11 +23,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration using System; using System.CodeDom.Compiler; using System.Globalization; + using System.Runtime.CompilerServices; - /// Provide core binding logic. [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] - file static class CoreBindingHelper + file static class BindingExtensions { + #region IConfiguration extensions. + /// Extracts the value with the specified key and converts it to the specified type. + [InterceptsLocationAttribute(@"src-0.cs", 10, 20)] + public static object? GetValue(this IConfiguration configuration, Type type, string key) => BindingExtensions.GetValueCore(configuration, type, key); + #endregion IConfiguration extensions. + + #region Core binding extensions. public static object? GetValueCore(this IConfiguration configuration, Type type, string key) { if (configuration is null) @@ -54,5 +68,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(bool)}'.", exception); } } + #endregion Core binding extensions. } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_TypeOf_Key_DefaultValue.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_TypeOf_Key_DefaultValue.generated.txt index f773f79ce6c2c..356f6bb1a933e 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_TypeOf_Key_DefaultValue.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue_TypeOf_Key_DefaultValue.generated.txt @@ -2,12 +2,19 @@ #nullable enable #pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code. -/// Generated helper providing an AOT and linking compatible implementation for configuration binding. -[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] -internal static class GeneratedConfigurationBinder +namespace System.Runtime.CompilerServices { - /// Extracts the value with the specified key and converts it to the specified type. - public static object? GetValue(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Type type, string key, object? defaultValue) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetValueCore(configuration, type, key) ?? defaultValue; + using System; + using System.CodeDom.Compiler; + + [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : Attribute + { + public InterceptsLocationAttribute(string filePath, int line, int column) + { + } + } } namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration @@ -16,11 +23,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration using System; using System.CodeDom.Compiler; using System.Globalization; + using System.Runtime.CompilerServices; - /// Provide core binding logic. [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] - file static class CoreBindingHelper + file static class BindingExtensions { + #region IConfiguration extensions. + /// Extracts the value with the specified key and converts it to the specified type. + [InterceptsLocationAttribute(@"src-0.cs", 11, 20)] + public static object? GetValue(this IConfiguration configuration, Type type, string key, object? defaultValue) => BindingExtensions.GetValueCore(configuration, type, key) ?? defaultValue; + #endregion IConfiguration extensions. + + #region Core binding extensions. public static object? GetValueCore(this IConfiguration configuration, Type type, string key) { if (configuration is null) @@ -54,5 +68,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(CultureInfo)}'.", exception); } } + #endregion Core binding extensions. } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T.generated.txt index de8201fe6fed2..ebf58aaae2040 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T.generated.txt @@ -2,12 +2,19 @@ #nullable enable #pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code. -/// Generated helper providing an AOT and linking compatible implementation for configuration binding. -[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] -internal static class GeneratedConfigurationBinder +namespace System.Runtime.CompilerServices { - /// Attempts to bind the configuration instance to a new instance of type T. - public static T? Get(this global::Microsoft.Extensions.Configuration.IConfiguration configuration) => (T?)(global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetCore(configuration, typeof(T), configureOptions: null) ?? default(T)); + using System; + using System.CodeDom.Compiler; + + [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : Attribute + { + public InterceptsLocationAttribute(string filePath, int line, int column) + { + } + } } namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration @@ -17,11 +24,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration using System.CodeDom.Compiler; using System.Collections.Generic; using System.Globalization; + using System.Runtime.CompilerServices; - /// Provide core binding logic. [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] - file static class CoreBindingHelper + file static class BindingExtensions { + #region IConfiguration extensions. + /// Attempts to bind the configuration instance to a new instance of type T. + [InterceptsLocationAttribute(@"src-0.cs", 11, 40)] + public static T? Get(this IConfiguration configuration) => (T?)(GetCore(configuration, typeof(T), configureOptions: null) ?? default(T)); + #endregion IConfiguration extensions. + + #region Core binding extensions. private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyArray", "MyDictionary" }); public static object? GetCore(this IConfiguration configuration, Type type, Action? configureOptions) @@ -45,7 +59,53 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return obj; } - throw new global::System.NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input."); + throw new NotSupportedException($"Unable to bind to type ''{type}'': generator did not detect the type as input."); + } + + public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) + { + if (configuration is null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + if (obj is null) + { + throw new ArgumentNullException(nameof(obj)); + } + + BinderOptions? binderOptions = GetBinderOptions(configureOptions); + + if (!HasValueOrChildren(configuration)) + { + return; + } + + if (obj is List typedObj1) + { + BindCore(configuration, ref typedObj1, binderOptions); + return; + } + + if (obj is int[] typedObj3) + { + BindCore(configuration, ref typedObj3, binderOptions); + return; + } + + if (obj is Dictionary typedObj5) + { + BindCore(configuration, ref typedObj5, binderOptions); + return; + } + + if (obj is Program.MyClass typedObj7) + { + BindCore(configuration, ref typedObj7, binderOptions); + return; + } + + throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); } public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) @@ -71,11 +131,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new ArgumentNullException(nameof(obj)); } - var temp1 = new List(); - BindCore(configuration, ref temp1, binderOptions); + var temp9 = new List(); + BindCore(configuration, ref temp9, binderOptions); int originalCount = obj.Length; - Array.Resize(ref obj, originalCount + temp1.Count); - temp1.CopyTo(obj, originalCount); + Array.Resize(ref obj, originalCount + temp9.Count); + temp9.CopyTo(obj, originalCount); } public static void BindCore(IConfiguration configuration, ref Dictionary obj, BinderOptions? binderOptions) @@ -105,33 +165,33 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration obj.MyString = configuration["MyString"]!; - if (configuration["MyInt"] is string value4) + if (configuration["MyInt"] is string value12) { - obj.MyInt = ParseInt(value4, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value12, () => configuration.GetSection("MyInt").Path); } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section13) { - List? temp7 = obj.MyList; - temp7 ??= new List(); - BindCore(section5, ref temp7, binderOptions); - obj.MyList = temp7; + List? temp15 = obj.MyList; + temp15 ??= new List(); + BindCore(section13, ref temp15, binderOptions); + obj.MyList = temp15; } - if (AsConfigWithChildren(configuration.GetSection("MyArray")) is IConfigurationSection section8) + if (AsConfigWithChildren(configuration.GetSection("MyArray")) is IConfigurationSection section16) { - int[]? temp10 = obj.MyArray; - temp10 ??= new int[0]; - BindCore(section8, ref temp10, binderOptions); - obj.MyArray = temp10; + int[]? temp18 = obj.MyArray; + temp18 ??= new int[0]; + BindCore(section16, ref temp18, binderOptions); + obj.MyArray = temp18; } - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section11) + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section19) { - Dictionary? temp13 = obj.MyDictionary; - temp13 ??= new Dictionary(); - BindCore(section11, ref temp13, binderOptions); - obj.MyDictionary = temp13; + Dictionary? temp21 = obj.MyDictionary; + temp21 ??= new Dictionary(); + BindCore(section19, ref temp21, binderOptions); + obj.MyDictionary = temp21; } } @@ -187,7 +247,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions.BindNonPublicProperties) { - throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); + throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } return binderOptions; @@ -204,5 +264,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception); } } + #endregion Core binding extensions. } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T_BinderOptions.generated.txt index 34fadacace146..a203fcc55ebcc 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T_BinderOptions.generated.txt @@ -2,12 +2,19 @@ #nullable enable #pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code. -/// Generated helper providing an AOT and linking compatible implementation for configuration binding. -[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] -internal static class GeneratedConfigurationBinder +namespace System.Runtime.CompilerServices { - /// Attempts to bind the configuration instance to a new instance of type T. - public static T? Get(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Action? configureOptions) => (T?)(global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetCore(configuration, typeof(T), configureOptions) ?? default(T)); + using System; + using System.CodeDom.Compiler; + + [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : Attribute + { + public InterceptsLocationAttribute(string filePath, int line, int column) + { + } + } } namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration @@ -17,11 +24,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration using System.CodeDom.Compiler; using System.Collections.Generic; using System.Globalization; + using System.Runtime.CompilerServices; - /// Provide core binding logic. [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] - file static class CoreBindingHelper + file static class BindingExtensions { + #region IConfiguration extensions. + /// Attempts to bind the configuration instance to a new instance of type T. + [InterceptsLocationAttribute(@"src-0.cs", 11, 40)] + public static T? Get(this IConfiguration configuration, Action? configureOptions) => (T?)(GetCore(configuration, typeof(T), configureOptions) ?? default(T)); + #endregion IConfiguration extensions. + + #region Core binding extensions. private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyArray", "MyDictionary" }); public static object? GetCore(this IConfiguration configuration, Type type, Action? configureOptions) @@ -45,7 +59,53 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return obj; } - throw new global::System.NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input."); + throw new NotSupportedException($"Unable to bind to type ''{type}'': generator did not detect the type as input."); + } + + public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) + { + if (configuration is null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + if (obj is null) + { + throw new ArgumentNullException(nameof(obj)); + } + + BinderOptions? binderOptions = GetBinderOptions(configureOptions); + + if (!HasValueOrChildren(configuration)) + { + return; + } + + if (obj is List typedObj1) + { + BindCore(configuration, ref typedObj1, binderOptions); + return; + } + + if (obj is int[] typedObj3) + { + BindCore(configuration, ref typedObj3, binderOptions); + return; + } + + if (obj is Dictionary typedObj5) + { + BindCore(configuration, ref typedObj5, binderOptions); + return; + } + + if (obj is Program.MyClass typedObj7) + { + BindCore(configuration, ref typedObj7, binderOptions); + return; + } + + throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); } public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) @@ -71,11 +131,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new ArgumentNullException(nameof(obj)); } - var temp1 = new List(); - BindCore(configuration, ref temp1, binderOptions); + var temp9 = new List(); + BindCore(configuration, ref temp9, binderOptions); int originalCount = obj.Length; - Array.Resize(ref obj, originalCount + temp1.Count); - temp1.CopyTo(obj, originalCount); + Array.Resize(ref obj, originalCount + temp9.Count); + temp9.CopyTo(obj, originalCount); } public static void BindCore(IConfiguration configuration, ref Dictionary obj, BinderOptions? binderOptions) @@ -105,33 +165,33 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration obj.MyString = configuration["MyString"]!; - if (configuration["MyInt"] is string value4) + if (configuration["MyInt"] is string value12) { - obj.MyInt = ParseInt(value4, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value12, () => configuration.GetSection("MyInt").Path); } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section13) { - List? temp7 = obj.MyList; - temp7 ??= new List(); - BindCore(section5, ref temp7, binderOptions); - obj.MyList = temp7; + List? temp15 = obj.MyList; + temp15 ??= new List(); + BindCore(section13, ref temp15, binderOptions); + obj.MyList = temp15; } - if (AsConfigWithChildren(configuration.GetSection("MyArray")) is IConfigurationSection section8) + if (AsConfigWithChildren(configuration.GetSection("MyArray")) is IConfigurationSection section16) { - int[]? temp10 = obj.MyArray; - temp10 ??= new int[0]; - BindCore(section8, ref temp10, binderOptions); - obj.MyArray = temp10; + int[]? temp18 = obj.MyArray; + temp18 ??= new int[0]; + BindCore(section16, ref temp18, binderOptions); + obj.MyArray = temp18; } - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section11) + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section19) { - Dictionary? temp13 = obj.MyDictionary; - temp13 ??= new Dictionary(); - BindCore(section11, ref temp13, binderOptions); - obj.MyDictionary = temp13; + Dictionary? temp21 = obj.MyDictionary; + temp21 ??= new Dictionary(); + BindCore(section19, ref temp21, binderOptions); + obj.MyDictionary = temp21; } } @@ -187,7 +247,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions.BindNonPublicProperties) { - throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); + throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } return binderOptions; @@ -204,5 +264,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception); } } + #endregion Core binding extensions. } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_TypeOf.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_TypeOf.generated.txt index 16a98c931a705..f0bb0023fe98a 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_TypeOf.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_TypeOf.generated.txt @@ -2,12 +2,19 @@ #nullable enable #pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code. -/// Generated helper providing an AOT and linking compatible implementation for configuration binding. -[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] -internal static class GeneratedConfigurationBinder +namespace System.Runtime.CompilerServices { - /// Attempts to bind the configuration instance to a new instance of type T. - public static object? Get(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Type type) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetCore(configuration, type, configureOptions: null); + using System; + using System.CodeDom.Compiler; + + [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : Attribute + { + public InterceptsLocationAttribute(string filePath, int line, int column) + { + } + } } namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration @@ -17,11 +24,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration using System.CodeDom.Compiler; using System.Collections.Generic; using System.Globalization; + using System.Runtime.CompilerServices; - /// Provide core binding logic. [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] - file static class CoreBindingHelper + file static class BindingExtensions { + #region IConfiguration extensions. + /// Attempts to bind the configuration instance to a new instance of type T. + [InterceptsLocationAttribute(@"src-0.cs", 11, 40)] + public static object? Get(this IConfiguration configuration, Type type) => GetCore(configuration, type, configureOptions: null); + #endregion IConfiguration extensions. + + #region Core binding extensions. private readonly static Lazy> s_configKeys_ProgramMyClass2 = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyInt" }); public static object? GetCore(this IConfiguration configuration, Type type, Action? configureOptions) @@ -45,7 +59,35 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return obj; } - throw new global::System.NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input."); + throw new NotSupportedException($"Unable to bind to type ''{type}'': generator did not detect the type as input."); + } + + public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) + { + if (configuration is null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + if (obj is null) + { + throw new ArgumentNullException(nameof(obj)); + } + + BinderOptions? binderOptions = GetBinderOptions(configureOptions); + + if (!HasValueOrChildren(configuration)) + { + return; + } + + if (obj is Program.MyClass2 typedObj1) + { + BindCore(configuration, ref typedObj1, binderOptions); + return; + } + + throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); } public static void BindCore(IConfiguration configuration, ref Program.MyClass2 obj, BinderOptions? binderOptions) @@ -57,9 +99,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions); - if (configuration["MyInt"] is string value1) + if (configuration["MyInt"] is string value3) { - obj.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value3, () => configuration.GetSection("MyInt").Path); } } @@ -115,7 +157,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions.BindNonPublicProperties) { - throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); + throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } return binderOptions; @@ -132,5 +174,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception); } } + #endregion Core binding extensions. } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_TypeOf_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_TypeOf_BinderOptions.generated.txt index 8d1ee9ed3cd9a..5425abdfc1813 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_TypeOf_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_TypeOf_BinderOptions.generated.txt @@ -2,12 +2,19 @@ #nullable enable #pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code. -/// Generated helper providing an AOT and linking compatible implementation for configuration binding. -[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] -internal static class GeneratedConfigurationBinder +namespace System.Runtime.CompilerServices { - /// Attempts to bind the configuration instance to a new instance of type T. - public static object? Get(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Type type, global::System.Action? configureOptions) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.GetCore(configuration, type, configureOptions); + using System; + using System.CodeDom.Compiler; + + [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : Attribute + { + public InterceptsLocationAttribute(string filePath, int line, int column) + { + } + } } namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration @@ -17,11 +24,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration using System.CodeDom.Compiler; using System.Collections.Generic; using System.Globalization; + using System.Runtime.CompilerServices; - /// Provide core binding logic. [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] - file static class CoreBindingHelper + file static class BindingExtensions { + #region IConfiguration extensions. + /// Attempts to bind the configuration instance to a new instance of type T. + [InterceptsLocationAttribute(@"src-0.cs", 11, 20)] + public static object? Get(this IConfiguration configuration, Type type, Action? configureOptions) => GetCore(configuration, type, configureOptions); + #endregion IConfiguration extensions. + + #region Core binding extensions. private readonly static Lazy> s_configKeys_ProgramMyClass2 = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyInt" }); public static object? GetCore(this IConfiguration configuration, Type type, Action? configureOptions) @@ -45,7 +59,35 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return obj; } - throw new global::System.NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input."); + throw new NotSupportedException($"Unable to bind to type ''{type}'': generator did not detect the type as input."); + } + + public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) + { + if (configuration is null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + if (obj is null) + { + throw new ArgumentNullException(nameof(obj)); + } + + BinderOptions? binderOptions = GetBinderOptions(configureOptions); + + if (!HasValueOrChildren(configuration)) + { + return; + } + + if (obj is Program.MyClass2 typedObj1) + { + BindCore(configuration, ref typedObj1, binderOptions); + return; + } + + throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); } public static void BindCore(IConfiguration configuration, ref Program.MyClass2 obj, BinderOptions? binderOptions) @@ -57,9 +99,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions); - if (configuration["MyInt"] is string value1) + if (configuration["MyInt"] is string value3) { - obj.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value3, () => configuration.GetSection("MyInt").Path); } } @@ -115,7 +157,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions.BindNonPublicProperties) { - throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); + throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } return binderOptions; @@ -132,5 +174,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception); } } + #endregion Core binding extensions. } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/BindConfiguration.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/BindConfiguration.generated.txt index a74dbfdd04b5b..99266d10a4370 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/BindConfiguration.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/BindConfiguration.generated.txt @@ -2,31 +2,18 @@ #nullable enable #pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code. -/// Generated helper providing an AOT and linking compatible implementation for configuration binding. -[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] -internal static class GeneratedOptionsBuilderBinder +namespace System.Runtime.CompilerServices { - /// Registers the dependency injection container to bind against the obtained from the DI service provider. - public static global::Microsoft.Extensions.Options.OptionsBuilder BindConfiguration(this global::Microsoft.Extensions.Options.OptionsBuilder optionsBuilder, string configSectionPath, global::System.Action? configureOptions = null) where TOptions : class - { - if (optionsBuilder is null) - { - throw new global::System.ArgumentNullException(nameof(optionsBuilder)); - } + using System; + using System.CodeDom.Compiler; - if (configSectionPath is null) + [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : Attribute + { + public InterceptsLocationAttribute(string filePath, int line, int column) { - throw new global::System.ArgumentNullException(nameof(configSectionPath)); } - - optionsBuilder.Configure((obj, configuration) => - { - global::Microsoft.Extensions.Configuration.IConfiguration section = string.Equals(string.Empty, configSectionPath, global::System.StringComparison.OrdinalIgnoreCase) ? configuration : configuration.GetSection(configSectionPath); - global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.BindCoreUntyped(section, obj, typeof(TOptions), configureOptions); - }); - - global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton, global::Microsoft.Extensions.Options.ConfigurationChangeTokenSource>(optionsBuilder.Services); - return optionsBuilder; } } @@ -34,24 +21,57 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Options; using System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.Globalization; + using System.Runtime.CompilerServices; - /// Provide core binding logic. [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] - file static class CoreBindingHelper + file static class BindingExtensions { + #region OptionsBuilder extensions. + /// Registers the dependency injection container to bind against the obtained from the DI service provider. + [InterceptsLocationAttribute(@"src-0.cs", 12, 24)] + public static OptionsBuilder BindConfiguration(this OptionsBuilder optionsBuilder, string configSectionPath, Action? configureOptions = null) where TOptions : class + { + if (optionsBuilder is null) + { + throw new ArgumentNullException(nameof(optionsBuilder)); + } + + if (configSectionPath is null) + { + throw new ArgumentNullException(nameof(configSectionPath)); + } + + optionsBuilder.Configure((obj, configuration) => + { + IConfiguration section = string.Equals(string.Empty, configSectionPath, StringComparison.OrdinalIgnoreCase) ? configuration : configuration.GetSection(configSectionPath); + BindCoreMain(section, obj, configureOptions); + }); + + optionsBuilder.Services.AddSingleton, ConfigurationChangeTokenSource>(); + return optionsBuilder; + } + #endregion OptionsBuilder extensions. + + #region Core binding extensions. private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList" }); - public static void BindCoreUntyped(this IConfiguration configuration, object obj, Type type, Action? configureOptions) + public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) { if (configuration is null) { throw new ArgumentNullException(nameof(configuration)); } + if (obj is null) + { + throw new ArgumentNullException(nameof(obj)); + } + BinderOptions? binderOptions = GetBinderOptions(configureOptions); if (!HasValueOrChildren(configuration)) @@ -59,14 +79,19 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return; } - if (type == typeof(Program.MyClass)) + if (obj is List typedObj0) + { + BindCore(configuration, ref typedObj0, binderOptions); + return; + } + + if (obj is Program.MyClass typedObj2) { - var temp = (Program.MyClass)obj; - BindCore(configuration, ref temp, binderOptions); + BindCore(configuration, ref typedObj2, binderOptions); return; } - throw new global::System.NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input."); + throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); } public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) @@ -96,17 +121,17 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration obj.MyString = configuration["MyString"]!; - if (configuration["MyInt"] is string value2) + if (configuration["MyInt"] is string value5) { - obj.MyInt = ParseInt(value2, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value5, () => configuration.GetSection("MyInt").Path); } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section3) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section6) { - List? temp5 = obj.MyList; - temp5 ??= new List(); - BindCore(section3, ref temp5, binderOptions); - obj.MyList = temp5; + List? temp8 = obj.MyList; + temp8 ??= new List(); + BindCore(section6, ref temp8, binderOptions); + obj.MyList = temp8; } } @@ -162,7 +187,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions.BindNonPublicProperties) { - throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); + throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } return binderOptions; @@ -179,5 +204,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception); } } + #endregion Core binding extensions. } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/Bind_T.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/Bind_T.generated.txt index ac53b58f24da2..69f3645910fcd 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/Bind_T.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/Bind_T.generated.txt @@ -2,49 +2,18 @@ #nullable enable #pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code. -/// Generated helper providing an AOT and linking compatible implementation for configuration binding. -[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] -internal static class GeneratedOptionsBuilderBinder +namespace System.Runtime.CompilerServices { - /// Registers a configuration instance which will bind against. - public static global::Microsoft.Extensions.Options.OptionsBuilder Bind(this global::Microsoft.Extensions.Options.OptionsBuilder optionsBuilder, global::Microsoft.Extensions.Configuration.IConfiguration configuration) where TOptions : class - { - return global::GeneratedOptionsBuilderBinder.Bind(optionsBuilder, configuration, configureOptions: null); - } - - /// Registers a configuration instance which will bind against. - public static global::Microsoft.Extensions.Options.OptionsBuilder Bind(this global::Microsoft.Extensions.Options.OptionsBuilder optionsBuilder, global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Action? configureOptions) where TOptions : class - { - if (optionsBuilder is null) - { - throw new global::System.ArgumentNullException(nameof(optionsBuilder)); - } - - global::GeneratedServiceCollectionBinder.Configure(optionsBuilder.Services, optionsBuilder.Name, configuration, configureOptions); - return optionsBuilder; - } -} + using System; + using System.CodeDom.Compiler; -/// Generated helper providing an AOT and linking compatible implementation for configuration binding. -[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] -internal static class GeneratedServiceCollectionBinder -{ - /// Registers a configuration instance which TOptions will bind against. - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection Configure(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, string? name, global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Action? configureOptions) where TOptions : class + [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : Attribute { - if (services is null) - { - throw new global::System.ArgumentNullException(nameof(services)); - } - - if (configuration is null) + public InterceptsLocationAttribute(string filePath, int line, int column) { - throw new global::System.ArgumentNullException(nameof(configuration)); } - - global::Microsoft.Extensions.DependencyInjection.OptionsServiceCollectionExtensions.AddOptions(services); - global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton>(services, new global::Microsoft.Extensions.Options.ConfigurationChangeTokenSource(name, configuration)); - return global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton>(services, new global::Microsoft.Extensions.Options.ConfigureNamedOptions(name, obj => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.BindCoreUntyped(configuration, obj, typeof(TOptions), configureOptions))); } } @@ -52,24 +21,72 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Options; using System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.Globalization; + using System.Runtime.CompilerServices; - /// Provide core binding logic. [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] - file static class CoreBindingHelper + file static class BindingExtensions { + #region OptionsBuilder extensions. + /// Registers a configuration instance which will bind against. + [InterceptsLocationAttribute(@"src-0.cs", 15, 24)] + public static OptionsBuilder Bind(this OptionsBuilder optionsBuilder, IConfiguration configuration) where TOptions : class + { + return Bind(optionsBuilder, configuration, configureOptions: null); + } + + /// Registers a configuration instance which will bind against. + public static OptionsBuilder Bind(this OptionsBuilder optionsBuilder, IConfiguration configuration, Action? configureOptions) where TOptions : class + { + if (optionsBuilder is null) + { + throw new ArgumentNullException(nameof(optionsBuilder)); + } + + Configure(optionsBuilder.Services, optionsBuilder.Name, configuration, configureOptions); + return optionsBuilder; + } + #endregion OptionsBuilder extensions. + + #region IServiceCollection extensions. + /// Registers a configuration instance which TOptions will bind against. + public static IServiceCollection Configure(this IServiceCollection services, string? name, IConfiguration configuration, Action? configureOptions) where TOptions : class + { + if (services is null) + { + throw new ArgumentNullException(nameof(services)); + } + + if (configuration is null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + OptionsServiceCollectionExtensions.AddOptions(services); + services.AddSingleton>(new ConfigurationChangeTokenSource(name, configuration)); + return services.AddSingleton>(new Microsoft.Extensions.Options.ConfigureNamedOptions(name, obj => BindCoreMain(configuration, obj, configureOptions))); + } + #endregion IServiceCollection extensions. + + #region Core binding extensions. private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList" }); - public static void BindCoreUntyped(this IConfiguration configuration, object obj, Type type, Action? configureOptions) + public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) { if (configuration is null) { throw new ArgumentNullException(nameof(configuration)); } + if (obj is null) + { + throw new ArgumentNullException(nameof(obj)); + } + BinderOptions? binderOptions = GetBinderOptions(configureOptions); if (!HasValueOrChildren(configuration)) @@ -77,14 +94,19 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return; } - if (type == typeof(Program.MyClass)) + if (obj is List typedObj0) + { + BindCore(configuration, ref typedObj0, binderOptions); + return; + } + + if (obj is Program.MyClass typedObj2) { - var temp = (Program.MyClass)obj; - BindCore(configuration, ref temp, binderOptions); + BindCore(configuration, ref typedObj2, binderOptions); return; } - throw new global::System.NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input."); + throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); } public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) @@ -114,17 +136,17 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration obj.MyString = configuration["MyString"]!; - if (configuration["MyInt"] is string value2) + if (configuration["MyInt"] is string value5) { - obj.MyInt = ParseInt(value2, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value5, () => configuration.GetSection("MyInt").Path); } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section3) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section6) { - List? temp5 = obj.MyList; - temp5 ??= new List(); - BindCore(section3, ref temp5, binderOptions); - obj.MyList = temp5; + List? temp8 = obj.MyList; + temp8 ??= new List(); + BindCore(section6, ref temp8, binderOptions); + obj.MyList = temp8; } } @@ -180,7 +202,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions.BindNonPublicProperties) { - throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); + throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } return binderOptions; @@ -197,5 +219,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception); } } + #endregion Core binding extensions. } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/Bind_T_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/Bind_T_BinderOptions.generated.txt index fd3ec70a8a328..36d798a445581 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/Bind_T_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/Bind_T_BinderOptions.generated.txt @@ -2,43 +2,18 @@ #nullable enable #pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code. -/// Generated helper providing an AOT and linking compatible implementation for configuration binding. -[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] -internal static class GeneratedOptionsBuilderBinder +namespace System.Runtime.CompilerServices { - /// Registers a configuration instance which will bind against. - public static global::Microsoft.Extensions.Options.OptionsBuilder Bind(this global::Microsoft.Extensions.Options.OptionsBuilder optionsBuilder, global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Action? configureOptions) where TOptions : class - { - if (optionsBuilder is null) - { - throw new global::System.ArgumentNullException(nameof(optionsBuilder)); - } - - global::GeneratedServiceCollectionBinder.Configure(optionsBuilder.Services, optionsBuilder.Name, configuration, configureOptions); - return optionsBuilder; - } -} + using System; + using System.CodeDom.Compiler; -/// Generated helper providing an AOT and linking compatible implementation for configuration binding. -[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] -internal static class GeneratedServiceCollectionBinder -{ - /// Registers a configuration instance which TOptions will bind against. - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection Configure(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, string? name, global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Action? configureOptions) where TOptions : class + [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : Attribute { - if (services is null) + public InterceptsLocationAttribute(string filePath, int line, int column) { - throw new global::System.ArgumentNullException(nameof(services)); } - - if (configuration is null) - { - throw new global::System.ArgumentNullException(nameof(configuration)); - } - - global::Microsoft.Extensions.DependencyInjection.OptionsServiceCollectionExtensions.AddOptions(services); - global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton>(services, new global::Microsoft.Extensions.Options.ConfigurationChangeTokenSource(name, configuration)); - return global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton>(services, new global::Microsoft.Extensions.Options.ConfigureNamedOptions(name, obj => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.BindCoreUntyped(configuration, obj, typeof(TOptions), configureOptions))); } } @@ -46,24 +21,66 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Options; using System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.Globalization; + using System.Runtime.CompilerServices; - /// Provide core binding logic. [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] - file static class CoreBindingHelper + file static class BindingExtensions { + #region OptionsBuilder extensions. + /// Registers a configuration instance which will bind against. + [InterceptsLocationAttribute(@"src-0.cs", 15, 24)] + public static OptionsBuilder Bind(this OptionsBuilder optionsBuilder, IConfiguration configuration, Action? configureOptions) where TOptions : class + { + if (optionsBuilder is null) + { + throw new ArgumentNullException(nameof(optionsBuilder)); + } + + Configure(optionsBuilder.Services, optionsBuilder.Name, configuration, configureOptions); + return optionsBuilder; + } + #endregion OptionsBuilder extensions. + + #region IServiceCollection extensions. + /// Registers a configuration instance which TOptions will bind against. + public static IServiceCollection Configure(this IServiceCollection services, string? name, IConfiguration configuration, Action? configureOptions) where TOptions : class + { + if (services is null) + { + throw new ArgumentNullException(nameof(services)); + } + + if (configuration is null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + OptionsServiceCollectionExtensions.AddOptions(services); + services.AddSingleton>(new ConfigurationChangeTokenSource(name, configuration)); + return services.AddSingleton>(new Microsoft.Extensions.Options.ConfigureNamedOptions(name, obj => BindCoreMain(configuration, obj, configureOptions))); + } + #endregion IServiceCollection extensions. + + #region Core binding extensions. private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList" }); - public static void BindCoreUntyped(this IConfiguration configuration, object obj, Type type, Action? configureOptions) + public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) { if (configuration is null) { throw new ArgumentNullException(nameof(configuration)); } + if (obj is null) + { + throw new ArgumentNullException(nameof(obj)); + } + BinderOptions? binderOptions = GetBinderOptions(configureOptions); if (!HasValueOrChildren(configuration)) @@ -71,14 +88,19 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return; } - if (type == typeof(Program.MyClass)) + if (obj is List typedObj0) + { + BindCore(configuration, ref typedObj0, binderOptions); + return; + } + + if (obj is Program.MyClass typedObj2) { - var temp = (Program.MyClass)obj; - BindCore(configuration, ref temp, binderOptions); + BindCore(configuration, ref typedObj2, binderOptions); return; } - throw new global::System.NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input."); + throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); } public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) @@ -108,17 +130,17 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration obj.MyString = configuration["MyString"]!; - if (configuration["MyInt"] is string value2) + if (configuration["MyInt"] is string value5) { - obj.MyInt = ParseInt(value2, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value5, () => configuration.GetSection("MyInt").Path); } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section3) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section6) { - List? temp5 = obj.MyList; - temp5 ??= new List(); - BindCore(section3, ref temp5, binderOptions); - obj.MyList = temp5; + List? temp8 = obj.MyList; + temp8 ??= new List(); + BindCore(section6, ref temp8, binderOptions); + obj.MyList = temp8; } } @@ -174,7 +196,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions.BindNonPublicProperties) { - throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); + throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } return binderOptions; @@ -191,5 +213,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception); } } + #endregion Core binding extensions. } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/Primitives.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/Primitives.generated.txt index 5d30288a21e78..214cc899bef26 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/Primitives.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/Primitives.generated.txt @@ -2,12 +2,19 @@ #nullable enable #pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code. -/// Generated helper providing an AOT and linking compatible implementation for configuration binding. -[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] -internal static class GeneratedConfigurationBinder +namespace System.Runtime.CompilerServices { - /// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively. - public static void Bind(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::Program.MyClass obj) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.BindCore(configuration, ref obj, binderOptions: null); + using System; + using System.CodeDom.Compiler; + + [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : Attribute + { + public InterceptsLocationAttribute(string filePath, int line, int column) + { + } + } } namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration @@ -17,13 +24,48 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration using System.CodeDom.Compiler; using System.Collections.Generic; using System.Globalization; + using System.Runtime.CompilerServices; - /// Provide core binding logic. [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] - file static class CoreBindingHelper + file static class BindingExtensions { + #region IConfiguration extensions. + /// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively. + [InterceptsLocationAttribute(@"src-0.cs", 13, 16)] + public static void Bind(this IConfiguration configuration, object? obj) => BindCoreMain(configuration, obj, configureOptions: null); + #endregion IConfiguration extensions. + + #region Core binding extensions. private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "Prop0", "Prop1", "Prop2", "Prop3", "Prop4", "Prop5", "Prop6", "Prop8", "Prop9", "Prop10", "Prop13", "Prop14", "Prop15", "Prop16", "Prop17", "Prop19", "Prop20", "Prop21", "Prop23", "Prop24", "Prop25", "Prop26", "Prop27", "Prop7", "Prop11", "Prop12", "Prop18", "Prop22" }); + public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) + { + if (configuration is null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + if (obj is null) + { + throw new ArgumentNullException(nameof(obj)); + } + + BinderOptions? binderOptions = GetBinderOptions(configureOptions); + + if (!HasValueOrChildren(configuration)) + { + return; + } + + if (obj is Program.MyClass typedObj0) + { + BindCore(configuration, ref typedObj0, binderOptions); + return; + } + + throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); + } + public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions) { if (obj is null) @@ -33,138 +75,138 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); - if (configuration["Prop0"] is string value0) + if (configuration["Prop0"] is string value2) { - obj.Prop0 = ParseBool(value0, () => configuration.GetSection("Prop0").Path); + obj.Prop0 = ParseBool(value2, () => configuration.GetSection("Prop0").Path); } - if (configuration["Prop1"] is string value1) + if (configuration["Prop1"] is string value3) { - obj.Prop1 = ParseByte(value1, () => configuration.GetSection("Prop1").Path); + obj.Prop1 = ParseByte(value3, () => configuration.GetSection("Prop1").Path); } - if (configuration["Prop2"] is string value2) + if (configuration["Prop2"] is string value4) { - obj.Prop2 = ParseSbyte(value2, () => configuration.GetSection("Prop2").Path); + obj.Prop2 = ParseSbyte(value4, () => configuration.GetSection("Prop2").Path); } - if (configuration["Prop3"] is string value3) + if (configuration["Prop3"] is string value5) { - obj.Prop3 = ParseChar(value3, () => configuration.GetSection("Prop3").Path); + obj.Prop3 = ParseChar(value5, () => configuration.GetSection("Prop3").Path); } - if (configuration["Prop4"] is string value4) + if (configuration["Prop4"] is string value6) { - obj.Prop4 = ParseDouble(value4, () => configuration.GetSection("Prop4").Path); + obj.Prop4 = ParseDouble(value6, () => configuration.GetSection("Prop4").Path); } obj.Prop5 = configuration["Prop5"]!; - if (configuration["Prop6"] is string value6) + if (configuration["Prop6"] is string value8) { - obj.Prop6 = ParseInt(value6, () => configuration.GetSection("Prop6").Path); + obj.Prop6 = ParseInt(value8, () => configuration.GetSection("Prop6").Path); } - if (configuration["Prop8"] is string value7) + if (configuration["Prop8"] is string value9) { - obj.Prop8 = ParseShort(value7, () => configuration.GetSection("Prop8").Path); + obj.Prop8 = ParseShort(value9, () => configuration.GetSection("Prop8").Path); } - if (configuration["Prop9"] is string value8) + if (configuration["Prop9"] is string value10) { - obj.Prop9 = ParseLong(value8, () => configuration.GetSection("Prop9").Path); + obj.Prop9 = ParseLong(value10, () => configuration.GetSection("Prop9").Path); } - if (configuration["Prop10"] is string value9) + if (configuration["Prop10"] is string value11) { - obj.Prop10 = ParseFloat(value9, () => configuration.GetSection("Prop10").Path); + obj.Prop10 = ParseFloat(value11, () => configuration.GetSection("Prop10").Path); } - if (configuration["Prop13"] is string value10) + if (configuration["Prop13"] is string value12) { - obj.Prop13 = ParseUshort(value10, () => configuration.GetSection("Prop13").Path); + obj.Prop13 = ParseUshort(value12, () => configuration.GetSection("Prop13").Path); } - if (configuration["Prop14"] is string value11) + if (configuration["Prop14"] is string value13) { - obj.Prop14 = ParseUint(value11, () => configuration.GetSection("Prop14").Path); + obj.Prop14 = ParseUint(value13, () => configuration.GetSection("Prop14").Path); } - if (configuration["Prop15"] is string value12) + if (configuration["Prop15"] is string value14) { - obj.Prop15 = ParseUlong(value12, () => configuration.GetSection("Prop15").Path); + obj.Prop15 = ParseUlong(value14, () => configuration.GetSection("Prop15").Path); } obj.Prop16 = configuration["Prop16"]!; - if (configuration["Prop17"] is string value14) + if (configuration["Prop17"] is string value16) { - obj.Prop17 = ParseCultureInfo(value14, () => configuration.GetSection("Prop17").Path); + obj.Prop17 = ParseCultureInfo(value16, () => configuration.GetSection("Prop17").Path); } - if (configuration["Prop19"] is string value15) + if (configuration["Prop19"] is string value17) { - obj.Prop19 = ParseDateTime(value15, () => configuration.GetSection("Prop19").Path); + obj.Prop19 = ParseDateTime(value17, () => configuration.GetSection("Prop19").Path); } - if (configuration["Prop20"] is string value16) + if (configuration["Prop20"] is string value18) { - obj.Prop20 = ParseDateTimeOffset(value16, () => configuration.GetSection("Prop20").Path); + obj.Prop20 = ParseDateTimeOffset(value18, () => configuration.GetSection("Prop20").Path); } - if (configuration["Prop21"] is string value17) + if (configuration["Prop21"] is string value19) { - obj.Prop21 = ParseDecimal(value17, () => configuration.GetSection("Prop21").Path); + obj.Prop21 = ParseDecimal(value19, () => configuration.GetSection("Prop21").Path); } - if (configuration["Prop23"] is string value18) + if (configuration["Prop23"] is string value20) { - obj.Prop23 = ParseInt(value18, () => configuration.GetSection("Prop23").Path); + obj.Prop23 = ParseInt(value20, () => configuration.GetSection("Prop23").Path); } - if (configuration["Prop24"] is string value19) + if (configuration["Prop24"] is string value21) { - obj.Prop24 = ParseDateTime(value19, () => configuration.GetSection("Prop24").Path); + obj.Prop24 = ParseDateTime(value21, () => configuration.GetSection("Prop24").Path); } - if (configuration["Prop25"] is string value20) + if (configuration["Prop25"] is string value22) { - obj.Prop25 = ParseUri(value20, () => configuration.GetSection("Prop25").Path); + obj.Prop25 = ParseUri(value22, () => configuration.GetSection("Prop25").Path); } - if (configuration["Prop26"] is string value21) + if (configuration["Prop26"] is string value23) { - obj.Prop26 = ParseVersion(value21, () => configuration.GetSection("Prop26").Path); + obj.Prop26 = ParseVersion(value23, () => configuration.GetSection("Prop26").Path); } - if (configuration["Prop27"] is string value22) + if (configuration["Prop27"] is string value24) { - obj.Prop27 = ParseEnum(value22, () => configuration.GetSection("Prop27").Path); + obj.Prop27 = ParseEnum(value24, () => configuration.GetSection("Prop27").Path); } - if (configuration["Prop7"] is string value23) + if (configuration["Prop7"] is string value25) { - obj.Prop7 = ParseInt128(value23, () => configuration.GetSection("Prop7").Path); + obj.Prop7 = ParseInt128(value25, () => configuration.GetSection("Prop7").Path); } - if (configuration["Prop11"] is string value24) + if (configuration["Prop11"] is string value26) { - obj.Prop11 = ParseHalf(value24, () => configuration.GetSection("Prop11").Path); + obj.Prop11 = ParseHalf(value26, () => configuration.GetSection("Prop11").Path); } - if (configuration["Prop12"] is string value25) + if (configuration["Prop12"] is string value27) { - obj.Prop12 = ParseUInt128(value25, () => configuration.GetSection("Prop12").Path); + obj.Prop12 = ParseUInt128(value27, () => configuration.GetSection("Prop12").Path); } - if (configuration["Prop18"] is string value26) + if (configuration["Prop18"] is string value28) { - obj.Prop18 = ParseDateOnly(value26, () => configuration.GetSection("Prop18").Path); + obj.Prop18 = ParseDateOnly(value28, () => configuration.GetSection("Prop18").Path); } - if (configuration["Prop22"] is string value27) + if (configuration["Prop22"] is string value29) { - obj.Prop22 = ParseByteArray(value27, () => configuration.GetSection("Prop22").Path); + obj.Prop22 = ParseByteArray(value29, () => configuration.GetSection("Prop22").Path); } } @@ -190,6 +232,42 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } } + public static bool HasValueOrChildren(IConfiguration configuration) + { + if ((configuration as IConfigurationSection)?.Value is not null) + { + return true; + } + return AsConfigWithChildren(configuration) is not null; + } + + public static IConfiguration? AsConfigWithChildren(IConfiguration configuration) + { + foreach (IConfigurationSection _ in configuration.GetChildren()) + { + return configuration; + } + return null; + } + + public static BinderOptions? GetBinderOptions(Action? configureOptions) + { + if (configureOptions is null) + { + return null; + } + + BinderOptions binderOptions = new(); + configureOptions(binderOptions); + + if (binderOptions.BindNonPublicProperties) + { + throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); + } + + return binderOptions; + } + public static bool ParseBool(string value, Func getPath) { try @@ -517,5 +595,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(byte[])}'.", exception); } } + #endregion Core binding extensions. } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T.generated.txt index 461f6050e2fdf..6e678300999be 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T.generated.txt @@ -2,57 +2,77 @@ #nullable enable #pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code. -/// Generated helper providing an AOT and linking compatible implementation for configuration binding. -[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] -internal static class GeneratedServiceCollectionBinder +namespace System.Runtime.CompilerServices { - /// Registers a configuration instance which TOptions will bind against. - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection Configure(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::Microsoft.Extensions.Configuration.IConfiguration configuration) where TOptions : class - { - return global::GeneratedServiceCollectionBinder.Configure(services, string.Empty, configuration, configureOptions: null); - } + using System; + using System.CodeDom.Compiler; - /// Registers a configuration instance which TOptions will bind against. - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection Configure(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, string? name, global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Action? configureOptions) where TOptions : class + [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : Attribute { - if (services is null) + public InterceptsLocationAttribute(string filePath, int line, int column) { - throw new global::System.ArgumentNullException(nameof(services)); } - - if (configuration is null) - { - throw new global::System.ArgumentNullException(nameof(configuration)); - } - - global::Microsoft.Extensions.DependencyInjection.OptionsServiceCollectionExtensions.AddOptions(services); - global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton>(services, new global::Microsoft.Extensions.Options.ConfigurationChangeTokenSource(name, configuration)); - return global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton>(services, new global::Microsoft.Extensions.Options.ConfigureNamedOptions(name, obj => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.BindCoreUntyped(configuration, obj, typeof(TOptions), configureOptions))); } } namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { using Microsoft.Extensions.Configuration; + using Microsoft.Extensions.DependencyInjection; using System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.Globalization; + using System.Runtime.CompilerServices; - /// Provide core binding logic. [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] - file static class CoreBindingHelper + file static class BindingExtensions { + #region IServiceCollection extensions. + /// Registers a configuration instance which TOptions will bind against. + [InterceptsLocationAttribute(@"src-0.cs", 14, 18)] + public static IServiceCollection Configure(this IServiceCollection services, IConfiguration configuration) where TOptions : class + { + return Configure(services, string.Empty, configuration, configureOptions: null); + } + + /// Registers a configuration instance which TOptions will bind against. + public static IServiceCollection Configure(this IServiceCollection services, string? name, IConfiguration configuration, Action? configureOptions) where TOptions : class + { + if (services is null) + { + throw new ArgumentNullException(nameof(services)); + } + + if (configuration is null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + OptionsServiceCollectionExtensions.AddOptions(services); + services.AddSingleton>(new ConfigurationChangeTokenSource(name, configuration)); + return services.AddSingleton>(new Microsoft.Extensions.Options.ConfigureNamedOptions(name, obj => BindCoreMain(configuration, obj, configureOptions))); + } + #endregion IServiceCollection extensions. + + #region Core binding extensions. private readonly static Lazy> s_configKeys_ProgramMyClass2 = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyInt" }); private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyList2", "MyDictionary" }); - public static void BindCoreUntyped(this IConfiguration configuration, object obj, Type type, Action? configureOptions) + public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) { if (configuration is null) { throw new ArgumentNullException(nameof(configuration)); } + if (obj is null) + { + throw new ArgumentNullException(nameof(obj)); + } + BinderOptions? binderOptions = GetBinderOptions(configureOptions); if (!HasValueOrChildren(configuration)) @@ -60,14 +80,37 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return; } - if (type == typeof(Program.MyClass)) + if (obj is List typedObj0) + { + BindCore(configuration, ref typedObj0, binderOptions); + return; + } + + if (obj is Program.MyClass2 typedObj2) + { + BindCore(configuration, ref typedObj2, binderOptions); + return; + } + + if (obj is List typedObj4) + { + BindCore(configuration, ref typedObj4, binderOptions); + return; + } + + if (obj is Dictionary typedObj6) + { + BindCore(configuration, ref typedObj6, binderOptions); + return; + } + + if (obj is Program.MyClass typedObj8) { - var temp = (Program.MyClass)obj; - BindCore(configuration, ref temp, binderOptions); + BindCore(configuration, ref typedObj8, binderOptions); return; } - throw new global::System.NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input."); + throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); } public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) @@ -95,9 +138,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions); - if (configuration["MyInt"] is string value1) + if (configuration["MyInt"] is string value10) { - obj.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value10, () => configuration.GetSection("MyInt").Path); } } @@ -143,33 +186,33 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration obj.MyString = configuration["MyString"]!; - if (configuration["MyInt"] is string value4) + if (configuration["MyInt"] is string value13) { - obj.MyInt = ParseInt(value4, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value13, () => configuration.GetSection("MyInt").Path); } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section14) { - List? temp7 = obj.MyList; - temp7 ??= new List(); - BindCore(section5, ref temp7, binderOptions); - obj.MyList = temp7; + List? temp16 = obj.MyList; + temp16 ??= new List(); + BindCore(section14, ref temp16, binderOptions); + obj.MyList = temp16; } - if (AsConfigWithChildren(configuration.GetSection("MyList2")) is IConfigurationSection section8) + if (AsConfigWithChildren(configuration.GetSection("MyList2")) is IConfigurationSection section17) { - List? temp10 = obj.MyList2; - temp10 ??= new List(); - BindCore(section8, ref temp10, binderOptions); - obj.MyList2 = temp10; + List? temp19 = obj.MyList2; + temp19 ??= new List(); + BindCore(section17, ref temp19, binderOptions); + obj.MyList2 = temp19; } - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section11) + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section20) { - Dictionary? temp13 = obj.MyDictionary; - temp13 ??= new Dictionary(); - BindCore(section11, ref temp13, binderOptions); - obj.MyDictionary = temp13; + Dictionary? temp22 = obj.MyDictionary; + temp22 ??= new Dictionary(); + BindCore(section20, ref temp22, binderOptions); + obj.MyDictionary = temp22; } } @@ -225,7 +268,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions.BindNonPublicProperties) { - throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); + throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } return binderOptions; @@ -242,5 +285,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception); } } + #endregion Core binding extensions. } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_BinderOptions.generated.txt index a57f652720bc8..f759068b61ae2 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_BinderOptions.generated.txt @@ -2,57 +2,77 @@ #nullable enable #pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code. -/// Generated helper providing an AOT and linking compatible implementation for configuration binding. -[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] -internal static class GeneratedServiceCollectionBinder +namespace System.Runtime.CompilerServices { - /// Registers a configuration instance which TOptions will bind against. - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection Configure(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Action? configureOptions) where TOptions : class - { - return global::GeneratedServiceCollectionBinder.Configure(services, string.Empty, configuration, configureOptions); - } + using System; + using System.CodeDom.Compiler; - /// Registers a configuration instance which TOptions will bind against. - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection Configure(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, string? name, global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Action? configureOptions) where TOptions : class + [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : Attribute { - if (services is null) + public InterceptsLocationAttribute(string filePath, int line, int column) { - throw new global::System.ArgumentNullException(nameof(services)); } - - if (configuration is null) - { - throw new global::System.ArgumentNullException(nameof(configuration)); - } - - global::Microsoft.Extensions.DependencyInjection.OptionsServiceCollectionExtensions.AddOptions(services); - global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton>(services, new global::Microsoft.Extensions.Options.ConfigurationChangeTokenSource(name, configuration)); - return global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton>(services, new global::Microsoft.Extensions.Options.ConfigureNamedOptions(name, obj => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.BindCoreUntyped(configuration, obj, typeof(TOptions), configureOptions))); } } namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { using Microsoft.Extensions.Configuration; + using Microsoft.Extensions.DependencyInjection; using System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.Globalization; + using System.Runtime.CompilerServices; - /// Provide core binding logic. [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] - file static class CoreBindingHelper + file static class BindingExtensions { + #region IServiceCollection extensions. + /// Registers a configuration instance which TOptions will bind against. + [InterceptsLocationAttribute(@"src-0.cs", 14, 18)] + public static IServiceCollection Configure(this IServiceCollection services, IConfiguration configuration, Action? configureOptions) where TOptions : class + { + return Configure(services, string.Empty, configuration, configureOptions); + } + + /// Registers a configuration instance which TOptions will bind against. + public static IServiceCollection Configure(this IServiceCollection services, string? name, IConfiguration configuration, Action? configureOptions) where TOptions : class + { + if (services is null) + { + throw new ArgumentNullException(nameof(services)); + } + + if (configuration is null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + OptionsServiceCollectionExtensions.AddOptions(services); + services.AddSingleton>(new ConfigurationChangeTokenSource(name, configuration)); + return services.AddSingleton>(new Microsoft.Extensions.Options.ConfigureNamedOptions(name, obj => BindCoreMain(configuration, obj, configureOptions))); + } + #endregion IServiceCollection extensions. + + #region Core binding extensions. private readonly static Lazy> s_configKeys_ProgramMyClass2 = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyInt" }); private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyList2", "MyDictionary" }); - public static void BindCoreUntyped(this IConfiguration configuration, object obj, Type type, Action? configureOptions) + public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) { if (configuration is null) { throw new ArgumentNullException(nameof(configuration)); } + if (obj is null) + { + throw new ArgumentNullException(nameof(obj)); + } + BinderOptions? binderOptions = GetBinderOptions(configureOptions); if (!HasValueOrChildren(configuration)) @@ -60,14 +80,37 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return; } - if (type == typeof(Program.MyClass)) + if (obj is List typedObj0) + { + BindCore(configuration, ref typedObj0, binderOptions); + return; + } + + if (obj is Program.MyClass2 typedObj2) + { + BindCore(configuration, ref typedObj2, binderOptions); + return; + } + + if (obj is List typedObj4) + { + BindCore(configuration, ref typedObj4, binderOptions); + return; + } + + if (obj is Dictionary typedObj6) + { + BindCore(configuration, ref typedObj6, binderOptions); + return; + } + + if (obj is Program.MyClass typedObj8) { - var temp = (Program.MyClass)obj; - BindCore(configuration, ref temp, binderOptions); + BindCore(configuration, ref typedObj8, binderOptions); return; } - throw new global::System.NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input."); + throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); } public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) @@ -95,9 +138,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions); - if (configuration["MyInt"] is string value1) + if (configuration["MyInt"] is string value10) { - obj.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value10, () => configuration.GetSection("MyInt").Path); } } @@ -143,33 +186,33 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration obj.MyString = configuration["MyString"]!; - if (configuration["MyInt"] is string value4) + if (configuration["MyInt"] is string value13) { - obj.MyInt = ParseInt(value4, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value13, () => configuration.GetSection("MyInt").Path); } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section14) { - List? temp7 = obj.MyList; - temp7 ??= new List(); - BindCore(section5, ref temp7, binderOptions); - obj.MyList = temp7; + List? temp16 = obj.MyList; + temp16 ??= new List(); + BindCore(section14, ref temp16, binderOptions); + obj.MyList = temp16; } - if (AsConfigWithChildren(configuration.GetSection("MyList2")) is IConfigurationSection section8) + if (AsConfigWithChildren(configuration.GetSection("MyList2")) is IConfigurationSection section17) { - List? temp10 = obj.MyList2; - temp10 ??= new List(); - BindCore(section8, ref temp10, binderOptions); - obj.MyList2 = temp10; + List? temp19 = obj.MyList2; + temp19 ??= new List(); + BindCore(section17, ref temp19, binderOptions); + obj.MyList2 = temp19; } - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section11) + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section20) { - Dictionary? temp13 = obj.MyDictionary; - temp13 ??= new Dictionary(); - BindCore(section11, ref temp13, binderOptions); - obj.MyDictionary = temp13; + Dictionary? temp22 = obj.MyDictionary; + temp22 ??= new Dictionary(); + BindCore(section20, ref temp22, binderOptions); + obj.MyDictionary = temp22; } } @@ -225,7 +268,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions.BindNonPublicProperties) { - throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); + throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } return binderOptions; @@ -242,5 +285,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception); } } + #endregion Core binding extensions. } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_name.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_name.generated.txt index 66975c3164d74..0b8103c75621c 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_name.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_name.generated.txt @@ -2,57 +2,77 @@ #nullable enable #pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code. -/// Generated helper providing an AOT and linking compatible implementation for configuration binding. -[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] -internal static class GeneratedServiceCollectionBinder +namespace System.Runtime.CompilerServices { - /// Registers a configuration instance which TOptions will bind against. - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection Configure(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, string? name, global::Microsoft.Extensions.Configuration.IConfiguration configuration) where TOptions : class - { - return global::GeneratedServiceCollectionBinder.Configure(services, name, configuration, configureOptions: null); - } + using System; + using System.CodeDom.Compiler; - /// Registers a configuration instance which TOptions will bind against. - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection Configure(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, string? name, global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Action? configureOptions) where TOptions : class + [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : Attribute { - if (services is null) + public InterceptsLocationAttribute(string filePath, int line, int column) { - throw new global::System.ArgumentNullException(nameof(services)); } - - if (configuration is null) - { - throw new global::System.ArgumentNullException(nameof(configuration)); - } - - global::Microsoft.Extensions.DependencyInjection.OptionsServiceCollectionExtensions.AddOptions(services); - global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton>(services, new global::Microsoft.Extensions.Options.ConfigurationChangeTokenSource(name, configuration)); - return global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton>(services, new global::Microsoft.Extensions.Options.ConfigureNamedOptions(name, obj => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.BindCoreUntyped(configuration, obj, typeof(TOptions), configureOptions))); } } namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { using Microsoft.Extensions.Configuration; + using Microsoft.Extensions.DependencyInjection; using System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.Globalization; + using System.Runtime.CompilerServices; - /// Provide core binding logic. [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] - file static class CoreBindingHelper + file static class BindingExtensions { + #region IServiceCollection extensions. + /// Registers a configuration instance which TOptions will bind against. + [InterceptsLocationAttribute(@"src-0.cs", 14, 18)] + public static IServiceCollection Configure(this IServiceCollection services, string? name, IConfiguration configuration) where TOptions : class + { + return Configure(services, name, configuration, configureOptions: null); + } + + /// Registers a configuration instance which TOptions will bind against. + public static IServiceCollection Configure(this IServiceCollection services, string? name, IConfiguration configuration, Action? configureOptions) where TOptions : class + { + if (services is null) + { + throw new ArgumentNullException(nameof(services)); + } + + if (configuration is null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + OptionsServiceCollectionExtensions.AddOptions(services); + services.AddSingleton>(new ConfigurationChangeTokenSource(name, configuration)); + return services.AddSingleton>(new Microsoft.Extensions.Options.ConfigureNamedOptions(name, obj => BindCoreMain(configuration, obj, configureOptions))); + } + #endregion IServiceCollection extensions. + + #region Core binding extensions. private readonly static Lazy> s_configKeys_ProgramMyClass2 = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyInt" }); private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyList2", "MyDictionary" }); - public static void BindCoreUntyped(this IConfiguration configuration, object obj, Type type, Action? configureOptions) + public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) { if (configuration is null) { throw new ArgumentNullException(nameof(configuration)); } + if (obj is null) + { + throw new ArgumentNullException(nameof(obj)); + } + BinderOptions? binderOptions = GetBinderOptions(configureOptions); if (!HasValueOrChildren(configuration)) @@ -60,14 +80,37 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return; } - if (type == typeof(Program.MyClass)) + if (obj is List typedObj0) + { + BindCore(configuration, ref typedObj0, binderOptions); + return; + } + + if (obj is Program.MyClass2 typedObj2) + { + BindCore(configuration, ref typedObj2, binderOptions); + return; + } + + if (obj is List typedObj4) + { + BindCore(configuration, ref typedObj4, binderOptions); + return; + } + + if (obj is Dictionary typedObj6) + { + BindCore(configuration, ref typedObj6, binderOptions); + return; + } + + if (obj is Program.MyClass typedObj8) { - var temp = (Program.MyClass)obj; - BindCore(configuration, ref temp, binderOptions); + BindCore(configuration, ref typedObj8, binderOptions); return; } - throw new global::System.NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input."); + throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); } public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) @@ -95,9 +138,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions); - if (configuration["MyInt"] is string value1) + if (configuration["MyInt"] is string value10) { - obj.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value10, () => configuration.GetSection("MyInt").Path); } } @@ -143,33 +186,33 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration obj.MyString = configuration["MyString"]!; - if (configuration["MyInt"] is string value4) + if (configuration["MyInt"] is string value13) { - obj.MyInt = ParseInt(value4, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value13, () => configuration.GetSection("MyInt").Path); } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section14) { - List? temp7 = obj.MyList; - temp7 ??= new List(); - BindCore(section5, ref temp7, binderOptions); - obj.MyList = temp7; + List? temp16 = obj.MyList; + temp16 ??= new List(); + BindCore(section14, ref temp16, binderOptions); + obj.MyList = temp16; } - if (AsConfigWithChildren(configuration.GetSection("MyList2")) is IConfigurationSection section8) + if (AsConfigWithChildren(configuration.GetSection("MyList2")) is IConfigurationSection section17) { - List? temp10 = obj.MyList2; - temp10 ??= new List(); - BindCore(section8, ref temp10, binderOptions); - obj.MyList2 = temp10; + List? temp19 = obj.MyList2; + temp19 ??= new List(); + BindCore(section17, ref temp19, binderOptions); + obj.MyList2 = temp19; } - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section11) + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section20) { - Dictionary? temp13 = obj.MyDictionary; - temp13 ??= new Dictionary(); - BindCore(section11, ref temp13, binderOptions); - obj.MyDictionary = temp13; + Dictionary? temp22 = obj.MyDictionary; + temp22 ??= new Dictionary(); + BindCore(section20, ref temp22, binderOptions); + obj.MyDictionary = temp22; } } @@ -225,7 +268,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions.BindNonPublicProperties) { - throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); + throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } return binderOptions; @@ -242,5 +285,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception); } } + #endregion Core binding extensions. } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_name_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_name_BinderOptions.generated.txt index 0263ef1217940..53d3ee8d7d431 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_name_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_name_BinderOptions.generated.txt @@ -2,51 +2,71 @@ #nullable enable #pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code. -/// Generated helper providing an AOT and linking compatible implementation for configuration binding. -[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] -internal static class GeneratedServiceCollectionBinder +namespace System.Runtime.CompilerServices { - /// Registers a configuration instance which TOptions will bind against. - public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection Configure(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, string? name, global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::System.Action? configureOptions) where TOptions : class - { - if (services is null) - { - throw new global::System.ArgumentNullException(nameof(services)); - } + using System; + using System.CodeDom.Compiler; - if (configuration is null) + [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : Attribute + { + public InterceptsLocationAttribute(string filePath, int line, int column) { - throw new global::System.ArgumentNullException(nameof(configuration)); } - - global::Microsoft.Extensions.DependencyInjection.OptionsServiceCollectionExtensions.AddOptions(services); - global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton>(services, new global::Microsoft.Extensions.Options.ConfigurationChangeTokenSource(name, configuration)); - return global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton>(services, new global::Microsoft.Extensions.Options.ConfigureNamedOptions(name, obj => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.BindCoreUntyped(configuration, obj, typeof(TOptions), configureOptions))); } } namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { using Microsoft.Extensions.Configuration; + using Microsoft.Extensions.DependencyInjection; using System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.Globalization; + using System.Runtime.CompilerServices; - /// Provide core binding logic. [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] - file static class CoreBindingHelper + file static class BindingExtensions { + #region IServiceCollection extensions. + /// Registers a configuration instance which TOptions will bind against. + [InterceptsLocationAttribute(@"src-0.cs", 14, 18)] + public static IServiceCollection Configure(this IServiceCollection services, string? name, IConfiguration configuration, Action? configureOptions) where TOptions : class + { + if (services is null) + { + throw new ArgumentNullException(nameof(services)); + } + + if (configuration is null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + OptionsServiceCollectionExtensions.AddOptions(services); + services.AddSingleton>(new ConfigurationChangeTokenSource(name, configuration)); + return services.AddSingleton>(new Microsoft.Extensions.Options.ConfigureNamedOptions(name, obj => BindCoreMain(configuration, obj, configureOptions))); + } + #endregion IServiceCollection extensions. + + #region Core binding extensions. private readonly static Lazy> s_configKeys_ProgramMyClass2 = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyInt" }); private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyList2", "MyDictionary" }); - public static void BindCoreUntyped(this IConfiguration configuration, object obj, Type type, Action? configureOptions) + public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) { if (configuration is null) { throw new ArgumentNullException(nameof(configuration)); } + if (obj is null) + { + throw new ArgumentNullException(nameof(obj)); + } + BinderOptions? binderOptions = GetBinderOptions(configureOptions); if (!HasValueOrChildren(configuration)) @@ -54,14 +74,37 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return; } - if (type == typeof(Program.MyClass)) + if (obj is List typedObj0) + { + BindCore(configuration, ref typedObj0, binderOptions); + return; + } + + if (obj is Program.MyClass2 typedObj2) + { + BindCore(configuration, ref typedObj2, binderOptions); + return; + } + + if (obj is List typedObj4) + { + BindCore(configuration, ref typedObj4, binderOptions); + return; + } + + if (obj is Dictionary typedObj6) + { + BindCore(configuration, ref typedObj6, binderOptions); + return; + } + + if (obj is Program.MyClass typedObj8) { - var temp = (Program.MyClass)obj; - BindCore(configuration, ref temp, binderOptions); + BindCore(configuration, ref typedObj8, binderOptions); return; } - throw new global::System.NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input."); + throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); } public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) @@ -89,9 +132,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions); - if (configuration["MyInt"] is string value1) + if (configuration["MyInt"] is string value10) { - obj.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value10, () => configuration.GetSection("MyInt").Path); } } @@ -137,33 +180,33 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration obj.MyString = configuration["MyString"]!; - if (configuration["MyInt"] is string value4) + if (configuration["MyInt"] is string value13) { - obj.MyInt = ParseInt(value4, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value13, () => configuration.GetSection("MyInt").Path); } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section14) { - List? temp7 = obj.MyList; - temp7 ??= new List(); - BindCore(section5, ref temp7, binderOptions); - obj.MyList = temp7; + List? temp16 = obj.MyList; + temp16 ??= new List(); + BindCore(section14, ref temp16, binderOptions); + obj.MyList = temp16; } - if (AsConfigWithChildren(configuration.GetSection("MyList2")) is IConfigurationSection section8) + if (AsConfigWithChildren(configuration.GetSection("MyList2")) is IConfigurationSection section17) { - List? temp10 = obj.MyList2; - temp10 ??= new List(); - BindCore(section8, ref temp10, binderOptions); - obj.MyList2 = temp10; + List? temp19 = obj.MyList2; + temp19 ??= new List(); + BindCore(section17, ref temp19, binderOptions); + obj.MyList2 = temp19; } - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section11) + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section20) { - Dictionary? temp13 = obj.MyDictionary; - temp13 ??= new Dictionary(); - BindCore(section11, ref temp13, binderOptions); - obj.MyDictionary = temp13; + Dictionary? temp22 = obj.MyDictionary; + temp22 ??= new Dictionary(); + BindCore(section20, ref temp22, binderOptions); + obj.MyDictionary = temp22; } } @@ -219,7 +262,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions.BindNonPublicProperties) { - throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); + throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } return binderOptions; @@ -236,5 +279,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception); } } + #endregion Core binding extensions. } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/ConfigurationBindingGeneratorTests.Baselines.Options.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/ConfigurationBindingGeneratorTests.Baselines.Options.cs index 8807dfb096220..c9eb9c70927f9 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/ConfigurationBindingGeneratorTests.Baselines.Options.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/ConfigurationBindingGeneratorTests.Baselines.Options.cs @@ -8,6 +8,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration.Tests { public partial class ConfigurationBindingGeneratorTests { + #region IServiceCollection extensions. private string GetConfigureSource(string paramList) => $$""" using System.Collections.Generic; using Microsoft.Extensions.Configuration; @@ -40,7 +41,9 @@ public class MyClass2 } } """; + #endregion IServiceCollection extensions. + #region OptionsBuilder extensions. [Fact] public async Task Configure_T() => await VerifyAgainstBaselineUsingFile("Configure_T.generated.txt", GetConfigureSource("section"), extType: ExtensionClassType.ServiceCollection); @@ -126,5 +129,6 @@ public class MyClass await VerifyAgainstBaselineUsingFile("BindConfiguration.generated.txt", GetSource(), extType: ExtensionClassType.OptionsBuilder); await VerifyAgainstBaselineUsingFile("BindConfiguration.generated.txt", GetSource(@", _ => { }"), extType: ExtensionClassType.OptionsBuilder); } + #endregion OptionsBuilder extensions. } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/ConfigurationBindingGeneratorTests.Baselines.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/ConfigurationBindingGeneratorTests.Baselines.cs index aba2a9f6184f2..b5c3fb49c5e7e 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/ConfigurationBindingGeneratorTests.Baselines.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/ConfigurationBindingGeneratorTests.Baselines.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Globalization; using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis; @@ -44,7 +43,6 @@ public class MyClass2 { } [Theory] [InlineData(LanguageVersion.Preview)] - [InlineData(LanguageVersion.CSharp11)] public async Task Bind(LanguageVersion langVersion) => await VerifyAgainstBaselineUsingFile("Bind.generated.txt", BindCallSampleCode, langVersion, extType: ExtensionClassType.ConfigurationBinder); @@ -651,7 +649,7 @@ public interface ICustomSet : ISet await VerifyAgainstBaselineUsingFile("Collections.generated.txt", source, assessDiagnostics: (d) => { - Console.WriteLine((d.Where(diag => diag.Id == Diagnostics.TypeNotSupported.Id).Count() , d.Where(diag => diag.Id == Diagnostics.PropertyNotSupported.Id).Count())); + Console.WriteLine((d.Where(diag => diag.Id == Diagnostics.TypeNotSupported.Id).Count(), d.Where(diag => diag.Id == Diagnostics.PropertyNotSupported.Id).Count())); Assert.Equal(3, d.Where(diag => diag.Id == Diagnostics.TypeNotSupported.Id).Count()); Assert.Equal(6, d.Where(diag => diag.Id == Diagnostics.PropertyNotSupported.Id).Count()); }); diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/ConfigurationBindingGeneratorTests.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/ConfigurationBindingGeneratorTests.cs index 5bc5145739daa..ad78d82f48d0f 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/ConfigurationBindingGeneratorTests.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/ConfigurationBindingGeneratorTests.cs @@ -52,10 +52,12 @@ private enum ExtensionClassType ServiceCollection, } - [Fact] - public async Task LangVersionMustBeCharp11OrHigher() + [Theory] + [InlineData(LanguageVersion.CSharp11)] + [InlineData(LanguageVersion.CSharp10)] + public async Task LangVersionMustBeCharp12OrHigher(LanguageVersion langVersion) { - var (d, r) = await RunGenerator(BindCallSampleCode, LanguageVersion.CSharp10); + var (d, r) = await RunGenerator(BindCallSampleCode, langVersion); Assert.Empty(r); Diagnostic diagnostic = Assert.Single(d); @@ -250,9 +252,9 @@ public class MyClass2 { } Assert.Single(r); string generatedSource = string.Join('\n', r[0].SourceText.Lines.Select(x => x.ToString())); - Assert.Contains($"public static void Bind(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::Program.MyClass0 obj) => {{ }};", generatedSource); - Assert.Contains($"public static void Bind(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::Program.MyClass1 obj, global::System.Action? configureOptions) => {{ }};", generatedSource); - Assert.Contains($"public static void Bind(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, string key, global::Program.MyClass2 obj) => {{ }};", generatedSource); + Assert.Contains("public static void Bind(this IConfiguration configuration, object? obj) => BindCoreMain(configuration, obj, configureOptions: null);", generatedSource); + Assert.Contains("public static void Bind(this IConfiguration configuration, object? obj, Action? configureOptions) => BindCoreMain(configuration, obj, configureOptions);", generatedSource); + Assert.Contains("public static void Bind(this IConfiguration configuration, string key, object? obj) => BindCoreMain(configuration?.GetSection(key), obj, configureOptions: null);", generatedSource); Assert.Empty(d); } @@ -395,10 +397,10 @@ private static async Task VerifyAgainstBaselineUsingFile( private static async Task<(ImmutableArray, ImmutableArray)> RunGenerator( string testSourceCode, - LanguageVersion langVersion = LanguageVersion.CSharp11, + LanguageVersion langVersion = LanguageVersion.Preview, IEnumerable? references = null) => await RoslynTestUtils.RunGenerator( - new ConfigurationBindingGenerator(), + new ConfigurationBindingGenerator() { EmitUniqueHelperNames = false }, references ?? s_compilationAssemblyRefs, new[] { testSourceCode }, langVersion: langVersion).ConfigureAwait(false); diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Microsoft.Extensions.Configuration.Binder.SourceGeneration.Tests.csproj b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Microsoft.Extensions.Configuration.Binder.SourceGeneration.Tests.csproj index 2108bc2574ed2..cfd45c365d42a 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Microsoft.Extensions.Configuration.Binder.SourceGeneration.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Microsoft.Extensions.Configuration.Binder.SourceGeneration.Tests.csproj @@ -4,7 +4,7 @@ true SYSLIB1100,SYSLIB1101 - + $(Features);InterceptorsPreview true diff --git a/src/libraries/Microsoft.Extensions.Logging.Configuration/src/Microsoft.Extensions.Logging.Configuration.csproj b/src/libraries/Microsoft.Extensions.Logging.Configuration/src/Microsoft.Extensions.Logging.Configuration.csproj index 545e2867e4de6..5a66e45d7f83d 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Configuration/src/Microsoft.Extensions.Logging.Configuration.csproj +++ b/src/libraries/Microsoft.Extensions.Logging.Configuration/src/Microsoft.Extensions.Logging.Configuration.csproj @@ -3,6 +3,8 @@ $(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum) true + $(Features);InterceptorsPreview + true true true Configuration support for Microsoft.Extensions.Logging. diff --git a/src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csproj b/src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csproj index abc5c9d9792ee..978f746ab1fc9 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csproj +++ b/src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csproj @@ -7,6 +7,8 @@ $(DefineConstants);NO_SUPPRESS_GC_TRANSITION true true + $(Features);InterceptorsPreview + true true Console logger provider implementation for Microsoft.Extensions.Logging. diff --git a/src/libraries/Microsoft.Extensions.Options.ConfigurationExtensions/tests/SourceGenerationTests/Microsoft.Extensions.Options.ConfigurationExtensions.SourceGeneration.Tests.csproj b/src/libraries/Microsoft.Extensions.Options.ConfigurationExtensions/tests/SourceGenerationTests/Microsoft.Extensions.Options.ConfigurationExtensions.SourceGeneration.Tests.csproj index f1843ebff94a2..0676fddc28982 100644 --- a/src/libraries/Microsoft.Extensions.Options.ConfigurationExtensions/tests/SourceGenerationTests/Microsoft.Extensions.Options.ConfigurationExtensions.SourceGeneration.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.Options.ConfigurationExtensions/tests/SourceGenerationTests/Microsoft.Extensions.Options.ConfigurationExtensions.SourceGeneration.Tests.csproj @@ -2,9 +2,9 @@ enable $(NetCoreAppCurrent);$(NetFrameworkMinimum) - true $(DefineConstants);BUILDING_SOURCE_GENERATOR_TESTS;ROSLYN4_0_OR_GREATER;ROSLYN4_4_OR_GREATER - + $(Features);InterceptorsPreview + true true diff --git a/src/libraries/Microsoft.Extensions.Options/gen/Emitter.cs b/src/libraries/Microsoft.Extensions.Options/gen/Emitter.cs index adbaa874ad7f3..bbbbf412c2007 100644 --- a/src/libraries/Microsoft.Extensions.Options/gen/Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Options/gen/Emitter.cs @@ -7,6 +7,7 @@ using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; +using SourceGenerators; namespace Microsoft.Extensions.Options.Generators { @@ -20,11 +21,11 @@ internal sealed class Emitter : EmitterBase private const string StaticValidationResultType = "global::System.ComponentModel.DataAnnotations.ValidationResult"; private const string StaticValidationAttributeType = "global::System.ComponentModel.DataAnnotations.ValidationAttribute"; - private string _staticValidationAttributeHolderClassName = "__Attributes"; - private string _staticValidatorHolderClassName = "__Validators"; - private string _staticValidationAttributeHolderClassFQN; - private string _staticValidatorHolderClassFQN; - private string _modifier; + private readonly string _staticValidationAttributeHolderClassName = "__Attributes"; + private readonly string _staticValidatorHolderClassName = "__Validators"; + private readonly string _staticValidationAttributeHolderClassFQN; + private readonly string _staticValidatorHolderClassFQN; + private readonly string _modifier; private sealed record StaticFieldInfo(string FieldTypeFQN, int FieldOrder, string FieldName, IList InstantiationLines); @@ -37,9 +38,8 @@ public Emitter(Compilation compilation, bool emitPreamble = true) : base(emitPre else { _modifier = "internal"; - string suffix = $"_{new Random().Next():X8}"; - _staticValidationAttributeHolderClassName += suffix; - _staticValidatorHolderClassName += suffix; + GeneratorHelpers.MakeNameUnique(ref _staticValidationAttributeHolderClassName); + GeneratorHelpers.MakeNameUnique(ref _staticValidatorHolderClassName); } _staticValidationAttributeHolderClassFQN = $"global::{StaticFieldHolderClassesNamespace}.{_staticValidationAttributeHolderClassName}"; diff --git a/src/libraries/Microsoft.Extensions.Options/gen/Microsoft.Extensions.Options.SourceGeneration.csproj b/src/libraries/Microsoft.Extensions.Options/gen/Microsoft.Extensions.Options.SourceGeneration.csproj index 41ceaf6739c33..cd27b518a8dc4 100644 --- a/src/libraries/Microsoft.Extensions.Options/gen/Microsoft.Extensions.Options.SourceGeneration.csproj +++ b/src/libraries/Microsoft.Extensions.Options/gen/Microsoft.Extensions.Options.SourceGeneration.csproj @@ -20,6 +20,7 @@ + From 8bd9571ad705d9d5db60d2daa596967ca54024e7 Mon Sep 17 00:00:00 2001 From: Layomi Akinrinade Date: Thu, 17 Aug 2023 16:29:46 -0700 Subject: [PATCH 2/6] Fix polymorphic issue and address feedback --- .../src/SourceGenerators/GeneratorHelpers.cs | 12 -- .../ConfigurationBindingGenerator.Emitter.cs | 14 +- .../ConfigurationBindingGenerator.Parser.cs | 48 ++++- ...onfigurationBindingGenerator.Suppressor.cs | 12 +- .../gen/ConfigurationBindingGenerator.cs | 12 +- .../Helpers/Emitter/ConfigurationBinder.cs | 58 ++++-- .../gen/Helpers/Emitter/CoreBindingHelpers.cs | 112 +++++------ .../gen/Helpers/Emitter/Helpers.cs | 31 +++- .../OptionsBuilderConfigurationExtensions.cs | 5 +- ...onfigurationServiceCollectionExtensions.cs | 2 +- .../gen/Helpers/InterceptorLocationInfo.cs | 54 +++++- .../gen/Helpers/MethodsToGen.cs | 6 +- .../gen/Helpers/Parser/ConfigurationBinder.cs | 25 ++- .../OptionsBuilderConfigurationExtensions.cs | 10 +- ...onfigurationServiceCollectionExtensions.cs | 4 +- ...nfiguration.Binder.SourceGeneration.csproj | 1 - .../gen/Model/ObjectSpec.cs | 4 - .../gen/Model/SourceGenerationSpec.cs | 4 +- .../gen/Resources/Strings.resx | 4 +- .../gen/Resources/xlf/Strings.cs.xlf | 8 +- .../gen/Resources/xlf/Strings.de.xlf | 8 +- .../gen/Resources/xlf/Strings.es.xlf | 8 +- .../gen/Resources/xlf/Strings.fr.xlf | 8 +- .../gen/Resources/xlf/Strings.it.xlf | 8 +- .../gen/Resources/xlf/Strings.ja.xlf | 8 +- .../gen/Resources/xlf/Strings.ko.xlf | 8 +- .../gen/Resources/xlf/Strings.pl.xlf | 8 +- .../gen/Resources/xlf/Strings.pt-BR.xlf | 8 +- .../gen/Resources/xlf/Strings.ru.xlf | 8 +- .../gen/Resources/xlf/Strings.tr.xlf | 8 +- .../gen/Resources/xlf/Strings.zh-Hans.xlf | 8 +- .../gen/Resources/xlf/Strings.zh-Hant.xlf | 8 +- .../tests/Common/ConfigurationBinderTests.cs | 6 +- .../Baselines/Collections.generated.txt | 164 +++-------------- .../ConfigurationBinder/Bind.generated.txt | 126 +++++-------- .../Bind_Instance.generated.txt | 128 +++---------- .../Bind_Instance_BinderOptions.generated.txt | 110 +++-------- .../Bind_Key_Instance.generated.txt | 128 +++---------- .../ConfigurationBinder/Get.generated.txt | 129 +++---------- .../GetValue.generated.txt | 9 +- .../ConfigurationBinder/Get_T.generated.txt | 111 +++-------- .../Get_T_BinderOptions.generated.txt | 111 +++-------- .../Get_TypeOf.generated.txt | 40 +--- .../Get_TypeOf_BinderOptions.generated.txt | 40 +--- .../BindConfiguration.generated.txt | 54 +++--- .../OptionsBuilder/Bind_T.generated.txt | 44 ++--- .../Bind_T_BinderOptions.generated.txt | 44 ++--- .../Baselines/Primitives.generated.txt | 174 ++++++------------ .../Configure_T.generated.txt | 101 +++------- .../Configure_T_BinderOptions.generated.txt | 101 +++------- .../Configure_T_name.generated.txt | 101 +++------- ...nfigure_T_name_BinderOptions.generated.txt | 101 +++------- .../ConfigurationBindingGeneratorTests.cs | 10 +- ...ft.Extensions.Logging.Configuration.csproj | 1 - ...icrosoft.Extensions.Logging.Console.csproj | 2 +- .../gen/Emitter.cs | 8 +- ...Extensions.Options.SourceGeneration.csproj | 1 - 57 files changed, 784 insertions(+), 1582 deletions(-) delete mode 100644 src/libraries/Common/src/SourceGenerators/GeneratorHelpers.cs diff --git a/src/libraries/Common/src/SourceGenerators/GeneratorHelpers.cs b/src/libraries/Common/src/SourceGenerators/GeneratorHelpers.cs deleted file mode 100644 index 1ec1d1883302c..0000000000000 --- a/src/libraries/Common/src/SourceGenerators/GeneratorHelpers.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; - -namespace SourceGenerators -{ - public static class GeneratorHelpers - { - public static string MakeNameUnique(ref string name) => name += $"_{new Random().Next():X8}"; - } -} diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Emitter.cs index 351c211c6c18a..756e10bc26b8d 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Emitter.cs @@ -17,7 +17,6 @@ private sealed partial class Emitter private readonly SourceProductionContext _context; private readonly SourceGenerationSpec _sourceGenSpec; - private readonly string _generatedNamespaceName = ProjectName; private bool _emitBlankLineBeforeNextStatement; private int _valueSuffixIndex; @@ -25,15 +24,10 @@ private sealed partial class Emitter private readonly SourceWriter _writer = new(); - public Emitter(SourceProductionContext context, SourceGenerationSpec sourceGenSpec, bool emitUniqueHelperNames) + public Emitter(SourceProductionContext context, SourceGenerationSpec sourceGenSpec) { _context = context; _sourceGenSpec = sourceGenSpec; - - if (emitUniqueHelperNames) - { - GeneratorHelpers.MakeNameUnique(ref _generatedNamespaceName); - } } public void Emit() @@ -51,7 +45,7 @@ public void Emit() EmitInterceptsLocationAttrDecl(); - EmitStartBlock($"namespace {_generatedNamespaceName}"); + EmitStartBlock($"namespace {ProjectName}"); EmitUsingStatements(); _writer.WriteLine(); @@ -260,10 +254,8 @@ private void EmitUsingStatements() private void EmitIConfigurationHasValueOrChildrenCheck(bool voidReturn) { string returnPostfix = voidReturn ? string.Empty : " null"; - string methodIdentifier = Identifier.HasValueOrChildren; - _writer.WriteLine($$""" - if (!{{methodIdentifier}}({{Identifier.configuration}})) + if (!{{Identifier.HasValueOrChildren}}({{Identifier.configuration}})) { return{{returnPostfix}}; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Parser.cs index 874d2cf9eab60..64db4eb58b1f7 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Parser.cs @@ -177,15 +177,14 @@ type.TypeKind is TypeKind.TypeParameter or TypeKind.Pointer or TypeKind.Error || return _createdSpecs[type] = spec; } - private void RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper method, TypeSpec type) + private void RegisterTypeForBindCoreMainGen(TypeSpec typeSpec) { - if (!_sourceGenSpec.TypesForGen_CoreBindingHelper_Methods.TryGetValue(method, out HashSet? types)) + if (typeSpec.NeedsMemberBinding) { - _sourceGenSpec.TypesForGen_CoreBindingHelper_Methods[method] = types = new HashSet(); + RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.BindCoreMain, typeSpec); + RegisterTypeForBindCoreGen(typeSpec); + _sourceGenSpec.MethodsToGen_CoreBindingHelper |= MethodsToGen_CoreBindingHelper.AsConfigWithChildren; } - - types.Add(type); - _sourceGenSpec.MethodsToGen_CoreBindingHelper |= method; } private void RegisterTypeForBindCoreGen(TypeSpec typeSpec) @@ -196,16 +195,30 @@ private void RegisterTypeForBindCoreGen(TypeSpec typeSpec) } } - private void RegisterGenMethodAsInterceptor(Enum method, IInvocationOperation operation) + private void RegisterTypeForGetCoreGen(TypeSpec typeSpec) + { + RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.GetCore, typeSpec); + _sourceGenSpec.MethodsToGen_CoreBindingHelper |= MethodsToGen_CoreBindingHelper.AsConfigWithChildren; + } + + private void RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper method, TypeSpec type) { - if (!_sourceGenSpec.GenMethodsInterceptionInfo.TryGetValue(method, out List? info)) + if (!_sourceGenSpec.TypesForGen_CoreBindingHelper_Methods.TryGetValue(method, out HashSet? types)) { - _sourceGenSpec.GenMethodsInterceptionInfo[method] = info = new List(); + _sourceGenSpec.TypesForGen_CoreBindingHelper_Methods[method] = types = new HashSet(); } - info.Add(new InterceptorLocationInfo(operation)); + types.Add(type); + _sourceGenSpec.MethodsToGen_CoreBindingHelper |= method; } + /// + /// Registers interceptors for root binding methods, except for ConfigurationBinder.Bind, + /// which is handled by + /// + private void RegisterAsInterceptor(Enum method, IInvocationOperation operation) => + _sourceGenSpec.InterceptionInfo.RegisterCacheEntry(method, new InterceptorLocationInfo(operation)); + private static bool IsNullable(ITypeSymbol type, [NotNullWhen(true)] out ITypeSymbol? underlyingType) { if (type is INamedTypeSymbol { IsGenericType: true } genericType && @@ -901,4 +914,19 @@ private void RegisterTypeDiagnostic(ITypeSymbol causingType, InvocationDiagnosti } } } + + public static class ParserExtensions + { + public static void RegisterCacheEntry(this Dictionary cache, TKey key, TEntry entry) + where TKey : notnull + where TValue : ICollection, new() + { + if (!cache.TryGetValue(key, out TValue? entryCollection)) + { + cache[key] = entryCollection = new TValue(); + } + + entryCollection.Add(entry); + } + } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Suppressor.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Suppressor.cs index 98af88f143fcc..13158753c3f07 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Suppressor.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Suppressor.cs @@ -12,17 +12,23 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public sealed partial class ConfigurationBindingGenerator { /// - /// Supresses false-positive diagnostics emitted by the linker analyzers - /// when analyzing binding invocations that we have substituted.Workaround - /// for https://github.com/dotnet/roslyn/issues/68669. + /// Supresses false-positive diagnostics emitted by the linker + /// when analyzing binding invocations that we have intercepted. + /// Workaround for https://github.com/dotnet/roslyn/issues/68669. /// [DiagnosticAnalyzer(LanguageNames.CSharp)] public sealed class Suppressor : DiagnosticSuppressor { private const string Justification = "The target method has been intercepted by a generated static variant."; + /// + /// Suppression descriptor for IL2026: Members attributed with RequiresUnreferencedCode may break when trimming. + /// private static readonly SuppressionDescriptor RUCDiagnostic = new(id: "SYSLIBSUPPRESS0002", suppressedDiagnosticId: "IL2026", Justification); + /// + /// Suppression descriptor for IL3050: Avoid calling members annotated with 'RequiresDynamicCodeAttribute' when publishing as native AOT. + /// private static readonly SuppressionDescriptor RDCDiagnostic = new(id: "SYSLIBSUPPRESS0003", suppressedDiagnosticId: "IL3050", Justification); public override ImmutableArray SupportedSuppressions => ImmutableArray.Create(RUCDiagnostic, RDCDiagnostic); diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs index 870a7410344a9..fbca2dd3cfc50 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.cs @@ -16,8 +16,6 @@ public sealed partial class ConfigurationBindingGenerator : IIncrementalGenerato { private static readonly string ProjectName = Emitter.s_assemblyName.Name; - public bool EmitUniqueHelperNames { get; init; } = true; - public void Initialize(IncrementalGeneratorInitializationContext context) { #if LAUNCH_DEBUGGER @@ -43,7 +41,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) context.RegisterSourceOutput(inputData, (spc, source) => Execute(source.Item1, source.Item2, spc)); } - private void Execute(CompilationData compilationData, ImmutableArray inputCalls, SourceProductionContext context) + private static void Execute(CompilationData compilationData, ImmutableArray inputCalls, SourceProductionContext context) { if (inputCalls.IsDefaultOrEmpty) { @@ -59,7 +57,7 @@ private void Execute(CompilationData compilationData, ImmutableArray= LanguageVersion.Preview; + // We don't have a CSharp21 value available yet. Polyfill the value here for forward compat, rather than use the LangugeVersion.Preview enum value. + // https://github.com/dotnet/roslyn/blob/168689931cb4e3150641ec2fb188a64ce4b3b790/src/Compilers/CSharp/Portable/LanguageVersion.cs#L218-L232 + const int LangVersion_CSharp12 = 1200; + LanguageVersionIsSupported = (int)compilation.LanguageVersion >= LangVersion_CSharp12; + if (LanguageVersionIsSupported) { TypeSymbols = new KnownTypeSymbols(compilation); diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/ConfigurationBinder.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/ConfigurationBinder.cs index 6040f0b63c8ca..64064887c7c70 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/ConfigurationBinder.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/ConfigurationBinder.cs @@ -1,6 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; +using SourceGenerators; + namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { public sealed partial class ConfigurationBindingGenerator @@ -45,14 +48,14 @@ private void EmitGetMethods() if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Get_TypeOf)) { StartMethodDefinition(MethodsToGen_ConfigurationBinder.Get_TypeOf, documentation); - _writer.WriteLine($"public static object? {Identifier.Get}(this {Identifier.IConfiguration} {Identifier.configuration}, {Identifier.Type} {Identifier.type}) => " + + _writer.WriteLine($"public static object? {Identifier.Get}(this {Identifier.IConfiguration} {Identifier.configuration}, Type {Identifier.type}) => " + $"{expressionForGetCore}({Identifier.configuration}, {Identifier.type}, {Identifier.configureOptions}: null);"); } if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Get_TypeOf_BinderOptions)) { StartMethodDefinition(MethodsToGen_ConfigurationBinder.Get_TypeOf_BinderOptions, documentation); - _writer.WriteLine($"public static object? {Identifier.Get}(this {Identifier.IConfiguration} {Identifier.configuration}, {Identifier.Type} {Identifier.type}, {TypeDisplayString.NullableActionOfBinderOptions} {Identifier.configureOptions}) => " + + _writer.WriteLine($"public static object? {Identifier.Get}(this {Identifier.IConfiguration} {Identifier.configuration}, Type {Identifier.type}, {TypeDisplayString.NullableActionOfBinderOptions} {Identifier.configureOptions}) => " + $"{expressionForGetCore}({Identifier.configuration}, {Identifier.type}, {Identifier.configureOptions});"); } } @@ -79,25 +82,30 @@ private void EmitGetValueMethods() if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.GetValue_TypeOf_key)) { StartMethodDefinition(MethodsToGen_ConfigurationBinder.GetValue_TypeOf_key, documentation); - _writer.WriteLine($"public static object? {Identifier.GetValue}(this {Identifier.IConfiguration} {Identifier.configuration}, {Identifier.Type} {Identifier.type}, string {Identifier.key}) => " + + _writer.WriteLine($"public static object? {Identifier.GetValue}(this {Identifier.IConfiguration} {Identifier.configuration}, Type {Identifier.type}, string {Identifier.key}) => " + $"{expressionForGetValueCore}({Identifier.configuration}, {Identifier.type}, {Identifier.key});"); } if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.GetValue_TypeOf_key_defaultValue)) { StartMethodDefinition(MethodsToGen_ConfigurationBinder.GetValue_TypeOf_key_defaultValue, documentation); - _writer.WriteLine($"public static object? {Identifier.GetValue}(this {Identifier.IConfiguration} {Identifier.configuration}, {Identifier.Type} {Identifier.type}, string {Identifier.key}, object? {Identifier.defaultValue}) => " + + _writer.WriteLine($"public static object? {Identifier.GetValue}(this {Identifier.IConfiguration} {Identifier.configuration}, Type {Identifier.type}, string {Identifier.key}, object? {Identifier.defaultValue}) => " + $"{expressionForGetValueCore}({Identifier.configuration}, {Identifier.type}, {Identifier.key}) ?? {Identifier.defaultValue};"); } } private void EmitBindMethods_ConfigurationBinder() { + if (!ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Bind)) + { + return; + } + string objParamExpr = $"object? {Identifier.obj}"; if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Bind_instance)) { - EmitMethodImplementation( + EmitMethods( MethodsToGen_ConfigurationBinder.Bind_instance, additionalParams: objParamExpr, configExpression: Identifier.configuration, @@ -106,7 +114,7 @@ private void EmitBindMethods_ConfigurationBinder() if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Bind_instance_BinderOptions)) { - EmitMethodImplementation( + EmitMethods( MethodsToGen_ConfigurationBinder.Bind_instance_BinderOptions, additionalParams: $"{objParamExpr}, {TypeDisplayString.NullableActionOfBinderOptions} {Identifier.configureOptions}", configExpression: Identifier.configuration, @@ -115,21 +123,41 @@ private void EmitBindMethods_ConfigurationBinder() if (ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Bind_key_instance)) { - EmitMethodImplementation( + EmitMethods( MethodsToGen_ConfigurationBinder.Bind_key_instance, additionalParams: $"string {Identifier.key}, {objParamExpr}", - configExpression: $"{Identifier.configuration}?.{Identifier.GetSection}({Identifier.key})", + configExpression: $"{Expression.configurationGetSection}({Identifier.key})", configureOptions: false); } - void EmitMethodImplementation(MethodsToGen_ConfigurationBinder method, string additionalParams, string configExpression, bool configureOptions) + void EmitMethods(MethodsToGen_ConfigurationBinder method, string additionalParams, string configExpression, bool configureOptions) { - string configureOptionsArg = configureOptions ? Identifier.configureOptions : $"{Identifier.configureOptions}: null"; - string returnExpression = $"{Identifier.BindCoreMain}({configExpression}, {Identifier.obj}, {configureOptionsArg})"; - - StartMethodDefinition(method, "Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively."); - _writer.WriteLine($"public static void {Identifier.Bind}(this {Identifier.IConfiguration} {Identifier.configuration}, {additionalParams}) => " - + $"{returnExpression};"); + foreach (KeyValuePair> pair in _sourceGenSpec.InterceptionInfo_ConfigBinder.GetOverloadInfo(method)) + { + (TypeSpec type, List interceptorInfoList) = (pair.Key, pair.Value); + + EmitBlankLineIfRequired(); + _writer.WriteLine($"/// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively."); + EmitInterceptsLocationAnnotations(interceptorInfoList); + EmitStartBlock($"public static void {Identifier.Bind}_{type.DisplayString.ToIdentifierSubstring()}(this {Identifier.IConfiguration} {Identifier.configuration}, {additionalParams})"); + + if (!EmitInitException(type) && type.NeedsMemberBinding) + { + string binderOptionsArg = configureOptions ? $"{Identifier.GetBinderOptions}({Identifier.configureOptions})" : $"{Identifier.binderOptions}: null"; + + EmitCheckForNullArgument_WithBlankLine(Identifier.configuration); + if (!type.IsValueType) + { + EmitCheckForNullArgument_WithBlankLine(Identifier.obj); + } + _writer.WriteLine($$""" + var {{Identifier.typedObj}} = ({{type.EffectiveType.DisplayString}}){{Identifier.obj}}; + {{nameof(MethodsToGen_CoreBindingHelper.BindCore)}}({{configExpression}}, ref {{Identifier.typedObj}}, {{binderOptionsArg}}); + """); + } + + EmitEndBlock(); + } } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/CoreBindingHelpers.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/CoreBindingHelpers.cs index db3f6660561a9..f30408fad596d 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/CoreBindingHelpers.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/CoreBindingHelpers.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Linq; using Microsoft.CodeAnalysis; +using SourceGenerators; namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { @@ -18,7 +19,6 @@ private sealed partial class Emitter private void EmitCoreBindingHelpers() { Debug.Assert(_emitBlankLineBeforeNextStatement); - EmitBindingExtStartRegion("Core binding"); EmitConfigurationKeyCaches(); EmitGetCoreMethod(); @@ -73,12 +73,14 @@ private void EmitGetCoreMethod() EmitIConfigurationHasValueOrChildrenCheck(voidReturn: false); + bool isFirstType = true; foreach (TypeSpec type in types) { TypeSpec effectiveType = type.EffectiveType; TypeSpecKind kind = effectiveType.SpecKind; + string conditionKindExpr = GetConditionKindExpr(ref isFirstType); - EmitStartBlock($"if (type == typeof({type.DisplayString}))"); + EmitStartBlock($"{conditionKindExpr} ({Identifier.type} == typeof({type.DisplayString}))"); if (effectiveType is ParsableFromStringSpec stringParsableType) { @@ -104,10 +106,10 @@ private void EmitGetCoreMethod() } EmitEndBlock(); - _writer.WriteLine(); } - Emit_NotSupportedException_TypeNotDetectedAsInput("'{type}'"); + _writer.WriteLine(); + Emit_NotSupportedException_TypeNotDetectedAsInput(); EmitEndBlock(); _emitBlankLineBeforeNextStatement = true; } @@ -135,9 +137,11 @@ private void EmitGetValueCoreMethod() _writer.WriteLine(); + bool isFirstType = true; foreach (TypeSpec type in targetTypes) { - EmitStartBlock($"if ({Identifier.type} == typeof({type.DisplayString}))"); + string conditionKindExpr = GetConditionKindExpr(ref isFirstType); + EmitStartBlock($"{conditionKindExpr} ({Identifier.type} == typeof({type.DisplayString}))"); EmitBindLogicFromString( (ParsableFromStringSpec)type.EffectiveType, @@ -148,9 +152,9 @@ private void EmitGetValueCoreMethod() useIncrementalStringValueIdentifier: false); EmitEndBlock(); - _writer.WriteLine(); } + _writer.WriteLine(); _writer.WriteLine("return null;"); EmitEndBlock(); _emitBlankLineBeforeNextStatement = true; @@ -158,53 +162,38 @@ private void EmitGetValueCoreMethod() private void EmitBindCoreMainMethod() { - if (!_sourceGenSpec.TypesForGen_CoreBindingHelper_Methods.TryGetValue(MethodsToGen_CoreBindingHelper.BindCore, out HashSet? targetTypes)) + if (!_sourceGenSpec.TypesForGen_CoreBindingHelper_Methods.TryGetValue(MethodsToGen_CoreBindingHelper.BindCoreMain, out HashSet? targetTypes)) { return; } EmitBlankLineIfRequired(); - - EmitStartBlock($"public static void {Identifier.BindCoreMain}({Identifier.IConfiguration}? {Identifier.configuration}, object? {Identifier.obj}, {TypeDisplayString.NullableActionOfBinderOptions} {Identifier.configureOptions})"); - + EmitStartBlock($"public static void {nameof(MethodsToGen_CoreBindingHelper.BindCoreMain)}({Identifier.IConfiguration} {Identifier.configuration}, object {Identifier.obj}, Type {Identifier.type}, {TypeDisplayString.NullableActionOfBinderOptions} {Identifier.configureOptions})"); EmitCheckForNullArgument_WithBlankLine(Identifier.configuration); EmitCheckForNullArgument_WithBlankLine(Identifier.obj); - + EmitIConfigurationHasValueOrChildrenCheck(voidReturn: true); _writer.WriteLine($"{Identifier.BinderOptions}? {Identifier.binderOptions} = {Identifier.GetBinderOptions}({Identifier.configureOptions});"); _writer.WriteLine(); - EmitIConfigurationHasValueOrChildrenCheck(voidReturn: true); - + bool isFirstType = true; foreach (TypeSpec type in targetTypes) { - string typedObjIdentifier = GetIncrementalIdentifier(Identifier.typedObj); - EmitStartBlock($"if ({Identifier.obj} is {type.DisplayString} {typedObjIdentifier})"); - TypeSpec effectiveType = type.EffectiveType; + string conditionKindExpr = GetConditionKindExpr(ref isFirstType); + + EmitStartBlock($"{conditionKindExpr} ({Identifier.type} == typeof({type.DisplayString}))"); if (!EmitInitException(effectiveType)) { - string objIdentifier; - if (type == effectiveType) - { - objIdentifier = typedObjIdentifier; - } - else - { - objIdentifier = Identifier.temp; - _writer.WriteLine($"var {Identifier.temp} = ({effectiveType.DisplayString}){typedObjIdentifier};"); - } - - EmitBindCoreCall(type, objIdentifier, Identifier.configuration, InitializationKind.None); + _writer.WriteLine($"var {Identifier.temp} = ({effectiveType.DisplayString}){Identifier.obj};"); + EmitBindCoreCall(type, Identifier.temp, Identifier.configuration, InitializationKind.None); _writer.WriteLine($"return;"); } - EmitEndBlock(); - _writer.WriteLine(); } - Emit_NotSupportedException_TypeNotDetectedAsInput("'{obj.GetType()}'"); + _writer.WriteLine(); + Emit_NotSupportedException_TypeNotDetectedAsInput(); EmitEndBlock(); - _emitBlankLineBeforeNextStatement = true; } private void EmitBindCoreMethods() @@ -229,11 +218,6 @@ private void EmitBindCoreMethod(TypeSpec type) string objParameterExpression = $"ref {type.DisplayString} {Identifier.obj}"; EmitStartBlock(@$"public static void {nameof(MethodsToGen_CoreBindingHelper.BindCore)}({Identifier.IConfiguration} {Identifier.configuration}, {objParameterExpression}, {Identifier.BinderOptions}? {Identifier.binderOptions})"); - if (!type.IsValueType) - { - EmitCheckForNullArgument_WithBlankLine(Identifier.obj); - } - TypeSpec effectiveType = type.EffectiveType; if (effectiveType is EnumerableSpec enumerable) { @@ -396,30 +380,37 @@ void EmitThrowBlock(string condition) => """); } } + private void EmitHelperMethods() { - if (ShouldEmitMethods(MethodsToGen_CoreBindingHelper.BindCore | MethodsToGen_CoreBindingHelper.GetCore)) + // Emitted if we are to bind objects with complex members, or if we're emitting BindCoreMain or GetCore methods. + bool emitAsConfigWithChildren = ShouldEmitMethods(MethodsToGen_CoreBindingHelper.AsConfigWithChildren); + + if (ShouldEmitMethods(MethodsToGen_CoreBindingHelper.BindCore)) { + EmitBlankLineIfRequired(); EmitValidateConfigurationKeysMethod(); - _writer.WriteLine(); + } + + if (ShouldEmitMethods(MethodsToGen_CoreBindingHelper.BindCoreMain | MethodsToGen_CoreBindingHelper.GetCore)) + { + // HasValueOrChildren references this method. + Debug.Assert(emitAsConfigWithChildren); + EmitBlankLineIfRequired(); EmitHasValueOrChildrenMethod(); - _writer.WriteLine(); - EmitAsConfigWithChildrenMethod(); - _emitBlankLineBeforeNextStatement = true; } - else if (ShouldEmitMethods(MethodsToGen_CoreBindingHelper.AsConfigWithChildren)) + + if (emitAsConfigWithChildren) { - _writer.WriteLine(); + EmitBlankLineIfRequired(); EmitAsConfigWithChildrenMethod(); - _emitBlankLineBeforeNextStatement = true; } - if (ShouldEmitMethods(MethodsToGen_CoreBindingHelper.BindCore | MethodsToGen_CoreBindingHelper.GetCore) || + if (ShouldEmitMethods(MethodsToGen_CoreBindingHelper.BindCoreMain | MethodsToGen_CoreBindingHelper.GetCore) || ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Bind_instance_BinderOptions)) { - _writer.WriteLine(); + EmitBlankLineIfRequired(); EmitGetBinderOptionsHelper(); - _emitBlankLineBeforeNextStatement = true; } bool enumTypeExists = false; @@ -483,7 +474,7 @@ private void EmitHasValueOrChildrenMethod() { return true; } - return {{Identifier.AsConfigWithChildren}}({{Identifier.configuration}}) is not null; + return {{MethodsToGen_CoreBindingHelper.AsConfigWithChildren}}({{Identifier.configuration}}) is not null; } """); } @@ -491,7 +482,7 @@ private void EmitHasValueOrChildrenMethod() private void EmitAsConfigWithChildrenMethod() { _writer.WriteLine($$""" - public static {{Identifier.IConfiguration}}? {{Identifier.AsConfigWithChildren}}({{Identifier.IConfiguration}} {{Identifier.configuration}}) + public static {{Identifier.IConfiguration}}? {{MethodsToGen_CoreBindingHelper.AsConfigWithChildren}}({{Identifier.IConfiguration}} {{Identifier.configuration}}) { foreach ({{Identifier.IConfigurationSection}} _ in {{Identifier.configuration}}.{{Identifier.GetChildren}}()) { @@ -807,7 +798,7 @@ private bool EmitBindImplForMember( return true; } - string sectionValidationCall = $"{Identifier.AsConfigWithChildren}({sectionParseExpr})"; + string sectionValidationCall = $"{MethodsToGen_CoreBindingHelper.AsConfigWithChildren}({sectionParseExpr})"; string sectionIdentifier = GetIncrementalIdentifier(Identifier.section); EmitStartBlock($"if ({sectionValidationCall} is {Identifier.IConfigurationSection} {sectionIdentifier})"); @@ -911,6 +902,9 @@ private void EmitCollectionCastIfRequired(CollectionSpec type, out string objIde private void Emit_Foreach_Section_In_ConfigChildren_StartBlock() => EmitStartBlock($"foreach ({Identifier.IConfigurationSection} {Identifier.section} in {Identifier.configuration}.{Identifier.GetChildren}())"); + private void Emit_NotSupportedException_TypeNotDetectedAsInput() => + _writer.WriteLine(@$"throw new NotSupportedException($""{string.Format(ExceptionMessages.TypeNotDetectedAsInput, "{type}")}"");"); + private static string GetSectionPathFromConfigurationExpression(string configurationKeyName) => $@"{GetSectionFromConfigurationExpression(configurationKeyName)}.{Identifier.Path}"; @@ -920,11 +914,19 @@ private static string GetSectionFromConfigurationExpression(string configuration return $@"{Identifier.configuration}.{Identifier.GetSection}({argExpr})"; } - private static string GetConfigKeyCacheFieldName(ObjectSpec type) => - $"s_configKeys_{type.DisplayStringWithoutSpecialCharacters}"; + private static string GetConditionKindExpr(ref bool isFirstType) + { + if (isFirstType) + { + isFirstType = false; + return "if"; + } - private void Emit_NotSupportedException_TypeNotDetectedAsInput(string typeArgExpr) => - _writer.WriteLine(@$"throw new NotSupportedException($""{string.Format(ExceptionMessages.TypeNotDetectedAsInput, typeArgExpr)}"");"); + return "else if"; + } + + private static string GetConfigKeyCacheFieldName(ObjectSpec type) => + $"s_configKeys_{type.DisplayString.ToIdentifierSubstring()}"; } } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/Helpers.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/Helpers.cs index ab214fc739436..bad56b7ce3275 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/Helpers.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/Helpers.cs @@ -71,9 +71,7 @@ private static class Identifier public const string AddSingleton = nameof(AddSingleton); public const string Any = nameof(Any); public const string Array = nameof(Array); - public const string AsConfigWithChildren = nameof(AsConfigWithChildren); public const string Bind = nameof(Bind); - public const string BindCoreMain = nameof(BindCoreMain); public const string BinderOptions = nameof(BinderOptions); public const string BindingExtensions = nameof(BindingExtensions); public const string ConfigurationChangeTokenSource = nameof(ConfigurationChangeTokenSource); @@ -111,7 +109,6 @@ private static class Identifier public const string TOptions = nameof(TOptions); public const string TryCreate = nameof(TryCreate); public const string TryGetValue = nameof(TryGetValue); - public const string TryParse = nameof(TryParse); public const string Type = nameof(Type); public const string Uri = nameof(Uri); public const string ValidateConfigurationKeys = nameof(ValidateConfigurationKeys); @@ -128,17 +125,22 @@ private void EmitInterceptsLocationAnnotations(Enum generatedBindingOverload) // The only time a generated binding method won't have any locations to // intercept is when either of these methods are used as helpers for // other generated OptionsBuilder or ServiceCollection binding extensions. - bool interceptsCalls = _sourceGenSpec.GenMethodsInterceptionInfo.TryGetValue(generatedBindingOverload, out List? infoList); + bool interceptsCalls = _sourceGenSpec.InterceptionInfo.TryGetValue(generatedBindingOverload, out List? infoList); Debug.Assert(interceptsCalls || generatedBindingOverload is MethodsToGen_Extensions_ServiceCollection.Configure_T_name_BinderOptions || generatedBindingOverload is MethodsToGen_Extensions_OptionsBuilder.Bind_T_BinderOptions); if (interceptsCalls) { - foreach (InterceptorLocationInfo info in infoList) - { - _writer.WriteLine($@"[{Identifier.InterceptsLocation}Attribute(@""{info.FilePath}"", {info.LineNumber}, {info.CharacterNumber})]"); - } + EmitInterceptsLocationAnnotations(infoList); + } + } + + private void EmitInterceptsLocationAnnotations(List infoList) + { + foreach (InterceptorLocationInfo info in infoList) + { + _writer.WriteLine($@"[{Identifier.InterceptsLocation}Attribute(@""{info.FilePath}"", {info.LineNumber}, {info.CharacterNumber})]"); } } @@ -237,7 +239,18 @@ private bool EmitInitException(TypeSpec type) private string GetIncrementalIdentifier(string prefix) => $"{prefix}{_valueSuffixIndex++}"; private static string GetInitalizeMethodDisplayString(ObjectSpec type) => - $"{nameof(MethodsToGen_CoreBindingHelper.Initialize)}{type.DisplayStringWithoutSpecialCharacters}"; + $"{nameof(MethodsToGen_CoreBindingHelper.Initialize)}{type.DisplayString.ToIdentifierSubstring()}"; } } + + internal static class EmitterExtensions + { + public static string ToIdentifierSubstring(this string typeDisplayName) => + typeDisplayName + .Replace("[]", nameof(Array)) + .Replace(", ", string.Empty) + .Replace(".", string.Empty) + .Replace("<", string.Empty) + .Replace(">", string.Empty); + } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsBuilderConfigurationExtensions.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsBuilderConfigurationExtensions.cs index 91f7fdd43478d..d49198196fd49 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsBuilderConfigurationExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsBuilderConfigurationExtensions.cs @@ -71,10 +71,11 @@ private void EmitBindConfigurationMethod() EmitCheckForNullArgument_WithBlankLine(Identifier.configSectionPath); EmitStartBlock($"{Identifier.optionsBuilder}.{Identifier.Configure}<{Identifier.IConfiguration}>(({Identifier.obj}, {Identifier.configuration}) =>"); - + EmitCheckForNullArgument_WithBlankLine(Identifier.obj); + EmitCheckForNullArgument_WithBlankLine(Identifier.configuration); _writer.WriteLine($$""" {{Identifier.IConfiguration}} {{Identifier.section}} = string.Equals(string.Empty, {{Identifier.configSectionPath}}, StringComparison.OrdinalIgnoreCase) ? {{Identifier.configuration}} : {{Identifier.configuration}}.{{Identifier.GetSection}}({{Identifier.configSectionPath}}); - {{Identifier.BindCoreMain}}({{Identifier.section}}, {{Identifier.obj}}, {{Identifier.configureOptions}}); + {{nameof(MethodsToGen_CoreBindingHelper.BindCoreMain)}}({{Identifier.section}}, {{Identifier.obj}}, typeof({{Identifier.TOptions}}), {{Identifier.configureOptions}}); """); EmitEndBlock(endBraceTrailingSource: ");"); diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsConfigurationServiceCollectionExtensions.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsConfigurationServiceCollectionExtensions.cs index 804159ccb4bf8..0348eb5047e97 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsConfigurationServiceCollectionExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsConfigurationServiceCollectionExtensions.cs @@ -62,7 +62,7 @@ private void EmitConfigureMethods() _writer.WriteLine($$""" OptionsServiceCollectionExtensions.AddOptions({{Identifier.services}}); {{Identifier.services}}.{{Identifier.AddSingleton}}<{{Identifier.IOptionsChangeTokenSource}}<{{Identifier.TOptions}}>>(new {{Identifier.ConfigurationChangeTokenSource}}<{{Identifier.TOptions}}>({{Identifier.name}}, {{Identifier.configuration}})); - return {{Identifier.services}}.{{Identifier.AddSingleton}}<{{optionsNamespaceName}}.IConfigureOptions<{{Identifier.TOptions}}>>(new {{optionsNamespaceName}}.ConfigureNamedOptions<{{Identifier.TOptions}}>({{Identifier.name}}, {{Identifier.obj}} => {{Identifier.BindCoreMain}}({{Identifier.configuration}}, {{Identifier.obj}}, {{Identifier.configureOptions}}))); + return {{Identifier.services}}.{{Identifier.AddSingleton}}<{{optionsNamespaceName}}.IConfigureOptions<{{Identifier.TOptions}}>>(new {{optionsNamespaceName}}.ConfigureNamedOptions<{{Identifier.TOptions}}>({{Identifier.name}}, {{Identifier.obj}} => {{nameof(MethodsToGen_CoreBindingHelper.BindCoreMain)}}({{Identifier.configuration}}, {{Identifier.obj}}, typeof({{Identifier.TOptions}}){{Identifier.configureOptions}}))); """); EmitEndBlock(); } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/InterceptorLocationInfo.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/InterceptorLocationInfo.cs index d355e071904a3..d1dc4f4afa7e7 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/InterceptorLocationInfo.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/InterceptorLocationInfo.cs @@ -1,6 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; +using System.Collections; +using System.Collections.Generic; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Operations; @@ -8,7 +11,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { - internal readonly record struct InterceptorLocationInfo + internal sealed record InterceptorLocationInfo { public InterceptorLocationInfo(IInvocationOperation operation) { @@ -35,4 +38,53 @@ public InterceptorLocationInfo(IInvocationOperation operation) private static string GetInterceptorFilePath(SyntaxTree tree, SourceReferenceResolver? resolver) => resolver?.NormalizePath(tree.FilePath, baseFilePath: null) ?? tree.FilePath; } + + internal sealed record ConfigurationBinderInterceptorInfo + { + private OverloadInterceptorInfo? _bind_Instance; + private OverloadInterceptorInfo? _bind_instance_BinderOptions; + private OverloadInterceptorInfo? _bind_key_instance; + + public void RegisterOverloadInfo(MethodsToGen_ConfigurationBinder overload, TypeSpec type, IInvocationOperation operation) + { + OverloadInterceptorInfo overloadInfo = DetermineOverload(overload, initIfNull: true); + overloadInfo.RegisterLocationInfo(type, operation); + } + + public OverloadInterceptorInfo GetOverloadInfo(MethodsToGen_ConfigurationBinder overload) => + DetermineOverload(overload, initIfNull: false) ?? throw new ArgumentNullException(nameof(overload)); + + private OverloadInterceptorInfo? DetermineOverload(MethodsToGen_ConfigurationBinder overload, bool initIfNull) + { + return overload switch + { + MethodsToGen_ConfigurationBinder.Bind_instance => InitIfNull(ref _bind_Instance), + MethodsToGen_ConfigurationBinder.Bind_instance_BinderOptions => InitIfNull(ref _bind_instance_BinderOptions), + MethodsToGen_ConfigurationBinder.Bind_key_instance => InitIfNull(ref _bind_key_instance), + _ => throw new InvalidOperationException(nameof(overload)), + }; + + OverloadInterceptorInfo InitIfNull(ref OverloadInterceptorInfo? info) + { + if (initIfNull) + { + info ??= new OverloadInterceptorInfo(); + } + + return info; + } + } + } + + internal sealed record OverloadInterceptorInfo : IEnumerable>> + { + private readonly Dictionary> _typeInterceptionInfo = new(); + + public void RegisterLocationInfo(TypeSpec type, IInvocationOperation operation) => + _typeInterceptionInfo.RegisterCacheEntry(type, new InterceptorLocationInfo(operation)); + + public IEnumerator>> GetEnumerator() => _typeInterceptionInfo.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/MethodsToGen.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/MethodsToGen.cs index d0ba0699b2aba..2c582b20e8ebd 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/MethodsToGen.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/MethodsToGen.cs @@ -12,8 +12,10 @@ public enum MethodsToGen_CoreBindingHelper BindCore = 0x1, GetCore = 0x2, GetValueCore = 0x4, - Initialize = 0x8, - AsConfigWithChildren = 0x10, + BindCoreMain = 0x8, + Initialize = 0x10, + HasValueOrChildren = 0x20, + AsConfigWithChildren = 0x40, } /// diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/ConfigurationBinder.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/ConfigurationBinder.cs index 5ff93853676a6..e24ce11fe4374 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/ConfigurationBinder.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/ConfigurationBinder.cs @@ -100,9 +100,9 @@ private void RegisterBindInvocation(BinderInvocation invocation) return; } - if (GetTargetTypeForRootInvocationCore(type, invocation.Location) is not null) + if (GetTargetTypeForRootInvocationCore(type, invocation.Location) is TypeSpec typeSpec) { - RegisterAsInterceptor(overload, invocation.Operation); + RegisterAsInterceptor_ConfigBinder_BindMethod(overload, typeSpec, invocation.Operation); } static ITypeSymbol? ResolveType(IOperation conversionOperation) => @@ -176,8 +176,8 @@ private void RegisterGetInvocation(BinderInvocation invocation) if (GetTargetTypeForRootInvocation(type, invocation.Location) is TypeSpec typeSpec) { - RegisterAsInterceptor(overload, invocation.Operation); - RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.GetCore, typeSpec); + RegisterAsInterceptor_ConfigBinder(overload, invocation.Operation); + RegisterTypeForGetCoreGen(typeSpec); } } @@ -245,15 +245,26 @@ private void RegisterGetValueInvocation(BinderInvocation invocation) if (IsParsableFromString(effectiveType, out _) && GetTargetTypeForRootInvocationCore(type, invocation.Location) is TypeSpec typeSpec) { - RegisterAsInterceptor(overload, invocation.Operation); + RegisterAsInterceptor_ConfigBinder(overload, invocation.Operation); RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.GetValueCore, typeSpec); } } - private void RegisterAsInterceptor(MethodsToGen_ConfigurationBinder overload, IInvocationOperation operation) + private void RegisterAsInterceptor_ConfigBinder(MethodsToGen_ConfigurationBinder overload, IInvocationOperation operation) { _sourceGenSpec.MethodsToGen_ConfigurationBinder |= overload; - RegisterGenMethodAsInterceptor(overload, operation); + RegisterAsInterceptor(overload, operation); + } + + /// + /// Registers generated Bind methods as interceptors. This is done differently from other root + /// methods because we need to + /// explicitly account for the type to bind, to avoid type-check issues for polymorphic objects. + /// + private void RegisterAsInterceptor_ConfigBinder_BindMethod(MethodsToGen_ConfigurationBinder overload, TypeSpec typeSpec, IInvocationOperation operation) + { + _sourceGenSpec.MethodsToGen_ConfigurationBinder |= overload; + _sourceGenSpec.InterceptionInfo_ConfigBinder.RegisterOverloadInfo(overload, typeSpec, operation); } } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/OptionsBuilderConfigurationExtensions.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/OptionsBuilderConfigurationExtensions.cs index eb7407a0a571e..a62e63c0d90d5 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/OptionsBuilderConfigurationExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/OptionsBuilderConfigurationExtensions.cs @@ -68,7 +68,7 @@ 3 when SymbolEqualityComparer.Default.Equals(_typeSymbols.ActionOfBinderOptions, if (overload is not MethodsToGen_Extensions_OptionsBuilder.None) { - RegisterAsInterceptor(overload, operation); + RegisterAsInterceptor_OptionsBuilder(overload, operation); RegisterTypeForMethodGen(MethodsToGen_Extensions_ServiceCollection.Configure_T_name_BinderOptions, typeSpec); } } @@ -85,15 +85,15 @@ private void ParseBindConfigurationInvocation(BinderInvocation invocation, TypeS @params[1].Type.SpecialType is SpecialType.System_String && SymbolEqualityComparer.Default.Equals(_typeSymbols.ActionOfBinderOptions, @params[2].Type)) { - RegisterAsInterceptor(MethodsToGen_Extensions_OptionsBuilder.BindConfiguration_T_path_BinderOptions, invocation.Operation); - RegisterTypeForBindCoreGen(typeSpec); + RegisterAsInterceptor_OptionsBuilder(MethodsToGen_Extensions_OptionsBuilder.BindConfiguration_T_path_BinderOptions, invocation.Operation); + RegisterTypeForBindCoreMainGen(typeSpec); } } - private void RegisterAsInterceptor(MethodsToGen_Extensions_OptionsBuilder overload, IInvocationOperation operation) + private void RegisterAsInterceptor_OptionsBuilder(MethodsToGen_Extensions_OptionsBuilder overload, IInvocationOperation operation) { _sourceGenSpec.MethodsToGen_OptionsBuilderExt |= overload; - RegisterGenMethodAsInterceptor(overload, operation); + RegisterAsInterceptor(overload, operation); // Emitting refs to IOptionsChangeTokenSource, ConfigurationChangeTokenSource. _sourceGenSpec.Namespaces.Add("Microsoft.Extensions.Options"); diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/OptionsConfigurationServiceCollectionExtensions.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/OptionsConfigurationServiceCollectionExtensions.cs index d4b1bb30f1b1a..c356b29a69eff 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/OptionsConfigurationServiceCollectionExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Parser/OptionsConfigurationServiceCollectionExtensions.cs @@ -79,14 +79,14 @@ @params[1].Type.SpecialType is SpecialType.System_String && } RegisterTypeForMethodGen(overload, typeSpec); - RegisterGenMethodAsInterceptor(overload, operation); + RegisterAsInterceptor(overload, operation); } private void RegisterTypeForMethodGen(MethodsToGen_Extensions_ServiceCollection overload, TypeSpec typeSpec) { _sourceGenSpec.MethodsToGen_ServiceCollectionExt |= overload; _sourceGenSpec.Namespaces.Add("Microsoft.Extensions.DependencyInjection"); - RegisterTypeForBindCoreGen(typeSpec); + RegisterTypeForBindCoreMainGen(typeSpec); } } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Microsoft.Extensions.Configuration.Binder.SourceGeneration.csproj b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Microsoft.Extensions.Configuration.Binder.SourceGeneration.csproj index 3ad3c09f9c2fd..e895d18c5556c 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Microsoft.Extensions.Configuration.Binder.SourceGeneration.csproj +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Microsoft.Extensions.Configuration.Binder.SourceGeneration.csproj @@ -22,7 +22,6 @@ - diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/ObjectSpec.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/ObjectSpec.cs index c8911850badb6..8cc0ba68b4938 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/ObjectSpec.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/ObjectSpec.cs @@ -22,10 +22,6 @@ public ObjectSpec(INamedTypeSymbol type) : base(type) { } public List ConstructorParameters { get; } = new(); - private string _displayStringWithoutSpecialCharacters; - public string DisplayStringWithoutSpecialCharacters => - _displayStringWithoutSpecialCharacters ??= $"{DisplayString.Replace(".", string.Empty).Replace("<", string.Empty).Replace(">", string.Empty)}"; - public override bool NeedsMemberBinding => CanInitialize && Properties.Values.Count > 0 && Properties.Values.Any(p => p.ShouldBind()); diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/SourceGenerationSpec.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/SourceGenerationSpec.cs index cf37acd6f39db..760d57b1dcc88 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/SourceGenerationSpec.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/SourceGenerationSpec.cs @@ -8,7 +8,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { internal sealed record SourceGenerationSpec { - public Dictionary> GenMethodsInterceptionInfo { get; } = new(); + public Dictionary> InterceptionInfo { get; } = new(); + public ConfigurationBinderInterceptorInfo InterceptionInfo_ConfigBinder { get; } = new(); + public Dictionary> TypesForGen_CoreBindingHelper_Methods { get; } = new(); public HashSet PrimitivesForHelperGen { get; } = new(); diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/Strings.resx b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/Strings.resx index 301913987d7c7..3978cbaac6ce4 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/Strings.resx +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/Strings.resx @@ -133,10 +133,10 @@ The collection element type is not supported: '{0}'. - The project's language version has to be at least 'C# 11'. + The project's language version has to be at least 'C# 12'. - Language version is required to be at least C# 11 + Language version is required to be at least C# 12 Cannot create instance of type '{0}' because it is missing a public instance constructor. diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.cs.xlf b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.cs.xlf index e248c54626865..c6672eaff0bcd 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.cs.xlf +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.cs.xlf @@ -28,13 +28,13 @@ - The project's language version has to be at least 'C# 11'. - Jazyková verze projektu musí být alespoň C# 11 + The project's language version has to be at least 'C# 12'. + Jazyková verze projektu musí být alespoň C# 11 - Language version is required to be at least C# 11 - Verze jazyka musí být alespoň C# 11 + Language version is required to be at least C# 12 + Verze jazyka musí být alespoň C# 11 diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.de.xlf b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.de.xlf index 1fa847592bd02..5f353065a5f51 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.de.xlf +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.de.xlf @@ -28,13 +28,13 @@ - The project's language version has to be at least 'C# 11'. - Die Sprachversion des Projekts muss mindestens „C# 11“ sein + The project's language version has to be at least 'C# 12'. + Die Sprachversion des Projekts muss mindestens „C# 11“ sein - Language version is required to be at least C# 11 - Die Sprachversion muss mindestens C# 11 sein + Language version is required to be at least C# 12 + Die Sprachversion muss mindestens C# 11 sein diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.es.xlf b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.es.xlf index c52b2317ceade..cd54149e66c2f 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.es.xlf +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.es.xlf @@ -28,13 +28,13 @@ - The project's language version has to be at least 'C# 11'. - La versión del lenguaje del proyecto debe ser al menos "C# 11". + The project's language version has to be at least 'C# 12'. + La versión del lenguaje del proyecto debe ser al menos "C# 11". - Language version is required to be at least C# 11 - La versión del lenguaje debe ser al menos C# 11 + Language version is required to be at least C# 12 + La versión del lenguaje debe ser al menos C# 11 diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.fr.xlf b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.fr.xlf index 19362d7336208..b1c0753a49e8a 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.fr.xlf +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.fr.xlf @@ -28,13 +28,13 @@ - The project's language version has to be at least 'C# 11'. - La version de langage du projet doit être au moins « C# 11 ». + The project's language version has to be at least 'C# 12'. + La version de langage du projet doit être au moins « C# 11 ». - Language version is required to be at least C# 11 - La version du langage doit être au moins C# 11 + Language version is required to be at least C# 12 + La version du langage doit être au moins C# 11 diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.it.xlf b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.it.xlf index f418a83d0d422..27d10a7ee5f9f 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.it.xlf +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.it.xlf @@ -28,13 +28,13 @@ - The project's language version has to be at least 'C# 11'. - La versione del linguaggio del progetto deve essere almeno 'C# 11'. + The project's language version has to be at least 'C# 12'. + La versione del linguaggio del progetto deve essere almeno 'C# 11'. - Language version is required to be at least C# 11 - La versione del linguaggio deve essere almeno C# 11 + Language version is required to be at least C# 12 + La versione del linguaggio deve essere almeno C# 11 diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.ja.xlf b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.ja.xlf index ba59cfba40a89..24621bcc8b3d2 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.ja.xlf +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.ja.xlf @@ -28,13 +28,13 @@ - The project's language version has to be at least 'C# 11'. - プロジェクトの言語バージョンは少なくとも 'C# 11' である必要があります。 + The project's language version has to be at least 'C# 12'. + プロジェクトの言語バージョンは少なくとも 'C# 11' である必要があります。 - Language version is required to be at least C# 11 - 言語バージョンは少なくとも C# 11 である必要があります + Language version is required to be at least C# 12 + 言語バージョンは少なくとも C# 11 である必要があります diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.ko.xlf b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.ko.xlf index 10b9b107c4aad..217a0cd9f8e54 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.ko.xlf +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.ko.xlf @@ -28,13 +28,13 @@ - The project's language version has to be at least 'C# 11'. - 프로젝트의 언어 버전은 'C# 11' 이상이어야 합니다. + The project's language version has to be at least 'C# 12'. + 프로젝트의 언어 버전은 'C# 11' 이상이어야 합니다. - Language version is required to be at least C# 11 - 언어 버전은 C# 11 이상이어야 합니다. + Language version is required to be at least C# 12 + 언어 버전은 C# 11 이상이어야 합니다. diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.pl.xlf b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.pl.xlf index 2b558c588ebfb..0b24813a77a80 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.pl.xlf +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.pl.xlf @@ -28,13 +28,13 @@ - The project's language version has to be at least 'C# 11'. - Wersja językowa projektu musi mieć wartość co najmniej „C# 11”. + The project's language version has to be at least 'C# 12'. + Wersja językowa projektu musi mieć wartość co najmniej „C# 11”. - Language version is required to be at least C# 11 - Wymagana jest wersja językowa co najmniej C# 11 + Language version is required to be at least C# 12 + Wymagana jest wersja językowa co najmniej C# 11 diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.pt-BR.xlf b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.pt-BR.xlf index 9d2a51c6aa9c9..0ad700e64e9eb 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.pt-BR.xlf +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.pt-BR.xlf @@ -28,13 +28,13 @@ - The project's language version has to be at least 'C# 11'. - A versão do idioma do projeto deve ser no mínimo 'C# 11'. + The project's language version has to be at least 'C# 12'. + A versão do idioma do projeto deve ser no mínimo 'C# 11'. - Language version is required to be at least C# 11 - A versão do idioma deve ser pelo menos C# 11 + Language version is required to be at least C# 12 + A versão do idioma deve ser pelo menos C# 11 diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.ru.xlf b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.ru.xlf index 1ed03c55891a9..5e53330060c30 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.ru.xlf +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.ru.xlf @@ -28,13 +28,13 @@ - The project's language version has to be at least 'C# 11'. - Версия языка проекта должна быть не ниже "C# 11". + The project's language version has to be at least 'C# 12'. + Версия языка проекта должна быть не ниже "C# 11". - Language version is required to be at least C# 11 - Версия языка должна быть не ниже C# 11 + Language version is required to be at least C# 12 + Версия языка должна быть не ниже C# 11 diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.tr.xlf b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.tr.xlf index 8a6dbf76bab7f..cfaea488fd195 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.tr.xlf +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.tr.xlf @@ -28,13 +28,13 @@ - The project's language version has to be at least 'C# 11'. - Projenin dil sürümü en az 'C# 11' olmalıdır. + The project's language version has to be at least 'C# 12'. + Projenin dil sürümü en az 'C# 11' olmalıdır. - Language version is required to be at least C# 11 - Dil sürümünün en az C# 11 olması gerekir + Language version is required to be at least C# 12 + Dil sürümünün en az C# 11 olması gerekir diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.zh-Hans.xlf b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.zh-Hans.xlf index 9d0c0eb3a5d6d..3dfb711b39b4e 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.zh-Hans.xlf @@ -28,13 +28,13 @@ - The project's language version has to be at least 'C# 11'. - 项目的语言版本必须至少为 "C# 11"。 + The project's language version has to be at least 'C# 12'. + 项目的语言版本必须至少为 "C# 11"。 - Language version is required to be at least C# 11 - 语言版本必须至少为 C# 11 + Language version is required to be at least C# 12 + 语言版本必须至少为 C# 11 diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.zh-Hant.xlf b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.zh-Hant.xlf index dc6ded618c8e9..9917b18949880 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/xlf/Strings.zh-Hant.xlf @@ -28,13 +28,13 @@ - The project's language version has to be at least 'C# 11'. - 專案的語言版本必須至少為 'C# 11'。 + The project's language version has to be at least 'C# 12'. + 專案的語言版本必須至少為 'C# 11'。 - Language version is required to be at least C# 11 - 語言版本要求至少為 C#11 + Language version is required to be at least C# 12 + 語言版本要求至少為 C#11 diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs index 4f16767be2f50..ccf936978c792 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs @@ -4,11 +4,9 @@ using System; using System.Collections.Generic; using System.ComponentModel; -using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; -using System.Text; #if BUILDING_SOURCE_GENERATOR_TESTS using Microsoft.Extensions.Configuration; #endif @@ -2037,6 +2035,7 @@ public void ComplexObj_As_Enumerable_Element() ValidateGeolocation(obj); } +#if !BUILDING_SOURCE_GENERATOR_TESTS [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] public void TraceSwitchTest() { @@ -2048,7 +2047,6 @@ public void TraceSwitchTest() configurationBuilder.AddInMemoryCollection(dic); var config = configurationBuilder.Build(); -#if !BUILDING_SOURCE_GENERATOR_TESTS TraceSwitch ts = new(displayName: "TraceSwitch", description: "This switch is set via config."); ConfigurationBinder.Bind(config, "TraceSwitch", ts); Assert.Equal(TraceLevel.Info, ts.Level); @@ -2056,8 +2054,8 @@ public void TraceSwitchTest() // Value property is not publicly exposed in .NET Framework. Assert.Equal("Info", ts.Value); #endif // NETCOREAPP -#endif // !BUILDING_SOURCE_GENERATOR_TESTS } +#endif private void ValidateGeolocation(IGeolocation location) { diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/Collections.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/Collections.generated.txt index 52d0cd6a593ab..528847cfeb3a3 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/Collections.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/Collections.generated.txt @@ -60,92 +60,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return obj; } - throw new NotSupportedException($"Unable to bind to type ''{type}'': generator did not detect the type as input."); - } - - public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) - { - if (configuration is null) - { - throw new ArgumentNullException(nameof(configuration)); - } - - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - - BinderOptions? binderOptions = GetBinderOptions(configureOptions); - - if (!HasValueOrChildren(configuration)) - { - return; - } - - if (obj is Program.CustomDictionary typedObj1) - { - BindCore(configuration, ref typedObj1, binderOptions); - return; - } - - if (obj is Program.CustomList typedObj3) - { - BindCore(configuration, ref typedObj3, binderOptions); - return; - } - - if (obj is List typedObj5) - { - BindCore(configuration, ref typedObj5, binderOptions); - return; - } - - if (obj is ICollection typedObj7) - { - BindCore(configuration, ref typedObj7, binderOptions); - return; - } - - if (obj is IReadOnlyList typedObj9) - { - BindCore(configuration, ref typedObj9, binderOptions); - return; - } - - if (obj is Dictionary typedObj11) - { - BindCore(configuration, ref typedObj11, binderOptions); - return; - } - - if (obj is IDictionary typedObj13) - { - BindCore(configuration, ref typedObj13, binderOptions); - return; - } - - if (obj is IReadOnlyDictionary typedObj15) - { - BindCore(configuration, ref typedObj15, binderOptions); - return; - } - - if (obj is Program.MyClassWithCustomCollections typedObj17) - { - BindCore(configuration, ref typedObj17, binderOptions); - return; - } - - throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); + throw new NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input."); } public static void BindCore(IConfiguration configuration, ref Program.CustomDictionary obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -157,11 +76,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Program.CustomList obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -173,11 +87,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -189,11 +98,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref ICollection obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -205,11 +109,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref IReadOnlyList obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - if (obj is not ICollection temp) { return; @@ -226,11 +125,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Dictionary obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -242,11 +136,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref IDictionary obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -258,11 +147,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref IReadOnlyDictionary obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - if (obj is not IDictionary temp) { return; @@ -279,46 +163,42 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Program.MyClassWithCustomCollections obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - ValidateConfigurationKeys(typeof(Program.MyClassWithCustomCollections), s_configKeys_ProgramMyClassWithCustomCollections, configuration, binderOptions); - if (AsConfigWithChildren(configuration.GetSection("CustomDictionary")) is IConfigurationSection section19) + if (AsConfigWithChildren(configuration.GetSection("CustomDictionary")) is IConfigurationSection section1) { - Program.CustomDictionary? temp21 = obj.CustomDictionary; - temp21 ??= new Program.CustomDictionary(); - BindCore(section19, ref temp21, binderOptions); - obj.CustomDictionary = temp21; + Program.CustomDictionary? temp3 = obj.CustomDictionary; + temp3 ??= new Program.CustomDictionary(); + BindCore(section1, ref temp3, binderOptions); + obj.CustomDictionary = temp3; } - if (AsConfigWithChildren(configuration.GetSection("CustomList")) is IConfigurationSection section22) + if (AsConfigWithChildren(configuration.GetSection("CustomList")) is IConfigurationSection section4) { - Program.CustomList? temp24 = obj.CustomList; - temp24 ??= new Program.CustomList(); - BindCore(section22, ref temp24, binderOptions); - obj.CustomList = temp24; + Program.CustomList? temp6 = obj.CustomList; + temp6 ??= new Program.CustomList(); + BindCore(section4, ref temp6, binderOptions); + obj.CustomList = temp6; } - if (AsConfigWithChildren(configuration.GetSection("IReadOnlyList")) is IConfigurationSection section25) + if (AsConfigWithChildren(configuration.GetSection("IReadOnlyList")) is IConfigurationSection section7) { - IReadOnlyList? temp27 = obj.IReadOnlyList; - temp27 = temp27 is null ? new List() : new List(temp27); - BindCore(section25, ref temp27, binderOptions); - obj.IReadOnlyList = temp27; + IReadOnlyList? temp9 = obj.IReadOnlyList; + temp9 = temp9 is null ? new List() : new List(temp9); + BindCore(section7, ref temp9, binderOptions); + obj.IReadOnlyList = temp9; } - if (AsConfigWithChildren(configuration.GetSection("IReadOnlyDictionary")) is IConfigurationSection section28) + if (AsConfigWithChildren(configuration.GetSection("IReadOnlyDictionary")) is IConfigurationSection section10) { - IReadOnlyDictionary? temp30 = obj.IReadOnlyDictionary; - temp30 = temp30 is null ? new Dictionary() : temp30.ToDictionary(pair => pair.Key, pair => pair.Value); - BindCore(section28, ref temp30, binderOptions); - obj.IReadOnlyDictionary = temp30; + IReadOnlyDictionary? temp12 = obj.IReadOnlyDictionary; + temp12 = temp12 is null ? new Dictionary() : temp12.ToDictionary(pair => pair.Key, pair => pair.Value); + BindCore(section10, ref temp12, binderOptions); + obj.IReadOnlyDictionary = temp12; } } + /// If required by the binder options, validates that there are no unknown keys in the input configuration object. public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions) { diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind.generated.txt index 35187f8c8d65b..36ac12fd31f83 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind.generated.txt @@ -32,21 +32,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration #region IConfiguration extensions. /// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively. [InterceptsLocationAttribute(@"src-0.cs", 13, 18)] - public static void Bind(this IConfiguration configuration, object? obj) => BindCoreMain(configuration, obj, configureOptions: null); - - /// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively. - [InterceptsLocationAttribute(@"src-0.cs", 14, 24)] - public static void Bind(this IConfiguration configuration, object? obj, Action? configureOptions) => BindCoreMain(configuration, obj, configureOptions); - - /// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively. - [InterceptsLocationAttribute(@"src-0.cs", 15, 24)] - public static void Bind(this IConfiguration configuration, string key, object? obj) => BindCoreMain(configuration?.GetSection(key), obj, configureOptions: null); - #endregion IConfiguration extensions. - - #region Core binding extensions. - private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyDictionary", "MyComplexDictionary" }); - - public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) + public static void Bind_ProgramMyClass(this IConfiguration configuration, object? obj) { if (configuration is null) { @@ -58,47 +44,52 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new ArgumentNullException(nameof(obj)); } - BinderOptions? binderOptions = GetBinderOptions(configureOptions); + var typedObj = (Program.MyClass)obj; + BindCore(configuration, ref typedObj, binderOptions: null); + } - if (!HasValueOrChildren(configuration)) + /// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively. + [InterceptsLocationAttribute(@"src-0.cs", 14, 24)] + public static void Bind_ProgramMyClass(this IConfiguration configuration, object? obj, Action? configureOptions) + { + if (configuration is null) { - return; + throw new ArgumentNullException(nameof(configuration)); } - if (obj is List typedObj0) + if (obj is null) { - BindCore(configuration, ref typedObj0, binderOptions); - return; + throw new ArgumentNullException(nameof(obj)); } - if (obj is Dictionary typedObj2) - { - BindCore(configuration, ref typedObj2, binderOptions); - return; - } + var typedObj = (Program.MyClass)obj; + BindCore(configuration, ref typedObj, GetBinderOptions(configureOptions)); + } - if (obj is Dictionary typedObj4) + /// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively. + [InterceptsLocationAttribute(@"src-0.cs", 15, 24)] + public static void Bind_ProgramMyClass(this IConfiguration configuration, string key, object? obj) + { + if (configuration is null) { - BindCore(configuration, ref typedObj4, binderOptions); - return; + throw new ArgumentNullException(nameof(configuration)); } - if (obj is Program.MyClass typedObj6) + if (obj is null) { - BindCore(configuration, ref typedObj6, binderOptions); - return; + throw new ArgumentNullException(nameof(obj)); } - throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); + var typedObj = (Program.MyClass)obj; + BindCore(configuration.GetSection(key), ref typedObj, binderOptions: null); } + #endregion IConfiguration extensions. + + #region Core binding extensions. + private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyDictionary", "MyComplexDictionary" }); public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -110,11 +101,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Dictionary obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -126,11 +112,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Dictionary obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (!(obj.TryGetValue(section.Key, out Program.MyClass2? element) && element is not null)) @@ -143,45 +124,41 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); obj.MyString = configuration["MyString"]!; - if (configuration["MyInt"] is string value9) + if (configuration["MyInt"] is string value1) { - obj.MyInt = ParseInt(value9, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path); } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section10) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2) { - List? temp12 = obj.MyList; - temp12 ??= new List(); - BindCore(section10, ref temp12, binderOptions); - obj.MyList = temp12; + List? temp4 = obj.MyList; + temp4 ??= new List(); + BindCore(section2, ref temp4, binderOptions); + obj.MyList = temp4; } - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section13) + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section5) { - Dictionary? temp15 = obj.MyDictionary; - temp15 ??= new Dictionary(); - BindCore(section13, ref temp15, binderOptions); - obj.MyDictionary = temp15; + Dictionary? temp7 = obj.MyDictionary; + temp7 ??= new Dictionary(); + BindCore(section5, ref temp7, binderOptions); + obj.MyDictionary = temp7; } - if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section16) + if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section8) { - Dictionary? temp18 = obj.MyComplexDictionary; - temp18 ??= new Dictionary(); - BindCore(section16, ref temp18, binderOptions); - obj.MyComplexDictionary = temp18; + Dictionary? temp10 = obj.MyComplexDictionary; + temp10 ??= new Dictionary(); + BindCore(section8, ref temp10, binderOptions); + obj.MyComplexDictionary = temp10; } } + /// If required by the binder options, validates that there are no unknown keys in the input configuration object. public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions) { @@ -204,15 +181,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } } - public static bool HasValueOrChildren(IConfiguration configuration) - { - if ((configuration as IConfigurationSection)?.Value is not null) - { - return true; - } - return AsConfigWithChildren(configuration) is not null; - } - public static IConfiguration? AsConfigWithChildren(IConfiguration configuration) { foreach (IConfigurationSection _ in configuration.GetChildren()) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance.generated.txt index e1946bd7caddd..02fb06957bb3d 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance.generated.txt @@ -32,13 +32,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration #region IConfiguration extensions. /// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively. [InterceptsLocationAttribute(@"src-0.cs", 12, 20)] - public static void Bind(this IConfiguration configuration, object? obj) => BindCoreMain(configuration, obj, configureOptions: null); - #endregion IConfiguration extensions. - - #region Core binding extensions. - private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyDictionary", "MyComplexDictionary" }); - - public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) + public static void Bind_ProgramMyClass(this IConfiguration configuration, object? obj) { if (configuration is null) { @@ -50,47 +44,16 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new ArgumentNullException(nameof(obj)); } - BinderOptions? binderOptions = GetBinderOptions(configureOptions); - - if (!HasValueOrChildren(configuration)) - { - return; - } - - if (obj is List typedObj0) - { - BindCore(configuration, ref typedObj0, binderOptions); - return; - } - - if (obj is Dictionary typedObj2) - { - BindCore(configuration, ref typedObj2, binderOptions); - return; - } - - if (obj is Dictionary typedObj4) - { - BindCore(configuration, ref typedObj4, binderOptions); - return; - } - - if (obj is Program.MyClass typedObj6) - { - BindCore(configuration, ref typedObj6, binderOptions); - return; - } - - throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); + var typedObj = (Program.MyClass)obj; + BindCore(configuration, ref typedObj, binderOptions: null); } + #endregion IConfiguration extensions. + + #region Core binding extensions. + private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyDictionary", "MyComplexDictionary" }); public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -102,11 +65,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Dictionary obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -118,11 +76,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Dictionary obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (!(obj.TryGetValue(section.Key, out Program.MyClass2? element) && element is not null)) @@ -135,45 +88,41 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); obj.MyString = configuration["MyString"]!; - if (configuration["MyInt"] is string value9) + if (configuration["MyInt"] is string value1) { - obj.MyInt = ParseInt(value9, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path); } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section10) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2) { - List? temp12 = obj.MyList; - temp12 ??= new List(); - BindCore(section10, ref temp12, binderOptions); - obj.MyList = temp12; + List? temp4 = obj.MyList; + temp4 ??= new List(); + BindCore(section2, ref temp4, binderOptions); + obj.MyList = temp4; } - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section13) + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section5) { - Dictionary? temp15 = obj.MyDictionary; - temp15 ??= new Dictionary(); - BindCore(section13, ref temp15, binderOptions); - obj.MyDictionary = temp15; + Dictionary? temp7 = obj.MyDictionary; + temp7 ??= new Dictionary(); + BindCore(section5, ref temp7, binderOptions); + obj.MyDictionary = temp7; } - if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section16) + if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section8) { - Dictionary? temp18 = obj.MyComplexDictionary; - temp18 ??= new Dictionary(); - BindCore(section16, ref temp18, binderOptions); - obj.MyComplexDictionary = temp18; + Dictionary? temp10 = obj.MyComplexDictionary; + temp10 ??= new Dictionary(); + BindCore(section8, ref temp10, binderOptions); + obj.MyComplexDictionary = temp10; } } + /// If required by the binder options, validates that there are no unknown keys in the input configuration object. public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions) { @@ -196,15 +145,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } } - public static bool HasValueOrChildren(IConfiguration configuration) - { - if ((configuration as IConfigurationSection)?.Value is not null) - { - return true; - } - return AsConfigWithChildren(configuration) is not null; - } - public static IConfiguration? AsConfigWithChildren(IConfiguration configuration) { foreach (IConfigurationSection _ in configuration.GetChildren()) @@ -214,24 +154,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return null; } - public static BinderOptions? GetBinderOptions(Action? configureOptions) - { - if (configureOptions is null) - { - return null; - } - - BinderOptions binderOptions = new(); - configureOptions(binderOptions); - - if (binderOptions.BindNonPublicProperties) - { - throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); - } - - return binderOptions; - } - public static int ParseInt(string value, Func getPath) { try diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance_BinderOptions.generated.txt index 9d099198acdc0..4703980996b88 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance_BinderOptions.generated.txt @@ -32,13 +32,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration #region IConfiguration extensions. /// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively. [InterceptsLocationAttribute(@"src-0.cs", 12, 20)] - public static void Bind(this IConfiguration configuration, object? obj, Action? configureOptions) => BindCoreMain(configuration, obj, configureOptions); - #endregion IConfiguration extensions. - - #region Core binding extensions. - private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyDictionary", "MyComplexDictionary" }); - - public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) + public static void Bind_ProgramMyClass(this IConfiguration configuration, object? obj, Action? configureOptions) { if (configuration is null) { @@ -50,47 +44,16 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new ArgumentNullException(nameof(obj)); } - BinderOptions? binderOptions = GetBinderOptions(configureOptions); - - if (!HasValueOrChildren(configuration)) - { - return; - } - - if (obj is List typedObj0) - { - BindCore(configuration, ref typedObj0, binderOptions); - return; - } - - if (obj is Dictionary typedObj2) - { - BindCore(configuration, ref typedObj2, binderOptions); - return; - } - - if (obj is Dictionary typedObj4) - { - BindCore(configuration, ref typedObj4, binderOptions); - return; - } - - if (obj is Program.MyClass typedObj6) - { - BindCore(configuration, ref typedObj6, binderOptions); - return; - } - - throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); + var typedObj = (Program.MyClass)obj; + BindCore(configuration, ref typedObj, GetBinderOptions(configureOptions)); } + #endregion IConfiguration extensions. + + #region Core binding extensions. + private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyDictionary", "MyComplexDictionary" }); public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -102,11 +65,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Dictionary obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -118,11 +76,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Dictionary obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (!(obj.TryGetValue(section.Key, out Program.MyClass2? element) && element is not null)) @@ -135,45 +88,41 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); obj.MyString = configuration["MyString"]!; - if (configuration["MyInt"] is string value9) + if (configuration["MyInt"] is string value1) { - obj.MyInt = ParseInt(value9, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path); } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section10) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2) { - List? temp12 = obj.MyList; - temp12 ??= new List(); - BindCore(section10, ref temp12, binderOptions); - obj.MyList = temp12; + List? temp4 = obj.MyList; + temp4 ??= new List(); + BindCore(section2, ref temp4, binderOptions); + obj.MyList = temp4; } - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section13) + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section5) { - Dictionary? temp15 = obj.MyDictionary; - temp15 ??= new Dictionary(); - BindCore(section13, ref temp15, binderOptions); - obj.MyDictionary = temp15; + Dictionary? temp7 = obj.MyDictionary; + temp7 ??= new Dictionary(); + BindCore(section5, ref temp7, binderOptions); + obj.MyDictionary = temp7; } - if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section16) + if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section8) { - Dictionary? temp18 = obj.MyComplexDictionary; - temp18 ??= new Dictionary(); - BindCore(section16, ref temp18, binderOptions); - obj.MyComplexDictionary = temp18; + Dictionary? temp10 = obj.MyComplexDictionary; + temp10 ??= new Dictionary(); + BindCore(section8, ref temp10, binderOptions); + obj.MyComplexDictionary = temp10; } } + /// If required by the binder options, validates that there are no unknown keys in the input configuration object. public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions) { @@ -196,15 +145,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } } - public static bool HasValueOrChildren(IConfiguration configuration) - { - if ((configuration as IConfigurationSection)?.Value is not null) - { - return true; - } - return AsConfigWithChildren(configuration) is not null; - } - public static IConfiguration? AsConfigWithChildren(IConfiguration configuration) { foreach (IConfigurationSection _ in configuration.GetChildren()) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Key_Instance.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Key_Instance.generated.txt index 74af70d28e767..9937129699716 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Key_Instance.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Key_Instance.generated.txt @@ -32,13 +32,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration #region IConfiguration extensions. /// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively. [InterceptsLocationAttribute(@"src-0.cs", 12, 20)] - public static void Bind(this IConfiguration configuration, string key, object? obj) => BindCoreMain(configuration?.GetSection(key), obj, configureOptions: null); - #endregion IConfiguration extensions. - - #region Core binding extensions. - private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyDictionary", "MyComplexDictionary" }); - - public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) + public static void Bind_ProgramMyClass(this IConfiguration configuration, string key, object? obj) { if (configuration is null) { @@ -50,47 +44,16 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new ArgumentNullException(nameof(obj)); } - BinderOptions? binderOptions = GetBinderOptions(configureOptions); - - if (!HasValueOrChildren(configuration)) - { - return; - } - - if (obj is List typedObj0) - { - BindCore(configuration, ref typedObj0, binderOptions); - return; - } - - if (obj is Dictionary typedObj2) - { - BindCore(configuration, ref typedObj2, binderOptions); - return; - } - - if (obj is Dictionary typedObj4) - { - BindCore(configuration, ref typedObj4, binderOptions); - return; - } - - if (obj is Program.MyClass typedObj6) - { - BindCore(configuration, ref typedObj6, binderOptions); - return; - } - - throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); + var typedObj = (Program.MyClass)obj; + BindCore(configuration.GetSection(key), ref typedObj, binderOptions: null); } + #endregion IConfiguration extensions. + + #region Core binding extensions. + private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyDictionary", "MyComplexDictionary" }); public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -102,11 +65,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Dictionary obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -118,11 +76,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Dictionary obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (!(obj.TryGetValue(section.Key, out Program.MyClass2? element) && element is not null)) @@ -135,45 +88,41 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); obj.MyString = configuration["MyString"]!; - if (configuration["MyInt"] is string value9) + if (configuration["MyInt"] is string value1) { - obj.MyInt = ParseInt(value9, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path); } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section10) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2) { - List? temp12 = obj.MyList; - temp12 ??= new List(); - BindCore(section10, ref temp12, binderOptions); - obj.MyList = temp12; + List? temp4 = obj.MyList; + temp4 ??= new List(); + BindCore(section2, ref temp4, binderOptions); + obj.MyList = temp4; } - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section13) + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section5) { - Dictionary? temp15 = obj.MyDictionary; - temp15 ??= new Dictionary(); - BindCore(section13, ref temp15, binderOptions); - obj.MyDictionary = temp15; + Dictionary? temp7 = obj.MyDictionary; + temp7 ??= new Dictionary(); + BindCore(section5, ref temp7, binderOptions); + obj.MyDictionary = temp7; } - if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section16) + if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section8) { - Dictionary? temp18 = obj.MyComplexDictionary; - temp18 ??= new Dictionary(); - BindCore(section16, ref temp18, binderOptions); - obj.MyComplexDictionary = temp18; + Dictionary? temp10 = obj.MyComplexDictionary; + temp10 ??= new Dictionary(); + BindCore(section8, ref temp10, binderOptions); + obj.MyComplexDictionary = temp10; } } + /// If required by the binder options, validates that there are no unknown keys in the input configuration object. public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions) { @@ -196,15 +145,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } } - public static bool HasValueOrChildren(IConfiguration configuration) - { - if ((configuration as IConfigurationSection)?.Value is not null) - { - return true; - } - return AsConfigWithChildren(configuration) is not null; - } - public static IConfiguration? AsConfigWithChildren(IConfiguration configuration) { foreach (IConfigurationSection _ in configuration.GetChildren()) @@ -214,24 +154,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return null; } - public static BinderOptions? GetBinderOptions(Action? configureOptions) - { - if (configureOptions is null) - { - return null; - } - - BinderOptions binderOptions = new(); - configureOptions(binderOptions); - - if (binderOptions.BindNonPublicProperties) - { - throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); - } - - return binderOptions; - } - public static int ParseInt(string value, Func getPath) { try diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get.generated.txt index 5b512b6414f94..3e6ce1459b289 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get.generated.txt @@ -71,76 +71,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration BindCore(configuration, ref obj, binderOptions); return obj; } - - if (type == typeof(Program.MyClass2)) + else if (type == typeof(Program.MyClass2)) { var obj = new Program.MyClass2(); BindCore(configuration, ref obj, binderOptions); return obj; } - throw new NotSupportedException($"Unable to bind to type ''{type}'': generator did not detect the type as input."); - } - - public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) - { - if (configuration is null) - { - throw new ArgumentNullException(nameof(configuration)); - } - - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - - BinderOptions? binderOptions = GetBinderOptions(configureOptions); - - if (!HasValueOrChildren(configuration)) - { - return; - } - - if (obj is List typedObj2) - { - BindCore(configuration, ref typedObj2, binderOptions); - return; - } - - if (obj is int[] typedObj4) - { - BindCore(configuration, ref typedObj4, binderOptions); - return; - } - - if (obj is Dictionary typedObj6) - { - BindCore(configuration, ref typedObj6, binderOptions); - return; - } - - if (obj is Program.MyClass typedObj8) - { - BindCore(configuration, ref typedObj8, binderOptions); - return; - } - - if (obj is Program.MyClass2 typedObj10) - { - BindCore(configuration, ref typedObj10, binderOptions); - return; - } - - throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); + throw new NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input."); } public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -152,25 +94,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref int[] obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - - var temp12 = new List(); - BindCore(configuration, ref temp12, binderOptions); + var temp2 = new List(); + BindCore(configuration, ref temp2, binderOptions); int originalCount = obj.Length; - Array.Resize(ref obj, originalCount + temp12.Count); - temp12.CopyTo(obj, originalCount); + Array.Resize(ref obj, originalCount + temp2.Count); + temp2.CopyTo(obj, originalCount); } public static void BindCore(IConfiguration configuration, ref Dictionary obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -182,60 +114,51 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); obj.MyString = configuration["MyString"]!; - if (configuration["MyInt"] is string value15) + if (configuration["MyInt"] is string value5) { - obj.MyInt = ParseInt(value15, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value5, () => configuration.GetSection("MyInt").Path); } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section16) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section6) { - List? temp18 = obj.MyList; - temp18 ??= new List(); - BindCore(section16, ref temp18, binderOptions); - obj.MyList = temp18; + List? temp8 = obj.MyList; + temp8 ??= new List(); + BindCore(section6, ref temp8, binderOptions); + obj.MyList = temp8; } - if (AsConfigWithChildren(configuration.GetSection("MyArray")) is IConfigurationSection section19) + if (AsConfigWithChildren(configuration.GetSection("MyArray")) is IConfigurationSection section9) { - int[]? temp21 = obj.MyArray; - temp21 ??= new int[0]; - BindCore(section19, ref temp21, binderOptions); - obj.MyArray = temp21; + int[]? temp11 = obj.MyArray; + temp11 ??= new int[0]; + BindCore(section9, ref temp11, binderOptions); + obj.MyArray = temp11; } - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section22) + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section12) { - Dictionary? temp24 = obj.MyDictionary; - temp24 ??= new Dictionary(); - BindCore(section22, ref temp24, binderOptions); - obj.MyDictionary = temp24; + Dictionary? temp14 = obj.MyDictionary; + temp14 ??= new Dictionary(); + BindCore(section12, ref temp14, binderOptions); + obj.MyDictionary = temp14; } } public static void BindCore(IConfiguration configuration, ref Program.MyClass2 obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions); - if (configuration["MyInt"] is string value25) + if (configuration["MyInt"] is string value15) { - obj.MyInt = ParseInt(value25, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value15, () => configuration.GetSection("MyInt").Path); } } + /// If required by the binder options, validates that there are no unknown keys in the input configuration object. public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions) { diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue.generated.txt index 1cf54cb905bb5..e4bcaf6a9b7c9 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/GetValue.generated.txt @@ -65,18 +65,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { return ParseInt(value, () => section.Path); } - - if (type == typeof(bool?)) + else if (type == typeof(bool?)) { return ParseBool(value, () => section.Path); } - - if (type == typeof(byte[])) + else if (type == typeof(byte[])) { return ParseByteArray(value, () => section.Path); } - - if (type == typeof(CultureInfo)) + else if (type == typeof(CultureInfo)) { return ParseCultureInfo(value, () => section.Path); } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T.generated.txt index ebf58aaae2040..85d0901de3ff6 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T.generated.txt @@ -59,62 +59,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return obj; } - throw new NotSupportedException($"Unable to bind to type ''{type}'': generator did not detect the type as input."); - } - - public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) - { - if (configuration is null) - { - throw new ArgumentNullException(nameof(configuration)); - } - - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - - BinderOptions? binderOptions = GetBinderOptions(configureOptions); - - if (!HasValueOrChildren(configuration)) - { - return; - } - - if (obj is List typedObj1) - { - BindCore(configuration, ref typedObj1, binderOptions); - return; - } - - if (obj is int[] typedObj3) - { - BindCore(configuration, ref typedObj3, binderOptions); - return; - } - - if (obj is Dictionary typedObj5) - { - BindCore(configuration, ref typedObj5, binderOptions); - return; - } - - if (obj is Program.MyClass typedObj7) - { - BindCore(configuration, ref typedObj7, binderOptions); - return; - } - - throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); + throw new NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input."); } public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -126,25 +75,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref int[] obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - - var temp9 = new List(); - BindCore(configuration, ref temp9, binderOptions); + var temp1 = new List(); + BindCore(configuration, ref temp1, binderOptions); int originalCount = obj.Length; - Array.Resize(ref obj, originalCount + temp9.Count); - temp9.CopyTo(obj, originalCount); + Array.Resize(ref obj, originalCount + temp1.Count); + temp1.CopyTo(obj, originalCount); } public static void BindCore(IConfiguration configuration, ref Dictionary obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -156,45 +95,41 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); obj.MyString = configuration["MyString"]!; - if (configuration["MyInt"] is string value12) + if (configuration["MyInt"] is string value4) { - obj.MyInt = ParseInt(value12, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value4, () => configuration.GetSection("MyInt").Path); } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section13) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) { - List? temp15 = obj.MyList; - temp15 ??= new List(); - BindCore(section13, ref temp15, binderOptions); - obj.MyList = temp15; + List? temp7 = obj.MyList; + temp7 ??= new List(); + BindCore(section5, ref temp7, binderOptions); + obj.MyList = temp7; } - if (AsConfigWithChildren(configuration.GetSection("MyArray")) is IConfigurationSection section16) + if (AsConfigWithChildren(configuration.GetSection("MyArray")) is IConfigurationSection section8) { - int[]? temp18 = obj.MyArray; - temp18 ??= new int[0]; - BindCore(section16, ref temp18, binderOptions); - obj.MyArray = temp18; + int[]? temp10 = obj.MyArray; + temp10 ??= new int[0]; + BindCore(section8, ref temp10, binderOptions); + obj.MyArray = temp10; } - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section19) + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section11) { - Dictionary? temp21 = obj.MyDictionary; - temp21 ??= new Dictionary(); - BindCore(section19, ref temp21, binderOptions); - obj.MyDictionary = temp21; + Dictionary? temp13 = obj.MyDictionary; + temp13 ??= new Dictionary(); + BindCore(section11, ref temp13, binderOptions); + obj.MyDictionary = temp13; } } + /// If required by the binder options, validates that there are no unknown keys in the input configuration object. public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions) { diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T_BinderOptions.generated.txt index a203fcc55ebcc..d394cc7b269f7 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T_BinderOptions.generated.txt @@ -59,62 +59,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return obj; } - throw new NotSupportedException($"Unable to bind to type ''{type}'': generator did not detect the type as input."); - } - - public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) - { - if (configuration is null) - { - throw new ArgumentNullException(nameof(configuration)); - } - - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - - BinderOptions? binderOptions = GetBinderOptions(configureOptions); - - if (!HasValueOrChildren(configuration)) - { - return; - } - - if (obj is List typedObj1) - { - BindCore(configuration, ref typedObj1, binderOptions); - return; - } - - if (obj is int[] typedObj3) - { - BindCore(configuration, ref typedObj3, binderOptions); - return; - } - - if (obj is Dictionary typedObj5) - { - BindCore(configuration, ref typedObj5, binderOptions); - return; - } - - if (obj is Program.MyClass typedObj7) - { - BindCore(configuration, ref typedObj7, binderOptions); - return; - } - - throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); + throw new NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input."); } public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -126,25 +75,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref int[] obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - - var temp9 = new List(); - BindCore(configuration, ref temp9, binderOptions); + var temp1 = new List(); + BindCore(configuration, ref temp1, binderOptions); int originalCount = obj.Length; - Array.Resize(ref obj, originalCount + temp9.Count); - temp9.CopyTo(obj, originalCount); + Array.Resize(ref obj, originalCount + temp1.Count); + temp1.CopyTo(obj, originalCount); } public static void BindCore(IConfiguration configuration, ref Dictionary obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -156,45 +95,41 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); obj.MyString = configuration["MyString"]!; - if (configuration["MyInt"] is string value12) + if (configuration["MyInt"] is string value4) { - obj.MyInt = ParseInt(value12, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value4, () => configuration.GetSection("MyInt").Path); } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section13) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) { - List? temp15 = obj.MyList; - temp15 ??= new List(); - BindCore(section13, ref temp15, binderOptions); - obj.MyList = temp15; + List? temp7 = obj.MyList; + temp7 ??= new List(); + BindCore(section5, ref temp7, binderOptions); + obj.MyList = temp7; } - if (AsConfigWithChildren(configuration.GetSection("MyArray")) is IConfigurationSection section16) + if (AsConfigWithChildren(configuration.GetSection("MyArray")) is IConfigurationSection section8) { - int[]? temp18 = obj.MyArray; - temp18 ??= new int[0]; - BindCore(section16, ref temp18, binderOptions); - obj.MyArray = temp18; + int[]? temp10 = obj.MyArray; + temp10 ??= new int[0]; + BindCore(section8, ref temp10, binderOptions); + obj.MyArray = temp10; } - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section19) + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section11) { - Dictionary? temp21 = obj.MyDictionary; - temp21 ??= new Dictionary(); - BindCore(section19, ref temp21, binderOptions); - obj.MyDictionary = temp21; + Dictionary? temp13 = obj.MyDictionary; + temp13 ??= new Dictionary(); + BindCore(section11, ref temp13, binderOptions); + obj.MyDictionary = temp13; } } + /// If required by the binder options, validates that there are no unknown keys in the input configuration object. public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions) { diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_TypeOf.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_TypeOf.generated.txt index f0bb0023fe98a..83cd88561310a 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_TypeOf.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_TypeOf.generated.txt @@ -59,52 +59,20 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return obj; } - throw new NotSupportedException($"Unable to bind to type ''{type}'': generator did not detect the type as input."); - } - - public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) - { - if (configuration is null) - { - throw new ArgumentNullException(nameof(configuration)); - } - - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - - BinderOptions? binderOptions = GetBinderOptions(configureOptions); - - if (!HasValueOrChildren(configuration)) - { - return; - } - - if (obj is Program.MyClass2 typedObj1) - { - BindCore(configuration, ref typedObj1, binderOptions); - return; - } - - throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); + throw new NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input."); } public static void BindCore(IConfiguration configuration, ref Program.MyClass2 obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions); - if (configuration["MyInt"] is string value3) + if (configuration["MyInt"] is string value1) { - obj.MyInt = ParseInt(value3, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path); } } + /// If required by the binder options, validates that there are no unknown keys in the input configuration object. public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions) { diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_TypeOf_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_TypeOf_BinderOptions.generated.txt index 5425abdfc1813..91714c80cd013 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_TypeOf_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_TypeOf_BinderOptions.generated.txt @@ -59,52 +59,20 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return obj; } - throw new NotSupportedException($"Unable to bind to type ''{type}'': generator did not detect the type as input."); - } - - public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) - { - if (configuration is null) - { - throw new ArgumentNullException(nameof(configuration)); - } - - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - - BinderOptions? binderOptions = GetBinderOptions(configureOptions); - - if (!HasValueOrChildren(configuration)) - { - return; - } - - if (obj is Program.MyClass2 typedObj1) - { - BindCore(configuration, ref typedObj1, binderOptions); - return; - } - - throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); + throw new NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input."); } public static void BindCore(IConfiguration configuration, ref Program.MyClass2 obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions); - if (configuration["MyInt"] is string value3) + if (configuration["MyInt"] is string value1) { - obj.MyInt = ParseInt(value3, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path); } } + /// If required by the binder options, validates that there are no unknown keys in the input configuration object. public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions) { diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/BindConfiguration.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/BindConfiguration.generated.txt index 99266d10a4370..88b35037c22c3 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/BindConfiguration.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/BindConfiguration.generated.txt @@ -48,8 +48,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration optionsBuilder.Configure((obj, configuration) => { + if (obj is null) + { + throw new ArgumentNullException(nameof(obj)); + } + + if (configuration is null) + { + throw new ArgumentNullException(nameof(configuration)); + } + IConfiguration section = string.Equals(string.Empty, configSectionPath, StringComparison.OrdinalIgnoreCase) ? configuration : configuration.GetSection(configSectionPath); - BindCoreMain(section, obj, configureOptions); + BindCoreMain(section, obj, typeof(TOptions), configureOptions); }); optionsBuilder.Services.AddSingleton, ConfigurationChangeTokenSource>(); @@ -60,7 +70,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration #region Core binding extensions. private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList" }); - public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) + public static void BindCoreMain(IConfiguration configuration, object obj, Type type, Action? configureOptions) { if (configuration is null) { @@ -72,35 +82,25 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new ArgumentNullException(nameof(obj)); } - BinderOptions? binderOptions = GetBinderOptions(configureOptions); - if (!HasValueOrChildren(configuration)) { return; } - if (obj is List typedObj0) - { - BindCore(configuration, ref typedObj0, binderOptions); - return; - } + BinderOptions? binderOptions = GetBinderOptions(configureOptions); - if (obj is Program.MyClass typedObj2) + if (type == typeof(Program.MyClass)) { - BindCore(configuration, ref typedObj2, binderOptions); + var temp = (Program.MyClass)obj; + BindCore(configuration, ref temp, binderOptions); return; } - throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); + throw new NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input."); } public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -112,29 +112,25 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); obj.MyString = configuration["MyString"]!; - if (configuration["MyInt"] is string value5) + if (configuration["MyInt"] is string value2) { - obj.MyInt = ParseInt(value5, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value2, () => configuration.GetSection("MyInt").Path); } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section6) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section3) { - List? temp8 = obj.MyList; - temp8 ??= new List(); - BindCore(section6, ref temp8, binderOptions); - obj.MyList = temp8; + List? temp5 = obj.MyList; + temp5 ??= new List(); + BindCore(section3, ref temp5, binderOptions); + obj.MyList = temp5; } } + /// If required by the binder options, validates that there are no unknown keys in the input configuration object. public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions) { diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/Bind_T.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/Bind_T.generated.txt index 69f3645910fcd..633196e7a742d 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/Bind_T.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/Bind_T.generated.txt @@ -68,14 +68,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration OptionsServiceCollectionExtensions.AddOptions(services); services.AddSingleton>(new ConfigurationChangeTokenSource(name, configuration)); - return services.AddSingleton>(new Microsoft.Extensions.Options.ConfigureNamedOptions(name, obj => BindCoreMain(configuration, obj, configureOptions))); + return services.AddSingleton>(new Microsoft.Extensions.Options.ConfigureNamedOptions(name, obj => BindCoreMain(configuration, obj, typeof(TOptions)configureOptions))); } #endregion IServiceCollection extensions. #region Core binding extensions. private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList" }); - public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) + public static void BindCoreMain(IConfiguration configuration, object obj, Type type, Action? configureOptions) { if (configuration is null) { @@ -87,35 +87,25 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new ArgumentNullException(nameof(obj)); } - BinderOptions? binderOptions = GetBinderOptions(configureOptions); - if (!HasValueOrChildren(configuration)) { return; } - if (obj is List typedObj0) - { - BindCore(configuration, ref typedObj0, binderOptions); - return; - } + BinderOptions? binderOptions = GetBinderOptions(configureOptions); - if (obj is Program.MyClass typedObj2) + if (type == typeof(Program.MyClass)) { - BindCore(configuration, ref typedObj2, binderOptions); + var temp = (Program.MyClass)obj; + BindCore(configuration, ref temp, binderOptions); return; } - throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); + throw new NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input."); } public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -127,29 +117,25 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); obj.MyString = configuration["MyString"]!; - if (configuration["MyInt"] is string value5) + if (configuration["MyInt"] is string value2) { - obj.MyInt = ParseInt(value5, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value2, () => configuration.GetSection("MyInt").Path); } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section6) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section3) { - List? temp8 = obj.MyList; - temp8 ??= new List(); - BindCore(section6, ref temp8, binderOptions); - obj.MyList = temp8; + List? temp5 = obj.MyList; + temp5 ??= new List(); + BindCore(section3, ref temp5, binderOptions); + obj.MyList = temp5; } } + /// If required by the binder options, validates that there are no unknown keys in the input configuration object. public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions) { diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/Bind_T_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/Bind_T_BinderOptions.generated.txt index 36d798a445581..fb5b4b4ad721d 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/Bind_T_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/Bind_T_BinderOptions.generated.txt @@ -62,14 +62,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration OptionsServiceCollectionExtensions.AddOptions(services); services.AddSingleton>(new ConfigurationChangeTokenSource(name, configuration)); - return services.AddSingleton>(new Microsoft.Extensions.Options.ConfigureNamedOptions(name, obj => BindCoreMain(configuration, obj, configureOptions))); + return services.AddSingleton>(new Microsoft.Extensions.Options.ConfigureNamedOptions(name, obj => BindCoreMain(configuration, obj, typeof(TOptions)configureOptions))); } #endregion IServiceCollection extensions. #region Core binding extensions. private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList" }); - public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) + public static void BindCoreMain(IConfiguration configuration, object obj, Type type, Action? configureOptions) { if (configuration is null) { @@ -81,35 +81,25 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new ArgumentNullException(nameof(obj)); } - BinderOptions? binderOptions = GetBinderOptions(configureOptions); - if (!HasValueOrChildren(configuration)) { return; } - if (obj is List typedObj0) - { - BindCore(configuration, ref typedObj0, binderOptions); - return; - } + BinderOptions? binderOptions = GetBinderOptions(configureOptions); - if (obj is Program.MyClass typedObj2) + if (type == typeof(Program.MyClass)) { - BindCore(configuration, ref typedObj2, binderOptions); + var temp = (Program.MyClass)obj; + BindCore(configuration, ref temp, binderOptions); return; } - throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); + throw new NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input."); } public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -121,29 +111,25 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); obj.MyString = configuration["MyString"]!; - if (configuration["MyInt"] is string value5) + if (configuration["MyInt"] is string value2) { - obj.MyInt = ParseInt(value5, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value2, () => configuration.GetSection("MyInt").Path); } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section6) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section3) { - List? temp8 = obj.MyList; - temp8 ??= new List(); - BindCore(section6, ref temp8, binderOptions); - obj.MyList = temp8; + List? temp5 = obj.MyList; + temp5 ??= new List(); + BindCore(section3, ref temp5, binderOptions); + obj.MyList = temp5; } } + /// If required by the binder options, validates that there are no unknown keys in the input configuration object. public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions) { diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/Primitives.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/Primitives.generated.txt index 214cc899bef26..1bf110454ec7b 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/Primitives.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/Primitives.generated.txt @@ -32,13 +32,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration #region IConfiguration extensions. /// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively. [InterceptsLocationAttribute(@"src-0.cs", 13, 16)] - public static void Bind(this IConfiguration configuration, object? obj) => BindCoreMain(configuration, obj, configureOptions: null); - #endregion IConfiguration extensions. - - #region Core binding extensions. - private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "Prop0", "Prop1", "Prop2", "Prop3", "Prop4", "Prop5", "Prop6", "Prop8", "Prop9", "Prop10", "Prop13", "Prop14", "Prop15", "Prop16", "Prop17", "Prop19", "Prop20", "Prop21", "Prop23", "Prop24", "Prop25", "Prop26", "Prop27", "Prop7", "Prop11", "Prop12", "Prop18", "Prop22" }); - - public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) + public static void Bind_ProgramMyClass(this IConfiguration configuration, object? obj) { if (configuration is null) { @@ -50,166 +44,154 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new ArgumentNullException(nameof(obj)); } - BinderOptions? binderOptions = GetBinderOptions(configureOptions); - - if (!HasValueOrChildren(configuration)) - { - return; - } - - if (obj is Program.MyClass typedObj0) - { - BindCore(configuration, ref typedObj0, binderOptions); - return; - } - - throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); + var typedObj = (Program.MyClass)obj; + BindCore(configuration, ref typedObj, binderOptions: null); } + #endregion IConfiguration extensions. + + #region Core binding extensions. + private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "Prop0", "Prop1", "Prop2", "Prop3", "Prop4", "Prop5", "Prop6", "Prop8", "Prop9", "Prop10", "Prop13", "Prop14", "Prop15", "Prop16", "Prop17", "Prop19", "Prop20", "Prop21", "Prop23", "Prop24", "Prop25", "Prop26", "Prop27", "Prop7", "Prop11", "Prop12", "Prop18", "Prop22" }); public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); - if (configuration["Prop0"] is string value2) + if (configuration["Prop0"] is string value0) { - obj.Prop0 = ParseBool(value2, () => configuration.GetSection("Prop0").Path); + obj.Prop0 = ParseBool(value0, () => configuration.GetSection("Prop0").Path); } - if (configuration["Prop1"] is string value3) + if (configuration["Prop1"] is string value1) { - obj.Prop1 = ParseByte(value3, () => configuration.GetSection("Prop1").Path); + obj.Prop1 = ParseByte(value1, () => configuration.GetSection("Prop1").Path); } - if (configuration["Prop2"] is string value4) + if (configuration["Prop2"] is string value2) { - obj.Prop2 = ParseSbyte(value4, () => configuration.GetSection("Prop2").Path); + obj.Prop2 = ParseSbyte(value2, () => configuration.GetSection("Prop2").Path); } - if (configuration["Prop3"] is string value5) + if (configuration["Prop3"] is string value3) { - obj.Prop3 = ParseChar(value5, () => configuration.GetSection("Prop3").Path); + obj.Prop3 = ParseChar(value3, () => configuration.GetSection("Prop3").Path); } - if (configuration["Prop4"] is string value6) + if (configuration["Prop4"] is string value4) { - obj.Prop4 = ParseDouble(value6, () => configuration.GetSection("Prop4").Path); + obj.Prop4 = ParseDouble(value4, () => configuration.GetSection("Prop4").Path); } obj.Prop5 = configuration["Prop5"]!; - if (configuration["Prop6"] is string value8) + if (configuration["Prop6"] is string value6) { - obj.Prop6 = ParseInt(value8, () => configuration.GetSection("Prop6").Path); + obj.Prop6 = ParseInt(value6, () => configuration.GetSection("Prop6").Path); } - if (configuration["Prop8"] is string value9) + if (configuration["Prop8"] is string value7) { - obj.Prop8 = ParseShort(value9, () => configuration.GetSection("Prop8").Path); + obj.Prop8 = ParseShort(value7, () => configuration.GetSection("Prop8").Path); } - if (configuration["Prop9"] is string value10) + if (configuration["Prop9"] is string value8) { - obj.Prop9 = ParseLong(value10, () => configuration.GetSection("Prop9").Path); + obj.Prop9 = ParseLong(value8, () => configuration.GetSection("Prop9").Path); } - if (configuration["Prop10"] is string value11) + if (configuration["Prop10"] is string value9) { - obj.Prop10 = ParseFloat(value11, () => configuration.GetSection("Prop10").Path); + obj.Prop10 = ParseFloat(value9, () => configuration.GetSection("Prop10").Path); } - if (configuration["Prop13"] is string value12) + if (configuration["Prop13"] is string value10) { - obj.Prop13 = ParseUshort(value12, () => configuration.GetSection("Prop13").Path); + obj.Prop13 = ParseUshort(value10, () => configuration.GetSection("Prop13").Path); } - if (configuration["Prop14"] is string value13) + if (configuration["Prop14"] is string value11) { - obj.Prop14 = ParseUint(value13, () => configuration.GetSection("Prop14").Path); + obj.Prop14 = ParseUint(value11, () => configuration.GetSection("Prop14").Path); } - if (configuration["Prop15"] is string value14) + if (configuration["Prop15"] is string value12) { - obj.Prop15 = ParseUlong(value14, () => configuration.GetSection("Prop15").Path); + obj.Prop15 = ParseUlong(value12, () => configuration.GetSection("Prop15").Path); } obj.Prop16 = configuration["Prop16"]!; - if (configuration["Prop17"] is string value16) + if (configuration["Prop17"] is string value14) { - obj.Prop17 = ParseCultureInfo(value16, () => configuration.GetSection("Prop17").Path); + obj.Prop17 = ParseCultureInfo(value14, () => configuration.GetSection("Prop17").Path); } - if (configuration["Prop19"] is string value17) + if (configuration["Prop19"] is string value15) { - obj.Prop19 = ParseDateTime(value17, () => configuration.GetSection("Prop19").Path); + obj.Prop19 = ParseDateTime(value15, () => configuration.GetSection("Prop19").Path); } - if (configuration["Prop20"] is string value18) + if (configuration["Prop20"] is string value16) { - obj.Prop20 = ParseDateTimeOffset(value18, () => configuration.GetSection("Prop20").Path); + obj.Prop20 = ParseDateTimeOffset(value16, () => configuration.GetSection("Prop20").Path); } - if (configuration["Prop21"] is string value19) + if (configuration["Prop21"] is string value17) { - obj.Prop21 = ParseDecimal(value19, () => configuration.GetSection("Prop21").Path); + obj.Prop21 = ParseDecimal(value17, () => configuration.GetSection("Prop21").Path); } - if (configuration["Prop23"] is string value20) + if (configuration["Prop23"] is string value18) { - obj.Prop23 = ParseInt(value20, () => configuration.GetSection("Prop23").Path); + obj.Prop23 = ParseInt(value18, () => configuration.GetSection("Prop23").Path); } - if (configuration["Prop24"] is string value21) + if (configuration["Prop24"] is string value19) { - obj.Prop24 = ParseDateTime(value21, () => configuration.GetSection("Prop24").Path); + obj.Prop24 = ParseDateTime(value19, () => configuration.GetSection("Prop24").Path); } - if (configuration["Prop25"] is string value22) + if (configuration["Prop25"] is string value20) { - obj.Prop25 = ParseUri(value22, () => configuration.GetSection("Prop25").Path); + obj.Prop25 = ParseUri(value20, () => configuration.GetSection("Prop25").Path); } - if (configuration["Prop26"] is string value23) + if (configuration["Prop26"] is string value21) { - obj.Prop26 = ParseVersion(value23, () => configuration.GetSection("Prop26").Path); + obj.Prop26 = ParseVersion(value21, () => configuration.GetSection("Prop26").Path); } - if (configuration["Prop27"] is string value24) + if (configuration["Prop27"] is string value22) { - obj.Prop27 = ParseEnum(value24, () => configuration.GetSection("Prop27").Path); + obj.Prop27 = ParseEnum(value22, () => configuration.GetSection("Prop27").Path); } - if (configuration["Prop7"] is string value25) + if (configuration["Prop7"] is string value23) { - obj.Prop7 = ParseInt128(value25, () => configuration.GetSection("Prop7").Path); + obj.Prop7 = ParseInt128(value23, () => configuration.GetSection("Prop7").Path); } - if (configuration["Prop11"] is string value26) + if (configuration["Prop11"] is string value24) { - obj.Prop11 = ParseHalf(value26, () => configuration.GetSection("Prop11").Path); + obj.Prop11 = ParseHalf(value24, () => configuration.GetSection("Prop11").Path); } - if (configuration["Prop12"] is string value27) + if (configuration["Prop12"] is string value25) { - obj.Prop12 = ParseUInt128(value27, () => configuration.GetSection("Prop12").Path); + obj.Prop12 = ParseUInt128(value25, () => configuration.GetSection("Prop12").Path); } - if (configuration["Prop18"] is string value28) + if (configuration["Prop18"] is string value26) { - obj.Prop18 = ParseDateOnly(value28, () => configuration.GetSection("Prop18").Path); + obj.Prop18 = ParseDateOnly(value26, () => configuration.GetSection("Prop18").Path); } - if (configuration["Prop22"] is string value29) + if (configuration["Prop22"] is string value27) { - obj.Prop22 = ParseByteArray(value29, () => configuration.GetSection("Prop22").Path); + obj.Prop22 = ParseByteArray(value27, () => configuration.GetSection("Prop22").Path); } } + /// If required by the binder options, validates that there are no unknown keys in the input configuration object. public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions) { @@ -232,42 +214,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } } - public static bool HasValueOrChildren(IConfiguration configuration) - { - if ((configuration as IConfigurationSection)?.Value is not null) - { - return true; - } - return AsConfigWithChildren(configuration) is not null; - } - - public static IConfiguration? AsConfigWithChildren(IConfiguration configuration) - { - foreach (IConfigurationSection _ in configuration.GetChildren()) - { - return configuration; - } - return null; - } - - public static BinderOptions? GetBinderOptions(Action? configureOptions) - { - if (configureOptions is null) - { - return null; - } - - BinderOptions binderOptions = new(); - configureOptions(binderOptions); - - if (binderOptions.BindNonPublicProperties) - { - throw new NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); - } - - return binderOptions; - } - public static bool ParseBool(string value, Func getPath) { try diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T.generated.txt index 6e678300999be..7f626f0e27c52 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T.generated.txt @@ -53,7 +53,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration OptionsServiceCollectionExtensions.AddOptions(services); services.AddSingleton>(new ConfigurationChangeTokenSource(name, configuration)); - return services.AddSingleton>(new Microsoft.Extensions.Options.ConfigureNamedOptions(name, obj => BindCoreMain(configuration, obj, configureOptions))); + return services.AddSingleton>(new Microsoft.Extensions.Options.ConfigureNamedOptions(name, obj => BindCoreMain(configuration, obj, typeof(TOptions)configureOptions))); } #endregion IServiceCollection extensions. @@ -61,7 +61,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration private readonly static Lazy> s_configKeys_ProgramMyClass2 = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyInt" }); private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyList2", "MyDictionary" }); - public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) + public static void BindCoreMain(IConfiguration configuration, object obj, Type type, Action? configureOptions) { if (configuration is null) { @@ -73,53 +73,25 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new ArgumentNullException(nameof(obj)); } - BinderOptions? binderOptions = GetBinderOptions(configureOptions); - if (!HasValueOrChildren(configuration)) { return; } - if (obj is List typedObj0) - { - BindCore(configuration, ref typedObj0, binderOptions); - return; - } - - if (obj is Program.MyClass2 typedObj2) - { - BindCore(configuration, ref typedObj2, binderOptions); - return; - } - - if (obj is List typedObj4) - { - BindCore(configuration, ref typedObj4, binderOptions); - return; - } - - if (obj is Dictionary typedObj6) - { - BindCore(configuration, ref typedObj6, binderOptions); - return; - } + BinderOptions? binderOptions = GetBinderOptions(configureOptions); - if (obj is Program.MyClass typedObj8) + if (type == typeof(Program.MyClass)) { - BindCore(configuration, ref typedObj8, binderOptions); + var temp = (Program.MyClass)obj; + BindCore(configuration, ref temp, binderOptions); return; } - throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); + throw new NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input."); } public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -131,26 +103,16 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Program.MyClass2 obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions); - if (configuration["MyInt"] is string value10) + if (configuration["MyInt"] is string value1) { - obj.MyInt = ParseInt(value10, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path); } } public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { var value = new Program.MyClass2(); @@ -161,11 +123,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Dictionary obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -177,45 +134,41 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); obj.MyString = configuration["MyString"]!; - if (configuration["MyInt"] is string value13) + if (configuration["MyInt"] is string value4) { - obj.MyInt = ParseInt(value13, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value4, () => configuration.GetSection("MyInt").Path); } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section14) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) { - List? temp16 = obj.MyList; - temp16 ??= new List(); - BindCore(section14, ref temp16, binderOptions); - obj.MyList = temp16; + List? temp7 = obj.MyList; + temp7 ??= new List(); + BindCore(section5, ref temp7, binderOptions); + obj.MyList = temp7; } - if (AsConfigWithChildren(configuration.GetSection("MyList2")) is IConfigurationSection section17) + if (AsConfigWithChildren(configuration.GetSection("MyList2")) is IConfigurationSection section8) { - List? temp19 = obj.MyList2; - temp19 ??= new List(); - BindCore(section17, ref temp19, binderOptions); - obj.MyList2 = temp19; + List? temp10 = obj.MyList2; + temp10 ??= new List(); + BindCore(section8, ref temp10, binderOptions); + obj.MyList2 = temp10; } - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section20) + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section11) { - Dictionary? temp22 = obj.MyDictionary; - temp22 ??= new Dictionary(); - BindCore(section20, ref temp22, binderOptions); - obj.MyDictionary = temp22; + Dictionary? temp13 = obj.MyDictionary; + temp13 ??= new Dictionary(); + BindCore(section11, ref temp13, binderOptions); + obj.MyDictionary = temp13; } } + /// If required by the binder options, validates that there are no unknown keys in the input configuration object. public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions) { diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_BinderOptions.generated.txt index f759068b61ae2..5848c2412f9b7 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_BinderOptions.generated.txt @@ -53,7 +53,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration OptionsServiceCollectionExtensions.AddOptions(services); services.AddSingleton>(new ConfigurationChangeTokenSource(name, configuration)); - return services.AddSingleton>(new Microsoft.Extensions.Options.ConfigureNamedOptions(name, obj => BindCoreMain(configuration, obj, configureOptions))); + return services.AddSingleton>(new Microsoft.Extensions.Options.ConfigureNamedOptions(name, obj => BindCoreMain(configuration, obj, typeof(TOptions)configureOptions))); } #endregion IServiceCollection extensions. @@ -61,7 +61,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration private readonly static Lazy> s_configKeys_ProgramMyClass2 = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyInt" }); private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyList2", "MyDictionary" }); - public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) + public static void BindCoreMain(IConfiguration configuration, object obj, Type type, Action? configureOptions) { if (configuration is null) { @@ -73,53 +73,25 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new ArgumentNullException(nameof(obj)); } - BinderOptions? binderOptions = GetBinderOptions(configureOptions); - if (!HasValueOrChildren(configuration)) { return; } - if (obj is List typedObj0) - { - BindCore(configuration, ref typedObj0, binderOptions); - return; - } - - if (obj is Program.MyClass2 typedObj2) - { - BindCore(configuration, ref typedObj2, binderOptions); - return; - } - - if (obj is List typedObj4) - { - BindCore(configuration, ref typedObj4, binderOptions); - return; - } - - if (obj is Dictionary typedObj6) - { - BindCore(configuration, ref typedObj6, binderOptions); - return; - } + BinderOptions? binderOptions = GetBinderOptions(configureOptions); - if (obj is Program.MyClass typedObj8) + if (type == typeof(Program.MyClass)) { - BindCore(configuration, ref typedObj8, binderOptions); + var temp = (Program.MyClass)obj; + BindCore(configuration, ref temp, binderOptions); return; } - throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); + throw new NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input."); } public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -131,26 +103,16 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Program.MyClass2 obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions); - if (configuration["MyInt"] is string value10) + if (configuration["MyInt"] is string value1) { - obj.MyInt = ParseInt(value10, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path); } } public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { var value = new Program.MyClass2(); @@ -161,11 +123,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Dictionary obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -177,45 +134,41 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); obj.MyString = configuration["MyString"]!; - if (configuration["MyInt"] is string value13) + if (configuration["MyInt"] is string value4) { - obj.MyInt = ParseInt(value13, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value4, () => configuration.GetSection("MyInt").Path); } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section14) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) { - List? temp16 = obj.MyList; - temp16 ??= new List(); - BindCore(section14, ref temp16, binderOptions); - obj.MyList = temp16; + List? temp7 = obj.MyList; + temp7 ??= new List(); + BindCore(section5, ref temp7, binderOptions); + obj.MyList = temp7; } - if (AsConfigWithChildren(configuration.GetSection("MyList2")) is IConfigurationSection section17) + if (AsConfigWithChildren(configuration.GetSection("MyList2")) is IConfigurationSection section8) { - List? temp19 = obj.MyList2; - temp19 ??= new List(); - BindCore(section17, ref temp19, binderOptions); - obj.MyList2 = temp19; + List? temp10 = obj.MyList2; + temp10 ??= new List(); + BindCore(section8, ref temp10, binderOptions); + obj.MyList2 = temp10; } - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section20) + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section11) { - Dictionary? temp22 = obj.MyDictionary; - temp22 ??= new Dictionary(); - BindCore(section20, ref temp22, binderOptions); - obj.MyDictionary = temp22; + Dictionary? temp13 = obj.MyDictionary; + temp13 ??= new Dictionary(); + BindCore(section11, ref temp13, binderOptions); + obj.MyDictionary = temp13; } } + /// If required by the binder options, validates that there are no unknown keys in the input configuration object. public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions) { diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_name.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_name.generated.txt index 0b8103c75621c..91226d730166f 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_name.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_name.generated.txt @@ -53,7 +53,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration OptionsServiceCollectionExtensions.AddOptions(services); services.AddSingleton>(new ConfigurationChangeTokenSource(name, configuration)); - return services.AddSingleton>(new Microsoft.Extensions.Options.ConfigureNamedOptions(name, obj => BindCoreMain(configuration, obj, configureOptions))); + return services.AddSingleton>(new Microsoft.Extensions.Options.ConfigureNamedOptions(name, obj => BindCoreMain(configuration, obj, typeof(TOptions)configureOptions))); } #endregion IServiceCollection extensions. @@ -61,7 +61,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration private readonly static Lazy> s_configKeys_ProgramMyClass2 = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyInt" }); private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyList2", "MyDictionary" }); - public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) + public static void BindCoreMain(IConfiguration configuration, object obj, Type type, Action? configureOptions) { if (configuration is null) { @@ -73,53 +73,25 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new ArgumentNullException(nameof(obj)); } - BinderOptions? binderOptions = GetBinderOptions(configureOptions); - if (!HasValueOrChildren(configuration)) { return; } - if (obj is List typedObj0) - { - BindCore(configuration, ref typedObj0, binderOptions); - return; - } - - if (obj is Program.MyClass2 typedObj2) - { - BindCore(configuration, ref typedObj2, binderOptions); - return; - } - - if (obj is List typedObj4) - { - BindCore(configuration, ref typedObj4, binderOptions); - return; - } - - if (obj is Dictionary typedObj6) - { - BindCore(configuration, ref typedObj6, binderOptions); - return; - } + BinderOptions? binderOptions = GetBinderOptions(configureOptions); - if (obj is Program.MyClass typedObj8) + if (type == typeof(Program.MyClass)) { - BindCore(configuration, ref typedObj8, binderOptions); + var temp = (Program.MyClass)obj; + BindCore(configuration, ref temp, binderOptions); return; } - throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); + throw new NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input."); } public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -131,26 +103,16 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Program.MyClass2 obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions); - if (configuration["MyInt"] is string value10) + if (configuration["MyInt"] is string value1) { - obj.MyInt = ParseInt(value10, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path); } } public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { var value = new Program.MyClass2(); @@ -161,11 +123,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Dictionary obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -177,45 +134,41 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); obj.MyString = configuration["MyString"]!; - if (configuration["MyInt"] is string value13) + if (configuration["MyInt"] is string value4) { - obj.MyInt = ParseInt(value13, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value4, () => configuration.GetSection("MyInt").Path); } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section14) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) { - List? temp16 = obj.MyList; - temp16 ??= new List(); - BindCore(section14, ref temp16, binderOptions); - obj.MyList = temp16; + List? temp7 = obj.MyList; + temp7 ??= new List(); + BindCore(section5, ref temp7, binderOptions); + obj.MyList = temp7; } - if (AsConfigWithChildren(configuration.GetSection("MyList2")) is IConfigurationSection section17) + if (AsConfigWithChildren(configuration.GetSection("MyList2")) is IConfigurationSection section8) { - List? temp19 = obj.MyList2; - temp19 ??= new List(); - BindCore(section17, ref temp19, binderOptions); - obj.MyList2 = temp19; + List? temp10 = obj.MyList2; + temp10 ??= new List(); + BindCore(section8, ref temp10, binderOptions); + obj.MyList2 = temp10; } - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section20) + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section11) { - Dictionary? temp22 = obj.MyDictionary; - temp22 ??= new Dictionary(); - BindCore(section20, ref temp22, binderOptions); - obj.MyDictionary = temp22; + Dictionary? temp13 = obj.MyDictionary; + temp13 ??= new Dictionary(); + BindCore(section11, ref temp13, binderOptions); + obj.MyDictionary = temp13; } } + /// If required by the binder options, validates that there are no unknown keys in the input configuration object. public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions) { diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_name_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_name_BinderOptions.generated.txt index 53d3ee8d7d431..8c9ccaa71a779 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_name_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_name_BinderOptions.generated.txt @@ -47,7 +47,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration OptionsServiceCollectionExtensions.AddOptions(services); services.AddSingleton>(new ConfigurationChangeTokenSource(name, configuration)); - return services.AddSingleton>(new Microsoft.Extensions.Options.ConfigureNamedOptions(name, obj => BindCoreMain(configuration, obj, configureOptions))); + return services.AddSingleton>(new Microsoft.Extensions.Options.ConfigureNamedOptions(name, obj => BindCoreMain(configuration, obj, typeof(TOptions)configureOptions))); } #endregion IServiceCollection extensions. @@ -55,7 +55,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration private readonly static Lazy> s_configKeys_ProgramMyClass2 = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyInt" }); private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyList2", "MyDictionary" }); - public static void BindCoreMain(IConfiguration? configuration, object? obj, Action? configureOptions) + public static void BindCoreMain(IConfiguration configuration, object obj, Type type, Action? configureOptions) { if (configuration is null) { @@ -67,53 +67,25 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration throw new ArgumentNullException(nameof(obj)); } - BinderOptions? binderOptions = GetBinderOptions(configureOptions); - if (!HasValueOrChildren(configuration)) { return; } - if (obj is List typedObj0) - { - BindCore(configuration, ref typedObj0, binderOptions); - return; - } - - if (obj is Program.MyClass2 typedObj2) - { - BindCore(configuration, ref typedObj2, binderOptions); - return; - } - - if (obj is List typedObj4) - { - BindCore(configuration, ref typedObj4, binderOptions); - return; - } - - if (obj is Dictionary typedObj6) - { - BindCore(configuration, ref typedObj6, binderOptions); - return; - } + BinderOptions? binderOptions = GetBinderOptions(configureOptions); - if (obj is Program.MyClass typedObj8) + if (type == typeof(Program.MyClass)) { - BindCore(configuration, ref typedObj8, binderOptions); + var temp = (Program.MyClass)obj; + BindCore(configuration, ref temp, binderOptions); return; } - throw new NotSupportedException($"Unable to bind to type ''{obj.GetType()}'': generator did not detect the type as input."); + throw new NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input."); } public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -125,26 +97,16 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Program.MyClass2 obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions); - if (configuration["MyInt"] is string value10) + if (configuration["MyInt"] is string value1) { - obj.MyInt = ParseInt(value10, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path); } } public static void BindCore(IConfiguration configuration, ref List obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { var value = new Program.MyClass2(); @@ -155,11 +117,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Dictionary obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - foreach (IConfigurationSection section in configuration.GetChildren()) { if (section.Value is string value) @@ -171,45 +128,41 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration public static void BindCore(IConfiguration configuration, ref Program.MyClass obj, BinderOptions? binderOptions) { - if (obj is null) - { - throw new ArgumentNullException(nameof(obj)); - } - ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); obj.MyString = configuration["MyString"]!; - if (configuration["MyInt"] is string value13) + if (configuration["MyInt"] is string value4) { - obj.MyInt = ParseInt(value13, () => configuration.GetSection("MyInt").Path); + obj.MyInt = ParseInt(value4, () => configuration.GetSection("MyInt").Path); } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section14) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) { - List? temp16 = obj.MyList; - temp16 ??= new List(); - BindCore(section14, ref temp16, binderOptions); - obj.MyList = temp16; + List? temp7 = obj.MyList; + temp7 ??= new List(); + BindCore(section5, ref temp7, binderOptions); + obj.MyList = temp7; } - if (AsConfigWithChildren(configuration.GetSection("MyList2")) is IConfigurationSection section17) + if (AsConfigWithChildren(configuration.GetSection("MyList2")) is IConfigurationSection section8) { - List? temp19 = obj.MyList2; - temp19 ??= new List(); - BindCore(section17, ref temp19, binderOptions); - obj.MyList2 = temp19; + List? temp10 = obj.MyList2; + temp10 ??= new List(); + BindCore(section8, ref temp10, binderOptions); + obj.MyList2 = temp10; } - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section20) + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section11) { - Dictionary? temp22 = obj.MyDictionary; - temp22 ??= new Dictionary(); - BindCore(section20, ref temp22, binderOptions); - obj.MyDictionary = temp22; + Dictionary? temp13 = obj.MyDictionary; + temp13 ??= new Dictionary(); + BindCore(section11, ref temp13, binderOptions); + obj.MyDictionary = temp13; } } + /// If required by the binder options, validates that there are no unknown keys in the input configuration object. public static void ValidateConfigurationKeys(Type type, Lazy> keys, IConfiguration configuration, BinderOptions? binderOptions) { diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/ConfigurationBindingGeneratorTests.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/ConfigurationBindingGeneratorTests.cs index ad78d82f48d0f..a512c5efe495b 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/ConfigurationBindingGeneratorTests.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/ConfigurationBindingGeneratorTests.cs @@ -62,7 +62,7 @@ public async Task LangVersionMustBeCharp12OrHigher(LanguageVersion langVersion) Diagnostic diagnostic = Assert.Single(d); Assert.True(diagnostic.Id == "SYSLIB1102"); - Assert.Contains("C# 11", diagnostic.Descriptor.Title.ToString(CultureInfo.InvariantCulture)); + Assert.Contains("C# 12", diagnostic.Descriptor.Title.ToString(CultureInfo.InvariantCulture)); Assert.Equal(DiagnosticSeverity.Error, diagnostic.Severity); } @@ -252,9 +252,9 @@ public class MyClass2 { } Assert.Single(r); string generatedSource = string.Join('\n', r[0].SourceText.Lines.Select(x => x.ToString())); - Assert.Contains("public static void Bind(this IConfiguration configuration, object? obj) => BindCoreMain(configuration, obj, configureOptions: null);", generatedSource); - Assert.Contains("public static void Bind(this IConfiguration configuration, object? obj, Action? configureOptions) => BindCoreMain(configuration, obj, configureOptions);", generatedSource); - Assert.Contains("public static void Bind(this IConfiguration configuration, string key, object? obj) => BindCoreMain(configuration?.GetSection(key), obj, configureOptions: null);", generatedSource); + Assert.Contains("public static void Bind_ProgramMyClass0(this IConfiguration configuration, object? obj)", generatedSource); + Assert.Contains("public static void Bind_ProgramMyClass1(this IConfiguration configuration, object? obj, Action? configureOptions)", generatedSource); + Assert.Contains("public static void Bind_ProgramMyClass2(this IConfiguration configuration, string key, object? obj)", generatedSource); Assert.Empty(d); } @@ -400,7 +400,7 @@ private static async Task VerifyAgainstBaselineUsingFile( LanguageVersion langVersion = LanguageVersion.Preview, IEnumerable? references = null) => await RoslynTestUtils.RunGenerator( - new ConfigurationBindingGenerator() { EmitUniqueHelperNames = false }, + new ConfigurationBindingGenerator(), references ?? s_compilationAssemblyRefs, new[] { testSourceCode }, langVersion: langVersion).ConfigureAwait(false); diff --git a/src/libraries/Microsoft.Extensions.Logging.Configuration/src/Microsoft.Extensions.Logging.Configuration.csproj b/src/libraries/Microsoft.Extensions.Logging.Configuration/src/Microsoft.Extensions.Logging.Configuration.csproj index 5a66e45d7f83d..c0074144e78d9 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Configuration/src/Microsoft.Extensions.Logging.Configuration.csproj +++ b/src/libraries/Microsoft.Extensions.Logging.Configuration/src/Microsoft.Extensions.Logging.Configuration.csproj @@ -4,7 +4,6 @@ $(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum) true $(Features);InterceptorsPreview - true true true Configuration support for Microsoft.Extensions.Logging. diff --git a/src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csproj b/src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csproj index 978f746ab1fc9..0dceab438f82f 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csproj +++ b/src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csproj @@ -8,8 +8,8 @@ true true $(Features);InterceptorsPreview - true true + true Console logger provider implementation for Microsoft.Extensions.Logging. diff --git a/src/libraries/Microsoft.Extensions.Options/gen/Emitter.cs b/src/libraries/Microsoft.Extensions.Options/gen/Emitter.cs index bbbbf412c2007..8431a112d4883 100644 --- a/src/libraries/Microsoft.Extensions.Options/gen/Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Options/gen/Emitter.cs @@ -7,7 +7,6 @@ using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -using SourceGenerators; namespace Microsoft.Extensions.Options.Generators { @@ -31,15 +30,16 @@ private sealed record StaticFieldInfo(string FieldTypeFQN, int FieldOrder, strin public Emitter(Compilation compilation, bool emitPreamble = true) : base(emitPreamble) { - if (((CSharpCompilation)compilation).LanguageVersion >= Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp11) + if (((CSharpCompilation)compilation).LanguageVersion >= LanguageVersion.CSharp11) { _modifier = "file"; } else { _modifier = "internal"; - GeneratorHelpers.MakeNameUnique(ref _staticValidationAttributeHolderClassName); - GeneratorHelpers.MakeNameUnique(ref _staticValidatorHolderClassName); + string suffix = $"_{new Random().Next():X8}"; + _staticValidationAttributeHolderClassName += suffix; + _staticValidatorHolderClassName += suffix; } _staticValidationAttributeHolderClassFQN = $"global::{StaticFieldHolderClassesNamespace}.{_staticValidationAttributeHolderClassName}"; diff --git a/src/libraries/Microsoft.Extensions.Options/gen/Microsoft.Extensions.Options.SourceGeneration.csproj b/src/libraries/Microsoft.Extensions.Options/gen/Microsoft.Extensions.Options.SourceGeneration.csproj index cd27b518a8dc4..41ceaf6739c33 100644 --- a/src/libraries/Microsoft.Extensions.Options/gen/Microsoft.Extensions.Options.SourceGeneration.csproj +++ b/src/libraries/Microsoft.Extensions.Options/gen/Microsoft.Extensions.Options.SourceGeneration.csproj @@ -20,7 +20,6 @@ - From e17fe3ebed8a9327a43f78f4d2cf4a8a81f204be Mon Sep 17 00:00:00 2001 From: Layomi Akinrinade Date: Fri, 18 Aug 2023 16:58:14 -0700 Subject: [PATCH 3/6] Fix source build issue --- eng/SourceBuildPrebuiltBaseline.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/eng/SourceBuildPrebuiltBaseline.xml b/eng/SourceBuildPrebuiltBaseline.xml index 74f6be96543a5..46dd7457d764a 100644 --- a/eng/SourceBuildPrebuiltBaseline.xml +++ b/eng/SourceBuildPrebuiltBaseline.xml @@ -10,6 +10,7 @@ + From 4596e8292f97b712c157e4459c4ad35f8281fe83 Mon Sep 17 00:00:00 2001 From: Layomi Akinrinade Date: Fri, 18 Aug 2023 17:36:03 -0700 Subject: [PATCH 4/6] Revert changes to options gen --- .../Microsoft.Extensions.Options/gen/Emitter.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Options/gen/Emitter.cs b/src/libraries/Microsoft.Extensions.Options/gen/Emitter.cs index 8431a112d4883..adbaa874ad7f3 100644 --- a/src/libraries/Microsoft.Extensions.Options/gen/Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Options/gen/Emitter.cs @@ -20,17 +20,17 @@ internal sealed class Emitter : EmitterBase private const string StaticValidationResultType = "global::System.ComponentModel.DataAnnotations.ValidationResult"; private const string StaticValidationAttributeType = "global::System.ComponentModel.DataAnnotations.ValidationAttribute"; - private readonly string _staticValidationAttributeHolderClassName = "__Attributes"; - private readonly string _staticValidatorHolderClassName = "__Validators"; - private readonly string _staticValidationAttributeHolderClassFQN; - private readonly string _staticValidatorHolderClassFQN; - private readonly string _modifier; + private string _staticValidationAttributeHolderClassName = "__Attributes"; + private string _staticValidatorHolderClassName = "__Validators"; + private string _staticValidationAttributeHolderClassFQN; + private string _staticValidatorHolderClassFQN; + private string _modifier; private sealed record StaticFieldInfo(string FieldTypeFQN, int FieldOrder, string FieldName, IList InstantiationLines); public Emitter(Compilation compilation, bool emitPreamble = true) : base(emitPreamble) { - if (((CSharpCompilation)compilation).LanguageVersion >= LanguageVersion.CSharp11) + if (((CSharpCompilation)compilation).LanguageVersion >= Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp11) { _modifier = "file"; } From 72bdf7a03abc708d4cc43d60d592f190fbe66567 Mon Sep 17 00:00:00 2001 From: Layomi Akinrinade Date: Fri, 18 Aug 2023 18:52:18 -0700 Subject: [PATCH 5/6] Fix --- .../tests/Common/ConfigurationBinderTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs index ccf936978c792..eea01092667e8 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; From 5e52ba8b3b487ed7ca06fdacc02fbe5cd11076a7 Mon Sep 17 00:00:00 2001 From: Layomi Akinrinade Date: Fri, 18 Aug 2023 19:43:45 -0700 Subject: [PATCH 6/6] Work around source build issue Co-authored-by: Eric StJohn --- ...ft.Extensions.Configuration.Binder.SourceGeneration.csproj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Microsoft.Extensions.Configuration.Binder.SourceGeneration.csproj b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Microsoft.Extensions.Configuration.Binder.SourceGeneration.csproj index e895d18c5556c..5b10a6f8e06c6 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Microsoft.Extensions.Configuration.Binder.SourceGeneration.csproj +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Microsoft.Extensions.Configuration.Binder.SourceGeneration.csproj @@ -10,7 +10,9 @@ - + + +