Exemple #1
0
        public async Task PropertiesGetterOnlyMixed()
        {
            using var tempWorkarea = TemporaryProjectWorkarea.Create();
            var contents = @"
using System;
using System.Collections.Generic;
using MessagePack;

namespace TempProject
{
    [MessagePackObject(true)]
    public class MyMessagePackObject
    {
        public int A { get; }
        public string B { get; set; }
    }
}
            ";

            tempWorkarea.AddFileToTargetProject("MyMessagePackObject.cs", contents);

            var compiler = new MessagePackCompiler.CodeGenerator(testOutputHelper.WriteLine, CancellationToken.None);
            await compiler.GenerateFileAsync(
                tempWorkarea.GetOutputCompilation().Compilation,
                tempWorkarea.OutputDirectory,
                "TempProjectResolver",
                "TempProject.Generated",
                false,
                string.Empty,
                Array.Empty <string>());

            var compilation = tempWorkarea.GetOutputCompilation();

            compilation.Compilation.GetDiagnostics().Should().NotContain(x => x.Severity == DiagnosticSeverity.Error);

            // Run tests with the generated resolver/formatter assembly.
            compilation.ExecuteWithGeneratedAssembly((ctx, assembly) =>
            {
                var mpoType = assembly.GetType("TempProject.MyMessagePackObject");
                var options = MessagePackSerializerOptions.Standard
                              .WithResolver(CompositeResolver.Create(
                                                StandardResolver.Instance,
                                                TestUtilities.GetResolverInstance(assembly, "TempProject.Generated.Resolvers.TempProjectResolver")));

                // Build `{ "A": -1, "B": "foobar" }`.
                var seq    = new Sequence <byte>();
                var writer = new MessagePackWriter(seq);
                writer.WriteMapHeader(2);
                writer.Write("A");
                writer.Write(-1);
                writer.Write("B");
                writer.Write("foobar");
                writer.Flush();

                // Verify deserialization
                dynamic result = MessagePackSerializer.Deserialize(mpoType, seq, options);
                ((int)result.A).Should().Be(0);           // default
                ((string)result.B).Should().Be("foobar"); // from input
            });
        }
        public async Task Generics_Constraints_ReferenceType_Nullable(bool isSingleFileOutput)
        {
            using var tempWorkarea = TemporaryProjectWorkarea.Create();
            var contents = @"
using System;
using System.Collections.Generic;
using MessagePack;

namespace TempProject
{
    [MessagePackObject]
    public class MyGenericObject<T1, T2>
        where T1 : class?
        where T2 : class
    {
        [Key(0)]
        public T1 Content1 { get; set; }
        [Key(1)]
        public T2 Content2 { get; set; }
    }
}
                ";

            tempWorkarea.AddFileToProject("MyMessagePackObject.cs", contents);

            var compiler = new MessagePackCompiler.CodeGenerator(testOutputHelper.WriteLine, CancellationToken.None);
            await compiler.GenerateFileAsync(
                tempWorkarea.GetOutputCompilation().Compilation,
                isSingleFileOutput?Path.Combine(tempWorkarea.OutputDirectory, "Generated.cs") : tempWorkarea.OutputDirectory,
                "TempProjectResolver",
                "TempProject.Generated",
                false,
                string.Empty,
                Array.Empty <string>());

            var compilation = tempWorkarea.GetOutputCompilation();

            compilation.Compilation.GetDiagnostics().Where(x => x.WarningLevel == 0).Should().BeEmpty();

            var symbols = compilation.GetNamedTypeSymbolsFromGenerated();

            var formatterType = symbols.FirstOrDefault(x => x.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) == "global::TempProject.Generated.Formatters.TempProject.MyGenericObjectFormatter<T1, T2>");

            formatterType.Should().NotBeNull();
            // class?
            formatterType.TypeParameters[0].HasReferenceTypeConstraint.Should().BeTrue();
            formatterType.TypeParameters[0].ConstraintTypes.Should().BeEmpty();
            formatterType.TypeParameters[0].ReferenceTypeConstraintNullableAnnotation.Should().Be(NullableAnnotation.Annotated);
            // class
            formatterType.TypeParameters[1].HasReferenceTypeConstraint.Should().BeTrue();
            formatterType.TypeParameters[1].ConstraintTypes.Should().BeEmpty();
            formatterType.TypeParameters[1].ReferenceTypeConstraintNullableAnnotation.Should().Be(NullableAnnotation.None);
        }
Exemple #3
0
        public async Task CanGenerateMessagePackFormatterAttr()
        {
            using var tempWorkarea = TemporaryProjectWorkarea.Create();
            var contents = @"
using System;
using System.Collections.Generic;
using MessagePack;

namespace TempProject
{
    [MessagePackFormatter(typeof(MyFormatter))]
    public class MyMessagePackObject
    {
        public int Foo { get; set; }

        public class MyFormatter : IMessagePackFormatter<MyMessagePackObject>
        {
            public MyMessagePackObject Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
            {
                throw new NotImplementedException();
            }

            public void Serialize(ref MessagePackWriter writer, MyMessagePackObject value, MessagePackSerializerOptions options)
            {
                throw new NotImplementedException();
            }
        }
    }

    [MessagePackObject]
    public class Bar
    {
        [Key(0)]
        public MyMessagePackObject Baz { get; set; }
    }
}
            ";

            tempWorkarea.AddFileToProject("MyMessagePackObject.cs", contents);

            var compiler = new MessagePackCompiler.CodeGenerator(testOutputHelper.WriteLine, CancellationToken.None);

            // can compile(does not throw MessagePackGeneratorResolveFailedException : Serialization Object must mark MessagePackObjectAttribute. type: global::TempProject.MyMessagePackObject)
            await compiler.GenerateFileAsync(
                tempWorkarea.CsProjectPath,
                tempWorkarea.OutputDirectory,
                string.Empty,
                "TempProjectResolver",
                "TempProject.Generated",
                false,
                string.Empty);
        }
Exemple #4
0
        public async Task GenericsOfTFormatter_FormatterOnly(bool isSingleFileOutput)
        {
            using var tempWorkarea = TemporaryProjectWorkarea.Create();
            var contents = @"
using System;
using System.Collections.Generic;
using MessagePack;

namespace TempProject
{
    // This type is not used by the project.
    // It may be referenced by other projects.
    [MessagePackObject]
    public class MyGenericObject<T>
    {
        [Key(0)]
        public T Content { get; set; }
    }
}
                ";

            tempWorkarea.AddFileToProject("MyMessagePackObject.cs", contents);

            var compiler = new MessagePackCompiler.CodeGenerator(testOutputHelper.WriteLine, CancellationToken.None);
            await compiler.GenerateFileAsync(
                tempWorkarea.CsProjectPath,
                isSingleFileOutput?Path.Combine(tempWorkarea.OutputDirectory, "Generated.cs") : tempWorkarea.OutputDirectory,
                string.Empty,
                "TempProjectResolver",
                "TempProject.Generated",
                false,
                string.Empty);

            var compilation = tempWorkarea.GetOutputCompilation();

            compilation.Compilation.GetDiagnostics().Where(x => x.WarningLevel == 0).Should().BeEmpty();

            var symbols = compilation.GetNamedTypeSymbolsFromGenerated();

            var types = symbols.Select(x => x.ToDisplayString()).ToArray();

            types.Should().Contain("TempProject.Generated.Formatters.TempProject.MyGenericObjectFormatter<T>");

            var formatters = symbols.SelectMany(x => x.Interfaces).Select(x => x.ToDisplayString()).ToArray();

            formatters.Should().Contain("MessagePack.Formatters.IMessagePackFormatter<TempProject.MyGenericObject<T>>");

            // The generated resolver doesn't know closed-type generic formatter.
            compilation.GetResolverKnownFormatterTypes().Should().BeEmpty();
        }
Exemple #5
0
        public async Task NullableFormatter(bool isSingleFileOutput)
        {
            using var tempWorkarea = TemporaryProjectWorkarea.Create(false);
            var contents = @"
using System;
using System.Collections.Generic;
using MessagePack;

namespace TempProject
{
    [MessagePackObject]
    public class MyObject
    {
        [Key(0)]
        public int? ValueNullableInt { get; set; }
        [Key(1)]
        public MyEnum? ValueNullableEnum { get; set; }
        [Key(2)]
        public ValueTuple<int, long>? ValueNullableStruct { get; set; }
    }

    public enum MyEnum
    {
        A, B, C
    }
}
            ";

            tempWorkarea.AddFileToProject("MyMessagePackObject.cs", contents);

            var compiler = new MessagePackCompiler.CodeGenerator(testOutputHelper.WriteLine, CancellationToken.None);
            await compiler.GenerateFileAsync(
                tempWorkarea.CsProjectPath,
                isSingleFileOutput?Path.Combine(tempWorkarea.OutputDirectory, "Generated.cs") : tempWorkarea.OutputDirectory,
                string.Empty,
                "TempProjectResolver",
                "TempProject.Generated",
                false,
                string.Empty);

            var compilation = tempWorkarea.GetOutputCompilation();

            compilation.Compilation.GetDiagnostics().Where(x => x.WarningLevel == 0).Should().BeEmpty();

            compilation.GetResolverKnownFormatterTypes().Should().Contain(new[]
            {
                "global::MessagePack.Formatters.NullableFormatter<(int, long)>",
                "global::MessagePack.Formatters.NullableFormatter<global::TempProject.MyEnum>",
            });
        }
Exemple #6
0
        public async Task GenericsUnionFormatter(bool isSingleFileOutput)
        {
            using var tempWorkarea = TemporaryProjectWorkarea.Create(false);
            var contents = @"
using System;
using System.Collections.Generic;
using MessagePack;

namespace TempProject
{
    [MessagePackObject]
    [Union(0, typeof(Wrapper<string>))]
    [Union(1, typeof(Wrapper<int[]>))]
    [Union(2, typeof(Wrapper<IEnumerable<Guid>>))]
    public class Wrapper<T>
    {
        [Key(0)]
        public T Content { get; set; }
    }
}
            ";

            tempWorkarea.AddFileToProject("MyMessagePackObject.cs", contents);

            var compiler = new MessagePackCompiler.CodeGenerator(testOutputHelper.WriteLine, CancellationToken.None);
            await compiler.GenerateFileAsync(
                tempWorkarea.CsProjectPath,
                isSingleFileOutput?Path.Combine(tempWorkarea.OutputDirectory, "Generated.cs") : tempWorkarea.OutputDirectory,
                string.Empty,
                "TempProjectResolver",
                "TempProject.Generated",
                false,
                string.Empty);

            var compilation = tempWorkarea.GetOutputCompilation();

            compilation.Compilation.GetDiagnostics().Where(x => x.WarningLevel == 0).Should().BeEmpty();

            var symbols    = compilation.GetNamedTypeSymbolsFromGenerated();
            var formatters = symbols.SelectMany(x => x.Interfaces).Select(x => x.ToDisplayString()).ToArray();

            formatters.Should().Contain("MessagePack.Formatters.IMessagePackFormatter<TempProject.Wrapper<T>>");

            compilation.GetResolverKnownFormatterTypes().Should().Contain(new[]
            {
                "TempProject.Generated.Formatters.TempProject.WrapperFormatter<global::System.Collections.Generic.IEnumerable<global::System.Guid>>",
                "TempProject.Generated.Formatters.TempProject.WrapperFormatter<int[]>",
                "TempProject.Generated.Formatters.TempProject.WrapperFormatter<string>",
            });
        }
Exemple #7
0
        public async Task Generics_Constraints_Type(bool isSingleFileOutput)
        {
            using var tempWorkarea = TemporaryProjectWorkarea.Create();
            var contents = @"
using System;
using System.Collections.Generic;
using MessagePack;

namespace TempProject
{
    [MessagePackObject]
    public class MyGenericObject<T>
        where T : IDisposable
    {
        [Key(0)]
        public T Content { get; set; }
    }
}
                ";

            tempWorkarea.AddFileToProject("MyMessagePackObject.cs", contents);

            var compiler = new MessagePackCompiler.CodeGenerator(testOutputHelper.WriteLine, CancellationToken.None);
            await compiler.GenerateFileAsync(
                tempWorkarea.CsProjectPath,
                isSingleFileOutput?Path.Combine(tempWorkarea.OutputDirectory, "Generated.cs") : tempWorkarea.OutputDirectory,
                string.Empty,
                "TempProjectResolver",
                "TempProject.Generated",
                false,
                string.Empty);

            var compilation = tempWorkarea.GetOutputCompilation();

            compilation.Compilation.GetDiagnostics().Where(x => x.WarningLevel == 0).Should().BeEmpty();

            var symbols = compilation.GetNamedTypeSymbolsFromGenerated();

            var formatterType = symbols.FirstOrDefault(x => x.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) == "global::TempProject.Generated.Formatters.TempProject.MyGenericObjectFormatter<T>");

            formatterType.Should().NotBeNull();
            // IDisposable
            formatterType.TypeParameters[0].HasReferenceTypeConstraint.Should().BeFalse();
            formatterType.TypeParameters[0].HasValueTypeConstraint.Should().BeFalse();
            formatterType.TypeParameters[0].HasConstructorConstraint.Should().BeFalse();
            formatterType.TypeParameters[0].HasNotNullConstraint.Should().BeFalse();
            formatterType.TypeParameters[0].HasUnmanagedTypeConstraint.Should().BeFalse();
            formatterType.TypeParameters[0].ConstraintTypes.Should().Contain(x => x.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) == "global::System.IDisposable");
        }
        public async Task WellKnownGenericsFormatter(bool isSingleFileOutput)
        {
            using var tempWorkarea = TemporaryProjectWorkarea.Create(false);
            var contents = @"
using System;
using System.Collections.Generic;
using MessagePack;

namespace TempProject
{
    [MessagePackObject]
    public class MyObject
    {
        [Key(0)]
        public List<int> ValueList { get; set; }
        [Key(1)]
        public List<List<int>> ValueListNested { get; set; }
        [Key(2)]
        public ValueTuple<int, string, long> ValueValueTuple { get; set; }
    }
}
            ";

            tempWorkarea.AddFileToProject("MyMessagePackObject.cs", contents);

            var compiler = new MessagePackCompiler.CodeGenerator(testOutputHelper.WriteLine, CancellationToken.None);
            await compiler.GenerateFileAsync(
                tempWorkarea.GetOutputCompilation().Compilation,
                isSingleFileOutput?Path.Combine(tempWorkarea.OutputDirectory, "Generated.cs") : tempWorkarea.OutputDirectory,
                "TempProjectResolver",
                "TempProject.Generated",
                false,
                string.Empty,
                Array.Empty <string>());

            var compilation = tempWorkarea.GetOutputCompilation();

            compilation.Compilation.GetDiagnostics().Where(x => x.WarningLevel == 0).Should().BeEmpty();

            compilation.GetResolverKnownFormatterTypes().Should().Contain(new[]
            {
                "global::MessagePack.Formatters.ListFormatter<int>",
                "global::MessagePack.Formatters.ListFormatter<global::System.Collections.Generic.List<int>>",
                "global::MessagePack.Formatters.ValueTupleFormatter<int, string, long>",
                "TempProject.Generated.Formatters.TempProject.MyObjectFormatter",
            });
        }
Exemple #9
0
        public async Task EnumFormatter()
        {
            using var tempWorkarea = TemporaryProjectWorkarea.Create();
            var contents = @"
using System;
using System.Collections.Generic;
using MessagePack;

namespace TempProject
{
    [MessagePackObject]
    public class MyMessagePackObject
    {
        [Key(0)]
        public MyEnum EnumValue { get; set; }
    }

    public enum MyEnum
    {
        A, B, C
    }
}
            ";

            tempWorkarea.AddFileToProject("MyMessagePackObject.cs", contents);

            var compiler = new MessagePackCompiler.CodeGenerator(testOutputHelper.WriteLine, CancellationToken.None);
            await compiler.GenerateFileAsync(
                tempWorkarea.CsProjectPath,
                tempWorkarea.OutputDirectory,
                string.Empty,
                "TempProjectResolver",
                "TempProject.Generated",
                false,
                string.Empty);

            var compilation = tempWorkarea.GetOutputCompilation();
            var symbols     = compilation.GetNamedTypeSymbolsFromGenerated();

            symbols.Should().Contain(x => x.Name == "MyEnumFormatter");
        }
Exemple #10
0
        public async Task GenericsOfT1T2Formatter(bool isSingleFileOutput)
        {
            using var tempWorkarea = TemporaryProjectWorkarea.Create();
            var contents = @"
using System;
using System.Collections.Generic;
using MessagePack;

namespace TempProject
{
    [MessagePackObject]
    public class MyGenericObject<T1, T2>
    {
        [Key(0)]
        public T1 ValueA { get; set; }
        [Key(1)]
        public T2 ValueB { get; set; }
    }

    [MessagePackObject]
    public class MyObject
    {
        [Key(0)]
        public MyGenericObject<int, string> Value { get; set; }
    }

    [MessagePackObject]
    public class MyObjectNested
    {
        [Key(0)]
        public MyGenericObject<MyGenericObject<int, string>, MyGenericObject<int, string>> Value { get; set; }
    }
}
            ";

            tempWorkarea.AddFileToProject("MyMessagePackObject.cs", contents);

            var compiler = new MessagePackCompiler.CodeGenerator(testOutputHelper.WriteLine, CancellationToken.None);
            await compiler.GenerateFileAsync(
                tempWorkarea.CsProjectPath,
                isSingleFileOutput?Path.Combine(tempWorkarea.OutputDirectory, "Generated.cs") : tempWorkarea.OutputDirectory,
                string.Empty,
                "TempProjectResolver",
                "TempProject.Generated",
                false,
                string.Empty);

            var compilation = tempWorkarea.GetOutputCompilation();

            compilation.Compilation.GetDiagnostics().Where(x => x.WarningLevel == 0).Should().BeEmpty();

            var symbols = compilation.GetNamedTypeSymbolsFromGenerated();

            var types = symbols.Select(x => x.ToDisplayString()).ToArray();

            types.Should().Contain("TempProject.Generated.Formatters.TempProject.MyGenericObjectFormatter<T1, T2>");

            var formatters = symbols.SelectMany(x => x.Interfaces).Select(x => x.ToDisplayString()).ToArray();

            formatters.Should().Contain("MessagePack.Formatters.IMessagePackFormatter<TempProject.MyGenericObject<T1, T2>>");

            compilation.GetResolverKnownFormatterTypes().Should().Contain(new[]
            {
                "TempProject.Generated.Formatters.TempProject.MyGenericObjectFormatter<global::TempProject.MyGenericObject<int, string>, global::TempProject.MyGenericObject<int, string>>",
                "TempProject.Generated.Formatters.TempProject.MyGenericObjectFormatter<int, string>",
                "TempProject.Generated.Formatters.TempProject.MyObjectFormatter",
                "TempProject.Generated.Formatters.TempProject.MyObjectNestedFormatter",
            });
        }
        public async Task Generics_Constraints_Multiple(bool isSingleFileOutput)
        {
            using var tempWorkarea = TemporaryProjectWorkarea.Create();
            var contents = @"
using System;
using System.Collections.Generic;
using MessagePack;

namespace TempProject
{
    [MessagePackObject]
    public class MyGenericObject<T1, T2, T3, T4>
        where T1 : struct
        where T2 : IDisposable, new()
        where T3 : notnull
        where T4 : unmanaged
    {
        [Key(0)]
        public T1 Content1 { get; set; }
        [Key(1)]
        public T2 Content2 { get; set; }
        [Key(2)]
        public T3 Content3 { get; set; }
        [Key(3)]
        public T4 Content4 { get; set; }
    }
}
                ";

            tempWorkarea.AddFileToProject("MyMessagePackObject.cs", contents);

            var compiler = new MessagePackCompiler.CodeGenerator(testOutputHelper.WriteLine, CancellationToken.None);
            await compiler.GenerateFileAsync(
                tempWorkarea.GetOutputCompilation().Compilation,
                isSingleFileOutput?Path.Combine(tempWorkarea.OutputDirectory, "Generated.cs") : tempWorkarea.OutputDirectory,
                "TempProjectResolver",
                "TempProject.Generated",
                false,
                string.Empty,
                Array.Empty <string>());

            var compilation = tempWorkarea.GetOutputCompilation();

            compilation.Compilation.GetDiagnostics().Where(x => x.WarningLevel == 0).Should().BeEmpty();

            var symbols = compilation.GetNamedTypeSymbolsFromGenerated();

            var formatterType = symbols.FirstOrDefault(x => x.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) == "global::TempProject.Generated.Formatters.TempProject.MyGenericObjectFormatter<T1, T2, T3, T4>");

            formatterType.Should().NotBeNull();
            // struct
            formatterType.TypeParameters[0].HasReferenceTypeConstraint.Should().BeFalse();
            formatterType.TypeParameters[0].HasValueTypeConstraint.Should().BeTrue();
            formatterType.TypeParameters[0].HasConstructorConstraint.Should().BeFalse();
            formatterType.TypeParameters[0].HasNotNullConstraint.Should().BeFalse();
            formatterType.TypeParameters[0].HasUnmanagedTypeConstraint.Should().BeFalse();
            formatterType.TypeParameters[0].ConstraintTypes.Should().BeEmpty();
            // IDisposable, new()
            formatterType.TypeParameters[1].HasReferenceTypeConstraint.Should().BeFalse();
            formatterType.TypeParameters[1].HasValueTypeConstraint.Should().BeFalse();
            formatterType.TypeParameters[1].HasConstructorConstraint.Should().BeTrue();
            formatterType.TypeParameters[1].HasNotNullConstraint.Should().BeFalse();
            formatterType.TypeParameters[1].HasUnmanagedTypeConstraint.Should().BeFalse();
            formatterType.TypeParameters[1].ConstraintTypes.Should().Contain(x => x.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) == "global::System.IDisposable");
            // notnull
            formatterType.TypeParameters[2].HasReferenceTypeConstraint.Should().BeFalse();
            formatterType.TypeParameters[2].HasValueTypeConstraint.Should().BeFalse();
            formatterType.TypeParameters[2].HasConstructorConstraint.Should().BeFalse();
            formatterType.TypeParameters[2].HasNotNullConstraint.Should().BeTrue();
            formatterType.TypeParameters[2].HasUnmanagedTypeConstraint.Should().BeFalse();
            formatterType.TypeParameters[2].ConstraintTypes.Should().BeEmpty();
            // unmanaged
            formatterType.TypeParameters[3].HasReferenceTypeConstraint.Should().BeFalse();
            formatterType.TypeParameters[3].HasValueTypeConstraint.Should().BeTrue(); // unmanaged constraint includes value-type constraint
            formatterType.TypeParameters[3].HasConstructorConstraint.Should().BeFalse();
            formatterType.TypeParameters[3].HasNotNullConstraint.Should().BeFalse();
            formatterType.TypeParameters[3].HasUnmanagedTypeConstraint.Should().BeTrue();
            formatterType.TypeParameters[3].ConstraintTypes.Should().BeEmpty();
        }
        public async Task Generics_Constraints_NullableReferenceType(bool isSingleFileOutput)
        {
            using var tempWorkarea = TemporaryProjectWorkarea.Create();
            var contents = @"
using System;
using System.Collections.Generic;
using MessagePack;

namespace TempProject
{
    [MessagePackObject]
    public class MyGenericObject<T1, T2, T3, T4>
        where T1 : MyClass?
        where T2 : MyClass
        where T3 : MyGenericClass<MyGenericClass<MyClass?>?>?
        where T4 : MyClass, IMyInterface?
    {
        [Key(0)]
        public T1 Content { get; set; }
    }

    public class MyClass {}
    public class MyGenericClass<T> {}
    public interface IMyInterface {}
}
                ";

            tempWorkarea.AddFileToProject("MyMessagePackObject.cs", contents);

            var compiler = new MessagePackCompiler.CodeGenerator(testOutputHelper.WriteLine, CancellationToken.None);
            await compiler.GenerateFileAsync(
                tempWorkarea.GetOutputCompilation().Compilation,
                isSingleFileOutput?Path.Combine(tempWorkarea.OutputDirectory, "Generated.cs") : tempWorkarea.OutputDirectory,
                "TempProjectResolver",
                "TempProject.Generated",
                false,
                string.Empty,
                Array.Empty <string>());

            var compilation = tempWorkarea.GetOutputCompilation();

            compilation.Compilation.GetDiagnostics().Where(x => x.WarningLevel == 0).Should().BeEmpty();

            var symbols = compilation.GetNamedTypeSymbolsFromGenerated();

            var displayFormat = SymbolDisplayFormat.FullyQualifiedFormat.WithMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier);
            var formatterType = symbols.FirstOrDefault(x => x.ToDisplayString(displayFormat) == "global::TempProject.Generated.Formatters.TempProject.MyGenericObjectFormatter<T1, T2, T3, T4>");

            formatterType.Should().NotBeNull();
            // MyClass?
            formatterType.TypeParameters[0].ConstraintTypes.Should().Contain(x => x.ToDisplayString(displayFormat) == "global::TempProject.MyClass?");
            formatterType.TypeParameters[0].ConstraintNullableAnnotations[0].Should().Be(NullableAnnotation.Annotated);
            // MyClass
            formatterType.TypeParameters[1].ConstraintTypes.Should().Contain(x => x.ToDisplayString(displayFormat) == "global::TempProject.MyClass");
            formatterType.TypeParameters[1].ConstraintNullableAnnotations[0].Should().Be(NullableAnnotation.None);
            // MyGenericClass<MyGenericClass<MyClass?>?>?
            formatterType.TypeParameters[2].ConstraintTypes.Should().Contain(x => x.ToDisplayString(displayFormat) == "global::TempProject.MyGenericClass<global::TempProject.MyGenericClass<global::TempProject.MyClass?>?>?");
            formatterType.TypeParameters[2].ConstraintNullableAnnotations[0].Should().Be(NullableAnnotation.Annotated);
            // MyClass, IMyInterface?
            formatterType.TypeParameters[3].ConstraintTypes.Should().Contain(x => x.ToDisplayString(displayFormat) == "global::TempProject.MyClass");
            formatterType.TypeParameters[3].ConstraintTypes.Should().Contain(x => x.ToDisplayString(displayFormat) == "global::TempProject.IMyInterface?");
            formatterType.TypeParameters[3].ConstraintNullableAnnotations[0].Should().Be(NullableAnnotation.None);
            formatterType.TypeParameters[3].ConstraintNullableAnnotations[1].Should().Be(NullableAnnotation.Annotated);
        }
        public async Task GenericsOfTFormatter_WithKnownTypes(bool isSingleFileOutput)
        {
            using var tempWorkarea = TemporaryProjectWorkarea.Create(false);
            var contents = @"
using System;
using System.Collections.Generic;
using MessagePack;

namespace TempProject
{
    [MessagePackObject]
    public class MyObject : MyGenericObject<MyObject2>
    {
    }

    [MessagePackObject]
    public class MyObject2
    { }

    [MessagePackObject]
    public class MyGenericObject<T>
    {
        [Key(0)]
        public List<T> Content1 { get; set; }
        [Key(1)]
        public T[] Content2 { get; set; }
    }
}
            ";

            tempWorkarea.AddFileToProject("MyMessagePackObject.cs", contents);

            var compiler = new MessagePackCompiler.CodeGenerator(testOutputHelper.WriteLine, CancellationToken.None);
            await compiler.GenerateFileAsync(
                tempWorkarea.GetOutputCompilation().Compilation,
                isSingleFileOutput?Path.Combine(tempWorkarea.OutputDirectory, "Generated.cs") : tempWorkarea.OutputDirectory,
                "TempProjectResolver",
                "TempProject.Generated",
                false,
                string.Empty,
                Array.Empty <string>());

            var compilation = tempWorkarea.GetOutputCompilation();

            compilation.Compilation.GetDiagnostics().Where(x => x.WarningLevel == 0).Should().BeEmpty();

            var symbols    = compilation.GetNamedTypeSymbolsFromGenerated();
            var formatters = symbols.SelectMany(x => x.Interfaces).Select(x => x.ToDisplayString()).ToArray();

            formatters.Should().Contain(new[]
            {
                "MessagePack.Formatters.IMessagePackFormatter<TempProject.MyObject>",
                "MessagePack.Formatters.IMessagePackFormatter<TempProject.MyObject2>",
                "MessagePack.Formatters.IMessagePackFormatter<TempProject.MyGenericObject<T>>",
            });

            compilation.GetResolverKnownFormatterTypes().Should().Contain(new[]
            {
                "global::MessagePack.Formatters.ListFormatter<global::TempProject.MyObject2>",
                "global::MessagePack.Formatters.ArrayFormatter<global::TempProject.MyObject2>",
                // "TempProject.Generated.Formatters.TempProject.MyGenericObjectFormatter<string>", // MyGenericObjectFormatter<T> is not used as a property/field in the code. The generated resolver can ignore it.
                "TempProject.Generated.Formatters.TempProject.MyObjectFormatter",
                "TempProject.Generated.Formatters.TempProject.MyObject2Formatter",
            });
        }