Skip to content

Commit

Permalink
Fix IReadOnlyDictionary net462 issue
Browse files Browse the repository at this point in the history
  • Loading branch information
layomia committed May 16, 2023
1 parent c4a3635 commit ef2b75b
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -773,14 +773,22 @@ void Emit_BindAndAddLogic_ForElement()
EmitObjectInit(elementType, Identifier.element, InitializationKind.SimpleAssignment);
_writer.WriteBlockEnd();

if (elementType is CollectionSpec { ConstructionStrategy: ConstructionStrategy.ParameterizedConstructor } collectionSpec)
if (elementType is CollectionSpec
{
ConstructionStrategy: ConstructionStrategy.ParameterizedConstructor or ConstructionStrategy.ToEnumerableMethod
} collectionSpec)
{
// This is a read-only collection. If the element exists and is not null,
// we need to copy its contents into a new instance & then append/bind to that.

string initExpression = collectionSpec.ConstructionStrategy is ConstructionStrategy.ParameterizedConstructor
? $"new {collectionSpec.ConcreteType.MinimalDisplayString}({Identifier.element})"
: $"{Identifier.element}.{collectionSpec.ToEnumerableMethodCall!}";

_writer.WriteBlock($$"""
else
{
{{Identifier.element}} = new {{collectionSpec.ConcreteType.MinimalDisplayString}}({{Identifier.element}});
{{Identifier.element}} = {{initExpression}};
}
""");
}
Expand Down Expand Up @@ -1092,22 +1100,18 @@ private void EmitObjectInit(TypeSpec type, string expressionForMemberAccess, Ini
}
else if (initKind == InitializationKind.AssignmentWithNullCheck)
{
if (collectionType?.ConstructionStrategy is not ConstructionStrategy.ParameterizedConstructor)
ConstructionStrategy? collectionConstructionStratey = collectionType?.ConstructionStrategy;
if (collectionConstructionStratey is ConstructionStrategy.ParameterizedConstructor)
{
_writer.WriteLine($"{expressionForMemberAccess} ??= {expressionForInit};");
_writer.WriteLine($"{expressionForMemberAccess} = {expressionForMemberAccess} is null ? {expressionForInit} : new {displayString}({expressionForMemberAccess});");
}
else if (collectionConstructionStratey is ConstructionStrategy.ToEnumerableMethod)
{
_writer.WriteLine($"{expressionForMemberAccess} = {expressionForMemberAccess} is null ? {expressionForInit} : {expressionForMemberAccess}.{collectionType.ToEnumerableMethodCall!};");
}
else
{
_writer.WriteBlock($$"""
if ({{expressionForMemberAccess}} is null)
{
{{expressionForMemberAccess}} = {{expressionForInit}};
}
else
{
{{expressionForMemberAccess}} = new {{displayString}}({{expressionForMemberAccess}});
}
""");
_writer.WriteLine($"{expressionForMemberAccess} ??= {expressionForInit};");
}
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection.Metadata;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Operations;

Expand Down Expand Up @@ -561,7 +562,7 @@ private bool TryGetTypeSpec(ITypeSymbol type, DiagnosticDescriptor descriptor, o
}

// We want a BindCore method for List<TElement> as a temp holder for the array values.
EnumerableSpec? listSpec = ConstructGenericTypeSpec(_typeSymbols.List, arrayType.ElementType) as EnumerableSpec;
EnumerableSpec? listSpec = GetOrCreateTypeSpec(_typeSymbols.List.Construct(arrayType.ElementType)) as EnumerableSpec;
// We know the element type is supported.
Debug.Assert(listSpec != null);
if (listSpec is not null)
Expand All @@ -575,6 +576,7 @@ private bool TryGetTypeSpec(ITypeSymbol type, DiagnosticDescriptor descriptor, o
ElementType = elementSpec,
ConcreteType = listSpec,
PopulationStrategy = CollectionPopulationStrategy.Array,
ToEnumerableMethodCall = null,
};
}

Expand Down Expand Up @@ -622,6 +624,7 @@ private DictionarySpec CreateDictionarySpec(INamedTypeSymbol type, Location? loc
CollectionPopulationStrategy populationStrategy;
INamedTypeSymbol? concreteType = null;
INamedTypeSymbol? populationCastType = null;
string? toEnumerableMethodCall = null;

if (HasPublicParameterlessCtor(type))
{
Expand Down Expand Up @@ -652,8 +655,10 @@ private DictionarySpec CreateDictionarySpec(INamedTypeSymbol type, Location? loc
{
concreteType = _typeSymbols.Dictionary;
populationCastType = _typeSymbols.GenericIDictionary;
constructionStrategy = ConstructionStrategy.ParameterizedConstructor;
constructionStrategy = ConstructionStrategy.ToEnumerableMethod;
populationStrategy = CollectionPopulationStrategy.Cast_Then_Add;
toEnumerableMethodCall = "ToDictionary(pair => pair.Key, pair => pair.Value)";
_namespaces.Add("System.Linq");
}
else
{
Expand All @@ -668,6 +673,7 @@ private DictionarySpec CreateDictionarySpec(INamedTypeSymbol type, Location? loc
ElementType = elementSpec,
ConstructionStrategy = constructionStrategy,
PopulationStrategy = populationStrategy,
ToEnumerableMethodCall = toEnumerableMethodCall,
};

Debug.Assert(!(populationStrategy is CollectionPopulationStrategy.Cast_Then_Add && populationCastType is null));
Expand Down Expand Up @@ -757,6 +763,7 @@ private DictionarySpec CreateDictionarySpec(INamedTypeSymbol type, Location? loc
ElementType = elementSpec,
ConstructionStrategy = constructionStrategy,
PopulationStrategy = populationStrategy,
ToEnumerableMethodCall = null,
};

Debug.Assert(!(populationStrategy is CollectionPopulationStrategy.Cast_Then_Add && populationCastType is null));
Expand Down Expand Up @@ -974,13 +981,13 @@ private static bool HasAddMethod(INamedTypeSymbol type, ITypeSymbol key, ITypeSy
private static bool IsEnum(ITypeSymbol type) => type is INamedTypeSymbol { EnumUnderlyingType: INamedTypeSymbol { } };

private CollectionSpec? ConstructGenericCollectionTypeSpec(INamedTypeSymbol? collectionType, params ITypeSymbol[] parameters) =>
(collectionType is not null ? ConstructGenericTypeSpec(collectionType, parameters) : null) as CollectionSpec;
(collectionType is not null ? ConstructGenericCollectionSpec(collectionType, parameters) : null);

private TypeSpec? ConstructGenericTypeSpec(INamedTypeSymbol type, params ITypeSymbol[] parameters)
private CollectionSpec? ConstructGenericCollectionSpec(INamedTypeSymbol type, params ITypeSymbol[] parameters)
{
Debug.Assert(type.IsGenericType);
INamedTypeSymbol constructedType = type.Construct(parameters);
return GetOrCreateTypeSpec(constructedType);
return CreateCollectionSpec(constructedType, location: null);
}

private void ReportUnsupportedType(ITypeSymbol type, DiagnosticDescriptor descriptor, Location? location = null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ public CollectionSpec(ITypeSymbol type) : base(type)
public CollectionSpec? PopulationCastType { get; set; }

public required CollectionPopulationStrategy PopulationStrategy { get; init; }

public required string? ToEnumerableMethodCall { get; init; }
}

internal sealed record EnumerableSpec : CollectionSpec
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
internal enum ConstructionStrategy
{
NotApplicable = 0,
None = 0,
ParameterlessConstructor = 1,
ParameterizedConstructor = 2,
ToEnumerableMethod = 3,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
using System.Globalization;
using Microsoft.Extensions.Configuration;
using System.Collections.Generic;
using System.Linq;

internal static class Helpers
{
Expand Down Expand Up @@ -74,61 +75,49 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
}
}

public static void BindCore(IConfiguration configuration, ref List<int> obj, BinderOptions? binderOptions)
public static void BindCore(IConfiguration configuration, ref IReadOnlyList<int> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}

foreach (IConfigurationSection section in configuration.GetChildren())
{
int element;
if (section.Value is string stringValue3)
{
element = ParseInt(stringValue3, () => section.Path);
obj.Add(element);
}
}
}

public static void BindCore(IConfiguration configuration, ref ICollection<int> obj, BinderOptions? binderOptions)
{
if (obj is null)
if (obj is not ICollection<int> temp)
{
throw new ArgumentNullException(nameof(obj));
return;
}

foreach (IConfigurationSection section in configuration.GetChildren())
{
int element;
if (section.Value is string stringValue4)
if (section.Value is string stringValue3)
{
element = ParseInt(stringValue4, () => section.Path);
obj.Add(element);
element = ParseInt(stringValue3, () => section.Path);
temp.Add(element);
}
}
}

public static void BindCore(IConfiguration configuration, ref IReadOnlyList<int> obj, BinderOptions? binderOptions)
public static void BindCore(IConfiguration configuration, ref IReadOnlyDictionary<string, int> obj, BinderOptions? binderOptions)
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}

if (obj is not ICollection<int> temp)
if (obj is not IDictionary<string, int> temp)
{
return;
}

foreach (IConfigurationSection section in configuration.GetChildren())
{
string key = section.Key;
int element;
if (section.Value is string stringValue5)
if (section.Value is string stringValue4)
{
element = ParseInt(stringValue5, () => section.Path);
temp.Add(element);
element = ParseInt(stringValue4, () => section.Path);
temp[key] = element;
}
}
}
Expand All @@ -149,52 +138,56 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
if (HasChildren(section))
{
Program.CustomDictionary<string, int> temp6 = obj.CustomDictionary;
temp6 ??= new Program.CustomDictionary<string, int>();
BindCore(section, ref temp6, binderOptions);
obj.CustomDictionary = temp6;
Program.CustomDictionary<string, int> temp5 = obj.CustomDictionary;
temp5 ??= new Program.CustomDictionary<string, int>();
BindCore(section, ref temp5, binderOptions);
obj.CustomDictionary = temp5;
}
}
break;
case "CustomList":
{
if (HasChildren(section))
{
Program.CustomList temp7 = obj.CustomList;
temp7 ??= new Program.CustomList();
BindCore(section, ref temp7, binderOptions);
obj.CustomList = temp7;
Program.CustomList temp6 = obj.CustomList;
temp6 ??= new Program.CustomList();
BindCore(section, ref temp6, binderOptions);
obj.CustomList = temp6;
}
}
break;
case "ICustomDictionary":
{
}
break;
case "CustomCollection":
case "ICustomCollection":
{
}
break;
case "ReadOnlyList":
case "IReadOnlyList":
{
if (HasChildren(section))
{
IReadOnlyList<int> temp8 = obj.ReadOnlyList;
if (temp8 is null)
{
temp8 = new List<int>();
}
else
{
temp8 = new List<int>(temp8);
}
BindCore(section, ref temp8, binderOptions);
obj.ReadOnlyList = temp8;
IReadOnlyList<int> temp7 = obj.IReadOnlyList;
temp7 = temp7 is null ? new List<int>() : new List<int>(temp7);
BindCore(section, ref temp7, binderOptions);
obj.IReadOnlyList = temp7;
}
}
break;
case "ReadOnlyDictionary":
case "UnsupportedIReadOnlyDictionaryUnsupported":
{
}
break;
case "IReadOnlyDictionary":
{
if (HasChildren(section))
{
IReadOnlyDictionary<string, int> temp8 = obj.IReadOnlyDictionary;
temp8 = temp8 is null ? new Dictionary<string, int>() : temp8.ToDictionary(pair => pair.Key, pair => pair.Value);
BindCore(section, ref temp8, binderOptions);
obj.IReadOnlyDictionary = temp8;
}
}
break;
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,9 +274,10 @@ public class MyClassWithCustomCollections
public CustomDictionary<string, int> CustomDictionary { get; set; }
public CustomList CustomList { get; set; }
public ICustomDictionary<string> ICustomDictionary { get; set; }
public ICustomSet<MyClassWithCustomCollections> CustomCollection { get; set; }
public IReadOnlyList<int> ReadOnlyList { get; set; }
public IReadOnlyDictionary<MyClassWithCustomCollections, int> ReadOnlyDictionary { get; set; }
public ICustomSet<MyClassWithCustomCollections> ICustomCollection { get; set; }
public IReadOnlyList<int> IReadOnlyList { get; set; }
public IReadOnlyDictionary<MyClassWithCustomCollections, int> UnsupportedIReadOnlyDictionaryUnsupported { get; set; }
public IReadOnlyDictionary<string, int> IReadOnlyDictionary { get; set; }
}
public class CustomDictionary<TKey, TValue> : Dictionary<TKey, TValue>
Expand Down

0 comments on commit ef2b75b

Please sign in to comment.