﻿// 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.

#nullable disable

using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Roslyn.Test.Utilities;
using System.Linq;
using System.Text;
using Xunit;

namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
    // Tests that should take a long time, perhaps even exceeding
    // test timeout, without shortcuts in overload resolution.
    public class OverloadResolutionPerfTests : CSharpTestBase
    {
        [WorkItem(13685, "https://github.com/dotnet/roslyn/issues/13685")]
        [ConditionalFact(typeof(IsRelease), typeof(NoIOperationValidation))]
        public void Overloads()
        {
            const int n = 3000;
            var builder = new StringBuilder();
            builder.AppendLine("class C");
            builder.AppendLine("{");
            builder.AppendLine($"    static void F() {{ F(null); }}"); // matches n overloads: F(C0), F(C1), ...
            for (int i = 0; i < n; i++)
            {
                builder.AppendLine($"    static void F(C{i} c) {{ }}");
            }
            builder.AppendLine("}");
            for (int i = 0; i < n; i++)
            {
                builder.AppendLine($"class C{i} {{ }}");
            }
            var source = builder.ToString();
            var comp = CreateCompilationWithMscorlib40AndSystemCore(source);
            comp.VerifyDiagnostics(
                // (3,23): error CS0121: The call is ambiguous between the following methods or properties: 'C.F(C0)' and 'C.F(C1)'
                //     static void F() { F(null); }
                Diagnostic(ErrorCode.ERR_AmbigCall, "F").WithArguments("C.F(C0)", "C.F(C1)").WithLocation(3, 23));
        }

        [WorkItem(13685, "https://github.com/dotnet/roslyn/issues/13685")]
        [ConditionalFact(typeof(IsRelease), typeof(NoIOperationValidation))]
        public void BinaryOperatorOverloads()
        {
            const int n = 3000;
            var builder = new StringBuilder();
            builder.AppendLine("class C");
            builder.AppendLine("{");
            builder.AppendLine($"    static object F(C x) => x + null;"); // matches n overloads: +(C, C0), +(C, C1), ...
            for (int i = 0; i < n; i++)
            {
                builder.AppendLine($"    public static object operator+(C x, C{i} y) => null;");
            }
            builder.AppendLine("}");
            for (int i = 0; i < n; i++)
            {
                builder.AppendLine($"class C{i} {{ }}");
            }
            var source = builder.ToString();
            var comp = CreateCompilationWithMscorlib40AndSystemCore(source);
            comp.VerifyDiagnostics(
                // (3,29): error CS0034: Operator '+' is ambiguous on operands of type 'C' and '<null>'
                //     static object F(C x) => x + null;
                Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "x + null").WithArguments("+", "C", "<null>").WithLocation(3, 29));
        }

        [ConditionalFact(typeof(IsRelease))]
        public void StaticMethodsWithLambda()
        {
            const int n = 100;
            var builder = new StringBuilder();
            builder.AppendLine("using System;");
            for (int i = 0; i < n; i++)
            {
                builder.AppendLine($"class C{i} {{ }}");
            }
            builder.AppendLine("static class S");
            builder.AppendLine("{");
            for (int i = 0; i < n; i++)
            {
                builder.AppendLine($"    internal static void F(C{i} x, Action<C{i}> a) {{ F(x, y => F(y, z => F(z, w => {{ }}))); }}");
            }
            builder.AppendLine("}");
            var source = builder.ToString();
            var comp = CreateCompilationWithMscorlib40AndSystemCore(source);
            comp.VerifyDiagnostics();
        }

        [ConditionalFact(typeof(IsRelease))]
        public void ConstructorsWithLambdaAndParams()
        {
            const int n = 100;
            var builder = new StringBuilder();
            builder.AppendLine("using System;");
            for (int i = 0; i < n; i++)
            {
                builder.AppendLine($"class C{i} {{ }}");
            }
            builder.AppendLine("class C");
            builder.AppendLine("{");
            for (int i = 0; i < n; i++)
            {
                builder.AppendLine($"    internal static C F(C{i} x, params object[] args) => new C(x, y => F(y, args[1]), args[0]);");
                builder.AppendLine($"    internal C(C{i} x, Func<C{i}, C> f, params object[] args) {{ }}");
            }
            builder.AppendLine("}");
            var source = builder.ToString();
            var comp = CreateCompilationWithMscorlib40AndSystemCore(source);
            comp.VerifyDiagnostics();
        }

        [ConditionalFact(typeof(IsRelease))]
        public void ExtensionMethodsWithLambda()
        {
            const int n = 100;
            var builder = new StringBuilder();
            builder.AppendLine("using System;");
            for (int i = 0; i < n; i++)
            {
                builder.AppendLine($"class C{i} {{ }}");
            }
            builder.AppendLine("static class S");
            builder.AppendLine("{");
            for (int i = 0; i < n; i++)
            {
                builder.AppendLine($"    internal static void F(this C{i} x, Action<C{i}> a) {{ x.F(y => y.F(z => z.F(w => {{ }}))); }}");
            }
            builder.AppendLine("}");
            var source = builder.ToString();
            var comp = CreateCompilationWithMscorlib40AndSystemCore(source);
            comp.VerifyDiagnostics();
        }

        [ConditionalFact(typeof(IsRelease))]
        public void ExtensionMethodsWithLambdaAndParams()
        {
            const int n = 100;
            var builder = new StringBuilder();
            builder.AppendLine("using System;");
            for (int i = 0; i < n; i++)
            {
                builder.AppendLine($"class C{i} {{ }}");
            }
            builder.AppendLine("static class S");
            builder.AppendLine("{");
            for (int i = 0; i < n; i++)
            {
                builder.AppendLine($"    internal static void F(this C{i} x, Action<C{i}> a, params object[] args) {{ x.F(y => y.F(z => z.F(w => {{ }}), args[1]), args[0]); }}");
            }
            builder.AppendLine("}");
            var source = builder.ToString();
            var comp = CreateCompilationWithMscorlib40AndSystemCore(source);
            comp.VerifyDiagnostics();
        }

        [ConditionalFact(typeof(IsRelease), typeof(NoIOperationValidation))]
        public void ExtensionMethodsWithLambdaAndErrors()
        {
            const int n = 200;
            var builder = new StringBuilder();
            builder.AppendLine("using System;");
            for (int i = 0; i < n; i++)
            {
                builder.AppendLine($"class C{i} {{ }}");
            }
            builder.AppendLine("static class S");
            builder.AppendLine("{");
            for (int i = 0; i < n; i++)
            {
                if (i % 2 == 0)
                {
                    builder.AppendLine($"    internal static void F(this C{i} x) {{ x.G(y => y.G(z => z.F())); }}"); // No match for x.G(...).
                }
                else
                {
                    builder.AppendLine($"    internal static void G(this C{i} x, Action<C{i}> a) {{ }}");
                }
            }
            builder.AppendLine("}");
            var source = builder.ToString();
            var comp = CreateCompilationWithMscorlib40AndSystemCore(source);
            // error CS1929: 'Ci' does not contain a definition for 'G' and the best extension method overload 'S.G(C1, Action<C1>)' requires a receiver of type 'C1'
            var diagnostics = Enumerable.Range(0, n / 2).
                Select(i => Diagnostic(ErrorCode.ERR_BadInstanceArgType, "x").WithArguments($"C{i * 2}", "G", "S.G(C1, System.Action<C1>)", "C1")).
                ToArray();
            comp.VerifyDiagnostics(diagnostics);
        }

        [Fact, WorkItem(29360, "https://github.com/dotnet/roslyn/pull/29360")]
        public void RaceConditionOnImproperlyCapturedAnalyzedArguments()
        {
            const int n = 6;
            var builder = new StringBuilder();
            builder.AppendLine("using System;");

            for (int i = 0; i < n; i++)
            {
                builder.AppendLine($"public class C{i}");
                builder.AppendLine("{");

                for (int j = 0; j < n; j++)
                {
                    builder.AppendLine($"    public string M{j}()");
                    builder.AppendLine("    {");

                    for (int k = 0; k < n; k++)
                    {
                        for (int l = 0; l < n; l++)
                        {
                            builder.AppendLine($"        Class.Method((C{k} x{k}) => x{k}.M{l});");
                        }
                    }

                    builder.AppendLine("        return null;");
                    builder.AppendLine("    }");
                }

                builder.AppendLine("}");
            }

            builder.AppendLine(@"
public static class Class
{
    public static void Method<TClass>(Func<TClass, Func<string>> method) { }
    public static void Method<TClass>(Func<TClass, Func<string, string>> method) { }
}
");
            var source = builder.ToString();
            var comp = CreateCompilation(source);
            comp.VerifyDiagnostics();
        }

        [WorkItem(35949, "https://github.com/dotnet/roslyn/issues/35949")]
        [ConditionalFact(typeof(IsRelease))]
        public void NotNull_Complexity()
        {
            var source = @"
#nullable enable
using System;
using System.Diagnostics.CodeAnalysis;
class C
{
    C f = null!;

    void M(C c)
    {
        c.f = c;
        c.NotNull(
            x => x.f.NotNull(
                y => y.f.NotNull(
                    z => z.f.NotNull(
                        q => q.f.NotNull(
                            w => w.f.NotNull(
                                e => e.f.NotNull(
                                    r => r.f.NotNull(
                                        _ =>
                                        {
                                            """".NotNull(s => s);
                                            """".NotNull(s => s);
                                            """".NotNull(s => s);
                                            """".NotNull(s => s);
                                            """".NotNull(s => s);
                                            """".NotNull(s => s);
                                            """".NotNull(s => s);
                                            """".NotNull(s => s);
                                            """".NotNull(s => s);
                                            """".NotNull(s => s);
                                            """".NotNull(s => s);
                                            """".NotNull(s => s);

                                            return """";
                                        }))))))));
    }
}

static class Ext
{
    public static V NotNull<T, V>([NotNull] this T t, Func<T, V> f) => throw null!;
}
";
            var comp = CreateCompilation(new[] { NotNullAttributeDefinition, source });
            comp.VerifyDiagnostics();
        }

        [ConditionalFact(typeof(IsRelease))]
        [WorkItem(40495, "https://github.com/dotnet/roslyn/issues/40495")]
        public void NestedLambdas_01()
        {
            var source =
@"#nullable enable
using System.Linq;
class Program
{
    static void Main()
    {
        Enumerable.Range(0, 1).Sum(a =>
            Enumerable.Range(0, 1).Sum(b =>
            Enumerable.Range(0, 1).Sum(c =>
            Enumerable.Range(0, 1).Sum(d =>
            Enumerable.Range(0, 1).Sum(e =>
            Enumerable.Range(0, 1).Sum(f =>
            Enumerable.Range(0, 1).Count(g => true)))))));
    }
}";
            var comp = CreateCompilation(source);
            comp.VerifyDiagnostics();
        }

        /// <summary>
        /// A variation of <see cref="NestedLambdas_01"/> but with
        /// explicit parameter types and return type for the lambdas.
        /// </summary>
        [ConditionalFact(typeof(IsRelease))]
        public void NestedLambdas_WithParameterAndReturnTypes()
        {
            var source =
@"#nullable enable
using System.Linq;
class Program
{
    static void Main()
    {
        Enumerable.Range(0, 1).Sum(int (int a) =>
            Enumerable.Range(0, 1).Sum(int (int b) =>
            Enumerable.Range(0, 1).Sum(int (int c) =>
            Enumerable.Range(0, 1).Sum(int (int d) =>
            Enumerable.Range(0, 1).Sum(int (int e) =>
            Enumerable.Range(0, 1).Sum(int (int f) =>
            Enumerable.Range(0, 1).Sum(int (int g) =>
            Enumerable.Range(0, 1).Sum(int (int h) =>
            Enumerable.Range(0, 1).Sum(int (int i) =>
            Enumerable.Range(0, 1).Sum(int (int j) =>
            Enumerable.Range(0, 1).Sum(int (int k) =>
            Enumerable.Range(0, 1).Count(l => true))))))))))));
    }
}";
            var comp = CreateCompilation(source);
            comp.VerifyDiagnostics();
        }

        // Test should complete in several seconds if UnboundLambda.ReallyBind
        // uses results from _returnInferenceCache.
        [ConditionalFact(typeof(IsRelease))]
        [WorkItem(1083969, "https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1083969")]
        public void NestedLambdas_02()
        {
            var source =
@"using System.Collections.Generic;
using System.Linq;
class Program
{
    static void F(IEnumerable<int[]> x)
    {
        x.GroupBy(y => y[1]).SelectMany(x =>
        x.GroupBy(y => y[2]).SelectMany(x =>
        x.GroupBy(y => y[3]).SelectMany(x =>
        x.GroupBy(y => y[4]).SelectMany(x =>
        x.GroupBy(y => y[5]).SelectMany(x =>
        x.GroupBy(y => y[6]).SelectMany(x =>
        x.GroupBy(y => y[7]).SelectMany(x =>
        x.GroupBy(y => y[8]).SelectMany(x =>
        x.GroupBy(y => y[9]).SelectMany(x =>
        x.GroupBy(y => y[0]).SelectMany(x =>
        x.GroupBy(y => y[1]).SelectMany(x =>
        x.GroupBy(y => y[2]).SelectMany(x =>
        x.GroupBy(y => y[3]).SelectMany(x =>
        x.GroupBy(y => y[4]).SelectMany(x =>
        x.GroupBy(y => y[5]).SelectMany(x =>
        x.GroupBy(y => y[6]).SelectMany(x =>
        x.GroupBy(y => y[7]).SelectMany(x =>
        x.GroupBy(y => y[8]).SelectMany(x =>
        x.GroupBy(y => y[9]).SelectMany(x =>
        x.GroupBy(y => y[0]).Select(x => x.Average(z => z[0])))))))))))))))))))));
    }
}";
            var comp = CreateCompilation(source);
            comp.VerifyDiagnostics();
        }

        [ConditionalFact(typeof(NoIOperationValidation), Reason = "Timeouts")]
        [WorkItem(48886, "https://github.com/dotnet/roslyn/issues/48886")]
        public void ArrayInitializationAnonymousTypes()
        {
            const int nTypes = 250;
            const int nItemsPerType = 1000;

            var builder = new StringBuilder();
            for (int i = 0; i < nTypes; i++)
            {
                builder.AppendLine($"class C{i}");
                builder.AppendLine("{");
                builder.AppendLine("    static object[] F = new[]");
                builder.AppendLine("    {");
                for (int j = 0; j < nItemsPerType; j++)
                {
                    builder.AppendLine($"        new {{ Id = {j} }},");
                }
                builder.AppendLine("    };");
                builder.AppendLine("}");
            }

            var source = builder.ToString();
            var comp = CreateCompilation(source);
            comp.VerifyEmitDiagnostics();
        }

        [Fact]
        [WorkItem(49746, "https://github.com/dotnet/roslyn/issues/49746")]
        public void AnalyzeMethodsInEnabledContextOnly()
        {
            const int nMethods = 10000;

            var builder = new StringBuilder();
            builder.AppendLine("static class Program");
            builder.AppendLine("{");
            for (int i = 0; i < nMethods; i++)
            {
                builder.AppendLine(i % 2 == 0 ? "#nullable enable" : "#nullable disable");
                builder.AppendLine($"    static object F{i}(object arg{i}) => arg{i};");
            }
            builder.AppendLine("}");

            var source = builder.ToString();
            var comp = CreateCompilation(source);
            var nullableAnalysisData = new NullableWalker.NullableAnalysisData();
            comp.TestOnlyCompilationData = nullableAnalysisData;
            comp.VerifyDiagnostics();

            int analyzed = nullableAnalysisData.Data.Where(pair => pair.Value.RequiredAnalysis).Count();
            Assert.Equal(nMethods / 2, analyzed);
        }

        [Fact]
        [WorkItem(49745, "https://github.com/dotnet/roslyn/issues/49745")]
        public void NullableStateLambdas()
        {
            const int nFunctions = 10000;

            var builder = new StringBuilder();
            builder.AppendLine("#nullable enable");
            builder.AppendLine("class Program");
            builder.AppendLine("{");
            builder.AppendLine("    static void F1(System.Func<object, object> f) { }");
            builder.AppendLine("    static void F2(object arg)");
            builder.AppendLine("    {");
            for (int i = 0; i < nFunctions; i++)
            {
                builder.AppendLine($"        F1(arg{i} => arg{i});");
            }
            builder.AppendLine("    }");
            builder.AppendLine("}");

            var source = builder.ToString();
            var comp = CreateCompilation(source);
            var nullableAnalysisData = new NullableWalker.NullableAnalysisData();
            comp.TestOnlyCompilationData = nullableAnalysisData;
            comp.VerifyDiagnostics();

            var method = comp.GetMember("Program.F2");
            Assert.Equal(1, nullableAnalysisData.Data[method].TrackedEntries);
        }

        [Fact]
        [WorkItem(49745, "https://github.com/dotnet/roslyn/issues/49745")]
        public void NullableStateLocalFunctions()
        {
            const int nFunctions = 2000;

            var builder = new StringBuilder();
            builder.AppendLine("#nullable enable");
            builder.AppendLine("class Program");
            builder.AppendLine("{");
            builder.AppendLine("    static void F(object arg)");
            builder.AppendLine("    {");
            for (int i = 0; i < nFunctions; i++)
            {
                builder.AppendLine($"        _ = F{i}(arg);");
                builder.AppendLine($"        static object F{i}(object arg{i}) => arg{i};");
            }
            builder.AppendLine("    }");
            builder.AppendLine("}");

            var source = builder.ToString();
            var comp = CreateCompilation(source);
            var nullableAnalysisData = new NullableWalker.NullableAnalysisData();
            comp.TestOnlyCompilationData = nullableAnalysisData;
            comp.VerifyDiagnostics();

            var method = comp.GetMember("Program.F");
            Assert.Equal(1, nullableAnalysisData.Data[method].TrackedEntries);
        }

        [ConditionalFact(typeof(NoIOperationValidation))]
        public void NullableStateTooManyLocals_01()
        {
            const int nLocals = 65536;

            var builder = new StringBuilder();
            builder.AppendLine("#pragma warning disable 168");
            builder.AppendLine("#nullable enable");
            builder.AppendLine("class Program");
            builder.AppendLine("{");
            builder.AppendLine("    static void F(object arg)");
            builder.AppendLine("    {");
            for (int i = 1; i < nLocals; i++)
            {
                builder.AppendLine($"        object i{i};");
            }
            builder.AppendLine("        object i0 = arg;");
            builder.AppendLine("        if (i0 == null) i0.ToString();");
            builder.AppendLine("    }");
            builder.AppendLine("}");

            var source = builder.ToString();
            var comp = CreateCompilation(source);
            // No warning for 'i0.ToString()' because the local is not tracked
            // by the NullableWalker.Variables instance (too many locals).
            comp.VerifyDiagnostics();
        }

        [ConditionalFact(typeof(NoIOperationValidation), typeof(IsRelease))]
        public void NullableStateTooManyLocals_02()
        {
            const int nLocals = 65536;

            var builder = new StringBuilder();
            builder.AppendLine("#nullable enable");
            builder.AppendLine("class Program");
            builder.AppendLine("{");
            builder.AppendLine("    static object F()");
            builder.AppendLine("    {");
            builder.AppendLine("        object i0 = null;");
            for (int i = 1; i < nLocals; i++)
            {
                builder.AppendLine($"        var i{i} = i{i - 1};");
            }
            builder.AppendLine($"        return i{nLocals - 1};");
            builder.AppendLine("    }");
            builder.AppendLine("}");

            var source = builder.ToString();
            var comp = CreateCompilation(source);
            // https://github.com/dotnet/roslyn/issues/50588: Improve performance of assignments to many variables.
            comp.VerifyDiagnostics(
                // (6,21): warning CS8600: Converting null literal or possible null value to non-nullable type.
                //         object i0 = null;
                Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithLocation(6, 21),
                // (65542,16): warning CS8603: Possible null reference return.
                //         return i65535;
                Diagnostic(ErrorCode.WRN_NullReferenceReturn, "i65535").WithLocation(65542, 16));
        }

        [ConditionalFact(typeof(NoIOperationValidation), typeof(IsRelease))]
        public void NullableStateManyNestedFunctions()
        {
            const int nFunctions = 32768;

            var builder = new StringBuilder();
            builder.AppendLine("#nullable enable");
            builder.AppendLine("class Program");
            builder.AppendLine("{");
            builder.AppendLine("    static void F0(System.Action a) { }");
            builder.AppendLine("    static U F1<T, U>(T arg, System.Func<T, U> f) => f(arg);");
            builder.AppendLine("    static object F2(object arg)");
            builder.AppendLine("    {");
            builder.AppendLine("        if (arg == null) { }");
            builder.AppendLine("        var value = arg;");
            builder.AppendLine("        F0(() => { });");
            for (int i = 0; i < nFunctions / 2; i++)
            {
                builder.AppendLine($"        F0(() => {{ value = F1(value, arg{i} => arg{i}?.ToString()); }});");
            }
            builder.AppendLine("        return value;");
            builder.AppendLine("    }");
            builder.AppendLine("}");

            var source = builder.ToString();
            var comp = CreateCompilation(source);
            comp.VerifyDiagnostics(
                // (16395,16): warning CS8603: Possible null reference return.
                //         return value;
                Diagnostic(ErrorCode.WRN_NullReferenceReturn, "value").WithLocation(16395, 16));
        }

        [WorkItem(51739, "https://github.com/dotnet/roslyn/issues/51739")]
        [ConditionalFact(typeof(IsRelease))]
        public void NullableAnalysisNestedExpressionsInMethod()
        {
            const int nestingLevel = 40;

            var builder = new StringBuilder();
            builder.AppendLine("#nullable enable");
            builder.AppendLine("class C");
            builder.AppendLine("{");
            builder.AppendLine("    C F(int i) => this;");
            builder.AppendLine("    static void Main()");
            builder.AppendLine("    {");
            builder.AppendLine("        C c = new C()");
            for (int i = 0; i < nestingLevel; i++)
            {
                builder.AppendLine($"            .F({i})");
            }
            builder.AppendLine("            ;");
            builder.AppendLine("    }");
            builder.AppendLine("}");

            var source = builder.ToString();
            var comp = CreateCompilation(source);
            comp.TestOnlyCompilationData = new NullableWalker.NullableAnalysisData(maxRecursionDepth: nestingLevel / 2);
            comp.VerifyDiagnostics(
                // (7,15): error CS8078: An expression is too long or complex to compile
                //         C c = new C()
                Diagnostic(ErrorCode.ERR_InsufficientStack, "new").WithLocation(7, 15));
        }

        [WorkItem(51739, "https://github.com/dotnet/roslyn/issues/51739")]
        [ConditionalFact(typeof(IsRelease))]
        public void NullableAnalysisNestedExpressionsInLocalFunction()
        {
            const int nestingLevel = 40;

            var builder = new StringBuilder();
            builder.AppendLine("#nullable enable");
            builder.AppendLine("class C");
            builder.AppendLine("{");
            builder.AppendLine("    C F(int i) => this;");
            builder.AppendLine("    static void Main()");
            builder.AppendLine("    {");
            builder.AppendLine("        Local();");
            builder.AppendLine("        static void Local()");
            builder.AppendLine("        {");
            builder.AppendLine("        C c = new C()");
            for (int i = 0; i < nestingLevel; i++)
            {
                builder.AppendLine($"            .F({i})");
            }
            builder.AppendLine("            ;");
            builder.AppendLine("        }");
            builder.AppendLine("        Local();");
            builder.AppendLine("    }");
            builder.AppendLine("}");

            var source = builder.ToString();
            var comp = CreateCompilation(source);
            comp.TestOnlyCompilationData = new NullableWalker.NullableAnalysisData(maxRecursionDepth: nestingLevel / 2);
            comp.VerifyDiagnostics(
                // (10,15): error CS8078: An expression is too long or complex to compile
                //         C c = new C()
                Diagnostic(ErrorCode.ERR_InsufficientStack, "new").WithLocation(10, 15));
        }

        [ConditionalFact(typeof(NoIOperationValidation), typeof(IsRelease))]
        public void NullableAnalysis_CondAccess_ComplexRightSide()
        {
            var source1 = @"
#nullable enable
object? x = null;
C? c = null;
if (
";
            var source2 = @"
    )
{
}

class C
{
    public bool M(object? obj) => false;
}
";
            var sourceBuilder = new StringBuilder();
            sourceBuilder.Append(source1);
            for (var i = 0; i < 15; i++)
            {
                sourceBuilder.AppendLine($"    c?.M(x = {i}) == (");
            }
            sourceBuilder.AppendLine("    c!.M(x)");

            sourceBuilder.Append("    ");
            for (var i = 0; i < 15; i++)
            {
                sourceBuilder.Append(")");
            }

            sourceBuilder.Append(source2);

            var comp = CreateCompilation(sourceBuilder.ToString());
            comp.VerifyDiagnostics();
        }

        [ConditionalFact(typeof(IsRelease))]
        public void DefiniteAssignment_ManySwitchCasesAndLabels()
        {
            const int nLabels = 1500;

            // #nullable enable
            // class Program
            // {
            //     static int GetIndex() => 0;
            //     static void Main()
            //     {
            //         int index = 0;
            //         int tmp1;
            //         int tmp2; // unused
            //         goto L1498;
            // L0:
            //         if (index < 64) goto LSwitch;
            // L1:
            //         tmp1 = GetIndex();
            //         if (index != tmp1)
            //         {
            //             if (index < 64) goto LSwitch;
            //             goto L0;
            //         }
            // // repeat for L2:, ..., L1498:
            // // ...
            // L1499:
            //         tmp1 = GetIndex();
            //         return;
            // LSwitch:
            //         int tmp3 = index + 1;
            //         switch (GetIndex())
            //         {
            //             case 0:
            //                 index++;
            //                 goto L0;
            //             // repeat for case 1:, ..., case 1499:
            //             // ...
            //             default:
            //                 break;
            //         }
            //     }
            // }

            var builder = new StringBuilder();
            builder.AppendLine("#nullable enable");
            builder.AppendLine("class Program");
            builder.AppendLine("{");
            builder.AppendLine("    static int GetIndex() => 0;");
            builder.AppendLine("    static void Main()");
            builder.AppendLine("    {");
            builder.AppendLine("        int index = 0;");
            builder.AppendLine("        int tmp1;");
            builder.AppendLine("        int tmp2; // unused");
            builder.AppendLine($"        goto L{nLabels - 2};");
            builder.AppendLine("L0:");
            builder.AppendLine("        if (index < 64) goto LSwitch;");
            for (int i = 0; i < nLabels - 2; i++)
            {
                builder.AppendLine($"L{i + 1}:");
                builder.AppendLine("        tmp1 = GetIndex();");
                builder.AppendLine("        if (index != tmp1)");
                builder.AppendLine("        {");
                builder.AppendLine("            if (index < 64) goto LSwitch;");
                builder.AppendLine($"            goto L{i};");
                builder.AppendLine("        }");
            }
            builder.AppendLine($"L{nLabels - 1}:");
            builder.AppendLine("        tmp1 = GetIndex();");
            builder.AppendLine("        return;");
            builder.AppendLine("LSwitch:");
            builder.AppendLine("        int tmp3 = index + 1;");
            builder.AppendLine("        switch (GetIndex())");
            builder.AppendLine("        {");
            for (int i = 0; i < nLabels; i++)
            {
                builder.AppendLine($"            case {i}:");
                builder.AppendLine("                index++;");
                builder.AppendLine($"                goto L{i};");
            }
            builder.AppendLine("            default:");
            builder.AppendLine("                break;");
            builder.AppendLine("        }");
            builder.AppendLine("    }");
            builder.AppendLine("}");

            var source = builder.ToString();
            var comp = CreateCompilation(source);
            comp.VerifyDiagnostics(
                // (9,13): warning CS0168: The variable 'tmp2' is declared but never used
                //         int tmp2; // unused
                Diagnostic(ErrorCode.WRN_UnreferencedVar, "tmp2").WithArguments("tmp2").WithLocation(9, 13));
        }
    }
}
