File: EditAndContinue\LineEditTests.cs
Web Access
Project: src\src\Features\CSharpTest\Microsoft.CodeAnalysis.CSharp.Features.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Features.UnitTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
#pragma warning disable IDE0055 // Collection expression formatting
 
using System;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.UnitTests;
using Microsoft.CodeAnalysis.EditAndContinue;
using Microsoft.CodeAnalysis.Contracts.EditAndContinue;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue.UnitTests;
 
[UseExportProvider]
public class LineEditTests : EditingTestBase
{
    #region Top-level Code
 
    [Fact, WorkItem("https://dev.azure.com/devdiv/DevDiv/_workitems/edit/1426286")]
    public void TopLevelCode_LineChange()
    {
        var src1 = @"
Console.ReadLine(1);
";
        var src2 = @"
 
Console.ReadLine(1);
";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(1, 2) });
    }
 
    [Fact, WorkItem("https://dev.azure.com/devdiv/DevDiv/_workitems/edit/1426286")]
    public void TopLevelCode_LocalFunction_LineChange()
    {
        var src1 = @"
void F()
{
    Console.ReadLine(1);
}
";
        var src2 = @"
void F()
{
 
    Console.ReadLine(1);
}
";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(3, 4) });
    }
 
    #endregion
 
    #region Methods
 
    [Fact]
    public void Method_Reorder1()
    {
        var src1 = @"
class C
{
    static void G()
    {
        Console.ReadLine(1);
    }
 
    static void F()
    {
        Console.ReadLine(2);
    }
}
";
        var src2 = @"
class C
{
    static void F()
    {
        Console.ReadLine(2);
    }
 
    static void G()
    {
        Console.ReadLine(1);
    }
}";
        var edits = GetTopEdits(src1, src2);
 
        // Consider: we could detect that the body of the method hasn't changed and avoid creating an update.
        edits.VerifyLineEdits(
            new[]
            {
                new SourceLineUpdate(4, 9),
                new SourceLineUpdate(7, 7),
                new SourceLineUpdate(9, 4)
            },
            semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F"))]);
    }
 
    [Fact]
    public void Method_Reorder2()
    {
        var src1 = @"
class C
{
    static void Main()
    {
        F();
        G();
    }
 
    static int G()
    {
        return 1;
    }
 
    static int F()
    {
        return 2;
    }
}";
        var src2 = @"
class C
{
    static int F()
    {
        return 1;
    }
 
    static void Main()
    {
        F();
        G();
    }
 
    static int G()
    {
        return 2;
    }
}";
        var edits = GetTopEdits(src1, src2);
 
        // Consider: we could detect that the body of the method hasn't changed and create line edits instead of an update.
        edits.VerifyLineEdits(
            new[]
            {
                new SourceLineUpdate(4, 9),
            },
            semanticEdits:
            [
                SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F")),
                SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.G"))
            ]);
    }
 
    [Fact]
    public void Method_MultilineBreakpointSpans()
    {
        var src1 = @"
class C
{
    void F()
    {
        var x =
1;
    }
}
";
        var src2 = @"
class C
{
    void F()
    {
        var x =
 
1;
    }
}";
        // We need to recompile the method since an active statement span [|var x = 1;|]
        // needs to be updated but can't be by a line update.
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F"))]);
    }
 
    [Fact]
    public void Method_BlockBody_EntireBody1()
    {
        var src1 = @"
class C
{
    static void Bar()
    {
        Console.ReadLine(2);
    }
}
";
        var src2 = @"
class C
{
 
 
    static void Bar()
    {
        Console.ReadLine(2);
    }
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(4, 6) });
    }
 
    [Fact]
    public void Method_BlockBody_EntireBody2()
    {
        var src1 = @"
class C
{
    static void Bar()
    {
        Console.ReadLine(2);
    }
}
";
        var src2 = @"
class C
{
    static void Bar()
 
    {
        Console.ReadLine(2);
    }
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(4, 5) });
    }
 
    [Fact]
    public void Method_BlockBody1()
    {
        var src1 = @"
class C
{
    static void Bar()
    {
        Console.ReadLine(2);
    }
}
";
        var src2 = @"
class C
{
    static void Bar()
    {
 
        Console.ReadLine(2);
    }
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(5, 6) });
    }
 
    [Fact]
    public void Method_BlockBody2()
    {
        var src1 = @"
class C
{
    static void Bar()
    {
 
        Console.ReadLine(2);
    }
}
";
        var src2 = @"
class C
{
    static void Bar()
    {
        Console.ReadLine(2);
    }
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(6, 5) });
    }
 
    [Fact]
    public void Method_BlockBody3()
    {
        var src1 = @"
class C
{
    static void Bar()
    /*1*/
    {
        Console.ReadLine(2);
    }
}
";
        var src2 = @"
class C
{
    static void Bar()
    {
        /*2*/
        Console.ReadLine(2);
    }
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(5, 4) });
    }
 
    [Fact]
    public void Method_BlockBody4()
    {
        var src1 = @"
class C
{
    static void Bar()
    {
        Console.ReadLine(2);
    }
}
";
        var src2 = @"
class C
{
    static void Bar()
    {
        Console.ReadLine(2);
 
    }
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(6, 7) });
    }
 
    [Fact]
    public void Method_BlockBody5()
    {
        var src1 = @"
class C
{
    static void Bar()
    {
        if (F())
        {
            Console.ReadLine(2);
        }
    }
}
";
        var src2 = @"
class C
{
    static void Bar()
    {
        if (F())
        {
            Console.ReadLine(2);
 
        }
    }
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(8, 9) });
    }
 
    [Fact]
    public void Method_BlockBody_Recompile()
    {
        var src1 = @"
class C { static void Bar() { } }
";
        var src2 = @"
class C { /*--*/static void Bar() { } }";
 
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.Bar"))]);
    }
 
    [Fact]
    public void Method_ExpressionBody_EntireBody()
    {
        var src1 = @"
class C
{
    static int X() => 1;
 
    static int Y() => 1;
}
";
        var src2 = @"
class C
{
 
    static int X() => 1;
    static int Y() => 1;
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[]
            {
                new SourceLineUpdate(3, 4),
                new SourceLineUpdate(4, 4)
            });
    }
 
    [Fact]
    public void Method_Statement_Recompile1()
    {
        var src1 = @"
class C
{
    static void Bar()
    {
        Console.ReadLine(2);
    }
}
";
        var src2 = @"
class C
{
    static void Bar()
    {
        /**/Console.ReadLine(2);
    }
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.Bar"))]);
    }
 
    [Fact]
    public void Method_Statement_Recompile2()
    {
        var src1 = @"
class C
{
    static void Bar()
    {
        int <N:0.0>a = 1</N:0.0>;
        int <N:0.1>b = 2</N:0.1>;
        <AS:0>System.Console.WriteLine(1);</AS:0>
    }
}
";
        var src2 = @"
class C
{
    static void Bar()
    {
             int <N:0.0>a = 1</N:0.0>;
        int <N:0.1>b = 2</N:0.1>;
        <AS:0>System.Console.WriteLine(1);</AS:0>
    }
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.Bar"))]);
 
        var active = GetActiveStatements(src1, src2);
        var syntaxMap = GetSyntaxMap(src1, src2);
 
        edits.VerifySemantics(
            active,
            [SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.Bar"), syntaxMap[0])]);
    }
 
    [Fact]
    public void Method_GenericType_LineChange()
    {
        var src1 = @"
class C<T>
{
    static void Bar()
    {
        /*edit*/
        Console.ReadLine(2);
    }
}
";
        var src2 = @"
class C<T>
{
    static void Bar()
    {
        Console.ReadLine(2);
    }
}";
 
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(6, 5) });
    }
 
    [Fact]
    public void Method_GenericType_Recompile()
    {
        var src1 = @"
class C<T>
{
    static void Bar()
    {
/******/Console.ReadLine(2);
    }
}
";
        var src2 = @"
class C<T>
{
    static void Bar()
    {
/******//*edit*/Console.ReadLine(2);
    }
}";
 
        var edits = GetTopEdits(src1, src2);
 
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            diagnostics: [Diagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, "/******//*edit*/", FeaturesResources.method)],
            capabilities: EditAndContinueCapabilities.Baseline);
 
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.Bar"))],
            capabilities: EditAndContinueCapabilities.GenericUpdateMethod);
    }
 
    [Fact]
    public void Method_GenericMethod_Recompile()
    {
        var src1 = @"
class C
{
    static void Bar<T>()
    {
/******//*edit*/Console.ReadLine(2);
    }
}
";
        var src2 = @"
class C
{
    static void Bar<T>()
    {
/******/Console.ReadLine(2);
    }
}";
 
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            diagnostics: [Diagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, "/******/", FeaturesResources.method)],
            capabilities: EditAndContinueCapabilities.Baseline);
 
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.Bar"))],
            capabilities: EditAndContinueCapabilities.GenericUpdateMethod);
    }
 
    [Fact]
    public void Method_Async_Recompile()
    {
        var src1 = @"
class C
{
    static async Task<int> Bar()
    {
        Console.ReadLine(2);
    }
}
";
        var src2 = @"
class C
{
    static async Task<int> Bar()
    {
        Console.ReadLine( 
 
2);
    }
}";
 
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.Bar"), preserveLocalVariables: true)]);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69027")]
    public void Method_StackAlloc_LineChange()
    {
        var src1 = @"
class C
{
    void F()
    {
        Span<bool> x = stackalloc bool[64];
    }
}
";
        var src2 = @"
class C
{
    void F()
    {
 
        Span<bool> x = stackalloc bool[64];
    }
}";
 
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(5, 6) });
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69027")]
    public void Method_StackAlloc_Recompile()
    {
        var src1 = @"
class C
{
    void F()
    <AS:0>{</AS:0>
        <N:0.0>Span<bool> x = stackalloc bool[64];</N:0.0>
    }
}
";
        var src2 = @"
class C
{
    void F()
    <AS:0>{</AS:0>
        /**/<N:0.0>Span<bool> x = stackalloc bool[64];</N:0.0>
    }
}";
 
        // TODO: https://github.com/dotnet/roslyn/issues/67307
        // When we allow updating non-active bodies with stack alloc we will need to pass active statements to VerifyLineEdits
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            diagnostics: [Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc bool[64]", GetResource("method"))]);
 
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(
            active,
            [Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc bool[64]", GetResource("method"))]);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69027")]
    public void Method_StackAlloc_NonActive()
    {
        var src1 = @"
class C
{
    void F()
    {
        Span<bool> x = stackalloc bool[64];
    }
}
";
        var src2 = @"
class C
{
    void F()
    {
        /**/Span<bool> x = stackalloc bool[64];
    }
}";
 
        // TODO: consider allowing change in non-active members
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            diagnostics: [Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc bool[64]", GetResource("method"))]);
    }
 
    [Fact]
    public void Lambda_Recompile()
    {
        var src1 = @"
class C
{
    void F()
    {
        var x = new System.Func<int>(
            () => 1
 
        );
    }
}";
        var src2 = @"
class C
{
    void F()
    {
        var x = new System.Func<int>(
            () =>
                  1
        );
    }
}";
 
        var edits = GetTopEdits(src1, src2);
 
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F"), preserveLocalVariables: true)]);
    }
 
    #endregion
 
    #region Constructors
 
    [Fact]
    public void Constructor_Reorder()
    {
        var src1 = @"
class C
{
    public C(int a)
    {
    }
 
    public C(bool a)
    {
    }
}
";
        var src2 = @"
class C
{
    public C(bool a)
    {
    }
 
    public C(int a)
    {
    }
}";
        var edits = GetTopEdits(src1, src2);
 
        // Consider: we could detect that the body of the method hasn't changed and avoid creating an update.
        edits.VerifyLineEdits(
            new[]
            {
                new SourceLineUpdate(3, 7),
                new SourceLineUpdate(6, 6),
                new SourceLineUpdate(7, 3)
            },
            semanticEdits:
            [
                SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(c => c.Parameters is [{ Type.SpecialType: SpecialType.System_Boolean }]), preserveLocalVariables: true)
            ]);
    }
 
    [Fact]
    public void Constructor_ImplicitInitializer_BlockBody_LineChange()
    {
        var src1 = @"
class C
{
    public C(int a)
 
    {}
}
";
        var src2 = @"
class C
{
 
    public C(int a)
    {}
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(new[]
        {
            new SourceLineUpdate(3, 4),
            new SourceLineUpdate(4, 4)
        });
    }
 
    [Fact]
    public void Constructor_ImplicitInitializer_BlockBody_Recompile()
    {
        var src1 = @"
class C
{
    public C(int a
    )
    {}
}
";
        var src2 = @"
class C
{
    public C(int a
        )
    {}
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(), preserveLocalVariables: true)]);
 
    }
 
    [Fact]
    public void Constructor_ImplicitInitializer_ExpressionBodied_LineChange1()
    {
        var src1 = @"
class C
{
    int _a;
    public C(int a) => 
      _a = a;
}
";
        var src2 = @"
class C
{
    int _a;
    public C(int a) =>
 
      _a = a;
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(5, 6) });
    }
 
    [Fact]
    public void Constructor_ImplicitInitializer_ExpressionBodied_LineChange2()
    {
        var src1 = @"
class C
{
    int _a;
    public C(int a) 
      => _a = a;
}
";
        var src2 = @"
class C
{
    int _a;
    public C(int a)
 
      => _a = a;
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(5, 6) });
    }
 
    [Fact]
    public void Constructor_ImplicitInitializer_ExpressionBodied_LineChange3()
    {
        var src1 = @"
class C
{
    int _a;
    public C(int a) => 
      _a = a;
}
";
        var src2 = @"
class C
{
    int _a;
    public C(int a) => 
 
      _a = a;
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(5, 6) });
    }
 
    [Fact]
    public void Constructor_ImplicitInitializer_ExpressionBodied_LineChange4()
    {
        var src1 = @"
class C
{
    int _a;
    public C(int a) 
      => 
      _a = a;
}
";
        var src2 = @"
class C
{
    int _a;
    public C(int a) 
      
      => 
 
      _a = a;
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(6, 8) });
    }
 
    [Fact]
    public void Constructor_ImplicitInitializer_Primary_LineChange()
    {
        var src1 = @"
class C(int a);
";
        var src2 = @"
class
      C(int a);";
 
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(new[]
        {
            new SourceLineUpdate(1, 2)
        });
    }
 
    [Fact]
    public void Constructor_ImplicitInitializer_Primary_Recompile1()
    {
        var src1 = @"
class C (int a);
";
        var src2 = @"
class  C(int a);
";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true)]);
 
    }
 
    [Fact]
    public void Constructor_ImplicitInitializer_Primary_Recompile2()
    {
        var src1 = @"
class C(int a);
";
        var src2 = @"
class C(int a );
";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true)]);
 
    }
 
    [Fact]
    public void Constructor_ImplicitInitializer_PrimaryRecord_Recompile1()
    {
        var src1 = @"
record C (int P);
";
        var src2 = @"
record  C(int P);
";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits:
            [
                SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
                SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true),
            ]);
    }
 
    [Fact]
    public void Constructor_ImplicitInitializer_PrimaryRecord_Recompile2()
    {
        var src1 = @"
record C(int P);
";
        var src2 = @"
record C(int P );
";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits:
            [
                SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true)
            ]);
 
    }
 
    [Fact]
    public void Constructor_ImplicitInitializer_PrimaryAndParameter_Recompile3()
    {
        var src1 = @"
record C(int P);
";
        var src2 = @"
record C( int P);
";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits:
            [
                SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.P")),
                SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P")),
                SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_P")),
                SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true),
            ]);
    }
 
    [Fact]
    public void Constructor_ImplicitInitializer_PrimaryAndCopyCtorAndParameter_Recompile3()
    {
        var src1 = @"
record C<T>(int P);
";
        var src2 = @"
record C<T >(int P);
";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits:
            [
                SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
                SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.P")),
                SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P")),
                SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_P")),
                SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true),
            ],
            capabilities: EditAndContinueCapabilities.GenericUpdateMethod);
    }
 
    [Fact]
    public void Constructor_ExplicitInitializer_BlockBody_LineChange1()
    {
        var src1 = @"
class C
{
    public C(int a)
      : base()
    {
    }
}
";
        var src2 = @"
class C
{
    public C(int a) 
 
      : base()
    {
    }
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(4, 5) });
    }
 
    [Fact]
    public void Constructor_ExplicitInitializer_BlockBody_Recompile()
    {
        var src1 = @"
class C
{
    public C(int a)
      : base()
    {
    }
}
";
        var src2 = @"
class C
{
    public C(int a)
          : base()
    {
    }
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(), preserveLocalVariables: true)]);
    }
 
    [Fact]
    public void Constructor_ExplicitInitializer_BlockBody_PartialBodyLineChange1()
    {
        var src1 = @"
class C
{
    public C(int a)
      : base()
    {
    }
}
";
        var src2 = @"
class C
{
    public C(int a)
      : base()
 
    {
    }
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new SourceLineUpdate[] { new(5, 6) });
    }
 
    [Fact]
    public void Constructor_ExplicitInitializer_BlockBody_RudeRecompile1()
    {
        var src1 = @"
class C<T>
{
    public C(int a)
      : base()
    {
    }
}
";
        var src2 = @"
class C<T>
{
    public C(int a)
          : base()
    {
    }
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            diagnostics: [Diagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, "base", GetResource("constructor"))],
            capabilities: EditAndContinueCapabilities.Baseline);
 
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(), preserveLocalVariables: true)],
            capabilities: EditAndContinueCapabilities.GenericUpdateMethod);
    }
 
    [Fact]
    public void Constructor_ExplicitInitializer_ExpressionBodied_LineChange1()
    {
        var src1 = @"
class C
{
    int _a;
    public C(int a)
      : base() => _a = a;
}
";
        var src2 = @"
class C
{
    int _a;
    public C(int a)
 
      : base() => _a = a;
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(5, 6) });
    }
 
    [Fact]
    public void Constructor_ExplicitInitializer_ExpressionBodied_LineChange2()
    {
        var src1 = @"
class C
{
    int _a;
    public C(int a)
      : base() => 
                  _a = a;
}
";
        var src2 = @"
class C
{
    int _a;
    public C(int a)
 
      : base() => _a = a;
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new SourceLineUpdate[] { new(5, 6) });
    }
 
    [Fact]
    public void Constructor_ExplicitInitializer_ExpressionBodied_Recompile1()
    {
        var src1 = @"
class C
{
    int _a;
    public C(int a)
      : base() => _a 
                     = a;
}
";
        var src2 = @"
class C
{
    int _a;
    public C(int a)
      : base() => _a = a;
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(), preserveLocalVariables: true)]);
    }
 
    #endregion
 
    #region Destructors
 
    [Fact]
    public void Destructor_LineChange1()
    {
        var src1 = @"
class C
{
    ~C()
 
    {
    }
}
";
        var src2 = @"
class C
{
    ~C()
    {
    }
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(5, 4) });
    }
 
    [Fact]
    public void Destructor_ExpressionBodied_LineChange1()
    {
        var src1 = @"
class C
{
    ~C() => F();
}
";
        var src2 = @"
class C
{
    ~C() => 
            F();
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(3, 4) });
    }
 
    [Fact]
    public void Destructor_ExpressionBodied_LineChange2()
    {
        var src1 = @"
class C
{
    ~C() => F();
}
";
        var src2 = @"
class C
{
    ~C() 
         => F();
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(3, 4) });
    }
 
    #endregion
 
    #region Field Initializers
 
    [Fact]
    public void ConstantField()
    {
        var src1 = @"
class C
{
    const int Goo = 1;
}
";
        var src2 = @"
class C
{
    const int Goo = 
                    1;
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>());
    }
 
    [Fact]
    public void NoInitializer()
    {
        var src1 = @"
class C
{
    int Goo;
}
";
        var src2 = @"
class C
{
    int 
        Goo;
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>());
    }
 
    [Fact]
    public void Field_Reorder()
    {
        var src1 = @"
class C
{
    static int Goo = 1;
    static int Bar = 2;
}
";
        var src2 = @"
class C
{
    static int Bar = 1;
    static int Goo = 2;
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").StaticConstructors.Single(), preserveLocalVariables: true)]);
    }
 
    [Fact]
    public void Field_LineChange1()
    {
        var src1 = @"
class C
{
    static int Goo = 1;
}
";
        var src2 = @"
class C
{
 
 
 
    static int Goo = 1;
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(3, 6) });
    }
 
    [Fact]
    public void Field_LineChange2()
    {
        var src1 = @"
class C
{
    int Goo = 1, Bar = 2;
}
";
        var src2 = @"
class C
{
    int Goo = 1,
                 Bar = 2;
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits:
            [
                SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(), preserveLocalVariables: true)
            ]);
    }
 
    [Fact]
    public void Field_LineChange3()
    {
        var src1 = @"
class C
{
    [A]static int Goo = 1, Bar = 2;
}
";
        var src2 = @"
class C
{
    [A]
       static int Goo = 1, Bar = 2;
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new SourceLineUpdate[] { new SourceLineUpdate(3, 4) });
    }
 
    [Fact]
    public void Field_LineChange_Reloadable()
    {
        var src1 = ReloadableAttributeSrc + @"
[CreateNewOnMetadataUpdate]
class C
{
    int Goo = 1, Bar = 2;
}
";
        var src2 = ReloadableAttributeSrc + @"
[CreateNewOnMetadataUpdate]
class C
{
    int Goo = 1,
                 Bar = 2;
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits:
            [
                SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))
            ],
            capabilities: EditAndContinueCapabilities.NewTypeDefinition);
    }
 
    [Fact]
    public void Field_Recompile1a()
    {
        var src1 = @"
class C
{
    static int Goo = 1;
}
";
        var src2 = @"
class C
{
    static int Goo = 
                     1;
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits:
            [
                SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").StaticConstructors.Single(), preserveLocalVariables: true)
            ]);
    }
 
    [Fact]
    public void Field_Recompile1b()
    {
        var src1 = @"
class C
{
    static int Goo = 1;
}
";
        var src2 = @"
class C
{
    static int Goo 
                   = 1;
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits:
            [
                SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").StaticConstructors.Single(), preserveLocalVariables: true)
            ]);
    }
 
    [Fact]
    public void Field_Recompile1c()
    {
        var src1 = @"
class C
{
    static int Goo = 1;
}
";
        var src2 = @"
class C
{
    static int 
               Goo = 1;
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits:
            [
                SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").StaticConstructors.Single(), preserveLocalVariables: true)
            ]);
    }
 
    [Fact]
    public void Field_Recompile1d()
    {
        var src1 = @"
class C
{
    static int Goo = 1;
}
";
        var src2 = @"
class C
{
    static 
           int Goo = 1;
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits:
            [
                SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").StaticConstructors.Single(), preserveLocalVariables: true)
            ]);
    }
 
    [Fact]
    public void Field_Recompile1e()
    {
        var src1 = @"
class C
{
    static int Goo = 1;
}
";
        var src2 = @"
class C
{
    static int Goo = 1
                      ;
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits:
            [
                SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").StaticConstructors.Single(), preserveLocalVariables: true)
            ]);
    }
 
    [Fact]
    public void Field_Recompile2()
    {
        var src1 = @"
class C
{
    static int Goo = 1 + 1;
}
";
        var src2 = @"
class C
{
    static int Goo = 1 +  1;
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits:
            [
                SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").StaticConstructors.Single(), preserveLocalVariables: true)
            ]);
    }
 
    [Fact]
    public void Field_RudeRecompile1()
    {
        var src1 = @"
class C<T>
{
    static int Goo = 1 + 1;
}
";
        var src2 = @"
class C<T>
{
    static int Goo = 1 +/**/1;
}";
        var edits = GetTopEdits(src1, src2);
 
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            diagnostics: [Diagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, "/**/", GetResource("field"))],
            capabilities: EditAndContinueCapabilities.Baseline);
 
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits:
            [
                SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").StaticConstructors.Single(), preserveLocalVariables: true)
            ],
            capabilities: EditAndContinueCapabilities.GenericUpdateMethod);
    }
 
    [Fact]
    public void Field_Generic_Reloadable()
    {
        var src1 = ReloadableAttributeSrc + @"
[CreateNewOnMetadataUpdate]
class C<T>
{
    static int Goo = 1 + 1;
}
";
        var src2 = ReloadableAttributeSrc + @"
[CreateNewOnMetadataUpdate]
class C<T>
{
    static int Goo = 1 +  1;
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits:
            [
                SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))
            ],
            capabilities: EditAndContinueCapabilities.NewTypeDefinition);
    }
 
    #endregion
 
    #region Properties
 
    [Fact]
    public void Property1()
    {
        var src1 = @"
class C
{
    int P { get { return 1; } }
}
";
        var src2 = @"
class C
{
    int P { get { return 
                         1; } }
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.P").GetMethod)]);
    }
 
    [Fact]
    public void Property2()
    {
        var src1 = @"
class C
{
    int P { get { return 1; } }
}
";
        var src2 = @"
class C
{
    int P { get 
                { return 1; } }
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(new[] { new SourceLineUpdate(3, 4) });
    }
 
    [Fact]
    public void Property3()
    {
        var src1 = @"
class C
{
    int P { get { return 1; } set { } }
}
";
        var src2 = @"
class C
{
    
    int P { get { return 1; } set { } }
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(3, 4) });
    }
 
    [Fact]
    public void Property_ExpressionBody1()
    {
        var src1 = @"
class C
{
    int P => 1;
}
";
        var src2 = @"
class C
{
    int P => 
             1;
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(3, 4) });
    }
 
    [Fact]
    public void Property_GetterExpressionBody1()
    {
        var src1 = @"
class C
{
    int P { get => 1; }
}
";
        var src2 = @"
class C
{
    int P { get => 
                   1; }
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(3, 4) });
    }
 
    [Fact]
    public void Property_SetterExpressionBody1()
    {
        var src1 = @"
class C
{
    int P { set => F(); }
}
";
        var src2 = @"
class C
{
    int P { set => 
                   F(); }
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(3, 4) });
    }
 
    [Fact]
    public void Property_Initializer1()
    {
        var src1 = @"
class C
{
    int P { get; } = 1;
}
";
        var src2 = @"
class C
{
    int P { 
            get; } = 1;
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(3, 4) });
    }
 
    [Fact]
    public void Property_Initializer2()
    {
        var src1 = @"
class C
{
    int P { get; } = 1;
}
";
        var src2 = @"
class C
{
    int P { get; } = 
                     1;
}";
        // We can only apply one delta per line, but that affects both getter and initializer. So we need to recompile one of them.
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            lineEdits: new[] { new SourceLineUpdate(3, 4) },
            semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P"))]);
    }
 
    [Fact]
    public void Property_Initializer3()
    {
        var src1 = @"
class C
{
    int P { get; } = 1;
}
";
        var src2 = @"
class C
{
    int P
          { get; } = 
                     1;
}";
        // We can only apply one delta per line, but that affects both getter and initializer. So we need to recompile one of them.
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            lineEdits: new[] { new SourceLineUpdate(3, 5) },
            semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P"))]);
    }
 
    [Fact]
    public void Property_Initializer4()
    {
        var src1 = @"
class C
{
    int P { get; } = 1;
}
";
        var src2 = @"
class C
{
    int P { get; } =  1;
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(), preserveLocalVariables: true)]);
    }
 
    #endregion
 
    #region Properties
 
    [Fact]
    public void Indexer1()
    {
        var src1 = @"
class C
{
    int this[int a] { get { return 1; } }
}
";
        var src2 = @"
class C
{
    int this[int a] { get { return 
                                   1; } }
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.this[]").GetMethod)]);
    }
 
    [Fact]
    public void Indexer2()
    {
        var src1 = @"
class C
{
    int this[int a] { get { return 1; } }
}
";
        var src2 = @"
class C
{
    int this[int a] { get 
                          { return 1; } }
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(new[] { new SourceLineUpdate(3, 4) });
    }
 
    [Fact]
    public void Indexer3()
    {
        var src1 = @"
class C
{
    int this[int a] { get { return 1; } set { } }
}
";
        var src2 = @"
class C
{
    
    int this[int a] { get { return 1; } set { } }
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(new[] { new SourceLineUpdate(3, 4) });
    }
 
    [Fact]
    public void Indexer_ExpressionBody1()
    {
        var src1 = @"
class C
{
    int this[int a] => 1;
}
";
        var src2 = @"
class C
{
    int this[int a] => 
                       1;
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(3, 4) });
    }
 
    [Fact]
    public void Indexer_GetterExpressionBody1()
    {
        var src1 = @"
class C
{
    int this[int a] { get => 1; }
}
";
        var src2 = @"
class C
{
    int this[int a] { get => 
                             1; }
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(3, 4) });
    }
 
    [Fact]
    public void Indexer_SetterExpressionBody1()
    {
        var src1 = @"
class C
{
    int this[int a] { set => F(); }
}
";
        var src2 = @"
class C
{
    int this[int a] { set => 
                             F(); }
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(3, 4) });
    }
 
    #endregion
 
    #region Events
 
    [Fact]
    public void Event_LineChange1()
    {
        var src1 = @"
class C
{
    event Action E { add { } remove { } }
}
";
        var src2 = @"
class C
{
 
    event Action E { add { } remove { } }
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(3, 4) });
    }
 
    [Fact]
    public void Event_LineChange2()
    {
        var src1 = @"
class C
{
    event Action E { add 
                         { } remove { } }
}
";
        var src2 = @"
class C
{
    event Action E { add { } remove { } }
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(4, 3) });
    }
 
    [Fact]
    public void Event_LineChange3()
    {
        var src1 = @"
class C
{
    event Action E { add {
                           } remove { } }
}
";
        var src2 = @"
class C
{
    event Action E { add { } remove { } }
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(4, 3) });
    }
 
    [Fact]
    public void Event_LineChange4()
    {
        var src1 = @"
class C
{
    event Action E { add { } remove {
                                      } }
}
";
        var src2 = @"
class C
{
    event Action E { add { } remove { } }
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(4, 3) });
    }
 
    [Fact]
    public void Event_Recompile1()
    {
        var src1 = @"
class C
{
    event Action E { add { } remove { } }
}
";
        var src2 = @"
class C
{
    event Action E { add { } remove 
                                    { } }
}";
        // we can only apply one delta per line, but that would affect add and remove differently, so need to recompile
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IEventSymbol>("C.E").RemoveMethod)]);
    }
 
    [Fact]
    public void Event_Recompile2()
    {
        var src1 = @"
class C
{
    event Action E { add { } remove { } }
}
";
        var src2 = @"
class C
{
    event Action E { add { } remove {
                                      } }
}";
        // we can only apply one delta per line, but that would affect add and remove differently, so need to recompile
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IEventSymbol>("C.E").RemoveMethod)]);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/53263")]
    public void Event_ExpressionBody_MultipleBodiesOnTheSameLine1()
    {
        var src1 = @"
class C
{
    event Action E { add => F(); remove => F(); }
}
";
        var src2 = @"
class C
{
    event Action E { add => 
                            F(); remove => 
                                           F(); }
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(3, 4) },
            semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IEventSymbol>("C.E").RemoveMethod)]);
    }
 
    [Fact]
    public void Event_ExpressionBody()
    {
        var src1 = @"
class C
{
    event Action E { add 
                         => F(); remove 
                                        => F(); }
}
";
        var src2 = @"
class C
{
    event Action E { add => F(); remove => F(); }
}";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[] { new SourceLineUpdate(4, 3), new SourceLineUpdate(5, 3) });
    }
 
    #endregion
 
    #region Types
 
    [Fact]
    public void Type_Reorder1()
    {
        var src1 = @"
class C
{
    static int F1() => 1;
    static int F2() => 1;
}
 
class D
{
    static int G1() => 1;
    static int G2() => 1;
}
";
        var src2 = @"
class D
{
    static int G1() => 1;
    static int G2() => 1;
}
 
class C
{
    static int F1() => 1;
    static int F2() => 1;
}
";
        var edits = GetTopEdits(src1, src2);
        edits.VerifyLineEdits(
            new[]
            {
                new SourceLineUpdate(3, 9),
                new SourceLineUpdate(5, 5),
                new SourceLineUpdate(9, 3)
            });
    }
 
    #endregion
 
    #region Line Mappings
 
    [Fact]
    public void LineMapping_ChangeLineNumber_WithinMethod_NoSequencePointImpact()
    {
        var src1 = @"
class C
{
    static void F()
    {
        G(
#line 2 ""c""
            123
#line default
        );
    }
}";
        var src2 = @"
class C
{
    static void F()
    {
        G(
#line 3 ""c""
            123
#line default
        );
    }
}";
        var edits = GetTopEdits(src1, src2);
 
        // Line deltas can't be applied on the whole breakpoint span hence recompilation.
        edits.VerifyLineEdits(
            Array.Empty<SequencePointUpdates>(),
            semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F"))]);
    }
 
    /// <summary>
    /// Validates that changes in #line directives produce semantic updates of the containing method.
    /// </summary>
    [Fact]
    public void LineMapping_ChangeLineNumber_OutsideOfMethod()
    {
        var src1 = @"
#line 1 ""a""
class C
{
    int x = 1;
    static int y = 1;
    void F1() { }
    void F2() { }
}
class D
{
    public D() {}
 
#line 5 ""a""
    void F3() {}
 
#line 6 ""a""
    void F4() {}
}";
        var src2 = @"
#line 11 ""a""
class C
{
    int x = 1;
    static int y = 1;
    void F1() { }
    void F2() { }
}
class D
{
    public D() {}
 
#line 5 ""a""
    void F3() {}
    void F4() {}
}
";
        var edits = GetTopEdits(src1, src2);
 
        edits.VerifyLineEdits(
            new SequencePointUpdates[]
            {
                new("a", ImmutableArray.Create(
                    new SourceLineUpdate(2, 12), // x, y, F1, F2
                    new SourceLineUpdate(6, 6), // lines between F2 and D ctor
                    new SourceLineUpdate(9, 19))) // D ctor
            },
            semanticEdits:
            [
                SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D.F3")), // overlaps with "void F1() { }"
                SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D.F4")), // overlaps with "void F2() { }"
            ]);
    }
 
    [Fact]
    public void LineMapping_LineDirectivesAndWhitespace()
    {
        var src1 = @"
class C
{
#line 5 ""a""
#line 6 ""a""
 
 
 
    static void F() { } // line 9
}";
        var src2 = @"
class C
{
#line 9 ""a""
    static void F() { }
}";
        var edits = GetTopEdits(src1, src2);
 
        edits.VerifySemantics();
    }
 
    [Fact]
    public void LineMapping_MultipleFiles()
    {
        var src1 = @"
class C
{
    static void F()
    {
#line 1 ""a""
        A();
#line 1 ""b""
        B();
#line default
    }
}";
        var src2 = @"
class C
{
    static void F()
    {
#line 2 ""a""
        A();
#line 2 ""b""
        B();
#line default
    }
}";
        var edits = GetTopEdits(src1, src2);
 
        edits.VerifyLineEdits(
            new SequencePointUpdates[]
            {
                new("a", ImmutableArray.Create(new SourceLineUpdate(0, 1))),
                new("b", ImmutableArray.Create(new SourceLineUpdate(0, 1))),
            });
    }
 
    [Fact]
    public void LineMapping_FileChange_Recompile()
    {
        var src1 = @"
class C
{
    static void F()
    {
        A();
#line 1 ""a""
        B();
#line 3 ""a""
        C();
    }
 
 
    int x = 1;
}";
        var src2 = @"
class C
{
    static void F()
    {
        A();
#line 1 ""b""
        B();
#line 2 ""a""
        C();
    }
 
    int x = 1;
}";
        var edits = GetTopEdits(src1, src2);
 
        edits.VerifyLineEdits(
            new SequencePointUpdates[]
            {
                new("a", ImmutableArray.Create(new SourceLineUpdate(6, 4))),
            },
            semanticEdits:
            [
                SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F"))
            ]);
    }
 
    [Fact]
    public void LineMapping_FileChange_RudeEdit()
    {
        var src1 = @"
#line 1 ""a""
class C { static void F<T>() { } }
";
        var src2 = @"
#line 1 ""b""
class C { static void F<T>() { } }";
 
        var edits = GetTopEdits(src1, src2);
 
        edits.VerifyLineEdits(
             Array.Empty<SequencePointUpdates>(),
             diagnostics:
             [
                 Diagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, "{", GetResource("method"))
             ],
             capabilities: EditAndContinueCapabilities.Baseline);
 
        edits.VerifyLineEdits(
             Array.Empty<SequencePointUpdates>(),
             semanticEdits:
             [
                 SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F"))
             ],
             capabilities: EditAndContinueCapabilities.GenericUpdateMethod);
    }
 
    #endregion
}