Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update binding generator to do case-insensitive config-key/property name matching, & make some formatting improvements #88338

Merged
merged 2 commits into from
Jul 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,13 @@ private sealed partial class Emitter
private readonly SourceProductionContext _context;
private readonly SourceGenerationSpec _sourceGenSpec;

// Postfix for stringValueX variables used to save config value indexer
// results e.g. if (configuration["Key"] is string stringValue0) { ... }
private int _parseValueCount;

private bool _precedingBlockExists;

private readonly SourceWriter _writer = new();
private bool _emitBlankLineBeforeNextStatement;
private bool _useFullyQualifiedNames;
private int _valueSuffixIndex;

private static readonly Regex s_arrayBracketsRegex = new(Regex.Escape("[]"));

public bool _useFullyQualifiedNames { get; private set; }
private readonly SourceWriter _writer = new();

public Emitter(SourceProductionContext context, SourceGenerationSpec sourceGenSpec)
{
Expand All @@ -40,9 +36,13 @@ public void Emit()
return;
}

_writer.WriteLine(@"// <auto-generated/>
#nullable enable
");
_writer.WriteBlock("""
// <auto-generated/>
#nullable enable
#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
""");
_writer.WriteBlankLine();

_useFullyQualifiedNames = true;
EmitBinder_ConfigurationBinder();
EmitBinder_Extensions_OptionsBuilder();
Expand All @@ -54,150 +54,128 @@ public void Emit()
_context.AddSource($"{Identifier.GeneratedConfigurationBinder}.g.cs", _writer.ToSourceText());
}

private void EmitBindLogicFromRootMethod(TypeSpec type, string expressionForMemberAccess, InitializationKind initKind)
{
TypeSpecKind kind = type.SpecKind;

if (kind is TypeSpecKind.Nullable)
{
EmitBindLogicFromRootMethod(((NullableSpec)type).UnderlyingType, expressionForMemberAccess, initKind);
}
else
{
if (type is ParsableFromStringSpec stringParsableType)
{
if (initKind is InitializationKind.Declaration)
{
EmitCastToIConfigurationSection();
_writer.WriteLine($"{GetTypeDisplayString(type)} {expressionForMemberAccess} = default!;");
}
else
{
EmitCastToIConfigurationSection();
}

EmitBindLogicFromString(stringParsableType, Expression.sectionValue, Expression.sectionPath);
}
else
{
EmitBindCoreCall(type, expressionForMemberAccess, Identifier.configuration, initKind);
}
}
}

private void EmitBindCoreCall(
TypeSpec type,
string expressionForMemberAccess,
string expressionForConfigArg,
InitializationKind initKind)
string memberAccessExpr,
string configArgExpr,
InitializationKind initKind,
Action<string>? writeOnSuccess = null)
{
Debug.Assert(type.CanInitialize);

string tempVarName = GetIncrementalVarName(Identifier.temp);
if (!type.NeedsMemberBinding)
{
EmitObjectInit(memberAccessExpr, initKind);
return;
}

string tempIdentifier = GetIncrementalIdentifier(Identifier.temp);
if (initKind is InitializationKind.AssignmentWithNullCheck)
{
_writer.WriteLine($"{type.MinimalDisplayString} {tempVarName} = {expressionForMemberAccess};");
EmitObjectInit(type, tempVarName, InitializationKind.AssignmentWithNullCheck);
EmitBindCoreCall(tempVarName);
_writer.WriteLine($"{type.MinimalDisplayString} {tempIdentifier} = {memberAccessExpr};");
EmitBindCoreCall(tempIdentifier, InitializationKind.AssignmentWithNullCheck);
}
else if (initKind is InitializationKind.None && type.IsValueType)
{
EmitObjectInit(type, tempVarName, InitializationKind.Declaration);
_writer.WriteLine($@"{Identifier.BindCore}({expressionForConfigArg}, ref {tempVarName}, {Identifier.binderOptions});");
_writer.WriteLine($"{expressionForMemberAccess} = {tempVarName};");
EmitBindCoreCall(tempIdentifier, InitializationKind.Declaration);
_writer.WriteLine($"{memberAccessExpr} = {tempIdentifier};");
}
else
{
EmitObjectInit(type, expressionForMemberAccess, initKind);
EmitBindCoreCall(expressionForMemberAccess);
EmitBindCoreCall(memberAccessExpr, initKind);
}

void EmitBindCoreCall(string varName)
void EmitBindCoreCall(string objExpression, InitializationKind initKind)
{
string bindCoreCall = $@"{GetHelperMethodDisplayString(Identifier.BindCore)}({expressionForConfigArg}, ref {varName}, {Identifier.binderOptions});";
string methodDisplayString = GetHelperMethodDisplayString(nameof(MethodsToGen_CoreBindingHelper.BindCore));
string bindCoreCall = $@"{methodDisplayString}({configArgExpr}, ref {objExpression}, {Identifier.binderOptions});";

EmitObjectInit(objExpression, initKind);
_writer.WriteLine(bindCoreCall);
writeOnSuccess?.Invoke(objExpression);
}

void EmitObjectInit(string objExpression, InitializationKind initKind)
{
if (initKind is not InitializationKind.None)
{
this.EmitObjectInit(type, objExpression, initKind, configArgExpr);
}
}
}

public void EmitBindLogicFromString(
private void EmitBindLogicFromString(
ParsableFromStringSpec type,
string configStringValueExpr,
string configValuePathExpr,
Action<string>? writeOnSuccess = null,
bool isCollectionElement = false)
string sectionValueExpr,
string sectionPathExpr,
Action<string>? writeOnSuccess,
bool checkForNullSectionValue,
bool useIncrementalStringValueIdentifier)
{
StringParsableTypeKind typeKind = type.StringParsableTypeKind;
Debug.Assert(typeKind is not StringParsableTypeKind.None);

string stringValueVarName = GetIncrementalVarName(Identifier.stringValue);
string parsedValueExpr;
string nonNull_StringValue_Identifier = useIncrementalStringValueIdentifier ? GetIncrementalIdentifier(Identifier.value) : Identifier.value;
string stringValueToParse_Expr = checkForNullSectionValue ? nonNull_StringValue_Identifier : sectionValueExpr;

if (typeKind is StringParsableTypeKind.ConfigValue)
string parsedValueExpr;
if (typeKind is StringParsableTypeKind.AssignFromSectionValue)
{
if (isCollectionElement)
{
parsedValueExpr = stringValueVarName;
}
else
{
writeOnSuccess?.Invoke(configStringValueExpr);
return;
}
parsedValueExpr = stringValueToParse_Expr;
}
else
{
string helperMethodDisplayString = GetHelperMethodDisplayString(type.ParseMethodName);
parsedValueExpr = $"{helperMethodDisplayString}({stringValueVarName}, () => {configValuePathExpr})";
parsedValueExpr = $"{helperMethodDisplayString}({stringValueToParse_Expr}, () => {sectionPathExpr})";
}

_writer.WriteBlockStart($"if ({configStringValueExpr} is string {stringValueVarName})");
writeOnSuccess?.Invoke(parsedValueExpr);
_writer.WriteBlockEnd();

return;
if (!checkForNullSectionValue)
{
writeOnSuccess?.Invoke(parsedValueExpr);
}
else
{
_writer.WriteBlockStart($"if ({sectionValueExpr} is string {nonNull_StringValue_Identifier})");
writeOnSuccess?.Invoke(parsedValueExpr);
_writer.WriteBlockEnd();
}
}

private bool EmitObjectInit(TypeSpec type, string expressionForMemberAccess, InitializationKind initKind)
private bool EmitObjectInit(TypeSpec type, string memberAccessExpr, InitializationKind initKind, string configArgExpr)
{
Debug.Assert(type.CanInitialize);

if (initKind is InitializationKind.None)
{
return true;
}
Debug.Assert(type.CanInitialize && initKind is not InitializationKind.None);

string expressionForInit;
string initExpr;
CollectionSpec? collectionType = type as CollectionSpec;

string effectiveDisplayString = GetTypeDisplayString(type);
if (collectionType is not null)
{
if (collectionType is EnumerableSpec { InitializationStrategy: InitializationStrategy.Array })
{
expressionForInit = $"new {s_arrayBracketsRegex.Replace(effectiveDisplayString, "[0]", 1)}";
initExpr = $"new {s_arrayBracketsRegex.Replace(effectiveDisplayString, "[0]", 1)}";
}
else
{
effectiveDisplayString = GetTypeDisplayString(collectionType.ConcreteType ?? collectionType);
expressionForInit = $"new {effectiveDisplayString}()";
initExpr = $"new {effectiveDisplayString}()";
}
}
else if (type.InitializationStrategy is InitializationStrategy.ParameterlessConstructor)
{
expressionForInit = $"new {effectiveDisplayString}()";
initExpr = $"new {effectiveDisplayString}()";
}
else
{
Debug.Assert(type.InitializationStrategy is InitializationStrategy.ParameterizedConstructor);
string expressionForConfigSection = initKind is InitializationKind.Declaration ? Identifier.configuration : Identifier.section;
string initMethodIdentifier = GetHelperMethodDisplayString(((ObjectSpec)type).InitializeMethodDisplayString);
expressionForInit = $"{initMethodIdentifier}({expressionForConfigSection}, {Identifier.binderOptions});";
string initMethodIdentifier = GetInitalizeMethodDisplayString(((ObjectSpec)type));
initExpr = $"{initMethodIdentifier}({configArgExpr}, {Identifier.binderOptions})";
}

if (initKind == InitializationKind.Declaration)
{
Debug.Assert(!expressionForMemberAccess.Contains("."));
_writer.WriteLine($"var {expressionForMemberAccess} = {expressionForInit};");
Debug.Assert(!memberAccessExpr.Contains("."));
_writer.WriteLine($"var {memberAccessExpr} = {initExpr};");
}
else if (initKind == InitializationKind.AssignmentWithNullCheck)
{
Expand All @@ -208,28 +186,28 @@ private bool EmitObjectInit(TypeSpec type, string expressionForMemberAccess, Ini
{
if (collectionType.InitializationStrategy is InitializationStrategy.ParameterizedConstructor)
{
_writer.WriteLine($"{expressionForMemberAccess} = {expressionForMemberAccess} is null ? {expressionForInit} : new {effectiveDisplayString}({expressionForMemberAccess});");
_writer.WriteLine($"{memberAccessExpr} = {memberAccessExpr} is null ? {initExpr} : new {effectiveDisplayString}({memberAccessExpr});");
}
else
{
_writer.WriteLine($"{expressionForMemberAccess} = {expressionForMemberAccess} is null ? {expressionForInit} : {expressionForMemberAccess}.{collectionType.ToEnumerableMethodCall!};");
_writer.WriteLine($"{memberAccessExpr} = {memberAccessExpr} is null ? {initExpr} : {memberAccessExpr}.{collectionType.ToEnumerableMethodCall!};");
}
}
else
{
_writer.WriteLine($"{expressionForMemberAccess} ??= {expressionForInit};");
_writer.WriteLine($"{memberAccessExpr} ??= {initExpr};");
}
}
else
{
Debug.Assert(initKind is InitializationKind.SimpleAssignment);
_writer.WriteLine($"{expressionForMemberAccess} = {expressionForInit};");
_writer.WriteLine($"{memberAccessExpr} = {initExpr};");
}

return true;
}

public void EmitCastToIConfigurationSection()
private void EmitCastToIConfigurationSection()
{
string sectionTypeDisplayString;
string exceptionTypeDisplayString;
Expand All @@ -252,7 +230,7 @@ public void EmitCastToIConfigurationSection()
""");
}

public void EmitIConfigurationHasValueOrChildrenCheck(bool voidReturn)
private void EmitIConfigurationHasValueOrChildrenCheck(bool voidReturn)
{
string returnPostfix = voidReturn ? string.Empty : " null";
string methodDisplayString = GetHelperMethodDisplayString(Identifier.HasValueOrChildren);
Expand Down
Loading