File: ConvertAnonymousType\ConvertAnonymousTypeToClassTests.cs
Web Access
Project: src\src\Features\CSharpTest\Microsoft.CodeAnalysis.CSharp.Features.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Features.UnitTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.ConvertAnonymousType;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeActions;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertAnonymousType;
 
[Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToClass)]
public class ConvertAnonymousTypeToClassTests : AbstractCSharpCodeActionTest_NoEditor
{
    private static readonly ParseOptions CSharp8 = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp8);
 
    protected override CodeRefactoringProvider CreateCodeRefactoringProvider(TestWorkspace workspace, TestParameters parameters)
        => new CSharpConvertAnonymousTypeToClassCodeRefactoringProvider();
 
    [Fact]
    public async Task ConvertSingleAnonymousType()
    {
        var text = """
            class Test
            {
                void Method()
                {
                    var t1 = [||]new { a = 1, b = 2 };
                }
            }
            """;
        var expected = """
            class Test
            {
                void Method()
                {
                    var t1 = new {|Rename:NewClass|}(1, 2);
                }
            }
 
            internal class NewClass
            {
                public int A { get; }
                public int B { get; }
 
                public NewClass(int a, int b)
                {
                    A = a;
                    B = b;
                }
 
                public override bool Equals(object obj)
                {
                    return obj is NewClass other &&
                           A == other.A &&
                           B == other.B;
                }
 
                public override int GetHashCode()
                {
                    var hashCode = -1817952719;
                    hashCode = hashCode * -1521134295 + A.GetHashCode();
                    hashCode = hashCode * -1521134295 + B.GetHashCode();
                    return hashCode;
                }
            }
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo(), parseOptions: CSharp8);
    }
 
    [Fact]
    public async Task ConvertSingleAnonymousType_FileScopedNamespace()
    {
        var text = """
            namespace N;
 
            class Test
            {
                void Method()
                {
                    var t1 = [||]new { a = 1, b = 2 };
                }
            }
            """;
        var expected = """
            namespace N;
 
            class Test
            {
                void Method()
                {
                    var t1 = new {|Rename:NewClass|}(1, 2);
                }
            }
 
            internal class NewClass
            {
                public int A { get; }
                public int B { get; }
 
                public NewClass(int a, int b)
                {
                    A = a;
                    B = b;
                }
 
                public override bool Equals(object obj)
                {
                    return obj is NewClass other &&
                           A == other.A &&
                           B == other.B;
                }
 
                public override int GetHashCode()
                {
                    var hashCode = -1817952719;
                    hashCode = hashCode * -1521134295 + A.GetHashCode();
                    hashCode = hashCode * -1521134295 + B.GetHashCode();
                    return hashCode;
                }
            }
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo(), parseOptions: CSharp8);
    }
 
    [Fact]
    public async Task ConvertSingleAnonymousType_CSharp9()
    {
        var text = """
            class Test
            {
                void Method()
                {
                    var t1 = [||]new { a = 1, b = 2 };
                }
            }
            """;
        var expected = """
            class Test
            {
                void Method()
                {
                    var t1 = new {|Rename:NewRecord|}(1, 2);
                }
            }
 
            internal record NewRecord(int A, int B);
 
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo());
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/39916")]
    public async Task ConvertSingleAnonymousType_Explicit()
    {
        var text = """
            class Test
            {
                void Method()
                {
                    var t1 = [||]new { a = 1, b = 2 };
                }
            }
            """;
        var expected = """
            class Test
            {
                void Method()
                {
                    var t1 = new {|Rename:NewClass|}(1, 2);
                }
            }
 
            internal class NewClass
            {
                public int A { get; }
                public int B { get; }
 
                public NewClass(int a, int b)
                {
                    A = a;
                    B = b;
                }
 
                public override bool Equals(object obj)
                {
                    return obj is NewClass other &&
                           A == other.A &&
                           B == other.B;
                }
 
                public override int GetHashCode()
                {
                    int hashCode = -1817952719;
                    hashCode = hashCode * -1521134295 + A.GetHashCode();
                    hashCode = hashCode * -1521134295 + B.GetHashCode();
                    return hashCode;
                }
            }
            """;
        await TestInRegularAndScriptAsync(text, expected, parseOptions: CSharp8);
    }
 
    [Fact]
    public async Task OnEmptyAnonymousType()
    {
        await TestInRegularAndScriptAsync("""
            class Test
            {
                void Method()
                {
                    var t1 = [||]new { };
                }
            }
            """,
            """
            class Test
            {
                void Method()
                {
                    var t1 = new {|Rename:NewClass|}();
                }
            }
 
            internal class NewClass
            {
                public NewClass()
                {
                }
 
                public override bool Equals(object obj)
                {
                    return obj is NewClass other;
                }
 
                public override int GetHashCode()
                {
                    return 0;
                }
            }
            """, parseOptions: CSharp8);
    }
 
    [Fact]
    public async Task OnEmptyAnonymousType_CSharp9()
    {
        await TestInRegularAndScriptAsync("""
            class Test
            {
                void Method()
                {
                    var t1 = [||]new { };
                }
            }
            """,
            """
            class Test
            {
                void Method()
                {
                    var t1 = new {|Rename:NewRecord|}();
                }
            }
 
            internal record NewRecord();
 
            """);
    }
 
    [Fact]
    public async Task OnSingleFieldAnonymousType()
    {
        await TestInRegularAndScriptAsync("""
            class Test
            {
                void Method()
                {
                    var t1 = [||]new { a = 1 };
                }
            }
            """,
            """
            class Test
            {
                void Method()
                {
                    var t1 = new {|Rename:NewClass|}(1);
                }
            }
 
            internal class NewClass
            {
                public int A { get; }
 
                public NewClass(int a)
                {
                    A = a;
                }
 
                public override bool Equals(object obj)
                {
                    return obj is NewClass other &&
                           A == other.A;
                }
 
                public override int GetHashCode()
                {
                    return -862436692 + A.GetHashCode();
                }
            }
            """, parseOptions: CSharp8);
    }
 
    [Fact]
    public async Task OnSingleFieldAnonymousType_CSharp9()
    {
        await TestInRegularAndScriptAsync("""
            class Test
            {
                void Method()
                {
                    var t1 = [||]new { a = 1 };
                }
            }
            """,
            """
            class Test
            {
                void Method()
                {
                    var t1 = new {|Rename:NewRecord|}(1);
                }
            }
 
            internal record NewRecord(int A);
 
            """);
    }
 
    [Fact]
    public async Task ConvertSingleAnonymousTypeWithInferredName()
    {
        var text = """
            class Test
            {
                void Method(int b)
                {
                    var t1 = [||]new { a = 1, b };
                }
            }
            """;
        var expected = """
            class Test
            {
                void Method(int b)
                {
                    var t1 = new {|Rename:NewClass|}(1, b);
                }
            }
 
            internal class NewClass
            {
                public int A { get; }
                public int B { get; }
 
                public NewClass(int a, int b)
                {
                    A = a;
                    B = b;
                }
 
                public override bool Equals(object obj)
                {
                    return obj is NewClass other &&
                           A == other.A &&
                           B == other.B;
                }
 
                public override int GetHashCode()
                {
                    var hashCode = -1817952719;
                    hashCode = hashCode * -1521134295 + A.GetHashCode();
                    hashCode = hashCode * -1521134295 + B.GetHashCode();
                    return hashCode;
                }
            }
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo(), parseOptions: CSharp8);
    }
 
    [Fact]
    public async Task ConvertSingleAnonymousTypeWithInferredName_CSharp9()
    {
        var text = """
            class Test
            {
                void Method(int b)
                {
                    var t1 = [||]new { a = 1, b };
                }
            }
            """;
        var expected = """
            class Test
            {
                void Method(int b)
                {
                    var t1 = new {|Rename:NewRecord|}(1, b);
                }
            }
 
            internal record NewRecord(int A, int B);
 
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo());
    }
 
    [Fact]
    public async Task ConvertMultipleInstancesInSameMethod()
    {
        var text = """
            class Test
            {
                void Method()
                {
                    var t1 = [||]new { a = 1, b = 2 };
                    var t2 = new { a = 3, b = 4 };
                }
            }
            """;
        var expected = """
            class Test
            {
                void Method()
                {
                    var t1 = new {|Rename:NewClass|}(1, 2);
                    var t2 = new NewClass(3, 4);
                }
            }
 
            internal class NewClass
            {
                public int A { get; }
                public int B { get; }
 
                public NewClass(int a, int b)
                {
                    A = a;
                    B = b;
                }
 
                public override bool Equals(object obj)
                {
                    return obj is NewClass other &&
                           A == other.A &&
                           B == other.B;
                }
 
                public override int GetHashCode()
                {
                    var hashCode = -1817952719;
                    hashCode = hashCode * -1521134295 + A.GetHashCode();
                    hashCode = hashCode * -1521134295 + B.GetHashCode();
                    return hashCode;
                }
            }
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo(), parseOptions: CSharp8);
    }
 
    [Fact]
    public async Task ConvertMultipleInstancesInSameMethod_CSharp9()
    {
        var text = """
            class Test
            {
                void Method()
                {
                    var t1 = [||]new { a = 1, b = 2 };
                    var t2 = new { a = 3, b = 4 };
                }
            }
            """;
        var expected = """
            class Test
            {
                void Method()
                {
                    var t1 = new {|Rename:NewRecord|}(1, 2);
                    var t2 = new NewRecord(3, 4);
                }
            }
 
            internal record NewRecord(int A, int B);
 
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo());
    }
 
    [Fact]
    public async Task ConvertMultipleInstancesAcrossMethods()
    {
        var text = """
            class Test
            {
                void Method()
                {
                    var t1 = [||]new { a = 1, b = 2 };
                    var t2 = new { a = 3, b = 4 };
                }
 
                void Method2()
                {
                    var t1 = new { a = 1, b = 2 };
                    var t2 = new { a = 3, b = 4 };
                }
            }
            """;
        var expected = """
            class Test
            {
                void Method()
                {
                    var t1 = new {|Rename:NewClass|}(1, 2);
                    var t2 = new NewClass(3, 4);
                }
 
                void Method2()
                {
                    var t1 = new { a = 1, b = 2 };
                    var t2 = new { a = 3, b = 4 };
                }
            }
 
            internal class NewClass
            {
                public int A { get; }
                public int B { get; }
 
                public NewClass(int a, int b)
                {
                    A = a;
                    B = b;
                }
 
                public override bool Equals(object obj)
                {
                    return obj is NewClass other &&
                           A == other.A &&
                           B == other.B;
                }
 
                public override int GetHashCode()
                {
                    var hashCode = -1817952719;
                    hashCode = hashCode * -1521134295 + A.GetHashCode();
                    hashCode = hashCode * -1521134295 + B.GetHashCode();
                    return hashCode;
                }
            }
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo(), parseOptions: CSharp8);
    }
 
    [Fact]
    public async Task OnlyConvertMatchingTypesInSameMethod()
    {
        var text = """
            class Test
            {
                void Method(int b)
                {
                    var t1 = [||]new { a = 1, b = 2 };
                    var t2 = new { a = 3, b };
                    var t3 = new { a = 4 };
                    var t4 = new { b = 5, a = 6 };
                }
            }
            """;
        var expected = """
            class Test
            {
                void Method(int b)
                {
                    var t1 = new {|Rename:NewClass|}(1, 2);
                    var t2 = new NewClass(3, b);
                    var t3 = new { a = 4 };
                    var t4 = new { b = 5, a = 6 };
                }
            }
 
            internal class NewClass
            {
                public int A { get; }
                public int B { get; }
 
                public NewClass(int a, int b)
                {
                    A = a;
                    B = b;
                }
 
                public override bool Equals(object obj)
                {
                    return obj is NewClass other &&
                           A == other.A &&
                           B == other.B;
                }
 
                public override int GetHashCode()
                {
                    var hashCode = -1817952719;
                    hashCode = hashCode * -1521134295 + A.GetHashCode();
                    hashCode = hashCode * -1521134295 + B.GetHashCode();
                    return hashCode;
                }
            }
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo(), parseOptions: CSharp8);
    }
 
    [Fact]
    public async Task TestFixAllMatchesInSingleMethod()
    {
        var text = """
            class Test
            {
                void Method(int b)
                {
                    var t1 = [||]new { a = 1, b = 2 };
                    var t2 = new { a = 3, b };
                    var t3 = new { a = 4 };
                    var t4 = new { b = 5, a = 6 };
                }
            }
            """;
        var expected = """
            class Test
            {
                void Method(int b)
                {
                    var t1 = new {|Rename:NewClass|}(1, 2);
                    var t2 = new NewClass(3, b);
                    var t3 = new { a = 4 };
                    var t4 = new { b = 5, a = 6 };
                }
            }
 
            internal class NewClass
            {
                public int A { get; }
                public int B { get; }
 
                public NewClass(int a, int b)
                {
                    A = a;
                    B = b;
                }
 
                public override bool Equals(object obj)
                {
                    return obj is NewClass other &&
                           A == other.A &&
                           B == other.B;
                }
 
                public override int GetHashCode()
                {
                    var hashCode = -1817952719;
                    hashCode = hashCode * -1521134295 + A.GetHashCode();
                    hashCode = hashCode * -1521134295 + B.GetHashCode();
                    return hashCode;
                }
            }
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo(), parseOptions: CSharp8);
    }
 
    [Fact]
    public async Task TestFixNotAcrossMethods()
    {
        var text = """
            class Test
            {
                void Method()
                {
                    var t1 = [||]new { a = 1, b = 2 };
                    var t2 = new { a = 3, b = 4 };
                }
 
                void Method2()
                {
                    var t1 = new { a = 1, b = 2 };
                    var t2 = new { a = 3, b = 4 };
                }
            }
            """;
        var expected = """
            class Test
            {
                void Method()
                {
                    var t1 = new {|Rename:NewClass|}(1, 2);
                    var t2 = new NewClass(3, 4);
                }
 
                void Method2()
                {
                    var t1 = new { a = 1, b = 2 };
                    var t2 = new { a = 3, b = 4 };
                }
            }
 
            internal class NewClass
            {
                public int A { get; }
                public int B { get; }
 
                public NewClass(int a, int b)
                {
                    A = a;
                    B = b;
                }
 
                public override bool Equals(object obj)
                {
                    return obj is NewClass other &&
                           A == other.A &&
                           B == other.B;
                }
 
                public override int GetHashCode()
                {
                    var hashCode = -1817952719;
                    hashCode = hashCode * -1521134295 + A.GetHashCode();
                    hashCode = hashCode * -1521134295 + B.GetHashCode();
                    return hashCode;
                }
            }
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo(), parseOptions: CSharp8);
    }
 
    [Fact]
    public async Task TestTrivia()
    {
        var text = """
            class Test
            {
                void Method()
                {
                    var t1 = /*1*/ [||]new /*2*/ { /*3*/ a /*4*/ = /*5*/ 1 /*7*/ , /*8*/ b /*9*/ = /*10*/ 2 /*11*/ } /*12*/ ;
                }
            }
            """;
        var expected = """
            class Test
            {
                void Method()
                {
                    var t1 = /*1*/ new {|Rename:NewClass|}( /*3*/ 1 /*7*/ , /*8*/ 2 /*11*/ ) /*12*/ ;
                }
            }
 
            internal class NewClass
            {
                public int A { get; }
                public int B { get; }
 
                public NewClass(int a, int b)
                {
                    A = a;
                    B = b;
                }
 
                public override bool Equals(object obj)
                {
                    return obj is NewClass other &&
                           A == other.A &&
                           B == other.B;
                }
 
                public override int GetHashCode()
                {
                    var hashCode = -1817952719;
                    hashCode = hashCode * -1521134295 + A.GetHashCode();
                    hashCode = hashCode * -1521134295 + B.GetHashCode();
                    return hashCode;
                }
            }
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo(), parseOptions: CSharp8);
    }
 
    [Fact]
    public async Task TestTrivia2()
    {
        var text = """
            class Test
            {
                void Method()
                {
                    var t1 = /*1*/ [||]new /*2*/ { /*3*/ a /*4*/ = /*5*/ 1 /*7*/ , /*8*/ b /*9*/ = /*10*/ 2 /*11*/ } /*12*/ ;
                    var t2 = /*1*/ new /*2*/ { /*3*/ a /*4*/ = /*5*/ 1 /*7*/ , /*8*/ b /*9*/ = /*10*/ 2 /*11*/ } /*12*/ ;
                }
            }
            """;
        var expected = """
            class Test
            {
                void Method()
                {
                    var t1 = /*1*/ new {|Rename:NewClass|}( /*3*/ 1 /*7*/ , /*8*/ 2 /*11*/ ) /*12*/ ;
                    var t2 = /*1*/ new NewClass( /*3*/ 1 /*7*/ , /*8*/ 2 /*11*/ ) /*12*/ ;
                }
            }
 
            internal class NewClass
            {
                public int A { get; }
                public int B { get; }
 
                public NewClass(int a, int b)
                {
                    A = a;
                    B = b;
                }
 
                public override bool Equals(object obj)
                {
                    return obj is NewClass other &&
                           A == other.A &&
                           B == other.B;
                }
 
                public override int GetHashCode()
                {
                    var hashCode = -1817952719;
                    hashCode = hashCode * -1521134295 + A.GetHashCode();
                    hashCode = hashCode * -1521134295 + B.GetHashCode();
                    return hashCode;
                }
            }
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo(), parseOptions: CSharp8);
    }
 
    [Fact]
    public async Task NotIfReferencesAnonymousTypeInternally()
    {
        var text = """
            class Test
            {
                void Method()
                {
                    var t1 = [||]new { a = 1, b = new { c = 1, d = 2 } };
                }
            }
            """;
 
        await TestMissingInRegularAndScriptAsync(text);
    }
 
    [Fact]
    public async Task ConvertMultipleNestedInstancesInSameMethod()
    {
        var text = """
            class Test
            {
                void Method()
                {
                    var t1 = [||]new { a = 1, b = (object)new { a = 1, b = default(object) } };
                }
            }
            """;
        var expected = """
            using System.Collections.Generic;
 
            class Test
            {
                void Method()
                {
                    var t1 = new {|Rename:NewClass|}(1, (object)new NewClass(1, default(object)));
                }
            }
 
            internal class NewClass
            {
                public int A { get; }
                public object B { get; }
 
                public NewClass(int a, object b)
                {
                    A = a;
                    B = b;
                }
 
                public override bool Equals(object obj)
                {
                    return obj is NewClass other &&
                           A == other.A &&
                           EqualityComparer<object>.Default.Equals(B, other.B);
                }
 
                public override int GetHashCode()
                {
                    var hashCode = -1817952719;
                    hashCode = hashCode * -1521134295 + A.GetHashCode();
                    hashCode = hashCode * -1521134295 + EqualityComparer<object>.Default.GetHashCode(B);
                    return hashCode;
                }
            }
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo(), parseOptions: CSharp8);
    }
 
    [Fact]
    public async Task RenameAnnotationOnStartingPoint()
    {
        var text = """
            class Test
            {
                void Method()
                {
                    var t1 = new { a = 1, b = 2 };
                    var t2 = [||]new { a = 3, b = 4 };
                }
            }
            """;
        var expected = """
            class Test
            {
                void Method()
                {
                    var t1 = new NewClass(1, 2);
                    var t2 = new {|Rename:NewClass|}(3, 4);
                }
            }
 
            internal class NewClass
            {
                public int A { get; }
                public int B { get; }
 
                public NewClass(int a, int b)
                {
                    A = a;
                    B = b;
                }
 
                public override bool Equals(object obj)
                {
                    return obj is NewClass other &&
                           A == other.A &&
                           B == other.B;
                }
 
                public override int GetHashCode()
                {
                    var hashCode = -1817952719;
                    hashCode = hashCode * -1521134295 + A.GetHashCode();
                    hashCode = hashCode * -1521134295 + B.GetHashCode();
                    return hashCode;
                }
            }
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo(), parseOptions: CSharp8);
    }
 
    [Fact]
    public async Task UpdateReferences()
    {
        var text = """
            class Test
            {
                void Method()
                {
                    var t1 = [||]new { a = 1, b = 2 };
                    Console.WriteLine(t1.a + t1?.b);
                }
            }
            """;
        var expected = """
            class Test
            {
                void Method()
                {
                    var t1 = new {|Rename:NewClass|}(1, 2);
                    Console.WriteLine(t1.A + t1?.B);
                }
            }
 
            internal class NewClass
            {
                public int A { get; }
                public int B { get; }
 
                public NewClass(int a, int b)
                {
                    A = a;
                    B = b;
                }
 
                public override bool Equals(object obj)
                {
                    return obj is NewClass other &&
                           A == other.A &&
                           B == other.B;
                }
 
                public override int GetHashCode()
                {
                    var hashCode = -1817952719;
                    hashCode = hashCode * -1521134295 + A.GetHashCode();
                    hashCode = hashCode * -1521134295 + B.GetHashCode();
                    return hashCode;
                }
            }
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo(), parseOptions: CSharp8);
    }
 
    [Fact]
    public async Task CapturedTypeParameters()
    {
        var text = """
            class Test<X> where X : struct
            {
                void Method<Y>(List<X> x, Y[] y) where Y : class, new()
                {
                    var t1 = [||]new { a = x, b = y };
                }
            }
            """;
        var expected = """
            class Test<X> where X : struct
            {
                void Method<Y>(List<X> x, Y[] y) where Y : class, new()
                {
                    var t1 = new {|Rename:NewClass|}<X, Y>(x, y);
                }
            }
 
            internal class NewClass<X, Y>
                where X : struct
                where Y : class, new()
            {
                public List<X> A { get; }
                public Y[] B { get; }
 
                public NewClass(List<X> a, Y[] b)
                {
                    A = a;
                    B = b;
                }
 
                public override bool Equals(object obj)
                {
                    return obj is NewClass<X, Y> other &&
                           System.Collections.Generic.EqualityComparer<List<X>>.Default.Equals(A, other.A) &&
                           System.Collections.Generic.EqualityComparer<Y[]>.Default.Equals(B, other.B);
                }
 
                public override int GetHashCode()
                {
                    var hashCode = -1817952719;
                    hashCode = hashCode * -1521134295 + System.Collections.Generic.EqualityComparer<List<X>>.Default.GetHashCode(A);
                    hashCode = hashCode * -1521134295 + System.Collections.Generic.EqualityComparer<Y[]>.Default.GetHashCode(B);
                    return hashCode;
                }
            }
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo(), parseOptions: CSharp8);
    }
 
    [Fact]
    public async Task CapturedTypeParameters_CSharp9()
    {
        var text = """
            class Test<X> where X : struct
            {
                void Method<Y>(List<X> x, Y[] y) where Y : class, new()
                {
                    var t1 = [||]new { a = x, b = y };
                }
            }
            """;
        var expected = """
            class Test<X> where X : struct
            {
                void Method<Y>(List<X> x, Y[] y) where Y : class, new()
                {
                    var t1 = new {|Rename:NewRecord|}<X, Y>(x, y);
                }
            }
 
            internal record NewRecord<X, Y>(List<X> A, Y[] B)
                where X : struct
                where Y : class, new();
 
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo());
    }
 
    [Fact]
    public async Task NewTypeNameCollision()
    {
        var text = """
            class Test
            {
                void Method()
                {
                    var t1 = [||]new { a = 1, b = 2 };
                }
            }
 
            class NewClass
            {
            }
            """;
        var expected = """
            class Test
            {
                void Method()
                {
                    var t1 = new {|Rename:NewClass1|}(1, 2);
                }
            }
 
            class NewClass
            {
            }
 
            internal class NewClass1
            {
                public int A { get; }
                public int B { get; }
 
                public NewClass1(int a, int b)
                {
                    A = a;
                    B = b;
                }
 
                public override bool Equals(object obj)
                {
                    return obj is NewClass1 other &&
                           A == other.A &&
                           B == other.B;
                }
 
                public override int GetHashCode()
                {
                    var hashCode = -1817952719;
                    hashCode = hashCode * -1521134295 + A.GetHashCode();
                    hashCode = hashCode * -1521134295 + B.GetHashCode();
                    return hashCode;
                }
            }
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo(), parseOptions: CSharp8);
    }
 
    [Fact]
    public async Task TestDuplicatedName()
    {
        var text = """
            class Test
            {
                void Method()
                {
                    var t1 = [||]new { a = 1, a = 2 };
                }
            }
            """;
        var expected = """
            class Test
            {
                void Method()
                {
                    var t1 = new {|Rename:NewClass|}(1, 2);
                }
            }
 
            internal class NewClass
            {
                public int A { get; }
                public int Item { get; }
 
                public NewClass(int a, int item)
                {
                    A = a;
                    Item = item;
                }
 
                public override bool Equals(object obj)
                {
                    return obj is NewClass other &&
                           A == other.A &&
                           Item == other.Item;
                }
 
                public override int GetHashCode()
                {
                    var hashCode = -335756622;
                    hashCode = hashCode * -1521134295 + A.GetHashCode();
                    hashCode = hashCode * -1521134295 + Item.GetHashCode();
                    return hashCode;
                }
            }
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo(), parseOptions: CSharp8);
    }
 
    [Fact]
    public async Task TestDuplicatedName_CSharp9()
    {
        var text = """
            class Test
            {
                void Method()
                {
                    var t1 = [||]new { a = 1, a = 2 };
                }
            }
            """;
        var expected = """
            class Test
            {
                void Method()
                {
                    var t1 = new {|Rename:NewRecord|}(1, 2);
                }
            }
 
            internal record NewRecord(int A, int Item);
 
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo());
    }
 
    [Fact]
    public async Task TestNewSelection()
    {
        var text = """
            class Test
            {
                void Method()
                {
                    var t1 = [|new|] { a = 1, b = 2 };
                }
            }
            """;
        var expected = """
            class Test
            {
                void Method()
                {
                    var t1 = new {|Rename:NewClass|}(1, 2);
                }
            }
 
            internal class NewClass
            {
                public int A { get; }
                public int B { get; }
 
                public NewClass(int a, int b)
                {
                    A = a;
                    B = b;
                }
 
                public override bool Equals(object obj)
                {
                    return obj is NewClass other &&
                           A == other.A &&
                           B == other.B;
                }
 
                public override int GetHashCode()
                {
                    var hashCode = -1817952719;
                    hashCode = hashCode * -1521134295 + A.GetHashCode();
                    hashCode = hashCode * -1521134295 + B.GetHashCode();
                    return hashCode;
                }
            }
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo(), parseOptions: CSharp8);
    }
 
    [Fact]
    public async Task TestInLambda1()
    {
        var text = """
            using System;
 
            class Test
            {
                void Method()
                {
                    var t1 = [||]new { a = 1, b = 2 };
                    Action a = () =>
                    {
                        var t2 = new { a = 3, b = 4 };
                    };
                }
            }
            """;
        var expected = """
            using System;
 
            class Test
            {
                void Method()
                {
                    var t1 = new {|Rename:NewClass|}(1, 2);
                    Action a = () =>
                    {
                        var t2 = new NewClass(3, 4);
                    };
                }
            }
 
            internal class NewClass
            {
                public int A { get; }
                public int B { get; }
 
                public NewClass(int a, int b)
                {
                    A = a;
                    B = b;
                }
 
                public override bool Equals(object obj)
                {
                    return obj is NewClass other &&
                           A == other.A &&
                           B == other.B;
                }
 
                public override int GetHashCode()
                {
                    var hashCode = -1817952719;
                    hashCode = hashCode * -1521134295 + A.GetHashCode();
                    hashCode = hashCode * -1521134295 + B.GetHashCode();
                    return hashCode;
                }
            }
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo(), parseOptions: CSharp8);
    }
 
    [Fact]
    public async Task TestInLambda2()
    {
        var text = """
            using System;
 
            class Test
            {
                void Method()
                {
                    var t1 = new { a = 1, b = 2 };
                    Action a = () =>
                    {
                        var t2 = [||]new { a = 3, b = 4 };
                    };
                }
            }
            """;
        var expected = """
            using System;
 
            class Test
            {
                void Method()
                {
                    var t1 = new NewClass(1, 2);
                    Action a = () =>
                    {
                        var t2 = new {|Rename:NewClass|}(3, 4);
                    };
                }
            }
 
            internal class NewClass
            {
                public int A { get; }
                public int B { get; }
 
                public NewClass(int a, int b)
                {
                    A = a;
                    B = b;
                }
 
                public override bool Equals(object obj)
                {
                    return obj is NewClass other &&
                           A == other.A &&
                           B == other.B;
                }
 
                public override int GetHashCode()
                {
                    var hashCode = -1817952719;
                    hashCode = hashCode * -1521134295 + A.GetHashCode();
                    hashCode = hashCode * -1521134295 + B.GetHashCode();
                    return hashCode;
                }
            }
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo(), parseOptions: CSharp8);
    }
 
    [Fact]
    public async Task TestInLocalFunction1()
    {
        var text = """
            using System;
 
            class Test
            {
                void Method()
                {
                    var t1 = [||]new { a = 1, b = 2 };
                    void Goo()
                    {
                        var t2 = new { a = 3, b = 4 };
                    }
                }
            }
            """;
        var expected = """
            using System;
 
            class Test
            {
                void Method()
                {
                    var t1 = new {|Rename:NewClass|}(1, 2);
                    void Goo()
                    {
                        var t2 = new NewClass(3, 4);
                    }
                }
            }
 
            internal class NewClass
            {
                public int A { get; }
                public int B { get; }
 
                public NewClass(int a, int b)
                {
                    A = a;
                    B = b;
                }
 
                public override bool Equals(object obj)
                {
                    return obj is NewClass other &&
                           A == other.A &&
                           B == other.B;
                }
 
                public override int GetHashCode()
                {
                    var hashCode = -1817952719;
                    hashCode = hashCode * -1521134295 + A.GetHashCode();
                    hashCode = hashCode * -1521134295 + B.GetHashCode();
                    return hashCode;
                }
            }
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo(), parseOptions: CSharp8);
    }
 
    [Fact]
    public async Task TestInLocalFunction2()
    {
        var text = """
            using System;
 
            class Test
            {
                void Method()
                {
                    var t1 = new { a = 1, b = 2 };
                    void Goo()
                    {
                        var t2 = [||]new { a = 3, b = 4 };
                    }
                }
            }
            """;
        var expected = """
            using System;
 
            class Test
            {
                void Method()
                {
                    var t1 = new NewClass(1, 2);
                    void Goo()
                    {
                        var t2 = new {|Rename:NewClass|}(3, 4);
                    }
                }
            }
 
            internal class NewClass
            {
                public int A { get; }
                public int B { get; }
 
                public NewClass(int a, int b)
                {
                    A = a;
                    B = b;
                }
 
                public override bool Equals(object obj)
                {
                    return obj is NewClass other &&
                           A == other.A &&
                           B == other.B;
                }
 
                public override int GetHashCode()
                {
                    var hashCode = -1817952719;
                    hashCode = hashCode * -1521134295 + A.GetHashCode();
                    hashCode = hashCode * -1521134295 + B.GetHashCode();
                    return hashCode;
                }
            }
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo(), parseOptions: CSharp8);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35180")]
    public async Task ConvertSingleAnonymousTypeSelection1()
    {
        var text = """
            class Test
            {
                void Method()
                {
                    var t1 = [|new { a = 1, b = 2 }|];
                }
            }
            """;
        var expected = """
            class Test
            {
                void Method()
                {
                    var t1 = new {|Rename:NewClass|}(1, 2);
                }
            }
 
            internal class NewClass
            {
                public int A { get; }
                public int B { get; }
 
                public NewClass(int a, int b)
                {
                    A = a;
                    B = b;
                }
 
                public override bool Equals(object obj)
                {
                    return obj is NewClass other &&
                           A == other.A &&
                           B == other.B;
                }
 
                public override int GetHashCode()
                {
                    var hashCode = -1817952719;
                    hashCode = hashCode * -1521134295 + A.GetHashCode();
                    hashCode = hashCode * -1521134295 + B.GetHashCode();
                    return hashCode;
                }
            }
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo(), parseOptions: CSharp8);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35180")]
    public async Task ConvertSingleAnonymousTypeSelection2()
    {
        var text = """
            class Test
            {
                void Method()
                {
                    [|var t1 = new { a = 1, b = 2 };|]
                }
            }
            """;
        var expected = """
            class Test
            {
                void Method()
                {
                    var t1 = new {|Rename:NewClass|}(1, 2);
                }
            }
 
            internal class NewClass
            {
                public int A { get; }
                public int B { get; }
 
                public NewClass(int a, int b)
                {
                    A = a;
                    B = b;
                }
 
                public override bool Equals(object obj)
                {
                    return obj is NewClass other &&
                           A == other.A &&
                           B == other.B;
                }
 
                public override int GetHashCode()
                {
                    var hashCode = -1817952719;
                    hashCode = hashCode * -1521134295 + A.GetHashCode();
                    hashCode = hashCode * -1521134295 + B.GetHashCode();
                    return hashCode;
                }
            }
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo(), parseOptions: CSharp8);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35180")]
    public async Task ConvertSingleAnonymousTypeSelection3()
    {
        var text = """
            class Test
            {
                void Method()
                {
                    var t1 = [|new { a = 1, b = 2 };|]
                }
            }
            """;
        var expected = """
            class Test
            {
                void Method()
                {
                    var t1 = new {|Rename:NewClass|}(1, 2);
                }
            }
 
            internal class NewClass
            {
                public int A { get; }
                public int B { get; }
 
                public NewClass(int a, int b)
                {
                    A = a;
                    B = b;
                }
 
                public override bool Equals(object obj)
                {
                    return obj is NewClass other &&
                           A == other.A &&
                           B == other.B;
                }
 
                public override int GetHashCode()
                {
                    var hashCode = -1817952719;
                    hashCode = hashCode * -1521134295 + A.GetHashCode();
                    hashCode = hashCode * -1521134295 + B.GetHashCode();
                    return hashCode;
                }
            }
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo(), parseOptions: CSharp8);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/45747")]
    public async Task ConvertOmittingTrailingComma()
    {
        var text = """
            class Test
            {
                void Method()
                {
                    var t1 = [||]new
                    {
                        a = 1,
                        b = 2,
                    };
                }
            }
            """;
        var expected = """
            class Test
            {
                void Method()
                {
                    var t1 = new {|Rename:NewClass|}(
            1,
            2
                    );
                }
            }
 
            internal class NewClass
            {
                public int A { get; }
                public int B { get; }
 
                public NewClass(int a, int b)
                {
                    A = a;
                    B = b;
                }
 
                public override bool Equals(object obj)
                {
                    return obj is NewClass other &&
                           A == other.A &&
                           B == other.B;
                }
 
                public override int GetHashCode()
                {
                    var hashCode = -1817952719;
                    hashCode = hashCode * -1521134295 + A.GetHashCode();
                    hashCode = hashCode * -1521134295 + B.GetHashCode();
                    return hashCode;
                }
            }
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo(), parseOptions: CSharp8);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/45747")]
    public async Task ConvertOmittingTrailingCommaButPreservingTrivia()
    {
        var text = """
            class Test
            {
                void Method()
                {
                    var t1 = [||]new
                    {
                        a = 1,
                        b = 2 // and
                              // more
                        ,
                    };
                }
            }
            """;
        var expected = """
            class Test
            {
                void Method()
                {
                    var t1 = new {|Rename:NewClass|}(
            1,
            2 // and
              // more
 
                    );
                }
            }
 
            internal class NewClass
            {
                public int A { get; }
                public int B { get; }
 
                public NewClass(int a, int b)
                {
                    A = a;
                    B = b;
                }
 
                public override bool Equals(object obj)
                {
                    return obj is NewClass other &&
                           A == other.A &&
                           B == other.B;
                }
 
                public override int GetHashCode()
                {
                    var hashCode = -1817952719;
                    hashCode = hashCode * -1521134295 + A.GetHashCode();
                    hashCode = hashCode * -1521134295 + B.GetHashCode();
                    return hashCode;
                }
            }
            """;
        await TestInRegularAndScriptAsync(text, expected, options: this.PreferImplicitTypeWithInfo(), parseOptions: CSharp8);
    }
}