Share SourceWriter between JSON & config binding generators (#89150)
authorLayomi Akinrinade <laakinri@microsoft.com>
Wed, 19 Jul 2023 19:56:50 +0000 (12:56 -0700)
committerGitHub <noreply@github.com>
Wed, 19 Jul 2023 19:56:50 +0000 (12:56 -0700)
* Share SourceWriter between JSON & config binding generators

* Fix failing test, clean up impl, and address feedback

32 files changed:
src/libraries/Common/src/SourceGenerators/SourceWriter.cs [moved from src/libraries/System.Text.Json/gen/Helpers/SourceWriter.cs with 79% similarity]
src/libraries/Common/tests/Common.Tests.csproj
src/libraries/Common/tests/Tests/SourceGenerators/SourceWriterTests.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Emitter.cs
src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/ConfigurationBinder.cs
src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/CoreBindingHelper.cs
src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/Helpers.cs
src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsBuilderConfigurationExtensions.cs
src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/OptionsConfigurationServiceCollectionExtensions.cs
src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/SourceWriter.cs [deleted file]
src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Microsoft.Extensions.Configuration.Binder.SourceGeneration.csproj
src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/Collections.generated.txt
src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind.generated.txt
src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance.generated.txt
src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Instance_BinderOptions.generated.txt
src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Bind_Key_Instance.generated.txt
src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get.generated.txt
src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T.generated.txt
src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_T_BinderOptions.generated.txt
src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_TypeOf.generated.txt
src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ConfigurationBinder/Get_TypeOf_BinderOptions.generated.txt
src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/BindConfiguration.generated.txt
src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/Bind_T.generated.txt
src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/OptionsBuilder/Bind_T_BinderOptions.generated.txt
src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/Primitives.generated.txt
src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T.generated.txt
src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_BinderOptions.generated.txt
src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_name.generated.txt
src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/ServiceCollection/Configure_T_name_BinderOptions.generated.txt
src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/ConfigurationBindingGeneratorTests.cs
src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs
src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.targets

@@ -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 @@ namespace System.Text.Json.SourceGeneration
             return SourceText.From(_sb.ToString(), Encoding.UTF8);
         }
 
+        public void Reset()
+        {
+            _sb.Clear();
+            _indentation = 0;
+        }
+
         private void AddIndentation()
             => _sb.Append(IndentationChar, CharsPerIndentation * _indentation);
 
index b7e40f2..710ac02 100644 (file)
@@ -18,6 +18,8 @@
              Link="Common\Interop\Linux\Interop.ProcFsStat.TryReadStatusFile.cs" />
     <Compile Include="$(CommonPath)Interop\Linux\os-release\Interop.OSReleaseFile.cs"
              Link="Common\Interop\Linux\os-release\Interop.OSReleaseFile.cs" />
+    <Compile Include="$(CommonPath)SourceGenerators\SourceWriter.cs"
+             Link="Common\SourceGenerators\SourceWriter.cs" />
     <Compile Include="$(CommonPath)System\CharArrayHelpers.cs"
              Link="Common\System\CharArrayHelpers.cs" />
     <Compile Include="$(CommonPath)System\StringExtensions.cs"
@@ -81,6 +83,7 @@
     <Compile Include="Tests\Interop\cgroupsTests.cs" />
     <Compile Include="Tests\Interop\procfsTests.cs" />
     <Compile Include="Tests\Interop\OSReleaseTests.cs" />
+    <Compile Include="Tests\SourceGenerators\SourceWriterTests.cs" />
     <Compile Include="Tests\System\IO\PathInternal.Tests.cs" />
     <Compile Include="Tests\System\IO\StringParserTests.cs" />
     <Compile Include="Tests\System\Net\HttpDateParserTests.cs" />
     <Folder Include="System\Net\VirtualNetwork\" />
   </ItemGroup>
   <ItemGroup>
+    <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="$(MicrosoftCodeAnalysisVersion_LatestVS)" PrivateAssets="all" />
+  </ItemGroup>
+  <ItemGroup>
     <ProjectReference Include="$(CommonTestPath)StreamConformanceTests\StreamConformanceTests.csproj" />
   </ItemGroup>
 </Project>
diff --git a/src/libraries/Common/tests/Tests/SourceGenerators/SourceWriterTests.cs b/src/libraries/Common/tests/Tests/SourceGenerators/SourceWriterTests.cs
new file mode 100644 (file)
index 0000000..ab0e53b
--- /dev/null
@@ -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<global::Microsoft.Extensions.Options.IOptionsChangeTokenSource<TOptions>>(services, new global::Microsoft.Extensions.Options.ConfigurationChangeTokenSource<TOptions>(name, configuration));\r\n    return global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton<global::Microsoft.Extensions.Options.IConfigureOptions<TOptions>>(services, new global::Microsoft.Extensions.Options.ConfigureNamedOptions<TOptions>(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();
+            }
+        }
+    }
+}
index f24a43c..fcfeece 100644 (file)
@@ -5,6 +5,7 @@ using System;
 using System.Diagnostics;
 using System.Text.RegularExpressions;
 using Microsoft.CodeAnalysis;
+using SourceGenerators;
 
 namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
 {
@@ -36,17 +37,17 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                     return;
                 }
 
-                _writer.WriteBlock("""
+                _writer.WriteLine("""
                     // <auto-generated/>
                     #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 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
 
                 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 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                     exceptionTypeDisplayString = nameof(InvalidOperationException);
                 }
 
-                _writer.WriteBlock($$"""
+                _writer.WriteLine($$"""
                     if ({{Identifier.configuration}} is not {{sectionTypeDisplayString}} {{Identifier.section}})
                     {
                         throw new {{exceptionTypeDisplayString}}();
@@ -235,13 +238,13 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                 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();
             }
         }
     }
index d71e414..c10e607 100644 (file)
@@ -14,7 +14,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
         {
             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 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                 }
 
                 _emitBlankLineBeforeNextStatement = false;
-                EmitRootBindingClassBlockStart(Identifier.GeneratedConfigurationBinder);
+                EmitRootBindingClassStartBlock(Identifier.GeneratedConfigurationBinder);
 
                 EmitGetMethods();
                 EmitGetValueMethods();
                 EmitBindMethods_ConfigurationBinder();
 
-                _writer.WriteBlockEnd();
+                EmitEndBlock();
                 _emitBlankLineBeforeNextStatement = true;
             }
 
index dd13ee8..08015d0 100644 (file)
@@ -19,19 +19,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
             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($$"""
                     /// <summary>Provide core binding logic.</summary>
                     {{GetGeneratedCodeAttributeSrc()}}
                     file static class {{Identifier.CoreBindingHelper}}
-                    {
                     """);
 
                 EmitConfigurationKeyCaches();
@@ -42,8 +41,8 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                 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 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                 }
 
                 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 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                 {
                     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 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                         _writer.WriteLine($"return {Identifier.obj};");
                     }
 
-                    _writer.WriteBlockEnd();
-                    _writer.WriteBlankLine();
+                    EmitEndBlock();
+                    _writer.WriteLine();
                 }
 
                 Emit_NotSupportedException_TypeNotDetectedAsInput();
-                _writer.WriteBlockEnd();
+                EmitEndBlock();
                 _emitBlankLineBeforeNextStatement = true;
             }
 
@@ -137,24 +136,24 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                 }
 
                 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 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                         checkForNullSectionValue: false,
                         useIncrementalStringValueIdentifier: false);
 
-                    _writer.WriteBlockEnd();
-                    _writer.WriteBlankLine();
+                    EmitEndBlock();
+                    _writer.WriteLine();
                 }
 
                 _writer.WriteLine("return null;");
-                _writer.WriteBlockEnd();
+                EmitEndBlock();
                 _emitBlankLineBeforeNextStatement = true;
             }
 
@@ -182,18 +181,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
 
                 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 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                         _writer.WriteLine($"return;");
                     }
 
-                    _writer.WriteBlockEnd();
-                    _writer.WriteBlankLine();
+                    EmitEndBlock();
+                    _writer.WriteLine();
                 }
 
                 Emit_NotSupportedException_TypeNotDetectedAsInput();
-                _writer.WriteBlockEnd();
+                EmitEndBlock();
                 _emitBlankLineBeforeNextStatement = true;
             }
 
@@ -232,7 +231,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                 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 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                     EmitBindCoreImplForObject((ObjectSpec)effectiveType);
                 }
 
-                _writer.WriteBlockEnd();
+                EmitEndBlock();
             }
 
             private void EmitInitializeMethods()
@@ -283,7 +282,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                 List<string> 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 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                 }
                 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 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                         {
                             string condition = $@" if ({Identifier.configuration}[""{member.ConfigurationKeyName}""] is not {memberType.MinimalDisplayString} {member.Name})";
                             EmitThrowBlock(condition);
-                            _writer.WriteBlankLine();
+                            _writer.WriteLine();
                             return;
                         }
                     }
@@ -386,11 +385,11 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                             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 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                             """);
                 }
             }
-
             private void EmitHelperMethods()
             {
                 if (ShouldEmitMethods(MethodsToGen_CoreBindingHelper.BindCore))
@@ -408,15 +406,15 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
 
                 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 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                     MethodsToGen_CoreBindingHelper.BindCoreUntyped | MethodsToGen_CoreBindingHelper.GetCore) ||
                     ShouldEmitMethods(MethodsToGen_ConfigurationBinder.Bind_instance_BinderOptions))
                 {
-                    _writer.WriteBlankLine();
+                    _writer.WriteLine();
                     EmitGetBinderOptionsHelper();
                     _emitBlankLineBeforeNextStatement = true;
                 }
@@ -443,7 +441,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                 string exceptionMessage = string.Format(ExceptionMessages.MissingConfig, Identifier.ErrorOnUnknownConfiguration, Identifier.BinderOptions, $"{{{Identifier.type}}}", $@"{{string.Join("", "", {Identifier.temp})}}");
 
                 EmitBlankLineIfRequired();
-                _writer.WriteBlock($$"""
+                _writer.WriteLine($$"""
                     /// <summary>If required by the binder options, validates that there are no unknown keys in the input configuration object.</summary>
                     public static void {{Identifier.ValidateConfigurationKeys}}(Type {{Identifier.type}}, {{MinimalDisplayString.LazyHashSetOfString}} {{keysIdentifier}}, {{Identifier.IConfiguration}} {{Identifier.configuration}}, {{Identifier.BinderOptions}}? {{Identifier.binderOptions}})
                     {
@@ -470,7 +468,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
 
             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 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
 
             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 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
 
             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 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                         }
                 }
 
-                _writer.WriteBlock($$"""
-                    public static {{typeDisplayString}} {{type.ParseMethodName}}(string {{Identifier.value}}, Func<string?> {{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<string?> {Identifier.getPath})");
+                EmitEndBlock($$"""
+                    try
+                    {
+                        return {{parsedValueExpr}};
+                    }
+                    catch ({{innerExceptionTypeDisplayString}} {{Identifier.exception}})
+                    {
+                        throw new {{GetInvalidOperationDisplayName()}}($"{{exceptionArg1}}", {{Identifier.exception}});
                     }
                     """);
             }
@@ -622,7 +615,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                 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 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
             {
                 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 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                     _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 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                             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 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                                 ? $"new {collectionSpec.ConcreteType.MinimalDisplayString}({Identifier.element})"
                                 : $"{Identifier.element}.{collectionSpec.ToEnumerableMethodCall!}";
 
-                            _writer.WriteBlock($$"""
+                            _writer.WriteLine($$"""
                                 else
                                 {
                                     {{Identifier.element}} = {{initExpression}};
@@ -734,10 +727,9 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                         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 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                     return true;
                 }
 
-                string sectionParseExpr = $"{GetSectionFromConfigurationExpression(member.ConfigurationKeyName)}";
+                string sectionParseExpr = GetSectionFromConfigurationExpression(member.ConfigurationKeyName);
 
                 EmitBlankLineIfRequired();
 
@@ -807,7 +799,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                 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 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                     EmitBindCoreCallForMember(member, memberAccessExpr, sectionIdentifier, canSet);
                 }
 
-                _writer.WriteBlockEnd();
+                EmitEndBlock();
                 return success;
             }
 
@@ -896,18 +888,18 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                 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}";
index 7325c29..e0e6a36 100644 (file)
@@ -129,11 +129,43 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                 ShouldEmitMethods(MethodsToGen_Extensions_OptionsBuilder.Any) ||
                 ShouldEmitMethods(MethodsToGen_Extensions_ServiceCollection.Any);
 
+            /// <summary>
+            /// Starts a block of source code.
+            /// </summary>
+            /// <param name="source">Source to write after the open brace.</param>
+            public void EmitStartBlock(string? source = null)
+            {
+                if (source is not null)
+                {
+                    _writer.WriteLine(source);
+                }
+
+                _writer.WriteLine("{");
+                _writer.Indentation++;
+            }
+
+            /// <summary>
+            /// Ends a block of source code.
+            /// </summary>
+            /// <param name="source">Source to write before the close brace.</param>
+            /// <param name="endBraceTrailingSource">Trailing source after the end brace, e.g. ";" to end an init statement.</param>
+            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 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                     ? "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 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                 return false;
             }
 
-            private void EmitRootBindingClassBlockStart(string className)
+            private void EmitRootBindingClassStartBlock(string className)
             {
                 EmitBlankLineIfRequired();
-                _writer.WriteBlock($$"""
+                EmitStartBlock($$"""
                     /// <summary>Generated helper providing an AOT and linking compatible implementation for configuration binding.</summary>
                     {{GetGeneratedCodeAttributeSrc()}}
                     internal static class {{className}}
-                    {
                     """);
 
                 _emitBlankLineBeforeNextStatement = false;
index 0055b2b..71d0b69 100644 (file)
@@ -16,12 +16,12 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                     return;
                 }
 
-                EmitRootBindingClassBlockStart(Identifier.GeneratedOptionsBuilderBinder);
+                EmitRootBindingClassStartBlock(Identifier.GeneratedOptionsBuilderBinder);
 
                 EmitBindMethods_Extensions_OptionsBuilder();
                 EmitBindConfigurationMethod();
 
-                _writer.WriteBlockEnd();
+                EmitEndBlock();
             }
 
             private void EmitBindMethods_Extensions_OptionsBuilder()
@@ -36,24 +36,24 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
 
                 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 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
                 const string documentation = $@"/// <summary>Registers the dependency injection container to bind <typeparamref name=""TOptions""/> against the <see cref=""{FullyQualifiedDisplayString.IConfiguration}""/> obtained from the DI service provider.</summary>";
                 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");
             }
         }
     }
index 5b4edc9..f4cd480 100644 (file)
@@ -9,14 +9,14 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
         {
             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 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
 
                 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($$"""
-                /// <summary>Registers a configuration instance which TOptions will bind against.</summary>
-                public static {{FullyQualifiedDisplayString.IServiceCollection}} {{Identifier.Configure}}<{{Identifier.TOptions}}>({{paramList}}) where {{Identifier.TOptions}} : class
-                {
-                """);
+                EmitStartBlock($$"""
+                    /// <summary>Registers a configuration instance which TOptions will bind against.</summary>
+                    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 (file)
index f610209..0000000
+++ /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<char> remainingText = source.AsSpan();
-
-            do
-            {
-                ReadOnlySpan<char> line = GetNextLine(ref remainingText, out isFinalLine);
-                switch (line)
-                {
-                    case "{":
-                        {
-                            WriteBlockStart();
-                        }
-                        break;
-                    case "}":
-                        {
-                            WriteBlockEnd();
-                        }
-                        break;
-                    default:
-                        {
-                            WriteLine(line);
-                        }
-                        break;
-                }
-            } while (!isFinalLine);
-        }
-
-        public void WriteBlankLine() => _sb.AppendLine();
-
-        public SourceText ToSourceText()
-        {
-            Debug.Assert(_indentation == 0 && _sb.Length > 0);
-            return SourceText.From(_sb.ToString(), Encoding.UTF8);
-        }
-
-        private static ReadOnlySpan<char> GetNextLine(ref ReadOnlySpan<char> remainingText, out bool isFinalLine)
-        {
-            if (remainingText.IsEmpty)
-            {
-                isFinalLine = true;
-                return default;
-            }
-
-            ReadOnlySpan<char> next;
-            ReadOnlySpan<char> rest;
-
-            remainingText = remainingText.Trim();
-
-            int lineLength = remainingText.IndexOf(s_newLine);
-            if (lineLength == -1)
-            {
-                lineLength = remainingText.Length;
-                isFinalLine = true;
-                rest = default;
-            }
-            else
-            {
-                rest = remainingText.Slice(lineLength + 1);
-                isFinalLine = false;
-            }
-
-            next = remainingText.Slice(0, lineLength);
-            remainingText = rest;
-            return next;
-        }
-
-        private unsafe void WriteLine(ReadOnlySpan<char> source)
-        {
-            _sb.Append(' ', 4 * _indentation);
-            fixed (char* ptr = source)
-            {
-                _sb.Append(ptr, source.Length);
-                WriteBlankLine();
-            }
-        }
-    }
-}
index f1c71a5..63f59a5 100644 (file)
@@ -21,6 +21,7 @@
     <Compile Include="$(CoreLibSharedDir)System\Collections\Generic\ValueListBuilder.cs" Link="Production\ValueListBuilder.cs" />
     <Compile Include="$(CoreLibSharedDir)System\Collections\Generic\ValueListBuilder.Pop.cs" Link="Production\ValueListBuilder.Pop.cs" />
     <Compile Include="$(CommonPath)\Roslyn\GetBestTypeByMetadataName.cs" Link="Common\Roslyn\GetBestTypeByMetadataName.cs" />
+    <Compile Include="$(CommonPath)\SourceGenerators\SourceWriter.cs" Link="Common\SourceGenerators\SourceWriter.cs" />
     <Compile Include="ConfigurationBindingGenerator.cs" />
     <Compile Include="ConfigurationBindingGenerator.Emitter.cs" />
     <Compile Include="ConfigurationBindingGenerator.Parser.cs" />
@@ -36,7 +37,6 @@
     <Compile Include="Helpers\Parser\Diagnostics.cs" />
     <Compile Include="Helpers\Parser\OptionsBuilderConfigurationExtensions.cs" />
     <Compile Include="Helpers\Parser\OptionsConfigurationServiceCollectionExtensions.cs" />
-    <Compile Include="Helpers\SourceWriter.cs" />
     <Compile Include="Model\CollectionSpec.cs" />
     <Compile Include="Model\ConfigurationSectionSpec.cs" />
     <Compile Include="Model\InitializationStrategy.cs" />
index f0b2ffb..1e593a4 100644 (file)
@@ -235,6 +235,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
             if (binderOptions?.ErrorOnUnknownConfiguration is true)
             {
                 List<string>? 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<string>()).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;
         }
 
index 260a7ad..e5d1e51 100644 (file)
@@ -126,6 +126,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
             if (binderOptions?.ErrorOnUnknownConfiguration is true)
             {
                 List<string>? 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<string>()).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;
         }
 
index ecc9953..31354af 100644 (file)
@@ -120,6 +120,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
             if (binderOptions?.ErrorOnUnknownConfiguration is true)
             {
                 List<string>? 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<string>()).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)}");
index 6132d8d..653ac97 100644 (file)
@@ -120,6 +120,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
             if (binderOptions?.ErrorOnUnknownConfiguration is true)
             {
                 List<string>? 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<string>()).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;
         }
 
index 0057027..575822f 100644 (file)
@@ -120,6 +120,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
             if (binderOptions?.ErrorOnUnknownConfiguration is true)
             {
                 List<string>? 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<string>()).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)}");
index edc299c..9be69f1 100644 (file)
@@ -173,6 +173,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
             if (binderOptions?.ErrorOnUnknownConfiguration is true)
             {
                 List<string>? 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<string>()).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;
         }
 
index 92768a5..8840001 100644 (file)
@@ -141,6 +141,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
             if (binderOptions?.ErrorOnUnknownConfiguration is true)
             {
                 List<string>? 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<string>()).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;
         }
 
index 528049d..aea24cb 100644 (file)
@@ -141,6 +141,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
             if (binderOptions?.ErrorOnUnknownConfiguration is true)
             {
                 List<string>? 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<string>()).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;
         }
 
index 01a865f..16a98c9 100644 (file)
@@ -69,6 +69,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
             if (binderOptions?.ErrorOnUnknownConfiguration is true)
             {
                 List<string>? 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<string>()).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;
         }
 
index 620a604..8d1ee9e 100644 (file)
@@ -69,6 +69,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
             if (binderOptions?.ErrorOnUnknownConfiguration is true)
             {
                 List<string>? 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<string>()).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;
         }
 
index 2c61207..6500dc0 100644 (file)
@@ -116,6 +116,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
             if (binderOptions?.ErrorOnUnknownConfiguration is true)
             {
                 List<string>? 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<string>()).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;
         }
 
index a496d89..7d5271b 100644 (file)
@@ -134,6 +134,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
             if (binderOptions?.ErrorOnUnknownConfiguration is true)
             {
                 List<string>? 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<string>()).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;
         }
 
index 23a3f50..315c9e3 100644 (file)
@@ -128,6 +128,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
             if (binderOptions?.ErrorOnUnknownConfiguration is true)
             {
                 List<string>? 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<string>()).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;
         }
 
index c38f1fd..2432136 100644 (file)
@@ -174,6 +174,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
             if (binderOptions?.ErrorOnUnknownConfiguration is true)
             {
                 List<string>? 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<string>()).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)}");
index c5717e3..13e9646 100644 (file)
@@ -179,6 +179,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
             if (binderOptions?.ErrorOnUnknownConfiguration is true)
             {
                 List<string>? 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<string>()).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;
         }
 
index 1b1405c..e2c8faa 100644 (file)
@@ -179,6 +179,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
             if (binderOptions?.ErrorOnUnknownConfiguration is true)
             {
                 List<string>? 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<string>()).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;
         }
 
index 26cb4aa..17d5050 100644 (file)
@@ -179,6 +179,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
             if (binderOptions?.ErrorOnUnknownConfiguration is true)
             {
                 List<string>? 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<string>()).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;
         }
 
index 53f8ead..a23f9b2 100644 (file)
@@ -173,6 +173,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
             if (binderOptions?.ErrorOnUnknownConfiguration is true)
             {
                 List<string>? 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<string>()).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;
         }
 
index ed93f66..4a82962 100644 (file)
@@ -257,8 +257,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration.Tests
                                              .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);
index d141391..5109227 100644 (file)
@@ -10,6 +10,7 @@ using System.Text.Json.Serialization;
 using Microsoft.CodeAnalysis;
 using Microsoft.CodeAnalysis.CSharp;
 using Microsoft.CodeAnalysis.Text;
+using SourceGenerators;
 
 namespace System.Text.Json.SourceGeneration
 {
index e14905d..aba764b 100644 (file)
@@ -28,6 +28,8 @@
     <Compile Include="$(CoreLibSharedDir)System\Runtime\CompilerServices\IsExternalInit.cs" Link="Common\System\Runtime\CompilerServices\IsExternalInit.cs" />
     <Compile Include="$(CoreLibSharedDir)System\Runtime\CompilerServices\CompilerFeatureRequiredAttribute.cs" Link="Common\System\Runtime\CompilerServices\CompilerFeatureRequiredAttribute.cs" />
     <Compile Include="$(CoreLibSharedDir)System\Runtime\CompilerServices\RequiredMemberAttribute.cs" Link="Common\System\Runtime\CompilerServices\RequiredMemberAttribute.cs" />
+    <Compile Include="$(CommonPath)\Roslyn\GetBestTypeByMetadataName.cs" Link="Common\Roslyn\GetBestTypeByMetadataName.cs" />
+    <Compile Include="$(CommonPath)\SourceGenerators\SourceWriter.cs" Link="Common\SourceGenerators\SourceWriter.cs" />
     <Compile Include="..\Common\JsonCamelCaseNamingPolicy.cs" Link="Common\System\Text\Json\JsonCamelCaseNamingPolicy.cs" />
     <Compile Include="..\Common\JsonNamingPolicy.cs" Link="Common\System\Text\Json\JsonNamingPolicy.cs" />
     <Compile Include="..\Common\JsonAttribute.cs" Link="Common\System\Text\Json\Serialization\JsonAttribute.cs" />
     <Compile Include="..\Common\JsonUnknownTypeHandling.cs" Link="Common\System\Text\Json\Serialization\JsonUnknownTypeHandling.cs" />
     <Compile Include="..\Common\JsonUnmappedMemberHandling.cs" Link="Common\System\Text\Json\Serialization\JsonUnmappedMemberHandling.cs" />
     <Compile Include="..\Common\ThrowHelper.cs" Link="Common\System\Text\Json\ThrowHelper.cs" />
-    <Compile Include="$(CommonPath)\Roslyn\GetBestTypeByMetadataName.cs" Link="Common\Roslyn\GetBestTypeByMetadataName.cs" />
     <Compile Include="Helpers\DiagnosticInfo.cs" />
     <Compile Include="Helpers\SourceGeneratorHelpers.cs" />
     <Compile Include="Helpers\ImmutableEquatableArray.cs" />
     <Compile Include="Helpers\KnownTypeSymbols.cs" />
     <Compile Include="Helpers\RoslynExtensions.cs" />
-    <Compile Include="Helpers\SourceWriter.cs" />
     <Compile Include="JsonConstants.cs" />
     <Compile Include="JsonSourceGenerator.DiagnosticDescriptors.cs" />
     <Compile Include="JsonSourceGenerator.Emitter.cs" />