未验证 提交 7d9f506c 编写于 作者: G github-actions[bot] 提交者: GitHub

[release/8.0-preview7] Share SourceWriter between JSON & config binding generators (#89196)

* Share SourceWriter between JSON & config binding generators

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

---------
Co-authored-by: NLayomi Akinrinade <laakinri@microsoft.com>
Co-authored-by: NEric StJohn <ericstj@microsoft.com>
上级 854cc2ce
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.CodeAnalysis.Text;
using System;
using System.Text;
using System.Diagnostics;
using Microsoft.CodeAnalysis.Text;
namespace System.Text.Json.SourceGeneration
namespace SourceGenerators
{
internal sealed class SourceWriter
{
private const char IndentationChar = ' ';
private const int CharsPerIndentation = 4;
private readonly StringBuilder _sb = new();
private int _indentation;
public SourceWriter()
{
IndentationChar = ' ';
CharsPerIndentation = 4;
}
public SourceWriter(char indentationChar, int charsPerIndentation)
{
if (!char.IsWhiteSpace(indentationChar))
{
throw new ArgumentOutOfRangeException(nameof(indentationChar));
}
if (charsPerIndentation < 1)
{
throw new ArgumentOutOfRangeException(nameof(charsPerIndentation));
}
IndentationChar = indentationChar;
CharsPerIndentation = charsPerIndentation;
}
public char IndentationChar { get; }
public int CharsPerIndentation { get; }
public int Length => _sb.Length;
public int Indentation
{
get => _indentation;
......@@ -88,6 +67,12 @@ public SourceText ToSourceText()
return SourceText.From(_sb.ToString(), Encoding.UTF8);
}
public void Reset()
{
_sb.Clear();
_indentation = 0;
}
private void AddIndentation()
=> _sb.Append(IndentationChar, CharsPerIndentation * _indentation);
......
......@@ -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" />
......@@ -155,6 +158,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>
......
// 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();
}
}
}
}
......@@ -5,6 +5,7 @@
using System.Diagnostics;
using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis;
using SourceGenerators;
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
......@@ -36,17 +37,17 @@ public void Emit()
return;
}
_writer.WriteBlock("""
_writer.WriteLine("""
// <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 @@ void EmitObjectInit(string objExpression, InitializationKind initKind)
if (!checkForNullSectionValue)
{
writeOnSuccess?.Invoke(parsedValueExpr);
InvokeWriteOnSuccess();
}
else
{
_writer.WriteBlockStart($"if ({sectionValueExpr} is string {nonNull_StringValue_Identifier})");
writeOnSuccess?.Invoke(parsedValueExpr);
_writer.WriteBlockEnd();
EmitStartBlock($"if ({sectionValueExpr} is string {nonNull_StringValue_Identifier})");
InvokeWriteOnSuccess();
EmitEndBlock();
}
void InvokeWriteOnSuccess() => writeOnSuccess?.Invoke(parsedValueExpr);
}
private bool EmitObjectInit(TypeSpec type, string memberAccessExpr, InitializationKind initKind, string configArgExpr)
......@@ -222,7 +225,7 @@ private void EmitCastToIConfigurationSection()
exceptionTypeDisplayString = nameof(InvalidOperationException);
}
_writer.WriteBlock($$"""
_writer.WriteLine($$"""
if ({{Identifier.configuration}} is not {{sectionTypeDisplayString}} {{Identifier.section}})
{
throw new {{exceptionTypeDisplayString}}();
......@@ -235,13 +238,13 @@ private void EmitIConfigurationHasValueOrChildrenCheck(bool voidReturn)
string returnPostfix = voidReturn ? string.Empty : " null";
string methodDisplayString = GetHelperMethodDisplayString(Identifier.HasValueOrChildren);
_writer.WriteBlock($$"""
_writer.WriteLine($$"""
if (!{{methodDisplayString}}({{Identifier.configuration}}))
{
return{{returnPostfix}};
}
""");
_writer.WriteBlankLine();
_writer.WriteLine();
}
}
}
......
......@@ -14,7 +14,7 @@ private sealed partial class Emitter
{
private bool ShouldEmitMethods(MethodsToGen_ConfigurationBinder methods) => (_sourceGenSpec.MethodsToGen_ConfigurationBinder & methods) != 0;
private void EmitBinder_ConfigurationBinder()
private void EmitBinder_Extensions_IConfiguration()
{
Debug.Assert(_sourceGenSpec.TypesForGen_ConfigurationBinder_BindMethods.Count <= 3 &&
!_sourceGenSpec.TypesForGen_ConfigurationBinder_BindMethods.Keys.Any(overload => (overload & MethodsToGen_ConfigurationBinder.Bind) is 0));
......@@ -25,13 +25,13 @@ private void EmitBinder_ConfigurationBinder()
}
_emitBlankLineBeforeNextStatement = false;
EmitRootBindingClassBlockStart(Identifier.GeneratedConfigurationBinder);
EmitRootBindingClassStartBlock(Identifier.GeneratedConfigurationBinder);
EmitGetMethods();
EmitGetValueMethods();
EmitBindMethods_ConfigurationBinder();
_writer.WriteBlockEnd();
EmitEndBlock();
_emitBlankLineBeforeNextStatement = true;
}
......
......@@ -129,11 +129,43 @@ private static class Identifier
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 @@ private void EmitCheckForNullArgument_WithBlankLine(string paramName)
? "global::System.ArgumentNullException"
: "ArgumentNullException";
_writer.WriteBlock($$"""
_writer.WriteLine($$"""
if ({{paramName}} is null)
{
throw new {{exceptionTypeDisplayString}}(nameof({{paramName}}));
}
""");
_writer.WriteBlankLine();
_writer.WriteLine();
}
private bool EmitInitException(TypeSpec type)
......@@ -176,14 +208,13 @@ private bool EmitInitException(TypeSpec type)
return false;
}
private void EmitRootBindingClassBlockStart(string className)
private void EmitRootBindingClassStartBlock(string className)
{
EmitBlankLineIfRequired();
_writer.WriteBlock($$"""
EmitStartBlock($$"""
/// <summary>Generated helper providing an AOT and linking compatible implementation for configuration binding.</summary>
{{GetGeneratedCodeAttributeSrc()}}
internal static class {{className}}
{
""");
_emitBlankLineBeforeNextStatement = false;
......
......@@ -16,12 +16,12 @@ private void EmitBinder_Extensions_OptionsBuilder()
return;
}
EmitRootBindingClassBlockStart(Identifier.GeneratedOptionsBuilderBinder);
EmitRootBindingClassStartBlock(Identifier.GeneratedOptionsBuilderBinder);
EmitBindMethods_Extensions_OptionsBuilder();
EmitBindConfigurationMethod();
_writer.WriteBlockEnd();
EmitEndBlock();
}
private void EmitBindMethods_Extensions_OptionsBuilder()
......@@ -36,24 +36,24 @@ private void EmitBindMethods_Extensions_OptionsBuilder()
if (ShouldEmitMethods(MethodsToGen_Extensions_OptionsBuilder.Bind_T))
{
EmitMethodBlockStart("Bind", paramList, documentation);
EmitMethodStartBlock("Bind", paramList, documentation);
_writer.WriteLine($"return global::{Identifier.GeneratedOptionsBuilderBinder}.Bind({Identifier.optionsBuilder}, {Identifier.configuration}, {Identifier.configureOptions}: null);");
_writer.WriteBlockEnd();
EmitEndBlock();
}
EmitMethodBlockStart(
EmitMethodStartBlock(
"Bind",
paramList + $", {FullyQualifiedDisplayString.ActionOfBinderOptions}? {Identifier.configureOptions}",
documentation);
EmitCheckForNullArgument_WithBlankLine(Identifier.optionsBuilder);
_writer.WriteBlock($$"""
_writer.WriteLine($$"""
global::{{Identifier.GeneratedServiceCollectionBinder}}.{{Identifier.Configure}}<{{Identifier.TOptions}}>({{Identifier.optionsBuilder}}.{{Identifier.Services}}, {{Identifier.optionsBuilder}}.Name, {{Identifier.configuration}}, {{Identifier.configureOptions}});
return {{Identifier.optionsBuilder}};
""");
_writer.WriteBlockEnd();
EmitEndBlock();
}
private void EmitBindConfigurationMethod()
......@@ -66,37 +66,37 @@ private void EmitBindConfigurationMethod()
const string documentation = $@"/// <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");
}
}
}
......
......@@ -9,14 +9,14 @@ private sealed partial class Emitter
{
private bool ShouldEmitMethods(MethodsToGen_Extensions_ServiceCollection methods) => (_sourceGenSpec.MethodsToGen_ServiceCollectionExt & methods) != 0;
private void EmitBinder_Extensions_ServiceCollection()
private void EmitBinder_Extensions_IServiceCollection()
{
if (!ShouldEmitMethods(MethodsToGen_Extensions_ServiceCollection.Any))
{
return;
}
EmitRootBindingClassBlockStart(Identifier.GeneratedServiceCollectionBinder);
EmitRootBindingClassStartBlock(Identifier.GeneratedServiceCollectionBinder);
const string defaultNameExpr = "string.Empty";
const string configureMethodString = $"global::{Identifier.GeneratedServiceCollectionBinder}.{Identifier.Configure}";
......@@ -24,56 +24,56 @@ private void EmitBinder_Extensions_ServiceCollection()
if (ShouldEmitMethods(MethodsToGen_Extensions_ServiceCollection.Configure_T))
{
EmitBlockStart(configParam);
EmitStartMethod(configParam);
_writer.WriteLine($"return {configureMethodString}<{Identifier.TOptions}>({Identifier.services}, {defaultNameExpr}, {Identifier.configuration}, {Identifier.configureOptions}: null);");
_writer.WriteBlockEnd();
EmitEndBlock();
}
if (ShouldEmitMethods(MethodsToGen_Extensions_ServiceCollection.Configure_T_name))
{
EmitBlockStart(
EmitStartMethod(
paramList: $"string? {Identifier.name}, " + configParam);
_writer.WriteLine($"return {configureMethodString}<{Identifier.TOptions}>({Identifier.services}, {Identifier.name}, {Identifier.configuration}, {Identifier.configureOptions}: null);");
_writer.WriteBlockEnd();
EmitEndBlock();
}
if (ShouldEmitMethods(MethodsToGen_Extensions_ServiceCollection.Configure_T_BinderOptions))
{
EmitBlockStart(
EmitStartMethod(
paramList: configParam + $", {FullyQualifiedDisplayString.ActionOfBinderOptions}? {Identifier.configureOptions}");
_writer.WriteLine($"return {configureMethodString}<{Identifier.TOptions}>({Identifier.services}, {defaultNameExpr}, {Identifier.configuration}, {Identifier.configureOptions});");
_writer.WriteBlockEnd();
EmitEndBlock();
}
// Core Configure method that the other overloads call.
// Like the others, it is public API that could be called directly by users.
// So, it is always generated whenever a Configure overload is called.
string optionsNamespaceName = "global::Microsoft.Extensions.Options";
string bindCoreUntypedDisplayString = GetHelperMethodDisplayString(nameof(MethodsToGen_CoreBindingHelper.BindCoreUntyped));
EmitBlockStart(paramList: $"string? {Identifier.name}, " + configParam + $", {FullyQualifiedDisplayString.ActionOfBinderOptions}? {Identifier.configureOptions}");
EmitStartMethod(paramList: $"string? {Identifier.name}, " + configParam + $", {FullyQualifiedDisplayString.ActionOfBinderOptions}? {Identifier.configureOptions}");
EmitCheckForNullArgument_WithBlankLine(Identifier.services);
EmitCheckForNullArgument_WithBlankLine(Identifier.configuration);
_writer.WriteBlock($$"""
_writer.WriteLine($$"""
global::Microsoft.Extensions.DependencyInjection.OptionsServiceCollectionExtensions.AddOptions({{Identifier.services}});
{{FullyQualifiedDisplayString.AddSingleton}}<{{FullyQualifiedDisplayString.IOptionsChangeTokenSource}}<{{Identifier.TOptions}}>>({{Identifier.services}}, new {{FullyQualifiedDisplayString.ConfigurationChangeTokenSource}}<{{Identifier.TOptions}}>({{Identifier.name}}, {{Identifier.configuration}}));
return {{FullyQualifiedDisplayString.AddSingleton}}<{{optionsNamespaceName}}.IConfigureOptions<{{Identifier.TOptions}}>>({{Identifier.services}}, new {{optionsNamespaceName}}.ConfigureNamedOptions<{{Identifier.TOptions}}>({{Identifier.name}}, {{Identifier.obj}} => {{bindCoreUntypedDisplayString}}({{Identifier.configuration}}, {{Identifier.obj}}, typeof({{Identifier.TOptions}}), {{Identifier.configureOptions}})));
}
""");
""");
EmitEndBlock();
_writer.WriteBlockEnd();
EmitEndBlock();
_emitBlankLineBeforeNextStatement = true;
}
private void EmitBlockStart(string paramList)
private void EmitStartMethod(string paramList)
{
paramList = $"this {FullyQualifiedDisplayString.IServiceCollection} {Identifier.services}, {paramList}";
EmitBlankLineIfRequired();
_writer.WriteBlock($$"""
/// <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
""");
}
}
}
......
// 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();
}
}
}
}
......@@ -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" />
......
......@@ -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;
}
......
......@@ -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;
}
......
......@@ -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)}");
......
......@@ -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;
}
......
......@@ -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)}");
......
......@@ -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;
}
......
......@@ -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;
}
......
......@@ -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;
}
......
......@@ -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;
}
......
......@@ -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;
}
......
......@@ -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;
}
......
......@@ -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;
}
......
......@@ -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;
}
......
......@@ -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)}");
......
......@@ -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;
}
......
......@@ -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;
}
......
......@@ -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;
}
......
......@@ -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;
}
......
......@@ -257,8 +257,7 @@ public class MyClass2 { }
.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);
......
......@@ -10,6 +10,7 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;
using SourceGenerators;
namespace System.Text.Json.SourceGeneration
{
......
......@@ -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" />
......@@ -50,13 +52,11 @@
<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" />
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册