Skip to content

Commit 1cbf763

Browse files
[release/8.0] Make src gen for property setters consistent with reflection (#92167)
* Make src gen for property setters consistent with reflection * Don't default value during initialization * Remove unnecessary bang operator * Don't default a property when it is a collection or child type * Simply use of new variable; add to test * Review feedback --------- Co-authored-by: Steve Harter <[email protected]>
1 parent 644856c commit 1cbf763

27 files changed

+632
-185
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ void EmitMethods(MethodsToGen_ConfigurationBinder method, string additionalParam
148148
EmitCheckForNullArgument_WithBlankLine(Identifier.instance, voidReturn: true);
149149
_writer.WriteLine($$"""
150150
var {{Identifier.typedObj}} = ({{type.EffectiveType.DisplayString}}){{Identifier.instance}};
151-
{{nameof(MethodsToGen_CoreBindingHelper.BindCore)}}({{configExpression}}, ref {{Identifier.typedObj}}, {{binderOptionsArg}});
151+
{{nameof(MethodsToGen_CoreBindingHelper.BindCore)}}({{configExpression}}, ref {{Identifier.typedObj}}, defaultValueIfNotFound: false, {{binderOptionsArg}});
152152
""");
153153
}
154154

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

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ private void EmitGetCoreMethod()
9797
Expression.sectionPath,
9898
writeOnSuccess: parsedValueExpr => _writer.WriteLine($"return {parsedValueExpr};"),
9999
checkForNullSectionValue: stringParsableType.StringParsableTypeKind is not StringParsableTypeKind.AssignFromSectionValue,
100+
useDefaultValueIfSectionValueIsNull: false,
100101
useIncrementalStringValueIdentifier: false);
101102
}
102103
break;
@@ -110,7 +111,7 @@ private void EmitGetCoreMethod()
110111
{
111112
if (complexType.CanInstantiate)
112113
{
113-
EmitBindingLogic(complexType, Identifier.instance, Identifier.configuration, InitializationKind.Declaration);
114+
EmitBindingLogic(complexType, Identifier.instance, Identifier.configuration, InitializationKind.Declaration, ValueDefaulting.CallSetter);
114115
_writer.WriteLine($"return {Identifier.instance};");
115116
}
116117
else if (type is ObjectSpec { InitExceptionMessage: string exMsg })
@@ -173,6 +174,7 @@ private void EmitGetValueCoreMethod()
173174
Expression.sectionPath,
174175
writeOnSuccess: (parsedValueExpr) => _writer.WriteLine($"return {parsedValueExpr};"),
175176
checkForNullSectionValue: false,
177+
useDefaultValueIfSectionValueIsNull: false,
176178
useIncrementalStringValueIdentifier: false);
177179

178180
EmitEndBlock();
@@ -207,7 +209,7 @@ private void EmitBindCoreMainMethod()
207209

208210
EmitStartBlock($"{conditionKindExpr} ({Identifier.type} == typeof({type.DisplayString}))");
209211
_writer.WriteLine($"var {Identifier.temp} = ({effectiveType.DisplayString}){Identifier.instance};");
210-
EmitBindingLogic(type, Identifier.temp, Identifier.configuration, InitializationKind.None);
212+
EmitBindingLogic(type, Identifier.temp, Identifier.configuration, InitializationKind.None, ValueDefaulting.None);
211213
_writer.WriteLine($"return;");
212214
EmitEndBlock();
213215
}
@@ -235,7 +237,7 @@ private void EmitBindCoreMethods()
235237
private void EmitBindCoreMethod(ComplexTypeSpec type)
236238
{
237239
string objParameterExpression = $"ref {type.DisplayString} {Identifier.instance}";
238-
EmitStartBlock(@$"public static void {nameof(MethodsToGen_CoreBindingHelper.BindCore)}({Identifier.IConfiguration} {Identifier.configuration}, {objParameterExpression}, {Identifier.BinderOptions}? {Identifier.binderOptions})");
240+
EmitStartBlock(@$"public static void {nameof(MethodsToGen_CoreBindingHelper.BindCore)}({Identifier.IConfiguration} {Identifier.configuration}, {objParameterExpression}, bool defaultValueIfNotFound, {Identifier.BinderOptions}? {Identifier.binderOptions})");
239241

240242
ComplexTypeSpec effectiveType = (ComplexTypeSpec)type.EffectiveType;
241243
if (effectiveType is EnumerableSpec enumerable)
@@ -334,8 +336,6 @@ private void EmitInitializeMethod(ObjectSpec type)
334336
void EmitBindImplForMember(MemberSpec member)
335337
{
336338
TypeSpec memberType = member.Type;
337-
bool errorOnFailedBinding = member.ErrorOnFailedBinding;
338-
339339
string parsedMemberDeclarationLhs = $"{memberType.DisplayString} {member.Name}";
340340
string configKeyName = member.ConfigurationKeyName;
341341
string parsedMemberAssignmentLhsExpr;
@@ -344,7 +344,7 @@ void EmitBindImplForMember(MemberSpec member)
344344
{
345345
case ParsableFromStringSpec { StringParsableTypeKind: StringParsableTypeKind.AssignFromSectionValue }:
346346
{
347-
if (errorOnFailedBinding)
347+
if (member is ParameterSpec parameter && parameter.ErrorOnFailedBinding)
348348
{
349349
string condition = $@"if ({Identifier.configuration}[""{configKeyName}""] is not {parsedMemberDeclarationLhs})";
350350
EmitThrowBlock(condition);
@@ -377,11 +377,12 @@ void EmitBindImplForMember(MemberSpec member)
377377
member,
378378
parsedMemberAssignmentLhsExpr,
379379
sectionPathExpr: GetSectionPathFromConfigurationExpression(configKeyName),
380-
canSet: true);
380+
canSet: true,
381+
InitializationKind.None);
381382

382383
if (canBindToMember)
383384
{
384-
if (errorOnFailedBinding)
385+
if (member is ParameterSpec parameter && parameter.ErrorOnFailedBinding)
385386
{
386387
// Add exception logic for parameter ctors; must be present in configuration object.
387388
EmitThrowBlock(condition: "else");
@@ -633,7 +634,7 @@ private void EmitPopulationImplForArray(EnumerableSpec type)
633634

634635
// Create list and bind elements.
635636
string tempIdentifier = GetIncrementalIdentifier(Identifier.temp);
636-
EmitBindingLogic(typeToInstantiate, tempIdentifier, Identifier.configuration, InitializationKind.Declaration);
637+
EmitBindingLogic(typeToInstantiate, tempIdentifier, Identifier.configuration, InitializationKind.Declaration, ValueDefaulting.None);
637638

638639
// Resize array and add binded elements.
639640
_writer.WriteLine($$"""
@@ -661,6 +662,7 @@ private void EmitPopulationImplForEnumerableWithAdd(EnumerableSpec type)
661662
Expression.sectionPath,
662663
(parsedValueExpr) => _writer.WriteLine($"{addExpr}({parsedValueExpr});"),
663664
checkForNullSectionValue: true,
665+
useDefaultValueIfSectionValueIsNull: false,
664666
useIncrementalStringValueIdentifier: false);
665667
}
666668
break;
@@ -671,7 +673,7 @@ private void EmitPopulationImplForEnumerableWithAdd(EnumerableSpec type)
671673
break;
672674
case ComplexTypeSpec { CanInstantiate: true } complexType:
673675
{
674-
EmitBindingLogic(complexType, Identifier.value, Identifier.section, InitializationKind.Declaration);
676+
EmitBindingLogic(complexType, Identifier.value, Identifier.section, InitializationKind.Declaration, ValueDefaulting.None);
675677
_writer.WriteLine($"{addExpr}({Identifier.value});");
676678
}
677679
break;
@@ -696,6 +698,7 @@ private void EmitBindCoreImplForDictionary(DictionarySpec type)
696698
Expression.sectionPath,
697699
Emit_BindAndAddLogic_ForElement,
698700
checkForNullSectionValue: false,
701+
useDefaultValueIfSectionValueIsNull: false,
699702
useIncrementalStringValueIdentifier: false);
700703

701704
void Emit_BindAndAddLogic_ForElement(string parsedKeyExpr)
@@ -710,6 +713,7 @@ void Emit_BindAndAddLogic_ForElement(string parsedKeyExpr)
710713
Expression.sectionPath,
711714
writeOnSuccess: parsedValueExpr => _writer.WriteLine($"{instanceIdentifier}[{parsedKeyExpr}] = {parsedValueExpr};"),
712715
checkForNullSectionValue: true,
716+
useDefaultValueIfSectionValueIsNull: false,
713717
useIncrementalStringValueIdentifier: false);
714718
}
715719
break;
@@ -746,7 +750,7 @@ void Emit_BindAndAddLogic_ForElement(string parsedKeyExpr)
746750
EmitObjectInit(complexElementType, Identifier.element, InitializationKind.SimpleAssignment, Identifier.section);
747751
EmitEndBlock();
748752

749-
EmitBindingLogic(complexElementType, Identifier.element, Identifier.section, InitializationKind.None);
753+
EmitBindingLogic(complexElementType, Identifier.element, Identifier.section, InitializationKind.None, ValueDefaulting.None);
750754
_writer.WriteLine($"{instanceIdentifier}[{parsedKeyExpr}] = {Identifier.element};");
751755
}
752756
break;
@@ -774,7 +778,8 @@ private void EmitBindCoreImplForObject(ObjectSpec type)
774778
property,
775779
memberAccessExpr: $"{containingTypeRef}.{property.Name}",
776780
GetSectionPathFromConfigurationExpression(property.ConfigurationKeyName),
777-
canSet: property.CanSet);
781+
canSet: property.CanSet,
782+
InitializationKind.Declaration);
778783
}
779784
}
780785
}
@@ -783,9 +788,11 @@ private bool EmitBindImplForMember(
783788
MemberSpec member,
784789
string memberAccessExpr,
785790
string sectionPathExpr,
786-
bool canSet)
791+
bool canSet,
792+
InitializationKind initializationKind)
787793
{
788794
TypeSpec effectiveMemberType = member.Type.EffectiveType;
795+
789796
string sectionParseExpr = GetSectionFromConfigurationExpression(member.ConfigurationKeyName);
790797

791798
switch (effectiveMemberType)
@@ -794,19 +801,20 @@ private bool EmitBindImplForMember(
794801
{
795802
if (canSet)
796803
{
797-
bool checkForNullSectionValue = member is ParameterSpec
798-
? true
799-
: stringParsableType.StringParsableTypeKind is not StringParsableTypeKind.AssignFromSectionValue;
800-
801-
string nullBangExpr = checkForNullSectionValue ? string.Empty : "!";
804+
bool useDefaultValueIfSectionValueIsNull =
805+
initializationKind == InitializationKind.Declaration &&
806+
member is PropertySpec &&
807+
member.Type.IsValueType &&
808+
member.Type.SpecKind is not TypeSpecKind.Nullable;
802809

803810
EmitBlankLineIfRequired();
804811
EmitBindingLogic(
805812
stringParsableType,
806813
$@"{Identifier.configuration}[""{member.ConfigurationKeyName}""]",
807814
sectionPathExpr,
808-
writeOnSuccess: parsedValueExpr => _writer.WriteLine($"{memberAccessExpr} = {parsedValueExpr}{nullBangExpr};"),
809-
checkForNullSectionValue,
815+
writeOnSuccess: parsedValueExpr => _writer.WriteLine($"{memberAccessExpr} = {parsedValueExpr};"),
816+
checkForNullSectionValue: true,
817+
useDefaultValueIfSectionValueIsNull,
810818
useIncrementalStringValueIdentifier: true);
811819
}
812820

@@ -906,14 +914,17 @@ private void EmitBindingLogicForComplexMember(
906914
targetObjAccessExpr,
907915
configArgExpr,
908916
initKind,
909-
writeOnSuccess);
917+
ValueDefaulting.None,
918+
writeOnSuccess
919+
);
910920
}
911921

912922
private void EmitBindingLogic(
913923
ComplexTypeSpec type,
914924
string memberAccessExpr,
915925
string configArgExpr,
916926
InitializationKind initKind,
927+
ValueDefaulting valueDefaulting,
917928
Action<string>? writeOnSuccess = null)
918929
{
919930
if (!type.HasBindableMembers)
@@ -952,7 +963,7 @@ private void EmitBindingLogic(
952963

953964
void EmitBindingLogic(string instanceToBindExpr, InitializationKind initKind)
954965
{
955-
string bindCoreCall = $@"{nameof(MethodsToGen_CoreBindingHelper.BindCore)}({configArgExpr}, ref {instanceToBindExpr}, {Identifier.binderOptions});";
966+
string bindCoreCall = $@"{nameof(MethodsToGen_CoreBindingHelper.BindCore)}({configArgExpr}, ref {instanceToBindExpr}, defaultValueIfNotFound: {FormatDefaultValueIfNotFound()}, {Identifier.binderOptions});";
956967

957968
if (type.CanInstantiate)
958969
{
@@ -984,6 +995,8 @@ void EmitBindCoreCall()
984995
_writer.WriteLine(bindCoreCall);
985996
writeOnSuccess?.Invoke(instanceToBindExpr);
986997
}
998+
999+
string FormatDefaultValueIfNotFound() => valueDefaulting == ValueDefaulting.CallSetter ? "true" : "false";
9871000
}
9881001
}
9891002

@@ -993,6 +1006,7 @@ private void EmitBindingLogic(
9931006
string sectionPathExpr,
9941007
Action<string>? writeOnSuccess,
9951008
bool checkForNullSectionValue,
1009+
bool useDefaultValueIfSectionValueIsNull,
9961010
bool useIncrementalStringValueIdentifier)
9971011
{
9981012
StringParsableTypeKind typeKind = type.StringParsableTypeKind;
@@ -1018,6 +1032,14 @@ private void EmitBindingLogic(
10181032
EmitEndBlock();
10191033
}
10201034

1035+
if (useDefaultValueIfSectionValueIsNull)
1036+
{
1037+
parsedValueExpr = $"default";
1038+
EmitStartBlock($"else if (defaultValueIfNotFound)");
1039+
InvokeWriteOnSuccess();
1040+
EmitEndBlock();
1041+
}
1042+
10211043
void InvokeWriteOnSuccess() => writeOnSuccess?.Invoke(parsedValueExpr);
10221044
}
10231045

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,23 @@ private enum InitializationKind
2424
Declaration = 3,
2525
}
2626

27+
/// <summary>
28+
/// The type of defaulting for a property if it does not have a config entry.
29+
/// This should only be applied for "Get" cases, not "Bind" and is also conditioned
30+
/// on the source generated for a particular property as to whether it uses this value.
31+
/// Note this is different than "InitializationKind.Declaration" since it only applied to
32+
/// complex types and not arrays\enumerables.
33+
/// </summary>
34+
private enum ValueDefaulting
35+
{
36+
None = 0,
37+
38+
/// <summary>
39+
/// Call the setter with the default value for the property's Type.
40+
/// </summary>
41+
CallSetter = 1,
42+
}
43+
2744
private static class Expression
2845
{
2946
public const string configurationGetSection = "configuration.GetSection";

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Specs/Members/MemberSpec.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ public MemberSpec(ISymbol member)
1616
}
1717

1818
public string Name { get; }
19-
public bool ErrorOnFailedBinding { get; protected set; }
2019
public string DefaultValueExpr { get; protected set; }
2120

2221
public required TypeSpec Type { get; init; }

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Specs/Members/ParameterSpec.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ public ParameterSpec(IParameterSymbol parameter) : base(parameter)
2626
}
2727
}
2828

29+
public bool ErrorOnFailedBinding { get; private set; }
30+
2931
public RefKind RefKind { get; }
3032

3133
public override bool CanGet => false;

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Specs/Types/SimpleTypeSpec.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ internal enum StringParsableTypeKind
5454
None = 0,
5555

5656
/// <summary>
57-
/// Declared types that can be assigned directly from IConfigurationSection.Value, i.e. string and tyepof(object).
57+
/// Declared types that can be assigned directly from IConfigurationSection.Value, i.e. string and typeof(object).
5858
/// </summary>
5959
AssignFromSectionValue = 1,
6060
Enum = 2,

0 commit comments

Comments
 (0)