Skip to content

Commit 9fd0a7a

Browse files
committed
Share SourceWriter between JSON & config binding generators
1 parent df8e7e8 commit 9fd0a7a

33 files changed

+291
-289
lines changed

src/libraries/System.Text.Json/gen/Helpers/SourceWriter.cs renamed to src/libraries/Common/src/SourceGenerators/SourceWriter.cs

Lines changed: 18 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,21 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using Microsoft.CodeAnalysis.Text;
4+
using System;
5+
using System.Text;
56
using System.Diagnostics;
7+
using Microsoft.CodeAnalysis.Text;
68

7-
namespace System.Text.Json.SourceGeneration
9+
namespace SourceGenerators
810
{
911
internal sealed class SourceWriter
1012
{
13+
private const char IndentationChar = ' ';
14+
private const int CharsPerIndentation = 4;
15+
1116
private readonly StringBuilder _sb = new();
1217
private int _indentation;
1318

14-
public SourceWriter()
15-
{
16-
IndentationChar = ' ';
17-
CharsPerIndentation = 4;
18-
}
19-
20-
public SourceWriter(char indentationChar, int charsPerIndentation)
21-
{
22-
if (!char.IsWhiteSpace(indentationChar))
23-
{
24-
throw new ArgumentOutOfRangeException(nameof(indentationChar));
25-
}
26-
27-
if (charsPerIndentation < 1)
28-
{
29-
throw new ArgumentOutOfRangeException(nameof(charsPerIndentation));
30-
}
31-
32-
IndentationChar = indentationChar;
33-
CharsPerIndentation = charsPerIndentation;
34-
}
35-
36-
public char IndentationChar { get; }
37-
public int CharsPerIndentation { get; }
38-
39-
public int Length => _sb.Length;
4019
public int Indentation
4120
{
4221
get => _indentation;
@@ -85,8 +64,18 @@ public void WriteLine(string text)
8564
public SourceText ToSourceText()
8665
{
8766
Debug.Assert(_indentation == 0 && _sb.Length > 0);
88-
return SourceText.From(_sb.ToString(), Encoding.UTF8);
67+
return SourceText.From(ToString(), Encoding.UTF8);
68+
}
69+
70+
public override string ToString() => _sb.ToString();
71+
72+
#region Test utilities.
73+
public void Reset()
74+
{
75+
_indentation = 0;
76+
_sb.Clear();
8977
}
78+
#endregion
9079

9180
private void AddIndentation()
9281
=> _sb.Append(IndentationChar, CharsPerIndentation * _indentation);

src/libraries/Common/tests/Common.Tests.csproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
Link="Common\System\Collections\DictionaryExtensions.cs" />
1111
<Compile Include="$(CommonTestPath)System\Security\Cryptography\ByteUtils.cs"
1212
Link="Common\System\Security\Cryptography\ByteUtils.cs" />
13+
<Compile Include="$(CommonTestPath)SourceGenerators\RoslynTestUtils.cs"
14+
Link="Common\System\SourceGenerators\RoslynTestUtils.cs" />
1315
<Compile Include="$(CommonPath)Interop\Linux\cgroups\Interop.cgroups.cs"
1416
Link="Common\Interop\Linux\cgroups\Interop.cgroups.cs" />
1517
<Compile Include="$(CommonPath)Interop\Linux\procfs\Interop.ProcFsStat.cs"
@@ -18,6 +20,8 @@
1820
Link="Common\Interop\Linux\Interop.ProcFsStat.TryReadStatusFile.cs" />
1921
<Compile Include="$(CommonPath)Interop\Linux\os-release\Interop.OSReleaseFile.cs"
2022
Link="Common\Interop\Linux\os-release\Interop.OSReleaseFile.cs" />
23+
<Compile Include="$(CommonPath)SourceGenerators\SourceWriter.cs"
24+
Link="Common\SourceGenerators\SourceWriter.cs" />
2125
<Compile Include="$(CommonPath)System\CharArrayHelpers.cs"
2226
Link="Common\System\CharArrayHelpers.cs" />
2327
<Compile Include="$(CommonPath)System\StringExtensions.cs"
@@ -81,6 +85,7 @@
8185
<Compile Include="Tests\Interop\cgroupsTests.cs" />
8286
<Compile Include="Tests\Interop\procfsTests.cs" />
8387
<Compile Include="Tests\Interop\OSReleaseTests.cs" />
88+
<Compile Include="Tests\SourceGenerators\SourceWriterTests.cs" />
8489
<Compile Include="Tests\System\IO\PathInternal.Tests.cs" />
8590
<Compile Include="Tests\System\IO\StringParserTests.cs" />
8691
<Compile Include="Tests\System\Net\HttpDateParserTests.cs" />
@@ -155,6 +160,9 @@
155160
<Folder Include="System\Net\Sockets\" />
156161
<Folder Include="System\Net\VirtualNetwork\" />
157162
</ItemGroup>
163+
<ItemGroup>
164+
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="$(MicrosoftCodeAnalysisVersion_LatestVS)" PrivateAssets="all" />
165+
</ItemGroup>
158166
<ItemGroup>
159167
<ProjectReference Include="$(CommonTestPath)StreamConformanceTests\StreamConformanceTests.csproj" />
160168
</ItemGroup>

src/libraries/Common/tests/TestUtilities/System/LineEndingsHelper.cs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,9 @@ namespace System
55
{
66
public static class LineEndingsHelper
77
{
8-
public const string CompiledNewline = @"
8+
private const string CompiledNewline = @"
99
";
10-
public static readonly bool s_consistentNewlines = StringComparer.Ordinal.Equals(CompiledNewline, Environment.NewLine);
11-
12-
static public bool IsNewLineConsistent
13-
{
14-
get { return s_consistentNewlines; }
15-
}
10+
private static readonly bool s_consistentNewlines = StringComparer.Ordinal.Equals(CompiledNewline, Environment.NewLine);
1611

1712
public static string Normalize(string expected)
1813
{
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using SourceGenerators;
5+
using Xunit;
6+
7+
namespace Common.Tests
8+
{
9+
public sealed class SourceWriterTests
10+
{
11+
[Fact]
12+
public void CanHandleVariousLineEndings()
13+
{
14+
string testTemplate = "public static void Main(){0}{{{1}\tConsole.WriteLine(\"Hello, world\");{2}}}";
15+
SourceWriter writer = new();
16+
17+
// Regression test for https://github.com/dotnet/runtime/issues/88918.
18+
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}");
19+
20+
CheckCanWrite(string.Format(testTemplate, "\n", "\n", "\n"));
21+
CheckCanWrite(string.Format(testTemplate, "\n\r", "\n\r", "\n\r"));
22+
CheckCanWrite(string.Format(testTemplate, "\n", "\n\r", "\n"));
23+
24+
void CheckCanWrite(string source)
25+
{
26+
// No exception expected.
27+
writer.WriteLine(source);
28+
writer.Reset();
29+
}
30+
}
31+
}
32+
}

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

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Diagnostics;
66
using System.Text.RegularExpressions;
77
using Microsoft.CodeAnalysis;
8+
using SourceGenerators;
89

910
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
1011
{
@@ -36,17 +37,17 @@ public void Emit()
3637
return;
3738
}
3839

39-
_writer.WriteBlock("""
40+
_writer.WriteLine("""
4041
// <auto-generated/>
4142
#nullable enable
4243
#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code.
4344
""");
44-
_writer.WriteBlankLine();
45+
_writer.WriteLine();
4546

4647
_useFullyQualifiedNames = true;
47-
EmitBinder_ConfigurationBinder();
48+
EmitBinder_Extensions_IConfiguration();
4849
EmitBinder_Extensions_OptionsBuilder();
49-
EmitBinder_Extensions_ServiceCollection();
50+
EmitBinder_Extensions_IServiceCollection();
5051

5152
_useFullyQualifiedNames = false;
5253
Emit_CoreBindingHelper();
@@ -135,9 +136,9 @@ private void EmitBindLogicFromString(
135136
}
136137
else
137138
{
138-
_writer.WriteBlockStart($"if ({sectionValueExpr} is string {nonNull_StringValue_Identifier})");
139+
EmitStartScope($"if ({sectionValueExpr} is string {nonNull_StringValue_Identifier})");
139140
writeOnSuccess?.Invoke(parsedValueExpr);
140-
_writer.WriteBlockEnd();
141+
EmitEndScope();
141142
}
142143
}
143144

@@ -222,7 +223,7 @@ private void EmitCastToIConfigurationSection()
222223
exceptionTypeDisplayString = nameof(InvalidOperationException);
223224
}
224225

225-
_writer.WriteBlock($$"""
226+
_writer.WriteLine($$"""
226227
if ({{Identifier.configuration}} is not {{sectionTypeDisplayString}} {{Identifier.section}})
227228
{
228229
throw new {{exceptionTypeDisplayString}}();
@@ -235,13 +236,13 @@ private void EmitIConfigurationHasValueOrChildrenCheck(bool voidReturn)
235236
string returnPostfix = voidReturn ? string.Empty : " null";
236237
string methodDisplayString = GetHelperMethodDisplayString(Identifier.HasValueOrChildren);
237238

238-
_writer.WriteBlock($$"""
239+
_writer.WriteLine($$"""
239240
if (!{{methodDisplayString}}({{Identifier.configuration}}))
240241
{
241242
return{{returnPostfix}};
242243
}
243244
""");
244-
_writer.WriteBlankLine();
245+
_writer.WriteLine();
245246
}
246247
}
247248
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ private sealed partial class Emitter
1414
{
1515
private bool ShouldEmitMethods(MethodsToGen_ConfigurationBinder methods) => (_sourceGenSpec.MethodsToGen_ConfigurationBinder & methods) != 0;
1616

17-
private void EmitBinder_ConfigurationBinder()
17+
private void EmitBinder_Extensions_IConfiguration()
1818
{
1919
Debug.Assert(_sourceGenSpec.TypesForGen_ConfigurationBinder_BindMethods.Count <= 3 &&
2020
!_sourceGenSpec.TypesForGen_ConfigurationBinder_BindMethods.Keys.Any(overload => (overload & MethodsToGen_ConfigurationBinder.Bind) is 0));
@@ -25,13 +25,13 @@ private void EmitBinder_ConfigurationBinder()
2525
}
2626

2727
_emitBlankLineBeforeNextStatement = false;
28-
EmitRootBindingClassBlockStart(Identifier.GeneratedConfigurationBinder);
28+
EmitRootBindingClassStartScope(Identifier.GeneratedConfigurationBinder);
2929

3030
EmitGetMethods();
3131
EmitGetValueMethods();
3232
EmitBindMethods_ConfigurationBinder();
3333

34-
_writer.WriteBlockEnd();
34+
EmitEndScope();
3535
_emitBlankLineBeforeNextStatement = true;
3636
}
3737

0 commit comments

Comments
 (0)