From 92da59ab87470e9e823c8ff3158014a8a2227ea7 Mon Sep 17 00:00:00 2001 From: Chris Sienkiewicz Date: Mon, 9 Aug 2021 12:12:14 -0700 Subject: [PATCH] Make JsonGenerator be an incremental generator --- eng/Versions.props | 6 +-- .../gen/JsonSourceGenerator.Emitter.cs | 12 ++--- .../gen/JsonSourceGenerator.Parser.cs | 19 ++++---- .../gen/JsonSourceGenerator.cs | 48 +++++++------------ 4 files changed, 38 insertions(+), 47 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index 021b31aa65bcc..02eefd820a2d4 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -41,9 +41,9 @@ - - 3.9.0 - 3.9.0 + + 4.0.0-3.final + 4.0.0-3.final diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs index 9fb4433e743bd..ceae5d7a55296 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs @@ -78,15 +78,15 @@ private sealed partial class Emitter defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true); - private readonly GeneratorExecutionContext _executionContext; + private readonly SourceProductionContext _sourceProductionContext; private ContextGenerationSpec _currentContext = null!; private readonly SourceGenerationSpec _generationSpec = null!; - public Emitter(in GeneratorExecutionContext executionContext, SourceGenerationSpec generationSpec) + public Emitter(in SourceProductionContext sourceProductionContext, SourceGenerationSpec generationSpec) { - _executionContext = executionContext; + _sourceProductionContext = sourceProductionContext; _generationSpec = generationSpec; } @@ -165,7 +165,7 @@ namespace {@namespace} sb.AppendLine("}"); } - _executionContext.AddSource(fileName, SourceText.From(sb.ToString(), Encoding.UTF8)); + _sourceProductionContext.AddSource(fileName, SourceText.From(sb.ToString(), Encoding.UTF8)); } private void GenerateTypeInfo(TypeGenerationSpec typeGenerationSpec) @@ -242,7 +242,7 @@ private void GenerateTypeInfo(TypeGenerationSpec typeGenerationSpec) break; case ClassType.TypeUnsupportedBySourceGen: { - _executionContext.ReportDiagnostic( + _sourceProductionContext.ReportDiagnostic( Diagnostic.Create(TypeNotSupported, Location.None, new string[] { typeGenerationSpec.TypeRef })); return; } @@ -258,7 +258,7 @@ private void GenerateTypeInfo(TypeGenerationSpec typeGenerationSpec) } catch (ArgumentException) { - _executionContext.ReportDiagnostic(Diagnostic.Create(DuplicateTypeName, Location.None, new string[] { typeGenerationSpec.TypeInfoPropertyName })); + _sourceProductionContext.ReportDiagnostic(Diagnostic.Create(DuplicateTypeName, Location.None, new string[] { typeGenerationSpec.TypeInfoPropertyName })); } } diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs index 33a60562a3a10..2d38c15ddc0ce 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs @@ -4,6 +4,7 @@ using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; @@ -31,7 +32,8 @@ private sealed class Parser private const string JsonPropertyNameAttributeFullName = "System.Text.Json.Serialization.JsonPropertyNameAttribute"; private const string JsonPropertyOrderAttributeFullName = "System.Text.Json.Serialization.JsonPropertyOrderAttribute"; - private readonly GeneratorExecutionContext _executionContext; + private readonly Compilation _compilation; + private readonly SourceProductionContext _sourceProductionContext; private readonly MetadataLoadContextInternal _metadataLoadContext; private readonly Type _ilistOfTType; @@ -96,10 +98,11 @@ private sealed class Parser defaultSeverity: DiagnosticSeverity.Error, isEnabledByDefault: true); - public Parser(in GeneratorExecutionContext executionContext) + public Parser(Compilation compilation, in SourceProductionContext sourceProductionContext) { - _executionContext = executionContext; - _metadataLoadContext = new MetadataLoadContextInternal(executionContext.Compilation); + _compilation = compilation; + _sourceProductionContext = sourceProductionContext; + _metadataLoadContext = new MetadataLoadContextInternal(_compilation); _ilistOfTType = _metadataLoadContext.Resolve(SpecialType.System_Collections_Generic_IList_T); _icollectionOfTType = _metadataLoadContext.Resolve(SpecialType.System_Collections_Generic_ICollection_T); @@ -138,9 +141,9 @@ public Parser(in GeneratorExecutionContext executionContext) PopulateKnownTypes(); } - public SourceGenerationSpec? GetGenerationSpec(List classDeclarationSyntaxList) + public SourceGenerationSpec? GetGenerationSpec(ImmutableArray classDeclarationSyntaxList) { - Compilation compilation = _executionContext.Compilation; + Compilation compilation = _compilation; INamedTypeSymbol jsonSerializerContextSymbol = compilation.GetTypeByMetadataName("System.Text.Json.Serialization.JsonSerializerContext"); INamedTypeSymbol jsonSerializableAttributeSymbol = compilation.GetTypeByMetadataName("System.Text.Json.Serialization.JsonSerializableAttribute"); INamedTypeSymbol jsonSourceGenerationOptionsAttributeSymbol = compilation.GetTypeByMetadataName("System.Text.Json.Serialization.JsonSourceGenerationOptionsAttribute"); @@ -198,7 +201,7 @@ public Parser(in GeneratorExecutionContext executionContext) if (!TryGetClassDeclarationList(contextTypeSymbol, out List classDeclarationList)) { // Class or one of its containing types is not partial so we can't add to it. - _executionContext.ReportDiagnostic(Diagnostic.Create(ContextClassesMustBePartial, Location.None, new string[] { contextTypeSymbol.Name })); + _sourceProductionContext.ReportDiagnostic(Diagnostic.Create(ContextClassesMustBePartial, Location.None, new string[] { contextTypeSymbol.Name })); continue; } @@ -729,7 +732,7 @@ private TypeGenerationSpec GetOrAddTypeGenerationSpec(Type type, JsonSourceGener if (!type.TryGetDeserializationConstructor(useDefaultCtorInAnnotatedStructs, out ConstructorInfo? constructor)) { classType = ClassType.TypeUnsupportedBySourceGen; - _executionContext.ReportDiagnostic(Diagnostic.Create(MultipleJsonConstructorAttribute, Location.None, new string[] { $"{type}" })); + _sourceProductionContext.ReportDiagnostic(Diagnostic.Create(MultipleJsonConstructorAttribute, Location.None, new string[] { $"{type}" })); } else { diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.cs index 84212e8c19c78..54b76c7e061ba 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Reflection; @@ -16,22 +17,23 @@ namespace System.Text.Json.SourceGeneration /// Generates source code to optimize serialization and deserialization with JsonSerializer. /// [Generator] - public sealed partial class JsonSourceGenerator : ISourceGenerator + public sealed partial class JsonSourceGenerator : IIncrementalGenerator { - /// - /// Registers a syntax resolver to receive compilation units. - /// - /// - public void Initialize(GeneratorInitializationContext context) + private const string SystemTextJsonSourceGenerationName = "System.Text.Json.SourceGeneration"; + private const string IJsonOnSerializedFullName = "System.Text.Json.Serialization.IJsonOnSerialized"; + private const string IJsonOnSerializingFullName = "System.Text.Json.Serialization.IJsonOnSerializing"; + + + public void Initialize(IncrementalGeneratorInitializationContext context) { - context.RegisterForSyntaxNotifications(() => new SyntaxReceiver()); + var classDeclarations = context.SyntaxProvider.CreateSyntaxProvider((s, _) => s is ClassDeclarationSyntax, (s, _) => (ClassDeclarationSyntax)s.Node); + + var compilationAndClasses = context.CompilationProvider.Combine(classDeclarations.Collect()); + + context.RegisterSourceOutput(compilationAndClasses, (spc, source) => Execute(source.Left, source.Right, spc)); } - /// - /// Generates source code to optimize serialization and deserialization with JsonSerializer. - /// - /// - public void Execute(GeneratorExecutionContext executionContext) + private void Execute(Compilation compilation, ImmutableArray contextClasses, SourceProductionContext context) { #if LAUNCH_DEBUGGER if (!Diagnostics.Debugger.IsAttached) @@ -39,36 +41,22 @@ public void Execute(GeneratorExecutionContext executionContext) Diagnostics.Debugger.Launch(); } #endif - SyntaxReceiver receiver = (SyntaxReceiver)executionContext.SyntaxReceiver; - List? contextClasses = receiver.ClassDeclarationSyntaxList; - if (contextClasses == null) + if (contextClasses.IsDefaultOrEmpty) { return; } - Parser parser = new(executionContext); - SourceGenerationSpec? spec = parser.GetGenerationSpec(receiver.ClassDeclarationSyntaxList); + Parser parser = new(compilation, context); + SourceGenerationSpec? spec = parser.GetGenerationSpec(contextClasses); if (spec != null) { _rootTypes = spec.ContextGenerationSpecList[0].RootSerializableTypes; - Emitter emitter = new(executionContext, spec); + Emitter emitter = new(context, spec); emitter.Emit(); } } - private sealed class SyntaxReceiver : ISyntaxReceiver - { - public List? ClassDeclarationSyntaxList { get; private set; } - - public void OnVisitSyntaxNode(SyntaxNode syntaxNode) - { - if (syntaxNode is ClassDeclarationSyntax { AttributeLists.Count: > 0, BaseList.Types.Count: > 0 } cds) - { - (ClassDeclarationSyntaxList ??= new List()).Add(cds); - } - } - } /// /// Helper for unit tests.