File: EditAndContinue\ActiveStatementTests.Methods.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 Microsoft.CodeAnalysis.CSharp.UnitTests;
using Microsoft.CodeAnalysis.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 ActiveStatementTests_Methods : EditingTestBase
{
    #region Methods
 
    [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/740443")]
    public void Method_Delete_Leaf1()
    {
        var src1 = @"
class C
{
    static void Main(string[] args)
    {
        <AS:1>Goo(1);</AS:1>
    }
 
    static void Goo(int a)
    {
        <AS:0>Console.WriteLine(a);</AS:0>
    }
}";
        var src2 = @"
<AS:0>class C</AS:0>
{
    static void Main(string[] args)
    {
        <AS:1>Goo(1);</AS:1>
    }
}
";
 
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        EditAndContinueValidation.VerifySemantics(
            [edits],
            [
                DocumentResults(
                    active,
                    diagnostics: [Diagnostic(RudeEditKind.DeleteActiveStatement, "class C", DeletedSymbolDisplay(FeaturesResources.method, "C.Goo(int)"))])
            ]);
    }
 
    [Fact]
    public void Method_Rename_Leaf1()
    {
        var src1 = @"
class C
{
    static void Goo(int a)
    {
        <AS:0>Console.WriteLine(a);</AS:0>
    }
}";
        var src2 = @"
class C
{
    static void Boo(int a)
    {
        <AS:0>Console.WriteLine(a);</AS:0>
    }
}
";
 
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(
            active,
            diagnostics: [Diagnostic(RudeEditKind.ChangingNameOrSignatureOfActiveMember, "static void Boo(int a)", GetResource("method"))]);
    }
 
    [Fact]
    public void Method_Body_Delete1()
    {
        var src1 = "class C { int M() { <AS:0>return 1;</AS:0> } }";
        var src2 = "class C { <AS:0>extern int M()</AS:0>; }";
 
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active,
            Diagnostic(RudeEditKind.ModifiersUpdate, "extern int M()", GetResource("method")));
    }
 
    [Fact]
    public void Method_ExpressionBody_Delete1()
    {
        var src1 = "class C { int M() => <AS:0>1</AS:0>; }";
        var src2 = "class C { <AS:0>extern int M()</AS:0>; }";
 
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active,
            Diagnostic(RudeEditKind.ModifiersUpdate, "extern int M()", GetResource("method")));
    }
 
    [Theory]
    [InlineData("public override string ToString() => <AS:0>null</AS:0>;")]
    [InlineData("public override int GetHashCode() => <AS:0>1</AS:0>;")]
    [InlineData("public virtual bool Equals(C other) => <AS:0>true</AS:0>;")]
    [InlineData("protected virtual bool PrintMembers(System.Text.StringBuilder builder) => <AS:0>true</AS:0>;")]
    [InlineData("public void Deconstruct(out int X) <AS:0>{</AS:0> X = 1; }")]
    [InlineData("protected C(C original) <AS:0>{</AS:0>}")]
    public void Record_Method_Delete_ReplacingCustomWithSynthesized(string methodImpl)
    {
        var src1 = "record C(int X) { " + methodImpl + " }";
        var src2 = "<AS:0>record C</AS:0>(int X) { }";
 
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active);
    }
 
    [Fact]
    public void Method_ExpressionBodyToBlockBody1()
    {
        var src1 = "class C { int M() => <AS:0>1</AS:0>; }";
        var src2 = "class C { int M() <AS:0>{</AS:0> return 1; } }";
 
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active);
    }
 
    [Fact]
    public void Method_BlockBodyToExpressionBody1()
    {
        var src1 = "class C { int M() { <AS:0>return 1;</AS:0> } }";
        var src2 = "class C { int M() => <AS:0>1</AS:0>; }";
 
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active);
    }
 
    // Generics
    [Fact]
    public void Update_Inner_GenericMethod()
    {
        var src1 = @"
class C
{
    static void Main(string[] args)
    {
        C c = new C();
        int a = 5;
        int b = 10;
        <AS:1>c.Swap(ref a, ref b);</AS:1>
    }
 
    void Swap<T>(ref T lhs, ref T rhs) where T : System.IComparable<T>
    {
        <AS:0>Console.WriteLine(""hello"");</AS:0>
    }
}";
        var src2 = @"
class C
{
    static void Main(string[] args)
    {
        while (true)
        {
            C c = new C();
            int a = 5;
            int b = 10;
            <AS:1>c.Swap(ref b, ref a);</AS:1>
        }
    }
 
    void Swap<T>(ref T lhs, ref T rhs) where T : System.IComparable<T>
    {
        <AS:0>Console.WriteLine(""hello"");</AS:0>
    }
}
";
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active,
            Diagnostic(RudeEditKind.ActiveStatementUpdate, "c.Swap(ref b, ref a);"));
    }
 
    [Fact]
    public void Update_Inner_ParameterType_GenericMethod()
    {
        var src1 = @"
class C
{
    static void Main(string[] args)
    {
        <AS:1>Swap(5,6);</AS:1>
    }
 
    static void Swap<T>(T lhs, T rhs) where T : System.IComparable<T>
    {
        <AS:0>Console.WriteLine(""hello"");</AS:0>
    }
}";
        var src2 = @"
class C
{
    static void Main(string[] args)
    {
        while (true)
        {
            <AS:1>Swap(null, null);</AS:1>
        }
    }
 
    static void Swap<T>(T lhs, T rhs) where T : System.IComparable<T>
    {
        <AS:0>Console.WriteLine(""hello"");</AS:0>
    }
}
";
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active,
            Diagnostic(RudeEditKind.ActiveStatementUpdate, "Swap(null, null);"));
    }
 
    [Fact]
    public void Update_Leaf_GenericMethod()
    {
        var src1 = @"
class C
{
    static void Main()
    {
        <AS:1>Swap(5,6);</AS:1>
    }
 
    static void Swap<T>(T lhs, T rhs) where T : System.IComparable<T>
    {
        <AS:0>Console.WriteLine(""hello"");</AS:0>
    }
}";
        var src2 = @"
class C
{
    static void Main()
    {
        <AS:1>Swap(5,6);</AS:1>
    }
 
    static void Swap<T>(T lhs, T rhs) where T : System.IComparable<T>
    {
        <AS:0>Console.WriteLine(""hello world!"");</AS:0>
    }
}
";
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(
            active,
            diagnostics: [Diagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, "static void Swap<T>(T lhs, T rhs)", GetResource("method"))],
            capabilities: EditAndContinueCapabilities.Baseline);
 
        edits.VerifySemantics(
            active,
            semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.Swap"), preserveLocalVariables: true)],
            capabilities: EditAndContinueCapabilities.GenericUpdateMethod);
    }
 
    // Async
    [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/749458")]
    public void Update_Leaf_AsyncMethod()
    {
        var src1 = @"
class Test
{
    static void Main(string[] args)
    {
        Test f = new Test();
        <AS:1>string result = f.WaitAsync().Result;</AS:1>
    }
 
    public async Task<string> WaitAsync()
    {
        <AS:0>await Task.Delay(1000);</AS:0>
        return ""Done"";
    }
}";
        var src2 = @"
class Test
{
    static void Main(string[] args)
    {
        Test f = new Test();
        <AS:1>string result = f.WaitAsync().Result;</AS:1>
    }
 
    public async Task<string> WaitAsync()
    {
        <AS:0>await Task.Delay(100);</AS:0>
        return ""Done"";
    }
}";
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active,
            capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType);
    }
 
    [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/749440")]
    public void Update_Inner_AsyncMethod()
    {
        var src1 = @"
class Test
{
    static void Main(string[] args)
    {
        Test f = new Test();
        <AS:1>string result = f.WaitAsync(5).Result;</AS:1>
    }
 
    public async Task<string> WaitAsync(int millis)
    {
        <AS:0>await Task.Delay(millis);</AS:0>
        return ""Done"";
    }
}";
        var src2 = @"
class Test
{
    static void Main(string[] args)
    {
        Test f = new Test();
        <AS:1>string result = f.WaitAsync(6).Result;</AS:1>
    }
 
    public async Task<string> WaitAsync(int millis)
    {
        <AS:0>await Task.Delay(millis);</AS:0>
        return ""Done"";
    }
}";
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active,
            Diagnostic(RudeEditKind.ActiveStatementUpdate, "string result = f.WaitAsync(6).Result;"));
    }
 
    [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/749440")]
    public void Update_Initializer_MultipleVariables1()
    {
        var src1 = @"
class Test
{
    static void Main(string[] args)
    {
        <AS:1>int a = F()</AS:1>, b = G();
    }
 
    public int F()
    {
        <AS:0>return 1;</AS:0>
    }
 
    public int G()
    {
        return 2;
    }
}";
        var src2 = @"
class Test
{
    static void Main(string[] args)
    {
        <AS:1>int a = G()</AS:1>, b = F();
    }
 
    public int F()
    {
        <AS:0>return 1;</AS:0>
    }
 
    public int G()
    {
        return 2;
    }
}";
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active,
            Diagnostic(RudeEditKind.ActiveStatementUpdate, "int a = G()"));
    }
 
    [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/749440")]
    public void Update_Initializer_MultipleVariables2()
    {
        var src1 = @"
class Test
{
    static void Main(string[] args)
    {
        int a = F(), <AS:1>b = G()</AS:1>;
    }
 
    public int F()
    {
        <AS:0>return 1;</AS:0>
    }
 
    public int G()
    {
        return 2;
    }
}";
        var src2 = @"
class Test
{
    static void Main(string[] args)
    {
        int a = G(), <AS:1>b = F()</AS:1>;
    }
 
    public int F()
    {
        <AS:0>return 1;</AS:0>
    }
 
    public int G()
    {
        return 2;
    }
}";
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active,
            Diagnostic(RudeEditKind.ActiveStatementUpdate, "b = F()"));
    }
 
    [Fact]
    public void MethodUpdateWithLocalVariables()
    {
        var src1 = @"
class C
{
    static void Main(string[] args)
    {
        int <N:0.0>a = 1</N:0.0>;
        int <N:0.1>b = 2</N:0.1>;
        <AS:0>System.Console.WriteLine(a + b);</AS:0>
    }
}
";
        var src2 = @"
class C
{
    static void Main(string[] args)
    {
        int <N:0.1>b = 2</N:0.1>;
        int <N:0.0>a = 1</N:0.0>;
        <AS:0>System.Console.WriteLine(a + b);</AS:0>
    }
}";
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
        var syntaxMap = GetSyntaxMap(src1, src2);
 
        edits.VerifySemantics(
            active,
            [SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.Main"), syntaxMap[0])]);
    }
 
    [Fact]
    public void Method_Partial_Update_Attribute_Definition()
    {
        var attribute = """
            public class A : System.Attribute { public A(int x) {} }
            """;
 
        var src1 = attribute +
            """
            partial class C { [A(1)]partial void F(); }
            partial class C { partial void F() <AS:0>{</AS:0> } }
            """;
 
        var src2 = attribute +
            """
            partial class C { [A(2)]partial void F(); }
            partial class C { partial void F() <AS:0>{</AS:0> } }
            """;
 
        var active = GetActiveStatements(src1, src2);
        var syntaxMap = GetSyntaxMap(src1, src2);
 
        EditAndContinueValidation.VerifySemantics(
            GetTopEdits(src1, src2),
            active,
            [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IMethodSymbol>("C.F").PartialImplementationPart, partialType: "C")],
            capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
    }
 
    [Fact]
    public void Method_Partial_Update_Attribute_DefinitionAndImplementation()
    {
        var attribute = """
            public class A : System.Attribute { public A(int x) {} }
            public class B : System.Attribute { public B(int x) {} }
            """;
 
        var srcA1 = attribute +
            """
            partial class C { [A(1)]partial void F(); }
            """;
 
        var srcB1 =
            """
            partial class C { [B(1)]partial void F() { var <N:0.0>x = 1</N:0.0>; <AS:0>}</AS:0> }
            """;
 
        var srcA2 = attribute +
            """
            partial class C { [A(2)]partial void F(); }
            """;
 
        var srcB2 =
            """
            partial class C { [B(2)]partial void F() { var <N:0.0>x = 1</N:0.0>; <AS:0>}</AS:0> }
            """;
 
        var syntaxMapB = GetSyntaxMap(srcB1, srcB2)[0];
 
        EditAndContinueValidation.VerifySemantics(
            [GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
            [
                DocumentResults(
                    activeStatements: GetActiveStatements(srcA1, srcA2),
                    semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IMethodSymbol>("C.F").PartialImplementationPart, partialType: "C")]),
                DocumentResults(
                    activeStatements: GetActiveStatements(srcB1, srcB2),
                    semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IMethodSymbol>("C.F").PartialImplementationPart, partialType: "C", syntaxMap: syntaxMapB)]),
            ],
            capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
    }
 
    [Fact]
    public void Method_Partial_SignatureChangeInsertDelete1()
    {
        var srcA1 = "partial class C { void F(byte x) <AS:0>{</AS:0> } }";
        var srcB1 = "partial class C { void F(char x) {} }";
        var srcA2 = "partial class C { <AS:0>void F(char x)</AS:0> {} }";
        var srcB2 = "partial class C { void F(byte x) {} }";
 
        EditAndContinueValidation.VerifySemantics(
            [GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
            [
                DocumentResults(
                    activeStatements: GetActiveStatements(srcA1, srcA2),
                    diagnostics: [Diagnostic(RudeEditKind.DeleteActiveStatement, "char x", GetResource("method"))]),
 
                DocumentResults(
                    activeStatements: GetActiveStatements(srcB1, srcB2),
                    diagnostics: [])
            ],
            capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
 
    }
 
    [Fact]
    public void Method_Partial_SignatureChangeInsertDelete2()
    {
        var srcA1 = "partial class C { void F(byte x) { } }";
        var srcB1 = "partial class C { void F(char x) <AS:0>{</AS:0>} }";
        var srcA2 = "partial class C { void F(char x) {} }";
        var srcB2 = "partial class C { <AS:0>void F(byte x)</AS:0> {} }";
 
        EditAndContinueValidation.VerifySemantics(
            [GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
            [
                DocumentResults(
                    activeStatements: GetActiveStatements(srcA1, srcA2),
                    diagnostics: []),
 
                DocumentResults(
                    activeStatements: GetActiveStatements(srcB1, srcB2),
                    diagnostics: [Diagnostic(RudeEditKind.DeleteActiveStatement, "byte x", GetResource("method"))])
            ],
            capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
 
    }
 
    #endregion
 
    #region Constuctors
 
    [Fact]
    public void Constructor_ExpressionBodyToBlockBody1()
    {
        var src1 = "class C { int x; C() => <AS:0>x = 1</AS:0>; }";
        var src2 = "class C { int x; <AS:0>C()</AS:0> { x = 1; } }";
 
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active);
    }
 
    [Fact]
    public void Constructor_BlockBodyToExpressionBody1()
    {
        var src1 = "class C { int x; C() <AS:0>{</AS:0> x = 1; } }";
        var src2 = "class C { int x; C() => <AS:0>x = 1</AS:0>; }";
 
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active);
    }
 
    [Fact]
    public void Constructor_BlockBodyToExpressionBody2()
    {
        var src1 = "class C { int x; <AS:0>C()</AS:0> { x = 1; } }";
        var src2 = "class C { int x; <AS:0>C()</AS:0> => x = 1; }";
 
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active);
    }
 
    [Fact]
    public void Constructor_BlockBodyToExpressionBody3()
    {
        var src1 = "class C { int x; C() : <AS:0>base()</AS:0> { x = 1; } }";
        var src2 = "class C { int x; <AS:0>C()</AS:0> => x = 1; }";
 
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active);
    }
 
    #endregion
 
    #region Properties
 
    [Fact]
    public void Property_ExpressionBodyToBlockBody1()
    {
        var src1 = "class C { int P => <AS:0>1</AS:0>; }";
        var src2 = "class C { int P { get <AS:0>{</AS:0> return 1; } } }";
 
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active);
    }
 
    [Fact]
    public void Property_ExpressionBodyToBlockBody2()
    {
        var src1 = "class C { int P => <AS:0>1</AS:0>; }";
        var src2 = "class C { int P { get <AS:0>{</AS:0> return 1; } set { } } }";
 
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(
            active,
            capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
    }
 
    [Fact]
    public void Property_ExpressionBodyToBlockBody3()
    {
        var src1 = "class C { int P => <AS:0>1</AS:0>; }";
        var src2 = "class C { int P { set { } get <AS:0>{</AS:0> return 1; } } }";
 
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(
            active,
            capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
    }
 
    [Fact]
    public void Property_ExpressionBodyToBlockBody_NonLeaf()
    {
        var src1 = @"
class C 
{ 
    int P => <AS:1>M()</AS:1>; 
    int M() { <AS:0>return 1;</AS:0> } 
}
";
        var src2 = @"
class C 
{ 
    int P { get <AS:1>{</AS:1> return M(); } } 
    int M() { <AS:0>return 1;</AS:0> } 
}
";
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active,
            Diagnostic(RudeEditKind.ActiveStatementUpdate, "{"));
    }
 
    [Fact]
    public void Property_ExpressionBody_NonLeaf()
    {
        var src1 = @"
class C 
{ 
    int P => <AS:1>M()</AS:1>; 
    int M() { <AS:0>return 1;</AS:0> } 
}
";
        var src2 = @"
class C 
{ 
    int P => <AS:1>M()</AS:1>; 
    int M() { <AS:0>return 2;</AS:0> } 
}
";
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active);
    }
 
    [Fact]
    public void Property_BlockBodyToExpressionBody1()
    {
        var src1 = "class C { int P { get { <AS:0>return 1;</AS:0> } } }";
        var src2 = "class C { int P => <AS:0>1</AS:0>; }";
 
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active);
    }
 
    [Fact]
    public void Property_BlockBodyToExpressionBody2()
    {
        var src1 = "class C { int P { set { } get { <AS:0>return 1;</AS:0> } } }";
        var src2 = "class C { int P => <AS:0>1</AS:0>; }";
 
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemantics(
            [
                SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P")),
                SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
            ],
            capabilities: EditAndContinueCapabilities.Baseline);
    }
 
    [Fact]
    public void Property_BlockBodyToExpressionBody_NonLeaf()
    {
        var src1 = @"
class C 
{ 
    int P { get { <AS:1>return M();</AS:1> } } 
    int M() { <AS:0>return 1;</AS:0> } 
}
";
        var src2 = @"
class C 
{ 
    int P => <AS:1>M()</AS:1>; 
    int M() { <AS:0>return 1;</AS:0> } 
}
";
 
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        // Can be improved with https://github.com/dotnet/roslyn/issues/22696
        edits.VerifySemanticDiagnostics(active,
            Diagnostic(RudeEditKind.DeleteActiveStatement, "int P", FeaturesResources.code));
    }
 
    #endregion
 
    #region Indexers
 
    [Fact]
    public void Indexer_ExpressionBodyToBlockBody1()
    {
        var src1 = "class C { int this[int a] => <AS:0>1</AS:0>; }";
        var src2 = "class C { int this[int a] { get <AS:0>{</AS:0> return 1; } } }";
 
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active);
    }
 
    [Fact]
    public void Indexer_ExpressionBodyToBlockBody2()
    {
        var src1 = "class C { int this[int a] => <AS:0>1</AS:0>; }";
        var src2 = "class C { int this[int a] { get <AS:0>{</AS:0> return 1; } set { } } }";
 
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(
            active,
            capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
    }
 
    [Fact]
    public void Indexer_BlockBodyToExpressionBody1()
    {
        var src1 = "class C { int this[int a] { get { <AS:0>return 1;</AS:0> } } }";
        var src2 = "class C { int this[int a] => <AS:0>1</AS:0>; }";
 
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active);
    }
 
    [Fact]
    public void Indexer_BlockBodyToExpressionBody2()
    {
        var src1 = "class C { int this[int a] { get { <AS:0>return 1;</AS:0> } set { } } }";
        var src2 = "class C { int this[int a] => <AS:0>1</AS:0>; }";
 
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemantics(
            [
                SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.this[]")),
                SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_Item")),
                SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_Item"), deletedSymbolContainerProvider: c => c.GetMember("C")),
            ],
            capabilities: EditAndContinueCapabilities.Baseline);
    }
 
    [Fact]
    public void Indexer_ExpressionBody_NonLeaf()
    {
        var src1 = @"
class C 
{ 
    int this[int index] => <AS:1>M()</AS:1>; 
    int M() { <AS:0>return 1;</AS:0> } 
}
";
        var src2 = @"
class C 
{ 
    int this[int index] => <AS:1>M()</AS:1>; 
    int M() { <AS:0>return 2;</AS:0> } 
}
";
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active);
    }
 
    [Fact]
    public void Update_Leaf_Indexers1()
    {
        var src1 = @"
class Test
{
    static void Main()
    {
        var c = new C<int>();
        <AS:1>c[0] = 1;</AS:1>
    }
}
 
class C<T>
{
    public T this[int i]
    {
        get => 0;
        set { <AS:0>value = i;</AS:0> }
    }
}";
        var src2 = @"
class Test
{
    static void Main()
    {
        var c = new C<int>();
        <AS:1>c[0] = 1;</AS:1>
    }
}
 
class C<T>
{
    public T this[int i]
    {
        get => 0;
        set { <AS:0>value = i + 1;</AS:0> }
    }
}";
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active,
            diagnostics: [Diagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, "set", GetResource("indexer setter"))],
            capabilities: EditAndContinueCapabilities.Baseline);
 
        edits.VerifySemantics(active,
            semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_Item"), preserveLocalVariables: true)],
            capabilities: EditAndContinueCapabilities.GenericUpdateMethod);
    }
 
    [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/750244")]
    public void Update_Inner_Indexers_Setter()
    {
        var src1 = @"
using System;
class Test
{
    static void Main(string[] args)
    {
        SampleCollection<string> stringCollection = new SampleCollection<string>();
        <AS:1>stringCollection[0] = ""hello"";</AS:1>
        Console.WriteLine(stringCollection[0]);
    }
}
 
class SampleCollection<T>
{
    private T[] arr = new T[100];
    public T this[int i]
    {
        get { return arr[i]; }
        set { <AS:0>arr[i] = value;</AS:0> }
    }
}";
        var src2 = @"
using System;
class Test
{
    static void Main(string[] args)
    {
        SampleCollection<string> stringCollection = new SampleCollection<string>();
        <AS:1>stringCollection[1] = ""hello"";</AS:1>
        Console.WriteLine(stringCollection[0]);
    }
}
 
class SampleCollection<T>
{
    private T[] arr = new T[100];
    public T this[int i]
    {
        get { return arr[i]; }
        set { <AS:0>arr[i+1] = value;</AS:0> }
    }
}";
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        // Rude edits of active statements (AS:1) are not reported if the top-level edits are rude.
        edits.VerifySemanticDiagnostics(active,
            Diagnostic(RudeEditKind.ActiveStatementUpdate, "stringCollection[1] = \"hello\";"));
    }
 
    [Fact]
    public void Update_Leaf_Indexers_Getter()
    {
        var src1 = @"
class Test
{
    static void Main()
    {
        var c = new C<int>();
        <AS:1>Console.WriteLine(c[0]);</AS:1>
    }
}
 
class C<T>
{
    public T this[int i]
    {
        get { <AS:0>return 0;</AS:0> }
        set { }
    }
}";
        var src2 = @"
class Test
{
    static void Main()
    {
        var c = new C<int>();
        <AS:1>Console.WriteLine(c[0]);</AS:1>
    }
}
 
class C<T>
{
    public T this[int i]
    {
        get { <AS:0>return 1;</AS:0> }
        set { }
    }
}";
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active,
            diagnostics: [Diagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, "get", GetResource("indexer getter"))],
            capabilities: EditAndContinueCapabilities.Baseline);
 
        edits.VerifySemantics(active,
            semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_Item"), preserveLocalVariables: true)],
            capabilities: EditAndContinueCapabilities.GenericUpdateMethod);
    }
 
    [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/750244")]
    public void Update_Inner_Indexers2()
    {
        var src1 = @"
class Test
{
    static void Main(string[] args)
    {
        SampleCollection<string> stringCollection = new SampleCollection<string>();
        stringCollection[0] = ""hello"";
        <AS:1>Console.WriteLine(stringCollection[0]);</AS:1>
    }
}
 
class SampleCollection<T>
{
    private T[] arr = new T[100];
    public T this[int i]
    {
        get { <AS:0>return arr[i];</AS:0> }
        set { arr[i] = value; }
    }
}";
        var src2 = @"
class Test
{
    static void Main(string[] args)
    {
        SampleCollection<string> stringCollection = new SampleCollection<string>();
        stringCollection[0] = ""hello"";
        <AS:1>Console.WriteLine(stringCollection[1]);</AS:1>
    }
}
 
class SampleCollection<T>
{
    private T[] arr = new T[100];
    public T this[int i]
    {
        get { <AS:0>return arr[0];</AS:0> }
        set { arr[i] = value; }
    }
}";
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        // Rude edits of active statements (AS:1) are not reported if the top-level edits are rude.
        edits.VerifySemanticDiagnostics(active,
            Diagnostic(RudeEditKind.ActiveStatementUpdate, "Console.WriteLine(stringCollection[1]);"));
    }
 
    [Fact]
    public void Deleted_Leaf_Indexers_Setter()
    {
        var src1 = @"
class Test
{
    static void Main()
    {
        var c = new C<int>();
        <AS:1>c[0] = 1;</AS:1>
    }
}
 
class C<T>
{
    public T this[int i]
    {
        get => 0;
        set { <AS:0>throw null;</AS:0> }
    }
}";
        var src2 = @"
class Test
{
    static void Main()
    {
        var c = new C<int>();
        <AS:1>c[0] = 1;</AS:1>
    }
}
 
class C<T>
{
    public T this[int i]
    {
        get => 0;
        set { <AS:0>}</AS:0>
    }
}";
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active,
            diagnostics: [Diagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, "set", GetResource("indexer setter"))],
            capabilities: EditAndContinueCapabilities.Baseline);
 
        edits.VerifySemantics(active,
            semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_Item"), preserveLocalVariables: true)],
            capabilities: EditAndContinueCapabilities.GenericUpdateMethod);
    }
 
    [Fact]
    public void Deleted_Inner_Indexers_Setter()
    {
        var src1 = @"
class Test
{
    static void Main(string[] args)
    {
        SampleCollection<string> stringCollection = new SampleCollection<string>();
        <AS:1>stringCollection[0] = ""hello"";</AS:1>
        Console.WriteLine(stringCollection[0]);
    }
}
 
class SampleCollection<T>
{
    private T[] arr = new T[100];
    public T this[int i]
    {
        get { return arr[i]; }
        set { <AS:0>arr[i] = value;</AS:0> }
    }
}";
        var src2 = @"
class Test
{
    static void Main(string[] args)
    {
        SampleCollection<string> stringCollection = new SampleCollection<string>();
        <AS:1>Console.WriteLine(stringCollection[0]);</AS:1>
    }
}
 
class SampleCollection<T>
{
    private T[] arr = new T[100];
    public T this[int i]
    {
        get { return arr[i]; }
        set { <AS:0>arr[i] = value;</AS:0> }
    }
}";
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active,
            Diagnostic(RudeEditKind.DeleteActiveStatement, "{", FeaturesResources.code));
    }
 
    [Fact]
    public void Deleted_Leaf_Indexers_Getter()
    {
        var src1 = @"
class Test
{
    static void Main(string[] args)
    {
        var c = new C<int>();
        <AS:1>Console.WriteLine(c[0]);</AS:1>
    }
}
 
class C<T>
{
    public T this[int i]
    {
        get { <AS:0>return 1;</AS:0> }
        set { }
    }
}";
        var src2 = @"
class Test
{
    static void Main(string[] args)
    {
        var c = new C<int>();
        <AS:1>Console.WriteLine(c[0]);</AS:1>
    }
}
 
class C<T>
{
    public T this[int i]
    {
        get { <AS:0>}</AS:0>
        set { }
    }
}";
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active,
            diagnostics: [Diagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, "get", GetResource("indexer getter"))],
            capabilities: EditAndContinueCapabilities.Baseline);
 
        edits.VerifySemantics(active,
            semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_Item"), preserveLocalVariables: true)],
            capabilities: EditAndContinueCapabilities.GenericUpdateMethod);
    }
 
    [Fact]
    public void Deleted_Inner_Indexers_Getter()
    {
        var src1 = @"
class Test
{
    static void Main(string[] args)
    {
        SampleCollection<string> stringCollection = new SampleCollection<string>();
        stringCollection[0] = ""hello"";
        <AS:1>Console.WriteLine(stringCollection[0]);</AS:1>
    }
}
 
class SampleCollection<T>
{
    private T[] arr = new T[100];
    public T this[int i]
    {
        get { <AS:0>return arr[i];</AS:0> }
        set { arr[i] = value; }
    }
}";
        var src2 = @"
class Test
{
    static void Main(string[] args)
    {
        SampleCollection<string> stringCollection = new SampleCollection<string>();
        stringCollection[0] = ""hello"";
    <AS:1>}</AS:1>
}
 
class SampleCollection<T>
{
    private T[] arr = new T[100];
    public T this[int i]
    {
        get { <AS:0>return arr[i];</AS:0> }
        set { arr[i] = value; }
    }
}";
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active,
            Diagnostic(RudeEditKind.DeleteActiveStatement, "{", FeaturesResources.code));
    }
 
    #endregion
 
    #region Operators
 
    [Fact]
    public void Operator_ExpressionBodyToBlockBody1()
    {
        var src1 = "class C { public static C operator +(C t1, C t2) => <AS:0>null</AS:0>; }";
        var src2 = "class C { public static C operator +(C t1, C t2) <AS:0>{</AS:0> return null; } }";
 
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active);
    }
 
    [Fact]
    public void Operator_ExpressionBodyToBlockBody2()
    {
        var src1 = "class C { public static explicit operator D(C t) => <AS:0>null</AS:0>; }";
        var src2 = "class C { public static explicit operator D(C t) <AS:0>{</AS:0> return null; } }";
 
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active);
    }
 
    [Fact]
    public void Operator_BlockBodyToExpressionBody1()
    {
        var src1 = "class C { public static C operator +(C t1, C t2) { <AS:0>return null;</AS:0> } }";
        var src2 = "class C { public static C operator +(C t1, C t2) => <AS:0>null</AS:0>; }";
 
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active);
    }
 
    [Fact]
    public void Operator_BlockBodyToExpressionBody2()
    {
        var src1 = "class C { public static explicit operator D(C t) { <AS:0>return null;</AS:0> } }";
        var src2 = "class C { public static explicit operator D(C t) => <AS:0>null</AS:0>; }";
 
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active);
    }
 
    [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/754274")]
    public void Update_Leaf_OverloadedOperator()
    {
        var src1 = @"
class Test
{
    static void Main(string[] args)
    {
        Test t1 = new Test(5);
        Test t2 = new Test(5);
        <AS:1>Test t3 = t1 + t2;</AS:1>
    }
    public static Test operator +(Test t1, Test t2)
    {
        <AS:0>return new Test(t1.a + t2.a);</AS:0>
    }
}";
        var src2 = @"
class Test
{
    static void Main(string[] args)
    {
        Test t1 = new Test(5);
        Test t2 = new Test(5);
        <AS:1>Test t3 = t1 + t2;</AS:1>
    }
    public static Test operator +(Test t1, Test t2)
    {
        <AS:0>return new Test(t1.a + 2 * t2.a);</AS:0>
    }
}";
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active);
    }
 
    [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/754274")]
    public void Update_Inner_OverloadedOperator()
    {
        var src1 = @"
class Test
{
    static void Main(string[] args)
    {
        Test t1 = new Test(5);
        Test t2 = new Test(5);
        <AS:1>Test t3 = t1 + t2;</AS:1>
    }
    public static Test operator +(Test t1, Test t2)
    {
        <AS:0>return new Test(t1.a + t2.a);</AS:0>
    }
    public static Test operator *(Test t1, Test t2)
    {
        return new Test(t1.a * t2.a);
    }
}";
        var src2 = @"
class Test
{
    static void Main(string[] args)
    {
        Test t1 = new Test(5);
        Test t2 = new Test(5);
        <AS:1>Test t3 = t1 * t2;</AS:1>
    }
    public static Test operator +(Test t1, Test t2)
    {
        <AS:0>return new Test(t1.a + t2.a);</AS:0>
    }
    public static Test operator *(Test t1, Test t2)
    {
        return new Test(t1.a * t2.a);
    }
}";
        var edits = GetTopEdits(src1, src2);
        var active = GetActiveStatements(src1, src2);
 
        edits.VerifySemanticDiagnostics(active,
            Diagnostic(RudeEditKind.ActiveStatementUpdate, "Test t3 = t1 * t2;"));
    }
 
    #endregion
}