From 7768cff79498ecee9908da131b2af7e1e3a84bd7 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Mon, 24 Apr 2023 14:45:55 +0300 Subject: [PATCH 1/3] Fix nullref on visitors with no acceptors --- VisitorPatternGenerator/VisitorPatternGenerator.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/VisitorPatternGenerator/VisitorPatternGenerator.cs b/VisitorPatternGenerator/VisitorPatternGenerator.cs index 8e43116..21fb626 100644 --- a/VisitorPatternGenerator/VisitorPatternGenerator.cs +++ b/VisitorPatternGenerator/VisitorPatternGenerator.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -82,6 +81,11 @@ public void Initialize(IncrementalGeneratorInitializationContext context) context.RegisterSourceOutput(visitorProvider, static (ctx, e) => { var ((visitor, acceptors), rootNamespace) = e; ctx.CancellationToken.ThrowIfCancellationRequested(); + + if (acceptors.IsDefaultOrEmpty) { + return; + } + _AddVisitorSource(ctx, rootNamespace, visitor, acceptors); }); } From 7403566d0d11523acc8a7012b70cc896bb0d9d79 Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Mon, 24 Apr 2023 17:55:13 +0300 Subject: [PATCH 2/3] Setup tests --- .../BaseSourceGeneratorTestContainer.cs | 102 ++++++ .../RuntimeReferences.cs | 15 + VisitorPatternGenerator.Tests/UnitTest1.cs | 12 - .../CSharpSourceGeneratorVerifier.cs | 22 ++ .../Verifiers/InterfacingSourceGenerator.cs | 19 ++ .../VisitorPatternGenerator.Tests.csproj | 24 +- .../VisitorPatternGeneratorTests.cs | 311 ++++++++++++++++++ 7 files changed, 485 insertions(+), 20 deletions(-) create mode 100644 VisitorPatternGenerator.Tests/BaseSourceGeneratorTestContainer.cs create mode 100644 VisitorPatternGenerator.Tests/RuntimeReferences.cs delete mode 100644 VisitorPatternGenerator.Tests/UnitTest1.cs create mode 100644 VisitorPatternGenerator.Tests/Verifiers/CSharpSourceGeneratorVerifier.cs create mode 100644 VisitorPatternGenerator.Tests/Verifiers/InterfacingSourceGenerator.cs create mode 100644 VisitorPatternGenerator.Tests/VisitorPatternGeneratorTests.cs diff --git a/VisitorPatternGenerator.Tests/BaseSourceGeneratorTestContainer.cs b/VisitorPatternGenerator.Tests/BaseSourceGeneratorTestContainer.cs new file mode 100644 index 0000000..59ad49b --- /dev/null +++ b/VisitorPatternGenerator.Tests/BaseSourceGeneratorTestContainer.cs @@ -0,0 +1,102 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.Text; + +using RoseLynn.Generators; +using RoseLynn.Testing; + +using VisitorPatternGenerator.Tests.Verifiers; + +using TupleSourceMappings = System.Collections.Generic.IEnumerable<(string fileName, string source)>; + +namespace VisitorPatternGenerator.Tests; + +public abstract class BaseSourceGeneratorTestContainer + where TSourceGenerator : IIncrementalGenerator, new() +{ + protected LanguageVersion LanguageVersion => LanguageVersion.CSharp11; + + protected virtual OutputKind OutputKind => OutputKind.NetModule; + + protected Compilation CreateCompilationRunGenerator(string source, out TSourceGenerator generator, out GeneratorDriver resultingGeneratorDriver, out Compilation initialCompilation, CancellationToken cancellationToken = default) + { + return this.CreateCompilationRunGenerator(new[] { source }, out generator, out resultingGeneratorDriver, out initialCompilation, cancellationToken); + } + + protected Compilation CreateCompilationRunGenerator(IEnumerable sources, out TSourceGenerator generator, out GeneratorDriver resultingGeneratorDriver, out Compilation initialCompilation, CancellationToken cancellationToken = default) + { + var defaultMetadataReferences = Enumerable.Empty(); + var parseOptions = new CSharpParseOptions(this.LanguageVersion); + var syntaxTrees = sources.Select((string source) => CSharpSyntaxTree.ParseText(source, parseOptions)); + var options = new CSharpCompilationOptions(this.OutputKind); + initialCompilation = CSharpCompilation.Create(null, syntaxTrees, defaultMetadataReferences, options); + generator = new TSourceGenerator(); + var cSharpGeneratorDriver = CSharpGeneratorDriver.Create(generator); + resultingGeneratorDriver = cSharpGeneratorDriver.RunGeneratorsAndUpdateCompilation(initialCompilation, out var outputCompilation, out var _, cancellationToken); + return outputCompilation; + } + + protected async Task VerifyAsync(string source, string generatedHintName, string generatedSource, CancellationToken cancellationToken = default) + { + var mappings = new GeneratedSourceMappings + { + { generatedHintName, generatedSource } + }; + await this.VerifyAsync(source, mappings, cancellationToken); + } + + protected async Task VerifyAsync(string source, GeneratedSourceMappings mappings, CancellationToken cancellationToken = default) + { + await this.VerifyAsync(new[] { source }, mappings, cancellationToken); + } + + protected async Task VerifyAsync(string source, TupleSourceMappings mappings, CancellationToken cancellationToken = default) + { + await this.VerifyAsync(new[] { source }, mappings, new CSharpSourceGeneratorVerifier.Test(), cancellationToken); + } + + protected async Task VerifyAsync(string source, GeneratedSourceMappings mappings, CSharpSourceGeneratorVerifier.Test test, CancellationToken cancellationToken = default) + { + await this.VerifyAsync(new[] { source }, mappings, test, cancellationToken); + } + + protected async Task VerifyAsync(IEnumerable sources, GeneratedSourceMappings mappings, CancellationToken cancellationToken = default) + { + await this.VerifyAsync(sources, mappings, new CSharpSourceGeneratorVerifier.Test(), cancellationToken); + } + + protected async Task VerifyAsync(IEnumerable sources, GeneratedSourceMappings mappings, CSharpSourceGeneratorVerifier.Test test, CancellationToken cancellationToken = default) + { + test.TestState.Sources.AddRange(sources); + foreach (var mapping in mappings) { + test.TestState.GeneratedSources.Add((typeof(TSourceGenerator), mapping.Key, mapping.Value)); + } + + await test.RunAsync(cancellationToken); + } + + protected async Task VerifyAsync(IEnumerable sources, IEnumerable<(string fileName, SourceText source)> mappings, CSharpSourceGeneratorVerifier.Test test, CancellationToken cancellationToken = default) + { + test.TestState.Sources.AddRange(sources); + foreach (var (fileName, source) in mappings) { + test.TestState.GeneratedSources.Add((typeof(TSourceGenerator), fileName, source)); + } + + await test.RunAsync(cancellationToken); + } + protected async Task VerifyAsync(IEnumerable sources, TupleSourceMappings mappings, CSharpSourceGeneratorVerifier.Test test, CancellationToken cancellationToken = default) + { + test.TestState.Sources.AddRange(sources); + foreach (var (fileName, source) in mappings) { + test.TestState.GeneratedSources.Add((typeof(TSourceGenerator), fileName, source)); + } + + await test.RunAsync(cancellationToken); + } +} diff --git a/VisitorPatternGenerator.Tests/RuntimeReferences.cs b/VisitorPatternGenerator.Tests/RuntimeReferences.cs new file mode 100644 index 0000000..7bcac5a --- /dev/null +++ b/VisitorPatternGenerator.Tests/RuntimeReferences.cs @@ -0,0 +1,15 @@ +using System.IO; + +using Microsoft.CodeAnalysis.Testing; + +namespace VisitorPatternGenerator.Tests; + +public static class RuntimeReferences +{ + public static readonly ReferenceAssemblies NET7_0Reference = + new( + "net7.0", + new PackageIdentity( + "Microsoft.NETCore.App.Ref", "7.0.0"), + Path.Combine("ref", "net7.0")); +} diff --git a/VisitorPatternGenerator.Tests/UnitTest1.cs b/VisitorPatternGenerator.Tests/UnitTest1.cs deleted file mode 100644 index bf9eea0..0000000 --- a/VisitorPatternGenerator.Tests/UnitTest1.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Xunit; - -namespace VisitorPatternGenerator.Tests; - -public class UnitTest1 -{ - [Fact] - public void Test1() - { - - } -} diff --git a/VisitorPatternGenerator.Tests/Verifiers/CSharpSourceGeneratorVerifier.cs b/VisitorPatternGenerator.Tests/Verifiers/CSharpSourceGeneratorVerifier.cs new file mode 100644 index 0000000..412530d --- /dev/null +++ b/VisitorPatternGenerator.Tests/Verifiers/CSharpSourceGeneratorVerifier.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Testing.Verifiers; + +using RoseLynn.Generators.Testing; + +namespace VisitorPatternGenerator.Tests.Verifiers; + +public static class CSharpSourceGeneratorVerifier + where TGenerator : IIncrementalGenerator, new() +{ + public class Test: CSharpSourceGeneratorTestEx + { + protected override IEnumerable GetSourceGenerators() => new[] { new TGenerator().AsSourceGenerator() }; + + public Test() + { + this.ReferenceAssemblies = RuntimeReferences.NET7_0Reference; + } + } +} diff --git a/VisitorPatternGenerator.Tests/Verifiers/InterfacingSourceGenerator.cs b/VisitorPatternGenerator.Tests/Verifiers/InterfacingSourceGenerator.cs new file mode 100644 index 0000000..e60acf5 --- /dev/null +++ b/VisitorPatternGenerator.Tests/Verifiers/InterfacingSourceGenerator.cs @@ -0,0 +1,19 @@ +using System; + +using Microsoft.CodeAnalysis; + +namespace VisitorPatternGenerator.Tests.Verifiers; + +/// +/// This dummy type acts as an interface between the two source generator interfaces to allow testing capabilities +/// only provided by . +/// Do not ever consider using this source generator as a real generator. +/// All source generator implementation methods throw. +/// +public sealed record InterfacingSourceGenerator: ISourceGenerator, IIncrementalGenerator +{ + void ISourceGenerator.Execute(GeneratorExecutionContext context) => throw new NotImplementedException(); + void ISourceGenerator.Initialize(GeneratorInitializationContext context) => throw new NotImplementedException(); + + void IIncrementalGenerator.Initialize(IncrementalGeneratorInitializationContext context) => throw new NotImplementedException(); +} diff --git a/VisitorPatternGenerator.Tests/VisitorPatternGenerator.Tests.csproj b/VisitorPatternGenerator.Tests/VisitorPatternGenerator.Tests.csproj index 3d58fed..4142f77 100644 --- a/VisitorPatternGenerator.Tests/VisitorPatternGenerator.Tests.csproj +++ b/VisitorPatternGenerator.Tests/VisitorPatternGenerator.Tests.csproj @@ -1,8 +1,9 @@ - net6.0 + net7.0 $(MSBuildProjectName) + false @@ -10,23 +11,30 @@ - + + + - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - runtime; build; native; contentfiles; analyzers; buildtransitive all + + + + + + + + + + Analyzer - fales + true diff --git a/VisitorPatternGenerator.Tests/VisitorPatternGeneratorTests.cs b/VisitorPatternGenerator.Tests/VisitorPatternGeneratorTests.cs new file mode 100644 index 0000000..dd8441e --- /dev/null +++ b/VisitorPatternGenerator.Tests/VisitorPatternGeneratorTests.cs @@ -0,0 +1,311 @@ +using NUnit.Framework; + +namespace VisitorPatternGenerator.Tests; + +public sealed class VisitorPatternGeneratorTests: BaseSourceGeneratorTestContainer +{ + [Test] + public void SimpleCase() + { + var source = """ + using VisitorPatternGenerator; + + namespace SurgicalThing.Tools; + + public struct Unit { } + + public interface ITool + { + bool IsAvailable { get; } + bool IsDisabled { get; set; } + } + + public abstract class Tool : ITool + { + public bool IsDisabled { get; set; } + + public virtual bool IsAvailable => !IsDisabled; + } + + public interface IConsumableTool : ITool + { + double CurrentDurability { get; set; } + double MaxDurability { get; set; } + + void ResetDurability(); + } + + public abstract class ConsumableTool : Tool, IConsumableTool + { + public double CurrentDurability { get; set; } + public double MaxDurability { get; set; } + + public override bool IsAvailable => CurrentDurability > 0 && base.IsAvailable; + + protected ConsumableTool(double maxDurability) + { + MaxDurability = maxDurability; + CurrentDurability = maxDurability; + } + + public void ResetDurability() + { + CurrentDurability = MaxDurability; + } + } + + public partial interface IMedication : IConsumableTool { } + + [Acceptor] + public abstract partial class Medication : ConsumableTool + { + protected Medication(double maxDurability) + : base(maxDurability) { } + } + + public partial interface IGeneralMedication { } + + [Acceptor] + [Acceptor] + public abstract partial class GeneralMedication : Medication + { + protected GeneralMedication(double maxDurability) + : base(maxDurability) { } + } + public partial interface ISpecializedMedication { } + + [Acceptor] + [Acceptor] + public abstract partial class SpecializedMedication : Medication + { + protected SpecializedMedication(double maxDurability) + : base(maxDurability) { } + } + + [Acceptor] + [Acceptor] + public sealed partial class Stabilizer : GeneralMedication + { + public Stabilizer(double maxDurability) + : base(maxDurability) { } + } + + // Visitors + [Visitor] + public partial interface IGeneratedMedicationVisitor { } + [Visitor] + public partial interface IGeneratedGeneralMedicationVisitor { } + [Visitor] + public partial interface IGeneratedSpecializedMedicationVisitor { } + [Visitor] + public partial interface IGeneratedToolVisitor { } + """; + + var annotationsTemplate = """ + // + #nullable enable + + namespace VisitorPatternGenerator; + + [System.Flags] + internal enum AcceptorOptions + { + None = 0, + MessagePackUnion = 0x100, + } + + [System.Diagnostics.Conditional("VISITOR_PATTERN_GENERATOR_PRESERVE_ANNOTATION")] + [System.AttributeUsage(System.AttributeTargets.Interface | System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + internal sealed class AcceptorAttribute: System.Attribute + { + public AcceptorOptions Options { get; } + + public AcceptorAttribute(AcceptorOptions options = AcceptorOptions.None) { this.Options = options; } + } + + [System.Diagnostics.Conditional("VISITOR_PATTERN_GENERATOR_PRESERVE_ANNOTATION")] + [System.AttributeUsage(System.AttributeTargets.Class, AllowMultiple = true, Inherited = false)] + internal sealed class AcceptorAttribute: System.Attribute where TAcceptor : class { } + + [System.Diagnostics.Conditional("VISITOR_PATTERN_GENERATOR_PRESERVE_ANNOTATION")] + [System.AttributeUsage(System.AttributeTargets.Class, AllowMultiple = true, Inherited = false)] + internal sealed class AcceptorAttribute: System.Attribute where TAcceptor : class where TSelf : class { } + + [System.Diagnostics.Conditional("VISITOR_PATTERN_GENERATOR_PRESERVE_ANNOTATION")] + [System.AttributeUsage(System.AttributeTargets.Class, AllowMultiple = true, Inherited = false)] + internal sealed class AcceptorAttribute: System.Attribute where TAcceptor : class where TSelf : class { } + + [System.Diagnostics.Conditional("VISITOR_PATTERN_GENERATOR_PRESERVE_ANNOTATION")] + [System.AttributeUsage(System.AttributeTargets.Interface, AllowMultiple = false, Inherited = false)] + internal sealed class VisitorAttribute: System.Attribute where TAcceptor : class + { + public bool VoidReturn { get; } + + public VisitorAttribute(bool voidReturn = false) { this.VoidReturn = voidReturn; } + } + + [System.Diagnostics.Conditional("VISITOR_PATTERN_GENERATOR_PRESERVE_ANNOTATION")] + [System.AttributeUsage(System.AttributeTargets.Interface, AllowMultiple = false, Inherited = false)] + internal sealed class VisitorAttribute: System.Attribute where TAcceptor : class { } + + [System.Diagnostics.Conditional("VISITOR_PATTERN_GENERATOR_PRESERVE_ANNOTATION")] + [System.AttributeUsage(System.AttributeTargets.Interface, AllowMultiple = false, Inherited = false)] + internal sealed class TaskVisitorAttribute: System.Attribute where TAcceptor : class + { + public bool VoidReturn { get; } + + public TaskVisitorAttribute(bool voidReturn = false) { this.VoidReturn = voidReturn; } + } + + [System.Diagnostics.Conditional("VISITOR_PATTERN_GENERATOR_PRESERVE_ANNOTATION")] + [System.AttributeUsage(System.AttributeTargets.Interface, AllowMultiple = false, Inherited = false)] + internal sealed class TaskVisitorAttribute: System.Attribute where TAcceptor : class { } + + [System.Diagnostics.Conditional("VISITOR_PATTERN_GENERATOR_PRESERVE_ANNOTATION")] + [System.AttributeUsage(System.AttributeTargets.Interface, AllowMultiple = false, Inherited = false)] + internal sealed class ValueTaskVisitorAttribute: System.Attribute where TAcceptor : class + { + public bool VoidReturn { get; } + + public ValueTaskVisitorAttribute(bool voidReturn = false) { this.VoidReturn = voidReturn; } + } + + [System.Diagnostics.Conditional("VISITOR_PATTERN_GENERATOR_PRESERVE_ANNOTATION")] + [System.AttributeUsage(System.AttributeTargets.Interface, AllowMultiple = false, Inherited = false)] + internal sealed class ValueTaskVisitorAttribute: System.Attribute where TAcceptor : class { } + + + """; + + var generatedMedicineVisitor = """ + // + + namespace SurgicalThing.Tools + { + partial interface IMedication + { + SurgicalThing.Tools.Unit Accept(SurgicalThing.Tools.IGeneratedMedicationVisitor visitor); + } + } + + namespace SurgicalThing.Tools + { + partial interface IGeneratedMedicationVisitor + { + SurgicalThing.Tools.Unit Visit(SurgicalThing.Tools.Medication value); + SurgicalThing.Tools.Unit Visit(SurgicalThing.Tools.GeneralMedication value); + SurgicalThing.Tools.Unit Visit(SurgicalThing.Tools.SpecializedMedication value); + SurgicalThing.Tools.Unit Visit(SurgicalThing.Tools.Stabilizer value); + } + } + + namespace SurgicalThing.Tools + { + partial class Medication: SurgicalThing.Tools.IMedication + { + SurgicalThing.Tools.Unit SurgicalThing.Tools.IMedication.Accept(SurgicalThing.Tools.IGeneratedMedicationVisitor visitor) => visitor.Visit(this); + } + } + + namespace SurgicalThing.Tools + { + partial class GeneralMedication: SurgicalThing.Tools.IMedication + { + SurgicalThing.Tools.Unit SurgicalThing.Tools.IMedication.Accept(SurgicalThing.Tools.IGeneratedMedicationVisitor visitor) => visitor.Visit(this); + } + } + + namespace SurgicalThing.Tools + { + partial class SpecializedMedication: SurgicalThing.Tools.IMedication + { + SurgicalThing.Tools.Unit SurgicalThing.Tools.IMedication.Accept(SurgicalThing.Tools.IGeneratedMedicationVisitor visitor) => visitor.Visit(this); + } + } + + namespace SurgicalThing.Tools + { + partial class Stabilizer: SurgicalThing.Tools.IMedication + { + SurgicalThing.Tools.Unit SurgicalThing.Tools.IMedication.Accept(SurgicalThing.Tools.IGeneratedMedicationVisitor visitor) => visitor.Visit(this); + } + } + + """; + var generatedGeneralMedicationVisitor = """ + // + + namespace SurgicalThing.Tools + { + partial interface IGeneralMedication + { + SurgicalThing.Tools.Unit Accept(SurgicalThing.Tools.IGeneratedGeneralMedicationVisitor visitor); + } + } + + namespace SurgicalThing.Tools + { + partial interface IGeneratedGeneralMedicationVisitor + { + SurgicalThing.Tools.Unit Visit(SurgicalThing.Tools.GeneralMedication value); + SurgicalThing.Tools.Unit Visit(SurgicalThing.Tools.Stabilizer value); + } + } + + namespace SurgicalThing.Tools + { + partial class GeneralMedication: SurgicalThing.Tools.IGeneralMedication + { + SurgicalThing.Tools.Unit SurgicalThing.Tools.IGeneralMedication.Accept(SurgicalThing.Tools.IGeneratedGeneralMedicationVisitor visitor) => visitor.Visit(this); + } + } + + namespace SurgicalThing.Tools + { + partial class Stabilizer: SurgicalThing.Tools.IGeneralMedication + { + SurgicalThing.Tools.Unit SurgicalThing.Tools.IGeneralMedication.Accept(SurgicalThing.Tools.IGeneratedGeneralMedicationVisitor visitor) => visitor.Visit(this); + } + } + + """; + var generatedSpecializedMedicationVisitor = """ + // + + namespace SurgicalThing.Tools + { + partial interface ISpecializedMedication + { + SurgicalThing.Tools.Unit Accept(SurgicalThing.Tools.IGeneratedSpecializedMedicationVisitor visitor); + } + } + + namespace SurgicalThing.Tools + { + partial interface IGeneratedSpecializedMedicationVisitor + { + SurgicalThing.Tools.Unit Visit(SurgicalThing.Tools.SpecializedMedication value); + } + } + + namespace SurgicalThing.Tools + { + partial class SpecializedMedication: SurgicalThing.Tools.ISpecializedMedication + { + SurgicalThing.Tools.Unit SurgicalThing.Tools.ISpecializedMedication.Accept(SurgicalThing.Tools.IGeneratedSpecializedMedicationVisitor visitor) => visitor.Visit(this); + } + } + + """; + + var mappings = new (string fileName, string source)[] { + ("AnnotationsTemplate.cs", annotationsTemplate), + + ("SurgicalThing.Tools.IGeneratedMedicationVisitor.cs", generatedMedicineVisitor), + ("SurgicalThing.Tools.IGeneratedGeneralMedicationVisitor.cs", generatedGeneralMedicationVisitor), + ("SurgicalThing.Tools.IGeneratedSpecializedMedicationVisitor.cs", generatedSpecializedMedicationVisitor), + }; + + this.VerifyAsync(source, mappings, default).Wait(); + } +} From cd803a10666da35e249d55d0339dc423a3897f3f Mon Sep 17 00:00:00 2001 From: Rekkonnect Date: Fri, 28 Apr 2023 22:25:31 +0300 Subject: [PATCH 3/3] Update to RoseLynn 0.6.4 --- .../BaseSourceGeneratorTestContainer.cs | 95 +------------------ .../CSharpSourceGeneratorVerifier.cs | 8 +- .../Verifiers/InterfacingSourceGenerator.cs | 19 ---- .../VisitorPatternGenerator.Tests.csproj | 4 +- 4 files changed, 8 insertions(+), 118 deletions(-) delete mode 100644 VisitorPatternGenerator.Tests/Verifiers/InterfacingSourceGenerator.cs diff --git a/VisitorPatternGenerator.Tests/BaseSourceGeneratorTestContainer.cs b/VisitorPatternGenerator.Tests/BaseSourceGeneratorTestContainer.cs index 59ad49b..ffbfbe1 100644 --- a/VisitorPatternGenerator.Tests/BaseSourceGeneratorTestContainer.cs +++ b/VisitorPatternGenerator.Tests/BaseSourceGeneratorTestContainer.cs @@ -1,102 +1,15 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Testing; -using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Testing.Verifiers; -using RoseLynn.Generators; using RoseLynn.Testing; using VisitorPatternGenerator.Tests.Verifiers; -using TupleSourceMappings = System.Collections.Generic.IEnumerable<(string fileName, string source)>; - namespace VisitorPatternGenerator.Tests; -public abstract class BaseSourceGeneratorTestContainer - where TSourceGenerator : IIncrementalGenerator, new() +public abstract class BaseSourceGeneratorTestContainer: BaseIncrementalGeneratorTestContainer.Test> + where TGenerator : IIncrementalGenerator, new() { - protected LanguageVersion LanguageVersion => LanguageVersion.CSharp11; - - protected virtual OutputKind OutputKind => OutputKind.NetModule; - - protected Compilation CreateCompilationRunGenerator(string source, out TSourceGenerator generator, out GeneratorDriver resultingGeneratorDriver, out Compilation initialCompilation, CancellationToken cancellationToken = default) - { - return this.CreateCompilationRunGenerator(new[] { source }, out generator, out resultingGeneratorDriver, out initialCompilation, cancellationToken); - } - - protected Compilation CreateCompilationRunGenerator(IEnumerable sources, out TSourceGenerator generator, out GeneratorDriver resultingGeneratorDriver, out Compilation initialCompilation, CancellationToken cancellationToken = default) - { - var defaultMetadataReferences = Enumerable.Empty(); - var parseOptions = new CSharpParseOptions(this.LanguageVersion); - var syntaxTrees = sources.Select((string source) => CSharpSyntaxTree.ParseText(source, parseOptions)); - var options = new CSharpCompilationOptions(this.OutputKind); - initialCompilation = CSharpCompilation.Create(null, syntaxTrees, defaultMetadataReferences, options); - generator = new TSourceGenerator(); - var cSharpGeneratorDriver = CSharpGeneratorDriver.Create(generator); - resultingGeneratorDriver = cSharpGeneratorDriver.RunGeneratorsAndUpdateCompilation(initialCompilation, out var outputCompilation, out var _, cancellationToken); - return outputCompilation; - } - - protected async Task VerifyAsync(string source, string generatedHintName, string generatedSource, CancellationToken cancellationToken = default) - { - var mappings = new GeneratedSourceMappings - { - { generatedHintName, generatedSource } - }; - await this.VerifyAsync(source, mappings, cancellationToken); - } - - protected async Task VerifyAsync(string source, GeneratedSourceMappings mappings, CancellationToken cancellationToken = default) - { - await this.VerifyAsync(new[] { source }, mappings, cancellationToken); - } - - protected async Task VerifyAsync(string source, TupleSourceMappings mappings, CancellationToken cancellationToken = default) - { - await this.VerifyAsync(new[] { source }, mappings, new CSharpSourceGeneratorVerifier.Test(), cancellationToken); - } - - protected async Task VerifyAsync(string source, GeneratedSourceMappings mappings, CSharpSourceGeneratorVerifier.Test test, CancellationToken cancellationToken = default) - { - await this.VerifyAsync(new[] { source }, mappings, test, cancellationToken); - } - - protected async Task VerifyAsync(IEnumerable sources, GeneratedSourceMappings mappings, CancellationToken cancellationToken = default) - { - await this.VerifyAsync(sources, mappings, new CSharpSourceGeneratorVerifier.Test(), cancellationToken); - } - - protected async Task VerifyAsync(IEnumerable sources, GeneratedSourceMappings mappings, CSharpSourceGeneratorVerifier.Test test, CancellationToken cancellationToken = default) - { - test.TestState.Sources.AddRange(sources); - foreach (var mapping in mappings) { - test.TestState.GeneratedSources.Add((typeof(TSourceGenerator), mapping.Key, mapping.Value)); - } - - await test.RunAsync(cancellationToken); - } - - protected async Task VerifyAsync(IEnumerable sources, IEnumerable<(string fileName, SourceText source)> mappings, CSharpSourceGeneratorVerifier.Test test, CancellationToken cancellationToken = default) - { - test.TestState.Sources.AddRange(sources); - foreach (var (fileName, source) in mappings) { - test.TestState.GeneratedSources.Add((typeof(TSourceGenerator), fileName, source)); - } - - await test.RunAsync(cancellationToken); - } - protected async Task VerifyAsync(IEnumerable sources, TupleSourceMappings mappings, CSharpSourceGeneratorVerifier.Test test, CancellationToken cancellationToken = default) - { - test.TestState.Sources.AddRange(sources); - foreach (var (fileName, source) in mappings) { - test.TestState.GeneratedSources.Add((typeof(TSourceGenerator), fileName, source)); - } - - await test.RunAsync(cancellationToken); - } + protected override LanguageVersion LanguageVersion => LanguageVersion.CSharp11; } diff --git a/VisitorPatternGenerator.Tests/Verifiers/CSharpSourceGeneratorVerifier.cs b/VisitorPatternGenerator.Tests/Verifiers/CSharpSourceGeneratorVerifier.cs index 412530d..8bdf2d6 100644 --- a/VisitorPatternGenerator.Tests/Verifiers/CSharpSourceGeneratorVerifier.cs +++ b/VisitorPatternGenerator.Tests/Verifiers/CSharpSourceGeneratorVerifier.cs @@ -1,19 +1,15 @@ -using System.Collections.Generic; - using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Testing.Verifiers; -using RoseLynn.Generators.Testing; +using RoseLynn.Testing; namespace VisitorPatternGenerator.Tests.Verifiers; public static class CSharpSourceGeneratorVerifier where TGenerator : IIncrementalGenerator, new() { - public class Test: CSharpSourceGeneratorTestEx + public class Test: CSharpIncrementalGeneratorTestEx { - protected override IEnumerable GetSourceGenerators() => new[] { new TGenerator().AsSourceGenerator() }; - public Test() { this.ReferenceAssemblies = RuntimeReferences.NET7_0Reference; diff --git a/VisitorPatternGenerator.Tests/Verifiers/InterfacingSourceGenerator.cs b/VisitorPatternGenerator.Tests/Verifiers/InterfacingSourceGenerator.cs deleted file mode 100644 index e60acf5..0000000 --- a/VisitorPatternGenerator.Tests/Verifiers/InterfacingSourceGenerator.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; - -using Microsoft.CodeAnalysis; - -namespace VisitorPatternGenerator.Tests.Verifiers; - -/// -/// This dummy type acts as an interface between the two source generator interfaces to allow testing capabilities -/// only provided by . -/// Do not ever consider using this source generator as a real generator. -/// All source generator implementation methods throw. -/// -public sealed record InterfacingSourceGenerator: ISourceGenerator, IIncrementalGenerator -{ - void ISourceGenerator.Execute(GeneratorExecutionContext context) => throw new NotImplementedException(); - void ISourceGenerator.Initialize(GeneratorInitializationContext context) => throw new NotImplementedException(); - - void IIncrementalGenerator.Initialize(IncrementalGeneratorInitializationContext context) => throw new NotImplementedException(); -} diff --git a/VisitorPatternGenerator.Tests/VisitorPatternGenerator.Tests.csproj b/VisitorPatternGenerator.Tests/VisitorPatternGenerator.Tests.csproj index 4142f77..95bbe3c 100644 --- a/VisitorPatternGenerator.Tests/VisitorPatternGenerator.Tests.csproj +++ b/VisitorPatternGenerator.Tests/VisitorPatternGenerator.Tests.csproj @@ -1,4 +1,4 @@ - + net7.0 @@ -20,7 +20,7 @@ all - +