Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Share SourceWriter between JSON & config binding generators #89150

Merged
merged 2 commits into from
Jul 19, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -85,8 +64,18 @@ public void WriteLine(string text)
public SourceText ToSourceText()
{
Debug.Assert(_indentation == 0 && _sb.Length > 0);
return SourceText.From(_sb.ToString(), Encoding.UTF8);
return SourceText.From(ToString(), Encoding.UTF8);
}

public override string ToString() => _sb.ToString();

#region Test utilities.
layomia marked this conversation as resolved.
Show resolved Hide resolved
public void Reset()
{
_indentation = 0;
_sb.Clear();
}
#endregion

private void AddIndentation()
=> _sb.Append(IndentationChar, CharsPerIndentation * _indentation);
Expand Down
8 changes: 8 additions & 0 deletions src/libraries/Common/tests/Common.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
Link="Common\System\Collections\DictionaryExtensions.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\ByteUtils.cs"
Link="Common\System\Security\Cryptography\ByteUtils.cs" />
<Compile Include="$(CommonTestPath)SourceGenerators\RoslynTestUtils.cs"
Link="Common\System\SourceGenerators\RoslynTestUtils.cs" />
<Compile Include="$(CommonPath)Interop\Linux\cgroups\Interop.cgroups.cs"
Link="Common\Interop\Linux\cgroups\Interop.cgroups.cs" />
<Compile Include="$(CommonPath)Interop\Linux\procfs\Interop.ProcFsStat.cs"
Expand All @@ -18,6 +20,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"
Expand Down Expand Up @@ -81,6 +85,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" />
Expand Down Expand Up @@ -155,6 +160,9 @@
<Folder Include="System\Net\Sockets\" />
<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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,9 @@ namespace System
{
public static class LineEndingsHelper
{
public const string CompiledNewline = @"
private const string CompiledNewline = @"
layomia marked this conversation as resolved.
Show resolved Hide resolved
";
public static readonly bool s_consistentNewlines = StringComparer.Ordinal.Equals(CompiledNewline, Environment.NewLine);

static public bool IsNewLineConsistent
{
get { return s_consistentNewlines; }
}
private static readonly bool s_consistentNewlines = StringComparer.Ordinal.Equals(CompiledNewline, Environment.NewLine);
layomia marked this conversation as resolved.
Show resolved Hide resolved

public static string Normalize(string expected)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -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();

// 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}");

CheckCanWrite(string.Format(testTemplate, "\n", "\n", "\n"));
CheckCanWrite(string.Format(testTemplate, "\n\r", "\n\r", "\n\r"));
CheckCanWrite(string.Format(testTemplate, "\n", "\n\r", "\n"));

void CheckCanWrite(string source)
{
// No exception expected.
writer.WriteLine(source);
writer.Reset();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Diagnostics;
using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis;
using SourceGenerators;

namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
Expand Down Expand Up @@ -36,17 +37,17 @@ public void Emit()
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();
Expand Down Expand Up @@ -135,9 +136,9 @@ private void EmitBindLogicFromString(
}
else
{
_writer.WriteBlockStart($"if ({sectionValueExpr} is string {nonNull_StringValue_Identifier})");
EmitStartScope($"if ({sectionValueExpr} is string {nonNull_StringValue_Identifier})");
writeOnSuccess?.Invoke(parsedValueExpr);
_writer.WriteBlockEnd();
EmitEndScope();
}
}

Expand Down Expand Up @@ -222,7 +223,7 @@ private void EmitCastToIConfigurationSection()
exceptionTypeDisplayString = nameof(InvalidOperationException);
}

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

_writer.WriteBlock($$"""
_writer.WriteLine($$"""
if (!{{methodDisplayString}}({{Identifier.configuration}}))
{
return{{returnPostfix}};
}
""");
_writer.WriteBlankLine();
_writer.WriteLine();
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ private sealed partial class Emitter
{
private bool ShouldEmitMethods(MethodsToGen_ConfigurationBinder methods) => (_sourceGenSpec.MethodsToGen_ConfigurationBinder & methods) != 0;

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

_emitBlankLineBeforeNextStatement = false;
EmitRootBindingClassBlockStart(Identifier.GeneratedConfigurationBinder);
EmitRootBindingClassStartScope(Identifier.GeneratedConfigurationBinder);

EmitGetMethods();
EmitGetValueMethods();
EmitBindMethods_ConfigurationBinder();

_writer.WriteBlockEnd();
EmitEndScope();
layomia marked this conversation as resolved.
Show resolved Hide resolved
_emitBlankLineBeforeNextStatement = true;
}

Expand Down
Loading