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 c38172cb97a32..d01c5dbae13f3 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Parser.cs @@ -155,11 +155,7 @@ private TypeRef EnqueueTransitiveType(TypeParseInfo containingTypeParseInfo, ITy if (_createdTypeSpecs.TryGetValue(memberTypeSymbol, out TypeSpec? memberTypeSpec)) { - if (memberTypeSpec is UnsupportedTypeSpec unsupportedTypeSpec) - { - RecordDiagnostic(unsupportedTypeSpec, memberTypeParseInfo); - } - + RecordTypeDiagnosticIfRequired(memberTypeParseInfo, memberTypeSpec); return memberTypeSpec.TypeRef; } @@ -208,6 +204,8 @@ private TypeSpec CreateTypeSpec(TypeParseInfo typeParseInfo) spec = CreateUnsupportedTypeSpec(typeParseInfo, NotSupportedReason.UnknownType); } + RecordTypeDiagnosticIfRequired(typeParseInfo, spec); + return spec; } @@ -566,7 +564,7 @@ private ObjectSpec CreateObjectSpec(TypeParseInfo typeParseInfo) if (initDiagDescriptor is not null) { Debug.Assert(initExceptionMessage is not null); - RecordDiagnostic(typeParseInfo, initDiagDescriptor); + RecordTypeDiagnostic(typeParseInfo, initDiagDescriptor); } Dictionary? properties = null; @@ -652,21 +650,17 @@ private ObjectSpec CreateObjectSpec(TypeParseInfo typeParseInfo) return new ObjectSpec( typeSymbol, initializationStrategy, - properties: properties?.Values.OrderBy(p => p.Name).ToImmutableEquatableArray(), + properties: properties?.Values.ToImmutableEquatableArray(), constructorParameters: ctorParams?.ToImmutableEquatableArray(), initExceptionMessage); } - private UnsupportedTypeSpec CreateUnsupportedTypeSpec(TypeParseInfo typeParseInfo, NotSupportedReason reason) - { - UnsupportedTypeSpec type = new(typeParseInfo.TypeSymbol) { NotSupportedReason = reason }; - RecordDiagnostic(type, typeParseInfo); - return type; - } - - private UnsupportedTypeSpec CreateUnsupportedCollectionSpec(TypeParseInfo typeParseInfo) + private static UnsupportedTypeSpec CreateUnsupportedCollectionSpec(TypeParseInfo typeParseInfo) => CreateUnsupportedTypeSpec(typeParseInfo, NotSupportedReason.CollectionNotSupported); + private static UnsupportedTypeSpec CreateUnsupportedTypeSpec(TypeParseInfo typeParseInfo, NotSupportedReason reason) => + new(typeParseInfo.TypeSymbol) { NotSupportedReason = reason }; + private bool TryGetElementType(INamedTypeSymbol type, [NotNullWhen(true)] out ITypeSymbol? elementType) { INamedTypeSymbol? candidate = GetInterface(type, _typeSymbols.GenericIEnumerable_Unbound); @@ -803,26 +797,43 @@ private static bool HasAddMethod(INamedTypeSymbol type, ITypeSymbol key, ITypeSy private static bool IsEnum(ITypeSymbol type) => type is INamedTypeSymbol { EnumUnderlyingType: INamedTypeSymbol { } }; - private void RecordDiagnostic(UnsupportedTypeSpec type, TypeParseInfo typeParseInfo) + private void RecordTypeDiagnosticIfRequired(TypeParseInfo typeParseInfo, TypeSpec typeSpec) { - DiagnosticDescriptor descriptor = DiagnosticDescriptors.GetNotSupportedDescriptor(type.NotSupportedReason); - RecordDiagnostic(typeParseInfo, descriptor); + ContainingTypeDiagnosticInfo? containingTypeDiagInfo = typeParseInfo.ContainingTypeDiagnosticInfo; + + if (typeSpec is UnsupportedTypeSpec unsupportedTypeSpec) + { + DiagnosticDescriptor descriptor = DiagnosticDescriptors.GetNotSupportedDescriptor(unsupportedTypeSpec.NotSupportedReason); + RecordTypeDiagnostic(typeParseInfo, descriptor); + } + else if (containingTypeDiagInfo?.Descriptor == DiagnosticDescriptors.DictionaryKeyNotSupported && + typeSpec is not ParsableFromStringSpec) + { + ReportContainingTypeDiagnosticIfRequired(typeParseInfo); + } } - private void RecordDiagnostic(TypeParseInfo typeParseInfo, DiagnosticDescriptor descriptor) + private void RecordTypeDiagnostic(TypeParseInfo typeParseInfo, DiagnosticDescriptor descriptor) { - string typeName = typeParseInfo.TypeSymbol.GetName(); - Location invocationLocation = typeParseInfo.BinderInvocation.Location; + RecordDiagnostic(descriptor, typeParseInfo.BinderInvocation.Location, new object?[] { typeParseInfo.TypeName }); + ReportContainingTypeDiagnosticIfRequired(typeParseInfo); + } - RecordDiagnostic(descriptor, invocationLocation, new object?[] { typeName }); + private void ReportContainingTypeDiagnosticIfRequired(TypeParseInfo typeParseInfo) + { + ContainingTypeDiagnosticInfo? containingTypeDiagInfo = typeParseInfo.ContainingTypeDiagnosticInfo; - if (typeParseInfo.ContainingTypeDiagnosticInfo is ContainingTypeDiagnosticInfo containingTypeDiagInfo) + while (containingTypeDiagInfo is not null) { + string containingTypeName = containingTypeDiagInfo.TypeName; + object[] messageArgs = containingTypeDiagInfo.MemberName is string memberName - ? new[] { memberName, typeName } - : new[] { typeName }; + ? new[] { memberName, containingTypeName } + : new[] { containingTypeName }; + + RecordDiagnostic(containingTypeDiagInfo.Descriptor, typeParseInfo.BinderInvocation.Location, messageArgs); - RecordDiagnostic(containingTypeDiagInfo.Descriptor, invocationLocation, messageArgs); + containingTypeDiagInfo = containingTypeDiagInfo.ContainingTypeInfo; } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/CoreBindingHelpers.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/CoreBindingHelpers.cs index 73abbf487882c..1e14094c66440 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/CoreBindingHelpers.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/CoreBindingHelpers.cs @@ -77,7 +77,7 @@ private void EmitConfigurationKeyCaches() Debug.Assert(keys is not null); string configKeysSource = string.Join(", ", keys); - string fieldName = GetConfigKeyCacheFieldName(objectType); + string fieldName = TypeIndex.GetConfigKeyCacheFieldName(objectType); _writer.WriteLine($@"private readonly static Lazy<{TypeDisplayString.HashSetOfString}> {fieldName} = new(() => new {TypeDisplayString.HashSetOfString}(StringComparer.OrdinalIgnoreCase) {{ {configKeysSource} }});"); } } @@ -124,7 +124,7 @@ private void EmitGetCoreMethod() useIncrementalStringValueIdentifier: false); } break; - case ConfigurationSectionSpec configurationSectionSpec: + case ConfigurationSectionSpec: { EmitCastToIConfigurationSection(); _writer.WriteLine($"return {Identifier.section};"); @@ -660,7 +660,7 @@ private void EmitPrimitiveParseMethod(ParsableFromStringSpec type) string exceptionArg1 = string.Format(ExceptionMessages.FailedBinding, $"{{{Identifier.getPath}()}}", $"{{typeof({typeDisplayString})}}"); - EmitStartBlock($"public static {typeDisplayString} {GetParseMethodName(type)}(string {Identifier.value}, Func {Identifier.getPath})"); + EmitStartBlock($"public static {typeDisplayString} {TypeIndex.GetParseMethodName(type)}(string {Identifier.value}, Func {Identifier.getPath})"); EmitEndBlock($$""" try { @@ -677,7 +677,7 @@ private void EmitBindCoreImplForArray(ArraySpec type) { TypeRef elementTypeRef = type.ElementTypeRef; string elementTypeDisplayString = _typeIndex.GetTypeSpec(elementTypeRef).DisplayString; - string tempIdentifier = Identifier.temp; + string tempIdentifier = GetIncrementalIdentifier(Identifier.temp); // Create temp list. _writer.WriteLine($"var {tempIdentifier} = new List<{elementTypeDisplayString}>();"); @@ -721,7 +721,7 @@ private void EmitBindingLogicForEnumerableWithAdd(TypeRef elementTypeRef, string useIncrementalStringValueIdentifier: false); } break; - case ConfigurationSectionSpec configurationSection: + case ConfigurationSectionSpec: { _writer.WriteLine($"{addExpr}({Identifier.section});"); } @@ -772,15 +772,13 @@ void Emit_BindAndAddLogic_ForElement(string parsedKeyExpr) useIncrementalStringValueIdentifier: false); } break; - case ConfigurationSectionSpec configurationSection: + case ConfigurationSectionSpec: { _writer.WriteLine($"{instanceIdentifier}[{parsedKeyExpr}] = {Identifier.section};"); } break; case ComplexTypeSpec complexElementType: { - Debug.Assert(_typeIndex.CanBindTo(complexElementType.TypeRef)); - if (keyType.StringParsableTypeKind is not StringParsableTypeKind.AssignFromSectionValue) { // Save value to local to avoid parsing twice - during look-up and during add. @@ -816,13 +814,17 @@ void Emit_BindAndAddLogic_ForElement(string parsedKeyExpr) EmitEndBlock(); } - void EmitBindingLogic() => this.EmitBindingLogic( - complexElementType, - Identifier.element, - Identifier.section, - InitializationKind.None, - ValueDefaulting.None, - writeOnSuccess: parsedValueExpr => _writer.WriteLine($"{instanceIdentifier}[{parsedKeyExpr}] = {parsedValueExpr};")); + void EmitBindingLogic() + { + this.EmitBindingLogic( + complexElementType, + Identifier.element, + Identifier.section, + InitializationKind.None, + ValueDefaulting.None); + + _writer.WriteLine($"{instanceIdentifier}[{parsedKeyExpr}] = {Identifier.element};"); + } } break; } @@ -835,7 +837,7 @@ private void EmitBindCoreImplForObject(ObjectSpec type) { Debug.Assert(_typeIndex.HasBindableMembers(type)); - string keyCacheFieldName = GetConfigKeyCacheFieldName(type); + string keyCacheFieldName = TypeIndex.GetConfigKeyCacheFieldName(type); string validateMethodCallExpr = $"{Identifier.ValidateConfigurationKeys}(typeof({type.DisplayString}), {keyCacheFieldName}, {Identifier.configuration}, {Identifier.binderOptions});"; _writer.WriteLine(validateMethodCallExpr); @@ -1086,7 +1088,7 @@ private void EmitBindingLogic( { StringParsableTypeKind.AssignFromSectionValue => stringValueToParse_Expr, StringParsableTypeKind.Enum => $"ParseEnum<{type.DisplayString}>({stringValueToParse_Expr}, () => {sectionPathExpr})", - _ => $"{GetParseMethodName(type)}({stringValueToParse_Expr}, () => {sectionPathExpr})", + _ => $"{TypeIndex.GetParseMethodName(type)}({stringValueToParse_Expr}, () => {sectionPathExpr})", }; if (!checkForNullSectionValue) @@ -1260,15 +1262,6 @@ private static string GetConditionKindExpr(ref bool isFirstType) return "else if"; } - - private static string GetConfigKeyCacheFieldName(ObjectSpec type) => - $"s_configKeys_{type.IdentifierCompatibleSubstring}"; - - private static string GetParseMethodName(ParsableFromStringSpec type) - { - Debug.Assert(type.StringParsableTypeKind is not StringParsableTypeKind.AssignFromSectionValue); - return $"Parse{type.IdentifierCompatibleSubstring}"; - } } } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Parser/ConfigurationBinder.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Parser/ConfigurationBinder.cs index 075aefd3367b2..645786e35c1c5 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Parser/ConfigurationBinder.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Parser/ConfigurationBinder.cs @@ -260,7 +260,7 @@ private void RegisterInterceptor_ConfigurationBinder(TypeParseInfo typeParseInfo if ((MethodsToGen.ConfigBinder_Bind & overload) is not 0) { if (typeSpec is ComplexTypeSpec complexTypeSpec && - _helperInfoBuilder!.TryRegisterComplexTypeForMethodGen(complexTypeSpec)) + _helperInfoBuilder!.TryRegisterTransitiveTypesForMethodGen(complexTypeSpec.TypeRef)) { _interceptorInfoBuilder.RegisterInterceptor_ConfigBinder_Bind(overload, complexTypeSpec, invocationOperation); } @@ -271,8 +271,8 @@ private void RegisterInterceptor_ConfigurationBinder(TypeParseInfo typeParseInfo (MethodsToGen.ConfigBinder_GetValue & overload) is not 0); bool registered = (MethodsToGen.ConfigBinder_Get & overload) is not 0 - ? _helperInfoBuilder!.TryRegisterTypeForGetCoreGen(typeSpec) - : _helperInfoBuilder!.TryRegisterTypeForGetValueCoreGen(typeSpec); + ? _helperInfoBuilder!.TryRegisterTypeForGetGen(typeSpec) + : _helperInfoBuilder!.TryRegisterTypeForGetValueGen(typeSpec); if (registered) { diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Parser/Extensions.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Parser/Extensions.cs index 5e6c501e952d8..f685842639966 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Parser/Extensions.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Parser/Extensions.cs @@ -39,17 +39,19 @@ public TypeParseInfo ToTransitiveTypeParseInfo(ITypeSymbol memberType, Diagnosti TypeName = TypeName, Descriptor = diagDescriptor, MemberName = memberName, + ContainingTypeInfo = ContainingTypeDiagnosticInfo, }; return Create(memberType, BindingOverload, BinderInvocation, diagnosticInfo); } } - private readonly struct ContainingTypeDiagnosticInfo + private sealed class ContainingTypeDiagnosticInfo { public required string TypeName { get; init; } public required string? MemberName { get; init; } public required DiagnosticDescriptor Descriptor { get; init; } + public required ContainingTypeDiagnosticInfo? ContainingTypeInfo { get; init; } } } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Specs/BindingHelperInfo.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Specs/BindingHelperInfo.cs index c0cad319a2b20..096c8410717ae 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Specs/BindingHelperInfo.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Specs/BindingHelperInfo.cs @@ -24,6 +24,8 @@ public sealed record BindingHelperInfo internal sealed class Builder(TypeIndex _typeIndex) { + private readonly Dictionary _seenTransitiveTypes = new(); + private MethodsToGen_CoreBindingHelper _methodsToGen; private bool _emitConfigurationKeyCaches; @@ -38,91 +40,6 @@ internal sealed class Builder(TypeIndex _typeIndex) "Microsoft.Extensions.Configuration", }; - public bool TryRegisterComplexTypeForMethodGen(ComplexTypeSpec type) - { - if (!_typeIndex.CanBindTo(type.TypeRef)) - { - return false; - } - - if (type is ObjectSpec { InstantiationStrategy: ObjectInstantiationStrategy.ParameterizedConstructor } objectType - && _typeIndex.CanInstantiate(objectType)) - { - RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.Initialize, type); - } - else if (type is DictionarySpec { InstantiationStrategy: CollectionInstantiationStrategy.LinqToDictionary }) - { - _namespaces.Add("System.Linq"); - } - - TryRegisterTypeForBindCoreGen(type); - return true; - } - - public bool TryRegisterTypeForBindCoreMainGen(ComplexTypeSpec type) - { - if (_typeIndex.HasBindableMembers(type)) - { - bool bindCoreRegistered = TryRegisterTypeForBindCoreGen(type); - Debug.Assert(bindCoreRegistered); - RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.BindCoreMain, type); - RegisterForGen_AsConfigWithChildrenHelper(); - return true; - } - - return false; - } - - public bool TryRegisterTypeForBindCoreGen(ComplexTypeSpec type) - { - if (_typeIndex.HasBindableMembers(type)) - { - RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.BindCore, type); - - if (type is ObjectSpec) - { - _emitConfigurationKeyCaches = true; - - // List is used in generated code as a temp holder for formatting - // an error for config properties that don't map to object properties. - _namespaces.Add("System.Collections.Generic"); - } - - return true; - } - - return false; - } - - public bool TryRegisterTypeForGetCoreGen(TypeSpec type) - { - if (!_typeIndex.CanBindTo(type.TypeRef)) - { - return false; - } - - RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.GetCore, type); - RegisterForGen_AsConfigWithChildrenHelper(); - - if (type is ComplexTypeSpec complexType) - { - bool registered = TryRegisterComplexTypeForMethodGen(complexType); - Debug.Assert(registered); - } - - return true; - } - - public bool TryRegisterTypeForGetValueCoreGen(TypeSpec type) - { - ParsableFromStringSpec effectiveType = (ParsableFromStringSpec)_typeIndex.GetEffectiveTypeSpec(type); - RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.GetValueCore, type); - RegisterStringParsableType(effectiveType); - return true; - } - - public void RegisterNamespace(string @namespace) => _namespaces.Add(@namespace); - public BindingHelperInfo ToIncrementalValue() { return new BindingHelperInfo @@ -158,81 +75,154 @@ public BindingHelperInfo ToIncrementalValue() static ImmutableEquatableArray GetTypesForGen(IEnumerable types) where TSpec : TypeSpec, IEquatable => - types.OrderBy(t => t.TypeRef.FullyQualifiedName).ToImmutableEquatableArray(); + types.ToImmutableEquatableArray(); } - private void RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper method, TypeSpec type) + public bool TryRegisterTypeForGetGen(TypeSpec type) { - if (!_typesForGen.TryGetValue(method, out HashSet? types)) + if (TryRegisterTransitiveTypesForMethodGen(type.TypeRef)) { - _typesForGen[method] = types = new HashSet(); + RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.GetCore, type); + RegisterForGen_AsConfigWithChildrenHelper(); + return true; } - if (types.Add(type)) + return false; + } + + public bool TryRegisterTypeForGetValueGen(TypeSpec typeSpec) + { + ParsableFromStringSpec effectiveType = (ParsableFromStringSpec)_typeIndex.GetEffectiveTypeSpec(typeSpec); + RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.GetValueCore, typeSpec); + RegisterStringParsableTypeIfApplicable(effectiveType); + return true; + } + + public bool TryRegisterTypeForBindCoreMainGen(ComplexTypeSpec type) + { + if (TryRegisterTransitiveTypesForMethodGen(type.TypeRef)) { - _methodsToGen |= method; + RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.BindCoreMain, type); + RegisterForGen_AsConfigWithChildrenHelper(); + return true; + } - if (type is { Namespace: string @namespace }) + return false; + } + + public bool TryRegisterTransitiveTypesForMethodGen(TypeRef typeRef) + { + return _seenTransitiveTypes.TryGetValue(typeRef, out bool isValid) + ? isValid + : (_seenTransitiveTypes[typeRef] = TryRegisterCore()); + + bool TryRegisterCore() + { + switch (_typeIndex.GetTypeSpec(typeRef)) { - _namespaces.Add(@namespace); + case NullableSpec nullableSpec: + { + return TryRegisterTransitiveTypesForMethodGen(nullableSpec.EffectiveTypeRef); + } + case ParsableFromStringSpec stringParsableSpec: + { + RegisterStringParsableTypeIfApplicable(stringParsableSpec); + return true; + } + case DictionarySpec dictionarySpec: + { + bool shouldRegister = _typeIndex.CanBindTo(typeRef) && + TryRegisterTransitiveTypesForMethodGen(dictionarySpec.KeyTypeRef) && + TryRegisterTransitiveTypesForMethodGen(dictionarySpec.ElementTypeRef) && + TryRegisterTypeForBindCoreGen(dictionarySpec); + + if (shouldRegister && dictionarySpec.InstantiationStrategy is CollectionInstantiationStrategy.LinqToDictionary) + { + _namespaces.Add("System.Linq"); + } + + return shouldRegister; + } + case CollectionSpec collectionSpec: + { + return TryRegisterTransitiveTypesForMethodGen(collectionSpec.ElementTypeRef) && + TryRegisterTypeForBindCoreGen(collectionSpec); + } + case ObjectSpec objectSpec: + { + // Base case to avoid stack overflow for recursive object graphs. + // Register all object types for gen; we need to throw runtime exceptions in some cases. + bool shouldRegister = true; + _seenTransitiveTypes.Add(typeRef, shouldRegister); + + // List is used in generated code as a temp holder for formatting + // an error for config properties that don't map to object properties. + _namespaces.Add("System.Collections.Generic"); + + if (_typeIndex.HasBindableMembers(objectSpec)) + { + foreach (PropertySpec property in objectSpec.Properties!) + { + TryRegisterTransitiveTypesForMethodGen(property.TypeRef); + + if (_typeIndex.GetTypeSpec(property.TypeRef) is ComplexTypeSpec) + { + RegisterForGen_AsConfigWithChildrenHelper(); + } + } + + bool registeredForBindCore = TryRegisterTypeForBindCoreGen(objectSpec); + Debug.Assert(registeredForBindCore); + + if (objectSpec is { InstantiationStrategy: ObjectInstantiationStrategy.ParameterizedConstructor, InitExceptionMessage: null }) + { + RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.Initialize, objectSpec); + } + } + + return true; + } + default: + { + return true; + } } + } + } + + public void RegisterNamespace(string @namespace) => _namespaces.Add(@namespace); - RegisterTransitiveTypesForMethodGen(type); + private bool TryRegisterTypeForBindCoreGen(ComplexTypeSpec type) + { + if (_typeIndex.HasBindableMembers(type)) + { + RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper.BindCore, type); + _emitConfigurationKeyCaches = true; + return true; } + + return false; } - private void RegisterTransitiveTypesForMethodGen(TypeSpec typeSpec) + private void RegisterTypeForMethodGen(MethodsToGen_CoreBindingHelper method, TypeSpec type) { - switch (typeSpec) + if (!_typesForGen.TryGetValue(method, out HashSet? types)) { - case NullableSpec nullableTypeSpec: - { - RegisterTransitiveTypesForMethodGen(_typeIndex.GetEffectiveTypeSpec(nullableTypeSpec)); - } - break; - case ArraySpec: - case EnumerableSpec: - { - RegisterComplexTypeForMethodGen(((CollectionSpec)typeSpec).ElementTypeRef); - } - break; - case DictionarySpec dictionaryTypeSpec: - { - RegisterComplexTypeForMethodGen(dictionaryTypeSpec.KeyTypeRef); - RegisterComplexTypeForMethodGen(dictionaryTypeSpec.ElementTypeRef); - } - break; - case ObjectSpec objectType when _typeIndex.HasBindableMembers(objectType): - { - foreach (PropertySpec property in objectType.Properties!) - { - RegisterComplexTypeForMethodGen(property.TypeRef); - } - } - break; + _typesForGen[method] = types = new HashSet(); } - void RegisterComplexTypeForMethodGen(TypeRef transitiveTypeRef) + if (types.Add(type)) { - TypeSpec effectiveTypeSpec = _typeIndex.GetTypeSpec(transitiveTypeRef); + _methodsToGen |= method; - if (effectiveTypeSpec is ParsableFromStringSpec parsableFromStringSpec) - { - RegisterStringParsableType(parsableFromStringSpec); - } - else if (effectiveTypeSpec is ComplexTypeSpec complexEffectiveTypeSpec) + if (type is { Namespace: string @namespace }) { - if (_typeIndex.HasBindableMembers(complexEffectiveTypeSpec)) - { - RegisterForGen_AsConfigWithChildrenHelper(); - } - - TryRegisterComplexTypeForMethodGen(complexEffectiveTypeSpec); + _namespaces.Add(@namespace); } } } - private void RegisterStringParsableType(ParsableFromStringSpec type) + private void RegisterStringParsableTypeIfApplicable(ParsableFromStringSpec type) { if (type.StringParsableTypeKind is not StringParsableTypeKind.AssignFromSectionValue) { diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Specs/InterceptorInfo.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Specs/InterceptorInfo.cs index 7d64cffab99a3..999ed6514f99d 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Specs/InterceptorInfo.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Specs/InterceptorInfo.cs @@ -79,8 +79,11 @@ public void RegisterInterceptor_ConfigBinder_Bind(MethodsToGen overload, Complex MethodsToGen |= overload; - void RegisterInterceptor(ref TypedInterceptorInfoBuildler? infoBuilder) => - (infoBuilder ??= new()).RegisterInterceptor(overload, type, invocation); + void RegisterInterceptor(ref TypedInterceptorInfoBuildler? infoBuilder) + { + infoBuilder ??= new TypedInterceptorInfoBuildler(); + infoBuilder.RegisterInterceptor(overload, type, invocation); + } } public void RegisterInterceptor(MethodsToGen overload, IInvocationOperation operation) @@ -103,8 +106,11 @@ public void RegisterInterceptor(MethodsToGen overload, IInvocationOperation oper MethodsToGen |= overload; - void RegisterInterceptor(ref List? infoList) => - (infoList ??= new()).Add(new InvocationLocationInfo(overload, operation)); + void RegisterInterceptor(ref List? infoList) + { + infoList ??= new List(); + infoList.Add(new InvocationLocationInfo(overload, operation)); + } } public InterceptorInfo ToIncrementalValue() => @@ -112,9 +118,9 @@ public InterceptorInfo ToIncrementalValue() => { MethodsToGen = MethodsToGen, - ConfigBinder = _interceptors_configBinder?.OrderBy(i => i.FilePath).ToImmutableEquatableArray(), - OptionsBuilderExt = _interceptors_OptionsBuilderExt?.OrderBy(i => i.FilePath).ToImmutableEquatableArray(), - ServiceCollectionExt = _interceptors_serviceCollectionExt?.OrderBy(i => i.FilePath).ToImmutableEquatableArray(), + ConfigBinder = _interceptors_configBinder?.ToImmutableEquatableArray(), + OptionsBuilderExt = _interceptors_OptionsBuilderExt?.ToImmutableEquatableArray(), + ServiceCollectionExt = _interceptors_serviceCollectionExt?.ToImmutableEquatableArray(), ConfigBinder_Bind_instance = _configBinder_InfoBuilder_Bind_instance?.ToIncrementalValue(), ConfigBinder_Bind_instance_BinderOptions = _configBinder_InfoBuilder_Bind_instance_BinderOptions?.ToIncrementalValue(), @@ -140,7 +146,6 @@ public void RegisterInterceptor(MethodsToGen overload, ComplexTypeSpec type, IIn public ImmutableEquatableArray? ToIncrementalValue() => _invocationInfoBuilderCache.Values .Select(b => b.ToIncrementalValue()) - .OrderBy(i => i.TargetType.TypeRef.FullyQualifiedName) .ToImmutableEquatableArray(); } @@ -155,7 +160,7 @@ public void RegisterInvocation(IInvocationOperation invocation) => public TypedInterceptorInvocationInfo ToIncrementalValue() => new( TargetType, - Locations: _infoList.OrderBy(i => i.FilePath).ToImmutableEquatableArray()); + Locations: _infoList.ToImmutableEquatableArray()); } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Specs/TypeIndex.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Specs/TypeIndex.cs index 9564008084ab7..5b59577b39292 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Specs/TypeIndex.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Specs/TypeIndex.cs @@ -22,11 +22,9 @@ internal sealed class TypeIndex(IEnumerable typeSpecs) public bool CanInstantiate(ComplexTypeSpec typeSpec) => typeSpec switch { - ObjectSpec objectType => objectType is { InstantiationStrategy: not ObjectInstantiationStrategy.None, InitExceptionMessage: null }, - DictionarySpec dictionaryType => - // Nullable not allowed; that would cause us to emit code that violates dictionary key notnull generic parameter constraint. - GetTypeSpec(dictionaryType.KeyTypeRef) is ParsableFromStringSpec, - CollectionSpec => true, + ObjectSpec objectSpec => objectSpec is { InstantiationStrategy: not ObjectInstantiationStrategy.None, InitExceptionMessage: null }, + DictionarySpec dictionarySpec => KeyIsSupported(dictionarySpec), + CollectionSpec collectionSpec => CanBindTo(collectionSpec.ElementTypeRef), _ => throw new InvalidOperationException(), }; @@ -34,22 +32,25 @@ public bool HasBindableMembers(ComplexTypeSpec typeSpec) => typeSpec switch { ObjectSpec objectSpec => objectSpec.Properties?.Any(ShouldBindTo) is true, - DictionarySpec dictSpec => GetTypeSpec(dictSpec.KeyTypeRef) is ParsableFromStringSpec && CanBindTo(dictSpec.ElementTypeRef), + DictionarySpec dictSpec => KeyIsSupported(dictSpec) && CanBindTo(dictSpec.ElementTypeRef), CollectionSpec collectionSpec => CanBindTo(collectionSpec.ElementTypeRef), _ => throw new InvalidOperationException(), }; public bool ShouldBindTo(PropertySpec property) { - bool isAccessible = property.CanGet || property.CanSet; + TypeSpec propTypeSpec = GetEffectiveTypeSpec(property.TypeRef); + return IsAccessible() && !IsCollectionAndCannotOverride() && !IsDictWithUnsupportedKey(); - bool isCollectionAndCannotOverride = !property.CanSet && - GetEffectiveTypeSpec(property.TypeRef) is CollectionWithCtorInitSpec + bool IsAccessible() => property.CanGet || property.CanSet; + + bool IsDictWithUnsupportedKey() => propTypeSpec is DictionarySpec dictionarySpec && !KeyIsSupported(dictionarySpec); + + bool IsCollectionAndCannotOverride() => !property.CanSet && + propTypeSpec is CollectionWithCtorInitSpec { InstantiationStrategy: CollectionInstantiationStrategy.CopyConstructor or CollectionInstantiationStrategy.LinqToDictionary }; - - return isAccessible && !isCollectionAndCannotOverride; } public TypeSpec GetEffectiveTypeSpec(TypeRef typeRef) @@ -95,5 +96,27 @@ public string GetGenericTypeDisplayString(CollectionWithCtorInitSpec type, Enum string keyTypeDisplayString = GetTypeSpec(((DictionarySpec)type).KeyTypeRef).DisplayString; return $"{proxyTypeNameStr}<{keyTypeDisplayString}, {elementTypeDisplayString}>"; } + + public bool KeyIsSupported(DictionarySpec typeSpec) => + // Only types that are parsable from string are supported. + // Nullable keys not allowed; that would cause us to emit + // code that violates dictionary key notnull constraint. + GetTypeSpec(typeSpec.KeyTypeRef) is ParsableFromStringSpec; + + public static string GetConfigKeyCacheFieldName(ObjectSpec type) => $"s_configKeys_{type.IdentifierCompatibleSubstring}"; + + public static string GetParseMethodName(ParsableFromStringSpec type) + { + Debug.Assert(type.StringParsableTypeKind is not StringParsableTypeKind.AssignFromSectionValue); + + string displayString = type.DisplayString; + + string parseMethod = type.StringParsableTypeKind is StringParsableTypeKind.ByteArray + ? "ParseByteArray" + // MinimalDisplayString.Length is certainly > 2. + : $"Parse{(char.ToUpper(displayString[0]) + displayString.Substring(1)).Replace(".", "")}"; + + return parseMethod; + } } } 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 ddd52c68b9989..ea4fba79cbc46 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 @@ -37,7 +37,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration #endregion IConfiguration extensions. #region Core binding extensions. - private readonly static Lazy> s_configKeys_ProgramMyClassWithCustomCollections = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "CustomDictionary", "CustomList", "IReadOnlyList", "IReadOnlyDictionary" }); + private readonly static Lazy> s_configKeys_ProgramMyClassWithCustomCollections = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "CustomDictionary", "CustomList", "ICustomDictionary", "ICustomCollection", "IReadOnlyList", "UnsupportedIReadOnlyDictionaryUnsupported", "IReadOnlyDictionary" }); public static object? GetCore(this IConfiguration configuration, Type type, Action? configureOptions) { @@ -85,28 +85,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } } - public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) - { - foreach (IConfigurationSection section in configuration.GetChildren()) - { - if (section.Value is string value) - { - instance.Add(ParseInt(value, () => section.Path)); - } - } - } - - public static void BindCore(IConfiguration configuration, ref ICollection instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) - { - foreach (IConfigurationSection section in configuration.GetChildren()) - { - if (section.Value is string value) - { - instance.Add(ParseInt(value, () => section.Path)); - } - } - } - public static void BindCore(IConfiguration configuration, ref IReadOnlyList instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) { if (instance is not ICollection temp) @@ -123,28 +101,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } } - public static void BindCore(IConfiguration configuration, ref Dictionary instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) - { - foreach (IConfigurationSection section in configuration.GetChildren()) - { - if (section.Value is string value) - { - instance[section.Key] = ParseInt(value, () => section.Path); - } - } - } - - public static void BindCore(IConfiguration configuration, ref IDictionary instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) - { - foreach (IConfigurationSection section in configuration.GetChildren()) - { - if (section.Value is string value) - { - instance[section.Key] = ParseInt(value, () => section.Path); - } - } - } - public static void BindCore(IConfiguration configuration, ref IReadOnlyDictionary instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) { if (instance is not IDictionary temp) @@ -184,7 +140,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (AsConfigWithChildren(configuration.GetSection("IReadOnlyList")) is IConfigurationSection section7) { IReadOnlyList? temp9 = instance.IReadOnlyList; - temp9 = temp9 is null ? new List() : new List(temp9); + temp9 = temp9 is null ? (IReadOnlyList)new List() : (IReadOnlyList)new List(temp9); BindCore(section7, ref temp9, defaultValueIfNotFound: false, binderOptions); instance.IReadOnlyList = temp9; } @@ -192,7 +148,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (AsConfigWithChildren(configuration.GetSection("IReadOnlyDictionary")) is IConfigurationSection section10) { IReadOnlyDictionary? temp12 = instance.IReadOnlyDictionary; - temp12 = temp12 is null ? new Dictionary() : temp12.ToDictionary(pair => pair.Key, pair => pair.Value); + temp12 = temp12 is null ? (IReadOnlyDictionary)new Dictionary() : (IReadOnlyDictionary)temp12.ToDictionary(pair => pair.Key, pair => pair.Value); BindCore(section10, ref temp12, defaultValueIfNotFound: false, binderOptions); instance.IReadOnlyDictionary = temp12; } 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 97ea6db5885bb..fc0dda4b5b3ae 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 @@ -86,58 +86,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration #endregion IConfiguration extensions. #region Core binding extensions. - private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyComplexDictionary", "MyDictionary", "MyInt", "MyList", "MyString" }); + private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyDictionary", "MyComplexDictionary" }); - public static void BindCore(IConfiguration configuration, ref Program.MyClass instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) - { - ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); - - if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section0) - { - Dictionary? temp2 = instance.MyComplexDictionary; - temp2 ??= new Dictionary(); - BindCore(section0, ref temp2, defaultValueIfNotFound: false, binderOptions); - instance.MyComplexDictionary = temp2; - } - - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section3) - { - Dictionary? temp5 = instance.MyDictionary; - temp5 ??= new Dictionary(); - BindCore(section3, ref temp5, defaultValueIfNotFound: false, binderOptions); - instance.MyDictionary = temp5; - } - - if (configuration["MyInt"] is string value6) - { - instance.MyInt = ParseInt32(value6, () => configuration.GetSection("MyInt").Path); - } - else if (defaultValueIfNotFound) - { - instance.MyInt = default; - } - - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section7) - { - List? temp9 = instance.MyList; - temp9 ??= new List(); - BindCore(section7, ref temp9, defaultValueIfNotFound: false, binderOptions); - instance.MyList = temp9; - } - - if (configuration["MyString"] is string value10) - { - instance.MyString = value10; - } - } - - public static void BindCore(IConfiguration configuration, ref Dictionary instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) { foreach (IConfigurationSection section in configuration.GetChildren()) { - if (!(instance.TryGetValue(section.Key, out Program.MyClass2? element) && element is not null)) + if (section.Value is string value) { - element = new Program.MyClass2(); + instance.Add(ParseInt(value, () => section.Path)); } } } @@ -153,14 +110,58 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } } - public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + public static void BindCore(IConfiguration configuration, ref Dictionary instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) { foreach (IConfigurationSection section in configuration.GetChildren()) { - if (section.Value is string value) + if (!(instance.TryGetValue(section.Key, out Program.MyClass2? element) && element is not null)) { - instance.Add(ParseInt32(value, () => section.Path)); + element = new Program.MyClass2(); } + instance[section.Key] = element; + } + } + + public static void BindCore(IConfiguration configuration, ref Program.MyClass instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + { + ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); + + if (configuration["MyString"] is string value0) + { + instance.MyString = value0; + } + + if (configuration["MyInt"] is string value1) + { + instance.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path); + } + else if (defaultValueIfNotFound) + { + instance.MyInt = default; + } + + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2) + { + List? temp4 = instance.MyList; + temp4 ??= new List(); + BindCore(section2, ref temp4, defaultValueIfNotFound: false, binderOptions); + instance.MyList = temp4; + } + + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section5) + { + Dictionary? temp7 = instance.MyDictionary; + temp7 ??= new Dictionary(); + BindCore(section5, ref temp7, defaultValueIfNotFound: false, binderOptions); + instance.MyDictionary = temp7; + } + + if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section8) + { + Dictionary? temp10 = instance.MyComplexDictionary; + temp10 ??= new Dictionary(); + BindCore(section8, ref temp10, defaultValueIfNotFound: false, binderOptions); + instance.MyComplexDictionary = temp10; } } @@ -214,7 +215,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return binderOptions; } - public static int ParseInt32(string value, Func getPath) + public static int ParseInt(string value, Func getPath) { try { 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 acf8152bd4ff6..9cbc0a22c5c84 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 @@ -50,58 +50,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration #endregion IConfiguration extensions. #region Core binding extensions. - private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyComplexDictionary", "MyDictionary", "MyInt", "MyList", "MyString" }); + private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyDictionary", "MyComplexDictionary" }); - public static void BindCore(IConfiguration configuration, ref Program.MyClass instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) - { - ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); - - if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section0) - { - Dictionary? temp2 = instance.MyComplexDictionary; - temp2 ??= new Dictionary(); - BindCore(section0, ref temp2, defaultValueIfNotFound: false, binderOptions); - instance.MyComplexDictionary = temp2; - } - - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section3) - { - Dictionary? temp5 = instance.MyDictionary; - temp5 ??= new Dictionary(); - BindCore(section3, ref temp5, defaultValueIfNotFound: false, binderOptions); - instance.MyDictionary = temp5; - } - - if (configuration["MyInt"] is string value6) - { - instance.MyInt = ParseInt32(value6, () => configuration.GetSection("MyInt").Path); - } - else if (defaultValueIfNotFound) - { - instance.MyInt = default; - } - - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section7) - { - List? temp9 = instance.MyList; - temp9 ??= new List(); - BindCore(section7, ref temp9, defaultValueIfNotFound: false, binderOptions); - instance.MyList = temp9; - } - - if (configuration["MyString"] is string value10) - { - instance.MyString = value10; - } - } - - public static void BindCore(IConfiguration configuration, ref Dictionary instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) { foreach (IConfigurationSection section in configuration.GetChildren()) { - if (!(instance.TryGetValue(section.Key, out Program.MyClass2? element) && element is not null)) + if (section.Value is string value) { - element = new Program.MyClass2(); + instance.Add(ParseInt(value, () => section.Path)); } } } @@ -117,14 +74,58 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } } - public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + public static void BindCore(IConfiguration configuration, ref Dictionary instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) { foreach (IConfigurationSection section in configuration.GetChildren()) { - if (section.Value is string value) + if (!(instance.TryGetValue(section.Key, out Program.MyClass2? element) && element is not null)) { - instance.Add(ParseInt32(value, () => section.Path)); + element = new Program.MyClass2(); } + instance[section.Key] = element; + } + } + + public static void BindCore(IConfiguration configuration, ref Program.MyClass instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + { + ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); + + if (configuration["MyString"] is string value0) + { + instance.MyString = value0; + } + + if (configuration["MyInt"] is string value1) + { + instance.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path); + } + else if (defaultValueIfNotFound) + { + instance.MyInt = default; + } + + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2) + { + List? temp4 = instance.MyList; + temp4 ??= new List(); + BindCore(section2, ref temp4, defaultValueIfNotFound: false, binderOptions); + instance.MyList = temp4; + } + + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section5) + { + Dictionary? temp7 = instance.MyDictionary; + temp7 ??= new Dictionary(); + BindCore(section5, ref temp7, defaultValueIfNotFound: false, binderOptions); + instance.MyDictionary = temp7; + } + + if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section8) + { + Dictionary? temp10 = instance.MyComplexDictionary; + temp10 ??= new Dictionary(); + BindCore(section8, ref temp10, defaultValueIfNotFound: false, binderOptions); + instance.MyComplexDictionary = temp10; } } @@ -160,7 +161,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return null; } - public static int ParseInt32(string value, Func getPath) + 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 b65e79c6bc99b..f3efa07fc0c5c 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 @@ -50,58 +50,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration #endregion IConfiguration extensions. #region Core binding extensions. - private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyComplexDictionary", "MyDictionary", "MyInt", "MyList", "MyString" }); + private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyDictionary", "MyComplexDictionary" }); - public static void BindCore(IConfiguration configuration, ref Program.MyClass instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) - { - ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); - - if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section0) - { - Dictionary? temp2 = instance.MyComplexDictionary; - temp2 ??= new Dictionary(); - BindCore(section0, ref temp2, defaultValueIfNotFound: false, binderOptions); - instance.MyComplexDictionary = temp2; - } - - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section3) - { - Dictionary? temp5 = instance.MyDictionary; - temp5 ??= new Dictionary(); - BindCore(section3, ref temp5, defaultValueIfNotFound: false, binderOptions); - instance.MyDictionary = temp5; - } - - if (configuration["MyInt"] is string value6) - { - instance.MyInt = ParseInt32(value6, () => configuration.GetSection("MyInt").Path); - } - else if (defaultValueIfNotFound) - { - instance.MyInt = default; - } - - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section7) - { - List? temp9 = instance.MyList; - temp9 ??= new List(); - BindCore(section7, ref temp9, defaultValueIfNotFound: false, binderOptions); - instance.MyList = temp9; - } - - if (configuration["MyString"] is string value10) - { - instance.MyString = value10; - } - } - - public static void BindCore(IConfiguration configuration, ref Dictionary instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) { foreach (IConfigurationSection section in configuration.GetChildren()) { - if (!(instance.TryGetValue(section.Key, out Program.MyClass2? element) && element is not null)) + if (section.Value is string value) { - element = new Program.MyClass2(); + instance.Add(ParseInt(value, () => section.Path)); } } } @@ -117,14 +74,58 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } } - public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + public static void BindCore(IConfiguration configuration, ref Dictionary instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) { foreach (IConfigurationSection section in configuration.GetChildren()) { - if (section.Value is string value) + if (!(instance.TryGetValue(section.Key, out Program.MyClass2? element) && element is not null)) { - instance.Add(ParseInt32(value, () => section.Path)); + element = new Program.MyClass2(); } + instance[section.Key] = element; + } + } + + public static void BindCore(IConfiguration configuration, ref Program.MyClass instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + { + ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); + + if (configuration["MyString"] is string value0) + { + instance.MyString = value0; + } + + if (configuration["MyInt"] is string value1) + { + instance.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path); + } + else if (defaultValueIfNotFound) + { + instance.MyInt = default; + } + + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2) + { + List? temp4 = instance.MyList; + temp4 ??= new List(); + BindCore(section2, ref temp4, defaultValueIfNotFound: false, binderOptions); + instance.MyList = temp4; + } + + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section5) + { + Dictionary? temp7 = instance.MyDictionary; + temp7 ??= new Dictionary(); + BindCore(section5, ref temp7, defaultValueIfNotFound: false, binderOptions); + instance.MyDictionary = temp7; + } + + if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section8) + { + Dictionary? temp10 = instance.MyComplexDictionary; + temp10 ??= new Dictionary(); + BindCore(section8, ref temp10, defaultValueIfNotFound: false, binderOptions); + instance.MyComplexDictionary = temp10; } } @@ -178,7 +179,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return binderOptions; } - public static int ParseInt32(string value, Func getPath) + public static int ParseInt(string value, Func getPath) { try { 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 2e91c870d1dc7..89b82d31bc19a 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 @@ -50,58 +50,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration #endregion IConfiguration extensions. #region Core binding extensions. - private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyComplexDictionary", "MyDictionary", "MyInt", "MyList", "MyString" }); + private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList", "MyDictionary", "MyComplexDictionary" }); - public static void BindCore(IConfiguration configuration, ref Program.MyClass instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) - { - ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); - - if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section0) - { - Dictionary? temp2 = instance.MyComplexDictionary; - temp2 ??= new Dictionary(); - BindCore(section0, ref temp2, defaultValueIfNotFound: false, binderOptions); - instance.MyComplexDictionary = temp2; - } - - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section3) - { - Dictionary? temp5 = instance.MyDictionary; - temp5 ??= new Dictionary(); - BindCore(section3, ref temp5, defaultValueIfNotFound: false, binderOptions); - instance.MyDictionary = temp5; - } - - if (configuration["MyInt"] is string value6) - { - instance.MyInt = ParseInt32(value6, () => configuration.GetSection("MyInt").Path); - } - else if (defaultValueIfNotFound) - { - instance.MyInt = default; - } - - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section7) - { - List? temp9 = instance.MyList; - temp9 ??= new List(); - BindCore(section7, ref temp9, defaultValueIfNotFound: false, binderOptions); - instance.MyList = temp9; - } - - if (configuration["MyString"] is string value10) - { - instance.MyString = value10; - } - } - - public static void BindCore(IConfiguration configuration, ref Dictionary instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) { foreach (IConfigurationSection section in configuration.GetChildren()) { - if (!(instance.TryGetValue(section.Key, out Program.MyClass2? element) && element is not null)) + if (section.Value is string value) { - element = new Program.MyClass2(); + instance.Add(ParseInt(value, () => section.Path)); } } } @@ -117,14 +74,58 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } } - public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + public static void BindCore(IConfiguration configuration, ref Dictionary instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) { foreach (IConfigurationSection section in configuration.GetChildren()) { - if (section.Value is string value) + if (!(instance.TryGetValue(section.Key, out Program.MyClass2? element) && element is not null)) { - instance.Add(ParseInt32(value, () => section.Path)); + element = new Program.MyClass2(); } + instance[section.Key] = element; + } + } + + public static void BindCore(IConfiguration configuration, ref Program.MyClass instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + { + ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); + + if (configuration["MyString"] is string value0) + { + instance.MyString = value0; + } + + if (configuration["MyInt"] is string value1) + { + instance.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path); + } + else if (defaultValueIfNotFound) + { + instance.MyInt = default; + } + + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2) + { + List? temp4 = instance.MyList; + temp4 ??= new List(); + BindCore(section2, ref temp4, defaultValueIfNotFound: false, binderOptions); + instance.MyList = temp4; + } + + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section5) + { + Dictionary? temp7 = instance.MyDictionary; + temp7 ??= new Dictionary(); + BindCore(section5, ref temp7, defaultValueIfNotFound: false, binderOptions); + instance.MyDictionary = temp7; + } + + if (AsConfigWithChildren(configuration.GetSection("MyComplexDictionary")) is IConfigurationSection section8) + { + Dictionary? temp10 = instance.MyComplexDictionary; + temp10 ??= new Dictionary(); + BindCore(section8, ref temp10, defaultValueIfNotFound: false, binderOptions); + instance.MyComplexDictionary = temp10; } } @@ -160,7 +161,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return null; } - public static int ParseInt32(string value, Func getPath) + public static int ParseInt(string value, Func getPath) { try { diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_ParseTypeFromMethodParam.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_ParseTypeFromMethodParam.generated.txt index 55998b03bdef0..14753a4a1f8e4 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_ParseTypeFromMethodParam.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_ParseTypeFromMethodParam.generated.txt @@ -22,6 +22,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration using Microsoft.Extensions.Configuration; using System; using System.CodeDom.Compiler; + using System.Collections.Generic; using System.Globalization; using System.Runtime.CompilerServices; 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 d41c4adf5479d..b6fb659d544d4 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 @@ -48,7 +48,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration #endregion IConfiguration extensions. #region Core binding extensions. - private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyArray", "MyDictionary", "MyInt", "MyList", "MyString" }); + 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" }); public static object? GetCore(this IConfiguration configuration, Type type, Action? configureOptions) @@ -81,100 +81,100 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration 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.MyClass instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) { - ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); - - if (AsConfigWithChildren(configuration.GetSection("MyArray")) is IConfigurationSection section2) + foreach (IConfigurationSection section in configuration.GetChildren()) { - int[]? temp4 = instance.MyArray; - temp4 ??= new int[0]; - BindCore(section2, ref temp4, defaultValueIfNotFound: false, binderOptions); - instance.MyArray = temp4; + if (section.Value is string value) + { + instance.Add(ParseInt(value, () => section.Path)); + } } + } - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section5) - { - Dictionary? temp7 = instance.MyDictionary; - temp7 ??= new Dictionary(); - BindCore(section5, ref temp7, defaultValueIfNotFound: false, binderOptions); - instance.MyDictionary = temp7; - } + public static void BindCore(IConfiguration configuration, ref int[] instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + { + var temp2 = new List(); - if (configuration["MyInt"] is string value8) - { - instance.MyInt = ParseInt32(value8, () => configuration.GetSection("MyInt").Path); - } - else if (defaultValueIfNotFound) + foreach (IConfigurationSection section in configuration.GetChildren()) { - instance.MyInt = default; + if (section.Value is string value) + { + temp2.Add(ParseInt(value, () => section.Path)); + } } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section9) - { - List? temp11 = instance.MyList; - temp11 ??= new List(); - BindCore(section9, ref temp11, defaultValueIfNotFound: false, binderOptions); - instance.MyList = temp11; - } + int originalCount = instance.Length; + Array.Resize(ref instance, originalCount + temp2.Count); + temp2.CopyTo(instance, originalCount); + } - if (configuration["MyString"] is string value12) + public static void BindCore(IConfiguration configuration, ref Dictionary instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + { + foreach (IConfigurationSection section in configuration.GetChildren()) { - instance.MyString = value12; + if (section.Value is string value) + { + instance[section.Key] = value; + } } } - public static void BindCore(IConfiguration configuration, ref Program.MyClass2 instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + public static void BindCore(IConfiguration configuration, ref Program.MyClass instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) { - ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions); + ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); - if (configuration["MyInt"] is string value13) + if (configuration["MyString"] is string value3) { - instance.MyInt = ParseInt32(value13, () => configuration.GetSection("MyInt").Path); + instance.MyString = value3; + } + + if (configuration["MyInt"] is string value4) + { + instance.MyInt = ParseInt(value4, () => configuration.GetSection("MyInt").Path); } else if (defaultValueIfNotFound) { instance.MyInt = default; } - } - public static void BindCore(IConfiguration configuration, ref Dictionary instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) - { - foreach (IConfigurationSection section in configuration.GetChildren()) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) { - if (section.Value is string value) - { - instance[section.Key] = value; - } + List? temp7 = instance.MyList; + temp7 ??= new List(); + BindCore(section5, ref temp7, defaultValueIfNotFound: false, binderOptions); + instance.MyList = temp7; } - } - public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) - { - foreach (IConfigurationSection section in configuration.GetChildren()) + if (AsConfigWithChildren(configuration.GetSection("MyArray")) is IConfigurationSection section8) { - if (section.Value is string value) - { - instance.Add(ParseInt32(value, () => section.Path)); - } + int[]? temp10 = instance.MyArray; + temp10 ??= new int[0]; + BindCore(section8, ref temp10, defaultValueIfNotFound: false, binderOptions); + instance.MyArray = temp10; + } + + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section11) + { + Dictionary? temp13 = instance.MyDictionary; + temp13 ??= new Dictionary(); + BindCore(section11, ref temp13, defaultValueIfNotFound: false, binderOptions); + instance.MyDictionary = temp13; } } - public static void BindCore(IConfiguration configuration, ref int[] instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + public static void BindCore(IConfiguration configuration, ref Program.MyClass2 instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) { - var temp = new List(); + ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions); - foreach (IConfigurationSection section in configuration.GetChildren()) + if (configuration["MyInt"] is string value14) { - if (section.Value is string value) - { - temp.Add(ParseInt32(value, () => section.Path)); - } + instance.MyInt = ParseInt(value14, () => configuration.GetSection("MyInt").Path); + } + else if (defaultValueIfNotFound) + { + instance.MyInt = default; } - - int originalCount = instance.Length; - Array.Resize(ref instance, originalCount + temp.Count); - temp.CopyTo(instance, originalCount); } @@ -236,7 +236,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return binderOptions; } - public static int ParseInt32(string value, Func getPath) + public static int ParseInt(string value, Func getPath) { try { 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 17660edfae642..bf7e64bd31f90 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 @@ -61,9 +61,13 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return null; } - if (type == typeof(bool?)) + if (type == typeof(int)) { - return ParseBoolean(value, () => section.Path); + return ParseInt(value, () => section.Path); + } + else if (type == typeof(bool?)) + { + return ParseBool(value, () => section.Path); } else if (type == typeof(byte[])) { @@ -73,59 +77,55 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { return ParseCultureInfo(value, () => section.Path); } - else if (type == typeof(int)) - { - return ParseInt32(value, () => section.Path); - } return null; } - public static bool ParseBoolean(string value, Func getPath) + public static int ParseInt(string value, Func getPath) { try { - return bool.Parse(value); + return int.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture); } catch (Exception exception) { - throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(bool)}'.", exception); + throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception); } } - public static byte[] ParseByteArray(string value, Func getPath) + public static bool ParseBool(string value, Func getPath) { try { - return Convert.FromBase64String(value); + return bool.Parse(value); } catch (Exception exception) { - throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(byte[])}'.", exception); + throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(bool)}'.", exception); } } - public static CultureInfo ParseCultureInfo(string value, Func getPath) + public static byte[] ParseByteArray(string value, Func getPath) { try { - return CultureInfo.GetCultureInfo(value); + return Convert.FromBase64String(value); } catch (Exception exception) { - throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(CultureInfo)}'.", exception); + throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(byte[])}'.", exception); } } - public static int ParseInt32(string value, Func getPath) + public static CultureInfo ParseCultureInfo(string value, Func getPath) { try { - return int.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture); + return CultureInfo.GetCultureInfo(value); } catch (Exception exception) { - throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception); + 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 4d3bde81dcab3..b86915b78303b 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 @@ -51,13 +51,13 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (type == typeof(int)) { - return ParseInt32(value, () => section.Path); + return ParseInt(value, () => section.Path); } return null; } - public static int ParseInt32(string value, Func getPath) + public static int ParseInt(string value, Func getPath) { try { 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 32c192c057089..697f710dff302 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 @@ -51,13 +51,13 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (type == typeof(int)) { - return ParseInt32(value, () => section.Path); + return ParseInt(value, () => section.Path); } return null; } - public static int ParseInt32(string value, Func getPath) + public static int ParseInt(string value, Func getPath) { try { 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 cb86c3581112a..b5a22e71ccefb 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 @@ -51,13 +51,13 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (type == typeof(bool?)) { - return ParseBoolean(value, () => section.Path); + return ParseBool(value, () => section.Path); } return null; } - public static bool ParseBoolean(string value, Func getPath) + public static bool ParseBool(string value, Func getPath) { try { diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_PrimitivesOnly.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_PrimitivesOnly.generated.txt new file mode 100644 index 0000000000000..b703fb5f1c864 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_PrimitivesOnly.generated.txt @@ -0,0 +1,182 @@ +// +#nullable enable +#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code. + +namespace System.Runtime.CompilerServices +{ + 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 +{ + using Microsoft.Extensions.Configuration; + using System; + using System.CodeDom.Compiler; + using System.Globalization; + using System.Runtime.CompilerServices; + + [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] + file static class BindingExtensions + { + #region IConfiguration extensions. + /// Attempts to bind the configuration instance to a new instance of type T. + [InterceptsLocation(@"src-0.cs", 10, 16)] + 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. + [InterceptsLocation(@"src-0.cs", 12, 16)] + 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. + [InterceptsLocation(@"src-0.cs", 11, 16)] + 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. + [InterceptsLocation(@"src-0.cs", 13, 16)] + public static object? Get(this IConfiguration configuration, Type type, Action? configureOptions) => GetCore(configuration, type, configureOptions); + #endregion IConfiguration extensions. + + #region Core binding extensions. + public static object? GetCore(this IConfiguration configuration, Type type, Action? configureOptions) + { + if (configuration is null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + BinderOptions? binderOptions = GetBinderOptions(configureOptions); + + if (!HasValueOrChildren(configuration)) + { + return null; + } + + if (type == typeof(int)) + { + if (configuration is not IConfigurationSection section) + { + throw new InvalidOperationException(); + } + if (section.Value is string value) + { + return ParseInt(value, () => section.Path); + } + } + else if (type == typeof(string)) + { + if (configuration is not IConfigurationSection section) + { + throw new InvalidOperationException(); + } + return section.Value; + } + else if (type == typeof(float)) + { + if (configuration is not IConfigurationSection section) + { + throw new InvalidOperationException(); + } + if (section.Value is string value) + { + return ParseFloat(value, () => section.Path); + } + } + else if (type == typeof(double)) + { + if (configuration is not IConfigurationSection section) + { + throw new InvalidOperationException(); + } + if (section.Value is string value) + { + return ParseDouble(value, () => section.Path); + } + } + + throw new NotSupportedException($"Unable to bind to type '{type}': generator did not detect the type as input."); + } + + 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 int ParseInt(string value, Func getPath) + { + try + { + return int.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture); + } + catch (Exception exception) + { + throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception); + } + } + + public static float ParseFloat(string value, Func getPath) + { + try + { + return float.Parse(value, NumberStyles.Float, CultureInfo.InvariantCulture); + } + catch (Exception exception) + { + throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(float)}'.", exception); + } + } + + public static double ParseDouble(string value, Func getPath) + { + try + { + return double.Parse(value, NumberStyles.Float, CultureInfo.InvariantCulture); + } + catch (Exception exception) + { + throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(double)}'.", 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 142ff6f4ebe7a..c2e8f167bb475 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 @@ -36,7 +36,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration #endregion IConfiguration extensions. #region Core binding extensions. - private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyArray", "MyDictionary", "MyInt", "MyList", "MyString" }); + 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) { @@ -62,47 +62,32 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration 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.MyClass instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) { - ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); - - if (AsConfigWithChildren(configuration.GetSection("MyArray")) is IConfigurationSection section1) - { - int[]? temp3 = instance.MyArray; - temp3 ??= new int[0]; - BindCore(section1, ref temp3, defaultValueIfNotFound: false, binderOptions); - instance.MyArray = temp3; - } - - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section4) + foreach (IConfigurationSection section in configuration.GetChildren()) { - Dictionary? temp6 = instance.MyDictionary; - temp6 ??= new Dictionary(); - BindCore(section4, ref temp6, defaultValueIfNotFound: false, binderOptions); - instance.MyDictionary = temp6; + if (section.Value is string value) + { + instance.Add(ParseInt(value, () => section.Path)); + } } + } - if (configuration["MyInt"] is string value7) - { - instance.MyInt = ParseInt32(value7, () => configuration.GetSection("MyInt").Path); - } - else if (defaultValueIfNotFound) - { - instance.MyInt = default; - } + public static void BindCore(IConfiguration configuration, ref int[] instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + { + var temp1 = new List(); - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section8) + foreach (IConfigurationSection section in configuration.GetChildren()) { - List? temp10 = instance.MyList; - temp10 ??= new List(); - BindCore(section8, ref temp10, defaultValueIfNotFound: false, binderOptions); - instance.MyList = temp10; + if (section.Value is string value) + { + temp1.Add(ParseInt(value, () => section.Path)); + } } - if (configuration["MyString"] is string value11) - { - instance.MyString = value11; - } + int originalCount = instance.Length; + Array.Resize(ref instance, originalCount + temp1.Count); + temp1.CopyTo(instance, originalCount); } public static void BindCore(IConfiguration configuration, ref Dictionary instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) @@ -116,32 +101,47 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } } - public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + public static void BindCore(IConfiguration configuration, ref Program.MyClass instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) { - foreach (IConfigurationSection section in configuration.GetChildren()) + ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); + + if (configuration["MyString"] is string value2) { - if (section.Value is string value) - { - instance.Add(ParseInt32(value, () => section.Path)); - } + instance.MyString = value2; } - } - public static void BindCore(IConfiguration configuration, ref int[] instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) - { - var temp = new List(); + if (configuration["MyInt"] is string value3) + { + instance.MyInt = ParseInt(value3, () => configuration.GetSection("MyInt").Path); + } + else if (defaultValueIfNotFound) + { + instance.MyInt = default; + } - foreach (IConfigurationSection section in configuration.GetChildren()) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section4) { - if (section.Value is string value) - { - temp.Add(ParseInt32(value, () => section.Path)); - } + List? temp6 = instance.MyList; + temp6 ??= new List(); + BindCore(section4, ref temp6, defaultValueIfNotFound: false, binderOptions); + instance.MyList = temp6; } - int originalCount = instance.Length; - Array.Resize(ref instance, originalCount + temp.Count); - temp.CopyTo(instance, originalCount); + if (AsConfigWithChildren(configuration.GetSection("MyArray")) is IConfigurationSection section7) + { + int[]? temp9 = instance.MyArray; + temp9 ??= new int[0]; + BindCore(section7, ref temp9, defaultValueIfNotFound: false, binderOptions); + instance.MyArray = temp9; + } + + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section10) + { + Dictionary? temp12 = instance.MyDictionary; + temp12 ??= new Dictionary(); + BindCore(section10, ref temp12, defaultValueIfNotFound: false, binderOptions); + instance.MyDictionary = temp12; + } } @@ -203,7 +203,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return binderOptions; } - public static int ParseInt32(string value, Func getPath) + public static int ParseInt(string value, Func getPath) { try { 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 7840028ca3061..cd3f237917d4e 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 @@ -36,7 +36,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration #endregion IConfiguration extensions. #region Core binding extensions. - private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyArray", "MyDictionary", "MyInt", "MyList", "MyString" }); + 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) { @@ -62,47 +62,32 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration 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.MyClass instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) { - ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); - - if (AsConfigWithChildren(configuration.GetSection("MyArray")) is IConfigurationSection section1) - { - int[]? temp3 = instance.MyArray; - temp3 ??= new int[0]; - BindCore(section1, ref temp3, defaultValueIfNotFound: false, binderOptions); - instance.MyArray = temp3; - } - - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section4) + foreach (IConfigurationSection section in configuration.GetChildren()) { - Dictionary? temp6 = instance.MyDictionary; - temp6 ??= new Dictionary(); - BindCore(section4, ref temp6, defaultValueIfNotFound: false, binderOptions); - instance.MyDictionary = temp6; + if (section.Value is string value) + { + instance.Add(ParseInt(value, () => section.Path)); + } } + } - if (configuration["MyInt"] is string value7) - { - instance.MyInt = ParseInt32(value7, () => configuration.GetSection("MyInt").Path); - } - else if (defaultValueIfNotFound) - { - instance.MyInt = default; - } + public static void BindCore(IConfiguration configuration, ref int[] instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + { + var temp1 = new List(); - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section8) + foreach (IConfigurationSection section in configuration.GetChildren()) { - List? temp10 = instance.MyList; - temp10 ??= new List(); - BindCore(section8, ref temp10, defaultValueIfNotFound: false, binderOptions); - instance.MyList = temp10; + if (section.Value is string value) + { + temp1.Add(ParseInt(value, () => section.Path)); + } } - if (configuration["MyString"] is string value11) - { - instance.MyString = value11; - } + int originalCount = instance.Length; + Array.Resize(ref instance, originalCount + temp1.Count); + temp1.CopyTo(instance, originalCount); } public static void BindCore(IConfiguration configuration, ref Dictionary instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) @@ -116,32 +101,47 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } } - public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + public static void BindCore(IConfiguration configuration, ref Program.MyClass instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) { - foreach (IConfigurationSection section in configuration.GetChildren()) + ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); + + if (configuration["MyString"] is string value2) { - if (section.Value is string value) - { - instance.Add(ParseInt32(value, () => section.Path)); - } + instance.MyString = value2; } - } - public static void BindCore(IConfiguration configuration, ref int[] instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) - { - var temp = new List(); + if (configuration["MyInt"] is string value3) + { + instance.MyInt = ParseInt(value3, () => configuration.GetSection("MyInt").Path); + } + else if (defaultValueIfNotFound) + { + instance.MyInt = default; + } - foreach (IConfigurationSection section in configuration.GetChildren()) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section4) { - if (section.Value is string value) - { - temp.Add(ParseInt32(value, () => section.Path)); - } + List? temp6 = instance.MyList; + temp6 ??= new List(); + BindCore(section4, ref temp6, defaultValueIfNotFound: false, binderOptions); + instance.MyList = temp6; } - int originalCount = instance.Length; - Array.Resize(ref instance, originalCount + temp.Count); - temp.CopyTo(instance, originalCount); + if (AsConfigWithChildren(configuration.GetSection("MyArray")) is IConfigurationSection section7) + { + int[]? temp9 = instance.MyArray; + temp9 ??= new int[0]; + BindCore(section7, ref temp9, defaultValueIfNotFound: false, binderOptions); + instance.MyArray = temp9; + } + + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section10) + { + Dictionary? temp12 = instance.MyDictionary; + temp12 ??= new Dictionary(); + BindCore(section10, ref temp12, defaultValueIfNotFound: false, binderOptions); + instance.MyDictionary = temp12; + } } @@ -203,7 +203,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return binderOptions; } - public static int ParseInt32(string value, Func getPath) + public static int ParseInt(string value, Func getPath) { try { 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 c838ae5aa61de..eca2001123ff0 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 @@ -68,7 +68,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (configuration["MyInt"] is string value1) { - instance.MyInt = ParseInt32(value1, () => configuration.GetSection("MyInt").Path); + instance.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path); } else if (defaultValueIfNotFound) { @@ -135,7 +135,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return binderOptions; } - public static int ParseInt32(string value, Func getPath) + public static int ParseInt(string value, Func getPath) { try { 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 f4c9cb643836f..7883f4a50da0f 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 @@ -68,7 +68,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (configuration["MyInt"] is string value1) { - instance.MyInt = ParseInt32(value1, () => configuration.GetSection("MyInt").Path); + instance.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path); } else if (defaultValueIfNotFound) { @@ -135,7 +135,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return binderOptions; } - public static int ParseInt32(string value, Func getPath) + public static int ParseInt(string value, Func getPath) { try { diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/EmptyConfigType.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/EmptyConfigType.generated.txt index 73121ed621a24..404ce7561cc47 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/EmptyConfigType.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/EmptyConfigType.generated.txt @@ -30,12 +30,6 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration 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. - [InterceptsLocation(@"src-0.cs", 18, 23)] - public static void Bind_ListAbstractType_CannotInit(this IConfiguration configuration, object? instance) - { - } - /// Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively. [InterceptsLocation(@"src-0.cs", 12, 23)] public static void Bind_TypeWithNoMembers(this IConfiguration configuration, object? instance) @@ -96,6 +90,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } } } + + public static IConfiguration? AsConfigWithChildren(IConfiguration configuration) + { + foreach (IConfigurationSection _ in configuration.GetChildren()) + { + return configuration; + } + return null; + } #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 1d84c37f84f39..44f1df2e78232 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 @@ -63,7 +63,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration #endregion OptionsBuilder extensions. #region Core binding extensions. - private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyInt", "MyList", "MyString" }); + private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList" }); public static void BindCoreMain(IConfiguration configuration, object instance, Type type, Action? configureOptions) { @@ -89,41 +89,41 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration 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 instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + { + foreach (IConfigurationSection section in configuration.GetChildren()) + { + if (section.Value is string value) + { + instance.Add(ParseInt(value, () => section.Path)); + } + } + } + public static void BindCore(IConfiguration configuration, ref Program.MyClass instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) { ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); - if (configuration["MyInt"] is string value1) - { - instance.MyInt = ParseInt32(value1, () => configuration.GetSection("MyInt").Path); - } - else if (defaultValueIfNotFound) + if (configuration["MyString"] is string value1) { - instance.MyInt = default; + instance.MyString = value1; } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2) + if (configuration["MyInt"] is string value2) { - List? temp4 = instance.MyList; - temp4 ??= new List(); - BindCore(section2, ref temp4, defaultValueIfNotFound: false, binderOptions); - instance.MyList = temp4; + instance.MyInt = ParseInt(value2, () => configuration.GetSection("MyInt").Path); } - - if (configuration["MyString"] is string value5) + else if (defaultValueIfNotFound) { - instance.MyString = value5; + instance.MyInt = default; } - } - public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) - { - foreach (IConfigurationSection section in configuration.GetChildren()) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section3) { - if (section.Value is string value) - { - instance.Add(ParseInt32(value, () => section.Path)); - } + List? temp5 = instance.MyList; + temp5 ??= new List(); + BindCore(section3, ref temp5, defaultValueIfNotFound: false, binderOptions); + instance.MyList = temp5; } } @@ -186,7 +186,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return binderOptions; } - public static int ParseInt32(string value, Func getPath) + public static int ParseInt(string value, Func getPath) { try { 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 6c0903bf75b55..8d7c70a27b3f2 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 @@ -73,7 +73,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration #endregion IServiceCollection extensions. #region Core binding extensions. - private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyInt", "MyList", "MyString" }); + private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList" }); public static void BindCoreMain(IConfiguration configuration, object instance, Type type, Action? configureOptions) { @@ -99,41 +99,41 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration 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 instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + { + foreach (IConfigurationSection section in configuration.GetChildren()) + { + if (section.Value is string value) + { + instance.Add(ParseInt(value, () => section.Path)); + } + } + } + public static void BindCore(IConfiguration configuration, ref Program.MyClass instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) { ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); - if (configuration["MyInt"] is string value1) - { - instance.MyInt = ParseInt32(value1, () => configuration.GetSection("MyInt").Path); - } - else if (defaultValueIfNotFound) + if (configuration["MyString"] is string value1) { - instance.MyInt = default; + instance.MyString = value1; } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2) + if (configuration["MyInt"] is string value2) { - List? temp4 = instance.MyList; - temp4 ??= new List(); - BindCore(section2, ref temp4, defaultValueIfNotFound: false, binderOptions); - instance.MyList = temp4; + instance.MyInt = ParseInt(value2, () => configuration.GetSection("MyInt").Path); } - - if (configuration["MyString"] is string value5) + else if (defaultValueIfNotFound) { - instance.MyString = value5; + instance.MyInt = default; } - } - public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) - { - foreach (IConfigurationSection section in configuration.GetChildren()) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section3) { - if (section.Value is string value) - { - instance.Add(ParseInt32(value, () => section.Path)); - } + List? temp5 = instance.MyList; + temp5 ??= new List(); + BindCore(section3, ref temp5, defaultValueIfNotFound: false, binderOptions); + instance.MyList = temp5; } } @@ -196,7 +196,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return binderOptions; } - public static int ParseInt32(string value, Func getPath) + public static int ParseInt(string value, Func getPath) { try { 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 dc8932679e3a0..385079af1709a 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 @@ -67,7 +67,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration #endregion IServiceCollection extensions. #region Core binding extensions. - private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyInt", "MyList", "MyString" }); + private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyString", "MyInt", "MyList" }); public static void BindCoreMain(IConfiguration configuration, object instance, Type type, Action? configureOptions) { @@ -93,41 +93,41 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration 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 instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + { + foreach (IConfigurationSection section in configuration.GetChildren()) + { + if (section.Value is string value) + { + instance.Add(ParseInt(value, () => section.Path)); + } + } + } + public static void BindCore(IConfiguration configuration, ref Program.MyClass instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) { ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); - if (configuration["MyInt"] is string value1) - { - instance.MyInt = ParseInt32(value1, () => configuration.GetSection("MyInt").Path); - } - else if (defaultValueIfNotFound) + if (configuration["MyString"] is string value1) { - instance.MyInt = default; + instance.MyString = value1; } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section2) + if (configuration["MyInt"] is string value2) { - List? temp4 = instance.MyList; - temp4 ??= new List(); - BindCore(section2, ref temp4, defaultValueIfNotFound: false, binderOptions); - instance.MyList = temp4; + instance.MyInt = ParseInt(value2, () => configuration.GetSection("MyInt").Path); } - - if (configuration["MyString"] is string value5) + else if (defaultValueIfNotFound) { - instance.MyString = value5; + instance.MyInt = default; } - } - public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) - { - foreach (IConfigurationSection section in configuration.GetChildren()) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section3) { - if (section.Value is string value) - { - instance.Add(ParseInt32(value, () => section.Path)); - } + List? temp5 = instance.MyList; + temp5 ??= new List(); + BindCore(section3, ref temp5, defaultValueIfNotFound: false, binderOptions); + instance.MyList = temp5; } } @@ -190,7 +190,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return binderOptions; } - public static int ParseInt32(string value, Func getPath) + public static int ParseInt(string value, Func getPath) { try { 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 949d2a46ae273..a8373ca527095 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 @@ -50,7 +50,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration #endregion IConfiguration extensions. #region Core binding extensions. - private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "Prop0", "Prop1", "Prop10", "Prop11", "Prop12", "Prop13", "Prop14", "Prop15", "Prop16", "Prop17", "Prop18", "Prop19", "Prop2", "Prop20", "Prop21", "Prop22", "Prop23", "Prop24", "Prop25", "Prop26", "Prop27", "Prop28", "Prop29", "Prop3", "Prop30", "Prop4", "Prop5", "Prop6", "Prop7", "Prop8", "Prop9" }); + 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", "Prop28", "Prop29", "Prop30" }); public static void BindCore(IConfiguration configuration, ref Program.MyClass instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) { @@ -58,7 +58,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (configuration["Prop0"] is string value0) { - instance.Prop0 = ParseBoolean(value0, () => configuration.GetSection("Prop0").Path); + instance.Prop0 = ParseBool(value0, () => configuration.GetSection("Prop0").Path); } else if (defaultValueIfNotFound) { @@ -74,241 +74,241 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration instance.Prop1 = default; } - if (configuration["Prop10"] is string value2) + if (configuration["Prop2"] is string value2) { - instance.Prop10 = ParseSingle(value2, () => configuration.GetSection("Prop10").Path); + instance.Prop2 = ParseSbyte(value2, () => configuration.GetSection("Prop2").Path); } else if (defaultValueIfNotFound) { - instance.Prop10 = default; + instance.Prop2 = default; } - if (configuration["Prop11"] is string value3) + if (configuration["Prop3"] is string value3) { - instance.Prop11 = ParseHalf(value3, () => configuration.GetSection("Prop11").Path); + instance.Prop3 = ParseChar(value3, () => configuration.GetSection("Prop3").Path); } else if (defaultValueIfNotFound) { - instance.Prop11 = default; + instance.Prop3 = default; } - if (configuration["Prop12"] is string value4) + if (configuration["Prop4"] is string value4) { - instance.Prop12 = ParseUInt128(value4, () => configuration.GetSection("Prop12").Path); + instance.Prop4 = ParseDouble(value4, () => configuration.GetSection("Prop4").Path); } else if (defaultValueIfNotFound) { - instance.Prop12 = default; + instance.Prop4 = default; } - if (configuration["Prop13"] is string value5) + if (configuration["Prop5"] is string value5) { - instance.Prop13 = ParseUInt16(value5, () => configuration.GetSection("Prop13").Path); + instance.Prop5 = value5; + } + + if (configuration["Prop6"] is string value6) + { + instance.Prop6 = ParseInt(value6, () => configuration.GetSection("Prop6").Path); } else if (defaultValueIfNotFound) { - instance.Prop13 = default; + instance.Prop6 = default; } - if (configuration["Prop14"] is string value6) + if (configuration["Prop8"] is string value7) { - instance.Prop14 = ParseUInt32(value6, () => configuration.GetSection("Prop14").Path); + instance.Prop8 = ParseShort(value7, () => configuration.GetSection("Prop8").Path); } else if (defaultValueIfNotFound) { - instance.Prop14 = default; + instance.Prop8 = default; } - if (configuration["Prop15"] is string value7) + if (configuration["Prop9"] is string value8) { - instance.Prop15 = ParseUInt64(value7, () => configuration.GetSection("Prop15").Path); + instance.Prop9 = ParseLong(value8, () => configuration.GetSection("Prop9").Path); } else if (defaultValueIfNotFound) { - instance.Prop15 = default; + instance.Prop9 = default; } - if (configuration["Prop16"] is string value8) + if (configuration["Prop10"] is string value9) { - instance.Prop16 = value8; + instance.Prop10 = ParseFloat(value9, () => configuration.GetSection("Prop10").Path); } - - if (configuration["Prop17"] is string value9) + else if (defaultValueIfNotFound) { - instance.Prop17 = ParseCultureInfo(value9, () => configuration.GetSection("Prop17").Path); + instance.Prop10 = default; } - if (configuration["Prop18"] is string value10) + if (configuration["Prop13"] is string value10) { - instance.Prop18 = ParseDateOnly(value10, () => configuration.GetSection("Prop18").Path); + instance.Prop13 = ParseUshort(value10, () => configuration.GetSection("Prop13").Path); } else if (defaultValueIfNotFound) { - instance.Prop18 = default; + instance.Prop13 = default; } - if (configuration["Prop19"] is string value11) + if (configuration["Prop14"] is string value11) { - instance.Prop19 = ParseDateTime(value11, () => configuration.GetSection("Prop19").Path); + instance.Prop14 = ParseUint(value11, () => configuration.GetSection("Prop14").Path); } else if (defaultValueIfNotFound) { - instance.Prop19 = default; + instance.Prop14 = default; } - if (configuration["Prop2"] is string value12) + if (configuration["Prop15"] is string value12) { - instance.Prop2 = ParseSByte(value12, () => configuration.GetSection("Prop2").Path); + instance.Prop15 = ParseUlong(value12, () => configuration.GetSection("Prop15").Path); } else if (defaultValueIfNotFound) { - instance.Prop2 = default; + instance.Prop15 = default; + } + + if (configuration["Prop16"] is string value13) + { + instance.Prop16 = value13; } - if (configuration["Prop20"] is string value13) + if (configuration["Prop17"] is string value14) { - instance.Prop20 = ParseDateTimeOffset(value13, () => configuration.GetSection("Prop20").Path); + instance.Prop17 = ParseCultureInfo(value14, () => configuration.GetSection("Prop17").Path); + } + + if (configuration["Prop19"] is string value15) + { + instance.Prop19 = ParseDateTime(value15, () => configuration.GetSection("Prop19").Path); } else if (defaultValueIfNotFound) { - instance.Prop20 = default; + instance.Prop19 = default; } - if (configuration["Prop21"] is string value14) + if (configuration["Prop20"] is string value16) { - instance.Prop21 = ParseDecimal(value14, () => configuration.GetSection("Prop21").Path); + instance.Prop20 = ParseDateTimeOffset(value16, () => configuration.GetSection("Prop20").Path); } else if (defaultValueIfNotFound) { - instance.Prop21 = default; + instance.Prop20 = default; } - if (configuration["Prop22"] is string value15) + if (configuration["Prop21"] is string value17) { - instance.Prop22 = ParseTimeOnly(value15, () => configuration.GetSection("Prop22").Path); + instance.Prop21 = ParseDecimal(value17, () => configuration.GetSection("Prop21").Path); } else if (defaultValueIfNotFound) { - instance.Prop22 = default; + instance.Prop21 = default; } - if (configuration["Prop23"] is string value16) + if (configuration["Prop23"] is string value18) { - instance.Prop23 = ParseTimeSpan(value16, () => configuration.GetSection("Prop23").Path); + instance.Prop23 = ParseTimeSpan(value18, () => configuration.GetSection("Prop23").Path); } else if (defaultValueIfNotFound) { instance.Prop23 = default; } - if (configuration["Prop24"] is string value17) + if (configuration["Prop24"] is string value19) { - instance.Prop24 = ParseGuid(value17, () => configuration.GetSection("Prop24").Path); + instance.Prop24 = ParseGuid(value19, () => configuration.GetSection("Prop24").Path); } else if (defaultValueIfNotFound) { instance.Prop24 = default; } - if (configuration["Prop25"] is string value18) + if (configuration["Prop25"] is string value20) { - instance.Prop25 = ParseUri(value18, () => configuration.GetSection("Prop25").Path); + instance.Prop25 = ParseUri(value20, () => configuration.GetSection("Prop25").Path); } - if (configuration["Prop26"] is string value19) + if (configuration["Prop26"] is string value21) { - instance.Prop26 = ParseVersion(value19, () => configuration.GetSection("Prop26").Path); + instance.Prop26 = ParseVersion(value21, () => configuration.GetSection("Prop26").Path); } - if (configuration["Prop27"] is string value20) + if (configuration["Prop27"] is string value22) { - instance.Prop27 = ParseEnum(value20, () => configuration.GetSection("Prop27").Path); + instance.Prop27 = ParseEnum(value22, () => configuration.GetSection("Prop27").Path); } else if (defaultValueIfNotFound) { instance.Prop27 = default; } - if (configuration["Prop28"] is string value21) - { - instance.Prop28 = ParseByteArray(value21, () => configuration.GetSection("Prop28").Path); - } - - if (configuration["Prop29"] is string value22) + if (configuration["Prop7"] is string value23) { - instance.Prop29 = ParseInt32(value22, () => configuration.GetSection("Prop29").Path); + instance.Prop7 = ParseInt128(value23, () => configuration.GetSection("Prop7").Path); } else if (defaultValueIfNotFound) { - instance.Prop29 = default; + instance.Prop7 = default; } - if (configuration["Prop3"] is string value23) + if (configuration["Prop11"] is string value24) { - instance.Prop3 = ParseChar(value23, () => configuration.GetSection("Prop3").Path); + instance.Prop11 = ParseHalf(value24, () => configuration.GetSection("Prop11").Path); } else if (defaultValueIfNotFound) { - instance.Prop3 = default; + instance.Prop11 = default; } - if (configuration["Prop30"] is string value24) + if (configuration["Prop12"] is string value25) { - instance.Prop30 = ParseDateTime(value24, () => configuration.GetSection("Prop30").Path); + instance.Prop12 = ParseUInt128(value25, () => configuration.GetSection("Prop12").Path); } else if (defaultValueIfNotFound) { - instance.Prop30 = default; + instance.Prop12 = default; } - if (configuration["Prop4"] is string value25) + if (configuration["Prop18"] is string value26) { - instance.Prop4 = ParseDouble(value25, () => configuration.GetSection("Prop4").Path); + instance.Prop18 = ParseDateOnly(value26, () => configuration.GetSection("Prop18").Path); } else if (defaultValueIfNotFound) { - instance.Prop4 = default; - } - - if (configuration["Prop5"] is string value26) - { - instance.Prop5 = value26; + instance.Prop18 = default; } - if (configuration["Prop6"] is string value27) + if (configuration["Prop22"] is string value27) { - instance.Prop6 = ParseInt32(value27, () => configuration.GetSection("Prop6").Path); + instance.Prop22 = ParseTimeOnly(value27, () => configuration.GetSection("Prop22").Path); } else if (defaultValueIfNotFound) { - instance.Prop6 = default; + instance.Prop22 = default; } - if (configuration["Prop7"] is string value28) + if (configuration["Prop28"] is string value28) { - instance.Prop7 = ParseInt128(value28, () => configuration.GetSection("Prop7").Path); - } - else if (defaultValueIfNotFound) - { - instance.Prop7 = default; + instance.Prop28 = ParseByteArray(value28, () => configuration.GetSection("Prop28").Path); } - if (configuration["Prop8"] is string value29) + if (configuration["Prop29"] is string value29) { - instance.Prop8 = ParseInt16(value29, () => configuration.GetSection("Prop8").Path); + instance.Prop29 = ParseInt(value29, () => configuration.GetSection("Prop29").Path); } else if (defaultValueIfNotFound) { - instance.Prop8 = default; + instance.Prop29 = default; } - if (configuration["Prop9"] is string value30) + if (configuration["Prop30"] is string value30) { - instance.Prop9 = ParseInt64(value30, () => configuration.GetSection("Prop9").Path); + instance.Prop30 = ParseDateTime(value30, () => configuration.GetSection("Prop30").Path); } else if (defaultValueIfNotFound) { - instance.Prop9 = default; + instance.Prop30 = default; } } @@ -335,7 +335,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } } - public static bool ParseBoolean(string value, Func getPath) + public static bool ParseBool(string value, Func getPath) { try { @@ -359,15 +359,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } } - public static byte[] ParseByteArray(string value, Func getPath) + public static sbyte ParseSbyte(string value, Func getPath) { try { - return Convert.FromBase64String(value); + return sbyte.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture); } catch (Exception exception) { - throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(byte[])}'.", exception); + throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(sbyte)}'.", exception); } } @@ -383,151 +383,147 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } } - public static decimal ParseDecimal(string value, Func getPath) + public static double ParseDouble(string value, Func getPath) { try { - return decimal.Parse(value, NumberStyles.Float, CultureInfo.InvariantCulture); + return double.Parse(value, NumberStyles.Float, CultureInfo.InvariantCulture); } catch (Exception exception) { - throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(decimal)}'.", exception); + throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(double)}'.", exception); } } - public static double ParseDouble(string value, Func getPath) + public static int ParseInt(string value, Func getPath) { try { - return double.Parse(value, NumberStyles.Float, CultureInfo.InvariantCulture); + return int.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture); } catch (Exception exception) { - throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(double)}'.", exception); + throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception); } } - public static float ParseSingle(string value, Func getPath) + public static short ParseShort(string value, Func getPath) { try { - return float.Parse(value, NumberStyles.Float, CultureInfo.InvariantCulture); + return short.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture); } catch (Exception exception) { - throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(float)}'.", exception); + throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(short)}'.", exception); } } - public static DateOnly ParseDateOnly(string value, Func getPath) + public static long ParseLong(string value, Func getPath) { try { - return DateOnly.Parse(value, CultureInfo.InvariantCulture); + return long.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture); } catch (Exception exception) { - throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(DateOnly)}'.", exception); + throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(long)}'.", exception); } } - public static DateTime ParseDateTime(string value, Func getPath) + public static float ParseFloat(string value, Func getPath) { try { - return DateTime.Parse(value, CultureInfo.InvariantCulture); + return float.Parse(value, NumberStyles.Float, CultureInfo.InvariantCulture); } catch (Exception exception) { - throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(DateTime)}'.", exception); + throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(float)}'.", exception); } } - public static DateTimeOffset ParseDateTimeOffset(string value, Func getPath) + public static ushort ParseUshort(string value, Func getPath) { try { - return DateTimeOffset.Parse(value, CultureInfo.InvariantCulture); + return ushort.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture); } catch (Exception exception) { - throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(DateTimeOffset)}'.", exception); + throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(ushort)}'.", exception); } } - public static T ParseEnum(string value, Func getPath) where T : struct + public static uint ParseUint(string value, Func getPath) { try { - #if NETFRAMEWORK || NETSTANDARD2_0 - return (T)Enum.Parse(typeof(T), value, ignoreCase: true); - #else - return Enum.Parse(value, ignoreCase: true); - #endif + return uint.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture); } catch (Exception exception) { - throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(T)}'.", exception); + throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(uint)}'.", exception); } } - public static CultureInfo ParseCultureInfo(string value, Func getPath) + public static ulong ParseUlong(string value, Func getPath) { try { - return CultureInfo.GetCultureInfo(value); + return ulong.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture); } catch (Exception exception) { - throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(CultureInfo)}'.", exception); + throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(ulong)}'.", exception); } } - public static Guid ParseGuid(string value, Func getPath) + public static CultureInfo ParseCultureInfo(string value, Func getPath) { try { - return Guid.Parse(value); + return CultureInfo.GetCultureInfo(value); } catch (Exception exception) { - throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(Guid)}'.", exception); + throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(CultureInfo)}'.", exception); } } - public static Half ParseHalf(string value, Func getPath) + public static DateTime ParseDateTime(string value, Func getPath) { try { - return Half.Parse(value, CultureInfo.InvariantCulture); + return DateTime.Parse(value, CultureInfo.InvariantCulture); } catch (Exception exception) { - throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(Half)}'.", exception); + throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(DateTime)}'.", exception); } } - public static Int128 ParseInt128(string value, Func getPath) + public static DateTimeOffset ParseDateTimeOffset(string value, Func getPath) { try { - return Int128.Parse(value, CultureInfo.InvariantCulture); + return DateTimeOffset.Parse(value, CultureInfo.InvariantCulture); } catch (Exception exception) { - throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(Int128)}'.", exception); + throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(DateTimeOffset)}'.", exception); } } - public static TimeOnly ParseTimeOnly(string value, Func getPath) + public static decimal ParseDecimal(string value, Func getPath) { try { - return TimeOnly.Parse(value, CultureInfo.InvariantCulture); + return decimal.Parse(value, NumberStyles.Float, CultureInfo.InvariantCulture); } catch (Exception exception) { - throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(TimeOnly)}'.", exception); + throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(decimal)}'.", exception); } } @@ -543,15 +539,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } } - public static UInt128 ParseUInt128(string value, Func getPath) + public static Guid ParseGuid(string value, Func getPath) { try { - return UInt128.Parse(value, CultureInfo.InvariantCulture); + return Guid.Parse(value); } catch (Exception exception) { - throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(UInt128)}'.", exception); + throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(Guid)}'.", exception); } } @@ -579,87 +575,91 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration } } - public static int ParseInt32(string value, Func getPath) + public static T ParseEnum(string value, Func getPath) where T : struct { try { - return int.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture); + #if NETFRAMEWORK || NETSTANDARD2_0 + return (T)Enum.Parse(typeof(T), value, ignoreCase: true); + #else + return Enum.Parse(value, ignoreCase: true); + #endif } catch (Exception exception) { - throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(int)}'.", exception); + throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(T)}'.", exception); } } - public static long ParseInt64(string value, Func getPath) + public static Int128 ParseInt128(string value, Func getPath) { try { - return long.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture); + return Int128.Parse(value, CultureInfo.InvariantCulture); } catch (Exception exception) { - throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(long)}'.", exception); + throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(Int128)}'.", exception); } } - public static sbyte ParseSByte(string value, Func getPath) + public static Half ParseHalf(string value, Func getPath) { try { - return sbyte.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture); + return Half.Parse(value, CultureInfo.InvariantCulture); } catch (Exception exception) { - throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(sbyte)}'.", exception); + throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(Half)}'.", exception); } } - public static short ParseInt16(string value, Func getPath) + public static UInt128 ParseUInt128(string value, Func getPath) { try { - return short.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture); + return UInt128.Parse(value, CultureInfo.InvariantCulture); } catch (Exception exception) { - throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(short)}'.", exception); + throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(UInt128)}'.", exception); } } - public static uint ParseUInt32(string value, Func getPath) + public static DateOnly ParseDateOnly(string value, Func getPath) { try { - return uint.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture); + return DateOnly.Parse(value, CultureInfo.InvariantCulture); } catch (Exception exception) { - throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(uint)}'.", exception); + throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(DateOnly)}'.", exception); } } - public static ulong ParseUInt64(string value, Func getPath) + public static TimeOnly ParseTimeOnly(string value, Func getPath) { try { - return ulong.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture); + return TimeOnly.Parse(value, CultureInfo.InvariantCulture); } catch (Exception exception) { - throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(ulong)}'.", exception); + throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(TimeOnly)}'.", exception); } } - public static ushort ParseUInt16(string value, Func getPath) + public static byte[] ParseByteArray(string value, Func getPath) { try { - return ushort.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture); + return Convert.FromBase64String(value); } catch (Exception exception) { - throw new InvalidOperationException($"Failed to convert configuration value at '{getPath()}' to type '{typeof(ushort)}'.", exception); + 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 145093ea55aac..3e38a484c8255 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 @@ -59,8 +59,8 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration #endregion IServiceCollection extensions. #region Core binding extensions. - private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyDictionary", "MyInt", "MyList", "MyList2", "MyString" }); 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 instance, Type type, Action? configureOptions) { @@ -86,92 +86,92 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration 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.MyClass instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) { - ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); - - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section1) + foreach (IConfigurationSection section in configuration.GetChildren()) { - Dictionary? temp3 = instance.MyDictionary; - temp3 ??= new Dictionary(); - BindCore(section1, ref temp3, defaultValueIfNotFound: false, binderOptions); - instance.MyDictionary = temp3; + if (section.Value is string value) + { + instance.Add(ParseInt(value, () => section.Path)); + } } + } - if (configuration["MyInt"] is string value4) + public static void BindCore(IConfiguration configuration, ref Program.MyClass2 instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + { + ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions); + + if (configuration["MyInt"] is string value1) { - instance.MyInt = ParseInt32(value4, () => configuration.GetSection("MyInt").Path); + instance.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path); } else if (defaultValueIfNotFound) { instance.MyInt = default; } + } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) - { - List? temp7 = instance.MyList; - temp7 ??= new List(); - BindCore(section5, ref temp7, defaultValueIfNotFound: false, binderOptions); - instance.MyList = temp7; - } - - if (AsConfigWithChildren(configuration.GetSection("MyList2")) is IConfigurationSection section8) + public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + { + foreach (IConfigurationSection section in configuration.GetChildren()) { - List? temp10 = instance.MyList2; - temp10 ??= new List(); - BindCore(section8, ref temp10, defaultValueIfNotFound: false, binderOptions); - instance.MyList2 = temp10; + var value = new Program.MyClass2(); + BindCore(section, ref value, defaultValueIfNotFound: false, binderOptions); + instance.Add(value); } + } - if (configuration["MyString"] is string value11) + public static void BindCore(IConfiguration configuration, ref Dictionary instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + { + foreach (IConfigurationSection section in configuration.GetChildren()) { - instance.MyString = value11; + if (section.Value is string value) + { + instance[section.Key] = value; + } } } - public static void BindCore(IConfiguration configuration, ref Program.MyClass2 instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + public static void BindCore(IConfiguration configuration, ref Program.MyClass instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) { - ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions); + ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); - if (configuration["MyInt"] is string value12) + if (configuration["MyString"] is string value3) { - instance.MyInt = ParseInt32(value12, () => configuration.GetSection("MyInt").Path); + instance.MyString = value3; + } + + if (configuration["MyInt"] is string value4) + { + instance.MyInt = ParseInt(value4, () => configuration.GetSection("MyInt").Path); } else if (defaultValueIfNotFound) { instance.MyInt = default; } - } - public static void BindCore(IConfiguration configuration, ref Dictionary instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) - { - foreach (IConfigurationSection section in configuration.GetChildren()) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) { - if (section.Value is string value) - { - instance[section.Key] = value; - } + List? temp7 = instance.MyList; + temp7 ??= new List(); + BindCore(section5, ref temp7, defaultValueIfNotFound: false, binderOptions); + instance.MyList = temp7; } - } - public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) - { - foreach (IConfigurationSection section in configuration.GetChildren()) + if (AsConfigWithChildren(configuration.GetSection("MyList2")) is IConfigurationSection section8) { - var value = new Program.MyClass2(); - BindCore(section, ref value, defaultValueIfNotFound: false, binderOptions); - instance.Add(value); + List? temp10 = instance.MyList2; + temp10 ??= new List(); + BindCore(section8, ref temp10, defaultValueIfNotFound: false, binderOptions); + instance.MyList2 = temp10; } - } - public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) - { - foreach (IConfigurationSection section in configuration.GetChildren()) + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section11) { - if (section.Value is string value) - { - instance.Add(ParseInt32(value, () => section.Path)); - } + Dictionary? temp13 = instance.MyDictionary; + temp13 ??= new Dictionary(); + BindCore(section11, ref temp13, defaultValueIfNotFound: false, binderOptions); + instance.MyDictionary = temp13; } } @@ -234,7 +234,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return binderOptions; } - public static int ParseInt32(string value, Func getPath) + public static int ParseInt(string value, Func getPath) { try { 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 4aa428bba74f2..186e93a49207b 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 @@ -59,8 +59,8 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration #endregion IServiceCollection extensions. #region Core binding extensions. - private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyDictionary", "MyInt", "MyList", "MyList2", "MyString" }); 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 instance, Type type, Action? configureOptions) { @@ -86,92 +86,92 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration 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.MyClass instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) { - ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); - - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section1) + foreach (IConfigurationSection section in configuration.GetChildren()) { - Dictionary? temp3 = instance.MyDictionary; - temp3 ??= new Dictionary(); - BindCore(section1, ref temp3, defaultValueIfNotFound: false, binderOptions); - instance.MyDictionary = temp3; + if (section.Value is string value) + { + instance.Add(ParseInt(value, () => section.Path)); + } } + } - if (configuration["MyInt"] is string value4) + public static void BindCore(IConfiguration configuration, ref Program.MyClass2 instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + { + ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions); + + if (configuration["MyInt"] is string value1) { - instance.MyInt = ParseInt32(value4, () => configuration.GetSection("MyInt").Path); + instance.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path); } else if (defaultValueIfNotFound) { instance.MyInt = default; } + } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) - { - List? temp7 = instance.MyList; - temp7 ??= new List(); - BindCore(section5, ref temp7, defaultValueIfNotFound: false, binderOptions); - instance.MyList = temp7; - } - - if (AsConfigWithChildren(configuration.GetSection("MyList2")) is IConfigurationSection section8) + public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + { + foreach (IConfigurationSection section in configuration.GetChildren()) { - List? temp10 = instance.MyList2; - temp10 ??= new List(); - BindCore(section8, ref temp10, defaultValueIfNotFound: false, binderOptions); - instance.MyList2 = temp10; + var value = new Program.MyClass2(); + BindCore(section, ref value, defaultValueIfNotFound: false, binderOptions); + instance.Add(value); } + } - if (configuration["MyString"] is string value11) + public static void BindCore(IConfiguration configuration, ref Dictionary instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + { + foreach (IConfigurationSection section in configuration.GetChildren()) { - instance.MyString = value11; + if (section.Value is string value) + { + instance[section.Key] = value; + } } } - public static void BindCore(IConfiguration configuration, ref Program.MyClass2 instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + public static void BindCore(IConfiguration configuration, ref Program.MyClass instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) { - ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions); + ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); - if (configuration["MyInt"] is string value12) + if (configuration["MyString"] is string value3) { - instance.MyInt = ParseInt32(value12, () => configuration.GetSection("MyInt").Path); + instance.MyString = value3; + } + + if (configuration["MyInt"] is string value4) + { + instance.MyInt = ParseInt(value4, () => configuration.GetSection("MyInt").Path); } else if (defaultValueIfNotFound) { instance.MyInt = default; } - } - public static void BindCore(IConfiguration configuration, ref Dictionary instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) - { - foreach (IConfigurationSection section in configuration.GetChildren()) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) { - if (section.Value is string value) - { - instance[section.Key] = value; - } + List? temp7 = instance.MyList; + temp7 ??= new List(); + BindCore(section5, ref temp7, defaultValueIfNotFound: false, binderOptions); + instance.MyList = temp7; } - } - public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) - { - foreach (IConfigurationSection section in configuration.GetChildren()) + if (AsConfigWithChildren(configuration.GetSection("MyList2")) is IConfigurationSection section8) { - var value = new Program.MyClass2(); - BindCore(section, ref value, defaultValueIfNotFound: false, binderOptions); - instance.Add(value); + List? temp10 = instance.MyList2; + temp10 ??= new List(); + BindCore(section8, ref temp10, defaultValueIfNotFound: false, binderOptions); + instance.MyList2 = temp10; } - } - public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) - { - foreach (IConfigurationSection section in configuration.GetChildren()) + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section11) { - if (section.Value is string value) - { - instance.Add(ParseInt32(value, () => section.Path)); - } + Dictionary? temp13 = instance.MyDictionary; + temp13 ??= new Dictionary(); + BindCore(section11, ref temp13, defaultValueIfNotFound: false, binderOptions); + instance.MyDictionary = temp13; } } @@ -234,7 +234,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return binderOptions; } - public static int ParseInt32(string value, Func getPath) + public static int ParseInt(string value, Func getPath) { try { 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 d456f83046649..7958adb112533 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 @@ -59,8 +59,8 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration #endregion IServiceCollection extensions. #region Core binding extensions. - private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyDictionary", "MyInt", "MyList", "MyList2", "MyString" }); 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 instance, Type type, Action? configureOptions) { @@ -86,92 +86,92 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration 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.MyClass instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) { - ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); - - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section1) + foreach (IConfigurationSection section in configuration.GetChildren()) { - Dictionary? temp3 = instance.MyDictionary; - temp3 ??= new Dictionary(); - BindCore(section1, ref temp3, defaultValueIfNotFound: false, binderOptions); - instance.MyDictionary = temp3; + if (section.Value is string value) + { + instance.Add(ParseInt(value, () => section.Path)); + } } + } - if (configuration["MyInt"] is string value4) + public static void BindCore(IConfiguration configuration, ref Program.MyClass2 instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + { + ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions); + + if (configuration["MyInt"] is string value1) { - instance.MyInt = ParseInt32(value4, () => configuration.GetSection("MyInt").Path); + instance.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path); } else if (defaultValueIfNotFound) { instance.MyInt = default; } + } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) - { - List? temp7 = instance.MyList; - temp7 ??= new List(); - BindCore(section5, ref temp7, defaultValueIfNotFound: false, binderOptions); - instance.MyList = temp7; - } - - if (AsConfigWithChildren(configuration.GetSection("MyList2")) is IConfigurationSection section8) + public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + { + foreach (IConfigurationSection section in configuration.GetChildren()) { - List? temp10 = instance.MyList2; - temp10 ??= new List(); - BindCore(section8, ref temp10, defaultValueIfNotFound: false, binderOptions); - instance.MyList2 = temp10; + var value = new Program.MyClass2(); + BindCore(section, ref value, defaultValueIfNotFound: false, binderOptions); + instance.Add(value); } + } - if (configuration["MyString"] is string value11) + public static void BindCore(IConfiguration configuration, ref Dictionary instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + { + foreach (IConfigurationSection section in configuration.GetChildren()) { - instance.MyString = value11; + if (section.Value is string value) + { + instance[section.Key] = value; + } } } - public static void BindCore(IConfiguration configuration, ref Program.MyClass2 instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + public static void BindCore(IConfiguration configuration, ref Program.MyClass instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) { - ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions); + ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); - if (configuration["MyInt"] is string value12) + if (configuration["MyString"] is string value3) { - instance.MyInt = ParseInt32(value12, () => configuration.GetSection("MyInt").Path); + instance.MyString = value3; + } + + if (configuration["MyInt"] is string value4) + { + instance.MyInt = ParseInt(value4, () => configuration.GetSection("MyInt").Path); } else if (defaultValueIfNotFound) { instance.MyInt = default; } - } - public static void BindCore(IConfiguration configuration, ref Dictionary instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) - { - foreach (IConfigurationSection section in configuration.GetChildren()) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) { - if (section.Value is string value) - { - instance[section.Key] = value; - } + List? temp7 = instance.MyList; + temp7 ??= new List(); + BindCore(section5, ref temp7, defaultValueIfNotFound: false, binderOptions); + instance.MyList = temp7; } - } - public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) - { - foreach (IConfigurationSection section in configuration.GetChildren()) + if (AsConfigWithChildren(configuration.GetSection("MyList2")) is IConfigurationSection section8) { - var value = new Program.MyClass2(); - BindCore(section, ref value, defaultValueIfNotFound: false, binderOptions); - instance.Add(value); + List? temp10 = instance.MyList2; + temp10 ??= new List(); + BindCore(section8, ref temp10, defaultValueIfNotFound: false, binderOptions); + instance.MyList2 = temp10; } - } - public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) - { - foreach (IConfigurationSection section in configuration.GetChildren()) + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section11) { - if (section.Value is string value) - { - instance.Add(ParseInt32(value, () => section.Path)); - } + Dictionary? temp13 = instance.MyDictionary; + temp13 ??= new Dictionary(); + BindCore(section11, ref temp13, defaultValueIfNotFound: false, binderOptions); + instance.MyDictionary = temp13; } } @@ -234,7 +234,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return binderOptions; } - public static int ParseInt32(string value, Func getPath) + public static int ParseInt(string value, Func getPath) { try { 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 6c483b07dc5b3..b87d0c0a259cc 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 @@ -53,8 +53,8 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration #endregion IServiceCollection extensions. #region Core binding extensions. - private readonly static Lazy> s_configKeys_ProgramMyClass = new(() => new HashSet(StringComparer.OrdinalIgnoreCase) { "MyDictionary", "MyInt", "MyList", "MyList2", "MyString" }); 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 instance, Type type, Action? configureOptions) { @@ -80,92 +80,92 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration 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.MyClass instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) { - ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); - - if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section1) + foreach (IConfigurationSection section in configuration.GetChildren()) { - Dictionary? temp3 = instance.MyDictionary; - temp3 ??= new Dictionary(); - BindCore(section1, ref temp3, defaultValueIfNotFound: false, binderOptions); - instance.MyDictionary = temp3; + if (section.Value is string value) + { + instance.Add(ParseInt(value, () => section.Path)); + } } + } - if (configuration["MyInt"] is string value4) + public static void BindCore(IConfiguration configuration, ref Program.MyClass2 instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + { + ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions); + + if (configuration["MyInt"] is string value1) { - instance.MyInt = ParseInt32(value4, () => configuration.GetSection("MyInt").Path); + instance.MyInt = ParseInt(value1, () => configuration.GetSection("MyInt").Path); } else if (defaultValueIfNotFound) { instance.MyInt = default; } + } - if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) - { - List? temp7 = instance.MyList; - temp7 ??= new List(); - BindCore(section5, ref temp7, defaultValueIfNotFound: false, binderOptions); - instance.MyList = temp7; - } - - if (AsConfigWithChildren(configuration.GetSection("MyList2")) is IConfigurationSection section8) + public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + { + foreach (IConfigurationSection section in configuration.GetChildren()) { - List? temp10 = instance.MyList2; - temp10 ??= new List(); - BindCore(section8, ref temp10, defaultValueIfNotFound: false, binderOptions); - instance.MyList2 = temp10; + var value = new Program.MyClass2(); + BindCore(section, ref value, defaultValueIfNotFound: false, binderOptions); + instance.Add(value); } + } - if (configuration["MyString"] is string value11) + public static void BindCore(IConfiguration configuration, ref Dictionary instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + { + foreach (IConfigurationSection section in configuration.GetChildren()) { - instance.MyString = value11; + if (section.Value is string value) + { + instance[section.Key] = value; + } } } - public static void BindCore(IConfiguration configuration, ref Program.MyClass2 instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) + public static void BindCore(IConfiguration configuration, ref Program.MyClass instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) { - ValidateConfigurationKeys(typeof(Program.MyClass2), s_configKeys_ProgramMyClass2, configuration, binderOptions); + ValidateConfigurationKeys(typeof(Program.MyClass), s_configKeys_ProgramMyClass, configuration, binderOptions); - if (configuration["MyInt"] is string value12) + if (configuration["MyString"] is string value3) { - instance.MyInt = ParseInt32(value12, () => configuration.GetSection("MyInt").Path); + instance.MyString = value3; + } + + if (configuration["MyInt"] is string value4) + { + instance.MyInt = ParseInt(value4, () => configuration.GetSection("MyInt").Path); } else if (defaultValueIfNotFound) { instance.MyInt = default; } - } - public static void BindCore(IConfiguration configuration, ref Dictionary instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) - { - foreach (IConfigurationSection section in configuration.GetChildren()) + if (AsConfigWithChildren(configuration.GetSection("MyList")) is IConfigurationSection section5) { - if (section.Value is string value) - { - instance[section.Key] = value; - } + List? temp7 = instance.MyList; + temp7 ??= new List(); + BindCore(section5, ref temp7, defaultValueIfNotFound: false, binderOptions); + instance.MyList = temp7; } - } - public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) - { - foreach (IConfigurationSection section in configuration.GetChildren()) + if (AsConfigWithChildren(configuration.GetSection("MyList2")) is IConfigurationSection section8) { - var value = new Program.MyClass2(); - BindCore(section, ref value, defaultValueIfNotFound: false, binderOptions); - instance.Add(value); + List? temp10 = instance.MyList2; + temp10 ??= new List(); + BindCore(section8, ref temp10, defaultValueIfNotFound: false, binderOptions); + instance.MyList2 = temp10; } - } - public static void BindCore(IConfiguration configuration, ref List instance, bool defaultValueIfNotFound, BinderOptions? binderOptions) - { - foreach (IConfigurationSection section in configuration.GetChildren()) + if (AsConfigWithChildren(configuration.GetSection("MyDictionary")) is IConfigurationSection section11) { - if (section.Value is string value) - { - instance.Add(ParseInt32(value, () => section.Path)); - } + Dictionary? temp13 = instance.MyDictionary; + temp13 ??= new Dictionary(); + BindCore(section11, ref temp13, defaultValueIfNotFound: false, binderOptions); + instance.MyDictionary = temp13; } } @@ -228,7 +228,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration return binderOptions; } - public static int ParseInt32(string value, Func getPath) + public static int ParseInt(string value, Func getPath) { try { diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/ConfigBindingGenTestDriver.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/ConfigBindingGenTestDriver.cs index ef2765c28a33e..4373b404fc67f 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/ConfigBindingGenTestDriver.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/ConfigBindingGenTestDriver.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; +using System.Globalization; using System.Linq; using System.Reflection; using System.Threading; @@ -90,28 +92,33 @@ private async Task UpdateCompilationWithSource(string? source = null) } } } + } - internal struct ConfigBindingGenRunResult - { - public required Compilation OutputCompilation { get; init; } + internal struct ConfigBindingGenRunResult + { + public required Compilation OutputCompilation { get; init; } - public required GeneratedSourceResult? GeneratedSource { get; init; } + public required GeneratedSourceResult? GeneratedSource { get; init; } - /// - /// Diagnostics produced by the generator alone. Doesn't include any from other build participants. - /// - public required ImmutableArray Diagnostics { get; init; } + /// + /// Diagnostics produced by the generator alone. Doesn't include any from other build participants. + /// + public required ImmutableArray Diagnostics { get; init; } - public required ImmutableArray TrackedSteps { get; init; } + public required ImmutableArray TrackedSteps { get; init; } - public required SourceGenerationSpec? GenerationSpec { get; init; } - } + public required SourceGenerationSpec? GenerationSpec { get; init; } + } + + internal enum ExpectedDiagnostics + { + None, + FromGeneratorOnly, } internal static class ConfigBindingGenTestDriverExtensions { - public static void ValidateIncrementalResult( - this ConfigurationBindingGeneratorTests.ConfigBindingGenRunResult result, + public static void ValidateIncrementalResult(this ConfigBindingGenRunResult result, IncrementalStepRunReason inputReason, IncrementalStepRunReason outputReason) { @@ -121,5 +128,29 @@ public static void ValidateIncrementalResult( Assert.Collection(step.Outputs, output => Assert.Equal(outputReason, output.Reason)); }); } + + public static void ValidateDiagnostics(this ConfigBindingGenRunResult result, ExpectedDiagnostics expectedDiags) + { + ImmutableArray outputDiagnostics = result.OutputCompilation.GetDiagnostics(); + + if (expectedDiags is ExpectedDiagnostics.None) + { + foreach (Diagnostic diagnostic in outputDiagnostics) + { + Assert.True( + IsPermitted(diagnostic), + $"Generator caused dagnostic in output compilation: {diagnostic.GetMessage(CultureInfo.InvariantCulture)}."); + } + } + else + { + Debug.Assert(expectedDiags is ExpectedDiagnostics.FromGeneratorOnly); + + Assert.NotEmpty(result.Diagnostics); + Assert.False(outputDiagnostics.Any(diag => !IsPermitted(diag))); + } + + static bool IsPermitted(Diagnostic diagnostic) => diagnostic.Severity <= DiagnosticSeverity.Info; + } } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/GeneratorTests.Baselines.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/GeneratorTests.Baselines.cs index 01acddd49c8f3..e05a773713712 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/GeneratorTests.Baselines.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/GeneratorTests.Baselines.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.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis; @@ -314,6 +315,30 @@ public class MyClass4 await VerifyAgainstBaselineUsingFile("Get.generated.txt", source, extType: ExtensionClassType.ConfigurationBinder); } + [Fact] + public async Task Get_PrimitivesOnly() + { + string source = """ + using Microsoft.Extensions.Configuration; + + public class Program + { + public static void Main() + { + ConfigurationBuilder configurationBuilder = new(); + IConfigurationRoot config = configurationBuilder.Build(); + + config.Get(); + config.Get(typeof(string)); + config.Get(binderOptions => { }); + config.Get(typeof(double), binderOptions => { }); + } + } + """; + + await VerifyAgainstBaselineUsingFile("Get_PrimitivesOnly.generated.txt", source, extType: ExtensionClassType.ConfigurationBinder); + } + [Fact] public async Task Get_T() { @@ -719,7 +744,6 @@ public class MyClass } [Fact] - [ActiveIssue("Work out why we aren't getting all the expected diagnostics.")] public async Task Collections() { string source = """ @@ -737,6 +761,7 @@ public static void Main() section.Get(); } + // Diagnostic warning because we don't know how to instantiate two properties on this type. public class MyClassWithCustomCollections { public CustomDictionary CustomDictionary { get; set; } @@ -744,6 +769,7 @@ public class MyClassWithCustomCollections public ICustomDictionary ICustomDictionary { get; set; } public ICustomSet ICustomCollection { get; set; } public IReadOnlyList IReadOnlyList { get; set; } + // Diagnostic warning because we don't know how to instantiate the property type. public IReadOnlyDictionary UnsupportedIReadOnlyDictionaryUnsupported { get; set; } public IReadOnlyDictionary IReadOnlyDictionary { get; set; } } @@ -756,21 +782,26 @@ public class CustomList : List { } + // Diagnostic warning because we don't know how to instantiate this type. public interface ICustomDictionary : IDictionary { } + // Diagnostic warning because we don't know how to instantiate this type. public interface ICustomSet : ISet { } } """; - await VerifyAgainstBaselineUsingFile("Collections.generated.txt", source, validateOutputDiags: false, assessDiagnostics: (d) => - { - Assert.Equal(3, d.Where(diag => diag.Id == Diagnostics.TypeNotSupported.Id).Count()); - Assert.Equal(6, d.Where(diag => diag.Id == Diagnostics.PropertyNotSupported.Id).Count()); - }); + ConfigBindingGenRunResult result = await VerifyAgainstBaselineUsingFile( + "Collections.generated.txt", + source, + expectedDiags: ExpectedDiagnostics.FromGeneratorOnly); + + ImmutableArray diagnostics = result.Diagnostics; + Assert.Equal(3, diagnostics.Where(diag => diag.Id == Diagnostics.TypeNotSupported.Id).Count()); + Assert.Equal(3, diagnostics.Where(diag => diag.Id == Diagnostics.PropertyNotSupported.Id).Count()); } [Fact] @@ -812,14 +843,12 @@ public abstract class AbstractType_CannotInit } """; - await VerifyAgainstBaselineUsingFile( + ConfigBindingGenRunResult result = await VerifyAgainstBaselineUsingFile( "EmptyConfigType.generated.txt", source, - assessDiagnostics: (d) => - { - Assert.Equal(2, d.Where(diag => diag.Id == Diagnostics.TypeNotSupported.Id).Count()); - }, - validateOutputDiags: false); + expectedDiags: ExpectedDiagnostics.FromGeneratorOnly); + + Assert.Equal(2, result.Diagnostics.Where(diag => diag.Id == Diagnostics.TypeNotSupported.Id).Count()); } } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/GeneratorTests.Helpers.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/GeneratorTests.Helpers.cs index 6ad0ccdc6afa6..cbbd34e7fc41d 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/GeneratorTests.Helpers.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/GeneratorTests.Helpers.cs @@ -66,6 +66,7 @@ private static class Diagnostics } private static readonly Assembly[] s_compilationAssemblyRefs = new[] { + typeof(BitArray).Assembly, typeof(ConfigurationBinder).Assembly, typeof(ConfigurationBuilder).Assembly, typeof(CultureInfo).Assembly, @@ -91,19 +92,18 @@ private enum ExtensionClassType private static async Task VerifyThatSourceIsGenerated(string testSourceCode) { ConfigBindingGenRunResult result = await RunGeneratorAndUpdateCompilation(testSourceCode); - GeneratedSourceResult? source = result.GeneratedSource; + Assert.NotNull(source); Assert.Empty(result.Diagnostics); Assert.True(source.Value.SourceText.Lines.Count > 10); } - private static async Task VerifyAgainstBaselineUsingFile( + private static async Task VerifyAgainstBaselineUsingFile( string filename, string testSourceCode, - Action>? assessDiagnostics = null, ExtensionClassType extType = ExtensionClassType.None, - bool validateOutputDiags = true) + ExpectedDiagnostics expectedDiags = ExpectedDiagnostics.None) { string path = extType is ExtensionClassType.None ? Path.Combine("Baselines", filename) @@ -112,15 +112,13 @@ private static async Task VerifyAgainstBaselineUsingFile( string[] expectedLines = baseline.Replace("%VERSION%", typeof(ConfigurationBindingGenerator).Assembly.GetName().Version?.ToString()) .Split(Environment.NewLine); - ConfigBindingGenTestDriver genDriver = new(); ConfigBindingGenRunResult result = await RunGeneratorAndUpdateCompilation(testSourceCode); - Assert.NotNull(result.GeneratedSource); - GeneratedSourceResult generatedSource = result.GeneratedSource.Value; + result.ValidateDiagnostics(expectedDiags); - SourceText resultSourceText = generatedSource.SourceText; + SourceText resultSourceText = result.GeneratedSource.Value.SourceText; bool resultEqualsBaseline = RoslynTestUtils.CompareLines(expectedLines, resultSourceText, out string errorMessage); -#if !UPDATE_BASELINES +#if UPDATE_BASELINES if (!resultEqualsBaseline) { const string envVarName = "RepoRootDir"; @@ -138,27 +136,18 @@ private static async Task VerifyAgainstBaselineUsingFile( } #endif - assessDiagnostics ??= static (diagnostics) => Assert.Empty(diagnostics); - assessDiagnostics(result.Diagnostics); Assert.True(resultEqualsBaseline, errorMessage); + + return result; } private static async Task RunGeneratorAndUpdateCompilation( string source, LanguageVersion langVersion = LanguageVersion.CSharp12, - IEnumerable? assemblyReferences = null, - bool validateCompilationDiagnostics = false) + IEnumerable? assemblyReferences = null) { ConfigBindingGenTestDriver driver = new ConfigBindingGenTestDriver(langVersion, assemblyReferences); - ConfigBindingGenRunResult result = await driver.RunGeneratorAndUpdateCompilation(source); - - if (validateCompilationDiagnostics) - { - ImmutableArray compilationDiags = result.OutputCompilation.GetDiagnostics(); - Assert.False(compilationDiags.Any(d => d.Severity > DiagnosticSeverity.Info)); - } - - return result; + return await driver.RunGeneratorAndUpdateCompilation(source); } private static List GetAssemblyRefsWithAdditional(params Type[] additional) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/GeneratorTests.Incremental.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/GeneratorTests.Incremental.cs index 68c39100bb7c7..aff9a0c20364c 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/GeneratorTests.Incremental.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/GeneratorTests.Incremental.cs @@ -51,9 +51,9 @@ public async Task RunWithNoDiags_Then_ChangeInputOrder() result = await driver.RunGeneratorAndUpdateCompilation(BindCallSampleCodeVariant_ReorderedInvocations); result.ValidateIncrementalResult(IncrementalStepRunReason.Modified, IncrementalStepRunReason.Modified); - // No diff from previous step because type model is the same. + // We expect different spec because members are reordered. result = await driver.RunGeneratorAndUpdateCompilation(BindCallSampleCodeVariant_ReorderedConfigTypeMembers); - result.ValidateIncrementalResult(IncrementalStepRunReason.Modified, IncrementalStepRunReason.Unchanged); + result.ValidateIncrementalResult(IncrementalStepRunReason.Modified, IncrementalStepRunReason.Modified); } [Fact] @@ -93,7 +93,7 @@ public async Task RunWithDiags_Then_NoEdit() } [Fact] - public async Task RunWithDiags_Then_NoOpEdit() + public async Task RunWithDiags_Then_ChangeInputOrder() { ConfigBindingGenTestDriver driver = new ConfigBindingGenTestDriver(); @@ -104,9 +104,9 @@ public async Task RunWithDiags_Then_NoOpEdit() result = await driver.RunGeneratorAndUpdateCompilation(BindCallSampleCodeVariant_WithUnsupportedMember_ReorderedInvocations); result.ValidateIncrementalResult(IncrementalStepRunReason.Modified, IncrementalStepRunReason.Modified); - // No diff from previous step because type model is the same. + // We expect different spec because members are reordered. result = await driver.RunGeneratorAndUpdateCompilation(BindCallSampleCodeVariant_WithUnsupportedMember_ReorderedConfigTypeMembers); - result.ValidateIncrementalResult(IncrementalStepRunReason.Modified, IncrementalStepRunReason.Unchanged); + result.ValidateIncrementalResult(IncrementalStepRunReason.Modified, IncrementalStepRunReason.Modified); } [Fact] 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 f4b493becf711..848d93b32a475 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 @@ -33,6 +33,7 @@ + @@ -53,7 +54,6 @@ - @@ -66,4 +66,4 @@ - \ No newline at end of file + diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs index 4396bd1f349d5..e0dac6a9ad82c 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.DotnetRuntime.Extensions; using SourceGenerators; namespace System.Text.Json.SourceGeneration @@ -61,7 +60,7 @@ public void ReportDiagnostic(DiagnosticDescriptor descriptor, Location? location location = _contextClassLocation; } - Diagnostics.Add(DiagnosticInfo.Create(descriptor, location, messageArgs ?? Array.Empty())); + Diagnostics.Add(DiagnosticInfo.Create(descriptor, location, messageArgs)); } public Parser(KnownTypeSymbols knownSymbols)