using System;
using System.Collections.Generic;
using System.Diagnostics;
-using System.Text;
using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.Text;
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
public const string nullableSectionValue = "section?.Value";
public const string sectionKey = "section.Key";
public const string sectionValue = "section.Value";
+
+ public const string ConvertFromBase64String = "Convert.FromBase64String";
}
- private static class GlobalName
+ private static class FullyQualifiedDisplayName
{
- public const string Enum = "global::System.Enum";
- public const string FromBase64String = "global::System.Convert.FromBase64String";
+ public const string ArgumentNullException = "global::System.ArgumentNullException";
+ public const string Helpers = $"global::{GeneratorProjectName}.{Identifier.Helpers}";
public const string IConfiguration = "global::Microsoft.Extensions.Configuration.IConfiguration";
- public const string IConfigurationSection = "global::Microsoft.Extensions.Configuration.IConfigurationSection";
- public const string Int32 = "int";
+ public const string IConfigurationSection = IConfiguration + "Section";
+ public const string InvalidOperationException = "global::System.InvalidOperationException";
public const string IServiceCollection = "global::Microsoft.Extensions.DependencyInjection.IServiceCollection";
- public const string Object = "object";
- public const string String = "string";
+ public const string NotSupportedException = "global::System.NotSupportedException";
}
- private static class KeyWord
+ private enum InitializationKind
{
- public const string @default = nameof(@default);
- public const string @null = nameof(@null);
+ None = 0,
+ SimpleAssignment = 1,
+ AssignmentWithNullCheck = 2,
+ Declaration = 3,
}
private readonly SourceProductionContext _context;
private readonly SourceGenerationSpec _generationSpec;
- private readonly Queue<TypeSpec> _privateBindCoreMethodGen_QueuedTypes = new();
-
- private readonly HashSet<TypeSpec> _internalBindMethodGen_ProcessedTypes = new();
- private readonly HashSet<TypeSpec> _privateBindCoreMethodGen_ProcessedTypes = new();
-
// Postfix for stringValueX variables used to save config value indexer
// results e.g. if (configuration["Key"] is string stringValue0) { ... }
private int _parseValueCount;
private readonly SourceWriter _writer = new();
+ private readonly Regex _arrayBracketsRegex = new(Regex.Escape("[]"));
+
+ private bool _useFullyQualifiedNames = true;
+
public Emitter(SourceProductionContext context, SourceGenerationSpec generationSpec)
{
_context = context;
{
_writer.WriteLine(@"// <auto-generated/>
#nullable enable
-
-using System.Linq;
");
- _writer.WriteBlockStart($"internal static class {Literal.GeneratedConfigurationBinder}");
-
+ // Generated binder for user consumption.
+ _writer.WriteBlockStart($"internal static class {Identifier.GeneratedConfigurationBinder}");
EmitConfigureMethod();
-
EmitGetMethod();
-
EmitBindMethods();
+ _writer.WriteBlockEnd();
- EmitIConfigurationHasChildrenHelperMethod();
+ _writer.WriteBlankLine();
- _writer.WriteBlockEnd();
+ EmitGenerationNamespaceAndHelpers();
- SourceText source = SourceText.From(_writer.GetSource(), Encoding.UTF8);
- _context.AddSource($"{Literal.GeneratedConfigurationBinder}.g.cs", source);
+ _context.AddSource($"{Identifier.GeneratedConfigurationBinder}.g.cs", _writer.ToSourceText());
}
private void EmitConfigureMethod()
{
- if (_generationSpec.TypesForConfigureMethodGen.Count == 0)
+ if (!IncludeMethodsForGen(MethodSpecifier.Configure))
{
return;
}
- _writer.WriteBlockStart($"public static {GlobalName.IServiceCollection} {Literal.Configure}<T>(this {GlobalName.IServiceCollection} {Literal.services}, {GlobalName.IConfiguration} {Literal.configuration})");
+ _writer.WriteBlockStart($"public static {FullyQualifiedDisplayName.IServiceCollection} {Identifier.Configure}<T>(this {FullyQualifiedDisplayName.IServiceCollection} {Identifier.services}, {FullyQualifiedDisplayName.IConfiguration} {Identifier.configuration})");
- foreach (TypeSpec type in _generationSpec.TypesForConfigureMethodGen)
+ EmitCheckForNullArgument_WithBlankLine(Identifier.configuration, useFullyQualifiedNames: true);
+
+ foreach (TypeSpec type in _generationSpec.Methods[MethodSpecifier.Configure])
{
- string typeDisplayString = type.DisplayString;
+ string typeDisplayString = type.FullyQualifiedDisplayString;
_writer.WriteBlockStart($"if (typeof(T) == typeof({typeDisplayString}))");
- _writer.WriteBlockStart($@"return {Literal.services}.{Literal.Configure}<{typeDisplayString}>({Literal.obj} =>");
- EmitBindLogicFromIConfiguration(type, Literal.obj, InitializationKind.None);
+ _writer.WriteBlockStart($@"return {Identifier.services}.{Identifier.Configure}<{typeDisplayString}>({Identifier.obj} =>");
+ EmitIConfigurationHasValueOrChildrenCheck();
+ EmitBindLogicFromIConfiguration(type, Identifier.obj, InitializationKind.None);
_writer.WriteBlockEnd(");");
_writer.WriteBlockEnd();
Emit_NotSupportedException_UnableToBindType(NotSupportedReason.TypeNotDetectedAsInput);
_writer.WriteBlockEnd();
-
- _writer.WriteBlankLine();
}
private void EmitGetMethod()
{
- if (_generationSpec.TypesForGetMethodGen.Count == 0)
+ if (!IncludeMethodsForGen(MethodSpecifier.Get))
{
return;
}
- _writer.WriteBlockStart($"public static T? {Literal.Get}<T>(this {GlobalName.IConfiguration} {Literal.configuration})");
+ if (IncludeMethodsForGen(MethodSpecifier.Configure))
+ {
+ _writer.WriteBlankLine();
+ }
+
+ _writer.WriteBlockStart($"public static T? {Identifier.Get}<T>(this {FullyQualifiedDisplayName.IConfiguration} {Identifier.configuration})");
+
+ EmitCheckForNullArgument_WithBlankLine(Identifier.configuration, useFullyQualifiedNames: true);
- EmitCheckForNullArgument_WithBlankLine(Literal.configuration);
+ EmitIConfigurationHasValueOrChildrenCheck();
- foreach (TypeSpec type in _generationSpec.TypesForGetMethodGen)
+ foreach (TypeSpec type in _generationSpec.Methods[MethodSpecifier.Get])
{
- string typeDisplayString = type.DisplayString;
+ string typeDisplayString = type.FullyQualifiedDisplayString;
_writer.WriteBlockStart($"if (typeof(T) == typeof({typeDisplayString}))");
- EmitBindLogicFromIConfiguration(type, Literal.obj, InitializationKind.Declaration);
- _writer.WriteLine($"return (T)({GlobalName.Object}){Literal.obj};");
+ EmitBindLogicFromIConfiguration(type, Identifier.obj, InitializationKind.Declaration);
+ _writer.WriteLine($"return (T)(object){Identifier.obj};");
_writer.WriteBlockEnd();
_writer.WriteBlankLine();
}
Emit_NotSupportedException_UnableToBindType(NotSupportedReason.TypeNotDetectedAsInput);
_writer.WriteBlockEnd();
- _writer.WriteBlankLine();
}
private void EmitBindMethods()
{
- if (_generationSpec.TypesForBindMethodGen.Count > 0)
+ if (!IncludeMethodsForGen(MethodSpecifier.Bind))
{
- foreach (TypeSpec type in _generationSpec.TypesForBindMethodGen)
- {
- EmitBindMethod(type);
- }
+ return;
}
- // Get & Configure method generation might have queued types for private BindCore impl
- while (_privateBindCoreMethodGen_QueuedTypes.Count > 0)
+ if (IncludeMethodsForGen(MethodSpecifier.Configure | MethodSpecifier.Get))
{
- EmitBindCoreMethod(_privateBindCoreMethodGen_QueuedTypes.Dequeue());
+ _writer.WriteBlankLine();
+ }
+
+ foreach (TypeSpec type in _generationSpec.Methods[MethodSpecifier.Bind])
+ {
+ EmitBindMethod(type);
+ _writer.WriteBlankLine();
}
+
+ _writer.RemoveBlankLine();
}
private void EmitBindMethod(TypeSpec type)
{
- if (_internalBindMethodGen_ProcessedTypes.Contains(type))
- {
- return;
- }
+ _writer.WriteLine(
+ @$"public static void {Identifier.Bind}(this {FullyQualifiedDisplayName.IConfiguration} {Identifier.configuration}, {type.FullyQualifiedDisplayString} {Identifier.obj}) => " +
+ $"{FullyQualifiedDisplayName.Helpers}.{Identifier.BindCore}({Identifier.configuration}, ref {Identifier.obj});");
+ }
- _internalBindMethodGen_ProcessedTypes.Add(type);
+ private void EmitGenerationNamespaceAndHelpers()
+ {
+ _useFullyQualifiedNames = false;
- // Binding to root level struct is a no-op.
- // TODO: maybe this should be a debug assert & the parser shouldn't include them.
- if (type.IsValueType)
+ if (IncludeMethodsForGen(MethodSpecifier.BindCore | MethodSpecifier.HasValueOrChildren | MethodSpecifier.HasChildren))
{
- return;
- }
+ // Helper class in source-generation namespace.
+ _writer.WriteBlockStart($"namespace {GeneratorProjectName}");
+ EmitHelperUsingStatements();
- _privateBindCoreMethodGen_QueuedTypes.Enqueue(type);
+ _writer.WriteBlankLine();
- _writer.WriteLine(
- @$"public static void {Literal.Bind}(this {GlobalName.IConfiguration} {Literal.configuration}, {type.DisplayString} {Literal.obj}) => " +
- $"{Literal.BindCore}({Literal.configuration}, ref {Literal.obj});");
- _writer.WriteBlankLine();
+ _writer.WriteBlockStart($"internal static class {Identifier.Helpers}");
+ EmitBindCoreMethods();
+ EmitHelperMethods();
+ _writer.WriteBlockEnd();
+
+ _writer.WriteBlockEnd();
+ }
}
- private void EmitBindCoreMethod(TypeSpec type)
+ private void EmitHelperUsingStatements()
{
- if (_privateBindCoreMethodGen_ProcessedTypes.Contains(type))
+ foreach (string @namespace in _generationSpec.Namespaces)
{
- return;
+ _writer.WriteLine($"using {@namespace};");
}
- _privateBindCoreMethodGen_ProcessedTypes.Add(type);
+ }
- string objParameterExpression = $"ref {type.DisplayString} {Literal.obj}";
- _writer.WriteBlockStart(@$"private static void {Literal.BindCore}({GlobalName.IConfiguration} {Literal.configuration}, {objParameterExpression})");
+ private void EmitBindCoreMethods()
+ {
+ foreach (TypeSpec type in _generationSpec.Methods[MethodSpecifier.BindCore])
+ {
+ EmitBindCoreMethod(type);
+ _writer.WriteBlankLine();
+ }
+ }
+
+ private void EmitBindCoreMethod(TypeSpec type)
+ {
+ string objParameterExpression = $"ref {type.MinimalDisplayString} {Identifier.obj}";
+ _writer.WriteBlockStart(@$"public static void {Identifier.BindCore}({Identifier.IConfiguration} {Identifier.configuration}, {objParameterExpression})");
EmitBindCoreImpl(type);
_writer.WriteBlockEnd();
- _writer.WriteBlankLine();
}
private void EmitBindCoreImpl(TypeSpec type)
case TypeSpecKind.IConfigurationSection:
{
EmitCastToIConfigurationSection();
- EmitAssignment(Literal.obj, Literal.section);
+ EmitAssignment(Identifier.obj, Identifier.section);
}
break;
case TypeSpecKind.Dictionary:
EmitCheckForNullArgument_WithBlankLine_IfRequired(isValueType: false);
- string tempVarName = GetIncrementalVarName(Literal.temp);
+ string tempVarName = GetIncrementalVarName(Identifier.temp);
// Create and bind to temp list
- EmitBindCoreCall(concreteType, tempVarName, Literal.configuration, InitializationKind.Declaration);
+ EmitBindCoreCall(concreteType, tempVarName, Identifier.configuration, InitializationKind.Declaration);
// Resize array and copy fill with additional
- EmitAssignment($"{GlobalName.Int32} {Literal.originalCount}", $"{Literal.obj}.{Literal.Length}");
- _writer.WriteLine($"{TypeFullName.Array}.{Literal.Resize}(ref {Literal.obj}, {Literal.originalCount} + {tempVarName}.{Literal.Count});");
- _writer.WriteLine($"{tempVarName}.{Literal.CopyTo}({Literal.obj}, {Literal.originalCount});");
+ _writer.WriteBlock($$"""
+ {{Identifier.Int32}} {{Identifier.originalCount}} = {{Identifier.obj}}.{{Identifier.Length}};
+ {{Identifier.Array}}.{{Identifier.Resize}}(ref {{Identifier.obj}}, {{Identifier.originalCount}} + {{tempVarName}}.{{Identifier.Count}});
+ {{tempVarName}}.{{Identifier.CopyTo}}({{Identifier.obj}}, {{Identifier.originalCount}});
+ """);
}
private void EmitBindCoreImplForDictionary(DictionarySpec type)
TypeSpec keyType = type.KeyType;
TypeSpec elementType = type.ElementType;
- EmitVarDeclaration(keyType, Literal.key);
+ EmitVarDeclaration(keyType, Identifier.key);
- _writer.WriteBlockStart($"foreach ({TypeFullName.IConfigurationSection} {Literal.section} in {Literal.configuration}.{Literal.GetChildren}())");
+ _writer.WriteBlockStart($"foreach ({Identifier.IConfigurationSection} {Identifier.section} in {Identifier.configuration}.{Identifier.GetChildren}())");
// Parse key
EmitBindLogicFromString(
keyType,
- Literal.key,
+ Identifier.key,
expressionForConfigStringValue: Expression.sectionKey,
writeExtraOnSuccess: Emit_BindAndAddLogic_ForElement);
// For simple types: do regular dictionary add
if (elementType.SpecKind == TypeSpecKind.StringBasedParse)
{
- EmitVarDeclaration(elementType, Literal.element);
+ EmitVarDeclaration(elementType, Identifier.element);
EmitBindLogicFromIConfigurationSectionValue(
elementType,
- Literal.element,
+ Identifier.element,
InitializationKind.SimpleAssignment,
- writeExtraOnSuccess: () => EmitAssignment($"{Literal.obj}[{Literal.key}]", Literal.element));
+ writeExtraOnSuccess: () => EmitAssignment($"{Identifier.obj}[{Identifier.key}]", Identifier.element));
}
else // For complex types:
{
- string displayString = elementType.DisplayString + (elementType.IsValueType ? string.Empty : "?");
+ string displayString = elementType.MinimalDisplayString + (elementType.IsValueType ? string.Empty : "?");
// If key already exists, bind to value to existing element instance if not null (for ref types)
- string conditionToUseExistingElement = $"if ({Literal.obj}.{Literal.TryGetValue}({Literal.key}, out {displayString} {Literal.element})";
+ string conditionToUseExistingElement = $"if ({Identifier.obj}.{Identifier.TryGetValue}({Identifier.key}, out {displayString} {Identifier.element})";
conditionToUseExistingElement += !elementType.IsValueType
- ? $" && {Literal.element} is not {KeyWord.@null})"
+ ? $" && {Identifier.element} is not null)"
: ")";
_writer.WriteBlockStart(conditionToUseExistingElement);
EmitBindLogicForElement(InitializationKind.None);
void EmitBindLogicForElement(InitializationKind initKind)
{
- EmitBindLogicFromIConfigurationSectionValue(elementType, Literal.element, initKind);
- EmitAssignment($"{Literal.obj}[{Literal.key}]", Literal.element);
+ EmitBindLogicFromIConfigurationSectionValue(elementType, Identifier.element, initKind);
+ EmitAssignment($"{Identifier.obj}[{Identifier.key}]", Identifier.element);
}
}
}
TypeSpec elementType = type.ElementType;
- EmitVarDeclaration(elementType, Literal.element);
- _writer.WriteBlockStart($"foreach ({TypeFullName.IConfigurationSection} {Literal.section} in {Literal.configuration}.{Literal.GetChildren}())");
+ EmitVarDeclaration(elementType, Identifier.element);
+ _writer.WriteBlockStart($"foreach ({Identifier.IConfigurationSection} {Identifier.section} in {Identifier.configuration}.{Identifier.GetChildren}())");
EmitBindLogicFromIConfigurationSectionValue(
elementType,
- Literal.element,
+ Identifier.element,
InitializationKind.SimpleAssignment,
writeExtraOnSuccess: EmitAddLogicForElement);
void EmitAddLogicForElement()
{
- string addExpression = $"{Literal.obj}.{Literal.Add}({Literal.element})";
+ string addExpression = $"{Identifier.obj}.{Identifier.Add}({Identifier.element})";
if (elementType.IsValueType)
{
_writer.WriteLine($"{addExpression};");
}
else
{
- _writer.WriteLine($"if ({Literal.element} is not {KeyWord.@null}) {{ {addExpression}; }}");
+ _writer.WriteLine($"if ({Identifier.element} is not null) {{ {addExpression}; }}");
}
}
private void EmitBindCoreImplForObject(ObjectSpec type)
{
+ List<PropertySpec> properties = type.Properties;
+ if (properties.Count == 0)
+ {
+ return;
+ }
+
EmitCheckForNullArgument_WithBlankLine_IfRequired(type.IsValueType);
foreach (PropertySpec property in type.Properties)
EmitBindCoreImplForProperty(property, propertyType, parentType: type);
_writer.WriteBlankLine();
}
+
+ _writer.RemoveBlankLine();
}
private void EmitBindCoreImplForProperty(PropertySpec property, TypeSpec propertyType, TypeSpec parentType)
{
string configurationKeyName = property.ConfigurationKeyName;
- string propertyParentReference = property.IsStatic ? parentType.DisplayString : Literal.obj;
+ string propertyParentReference = property.IsStatic ? parentType.MinimalDisplayString : Identifier.obj;
string expressionForPropertyAccess = $"{propertyParentReference}.{property.Name}";
- string expressionForConfigSectionAccess = $@"{Literal.configuration}.{Literal.GetSection}(""{configurationKeyName}"")";
- string expressionForConfigValueIndexer = $@"{Literal.configuration}[""{configurationKeyName}""]";
+ string expressionForConfigSectionAccess = $@"{Identifier.configuration}.{Identifier.GetSection}(""{configurationKeyName}"")";
+ string expressionForConfigValueIndexer = $@"{Identifier.configuration}[""{configurationKeyName}""]";
bool canGet = property.CanGet;
bool canSet = property.CanSet;
{
if (type.SpecKind is TypeSpecKind.StringBasedParse or TypeSpecKind.ByteArray)
{
- EmitCastToIConfigurationSection();
if (initKind is InitializationKind.Declaration)
{
- EmitAssignment($"{type.DisplayString} {expressionForMemberAccess}", KeyWord.@default);
+ EmitCastToIConfigurationSection();
+ EmitAssignment($"{GetTypeDisplayString(type)} {expressionForMemberAccess}", "default");
+ }
+ else
+ {
+ EmitCastToIConfigurationSection();
}
EmitBindLogicFromString(type, expressionForMemberAccess, Expression.sectionValue);
}
else
{
- if (initKind is InitializationKind.Declaration)
- {
- EmitAssignment($"{TypeFullName.IConfigurationSection}? {Literal.section}", $"{Literal.configuration} as {TypeFullName.IConfigurationSection}");
- _writer.WriteBlockStart($"if ({Expression.nullableSectionValue} is null && !{Literal.configuration}.{Literal.GetChildren}().{Literal.Any}())");
- _writer.WriteLine($"return {KeyWord.@default};");
- _writer.WriteBlockEnd();
- _writer.WriteBlankLine();
- }
-
- EmitBindCoreCall(type, expressionForMemberAccess, Literal.configuration, initKind);
+ EmitBindCoreCall(type, expressionForMemberAccess, Identifier.configuration, initKind);
}
}
}
else
{
- EmitBindCoreCall(type, expressionForMemberAccess, Literal.section, initKind);
+ EmitBindCoreCall(type, expressionForMemberAccess, Identifier.section, initKind);
writeExtraOnSuccess?.Invoke();
}
}
string expressionForConfigArg,
InitializationKind initKind)
{
- string tempVarName = GetIncrementalVarName(Literal.temp);
+ string tempVarName = GetIncrementalVarName(Identifier.temp);
if (initKind is InitializationKind.AssignmentWithNullCheck)
{
- EmitAssignment($"{type.DisplayString} {tempVarName}", $"{expressionForMemberAccess}");
+ EmitAssignment($"{type.MinimalDisplayString} {tempVarName}", $"{expressionForMemberAccess}");
EmitObjectInit(type, tempVarName, InitializationKind.AssignmentWithNullCheck);
- _writer.WriteLine($@"{Literal.BindCore}({expressionForConfigArg}, ref {tempVarName});");
+ EmitBindCoreCall(tempVarName);
}
else if (initKind is InitializationKind.None && type.IsValueType)
{
EmitObjectInit(type, tempVarName, InitializationKind.Declaration);
- _writer.WriteLine($@"{Literal.BindCore}({expressionForConfigArg}, ref {tempVarName});");
+ _writer.WriteLine($@"{Identifier.BindCore}({expressionForConfigArg}, ref {tempVarName});");
EmitAssignment(expressionForMemberAccess, tempVarName);
}
else
{
EmitObjectInit(type, expressionForMemberAccess, initKind);
- _writer.WriteLine($@"{Literal.BindCore}({expressionForConfigArg}, ref {expressionForMemberAccess});");
+ EmitBindCoreCall(expressionForMemberAccess);
}
- _privateBindCoreMethodGen_QueuedTypes.Enqueue(type);
+ void EmitBindCoreCall(string varName)
+ {
+ string bindCoreCall = $@"{GetHelperMethodDisplayString(Identifier.BindCore)}({expressionForConfigArg}, ref {varName});";
+ _writer.WriteLine(bindCoreCall);
+ }
}
private void EmitBindCoreCallForProperty(
string expressionForPropertyAccess,
string expressionForConfigSectionAccess)
{
- string bindCoreConfigArg = GetIncrementalVarName(Literal.section);
- EmitAssignment($"{GlobalName.IConfigurationSection} {bindCoreConfigArg}", expressionForConfigSectionAccess);
- _writer.WriteBlockStart($"if ({Literal.HasChildren}({bindCoreConfigArg}))");
+ string bindCoreConfigArg = GetIncrementalVarName(Identifier.section);
+ EmitAssignment($"{Identifier.IConfigurationSection} {bindCoreConfigArg}", expressionForConfigSectionAccess);
+ _writer.WriteBlockStart($"if ({Identifier.HasChildren}({bindCoreConfigArg}))");
bool canGet = property.CanGet;
bool canSet = property.CanSet;
+ string effectivePropertyTypeDisplayString = effectivePropertyType.MinimalDisplayString;
- string tempVarName = GetIncrementalVarName(Literal.temp);
+ string tempVarName = GetIncrementalVarName(Identifier.temp);
if (effectivePropertyType.IsValueType)
{
if (canSet)
TypeSpec actualPropertyType = property.Type;
if (actualPropertyType.SpecKind is TypeSpecKind.Nullable)
{
- string nullableTempVarName = GetIncrementalVarName(Literal.temp);
+ string nullableTempVarName = GetIncrementalVarName(Identifier.temp);
EmitAssignment(
- $"{actualPropertyType.DisplayString} {nullableTempVarName}", expressionForPropertyAccess);
+ $"{actualPropertyType.MinimalDisplayString} {nullableTempVarName}", expressionForPropertyAccess);
EmitAssignment(
- $"{effectivePropertyType.DisplayString} {tempVarName}",
- $"{nullableTempVarName}.{Literal.HasValue} ? {nullableTempVarName}.{Literal.Value} : new {effectivePropertyType.DisplayString}()");
+ $"{effectivePropertyTypeDisplayString} {tempVarName}",
+ $"{nullableTempVarName}.{Identifier.HasValue} ? {nullableTempVarName}.{Identifier.Value} : new {effectivePropertyTypeDisplayString}()");
}
else
{
- EmitAssignment($"{effectivePropertyType.DisplayString} {tempVarName}", $"{expressionForPropertyAccess}");
+ EmitAssignment($"{effectivePropertyTypeDisplayString} {tempVarName}", $"{expressionForPropertyAccess}");
}
}
else
EmitObjectInit(effectivePropertyType, tempVarName, InitializationKind.Declaration);
}
- _writer.WriteLine($@"{Literal.BindCore}({bindCoreConfigArg}, ref {tempVarName});");
+ _writer.WriteLine($@"{Identifier.BindCore}({bindCoreConfigArg}, ref {tempVarName});");
EmitAssignment(expressionForPropertyAccess, tempVarName);
- _privateBindCoreMethodGen_QueuedTypes.Enqueue(effectivePropertyType);
}
}
else if (canGet)
{
- EmitAssignment($"{effectivePropertyType.DisplayString} {tempVarName}", $"{expressionForPropertyAccess}");
+ EmitAssignment($"{effectivePropertyTypeDisplayString} {tempVarName}", $"{expressionForPropertyAccess}");
EmitObjectInit(effectivePropertyType, tempVarName, InitializationKind.AssignmentWithNullCheck);
- _writer.WriteLine($@"{Literal.BindCore}({bindCoreConfigArg}, ref {tempVarName});");
+ _writer.WriteLine($@"{Identifier.BindCore}({bindCoreConfigArg}, ref {tempVarName});");
if (canSet)
{
{
Debug.Assert(canSet);
EmitObjectInit(effectivePropertyType, tempVarName, InitializationKind.Declaration);
- _writer.WriteLine($@"{Literal.BindCore}({bindCoreConfigArg}, ref {tempVarName});");
+ _writer.WriteLine($@"{Identifier.BindCore}({bindCoreConfigArg}, ref {tempVarName});");
EmitAssignment(expressionForPropertyAccess, tempVarName);
}
_writer.WriteBlockEnd();
-
- _privateBindCoreMethodGen_QueuedTypes.Enqueue(effectivePropertyType);
}
private void EmitBindLogicFromString(
string expressionForConfigStringValue,
Action? writeExtraOnSuccess = null)
{
- string typeDisplayString = type.DisplayString;
- string stringValueVarName = GetIncrementalVarName(Literal.stringValue);
- string assignmentCondition = $"{expressionForConfigStringValue} is {GlobalName.String} {stringValueVarName}";
+ string typeDisplayString = type.FullyQualifiedDisplayString;
+ string stringValueVarName = GetIncrementalVarName(Identifier.stringValue);
+ string assignmentCondition = $"{expressionForConfigStringValue} is string {stringValueVarName}";
string rhs;
if (type.SpecialType != SpecialType.None)
rhs = type.SpecialType switch
{
SpecialType.System_String => stringValueVarName,
- SpecialType.System_Object => KeyWord.@default,
- _ => $"{typeDisplayString}.{Literal.Parse}({stringValueVarName})"
+ SpecialType.System_Object => "default",
+ _ => $"{typeDisplayString}.{Identifier.Parse}({stringValueVarName})"
};
}
else if (type.SpecKind == TypeSpecKind.Enum)
{
- string enumValueVarName = GetIncrementalVarName(Literal.enumValue);
- assignmentCondition += $" && {GlobalName.Enum}.{Literal.TryParse}({stringValueVarName}, true, out {typeDisplayString} {enumValueVarName})";
+ string enumValueVarName = GetIncrementalVarName(Identifier.enumValue);
+ assignmentCondition += $" && {Identifier.Enum}.{Identifier.TryParse}({stringValueVarName}, true, out {typeDisplayString} {enumValueVarName})";
rhs = enumValueVarName;
}
else if (type.SpecKind == TypeSpecKind.ByteArray)
{
- rhs = $"{GlobalName.FromBase64String}({stringValueVarName})";
+ rhs = $"{Expression.ConvertFromBase64String}({stringValueVarName})";
}
else
{
return;
}
- string displayString = type.DisplayString;
+ string displayString = GetTypeDisplayString(type);
+
string expressionForInit = null;
if (type is EnumerableSpec { SpecKind: TypeSpecKind.Array } arrayType)
{
- Regex regex = new(Regex.Escape("[]"));
- expressionForInit = $"new {regex.Replace(type.DisplayString, "[0]", 1)};";
+ expressionForInit = $"new {_arrayBracketsRegex.Replace(displayString, "[0]", 1)};";
}
else if (type.ConstructionStrategy != ConstructionStrategy.ParameterlessConstructor)
{
}
else if (type is CollectionSpec { ConcreteType: { } concreteType })
{
- displayString = concreteType.DisplayString;
+ displayString = GetTypeDisplayString(concreteType);
}
// Not an array.
if (initKind == InitializationKind.Declaration)
{
Debug.Assert(!expressionForMemberAccess.Contains("."));
- EmitAssignment($"{displayString} {expressionForMemberAccess}", expressionForInit);
+ EmitAssignment($"var {expressionForMemberAccess}", expressionForInit);
}
else if (initKind == InitializationKind.AssignmentWithNullCheck)
{
}
}
- private void EmitCastToIConfigurationSection()
+ private void EmitIConfigurationHasValueOrChildrenCheck()
{
- _writer.WriteBlockStart($"if ({Literal.configuration} is not {TypeFullName.IConfigurationSection} {Literal.section})");
- _writer.WriteLine("throw new global::System.InvalidOperationException();");
- _writer.WriteBlockEnd();
+ _writer.WriteBlock($$"""
+ if (!{{GetHelperMethodDisplayString(Identifier.HasValueOrChildren)}}({{Identifier.configuration}}))
+ {
+ return default;
+ }
+ """);
+ _writer.WriteBlankLine();
}
- private void EmitIConfigurationHasChildrenHelperMethod()
+ private void EmitHelperMethods()
{
- _writer.WriteBlockStart($"public static bool {Literal.HasChildren}({GlobalName.IConfiguration} {Literal.configuration})");
- _writer.WriteBlockStart($"foreach ({GlobalName.IConfigurationSection} {Literal.section} in {Literal.configuration}.{Literal.GetChildren}())");
- _writer.WriteLine($"return true;");
- _writer.WriteBlockEnd();
- _writer.WriteLine($"return false;");
- _writer.WriteBlockEnd();
+ if (IncludeMethodsForGen(MethodSpecifier.HasValueOrChildren))
+ {
+ EmitHasValueOrChildrenMethod();
+ _writer.WriteBlankLine();
+ EmitHasChildrenMethod();
+ }
+ else if (IncludeMethodsForGen(MethodSpecifier.HasChildren))
+ {
+ EmitHasChildrenMethod();
+ }
+ }
+
+ private void EmitHasValueOrChildrenMethod()
+ {
+ _writer.WriteBlock($$"""
+ public static bool {{Identifier.HasValueOrChildren}}({{Identifier.IConfiguration}} {{Identifier.configuration}})
+ {
+ if (({{Identifier.configuration}} as {{Identifier.IConfigurationSection}})?.{{Identifier.Value}} is not null)
+ {
+ return true;
+ }
+ return {{Identifier.HasChildren}}({{Identifier.configuration}});
+ }
+ """);
+ }
+
+ private void EmitHasChildrenMethod()
+ {
+ _writer.WriteBlock($$"""
+ public static bool {{Identifier.HasChildren}}({{Identifier.IConfiguration}} {{Identifier.configuration}})
+ {
+ foreach ({{Identifier.IConfigurationSection}} {{Identifier.section}} in {{Identifier.configuration}}.{{Identifier.GetChildren}}())
+ {
+ return true;
+ }
+ return false;
+ }
+ """);
}
- private void EmitVarDeclaration(TypeSpec type, string varName) => _writer.WriteLine($"{type.DisplayString} {varName};");
+ private void EmitVarDeclaration(TypeSpec type, string varName) => _writer.WriteLine($"{type.MinimalDisplayString} {varName};");
private void EmitAssignment(string lhsSource, string rhsSource) => _writer.WriteLine($"{lhsSource} = {rhsSource};");
+ private void EmitCastToIConfigurationSection()
+ {
+ string sectionTypeDisplayString;
+ string exceptionTypeDisplayString;
+ if (_useFullyQualifiedNames)
+ {
+ sectionTypeDisplayString = FullyQualifiedDisplayName.IConfigurationSection;
+ exceptionTypeDisplayString = FullyQualifiedDisplayName.InvalidOperationException;
+ }
+ else
+ {
+ sectionTypeDisplayString = Identifier.IConfigurationSection;
+ exceptionTypeDisplayString = nameof(InvalidOperationException);
+ }
+
+ _writer.WriteBlock($$"""
+ if ({{Identifier.configuration}} is not {{sectionTypeDisplayString}} {{Identifier.section}})
+ {
+ throw new {{exceptionTypeDisplayString}}();
+ }
+ """);
+ }
+
private void Emit_NotSupportedException_UnableToBindType(string reason, string typeDisplayString = "{typeof(T)}") =>
- _writer.WriteLine(@$"throw new global::System.NotSupportedException($""{string.Format(ExceptionMessages.TypeNotSupported, typeDisplayString, reason)}"");");
+ _writer.WriteLine(@$"throw new {FullyQualifiedDisplayName.NotSupportedException}($""{string.Format(ExceptionMessages.TypeNotSupported, typeDisplayString, reason)}"");");
private void EmitCheckForNullArgument_WithBlankLine_IfRequired(bool isValueType)
{
if (!isValueType)
{
- EmitCheckForNullArgument_WithBlankLine(Literal.obj);
+ EmitCheckForNullArgument_WithBlankLine(Identifier.obj);
}
}
- private void EmitCheckForNullArgument_WithBlankLine(string argName)
+ private void EmitCheckForNullArgument_WithBlankLine(string argName, bool useFullyQualifiedNames = false)
{
- _writer.WriteBlockStart($"if ({argName} is {KeyWord.@null})");
- _writer.WriteLine($"throw new global::System.ArgumentNullException(nameof({argName}));");
- _writer.WriteBlockEnd();
+ string exceptionTypeDisplayString = useFullyQualifiedNames
+ ? FullyQualifiedDisplayName.ArgumentNullException
+ : Identifier.ArgumentNullException;
+
+ _writer.WriteBlock($$"""
+ if ({{argName}} is null)
+ {
+ throw new {{exceptionTypeDisplayString}}(nameof({{argName}}));
+ }
+ """);
+
_writer.WriteBlankLine();
}
+ private bool IncludeMethodsForGen(MethodSpecifier method)
+ => (_generationSpec.MethodsToGen & method) != 0;
+
private string GetIncrementalVarName(string prefix) => $"{prefix}{_parseValueCount++}";
+
+ private string GetTypeDisplayString(TypeSpec type) => _useFullyQualifiedNames ? type.FullyQualifiedDisplayString : type.MinimalDisplayString;
+
+ private string GetHelperMethodDisplayString(string methodName)
+ {
+ if (_useFullyQualifiedNames)
+ {
+ methodName = FullyQualifiedDisplayName.Helpers + "." + methodName;
+ }
+
+ return methodName;
+ }
}
}
}
using System.Diagnostics;
using System.Text;
using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
public const string TypeNotSupported = "Unable to bind to type '{0}': '{1}'";
}
- private static class Literal
+ private static class Identifier
{
public const string configuration = nameof(configuration);
public const string element = nameof(element);
public const string Add = nameof(Add);
public const string Any = nameof(Any);
+ public const string ArgumentNullException = nameof(ArgumentNullException);
+ public const string Array = nameof(Array);
public const string Bind = nameof(Bind);
public const string BindCore = nameof(BindCore);
public const string Configure = nameof(Configure);
public const string CopyTo = nameof(CopyTo);
public const string ContainsKey = nameof(ContainsKey);
public const string Count = nameof(Count);
+ public const string Enum = nameof(Enum);
public const string GeneratedConfigurationBinder = nameof(GeneratedConfigurationBinder);
public const string Get = nameof(Get);
public const string GetChildren = nameof(GetChildren);
public const string GetSection = nameof(GetSection);
public const string HasChildren = nameof(HasChildren);
+ public const string HasValueOrChildren = nameof(HasValueOrChildren);
public const string HasValue = nameof(HasValue);
+ public const string Helpers = nameof(Helpers);
public const string IConfiguration = nameof(IConfiguration);
public const string IConfigurationSection = nameof(IConfigurationSection);
+ public const string Int32 = "int";
public const string Length = nameof(Length);
public const string Parse = nameof(Parse);
public const string Resize = nameof(Resize);
private static class TypeFullName
{
- public const string Array = "System.Array";
public const string ConfigurationKeyNameAttribute = "Microsoft.Extensions.Configuration.ConfigurationKeyNameAttribute";
public const string Dictionary = "System.Collections.Generic.Dictionary`2";
public const string GenericIDictionary = "System.Collections.Generic.IDictionary`2";
public const string HashSet = "System.Collections.Generic.HashSet`1";
- public const string ISet = "System.Collections.Generic.ISet`1";
- public const string IConfigurationSection = "Microsoft.Extensions.Configuration.IConfigurationSection";
public const string IConfiguration = "Microsoft.Extensions.Configuration.IConfiguration";
+ public const string IConfigurationSection = "Microsoft.Extensions.Configuration.IConfigurationSection";
public const string IDictionary = "System.Collections.Generic.IDictionary";
+ public const string ISet = "System.Collections.Generic.ISet`1";
public const string IServiceCollection = "Microsoft.Extensions.DependencyInjection.IServiceCollection";
public const string List = "System.Collections.Generic.List`1";
}
private static bool TypesAreEqual(ITypeSymbol first, ITypeSymbol second)
=> first.Equals(second, SymbolEqualityComparer.Default);
-
- private enum InitializationKind
- {
- None = 0,
- SimpleAssignment = 1,
- AssignmentWithNullCheck = 2,
- Declaration = 3,
- }
-
- private sealed class SourceWriter
- {
- private readonly StringBuilder _sb = new();
- private int _indentationLevel;
-
- public int Length => _sb.Length;
- public int IndentationLevel => _indentationLevel;
-
- public void WriteBlockStart(string declaration)
- {
- WriteLine(declaration);
- WriteLine("{");
- _indentationLevel++;
- }
-
- public void WriteBlockEnd(string? extra = null)
- {
- _indentationLevel--;
- Debug.Assert(_indentationLevel > -1);
- WriteLine($"}}{extra}");
- }
-
- public void WriteLine(string source)
- {
- _sb.Append(' ', 4 * _indentationLevel);
- _sb.AppendLine(source);
- }
-
- public void WriteBlankLine() => _sb.AppendLine();
-
- public string GetSource() => _sb.ToString();
- }
}
}
{
private sealed class Parser
{
+ private const string GlobalNameSpaceString = "<global namespace>";
+
private readonly SourceProductionContext _context;
private readonly KnownTypeData _typeData;
private readonly HashSet<TypeSpec> _typesForBindMethodGen = new();
private readonly HashSet<TypeSpec> _typesForGetMethodGen = new();
private readonly HashSet<TypeSpec> _typesForConfigureMethodGen = new();
+ private readonly HashSet<TypeSpec> _typesForBindCoreMethodGen = new();
+
private readonly HashSet<ITypeSymbol> _unsupportedTypes = new(SymbolEqualityComparer.Default);
private readonly Dictionary<ITypeSymbol, TypeSpec?> _createdSpecs = new(SymbolEqualityComparer.Default);
+ private readonly HashSet<string> _namespaces = new()
+ {
+ "System",
+ "System.Linq",
+ "Microsoft.Extensions.Configuration"
+ };
+
public Parser(SourceProductionContext context, KnownTypeData typeData)
{
_context = context;
}
}
- return new SourceGenerationSpec(_typesForBindMethodGen, _typesForGetMethodGen, _typesForConfigureMethodGen);
+ Dictionary<MethodSpecifier, HashSet<TypeSpec>> methods = new()
+ {
+ [MethodSpecifier.Bind] = _typesForBindMethodGen,
+ [MethodSpecifier.Get] = _typesForGetMethodGen,
+ [MethodSpecifier.Configure] = _typesForConfigureMethodGen,
+ [MethodSpecifier.BindCore] = _typesForBindCoreMethodGen,
+ };
+
+ return new SourceGenerationSpec(methods, _namespaces);
}
private void ProcessBindCall(BinderInvocationOperation binderOperation)
IConversionOperation argument = arguments[1].Value as IConversionOperation;
ITypeSymbol? type = ResolveType(argument)?.WithNullableAnnotation(NullableAnnotation.None);
- // TODO: do we need diagnostic for System.Object?
if (type is not INamedTypeSymbol { } namedType ||
namedType.SpecialType == SpecialType.System_Object ||
- namedType.SpecialType == SpecialType.System_Void)
+ namedType.SpecialType == SpecialType.System_Void ||
+ // Binding to root-level struct is a no-op.
+ namedType.IsValueType)
{
return;
}
}
TypeSpec? spec = GetOrCreateTypeSpec(namedType, location);
- if (spec != null && !specs.Contains(spec))
+ if (spec != null &&
+ !specs.Contains(spec))
{
specs.Add(spec);
}
else if (type is IArrayTypeSymbol { } arrayType)
{
spec = CreateArraySpec(arrayType, location);
- return spec == null ? null : CacheSpec(spec);
+ if (spec is null)
+ {
+ return null;
+ }
+
+ if (spec.SpecKind != TypeSpecKind.ByteArray)
+ {
+ Debug.Assert(spec.SpecKind is TypeSpecKind.Array);
+ _typesForBindCoreMethodGen.Add(spec);
+ }
+
+ return CacheSpec(spec);
}
else if (TypesAreEqual(type, _typeData.SymbolForIConfigurationSection))
{
}
else if (type is INamedTypeSymbol namedType)
{
- return IsCollection(namedType)
- ? CacheSpec(CreateCollectionSpec(namedType, location))
- : CacheSpec(CreateObjectSpec(namedType, location));
+ spec = IsCollection(namedType)
+ ? CreateCollectionSpec(namedType, location)
+ : CreateObjectSpec(namedType, location);
+
+ if (spec is null)
+ {
+ return null;
+ }
+
+ _typesForBindCoreMethodGen.Add(spec);
+ return CacheSpec(spec);
}
ReportUnsupportedType(type, NotSupportedReason.TypeNotSupported, location);
T CacheSpec<T>(T? s) where T : TypeSpec
{
+ string @namespace = s.Namespace;
+ if (@namespace != null && @namespace != GlobalNameSpaceString)
+ {
+ _namespaces.Add(@namespace);
+ }
+
_createdSpecs[type] = s;
return s;
}
INamedTypeSymbol current = type;
while (current != null)
{
- if (current.GetMembers(Literal.Add).Any(member =>
+ if (current.GetMembers(Identifier.Add).Any(member =>
member is IMethodSymbol { Parameters.Length: 1 } method &&
TypesAreEqual(element, method.Parameters[0].Type)))
{
INamedTypeSymbol current = type;
while (current != null)
{
- if (current.GetMembers(Literal.Add).Any(member =>
+ if (current.GetMembers(Identifier.Add).Any(member =>
member is IMethodSymbol { Parameters.Length: 2 } method &&
TypesAreEqual(key, method.Parameters[0].Type) &&
TypesAreEqual(element, method.Parameters[1].Type)))
internal enum ConstructionStrategy
{
NotApplicable = 0,
- ParameterlessConstructor = 1,
+ NotSupported = 1,
+ ParameterlessConstructor = 2,
}
}
<EnableDefaultEmbeddedResourceItems>false</EnableDefaultEmbeddedResourceItems>
<UsingToolXliff>true</UsingToolXliff>
<AnalyzerLanguage>cs</AnalyzerLanguage>
- </PropertyGroup>
-
- <PropertyGroup>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefineConstants Condition="'$(LaunchDebugger)' == 'true'">$(DefineConstants);LAUNCH_DEBUGGER</DefineConstants>
</PropertyGroup>
<Compile Include="PopulationStrategy.cs" />
<Compile Include="PropertySpec.cs" />
<Compile Include="SourceGenerationSpec.cs" />
+ <Compile Include="SourceWriter.cs" />
<Compile Include="TypeSpecKind.cs" />
<Compile Include="TypeSpec.cs" />
</ItemGroup>
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System;
using System.Collections.Generic;
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
+
internal sealed record SourceGenerationSpec(
- HashSet<TypeSpec> TypesForBindMethodGen,
- HashSet<TypeSpec> TypesForGetMethodGen,
- HashSet<TypeSpec> TypesForConfigureMethodGen);
+ Dictionary<MethodSpecifier, HashSet<TypeSpec>> Methods,
+ HashSet<string> Namespaces)
+ {
+ private MethodSpecifier? _methodsToGen;
+ public MethodSpecifier MethodsToGen
+ {
+ get
+ {
+ if (!_methodsToGen.HasValue)
+ {
+ _methodsToGen = MethodSpecifier.None;
+
+ foreach (KeyValuePair<MethodSpecifier, HashSet<TypeSpec>> method in Methods)
+ {
+ if (method.Value.Count > 0)
+ {
+ MethodSpecifier specifier = method.Key;
+
+ if (specifier is MethodSpecifier.Configure or MethodSpecifier.Get)
+ {
+ _methodsToGen |= MethodSpecifier.HasValueOrChildren;
+ }
+ else if (specifier is MethodSpecifier.BindCore)
+ {
+ _methodsToGen |= MethodSpecifier.HasChildren;
+ }
+
+ _methodsToGen |= specifier;
+ }
+ }
+ }
+
+ return _methodsToGen.Value;
+ }
+ }
+ }
+
+ [Flags]
+ internal enum MethodSpecifier
+ {
+ None = 0x0,
+ Bind = 0x1,
+ Get = 0x2,
+ Configure = 0x4,
+ BindCore = 0x8,
+ HasValueOrChildren = 0x10,
+ HasChildren = 0x20,
+ }
}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Diagnostics;
+using System.Text;
+using Microsoft.CodeAnalysis.Text;
+
+namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
+{
+ internal sealed class SourceWriter
+ {
+ private readonly StringBuilder _sb = new();
+ private int _indentationLevel;
+
+ public int Length => _sb.Length;
+ public int IndentationLevel => _indentationLevel;
+
+ private static readonly char[] s_newLine = Environment.NewLine.ToCharArray();
+
+ public void WriteBlockStart(string? declaration = null)
+ {
+ if (declaration is not null)
+ {
+ WriteLine(declaration);
+ }
+ WriteLine("{");
+ _indentationLevel++;
+ }
+
+ public void WriteBlockEnd(string? extra = null)
+ {
+ _indentationLevel--;
+ Debug.Assert(_indentationLevel > -1);
+ WriteLine($"}}{extra}");
+ }
+
+ public void WriteLine(string source)
+ {
+ _sb.Append(' ', 4 * _indentationLevel);
+ _sb.AppendLine(source);
+ }
+
+ public unsafe void WriteLine(ReadOnlySpan<char> source)
+ {
+ _sb.Append(' ', 4 * _indentationLevel);
+ fixed (char* ptr = source)
+ {
+ _sb.Append(ptr, source.Length);
+ WriteBlankLine();
+ }
+ }
+
+ public void WriteBlock(string source)
+ {
+ bool isFinalLine;
+ ReadOnlySpan<char> remainingText = source.AsSpan();
+
+ do
+ {
+ ReadOnlySpan<char> line = GetNextLine(ref remainingText, out isFinalLine);
+ switch (line)
+ {
+ case "{":
+ {
+ WriteBlockStart();
+ }
+ break;
+ case "}":
+ {
+ WriteBlockEnd();
+ }
+ break;
+ default:
+ {
+ WriteLine(line);
+ }
+ break;
+ }
+ } while (!isFinalLine);
+ }
+
+ public void WriteBlankLine() => _sb.AppendLine();
+
+ public void RemoveBlankLine()
+ {
+ int newLineLength = s_newLine.Length;
+ int lastNewLineStartIndex = Length - newLineLength;
+ _sb.Remove(lastNewLineStartIndex, newLineLength);
+ }
+
+ public SourceText ToSourceText()
+ {
+ Debug.Assert(_indentationLevel == 0 && _sb.Length > 0);
+ return SourceText.From(_sb.ToString(), Encoding.UTF8);
+ }
+
+ private static ReadOnlySpan<char> GetNextLine(ref ReadOnlySpan<char> remainingText, out bool isFinalLine)
+ {
+ if (remainingText.IsEmpty)
+ {
+ isFinalLine = true;
+ return default;
+ }
+
+ ReadOnlySpan<char> next;
+ ReadOnlySpan<char> rest;
+
+ remainingText = remainingText.Trim();
+
+ int lineLength = remainingText.IndexOf(s_newLine);
+ if (lineLength == -1)
+ {
+ lineLength = remainingText.Length;
+ isFinalLine = true;
+ rest = default;
+ }
+ else
+ {
+ rest = remainingText.Slice(lineLength + 1);
+ isFinalLine = false;
+ }
+
+ next = remainingText.Slice(0, lineLength);
+ remainingText = rest;
+ return next;
+ }
+ }
+}
{
internal record TypeSpec
{
+ private static readonly SymbolDisplayFormat s_minimalDisplayFormat = new SymbolDisplayFormat(
+ globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
+ typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes,
+ genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
+ miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes);
+
public TypeSpec(ITypeSymbol type)
{
- DisplayString = type.ToDisplayString();
+ FullyQualifiedDisplayString = type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
+ MinimalDisplayString = type.ToDisplayString(s_minimalDisplayFormat);
+ Namespace = type.ContainingNamespace?.ToDisplayString();
SpecialType = type.SpecialType;
IsValueType = type.IsValueType;
}
- public string DisplayString { get; }
+ public string FullyQualifiedDisplayString { get; }
+
+ public string MinimalDisplayString { get; }
+
+ public string? Namespace { get; }
public SpecialType SpecialType { get; }
var configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddInMemoryCollection(dic);
var config = configurationBuilder.Build();
-
var options = config.Get<ByteArrayOptions>();
Assert.Equal(bytes, options.MyByteArray);
}
// <auto-generated/>
#nullable enable
-using System.Linq;
-
internal static class GeneratedConfigurationBinder
{
- public static void Bind(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, Program.MyClass obj) => BindCore(configuration, ref obj);
+ public static void Bind(this global::Microsoft.Extensions.Configuration.IConfiguration configuration, global::Program.MyClass obj) => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.Helpers.BindCore(configuration, ref obj);
+}
+
+namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
+{
+ using System;
+ using System.Linq;
+ using Microsoft.Extensions.Configuration;
+ using System.Collections.Generic;
- private static void BindCore(global::Microsoft.Extensions.Configuration.IConfiguration configuration, ref Program.MyClass obj)
+ internal static class Helpers
{
- if (obj is null)
+ public static void BindCore(IConfiguration configuration, ref List<int> obj)
{
- throw new global::System.ArgumentNullException(nameof(obj));
- }
+ if (obj is null)
+ {
+ throw new ArgumentNullException(nameof(obj));
+ }
- if (configuration["MyString"] is string stringValue0)
- {
- obj.MyString = stringValue0;
+ int element;
+ foreach (IConfigurationSection section in configuration.GetChildren())
+ {
+ if (section.Value is string stringValue0)
+ {
+ element = int.Parse(stringValue0);
+ obj.Add(element);
+ }
+ }
}
- if (configuration["MyInt"] is string stringValue1)
+ public static void BindCore(IConfiguration configuration, ref Dictionary<string, string> obj)
{
- obj.MyInt = int.Parse(stringValue1);
- }
+ if (obj is null)
+ {
+ throw new ArgumentNullException(nameof(obj));
+ }
- global::Microsoft.Extensions.Configuration.IConfigurationSection section2 = configuration.GetSection("MyList");
- if (HasChildren(section2))
- {
- System.Collections.Generic.List<int> temp3 = obj.MyList;
- temp3 ??= new System.Collections.Generic.List<int>();
- BindCore(section2, ref temp3);
- obj.MyList = temp3;
+ string key;
+ foreach (IConfigurationSection section in configuration.GetChildren())
+ {
+ if (section.Key is string stringValue1)
+ {
+ key = stringValue1;
+ string element;
+ if (section.Value is string stringValue2)
+ {
+ element = stringValue2;
+ obj[key] = element;
+ }
+ }
+ }
}
- global::Microsoft.Extensions.Configuration.IConfigurationSection section4 = configuration.GetSection("MyDictionary");
- if (HasChildren(section4))
+ public static void BindCore(IConfiguration configuration, ref Program.MyClass2 obj)
{
- System.Collections.Generic.Dictionary<string, string> temp5 = obj.MyDictionary;
- temp5 ??= new System.Collections.Generic.Dictionary<string, string>();
- BindCore(section4, ref temp5);
- obj.MyDictionary = temp5;
}
- global::Microsoft.Extensions.Configuration.IConfigurationSection section6 = configuration.GetSection("MyComplexDictionary");
- if (HasChildren(section6))
+ public static void BindCore(IConfiguration configuration, ref Dictionary<string, Program.MyClass2> obj)
{
- System.Collections.Generic.Dictionary<string, Program.MyClass2> temp7 = obj.MyComplexDictionary;
- temp7 ??= new System.Collections.Generic.Dictionary<string, Program.MyClass2>();
- BindCore(section6, ref temp7);
- obj.MyComplexDictionary = temp7;
- }
-
- }
+ if (obj is null)
+ {
+ throw new ArgumentNullException(nameof(obj));
+ }
- private static void BindCore(global::Microsoft.Extensions.Configuration.IConfiguration configuration, ref System.Collections.Generic.List<int> obj)
- {
- if (obj is null)
- {
- throw new global::System.ArgumentNullException(nameof(obj));
+ string key;
+ foreach (IConfigurationSection section in configuration.GetChildren())
+ {
+ if (section.Key is string stringValue3)
+ {
+ key = stringValue3;
+ if (obj.TryGetValue(key, out Program.MyClass2? element) && element is not null)
+ {
+ BindCore(section, ref element);
+ obj[key] = element;
+ }
+ else
+ {
+ element = new Program.MyClass2();
+ BindCore(section, ref element);
+ obj[key] = element;
+ }
+ }
+ }
}
- int element;
- foreach (Microsoft.Extensions.Configuration.IConfigurationSection section in configuration.GetChildren())
+ public static void BindCore(IConfiguration configuration, ref Program.MyClass obj)
{
- if (section.Value is string stringValue8)
+ if (obj is null)
{
- element = int.Parse(stringValue8);
- obj.Add(element);
+ throw new ArgumentNullException(nameof(obj));
}
- }
- }
- private static void BindCore(global::Microsoft.Extensions.Configuration.IConfiguration configuration, ref System.Collections.Generic.Dictionary<string, string> obj)
- {
- if (obj is null)
- {
- throw new global::System.ArgumentNullException(nameof(obj));
- }
+ if (configuration["MyString"] is string stringValue6)
+ {
+ obj.MyString = stringValue6;
+ }
- string key;
- foreach (Microsoft.Extensions.Configuration.IConfigurationSection section in configuration.GetChildren())
- {
- if (section.Key is string stringValue9)
+ if (configuration["MyInt"] is string stringValue7)
{
- key = stringValue9;
- string element;
- if (section.Value is string stringValue10)
- {
- element = stringValue10;
- obj[key] = element;
- }
+ obj.MyInt = int.Parse(stringValue7);
}
- }
- }
- private static void BindCore(global::Microsoft.Extensions.Configuration.IConfiguration configuration, ref System.Collections.Generic.Dictionary<string, Program.MyClass2> obj)
- {
- if (obj is null)
- {
- throw new global::System.ArgumentNullException(nameof(obj));
- }
+ IConfigurationSection section8 = configuration.GetSection("MyList");
+ if (HasChildren(section8))
+ {
+ List<int> temp9 = obj.MyList;
+ temp9 ??= new List<int>();
+ BindCore(section8, ref temp9);
+ obj.MyList = temp9;
+ }
- string key;
- foreach (Microsoft.Extensions.Configuration.IConfigurationSection section in configuration.GetChildren())
- {
- if (section.Key is string stringValue11)
+ IConfigurationSection section10 = configuration.GetSection("MyDictionary");
+ if (HasChildren(section10))
{
- key = stringValue11;
- if (obj.TryGetValue(key, out Program.MyClass2? element) && element is not null)
- {
- BindCore(section, ref element);
- obj[key] = element;
- }
- else
- {
- element = new Program.MyClass2();
- BindCore(section, ref element);
- obj[key] = element;
- }
+ Dictionary<string, string> temp11 = obj.MyDictionary;
+ temp11 ??= new Dictionary<string, string>();
+ BindCore(section10, ref temp11);
+ obj.MyDictionary = temp11;
}
- }
- }
- private static void BindCore(global::Microsoft.Extensions.Configuration.IConfiguration configuration, ref Program.MyClass2 obj)
- {
- if (obj is null)
- {
- throw new global::System.ArgumentNullException(nameof(obj));
+ IConfigurationSection section12 = configuration.GetSection("MyComplexDictionary");
+ if (HasChildren(section12))
+ {
+ Dictionary<string, Program.MyClass2> temp13 = obj.MyComplexDictionary;
+ temp13 ??= new Dictionary<string, Program.MyClass2>();
+ BindCore(section12, ref temp13);
+ obj.MyComplexDictionary = temp13;
+ }
}
- }
-
- public static bool HasChildren(global::Microsoft.Extensions.Configuration.IConfiguration configuration)
- {
- foreach (global::Microsoft.Extensions.Configuration.IConfigurationSection section in configuration.GetChildren())
+ public static bool HasChildren(IConfiguration configuration)
{
- return true;
+ foreach (IConfigurationSection section in configuration.GetChildren())
+ {
+ return true;
+ }
+ return false;
}
- return false;
}
}
// <auto-generated/>
#nullable enable
-using System.Linq;
-
internal static class GeneratedConfigurationBinder
{
public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection Configure<T>(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::Microsoft.Extensions.Configuration.IConfiguration configuration)
{
- if (typeof(T) == typeof(Program.MyClass))
+ if (configuration is null)
+ {
+ throw new global::System.ArgumentNullException(nameof(configuration));
+ }
+
+ if (typeof(T) == typeof(global::Program.MyClass))
{
- return services.Configure<Program.MyClass>(obj =>
+ return services.Configure<global::Program.MyClass>(obj =>
{
- BindCore(configuration, ref obj);
+ if (!global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.Helpers.HasValueOrChildren(configuration))
+ {
+ return default;
+ }
+
+ global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.Helpers.BindCore(configuration, ref obj);
});
}
throw new global::System.NotSupportedException($"Unable to bind to type '{typeof(T)}': 'Generator parser did not detect the type as input'");
}
+}
+
+namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
+{
+ using System;
+ using System.Linq;
+ using Microsoft.Extensions.Configuration;
+ using System.Collections.Generic;
- private static void BindCore(global::Microsoft.Extensions.Configuration.IConfiguration configuration, ref Program.MyClass obj)
+ internal static class Helpers
{
- if (obj is null)
+ public static void BindCore(IConfiguration configuration, ref List<int> obj)
{
- throw new global::System.ArgumentNullException(nameof(obj));
- }
+ if (obj is null)
+ {
+ throw new ArgumentNullException(nameof(obj));
+ }
- if (configuration["MyString"] is string stringValue1)
- {
- obj.MyString = stringValue1;
+ int element;
+ foreach (IConfigurationSection section in configuration.GetChildren())
+ {
+ if (section.Value is string stringValue1)
+ {
+ element = int.Parse(stringValue1);
+ obj.Add(element);
+ }
+ }
}
- if (configuration["MyInt"] is string stringValue2)
+ public static void BindCore(IConfiguration configuration, ref Dictionary<string, string> obj)
{
- obj.MyInt = int.Parse(stringValue2);
- }
+ if (obj is null)
+ {
+ throw new ArgumentNullException(nameof(obj));
+ }
- global::Microsoft.Extensions.Configuration.IConfigurationSection section3 = configuration.GetSection("MyList");
- if (HasChildren(section3))
- {
- System.Collections.Generic.List<int> temp4 = obj.MyList;
- temp4 ??= new System.Collections.Generic.List<int>();
- BindCore(section3, ref temp4);
- obj.MyList = temp4;
+ string key;
+ foreach (IConfigurationSection section in configuration.GetChildren())
+ {
+ if (section.Key is string stringValue2)
+ {
+ key = stringValue2;
+ string element;
+ if (section.Value is string stringValue3)
+ {
+ element = stringValue3;
+ obj[key] = element;
+ }
+ }
+ }
}
- global::Microsoft.Extensions.Configuration.IConfigurationSection section5 = configuration.GetSection("MyDictionary");
- if (HasChildren(section5))
+ public static void BindCore(IConfiguration configuration, ref Program.MyClass obj)
{
- System.Collections.Generic.Dictionary<string, string> temp6 = obj.MyDictionary;
- temp6 ??= new System.Collections.Generic.Dictionary<string, string>();
- BindCore(section5, ref temp6);
- obj.MyDictionary = temp6;
- }
+ if (obj is null)
+ {
+ throw new ArgumentNullException(nameof(obj));
+ }
- }
+ if (configuration["MyString"] is string stringValue4)
+ {
+ obj.MyString = stringValue4;
+ }
- private static void BindCore(global::Microsoft.Extensions.Configuration.IConfiguration configuration, ref System.Collections.Generic.List<int> obj)
- {
- if (obj is null)
- {
- throw new global::System.ArgumentNullException(nameof(obj));
- }
+ if (configuration["MyInt"] is string stringValue5)
+ {
+ obj.MyInt = int.Parse(stringValue5);
+ }
- int element;
- foreach (Microsoft.Extensions.Configuration.IConfigurationSection section in configuration.GetChildren())
- {
- if (section.Value is string stringValue7)
+ IConfigurationSection section6 = configuration.GetSection("MyList");
+ if (HasChildren(section6))
{
- element = int.Parse(stringValue7);
- obj.Add(element);
+ List<int> temp7 = obj.MyList;
+ temp7 ??= new List<int>();
+ BindCore(section6, ref temp7);
+ obj.MyList = temp7;
}
- }
- }
- private static void BindCore(global::Microsoft.Extensions.Configuration.IConfiguration configuration, ref System.Collections.Generic.Dictionary<string, string> obj)
- {
- if (obj is null)
- {
- throw new global::System.ArgumentNullException(nameof(obj));
+ IConfigurationSection section8 = configuration.GetSection("MyDictionary");
+ if (HasChildren(section8))
+ {
+ Dictionary<string, string> temp9 = obj.MyDictionary;
+ temp9 ??= new Dictionary<string, string>();
+ BindCore(section8, ref temp9);
+ obj.MyDictionary = temp9;
+ }
}
- string key;
- foreach (Microsoft.Extensions.Configuration.IConfigurationSection section in configuration.GetChildren())
+ public static bool HasValueOrChildren(IConfiguration configuration)
{
- if (section.Key is string stringValue8)
+ if ((configuration as IConfigurationSection)?.Value is not null)
{
- key = stringValue8;
- string element;
- if (section.Value is string stringValue9)
- {
- element = stringValue9;
- obj[key] = element;
- }
+ return true;
}
+ return HasChildren(configuration);
}
- }
- public static bool HasChildren(global::Microsoft.Extensions.Configuration.IConfiguration configuration)
- {
- foreach (global::Microsoft.Extensions.Configuration.IConfigurationSection section in configuration.GetChildren())
+ public static bool HasChildren(IConfiguration configuration)
{
- return true;
+ foreach (IConfigurationSection section in configuration.GetChildren())
+ {
+ return true;
+ }
+ return false;
}
- return false;
}
}
// <auto-generated/>
#nullable enable
-using System.Linq;
-
internal static class GeneratedConfigurationBinder
{
public static T? Get<T>(this global::Microsoft.Extensions.Configuration.IConfiguration configuration)
throw new global::System.ArgumentNullException(nameof(configuration));
}
- if (typeof(T) == typeof(Program.MyClass))
+ if (!global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.Helpers.HasValueOrChildren(configuration))
{
- Microsoft.Extensions.Configuration.IConfigurationSection? section = configuration as Microsoft.Extensions.Configuration.IConfigurationSection;
- if (section?.Value is null && !configuration.GetChildren().Any())
- {
- return default;
- }
+ return default;
+ }
- Program.MyClass obj = new Program.MyClass();
- BindCore(configuration, ref obj);
+ if (typeof(T) == typeof(global::Program.MyClass))
+ {
+ var obj = new global::Program.MyClass();
+ global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.Helpers.BindCore(configuration, ref obj);
return (T)(object)obj;
}
throw new global::System.NotSupportedException($"Unable to bind to type '{typeof(T)}': 'Generator parser did not detect the type as input'");
}
+}
- private static void BindCore(global::Microsoft.Extensions.Configuration.IConfiguration configuration, ref Program.MyClass obj)
+namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
+{
+ using System;
+ using System.Linq;
+ using Microsoft.Extensions.Configuration;
+ using System.Collections.Generic;
+
+ internal static class Helpers
{
- if (obj is null)
+ public static void BindCore(IConfiguration configuration, ref List<int> obj)
{
- throw new global::System.ArgumentNullException(nameof(obj));
- }
+ if (obj is null)
+ {
+ throw new ArgumentNullException(nameof(obj));
+ }
- if (configuration["MyString"] is string stringValue1)
- {
- obj.MyString = stringValue1;
+ int element;
+ foreach (IConfigurationSection section in configuration.GetChildren())
+ {
+ if (section.Value is string stringValue1)
+ {
+ element = int.Parse(stringValue1);
+ obj.Add(element);
+ }
+ }
}
- if (configuration["MyInt"] is string stringValue2)
+ public static void BindCore(IConfiguration configuration, ref Dictionary<string, string> obj)
{
- obj.MyInt = int.Parse(stringValue2);
- }
+ if (obj is null)
+ {
+ throw new ArgumentNullException(nameof(obj));
+ }
- global::Microsoft.Extensions.Configuration.IConfigurationSection section3 = configuration.GetSection("MyList");
- if (HasChildren(section3))
- {
- System.Collections.Generic.List<int> temp4 = obj.MyList;
- temp4 ??= new System.Collections.Generic.List<int>();
- BindCore(section3, ref temp4);
- obj.MyList = temp4;
+ string key;
+ foreach (IConfigurationSection section in configuration.GetChildren())
+ {
+ if (section.Key is string stringValue2)
+ {
+ key = stringValue2;
+ string element;
+ if (section.Value is string stringValue3)
+ {
+ element = stringValue3;
+ obj[key] = element;
+ }
+ }
+ }
}
- global::Microsoft.Extensions.Configuration.IConfigurationSection section5 = configuration.GetSection("MyDictionary");
- if (HasChildren(section5))
+ public static void BindCore(IConfiguration configuration, ref Program.MyClass obj)
{
- System.Collections.Generic.Dictionary<string, string> temp6 = obj.MyDictionary;
- temp6 ??= new System.Collections.Generic.Dictionary<string, string>();
- BindCore(section5, ref temp6);
- obj.MyDictionary = temp6;
- }
+ if (obj is null)
+ {
+ throw new ArgumentNullException(nameof(obj));
+ }
- }
+ if (configuration["MyString"] is string stringValue4)
+ {
+ obj.MyString = stringValue4;
+ }
- private static void BindCore(global::Microsoft.Extensions.Configuration.IConfiguration configuration, ref System.Collections.Generic.List<int> obj)
- {
- if (obj is null)
- {
- throw new global::System.ArgumentNullException(nameof(obj));
- }
+ if (configuration["MyInt"] is string stringValue5)
+ {
+ obj.MyInt = int.Parse(stringValue5);
+ }
- int element;
- foreach (Microsoft.Extensions.Configuration.IConfigurationSection section in configuration.GetChildren())
- {
- if (section.Value is string stringValue7)
+ IConfigurationSection section6 = configuration.GetSection("MyList");
+ if (HasChildren(section6))
{
- element = int.Parse(stringValue7);
- obj.Add(element);
+ List<int> temp7 = obj.MyList;
+ temp7 ??= new List<int>();
+ BindCore(section6, ref temp7);
+ obj.MyList = temp7;
}
- }
- }
- private static void BindCore(global::Microsoft.Extensions.Configuration.IConfiguration configuration, ref System.Collections.Generic.Dictionary<string, string> obj)
- {
- if (obj is null)
- {
- throw new global::System.ArgumentNullException(nameof(obj));
+ IConfigurationSection section8 = configuration.GetSection("MyDictionary");
+ if (HasChildren(section8))
+ {
+ Dictionary<string, string> temp9 = obj.MyDictionary;
+ temp9 ??= new Dictionary<string, string>();
+ BindCore(section8, ref temp9);
+ obj.MyDictionary = temp9;
+ }
}
- string key;
- foreach (Microsoft.Extensions.Configuration.IConfigurationSection section in configuration.GetChildren())
+ public static bool HasValueOrChildren(IConfiguration configuration)
{
- if (section.Key is string stringValue8)
+ if ((configuration as IConfigurationSection)?.Value is not null)
{
- key = stringValue8;
- string element;
- if (section.Value is string stringValue9)
- {
- element = stringValue9;
- obj[key] = element;
- }
+ return true;
}
+ return HasChildren(configuration);
}
- }
- public static bool HasChildren(global::Microsoft.Extensions.Configuration.IConfiguration configuration)
- {
- foreach (global::Microsoft.Extensions.Configuration.IConfigurationSection section in configuration.GetChildren())
+ public static bool HasChildren(IConfiguration configuration)
{
- return true;
+ foreach (IConfigurationSection section in configuration.GetChildren())
+ {
+ return true;
+ }
+ return false;
}
- return false;
}
}