// 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.CodeFixes;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.Diagnostics.TypeStyle;
using Microsoft.CodeAnalysis.CSharp.TypeStyle;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
using Xunit.Abstractions;

namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.UseImplicitType
{
    [Trait(Traits.Feature, Traits.Features.CodeActionsUseImplicitType)]
    public partial class UseImplicitTypeTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
    {
        public UseImplicitTypeTests(ITestOutputHelper? logger = null)
          : base(logger)
        {
        }

        internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace)
            => (new CSharpUseImplicitTypeDiagnosticAnalyzer(), new UseImplicitTypeCodeFixProvider());

        private static readonly CodeStyleOption2<bool> onWithSilent = new(true, NotificationOption2.Silent);
        private static readonly CodeStyleOption2<bool> onWithInfo = new(true, NotificationOption2.Suggestion);
        private static readonly CodeStyleOption2<bool> offWithInfo = new(false, NotificationOption2.Suggestion);
        private static readonly CodeStyleOption2<bool> onWithWarning = new(true, NotificationOption2.Warning);
        private static readonly CodeStyleOption2<bool> onWithError = new(true, NotificationOption2.Error);

        // specify all options explicitly to override defaults.
        internal OptionsCollection ImplicitTypeEverywhere()
            => new(GetLanguage())
            {
                { CSharpCodeStyleOptions.VarElsewhere, onWithInfo },
                { CSharpCodeStyleOptions.VarWhenTypeIsApparent, onWithInfo },
                { CSharpCodeStyleOptions.VarForBuiltInTypes, onWithInfo },
            };

        private OptionsCollection ImplicitTypeWhereApparent()
            => new(GetLanguage())
            {
                { CSharpCodeStyleOptions.VarElsewhere, offWithInfo },
                { CSharpCodeStyleOptions.VarWhenTypeIsApparent, onWithInfo },
                { CSharpCodeStyleOptions.VarForBuiltInTypes, offWithInfo },
            };

        private OptionsCollection ImplicitTypeWhereApparentAndForIntrinsics()
            => new(GetLanguage())
            {
                { CSharpCodeStyleOptions.VarElsewhere, offWithInfo },
                { CSharpCodeStyleOptions.VarWhenTypeIsApparent, onWithInfo },
                { CSharpCodeStyleOptions.VarForBuiltInTypes, onWithInfo },
            };

        internal OptionsCollection ImplicitTypeButKeepIntrinsics()
            => new(GetLanguage())
            {
                { CSharpCodeStyleOptions.VarElsewhere, onWithInfo },
                { CSharpCodeStyleOptions.VarForBuiltInTypes, offWithInfo },
                { CSharpCodeStyleOptions.VarWhenTypeIsApparent, onWithInfo },
            };

        private OptionsCollection ImplicitTypeEnforcements()
            => new(GetLanguage())
            {
                { CSharpCodeStyleOptions.VarElsewhere, onWithWarning },
                { CSharpCodeStyleOptions.VarWhenTypeIsApparent, onWithError },
                { CSharpCodeStyleOptions.VarForBuiltInTypes, onWithInfo },
            };

        private OptionsCollection ImplicitTypeSilentEnforcement()
            => new(GetLanguage())
            {
                { CSharpCodeStyleOptions.VarElsewhere, onWithSilent },
                { CSharpCodeStyleOptions.VarWhenTypeIsApparent, onWithSilent },
                { CSharpCodeStyleOptions.VarForBuiltInTypes, onWithSilent },
            };

        [WpfFact]
        public async Task NotOnFieldDeclaration()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class Program
                {
                    [|int|] _myfield = 5;
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact]
        public async Task NotOnFieldLikeEvents()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class Program
                {
                    public event [|D|] _myevent;
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact]
        public async Task NotOnConstants()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class Program
                {
                    void Method()
                    {
                        const [|int|] x = 5;
                    }
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact]
        public async Task NotOnNullLiteral()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class Program
                {
                    void Method()
                    {
                        [|Program|] x = null;
                    }
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/27221")]
        public async Task NotOnRefVar()
        {
            await TestMissingInRegularAndScriptAsync("""
                class Program
                {
                    void Method()
                    {
                        ref [|var|] x = Method2();
                    }
                    ref int Method2() => throw null;
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact]
        public async Task NotOnDynamic()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class Program
                {
                    void Method()
                    {
                        [|dynamic|] x = 1;
                    }
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact]
        public async Task NotOnAnonymousMethodExpression()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class Program
                {
                    void Method()
                    {
                        [|Func<string, bool>|] comparer = delegate (string value) {
                            return value != "0";
                        };
                    }
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact]
        public async Task NotOnLambdaExpression()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class Program
                {
                    void Method()
                    {
                        [|Func<int, int>|] x = y => y * y;
                    }
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact]
        public async Task NotOnMethodGroup()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class Program
                {
                    void Method()
                    {
                        [|Func<string, string>|] copyStr = string.Copy;
                    }
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact]
        public async Task NotOnDeclarationWithMultipleDeclarators()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class Program
                {
                    void Method()
                    {
                        [|int|] x = 5, y = x;
                    }
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact]
        public async Task NotOnDeclarationWithoutInitializer()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class Program
                {
                    void Method()
                    {
                        [|Program|] x;
                    }
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact]
        public async Task NotOnIFormattable()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class Program
                {
                    void Method()
                    {
                        [|IFormattable|] s = $"Hello, {name}"
                    }
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact]
        public async Task NotOnFormattableString()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class Program
                {
                    void Method()
                    {
                        [|FormattableString|] s = $"Hello, {name}"
                    }
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact]
        public async Task NotInCatchDeclaration()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class Program
                {
                    void Method()
                    {
                        try
                        {
                        }
                        catch ([|Exception|] e)
                        {
                            throw;
                        }
                    }
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact]
        public async Task NotDuringConflicts()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class Program
                {
                    void Method()
                    {
                        [|Program|] p = new Program();
                    }

                    class var
                    {
                    }
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact]
        public async Task NotIfAlreadyImplicitlyTyped()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class Program
                {
                    void Method()
                    {
                        [|var|] p = new Program();
                    }
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact]
        public async Task NotOnImplicitConversion()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class Program
                {
                    void Method()
                    {
                        int i = int.MaxValue;
                        [|long|] l = i;
                    }
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact]
        public async Task NotOnBoxingImplicitConversion()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class Program
                {
                    void Method()
                    {
                        int i = int.MaxValue;
                        [|object|] o = i;
                    }
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact]
        public async Task NotOnRHS()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    void M()
                    {
                        C c = new [|C|]();
                    }
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact]
        public async Task NotOnVariablesUsedInInitalizerExpression()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    void M()
                    {
                        [|int|] i = (i = 20);
                    }
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/26894")]
        public async Task NotOnVariablesOfEnumTypeNamedAsEnumTypeUsedInInitalizerExpressionAtFirstPosition()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                enum A { X, Y }

                class C
                {
                    void M()
                    {
                        [|A|] A = A.X;
                    }
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/26894")]
        public async Task NotOnVariablesNamedAsTypeUsedInInitalizerExpressionContainingTypeNameAtFirstPositionOfMemberAccess()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class A 
                { 
                    public static A Instance;
                }

                class C
                {
                    void M()
                    {
                        [|A|] A = A.Instance;
                    }
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/26894")]
        public async Task SuggestOnVariablesUsedInInitalizerExpressionAsInnerPartsOfQualifiedNameStartedWithGlobal()
        {
            await TestAsync(
                """
                enum A { X, Y }

                class C
                {
                    void M()
                    {
                        [|A|] A = global::A.X;
                    }
                }
                """,
                """
                enum A { X, Y }

                class C
                {
                    void M()
                    {
                        var A = global::A.X;
                    }
                }
                """, CSharpParseOptions.Default, options: ImplicitTypeEverywhere());
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/26894")]
        public async Task SuggestOnVariablesUsedInInitalizerExpressionAsInnerPartsOfQualifiedName()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                namespace N
                {
                    class A 
                    { 
                        public static A Instance;
                    }
                }

                class C
                {
                    void M()
                    {
                        [|N.A|] A = N.A.Instance;
                    }
                }
                """,
                """
                using System;

                namespace N
                {
                    class A 
                    { 
                        public static A Instance;
                    }
                }

                class C
                {
                    void M()
                    {
                        var A = N.A.Instance;
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/26894")]
        public async Task SuggestOnVariablesUsedInInitalizerExpressionAsLastPartOfQualifiedName()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class A {}

                class X
                { 
                    public static A A;
                }

                class C
                {
                    void M()
                    {
                        [|A|] A = X.A;
                    }
                }
                """,
                """
                using System;

                class A {}

                class X
                { 
                    public static A A;
                }

                class C
                {
                    void M()
                    {
                        var A = X.A;
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact]
        public async Task NotOnAssignmentToInterfaceType()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    public void ProcessRead()
                    {
                        [|IInterface|] i = new A();
                    }
                }

                class A : IInterface
                {
                }

                interface IInterface
                {
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact]
        public async Task NotOnArrayInitializerWithoutNewKeyword()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    static void M()
                    {
                        [|int[]|] n1 = {
                            2,
                            4,
                            6,
                            8
                        };
                    }
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact]
        public async Task SuggestVarOnLocalWithIntrinsicTypeString()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    static void M()
                    {
                        [|string|] s = "hello";
                    }
                }
                """,
                """
                using System;

                class C
                {
                    static void M()
                    {
                        var s = "hello";
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact]
        public async Task SuggestVarOnIntrinsicType()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    static void M()
                    {
                        [|int|] s = 5;
                    }
                }
                """,
                """
                using System;

                class C
                {
                    static void M()
                    {
                        var s = 5;
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/27221")]
        public async Task SuggestVarOnRefIntrinsicType()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    static void M()
                    {
                        ref [|int|] s = Ref();
                    }
                    static ref int Ref() => throw null;
                }
                """,
                """
                using System;

                class C
                {
                    static void M()
                    {
                        ref var s = Ref();
                    }
                    static ref int Ref() => throw null;
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/27221")]
        public async Task WithRefIntrinsicTypeInForeach()
        {
            var before = """
                class E
                {
                    public ref int Current => throw null;
                    public bool MoveNext() => throw null;
                    public E GetEnumerator() => throw null;

                    void M()
                    {
                        foreach (ref [|int|] x in this) { }
                    }
                }
                """;
            var after = """
                class E
                {
                    public ref int Current => throw null;
                    public bool MoveNext() => throw null;
                    public E GetEnumerator() => throw null;

                    void M()
                    {
                        foreach (ref var x in this) { }
                    }
                }
                """;
            await TestInRegularAndScriptAsync(before, after, options: ImplicitTypeEverywhere());
        }

        [WpfFact]
        public async Task SuggestVarOnFrameworkType()
        {
            await TestInRegularAndScriptAsync(
                """
                using System.Collections.Generic;

                class C
                {
                    static void M()
                    {
                        [|List<int>|] c = new List<int>();
                    }
                }
                """,
                """
                using System.Collections.Generic;

                class C
                {
                    static void M()
                    {
                        var c = new List<int>();
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact]
        public async Task SuggestVarOnUserDefinedType()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    void M()
                    {
                        [|C|] c = new C();
                    }
                }
                """,
                """
                using System;

                class C
                {
                    void M()
                    {
                        var c = new C();
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact]
        public async Task SuggestVarOnGenericType()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class C<T>
                {
                    static void M()
                    {
                        [|C<int>|] c = new C<int>();
                    }
                }
                """,
                """
                using System;

                class C<T>
                {
                    static void M()
                    {
                        var c = new C<int>();
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact]
        public async Task SuggestVarOnSeeminglyConflictingType()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class var<T>
                {
                    void M()
                    {
                        [|var<int>|] c = new var<int>();
                    }
                }
                """,
                """
                using System;

                class var<T>
                {
                    void M()
                    {
                        var c = new var<int>();
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact]
        public async Task SuggestVarOnSingleDimensionalArrayTypeWithNewOperator()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    static void M()
                    {
                        [|int[]|] n1 = new int[4] { 2, 4, 6, 8 };
                    }
                }
                """,
                """
                using System;

                class C
                {
                    static void M()
                    {
                        var n1 = new int[4] { 2, 4, 6, 8 };
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact]
        public async Task SuggestVarOnSingleDimensionalArrayTypeWithNewOperator2()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    static void M()
                    {
                        [|int[]|] n1 = new[] { 2, 4, 6, 8 };
                    }
                }
                """,
                """
                using System;

                class C
                {
                    static void M()
                    {
                        var n1 = new[] { 2, 4, 6, 8 };
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact]
        public async Task SuggestVarOnSingleDimensionalJaggedArrayType()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    static void M()
                    {
                        [|int[][]|] cs = new[] {
                            new[] { 1, 2, 3, 4 },
                            new[] { 5, 6, 7, 8 }
                        };
                    }
                }
                """,
                """
                using System;

                class C
                {
                    static void M()
                    {
                        var cs = new[] {
                            new[] { 1, 2, 3, 4 },
                            new[] { 5, 6, 7, 8 }
                        };
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact]
        public async Task SuggestVarOnDeclarationWithObjectInitializer()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    static void M()
                    {
                        [|Customer|] cc = new Customer { City = "Madras" };
                    }

                    private class Customer
                    {
                        public string City { get; set; }
                    }
                }
                """,
                """
                using System;

                class C
                {
                    static void M()
                    {
                        var cc = new Customer { City = "Madras" };
                    }

                    private class Customer
                    {
                        public string City { get; set; }
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact]
        public async Task SuggestVarOnDeclarationWithCollectionInitializer()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;
                using System.Collections.Generic;

                class C
                {
                    static void M()
                    {
                        [|List<int>|] digits = new List<int> { 1, 2, 3 };
                    }
                }
                """,
                """
                using System;
                using System.Collections.Generic;

                class C
                {
                    static void M()
                    {
                        var digits = new List<int> { 1, 2, 3 };
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact]
        public async Task SuggestVarOnDeclarationWithCollectionAndObjectInitializers()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;
                using System.Collections.Generic;

                class C
                {
                    static void M()
                    {
                        [|List<Customer>|] cs = new List<Customer>
                        {
                            new Customer { City = "Madras" }
                        };
                    }

                    private class Customer
                    {
                        public string City { get; set; }
                    }
                }
                """,
                """
                using System;
                using System.Collections.Generic;

                class C
                {
                    static void M()
                    {
                        var cs = new List<Customer>
                        {
                            new Customer { City = "Madras" }
                        };
                    }

                    private class Customer
                    {
                        public string City { get; set; }
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact]
        public async Task SuggestVarOnForStatement()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    static void M()
                    {
                        for ([|int|] i = 0; i < 5; i++)
                        {
                        }
                    }
                }
                """,
                """
                using System;

                class C
                {
                    static void M()
                    {
                        for (var i = 0; i < 5; i++)
                        {
                        }
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact]
        public async Task SuggestVarOnForeachStatement()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;
                using System.Collections.Generic;

                class C
                {
                    static void M()
                    {
                        var l = new List<int> { 1, 3, 5 };
                        foreach ([|int|] item in l)
                        {
                        }
                    }
                }
                """,
                """
                using System;
                using System.Collections.Generic;

                class C
                {
                    static void M()
                    {
                        var l = new List<int> { 1, 3, 5 };
                        foreach (var item in l)
                        {
                        }
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact]
        public async Task SuggestVarOnQueryExpression()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;
                using System.Collections.Generic;
                using System.Linq;

                class C
                {
                    static void M()
                    {
                        var customers = new List<Customer>();
                        [|IEnumerable<Customer>|] expr = from c in customers
                                                     where c.City == "London"
                                                     select c;
                    }

                    private class Customer
                    {
                        public string City { get; set; }
                    }
                }
                }
                """,
                """
                using System;
                using System.Collections.Generic;
                using System.Linq;

                class C
                {
                    static void M()
                    {
                        var customers = new List<Customer>();
                        var expr = from c in customers
                                                     where c.City == "London"
                                                     select c;
                    }

                    private class Customer
                    {
                        public string City { get; set; }
                    }
                }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact]
        public async Task SuggestVarInUsingStatement()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    static void M()
                    {
                        using ([|Res|] r = new Res())
                        {
                        }
                    }

                    private class Res : IDisposable
                    {
                        public void Dispose()
                        {
                            throw new NotImplementedException();
                        }
                    }
                }
                """,
                """
                using System;

                class C
                {
                    static void M()
                    {
                        using (var r = new Res())
                        {
                        }
                    }

                    private class Res : IDisposable
                    {
                        public void Dispose()
                        {
                            throw new NotImplementedException();
                        }
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact]
        public async Task SuggestVarOnExplicitConversion()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class Program
                {
                    void Method()
                    {
                        double x = 1234.7;
                        [|int|] a = (int)x;
                    }
                }
                """,
                """
                using System;

                class Program
                {
                    void Method()
                    {
                        double x = 1234.7;
                        var a = (int)x;
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact]
        public async Task SuggestVarInConditionalAccessExpression()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    static void M()
                    {
                        C obj = new C();
                        [|C|] anotherObj = obj?.Test();
                    }

                    C Test()
                    {
                        return this;
                    }
                }
                """,
                """
                using System;

                class C
                {
                    static void M()
                    {
                        C obj = new C();
                        var anotherObj = obj?.Test();
                    }

                    C Test()
                    {
                        return this;
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact]
        public async Task SuggestVarInCheckedExpression()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    static void M()
                    {
                        long number1 = int.MaxValue + 20L;
                        [|int|] intNumber = checked((int)number1);
                    }
                }
                """,
                """
                using System;

                class C
                {
                    static void M()
                    {
                        long number1 = int.MaxValue + 20L;
                        var intNumber = checked((int)number1);
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact]
        public async Task SuggestVarInUnCheckedExpression()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    static void M()
                    {
                        long number1 = int.MaxValue + 20L;
                        [|int|] intNumber = unchecked((int)number1);
                    }
                }
                """,
                """
                using System;

                class C
                {
                    static void M()
                    {
                        long number1 = int.MaxValue + 20L;
                        var intNumber = unchecked((int)number1);
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact]
        public async Task SuggestVarInAwaitExpression()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;
                using System.Threading.Tasks;

                class C
                {
                    public async void ProcessRead()
                    {
                        [|string|] text = await ReadTextAsync(null);
                    }

                    private async Task<string> ReadTextAsync(string filePath)
                    {
                        return string.Empty;
                    }
                }
                """,
                """
                using System;
                using System.Threading.Tasks;

                class C
                {
                    public async void ProcessRead()
                    {
                        var text = await ReadTextAsync(null);
                    }

                    private async Task<string> ReadTextAsync(string filePath)
                    {
                        return string.Empty;
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact]
        public async Task SuggestVarInParenthesizedExpression()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    public void ProcessRead()
                    {
                        [|int|] text = (5);
                    }
                }
                """,
                """
                using System;

                class C
                {
                    public void ProcessRead()
                    {
                        var text = (5);
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact]
        public async Task DoNotSuggestVarOnBuiltInType_Literal_WithOption()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    static void M()
                    {
                        [|int|] s = 5;
                    }
                }
                """, new TestParameters(options: ImplicitTypeButKeepIntrinsics()));
        }

        [WpfFact]
        public async Task DoNotSuggestVarOnBuiltInType_WithOption()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    private const int maxValue = int.MaxValue;

                    static void M()
                    {
                        [|int|] s = (unchecked(maxValue + 10));
                    }
                }
                """, new TestParameters(options: ImplicitTypeButKeepIntrinsics()));
        }

        [WpfFact]
        public async Task DoNotSuggestVarOnFrameworkTypeEquivalentToBuiltInType()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    private const int maxValue = int.MaxValue;

                    static void M()
                    {
                        [|Int32|] s = (unchecked(maxValue + 10));
                    }
                }
                """, new TestParameters(options: ImplicitTypeButKeepIntrinsics()));
        }

        [WpfFact]
        public async Task SuggestVarWhereTypeIsEvident_DefaultExpression()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    public void Process()
                    {
                        [|C|] text = default(C);
                    }
                }
                """,
                """
                using System;

                class C
                {
                    public void Process()
                    {
                        var text = default(C);
                    }
                }
                """, options: ImplicitTypeWhereApparent());
        }

        [WpfFact]
        public async Task SuggestVarWhereTypeIsEvident_Literals()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    public void Process()
                    {
                        [|int|] text = 5;
                    }
                }
                """,
                """
                using System;

                class C
                {
                    public void Process()
                    {
                        var text = 5;
                    }
                }
                """, options: ImplicitTypeWhereApparentAndForIntrinsics());
        }

        [WpfFact]
        public async Task DoNotSuggestVarWhereTypeIsEvident_Literals()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    public void Process()
                    {
                        [|int|] text = 5;
                    }
                }
                """, new TestParameters(options: ImplicitTypeWhereApparent()));
        }

        [WpfFact]
        public async Task SuggestVarWhereTypeIsEvident_ObjectCreationExpression()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    public void Process()
                    {
                        [|C|] c = new C();
                    }
                }
                """,
                """
                using System;

                class C
                {
                    public void Process()
                    {
                        var c = new C();
                    }
                }
                """, options: ImplicitTypeWhereApparent());
        }

        [WpfFact]
        public async Task SuggestVarWhereTypeIsEvident_CastExpression()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    public void Process()
                    {
                        object o = DateTime.MaxValue;
                        [|DateTime|] date = (DateTime)o;
                    }
                }
                """,
                """
                using System;

                class C
                {
                    public void Process()
                    {
                        object o = DateTime.MaxValue;
                        var date = (DateTime)o;
                    }
                }
                """, options: ImplicitTypeWhereApparent());
        }

        [WpfFact]
        public async Task DoNotSuggestVar_BuiltInTypesRulePrecedesOverTypeIsApparentRule1()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    public void Process()
                    {
                        object o = int.MaxValue;
                        [|int|] i = (Int32)o;
                    }
                }
                """, new TestParameters(options: ImplicitTypeWhereApparent()));
        }

        [WpfFact]
        public async Task DoNotSuggestVar_BuiltInTypesRulePrecedesOverTypeIsApparentRule2()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    public void Process()
                    {
                        object o = int.MaxValue;
                        [|Int32|] i = (Int32)o;
                    }
                }
                """, new TestParameters(options: ImplicitTypeWhereApparent()));
        }

        [WpfFact]
        public async Task DoNotSuggestVarWhereTypeIsEvident_IsExpression()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    public void Process()
                    {
                        A a = new A();
                        [|Boolean|] s = a is IInterface;
                    }
                }

                class A : IInterface
                {
                }

                interface IInterface
                {
                }
                """, new TestParameters(options: ImplicitTypeWhereApparent()));
        }

        [WpfFact]
        public async Task SuggestVarWhereTypeIsEvident_AsExpression()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    public void Process()
                    {
                        A a = new A();
                        [|IInterface|] s = a as IInterface;
                    }
                }

                class A : IInterface
                {
                }

                interface IInterface
                {
                }
                """,
                """
                using System;

                class C
                {
                    public void Process()
                    {
                        A a = new A();
                        var s = a as IInterface;
                    }
                }

                class A : IInterface
                {
                }

                interface IInterface
                {
                }
                """, options: ImplicitTypeWhereApparent());
        }

        [WpfFact]
        public async Task SuggestVarWhereTypeIsEvident_ConversionHelpers()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    public void Process()
                    {
                        [|DateTime|] a = DateTime.Parse("1");
                    }
                }
                """,
                """
                using System;

                class C
                {
                    public void Process()
                    {
                        var a = DateTime.Parse("1");
                    }
                }
                """, options: ImplicitTypeWhereApparent());
        }

        [WpfFact]
        public async Task SuggestVarWhereTypeIsEvident_CreationHelpers()
        {
            await TestInRegularAndScriptAsync(
                """
                class C
                {
                    public void Process()
                    {
                        [|XElement|] a = XElement.Load();
                    }
                }

                class XElement
                {
                    internal static XElement Load() => return null;
                }
                """,
                """
                class C
                {
                    public void Process()
                    {
                        var a = XElement.Load();
                    }
                }

                class XElement
                {
                    internal static XElement Load() => return null;
                }
                """, options: ImplicitTypeWhereApparent());
        }

        [WpfFact]
        public async Task SuggestVarWhereTypeIsEvident_CreationHelpersWithInferredTypeArguments()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    public void Process()
                    {
                        [|Tuple<int, bool>|] a = Tuple.Create(0, true);
                    }
                }
                """,
                """
                using System;

                class C
                {
                    public void Process()
                    {
                        var a = Tuple.Create(0, true);
                    }
                }
                """, options: ImplicitTypeWhereApparent());
        }

        [WpfFact]
        public async Task SuggestVarWhereTypeIsEvident_ConvertToType()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    public void Process()
                    {
                        int integralValue = 12534;
                        [|DateTime|] date = Convert.ToDateTime(integralValue);
                    }
                }
                """,
                """
                using System;

                class C
                {
                    public void Process()
                    {
                        int integralValue = 12534;
                        var date = Convert.ToDateTime(integralValue);
                    }
                }
                """, options: ImplicitTypeWhereApparent());
        }

        [WpfFact]
        public async Task SuggestVarWhereTypeIsEvident_IConvertibleToType()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    public void Process()
                    {
                        int codePoint = 1067;
                        IConvertible iConv = codePoint;
                        [|DateTime|] date = iConv.ToDateTime(null);
                    }
                }
                """,
                """
                using System;

                class C
                {
                    public void Process()
                    {
                        int codePoint = 1067;
                        IConvertible iConv = codePoint;
                        var date = iConv.ToDateTime(null);
                    }
                }
                """, options: ImplicitTypeWhereApparent());
        }

        [WpfFact]
        public async Task SuggestVarNotificationLevelSilent()
        {
            var source =
                """
                using System;
                class C
                {
                    static void M()
                    {
                        [|C|] n1 = new C();
                    }
                }
                """;
            await TestDiagnosticInfoAsync(source,
                options: ImplicitTypeSilentEnforcement(),
                diagnosticId: IDEDiagnosticIds.UseImplicitTypeDiagnosticId,
                diagnosticSeverity: DiagnosticSeverity.Hidden);
        }

        [WpfFact]
        public async Task SuggestVarNotificationLevelInfo()
        {
            var source =
                """
                using System;
                class C
                {
                    static void M()
                    {
                        [|int|] s = 5;
                    }
                }
                """;
            await TestDiagnosticInfoAsync(source,
                options: ImplicitTypeEnforcements(),
                diagnosticId: IDEDiagnosticIds.UseImplicitTypeDiagnosticId,
                diagnosticSeverity: DiagnosticSeverity.Info);
        }

        [WpfFact]
        public async Task SuggestVarNotificationLevelWarning()
        {
            var source =
                """
                using System;
                class C
                {
                    static void M()
                    {
                        [|C[]|] n1 = new[] { new C() }; // type not apparent and not intrinsic
                    }
                }
                """;
            await TestDiagnosticInfoAsync(source,
                options: ImplicitTypeEnforcements(),
                diagnosticId: IDEDiagnosticIds.UseImplicitTypeDiagnosticId,
                diagnosticSeverity: DiagnosticSeverity.Warning);
        }

        [WpfFact]
        public async Task SuggestVarNotificationLevelError()
        {
            var source =
                """
                using System;
                class C
                {
                    static void M()
                    {
                        [|C|] n1 = new C();
                    }
                }
                """;
            await TestDiagnosticInfoAsync(source,
                options: ImplicitTypeEnforcements(),
                diagnosticId: IDEDiagnosticIds.UseImplicitTypeDiagnosticId,
                diagnosticSeverity: DiagnosticSeverity.Error);
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/23893")]
        public async Task SuggestVarOnLocalWithIntrinsicArrayType()
        {
            var before = @"class C { static void M() { [|int[]|] s = new int[0]; } }";
            var after = @"class C { static void M() { var s = new int[0]; } }";

            //The type is intrinsic and apparent
            await TestInRegularAndScriptAsync(before, after, options: ImplicitTypeEverywhere());
            await TestMissingInRegularAndScriptAsync(before, new TestParameters(options: ImplicitTypeButKeepIntrinsics()));
            await TestMissingInRegularAndScriptAsync(before, new TestParameters(options: ImplicitTypeWhereApparent())); // Preference of intrinsic types dominates
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/23893")]
        public async Task SuggestVarOnLocalWithCustomArrayType()
        {
            var before = @"class C { static void M() { [|C[]|] s = new C[0]; } }";
            var after = @"class C { static void M() { var s = new C[0]; } }";

            //The type is not intrinsic but apparent
            await TestInRegularAndScriptAsync(before, after, options: ImplicitTypeEverywhere());
            await TestInRegularAndScriptAsync(before, after, options: ImplicitTypeButKeepIntrinsics());
            await TestInRegularAndScriptAsync(before, after, options: ImplicitTypeWhereApparent());
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/23893")]
        public async Task SuggestVarOnLocalWithNonApparentCustomArrayType()
        {
            var before = @"class C { static void M() { [|C[]|] s = new[] { new C() }; } }";
            var after = @"class C { static void M() { var s = new[] { new C() }; } }";

            //The type is not intrinsic and not apparent
            await TestInRegularAndScriptAsync(before, after, options: ImplicitTypeEverywhere());
            await TestInRegularAndScriptAsync(before, after, options: ImplicitTypeButKeepIntrinsics());
            await TestMissingInRegularAndScriptAsync(before, new TestParameters(options: ImplicitTypeWhereApparent()));
        }

        private static readonly string trivial2uple =
            """
            namespace System
            {
                public class ValueTuple
                {
                    public static ValueTuple<T1, T2> Create<T1, T2>(T1 item1, T2 item2) => new ValueTuple<T1, T2>(item1, item2);
                }
                public struct ValueTuple<T1, T2>
                {
                    public T1 Item1;
                    public T2 Item2;

                    public ValueTuple(T1 item1, T2 item2) { }
                }
            }
            """;

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/11094")]
        public async Task SuggestVarOnLocalWithIntrinsicTypeTuple()
        {
            var before = @"class C { static void M() { [|(int a, string)|] s = (a: 1, ""hello""); } }";
            var after = @"class C { static void M() { var s = (a: 1, ""hello""); } }";

            await TestInRegularAndScriptAsync(before, after, options: ImplicitTypeEverywhere());
            await TestInRegularAndScriptAsync(before, after, options: ImplicitTypeWhereApparentAndForIntrinsics());
            await TestMissingInRegularAndScriptAsync(before, new TestParameters(options: ImplicitTypeWhereApparent()));
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/11094")]
        public async Task SuggestVarOnLocalWithNonApparentTupleType()
        {
            var before = @"class C { static void M(C c) { [|(int a, C b)|] s = (a: 1, b: c); } }";
            var after = @"class C { static void M(C c) { var s = (a: 1, b: c); } }";

            await TestInRegularAndScriptAsync(before, after, options: ImplicitTypeEverywhere());
            await TestMissingInRegularAndScriptAsync(before, new TestParameters(options: ImplicitTypeWhereApparentAndForIntrinsics()));
            await TestMissingInRegularAndScriptAsync(before, new TestParameters(options: ImplicitTypeWhereApparent()));
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/11154")]
        public async Task ValueTupleCreate()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    static void M()
                    {
                        [|ValueTuple<int, int>|] s = ValueTuple.Create(1, 1);
                    }
                }
                """ + trivial2uple,
                """
                using System;

                class C
                {
                    static void M()
                    {
                        var s = ValueTuple.Create(1, 1);
                    }
                }
                """ + trivial2uple,
options: ImplicitTypeWhereApparent());
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/11095")]
        public async Task ValueTupleCreate_2()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    static void M()
                    {
                        [|(int, int)|] s = ValueTuple.Create(1, 1);
                    }
                }
                """ + trivial2uple,
                """
                using System;

                class C
                {
                    static void M()
                    {
                        var s = ValueTuple.Create(1, 1);
                    }
                }
                """ + trivial2uple,
options: ImplicitTypeWhereApparent());
        }

        [WpfFact]
        public async Task TupleWithDifferentNames()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                class C
                {
                    static void M()
                    {
                        [|(int, string)|] s = (c: 1, d: "hello");
                    }
                }
                """,
new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/14052")]
        public async Task DoNotOfferOnForEachConversionIfItChangesSemantics()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;
                using System.Collections.Generic;

                interface IContractV1
                {
                }

                interface IContractV2 : IContractV1
                {
                }

                class ContractFactory
                {
                    public IEnumerable<IContractV1> GetContracts()
                    {
                    }
                }

                class Program
                {
                    static void M()
                    {
                        var contractFactory = new ContractFactory();
                        foreach ([|IContractV2|] contract in contractFactory.GetContracts())
                        {
                        }
                    }
                }
                """,
new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/14052")]
        public async Task OfferOnForEachConversionIfItDoesNotChangesSemantics()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;
                using System.Collections.Generic;

                interface IContractV1
                {
                }

                interface IContractV2 : IContractV1
                {
                }

                class ContractFactory
                {
                    public IEnumerable<IContractV1> GetContracts()
                    {
                    }
                }

                class Program
                {
                    static void M()
                    {
                        var contractFactory = new ContractFactory();
                        foreach ([|IContractV1|] contract in contractFactory.GetContracts())
                        {
                        }
                    }
                }
                """,
                """
                using System;
                using System.Collections.Generic;

                interface IContractV1
                {
                }

                interface IContractV2 : IContractV1
                {
                }

                class ContractFactory
                {
                    public IEnumerable<IContractV1> GetContracts()
                    {
                    }
                }

                class Program
                {
                    static void M()
                    {
                        var contractFactory = new ContractFactory();
                        foreach (var contract in contractFactory.GetContracts())
                        {
                        }
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/20437")]
        public async Task SuggestVarOnDeclarationExpressionSyntax()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class C
                {
                    static void M()
                    {
                        DateTime.TryParse(string.Empty, [|out DateTime|] date);
                    }
                }
                """,
                """
                using System;

                class C
                {
                    static void M()
                    {
                        DateTime.TryParse(string.Empty, out var date);
                    }
                }
                """,
options: ImplicitTypeEverywhere());
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/23893")]
        public async Task DoNotSuggestVarOnDeclarationExpressionSyntaxWithIntrinsicType()
        {
            var before =
                """
                class C
                {
                    static void M(out int x)
                    {
                        M([|out int|] x);
                    }
                }
                """;
            await TestMissingInRegularAndScriptAsync(before, new TestParameters(options: ImplicitTypeButKeepIntrinsics()));
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/22768")]
        public async Task DoNotSuggestVarOnStackAllocExpressions_SpanType()
        {
            await TestMissingInRegularAndScriptAsync("""
                using System;
                namespace System
                {
                    public readonly ref struct Span<T> 
                    {
                        unsafe public Span(void* pointer, int length) { }
                    }
                }
                class C
                {
                    static void M()
                    {
                        [|Span<int>|] x = stackalloc int [10];
                    }
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/22768")]
        public async Task DoNotSuggestVarOnStackAllocExpressions_SpanType_NestedConditional()
        {
            await TestMissingInRegularAndScriptAsync("""
                using System;
                namespace System
                {
                    public readonly ref struct Span<T> 
                    {
                        unsafe public Span(void* pointer, int length) { }
                    }
                }
                class C
                {
                    static void M(bool choice)
                    {
                        [|Span<int>|] x = choice ? stackalloc int [10] : stackalloc int [100];
                    }
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/22768")]
        public async Task DoNotSuggestVarOnStackAllocExpressions_SpanType_NestedCast()
        {
            await TestMissingInRegularAndScriptAsync("""
                using System;
                namespace System
                {
                    public readonly ref struct Span<T> 
                    {
                        unsafe public Span(void* pointer, int length) { }
                    }
                }
                class C
                {
                    static void M()
                    {
                        [|Span<int>|] x = (Span<int>)stackalloc int [100];
                    }
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/22768")]
        public async Task SuggestVarOnLambdasWithNestedStackAllocs()
        {
            await TestInRegularAndScriptAsync("""
                using System.Linq;
                class C
                {
                    unsafe static void M()
                    {
                        [|int|] x = new int[] { 1, 2, 3 }.First(i =>
                        {
                            int* y = stackalloc int[10];
                            return i == 1;
                        });
                    }
                }
                """, """
                using System.Linq;
                class C
                {
                    unsafe static void M()
                    {
                        var x = new int[] { 1, 2, 3 }.First(i =>
                        {
                            int* y = stackalloc int[10];
                            return i == 1;
                        });
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/22768")]
        public async Task SuggestVarOnAnonymousMethodsWithNestedStackAllocs()
        {
            await TestInRegularAndScriptAsync("""
                using System.Linq;
                class C
                {
                    unsafe static void M()
                    {
                        [|int|] x = new int[] { 1, 2, 3 }.First(delegate (int i)
                        {
                            int* y = stackalloc int[10];
                            return i == 1;
                        });
                    }
                }
                """, """
                using System.Linq;
                class C
                {
                    unsafe static void M()
                    {
                        var x = new int[] { 1, 2, 3 }.First(delegate (int i)
                        {
                            int* y = stackalloc int[10];
                            return i == 1;
                        });
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/22768")]
        public async Task SuggestVarOnStackAllocsNestedInLambdas()
        {
            await TestInRegularAndScriptAsync("""
                using System.Linq;
                class C
                {
                    unsafe static void M()
                    {
                        var x = new int[] { 1, 2, 3 }.First(i =>
                        {
                            [|int*|] y = stackalloc int[10];
                            return i == 1;
                        });
                    }
                }
                """, """
                using System.Linq;
                class C
                {
                    unsafe static void M()
                    {
                        var x = new int[] { 1, 2, 3 }.First(i =>
                        {
                            var y = stackalloc int[10];
                            return i == 1;
                        });
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/22768")]
        public async Task SuggestVarOnStackAllocsNestedInAnonymousMethods()
        {
            await TestInRegularAndScriptAsync("""
                using System.Linq;
                class C
                {
                    unsafe static void M()
                    {
                        var x = new int[] { 1, 2, 3 }.First(delegate (int i)
                        {
                            [|int*|] y = stackalloc int[10];
                            return i == 1;
                        });
                    }
                }
                """, """
                using System.Linq;
                class C
                {
                    unsafe static void M()
                    {
                        var x = new int[] { 1, 2, 3 }.First(delegate (int i)
                        {
                            var y = stackalloc int[10];
                            return i == 1;
                        });
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/22768")]
        public async Task SuggestVarOnStackAllocsInOuterMethodScope()
        {
            await TestInRegularAndScriptAsync("""
                class C
                {
                    unsafe static void M()
                    {
                        [|int*|] x = stackalloc int [10];
                    }
                }
                """, """
                class C
                {
                    unsafe static void M()
                    {
                        var x = stackalloc int [10];
                    }
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/23116")]
        public async Task DoSuggestForDeclarationExpressionIfItWouldNotChangeOverloadResolution2()
        {
            await TestInRegularAndScriptAsync("""
                class Program
                {
                    static int Main(string[] args)
                    {
                        TryGetValue("key", out [|int|] value);
                        return value;
                    }

                    public static bool TryGetValue(string key, out int value) => false;
                    public static bool TryGetValue(string key, out bool value, int x) => false;
                }
                """, """
                class Program
                {
                    static int Main(string[] args)
                    {
                        TryGetValue("key", out var value);
                        return value;
                    }

                    public static bool TryGetValue(string key, out int value) => false;
                    public static bool TryGetValue(string key, out bool value, int x) => false;
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/23116")]
        public async Task DoNotSuggestForDeclarationExpressionIfItWouldChangeOverloadResolution()
        {
            await TestMissingInRegularAndScriptAsync("""
                class Program
                {
                    static int Main(string[] args)
                    {
                        TryGetValue("key", out [|int|] value);
                        return value;
                    }

                    public static bool TryGetValue(string key, out object value) => false;

                    public static bool TryGetValue<T>(string key, out T value) => false;
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/23116")]
        public async Task DoNotSuggestIfChangesGenericTypeInference()
        {
            await TestMissingInRegularAndScriptAsync("""
                class Program
                {
                    static int Main(string[] args)
                    {
                        TryGetValue("key", out [|int|] value);
                        return value;
                    }

                    public static bool TryGetValue<T>(string key, out T value) => false;
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/23116")]
        public async Task SuggestIfDoesNotChangeGenericTypeInference1()
        {
            await TestInRegularAndScriptAsync("""
                class Program
                {
                    static int Main(string[] args)
                    {
                        TryGetValue<int>("key", out [|int|] value);
                        return value;
                    }

                    public static bool TryGetValue<T>(string key, out T value) => false;
                }
                """, """
                class Program
                {
                    static int Main(string[] args)
                    {
                        TryGetValue<int>("key", out var value);
                        return value;
                    }

                    public static bool TryGetValue<T>(string key, out T value) => false;
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/23116")]
        public async Task SuggestIfDoesNotChangeGenericTypeInference2()
        {
            await TestInRegularAndScriptAsync("""
                class Program
                {
                    static int Main(string[] args)
                    {
                        TryGetValue(0, out [|int|] value);
                        return value;
                    }

                    public static bool TryGetValue<T>(T key, out T value) => false;
                }
                """, """
                class Program
                {
                    static int Main(string[] args)
                    {
                        TryGetValue(0, out var value);
                        return value;
                    }

                    public static bool TryGetValue<T>(T key, out T value) => false;
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/23711")]
        public async Task SuggestVarForDelegateType()
        {
            await TestInRegularAndScriptAsync("""
                class Program
                {
                    static void Main(string[] args)
                    {
                        [|GetHandler|] handler = Handler;
                    }

                    private static GetHandler Handler;

                    delegate object GetHandler();
                }
                """, """
                class Program
                {
                    static void Main(string[] args)
                    {
                        var handler = Handler;
                    }

                    private static GetHandler Handler;

                    delegate object GetHandler();
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/23711")]
        public async Task DoNotSuggestVarForDelegateType1()
        {
            await TestMissingInRegularAndScriptAsync("""
                class Program
                {
                    static void Main(string[] args)
                    {
                        [|GetHandler|] handler = () => new object();
                    }

                    private static GetHandler Handler;

                    delegate object GetHandler();
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/23711")]
        public async Task DoNotSuggestVarForDelegateType2()
        {
            await TestMissingInRegularAndScriptAsync("""
                class Program
                {
                    static void Main(string[] args)
                    {
                        [|GetHandler|] handler = Foo;
                    }

                    private static GetHandler Handler;

                    private static object Foo() => new object();

                    delegate object GetHandler();
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/23711")]
        public async Task DoNotSuggestVarForDelegateType3()
        {
            await TestMissingInRegularAndScriptAsync("""
                class Program
                {
                    static void Main(string[] args)
                    {
                        [|GetHandler|] handler = delegate { return new object(); };
                    }

                    private static GetHandler Handler;

                    delegate object GetHandler();
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/24262")]
        public async Task DoNotSuggestVarForInterfaceVariableInForeachStatement()
        {
            await TestMissingInRegularAndScriptAsync("""
                public interface ITest
                {
                    string Value { get; }
                }
                public class TestInstance : ITest
                {
                    string ITest.Value => "Hi";
                }

                public class Test
                {
                    public TestInstance[] Instances { get; }

                    public void TestIt()
                    {
                        foreach ([|ITest|] test in Instances)
                        {
                            Console.WriteLine(test.Value);
                        }
                    }
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/24262")]
        public async Task DoNotSuggestVarForInterfaceVariableInDeclarationStatement()
        {
            await TestMissingInRegularAndScriptAsync("""
                public interface ITest
                {
                    string Value { get; }
                }
                public class TestInstance : ITest
                {
                    string ITest.Value => "Hi";
                }

                public class Test
                {
                    public void TestIt()
                    {
                        [|ITest|] test = new TestInstance();
                        Console.WriteLine(test.Value);
                    }
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/24262")]
        public async Task DoNotSuggestVarForAbstractClassVariableInForeachStatement()
        {
            await TestMissingInRegularAndScriptAsync("""
                public abstract class MyAbClass
                {
                    string Value { get; }
                }

                public class TestInstance : MyAbClass
                {
                    public string Value => "Hi";
                }

                public class Test
                {
                    public TestInstance[] Instances { get; }

                    public void TestIt()
                    {
                        foreach ([|MyAbClass|] instance in Instances)
                        {
                            Console.WriteLine(instance);
                        }
                    }
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/24262")]
        public async Task DoNotSuggestVarForAbstractClassVariableInDeclarationStatement()
        {
            await TestMissingInRegularAndScriptAsync("""
                public abstract class MyAbClass
                {
                    string Value { get; }
                }

                public class TestInstance : MyAbClass
                {
                    public string Value => "Hi";
                }

                public class Test
                {
                    public TestInstance[] Instances { get; }

                    public void TestIt()
                    {
                        [|MyAbClass|]  test = new TestInstance();
                    }
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact]
        public async Task DoNoSuggestVarForRefForeachVar()
        {
            await TestMissingInRegularAndScriptAsync("""
                using System;
                namespace System
                {
                    public readonly ref struct Span<T>
                    {
                        unsafe public Span(void* pointer, int length) { }

                        public ref SpanEnum GetEnumerator() => throw new Exception();

                        public struct SpanEnum
                        {
                            public ref int Current => 0;
                            public bool MoveNext() => false;
                        }
                    }
                }
                class C
                {
                    public void M(Span<int> span)
                    {
                        foreach ([|ref|] var rx in span)
                        {
                        }
                    }
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/26923")]
        public async Task NoSuggestionOnForeachCollectionExpression()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;
                using System.Collections.Generic;

                class C
                {
                    static void Main(string[] args)
                    {
                        foreach (string arg in [|args|])
                        {

                        }
                    }
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/39171")]
        [WpfFact]
        public async Task NoSuggestionForSwitchExpressionDifferentTypes()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                class Program
                {
                    interface IFruit { }

                    class Apple : IFruit { }

                    class Banana : IFruit { }

                    public static void Test(string name)
                    {
                        [|IFruit|] fruit = name switch
                        {
                            "apple" => new Apple(),
                            "banana" => new Banana(),
                            _ => null,
                        };
                    }
                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WorkItem("https://github.com/dotnet/roslyn/issues/39171")]
        [WpfFact]
        public async Task SuggestSwitchExpressionSameOrInheritedTypes()
        {
            await TestInRegularAndScriptAsync(
                """
                using System;

                class Test { }

                class Test2 : Test { }

                class C
                {
                    void M()
                    {
                        var str = "one";
                        [|Test|] t = str switch
                        {
                            "one" => new Test(),
                            "two" => new Test2(),
                            _ => throw new InvalidOperationException("Unknown test."),
                        };
                    }     
                }
                """,
                """
                using System;

                class Test { }

                class Test2 : Test { }

                class C
                {
                    void M()
                    {
                        var str = "one";
                        var t = str switch
                        {
                            "one" => new Test(),
                            "two" => new Test2(),
                            _ => throw new InvalidOperationException("Unknown test."),
                        };
                    }     
                }
                """, options: ImplicitTypeEverywhere());
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/32088")]
        public async Task DoNotSuggestVarOnDeclarationExpressionWithInferredTupleNames()
        {
            await TestMissingAsync(
                """
                using System.Collections.Generic;
                using System.Linq;

                static class Program
                {
                    static void Main(string[] args)
                    {
                        if (!_data.TryGetValue(0, [|out List<(int X, int Y)>|] value))
                            return;

                        var x = value.FirstOrDefault().X;
                    }

                    private static Dictionary<int, List<(int, int)>> _data =
                        new Dictionary<int, List<(int, int)>>();
                }
                """, parameters: new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/32088")]
        public async Task DoSuggestVarOnDeclarationExpressionWithMatchingTupleNames()
        {
            await TestInRegularAndScriptAsync(
                """
                using System.Collections.Generic;
                using System.Linq;

                static class Program
                {
                    static void Main(string[] args)
                    {
                        if (!_data.TryGetValue(0, [|out List<(int X, int Y)>|] value))
                            return;

                        var x = value.FirstOrDefault().X;
                    }

                    private static Dictionary<int, List<(int X, int Y)>> _data =
                        new Dictionary<int, List<(int, int)>>();
                }
                """,
                """
                using System.Collections.Generic;
                using System.Linq;

                static class Program
                {
                    static void Main(string[] args)
                    {
                        if (!_data.TryGetValue(0, out var value))
                            return;

                        var x = value.FirstOrDefault().X;
                    }

                    private static Dictionary<int, List<(int X, int Y)>> _data =
                        new Dictionary<int, List<(int, int)>>();
                }
                """,
options: ImplicitTypeEverywhere());
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/44507")]
        public async Task DoNotSuggestVarInAmbiguousSwitchExpression()
        {
            await TestMissingAsync(
                """
                using System;

                class C
                {
                    void M()
                    {
                        var i = 1;
                        [||]C x = i switch
                        {
                            0 => new A(),
                            1 => new B(),
                            _ => throw new ArgumentException(),
                        };
                    }
                }

                class A : C
                {
                }

                class B : C
                {
                }
                """, parameters: new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/44507")]
        public async Task DoNotSuggestVarInSwitchExpressionWithDelegateType()
        {
            await TestMissingAsync(
                """
                using System;

                class C
                {
                    private void M(object sender, EventArgs e)
                    {
                        var x = 1;
                        [||]Action<object, EventArgs> a = x switch
                        {
                            0 => (sender, e) => f1(sender, e),
                            1 => (sender, e) => f2(sender, e),
                            _ => throw new ArgumentException()
                        };

                        a(sender, e);
                    }

                    private readonly Action<object, EventArgs> f1;
                    private readonly Action<object, EventArgs> f2;
                }
                """, parameters: new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [WpfFact]
        public async Task DoNotSuggestVarForImplicitObjectCreation()
        {
            await TestMissingInRegularAndScriptAsync(
                """
                using System;

                class Program
                {
                    void Method()
                    {
                        [|string|] p = new('c', 1);
                    }

                }
                """, new TestParameters(options: ImplicitTypeEverywhere()));
        }

        [Fact]
        public Task SuggestForNullable1()
            => TestInRegularAndScriptAsync(
                """
                #nullable enable

                class C
                {
                    string? M()
                    {
                        [|string?|] a = NullableString();
                        return a;
                    }

                    string? NullableString() => null;
                }
                """,
                """
                #nullable enable

                class C
                {
                    string? M()
                    {
                        var a = NullableString();
                        return a;
                    }

                    string? NullableString() => null;
                }
                """,
options: ImplicitTypeEverywhere());

        [Fact]
        public Task SuggestForNullable2()
            => TestInRegularAndScriptAsync(
                """
                #nullable enable

                class C
                {
                    string? M()
                    {
                        [|string?|] a = NonNullString();
                        return a;
                    }

                    string NonNullString() => string.Empty;
                }
                """,
                """
                #nullable enable

                class C
                {
                    string? M()
                    {
                        var a = NonNullString();
                        return a;
                    }

                    string NonNullString() => string.Empty;
                }
                """,
options: ImplicitTypeEverywhere());

        [Fact]
        public Task SuggestForNullable3()
            => TestInRegularAndScriptAsync(
                """
                #nullable enable

                class C
                {
                    string? M()
                    {
                        [|string|] a = NonNullString();
                        return a;
                    }

                    string NonNullString() => string.Empty;
                }
                """,
                """
                #nullable enable

                class C
                {
                    string? M()
                    {
                        var a = NonNullString();
                        return a;
                    }

                    string NonNullString() => string.Empty;
                }
                """,
options: ImplicitTypeEverywhere());

        [Fact]
        public Task SuggestForNullableOut1()
            => TestInRegularAndScriptAsync(
                """
                #nullable enable

                class C
                {
                    string? M()
                    {
                        if (GetNullString(out [|string?|] a))
                        {
                            return a;
                        }

                        return null;
                    }

                    bool GetNullString(out string? s)
                    {
                        s = null;
                        return true;
                    }
                }
                """,
                """
                #nullable enable

                class C
                {
                    string? M()
                    {
                        if (GetNullString(out var a))
                        {
                            return a;
                        }

                        return null;
                    }

                    bool GetNullString(out string? s)
                    {
                        s = null;
                        return true;
                    }
                }
                """,
options: ImplicitTypeEverywhere());

        [Fact]
        public Task SuggestForNullableOut2()
            => TestInRegularAndScriptAsync(
                """
                #nullable enable

                class C
                {
                    string? M()
                    {
                        if (GetNonNullString(out [|string?|] a))
                        {
                            return a;
                        }

                        return null;
                    }

                    bool GetNonNullString(out string s)
                    {
                        s = string.Empty;
                        return true;
                    }
                }
                """,
                """
                #nullable enable

                class C
                {
                    string? M()
                    {
                        if (GetNonNullString(out var a))
                        {
                            return a;
                        }

                        return null;
                    }

                    bool GetNonNullString(out string s)
                    {
                        s = string.Empty;
                        return true;
                    }
                }
                """,
options: ImplicitTypeEverywhere());

        [Fact]
        public Task SuggestForNullableOut3()
            => TestInRegularAndScriptAsync(
                """
                #nullable enable

                class C
                {
                    string? M()
                    {
                        if (GetNonNullString(out [|string|] a))
                        {
                            return a;
                        }

                        return null;
                    }

                    bool GetNonNullString(out string s)
                    {
                        s = string.Empty;
                        return true;
                    }
                }
                """,
                """
                #nullable enable

                class C
                {
                    string? M()
                    {
                        if (GetNonNullString(out var a))
                        {
                            return a;
                        }

                        return null;
                    }

                    bool GetNonNullString(out string s)
                    {
                        s = string.Empty;
                        return true;
                    }
                }
                """,
options: ImplicitTypeEverywhere());

        [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/41780")]
        public async Task SuggestOnRefType1()
        {
            await TestAsync(
                """
                class C
                {
                    void Method(ref int x)
                    {
                      ref [|int|] y = ref x;
                    }
                }
                """,
                """
                class C
                {
                    void Method(ref int x)
                    {
                      ref var y = ref x;
                    }
                }
                """, CSharpParseOptions.Default, options: ImplicitTypeEverywhere());
        }
    }
}
