From 7036cba28f20f6a12f4cd2c467f095819983a8c1 Mon Sep 17 00:00:00 2001 From: Layomi Akinrinade Date: Wed, 19 Jul 2023 12:56:50 -0700 Subject: [PATCH] Share SourceWriter between JSON & config binding generators (#89150) * Share SourceWriter between JSON & config binding generators * Fix failing test, clean up impl, and address feedback --- .../src/SourceGenerators}/SourceWriter.cs | 41 ++--- .../Common/tests/Common.Tests.csproj | 6 + .../SourceGenerators/SourceWriterTests.cs | 32 ++++ .../ConfigurationBindingGenerator.Emitter.cs | 25 +-- .../Helpers/Emitter/ConfigurationBinder.cs | 6 +- .../gen/Helpers/Emitter/CoreBindingHelper.cs | 142 +++++++++--------- .../gen/Helpers/Emitter/Helpers.cs | 43 +++++- .../OptionsBuilderConfigurationExtensions.cs | 38 ++--- ...onfigurationServiceCollectionExtensions.cs | 42 +++--- .../gen/Helpers/SourceWriter.cs | 118 --------------- ...nfiguration.Binder.SourceGeneration.csproj | 2 +- .../Baselines/Collections.generated.txt | 5 + .../ConfigurationBinder/Bind.generated.txt | 5 + .../Bind_Instance.generated.txt | 2 + .../Bind_Instance_BinderOptions.generated.txt | 5 + .../Bind_Key_Instance.generated.txt | 2 + .../ConfigurationBinder/Get.generated.txt | 5 + .../ConfigurationBinder/Get_T.generated.txt | 5 + .../Get_T_BinderOptions.generated.txt | 5 + .../Get_TypeOf.generated.txt | 5 + .../Get_TypeOf_BinderOptions.generated.txt | 5 + .../BindConfiguration.generated.txt | 5 + .../OptionsBuilder/Bind_T.generated.txt | 5 + .../Bind_T_BinderOptions.generated.txt | 5 + .../Baselines/Primitives.generated.txt | 2 + .../Configure_T.generated.txt | 5 + .../Configure_T_BinderOptions.generated.txt | 5 + .../Configure_T_name.generated.txt | 5 + ...nfigure_T_name_BinderOptions.generated.txt | 5 + .../ConfigurationBindingGeneratorTests.cs | 3 +- .../gen/JsonSourceGenerator.Emitter.cs | 1 + .../System.Text.Json.SourceGeneration.targets | 4 +- 32 files changed, 298 insertions(+), 286 deletions(-) rename src/libraries/{System.Text.Json/gen/Helpers => Common/src/SourceGenerators}/SourceWriter.cs (79%) create mode 100644 src/libraries/Common/tests/Tests/SourceGenerators/SourceWriterTests.cs delete mode 100644 src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/SourceWriter.cs diff --git a/src/libraries/System.Text.Json/gen/Helpers/SourceWriter.cs b/src/libraries/Common/src/SourceGenerators/SourceWriter.cs similarity index 79% rename from src/libraries/System.Text.Json/gen/Helpers/SourceWriter.cs rename to src/libraries/Common/src/SourceGenerators/SourceWriter.cs index 8bada81593a64..0cc443d86fc3f 100644 --- a/src/libraries/System.Text.Json/gen/Helpers/SourceWriter.cs +++ b/src/libraries/Common/src/SourceGenerators/SourceWriter.cs @@ -1,42 +1,21 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.CodeAnalysis.Text; +using System; +using System.Text; using System.Diagnostics; +using Microsoft.CodeAnalysis.Text; -namespace System.Text.Json.SourceGeneration +namespace SourceGenerators { internal sealed class SourceWriter { + private const char IndentationChar = ' '; + private const int CharsPerIndentation = 4; + private readonly StringBuilder _sb = new(); private int _indentation; - public SourceWriter() - { - IndentationChar = ' '; - CharsPerIndentation = 4; - } - - public SourceWriter(char indentationChar, int charsPerIndentation) - { - if (!char.IsWhiteSpace(indentationChar)) - { - throw new ArgumentOutOfRangeException(nameof(indentationChar)); - } - - if (charsPerIndentation < 1) - { - throw new ArgumentOutOfRangeException(nameof(charsPerIndentation)); - } - - IndentationChar = indentationChar; - CharsPerIndentation = charsPerIndentation; - } - - public char IndentationChar { get; } - public int CharsPerIndentation { get; } - - public int Length => _sb.Length; public int Indentation { get => _indentation; @@ -88,6 +67,12 @@ public SourceText ToSourceText() return SourceText.From(_sb.ToString(), Encoding.UTF8); } + public void Reset() + { + _sb.Clear(); + _indentation = 0; + } + private void AddIndentation() => _sb.Append(IndentationChar, CharsPerIndentation * _indentation); diff --git a/src/libraries/Common/tests/Common.Tests.csproj b/src/libraries/Common/tests/Common.Tests.csproj index b7e40f2e79890..710ac02810174 100644 --- a/src/libraries/Common/tests/Common.Tests.csproj +++ b/src/libraries/Common/tests/Common.Tests.csproj @@ -18,6 +18,8 @@ Link="Common\Interop\Linux\Interop.ProcFsStat.TryReadStatusFile.cs" /> + + @@ -155,6 +158,9 @@ + + + diff --git a/src/libraries/Common/tests/Tests/SourceGenerators/SourceWriterTests.cs b/src/libraries/Common/tests/Tests/SourceGenerators/SourceWriterTests.cs new file mode 100644 index 0000000000000..ab0e53b07d526 --- /dev/null +++ b/src/libraries/Common/tests/Tests/SourceGenerators/SourceWriterTests.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using SourceGenerators; +using Xunit; + +namespace Common.Tests +{ + public sealed class SourceWriterTests + { + [Fact] + public void CanHandleVariousLineEndings() + { + string testTemplate = "public static void Main(){0}{{{1}\tConsole.WriteLine(\"Hello, world\");{2}}}"; + SourceWriter writer = new(); + + CheckCanWrite(string.Format(testTemplate, "\n", "\n", "\n")); + CheckCanWrite(string.Format(testTemplate, "\r\n", "\r\n", "\r\n")); + CheckCanWrite(string.Format(testTemplate, "\n", "\r\n", "\n")); + + // Regression test for https://github.com/dotnet/runtime/issues/88918. + CheckCanWrite(" global::Microsoft.Extensions.DependencyInjection.OptionsServiceCollectionExtensions.AddOptions(services);\r\n global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton>(services, new global::Microsoft.Extensions.Options.ConfigurationChangeTokenSource(name, configuration));\r\n return global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton>(services, new global::Microsoft.Extensions.Options.ConfigureNamedOptions(name, obj => global::Microsoft.Extensions.Configuration.Binder.SourceGeneration.CoreBindingHelper.BindCoreUntyped(configuration, obj, typeof(TOptions), configureOptions)));\r\n}"); + + void CheckCanWrite(string source) + { + // No exception expected. + writer.WriteLine(source); + writer.Reset(); + } + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Emitter.cs index f24a43c022ae4..fcfeece70f7b7 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Emitter.cs @@ -5,6 +5,7 @@ using System.Diagnostics; using System.Text.RegularExpressions; using Microsoft.CodeAnalysis; +using SourceGenerators; namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { @@ -36,17 +37,17 @@ public void Emit() return; } - _writer.WriteBlock(""" + _writer.WriteLine(""" // #nullable enable #pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code. """); - _writer.WriteBlankLine(); + _writer.WriteLine(); _useFullyQualifiedNames = true; - EmitBinder_ConfigurationBinder(); + EmitBinder_Extensions_IConfiguration(); EmitBinder_Extensions_OptionsBuilder(); - EmitBinder_Extensions_ServiceCollection(); + EmitBinder_Extensions_IServiceCollection(); _useFullyQualifiedNames = false; Emit_CoreBindingHelper(); @@ -131,14 +132,16 @@ private void EmitBindLogicFromString( if (!checkForNullSectionValue) { - writeOnSuccess?.Invoke(parsedValueExpr); + InvokeWriteOnSuccess(); } else { - _writer.WriteBlockStart($"if ({sectionValueExpr} is string {nonNull_StringValue_Identifier})"); - writeOnSuccess?.Invoke(parsedValueExpr); - _writer.WriteBlockEnd(); + EmitStartBlock($"if ({sectionValueExpr} is string {nonNull_StringValue_Identifier})"); + InvokeWriteOnSuccess(); + EmitEndBlock(); } + + void InvokeWriteOnSuccess() => writeOnSuccess?.Invoke(parsedValueExpr); } private bool EmitObjectInit(TypeSpec type, string memberAccessExpr, InitializationKind initKind, string configArgExpr) @@ -222,7 +225,7 @@ private void EmitCastToIConfigurationSection() exceptionTypeDisplayString = nameof(InvalidOperationException); } - _writer.WriteBlock($$""" + _writer.WriteLine($$""" if ({{Identifier.configuration}} is not {{sectionTypeDisplayString}} {{Identifier.section}}) { throw new {{exceptionTypeDisplayString}}(); @@ -235,13 +238,13 @@ private void EmitIConfigurationHasValueOrChildrenCheck(bool voidReturn) string returnPostfix = voidReturn ? string.Empty : " null"; string methodDisplayString = GetHelperMethodDisplayString(Identifier.HasValueOrChildren); - _writer.WriteBlock($$""" + _writer.WriteLine($$""" if (!{{methodDisplayString}}({{Identifier.configuration}})) { return{{returnPostfix}}; } """); - _writer.WriteBlankLine(); + _writer.WriteLine(); } } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/ConfigurationBinder.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/ConfigurationBinder.cs index d71e414af19dc..c10e607df75d0 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/ConfigurationBinder.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/ConfigurationBinder.cs @@ -14,7 +14,7 @@ private sealed partial class Emitter { private bool ShouldEmitMethods(MethodsToGen_ConfigurationBinder methods) => (_sourceGenSpec.MethodsToGen_ConfigurationBinder & methods) != 0; - private void EmitBinder_ConfigurationBinder() + private void EmitBinder_Extensions_IConfiguration() { Debug.Assert(_sourceGenSpec.TypesForGen_ConfigurationBinder_BindMethods.Count <= 3 && !_sourceGenSpec.TypesForGen_ConfigurationBinder_BindMethods.Keys.Any(overload => (overload & MethodsToGen_ConfigurationBinder.Bind) is 0)); @@ -25,13 +25,13 @@ private void EmitBinder_ConfigurationBinder() } _emitBlankLineBeforeNextStatement = false; - EmitRootBindingClassBlockStart(Identifier.GeneratedConfigurationBinder); + EmitRootBindingClassStartBlock(Identifier.GeneratedConfigurationBinder); EmitGetMethods(); EmitGetValueMethods(); EmitBindMethods_ConfigurationBinder(); - _writer.WriteBlockEnd(); + EmitEndBlock(); _emitBlankLineBeforeNextStatement = true; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/CoreBindingHelper.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/CoreBindingHelper.cs index dd13ee8e2a358..08015d0abc59f 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/CoreBindingHelper.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/CoreBindingHelper.cs @@ -19,19 +19,18 @@ private sealed partial class Emitter private void Emit_CoreBindingHelper() { Debug.Assert(_emitBlankLineBeforeNextStatement); - _writer.WriteBlankLine(); + _writer.WriteLine(); _emitBlankLineBeforeNextStatement = false; - _writer.WriteBlockStart($"namespace {ProjectName}"); + EmitStartBlock($"namespace {ProjectName}"); EmitHelperUsingStatements(); - _writer.WriteBlankLine(); + _writer.WriteLine(); - _writer.WriteBlock($$""" + EmitStartBlock($$""" /// Provide core binding logic. {{GetGeneratedCodeAttributeSrc()}} file static class {{Identifier.CoreBindingHelper}} - { """); EmitConfigurationKeyCaches(); @@ -42,8 +41,8 @@ file static class {{Identifier.CoreBindingHelper}} EmitInitializeMethods(); EmitHelperMethods(); - _writer.WriteBlockEnd(); // End helper class. - _writer.WriteBlockEnd(); // End namespace. + EmitEndBlock(); // End helper class. + EmitEndBlock(); // End namespace. } private void EmitHelperUsingStatements() @@ -88,12 +87,12 @@ private void EmitGetCoreMethod() } EmitBlankLineIfRequired(); - _writer.WriteBlockStart($"public static object? {nameof(MethodsToGen_CoreBindingHelper.GetCore)}(this {Identifier.IConfiguration} {Identifier.configuration}, Type {Identifier.type}, Action<{Identifier.BinderOptions}>? {Identifier.configureOptions})"); + EmitStartBlock($"public static object? {nameof(MethodsToGen_CoreBindingHelper.GetCore)}(this {Identifier.IConfiguration} {Identifier.configuration}, Type {Identifier.type}, Action<{Identifier.BinderOptions}>? {Identifier.configureOptions})"); EmitCheckForNullArgument_WithBlankLine(Identifier.configuration); _writer.WriteLine($"{Identifier.BinderOptions}? {Identifier.binderOptions} = {Identifier.GetBinderOptions}({Identifier.configureOptions});"); - _writer.WriteBlankLine(); + _writer.WriteLine(); EmitIConfigurationHasValueOrChildrenCheck(voidReturn: false); @@ -101,7 +100,7 @@ private void EmitGetCoreMethod() { TypeSpecKind kind = type.SpecKind; - _writer.WriteBlockStart($"if (type == typeof({type.MinimalDisplayString}))"); + EmitStartBlock($"if (type == typeof({type.MinimalDisplayString}))"); if (type is ParsableFromStringSpec stringParsableType) { @@ -120,12 +119,12 @@ private void EmitGetCoreMethod() _writer.WriteLine($"return {Identifier.obj};"); } - _writer.WriteBlockEnd(); - _writer.WriteBlankLine(); + EmitEndBlock(); + _writer.WriteLine(); } Emit_NotSupportedException_TypeNotDetectedAsInput(); - _writer.WriteBlockEnd(); + EmitEndBlock(); _emitBlankLineBeforeNextStatement = true; } @@ -137,24 +136,24 @@ private void EmitGetValueCoreMethod() } EmitBlankLineIfRequired(); - _writer.WriteBlockStart($"public static object? {nameof(MethodsToGen_CoreBindingHelper.GetValueCore)}(this {Identifier.IConfiguration} {Identifier.configuration}, Type {Identifier.type}, string {Identifier.key})"); + EmitStartBlock($"public static object? {nameof(MethodsToGen_CoreBindingHelper.GetValueCore)}(this {Identifier.IConfiguration} {Identifier.configuration}, Type {Identifier.type}, string {Identifier.key})"); EmitCheckForNullArgument_WithBlankLine(Identifier.configuration); _writer.WriteLine($@"{Identifier.IConfigurationSection} {Identifier.section} = {GetSectionFromConfigurationExpression(Identifier.key, addQuotes: false)};"); - _writer.WriteBlankLine(); + _writer.WriteLine(); - _writer.WriteBlock($$""" + _writer.WriteLine($$""" if ({{Expression.sectionValue}} is not string {{Identifier.value}}) { return null; } """); - _writer.WriteBlankLine(); + _writer.WriteLine(); foreach (TypeSpec type in targetTypes) { - _writer.WriteBlockStart($"if ({Identifier.type} == typeof({type.MinimalDisplayString}))"); + EmitStartBlock($"if ({Identifier.type} == typeof({type.MinimalDisplayString}))"); EmitBindLogicFromString( (ParsableFromStringSpec)type.EffectiveType, @@ -164,12 +163,12 @@ private void EmitGetValueCoreMethod() checkForNullSectionValue: false, useIncrementalStringValueIdentifier: false); - _writer.WriteBlockEnd(); - _writer.WriteBlankLine(); + EmitEndBlock(); + _writer.WriteLine(); } _writer.WriteLine("return null;"); - _writer.WriteBlockEnd(); + EmitEndBlock(); _emitBlankLineBeforeNextStatement = true; } @@ -182,18 +181,18 @@ private void EmitBindCoreUntypedMethod() EmitBlankLineIfRequired(); - _writer.WriteBlockStart($"public static void {nameof(MethodsToGen_CoreBindingHelper.BindCoreUntyped)}(this {Identifier.IConfiguration} {Identifier.configuration}, object {Identifier.obj}, Type {Identifier.type}, {MinimalDisplayString.NullableActionOfBinderOptions} {Identifier.configureOptions})"); + EmitStartBlock($"public static void {nameof(MethodsToGen_CoreBindingHelper.BindCoreUntyped)}(this {Identifier.IConfiguration} {Identifier.configuration}, object {Identifier.obj}, Type {Identifier.type}, {MinimalDisplayString.NullableActionOfBinderOptions} {Identifier.configureOptions})"); EmitCheckForNullArgument_WithBlankLine(Identifier.configuration); _writer.WriteLine($"{Identifier.BinderOptions}? {Identifier.binderOptions} = {Identifier.GetBinderOptions}({Identifier.configureOptions});"); - _writer.WriteBlankLine(); + _writer.WriteLine(); EmitIConfigurationHasValueOrChildrenCheck(voidReturn: true); foreach (TypeSpec type in targetTypes) { - _writer.WriteBlockStart($"if (type == typeof({type.MinimalDisplayString}))"); + EmitStartBlock($"if (type == typeof({type.MinimalDisplayString}))"); TypeSpec effectiveType = type.EffectiveType; if (!EmitInitException(effectiveType)) @@ -203,12 +202,12 @@ private void EmitBindCoreUntypedMethod() _writer.WriteLine($"return;"); } - _writer.WriteBlockEnd(); - _writer.WriteBlankLine(); + EmitEndBlock(); + _writer.WriteLine(); } Emit_NotSupportedException_TypeNotDetectedAsInput(); - _writer.WriteBlockEnd(); + EmitEndBlock(); _emitBlankLineBeforeNextStatement = true; } @@ -232,7 +231,7 @@ private void EmitBindCoreMethod(TypeSpec type) Debug.Assert(type.CanInitialize); string objParameterExpression = $"ref {type.MinimalDisplayString} {Identifier.obj}"; - _writer.WriteBlockStart(@$"public static void {nameof(MethodsToGen_CoreBindingHelper.BindCore)}({Identifier.IConfiguration} {Identifier.configuration}, {objParameterExpression}, {Identifier.BinderOptions}? {Identifier.binderOptions})"); + EmitStartBlock(@$"public static void {nameof(MethodsToGen_CoreBindingHelper.BindCore)}({Identifier.IConfiguration} {Identifier.configuration}, {objParameterExpression}, {Identifier.BinderOptions}? {Identifier.binderOptions})"); EmitCheckForNullArgument_WithBlankLine_IfRequired(type.IsValueType); @@ -258,7 +257,7 @@ private void EmitBindCoreMethod(TypeSpec type) EmitBindCoreImplForObject((ObjectSpec)effectiveType); } - _writer.WriteBlockEnd(); + EmitEndBlock(); } private void EmitInitializeMethods() @@ -283,7 +282,7 @@ private void EmitInitializeMethod(ObjectSpec type) List ctorArgList = new(); string displayString = type.MinimalDisplayString; - _writer.WriteBlockStart($"public static {type.MinimalDisplayString} {GetInitalizeMethodDisplayString(type)}({Identifier.IConfiguration} {Identifier.configuration}, {Identifier.BinderOptions}? {Identifier.binderOptions})"); + EmitStartBlock($"public static {type.MinimalDisplayString} {GetInitalizeMethodDisplayString(type)}({Identifier.IConfiguration} {Identifier.configuration}, {Identifier.BinderOptions}? {Identifier.binderOptions})"); _emitBlankLineBeforeNextStatement = false; foreach (ParameterSpec parameter in ctorParams) @@ -317,17 +316,17 @@ private void EmitInitializeMethod(ObjectSpec type) } else { - _writer.WriteBlockStart(returnExpression); + EmitStartBlock(returnExpression); foreach (PropertySpec property in initOnlyProps) { string propertyName = property.Name; _writer.WriteLine($@"{propertyName} = {propertyName},"); } - _writer.WriteBlockEnd(";"); + EmitEndBlock(endBraceTrailingSource: ";"); } // End method. - _writer.WriteBlockEnd(); + EmitEndBlock(); _emitBlankLineBeforeNextStatement = true; void EmitBindImplForMember(MemberSpec member) @@ -346,7 +345,7 @@ void EmitBindImplForMember(MemberSpec member) { string condition = $@" if ({Identifier.configuration}[""{member.ConfigurationKeyName}""] is not {memberType.MinimalDisplayString} {member.Name})"; EmitThrowBlock(condition); - _writer.WriteBlankLine(); + _writer.WriteLine(); return; } } @@ -386,11 +385,11 @@ void EmitBindImplForMember(MemberSpec member) EmitThrowBlock(condition: "else"); } - _writer.WriteBlankLine(); + _writer.WriteLine(); } void EmitThrowBlock(string condition) => - _writer.WriteBlock($$""" + _writer.WriteLine($$""" {{condition}} { throw new {{GetInvalidOperationDisplayName()}}("{{string.Format(ExceptionMessages.ParameterHasNoMatchingConfig, type.Name, member.Name)}}"); @@ -398,7 +397,6 @@ void EmitThrowBlock(string condition) => """); } } - private void EmitHelperMethods() { if (ShouldEmitMethods(MethodsToGen_CoreBindingHelper.BindCore)) @@ -408,15 +406,15 @@ private void EmitHelperMethods() if (ShouldEmitMethods(MethodsToGen_CoreBindingHelper.BindCoreUntyped | MethodsToGen_CoreBindingHelper.GetCore)) { - _writer.WriteBlankLine(); + _writer.WriteLine(); EmitHasValueOrChildrenMethod(); - _writer.WriteBlankLine(); + _writer.WriteLine(); EmitAsConfigWithChildrenMethod(); _emitBlankLineBeforeNextStatement = true; } else if (ShouldEmitMethods(MethodsToGen_CoreBindingHelper.AsConfigWithChildren)) { - _writer.WriteBlankLine(); + _writer.WriteLine(); EmitAsConfigWithChildrenMethod(); _emitBlankLineBeforeNextStatement = true; } @@ -425,7 +423,7 @@ private void EmitHelperMethods() MethodsToGen_CoreBindingHelper.BindCoreUntyped | MethodsToGen_CoreBindingHelper.GetCore) || ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Bind_instance_BinderOptions)) { - _writer.WriteBlankLine(); + _writer.WriteLine(); EmitGetBinderOptionsHelper(); _emitBlankLineBeforeNextStatement = true; } @@ -443,7 +441,7 @@ private void EmitValidateConfigurationKeysMethod() string exceptionMessage = string.Format(ExceptionMessages.MissingConfig, Identifier.ErrorOnUnknownConfiguration, Identifier.BinderOptions, $"{{{Identifier.type}}}", $@"{{string.Join("", "", {Identifier.temp})}}"); EmitBlankLineIfRequired(); - _writer.WriteBlock($$""" + _writer.WriteLine($$""" /// If required by the binder options, validates that there are no unknown keys in the input configuration object. public static void {{Identifier.ValidateConfigurationKeys}}(Type {{Identifier.type}}, {{MinimalDisplayString.LazyHashSetOfString}} {{keysIdentifier}}, {{Identifier.IConfiguration}} {{Identifier.configuration}}, {{Identifier.BinderOptions}}? {{Identifier.binderOptions}}) { @@ -470,7 +468,7 @@ private void EmitValidateConfigurationKeysMethod() private void EmitHasValueOrChildrenMethod() { - _writer.WriteBlock($$""" + _writer.WriteLine($$""" public static bool {{Identifier.HasValueOrChildren}}({{Identifier.IConfiguration}} {{Identifier.configuration}}) { if (({{Identifier.configuration}} as {{Identifier.IConfigurationSection}})?.{{Identifier.Value}} is not null) @@ -484,7 +482,7 @@ private void EmitHasValueOrChildrenMethod() private void EmitAsConfigWithChildrenMethod() { - _writer.WriteBlock($$""" + _writer.WriteLine($$""" public static {{Identifier.IConfiguration}}? {{Identifier.AsConfigWithChildren}}({{Identifier.IConfiguration}} {{Identifier.configuration}}) { foreach ({{Identifier.IConfigurationSection}} _ in {{Identifier.configuration}}.{{Identifier.GetChildren}}()) @@ -498,7 +496,7 @@ private void EmitAsConfigWithChildrenMethod() private void EmitGetBinderOptionsHelper() { - _writer.WriteBlock($$""" + _writer.WriteLine($$""" public static {{Identifier.BinderOptions}}? {{Identifier.GetBinderOptions}}({{MinimalDisplayString.NullableActionOfBinderOptions}} {{Identifier.configureOptions}}) { if ({{Identifier.configureOptions}} is null) @@ -593,22 +591,17 @@ private void EmitPrimitiveParseMethod(ParsableFromStringSpec type) } } - _writer.WriteBlock($$""" - public static {{typeDisplayString}} {{type.ParseMethodName}}(string {{Identifier.value}}, Func {{Identifier.getPath}}) - { - try - { - return {{parsedValueExpr}}; - """); - string exceptionArg1 = string.Format(ExceptionMessages.FailedBinding, $"{{{Identifier.getPath}()}}", $"{{typeof({typeDisplayString})}}"); - _writer.WriteBlock($$""" - } - catch ({{innerExceptionTypeDisplayString}} {{Identifier.exception}}) - { - throw new {{GetInvalidOperationDisplayName()}}($"{{exceptionArg1}}", {{Identifier.exception}}); - } + EmitStartBlock($"public static {typeDisplayString} {type.ParseMethodName}(string {Identifier.value}, Func {Identifier.getPath})"); + EmitEndBlock($$""" + try + { + return {{parsedValueExpr}}; + } + catch ({{innerExceptionTypeDisplayString}} {{Identifier.exception}}) + { + throw new {{GetInvalidOperationDisplayName()}}($"{{exceptionArg1}}", {{Identifier.exception}}); } """); } @@ -622,7 +615,7 @@ private void EmitPopulationImplForArray(EnumerableSpec type) EmitBindCoreCall(concreteType, tempIdentifier, Identifier.configuration, InitializationKind.Declaration); // Resize array and add binded elements. - _writer.WriteBlock($$""" + _writer.WriteLine($$""" {{Identifier.Int32}} {{Identifier.originalCount}} = {{Identifier.obj}}.{{Identifier.Length}}; {{Identifier.Array}}.{{Identifier.Resize}}(ref {{Identifier.obj}}, {{Identifier.originalCount}} + {{tempIdentifier}}.{{Identifier.Count}}); {{tempIdentifier}}.{{Identifier.CopyTo}}({{Identifier.obj}}, {{Identifier.originalCount}}); @@ -633,7 +626,7 @@ private void EmitPopulationImplForEnumerableWithAdd(EnumerableSpec type) { EmitCollectionCastIfRequired(type, out string objIdentifier); - Emit_Foreach_Section_In_ConfigChildren_BlockHeader(); + Emit_Foreach_Section_In_ConfigChildren_StartBlock(); TypeSpec elementType = type.ElementType; @@ -653,14 +646,14 @@ private void EmitPopulationImplForEnumerableWithAdd(EnumerableSpec type) _writer.WriteLine($"{objIdentifier}.{Identifier.Add}({Identifier.value});"); } - _writer.WriteBlockEnd(); + EmitEndBlock(); } private void EmitBindCoreImplForDictionary(DictionarySpec type) { EmitCollectionCastIfRequired(type, out string objIdentifier); - Emit_Foreach_Section_In_ConfigChildren_BlockHeader(); + Emit_Foreach_Section_In_ConfigChildren_StartBlock(); ParsableFromStringSpec keyType = type.KeyType; TypeSpec elementType = type.ElementType; @@ -710,9 +703,9 @@ void Emit_BindAndAddLogic_ForElement(string parsedKeyExpr) conditionToUseExistingElement += $" && {expressionForElementIsNotNull}"; } - _writer.WriteBlockStart($"if (!({conditionToUseExistingElement}))"); + EmitStartBlock($"if (!({conditionToUseExistingElement}))"); EmitObjectInit(elementType, Identifier.element, InitializationKind.SimpleAssignment, Identifier.section); - _writer.WriteBlockEnd(); + EmitEndBlock(); if (elementType is CollectionSpec { InitializationStrategy: InitializationStrategy.ParameterizedConstructor or InitializationStrategy.ToEnumerableMethod } collectionSpec) { @@ -723,7 +716,7 @@ void Emit_BindAndAddLogic_ForElement(string parsedKeyExpr) ? $"new {collectionSpec.ConcreteType.MinimalDisplayString}({Identifier.element})" : $"{Identifier.element}.{collectionSpec.ToEnumerableMethodCall!}"; - _writer.WriteBlock($$""" + _writer.WriteLine($$""" else { {{Identifier.element}} = {{initExpression}}; @@ -734,10 +727,9 @@ void Emit_BindAndAddLogic_ForElement(string parsedKeyExpr) EmitBindCoreCall(elementType, Identifier.element, Identifier.section, InitializationKind.None); _writer.WriteLine($"{objIdentifier}[{parsedKeyExpr}] = {Identifier.element};"); } - } - _writer.WriteBlockEnd(); + EmitEndBlock(); } private void EmitBindCoreImplForObject(ObjectSpec type) @@ -794,7 +786,7 @@ private bool EmitBindImplForMember( return true; } - string sectionParseExpr = $"{GetSectionFromConfigurationExpression(member.ConfigurationKeyName)}"; + string sectionParseExpr = GetSectionFromConfigurationExpression(member.ConfigurationKeyName); EmitBlankLineIfRequired(); @@ -807,7 +799,7 @@ private bool EmitBindImplForMember( string sectionValidationCall = $"{Identifier.AsConfigWithChildren}({sectionParseExpr})"; string sectionIdentifier = GetIncrementalIdentifier(Identifier.section); - _writer.WriteBlockStart($"if ({sectionValidationCall} is {Identifier.IConfigurationSection} {sectionIdentifier})"); + EmitStartBlock($"if ({sectionValidationCall} is {Identifier.IConfigurationSection} {sectionIdentifier})"); bool success = !EmitInitException(effectiveMemberType); if (success) @@ -815,7 +807,7 @@ private bool EmitBindImplForMember( EmitBindCoreCallForMember(member, memberAccessExpr, sectionIdentifier, canSet); } - _writer.WriteBlockEnd(); + EmitEndBlock(); return success; } @@ -896,18 +888,18 @@ private void EmitCollectionCastIfRequired(CollectionSpec type, out string objIde if (type.PopulationStrategy is CollectionPopulationStrategy.Cast_Then_Add) { objIdentifier = Identifier.temp; - _writer.WriteBlock($$""" + _writer.WriteLine($$""" if ({{Identifier.obj}} is not {{type.PopulationCastType!.MinimalDisplayString}} {{objIdentifier}}) { return; } """); - _writer.WriteBlankLine(); + _writer.WriteLine(); } } - private void Emit_Foreach_Section_In_ConfigChildren_BlockHeader() => - _writer.WriteBlockStart($"foreach ({Identifier.IConfigurationSection} {Identifier.section} in {Identifier.configuration}.{Identifier.GetChildren}())"); + private void Emit_Foreach_Section_In_ConfigChildren_StartBlock() => + EmitStartBlock($"foreach ({Identifier.IConfigurationSection} {Identifier.section} in {Identifier.configuration}.{Identifier.GetChildren}())"); private static string GetSectionPathFromConfigurationExpression(string configurationKeyName) => $@"{GetSectionFromConfigurationExpression(configurationKeyName)}.{Identifier.Path}"; diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/Helpers.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/Helpers.cs index 7325c292a0036..e0e6a36aabaa7 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/Helpers.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/Helpers.cs @@ -129,11 +129,43 @@ private bool ShouldEmitBinders() => ShouldEmitMethods(MethodsToGen_Extensions_OptionsBuilder.Any) || ShouldEmitMethods(MethodsToGen_Extensions_ServiceCollection.Any); + /// + /// Starts a block of source code. + /// + /// Source to write after the open brace. + public void EmitStartBlock(string? source = null) + { + if (source is not null) + { + _writer.WriteLine(source); + } + + _writer.WriteLine("{"); + _writer.Indentation++; + } + + /// + /// Ends a block of source code. + /// + /// Source to write before the close brace. + /// Trailing source after the end brace, e.g. ";" to end an init statement. + public void EmitEndBlock(string? source = null, string? endBraceTrailingSource = null) + { + if (source is not null) + { + _writer.WriteLine(source); + } + + string endBlockSource = endBraceTrailingSource is null ? "}" : $"}}{endBraceTrailingSource}"; + _writer.Indentation--; + _writer.WriteLine(endBlockSource); + } + private void EmitBlankLineIfRequired() { if (_emitBlankLineBeforeNextStatement) { - _writer.WriteBlankLine(); + _writer.WriteLine(); } _emitBlankLineBeforeNextStatement = true; @@ -153,14 +185,14 @@ private void EmitCheckForNullArgument_WithBlankLine(string paramName) ? "global::System.ArgumentNullException" : "ArgumentNullException"; - _writer.WriteBlock($$""" + _writer.WriteLine($$""" if ({{paramName}} is null) { throw new {{exceptionTypeDisplayString}}(nameof({{paramName}})); } """); - _writer.WriteBlankLine(); + _writer.WriteLine(); } private bool EmitInitException(TypeSpec type) @@ -176,14 +208,13 @@ private bool EmitInitException(TypeSpec type) return false; } - private void EmitRootBindingClassBlockStart(string className) + private void EmitRootBindingClassStartBlock(string className) { EmitBlankLineIfRequired(); - _writer.WriteBlock($$""" + EmitStartBlock($$""" /// Generated helper providing an AOT and linking compatible implementation for configuration binding. {{GetGeneratedCodeAttributeSrc()}} internal static class {{className}} - { """); _emitBlankLineBeforeNextStatement = false; diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsBuilderConfigurationExtensions.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsBuilderConfigurationExtensions.cs index 0055b2bf127c0..71d0b6989dd97 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsBuilderConfigurationExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsBuilderConfigurationExtensions.cs @@ -16,12 +16,12 @@ private void EmitBinder_Extensions_OptionsBuilder() return; } - EmitRootBindingClassBlockStart(Identifier.GeneratedOptionsBuilderBinder); + EmitRootBindingClassStartBlock(Identifier.GeneratedOptionsBuilderBinder); EmitBindMethods_Extensions_OptionsBuilder(); EmitBindConfigurationMethod(); - _writer.WriteBlockEnd(); + EmitEndBlock(); } private void EmitBindMethods_Extensions_OptionsBuilder() @@ -36,24 +36,24 @@ private void EmitBindMethods_Extensions_OptionsBuilder() if (ShouldEmitMethods(MethodsToGen_Extensions_OptionsBuilder.Bind_T)) { - EmitMethodBlockStart("Bind", paramList, documentation); + EmitMethodStartBlock("Bind", paramList, documentation); _writer.WriteLine($"return global::{Identifier.GeneratedOptionsBuilderBinder}.Bind({Identifier.optionsBuilder}, {Identifier.configuration}, {Identifier.configureOptions}: null);"); - _writer.WriteBlockEnd(); + EmitEndBlock(); } - EmitMethodBlockStart( + EmitMethodStartBlock( "Bind", paramList + $", {FullyQualifiedDisplayString.ActionOfBinderOptions}? {Identifier.configureOptions}", documentation); EmitCheckForNullArgument_WithBlankLine(Identifier.optionsBuilder); - _writer.WriteBlock($$""" + _writer.WriteLine($$""" global::{{Identifier.GeneratedServiceCollectionBinder}}.{{Identifier.Configure}}<{{Identifier.TOptions}}>({{Identifier.optionsBuilder}}.{{Identifier.Services}}, {{Identifier.optionsBuilder}}.Name, {{Identifier.configuration}}, {{Identifier.configureOptions}}); return {{Identifier.optionsBuilder}}; """); - _writer.WriteBlockEnd(); + EmitEndBlock(); } private void EmitBindConfigurationMethod() @@ -66,37 +66,37 @@ private void EmitBindConfigurationMethod() const string documentation = $@"/// Registers the dependency injection container to bind against the obtained from the DI service provider."; string paramList = $"string {Identifier.configSectionPath}, {FullyQualifiedDisplayString.ActionOfBinderOptions}? {Identifier.configureOptions} = null"; - EmitMethodBlockStart("BindConfiguration", paramList, documentation); + EmitMethodStartBlock("BindConfiguration", paramList, documentation); EmitCheckForNullArgument_WithBlankLine(Identifier.optionsBuilder); EmitCheckForNullArgument_WithBlankLine(Identifier.configSectionPath); - _writer.WriteBlockStart($"{Identifier.optionsBuilder}.{Identifier.Configure}<{FullyQualifiedDisplayString.IConfiguration}>(({Identifier.obj}, {Identifier.configuration}) =>"); + EmitStartBlock($"{Identifier.optionsBuilder}.{Identifier.Configure}<{FullyQualifiedDisplayString.IConfiguration}>(({Identifier.obj}, {Identifier.configuration}) =>"); - _writer.WriteBlock($$""" - {{FullyQualifiedDisplayString.IConfiguration}} {{Identifier.section}} = string.Equals(string.Empty, {{Identifier.configSectionPath}}, global::System.StringComparison.OrdinalIgnoreCase) ? {{Identifier.configuration}} : {{Identifier.configuration}}.{{Identifier.GetSection}}({{Identifier.configSectionPath}}); - {{FullyQualifiedDisplayString.CoreBindingHelper}}.{{nameof(MethodsToGen_CoreBindingHelper.BindCoreUntyped)}}({{Identifier.section}}, {{Identifier.obj}}, typeof({{Identifier.TOptions}}), {{Identifier.configureOptions}}); - """); + _writer.WriteLine($$""" + {{FullyQualifiedDisplayString.IConfiguration}} {{Identifier.section}} = string.Equals(string.Empty, {{Identifier.configSectionPath}}, global::System.StringComparison.OrdinalIgnoreCase) ? {{Identifier.configuration}} : {{Identifier.configuration}}.{{Identifier.GetSection}}({{Identifier.configSectionPath}}); + {{FullyQualifiedDisplayString.CoreBindingHelper}}.{{nameof(MethodsToGen_CoreBindingHelper.BindCoreUntyped)}}({{Identifier.section}}, {{Identifier.obj}}, typeof({{Identifier.TOptions}}), {{Identifier.configureOptions}}); + """); - _writer.WriteBlockEnd(");"); + EmitEndBlock(endBraceTrailingSource: ");"); - _writer.WriteBlankLine(); + _writer.WriteLine(); - _writer.WriteBlock($$""" + _writer.WriteLine($$""" {{FullyQualifiedDisplayString.AddSingleton}}<{{FullyQualifiedDisplayString.IOptionsChangeTokenSource}}<{{Identifier.TOptions}}>, {{FullyQualifiedDisplayString.ConfigurationChangeTokenSource}}<{{Identifier.TOptions}}>>({{Identifier.optionsBuilder}}.{{Identifier.Services}}); return {{Identifier.optionsBuilder}}; """); - _writer.WriteBlockEnd(); + EmitEndBlock(); } - private void EmitMethodBlockStart(string methodName, string paramList, string documentation) + private void EmitMethodStartBlock(string methodName, string paramList, string documentation) { paramList = $"this {FullyQualifiedDisplayString.OptionsBuilderOfTOptions} {Identifier.optionsBuilder}, {paramList}"; EmitBlankLineIfRequired(); _writer.WriteLine(documentation); - _writer.WriteBlockStart($"public static {FullyQualifiedDisplayString.OptionsBuilderOfTOptions} {methodName}<{Identifier.TOptions}>({paramList}) where {Identifier.TOptions} : class"); + EmitStartBlock($"public static {FullyQualifiedDisplayString.OptionsBuilderOfTOptions} {methodName}<{Identifier.TOptions}>({paramList}) where {Identifier.TOptions} : class"); } } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsConfigurationServiceCollectionExtensions.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsConfigurationServiceCollectionExtensions.cs index 5b4edc93b8a51..f4cd4800df123 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsConfigurationServiceCollectionExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsConfigurationServiceCollectionExtensions.cs @@ -9,14 +9,14 @@ private sealed partial class Emitter { private bool ShouldEmitMethods(MethodsToGen_Extensions_ServiceCollection methods) => (_sourceGenSpec.MethodsToGen_ServiceCollectionExt & methods) != 0; - private void EmitBinder_Extensions_ServiceCollection() + private void EmitBinder_Extensions_IServiceCollection() { if (!ShouldEmitMethods(MethodsToGen_Extensions_ServiceCollection.Any)) { return; } - EmitRootBindingClassBlockStart(Identifier.GeneratedServiceCollectionBinder); + EmitRootBindingClassStartBlock(Identifier.GeneratedServiceCollectionBinder); const string defaultNameExpr = "string.Empty"; const string configureMethodString = $"global::{Identifier.GeneratedServiceCollectionBinder}.{Identifier.Configure}"; @@ -24,56 +24,56 @@ private void EmitBinder_Extensions_ServiceCollection() if (ShouldEmitMethods(MethodsToGen_Extensions_ServiceCollection.Configure_T)) { - EmitBlockStart(configParam); + EmitStartMethod(configParam); _writer.WriteLine($"return {configureMethodString}<{Identifier.TOptions}>({Identifier.services}, {defaultNameExpr}, {Identifier.configuration}, {Identifier.configureOptions}: null);"); - _writer.WriteBlockEnd(); + EmitEndBlock(); } if (ShouldEmitMethods(MethodsToGen_Extensions_ServiceCollection.Configure_T_name)) { - EmitBlockStart( + EmitStartMethod( paramList: $"string? {Identifier.name}, " + configParam); _writer.WriteLine($"return {configureMethodString}<{Identifier.TOptions}>({Identifier.services}, {Identifier.name}, {Identifier.configuration}, {Identifier.configureOptions}: null);"); - _writer.WriteBlockEnd(); + EmitEndBlock(); } if (ShouldEmitMethods(MethodsToGen_Extensions_ServiceCollection.Configure_T_BinderOptions)) { - EmitBlockStart( + EmitStartMethod( paramList: configParam + $", {FullyQualifiedDisplayString.ActionOfBinderOptions}? {Identifier.configureOptions}"); _writer.WriteLine($"return {configureMethodString}<{Identifier.TOptions}>({Identifier.services}, {defaultNameExpr}, {Identifier.configuration}, {Identifier.configureOptions});"); - _writer.WriteBlockEnd(); + EmitEndBlock(); } + // Core Configure method that the other overloads call. + // Like the others, it is public API that could be called directly by users. + // So, it is always generated whenever a Configure overload is called. string optionsNamespaceName = "global::Microsoft.Extensions.Options"; string bindCoreUntypedDisplayString = GetHelperMethodDisplayString(nameof(MethodsToGen_CoreBindingHelper.BindCoreUntyped)); - EmitBlockStart(paramList: $"string? {Identifier.name}, " + configParam + $", {FullyQualifiedDisplayString.ActionOfBinderOptions}? {Identifier.configureOptions}"); - + EmitStartMethod(paramList: $"string? {Identifier.name}, " + configParam + $", {FullyQualifiedDisplayString.ActionOfBinderOptions}? {Identifier.configureOptions}"); EmitCheckForNullArgument_WithBlankLine(Identifier.services); EmitCheckForNullArgument_WithBlankLine(Identifier.configuration); - - _writer.WriteBlock($$""" + _writer.WriteLine($$""" global::Microsoft.Extensions.DependencyInjection.OptionsServiceCollectionExtensions.AddOptions({{Identifier.services}}); {{FullyQualifiedDisplayString.AddSingleton}}<{{FullyQualifiedDisplayString.IOptionsChangeTokenSource}}<{{Identifier.TOptions}}>>({{Identifier.services}}, new {{FullyQualifiedDisplayString.ConfigurationChangeTokenSource}}<{{Identifier.TOptions}}>({{Identifier.name}}, {{Identifier.configuration}})); return {{FullyQualifiedDisplayString.AddSingleton}}<{{optionsNamespaceName}}.IConfigureOptions<{{Identifier.TOptions}}>>({{Identifier.services}}, new {{optionsNamespaceName}}.ConfigureNamedOptions<{{Identifier.TOptions}}>({{Identifier.name}}, {{Identifier.obj}} => {{bindCoreUntypedDisplayString}}({{Identifier.configuration}}, {{Identifier.obj}}, typeof({{Identifier.TOptions}}), {{Identifier.configureOptions}}))); - } - """); + """); + EmitEndBlock(); - _writer.WriteBlockEnd(); + EmitEndBlock(); _emitBlankLineBeforeNextStatement = true; } - private void EmitBlockStart(string paramList) + private void EmitStartMethod(string paramList) { paramList = $"this {FullyQualifiedDisplayString.IServiceCollection} {Identifier.services}, {paramList}"; EmitBlankLineIfRequired(); - _writer.WriteBlock($$""" - /// Registers a configuration instance which TOptions will bind against. - public static {{FullyQualifiedDisplayString.IServiceCollection}} {{Identifier.Configure}}<{{Identifier.TOptions}}>({{paramList}}) where {{Identifier.TOptions}} : class - { - """); + EmitStartBlock($$""" + /// Registers a configuration instance which TOptions will bind against. + public static {{FullyQualifiedDisplayString.IServiceCollection}} {{Identifier.Configure}}<{{Identifier.TOptions}}>({{paramList}}) where {{Identifier.TOptions}} : class + """); } } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/SourceWriter.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/SourceWriter.cs deleted file mode 100644 index f610209874c7d..0000000000000 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/SourceWriter.cs +++ /dev/null @@ -1,118 +0,0 @@ -// 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 static readonly char[] s_newLine = Environment.NewLine.ToCharArray(); - private int _indentation; - - public void WriteBlockStart(string? declaration = null) - { - if (declaration is not null) - { - WriteLine(declaration); - } - WriteLine("{"); - _indentation++; - } - - public void WriteBlockEnd(string? extra = null) - { - _indentation--; - Debug.Assert(_indentation > -1); - WriteLine($"}}{extra}"); - } - - public void WriteLine(string source) - { - _sb.Append(' ', 4 * _indentation); - _sb.AppendLine(source); - } - - public void WriteBlock(string source) - { - bool isFinalLine; - ReadOnlySpan remainingText = source.AsSpan(); - - do - { - ReadOnlySpan 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 SourceText ToSourceText() - { - Debug.Assert(_indentation == 0 && _sb.Length > 0); - return SourceText.From(_sb.ToString(), Encoding.UTF8); - } - - private static ReadOnlySpan GetNextLine(ref ReadOnlySpan remainingText, out bool isFinalLine) - { - if (remainingText.IsEmpty) - { - isFinalLine = true; - return default; - } - - ReadOnlySpan next; - ReadOnlySpan 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; - } - - private unsafe void WriteLine(ReadOnlySpan source) - { - _sb.Append(' ', 4 * _indentation); - fixed (char* ptr = source) - { - _sb.Append(ptr, source.Length); - WriteBlankLine(); - } - } - } -} diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Microsoft.Extensions.Configuration.Binder.SourceGeneration.csproj b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Microsoft.Extensions.Configuration.Binder.SourceGeneration.csproj index f1c71a5575c23..63f59a53d1d91 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Microsoft.Extensions.Configuration.Binder.SourceGeneration.csproj +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Microsoft.Extensions.Configuration.Binder.SourceGeneration.csproj @@ -21,6 +21,7 @@ + @@ -36,7 +37,6 @@ - diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/Collections.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/Collections.generated.txt index f0b2ffb49b486..1e593a47c1d2f 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/Collections.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/Collections.generated.txt @@ -235,6 +235,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions?.ErrorOnUnknownConfiguration is true) { List? temp = null; + foreach (IConfigurationSection section in configuration.GetChildren()) { if (!keys.Value.Contains(section.Key)) @@ -242,6 +243,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration (temp ??= new List()).Add($"'{section.Key}'"); } } + if (temp is not null) { throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {type}: {string.Join(", ", temp)}"); @@ -273,12 +275,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { return null; } + BinderOptions binderOptions = new(); configureOptions(binderOptions); + if (binderOptions.BindNonPublicProperties) { throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } + return binderOptions; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind.generated.txt index 260a7ad627951..e5d1e51afaaa4 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind.generated.txt @@ -126,6 +126,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions?.ErrorOnUnknownConfiguration is true) { List? temp = null; + foreach (IConfigurationSection section in configuration.GetChildren()) { if (!keys.Value.Contains(section.Key)) @@ -133,6 +134,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration (temp ??= new List()).Add($"'{section.Key}'"); } } + if (temp is not null) { throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {type}: {string.Join(", ", temp)}"); @@ -155,12 +157,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { return null; } + BinderOptions binderOptions = new(); configureOptions(binderOptions); + if (binderOptions.BindNonPublicProperties) { throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } + return binderOptions; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance.generated.txt index ecc9953f810c1..31354af58276b 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance.generated.txt @@ -120,6 +120,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions?.ErrorOnUnknownConfiguration is true) { List? temp = null; + foreach (IConfigurationSection section in configuration.GetChildren()) { if (!keys.Value.Contains(section.Key)) @@ -127,6 +128,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration (temp ??= new List()).Add($"'{section.Key}'"); } } + if (temp is not null) { throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {type}: {string.Join(", ", temp)}"); diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance_BinderOptions.generated.txt index 6132d8d5e2b0b..653ac975652aa 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance_BinderOptions.generated.txt @@ -120,6 +120,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions?.ErrorOnUnknownConfiguration is true) { List? temp = null; + foreach (IConfigurationSection section in configuration.GetChildren()) { if (!keys.Value.Contains(section.Key)) @@ -127,6 +128,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration (temp ??= new List()).Add($"'{section.Key}'"); } } + if (temp is not null) { throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {type}: {string.Join(", ", temp)}"); @@ -149,12 +151,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { return null; } + BinderOptions binderOptions = new(); configureOptions(binderOptions); + if (binderOptions.BindNonPublicProperties) { throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } + return binderOptions; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Key_Instance.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Key_Instance.generated.txt index 00570279261ad..575822f5659e8 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Key_Instance.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Key_Instance.generated.txt @@ -120,6 +120,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions?.ErrorOnUnknownConfiguration is true) { List? temp = null; + foreach (IConfigurationSection section in configuration.GetChildren()) { if (!keys.Value.Contains(section.Key)) @@ -127,6 +128,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration (temp ??= new List()).Add($"'{section.Key}'"); } } + if (temp is not null) { throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {type}: {string.Join(", ", temp)}"); diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get.generated.txt index edc299c57eb44..9be69f10f5322 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get.generated.txt @@ -173,6 +173,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions?.ErrorOnUnknownConfiguration is true) { List? temp = null; + foreach (IConfigurationSection section in configuration.GetChildren()) { if (!keys.Value.Contains(section.Key)) @@ -180,6 +181,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration (temp ??= new List()).Add($"'{section.Key}'"); } } + if (temp is not null) { throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {type}: {string.Join(", ", temp)}"); @@ -211,12 +213,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { return null; } + BinderOptions binderOptions = new(); configureOptions(binderOptions); + if (binderOptions.BindNonPublicProperties) { throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } + return binderOptions; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T.generated.txt index 92768a54a644d..88400019e6b7b 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T.generated.txt @@ -141,6 +141,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions?.ErrorOnUnknownConfiguration is true) { List? temp = null; + foreach (IConfigurationSection section in configuration.GetChildren()) { if (!keys.Value.Contains(section.Key)) @@ -148,6 +149,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration (temp ??= new List()).Add($"'{section.Key}'"); } } + if (temp is not null) { throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {type}: {string.Join(", ", temp)}"); @@ -179,12 +181,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { return null; } + BinderOptions binderOptions = new(); configureOptions(binderOptions); + if (binderOptions.BindNonPublicProperties) { throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } + return binderOptions; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T_BinderOptions.generated.txt index 528049d9617e2..aea24cb7ddaed 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T_BinderOptions.generated.txt @@ -141,6 +141,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions?.ErrorOnUnknownConfiguration is true) { List? temp = null; + foreach (IConfigurationSection section in configuration.GetChildren()) { if (!keys.Value.Contains(section.Key)) @@ -148,6 +149,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration (temp ??= new List()).Add($"'{section.Key}'"); } } + if (temp is not null) { throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {type}: {string.Join(", ", temp)}"); @@ -179,12 +181,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { return null; } + BinderOptions binderOptions = new(); configureOptions(binderOptions); + if (binderOptions.BindNonPublicProperties) { throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } + return binderOptions; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_TypeOf.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_TypeOf.generated.txt index 01a865f14b23c..16a98c931a705 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_TypeOf.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_TypeOf.generated.txt @@ -69,6 +69,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions?.ErrorOnUnknownConfiguration is true) { List? temp = null; + foreach (IConfigurationSection section in configuration.GetChildren()) { if (!keys.Value.Contains(section.Key)) @@ -76,6 +77,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration (temp ??= new List()).Add($"'{section.Key}'"); } } + if (temp is not null) { throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {type}: {string.Join(", ", temp)}"); @@ -107,12 +109,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { return null; } + BinderOptions binderOptions = new(); configureOptions(binderOptions); + if (binderOptions.BindNonPublicProperties) { throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } + return binderOptions; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_TypeOf_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_TypeOf_BinderOptions.generated.txt index 620a6049fbb85..8d1ee9ed3cd9a 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_TypeOf_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_TypeOf_BinderOptions.generated.txt @@ -69,6 +69,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions?.ErrorOnUnknownConfiguration is true) { List? temp = null; + foreach (IConfigurationSection section in configuration.GetChildren()) { if (!keys.Value.Contains(section.Key)) @@ -76,6 +77,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration (temp ??= new List()).Add($"'{section.Key}'"); } } + if (temp is not null) { throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {type}: {string.Join(", ", temp)}"); @@ -107,12 +109,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { return null; } + BinderOptions binderOptions = new(); configureOptions(binderOptions); + if (binderOptions.BindNonPublicProperties) { throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } + return binderOptions; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/BindConfiguration.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/BindConfiguration.generated.txt index 2c61207f45369..6500dc091fccc 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/BindConfiguration.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/BindConfiguration.generated.txt @@ -116,6 +116,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions?.ErrorOnUnknownConfiguration is true) { List? temp = null; + foreach (IConfigurationSection section in configuration.GetChildren()) { if (!keys.Value.Contains(section.Key)) @@ -123,6 +124,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration (temp ??= new List()).Add($"'{section.Key}'"); } } + if (temp is not null) { throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {type}: {string.Join(", ", temp)}"); @@ -154,12 +156,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { return null; } + BinderOptions binderOptions = new(); configureOptions(binderOptions); + if (binderOptions.BindNonPublicProperties) { throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } + return binderOptions; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/Bind_T.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/Bind_T.generated.txt index a496d89b0000d..7d5271b0a17a7 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/Bind_T.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/Bind_T.generated.txt @@ -134,6 +134,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions?.ErrorOnUnknownConfiguration is true) { List? temp = null; + foreach (IConfigurationSection section in configuration.GetChildren()) { if (!keys.Value.Contains(section.Key)) @@ -141,6 +142,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration (temp ??= new List()).Add($"'{section.Key}'"); } } + if (temp is not null) { throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {type}: {string.Join(", ", temp)}"); @@ -172,12 +174,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { return null; } + BinderOptions binderOptions = new(); configureOptions(binderOptions); + if (binderOptions.BindNonPublicProperties) { throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } + return binderOptions; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/Bind_T_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/Bind_T_BinderOptions.generated.txt index 23a3f5028245c..315c9e3846243 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/Bind_T_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/Bind_T_BinderOptions.generated.txt @@ -128,6 +128,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions?.ErrorOnUnknownConfiguration is true) { List? temp = null; + foreach (IConfigurationSection section in configuration.GetChildren()) { if (!keys.Value.Contains(section.Key)) @@ -135,6 +136,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration (temp ??= new List()).Add($"'{section.Key}'"); } } + if (temp is not null) { throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {type}: {string.Join(", ", temp)}"); @@ -166,12 +168,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { return null; } + BinderOptions binderOptions = new(); configureOptions(binderOptions); + if (binderOptions.BindNonPublicProperties) { throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } + return binderOptions; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/Primitives.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/Primitives.generated.txt index c38f1fd39c525..2432136175701 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/Primitives.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/Primitives.generated.txt @@ -174,6 +174,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions?.ErrorOnUnknownConfiguration is true) { List? temp = null; + foreach (IConfigurationSection section in configuration.GetChildren()) { if (!keys.Value.Contains(section.Key)) @@ -181,6 +182,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration (temp ??= new List()).Add($"'{section.Key}'"); } } + if (temp is not null) { throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {type}: {string.Join(", ", temp)}"); diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T.generated.txt index c5717e3fc8333..13e96463ef1a0 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T.generated.txt @@ -179,6 +179,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions?.ErrorOnUnknownConfiguration is true) { List? temp = null; + foreach (IConfigurationSection section in configuration.GetChildren()) { if (!keys.Value.Contains(section.Key)) @@ -186,6 +187,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration (temp ??= new List()).Add($"'{section.Key}'"); } } + if (temp is not null) { throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {type}: {string.Join(", ", temp)}"); @@ -217,12 +219,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { return null; } + BinderOptions binderOptions = new(); configureOptions(binderOptions); + if (binderOptions.BindNonPublicProperties) { throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } + return binderOptions; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_BinderOptions.generated.txt index 1b1405c2fc5c5..e2c8faaa9ccf3 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_BinderOptions.generated.txt @@ -179,6 +179,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions?.ErrorOnUnknownConfiguration is true) { List? temp = null; + foreach (IConfigurationSection section in configuration.GetChildren()) { if (!keys.Value.Contains(section.Key)) @@ -186,6 +187,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration (temp ??= new List()).Add($"'{section.Key}'"); } } + if (temp is not null) { throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {type}: {string.Join(", ", temp)}"); @@ -217,12 +219,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { return null; } + BinderOptions binderOptions = new(); configureOptions(binderOptions); + if (binderOptions.BindNonPublicProperties) { throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } + return binderOptions; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_name.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_name.generated.txt index 26cb4aacd72d5..17d5050baf822 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_name.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_name.generated.txt @@ -179,6 +179,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions?.ErrorOnUnknownConfiguration is true) { List? temp = null; + foreach (IConfigurationSection section in configuration.GetChildren()) { if (!keys.Value.Contains(section.Key)) @@ -186,6 +187,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration (temp ??= new List()).Add($"'{section.Key}'"); } } + if (temp is not null) { throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {type}: {string.Join(", ", temp)}"); @@ -217,12 +219,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { return null; } + BinderOptions binderOptions = new(); configureOptions(binderOptions); + if (binderOptions.BindNonPublicProperties) { throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } + return binderOptions; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_name_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_name_BinderOptions.generated.txt index 53f8ead3ed587..a23f9b26a7e5f 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_name_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_name_BinderOptions.generated.txt @@ -173,6 +173,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration if (binderOptions?.ErrorOnUnknownConfiguration is true) { List? temp = null; + foreach (IConfigurationSection section in configuration.GetChildren()) { if (!keys.Value.Contains(section.Key)) @@ -180,6 +181,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration (temp ??= new List()).Add($"'{section.Key}'"); } } + if (temp is not null) { throw new InvalidOperationException($"'ErrorOnUnknownConfiguration' was set on the provided BinderOptions, but the following properties were not found on the instance of {type}: {string.Join(", ", temp)}"); @@ -211,12 +213,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { return null; } + BinderOptions binderOptions = new(); configureOptions(binderOptions); + if (binderOptions.BindNonPublicProperties) { throw new global::System.NotSupportedException($"The configuration binding source generator does not support 'BinderOptions.BindNonPublicProperties'."); } + return binderOptions; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/ConfigurationBindingGeneratorTests.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/ConfigurationBindingGeneratorTests.cs index ed93f666b0641..4a8296294d4db 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/ConfigurationBindingGeneratorTests.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/ConfigurationBindingGeneratorTests.cs @@ -257,8 +257,7 @@ private static async Task VerifyAgainstBaselineUsingFile( .Split(Environment.NewLine); var (d, r) = await RunGenerator(testSourceCode, languageVersion); - bool success = RoslynTestUtils.CompareLines(expectedLines, r[0].SourceText, - out string errorMessage); + bool success = RoslynTestUtils.CompareLines(expectedLines, r[0].SourceText, out string errorMessage); #if !SKIP_BASELINES Assert.Single(r); diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs index d1413919c1e2f..5109227a9b12d 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Text; +using SourceGenerators; namespace System.Text.Json.SourceGeneration { diff --git a/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.targets b/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.targets index e14905d814875..aba764b194382 100644 --- a/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.targets +++ b/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.targets @@ -28,6 +28,8 @@ + + @@ -50,13 +52,11 @@ - -